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}