fix(quickshell): let popout-spawned prompts take keyboard focus
Update PKGBUILD version / update-pkgver (push) Successful in 5s

The popout used Exclusive layer-shell keyboard focus while open. On the overlay layer that sits above all normal windows, so a keyring/PSK/polkit prompt spawned by a popout action (e.g. selecting a VPN) mapped unfocused and stayed untypeable — focus was stuck on quickshell.

Switch to OnDemand. Verified on niri 26.04: a keybind-opened popout still grabs the keyboard on open (Escape/Tab/arrow navigation preserved) while an externally-mapped toplevel can steal focus. General fix — covers every popout and every prompt type, no per-action handler.
This commit is contained in:
2026-07-09 09:17:09 +02:00
parent 9566f0d45c
commit 9760400c18
2 changed files with 12 additions and 3 deletions
+6
View File
@@ -236,3 +236,9 @@
- **Why**: Each `Bar` owns a per-screen `PopoutState`, so on a multi-monitor setup a popout could stay open on screen 1 while another opened on screen 2. Worse, popout `shown` drives *global* singleton services (`adapter.discovering`, `wifiDev.scannerEnabled`), so closing one screen's popout could stop the service under another screen's still-open popout. A popout is a single focused interaction — only one should exist at a time.
- **Tradeoffs**: Kept the per-screen `PopoutState` (rendering + anchor coordinates are screen-local; a single global state would either mirror the popout onto every monitor or need a screen field threaded through `open()`/`PopoutHost`). Added only the minimal cross-screen coordination — one `owner` token. Rejected a central registry (the composable-popouts decision just removed one). Not a compositor grab: pure QML state, so it never preempts other windows.
- **How**: New `PopoutBus.qml` singleton with `property var owner`. `PopoutState.open()` sets `PopoutBus.owner = root`; `close()` clears it if still owned. A `readonly property bool ownsSlot: PopoutBus.owner === root` + `onOwnsSlotChanged` folds up any state that loses the slot (no loop: a foreign-close leaves `owner` untouched). `qmldir` regenerated (adds `singleton PopoutBus`). Verified: throwaway `quickshell -p …/moonarch``Configuration Loaded`, no binding loop/error; after deploy (`moonarch-git-r142.c34d1d2`) the running instance's log stayed error/warning-free through popout interaction, and the cross-screen fold was confirmed live on a multi-monitor setup.
## 2026-07-09 Quickshell popout: OnDemand keyboard focus (prompts spawned by a popout action can steal focus)
- **Who**: Dominik, ClaudeCode
- **Why**: A popout action that triggers an external auth toplevel left focus stuck on quickshell — reproduced with the network popout: select a VPN → Enter → `nmcli connection up` fires nm-applet's secret agent → gnome-keyring/gcr shows a GTK unlock prompt, but keyboard focus stayed on the popout, so the prompt was untypeable. Same class for Wi-Fi PSK, polkit, any dialog spawned by any popout. Not VPN-specific — the cause is the popout's keyboard-focus grab.
- **Tradeoffs**: Root cause is `PopoutHost`'s `WlrKeyboardFocus.Exclusive` while open. Per wlr-layer-shell, Exclusive on the overlay layer sits above all normal windows, so the compositor never focuses a newly-mapped toplevel while it holds. Empirically verified on niri 26.04 (`niri msg focused-window`): under **Exclusive**, a prompt maps with `is_focused=false` and `focused-window` stays `null` — no `WindowFocusChanged` fires, so a listen-and-close approach is impossible (there is no focus event to react to). Under **OnDemand**, a keybind-opened popout *still* grabs the keyboard on open (`focused-window: null` → Escape/Tab/arrow nav preserved) **and** an externally-mapped toplevel takes focus (`nm-connection-editor is_focused=true`). So OnDemand fixes it with no loss of the keyboard-nav feature and no per-action handler code. Rejected: closing the popout on the VPN toggle (symptom fix, per-action, doesn't generalise).
- **How**: `PopoutHost.qml``keyboardFocus: root.wantOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None` (was `Exclusive`), comment updated with the verified niri behaviour. One line; covers every popout and every prompt type.
@@ -50,9 +50,12 @@ PanelWindow {
visible: root.rendered
WlrLayershell.layer: WlrLayer.Overlay
// Exclusive while open so a keybind-opened popout gets the keyboard (Escape, navigation) with no
// click; None while closed to release it. Compositor keybinds still fire (niri sees keys first).
WlrLayershell.keyboardFocus: root.wantOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
// OnDemand while open so a keybind-opened popout still grabs the keyboard (Escape, navigation) with
// no click, yet an externally-mapped toplevel — a keyring/PSK/polkit prompt spawned by a popout
// action — can steal focus and become typeable. Exclusive would sit above all normal windows and
// block that prompt from ever getting the keyboard (verified on niri 26.04: under Exclusive the
// prompt maps with is_focused=false; under OnDemand it takes focus). None while closed to release it.
WlrLayershell.keyboardFocus: root.wantOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.namespace: "moonarch-popout"
// Backdrop — click outside the menu closes it. Our own MouseArea (not a compositor grab).