1{ config, lib, pkgs, ... }:
2
3with lib;
4let
5 xcfg = config.services.xserver;
6 dmcfg = xcfg.displayManager;
7 cfg = dmcfg.sddm;
8 xEnv = config.systemd.services.display-manager.environment;
9
10 sddm = pkgs.libsForQt5.sddm;
11
12 iniFmt = pkgs.formats.ini { };
13
14 xserverWrapper = pkgs.writeShellScript "xserver-wrapper" ''
15 ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)}
16 exec systemd-cat -t xserver-wrapper ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} "$@"
17 '';
18
19 Xsetup = pkgs.writeShellScript "Xsetup" ''
20 ${cfg.setupScript}
21 ${dmcfg.setupCommands}
22 '';
23
24 Xstop = pkgs.writeShellScript "Xstop" ''
25 ${cfg.stopScript}
26 '';
27
28 defaultConfig = {
29 General = {
30 HaltCommand = "/run/current-system/systemd/bin/systemctl poweroff";
31 RebootCommand = "/run/current-system/systemd/bin/systemctl reboot";
32 Numlock = if cfg.autoNumlock then "on" else "none"; # on, off none
33
34 # Implementation is done via pkgs/applications/display-managers/sddm/sddm-default-session.patch
35 DefaultSession = optionalString (dmcfg.defaultSession != null) "${dmcfg.defaultSession}.desktop";
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
44 Users = {
45 MaximumUid = config.ids.uids.nixbld;
46 HideUsers = concatStringsSep "," dmcfg.hiddenUsers;
47 HideShells = "/run/current-system/sw/bin/nologin";
48 };
49
50 X11 = {
51 MinimumVT = if xcfg.tty != null then xcfg.tty else 7;
52 ServerPath = toString xserverWrapper;
53 XephyrPath = "${pkgs.xorg.xorgserver.out}/bin/Xephyr";
54 SessionCommand = toString dmcfg.sessionData.wrapper;
55 SessionDir = "${dmcfg.sessionData.desktops}/share/xsessions";
56 XauthPath = "${pkgs.xorg.xauth}/bin/xauth";
57 DisplayCommand = toString Xsetup;
58 DisplayStopCommand = toString Xstop;
59 EnableHiDPI = cfg.enableHidpi;
60 };
61
62 Wayland = {
63 EnableHiDPI = cfg.enableHidpi;
64 SessionDir = "${dmcfg.sessionData.desktops}/share/wayland-sessions";
65 };
66 } // lib.optionalAttrs dmcfg.autoLogin.enable {
67 Autologin = {
68 User = dmcfg.autoLogin.user;
69 Session = autoLoginSessionName;
70 Relogin = cfg.autoLogin.relogin;
71 };
72 };
73
74 cfgFile =
75 iniFmt.generate "sddm.conf" (lib.recursiveUpdate defaultConfig cfg.settings);
76
77 autoLoginSessionName =
78 "${dmcfg.sessionData.autologinSession}.desktop";
79
80in
81{
82 imports = [
83 (mkRemovedOptionModule
84 [ "services" "xserver" "displayManager" "sddm" "themes" ]
85 "Set the option `services.xserver.displayManager.sddm.package' instead.")
86 (mkRenamedOptionModule
87 [ "services" "xserver" "displayManager" "sddm" "autoLogin" "enable" ]
88 [ "services" "xserver" "displayManager" "autoLogin" "enable" ])
89 (mkRenamedOptionModule
90 [ "services" "xserver" "displayManager" "sddm" "autoLogin" "user" ]
91 [ "services" "xserver" "displayManager" "autoLogin" "user" ])
92 (mkRemovedOptionModule
93 [ "services" "xserver" "displayManager" "sddm" "extraConfig" ]
94 "Set the option `services.xserver.displayManager.sddm.settings' instead.")
95 ];
96
97 options = {
98
99 services.xserver.displayManager.sddm = {
100 enable = mkOption {
101 type = types.bool;
102 default = false;
103 description = lib.mdDoc ''
104 Whether to enable sddm as the display manager.
105 '';
106 };
107
108 enableHidpi = mkOption {
109 type = types.bool;
110 default = true;
111 description = lib.mdDoc ''
112 Whether to enable automatic HiDPI mode.
113 '';
114 };
115
116 settings = mkOption {
117 type = iniFmt.type;
118 default = { };
119 example = {
120 Autologin = {
121 User = "john";
122 Session = "plasma.desktop";
123 };
124 };
125 description = lib.mdDoc ''
126 Extra settings merged in and overwriting defaults in sddm.conf.
127 '';
128 };
129
130 theme = mkOption {
131 type = types.str;
132 default = "";
133 description = lib.mdDoc ''
134 Greeter theme to use.
135 '';
136 };
137
138 autoNumlock = mkOption {
139 type = types.bool;
140 default = false;
141 description = lib.mdDoc ''
142 Enable numlock at login.
143 '';
144 };
145
146 setupScript = mkOption {
147 type = types.str;
148 default = "";
149 example = ''
150 # workaround for using NVIDIA Optimus without Bumblebee
151 xrandr --setprovideroutputsource modesetting NVIDIA-0
152 xrandr --auto
153 '';
154 description = lib.mdDoc ''
155 A script to execute when starting the display server. DEPRECATED, please
156 use {option}`services.xserver.displayManager.setupCommands`.
157 '';
158 };
159
160 stopScript = mkOption {
161 type = types.str;
162 default = "";
163 description = lib.mdDoc ''
164 A script to execute when stopping the display server.
165 '';
166 };
167
168 # Configuration for automatic login specific to SDDM
169 autoLogin = {
170 relogin = mkOption {
171 type = types.bool;
172 default = false;
173 description = lib.mdDoc ''
174 If true automatic login will kick in again on session exit (logout), otherwise it
175 will only log in automatically when the display-manager is started.
176 '';
177 };
178
179 minimumUid = mkOption {
180 type = types.ints.u16;
181 default = 1000;
182 description = lib.mdDoc ''
183 Minimum user ID for auto-login user.
184 '';
185 };
186 };
187 };
188 };
189
190 config = mkIf cfg.enable {
191
192 assertions = [
193 {
194 assertion = xcfg.enable;
195 message = ''
196 SDDM requires services.xserver.enable to be true
197 '';
198 }
199 {
200 assertion = dmcfg.autoLogin.enable -> autoLoginSessionName != null;
201 message = ''
202 SDDM auto-login requires that services.xserver.displayManager.defaultSession is set.
203 '';
204 }
205 ];
206
207 services.xserver.displayManager.job = {
208 environment = {
209 # Load themes from system environment
210 QT_PLUGIN_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtPluginPrefix;
211 QML2_IMPORT_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtQmlPrefix;
212 };
213
214 execCmd = "exec /run/current-system/sw/bin/sddm";
215 };
216
217 security.pam.services = {
218 sddm.text = ''
219 auth substack login
220 account include login
221 password substack login
222 session include login
223 '';
224
225 sddm-greeter.text = ''
226 auth required pam_succeed_if.so audit quiet_success user = sddm
227 auth optional pam_permit.so
228
229 account required pam_succeed_if.so audit quiet_success user = sddm
230 account sufficient pam_unix.so
231
232 password required pam_deny.so
233
234 session required pam_succeed_if.so audit quiet_success user = sddm
235 session required pam_env.so conffile=/etc/pam/environment readenv=0
236 session optional ${config.systemd.package}/lib/security/pam_systemd.so
237 session optional pam_keyinit.so force revoke
238 session optional pam_permit.so
239 '';
240
241 sddm-autologin.text = ''
242 auth requisite pam_nologin.so
243 auth required pam_succeed_if.so uid >= ${toString cfg.autoLogin.minimumUid} quiet
244 auth required pam_permit.so
245
246 account include sddm
247
248 password include sddm
249
250 session include sddm
251 '';
252 };
253
254 users.users.sddm = {
255 createHome = true;
256 home = "/var/lib/sddm";
257 group = "sddm";
258 uid = config.ids.uids.sddm;
259 };
260
261 environment.etc."sddm.conf".source = cfgFile;
262 environment.pathsToLink = [
263 "/share/sddm"
264 ];
265
266 users.groups.sddm.gid = config.ids.gids.sddm;
267
268 environment.systemPackages = [ sddm ];
269 services.dbus.packages = [ sddm ];
270
271 # To enable user switching, allow sddm to allocate TTYs/displays dynamically.
272 services.xserver.tty = null;
273 services.xserver.display = null;
274 };
275}