nixos/display-managers: extract generic display-manager bits

Some settings which where before inside the xserver module, are generic
and also required for SDDM under wayland. To make them easily re-usable
lets extract them.
This commit is contained in:
Sandro Jäckel 2024-02-27 10:06:13 +01:00
parent f955c92363
commit 6be2bfcc32
No known key found for this signature in database
GPG Key ID: 3AF5A43A3EECC2E5
5 changed files with 274 additions and 225 deletions

View File

@ -508,6 +508,7 @@
./services/development/nixseparatedebuginfod.nix ./services/development/nixseparatedebuginfod.nix
./services/development/rstudio-server/default.nix ./services/development/rstudio-server/default.nix
./services/development/zammad.nix ./services/development/zammad.nix
./services/display-managers/default.nix
./services/display-managers/greetd.nix ./services/display-managers/greetd.nix
./services/editors/emacs.nix ./services/editors/emacs.nix
./services/editors/haste.nix ./services/editors/haste.nix

View File

@ -0,0 +1,257 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.displayManager;
installedSessions = pkgs.runCommand "desktops"
{ # trivial derivation
preferLocalBuild = true;
allowSubstitutes = false;
}
''
mkdir -p "$out/share/"{xsessions,wayland-sessions}
${lib.concatMapStrings (pkg: ''
for n in ${lib.concatStringsSep " " pkg.providedSessions}; do
if ! test -f ${pkg}/share/wayland-sessions/$n.desktop -o \
-f ${pkg}/share/xsessions/$n.desktop; then
echo "Couldn't find provided session name, $n.desktop, in session package ${pkg.name}:"
echo " ${pkg}"
return 1
fi
done
if test -d ${pkg}/share/xsessions; then
${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/xsessions $out/share/xsessions
fi
if test -d ${pkg}/share/wayland-sessions; then
${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/wayland-sessions $out/share/wayland-sessions
fi
'') cfg.sessionPackages}
'';
dmDefault = config.services.xserver.desktopManager.default;
# fallback default for cases when only default wm is set
dmFallbackDefault = if dmDefault != null then dmDefault else "none";
wmDefault = config.services.xserver.windowManager.default;
defaultSessionFromLegacyOptions = dmFallbackDefault + lib.optionalString (wmDefault != null && wmDefault != "none") "+${wmDefault}";
in
{
options = {
services.displayManager = {
enable = lib.mkEnableOption "systemd's display-manager service";
preStart = lib.mkOption {
type = lib.types.lines;
default = "";
example = "rm -f /var/log/my-display-manager.log";
description = lib.mdDoc "Script executed before the display manager is started.";
};
execCmd = lib.mkOption {
type = lib.types.str;
example = lib.literalExpression ''"''${pkgs.lightdm}/bin/lightdm"'';
description = lib.mdDoc "Command to start the display manager.";
};
environment = lib.mkOption {
type = with lib.types; attrsOf unspecified;
default = {};
description = lib.mdDoc "Additional environment variables needed by the display manager.";
};
hiddenUsers = lib.mkOption {
type = with lib.types; listOf str;
default = [ "nobody" ];
description = lib.mdDoc ''
A list of users which will not be shown in the display manager.
'';
};
logToFile = lib.mkOption {
type = lib.types.bool;
default = false;
description = lib.mdDoc ''
Whether the display manager redirects the output of the
session script to {file}`~/.xsession-errors`.
'';
};
logToJournal = lib.mkOption {
type = lib.types.bool;
default = true;
description = lib.mdDoc ''
Whether the display manager redirects the output of the
session script to the systemd journal.
'';
};
# Configuration for automatic login. Common for all DM.
autoLogin = lib.mkOption {
type = lib.types.submodule ({ config, options, ... }: {
options = {
enable = lib.mkOption {
type = lib.types.bool;
default = config.user != null;
defaultText = lib.literalExpression "config.${options.user} != null";
description = lib.mdDoc ''
Automatically log in as {option}`autoLogin.user`.
'';
};
user = lib.mkOption {
type = with lib.types; nullOr str;
default = null;
description = lib.mdDoc ''
User to be used for the automatic login.
'';
};
};
});
default = {};
description = lib.mdDoc ''
Auto login configuration attrset.
'';
};
defaultSession = lib.mkOption {
type = lib.types.nullOr lib.types.str // {
description = "session name";
check = d:
lib.assertMsg (d != null -> (lib.types.str.check d && lib.elem d config.services.displayManager.sessionData.sessionNames)) ''
Default graphical session, '${d}', not found.
Valid names for 'services.xserver.displayManager.defaultSession' are:
${lib.concatStringsSep "\n " cfg.displayManager.sessionData.sessionNames}
'';
};
default =
if dmDefault != null || wmDefault != null then
defaultSessionFromLegacyOptions
else
null;
defaultText = lib.literalMD ''
Taken from display manager settings or window manager settings, if either is set.
'';
example = "gnome";
description = lib.mdDoc ''
Graphical session to pre-select in the session chooser (only effective for GDM, LightDM and SDDM).
On GDM, LightDM and SDDM, it will also be used as a session for auto-login.
'';
};
sessionData = lib.mkOption {
description = lib.mdDoc "Data exported for display managers convenience";
internal = true;
default = {};
};
sessionPackages = lib.mkOption {
type = lib.types.listOf (lib.types.package // {
description = "package with provided sessions";
check = p: lib.assertMsg
(lib.types.package.check p && p ? providedSessions
&& p.providedSessions != [] && lib.all lib.isString p.providedSessions)
''
Package, '${p.name}', did not specify any session names, as strings, in
'passthru.providedSessions'. This is required when used as a session package.
The session names can be looked up in:
${p}/share/xsessions
${p}/share/wayland-sessions
'';
});
default = [];
description = lib.mdDoc ''
A list of packages containing x11 or wayland session files to be passed to the display manager.
'';
};
};
};
imports = [
(lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "autoLogin" ] [ "services" "displayManager" "autoLogin" ])
(lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "defaultSession" ] [ "services" "displayManager" "defaultSession" ])
(lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "environment" ] [ "services" "displayManager" "environment" ])
(lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "execCmd" ] [ "services" "displayManager" "execCmd" ])
(lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logToFile" ] [ "services" "displayManager" "logToFile" ])
(lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logToJournal" ] [ "services" "displayManager" "logToJournal" ])
(lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "preStart" ] [ "services" "displayManager" "preStart" ])
];
config = lib.mkIf cfg.enable {
assertions = [
{ assertion = cfg.autoLogin.enable -> cfg.autoLogin.user != null;
message = ''
services.displayManager.autoLogin.enable requires services.displayManager.autoLogin.user to be set
'';
}
];
warnings =
lib.mkIf (dmDefault != null || wmDefault != null) [
''
The following options are deprecated:
${lib.concatStringsSep "\n " (map ({c, t}: t) (lib.filter ({c, t}: c != null) [
{ c = dmDefault; t = "- services.xserver.desktopManager.default"; }
{ c = wmDefault; t = "- services.xserver.windowManager.default"; }
]))}
Please use
services.displayManager.defaultSession = "${defaultSessionFromLegacyOptions}";
instead.
''
];
# Make xsessions and wayland sessions available in XDG_DATA_DIRS
# as some programs have behavior that depends on them being present
environment.sessionVariables.XDG_DATA_DIRS = lib.mkIf (cfg.sessionPackages != [ ]) [
"${cfg.sessionData.desktops}/share"
];
services.displayManager.sessionData = {
desktops = installedSessions;
sessionNames = lib.concatMap (p: p.providedSessions) config.services.displayManager.sessionPackages;
# We do not want to force users to set defaultSession when they have only single DE.
autologinSession =
if cfg.defaultSession != null then
cfg.defaultSession
else if cfg.sessionData.sessionNames != [] then
lib.head cfg.sessionData.sessionNames
else
null;
};
# so that the service won't be enabled when only startx is used
systemd.services.display-manager.enable =
let dmConf = config.services.xserver.displayManager;
noDmUsed = !(dmConf.gdm.enable
|| dmConf.sddm.enable
|| dmConf.xpra.enable
|| dmConf.lightdm.enable);
in lib.mkIf noDmUsed (lib.mkDefault false);
systemd.services.display-manager = {
description = "Display Manager";
after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ];
restartIfChanged = false;
environment = lib.optionalAttrs config.hardware.opengl.setLdLibraryPath {
LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.addOpenGLRunpath.driverLink ];
} // cfg.environment;
preStart = cfg.preStart;
script = lib.mkIf (config.systemd.services.display-manager.enable == true) cfg.execCmd;
# Stop restarting if the display manager stops (crashes) 2 times
# in one minute. Starting X typically takes 3-4s.
startLimitIntervalSec = 30;
startLimitBurst = 3;
serviceConfig = {
Restart = "always";
RestartSec = "200ms";
SyslogIdentifier = "display-manager";
};
};
};
}

