I recently got the opportunity to work on adapting the default Debian Gnome experience to a client’s corporate design, and it felt a LOT like reverse-engineering deeply into the undocumented.
I found the work to fall into a number of categories, which I will classify as “dconf policy”, “css”, “xml-manifest” and “packaging”.
GDM logo | dconf policy, packaging |
GDM banner message | dconf policy |
GDM background color | css, xml-manifest |
GDM wallpaper | css, xml-manifest, packaging |
Gnome default wallpaper | dconf policy, packaging |
Gnome default theme | dconf policy |
Gnome shell plugins | dconf policy, packaging |
Gnome UI and plugin defaults | dconf policy |
Gnome wallpapers | xml-manifest |
Note I’m not familiar with any underlying Gnome/GTK philosopy aspects but come from a Linux engineering role and just need to get the job done.
Packaging
The “packaging” class really just means that required assets need to be packaged onto the system and that any shell plugins that should be enabled by default, must be installed.
GDM
The GDM-Settings workflow
For GDM customization, GDM-Settings proved immensely helpful for identifying where to make changes.
# Install via flatpak
sudo apt-get install flatpak gnome-software-plugin-flatpak
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak -y install io.github.realmazharhussain.GdmSettings
# Keep track of where we started off
touch /tmp/now
# Run gdm-settings
flatpak run io.github.realmazharhussain.GdmSettings
# See what changed
find / -type f -newer /tmp/now 2>/dev/null | egrep -v '^/(dev|run|proc|sys|home|var|tmp)'
For this post, I will stick with the default dconf policy filename used by GDM-Settings.
Logo and banner
# /etc/dconf/db/gdm.d/95-gdm-settings
[org/gnome/login-screen]
logo='/usr/share/icons/hicolor/48x48/apps/gvim.png'
banner-message-enable=true
banner-message-text='Welcome to VIMnux'
dconf needs an accompanying profile definition, /etc/dconf/profile/gdm:
user-db:user
system-db:gdm
dconf update needs to be run after modifying these files.
Background color and wallpaper
GDM background settings are hidden deep in the global gnome-shell theme CSS, which itself is hidden in /usr/share/gnome-shell/gnome-shell-theme.gresource.
GDM-Settings completely hides the tedious process of drilling down to the CSS away from the user, which is great from a user perspective, but not what I needed for my customizations. I went with the following workflow for unpacking the files. gresource list lists the file names contained in the gresource file, gresource extract extracts them one by one.
# Unpack /usr/share/gnome-shell/gnome-shell-theme.gresource
# to a temporary directory:
T=$(mktemp -d /tmp/gres.XXX); printf "Resources tempdir: %s\n" $T
cd $T
while read R
do
gresource extract /usr/share/gnome-shell/gnome-shell-theme.gresource $R > $(basename $R)
done < <(gresource list /usr/share/gnome-shell/gnome-shell-theme.gresource)
At this point, the only file I’m interested in is gnome-shell.css, where I set a black background for my application.
.login-dialog { background: transparent; }
#lockDialogGroup { background-color: rgb(0,0,0); }
Similar CSS for a wallpaper:
.login-dialog { background: transparent; }
#lockDialogGroup {
background-image: url('file:///usr/share/backgrounds/gnome/wood-d.webp');
background-position: center;
background-size: cover;
}
Reassembly of the gresource file requires an XML manifest which I generate using the following script, manifest.py:
#!/usr/bin/env python3
import os, sys, glob
import xml.etree.ElementTree as ET
from io import BytesIO
os.chdir(sys.argv[1])
gresources = ET.Element('gresources')
gresource = ET.SubElement(gresources, 'gresource', attrib = {'prefix': '/org/gnome/shell/theme'})
for resourcefile in glob.glob('*'):
file = ET.SubElement(gresource, 'file')
file.text = resourcefile
out = BytesIO()
xmldoc = ET.ElementTree(gresources)
ET.indent(xmldoc)
xmldoc.write(out, encoding='utf-8', xml_declaration=True)
print(out.getvalue().decode())
First generate the XML manifest, then compile the gresources file.
# Generate XML
./manifest.py $T > gnome-shell-theme.gresource.xml
# Compile gresources (glib-compile-resources from libglib2.0-dev-bin)
glib-compile-resources gnome-shell-theme.gresource.xml --sourcedir=$T --target=gnome-shell-theme.gresource
Someone over here decided to indirect /usr/share/gnome-shell/gnome-shell-theme.gresource via /etc/alternatives, do whatever you like.
Note that on the systems I tested this on, gdm.css and gdm3.css could be left out of the gresource file and all changes were made in gnome-shell.css.
Gnome Wallpapers
Speaking of XML, Wallpapers can be installed to someplace intuitive such as /usr/share/backgrounds/corporate but must be accompanied by another XML manifest in /usr/share/gnome-background-properties, which I generate using another XML generator, properties-xml.py:
#!/usr/bin/env python3
import os, sys, glob
import xml.etree.ElementTree as ET
from io import BytesIO
os.chdir(sys.argv[1])
dirname='/usr/share/backgrounds/corporate'
wallpapers = ET.Element('wallpapers')
for wallpaper in glob.glob('*'):
wallpaper_element = ET.SubElement(wallpapers, 'wallpaper', attrib = {'deleted': 'false'})
filename = ET.SubElement(wallpaper_element, 'filename')
filename.text = f"{dirname}/{wallpaper}"
name = ET.SubElement(wallpaper_element, 'name')
name.text = wallpaper
options = ET.SubElement(wallpaper_element, 'options')
options.text = 'zoom'
pcolor = ET.SubElement(wallpaper_element, 'pcolor')
pcolor.text = '#000000'
scolor = ET.SubElement(wallpaper_element, 'scolor')
scolor.text = '#ffffff'
out = BytesIO()
xmldoc = ET.ElementTree(wallpapers)
ET.indent(xmldoc)
xmldoc.write(out)
print('<?xml version="1.0" encoding="UTF-8"?>')
print('<!DOCTYPE wallpapers SYSTEM "gnome-wp-list.dtd">')
print(out.getvalue().decode())
Which I run as follows:
./properties-xml.py backgrounds > corporate.xml
/usr/share/backgrounds/corporate/* and /usr/share/gnome-background-properties/corporate.xml then get packaged onto the system.
Gnome Extensions and Defaults
At this point, a dconf user profile needs to be introduced:
$ cat /etc/dconf/profile/user
user-db:user
system-db:local
(Things get easier from here.)
Default-enabled extensions
I chose to enable the extensions and set related defaults in /etc/dconf/db/local.d/99-extensions:
[org/gnome/shell]
enabled-extensions=[ 'ding@rastersoft.com', 'no-overview@fthx', 'dash-to-dock@micxgx.gmail.com', 'ubuntu-appindicators@ubuntu.com', 'TopIcons@phocean.net' ]
[org/gnome/shell/extensions/dash-to-dock]
dock-fixed=true
background-opacity=0.2
transparency-mode='FIXED'
dconf update needs to be run after modifying this file.
Other Gnome defaults
dconf watch /
dconf watch / in a terminal makes it possible to take note of what configuration options change as changes are being made. They can now be made the defaults in a policy file such as /etc/dconf/db/local.d/99-misc-defaults:
[org/gnome/desktop/wm/preferences]
button-layout='appmenu:minimize,maximize,close'
[org/gnome/terminal/legacy]
theme-variant='dark'
[org/gnome/desktop/interface]
color-scheme='prefer-dark'
gtk-theme='Adwaita-dark'
dconf update needs to be run after modifying this file.
tl;dr: Example customization package
A debian package that provides live examples, can be found here: https://github.com/mschmitt/gnome-local-custom