grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / display-managers / sddm.nix
blobca46d627101ad457ff1f77b7ebdf485f323fd22d
1 { config, lib, pkgs, ... }:
3 let
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;
13   });
15   iniFmt = pkgs.formats.ini { };
17   inherit (lib)
18     concatMapStrings concatStringsSep getExe
19     attrNames getAttr optionalAttrs optionalString
20     mkRemovedOptionModule mkRenamedOptionModule mkIf mkEnableOption mkOption mkPackageOption types
21     ;
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} "$@"
26   '';
28   Xsetup = pkgs.writeShellScript "Xsetup" ''
29     ${cfg.setupScript}
30     ${xcfg.displayManager.setupCommands}
31   '';
33   Xstop = pkgs.writeShellScript "Xstop" ''
34     ${cfg.stopScript}
35   '';
37   defaultConfig = {
38     General = {
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
50     };
52     Theme = {
53       Current = cfg.theme;
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";
58       CursorSize = 24;
59     };
61     Users = {
62       MaximumUid = config.ids.uids.nixbld;
63       HideUsers = concatStringsSep "," dmcfg.hiddenUsers;
64       HideShells = "/run/current-system/sw/bin/nologin";
65     };
67     Wayland = {
68       EnableHiDPI = cfg.enableHidpi;
69       SessionDir = "${dmcfg.sessionData.desktops}/share/wayland-sessions";
70       CompositorCommand = lib.optionalString cfg.wayland.enable cfg.wayland.compositorCommand;
71     };
73   } // optionalAttrs xcfg.enable {
74     X11 = {
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;
84     };
85   } // optionalAttrs dmcfg.autoLogin.enable {
86     Autologin = {
87       User = dmcfg.autoLogin.user;
88       Session = autoLoginSessionName;
89       Relogin = cfg.autoLogin.relogin;
90     };
91   };
93   cfgFile =
94     iniFmt.generate "sddm.conf" (lib.recursiveUpdate defaultConfig cfg.settings);
96   autoLoginSessionName =
97     "${dmcfg.sessionData.autologinSession}.desktop";
99   compositorCmds = {
100     kwin = concatStringsSep " " [
101       "${lib.getBin pkgs.kdePackages.kwin}/bin/kwin_wayland"
102       "--no-global-shortcuts"
103       "--no-kactivities"
104       "--no-lockscreen"
105       "--locale1"
106     ];
107     # This is basically the upstream default, but with Weston referenced by full path
108     # and the configuration generated from NixOS options.
109     weston =
110       let
111         westonIni = (pkgs.formats.ini { }).generate "weston.ini" {
112           libinput = {
113             enable-tap = config.services.libinput.mouse.tapping;
114             left-handed = config.services.libinput.mouse.leftHanded;
115           };
116           keyboard = {
117             keymap_model = xcfg.xkb.model;
118             keymap_layout = xcfg.xkb.layout;
119             keymap_variant = xcfg.xkb.variant;
120             keymap_options = xcfg.xkb.options;
121           };
122         };
123       in
124       "${getExe pkgs.weston} --shell=kiosk -c ${westonIni}";
125   };
129   imports = [
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.")
155   ];
157   options = {
159     services.displayManager.sddm = {
160       enable = mkOption {
161         type = types.bool;
162         default = false;
163         description = ''
164           Whether to enable sddm as the display manager.
165         '';
166       };
168       package = mkPackageOption pkgs [ "plasma5Packages" "sddm" ] { };
170       enableHidpi = mkOption {
171         type = types.bool;
172         default = true;
173         description = ''
174           Whether to enable automatic HiDPI mode.
175         '';
176       };
178       settings = mkOption {
179         type = iniFmt.type;
180         default = { };
181         example = {
182           Autologin = {
183             User = "john";
184             Session = "plasma.desktop";
185           };
186         };
187         description = ''
188           Extra settings merged in and overwriting defaults in sddm.conf.
189         '';
190       };
192       theme = mkOption {
193         type = types.str;
194         default = "";
195         description = ''
196           Greeter theme to use.
197         '';
198       };
200       extraPackages = mkOption {
201         type = types.listOf types.package;
202         default = [ ];
203         defaultText = "[]";
204         description = ''
205           Extra Qt plugins / QML libraries to add to the environment.
206         '';
207       };
209       autoNumlock = mkOption {
210         type = types.bool;
211         default = false;
212         description = ''
213           Enable numlock at login.
214         '';
215       };
217       setupScript = mkOption {
218         type = types.str;
219         default = "";
220         example = ''
221           # workaround for using NVIDIA Optimus without Bumblebee
222           xrandr --setprovideroutputsource modesetting NVIDIA-0
223           xrandr --auto
224         '';
225         description = ''
226           A script to execute when starting the display server. DEPRECATED, please
227           use {option}`services.xserver.displayManager.setupCommands`.
228         '';
229       };
231       stopScript = mkOption {
232         type = types.str;
233         default = "";
234         description = ''
235           A script to execute when stopping the display server.
236         '';
237       };
239       # Configuration for automatic login specific to SDDM
240       autoLogin = {
241         relogin = mkOption {
242           type = types.bool;
243           default = false;
244           description = ''
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.
247           '';
248         };
250         minimumUid = mkOption {
251           type = types.ints.u16;
252           default = 1000;
253           description = ''
254             Minimum user ID for auto-login user.
255           '';
256         };
257       };
259       # Experimental Wayland support
260       wayland = {
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);
266           default = "weston";
267         };
269         compositorCommand = mkOption {
270           type = types.str;
271           internal = true;
272           default = compositorCmds.${cfg.wayland.compositor};
273           description = "Command used to start the selected compositor";
274         };
275       };
276     };
277   };
279   config = mkIf cfg.enable {
281     assertions = [
282       {
283         assertion = xcfg.enable || cfg.wayland.enable;
284         message = ''
285           SDDM requires either services.xserver.enable or services.displayManager.sddm.wayland.enable to be true
286         '';
287       }
288       {
289         assertion = config.services.displayManager.autoLogin.enable -> autoLoginSessionName != null;
290         message = ''
291           SDDM auto-login requires that services.displayManager.defaultSession is set.
292         '';
293       }
294     ];
296     services.displayManager = {
297       enable = true;
298       execCmd = "exec /run/current-system/sw/bin/sddm";
299     };
301     security.pam.services = {
302       sddm.text = ''
303         auth      substack      login
304         account   include       login
305         password  substack      login
306         session   include       login
307       '';
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
323       '';
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
330         account  include   sddm
332         password include   sddm
334         session  include   sddm
335       '';
336     };
338     users.users.sddm = {
339       createHome = true;
340       home = "/var/lib/sddm";
341       group = "sddm";
342       uid = config.ids.uids.sddm;
343     };
345     environment = {
346       etc."sddm.conf".source = cfgFile;
347       pathsToLink = [
348         "/share/sddm"
349       ];
350       systemPackages = [ sddm ];
351     };
353     users.groups.sddm.gid = config.ids.gids.sddm;
355     services = {
356       dbus.packages = [ sddm ];
357       xserver = {
358         # To enable user switching, allow sddm to allocate TTYs/displays dynamically.
359         tty = null;
360         display = null;
361       };
362     };
364     systemd = {
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 = {
369         after = [
370           "systemd-user-sessions.service"
371           "getty@tty7.service"
372           "plymouth-quit.service"
373           "systemd-logind.service"
374         ];
375         conflicts = [
376           "getty@tty7.service"
377         ];
378       };
379     };
380   };