Enabling Emacs 24bit themes
This weekend I've spent some time on /r/unixporn
and enjoyed their beautiful desktop customizations. The next things I know is that I've ended up into another hell-hole to enable one theme on my Emacs. Let's see how deep the rabbit hole goes and summarize the keypoints learned.
§ Preface: Linux shells suck
Mr. Obvious, I suppose?
There is a crowd of lost souls out there trying to figure out and explain in which order the linux shell loads configuration files, because the most common answer is: "it depends". It's one of the classic UNIX stratified crust dating back to (I guess) ~20 (30?) years ago and always kept back compatible. Anyway, done with the complaints, let's move on.
Looking back, these were the steps I went through to reach the goal:
- Why Emacs screws up theme colors
- Because it's a 24bit colors theme: how do I have Emacs support such themes?
- It depends on the shell: what shell do I have and why it doesn't behave the way I want
- The shell doesn't know how to manage more than 256 colors: how do I fix this?
- I have the fix: how to apply it in a consistent way?
In one sentence: on a Linux shell, you need to customize terminfo
to have Emacs display more than 256 colors.
Each damn step took its own good deal of research, trial and error, let's go through each one.
§ 1) Why Emacs screws up theme colors
Many Emacs themes (especially the most beautiful) have more than 256 colors. In my narrow-minded view, never could I imagine that so many colors could be needed.
Installed the theme, run Emacs, I get slightly disappointed, the product does not match the label on the tin.
§ 2) How do I have Emacs display more colors?
Emacs support 24bit colors since 26.x. I notice that "graphical" Emacs (emacs-gtk
and emacs-lucid
, compiled against X11 and more libraries) behave differently. They bring a set of dependencies and eLisp functions to check for graphical capabilities. Now I understand why I don't have any of those. I've always used emacs-nox
, the version without dependencies, without even realizing the limitations.
Running emacs -nw
(no window) shows the same behaviour as using emacs-nox
, so I can test capabilities both on the "enhanced" emacs version and the barebone one.
All good, then? Nope. It's not a problem of emacs, rather of the shell I'm sitting on.
§ 3) What shell do I have and why it doesn't behave the way I want
Let's first check the terminal capabilities:
$ echo $TERM
xterm-256color
but
$ echo $COLORTERM
truecolor
This Github gist details 24bit support in many shells: it's very likely that a modern shell supports 24bit colors.
So how do I do that?
§ 4) Have more color on the Linux shell
In order to have 24bit colors, you need to first instruct the shell to use such a palette, being the standard 256 colors (or worse, if you're out of luck). This is done by generating a new terminfo
file (a database describing terminals).
You can verify this with one of the many scripts around, example this.
This procedure is detailed on GNU's emacs faq and it takes 10 seconds.
Let's generate the 24bit terminfo
file:
$ tic -x -o ~/.terminfo terminfo-24bit.src
# or better, a XDG basedir compliant path:
$ export TERMINFO=$XDG_CONFIG_HOME/terminfo
$ tic -x -o $TERMINFO terminfo-24bit.src
and tell our shell to use more colors:
$ export TERM=xterm-24bit
Now let's run emacs and compare the colors available with M-x list-colors-display
with the new env var enabled.
Interesting fact: even on a 24bit enabled shell, Emacs only has ~550 colors instead of ~256: this is something curious I didn't figure out.
As always, the funny thing is that once you identify exactly the problem, there is always a place where you could find the correct solution, this blog post for example - provided you could formulate the right question.
§ 5) Persist this configuration
I throw the new env vars in ~/.profile
so after the next login I will have everything set.
Nope.
When I open a new terminal (I use Gnome Terminal) from X11 or Wayland I don't see my $TERM applied and I am back to 256 colors. Terminal (non graphical) shells are fine.
More digging. And here I've learned the exact differences between shells: login, non-login, interactive and non-interactive.
Turns out that Gnome Terminal is the "culprit". By default it doesn't open a login shell (which makes sense) but that means that it overwrites your ~/.profile
with something else (which is not ok).
I can say "overwrite" by placing echo
statements all along the login process and save a log file:
-- Loading /etc/profile [start]: $TERM=dumb
-- Loading /etc/profile [end]: $TERM=dumb
-- Loading ~/.profile [start]: $TERM=dumb
-- Loading ~/.profile [end]: $TERM=xterm-24bit
-- Loading ~/.bashrc: $TERM=xterm-256color # <-- WHAT THE HELL?!
user@localhost:~$
printf
debugging never disappoints.
You have two choices: create ~/.bash_profile
or tell Gnome Terminal to behave like a login shell which will force reading the ./profile
file. I choose the second option because I don't want another confusing file lingering around.
Now all settings will survive a reboot.
§ Finished?
Of course not: bugs!
If you followed and implemented the above steps, congratulations: I have just broke your shell when you ssh into a remote server supporting only 256 colors! The TERM environment variable is always sent.
You can workaround this by aliasing the old color scheme:
alias ssh256="TERM=xterm-256color ssh"
EDIT: But that will have other problems, such giving ssh
will not autocomplete hosts anymore. So the most sensitive choice is to not set this configuration in the shell settings and only launch emacs with it:
alias emacs='TERM=xterm-24bit emacs'