1 { config, lib, pkgs, ... }:
4 xcfg = config.services.xserver;
5 dmcfg = config.services.displayManager;
6 cfg = config.services.displayManager.sddm;
7 xEnv = config.systemd.services.display-manager.environment;
9 sddm = cfg.package.override (old: {
10 withWayland = cfg.wayland.enable;
11 withLayerShellQt = cfg.wayland.compositor == "kwin";
12 extraPackages = old.extraPackages or [ ] ++ cfg.extraPackages;
15 iniFmt = pkgs.formats.ini { };
18 concatMapStrings concatStringsSep getExe
19 attrNames getAttr optionalAttrs optionalString
20 mkRemovedOptionModule mkRenamedOptionModule mkIf mkEnableOption mkOption mkPackageOption types
23 xserverWrapper = pkgs.writeShellScript "xserver-wrapper" ''
24 ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)}
25 exec systemd-cat -t xserver-wrapper ${xcfg.displayManager.xserverBin} ${toString xcfg.displayManager.xserverArgs} "$@"
28 Xsetup = pkgs.writeShellScript "Xsetup" ''
30 ${xcfg.displayManager.setupCommands}
33 Xstop = pkgs.writeShellScript "Xstop" ''
39 HaltCommand = "/run/current-system/systemd/bin/systemctl poweroff";
40 RebootCommand = "/run/current-system/systemd/bin/systemctl reboot";
41 Numlock = if cfg.autoNumlock then "on" else "none"; # on, off none
43 # Implementation is done via pkgs/applications/display-managers/sddm/sddm-default-session.patch
44 DefaultSession = optionalString (config.services.displayManager.defaultSession != null) "${config.services.displayManager.defaultSession}.desktop";
46 DisplayServer = if cfg.wayland.enable then "wayland" else "x11";
47 } // optionalAttrs (cfg.wayland.enable && cfg.wayland.compositor == "kwin") {
48 GreeterEnvironment = "QT_WAYLAND_SHELL_INTEGRATION=layer-shell";
49 InputMethod = ""; # needed if we are using --inputmethod with kwin
54 ThemeDir = "/run/current-system/sw/share/sddm/themes";
55 FacesDir = "/run/current-system/sw/share/sddm/faces";
56 } // optionalAttrs (cfg.theme == "breeze") {
57 CursorTheme = "breeze_cursors";
62 MaximumUid = config.ids.uids.nixbld;
63 HideUsers = concatStringsSep "," dmcfg.hiddenUsers;
64 HideShells = "/run/current-system/sw/bin/nologin";
68 EnableHiDPI = cfg.enableHidpi;
69 SessionDir = "${dmcfg.sessionData.desktops}/share/wayland-sessions";
70 CompositorCommand = lib.optionalString cfg.wayland.enable cfg.wayland.compositorCommand;
73 } // optionalAttrs xcfg.enable {
75 MinimumVT = if xcfg.tty != null then xcfg.tty else 7;
76 ServerPath = toString xserverWrapper;
77 XephyrPath = "${pkgs.xorg.xorgserver.out}/bin/Xephyr";
78 SessionCommand = toString dmcfg.sessionData.wrapper;
79 SessionDir = "${dmcfg.sessionData.desktops}/share/xsessions";
80 XauthPath = "${pkgs.xorg.xauth}/bin/xauth";
81 DisplayCommand = toString Xsetup;
82 DisplayStopCommand = toString Xstop;
83 EnableHiDPI = cfg.enableHidpi;
85 } // optionalAttrs dmcfg.autoLogin.enable {
87 User = dmcfg.autoLogin.user;
88 Session = autoLoginSessionName;
89 Relogin = cfg.autoLogin.relogin;
94 iniFmt.generate "sddm.conf" (lib.recursiveUpdate defaultConfig cfg.settings);
96 autoLoginSessionName =
97 "${dmcfg.sessionData.autologinSession}.desktop";
100 kwin = concatStringsSep " " [
101 "${lib.getBin pkgs.kdePackages.kwin}/bin/kwin_wayland"
102 "--no-global-shortcuts"
107 # This is basically the upstream default, but with Weston referenced by full path
108 # and the configuration generated from NixOS options.
111 westonIni = (pkgs.formats.ini { }).generate "weston.ini" {
113 enable-tap = config.services.libinput.mouse.tapping;
114 left-handed = config.services.libinput.mouse.leftHanded;
117 keymap_model = xcfg.xkb.model;
118 keymap_layout = xcfg.xkb.layout;
119 keymap_variant = xcfg.xkb.variant;
120 keymap_options = xcfg.xkb.options;
124 "${getExe pkgs.weston} --shell=kiosk -c ${westonIni}";
130 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "autoLogin" "minimumUid" ] [ "services" "displayManager" "sddm" "autoLogin" "minimumUid" ])
131 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "autoLogin" "relogin" ] [ "services" "displayManager" "sddm" "autoLogin" "relogin" ])
132 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "autoNumlock" ] [ "services" "displayManager" "sddm" "autoNumlock" ])
133 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "enable" ] [ "services" "displayManager" "sddm" "enable" ])
134 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "enableHidpi" ] [ "services" "displayManager" "sddm" "enableHidpi" ])
135 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "extraPackages" ] [ "services" "displayManager" "sddm" "extraPackages" ])
136 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "package" ] [ "services" "displayManager" "sddm" "package" ])
137 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "settings" ] [ "services" "displayManager" "sddm" "settings" ])
138 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "setupScript" ] [ "services" "displayManager" "sddm" "setupScript" ])
139 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "stopScript" ] [ "services" "displayManager" "sddm" "stopScript" ])
140 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "theme" ] [ "services" "displayManager" "sddm" "theme" ])
141 (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "wayland" "enable" ] [ "services" "displayManager" "sddm" "wayland" "enable" ])
143 (mkRemovedOptionModule
144 [ "services" "displayManager" "sddm" "themes" ]
145 "Set the option `services.displayManager.sddm.package' instead.")
146 (mkRenamedOptionModule
147 [ "services" "displayManager" "sddm" "autoLogin" "enable" ]
148 [ "services" "displayManager" "autoLogin" "enable" ])
149 (mkRenamedOptionModule
150 [ "services" "displayManager" "sddm" "autoLogin" "user" ]
151 [ "services" "displayManager" "autoLogin" "user" ])
152 (mkRemovedOptionModule
153 [ "services" "displayManager" "sddm" "extraConfig" ]
154 "Set the option `services.displayManager.sddm.settings' instead.")
159 services.displayManager.sddm = {
164 Whether to enable sddm as the display manager.
168 package = mkPackageOption pkgs [ "plasma5Packages" "sddm" ] { };
170 enableHidpi = mkOption {
174 Whether to enable automatic HiDPI mode.
178 settings = mkOption {
184 Session = "plasma.desktop";
188 Extra settings merged in and overwriting defaults in sddm.conf.
196 Greeter theme to use.
200 extraPackages = mkOption {
201 type = types.listOf types.package;
205 Extra Qt plugins / QML libraries to add to the environment.
209 autoNumlock = mkOption {
213 Enable numlock at login.
217 setupScript = mkOption {
221 # workaround for using NVIDIA Optimus without Bumblebee
222 xrandr --setprovideroutputsource modesetting NVIDIA-0
226 A script to execute when starting the display server. DEPRECATED, please
227 use {option}`services.xserver.displayManager.setupCommands`.
231 stopScript = mkOption {
235 A script to execute when stopping the display server.
239 # Configuration for automatic login specific to SDDM
245 If true automatic login will kick in again on session exit (logout), otherwise it
246 will only log in automatically when the display-manager is started.
250 minimumUid = mkOption {
251 type = types.ints.u16;
254 Minimum user ID for auto-login user.
259 # Experimental Wayland support
261 enable = mkEnableOption "experimental Wayland support";
263 compositor = mkOption {
264 description = "The compositor to use: ${lib.concatStringsSep ", " (builtins.attrNames compositorCmds)}";
265 type = types.enum (builtins.attrNames compositorCmds);
269 compositorCommand = mkOption {
272 default = compositorCmds.${cfg.wayland.compositor};
273 description = "Command used to start the selected compositor";
279 config = mkIf cfg.enable {
283 assertion = xcfg.enable || cfg.wayland.enable;
285 SDDM requires either services.xserver.enable or services.displayManager.sddm.wayland.enable to be true
289 assertion = config.services.displayManager.autoLogin.enable -> autoLoginSessionName != null;
291 SDDM auto-login requires that services.displayManager.defaultSession is set.
296 services.displayManager = {
298 execCmd = "exec /run/current-system/sw/bin/sddm";
301 security.pam.services = {
304 account include login
305 password substack login
306 session include login
309 sddm-greeter.text = ''
310 auth required pam_succeed_if.so audit quiet_success user = sddm
311 auth optional pam_permit.so
313 account required pam_succeed_if.so audit quiet_success user = sddm
314 account sufficient pam_unix.so
316 password required pam_deny.so
318 session required pam_succeed_if.so audit quiet_success user = sddm
319 session required pam_env.so conffile=/etc/pam/environment readenv=0
320 session optional ${config.systemd.package}/lib/security/pam_systemd.so
321 session optional pam_keyinit.so force revoke
322 session optional pam_permit.so
325 sddm-autologin.text = ''
326 auth requisite pam_nologin.so
327 auth required pam_succeed_if.so uid >= ${toString cfg.autoLogin.minimumUid} quiet
328 auth required pam_permit.so
332 password include sddm
340 home = "/var/lib/sddm";
342 uid = config.ids.uids.sddm;
346 etc."sddm.conf".source = cfgFile;
350 systemPackages = [ sddm ];
353 users.groups.sddm.gid = config.ids.gids.sddm;
356 dbus.packages = [ sddm ];
358 # To enable user switching, allow sddm to allocate TTYs/displays dynamically.
365 tmpfiles.packages = [ sddm ];
367 # We're not using the upstream unit, so copy these: https://github.com/sddm/sddm/blob/develop/services/sddm.service.in
368 services.display-manager = {
370 "systemd-user-sessions.service"
372 "plymouth-quit.service"
373 "systemd-logind.service"