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}