nixos/xsession: use graphical systemd user target

While systemd suggests using the pre-defined graphical-session user
target, I found that this interface is difficult to use. Additionally,
no other major distribution, even in their unstable versions, currently
use this mechanism.

The window or desktop manager is supposed to run in a systemd user service
which activates graphical-session.target and the user services that are
binding to this target. The issue is that we can't elegantly pass the
xsession environment to the window manager session, in particular
whereas the PassEnvironment option does work for DISPLAY, it for some
mysterious reason won't for PATH.

This commit implements a new graphical user target that works just like
default.target. Services which should be run in a graphical session just
need to declare wantedBy graphical.target. The graphical target will be
activated in the xsession before executing the window or display manager.

Fixes #17858.

Changed files
+35 -25
nixos
+5 -2
nixos/modules/config/pulseaudio.nix
···
};
systemd.user = {
services.pulseaudio = {
serviceConfig = {
RestartSec = "500ms";
};
-
environment = { DISPLAY = ":${toString config.services.xserver.display}"; };
-
restartIfChanged = true;
};
};
})
···
};
systemd.user = {
services.pulseaudio = {
+
restartIfChanged = true;
serviceConfig = {
RestartSec = "500ms";
+
PassEnvironment = "DISPLAY";
};
+
};
+
sockets.pulseaudio = {
+
wantedBy = [ "sockets.target" ];
};
};
})
+2 -3
nixos/modules/programs/ssh.nix
···
environment.etc."ssh/ssh_known_hosts".text = knownHostsText;
# FIXME: this should really be socket-activated for über-awesomeness.
-
systemd.user.services.ssh-agent =
-
{ enable = cfg.startAgent;
-
description = "SSH Agent";
wantedBy = [ "default.target" ];
serviceConfig =
{ ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent";
···
environment.etc."ssh/ssh_known_hosts".text = knownHostsText;
# FIXME: this should really be socket-activated for über-awesomeness.
+
systemd.user.services.ssh-agent = mkIf cfg.startAgent
+
{ description = "SSH Agent";
wantedBy = [ "default.target" ];
serviceConfig =
{ ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent";
+2 -1
nixos/modules/services/monitoring/arbtt.nix
···
config = mkIf cfg.enable {
systemd.user.services.arbtt = {
description = "arbtt statistics capture service";
-
wantedBy = [ "default.target" ];
serviceConfig = {
Type = "simple";
···
config = mkIf cfg.enable {
systemd.user.services.arbtt = {
description = "arbtt statistics capture service";
+
wantedBy = [ "graphical-session.target" ];
+
partOf = [ "graphical-session.target" ];
serviceConfig = {
Type = "simple";
+2 -2
nixos/modules/services/x11/compton.nix
···
config = mkIf cfg.enable {
systemd.user.services.compton = {
description = "Compton composite manager";
-
wantedBy = [ "default.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/compton --config ${configFile}";
RestartSec = 3;
Restart = "always";
};
-
environment.DISPLAY = ":0";
};
environment.systemPackages = [ cfg.package ];
···
config = mkIf cfg.enable {
systemd.user.services.compton = {
description = "Compton composite manager";
+
wantedBy = [ "graphical-session.target" ];
+
partOf = [ "graphical-session.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/compton --config ${configFile}";
RestartSec = 3;
Restart = "always";
};
};
environment.systemPackages = [ cfg.package ];
+13
nixos/modules/services/x11/display-managers/default.nix
···
source ~/.xprofile
fi
# Allow the user to setup a custom session type.
if test -x ~/.xsession; then
exec ~/.xsession
···
''}
test -n "$waitPID" && wait "$waitPID"
exit 0
'';
···
config = {
services.xserver.displayManager.xserverBin = "${xorg.xorgserver.out}/bin/X";
};
imports = [
···
source ~/.xprofile
fi
+
# Start systemd user services for graphical sessions
+
${config.systemd.package}/bin/systemctl --user start graphical-session.target
+
# Allow the user to setup a custom session type.
if test -x ~/.xsession; then
exec ~/.xsession
···
''}
test -n "$waitPID" && wait "$waitPID"
+
+
${config.systemd.package}/bin/systemctl --user stop graphical-session.target
+
exit 0
'';
···
config = {
services.xserver.displayManager.xserverBin = "${xorg.xorgserver.out}/bin/X";
+
+
systemd.user.targets.graphical-session = {
+
unitConfig = {
+
RefuseManualStart = false;
+
StopWhenUnneeded = false;
+
};
+
};
};
imports = [
+2 -7
nixos/modules/services/x11/redshift.nix
···
config = mkIf cfg.enable {
systemd.user.services.redshift = {
description = "Redshift colour temperature adjuster";
-
wantedBy = [ "default.target" ];
serviceConfig = {
ExecStart = ''
${cfg.package}/bin/redshift \
···
'';
RestartSec = 3;
Restart = "always";
-
};
-
environment = {
-
DISPLAY = ":${toString (
-
let display = config.services.xserver.display;
-
in if display != null then display else 0
-
)}";
};
};
};
···
config = mkIf cfg.enable {
systemd.user.services.redshift = {
description = "Redshift colour temperature adjuster";
+
wantedBy = [ "graphical-session.target" ];
+
partOf = [ "graphical-session.target" ];
serviceConfig = {
ExecStart = ''
${cfg.package}/bin/redshift \
···
'';
RestartSec = 3;
Restart = "always";
};
};
};
+2 -1
nixos/modules/services/x11/unclutter-xfixes.nix
···
config = mkIf cfg.enable {
systemd.user.services.unclutter-xfixes = {
description = "unclutter-xfixes";
-
wantedBy = [ "graphical.target" ];
serviceConfig.ExecStart = ''
${cfg.package}/bin/unclutter \
--timeout ${toString cfg.timeout} \
···
config = mkIf cfg.enable {
systemd.user.services.unclutter-xfixes = {
description = "unclutter-xfixes";
+
wantedBy = [ "graphical-session.target" ];
+
partOf = [ "graphical-session.target" ];
serviceConfig.ExecStart = ''
${cfg.package}/bin/unclutter \
--timeout ${toString cfg.timeout} \
+3 -5
nixos/modules/services/x11/unclutter.nix
···
config = mkIf cfg.enable {
systemd.user.services.unclutter = {
description = "unclutter";
-
wantedBy = [ "default.target" ];
serviceConfig.ExecStart = ''
${cfg.package}/bin/unclutter \
-idle ${toString cfg.timeout} \
-
-display :${toString (
-
let display = config.services.xserver.display;
-
in if display != null then display else 0
-
)} \
-jitter ${toString (cfg.threeshold - 1)} \
${optionalString cfg.keystroke "-keystroke"} \
${concatMapStrings (x: " -"+x) cfg.extraOptions} \
-not ${concatStringsSep " " cfg.excluded} \
'';
serviceConfig.RestartSec = 3;
serviceConfig.Restart = "always";
};
···
config = mkIf cfg.enable {
systemd.user.services.unclutter = {
description = "unclutter";
+
wantedBy = [ "graphical-session.target" ];
+
partOf = [ "graphical-session.target" ];
serviceConfig.ExecStart = ''
${cfg.package}/bin/unclutter \
-idle ${toString cfg.timeout} \
-jitter ${toString (cfg.threeshold - 1)} \
${optionalString cfg.keystroke "-keystroke"} \
${concatMapStrings (x: " -"+x) cfg.extraOptions} \
-not ${concatStringsSep " " cfg.excluded} \
'';
+
serviceConfig.PassEnvironment = "DISPLAY";
serviceConfig.RestartSec = 3;
serviceConfig.Restart = "always";
};
+2 -3
nixos/modules/services/x11/urxvtd.nix
···
systemd.user = {
sockets.urxvtd = {
description = "socket for urxvtd, the urxvt terminal daemon";
-
after = [ "graphical.target" ];
-
wants = [ "graphical.target" ];
-
wantedBy = [ "sockets.target" ];
socketConfig = {
ListenStream = "%t/urxvtd-socket";
};
···
systemd.user = {
sockets.urxvtd = {
description = "socket for urxvtd, the urxvt terminal daemon";
+
wantedBy = [ "graphical-session.target" ];
+
partOf = [ "graphical-session.target" ];
socketConfig = {
ListenStream = "%t/urxvtd-socket";
};
+2 -1
nixos/modules/services/x11/xbanish.nix
···
config = mkIf cfg.enable {
systemd.user.services.xbanish = {
description = "xbanish hides the mouse pointer";
-
wantedBy = [ "default.target" ];
serviceConfig.ExecStart = ''
${pkgs.xbanish}/bin/xbanish ${cfg.arguments}
'';
···
config = mkIf cfg.enable {
systemd.user.services.xbanish = {
description = "xbanish hides the mouse pointer";
+
wantedBy = [ "graphical-session.target" ];
+
partOf = [ "graphical-session.target" ];
serviceConfig.ExecStart = ''
${pkgs.xbanish}/bin/xbanish ${cfg.arguments}
'';