blog/display-graphic-p-cl-letf-and-themes.post
2015-01-02 20:08:44 +01:00

89 lines
3.3 KiB
Text

;;;;;
title: display-graphic-p, cl-letf and themes
tags: emacs, elisp
date: 2014-10-27 21:18
format: md
;;;;;
I had a problem with my
[theme](https://github.com/ryuslash/yoshi-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.
``` emacs-lisp
(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.
``` emacs-lisp
(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` did'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](https://github.com/nicferrier/emacs-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.
``` emacs-lisp
(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](http://endlessparentheses.com/understanding-letf-and-how-it-replaces-flet.html)
about using `cl-letf` and things became very clear.
``` emacs-lisp
(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.
<!-- Local Variables: -->
<!-- mode: markdown -->
<!-- End: -->