Lobotomizing GNOME

I have pretty basic needs from my desktop. I do 99% of my work using just three programs: a terminal emulator, Emacs, and Firefox. I don't want a lot of bells and whistles in my desktop, and I really just want it to get out of the way so I can do my work.

But just because my needs are basic doesn't mean that I want a 1990's window manager experience. I want good text rendering and windows and buttons with rounded corners. I want my laptop to work correctly when connecting it to external displays or projectors without a lot of futzing around. I want vsync to work with my monitor out of the box, I want to be able to watch video without tearing, and I want a desktop that has first class support for high-DPI displays. I also want to have some basic integration with the other system features provided by my distro, which increasingly means high-quality integration with NetworkManager and different systemd components. I want to get integrated notifications when a program segfaults on my computer or in case there's an SELinux AVC denial.

In my opinion, in these areas GNOME is far ahead of everything else. The stock GNOME configuration is beautiful but minimal---I get a discreet black bar across the top of my screen that shows the time and date, and lets me control the fundamentals like network access. I think that GNOME Shell is the most attractive and useful window manager for any operating system out there. And GNOME has really good integration with the other parts of my system, which makes sense because it's the default desktop environment on my distro (Fedora) and most others, including Debian and Ubuntu. GNOME is also light-years ahead of everything else in terms of Wayland support. Fedora has been shipping Wayland as the default GNOME display backend since Fedora 25 (2016), and it works incredibly well. The most compelling user-visible feature that has come out of this is GNOME's "fractional scaling" feature, which is a quantum leap in terms of how content is scaled on high-DPI screens.

But I'll be honest: GNOME is huge and kind of bloated, and it's hard to disable various unwanted components. GNOME Shell is amazing, but a lot of the other components of GNOME are simply unwanted. This is what turns a lot of power users away from GNOME, which I think is a shame given all of the other amazing things about GNOME. While you won't find these instructions in the GNOME manuals, if you know what you're doing modern GNOME releases make it very easy to lobotomize a lot of the unneeded and unwanted features.

Configuration With dconf

The first step in improving the GNOME user experience is toggling basic features on or off. You can do this either by clicking around in the GUI, or using the dconf command line tool. One of the interesting things about GNOME is that it has a lot of hidden options that either not at all exposed in the GUI, or only accessible through a tool like gnome-tweaks. But if you know what you're doing you can change a lot of GNOME behavior.

Most people are already in the habit of keeping their dot files in a git repo somewhere. I recently took this up a notch, and I now maintain my whole desktop configuration (including my dot files) using Ansible. To illustrate my GNOME configuration I'll be posting snippets from current Ansible configuration. If you're not familiar with Ansible don't worry: it's just YAML, and can easily be translated into command line invocations.

Here's what my GNOME dconf settings look like in Ansible:

- name: update gnome dconf settings
    key: "{{item.key}}"
    value: "{{item.value}}"
    - {key: '/org/gnome/desktop/input-sources/xkb-options', value: "['caps:ctrl_modifier']"}
    - {key: '/org/gnome/desktop/interface/clock-show-date', value: 'true'}
    - {key: '/org/gnome/desktop/interface/cursor-blink', value: 'false'}
    - {key: '/org/gnome/desktop/interface/gtk-theme', value: "'Adwaita-dark'"}
    - {key: '/org/gnome/desktop/privacy/old-files-age', value: 'uint32 7'}
    - {key: '/org/gnome/desktop/privacy/remove-old-trash-files', value: 'true'}
    - {key: '/org/gnome/desktop/privacy/report-technical-problems', value: 'false'}
    - {key: '/org/gnome/desktop/search-providers/disable-external', value: 'true'}
    - {key: '/org/gnome/desktop/wm/preferences/audible-bell', value: 'false'}
    - {key: '/org/gnome/desktop/wm/preferences/focus-mode', value: "'sloppy'"}
    - {key: '/org/gnome/settings-daemon/plugins/color/night-light-enabled', value: 'true'}
    - {key: '/org/gnome/settings-daemon/plugins/xsettings/antialiasing', value: "'rgba'"}
    - {key: '/org/gnome/shell/disable-user-extensions', value: 'true'}
    - {key: '/org/gnome/software/allow-updates', value: 'false'}
    - {key: '/org/gnome/software/download-updates', value: 'false'}
    - {key: '/org/gnome/terminal/legacy/default-show-menubar', value: 'false'}

You can probably guess what most of these options do based on the key name. A lot of them turn off various options, or change basic features of the window manager (such as setting "sloppy" focus, a must-have for me).

There's one I want to call out in particular though: I set /org/gnome/shell/disable-user-extensions to false, which completely disables the user extensions feature of GNOME. User extensions are a mechanism that allow users to write GNOME extensions in Javascript, similar to how Chrome and Firefox extensions work. In my opinion this idea has dubious merit, and my personal feeling is the less Javascript in my life the better. I felt somewhat vindicated about this decision during recent coverage of a memory leak in GNOME Shell. The underlying issue was related to the Javascript garbage collector in GNOME Shell not collecting object references in a timely manner. I'm not sure that disabling user extensions actually disables the Javascript engine completely, but it definitely minimizes it to the least possible scope.

You might wonder how you're actually supposed to find these options in the first place---how are you supposed to know what keys are available, and what values they take? In practice I found most of these in a kind of hacky way. The dconf command lets you dump the entire database using dconf dump /. So what I did to find most of these options was some process kind of like the following:

# Get the initial dconf state.
$ dconf dump / >a

# Click around in the GUI, change things in gnome-tweaks, etc.

# Dump the final dconf state.
$ dconf dump / >b

# Manually look at the diff of the two states.
$ diff -u a b

