{ config, pkgs, lib, utils, ... }: let cfg = config.wm.gnome; settingsFormat = pkgs.formats.ini { }; configFile = settingsFormat.generate "custom.conf" cfg.gdm.settings; mimeAppsList = pkgs.writeTextFile { name = "gnome-mimeapps"; destination = "/share/applications/mimeapps.list"; text = '' [Default Applications] inode/directory=nautilus.desktop;org.gnome.Nautilus.desktop ''; }; nixos-background-info = pkgs.writeTextFile rec { name = "nixos-background-info"; destination = "/share/gnome-background-properties/nixos.xml"; text = '' Blobs ${pkgs.nixos-artwork.wallpapers.simple-blue.gnomeFilePath} ${pkgs.nixos-artwork.wallpapers.simple-dark-gray.gnomeFilePath} zoom solid #3a4ba0 #2f302f ''; }; in { options = { wm.gnome = { enable = lib.mkEnableOption "gnome"; gdm = { dconfSettings = lib.mkOption { type = lib.types.attrs; default = { }; }; settings = lib.mkOption { type = settingsFormat.type; default = { }; }; }; }; }; config = lib.mkIf cfg.enable { # patched to remove xorg and xwayland completely nixpkgs.overlays = [(final: prev: { mutter = prev.mutter.overrideAttrs( prevAttrs: { mesonFlags = [ "-Dinstalled_tests=false" "-Dtests=disabled" "-Ddocs=true" "-Degl_device=true" "-Dwayland_eglstream=true" "-Dprofiler=true" "-Dsm=false" "-Dx11=false" "-Dxwayland=false" "-Dwayland=true" ]; buildInputs = (utils.removePackagesByName prevAttrs.buildInputs [ prev.xorg.libSM prev.xwayland prev.gtk4 prev.xorg.libICE prev.xorg.libX11 prev.xorg.libXcomposite prev.xorg.libXcursor prev.xorg.libXdamage prev.xorg.libXext prev.xorg.libXfixes prev.xorg.libXi prev.xorg.libXtst prev.xorg.libxkbfile prev.xkeyboard_config prev.xorg.libxcb prev.xorg.libXrandr prev.xorg.libXinerama prev.xorg.libXau ]) ++ [ prev.libGL ]; nativeBuildInputs = utils.removePackagesByName prevAttrs.nativeBuildInputs [ prev.xorg.xorgserver ]; }); gdm = prev.gdm.overrideAttrs( prevAttrs: { mesonFlags = prev.lib.lists.remove "--Dgdm-xsession=true" (prevAttrs.mesonFlags ++ [ "-Dgdm-xsession=false" "-Dx11-support=false" ]); patches = [ # GDM fails to find g-s with the following error in the journal. # gdm-x-session[976]: dbus-run-session: failed to exec 'gnome-session': No such file or directory # https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/92 (prev.fetchpatch { url = "https://gitlab.gnome.org/GNOME/gdm/-/commit/ccecd9c975d04da80db4cd547b67a1a94fa83292.patch"; hash = "sha256-5hKS9wjjhuSAYwXct5vS0dPbmPRIINJoLC0Zm1naz6Q="; revert = true; }) ../../packages/patches/gdm-fix-wayland.patch # Change hardcoded paths to nix store paths. (prev.substituteAll { src = ../../packages/patches/gdm-fix-paths.patch; coreutils = final.coreutils; plymouth = final.plymouth; dbus = final.dbus; }) ]; postPatch = '' # Reverts https://gitlab.gnome.org/GNOME/gdm/-/commit/b0f802e36ff948a415bfd2bccaa268b6990515b7 # The gdm-auth-config tool is probably not too useful for NixOS, but we still want the dconf profile # installed (mostly just because .passthru.tests can make use of it). substituteInPlace meson.build \ --replace-fail "dconf_prefix = dconf_dep.get_variable(pkgconfig: 'prefix')" "dconf_prefix = gdm_prefix" ''; buildInputs = utils.removePackagesByName prevAttrs.buildInputs [ prev.xorg.libX11 prev.xorg.libXdmcp prev.xorg.libxcb ]; }); gnome-session = prev.gnome-session.overrideAttrs( prevAttrs: { mesonFlags = [ "-Dx11=false" ]; buildInputs = utils.removePackagesByName prevAttrs.buildInputs [ prev.xorg.libICE prev.xorg.xtrans ]; }); })]; users.groups.gdm.gid = config.ids.gids.gdm; users.users.gdm = { name = "gdm"; uid = config.ids.uids.gdm; group = "gdm"; home = "/run/gdm"; description = "GDM user"; }; security.polkit.enable = true; networking.networkmanager.enable = lib.mkDefault true; hardware = { graphics.enable = true; bluetooth.enable = lib.mkDefault true; }; fonts.packages = with pkgs; [ cantarell-fonts dejavu_fonts source-code-pro source-sans ]; environment = { etc."gdm/custom.conf".source = configFile; systemPackages = with pkgs; [ gnome-shell (lib.mkIf config.hardware.bluetooth.enable gnome-bluetooth) (lib.mkIf config.services.colord.enable gnome-color-manager) gnome-control-center ghostty nautilus sushi #quick-preview for nautilus adwaita-icon-theme sound-theme-freedesktop nixos-icons nixos-background-info glib # for gsettings program gnome-menus gtk3.out # for gtk-launch program xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/ xdg-user-dirs-gtk ]; # Needed for themes and backgrounds pathsToLink = [ "/share" # TODO: https://github.com/NixOS/nixpkgs/issues/47173 "/share/nautilus-python/extensions" ]; sessionVariables = { # Let nautilus find extensions NAUTILUS_4_EXTENSION_DIR = "${config.system.path}/lib/nautilus/extensions-4"; # Override default mimeapps for nautilus XDG_DATA_DIRS = [ "${mimeAppsList}/share" ]; }; }; services = { gvfs.enable = true; udisks2.enable = true; libinput.enable = true; accounts-daemon.enable = true; pipewire.enable = lib.mkDefault true; hardware.bolt.enable = lib.mkDefault true; colord.enable = lib.mkDefault true; power-profiles-daemon.enable = lib.mkDefault true; upower.enable = config.powerManagement.enable; system-config-printer.enable = (lib.mkIf config.services.printing.enable (lib.mkDefault true)); gnome = { glib-networking.enable = true; gnome-settings-daemon.enable = true; at-spi2-core.enable = lib.mkDefault true; gnome-keyring.enable = lib.mkDefault true; }; geoclue2 = { enable = lib.mkDefault true; enableDemoAgent = false; # GNOME has its own geoclue agent appConfig = { gnome-datetime-panel = { isAllowed = true; isSystem = true; }; gnome-color-panel = { isAllowed = true; isSystem = true; }; "org.gnome.Shell" = { isAllowed = true; isSystem = true; }; }; }; udev.packages = [ pkgs.mutter ]; dbus.packages = with pkgs; [ gdm sushi ]; }; programs = { dconf.enable = true; dconf.profiles.gdm.databases = [ { settings = cfg.gdm.dconfSettings; } "${pkgs.gdm}/share/gdm/greeter-dconf-defaults" ]; }; xdg = { mime.enable = true; icons.enable = true; portal = { enable = true; configPackages = lib.mkDefault [ pkgs.gnome-session ]; extraPortals = with pkgs; [ xdg-desktop-portal-gnome xdg-desktop-portal-gtk ]; }; }; systemd = { packages = with pkgs; [ gdm gnome-session gnome-shell ]; tmpfiles.rules = [ "d /run/gdm/.config 0711 gdm gdm" ]; user.services.dbus.wantedBy = [ "default.target" ]; services = { # 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 gdm.enable = false; display-manager = { description = "Display Manager"; wants = [ "systemd-machined.service" "accounts-daemon.service" ]; conflicts = [ "getty@${pkgs.gdm.initialVT}.service" "plymouth-quit.service" ]; onFailure = [ "plymouth-quit.service" ]; wantedBy = [ "multi-user.target" ]; after = [ "systemd-logind.service" "systemd-user-sessions.service" "systemd-machined.service" "getty@${pkgs.gdm.initialVT}.service" "acpid.service" "plymouth-quit.service" "plymouth-start.service" ]; path = [ pkgs.gnome-session ]; environment = { XDG_DATA_DIRS = lib.makeSearchPath "share" (with pkgs; [ gdm gnome-session.sessions gnome-control-center # for accessibility icon adwaita-icon-theme hicolor-icon-theme ]); }; serviceConfig = { KillMode = "mixed"; IgnoreSIGPIPE = "no"; BusName = "org.gnome.DisplayManager"; StandardError = "inherit"; ExecStart = "${pkgs.gdm}/bin/gdm"; ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID"; KeyringMode = "shared"; EnvironmentFile = "-/etc/locale.conf"; Restart = "always"; RestartSec = "200ms"; SyslogIdentifier = "display-manager"; }; restartIfChanged = false; # Stop restarting if the display manager stops (crashes) 2 times in one minute. startLimitIntervalSec = 30; startLimitBurst = 3; }; # 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.) plymouth-quit = lib.mkIf config.boot.plymouth.enable { wantedBy = lib.mkForce []; }; }; }; # 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 ${config.systemd.package}/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 ''; }; }; }