Echo Area

display-graphic-p, cl-letf and themes

28 October 2014 5:18 AM (emacs | elisp)

I had a problem with my theme a while where the colors would be different depending on whether I was starting Emacs as a daemon or not. The colors module has some code that changes the way it works depending on whether a graphical or non-graphical display is run. This was messing with some of the colors in my theme.

My first thought was that it should just wait until the first frame is created before setting the theme. For a while this worked.

(if (daemonp)
    (add-hook after-make-frame-functions
              (lambda (frame) (load-theme yoshi t)))
  (add-hook emacs-startup-hook (lambda () (load-theme yoshi t))))

Some time later, that stopped working, because apparently display-graphic-p stopped returning true during the execution of the after-make-frame-functions hook. So I set out to change it so it would work again. This would have been easy in earlier versions of Emacs, where flet wasn���t yet deprecated.

(if (daemonp)
    (add-hook after-make-frame-functions
              (lambda (frame)
                (flet ((display-graphic-p () t))
                  (load-theme yoshi t))))
  (add-hook emacs-startup-hook (lambda () (load-theme yoshi t))))

Unfortunately, as stated, flet is deprecated. Whenever you compile it you���re warned and told that you should use cl-flet or cl-letf instead. I couldn���t quite figure out how, though. cl-flet does almost the same thing as flet, but lexically, so you can���t dynamically rebind display-graphic-p that way. cl-letf didn���t seem to be anything like flet at all, so I didn���t know how to ever use that to accomplish this.

Thankfully, there was noflet. This, for my purposes at least, works the same way as flet did. It is not part of Emacs, however. But in this case I didn���t know what else to do.

(if (daemonp)
    (add-hook after-make-frame-functions
              (lambda (frame)
                (noflet ((display-graphic-p (&optional display) t))
                  (load-theme yoshi t))))
  (add-hook emacs-startup-hook (lambda () (load-theme yoshi t))))

This worked perfectly, but I really don���t like depending on external packages for starting up emacs.

Then Artur Malabarba wrote a post about using cl-letf and things became very clear.

(if (daemonp)
    (add-hook after-make-frame-functions
              (lambda (frame)
                (cl-letf (((symbol-function display-graphic-p))
                          (lambda (&optional display) t))
                  (load-theme yoshi-theme t))))
  (add-hook emacs-startup-hook (lambda () (load-theme yoshi-theme t))))

It���s a bit more involved than the noflet solution, but it does remove a requirement from my init.el. What I failed to realize before reading Artur���s post is that cl-letf works a lot like Emacs��� new setf, which works a lot like Common Lisp���s setf, which makes it very powerful.

By now, my theme doesn���t use the colors module anymore.

No responses

Leave a Reply