This is simple and effective, and just takes a minute or so of work. The more robust (but much more labor intensive) way to do this is to look at the GNOME settings schemas. GNOME packages install files ending in a .gschema.xml extension, which are XML descriptions of all of the possible options supported by the application, a description of what the options do, and their default values. In principle you could discover all of these options by digging around in /usr/share/glib-2.0/schemas and reading a bunch of XML schema descriptions. In practice I do this very rarely, really only when if I need to read the complete documentation for an option.

Removing Unneeded Components

GNOME bundles a lot of components that I find to be really annoying and unwelcome. These are the biggest offenders:

It turns out that you can remove a lot of these components without breaking anything. I remove as much of these as possible, as well as a number of programs that I know I don't plan on using. The following list is specific to Fedora 28, but shows what I remove:

# remove unwanted gnome packages
- name: dnf remove gnome bloat
      - cheese
      - evolution
      - evolution-ews
      - evolution-help
      - gfbgraph
      - gnome-boxes
      - gnome-calendar
      - gnome-contacts
      - gnome-dictionary
      - gnome-documents
      - gnome-getting-started-docs
      - gnome-initial-setup
      - gnome-maps
      - gnome-online-miners
      - gnome-photos
      - gnome-software
      - gnome-user-docs
      - gnome-user-share
      - gnome-video-effects
      - gnome-weather
      - simple-scan
      - totem
      - tracker-miners
      - yelp
    state: absent

Some of these are harmless programs that don't actually install session daemons, but are also programs I don't plan on using. In other cases (e.g. gnome-software) uninstalling the program actually removes daemons that are run by default in the session, so removing those components actually causes less programs to run and waste memory.

They key thing here is to make sure you don't remove dependencies of any of the core components, which includes gnome-shell but also some other programs like nautilus. To double check that I didn't actually break anything, I also have the following stanza in my config. This is just a sanity check to make sure that nothing critical was accidentally removed.

# double-check that we still have the basics
- name: ensure core gnome packages are installed
      - eog
      - evince
      - evolution-data-server
      - flatpak
      - gdm
      - gnome-keyring
      - gnome-menus
      - gnome-screenshot
      - gnome-shell
      - gnome-terminal
      - gnome-tweaks
      - nautilus
      - redhat-menus
    state: present

I'm removing a lot of stuff here, including some things you might want to keep. So look at the list for yourself and apply caution before removing anything. After removing these packages there are still a few annoying components like evolution-data-server and tracker that still remain because they can't be completely uninstalled without breaking critical components. But they can still be disabled in user sessions, as I'll explain below.

Disabling evolution-data-server, Tracker, and other bloat

GNOME used to have a horribly complicated system that was used to manage start user session services, and in the past it was very difficult or impossible to disable certain components. As part of the Wayland effort, GNOME moved to managing user services using systemd. This is really awesome because it means you can control GNOME startup services using systemctl just like regular system services.

On Wayland GNOME services run as part of your systemd user session. To see these services, use one of the following commands:

# Show running user services.
$ systemctl --user status

# Show all user units and their status.
$ systemctl --user list-unit-files

From these commands I identified various services I want to disable. The trick to disabling these is to use the systemctl --user mask command, which inhibits the system installed service units from running. In theory masking services could cause problems, but at least for the set presented here I haven't observed any issues.

Here's my Ansible code:

- name: mask unwanted gnome services
    name: "{{item}}"
    user: yes
    masked: yes
    state: stopped
    - evolution-addressbook-factory.service
    - evolution-calendar-factory.service
    - evolution-source-registry.service
    - gvfs-goa-volume-monitor.service
    - tracker-store.service

This list lobotomizes evolution-data-server, GNOME Online Accounts, and Tracker. I also have an Ansible command to ensure that the tracker database is completely removed from my system:

- name: remove tracker databases
    name: ~/.cache/tracker
    state: absent

I'll note that even after removing these services there are still a lot of GNOME user services running in my session. If you wanted to you could mask a lot more services. In general the approach I take is to only mask services whose behavior I understand. Most of the remaining services in my GNOME session are daemons that use very little memory and zero CPU, so I don't mind keeping them around.

Firefox/GNOME Integration

I version control my Firefox preferences using Firefox's obscure user.js feature, which essentially lets you put about:config preferences in a Javascript file. I have a fairly extensive user.js file that is a relaxed version of options suggested by pyllyukko/user.js, which I might write about in more detail another time. Two of the options I set in this file are specific to GNOME, so I'll cover them here.

The GNOME Extensions site lets you install GNOME extensions from Firefox. This is done using a native plugin, meaning that unlike normal extensions it runs in a privileged context that allows it to interact with GNOME. Since I have user extensions disabled anyway this serves no purpose, and could possibly do nasty things if a security flaw is found in it later. Therefore I disable this plugin:

// Disable GNOME browser plugin.
user_pref("plugin.state.libgnome-shell-browser-plugin", 0);

The second option is related to a Firefox bug (present as of Firefox 60.0) related to GNOME 3.28 when using the Adwaita dark theme. When using this theme Firefox will render certain widgets/text areas with inverted colors. This is more than just strange looking: in some cases it can cause things to be completely unreadable (e.g. a white text color can get applied to text in a white text area). This can be fixed by forcing Firefox to render widgets use the Adwaita light theme:

// Force Adwaita light theme.
user_pref("widget.content.gtk-theme-override", "Adwaita:light");

Parting Thoughts

The configuration listed above is the accumulation of a few years of efforts for me. Things change from release to release, and therefore I revisit things from time to time. Generally though I'm very happy with the direction GNOME has been doing, and it's a much more pleasant experience dealing with certain aspects of it today than it was a few years ago. I hope the information presented here is useful, and I hope to see more great things from the GNOME project in the future.