1
0
Fork 1
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-06-09 09:36:20 +09:00

nixos/startx: try to improve UX

There are some common pitfalls and no documentation around how to write
the .xinitrc to correctly start the window manager, the systemd
graphical session and, ideally, cleaning up afterwards.

To improve the user experience around startx this change:

1. Adds two options to generate a sane default script and extend
   it declaratively from NixOS.

2. Adds assertions to graphical-session.target so that it will fail
   clearly and immediately when users writing their own script forget to
   import the necessary environment variables.
This commit is contained in:
rnhmjoj 2025-03-10 16:57:20 +01:00 committed by Valentin Gagarin
parent 7252fbc580
commit e1c3082085

View file

@ -5,12 +5,16 @@
...
}:
with lib;
let
cfg = config.services.xserver.displayManager.startx;
# WM session script
# Note: this assumes a single WM has been enabled
sessionScript = lib.concatMapStringsSep "\n" (
i: i.start
) config.services.xserver.windowManager.session;
in
{
@ -19,40 +23,93 @@ in
options = {
services.xserver.displayManager.startx = {
enable = mkOption {
type = types.bool;
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to enable the dummy "startx" pseudo-display manager,
which allows users to start X manually via the "startx" command
from a vt shell. The X server runs under the user's id, not as root.
The user must provide a ~/.xinitrc file containing session startup
commands, see {manpage}`startx(1)`. This is not automatically generated
from the desktopManager and windowManager settings.
Whether to enable the dummy "startx" pseudo-display manager, which
allows users to start X manually via the `startx` command from a
virtual terminal.
::: {.note}
The X server will run under the current user, not as root.
:::
'';
};
generateScript = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to generate the system-wide xinitrc script (/etc/X11/xinit/xinitrc).
This script will take care of setting up the session for systemd user
services, running the window manager and cleaning up on exit.
::: {.note}
This script will only be used by `startx` when both `.xinitrc` does not
exists and the `XINITRC` environment variable is unset.
:::
'';
};
extraCommands = lib.mkOption {
type = lib.types.lines;
default = "";
description = ''
Shell commands to be added to the system-wide xinitrc script.
'';
};
};
};
###### implementation
config = mkIf cfg.enable {
services.xserver = {
exportConfiguration = true;
};
config = lib.mkIf cfg.enable {
services.xserver.exportConfiguration = true;
# Other displayManagers log to /dev/null because they're services and put
# Xorg's stdout in the journal
#
# To send log to Xorg's default log location ($XDG_DATA_HOME/xorg/), we do
# not specify a log file when running X
services.xserver.logFile = mkDefault null;
services.xserver.logFile = lib.mkDefault null;
# Implement xserverArgs via xinit's system-wide xserverrc
environment.etc."X11/xinit/xserverrc".source = pkgs.writeShellScript "xserverrc" ''
exec ${pkgs.xorg.xorgserver}/bin/X ${toString config.services.xserver.displayManager.xserverArgs} "$@"
exec ${pkgs.xorg.xorgserver}/bin/X \
${toString config.services.xserver.displayManager.xserverArgs} "$@"
'';
# Add a sane system-wide xinitrc script
environment.etc."X11/xinit/xinitrc".source = lib.mkIf cfg.generateScript (
pkgs.writeShellScript "xinitrc" ''
${cfg.extraCommands}
# start user services
systemctl --user import-environment DISPLAY XDG_SESSION_ID
systemctl --user start nixos-fake-graphical-session.target
# run the window manager script
${sessionScript}
wait $waitPID
# stop services and all subprocesses
systemctl --user stop nixos-fake-graphical-session.target
kill 0
''
);
environment.systemPackages = with pkgs; [ xorg.xinit ];
# Make graphical-session fail if the user environment has not been imported
systemd.user.targets.graphical-session = {
unitConfig.AssertEnvironment = [
"DISPLAY"
"XDG_SESSION_ID"
];
};
};
}