In this post we will learn how to share environment variables (e.g.
$GDK_SCALE
) between a system user session and X11/Xorg.
The typical ~/.xinitrc
and/or ~/.xprofile
setup in
2020s involves some environment variable exports such as the following:
# fix java application decorations, for tiling window managers
export _JAVA_AWT_WM_NONREPARENTING=1
# make Chrome pick up proxy settings stored in gconf
export DESKTOP_SESSION=gnome
# HiDPI settings for GTK3+
export GDK_DPI_SCALE=0.5
export GDK_SCALE=2
# HiDPI settings for QT
export QT_FONT_DPI=192
This particular set of customizations stems from my dotfiles but there isn’t anything special about it. I’ll include an explanation anyway for completeness:
The java setting is meant for launching certain java-based applications from within a tiling window manager.
All the other settings are meant for 4K HiDPI displays. The baseline DPI is 96, which is too small for 4K monitors, the fonts and icons all look tiny. In order to make them scale it’s necessary to use a higher DPI. Typical setups use either 144 (x1.5) or 192 (x2.0), the bigger the DPI the bigger fonts and icons will appear in the screen.
Those exports work well for graphical applications launched from your favorite
window manager after it has already started, however if you decide to launch an
application from systemd
, those settings will not be picked up by it.
For example, if you decide to manage redshift
1 (more
specifically, redshift-gtk
which has a system tray app) from a systemd user
session2, its fonts will look small.
There are several ways to address this issue.
One of them is to edit the service file directly:
$ systemctl --user edit redshift-gtk
And then add:
[Unit]
Environment=GDK_SCALE=2 GDK_DPI_SCALE=0.5
Which results in:
$ cat ~/.config/systemd/user/redshift-gtk.service.d/override.conf
[Unit]
Environment=GDK_SCALE=2 GDK_DPI_SCALE=0.5
Which you can make effective by:
$ systemctl --user daemon-reload
$ systemctl --user restart redshift-gtk
I am not a fan of this approach though, because this step would need to be repeated to all service files you want to manage this way. There’s a better, DRY way to do so.
systemd
supports environment
files
(environment.d(5)
). User-defined ones live in
~/.config/environment.d/*.conf
by default.
This means we could produce the following file:
$ cat ~/.config/environment.d/user.conf
# systemd environment.d(5) EnvironmentFile
# https://www.freedesktop.org/software/systemd/man/environment.d.html
#
# Do not use export here.
#
# Alternatively
# systemctl --user import-environment [var1] [var2] [...]
#
# Troubleshooting
# systemctl --user show-environment
# fix java application decorations, for tiling window managers
_JAVA_AWT_WM_NONREPARENTING=1
# make Chrome pick up proxy settings stored in gconf
DESKTOP_SESSION=gnome
# HiDPI settings for GTK3+
GDK_DPI_SCALE=0.5
GDK_SCALE=2
# HiDPI settings for QT
QT_FONT_DPI=192
Which is applied to all systemd user service files automatically, no need to
set Environment=
manually everywhere.
However, now we need to maintain two different files: the systemd .conf
one
and the xorg ~/.xinitrc
one.
One elegant way to reduce maintenance burden is, in my opinion, the follownig:
$ cat ~/.xinitrc
...
# Parse user session environment variables.
# This file is shared with the systemd user instance.
# Export all variables: https://stackoverflow.com/a/30969768/1745064
set -a
[ -r ~/.config/environment.d/user.conf ] && . ~/.config/environment.d/user.conf
set +a
It does what you expect: the underlying shell sources the *.conf
file as if
you were export
ing each variable therein.
One caveat of this setup is that you cannot define the variables dynamically; for example, with subshells, with external programs, or with simple mathematical operations derived from other variables3.
Ultimately though you end up with only one file to manage, which is the systemd one. KISS™.