Skip to content

Unpin pinned windows before parking; re-pin on restore - #8

Merged
GreyforgeLabs merged 1 commit into
GreyforgeLabs:mainfrom
keylimesoda:fix/pinned-park
Oct 15, 2026
Merged

GreyforgeLabs merged 1 commit into
GreyforgeLabs:mainfrom
keylimesoda:fix/pinned-park

Conversation

@keylimesoda

Copy link
Copy Markdown
Contributor

Symptom

SUPER+W on a pinned window — e.g. one put into overlay mode with Omarchy's Super+O (omarchy-hyprland-window-pop, which pins via hl.dsp.window.pin) — reports parked, the bar counts it, but the window stays exactly where it is. Hyprland silently no-ops a pinned window's move to a special workspace: window.move to special:reprieve returns ok and does nothing. The model then tracks a window that was never actually parked.

Root cause

  • snapshotFromHandle never captured pinned, and the park cut moved blindly.
  • lastIpcObject is only refreshed when toplevels are fetched, so even reading ipc.pinned at park time is unreliable: a pin applied after the shell's last refresh (the normal case — the user just pressed Super+O) is invisible to the snapshot.

Fix

  • Park cut: hl.dsp.window.pin({ action: "disable" }) before moveSilent; restore re-enables the pin after the float restore (pin requires a floating window).
  • The cut now always waits for the existing fresh park geometry read (hyprctl -j clients) — including with flight off — and the unpin decision plus the journal's pinned flag come from that read, not from the stale snapshot. Model.updatePinned corrects the parked action from the read before any undo can run.
  • pinned is carried through parkAction, undoEffects, redo live update, journal round-trip (old journals parse as unpinned), and stranded recovery.

Tests

  • Model: pinned park/undo, redo live state, journal round-trip, updatePinned correction incl. unknown-address no-op.
  • Service: unpin precedes the move, re-pin follows the float restore; flight-off park still runs the read-gated cut.
  • End-to-end in a headless Hyprland box: pin applied after shell start (stale lastIpcObject), park → window lands in special:reprieve with pinned=false; undo → back on its workspace pinned=true floating=true; journal persists pinned: true.

No behavior change for unpinned windows: the pin dispatches are gated on the read's pinned flag.

SUPER+W on a pinned window (Omarchy overlay mode, Super+O) reported
"parked" but left the window on screen: Hyprland silently no-ops a
pinned window's move to a special workspace, so the park's moveSilent
did nothing while the model tracked the window as parked.

The park cut now disables the pin before the move, and restore
re-enables it after the float restore (pin requires a floating
window). pinned is carried through the park action, undo effects,
redo, journal round-trip, and stranded recovery.

lastIpcObject is only refreshed when toplevels are fetched, so a pin
applied after the last refresh is invisible to the park snapshot. The
unpin decision and the journal's pinned flag therefore come from the
fresh park geometry read (hyprctl clients) instead: the cut always
waits for that read (including with flight off), and Model.updatePinned
corrects the parked action from it before any undo can run.

Verified end-to-end in a headless Hyprland box: park moves the pinned
window to special:reprieve with pinned=false, undo returns it to its
workspace pinned and floating, and the journal persists pinned=true.
@GreyforgeLabs
GreyforgeLabs merged commit 991fa1e into GreyforgeLabs:main Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants