Unverified Commit 6be2bfcc authored by Sandro Jäckel's avatar Sandro Jäckel
Browse files

nixos/display-managers: extract generic display-manager bits

Some settings which where before inside the xserver module, are generic
and also required for SDDM under wayland. To make them easily re-usable
lets extract them.
parent f955c923
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -508,6 +508,7 @@
  ./services/development/nixseparatedebuginfod.nix
  ./services/development/rstudio-server/default.nix
  ./services/development/zammad.nix
  ./services/display-managers/default.nix
  ./services/display-managers/greetd.nix
  ./services/editors/emacs.nix
  ./services/editors/haste.nix
+257 −0
Original line number Diff line number Diff line
{ config, lib, pkgs, ... }:

let
  cfg = config.services.displayManager;

  installedSessions = pkgs.runCommand "desktops"
    { # trivial derivation
      preferLocalBuild = true;
      allowSubstitutes = false;
    }
    ''
      mkdir -p "$out/share/"{xsessions,wayland-sessions}

      ${lib.concatMapStrings (pkg: ''
        for n in ${lib.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.sessionPackages}
    '';

  dmDefault = config.services.xserver.desktopManager.default;
  # fallback default for cases when only default wm is set
  dmFallbackDefault = if dmDefault != null then dmDefault else "none";
  wmDefault = config.services.xserver.windowManager.default;
  defaultSessionFromLegacyOptions = dmFallbackDefault + lib.optionalString (wmDefault != null && wmDefault != "none") "+${wmDefault}";
in
{
  options = {
    services.displayManager = {
      enable = lib.mkEnableOption "systemd's display-manager service";

      preStart = lib.mkOption {
        type = lib.types.lines;
        default = "";
        example = "rm -f /var/log/my-display-manager.log";
        description = lib.mdDoc "Script executed before the display manager is started.";
      };

      execCmd = lib.mkOption {
        type = lib.types.str;
        example = lib.literalExpression ''"''${pkgs.lightdm}/bin/lightdm"'';
        description = lib.mdDoc "Command to start the display manager.";
      };

      environment = lib.mkOption {
        type = with lib.types; attrsOf unspecified;
        default = {};
        description = lib.mdDoc "Additional environment variables needed by the display manager.";
      };

      hiddenUsers = lib.mkOption {
        type = with lib.types; listOf str;
        default = [ "nobody" ];
        description = lib.mdDoc ''
          A list of users which will not be shown in the display manager.
        '';
      };

      logToFile = lib.mkOption {
        type = lib.types.bool;
        default = false;
        description = lib.mdDoc ''
          Whether the display manager redirects the output of the
          session script to {file}`~/.xsession-errors`.
        '';
      };

      logToJournal = lib.mkOption {
        type = lib.types.bool;
        default = true;
        description = lib.mdDoc ''
          Whether the display manager redirects the output of the
          session script to the systemd journal.
        '';
      };

      # Configuration for automatic login. Common for all DM.
      autoLogin = lib.mkOption {
        type = lib.types.submodule ({ config, options, ... }: {
          options = {
            enable = lib.mkOption {
              type = lib.types.bool;
              default = config.user != null;
              defaultText = lib.literalExpression "config.${options.user} != null";
              description = lib.mdDoc ''
                Automatically log in as {option}`autoLogin.user`.
              '';
            };

            user = lib.mkOption {
              type = with lib.types; nullOr str;
              default = null;
              description = lib.mdDoc ''
                User to be used for the automatic login.
              '';
            };
          };
        });

        default = {};
        description = lib.mdDoc ''
          Auto login configuration attrset.
        '';
      };

      defaultSession = lib.mkOption {
        type = lib.types.nullOr lib.types.str // {
          description = "session name";
          check = d:
            lib.assertMsg (d != null -> (lib.types.str.check d && lib.elem d config.services.displayManager.sessionData.sessionNames)) ''
                Default graphical session, '${d}', not found.
                Valid names for 'services.xserver.displayManager.defaultSession' are:
                  ${lib.concatStringsSep "\n  " cfg.displayManager.sessionData.sessionNames}
              '';
        };
        default =
          if dmDefault != null || wmDefault != null then
            defaultSessionFromLegacyOptions
          else
            null;
        defaultText = lib.literalMD ''
          Taken from display manager settings or window manager settings, if either is set.
        '';
        example = "gnome";
        description = lib.mdDoc ''
          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.
        '';
      };

      sessionData = lib.mkOption {
        description = lib.mdDoc "Data exported for display managers’ convenience";
        internal = true;
        default = {};
      };

      sessionPackages = lib.mkOption {
        type = lib.types.listOf (lib.types.package // {
          description = "package with provided sessions";
          check = p: lib.assertMsg
            (lib.types.package.check p && p ? providedSessions
            && p.providedSessions != [] && lib.all lib.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 = lib.mdDoc ''
          A list of packages containing x11 or wayland session files to be passed to the display manager.
        '';
      };
    };
  };

  imports = [
    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "autoLogin" ] [ "services" "displayManager" "autoLogin" ])
    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "defaultSession" ] [ "services" "displayManager" "defaultSession" ])
    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "environment" ] [ "services" "displayManager" "environment" ])
    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "execCmd" ] [ "services" "displayManager" "execCmd" ])
    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logToFile" ] [ "services" "displayManager" "logToFile" ])
    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logToJournal" ] [ "services" "displayManager" "logToJournal" ])
    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "preStart" ] [ "services" "displayManager" "preStart" ])
  ];

  config = lib.mkIf cfg.enable {
    assertions = [
      { assertion = cfg.autoLogin.enable -> cfg.autoLogin.user != null;
        message = ''
          services.displayManager.autoLogin.enable requires services.displayManager.autoLogin.user to be set
        '';
      }
    ];

    warnings =
      lib.mkIf (dmDefault != null || wmDefault != null) [
        ''
          The following options are deprecated:
            ${lib.concatStringsSep "\n  " (map ({c, t}: t) (lib.filter ({c, t}: c != null) [
            { c = dmDefault; t = "- services.xserver.desktopManager.default"; }
            { c = wmDefault; t = "- services.xserver.windowManager.default"; }
            ]))}
          Please use
            services.displayManager.defaultSession = "${defaultSessionFromLegacyOptions}";
          instead.
        ''
      ];

    # 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 = lib.mkIf (cfg.sessionPackages != [ ]) [
      "${cfg.sessionData.desktops}/share"
    ];

    services.displayManager.sessionData = {
      desktops = installedSessions;
      sessionNames = lib.concatMap (p: p.providedSessions) config.services.displayManager.sessionPackages;
      # We do not want to force users to set defaultSession when they have only single DE.
      autologinSession =
        if cfg.defaultSession != null then
          cfg.defaultSession
        else if cfg.sessionData.sessionNames != [] then
          lib.head cfg.sessionData.sessionNames
        else
          null;
    };

    # so that the service won't be enabled when only startx is used
    systemd.services.display-manager.enable =
      let dmConf = config.services.xserver.displayManager;
          noDmUsed = !(dmConf.gdm.enable
                    || dmConf.sddm.enable
                    || dmConf.xpra.enable
                    || dmConf.lightdm.enable);
      in lib.mkIf noDmUsed (lib.mkDefault false);

    systemd.services.display-manager = {
      description = "Display Manager";
      after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ];
      restartIfChanged = false;

      environment = lib.optionalAttrs config.hardware.opengl.setLdLibraryPath {
        LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.addOpenGLRunpath.driverLink ];
      } // cfg.environment;

      preStart = cfg.preStart;
      script = lib.mkIf (config.systemd.services.display-manager.enable == true) cfg.execCmd;

      # Stop restarting if the display manager stops (crashes) 2 times
      # in one minute. Starting X typically takes 3-4s.
      startLimitIntervalSec = 30;
      startLimitBurst = 3;
      serviceConfig = {
        Restart = "always";
        RestartSec = "200ms";
        SyslogIdentifier = "display-manager";
      };
    };
  };
}
+5 −208
Original line number Diff line number Diff line
@@ -14,7 +14,6 @@ with lib;
let

  cfg = config.services.xserver;
  opt = options.services.xserver;
  xorg = pkgs.xorg;

  fontconfig = config.fonts.fontconfig;
