1# This module declares the options to define a *display manager*, the 2# program responsible for handling X logins (such as xdm, kdm, gdb, or 3# SLiM). The display manager allows the user to select a *session 4# type*. When the user logs in, the display manager starts the 5# *session script* ("xsession" below) to launch the selected session 6# type. The session type defines two things: the *desktop manager* 7# (e.g., KDE, Gnome or a plain xterm), and optionally the *window 8# manager* (e.g. kwin or twm). 9 10{ config, lib, pkgs, ... }: 11 12with lib; 13 14let 15 16 cfg = config.services.xserver; 17 xorg = pkgs.xorg; 18 19 vaapiDrivers = pkgs.buildEnv { 20 name = "vaapi-drivers"; 21 paths = cfg.vaapiDrivers; 22 # We only want /lib/dri, but with a single input path, we need "/" for it to work 23 pathsToLink = [ "/" ]; 24 }; 25 26 fontconfig = config.fonts.fontconfig; 27 xresourcesXft = pkgs.writeText "Xresources-Xft" '' 28 ${optionalString (fontconfig.dpi != 0) ''Xft.dpi: ${toString fontconfig.dpi}''} 29 Xft.antialias: ${if fontconfig.antialias then "1" else "0"} 30 Xft.rgba: ${fontconfig.subpixel.rgba} 31 Xft.lcdfilter: lcd${fontconfig.subpixel.lcdfilter} 32 Xft.hinting: ${if fontconfig.hinting.enable then "1" else "0"} 33 Xft.autohint: ${if fontconfig.hinting.autohint then "1" else "0"} 34 Xft.hintstyle: hint${fontconfig.hinting.style} 35 ''; 36 37 # file provided by services.xserver.displayManager.session.script 38 xsession = wm: dm: pkgs.writeScript "xsession" 39 '' 40 #! /bin/sh 41 42 . /etc/profile 43 cd "$HOME" 44 45 # The first argument of this script is the session type. 46 sessionType="$1" 47 if [ "$sessionType" = default ]; then sessionType=""; fi 48 49 ${optionalString (!cfg.displayManager.job.logsXsession) '' 50 exec > ~/.xsession-errors 2>&1 51 ''} 52 53 ${optionalString cfg.startDbusSession '' 54 if test -z "$DBUS_SESSION_BUS_ADDRESS"; then 55 exec ${pkgs.dbus.tools}/bin/dbus-launch --exit-with-session "$0" "$sessionType" 56 fi 57 ''} 58 59 ${optionalString cfg.displayManager.desktopManagerHandlesLidAndPower '' 60 # Stop systemd from handling the power button and lid switch, 61 # since presumably the desktop environment will handle these. 62 if [ -z "$_INHIBITION_LOCK_TAKEN" ]; then 63 export _INHIBITION_LOCK_TAKEN=1 64 if ! ${config.systemd.package}/bin/loginctl show-session $XDG_SESSION_ID | grep -q '^RemoteHost='; then 65 exec ${config.systemd.package}/bin/systemd-inhibit --what=handle-lid-switch:handle-power-key --why="See NixOS configuration option 'services.xserver.displayManager.desktopManagerHandlesLidAndPower' for more information." "$0" "$sessionType" 66 fi 67 fi 68 69 ''} 70 71 ${optionalString cfg.startGnuPGAgent '' 72 if test -z "$SSH_AUTH_SOCK"; then 73 # Restart this script as a child of the GnuPG agent. 74 exec "${pkgs.gnupg}/bin/gpg-agent" \ 75 --enable-ssh-support --daemon \ 76 --pinentry-program "${pkgs.pinentry}/bin/pinentry-gtk-2" \ 77 --write-env-file "$HOME/.gpg-agent-info" \ 78 "$0" "$sessionType" 79 fi 80 ''} 81 82 # Handle being called by kdm. 83 if test "''${1:0:1}" = /; then eval exec "$1"; fi 84 85 # Start PulseAudio if enabled. 86 ${optionalString (config.hardware.pulseaudio.enable) '' 87 ${optionalString (!config.hardware.pulseaudio.systemWide) 88 "${config.hardware.pulseaudio.package}/bin/pulseaudio --start" 89 } 90 91 # Publish access credentials in the root window. 92 ${config.hardware.pulseaudio.package}/bin/pactl load-module module-x11-publish "display=$DISPLAY" 93 94 # Keep track of devices. Mostly useful for Phonon/KDE. 95 ${config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1" 96 ''} 97 98 # Tell systemd about our $DISPLAY. This is needed by the 99 # ssh-agent unit. 100 ${config.systemd.package}/bin/systemctl --user import-environment DISPLAY 101 102 # Load X defaults. 103 ${xorg.xrdb}/bin/xrdb -merge ${xresourcesXft} 104 if test -e ~/.Xresources; then 105 ${xorg.xrdb}/bin/xrdb -merge ~/.Xresources 106 elif test -e ~/.Xdefaults; then 107 ${xorg.xrdb}/bin/xrdb -merge ~/.Xdefaults 108 fi 109 110 export LIBVA_DRIVERS_PATH=${vaapiDrivers}/lib/dri 111 112 # Speed up application start by 50-150ms according to 113 # http://kdemonkey.blogspot.nl/2008/04/magic-trick.html 114 rm -rf $HOME/.compose-cache 115 mkdir $HOME/.compose-cache 116 117 ${cfg.displayManager.sessionCommands} 118 119 # Allow the user to execute commands at the beginning of the X session. 120 if test -f ~/.xprofile; then 121 source ~/.xprofile 122 fi 123 124 # Allow the user to setup a custom session type. 125 if test -x ~/.xsession; then 126 exec ~/.xsession 127 else 128 if test "$sessionType" = "custom"; then 129 sessionType="" # fall-thru if there is no ~/.xsession 130 fi 131 fi 132 133 # The session type is "<desktop-manager> + <window-manager>", so 134 # extract those. 135 windowManager="''${sessionType##* + }" 136 : ''${windowManager:=${cfg.windowManager.default}} 137 desktopManager="''${sessionType% + *}" 138 : ''${desktopManager:=${cfg.desktopManager.default}} 139 140 # Start the window manager. 141 case $windowManager in 142 ${concatMapStrings (s: '' 143 (${s.name}) 144 ${s.start} 145 ;; 146 '') wm} 147 (*) echo "$0: Window manager '$windowManager' not found.";; 148 esac 149 150 # Start the desktop manager. 151 case $desktopManager in 152 ${concatMapStrings (s: '' 153 (${s.name}) 154 ${s.start} 155 ;; 156 '') dm} 157 (*) echo "$0: Desktop manager '$desktopManager' not found.";; 158 esac 159 160 test -n "$waitPID" && wait "$waitPID" 161 exit 0 162 ''; 163 164 mkDesktops = names: pkgs.runCommand "desktops" {} 165 '' 166 mkdir -p $out 167 ${concatMapStrings (n: '' 168 cat - > "$out/${n}.desktop" << EODESKTOP 169 [Desktop Entry] 170 Version=1.0 171 Type=XSession 172 TryExec=${cfg.displayManager.session.script} 173 Exec=${cfg.displayManager.session.script} '${n}' 174 X-GDM-BypassXsession=true 175 Name=${n} 176 Comment= 177 EODESKTOP 178 '') names} 179 ''; 180 181in 182 183{ 184 185 options = { 186 187 services.xserver.displayManager = { 188 189 xauthBin = mkOption { 190 internal = true; 191 default = "${xorg.xauth}/bin/xauth"; 192 description = "Path to the <command>xauth</command> program used by display managers."; 193 }; 194 195 xserverBin = mkOption { 196 type = types.path; 197 description = "Path to the X server used by display managers."; 198 }; 199 200 xserverArgs = mkOption { 201 type = types.listOf types.str; 202 default = []; 203 example = [ "-ac" "-logverbose" "-verbose" "-nolisten tcp" ]; 204 description = "List of arguments for the X server."; 205 apply = toString; 206 }; 207 208 sessionCommands = mkOption { 209 type = types.lines; 210 default = ""; 211 example = 212 '' 213 xmessage "Hello World!" & 214 ''; 215 description = "Shell commands executed just before the window or desktop manager is started."; 216 }; 217 218 hiddenUsers = mkOption { 219 type = types.listOf types.str; 220 default = [ "nobody" ]; 221 description = '' 222 A list of users which will not be shown in the display manager. 223 ''; 224 }; 225 226 desktopManagerHandlesLidAndPower = mkOption { 227 type = types.bool; 228 default = true; 229 description = '' 230 Whether the display manager should prevent systemd from handling 231 lid and power events. This is normally handled by the desktop 232 environment's power manager. Turn this off when using a minimal 233 X11 setup without a full power manager. 234 ''; 235 }; 236 237 session = mkOption { 238 default = []; 239 example = literalExample 240 '' 241 [ { manage = "desktop"; 242 name = "xterm"; 243 start = ''' 244 ''${pkgs.xterm}/bin/xterm -ls & 245 waitPID=$! 246 '''; 247 } 248 ] 249 ''; 250 description = '' 251 List of sessions supported with the command used to start each 252 session. Each session script can set the 253 <varname>waitPID</varname> shell variable to make this script 254 wait until the end of the user session. Each script is used 255 to define either a windows manager or a desktop manager. These 256 can be differentiated by setting the attribute 257 <varname>manage</varname> either to <literal>"window"</literal> 258 or <literal>"desktop"</literal>. 259 260 The list of desktop manager and window manager should appear 261 inside the display manager with the desktop manager name 262 followed by the window manager name. 263 ''; 264 apply = list: rec { 265 wm = filter (s: s.manage == "window") list; 266 dm = filter (s: s.manage == "desktop") list; 267 names = flip concatMap dm 268 (d: map (w: d.name + optionalString (w.name != "none") (" + " + w.name)) 269 (filter (w: d.name != "none" || w.name != "none") wm)); 270 desktops = mkDesktops names; 271 script = xsession wm dm; 272 }; 273 }; 274 275 job = { 276 277 preStart = mkOption { 278 type = types.lines; 279 default = ""; 280 example = "rm -f /var/log/my-display-manager.log"; 281 description = "Script executed before the display manager is started."; 282 }; 283 284 execCmd = mkOption { 285 type = types.str; 286 example = literalExample '' 287 "''${pkgs.slim}/bin/slim" 288 ''; 289 description = "Command to start the display manager."; 290 }; 291 292 environment = mkOption { 293 type = types.attrsOf types.unspecified; 294 default = {}; 295 example = { SLIM_CFGFILE = "/etc/slim.conf"; }; 296 description = "Additional environment variables needed by the display manager."; 297 }; 298 299 logsXsession = mkOption { 300 type = types.bool; 301 default = false; 302 description = '' 303 Whether the display manager redirects the 304 output of the session script to 305 <filename>~/.xsession-errors</filename>. 306 ''; 307 }; 308 309 }; 310 311 }; 312 313 }; 314 315 config = { 316 317 services.xserver.displayManager.xserverBin = "${xorg.xorgserver}/bin/X"; 318 319 }; 320 321}