#670

Managed focus on Hyprland KMS

Openaronjanoschwants to merge intofrom

Follow-up to #631. Managed focus now works under hyprland-kms through Hyprland's IPC socket, after checking that the socket's peer is the bound compositor (Hyprland doesn't authenticate IPC clients). Two fixes that also apply to KDE: the helper no longer uses up its attempts on a game that already has focus, and its output now shows up in the journal.

Tested on the same setup: focus is recovered after switching to a browser or Big Picture during the focus window.

Activity

14
Nonaryrequested changes14d ago
Nonary14d ago

Reviewed head: 9d1c42d8059a520195820095b3d5ce3e849f6081

Changes requested:

  1. P2: Compare the foreground window identity, not just its PID.

managed_app_focus_step.h:31–39 skips activation when the preferred game window and a different foreground window share a PID. A process with a fullscreen game and foreground launcher/dialog therefore stays on the wrong window. This affects the shared KWin and Hyprland path.

The production Hyprland adapter selected fullscreen 700:0xaaaa, while the active window was 700:0xbbbb; the production focus step then emitted zero activation requests. Compare the exact active window address/internalId with the preferred target. Add same-PID/wrong-window and exact-target-already-active cases for both compositors, preserving the latter's no-attempt-consumption behavior.

The focus-step and broker fixtures passed. The Hyprland focus fixture compiled, but stopped at the socket fixture's combined socket/bind/listen assertion; no errno identifies which operation failed. Socket transport and real compositor behavior remain unverified. The current PR CI run is action_required, not green.

#669 and this PR are sibling changes, not a dependency stack. Combining them currently produces conflicts in packaging/linux/vibepollo-session-broker.c and tests/unit/platform/linux/test_hyprland_broker.c. If #669 lands first, please resolve those in this branch, retaining its identity-bound Steam/Lutris DISPLAY checks alongside managed-focus support, and rerun both sets of tests on the resolved head.

aronjanosch12d ago

Rebased onto canary after #669; the broker and test conflicts keep the DISPLAY-gated Steam/Lutris launches next to managed-focus.

Both adapters now report the exact active window (pid:address on Hyprland, pid:internalId on KWin) and the focus step compares that with the preferred window instead of the PID. Added same-PID/wrong-window and exact-target-already-active cases for the step, the Hyprland adapter (through the production focus step) and the KWin script. The socket fixture now reports which of socket/bind/listen failed, with errno.

Live-tested on Arch + Hyprland KMS via Steam: Balatro got initial focus and two refocuses after another window took focus, with no extra activations while it stayed in front; FINAL FANTASY VII went through its launcher into the game without issues. Its launcher runs in a separate process, so the same-PID case is covered by the tests only.

aronjanosch11d ago

Fixed in 715ce6d6d: focus_step now reads the clock after the blocking observation and judges the foreground against the first eligible window, so an expired fullscreen window listed first no longer spends an attempt. Eligibility is checked immediately before the activation call; the Hyprland adapter still re-reads clients before dispatching. Added tests for both cases you described; both fail against the previous step.

2c023b4e0 also ranks only Hyprland fullscreen mode 2 first (mode 1 is maximized, so a maximized launcher could outrank the game) and closes a descriptor leak when fdopendir fails.

aronjanosch3d ago

Rebased onto canary.

e7f96e410: focus_step hands the adapter an allowance check. Hyprland asks it immediately before each dispatch (including the legacy fallback). test_hyprland_focus.cpp advances a fake clock inside the fake IPC replies: a slow second j/clients across the 15 s allowance, the same across the hard cap, and a refused Lua dispatch that crosses the deadline; none sends a focus request.

4ae828ad4: KWin rechecks between loadScript and run (kwin_load_and_run, tested the same way). A step now activates only the preferred window, so a refused game focus no longer falls through to a same-process launcher and counts as success; both adapters skip a target the second observation already finds active.

aee852e60 / d7d9f6d01: KWin results carry a call number, so a late result from an earlier script is not confirmed; results of the wrong shape or type are dropped instead of throwing.