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