git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit5207bda7d2253edff1d298f3aedd119e35d3d947
parent03348e1bfe
authorLucas Galante <[email protected]>
date2026-09-17 12:08
fix(input): Super-held adjust mode keeps focus; hover-to-focus is overview only

With Super held at zoom 1 the frame stays on the focused window rather
than following the pointer, so a focus chord pressed next (super+h/j/k/l)
acts on the window the user had. Any window's body still drags; the grab
focuses it on press. Overview is unchanged.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

 CLAUDE.md            |  6 ++++--
 src/server/cursor.rs | 15 ++++++++++-----
 2 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index ed280b7..3470cbf 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -426,8 +426,10 @@ where the old band sat outside them.
   this arrangement exists to prevent. Note the *grab* zone stays the full
   even band (the four catcher rects) even where the ring is drawn thin: the
   swell is ornament, and a corner you can see but not grab would be worse.
-- **Holding Super is window-adjust mode at zoom 1**: the same handles,
-  hover-to-focus and body-drag as overview, gated by one predicate,
+- **Holding Super is window-adjust mode at zoom 1**: the same handles and
+  body-drag as overview (but NOT hover-to-focus — the frame stays on the
+  focused window, so a focus chord pressed next acts on the window the
+  user had), gated by one predicate,
   `WindowManager::window_adjust_active()` (overview OR `adjust_held`).
   `adjust_held` is refreshed from the keyboard's modifier mask on every
   modifiers event (`refresh_adjust_held`), which also re-runs the pointer
diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 00029c8..686b4b9 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -876,10 +876,11 @@ impl Cursor {
                 && !hovered_chrome
                 && (*server).wm.window_adjust_active()
             {
-                // Focus follows the pointer in overview — and while Super is
-                // held, which is the same adjust mode at zoom 1: the ring is
-                // drawn on the focused window only, so hovering is how it
-                // moves between windows without a click. Guarded on an actual change —
+                // Focus follows the pointer in overview: the ring is drawn on
+                // the focused window only, so hovering is how it moves between
+                // windows without a click. (Super-held adjust mode takes this
+                // branch too, for the pointer-focus clear below, but not the
+                // refocus.) Guarded on an actual change —
                 // seat.focus raises a Floating window BEFORE its same-focus
                 // short-circuit, so an unguarded call would raise and relayout
                 // on every motion event. And with the pan suppressed: hovering
@@ -893,7 +894,11 @@ impl Cursor {
                 // launcher's own first configure — so a stationary pointer
                 // resting on a world window was refocusing that window and
                 // dismissing the launcher the instant it mapped.
-                if !hovered_toplevel.is_null()
+                // Overview only: with Super held at zoom 1 the frame stays
+                // on the focused window, so a focus chord pressed next acts
+                // on the window the user had, not the one under the pointer.
+                if (*server).wm.mode == crate::window_manager::WindowManagerMode::Overview
+                    && !hovered_toplevel.is_null()
                     && !(*self.seat).focus_is_chrome()
                     && (*self.seat).focused
                         != crate::seat::Focus::Window(hovered_toplevel)