View File

@ -14,7 +14,6 @@ with lib;
let let
cfg = config.services.xserver; cfg = config.services.xserver;
opt = options.services.xserver;
xorg = pkgs.xorg; xorg = pkgs.xorg;
fontconfig = config.fonts.fontconfig; fontconfig = config.fonts.fontconfig;
@ -130,41 +129,6 @@ let
exit 1 exit 1
fi fi
''; '';
installedSessions = pkgs.runCommand "desktops"
{ # trivial derivation
preferLocalBuild = true;
allowSubstitutes = false;
}
''
mkdir -p "$out/share/"{xsessions,wayland-sessions}
${concatMapStrings (pkg: ''
for n in ${concatStringsSep " " pkg.providedSessions}; do
if ! test -f ${pkg}/share/wayland-sessions/$n.desktop -o \
-f ${pkg}/share/xsessions/$n.desktop; then
echo "Couldn't find provided session name, $n.desktop, in session package ${pkg.name}:"
echo " ${pkg}"
return 1
fi
done
if test -d ${pkg}/share/xsessions; then
${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/xsessions $out/share/xsessions
fi
if test -d ${pkg}/share/wayland-sessions; then
${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/wayland-sessions $out/share/wayland-sessions
fi
'') cfg.displayManager.sessionPackages}
'';
dmDefault = cfg.desktopManager.default;
# fallback default for cases when only default wm is set
dmFallbackDefault = if dmDefault != null then dmDefault else "none";
wmDefault = cfg.windowManager.default;
defaultSessionFromLegacyOptions = dmFallbackDefault + optionalString (wmDefault != null && wmDefault != "none") "+${wmDefault}";
in in
{ {
@ -215,35 +179,6 @@ in
''; '';
}; };
hiddenUsers = mkOption {
type = types.listOf types.str;
default = [ "nobody" ];
description = lib.mdDoc ''
A list of users which will not be shown in the display manager.
'';
};
sessionPackages = mkOption {
type = with types; listOf (package // {
description = "package with provided sessions";
check = p: assertMsg
(package.check p && p ? providedSessions
&& p.providedSessions != [] && all isString p.providedSessions)
''
Package, '${p.name}', did not specify any session names, as strings, in
'passthru.providedSessions'. This is required when used as a session package.
The session names can be looked up in:
${p}/share/xsessions
${p}/share/wayland-sessions
'';
});
default = [];
description = lib.mdDoc ''
A list of packages containing x11 or wayland session files to be passed to the display manager.
'';
};
session = mkOption { session = mkOption {
default = []; default = [];
type = types.listOf types.attrs; type = types.listOf types.attrs;
@ -274,51 +209,6 @@ in
''; '';
}; };
sessionData = mkOption {
description = lib.mdDoc "Data exported for display managers convenience";
internal = true;
default = {};
apply = val: {
wrapper = xsessionWrapper;
desktops = installedSessions;
sessionNames = concatMap (p: p.providedSessions) cfg.displayManager.sessionPackages;
# We do not want to force users to set defaultSession when they have only single DE.
autologinSession =
if cfg.displayManager.defaultSession != null then
cfg.displayManager.defaultSession
else if cfg.displayManager.sessionData.sessionNames != [] then
head cfg.displayManager.sessionData.sessionNames
else
null;
};
};
defaultSession = mkOption {
type = with types; nullOr str // {
description = "session name";
check = d:
assertMsg (d != null -> (str.check d && elem d cfg.displayManager.sessionData.sessionNames)) ''
Default graphical session, '${d}', not found.
Valid names for 'services.xserver.displayManager.defaultSession' are:
${concatStringsSep "\n " cfg.displayManager.sessionData.sessionNames}
'';
};
default =
if dmDefault != null || wmDefault != null then
defaultSessionFromLegacyOptions
else
null;
defaultText = literalMD ''
Taken from display manager settings or window manager settings, if either is set.
'';
example = "gnome";
description = lib.mdDoc ''
Graphical session to pre-select in the session chooser (only effective for GDM, LightDM and SDDM).
On GDM, LightDM and SDDM, it will also be used as a session for auto-login.
'';
};
importedVariables = mkOption { importedVariables = mkOption {
type = types.listOf (types.strMatching "[a-zA-Z_][a-zA-Z0-9_]*"); type = types.listOf (types.strMatching "[a-zA-Z_][a-zA-Z0-9_]*");
visible = false; visible = false;
@ -327,106 +217,19 @@ in
''; '';
}; };
job = {
preStart = mkOption {
type = types.lines;
default = "";
example = "rm -f /var/log/my-display-manager.log";
description = lib.mdDoc "Script executed before the display manager is started.";
};
execCmd = mkOption {
type = types.str;
example = literalExpression ''"''${pkgs.lightdm}/bin/lightdm"'';
description = lib.mdDoc "Command to start the display manager.";
};
environment = mkOption {
type = types.attrsOf types.unspecified;
default = {};
description = lib.mdDoc "Additional environment variables needed by the display manager.";
};
logToFile = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Whether the display manager redirects the output of the
session script to {file}`~/.xsession-errors`.
'';
};
logToJournal = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc ''
Whether the display manager redirects the output of the
session script to the systemd journal.
'';
};
};
# Configuration for automatic login. Common for all DM.
autoLogin = mkOption {
type = types.submodule ({ config, options, ... }: {
options = {
enable = mkOption {
type = types.bool;
default = config.user != null;
defaultText = literalExpression "config.${options.user} != null";
description = lib.mdDoc ''
Automatically log in as {option}`autoLogin.user`.
'';
};
user = mkOption {
type = types.nullOr types.str;
default = null;
description = lib.mdDoc ''
User to be used for the automatic login.
'';
};
};
});
default = {};
description = lib.mdDoc ''
Auto login configuration attrset.
'';
};
}; };
}; };
config = { config = {
assertions = [ assertions = [
{ assertion = cfg.displayManager.autoLogin.enable -> cfg.displayManager.autoLogin.user != null;
message = ''
services.xserver.displayManager.autoLogin.enable requires services.xserver.displayManager.autoLogin.user to be set
'';
}
{ {
assertion = cfg.desktopManager.default != null || cfg.windowManager.default != null -> cfg.displayManager.defaultSession == defaultSessionFromLegacyOptions; assertion = cfg.desktopManager.default != null || cfg.windowManager.default != null -> cfg.displayManager.defaultSession == defaultSessionFromLegacyOptions;
message = "You cannot use both services.xserver.displayManager.defaultSession option and legacy options (services.xserver.desktopManager.default and services.xserver.windowManager.default)."; message = "You cannot use both services.xserver.displayManager.defaultSession option and legacy options (services.xserver.desktopManager.default and services.xserver.windowManager.default).";
} }
]; ];
warnings = services.displayManager.sessionData.wrapper = xsessionWrapper;
mkIf (dmDefault != null || wmDefault != null) [
''
The following options are deprecated:
${concatStringsSep "\n " (map ({c, t}: t) (filter ({c, t}: c != null) [
{ c = dmDefault; t = "- services.xserver.desktopManager.default"; }
{ c = wmDefault; t = "- services.xserver.windowManager.default"; }
]))}
Please use
services.xserver.displayManager.defaultSession = "${defaultSessionFromLegacyOptions}";
instead.
''
];
services.xserver.displayManager.xserverBin = "${xorg.xorgserver.out}/bin/X"; services.xserver.displayManager.xserverBin = "${xorg.xorgserver.out}/bin/X";
@ -449,7 +252,7 @@ in
# Create desktop files and scripts for starting sessions for WMs/DMs # Create desktop files and scripts for starting sessions for WMs/DMs
# that do not have upstream session files (those defined using services.{display,desktop,window}Manager.session options). # that do not have upstream session files (those defined using services.{display,desktop,window}Manager.session options).
services.xserver.displayManager.sessionPackages = services.displayManager.sessionPackages =
let let
dms = filter (s: s.manage == "desktop") cfg.displayManager.session; dms = filter (s: s.manage == "desktop") cfg.displayManager.session;
wms = filter (s: s.manage == "window") cfg.displayManager.session; wms = filter (s: s.manage == "window") cfg.displayManager.session;
@ -511,20 +314,14 @@ in
) )
(cartesianProductOfSets { dm = dms; wm = wms; }) (cartesianProductOfSets { dm = dms; wm = wms; })
); );
# Make xsessions and wayland sessions available in XDG_DATA_DIRS
# as some programs have behavior that depends on them being present
environment.sessionVariables.XDG_DATA_DIRS = lib.mkIf (cfg.displayManager.sessionPackages != [ ]) [
"${cfg.displayManager.sessionData.desktops}/share"
];
}; };
imports = [ imports = [
(mkRemovedOptionModule [ "services" "xserver" "displayManager" "desktopManagerHandlesLidAndPower" ] (mkRemovedOptionModule [ "services" "xserver" "displayManager" "desktopManagerHandlesLidAndPower" ]
"The option is no longer necessary because all display managers have already delegated lid management to systemd.") "The option is no longer necessary because all display managers have already delegated lid management to systemd.")
(mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logsXsession" ] [ "services" "xserver" "displayManager" "job" "logToFile" ]) (mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logsXsession" ] [ "services" "displayManager" "logToFile" ])
(mkRenamedOptionModule [ "services" "xserver" "displayManager" "logToJournal" ] [ "services" "xserver" "displayManager" "job" "logToJournal" ]) (mkRenamedOptionModule [ "services" "xserver" "displayManager" "logToJournal" ] [ "services" "displayManager" "logToJournal" ])
(mkRenamedOptionModule [ "services" "xserver" "displayManager" "extraSessionFilesPackages" ] [ "services" "xserver" "displayManager" "sessionPackages" ]) (mkRenamedOptionModule [ "services" "xserver" "displayManager" "extraSessionFilesPackages" ] [ "services" "displayManager" "sessionPackages" ])
]; ];
} }

