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

# 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 redshift1 (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:

Environment=GDK_SCALE=2 GDK_DPI_SCALE=0.5

Which results in:

$ cat ~/.config/systemd/user/redshift-gtk.service.d/override.conf
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
# 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

# make Chrome pick up proxy settings stored in gconf

# HiDPI settings for GTK3+

# HiDPI settings for QT

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:
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 exporting 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™.

Thomas Jefferson thought that every law and every constitution should be torn down and rewritten from scratch every nineteen years--which means X is overdue.

XKCD Courtesy of Randall Munroe

  1. Redshift adjusts the color temperature of your screen according to your surroundings. This may help your eyes hurt less if you are working in front of the screen at night. Redshift is similar to f.lux↩︎

  2. systemctl --user start redshift↩︎

  3. For example, QT_FONT_DPI=$(($GDK_SCALE * 96)) or similar. ↩︎