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