@@ -130,41 +129,6 @@ let
          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

{
@@ -215,35 +179,6 @@ in
        '';
      };

      hiddenUsers = mkOption {
        type = types.listOf types.str;
        default = [ "nobody" ];
        description = lib.mdDoc ''
          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 = lib.mdDoc ''
          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;
@@ -274,51 +209,6 @@ in
        '';
      };

      sessionData = mkOption {
        description = lib.mdDoc "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 = literalMD ''
          Taken from display manager settings or window manager settings, if either is set.
        '';
        example = "gnome";
        description = lib.mdDoc ''
          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;
@@ -327,106 +217,19 @@ in
        '';
      };

      job = {

        preStart = mkOption {
          type = types.lines;
          default = "";
          example = "rm -f /var/log/my-display-manager.log";
          description = lib.mdDoc "Script executed before the display manager is started.";
        };

        execCmd = mkOption {
          type = types.str;
          example = literalExpression ''"''${pkgs.lightdm}/bin/lightdm"'';
          description = lib.mdDoc "Command to start the display manager.";
        };

        environment = mkOption {
          type = types.attrsOf types.unspecified;
          default = {};
          description = lib.mdDoc "Additional environment variables needed by the display manager.";
        };

        logToFile = mkOption {
          type = types.bool;
          default = false;
          description = lib.mdDoc ''
            Whether the display manager redirects the output of the
            session script to {file}`~/.xsession-errors`.
          '';
        };

        logToJournal = mkOption {
          type = types.bool;
          default = true;
          description = lib.mdDoc ''
            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 = lib.mdDoc ''
                Automatically log in as {option}`autoLogin.user`.
              '';
            };

            user = mkOption {
              type = types.nullOr types.str;
              default = null;
              description = lib.mdDoc ''
                User to be used for the automatic login.
              '';
            };
          };
        });

        default = {};
        description = lib.mdDoc ''
          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.displayManager.sessionData.wrapper = xsessionWrapper;

    services.xserver.displayManager.xserverBin = "${xorg.xorgserver.out}/bin/X";

