On themes, sway, emacs and kitty - the dark-mode script
Unlike Desktop Environments like Gnome and KDE, Window Managers like sway do not come with all the tools you might need or want. It's up to the user to perceive a need, do the research and then install, configure and use the appropriate tools for the job. Notwithstanding the earnest attempts by the DE's to bury the implementation details in obscure code and non-existent documentation, this approach gives me a lean and mean working environment albeit not as pretty. Plus the satisfaction of knowing what's going on.
In my own history through fvwm, fluxbox, i3wm and now sway I've never really wanted to bother with themes. I just made terminals and emacs have a black background and other programs could do what ever they liked. Such a heathen!
Recently I felt that need and did some research into the topic. It was rather more complex and harder to discover than I had thought.
I wanted to have a simple switch I could flick, perhaps a script, that would turn night into day. Let's have black text on a bright, white background in the daytime and reverse it in the evening. I wanted to have the changes applied to running programs without restarting them.
Sounds simple? Here are some notes on what I discovered for customising and a script that I use to do the flick.
All of this is on Fedora-31 but should be similar for other Linux distros.
Apologies if this is all obvious and well documented elsewhere. It was a journey for me and non-trivial.
GTK2 programs
GTK2 programs start up referring to ~/.gtkrc-2.0
for their theme
hints. Simple really, just lob in a new value:
gtk-theme-name=“Adwaita”
or
gtk-theme-name=“Adwaita-dark”
The hard part was understanding how to get running programs to notice
the change. lxappearance(1)
is a program from the LXDE desktop
environment that does both jobs. Unfortunately, it lacks a CLI
interface so it can only be used in the pointy-clicky mode and not in
a script.
By using strace(1)
on the different components, I was able to track this
down to a new (for me) system call eventfd(2)
which GTK2 uses as a
signalling mechanism. With a bit more research I found this script
fragment at https://crunchbang.org/forums/viewtopic.php?id=39646 which
invokes the correct event in the GTK2 world and makes those programs
re-read ~/.gtkrc-2.0
:
#!/usr/bin/python2 import sys, gtk events=gtk.gdk.Event(gtk.gdk.CLIENT_EVENT) data=gtk.gdk.atom_intern("_GTK_READ_RCFILES", False) events.data_format=8 events.send_event=True events.message_type=data events.send_clientmessage_toall()''
GTK3 programs
GTK3 programs start up referring to ~/.config/gtk-3.0/settings.ini
but the format is slightly different (of course!)
gtk-theme-name=Adwaita-dark
Note the cunning change in the use of quotation marks.
Again, getting programs that are already running to notice the change is lightly documented, if at all. But I found that by installing gnome-settings-daemon (a bit of an overkill for my lightweight purposes) there is a daemon that can do the job. So I added this to my sway config:
exec /usr/libexec/gsd-xsettings
I just wonder if this could be replaced by a simple script like the GTK2 one?
But that appears to be not the entire story.
dconf
Some programs do not respond to gsd-xsettings and dconf is needed:
dconf write /org/gnome/desktop/interface/gtk-theme "'Adwaita-dark'"
Again, note the amusing change in quotation - single quotes are
essential. The dconf approach also appears to work for live GTK2
programs and overrides the value in ~/.gtkrc-2.0
and
~/.config/gtk-3.0/settings.ini
gsettings
More comedy arises from yet another path to the same outcome. This invocation appears to be completely equivalent with it's own permutation on the quotation merry-go-around:
gsettings set org.gnome.desktop.interface gtk-theme Adwaita-dark
emacs
emacs(1)
. Hmmm. I've grown up with that program and am unlikely to
wean myself off it. There are 'PureGTK' versions floating around as
experiments but they are yet to hit the Fedora repositories outside
COPR so I haven't tried them to see how they respond to the standard
GTK signals.
In any case, for emacs(1)
I need to also modify the internal text areas
and emacs(1)
has its own themes spelled out in elisp. I decided on a
couple of good themes and added some code to my script to flip from
one theme to another. The script should be self explanatory. The
themes I chose were a modified dichromacy (for the colourblind) and a
derived dichromacy-dark which I created by 'inverting' the original
using a script at https://explog.in/notes/poet.html#monochrome
kitty
Now for my favorite wayland terminal emulator, kitty(1)
. It has it's
own unique configuration language and can have its colour changed on
the fly like this:
kitty @ set_colors --all foreground=white background=black
bash
My bash(1)
prompt contains colours and I have a separate script
(actually a function in ~/.bashrc
) that responds appropriately to the
setting of TERM_BACKGROUND
in {dark,light} so my script outputs that
setting and invokes setup_prompt.
eval $( dark-mode on )
… 'dark-mode on' outputs TERM_BACKGROUND=dark; setup_prompt
and
that's what gets eval'd.
These final 2 steps are the reason I don't put dark-mode(1)
into a cron
job - it needs to run in my stack of kitty(1)
terminal tabs.
KDE
KDE/Qt5/Plasma/whatcha-ma-call-it
Thanks to redditor r/progandy for this:
qt5ct(1)
is the key - install it (Fedora) with:
sudo dnf install qt5ct
This needs to be in the session environment:
export QT_QPA_PLATFORMTHEME=qt5ct
After running qt5ct(1)
I now have a ~/.config/qt5ct/qt5ct.conf
file that contains a line:
style=Adwaita
… I can hit that with my script and the qt5 programs automatically change their appearance!
Oddly, there's a 4s delay after changing the style in the file before the new style takes effect. But that's OK.
I also built qt5ct-refresh but it doesn't seem to speed up the 4s delay - or do anything AFAICT.
dark-mode
Here is the result of all this rambling: https://gitlab.com/wef/dotfiles/-/blob/master/bin/dark-mode