From c5d0d5e837da4b5785bb0baa30e266beb7671462 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 16 Dec 2025 18:01:07 +0100 Subject: [PATCH] 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 --- libexec/i3-session.in | 18 +++++ meson.build | 70 ++++++++++++++++++- share/xsessions/i3-with-shmlog.desktop | 6 -- share/xsessions/{i3.desktop => i3.desktop.in} | 6 +- systemd/i3.service.in | 25 +++++++ 5 files changed, 114 insertions(+), 11 deletions(-) create mode 100755 libexec/i3-session.in delete mode 100644 share/xsessions/i3-with-shmlog.desktop rename share/xsessions/{i3.desktop => i3.desktop.in} (81%) create mode 100644 systemd/i3.service.in diff --git a/libexec/i3-session.in b/libexec/i3-session.in new file mode 100755 index 00000000..a9e8c8a3 --- /dev/null +++ b/libexec/i3-session.in @@ -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 diff --git a/meson.build b/meson.build index c629413d..3f9ef25c 100644 --- a/meson.build +++ b/meson.build @@ -325,6 +325,8 @@ pangocairo_dep = dependency('pangocairo', method: 'pkg-config') glib_dep = dependency('glib-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') inc = include_directories('include') @@ -599,11 +601,75 @@ install_subdir( ) install_subdir( - 'share/', + 'share/applications', 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( 'include/i3/ipc.h', subdir: 'i3', diff --git a/share/xsessions/i3-with-shmlog.desktop b/share/xsessions/i3-with-shmlog.desktop deleted file mode 100644 index 3c634500..00000000 --- a/share/xsessions/i3-with-shmlog.desktop +++ /dev/null @@ -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; diff --git a/share/xsessions/i3.desktop b/share/xsessions/i3.desktop.in similarity index 81% rename from share/xsessions/i3.desktop rename to share/xsessions/i3.desktop.in index df33160c..479acbb9 100644 --- a/share/xsessions/i3.desktop +++ b/share/xsessions/i3.desktop.in @@ -1,8 +1,8 @@ [Desktop Entry] -Name=i3 +Name=@NAME@ Comment=improved dynamic tiling window manager -Exec=i3 -TryExec=i3 +Exec=@EXEC@ +TryExec=@EXEC@ Type=XSession X-LightDM-DesktopName=i3 DesktopNames=i3 diff --git a/systemd/i3.service.in b/systemd/i3.service.in new file mode 100644 index 00000000..3237c7a8 --- /dev/null +++ b/systemd/i3.service.in @@ -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