@@ -449,7 +252,7 @@ in

    # 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 =
    services.displayManager.sessionPackages =
      let
        dms = filter (s: s.manage == "desktop") cfg.displayManager.session;
        wms = filter (s: s.manage == "window") cfg.displayManager.session;
@@ -511,20 +314,14 @@ in
            )
            (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 = lib.mkIf (cfg.displayManager.sessionPackages != [ ]) [
      "${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" ])
    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logsXsession" ] [ "services" "displayManager" "logToFile" ])
    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "logToJournal" ] [ "services" "displayManager" "logToJournal" ])
    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "extraSessionFilesPackages" ] [ "services" "displayManager" "sessionPackages" ])
  ];

}
+10 −8
Original line number Diff line number Diff line
@@ -129,17 +129,17 @@ in
{
  imports = [
    (mkRemovedOptionModule
      [ "services" "xserver" "displayManager" "sddm" "themes" ]
      "Set the option `services.xserver.displayManager.sddm.package' instead.")
      [ "services" "displayManager" "sddm" "themes" ]
      "Set the option `services.displayManager.sddm.package' instead.")
    (mkRenamedOptionModule
      [ "services" "xserver" "displayManager" "sddm" "autoLogin" "enable" ]
      [ "services" "xserver" "displayManager" "autoLogin" "enable" ])
      [ "services" "displayManager" "sddm" "autoLogin" "enable" ]
      [ "services" "displayManager" "autoLogin" "enable" ])
    (mkRenamedOptionModule
      [ "services" "xserver" "displayManager" "sddm" "autoLogin" "user" ]
      [ "services" "xserver" "displayManager" "autoLogin" "user" ])
      [ "services" "displayManager" "sddm" "autoLogin" "user" ]
      [ "services" "displayManager" "autoLogin" "user" ])
    (mkRemovedOptionModule
      [ "services" "xserver" "displayManager" "sddm" "extraConfig" ]
      "Set the option `services.xserver.displayManager.sddm.settings' instead.")
      [ "services" "displayManager" "sddm" "extraConfig" ]
      "Set the option `services.displayManager.sddm.settings' instead.")
  ];

  options = {
@@ -281,6 +281,8 @@ in
      }
    ];

    services.displayManager.execCmd = "exec /run/current-system/sw/bin/sddm";

    security.pam.services = {
      sddm.text = ''
        auth      substack      login
+1 −9
Original line number Diff line number Diff line
@@ -639,6 +639,7 @@ in
  ###### implementation

  config = mkIf cfg.enable {
    services.displayManager.enable = true;

    services.xserver.displayManager.lightdm.enable =
      let dmConf = cfg.displayManager;
@@ -650,15 +651,6 @@ in
                    || config.services.greetd.enable);
      in mkIf (default) (mkDefault true);

    # so that the service won't be enabled when only startx is used
    systemd.services.display-manager.enable  =
      let dmConf = cfg.displayManager;
          noDmUsed = !(dmConf.gdm.enable
                    || dmConf.sddm.enable
                    || dmConf.xpra.enable
                    || dmConf.lightdm.enable);
      in mkIf (noDmUsed) (mkDefault false);

    hardware.opengl.enable = mkDefault true;

    services.xserver.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ];