Files
Panama/setup/scripts/change-settings
T
Gabriel Brown 9530e32326 Give a laptop its first week: the keys, the lid, the battery's endgame
The audit's fourth tier -- what a Framework owner reaches for in the first
week and found missing.

The power button stops being an instant, unconfirmed poweroff: a shipped
logind drop-in tells the daemon to stand down and the compositor binds the
key to the power menu, the way GNOME turns it into a question. Holding it
still hard-cuts through firmware. change-settings restarts logind so the
change applies without waiting for a boot, and the Power page says what the
button does now.

The function row fills in: F10 (XF86RFKill) toggles airplane mode through a
new panama-osd verb that blocks or unblocks every radio and says which way it
went; F9 (XF86Display) opens the Displays page, the honest action until
mirroring exists. And the lid becomes a switch bind: closing a docked lid
turns the internal panel off so nothing renders inside a closed shell and no
workspace strands on an invisible output, and opening it restores the panel
with the mode and scale chosen in Settings. panama-lid owns both decisions;
undocked machines suspend via logind before any of it matters.

The battery gets an endgame. On battery the screen dims to 30% two-thirds of
the way to blanking -- GNOME's single largest idle battery saver -- and
restores exactly the level it saved. At the urgent threshold the machine
suspends after a fifteen-second grace, cancelled by plugging in, because a
suspend preserves the session for days and a hard cut at 0% preserves
nothing; "Only warn" remains a choice on the Power page. Hibernate joins the
power menu, but only where logind answers CanHibernate with yes -- an entry
that fails silently is worse than none.

And brightness stops being two code paths: the Displays page now embeds the
same control the quick-settings panel uses, so the built-in backlight and
DDC/CI monitors share one surface that withdraws itself where neither
exists. The lid contract narrows to what its principle protects -- a
HandleLidSwitch drop-in -- so deliberate policy for other keys can ship.

Claude-Session: https://claude.ai/code/session_01Epx9ZC1gwm81K3jm9x9CKh
2026-08-23 12:21:08 -04:00

132 lines
6.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# Strict: the copy over / below is the one thing this stage exists to do, and
# without set -e its failure fell through to guarded no-ops and exited 0 --
# a failed stage the installer's summary could never see.
set -euo pipefail
# --- Helper functions ---
log() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
exists() { command -v "$1" >/dev/null 2>&1; }
# --- Defined Paths ---
# The default, not an assignment: an exported PANAMA_PATH from a clone at
# another location must win, or the copy over / below reads the wrong tree.
PANAMA_PATH="${PANAMA_PATH:-$HOME/.local/share/Panama}"
echo -e "\n--- Copying System & User files ---"
# dnf options are applied as individual keys rather than shipped as a file.
# config/copy once carried a whole /etc/dnf/dnf.conf, which clobbered proxies
# and mirrors a machine already had -- and set defaultyes=True system-wide, so
# every `dnf remove` for every user treated a bare Enter as yes. These two are
# performance knobs with no behavior change; anything else stays the machine's
# own business. (fastestmirror and deltarpm were dnf4-era keys dnf5 ignores,
# so they simply stop being written.)
log "Setting dnf performance options (max_parallel_downloads, keepcache)"
sudo dnf config-manager setopt max_parallel_downloads=10 keepcache=True 2>/dev/null \
|| log "Could not set dnf options; defaults apply"
log "Copying system files (udev rules, systemd drop-ins)"
sudo cp -r "$PANAMA_PATH/config/copy/." "/"
# The GPU symlink rules land via the copy above. Apply them without a reboot so
# AQ_DRM_DEVICES resolves on the first Hyprland login rather than the second.
if [[ -f /etc/udev/rules.d/99-panama-gpu.rules ]]; then
log "Reloading udev rules for stable GPU symlinks"
sudo udevadm control --reload
sudo udevadm trigger --subsystem-match=drm
fi
# The power-key drop-in lands via the same copy; logind reads its config only
# at start, so without this the button keeps hard-powering-off until the next
# boot. Restarting logind is what Fedora's own updates do and current systemd
# preserves sessions across it.
if [[ -f /etc/systemd/logind.conf.d/panama-power-key.conf ]]; then
log "Reloading logind so the power button opens the menu instead of powering off"
sudo systemctl try-restart systemd-logind 2>/dev/null \
|| log "Could not restart logind; the power-button change applies at next boot"
fi
# The document-portal drop-in lands via the same copy. A user unit's drop-ins
# are read at daemon-reload, so without this the guard applies from the next
# login rather than from now -- and the failure it guards against is one you
# only find days later, when you next open a flatpak you had not opened yet.
if [[ -f /etc/systemd/user/xdg-document-portal.service.d/panama-stale-mount.conf ]]; then
log "Reloading the user manager so the document-portal drop-in applies"
systemctl --user daemon-reload || log "Could not reload the user manager"
fi
echo -e "\n--- Desktop appearance ---"
# GTK and Qt apps read these directly from gsettings; there is no settings
# daemon outside GNOME, so they must be correct rather than merely overridden.
# These match config/dot/gtk-3.0/settings.ini and quickshell's Theme.qml.
if exists gsettings; then
log "Applying interface settings"
gsettings set org.gnome.desktop.interface gtk-theme 'adw-gtk3-dark'
gsettings set org.gnome.desktop.interface icon-theme 'Adwaita'
gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'
gsettings set org.gnome.desktop.interface font-name 'Adwaita Sans 11'
gsettings set org.gnome.desktop.interface monospace-font-name 'VictorMono Nerd Font 10'
gsettings set org.gnome.desktop.interface cursor-theme 'oreo_blue_cursors'
# Deliberately NOT setting cursor-size. GNOME here is on 16 (smaller than
# GNOME's own default of 24) and changing it would visibly alter the working
# GNOME session. Hyprland uses XCURSOR_SIZE=24 from uwsm/env; if the cursor
# feels too large there, change that one value rather than this one.
fi
echo -e "\n--- Hyprland session services ---"
# Deliberately NOT enabling these.
#
# hyprpaper / hypridle / hyprpolkitagent / vicinae all ship units that are
# WantedBy=graphical-session.target -- and that target is active under GNOME
# too. Enabling them would start hypridle alongside GNOME's own idle handling
# (two daemons blanking and locking the screen on different timers) and start
# hyprpaper where there is no layer shell for it to draw on.
#
# Instead hypr/autostart.lua runs `systemctl --user start ...` on
# hyprland.start, which scopes them to the Hyprland session. They still get the
# correct uwsm activation environment and still stop in order, because they are
# PartOf=graphical-session.target.
log "Hyprland services are started per-session by hypr/autostart.lua, not enabled globally"
# espanso is the exception to the rule above: text expansion is wanted under
# any compositor and conflicts with nothing GNOME runs, and upstream's own
# mechanism is a registered user unit. The RPM ships no unit -- without this,
# the package installs, the config links, and no trigger ever fires.
# `service register` writes and enables the unit and is idempotent on re-runs;
# autostart.lua also starts it so the first Hyprland login after a fresh
# install expands text without waiting for the next one.
if exists espanso && ! systemctl --user cat espanso.service >/dev/null 2>&1; then
log "Registering the espanso text-expansion service"
espanso service register >/dev/null 2>&1 || log "Could not register espanso"
fi
# Notification daemons must NOT be installed: mako, dunst and swaync all
# register Name=org.freedesktop.Notifications for D-Bus activation, which races
# Quickshell's own notification server at login. Whoever wins keeps the name.
for pkg in mako dunst SwayNotificationCenter; do
if rpm -q "$pkg" >/dev/null 2>&1; then
log "WARNING: $pkg is installed and will fight Quickshell for the notification D-Bus name."
log " Fix with: systemctl --user mask ${pkg,,}.service"
fi
done
# Select the launcher theme once vicinae exists.
if exists vicinae; then
log "Setting vicinae theme"
vicinae theme set tokyonight-moon >/dev/null 2>&1 || true
fi
# A machine with a battery gets managed idle timings from the start. The
# shipped hypridle.conf deliberately never suspends -- correct for a desktop,
# and exactly wrong for a laptop in a bag. panama-idle generates the
# power-source-aware config and points hypridle at it with a drop-in; it does
# not start hypridle here (the Hyprland session does that), so running it
# under GNOME during install is safe. A desktop skips this entirely and keeps
# the shipped file, as before.
if "${PANAMA_PATH:-$HOME/.local/share/Panama}/bin/panama-hw" battery 2>/dev/null; then
log "Battery detected: enabling managed idle timings (suspend on battery works out of the box)"
"${PANAMA_PATH:-$HOME/.local/share/Panama}/config/dot/quickshell/scripts/panama-idle" install \
|| log "Could not enable managed idle timings; the Power page can turn them on later"
fi