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