dotfiles/.local/bin/gdm-settings-sync
Alexander 55de29bf87
Add gdm-settings-sync utility ...
... that syncs `dconf` settings of the local user
to `gdm3`, the main login window after boot
2026-10-20 15:55:20 +02:00

189 lines
5 KiB
Shell
Executable file

#!/bin/sh
# Install the GDM greeter's `dconf` settings into /etc/dconf/db/gdm.d/
#
# The keys listed in `$MIRROR_PATHS` below are read from
# the live user session and written as one keyfile
#
# The greeter runs as the `Debian-gdm` user and never sees
# the user-level settings in ~/.config/gnome-settings/*.ini
#
# The keyfile is written, not symlinked,
# so that the greeter does not depend on ~/ being readable
#
# Without root privileges, this script only checks and reports
# => Run with `sudo` to install
set -eu
SELF_DIR=$(cd "$(dirname "$0")" && pwd)
TARGET_DIR=/etc/dconf/db/gdm.d
COMPILED_DB=/etc/dconf/db/gdm
# Only files with this prefix are managed (and pruned) here
PREFIX=90-dotfiles-
MIRRORED=mirrored
# Keys mirrored from the live user session into the GDM greeter
#
# One absolute `dconf` path per line
# => Unset keys are skipped, so the greeter keeps its defaults
MIRROR_PATHS='
# Idle and power
#
# Only the AC keys => On battery, the greeter keeps suspending
/org/gnome/desktop/session/idle-delay
/org/gnome/settings-daemon/plugins/power/idle-brightness
/org/gnome/settings-daemon/plugins/power/idle-dim
/org/gnome/settings-daemon/plugins/power/power-saver-profile-on-low-battery
/org/gnome/settings-daemon/plugins/power/sleep-inactive-ac-timeout
/org/gnome/settings-daemon/plugins/power/sleep-inactive-ac-type
# Would disarm the physical power button at the greeter as well
# /org/gnome/settings-daemon/plugins/power/power-button-action
# Keyboard (e.g., Caps Lock as Control while typing the password)
/org/gnome/desktop/input-sources/sources
/org/gnome/desktop/input-sources/xkb-options
/org/gnome/desktop/peripherals/keyboard/numlock-state
# Touchpad and mouse
/org/gnome/desktop/peripherals/mouse/natural-scroll
/org/gnome/desktop/peripherals/touchpad/natural-scroll
/org/gnome/desktop/peripherals/touchpad/tap-to-click
# Top bar
/org/gnome/desktop/interface/accent-color
/org/gnome/desktop/interface/clock-format
/org/gnome/desktop/interface/clock-show-date
/org/gnome/desktop/interface/clock-show-seconds
/org/gnome/desktop/interface/clock-show-weekday
/org/gnome/desktop/interface/show-battery-percentage
'
if ! command -v gdm3 >/dev/null 2>&1 && [ ! -d /etc/gdm3 ]; then
echo "GDM is not installed => Nothing to do"
exit 0
fi
if ! command -v dconf >/dev/null 2>&1; then
echo "dconf is not installed" >&2
exit 1
fi
# As root, the live settings must come from the invoking user
if [ "$(id -u)" = 0 ] && [ "${SUDO_USER:-root}" = "root" ]; then
echo "Run via sudo from the user whose settings to mirror" >&2
exit 1
fi
# The mirrored paths, without comments and blank lines
_mirror_paths() {
printf '%s\n' "$MIRROR_PATHS" | grep -Ev '^[[:space:]]*(#|$)' | sort -u
}
# `dconf read` as the session user, also when run via `sudo`
# (reads need no D-Bus, only the user's database file)
_dconf_read() {
if [ "$(id -u)" = 0 ]; then
_home=$(getent passwd "$SUDO_USER" | cut -d: -f6)
runuser -u "$SUDO_USER" -- env \
HOME="$_home" \
XDG_CONFIG_HOME="$_home/.config" \
XDG_RUNTIME_DIR="/run/user/$(id -u "$SUDO_USER")" \
dconf read "$1"
else
dconf read "$1"
fi
}
_render_mirrored() {
echo "# Written by gdm-settings-install from the live user session - do not edit"
_mirror_paths | while IFS= read -r path; do
_value=$(_dconf_read "$path")
[ -n "$_value" ] || continue # Unset => Keep the greeter's default
_dir=${path%/*}
_dir=${_dir#/}
if [ "$_dir" != "${_last:-}" ]; then
printf '\n[%s]\n' "$_dir"
_last=$_dir
fi
printf '%s=%s\n' "${path##*/}" "$_value"
done
}
_in_sync() {
[ -f "$COMPILED_DB" ] || return 1
_render_mirrored | cmp -s - "$TARGET_DIR/$PREFIX$MIRRORED" || return 1
# No other managed file may be left over (e.g., from older versions)
for dst in "$TARGET_DIR/$PREFIX"*; do
[ -e "$dst" ] || continue
[ "${dst##*/$PREFIX}" = "$MIRRORED" ] || return 1
done
# The compiled database must be newer than the keyfiles
[ -z "$(find "$TARGET_DIR" -newer "$COMPILED_DB" 2>/dev/null)" ]
}
if _in_sync; then
echo "GDM settings are up to date"
exit 0
fi
if [ "$(id -u)" != 0 ]; then
echo "" >&2
echo "GDM settings are not installed or outdated" >&2
echo "" >&2
echo "Fix with: sudo $SELF_DIR/$(basename "$0")" >&2
echo "" >&2
exit 1
fi
mkdir -p "$TARGET_DIR"
for dst in "$TARGET_DIR/$PREFIX"*; do
[ -e "$dst" ] || continue
[ "${dst##*/$PREFIX}" = "$MIRRORED" ] || rm -v -- "$dst"
done
_rendered=$(mktemp)
trap 'rm -f "$_rendered"' EXIT
_render_mirrored > "$_rendered"
install -v -m 644 -- "$_rendered" "$TARGET_DIR/$PREFIX$MIRRORED"
dconf update
# Debian's `gdm` profile must read the "gdm" system database
if ! grep -qs '^system-db:gdm$' /etc/dconf/profile/gdm /usr/share/dconf/profile/gdm; then
echo "WARN: no dconf profile for gdm reads system-db:gdm" >&2
fi
echo "Installed the GDM settings into: $TARGET_DIR"
echo "They take effect at the next start of the greeter (e.g., reboot)"