diff --git a/nixos/modules/services/x11/display-managers/startx.nix b/nixos/modules/services/x11/display-managers/startx.nix index 2cf0657f5e0e..b388224398fd 100644 --- a/nixos/modules/services/x11/display-managers/startx.nix +++ b/nixos/modules/services/x11/display-managers/startx.nix @@ -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" + ]; + }; + }; }