1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 xcfg = config.services.xserver;
8 dmcfg = xcfg.displayManager;
9 cfg = dmcfg.sddm;
10 xEnv = config.systemd.services."display-manager".environment;
11
12 inherit (pkgs) sddm;
13
14 xserverWrapper = pkgs.writeScript "xserver-wrapper" ''
15 #!/bin/sh
16 ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)}
17 exec systemd-cat ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} "$@"
18 '';
19
20 Xsetup = pkgs.writeScript "Xsetup" ''
21 #!/bin/sh
22 ${cfg.setupScript}
23 '';
24
25 Xstop = pkgs.writeScript "Xstop" ''
26 #!/bin/sh
27 ${cfg.stopScript}
28 '';
29
30 cfgFile = pkgs.writeText "sddm.conf" ''
31 [General]
32 HaltCommand=${pkgs.systemd}/bin/systemctl poweroff
33 RebootCommand=${pkgs.systemd}/bin/systemctl reboot
34 ${optionalString cfg.autoNumlock ''
35 Numlock=on
36 ''}
37
38 [Theme]
39 Current=${cfg.theme}
40 ThemeDir=/run/current-system/sw/share/sddm/themes
41 FacesDir=/run/current-system/sw/share/sddm/faces
42
43 [Users]
44 MaximumUid=${toString config.ids.uids.nixbld}
45 HideUsers=${concatStringsSep "," dmcfg.hiddenUsers}
46 HideShells=/run/current-system/sw/bin/nologin
47
48 [X11]
49 MinimumVT=${toString (if xcfg.tty != null then xcfg.tty else 7)}
50 ServerPath=${xserverWrapper}
51 XephyrPath=${pkgs.xorg.xorgserver.out}/bin/Xephyr
52 SessionCommand=${dmcfg.session.wrapper}
53 SessionDir=${dmcfg.session.desktops}/share/xsessions
54 XauthPath=${pkgs.xorg.xauth}/bin/xauth
55 DisplayCommand=${Xsetup}
56 DisplayStopCommand=${Xstop}
57 EnableHidpi=${if cfg.enableHidpi then "true" else "false"}
58
59 [Wayland]
60 EnableHidpi=${if cfg.enableHidpi then "true" else "false"}
61
62 ${optionalString cfg.autoLogin.enable ''
63 [Autologin]
64 User=${cfg.autoLogin.user}
65 Session=${defaultSessionName}.desktop
66 Relogin=${boolToString cfg.autoLogin.relogin}
67 ''}
68
69 ${cfg.extraConfig}
70 '';
71
72 defaultSessionName =
73 let
74 dm = xcfg.desktopManager.default;
75 wm = xcfg.windowManager.default;
76 in dm + optionalString (wm != "none") ("+" + wm);
77
78in
79{
80 options = {
81
82 services.xserver.displayManager.sddm = {
83 enable = mkOption {
84 type = types.bool;
85 default = false;
86 description = ''
87 Whether to enable sddm as the display manager.
88 '';
89 };
90
91 enableHidpi = mkOption {
92 type = types.bool;
93 default = true;
94 description = ''
95 Whether to enable automatic HiDPI mode.
96 </para>
97 <para>
98 Versions up to 0.17 are broken so this only works from 0.18 onwards.
99 '';
100 };
101
102 extraConfig = mkOption {
103 type = types.lines;
104 default = "";
105 example = ''
106 [Autologin]
107 User=john
108 Session=plasma.desktop
109 '';
110 description = ''
111 Extra lines appended to the configuration of SDDM.
112 '';
113 };
114
115 theme = mkOption {
116 type = types.str;
117 default = "";
118 description = ''
119 Greeter theme to use.
120 '';
121 };
122
123 autoNumlock = mkOption {
124 type = types.bool;
125 default = false;
126 description = ''
127 Enable numlock at login.
128 '';
129 };
130
131 setupScript = mkOption {
132 type = types.str;
133 default = "";
134 example = ''
135 # workaround for using NVIDIA Optimus without Bumblebee
136 xrandr --setprovideroutputsource modesetting NVIDIA-0
137 xrandr --auto
138 '';
139 description = ''
140 A script to execute when starting the display server.
141 '';
142 };
143
144 stopScript = mkOption {
145 type = types.str;
146 default = "";
147 description = ''
148 A script to execute when stopping the display server.
149 '';
150 };
151
152 autoLogin = mkOption {
153 default = {};
154 description = ''
155 Configuration for automatic login.
156 '';
157
158 type = types.submodule {
159 options = {
160 enable = mkOption {
161 type = types.bool;
162 default = false;
163 description = ''
164 Automatically log in as <option>autoLogin.user</option>.
165 '';
166 };
167
168 user = mkOption {
169 type = types.nullOr types.str;
170 default = null;
171 description = ''
172 User to be used for the automatic login.
173 '';
174 };
175
176 relogin = mkOption {
177 type = types.bool;
178 default = false;
179 description = ''
180 If true automatic login will kick in again on session exit (logout), otherwise it
181 will only log in automatically when the display-manager is started.
182 '';
183 };
184 };
185 };
186 };
187
188 };
189
190 };
191
192 config = mkIf cfg.enable {
193
194 assertions = [
195 { assertion = cfg.autoLogin.enable -> cfg.autoLogin.user != null;
196 message = ''
197 SDDM auto-login requires services.xserver.displayManager.sddm.autoLogin.user to be set
198 '';
199 }
200 { assertion = cfg.autoLogin.enable -> elem defaultSessionName dmcfg.session.names;
201 message = ''
202 SDDM auto-login requires that services.xserver.desktopManager.default and
203 services.xserver.windowMananger.default are set to valid values. The current
204 default session: ${defaultSessionName} is not valid.
205 '';
206 }
207 ];
208
209 services.xserver.displayManager.job = {
210 logToFile = true;
211
212 environment = {
213 # Load themes from system environment
214 QT_PLUGIN_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtPluginPrefix;
215 QML2_IMPORT_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtQmlPrefix;
216
217 XDG_DATA_DIRS = "/run/current-system/sw/share";
218 };
219
220 execCmd = "exec /run/current-system/sw/bin/sddm";
221 };
222
223 security.pam.services = {
224 sddm = {
225 allowNullPassword = true;
226 startSession = true;
227 };
228
229 sddm-greeter.text = ''
230 auth required pam_succeed_if.so audit quiet_success user = sddm
231 auth optional pam_permit.so
232
233 account required pam_succeed_if.so audit quiet_success user = sddm
234 account sufficient pam_unix.so
235
236 password required pam_deny.so
237
238 session required pam_succeed_if.so audit quiet_success user = sddm
239 session required pam_env.so envfile=${config.system.build.pamEnvironment}
240 session optional ${pkgs.systemd}/lib/security/pam_systemd.so
241 session optional pam_keyinit.so force revoke
242 session optional pam_permit.so
243 '';
244
245 sddm-autologin.text = ''
246 auth requisite pam_nologin.so
247 auth required pam_succeed_if.so uid >= 1000 quiet
248 auth required pam_permit.so
249
250 account include sddm
251
252 password include sddm
253
254 session include sddm
255 '';
256 };
257
258 users.users.sddm = {
259 createHome = true;
260 home = "/var/lib/sddm";
261 group = "sddm";
262 uid = config.ids.uids.sddm;
263 };
264
265 environment.etc."sddm.conf".source = cfgFile;
266 environment.pathsToLink = [ "/share/sddm/themes" ];
267
268 users.groups.sddm.gid = config.ids.gids.sddm;
269
270 environment.systemPackages = [ sddm ];
271 services.dbus.packages = [ sddm ];
272
273 # To enable user switching, allow sddm to allocate TTYs/displays dynamically.
274 services.xserver.tty = null;
275 services.xserver.display = null;
276
277 systemd.tmpfiles.rules = [
278 # Prior to Qt 5.9.2, there is a QML cache invalidation bug which sometimes
279 # strikes new Plasma 5 releases. If the QML cache is not invalidated, SDDM
280 # will segfault without explanation. We really tore our hair out for awhile
281 # before finding the bug:
282 # https://bugreports.qt.io/browse/QTBUG-62302
283 # We work around the problem by deleting the QML cache before startup.
284 # This was supposedly fixed in Qt 5.9.2 however it has been reported with
285 # 5.10 and 5.11 as well. The initial workaround was to delete the directory
286 # in the Xsetup script but that doesn't do anything.
287 # Instead we use tmpfiles.d to ensure it gets wiped.
288 # This causes a small but perceptible delay when SDDM starts.
289 "e ${config.users.users.sddm.home}/.cache - - - 0"
290 ];
291 };
292}