diff --git a/.local/bin/gdm-settings-sync b/.local/bin/gdm-settings-sync new file mode 100755 index 0000000..1e74935 --- /dev/null +++ b/.local/bin/gdm-settings-sync @@ -0,0 +1,189 @@ +#!/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)"