diff --git a/dotfiles/noctalia/.local/state/noctalia/settings.toml b/dotfiles/noctalia/.local/state/noctalia/settings.toml index 8f720a4..0f20afc 100644 --- a/dotfiles/noctalia/.local/state/noctalia/settings.toml +++ b/dotfiles/noctalia/.local/state/noctalia/settings.toml @@ -112,8 +112,8 @@ widget_order = [ [lockscreen_widgets.widget."lockscreen-login-box@eDP-1"] box_height = 196.0 box_width = 810.0 - cx = 960.0 - cy = 852.5 + cx = 960.000244140625 + cy = 852.500244140625 output = "eDP-1" placement_height = 1080.0 placement_width = 1920.0 diff --git a/dotfiles/zz-blankpad/.config/systemd/user/blankpad-desk-autoswitch.service b/dotfiles/zz-blankpad/.config/systemd/user/blankpad-desk-autoswitch.service index 5d68c7a..4bc7cc4 100644 --- a/dotfiles/zz-blankpad/.config/systemd/user/blankpad-desk-autoswitch.service +++ b/dotfiles/zz-blankpad/.config/systemd/user/blankpad-desk-autoswitch.service @@ -5,15 +5,13 @@ ConditionHost=blankpad [Service] Type=simple -User=%u -Group=%u -ExecStart=/usr/bin/env bash %h/.omp/scripts/desk-autoswitch.sh watch +ExecStart=%h/.local/bin/desk-autoswitch.sh watch Restart=on-failure RestartSec=5 StandardOutput=journal StandardError=journal Environment=DESK_NIRI=/usr/bin/niri -Environment=DESK_NOTIFY=noctalia msg notification-show +Environment="DESK_NOTIFY=noctalia msg notification-show" [Install] WantedBy=default.target diff --git a/dotfiles/zz-blankpad/.local/bin/desk-autoswitch.sh b/dotfiles/zz-blankpad/.local/bin/desk-autoswitch.sh new file mode 120000 index 0000000..e64742a --- /dev/null +++ b/dotfiles/zz-blankpad/.local/bin/desk-autoswitch.sh @@ -0,0 +1 @@ +../../../../scripts/desk-autoswitch.sh \ No newline at end of file diff --git a/omp-docs/log.md b/omp-docs/log.md index b0c23be..0b467fa 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -15,7 +15,7 @@ timestamp: 2026-08-28 Branch `blankpad-de-redesign` (worktree `wt/de-redesign-20261009`). Full sweep + redesign of the niri/noctalia/Handy stack on blankpad. -* **Desk profiles.** `cfg/display.kdl` retired to a symlink; per-desk profiles `desk-1-home.kdl` / `desk-2-portable.kdl` declare monitors by **EDID long name** (`// requires:` lines) — connector names reshuffle between docks, identity travels. `scripts/desk.sh` = list/current/switch/cycle/match/capture; apply = relink → `niri validate` (rollback on failure) → `load-config-file` → noctalia notification. `scripts/desk-autoswitch.sh` + `blankpad-desk-autoswitch.service` watch the `niri msg --json event-stream` for `OutputsChanged` and hot-apply the matching profile (debounced 1.5 s). PM1601 EDID still uncaptured (panel disconnected) — connector-keyed placeholder, `desk.sh capture` pending. +* **Desk profiles.** `cfg/display.kdl` retired to a symlink; per-desk profiles `desk-1-home.kdl` / `desk-2-portable.kdl` declare monitors by **EDID long name** (`// requires:` lines) — connector names reshuffle between docks, identity travels. `scripts/desk.sh` = list/current/switch/cycle/match/capture; apply = relink → `niri validate` (rollback on failure) → `load-config-file` → noctalia notification. `scripts/desk-autoswitch.sh` + `blankpad-desk-autoswitch.service` watch `niri msg --json event-stream`: niri 26.04 has **no hotplug event** (`OutputConfigChanged` is only an ACK that an IPC/config output change was Applied, per niri-ipc); the daemon diffs the sorted output set carried by `WorkspacesChanged` and hot-applies the matching profile (debounced 1.5 s). Proven live with an eDP-1 off/on cycle: `[DP-2 eDP-1] → [DP-2] → [DP-2 eDP-1]`, apply + reload each time. PM1601 EDID still uncaptured (panel disconnected) — connector-keyed placeholder, `desk.sh capture` pending. * **Keybinds.** `swap-window-left/right` on Mod+CTRL+H/L; move-column demoted to Mod+CTRL+Alt+H/L; 7 column-width presets 12.5–87.5 %; Print trio (full / gui / screen) via noctalia screenshot IPC; launcher Mod+D; Alt+Tab → noctalia window-switcher; Mod+V → clipboard panel (id verified live); Mod+Shift+D → desk cycle; Mod+Shift+Space → `handy --toggle-post-process`; lid-close → `noctalia msg session lock-and-suspend` (pairs with logind `HandleLidSwitch=ignore`). Cheat sheet now GENERATED: `scripts/gen-keybinds.sh` → `omp-docs/tools/niri/keybindings.md` (119 bindings). * **Noctalia.** `config.toml` + `settings.toml` (lives in `~/.local/state/`, not `~/.config/`) tracked; `taskbar` widget added to bar center, applied live via `noctalia msg config-reload`. Docs corrected to the v5 `noctalia msg` API. * **Handy.** Self-autostart killed (`~/.config/autostart/Handy.desktop` → `.AGENTTRASH`; `autostart_enabled=false`); niri owns autostart (`spawn-at-startup "handy" "--start-hidden"`). Fleet post-processing wired into the live store: provider `custom` → `http://192.168.0.143:7771/v1`, model `frank-secondary`, prompt `default_improve_transcriptions`. **Open breaker:** frank-secondary answers in `reasoning_content` and leaves `content` empty; per-request cure `chat_template_kwargs.enable_thinking=false` verified, but Handy cannot send it and the server is shared with fleet compaction — dedicated no-think port proposed. 0.9.6→0.9.8 upgrade is operator-owned (`yay -Syu handy-bin`). diff --git a/omp-docs/tools/niri/displays.md b/omp-docs/tools/niri/displays.md index 87d43cf..e686f5b 100644 --- a/omp-docs/tools/niri/displays.md +++ b/omp-docs/tools/niri/displays.md @@ -56,7 +56,7 @@ the ultrawide wins when present and the laptop panel is the fallback. ```mermaid graph LR - P[plug / unplug] --> E[niri event-stream
OutputsChanged] + P[plug / unplug] --> E[niri event-stream
WorkspacesChanged → output-set diff] E --> M[desk-autoswitch.sh
debounced matcher] M --> R{profile requires
subset of connected?} R -->|best match| A[desk.sh apply:
validate - relink - reload - notify] diff --git a/scripts/desk-autoswitch.sh b/scripts/desk-autoswitch.sh index 5efc57f..29e48e8 100755 --- a/scripts/desk-autoswitch.sh +++ b/scripts/desk-autoswitch.sh @@ -1,6 +1,12 @@ #!/usr/bin/env bash # scripts/desk-autoswitch.sh — hotplug daemon: apply the matching desk profile -# whenever niri reports an OutputsChanged event. +# when the SET OF OUTPUTS changes. +# +# niri has no dedicated hotplug event (verified on 26.04): +# - WorkspacesChanged carries every workspace's `output`, so the sorted +# unique set of outputs changes exactly on connect/disconnect. +# - OutputConfigChanged is only an ACK that an IPC/config output change was +# Applied (niri-ipc), NOT a hotplug signal. # # Usage: desk-autoswitch.sh watch # @@ -8,7 +14,7 @@ # DESK_AUTO_HOSTNAME only run on this hostname (default: blankpad) # DESK_SCRIPT path to desk.sh (default ~/.local/bin/desk.sh) # DESK_NIRI niri binary (default niri) -# DESK_DEBOUNCE_S debounce window in seconds (default 1.5) +# DESK_DEBOUNCE_S min seconds between applies (default 1.5) set -euo pipefail @@ -17,8 +23,10 @@ DESK="${DESK_SCRIPT:-$HOME/.local/bin/desk.sh}" NIRI="${DESK_NIRI:-niri}" DEBOUNCE="${DESK_DEBOUNCE_S:-1.5}" -usage() { - sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//' +usage() { sed -n '2,18p' "$0" | sed 's/^# \{0,1\}//'; } + +outputs_from_event() { # $1 = one JSON event line + jq -r '[.WorkspacesChanged.workspaces[]?.output // empty] | unique | join(" ")' <<<"$1" 2>/dev/null || true } watch_mode() { @@ -26,16 +34,20 @@ watch_mode() { echo "desk-autoswitch: initial match" "$DESK" match --apply || true - local last=0 now + local seen="" last_seen="" last=0 now # One JSON event per line; reconnect forever (niri restart drops the stream). while "$NIRI" msg --json event-stream 2>/dev/null | while IFS= read -r line; do case "$line" in - *OutputsChanged*) + *WorkspacesChanged*) + seen="$(outputs_from_event "$line")" + [ -n "$seen" ] || continue + [ "$seen" = "$last_seen" ] && continue + last_seen="$seen" now="$(date +%s.%N)" # swallow bursts inside the debounce window if awk -v l="$last" -v n="$now" -v d="$DEBOUNCE" 'BEGIN { exit !(n - l >= d) }'; then last="$now" - echo "desk-autoswitch: OutputsChanged" + echo "desk-autoswitch: outputs changed [$seen]" "$DESK" match --apply || true fi ;; diff --git a/scripts/desk.sh b/scripts/desk.sh index 71d355b..4012786 100755 --- a/scripts/desk.sh +++ b/scripts/desk.sh @@ -61,7 +61,7 @@ requires() { notify() { # shellcheck disable=SC2086 # word splitting of the prefix is intended - $NOTIFY "Desk" "$1" 2>/dev/null || true + $NOTIFY "Desk" "$1" >/dev/null 2>/dev/null || true } # Apply a profile: relink, validate live config, roll back on failure,