diff options
Diffstat (limited to 'nixos/modules/services/x11/display-managers')
15 files changed, 2494 insertions, 0 deletions
diff --git a/nixos/modules/services/x11/display-managers/account-service-util.nix b/nixos/modules/services/x11/display-managers/account-service-util.nix new file mode 100644 index 00000000000..861976d1186 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/account-service-util.nix @@ -0,0 +1,44 @@ +{ accountsservice +, glib +, gobject-introspection +, python3 +, wrapGAppsHook +, lib +}: + +python3.pkgs.buildPythonApplication { + name = "set-session"; + + format = "other"; + + src = ./set-session.py; + + dontUnpack = true; + + strictDeps = false; + + nativeBuildInputs = [ + wrapGAppsHook + gobject-introspection + ]; + + buildInputs = [ + accountsservice + glib + ]; + + propagatedBuildInputs = with python3.pkgs; [ + pygobject3 + ordered-set + ]; + + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/set-session + chmod +x $out/bin/set-session + ''; + + meta = with lib; { + maintainers = with maintainers; [ ] ++ teams.pantheon.members; + }; +} diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix new file mode 100644 index 00000000000..a5db3dd5dd4 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/default.nix @@ -0,0 +1,503 @@ +# This module declares the options to define a *display manager*, the +# program responsible for handling X logins (such as LightDM, GDM, or SDDM). +# 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, options, pkgs, ... }: + +with lib; + +let + + cfg = config.services.xserver; + opt = options.services.xserver; + xorg = pkgs.xorg; + + fontconfig = config.fonts.fontconfig; + xresourcesXft = pkgs.writeText "Xresources-Xft" '' + 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 + ''; + + # file provided by services.xserver.displayManager.sessionData.wrapper + xsessionWrapper = pkgs.writeScript "xsession-wrapper" + '' + #! ${pkgs.bash}/bin/bash + + # Shared environment setup for graphical sessions. + + . /etc/profile + cd "$HOME" + + # Allow the user to execute commands at the beginning of the X session. + if test -f ~/.xprofile; then + source ~/.xprofile + 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) + ''} + + # 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 + + # Import environment variables into the systemd user environment. + ${optionalString (cfg.displayManager.importedVariables != []) ( + "/run/current-system/systemd/bin/systemctl --user import-environment " + + toString (unique cfg.displayManager.importedVariables) + )} + + # Speed up application start by 50-150ms according to + # http://kdemonkey.blogspot.nl/2008/04/magic-trick.html + compose_cache="''${XCOMPOSECACHE:-$HOME/.compose-cache}" + mkdir -p "$compose_cache" + # To avoid accidentally deleting a wrongly set up XCOMPOSECACHE directory, + # defensively try to delete cache *files* only, following the file format specified in + # https://gitlab.freedesktop.org/xorg/lib/libx11/-/blob/master/modules/im/ximcp/imLcIm.c#L353-358 + # sprintf (*res, "%s/%c%d_%03x_%08x_%08x", dir, _XimGetMyEndian(), XIM_CACHE_VERSION, (unsigned int)sizeof (DefTree), hash, hash2); + ${pkgs.findutils}/bin/find "$compose_cache" -maxdepth 1 -regextype posix-extended -regex '.*/[Bl][0-9]+_[0-9a-f]{3}_[0-9a-f]{8}_[0-9a-f]{8}' -delete + unset compose_cache + + # Work around KDE errors when a user first logs in and + # .local/share doesn't exist yet. + mkdir -p "''${XDG_DATA_HOME:-$HOME/.local/share}" + + unset _DID_SYSTEMD_CAT + + ${cfg.displayManager.sessionCommands} + + # Start systemd user services for graphical sessions + /run/current-system/systemd/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 + # TODO: Do we need this? Should not the session always exist? + echo "error: unknown session $1" 1>&2 + exit 1 + fi + ''; + + installedSessions = pkgs.runCommand "desktops" + { # trivial derivation + preferLocalBuild = true; + allowSubstitutes = false; + } + '' + mkdir -p "$out/share/"{xsessions,wayland-sessions} + + ${concatMapStrings (pkg: '' + for n in ${concatStringsSep " " pkg.providedSessions}; do + if ! test -f ${pkg}/share/wayland-sessions/$n.desktop -o \ + -f ${pkg}/share/xsessions/$n.desktop; then + echo "Couldn't find provided session name, $n.desktop, in session package ${pkg.name}:" + echo " ${pkg}" + return 1 + fi + done + + if test -d ${pkg}/share/xsessions; then + ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/xsessions $out/share/xsessions + fi + if test -d ${pkg}/share/wayland-sessions; then + ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/wayland-sessions $out/share/wayland-sessions + fi + '') cfg.displayManager.sessionPackages} + ''; + + dmDefault = cfg.desktopManager.default; + # fallback default for cases when only default wm is set + dmFallbackDefault = if dmDefault != null then dmDefault else "none"; + wmDefault = cfg.windowManager.default; + + defaultSessionFromLegacyOptions = dmFallbackDefault + optionalString (wmDefault != null && wmDefault != "none") "+${wmDefault}"; + +in + +{ + options = { + + services.xserver.displayManager = { + + xauthBin = mkOption { + internal = true; + default = "${xorg.xauth}/bin/xauth"; + defaultText = literalExpression ''"''${pkgs.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. + ''; + }; + + sessionPackages = mkOption { + type = with types; listOf (package // { + description = "package with provided sessions"; + check = p: assertMsg + (package.check p && p ? providedSessions + && p.providedSessions != [] && all isString p.providedSessions) + '' + Package, '${p.name}', did not specify any session names, as strings, in + 'passthru.providedSessions'. This is required when used as a session package. + + The session names can be looked up in: + ${p}/share/xsessions + ${p}/share/wayland-sessions + ''; + }); + default = []; + description = '' + A list of packages containing x11 or wayland session files to be passed to the display manager. + ''; + }; + + session = mkOption { + default = []; + type = types.listOf types.attrs; + example = literalExpression + '' + [ { 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. + ''; + }; + + sessionData = mkOption { + description = "Data exported for display managers’ convenience"; + internal = true; + default = {}; + apply = val: { + wrapper = xsessionWrapper; + desktops = installedSessions; + sessionNames = concatMap (p: p.providedSessions) cfg.displayManager.sessionPackages; + # We do not want to force users to set defaultSession when they have only single DE. + autologinSession = + if cfg.displayManager.defaultSession != null then + cfg.displayManager.defaultSession + else if cfg.displayManager.sessionData.sessionNames != [] then + head cfg.displayManager.sessionData.sessionNames + else + null; + }; + }; + + defaultSession = mkOption { + type = with types; nullOr str // { + description = "session name"; + check = d: + assertMsg (d != null -> (str.check d && elem d cfg.displayManager.sessionData.sessionNames)) '' + Default graphical session, '${d}', not found. + Valid names for 'services.xserver.displayManager.defaultSession' are: + ${concatStringsSep "\n " cfg.displayManager.sessionData.sessionNames} + ''; + }; + default = + if dmDefault != null || wmDefault != null then + defaultSessionFromLegacyOptions + else + null; + defaultText = literalDocBook '' + Taken from display manager settings or window manager settings, if either is set. + ''; + example = "gnome"; + description = '' + Graphical session to pre-select in the session chooser (only effective for GDM, LightDM and SDDM). + + On GDM, LightDM and SDDM, it will also be used as a session for auto-login. + ''; + }; + + importedVariables = mkOption { + type = types.listOf (types.strMatching "[a-zA-Z_][a-zA-Z0-9_]*"); + visible = false; + description = '' + Environment variables to import into the systemd user environment. + ''; + }; + + 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 = literalExpression ''"''${pkgs.lightdm}/bin/lightdm"''; + description = "Command to start the display manager."; + }; + + environment = mkOption { + type = types.attrsOf types.unspecified; + default = {}; + 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. + ''; + }; + + }; + + # Configuration for automatic login. Common for all DM. + autoLogin = mkOption { + type = types.submodule ({ config, options, ... }: { + options = { + enable = mkOption { + type = types.bool; + default = config.user != null; + defaultText = literalExpression "config.${options.user} != null"; + description = '' + Automatically log in as <option>autoLogin.user</option>. + ''; + }; + + user = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + User to be used for the automatic login. + ''; + }; + }; + }); + + default = {}; + description = '' + Auto login configuration attrset. + ''; + }; + + }; + + }; + + config = { + assertions = [ + { assertion = cfg.displayManager.autoLogin.enable -> cfg.displayManager.autoLogin.user != null; + message = '' + services.xserver.displayManager.autoLogin.enable requires services.xserver.displayManager.autoLogin.user to be set + ''; + } + { + assertion = cfg.desktopManager.default != null || cfg.windowManager.default != null -> cfg.displayManager.defaultSession == defaultSessionFromLegacyOptions; + message = "You cannot use both services.xserver.displayManager.defaultSession option and legacy options (services.xserver.desktopManager.default and services.xserver.windowManager.default)."; + } + ]; + + warnings = + mkIf (dmDefault != null || wmDefault != null) [ + '' + The following options are deprecated: + ${concatStringsSep "\n " (map ({c, t}: t) (filter ({c, t}: c != null) [ + { c = dmDefault; t = "- services.xserver.desktopManager.default"; } + { c = wmDefault; t = "- services.xserver.windowManager.default"; } + ]))} + Please use + services.xserver.displayManager.defaultSession = "${defaultSessionFromLegacyOptions}"; + instead. + '' + ]; + + services.xserver.displayManager.xserverBin = "${xorg.xorgserver.out}/bin/X"; + + services.xserver.displayManager.importedVariables = [ + # This is required by user units using the session bus. + "DBUS_SESSION_BUS_ADDRESS" + # These are needed by the ssh-agent unit. + "DISPLAY" + "XAUTHORITY" + # This is required to specify session within user units (e.g. loginctl lock-session). + "XDG_SESSION_ID" + ]; + + systemd.user.targets.graphical-session = { + unitConfig = { + RefuseManualStart = false; + StopWhenUnneeded = false; + }; + }; + + # Create desktop files and scripts for starting sessions for WMs/DMs + # that do not have upstream session files (those defined using services.{display,desktop,window}Manager.session options). + services.xserver.displayManager.sessionPackages = + let + dms = filter (s: s.manage == "desktop") cfg.displayManager.session; + wms = filter (s: s.manage == "window") cfg.displayManager.session; + + # Script responsible for starting the window manager and the desktop manager. + xsession = dm: wm: pkgs.writeScript "xsession" '' + #! ${pkgs.bash}/bin/bash + + # Legacy session script used to construct .desktop files from + # `services.xserver.displayManager.session` entries. Called from + # `sessionWrapper`. + + # Start the window manager. + ${wm.start} + + # Start the desktop manager. + ${dm.start} + + ${optionalString cfg.updateDbusEnvironment '' + ${lib.getBin pkgs.dbus}/bin/dbus-update-activation-environment --systemd --all + ''} + + test -n "$waitPID" && wait "$waitPID" + + /run/current-system/systemd/bin/systemctl --user stop graphical-session.target + + exit 0 + ''; + in + # We will generate every possible pair of WM and DM. + concatLists ( + builtins.map + ({dm, wm}: let + sessionName = "${dm.name}${optionalString (wm.name != "none") ("+" + wm.name)}"; + script = xsession dm wm; + desktopNames = if dm ? desktopNames + then concatStringsSep ";" dm.desktopNames + else sessionName; + in + optional (dm.name != "none" || wm.name != "none") + (pkgs.writeTextFile { + name = "${sessionName}-xsession"; + destination = "/share/xsessions/${sessionName}.desktop"; + # Desktop Entry Specification: + # - https://standards.freedesktop.org/desktop-entry-spec/latest/ + # - https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html + text = '' + [Desktop Entry] + Version=1.0 + Type=XSession + TryExec=${script} + Exec=${script} + Name=${sessionName} + DesktopNames=${desktopNames} + ''; + } // { + providedSessions = [ sessionName ]; + }) + ) + (cartesianProductOfSets { dm = dms; wm = wms; }) + ); + + # Make xsessions and wayland sessions available in XDG_DATA_DIRS + # as some programs have behavior that depends on them being present + environment.sessionVariables.XDG_DATA_DIRS = [ + "${cfg.displayManager.sessionData.desktops}/share" + ]; + }; + + imports = [ + (mkRemovedOptionModule [ "services" "xserver" "displayManager" "desktopManagerHandlesLidAndPower" ] + "The option is no longer necessary because all display managers have already delegated lid management to systemd.") + (mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logsXsession" ] [ "services" "xserver" "displayManager" "job" "logToFile" ]) + (mkRenamedOptionModule [ "services" "xserver" "displayManager" "logToJournal" ] [ "services" "xserver" "displayManager" "job" "logToJournal" ]) + (mkRenamedOptionModule [ "services" "xserver" "displayManager" "extraSessionFilesPackages" ] [ "services" "xserver" "displayManager" "sessionPackages" ]) + ]; + +} diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix new file mode 100644 index 00000000000..b1dc6643be8 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/gdm.nix @@ -0,0 +1,331 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.xserver.displayManager; + gdm = pkgs.gnome.gdm; + settingsFormat = pkgs.formats.ini { }; + configFile = settingsFormat.generate "custom.conf" cfg.gdm.settings; + + xSessionWrapper = if (cfg.setupCommands == "") then null else + pkgs.writeScript "gdm-x-session-wrapper" '' + #!${pkgs.bash}/bin/bash + ${cfg.setupCommands} + exec "$@" + ''; + + # Solves problems like: + # https://wiki.archlinux.org/index.php/Talk:Bluetooth_headset#GDMs_pulseaudio_instance_captures_bluetooth_headset + # Instead of blacklisting plugins, we use Fedora's PulseAudio configuration for GDM: + # https://src.fedoraproject.org/rpms/gdm/blob/master/f/default.pa-for-gdm + pulseConfig = pkgs.writeText "default.pa" '' + load-module module-device-restore + load-module module-card-restore + load-module module-udev-detect + load-module module-native-protocol-unix + load-module module-default-device-restore + load-module module-always-sink + load-module module-intended-roles + load-module module-suspend-on-idle + load-module module-position-event-sounds + ''; + + defaultSessionName = config.services.xserver.displayManager.defaultSession; + + setSessionScript = pkgs.callPackage ./account-service-util.nix { }; +in + +{ + imports = [ + (mkRenamedOptionModule [ "services" "xserver" "displayManager" "gdm" "autoLogin" "enable" ] [ + "services" + "xserver" + "displayManager" + "autoLogin" + "enable" + ]) + (mkRenamedOptionModule [ "services" "xserver" "displayManager" "gdm" "autoLogin" "user" ] [ + "services" + "xserver" + "displayManager" + "autoLogin" + "user" + ]) + + (mkRemovedOptionModule [ "services" "xserver" "displayManager" "gdm" "nvidiaWayland" ] "We defer to GDM whether Wayland should be enabled.") + ]; + + meta = { + maintainers = teams.gnome.members; + }; + + ###### interface + + options = { + + services.xserver.displayManager.gdm = { + + enable = mkEnableOption "GDM, the GNOME Display Manager"; + + debug = mkEnableOption "debugging messages in GDM"; + + # Auto login options specific to GDM + autoLogin.delay = mkOption { + type = types.int; + default = 0; + description = '' + Seconds of inactivity after which the autologin will be performed. + ''; + }; + + wayland = mkOption { + type = types.bool; + default = true; + description = '' + Allow GDM to run on Wayland instead of Xserver. + ''; + }; + + autoSuspend = mkOption { + default = true; + description = '' + On the GNOME Display Manager login screen, suspend the machine after inactivity. + (Does not affect automatic suspend while logged in, or at lock screen.) + ''; + type = types.bool; + }; + + settings = mkOption { + type = settingsFormat.type; + default = { }; + example = { + debug.enable = true; + }; + description = '' + Options passed to the gdm daemon. + See <link xlink:href="https://help.gnome.org/admin/gdm/stable/configuration.html.en#daemonconfig">here</link> for supported options. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.gdm.enable { + + services.xserver.displayManager.lightdm.enable = false; + + users.users.gdm = + { name = "gdm"; + uid = config.ids.uids.gdm; + group = "gdm"; + home = "/run/gdm"; + description = "GDM user"; + }; + + users.groups.gdm.gid = config.ids.gids.gdm; + + # GDM needs different xserverArgs, presumable because using wayland by default. + services.xserver.tty = null; + services.xserver.display = null; + services.xserver.verbose = null; + + services.xserver.displayManager.job = + { + environment = { + GDM_X_SERVER_EXTRA_ARGS = toString + (filter (arg: arg != "-terminate") cfg.xserverArgs); + # GDM is needed for gnome-login.session + XDG_DATA_DIRS = "${gdm}/share:${cfg.sessionData.desktops}/share"; + } // optionalAttrs (xSessionWrapper != null) { + # Make GDM use this wrapper before running the session, which runs the + # configured setupCommands. This relies on a patched GDM which supports + # this environment variable. + GDM_X_SESSION_WRAPPER = "${xSessionWrapper}"; + }; + execCmd = "exec ${gdm}/bin/gdm"; + preStart = optionalString (defaultSessionName != null) '' + # Set default session in session chooser to a specified values – basically ignore session history. + ${setSessionScript}/bin/set-session ${cfg.sessionData.autologinSession} + ''; + }; + + systemd.tmpfiles.rules = [ + "d /run/gdm/.config 0711 gdm gdm" + ] ++ optionals config.hardware.pulseaudio.enable [ + "d /run/gdm/.config/pulse 0711 gdm gdm" + "L+ /run/gdm/.config/pulse/${pulseConfig.name} - - - - ${pulseConfig}" + ] ++ optionals config.services.gnome.gnome-initial-setup.enable [ + # Create stamp file for gnome-initial-setup to prevent it starting in GDM. + "f /run/gdm/.config/gnome-initial-setup-done 0711 gdm gdm - yes" + ]; + + # Otherwise GDM will not be able to start correctly and display Wayland sessions + systemd.packages = with pkgs.gnome; [ gdm gnome-session gnome-shell ]; + environment.systemPackages = [ pkgs.gnome.adwaita-icon-theme ]; + + # We dont use the upstream gdm service + # it has to be disabled since the gdm package has it + # https://github.com/NixOS/nixpkgs/issues/108672 + systemd.services.gdm.enable = false; + + systemd.services.display-manager.wants = [ + # Because sd_login_monitor_new requires /run/systemd/machines + "systemd-machined.service" + # setSessionScript wants AccountsService + "accounts-daemon.service" + ]; + + systemd.services.display-manager.after = [ + "rc-local.service" + "systemd-machined.service" + "systemd-user-sessions.service" + "getty@tty${gdm.initialVT}.service" + "plymouth-quit.service" + "plymouth-start.service" + ]; + systemd.services.display-manager.conflicts = [ + "getty@tty${gdm.initialVT}.service" + "plymouth-quit.service" + ]; + systemd.services.display-manager.onFailure = [ + "plymouth-quit.service" + ]; + + # Prevent nixos-rebuild switch from bringing down the graphical + # session. (If multi-user.target wants plymouth-quit.service which + # conflicts display-manager.service, then when nixos-rebuild + # switch starts multi-user.target, display-manager.service is + # stopped so plymouth-quit.service can be started.) + systemd.services.plymouth-quit.wantedBy = lib.mkForce []; + + systemd.services.display-manager.serviceConfig = { + # Restart = "always"; - already defined in xserver.nix + KillMode = "mixed"; + IgnoreSIGPIPE = "no"; + BusName = "org.gnome.DisplayManager"; + StandardError = "inherit"; + ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID"; + KeyringMode = "shared"; + EnvironmentFile = "-/etc/locale.conf"; + }; + + systemd.services.display-manager.path = [ pkgs.gnome.gnome-session ]; + + # Allow choosing an user account + services.accounts-daemon.enable = true; + + services.dbus.packages = [ gdm ]; + + systemd.user.services.dbus.wantedBy = [ "default.target" ]; + + programs.dconf.profiles.gdm = + let + customDconf = pkgs.writeTextFile { + name = "gdm-dconf"; + destination = "/dconf/gdm-custom"; + text = '' + ${optionalString (!cfg.gdm.autoSuspend) '' + [org/gnome/settings-daemon/plugins/power] + sleep-inactive-ac-type='nothing' + sleep-inactive-battery-type='nothing' + sleep-inactive-ac-timeout=0 + sleep-inactive-battery-timeout=0 + ''} + ''; + }; + + customDconfDb = pkgs.stdenv.mkDerivation { + name = "gdm-dconf-db"; + buildCommand = '' + ${pkgs.dconf}/bin/dconf compile $out ${customDconf}/dconf + ''; + }; + in pkgs.stdenv.mkDerivation { + name = "dconf-gdm-profile"; + buildCommand = '' + # Check that the GDM profile starts with what we expect. + if [ $(head -n 1 ${gdm}/share/dconf/profile/gdm) != "user-db:user" ]; then + echo "GDM dconf profile changed, please update gdm.nix" + exit 1 + fi + # Insert our custom DB behind it. + sed '2ifile-db:${customDconfDb}' ${gdm}/share/dconf/profile/gdm > $out + ''; + }; + + # Use AutomaticLogin if delay is zero, because it's immediate. + # Otherwise with TimedLogin with zero seconds the prompt is still + # presented and there's a little delay. + services.xserver.displayManager.gdm.settings = { + daemon = mkMerge [ + { WaylandEnable = cfg.gdm.wayland; } + # nested if else didn't work + (mkIf (cfg.autoLogin.enable && cfg.gdm.autoLogin.delay != 0 ) { + TimedLoginEnable = true; + TimedLogin = cfg.autoLogin.user; + TimedLoginDelay = cfg.gdm.autoLogin.delay; + }) + (mkIf (cfg.autoLogin.enable && cfg.gdm.autoLogin.delay == 0 ) { + AutomaticLoginEnable = true; + AutomaticLogin = cfg.autoLogin.user; + }) + ]; + debug = mkIf cfg.gdm.debug { + Enable = true; + }; + }; + + environment.etc."gdm/custom.conf".source = configFile; + + environment.etc."gdm/Xsession".source = config.services.xserver.displayManager.sessionData.wrapper; + + # GDM LFS PAM modules, adapted somehow to NixOS + security.pam.services = { + gdm-launch-environment.text = '' + auth required pam_succeed_if.so audit quiet_success user = gdm + auth optional pam_permit.so + + account required pam_succeed_if.so audit quiet_success user = gdm + account sufficient pam_unix.so + + password required pam_deny.so + + session required pam_succeed_if.so audit quiet_success user = gdm + session required pam_env.so conffile=/etc/pam/environment readenv=0 + session optional ${pkgs.systemd}/lib/security/pam_systemd.so + session optional pam_keyinit.so force revoke + session optional pam_permit.so + ''; + + gdm-password.text = '' + auth substack login + account include login + password substack login + session include login + ''; + + gdm-autologin.text = '' + auth requisite pam_nologin.so + + auth required pam_succeed_if.so uid >= 1000 quiet + auth required pam_permit.so + + account sufficient pam_unix.so + + password requisite pam_unix.so nullok sha512 + + session optional pam_keyinit.so revoke + session include login + ''; + + }; + + }; + +} diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/enso-os.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/enso-os.nix new file mode 100644 index 00000000000..930ee96b384 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/enso-os.nix @@ -0,0 +1,140 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + dmcfg = config.services.xserver.displayManager; + ldmcfg = dmcfg.lightdm; + cfg = ldmcfg.greeters.enso; + + theme = cfg.theme.package; + icons = cfg.iconTheme.package; + cursors = cfg.cursorTheme.package; + + ensoGreeterConf = pkgs.writeText "lightdm-enso-os-greeter.conf" '' + [greeter] + default-wallpaper=${ldmcfg.background} + gtk-theme=${cfg.theme.name} + icon-theme=${cfg.iconTheme.name} + cursor-theme=${cfg.cursorTheme.name} + blur=${toString cfg.blur} + brightness=${toString cfg.brightness} + ${cfg.extraConfig} + ''; +in { + options = { + services.xserver.displayManager.lightdm.greeters.enso = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable enso-os-greeter as the lightdm greeter + ''; + }; + + theme = { + package = mkOption { + type = types.package; + default = pkgs.gnome.gnome-themes-extra; + defaultText = literalExpression "pkgs.gnome.gnome-themes-extra"; + description = '' + The package path that contains the theme given in the name option. + ''; + }; + + name = mkOption { + type = types.str; + default = "Adwaita"; + description = '' + Name of the theme to use for the lightdm-enso-os-greeter + ''; + }; + }; + + iconTheme = { + package = mkOption { + type = types.package; + default = pkgs.papirus-icon-theme; + defaultText = literalExpression "pkgs.papirus-icon-theme"; + description = '' + The package path that contains the icon theme given in the name option. + ''; + }; + + name = mkOption { + type = types.str; + default = "ePapirus"; + description = '' + Name of the icon theme to use for the lightdm-enso-os-greeter + ''; + }; + }; + + cursorTheme = { + package = mkOption { + type = types.package; + default = pkgs.capitaine-cursors; + defaultText = literalExpression "pkgs.capitaine-cursors"; + description = '' + The package path that contains the cursor theme given in the name option. + ''; + }; + + name = mkOption { + type = types.str; + default = "capitane-cursors"; + description = '' + Name of the cursor theme to use for the lightdm-enso-os-greeter + ''; + }; + }; + + blur = mkOption { + type = types.bool; + default = false; + description = '' + Whether or not to enable blur + ''; + }; + + brightness = mkOption { + type = types.int; + default = 7; + description = '' + Brightness + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration that should be put in the greeter.conf + configuration file + ''; + }; + }; + }; + + config = mkIf (ldmcfg.enable && cfg.enable) { + environment.etc."lightdm/greeter.conf".source = ensoGreeterConf; + + environment.systemPackages = [ + cursors + icons + theme + ]; + + services.xserver.displayManager.lightdm = { + greeter = mkDefault { + package = pkgs.lightdm-enso-os-greeter.xgreeters; + name = "pantheon-greeter"; + }; + + greeters = { + gtk = { + enable = mkDefault false; + }; + }; + }; + }; +} diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix new file mode 100644 index 00000000000..debd4b568bf --- /dev/null +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix @@ -0,0 +1,174 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + dmcfg = config.services.xserver.displayManager; + ldmcfg = dmcfg.lightdm; + xcfg = config.services.xserver; + cfg = ldmcfg.greeters.gtk; + + inherit (pkgs) writeText; + + theme = cfg.theme.package; + icons = cfg.iconTheme.package; + cursors = cfg.cursorTheme.package; + + gtkGreeterConf = writeText "lightdm-gtk-greeter.conf" + '' + [greeter] + theme-name = ${cfg.theme.name} + icon-theme-name = ${cfg.iconTheme.name} + cursor-theme-name = ${cfg.cursorTheme.name} + cursor-theme-size = ${toString cfg.cursorTheme.size} + background = ${ldmcfg.background} + ${optionalString (cfg.clock-format != null) "clock-format = ${cfg.clock-format}"} + ${optionalString (cfg.indicators != null) "indicators = ${concatStringsSep ";" cfg.indicators}"} + ${optionalString (xcfg.dpi != null) "xft-dpi=${toString xcfg.dpi}"} + ${cfg.extraConfig} + ''; + +in +{ + options = { + + services.xserver.displayManager.lightdm.greeters.gtk = { + + enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable lightdm-gtk-greeter as the lightdm greeter. + ''; + }; + + theme = { + + package = mkOption { + type = types.package; + default = pkgs.gnome.gnome-themes-extra; + defaultText = literalExpression "pkgs.gnome.gnome-themes-extra"; + description = '' + The package path that contains the theme given in the name option. + ''; + }; + + name = mkOption { + type = types.str; + default = "Adwaita"; + description = '' + Name of the theme to use for the lightdm-gtk-greeter. + ''; + }; + + }; + + iconTheme = { + + package = mkOption { + type = types.package; + default = pkgs.gnome.adwaita-icon-theme; + defaultText = literalExpression "pkgs.gnome.adwaita-icon-theme"; + description = '' + The package path that contains the icon theme given in the name option. + ''; + }; + + name = mkOption { + type = types.str; + default = "Adwaita"; + description = '' + Name of the icon theme to use for the lightdm-gtk-greeter. + ''; + }; + + }; + + cursorTheme = { + + package = mkOption { + type = types.package; + default = pkgs.gnome.adwaita-icon-theme; + defaultText = literalExpression "pkgs.gnome.adwaita-icon-theme"; + description = '' + The package path that contains the cursor theme given in the name option. + ''; + }; + + name = mkOption { + type = types.str; + default = "Adwaita"; + description = '' + Name of the cursor theme to use for the lightdm-gtk-greeter. + ''; + }; + + size = mkOption { + type = types.int; + default = 16; + description = '' + Size of the cursor theme to use for the lightdm-gtk-greeter. + ''; + }; + }; + + clock-format = mkOption { + type = types.nullOr types.str; + default = null; + example = "%F"; + description = '' + Clock format string (as expected by strftime, e.g. "%H:%M") + to use with the lightdm gtk greeter panel. + + If set to null the default clock format is used. + ''; + }; + + indicators = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + example = [ "~host" "~spacer" "~clock" "~spacer" "~session" "~language" "~a11y" "~power" ]; + description = '' + List of allowed indicator modules to use for the lightdm gtk + greeter panel. + + Built-in indicators include "~a11y", "~language", "~session", + "~power", "~clock", "~host", "~spacer". Unity indicators can be + represented by short name (e.g. "sound", "power"), service file name, + or absolute path. + + If set to null the default indicators are used. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration that should be put in the lightdm-gtk-greeter.conf + configuration file. + ''; + }; + + }; + + }; + + config = mkIf (ldmcfg.enable && cfg.enable) { + + services.xserver.displayManager.lightdm.greeter = mkDefault { + package = pkgs.lightdm_gtk_greeter.xgreeters; + name = "lightdm-gtk-greeter"; + }; + + environment.systemPackages = [ + cursors + icons + theme + ]; + + environment.etc."lightdm/lightdm-gtk-greeter.conf".source = gtkGreeterConf; + + }; +} diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix new file mode 100644 index 00000000000..16d7fdf15cf --- /dev/null +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix @@ -0,0 +1,100 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + dmcfg = config.services.xserver.displayManager; + ldmcfg = dmcfg.lightdm; + cfg = ldmcfg.greeters.mini; + + miniGreeterConf = pkgs.writeText "lightdm-mini-greeter.conf" + '' + [greeter] + user = ${cfg.user} + show-password-label = true + password-label-text = Password: + invalid-password-text = Invalid Password + show-input-cursor = true + password-alignment = right + + [greeter-hotkeys] + mod-key = meta + shutdown-key = s + restart-key = r + hibernate-key = h + suspend-key = u + + [greeter-theme] + font = Sans + font-size = 1em + font-weight = bold + font-style = normal + text-color = "#080800" + error-color = "#F8F8F0" + background-image = "${ldmcfg.background}" + background-color = "#1B1D1E" + window-color = "#F92672" + border-color = "#080800" + border-width = 2px + layout-space = 15 + password-color = "#F8F8F0" + password-background-color = "#1B1D1E" + password-border-color = "#080800" + password-border-width = 2px + + ${cfg.extraConfig} + ''; + +in +{ + options = { + + services.xserver.displayManager.lightdm.greeters.mini = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable lightdm-mini-greeter as the lightdm greeter. + + Note that this greeter starts only the default X session. + You can configure the default X session using + <xref linkend="opt-services.xserver.displayManager.defaultSession"/>. + ''; + }; + + user = mkOption { + type = types.str; + default = "root"; + description = '' + The user to login as. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration that should be put in the lightdm-mini-greeter.conf + configuration file. + ''; + }; + + }; + + }; + + config = mkIf (ldmcfg.enable && cfg.enable) { + + services.xserver.displayManager.lightdm.greeters.gtk.enable = false; + + services.xserver.displayManager.lightdm.greeter = mkDefault { + package = pkgs.lightdm-mini-greeter.xgreeters; + name = "lightdm-mini-greeter"; + }; + + environment.etc."lightdm/lightdm-mini-greeter.conf".source = miniGreeterConf; + + }; +} diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix new file mode 100644 index 00000000000..f18e4a914e5 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix @@ -0,0 +1,49 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + dmcfg = config.services.xserver.displayManager; + ldmcfg = dmcfg.lightdm; + cfg = ldmcfg.greeters.pantheon; + +in +{ + meta = with lib; { + maintainers = with maintainers; [ ] ++ teams.pantheon.members; + }; + + options = { + + services.xserver.displayManager.lightdm.greeters.pantheon = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable elementary-greeter as the lightdm greeter. + ''; + }; + + }; + + }; + + config = mkIf (ldmcfg.enable && cfg.enable) { + + services.xserver.displayManager.lightdm.greeters.gtk.enable = false; + + services.xserver.displayManager.lightdm.greeter = mkDefault { + package = pkgs.pantheon.elementary-greeter.xgreeters; + name = "io.elementary.greeter"; + }; + + # Show manual login card. + services.xserver.displayManager.lightdm.extraSeatDefaults = "greeter-show-manual-login=true"; + + environment.etc."lightdm/io.elementary.greeter.conf".source = "${pkgs.pantheon.elementary-greeter}/etc/lightdm/io.elementary.greeter.conf"; + environment.etc."wingpanel.d/io.elementary.greeter.allowed".source = "${pkgs.pantheon.elementary-default-settings}/etc/wingpanel.d/io.elementary.greeter.allowed"; + + }; +} diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix new file mode 100644 index 00000000000..a9ba8e6280d --- /dev/null +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix @@ -0,0 +1,92 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + dmcfg = config.services.xserver.displayManager; + ldmcfg = dmcfg.lightdm; + cfg = ldmcfg.greeters.tiny; + +in +{ + options = { + + services.xserver.displayManager.lightdm.greeters.tiny = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable lightdm-tiny-greeter as the lightdm greeter. + + Note that this greeter starts only the default X session. + You can configure the default X session using + <xref linkend="opt-services.xserver.displayManager.defaultSession"/>. + ''; + }; + + label = { + user = mkOption { + type = types.str; + default = "Username"; + description = '' + The string to represent the user_text label. + ''; + }; + + pass = mkOption { + type = types.str; + default = "Password"; + description = '' + The string to represent the pass_text label. + ''; + }; + }; + + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Section to describe style and ui. + ''; + }; + + }; + + }; + + config = mkIf (ldmcfg.enable && cfg.enable) { + + services.xserver.displayManager.lightdm.greeters.gtk.enable = false; + + nixpkgs.config.lightdm-tiny-greeter.conf = + let + configHeader = '' + #include <gtk/gtk.h> + static const char *user_text = "${cfg.label.user}"; + static const char *pass_text = "${cfg.label.pass}"; + static const char *session = "${dmcfg.defaultSession}"; + ''; + in + optionalString (cfg.extraConfig != "") + (configHeader + cfg.extraConfig); + + services.xserver.displayManager.lightdm.greeter = + mkDefault { + package = pkgs.lightdm-tiny-greeter.xgreeters; + name = "lightdm-tiny-greeter"; + }; + + assertions = [ + { + assertion = dmcfg.defaultSession != null; + message = '' + Please set: services.xserver.displayManager.defaultSession + ''; + } + ]; + + }; +} diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix new file mode 100644 index 00000000000..27dfed3cc14 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/lightdm.nix @@ -0,0 +1,328 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + xcfg = config.services.xserver; + dmcfg = xcfg.displayManager; + xEnv = config.systemd.services.display-manager.environment; + cfg = dmcfg.lightdm; + sessionData = dmcfg.sessionData; + + setSessionScript = pkgs.callPackage ./account-service-util.nix { }; + + inherit (pkgs) lightdm writeScript writeText; + + # lightdm runs with clearenv(), but we need a few things in the environment for X to startup + xserverWrapper = writeScript "xserver-wrapper" + '' + #! ${pkgs.bash}/bin/bash + ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)} + + display=$(echo "$@" | xargs -n 1 | grep -P ^:\\d\$ | head -n 1 | sed s/^://) + if [ -z "$display" ] + then additionalArgs=":0 -logfile /var/log/X.0.log" + else additionalArgs="-logfile /var/log/X.$display.log" + fi + + exec ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} $additionalArgs "$@" + ''; + + usersConf = writeText "users.conf" + '' + [UserList] + minimum-uid=500 + hidden-users=${concatStringsSep " " dmcfg.hiddenUsers} + hidden-shells=/run/current-system/sw/bin/nologin + ''; + + lightdmConf = writeText "lightdm.conf" + '' + [LightDM] + ${optionalString cfg.greeter.enable '' + greeter-user = ${config.users.users.lightdm.name} + greeters-directory = ${cfg.greeter.package} + ''} + sessions-directory = ${dmcfg.sessionData.desktops}/share/xsessions:${dmcfg.sessionData.desktops}/share/wayland-sessions + ${cfg.extraConfig} + + [Seat:*] + xserver-command = ${xserverWrapper} + session-wrapper = ${dmcfg.sessionData.wrapper} + ${optionalString cfg.greeter.enable '' + greeter-session = ${cfg.greeter.name} + ''} + ${optionalString dmcfg.autoLogin.enable '' + autologin-user = ${dmcfg.autoLogin.user} + autologin-user-timeout = ${toString cfg.autoLogin.timeout} + autologin-session = ${sessionData.autologinSession} + ''} + ${optionalString (dmcfg.setupCommands != "") '' + display-setup-script=${pkgs.writeScript "lightdm-display-setup" '' + #!${pkgs.bash}/bin/bash + ${dmcfg.setupCommands} + ''} + ''} + ${cfg.extraSeatDefaults} + ''; + +in +{ + meta = with lib; { + maintainers = with maintainers; [ ] ++ teams.pantheon.members; + }; + + # Note: the order in which lightdm greeter modules are imported + # here determines the default: later modules (if enable) are + # preferred. + imports = [ + ./lightdm-greeters/gtk.nix + ./lightdm-greeters/mini.nix + ./lightdm-greeters/enso-os.nix + ./lightdm-greeters/pantheon.nix + ./lightdm-greeters/tiny.nix + (mkRenamedOptionModule [ "services" "xserver" "displayManager" "lightdm" "autoLogin" "enable" ] [ + "services" + "xserver" + "displayManager" + "autoLogin" + "enable" + ]) + (mkRenamedOptionModule [ "services" "xserver" "displayManager" "lightdm" "autoLogin" "user" ] [ + "services" + "xserver" + "displayManager" + "autoLogin" + "user" + ]) + ]; + + options = { + + services.xserver.displayManager.lightdm = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable lightdm as the display manager. + ''; + }; + + greeter = { + enable = mkOption { + type = types.bool; + default = true; + description = '' + If set to false, run lightdm in greeterless mode. This only works if autologin + is enabled and autoLogin.timeout is zero. + ''; + }; + package = mkOption { + type = types.package; + description = '' + The LightDM greeter to login via. The package should be a directory + containing a .desktop file matching the name in the 'name' option. + ''; + + }; + name = mkOption { + type = types.str; + description = '' + The name of a .desktop file in the directory specified + in the 'package' option. + ''; + }; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + user-authority-in-system-dir = true + ''; + description = "Extra lines to append to LightDM section."; + }; + + background = mkOption { + type = types.either types.path (types.strMatching "^#[0-9]\{6\}$"); + # Manual cannot depend on packages, we are actually setting the default in config below. + defaultText = literalExpression "pkgs.nixos-artwork.wallpapers.simple-dark-gray-bottom.gnomeFilePath"; + description = '' + The background image or color to use. + ''; + }; + + extraSeatDefaults = mkOption { + type = types.lines; + default = ""; + example = '' + greeter-show-manual-login=true + ''; + description = "Extra lines to append to SeatDefaults section."; + }; + + # Configuration for automatic login specific to LightDM + autoLogin.timeout = mkOption { + type = types.int; + default = 0; + description = '' + Show the greeter for this many seconds before automatic login occurs. + ''; + }; + + }; + }; + + config = mkIf cfg.enable { + + assertions = [ + { assertion = xcfg.enable; + message = '' + LightDM requires services.xserver.enable to be true + ''; + } + { assertion = dmcfg.autoLogin.enable -> sessionData.autologinSession != null; + message = '' + LightDM auto-login requires that services.xserver.displayManager.defaultSession is set. + ''; + } + { assertion = !cfg.greeter.enable -> (dmcfg.autoLogin.enable && cfg.autoLogin.timeout == 0); + message = '' + LightDM can only run without greeter if automatic login is enabled and the timeout for it + is set to zero. + ''; + } + ]; + + # Keep in sync with the defaultText value from the option definition. + services.xserver.displayManager.lightdm.background = mkDefault pkgs.nixos-artwork.wallpapers.simple-dark-gray-bottom.gnomeFilePath; + + # Set default session in session chooser to a specified values – basically ignore session history. + # Auto-login is already covered by a config value. + services.xserver.displayManager.job.preStart = optionalString (!dmcfg.autoLogin.enable && dmcfg.defaultSession != null) '' + ${setSessionScript}/bin/set-session ${dmcfg.defaultSession} + ''; + + # setSessionScript needs session-files in XDG_DATA_DIRS + services.xserver.displayManager.job.environment.XDG_DATA_DIRS = "${dmcfg.sessionData.desktops}/share/"; + + # setSessionScript wants AccountsService + systemd.services.display-manager.wants = [ + "accounts-daemon.service" + ]; + + # lightdm relaunches itself via just `lightdm`, so needs to be on the PATH + services.xserver.displayManager.job.execCmd = '' + export PATH=${lightdm}/sbin:$PATH + exec ${lightdm}/sbin/lightdm + ''; + + # Replaces getty + systemd.services.display-manager.conflicts = [ + "getty@tty7.service" + # TODO: Add "plymouth-quit.service" so LightDM can control when plymouth + # quits. Currently this breaks switching to configurations with plymouth. + ]; + + # Pull in dependencies of services we replace. + systemd.services.display-manager.after = [ + "rc-local.service" + "systemd-machined.service" + "systemd-user-sessions.service" + "getty@tty7.service" + "user.slice" + ]; + + # user.slice needs to be present + systemd.services.display-manager.requires = [ + "user.slice" + ]; + + # lightdm stops plymouth so when it fails make sure plymouth stops. + systemd.services.display-manager.onFailure = [ + "plymouth-quit.service" + ]; + + systemd.services.display-manager.serviceConfig = { + BusName = "org.freedesktop.DisplayManager"; + IgnoreSIGPIPE = "no"; + # This allows lightdm to pass the LUKS password through to PAM. + # login keyring is unlocked automatic when autologin is used. + KeyringMode = "shared"; + KillMode = "mixed"; + StandardError = "inherit"; + }; + + environment.etc."lightdm/lightdm.conf".source = lightdmConf; + environment.etc."lightdm/users.conf".source = usersConf; + + services.dbus.enable = true; + services.dbus.packages = [ lightdm ]; + + # lightdm uses the accounts daemon to remember language/window-manager per user + services.accounts-daemon.enable = true; + + # Enable the accounts daemon to find lightdm's dbus interface + environment.systemPackages = [ lightdm ]; + + security.polkit.enable = true; + + security.pam.services.lightdm.text = '' + auth substack login + account include login + password substack login + session include login + ''; + + security.pam.services.lightdm-greeter.text = '' + auth required pam_succeed_if.so audit quiet_success user = lightdm + auth optional pam_permit.so + + account required pam_succeed_if.so audit quiet_success user = lightdm + account sufficient pam_unix.so + + password required pam_deny.so + + session required pam_succeed_if.so audit quiet_success user = lightdm + session required pam_env.so conffile=/etc/pam/environment readenv=0 + session optional ${pkgs.systemd}/lib/security/pam_systemd.so + session optional pam_keyinit.so force revoke + session optional pam_permit.so + ''; + + security.pam.services.lightdm-autologin.text = '' + auth requisite pam_nologin.so + + auth required pam_succeed_if.so uid >= 1000 quiet + auth required pam_permit.so + + account sufficient pam_unix.so + + password requisite pam_unix.so nullok sha512 + + session optional pam_keyinit.so revoke + session include login + ''; + + users.users.lightdm = { + home = "/var/lib/lightdm"; + group = "lightdm"; + uid = config.ids.uids.lightdm; + shell = pkgs.bash; + }; + + systemd.tmpfiles.rules = [ + "d /run/lightdm 0711 lightdm lightdm -" + "d /var/cache/lightdm 0711 root lightdm -" + "d /var/lib/lightdm 1770 lightdm lightdm -" + "d /var/lib/lightdm-data 1775 lightdm lightdm -" + "d /var/log/lightdm 0711 root lightdm -" + ]; + + users.groups.lightdm.gid = config.ids.gids.lightdm; + services.xserver.tty = null; # We might start multiple X servers so let the tty increment themselves.. + services.xserver.display = null; # We specify our own display (and logfile) in xserver-wrapper up there + }; +} diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix new file mode 100644 index 00000000000..529a086381f --- /dev/null +++ b/nixos/modules/services/x11/display-managers/sddm.nix @@ -0,0 +1,288 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + xcfg = config.services.xserver; + dmcfg = xcfg.displayManager; + cfg = dmcfg.sddm; + xEnv = config.systemd.services.display-manager.environment; + + sddm = pkgs.libsForQt5.sddm; + + iniFmt = pkgs.formats.ini { }; + + xserverWrapper = pkgs.writeShellScript "xserver-wrapper" '' + ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)} + exec systemd-cat -t xserver-wrapper ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} "$@" + ''; + + Xsetup = pkgs.writeShellScript "Xsetup" '' + ${cfg.setupScript} + ${dmcfg.setupCommands} + ''; + + Xstop = pkgs.writeShellScript "Xstop" '' + ${cfg.stopScript} + ''; + + defaultConfig = { + General = { + HaltCommand = "/run/current-system/systemd/bin/systemctl poweroff"; + RebootCommand = "/run/current-system/systemd/bin/systemctl reboot"; + Numlock = if cfg.autoNumlock then "on" else "none"; # on, off none + + # Implementation is done via pkgs/applications/display-managers/sddm/sddm-default-session.patch + DefaultSession = optionalString (dmcfg.defaultSession != null) "${dmcfg.defaultSession}.desktop"; + }; + + Theme = { + Current = cfg.theme; + ThemeDir = "/run/current-system/sw/share/sddm/themes"; + FacesDir = "/run/current-system/sw/share/sddm/faces"; + }; + + Users = { + MaximumUid = config.ids.uids.nixbld; + HideUsers = concatStringsSep "," dmcfg.hiddenUsers; + HideShells = "/run/current-system/sw/bin/nologin"; + }; + + X11 = { + MinimumVT = if xcfg.tty != null then xcfg.tty else 7; + ServerPath = toString xserverWrapper; + XephyrPath = "${pkgs.xorg.xorgserver.out}/bin/Xephyr"; + SessionCommand = toString dmcfg.sessionData.wrapper; + SessionDir = "${dmcfg.sessionData.desktops}/share/xsessions"; + XauthPath = "${pkgs.xorg.xauth}/bin/xauth"; + DisplayCommand = toString Xsetup; + DisplayStopCommand = toString Xstop; + EnableHiDPI = cfg.enableHidpi; + }; + + Wayland = { + EnableHiDPI = cfg.enableHidpi; + SessionDir = "${dmcfg.sessionData.desktops}/share/wayland-sessions"; + }; + } // lib.optionalAttrs dmcfg.autoLogin.enable { + Autologin = { + User = dmcfg.autoLogin.user; + Session = autoLoginSessionName; + Relogin = cfg.autoLogin.relogin; + }; + }; + + cfgFile = + iniFmt.generate "sddm.conf" (lib.recursiveUpdate defaultConfig cfg.settings); + + autoLoginSessionName = + "${dmcfg.sessionData.autologinSession}.desktop"; + +in +{ + imports = [ + (mkRemovedOptionModule + [ "services" "xserver" "displayManager" "sddm" "themes" ] + "Set the option `services.xserver.displayManager.sddm.package' instead.") + (mkRenamedOptionModule + [ "services" "xserver" "displayManager" "sddm" "autoLogin" "enable" ] + [ "services" "xserver" "displayManager" "autoLogin" "enable" ]) + (mkRenamedOptionModule + [ "services" "xserver" "displayManager" "sddm" "autoLogin" "user" ] + [ "services" "xserver" "displayManager" "autoLogin" "user" ]) + (mkRemovedOptionModule + [ "services" "xserver" "displayManager" "sddm" "extraConfig" ] + "Set the option `services.xserver.displayManager.sddm.settings' instead.") + ]; + + options = { + + services.xserver.displayManager.sddm = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable sddm as the display manager. + ''; + }; + + enableHidpi = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable automatic HiDPI mode. + ''; + }; + + settings = mkOption { + type = iniFmt.type; + default = { }; + example = { + Autologin = { + User = "john"; + Session = "plasma.desktop"; + }; + }; + description = '' + Extra settings merged in and overwritting defaults in sddm.conf. + ''; + }; + + theme = mkOption { + type = types.str; + default = ""; + description = '' + Greeter theme to use. + ''; + }; + + autoNumlock = mkOption { + type = types.bool; + default = false; + description = '' + Enable numlock at login. + ''; + }; + + setupScript = mkOption { + type = types.str; + default = ""; + example = '' + # workaround for using NVIDIA Optimus without Bumblebee + xrandr --setprovideroutputsource modesetting NVIDIA-0 + xrandr --auto + ''; + description = '' + A script to execute when starting the display server. DEPRECATED, please + use <option>services.xserver.displayManager.setupCommands</option>. + ''; + }; + + stopScript = mkOption { + type = types.str; + default = ""; + description = '' + A script to execute when stopping the display server. + ''; + }; + + # Configuration for automatic login specific to SDDM + autoLogin = { + relogin = mkOption { + type = types.bool; + default = false; + description = '' + If true automatic login will kick in again on session exit (logout), otherwise it + will only log in automatically when the display-manager is started. + ''; + }; + + minimumUid = mkOption { + type = types.ints.u16; + default = 1000; + description = '' + Minimum user ID for auto-login user. + ''; + }; + }; + }; + }; + + config = mkIf cfg.enable { + + assertions = [ + { + assertion = xcfg.enable; + message = '' + SDDM requires services.xserver.enable to be true + ''; + } + { + assertion = dmcfg.autoLogin.enable -> autoLoginSessionName != null; + message = '' + SDDM auto-login requires that services.xserver.displayManager.defaultSession is set. + ''; + } + ]; + + services.xserver.displayManager.job = { + environment = { + # Load themes from system environment + QT_PLUGIN_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtPluginPrefix; + QML2_IMPORT_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtQmlPrefix; + }; + + execCmd = "exec /run/current-system/sw/bin/sddm"; + }; + + security.pam.services = { + sddm = { + allowNullPassword = true; + startSession = true; + }; + + sddm-greeter.text = '' + auth required pam_succeed_if.so audit quiet_success user = sddm + auth optional pam_permit.so + + account required pam_succeed_if.so audit quiet_success user = sddm + account sufficient pam_unix.so + + password required pam_deny.so + + session required pam_succeed_if.so audit quiet_success user = sddm + session required pam_env.so conffile=/etc/pam/environment readenv=0 + session optional ${pkgs.systemd}/lib/security/pam_systemd.so + session optional pam_keyinit.so force revoke + session optional pam_permit.so + ''; + + sddm-autologin.text = '' + auth requisite pam_nologin.so + auth required pam_succeed_if.so uid >= ${toString cfg.autoLogin.minimumUid} quiet + auth required pam_permit.so + + account include sddm + + password include sddm + + session include sddm + ''; + }; + + users.users.sddm = { + createHome = true; + home = "/var/lib/sddm"; + group = "sddm"; + uid = config.ids.uids.sddm; + }; + + environment.etc."sddm.conf".source = cfgFile; + environment.pathsToLink = [ + "/share/sddm" + ]; + + users.groups.sddm.gid = config.ids.gids.sddm; + + environment.systemPackages = [ sddm ]; + services.dbus.packages = [ sddm ]; + + # To enable user switching, allow sddm to allocate TTYs/displays dynamically. + services.xserver.tty = null; + services.xserver.display = null; + + systemd.tmpfiles.rules = [ + # Prior to Qt 5.9.2, there is a QML cache invalidation bug which sometimes + # strikes new Plasma 5 releases. If the QML cache is not invalidated, SDDM + # will segfault without explanation. We really tore our hair out for awhile + # before finding the bug: + # https://bugreports.qt.io/browse/QTBUG-62302 + # We work around the problem by deleting the QML cache before startup. + # This was supposedly fixed in Qt 5.9.2 however it has been reported with + # 5.10 and 5.11 as well. The initial workaround was to delete the directory + # in the Xsetup script but that doesn't do anything. + # Instead we use tmpfiles.d to ensure it gets wiped. + # This causes a small but perceptible delay when SDDM starts. + "e ${config.users.users.sddm.home}/.cache - - - 0" + ]; + }; +} diff --git a/nixos/modules/services/x11/display-managers/set-session.py b/nixos/modules/services/x11/display-managers/set-session.py new file mode 100755 index 00000000000..75940efe32b --- /dev/null +++ b/nixos/modules/services/x11/display-managers/set-session.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +import gi, argparse, os, logging, sys + +gi.require_version("AccountsService", "1.0") +from gi.repository import AccountsService, GLib +from ordered_set import OrderedSet + + +def get_session_file(session): + system_data_dirs = GLib.get_system_data_dirs() + + session_dirs = OrderedSet( + os.path.join(data_dir, session) + for data_dir in system_data_dirs + for session in {"wayland-sessions", "xsessions"} + ) + + session_files = OrderedSet( + os.path.join(dir, session + ".desktop") + for dir in session_dirs + if os.path.exists(os.path.join(dir, session + ".desktop")) + ) + + # Deal with duplicate wayland-sessions and xsessions. + # Needed for the situation in gnome-session, where there's + # a xsession named the same as a wayland session. + if any(map(is_session_wayland, session_files)): + session_files = OrderedSet( + session for session in session_files if is_session_wayland(session) + ) + else: + session_files = OrderedSet( + session for session in session_files if is_session_xsession(session) + ) + + if len(session_files) == 0: + logging.warning("No session files are found.") + sys.exit(0) + else: + return session_files[0] + + +def is_session_xsession(session_file): + return "/xsessions/" in session_file + + +def is_session_wayland(session_file): + return "/wayland-sessions/" in session_file + + +def main(): + parser = argparse.ArgumentParser( + description="Set session type for all normal users." + ) + parser.add_argument("session", help="Name of session to set.") + + args = parser.parse_args() + + session = getattr(args, "session") + session_file = get_session_file(session) + + user_manager = AccountsService.UserManager.get_default() + users = user_manager.list_users() + + for user in users: + if user.is_system_account(): + continue + else: + if is_session_wayland(session_file): + logging.debug( + f"Setting session name: {session}, as we found the existing wayland-session: {session_file}" + ) + user.set_session(session) + user.set_session_type("wayland") + elif is_session_xsession(session_file): + logging.debug( + f"Setting session name: {session}, as we found the existing xsession: {session_file}" + ) + user.set_x_session(session) + user.set_session(session) + user.set_session_type("x11") + else: + logging.error(f"Couldn't figure out session type for {session_file}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix new file mode 100644 index 00000000000..4b0948a5b7a --- /dev/null +++ b/nixos/modules/services/x11/display-managers/slim.nix @@ -0,0 +1,16 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + # added 2019-11-11 + imports = [ + (mkRemovedOptionModule [ "services" "xserver" "displayManager" "slim" ] '' + The SLIM project is abandoned and their last release was in 2013. + Because of this it poses a security risk to your system. + Other issues include it not fully supporting systemd and logind sessions. + Please use a different display manager such as LightDM, SDDM, or GDM. + You can also use the startx module which uses Xinitrc. + '') + ]; +} diff --git a/nixos/modules/services/x11/display-managers/startx.nix b/nixos/modules/services/x11/display-managers/startx.nix new file mode 100644 index 00000000000..a48566ae068 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/startx.nix @@ -0,0 +1,54 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.xserver.displayManager.startx; + +in + +{ + + ###### interface + + options = { + services.xserver.displayManager.startx = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the dummy "startx" pseudo-display manager, + which allows users to start X manually via the "startx" command + from a vt shell. The X server runs under the user's id, not as root. + The user must provide a ~/.xinitrc file containing session startup + commands, see startx(1). This is not automatically generated + from the desktopManager and windowManager settings. + ''; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + services.xserver = { + exportConfiguration = true; + }; + + # Other displayManagers log to /dev/null because they're services and put + # Xorg's stdout in the journal + # + # To send log to Xorg's default log location ($XDG_DATA_HOME/xorg/), we do + # not specify a log file when running X + services.xserver.logFile = mkDefault null; + + # Implement xserverArgs via xinit's system-wide xserverrc + environment.etc."X11/xinit/xserverrc".source = pkgs.writeShellScript "xserverrc" '' + exec ${pkgs.xorg.xorgserver}/bin/X ${toString config.services.xserver.displayManager.xserverArgs} "$@" + ''; + environment.systemPackages = with pkgs; [ xorg.xinit ]; + }; + +} diff --git a/nixos/modules/services/x11/display-managers/sx.nix b/nixos/modules/services/x11/display-managers/sx.nix new file mode 100644 index 00000000000..e3097736430 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/sx.nix @@ -0,0 +1,34 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.services.xserver.displayManager.sx; + +in { + options = { + services.xserver.displayManager.sx = { + enable = mkEnableOption "sx pseudo-display manager" // { + description = '' + Whether to enable the "sx" pseudo-display manager, which allows users + to start manually via the "sx" command from a vt shell. The X server + runs under the user's id, not as root. The user must provide a + ~/.config/sx/sxrc file containing session startup commands, see + sx(1). This is not automatically generated from the desktopManager + and windowManager settings. sx doesn't have a way to directly set + X server flags, but it can be done by overriding its xorgserver + dependency. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.sx ]; + services.xserver = { + exportConfiguration = true; + logFile = mkDefault null; + }; + }; + + meta.maintainers = with maintainers; [ figsoda ]; +} diff --git a/nixos/modules/services/x11/display-managers/xpra.nix b/nixos/modules/services/x11/display-managers/xpra.nix new file mode 100644 index 00000000000..c23e479140f --- /dev/null +++ b/nixos/modules/services/x11/display-managers/xpra.nix @@ -0,0 +1,252 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.xserver.displayManager.xpra; + dmcfg = config.services.xserver.displayManager; + +in + +{ + ###### interface + + options = { + services.xserver.displayManager.xpra = { + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable xpra as display manager."; + }; + + bindTcp = mkOption { + default = "127.0.0.1:10000"; + example = "0.0.0.0:10000"; + type = types.nullOr types.str; + description = "Bind xpra to TCP"; + }; + + auth = mkOption { + type = types.str; + default = "pam"; + example = "password:value=mysecret"; + description = "Authentication to use when connecting to xpra"; + }; + + pulseaudio = mkEnableOption "pulseaudio audio streaming"; + + extraOptions = mkOption { + description = "Extra xpra options"; + default = []; + type = types.listOf types.str; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + services.xserver.videoDrivers = ["dummy"]; + + services.xserver.monitorSection = '' + HorizSync 1.0 - 2000.0 + VertRefresh 1.0 - 200.0 + #To add your own modes here, use a modeline calculator, like: + # cvt: + # http://www.x.org/archive/X11R7.5/doc/man/man1/cvt.1.html + # xtiming: + # http://xtiming.sourceforge.net/cgi-bin/xtiming.pl + # gtf: + # http://gtf.sourceforge.net/ + #This can be used to get a specific DPI, but only for the default resolution: + #DisplaySize 508 317 + #NOTE: the highest modes will not work without increasing the VideoRam + # for the dummy video card. + #Modeline "16000x15000" 300.00 16000 16408 18000 20000 15000 15003 15013 15016 + #Modeline "15000x15000" 281.25 15000 15376 16872 18744 15000 15003 15013 15016 + #Modeline "16384x8192" 167.75 16384 16800 18432 20480 8192 8195 8205 8208 + #Modeline "15360x8640" 249.00 15360 15752 17280 19200 8640 8643 8648 8651 + Modeline "8192x4096" 193.35 8192 8224 8952 8984 4096 4196 4200 4301 + Modeline "7680x4320" 208.00 7680 7880 8640 9600 4320 4323 4328 4335 + Modeline "6400x4096" 151.38 6400 6432 7000 7032 4096 4196 4200 4301 + Modeline "6400x2560" 91.59 6400 6432 6776 6808 2560 2623 2626 2689 + Modeline "6400x2160" 160.51 6400 6432 7040 7072 2160 2212 2216 2269 + Modeline "5760x2160" 149.50 5760 5768 6320 6880 2160 2161 2164 2173 + Modeline "5680x1440" 142.66 5680 5712 6248 6280 1440 1474 1478 1513 + Modeline "5496x1200" 199.13 5496 5528 6280 6312 1200 1228 1233 1261 + Modeline "5280x2560" 75.72 5280 5312 5592 5624 2560 2623 2626 2689 + Modeline "5280x1920" 56.04 5280 5312 5520 5552 1920 1967 1969 2017 + Modeline "5280x1200" 191.40 5280 5312 6032 6064 1200 1228 1233 1261 + Modeline "5280x1080" 169.96 5280 5312 5952 5984 1080 1105 1110 1135 + Modeline "5120x3200" 199.75 5120 5152 5904 5936 3200 3277 3283 3361 + Modeline "5120x2560" 73.45 5120 5152 5424 5456 2560 2623 2626 2689 + Modeline "5120x2880" 185.50 5120 5256 5760 6400 2880 2883 2888 2899 + Modeline "4800x1200" 64.42 4800 4832 5072 5104 1200 1229 1231 1261 + Modeline "4720x3840" 227.86 4720 4752 5616 5648 3840 3933 3940 4033 + Modeline "4400x2560" 133.70 4400 4432 4936 4968 2560 2622 2627 2689 + Modeline "4480x1440" 72.94 4480 4512 4784 4816 1440 1475 1478 1513 + Modeline "4240x1440" 69.09 4240 4272 4528 4560 1440 1475 1478 1513 + Modeline "4160x1440" 67.81 4160 4192 4448 4480 1440 1475 1478 1513 + Modeline "4096x2304" 249.25 4096 4296 4720 5344 2304 2307 2312 2333 + Modeline "4096x2160" 111.25 4096 4200 4608 5120 2160 2163 2173 2176 + Modeline "4000x1660" 170.32 4000 4128 4536 5072 1660 1661 1664 1679 + Modeline "4000x1440" 145.00 4000 4088 4488 4976 1440 1441 1444 1457 + Modeline "3904x1440" 63.70 3904 3936 4176 4208 1440 1475 1478 1513 + Modeline "3840x2880" 133.43 3840 3872 4376 4408 2880 2950 2955 3025 + Modeline "3840x2560" 116.93 3840 3872 4312 4344 2560 2622 2627 2689 + Modeline "3840x2160" 104.25 3840 3944 4320 4800 2160 2163 2168 2175 + Modeline "3840x2048" 91.45 3840 3872 4216 4248 2048 2097 2101 2151 + Modeline "3840x1200" 108.89 3840 3872 4280 4312 1200 1228 1232 1261 + Modeline "3840x1080" 100.38 3840 3848 4216 4592 1080 1081 1084 1093 + Modeline "3864x1050" 94.58 3864 3896 4248 4280 1050 1074 1078 1103 + Modeline "3600x1200" 106.06 3600 3632 3984 4368 1200 1201 1204 1214 + Modeline "3600x1080" 91.02 3600 3632 3976 4008 1080 1105 1109 1135 + Modeline "3520x1196" 99.53 3520 3552 3928 3960 1196 1224 1228 1256 + Modeline "3360x2560" 102.55 3360 3392 3776 3808 2560 2622 2627 2689 + Modeline "3360x1050" 293.75 3360 3576 3928 4496 1050 1053 1063 1089 + Modeline "3288x1080" 39.76 3288 3320 3464 3496 1080 1106 1108 1135 + Modeline "3200x1800" 233.00 3200 3384 3720 4240 1800 1803 1808 1834 + Modeline "3200x1080" 236.16 3200 3232 4128 4160 1080 1103 1112 1135 + Modeline "3120x2560" 95.36 3120 3152 3512 3544 2560 2622 2627 2689 + Modeline "3120x1050" 272.75 3120 3320 3648 4176 1050 1053 1063 1089 + Modeline "3072x2560" 93.92 3072 3104 3456 3488 2560 2622 2627 2689 + Modeline "3008x1692" 130.93 3008 3112 3416 3824 1692 1693 1696 1712 + Modeline "3000x2560" 91.77 3000 3032 3376 3408 2560 2622 2627 2689 + Modeline "2880x1620" 396.25 2880 3096 3408 3936 1620 1623 1628 1679 + Modeline "2728x1680" 148.02 2728 2760 3320 3352 1680 1719 1726 1765 + Modeline "2560x2240" 151.55 2560 2688 2952 3344 2240 2241 2244 2266 + Modeline "2560x1600" 47.12 2560 2592 2768 2800 1600 1639 1642 1681 + Modeline "2560x1440" 42.12 2560 2592 2752 2784 1440 1475 1478 1513 + Modeline "2560x1400" 267.86 2560 2592 3608 3640 1400 1429 1441 1471 + Modeline "2048x2048" 49.47 2048 2080 2264 2296 2048 2097 2101 2151 + Modeline "2048x1536" 80.06 2048 2104 2312 2576 1536 1537 1540 1554 + Modeline "2048x1152" 197.97 2048 2184 2408 2768 1152 1153 1156 1192 + Modeline "2048x1152" 165.92 2048 2080 2704 2736 1152 1176 1186 1210 + Modeline "1920x1440" 69.47 1920 1960 2152 2384 1440 1441 1444 1457 + Modeline "1920x1200" 26.28 1920 1952 2048 2080 1200 1229 1231 1261 + Modeline "1920x1080" 23.53 1920 1952 2040 2072 1080 1106 1108 1135 + Modeline "1728x1520" 205.42 1728 1760 2536 2568 1520 1552 1564 1597 + Modeline "1680x1050" 20.08 1680 1712 1784 1816 1050 1075 1077 1103 + Modeline "1600x1200" 22.04 1600 1632 1712 1744 1200 1229 1231 1261 + Modeline "1600x900" 33.92 1600 1632 1760 1792 900 921 924 946 + Modeline "1440x900" 30.66 1440 1472 1584 1616 900 921 924 946 + Modeline "1400x900" 103.50 1400 1480 1624 1848 900 903 913 934 + ModeLine "1366x768" 72.00 1366 1414 1446 1494 768 771 777 803 + Modeline "1360x768" 24.49 1360 1392 1480 1512 768 786 789 807 + Modeline "1280x1024" 31.50 1280 1312 1424 1456 1024 1048 1052 1076 + Modeline "1280x800" 24.15 1280 1312 1400 1432 800 819 822 841 + Modeline "1280x768" 23.11 1280 1312 1392 1424 768 786 789 807 + Modeline "1280x720" 59.42 1280 1312 1536 1568 720 735 741 757 + Modeline "1024x768" 18.71 1024 1056 1120 1152 768 786 789 807 + Modeline "1024x640" 41.98 1024 1056 1208 1240 640 653 659 673 + Modeline "1024x576" 46.50 1024 1064 1160 1296 576 579 584 599 + Modeline "768x1024" 19.50 768 800 872 904 1024 1048 1052 1076 + Modeline "960x540" 40.75 960 992 1088 1216 540 543 548 562 + Modeline "864x486" 32.50 864 888 968 1072 486 489 494 506 + Modeline "720x405" 22.50 720 744 808 896 405 408 413 422 + Modeline "640x360" 14.75 640 664 720 800 360 363 368 374 + #common resolutions for android devices (both orientations): + Modeline "800x1280" 25.89 800 832 928 960 1280 1310 1315 1345 + Modeline "1280x800" 24.15 1280 1312 1400 1432 800 819 822 841 + Modeline "720x1280" 30.22 720 752 864 896 1280 1309 1315 1345 + Modeline "1280x720" 27.41 1280 1312 1416 1448 720 737 740 757 + Modeline "768x1024" 24.93 768 800 888 920 1024 1047 1052 1076 + Modeline "1024x768" 23.77 1024 1056 1144 1176 768 785 789 807 + Modeline "600x1024" 19.90 600 632 704 736 1024 1047 1052 1076 + Modeline "1024x600" 18.26 1024 1056 1120 1152 600 614 617 631 + Modeline "536x960" 16.74 536 568 624 656 960 982 986 1009 + Modeline "960x536" 15.23 960 992 1048 1080 536 548 551 563 + Modeline "600x800" 15.17 600 632 688 720 800 818 822 841 + Modeline "800x600" 14.50 800 832 880 912 600 614 617 631 + Modeline "480x854" 13.34 480 512 560 592 854 873 877 897 + Modeline "848x480" 12.09 848 880 920 952 480 491 493 505 + Modeline "480x800" 12.43 480 512 552 584 800 818 822 841 + Modeline "800x480" 11.46 800 832 872 904 480 491 493 505 + #resolutions for android devices (both orientations) + #minus the status bar + #38px status bar (and width rounded up) + Modeline "800x1242" 25.03 800 832 920 952 1242 1271 1275 1305 + Modeline "1280x762" 22.93 1280 1312 1392 1424 762 780 783 801 + Modeline "720x1242" 29.20 720 752 856 888 1242 1271 1276 1305 + Modeline "1280x682" 25.85 1280 1312 1408 1440 682 698 701 717 + Modeline "768x986" 23.90 768 800 888 920 986 1009 1013 1036 + Modeline "1024x730" 22.50 1024 1056 1136 1168 730 747 750 767 + Modeline "600x986" 19.07 600 632 704 736 986 1009 1013 1036 + Modeline "1024x562" 17.03 1024 1056 1120 1152 562 575 578 591 + Modeline "536x922" 16.01 536 568 624 656 922 943 947 969 + Modeline "960x498" 14.09 960 992 1040 1072 498 509 511 523 + Modeline "600x762" 14.39 600 632 680 712 762 779 783 801 + Modeline "800x562" 13.52 800 832 880 912 562 575 578 591 + Modeline "480x810" 12.59 480 512 552 584 810 828 832 851 + Modeline "848x442" 11.09 848 880 920 952 442 452 454 465 + Modeline "480x762" 11.79 480 512 552 584 762 779 783 801 + ''; + + services.xserver.resolutions = [ + {x="8192"; y="4096";} + {x="5120"; y="3200";} + {x="3840"; y="2880";} + {x="3840"; y="2560";} + {x="3840"; y="2048";} + {x="3840"; y="2160";} + {x="2048"; y="2048";} + {x="2560"; y="1600";} + {x="1920"; y="1440";} + {x="1920"; y="1200";} + {x="1920"; y="1080";} + {x="1600"; y="1200";} + {x="1680"; y="1050";} + {x="1600"; y="900";} + {x="1400"; y="1050";} + {x="1440"; y="900";} + {x="1280"; y="1024";} + {x="1366"; y="768";} + {x="1280"; y="800";} + {x="1024"; y="768";} + {x="1024"; y="600";} + {x="800"; y="600";} + {x="320"; y="200";} + ]; + + services.xserver.serverFlagsSection = '' + Option "DontVTSwitch" "true" + Option "PciForceNone" "true" + Option "AutoEnableDevices" "false" + Option "AutoAddDevices" "false" + ''; + + services.xserver.deviceSection = '' + VideoRam 192000 + ''; + + services.xserver.displayManager.job.execCmd = '' + ${optionalString (cfg.pulseaudio) + "export PULSE_COOKIE=/run/pulse/.config/pulse/cookie"} + exec ${pkgs.xpra}/bin/xpra start \ + --daemon=off \ + --log-dir=/var/log \ + --log-file=xpra.log \ + --opengl=on \ + --clipboard=on \ + --notifications=on \ + --speaker=yes \ + --mdns=no \ + --pulseaudio=no \ + ${optionalString (cfg.pulseaudio) "--sound-source=pulse"} \ + --socket-dirs=/run/xpra \ + --xvfb="xpra_Xdummy ${concatStringsSep " " dmcfg.xserverArgs}" \ + ${optionalString (cfg.bindTcp != null) "--bind-tcp=${cfg.bindTcp}"} \ + --auth=${cfg.auth} \ + ${concatStringsSep " " cfg.extraOptions} + ''; + + services.xserver.terminateOnReset = false; + + environment.systemPackages = [pkgs.xpra]; + + virtualisation.virtualbox.guest.x11 = false; + hardware.pulseaudio.enable = mkDefault cfg.pulseaudio; + hardware.pulseaudio.systemWide = mkDefault cfg.pulseaudio; + }; + +} |