Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix(ctl): exit overview onto the focused window, not the idle pointer
`ccectl overview` (and `wm-mode normal`) ran the pointer-driven toggle,
whose exit re-centres on the hovered window or else on the virtual point
under the cursor. That is right for a gesture, but a socket command has no
pointer of its own: in a headless shadow the pointer rests at its startup
position on the background, so every scripted exit came back panned a
screen away from the windows, and the next scripted click landed on the
desktop and unfocused the window under test.
The socket commands now dispatch the halves instead of the toggle: the
keyed exit (OverviewExit — the focused window, falling back to the cursor
only when nothing is focused) when in overview, OverviewEnter otherwise.
The enter half is identical to the toggle's; only the exit changes.
Verified in a fresh shadow: with the pointer untouched at (100,100), an
`overview` on/off pair now brings the focused 800x600 window back
centred at (240,60) instead of at (863,360); `wm-mode overview` then
`wm-mode normal` lands the same.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
src/cce_ctl.rs | 2 +-
src/server/window_manager.rs | 30 ++++++++++++++++++++++++++----
2 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/src/cce_ctl.rs b/src/cce_ctl.rs
index 0577d31..9d6196e 100644
--- a/src/cce_ctl.rs
+++ b/src/cce_ctl.rs
@@ -60,7 +60,7 @@ fn usage(name: &str, to_stderr: bool) {
print(" place-next <app_id> <x> <y> # one-shot: next map of app_id lands near this layout pos");
print(" place-next-cell <app_id> <x> <y> # one-shot: next map covers the grid square at this pos,");
print(" # keeping its size and growing away from its neighbours");
- print(" overview");
+ print(" overview # toggle; the exit lands on the focused window, not the pointer");
print(" windows [--json] # list windows; --json emits one JSON object per line");
print(" status-hide-mode [true|false]");
print(" adjust-position-mode [true|false|query]");
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 23a48e9..7c535f1 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -3627,6 +3627,17 @@ impl WindowManager {
self.keep_status_bar_on_top();
}
+ /// The overview action a socket command stands for: the keyed
+ /// (focused-window) exit when in overview, else the enter. See the
+ /// "overview" arm of the control handler for why not the toggle.
+ fn overview_action_for_socket(&self) -> crate::config::Action {
+ if self.mode == WindowManagerMode::Overview {
+ crate::config::Action::OverviewExit
+ } else {
+ crate::config::Action::OverviewEnter
+ }
+ }
+
pub unsafe fn execute_action(&mut self, action: &crate::config::Action, command: Option<&str>) {
use crate::config::Action;
self.stop_panning_animation();
@@ -4097,9 +4108,19 @@ impl WindowManager {
self.execute_action(&crate::config::Action::Close, None);
"ok\n".to_string()
}
- // "expose" is the retired name for the overview toggle.
+ // "expose" is the retired name for the overview toggle. The
+ // halves rather than `Action::Overview`: the toggle's exit is
+ // cursor-driven (it lands on the hovered window, else on the
+ // virtual point under the pointer), which suits a gesture but
+ // not a socket command, whose pointer is wherever it was left —
+ // a scripted exit in a headless shadow, pointer idle at its
+ // startup position on the background, came back panned a
+ // screen away from every window. The keyed exit lands on the
+ // focused window and falls back to the cursor only when
+ // nothing is focused; the enter half is identical to the
+ // toggle's.
"overview" | "expose" => {
- self.execute_action(&crate::config::Action::Overview, None);
+ self.execute_action(&self.overview_action_for_socket(), None);
"ok\n".to_string()
}
"wm-mode" => {
@@ -4107,14 +4128,15 @@ impl WindowManager {
return format!("{:?}\n", self.mode).to_lowercase();
}
let target = parts[1].to_lowercase();
+ // Same pointer-less halves as the "overview" command above.
if target == "normal" {
if self.mode == WindowManagerMode::Overview {
- self.execute_action(&crate::config::Action::Overview, None);
+ self.execute_action(&crate::config::Action::OverviewExit, None);
}
return "ok\n".to_string();
} else if target == "overview" {
if self.mode == WindowManagerMode::Normal {
- self.execute_action(&crate::config::Action::Overview, None);
+ self.execute_action(&crate::config::Action::OverviewEnter, None);
}
return "ok\n".to_string();
}