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