include i3.service systemd user unit (#6547)

On Linux systems using systemd, we should activate graphical-session.target.

We conceptually need our i3.desktop file to do a
blockingly-run-via-systemd action (this is what scopes the activation of
graphical-session.target to the X session), and shipping an i3.service
is the best way to define the systemd unit we use for that.

Based on https://github.com/i3/i3/pull/5591 by David Sansome.

fixes https://github.com/i3/i3/issues/5186
This commit is contained in:
Michael Stapelberg
2025-12-16 18:01:07 +01:00
committed by GitHub
parent d45c6ebda5
commit c5d0d5e837
5 changed files with 114 additions and 11 deletions

18
libexec/i3-session.in Executable file
View File

@ -0,0 +1,18 @@
#!/bin/sh
# is-enabled seems like an elegant way to check for i3.service at first,
# but on NixOS it returns non-zero ('linked-runtime').
if [ "$(systemctl --user show -P CanStart @UNIT@ 2>/dev/null)" = "yes" ]
then
# Ensure the X11 environment variables are available
# to the systemd user session (required on some distros).
systemctl --user import-environment DISPLAY XAUTHORITY
if command -v dbus-update-activation-environment >/dev/null 2>&1; then
dbus-update-activation-environment DISPLAY XAUTHORITY
fi
systemctl --user --wait start @UNIT@
else
i3
fi

View File

@ -325,6 +325,8 @@ pangocairo_dep = dependency('pangocairo', method: 'pkg-config')
glib_dep = dependency('glib-2.0', method: 'pkg-config') glib_dep = dependency('glib-2.0', method: 'pkg-config')
gobject_dep = dependency('gobject-2.0', method: 'pkg-config') gobject_dep = dependency('gobject-2.0', method: 'pkg-config')
systemd_dep = dependency('systemd', required: false)
ev_dep = cc.find_library('ev') ev_dep = cc.find_library('ev')
inc = include_directories('include') inc = include_directories('include')
@ -599,11 +601,75 @@ install_subdir(
) )
install_subdir( install_subdir(
'share/', 'share/applications',
strip_directory: true, strip_directory: true,
install_dir: get_option('datadir'), install_dir: join_paths(get_option('datadir'), 'applications'),
) )
xsessions = join_paths(get_option('datadir'), 'xsessions')
xsession_cdata = configuration_data()
xsession_cdata.set('NAME', 'i3')
xsession_cdata.set('UNIT', 'i3.service')
xsession_cdata.set('EXEC', join_paths(get_option('prefix'), get_option('libexecdir'), 'i3-session'))
configure_file(
input: 'share/xsessions/i3.desktop.in',
output: 'i3.desktop',
configuration: xsession_cdata,
install: true,
install_dir: xsessions,
)
configure_file(
input: 'libexec/i3-session.in',
output: 'i3-session',
configuration: xsession_cdata,
install: true,
install_dir: get_option('libexecdir'),
)
xsession_log_cdata = configuration_data()
xsession_log_cdata.set('NAME', 'i3-with-shmlog')
xsession_log_cdata.set('UNIT', 'i3-with-shmlog.service')
xsession_log_cdata.set('EXEC', join_paths(get_option('prefix'), get_option('libexecdir'), 'i3-session-with-shmlog'))
configure_file(
input: 'share/xsessions/i3.desktop.in',
output: 'i3-with-shmlog.desktop',
configuration: xsession_log_cdata,
install: true,
install_dir: xsessions,
)
configure_file(
input: 'libexec/i3-session.in',
output: 'i3-session-with-shmlog',
configuration: xsession_log_cdata,
install: true,
install_dir: get_option('libexecdir'),
)
if systemd_dep.found()
userunitdir = systemd_dep.get_pkgconfig_variable('systemduserunitdir')
# The global cdata uses set_quoted (for C header files),
# but for systemd unit files, we must not use quoting.
systemd_cdata = configuration_data()
systemd_cdata.set('BINDIR', join_paths(get_option('prefix'), get_option('bindir')))
systemd_cdata.set('EXECSTART', join_paths(get_option('prefix'), get_option('bindir'), 'i3'))
configure_file(
input: 'systemd/i3.service.in',
output: 'i3.service',
configuration: systemd_cdata,
install_dir: userunitdir,
)
systemd_shmlog_cdata = configuration_data()
systemd_shmlog_cdata.merge_from(systemd_cdata)
systemd_shmlog_cdata.set('EXECSTART', join_paths(get_option('prefix'), get_option('bindir'), 'i3-with-shmlog'))
configure_file(
input: 'systemd/i3.service.in',
output: 'i3-with-shmlog.service',
configuration: systemd_shmlog_cdata,
install_dir: userunitdir,
)
endif
install_headers( install_headers(
'include/i3/ipc.h', 'include/i3/ipc.h',
subdir: 'i3', subdir: 'i3',

View File

@ -1,6 +0,0 @@
[Desktop Entry]
Name=i3 (with debug log)
Comment=improved dynamic tiling window manager
Exec=i3-with-shmlog
Type=Application
Keywords=tiling;wm;windowmanager;window;manager;

View File

@ -1,8 +1,8 @@
[Desktop Entry] [Desktop Entry]
Name=i3 Name=@NAME@
Comment=improved dynamic tiling window manager Comment=improved dynamic tiling window manager
Exec=i3 Exec=@EXEC@
TryExec=i3 TryExec=@EXEC@
Type=XSession Type=XSession
X-LightDM-DesktopName=i3 X-LightDM-DesktopName=i3
DesktopNames=i3 DesktopNames=i3

25
systemd/i3.service.in Normal file
View File

@ -0,0 +1,25 @@
[Unit]
Description=i3 window manager
# For more details, see the systemd.special man page:
# https://www.freedesktop.org/software/systemd/man/latest/systemd.special.html
# "The graphical-session-pre target contains services which set up
# the environment or global configuration of a graphical session,
# such as SSH/GPG agents (which need to export an environment variable
# into all desktop processes)."
Requires=graphical-session-pre.target
After=graphical-session-pre.target
# When i3.service is activated, activate graphical-session.target, too.
# When graphical-session.target deactivates, i3.service is deactivated.
# When i3.service deactivates, nothing keeps graphical-session.target
# alive anymore, so due to its StopWhenUnneeded property, it deactivates.
BindsTo=graphical-session.target
# Only consider graphical-session.target active once i3 has started.
Before=graphical-session.target
[Service]
Type=notify
ExecStart=@EXECSTART@
ExecReload=@BINDIR@/i3-msg reload