diff options
Diffstat (limited to 'nixpkgs/nixos/modules/services/x11/display-managers/default.nix')
-rw-r--r-- | nixpkgs/nixos/modules/services/x11/display-managers/default.nix | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/default.nix b/nixpkgs/nixos/modules/services/x11/display-managers/default.nix new file mode 100644 index 000000000000..bf6b048654b3 --- /dev/null +++ b/nixpkgs/nixos/modules/services/x11/display-managers/default.nix @@ -0,0 +1,377 @@ +# This module declares the options to define a *display manager*, the +# program responsible for handling X logins (such as xdm, gdb, or +# SLiM). The display manager allows the user to select a *session +# type*. When the user logs in, the display manager starts the +# *session script* ("xsession" below) to launch the selected session +# type. The session type defines two things: the *desktop manager* +# (e.g., KDE, Gnome or a plain xterm), and optionally the *window +# manager* (e.g. kwin or twm). + +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.xserver; + xorg = pkgs.xorg; + + fontconfig = config.fonts.fontconfig; + xresourcesXft = pkgs.writeText "Xresources-Xft" '' + ${optionalString (fontconfig.dpi != 0) ''Xft.dpi: ${toString fontconfig.dpi}''} + Xft.antialias: ${if fontconfig.antialias then "1" else "0"} + Xft.rgba: ${fontconfig.subpixel.rgba} + Xft.lcdfilter: lcd${fontconfig.subpixel.lcdfilter} + Xft.hinting: ${if fontconfig.hinting.enable then "1" else "0"} + Xft.autohint: ${if fontconfig.hinting.autohint then "1" else "0"} + Xft.hintstyle: hintslight + ''; + + mkCases = session: + concatStrings ( + mapAttrsToList (name: starts: '' + (${name}) + ${concatMapStringsSep "\n " (n: n.start) starts} + ;; + '') (lib.groupBy (n: n.name) session) + ); + + # file provided by services.xserver.displayManager.session.wrapper + xsessionWrapper = pkgs.writeScript "xsession-wrapper" + '' + #! ${pkgs.bash}/bin/bash + + # Shared environment setup for graphical sessions. + + . /etc/profile + cd "$HOME" + + ${optionalString cfg.startDbusSession '' + if test -z "$DBUS_SESSION_BUS_ADDRESS"; then + exec ${pkgs.dbus.dbus-launch} --exit-with-session "$0" "$@" + fi + ''} + + ${optionalString cfg.displayManager.job.logToJournal '' + if [ -z "$_DID_SYSTEMD_CAT" ]; then + export _DID_SYSTEMD_CAT=1 + exec ${config.systemd.package}/bin/systemd-cat -t xsession "$0" "$@" + fi + ''} + + ${optionalString cfg.displayManager.job.logToFile '' + exec &> >(tee ~/.xsession-errors) + ''} + + # Start PulseAudio if enabled. + ${optionalString (config.hardware.pulseaudio.enable) '' + # Publish access credentials in the root window. + if ${config.hardware.pulseaudio.package.out}/bin/pulseaudio --dump-modules | grep module-x11-publish &> /dev/null; then + ${config.hardware.pulseaudio.package.out}/bin/pactl load-module module-x11-publish "display=$DISPLAY" + fi + ''} + + # Tell systemd about our $DISPLAY and $XAUTHORITY. + # This is needed by the ssh-agent unit. + # + # Also tell systemd about the dbus session bus address. + # This is required by user units using the session bus. + ${config.systemd.package}/bin/systemctl --user import-environment DISPLAY XAUTHORITY DBUS_SESSION_BUS_ADDRESS + + # Load X defaults. This should probably be safe on wayland too. + ${xorg.xrdb}/bin/xrdb -merge ${xresourcesXft} + if test -e ~/.Xresources; then + ${xorg.xrdb}/bin/xrdb -merge ~/.Xresources + elif test -e ~/.Xdefaults; then + ${xorg.xrdb}/bin/xrdb -merge ~/.Xdefaults + fi + + # Speed up application start by 50-150ms according to + # http://kdemonkey.blogspot.nl/2008/04/magic-trick.html + rm -rf "$HOME/.compose-cache" + mkdir "$HOME/.compose-cache" + + # Work around KDE errors when a user first logs in and + # .local/share doesn't exist yet. + mkdir -p "$HOME/.local/share" + + unset _DID_SYSTEMD_CAT + + ${cfg.displayManager.sessionCommands} + + # Allow the user to execute commands at the beginning of the X session. + if test -f ~/.xprofile; then + source ~/.xprofile + fi + + # Start systemd user services for graphical sessions + ${config.systemd.package}/bin/systemctl --user start graphical-session.target + + # Allow the user to setup a custom session type. + if test -x ~/.xsession; then + eval exec ~/.xsession "$@" + fi + + if test "$1"; then + # Run the supplied session command. Remove any double quotes with eval. + eval exec "$@" + else + # Fall back to the default window/desktopManager + exec ${cfg.displayManager.session.script} + fi + ''; + + # file provided by services.xserver.displayManager.session.script + xsession = wm: dm: pkgs.writeScript "xsession" + '' + #! ${pkgs.bash}/bin/bash + + # Legacy session script used to construct .desktop files from + # `services.xserver.displayManager.session` entries. Called from + # `sessionWrapper`. + + # Expected parameters: + # $1 = <desktop-manager>+<window-manager> + + # The first argument of this script is the session type. + sessionType="$1" + if [ "$sessionType" = default ]; then sessionType=""; fi + + # The session type is "<desktop-manager>+<window-manager>", so + # extract those (see: + # http://wiki.bash-hackers.org/syntax/pe#substring_removal). + windowManager="''${sessionType##*+}" + : ''${windowManager:=${cfg.windowManager.default}} + desktopManager="''${sessionType%%+*}" + : ''${desktopManager:=${cfg.desktopManager.default}} + + # Start the window manager. + case "$windowManager" in + ${mkCases wm} + (*) echo "$0: Window manager '$windowManager' not found.";; + esac + + # Start the desktop manager. + case "$desktopManager" in + ${mkCases dm} + (*) echo "$0: Desktop manager '$desktopManager' not found.";; + esac + + ${optionalString cfg.updateDbusEnvironment '' + ${lib.getBin pkgs.dbus}/bin/dbus-update-activation-environment --systemd --all + ''} + + test -n "$waitPID" && wait "$waitPID" + + ${config.systemd.package}/bin/systemctl --user stop graphical-session.target + + exit 0 + ''; + + # Desktop Entry Specification: + # - https://standards.freedesktop.org/desktop-entry-spec/latest/ + # - https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html + mkDesktops = names: pkgs.runCommand "desktops" + { # trivial derivation + preferLocalBuild = true; + allowSubstitutes = false; + } + '' + mkdir -p "$out/share/xsessions" + ${concatMapStrings (n: '' + cat - > "$out/share/xsessions/${n}.desktop" << EODESKTOP + [Desktop Entry] + Version=1.0 + Type=XSession + TryExec=${cfg.displayManager.session.script} + Exec=${cfg.displayManager.session.script} "${n}" + Name=${n} + Comment= + EODESKTOP + '') names} + + ${concatMapStrings (pkg: '' + if test -d ${pkg}/share/xsessions; then + ${xorg.lndir}/bin/lndir ${pkg}/share/xsessions $out/share/xsessions + fi + '') cfg.displayManager.extraSessionFilePackages} + + + ${concatMapStrings (pkg: '' + if test -d ${pkg}/share/wayland-sessions; then + mkdir -p "$out/share/wayland-sessions" + ${xorg.lndir}/bin/lndir ${pkg}/share/wayland-sessions $out/share/wayland-sessions + fi + '') cfg.displayManager.extraSessionFilePackages} + ''; + +in + +{ + + options = { + + services.xserver.displayManager = { + + xauthBin = mkOption { + internal = true; + default = "${xorg.xauth}/bin/xauth"; + description = "Path to the <command>xauth</command> program used by display managers."; + }; + + xserverBin = mkOption { + type = types.path; + description = "Path to the X server used by display managers."; + }; + + xserverArgs = mkOption { + type = types.listOf types.str; + default = []; + example = [ "-ac" "-logverbose" "-verbose" "-nolisten tcp" ]; + description = "List of arguments for the X server."; + }; + + setupCommands = mkOption { + type = types.lines; + default = ""; + description = '' + Shell commands executed just after the X server has started. + + This option is only effective for display managers for which this feature + is supported; currently these are LightDM, GDM and SDDM. + ''; + }; + + sessionCommands = mkOption { + type = types.lines; + default = ""; + example = + '' + xmessage "Hello World!" & + ''; + description = '' + Shell commands executed just before the window or desktop manager is + started. These commands are not currently sourced for Wayland sessions. + ''; + }; + + hiddenUsers = mkOption { + type = types.listOf types.str; + default = [ "nobody" ]; + description = '' + A list of users which will not be shown in the display manager. + ''; + }; + + extraSessionFilePackages = mkOption { + type = types.listOf types.package; + default = []; + description = '' + A list of packages containing xsession files to be passed to the display manager. + ''; + }; + + session = mkOption { + default = []; + example = literalExample + '' + [ { manage = "desktop"; + name = "xterm"; + start = ''' + ''${pkgs.xterm}/bin/xterm -ls & + waitPID=$! + '''; + } + ] + ''; + description = '' + List of sessions supported with the command used to start each + session. Each session script can set the + <varname>waitPID</varname> shell variable to make this script + wait until the end of the user session. Each script is used + to define either a window manager or a desktop manager. These + can be differentiated by setting the attribute + <varname>manage</varname> either to <literal>"window"</literal> + or <literal>"desktop"</literal>. + + The list of desktop manager and window manager should appear + inside the display manager with the desktop manager name + followed by the window manager name. + ''; + apply = list: rec { + wm = filter (s: s.manage == "window") list; + dm = filter (s: s.manage == "desktop") list; + names = flip concatMap dm + (d: map (w: d.name + optionalString (w.name != "none") ("+" + w.name)) + (filter (w: d.name != "none" || w.name != "none") wm)); + desktops = mkDesktops names; + script = xsession wm dm; + wrapper = xsessionWrapper; + }; + }; + + job = { + + preStart = mkOption { + type = types.lines; + default = ""; + example = "rm -f /var/log/my-display-manager.log"; + description = "Script executed before the display manager is started."; + }; + + execCmd = mkOption { + type = types.str; + example = literalExample '' + "''${pkgs.slim}/bin/slim" + ''; + description = "Command to start the display manager."; + }; + + environment = mkOption { + type = types.attrsOf types.unspecified; + default = {}; + example = { SLIM_CFGFILE = "/etc/slim.conf"; }; + description = "Additional environment variables needed by the display manager."; + }; + + logToFile = mkOption { + type = types.bool; + default = false; + description = '' + Whether the display manager redirects the output of the + session script to <filename>~/.xsession-errors</filename>. + ''; + }; + + logToJournal = mkOption { + type = types.bool; + default = true; + description = '' + Whether the display manager redirects the output of the + session script to the systemd journal. + ''; + }; + + }; + + }; + + }; + + config = { + services.xserver.displayManager.xserverBin = "${xorg.xorgserver.out}/bin/X"; + + systemd.user.targets.graphical-session = { + unitConfig = { + RefuseManualStart = false; + StopWhenUnneeded = false; + }; + }; + }; + + imports = [ + (mkRemovedOptionModule [ "services" "xserver" "displayManager" "desktopManagerHandlesLidAndPower" ] + "The option is no longer necessary because all display managers have already delegated lid management to systemd.") + ]; + +} |