View File

@ -129,17 +129,17 @@ in
{ {
imports = [ imports = [
(mkRemovedOptionModule (mkRemovedOptionModule
[ "services" "xserver" "displayManager" "sddm" "themes" ] [ "services" "displayManager" "sddm" "themes" ]
"Set the option `services.xserver.displayManager.sddm.package' instead.") "Set the option `services.displayManager.sddm.package' instead.")
(mkRenamedOptionModule (mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "autoLogin" "enable" ] [ "services" "displayManager" "sddm" "autoLogin" "enable" ]
[ "services" "xserver" "displayManager" "autoLogin" "enable" ]) [ "services" "displayManager" "autoLogin" "enable" ])
(mkRenamedOptionModule (mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "autoLogin" "user" ] [ "services" "displayManager" "sddm" "autoLogin" "user" ]
[ "services" "xserver" "displayManager" "autoLogin" "user" ]) [ "services" "displayManager" "autoLogin" "user" ])
(mkRemovedOptionModule (mkRemovedOptionModule
[ "services" "xserver" "displayManager" "sddm" "extraConfig" ] [ "services" "displayManager" "sddm" "extraConfig" ]
"Set the option `services.xserver.displayManager.sddm.settings' instead.") "Set the option `services.displayManager.sddm.settings' instead.")
]; ];
options = { options = {
@ -281,6 +281,8 @@ in
} }
]; ];
services.displayManager.execCmd = "exec /run/current-system/sw/bin/sddm";
security.pam.services = { security.pam.services = {
sddm.text = '' sddm.text = ''
auth substack login auth substack login

View File

@ -639,6 +639,7 @@ in
###### implementation ###### implementation
config = mkIf cfg.enable { config = mkIf cfg.enable {
services.displayManager.enable = true;
services.xserver.displayManager.lightdm.enable = services.xserver.displayManager.lightdm.enable =
let dmConf = cfg.displayManager; let dmConf = cfg.displayManager;
@ -650,15 +651,6 @@ in
|| config.services.greetd.enable); || config.services.greetd.enable);
in mkIf (default) (mkDefault true); in mkIf (default) (mkDefault true);
# so that the service won't be enabled when only startx is used
systemd.services.display-manager.enable =
let dmConf = cfg.displayManager;
noDmUsed = !(dmConf.gdm.enable
|| dmConf.sddm.enable
|| dmConf.xpra.enable
|| dmConf.lightdm.enable);
in mkIf (noDmUsed) (mkDefault false);
hardware.opengl.enable = mkDefault true; hardware.opengl.enable = mkDefault true;
services.xserver.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ]; services.xserver.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ];