fix(swaylock): ship config to /etc/swaylock, blur the background

swaylock reads ~/.swaylock/config, $XDG_CONFIG_HOME/swaylock/config and
SYSCONFDIR/swaylock/config — never /etc/xdg/, where the generic XDG sweep
put it. The config was made effective by seeding it into user homes, but
both seeds are conditional, so a machine that pulled no moonarch-git
update since swaylock arrived ran with no config at all: no wallpaper,
no Catppuccin ring.

The config now lives in defaults/etc/swaylock/config and is owned at
/etc/swaylock/config, which the package alone makes effective. Home
copies still win by search order, so the .install hook retires the
pristine ones; an edited copy stays.

Upstream swaylock has no blur (effect-blur is swaylock-effects only), so
the blur is baked into a second image: wallpaper-blur.jpg, generated with
downsample-blur-upsample. Regenerate it when the wallpaper changes.

moonarch-doctor gains check_swaylock_config, which names the config file
that actually wins and fails when none exists.
This commit is contained in:
2026-08-18 17:33:28 +02:00
parent 5526cdb6ad
commit e9e12cc280
7 changed files with 49 additions and 12 deletions
+7
View File
@@ -9,6 +9,13 @@ Reproducible Arch Linux setup based on archinstall + post-install automation.
- `packages/` — package lists (official + AUR), maintained separately
- `defaults/` — XDG configs, shell config, helper binaries, systemd services, udev rules, greetd/regreet config, swaylock config, wallpaper
## Lock Screen (swaylock)
- **Config path is not XDG.** swaylock searches `~/.swaylock/config`, `$XDG_CONFIG_HOME/swaylock/config` and `SYSCONFDIR/swaylock/config` (`/etc/swaylock/config` on Arch) — **never `/etc/xdg/`**. Verified by running swaylock against a throwaway `HOME` containing an invalid option: it rejects the option before it even connects to the compositor, and reports nothing at all when the file sits in `/etc/xdg/`. The config therefore lives in `defaults/etc/swaylock/config` and is installed by an explicit `install` line in the PKGBUILD, deliberately **outside** the generic `defaults/xdg/``/etc/xdg/` sweep.
- **No home seeding.** Owning `/etc/swaylock/config` makes the config effective through the package alone. The earlier approach seeded it into `/etc/skel` and existing homes from the `.install` hook; that hook only fires on a moonarch-git install/upgrade and only under `[ ! -f ]`, so a machine that pulled no update since swaylock arrived ran entirely unconfigured — no wallpaper, no Catppuccin ring. `pre_upgrade` retires those copies, but only the pristine ones (`cmp` against the still-old `/etc/xdg/swaylock/config`); an edited copy stays and keeps winning, which is what the home path is for. `moonarch-doctor` reports which file actually wins.
- **Blur is baked into the image.** Upstream swaylock has no blur — `effect-blur` exists only in the `swaylock-effects` fork, which is stuck on 1.7 upstream. So `defaults/backgrounds/wallpaper-blur.jpg` ships next to the plain wallpaper, generated once with `magick wallpaper.jpg -resize 25% -blur 0x12 -resize 4717x2984! -quality 88 -strip wallpaper-blur.jpg`. Downsample-blur-upsample rather than a large-radius blur at full resolution: same look, and the file compresses to 167 KB against the original's 374 KB. Regenerate it whenever the wallpaper changes — nothing derives it automatically.
- **NixOS reads a different path.** nixpkgs leaves meson's `sysconfdir` at `$out/etc`, so `/etc/swaylock/config` is never consulted there. moonix keeps its own `systemd.user.tmpfiles` seed into `~/.config/swaylock/config` (`moonix/modules/desktop.nix`) and substitutes the wallpaper path for a store path — that seed is why moonix is unaffected by the change above.
## Battery Conservation Mode
Laptops with `charge_control_end_threshold` support (ThinkPad, Framework, etc.) get a Waybar toggle:
+6
View File
@@ -1,5 +1,11 @@
# Decisions
## 2026-08-18 swaylock config moves to /etc/swaylock, background is pre-blurred
- **Who**: Dominik, ClaudeCode
- **Why**: A second machine showed no lock screen background. The config was installed to `/etc/xdg/swaylock/config`, which swaylock never reads (only `~/.swaylock/config`, `$XDG_CONFIG_HOME/swaylock/config`, `SYSCONFDIR/swaylock/config`), and was made effective by seeding it into user homes from `post-install.sh` and the `.install` hook. Both seeds are conditional — the hook fires only on a moonarch-git install/upgrade and only under `[ ! -f ]` — so a machine that pulled no update since swaylock arrived (2026-08-14) ran with no config at all. Verified by running swaylock against a throwaway `HOME`: it rejects an invalid option from `~/.config/swaylock/config` before connecting to the compositor, and reports nothing when the same file sits in `/etc/xdg/`. The machine this was investigated on had the same gap unnoticed — its home copy had been renamed to `config.bak`, so neither machine was actually reading a config. Second request: the background should be blurred.
- **Tradeoffs**: (1) **Where the config lives** — owning `/etc/swaylock/config` was chosen over keeping the seed. It takes effect through the package alone, with no home state and no `[ ! -f ]` window to miss. Keeping both paths was rejected as redundant, and repairing only the seeding was rejected because it needs a manual step per machine and repeats on the next one. The original reason for seeding (`DECISIONS.md` 2026-08-14, tradeoff 4) was NixOS, where nixpkgs leaves meson's `sysconfdir` at `$out/etc` — but moonix runs its own tmpfiles seed anyway, so the Arch path does not have to carry that constraint. (2) **The shadowing problem** — home paths are searched first, so every already-seeded copy would hide the new system config forever, blur included. `pre_upgrade` deletes those copies, but only where `cmp` against the still-old `/etc/xdg/swaylock/config` proves them pristine; an edited copy stays and keeps winning. Only one config version was ever shipped (`a17474f`), so that comparison is unambiguous. (3) **How to blur**`swaylock-effects` would blur the live screen contents (`screenshots` + `effect-blur`, as an older config of Dominik's did), but the fork replaces the swaylock package and its AUR release sits on 1.7 while upstream is at 1.8.6. A `grim` + ImageMagick wrapper would blur the real screen without a fork, at the price of lock latency and a blurred copy of the screen briefly on disk. Dominik chose the third option: a blurred copy of the wallpaper, shipped as an image. No fork, no wrapper, identical on Arch and NixOS — the price is that it blurs the wallpaper, not what is on screen. (4) **Blur method** — downsample, blur, upsample instead of a large-radius blur at full resolution. Same result to the eye, and the file compresses to 167 KB against the plain wallpaper's 374 KB.
- **How**: `defaults/xdg/swaylock/config``defaults/etc/swaylock/config`, `image=` pointing at the new `defaults/backgrounds/wallpaper-blur.jpg` (`magick wallpaper.jpg -resize 25% -blur 0x12 -resize 4717x2984! -quality 88 -strip`). The PKGBUILD installs both with explicit `install` lines — the config deliberately outside the generic `defaults/xdg/``/etc/xdg/` sweep that caused the wrong path in the first place. `moonarch.install` loses its swaylock seeding block and gains the `pre_upgrade` retirement of the home copies; `post-install.sh` loses its seeding block. New `check_swaylock_config` in `moonarch-doctor` names the file that actually wins and fails when none exists. `moonix/modules/desktop.nix` substitutes from the new source path and points at the blurred wallpaper; its tmpfiles seed stays, since `/etc/swaylock/config` is not read there.
## 2026-08-18 Popouts close when the focus leaves them
- **Who**: Dominik, ClaudeCode
- **Why**: An open popout only closed on a backdrop click, Escape, a toggle, or another screen claiming the system-wide slot. Anything that moved the focus without a click — Alt+Tab, a focus keybind, a workspace switch, a newly mapped window — left the popout standing over the desktop.
+1 -1
View File
@@ -14,7 +14,7 @@ desktop that can be rebuilt from scratch in minutes.
|-----------|--------|-----|
| **Compositor** | [Niri](https://github.com/YaLTeR/niri) | Scrollable tiling — no fixed grid, infinite horizontal workspace. Purpose-built Wayland compositor, not an X11 port. |
| **Greeter** | [greetd](https://sr.ht/~kennylevinsen/greetd/) + [ReGreet](https://github.com/rharish101/ReGreet) | Minimal, Wayland-native login. ReGreet is a GTK4 greeter from the official repos, running inside its own Niri instance and inheriting the system GTK theme. |
| **Lock Screen** | [swaylock](https://github.com/swaywm/swaylock) | ext-session-lock-v1 protocol — compositor guarantees lock on crash. PAM authentication, one Wayland protocol, no toolkit. |
| **Lock Screen** | [swaylock](https://github.com/swaywm/swaylock) | ext-session-lock-v1 protocol — compositor guarantees lock on crash. PAM authentication, one Wayland protocol, no toolkit. Background is a blurred copy of the default wallpaper, blurred at build time because upstream swaylock has no blur effect. |
| **Power Menu** | Quickshell popout (`PowerPopout.qml`) | Lock, logout, hibernate, reboot, shutdown; every action except lock needs a second click. Part of the bar, so it needs no own package. |
| **Idle Manager** | [stasis](https://aur.archlinux.org/packages/stasis) + [wayland-pipewire-idle-inhibit](https://github.com/rafaelrc7/wayland-pipewire-idle-inhibit) | Separate AC/battery power plans. Brightness dimming, DPMS, lock (via swaylock), and suspend on configurable timeouts. The companion inhibitor holds a Wayland idle-inhibitor while audio plays — keeps the screen awake during windowed browser video, which stasis' pactl detection skips by design. |
| **Bar** | [Waybar](https://github.com/Alexays/Waybar) | Wayland-native, highly customizable. Niri workspace/window modules via community plugins. |
Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

@@ -1,10 +1,18 @@
# ABOUTME: Moonarch default swaylock configuration, Catppuccin Mocha.
# ABOUTME: Seeded to ~/.config/swaylock/config; callers pass -f themselves.
# ABOUTME: Installed to /etc/swaylock/config; callers pass -f themselves.
#
# Each line is turned into a --long-option by swaylock, so key=value applies.
# Colors are rrggbb[aa] without a leading '#'.
#
# swaylock searches ~/.swaylock/config, $XDG_CONFIG_HOME/swaylock/config and
# SYSCONFDIR/swaylock/config (= /etc/swaylock/config) — never /etc/xdg/. This
# file therefore ships to /etc/swaylock/, not to /etc/xdg/swaylock/. A user copy
# in ~/.config/swaylock/config still wins, since the home paths are searched first.
image=/usr/share/moonarch/wallpaper.jpg
# Blurred variant of the default wallpaper. swaylock has no blur of its own —
# effect-blur exists only in the swaylock-effects fork — so the blur is baked
# into the image at build time.
image=/usr/share/moonarch/wallpaper-blur.jpg
scaling=fill
font=UbuntuSans Nerd Font
+23
View File
@@ -71,6 +71,27 @@ check_user_service() {
fi
}
# swaylock reads ~/.swaylock/config, $XDG_CONFIG_HOME/swaylock/config and
# /etc/swaylock/config, in that order. Moonarch owns the last one, so a copy in
# the home shadows it — silently, and a stale one from the retired seeding drops
# the blurred wallpaper. Report the file that actually wins.
check_swaylock_config() {
local system="/etc/swaylock/config"
local home_cfg
for home_cfg in "$HOME/.swaylock/config" "${XDG_CONFIG_HOME:-$HOME/.config}/swaylock/config"; do
if [[ -f "$home_cfg" ]]; then
warn "swaylock config: $home_cfg shadows $system"
return
fi
done
if [[ -f "$system" ]]; then
pass "swaylock config: $system"
else
fail "swaylock config: $system (missing — swaylock starts unconfigured)"
fi
}
check_config_match() {
local deployed="$1"
local source="$2"
@@ -217,6 +238,8 @@ check_config_match "/etc/greetd/niri-greeter.kdl" "$SRC/greetd/niri-greeter.k
check_config_match "/etc/greetd/regreet.toml" "$SRC/greetd/regreet.toml"
check_config_match "/etc/greetd/regreet.css" "$SRC/greetd/regreet.css"
check_swaylock_config
# --- 5. Helper Scripts ---
section "Helper Scripts"
+2 -9
View File
@@ -128,15 +128,8 @@ if [[ ! -f "$HOME/.config/stasis/stasis.rune" && -f /etc/xdg/stasis/stasis.rune
install -Dm644 /etc/xdg/stasis/stasis.rune "$HOME/.config/stasis/stasis.rune"
fi
# --- Seed swaylock user config ---
#
# swaylock searches ~/.swaylock/config, $XDG_CONFIG_HOME/swaylock/config and
# SYSCONFDIR/swaylock/config, but never /etc/xdg/. Seed Moonarch's template to
# the user home. Never overwrite an existing user config.
if [[ ! -f "$HOME/.config/swaylock/config" && -f /etc/xdg/swaylock/config ]]; then
log "Seeding Moonarch swaylock config to user home."
install -Dm644 /etc/xdg/swaylock/config "$HOME/.config/swaylock/config"
fi
# swaylock needs no seeding: its config ships to /etc/swaylock/config, which is
# swaylock's own SYSCONFDIR path, so the package alone makes it effective.
# --- Enable systemd user services ---