1{
2 config,
3 lib,
4 pkgs,
5 utils,
6 ...
7}:
8
9let
10 inherit (lib)
11 mkOption
12 types
13 mkDefault
14 mkEnableOption
15 literalExpression
16 ;
17
18 cfg = config.services.xserver.desktopManager.gnome;
19 serviceCfg = config.services.gnome;
20
21 # Prioritize nautilus by default when opening directories
22 mimeAppsList = pkgs.writeTextFile {
23 name = "gnome-mimeapps";
24 destination = "/share/applications/mimeapps.list";
25 text = ''
26 [Default Applications]
27 inode/directory=nautilus.desktop;org.gnome.Nautilus.desktop
28 '';
29 };
30
31 defaultFavoriteAppsOverride = ''
32 [org.gnome.shell]
33 favorite-apps=[ 'org.gnome.Epiphany.desktop', 'org.gnome.Geary.desktop', 'org.gnome.Calendar.desktop', 'org.gnome.Music.desktop', 'org.gnome.Nautilus.desktop' ]
34 '';
35
36 nixos-background-light = pkgs.nixos-artwork.wallpapers.simple-blue;
37 nixos-background-dark = pkgs.nixos-artwork.wallpapers.simple-dark-gray;
38
39 # TODO: Having https://github.com/NixOS/nixpkgs/issues/54150 would supersede this
40 nixos-gsettings-desktop-schemas = pkgs.gnome.nixos-gsettings-overrides.override {
41 inherit (cfg) extraGSettingsOverrides extraGSettingsOverridePackages favoriteAppsOverride;
42 inherit flashbackEnabled nixos-background-dark nixos-background-light;
43 };
44
45 nixos-background-info = pkgs.writeTextFile rec {
46 name = "nixos-background-info";
47 text = ''
48 <?xml version="1.0"?>
49 <!DOCTYPE wallpapers SYSTEM "gnome-wp-list.dtd">
50 <wallpapers>
51 <wallpaper deleted="false">
52 <name>Blobs</name>
53 <filename>${nixos-background-light.gnomeFilePath}</filename>
54 <filename-dark>${nixos-background-dark.gnomeFilePath}</filename-dark>
55 <options>zoom</options>
56 <shade_type>solid</shade_type>
57 <pcolor>#3a4ba0</pcolor>
58 <scolor>#2f302f</scolor>
59 </wallpaper>
60 </wallpapers>
61 '';
62 destination = "/share/gnome-background-properties/nixos.xml";
63 };
64
65 flashbackEnabled = cfg.flashback.enableMetacity || lib.length cfg.flashback.customSessions > 0;
66 flashbackWms =
67 lib.optional cfg.flashback.enableMetacity {
68 wmName = "metacity";
69 wmLabel = "Metacity";
70 wmCommand = "${pkgs.metacity}/bin/metacity";
71 enableGnomePanel = true;
72 }
73 ++ cfg.flashback.customSessions;
74
75 notExcluded =
76 pkg: mkDefault (utils.disablePackageByName pkg config.environment.gnome.excludePackages);
77
78in
79
80{
81
82 meta = {
83 doc = ./gnome.md;
84 maintainers = lib.teams.gnome.members;
85 };
86
87 imports = [
88 (lib.mkRenamedOptionModule
89 [ "services" "gnome" "core-utilities" "enable" ]
90 [ "services" "gnome" "core-apps" "enable" ]
91 )
92 ];
93
94 options = {
95
96 services.gnome = {
97 core-os-services.enable = mkEnableOption "essential services for GNOME3";
98 core-shell.enable = mkEnableOption "GNOME Shell services";
99 core-apps.enable = mkEnableOption "GNOME core apps";
100 core-developer-tools.enable = mkEnableOption "GNOME core developer tools";
101 games.enable = mkEnableOption "GNOME games";
102 };
103
104 services.xserver.desktopManager.gnome = {
105 enable = mkOption {
106 type = types.bool;
107 default = false;
108 description = "Enable GNOME desktop manager.";
109 };
110
111 sessionPath = mkOption {
112 default = [ ];
113 type = types.listOf types.package;
114 example = literalExpression "[ pkgs.gpaste ]";
115 description = ''
116 Additional list of packages to be added to the session search path.
117 Useful for GNOME Shell extensions or GSettings-conditional autostart.
118
119 Note that this should be a last resort; patching the package is preferred (see GPaste).
120 '';
121 };
122
123 favoriteAppsOverride = mkOption {
124 internal = true; # this is messy
125 default = defaultFavoriteAppsOverride;
126 type = types.lines;
127 example = literalExpression ''
128 '''
129 [org.gnome.shell]
130 favorite-apps=[ 'firefox.desktop', 'org.gnome.Calendar.desktop' ]
131 '''
132 '';
133 description = "List of desktop files to put as favorite apps into pkgs.gnome-shell. These need to be installed somehow globally.";
134 };
135
136 extraGSettingsOverrides = mkOption {
137 default = "";
138 type = types.lines;
139 description = "Additional gsettings overrides.";
140 };
141
142 extraGSettingsOverridePackages = mkOption {
143 default = [ ];
144 type = types.listOf types.path;
145 description = "List of packages for which gsettings are overridden.";
146 };
147
148 debug = mkEnableOption "pkgs.gnome-session debug messages";
149
150 flashback = {
151 enableMetacity = mkEnableOption "the standard GNOME Flashback session with Metacity";
152
153 customSessions = mkOption {
154 type = types.listOf (
155 types.submodule {
156 options = {
157 wmName = mkOption {
158 type = types.strMatching "[a-zA-Z0-9_-]+";
159 description = "A unique identifier for the window manager.";
160 example = "xmonad";
161 };
162
163 wmLabel = mkOption {
164 type = types.str;
165 description = "The name of the window manager to show in the session chooser.";
166 example = "XMonad";
167 };
168
169 wmCommand = mkOption {
170 type = types.str;
171 description = "The executable of the window manager to use.";
172 example = literalExpression ''"''${pkgs.haskellPackages.xmonad}/bin/xmonad"'';
173 };
174
175 enableGnomePanel = mkOption {
176 type = types.bool;
177 default = true;
178 example = false;
179 description = "Whether to enable the GNOME panel in this session.";
180 };
181 };
182 }
183 );
184 default = [ ];
185 description = "Other GNOME Flashback sessions to enable.";
186 };
187
188 panelModulePackages = mkOption {
189 default = [ pkgs.gnome-applets ];
190 defaultText = literalExpression "[ pkgs.gnome-applets ]";
191 type = types.listOf types.package;
192 description = ''
193 Packages containing modules that should be made available to `pkgs.gnome-panel` (usually for applets).
194
195 If you're packaging something to use here, please install the modules in `$out/lib/gnome-panel/modules`.
196 '';
197 };
198 };
199 };
200
201 environment.gnome.excludePackages = mkOption {
202 default = [ ];
203 example = literalExpression "[ pkgs.totem ]";
204 type = types.listOf types.package;
205 description = "Which packages gnome should exclude from the default environment";
206 };
207
208 };
209
210 config = lib.mkMerge [
211 (lib.mkIf (cfg.enable || flashbackEnabled) {
212 # Seed our configuration into nixos-generate-config
213 system.nixos-generate-config.desktopConfiguration = [
214 ''
215 # Enable the GNOME Desktop Environment.
216 services.xserver.displayManager.gdm.enable = true;
217 services.xserver.desktopManager.gnome.enable = true;
218 ''
219 ];
220
221 services.gnome.core-os-services.enable = true;
222 services.gnome.core-shell.enable = true;
223 services.gnome.core-apps.enable = mkDefault true;
224
225 services.displayManager.sessionPackages = [ pkgs.gnome-session.sessions ];
226
227 environment.extraInit = ''
228 ${lib.concatMapStrings (p: ''
229 if [ -d "${p}/share/gsettings-schemas/${p.name}" ]; then
230 export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${p}/share/gsettings-schemas/${p.name}
231 fi
232
233 if [ -d "${p}/lib/girepository-1.0" ]; then
234 export GI_TYPELIB_PATH=$GI_TYPELIB_PATH''${GI_TYPELIB_PATH:+:}${p}/lib/girepository-1.0
235 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH''${LD_LIBRARY_PATH:+:}${p}/lib
236 fi
237 '') cfg.sessionPath}
238 '';
239
240 environment.systemPackages = cfg.sessionPath;
241
242 environment.sessionVariables.GNOME_SESSION_DEBUG = lib.mkIf cfg.debug "1";
243
244 # Override GSettings schemas
245 environment.sessionVariables.NIX_GSETTINGS_OVERRIDES_DIR = "${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas";
246 })
247
248 (lib.mkIf flashbackEnabled {
249 services.displayManager.sessionPackages =
250 let
251 wmNames = map (wm: wm.wmName) flashbackWms;
252 namesAreUnique = lib.unique wmNames == wmNames;
253 in
254 assert (lib.assertMsg namesAreUnique "Flashback WM names must be unique.");
255 map (
256 wm:
257 pkgs.gnome-flashback.mkSessionForWm {
258 inherit (wm) wmName wmLabel wmCommand;
259 }
260 ) flashbackWms;
261
262 security.pam.services.gnome-flashback = {
263 enableGnomeKeyring = true;
264 };
265
266 systemd.packages = [
267 pkgs.gnome-flashback
268 ] ++ map pkgs.gnome-flashback.mkSystemdTargetForWm flashbackWms;
269
270 environment.systemPackages =
271 [
272 pkgs.gnome-flashback
273 (pkgs.gnome-panel-with-modules.override {
274 panelModulePackages = cfg.flashback.panelModulePackages;
275 })
276 ]
277 # For /share/applications/${wmName}.desktop
278 ++ (map (
279 wm: pkgs.gnome-flashback.mkWmApplication { inherit (wm) wmName wmLabel wmCommand; }
280 ) flashbackWms)
281 # For /share/pkgs.gnome-session/sessions/gnome-flashback-${wmName}.session
282 ++ (map (
283 wm: pkgs.gnome-flashback.mkGnomeSession { inherit (wm) wmName wmLabel enableGnomePanel; }
284 ) flashbackWms);
285 })
286
287 (lib.mkIf serviceCfg.core-os-services.enable {
288 hardware.bluetooth.enable = mkDefault true;
289 programs.dconf.enable = true;
290 security.polkit.enable = true;
291 security.rtkit.enable = mkDefault true;
292 services.accounts-daemon.enable = true;
293 services.dleyna.enable = mkDefault true;
294 services.power-profiles-daemon.enable = mkDefault true;
295 services.gnome.at-spi2-core.enable = true;
296 services.gnome.evolution-data-server.enable = true;
297 services.gnome.gnome-keyring.enable = true;
298 services.gnome.gnome-online-accounts.enable = mkDefault true;
299 services.gnome.localsearch.enable = mkDefault true;
300 services.gnome.tinysparql.enable = mkDefault true;
301 services.hardware.bolt.enable = mkDefault true;
302 # TODO: Enable once #177946 is resolved
303 # services.packagekit.enable = mkDefault true;
304 services.udisks2.enable = true;
305 services.upower.enable = config.powerManagement.enable;
306 services.libinput.enable = mkDefault true; # for controlling touchpad settings via gnome control center
307
308 # Explicitly enabled since GNOME will be severely broken without these.
309 xdg.mime.enable = true;
310 xdg.icons.enable = true;
311
312 xdg.portal.enable = true;
313 xdg.portal.extraPortals = [
314 pkgs.xdg-desktop-portal-gnome
315 pkgs.xdg-desktop-portal-gtk
316 ];
317 xdg.portal.configPackages = mkDefault [ pkgs.gnome-session ];
318
319 networking.networkmanager.enable = mkDefault true;
320
321 services.xserver.updateDbusEnvironment = true;
322
323 # gnome has a custom alert theme but it still
324 # inherits from the freedesktop theme.
325 environment.systemPackages = with pkgs; [
326 sound-theme-freedesktop
327 ];
328
329 # Needed for themes and backgrounds
330 environment.pathsToLink = [
331 "/share" # TODO: https://github.com/NixOS/nixpkgs/issues/47173
332 ];
333 })
334
335 (lib.mkIf serviceCfg.core-shell.enable {
336 services.xserver.desktopManager.gnome.sessionPath = [
337 pkgs.gnome-shell
338 ];
339
340 services.colord.enable = mkDefault true;
341 services.gnome.glib-networking.enable = true;
342 services.gnome.gnome-browser-connector.enable = mkDefault true;
343 services.gnome.gnome-initial-setup.enable = mkDefault true;
344 services.gnome.gnome-remote-desktop.enable = mkDefault true;
345 services.gnome.gnome-settings-daemon.enable = true;
346 services.gnome.gnome-user-share.enable = mkDefault true;
347 services.gnome.rygel.enable = mkDefault true;
348 services.gvfs.enable = true;
349 services.system-config-printer.enable = (lib.mkIf config.services.printing.enable (mkDefault true));
350
351 systemd.packages = [
352 pkgs.gnome-session
353 pkgs.gnome-shell
354 ];
355
356 services.udev.packages = [
357 # Force enable KMS modifiers for devices that require them.
358 # https://gitlab.gnome.org/GNOME/pkgs.mutter/-/merge_requests/1443
359 pkgs.mutter
360 ];
361
362 services.avahi.enable = mkDefault true;
363
364 services.geoclue2.enable = mkDefault true;
365 services.geoclue2.enableDemoAgent = false; # GNOME has its own geoclue agent
366
367 services.geoclue2.appConfig.gnome-datetime-panel = {
368 isAllowed = true;
369 isSystem = true;
370 };
371 services.geoclue2.appConfig.gnome-color-panel = {
372 isAllowed = true;
373 isSystem = true;
374 };
375 services.geoclue2.appConfig."org.gnome.Shell" = {
376 isAllowed = true;
377 isSystem = true;
378 };
379
380 services.orca.enable = notExcluded pkgs.orca;
381
382 fonts.packages = utils.removePackagesByName [
383 pkgs.adwaita-fonts
384 ] config.environment.gnome.excludePackages;
385
386 # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/blob/gnome-48/elements/core/meta-gnome-core-shell.bst
387 environment.systemPackages =
388 let
389 mandatoryPackages = [
390 pkgs.gnome-shell
391 ];
392 optionalPackages = [
393 pkgs.adwaita-icon-theme
394 nixos-background-info
395 pkgs.gnome-backgrounds
396 pkgs.gnome-bluetooth
397 pkgs.gnome-color-manager
398 pkgs.gnome-control-center
399 pkgs.gnome-tour # GNOME Shell detects the .desktop file on first log-in.
400 pkgs.gnome-user-docs
401 pkgs.glib # for gsettings program
402 pkgs.gnome-menus
403 pkgs.gtk3.out # for gtk-launch program
404 pkgs.xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/
405 pkgs.xdg-user-dirs-gtk # Used to create the default bookmarks
406 ];
407 in
408 mandatoryPackages
409 ++ utils.removePackagesByName optionalPackages config.environment.gnome.excludePackages;
410 })
411
412 # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/-/blob/gnome-48/elements/core/meta-gnome-core-apps.bst
413 (lib.mkIf serviceCfg.core-apps.enable {
414 environment.systemPackages = utils.removePackagesByName (
415 [
416 pkgs.baobab
417 pkgs.decibels
418 pkgs.epiphany
419 pkgs.gnome-text-editor
420 pkgs.gnome-calculator
421 pkgs.gnome-calendar
422 pkgs.gnome-characters
423 pkgs.gnome-clocks
424 pkgs.gnome-console
425 pkgs.gnome-contacts
426 pkgs.gnome-font-viewer
427 pkgs.gnome-logs
428 pkgs.gnome-maps
429 pkgs.gnome-music
430 pkgs.gnome-system-monitor
431 pkgs.gnome-weather
432 pkgs.loupe
433 pkgs.nautilus
434 pkgs.gnome-connections
435 pkgs.simple-scan
436 pkgs.snapshot
437 pkgs.totem
438 pkgs.yelp
439 ]
440 ++ lib.optionals config.services.flatpak.enable [
441 # Since PackageKit Nix support is not there yet,
442 # only install gnome-software if flatpak is enabled.
443 pkgs.gnome-software
444 ]
445 ) config.environment.gnome.excludePackages;
446
447 # Enable default program modules
448 # Since some of these have a corresponding package, we only
449 # enable that program module if the package hasn't been excluded
450 # through `environment.gnome.excludePackages`
451 programs.evince.enable = notExcluded pkgs.evince;
452 programs.file-roller.enable = notExcluded pkgs.file-roller;
453 programs.geary.enable = notExcluded pkgs.geary;
454 programs.gnome-disks.enable = notExcluded pkgs.gnome-disk-utility;
455 programs.seahorse.enable = notExcluded pkgs.seahorse;
456 services.gnome.sushi.enable = notExcluded pkgs.sushi;
457
458 # VTE shell integration for gnome-console
459 programs.bash.vteIntegration = mkDefault true;
460 programs.zsh.vteIntegration = mkDefault true;
461
462 # Let nautilus find extensions
463 # TODO: Create nautilus-with-extensions package
464 environment.sessionVariables.NAUTILUS_4_EXTENSION_DIR = "${config.system.path}/lib/nautilus/extensions-4";
465
466 # Override default mimeapps for nautilus
467 environment.sessionVariables.XDG_DATA_DIRS = [ "${mimeAppsList}/share" ];
468
469 environment.pathsToLink = [
470 "/share/nautilus-python/extensions"
471 ];
472 })
473
474 (lib.mkIf serviceCfg.games.enable {
475 environment.systemPackages = utils.removePackagesByName [
476 pkgs.aisleriot
477 pkgs.atomix
478 pkgs.five-or-more
479 pkgs.four-in-a-row
480 pkgs.gnome-2048
481 pkgs.gnome-chess
482 pkgs.gnome-klotski
483 pkgs.gnome-mahjongg
484 pkgs.gnome-mines
485 pkgs.gnome-nibbles
486 pkgs.gnome-robots
487 pkgs.gnome-sudoku
488 pkgs.gnome-taquin
489 pkgs.gnome-tetravex
490 pkgs.hitori
491 pkgs.iagno
492 pkgs.lightsoff
493 pkgs.quadrapassel
494 pkgs.swell-foop
495 pkgs.tali
496 ] config.environment.gnome.excludePackages;
497 })
498
499 # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/-/blob/gnome-48/elements/core/meta-gnome-core-developer-tools.bst
500 (lib.mkIf serviceCfg.core-developer-tools.enable {
501 environment.systemPackages = utils.removePackagesByName [
502 pkgs.dconf-editor
503 pkgs.devhelp
504 pkgs.d-spy
505 pkgs.gnome-builder
506 # boxes would make sense in this option, however
507 # it doesn't function well enough to be included
508 # in default configurations.
509 # https://github.com/NixOS/nixpkgs/issues/60908
510 # pkgs.gnome-boxes
511 pkgs.sysprof
512 ] config.environment.gnome.excludePackages;
513
514 services.sysprof.enable = notExcluded pkgs.sysprof;
515 })
516 ];
517
518}