Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix(cursor): a click never focuses a layer surface that asked for none
A button press or touch-down on any layer surface called
seat.focus(LayerSurface), whatever its keyboard_interactivity. wlr-layer-shell
says a surface with interactivity none is never given keyboard focus, and the
first client that depends on it -- cce-keyboard, an on-screen keyboard on the
Overlay layer -- took focus off the window it was typing into on its first key,
so nothing it sent arrived.
layer_takes_click_focus gates both paths; on_demand and exclusive surfaces are
focused on click as before, and the press still counts as clicking something.
Verified in scale-1 and scale-2 shadows: cce-text-editor keeps focus=true
while cce-keyboard's keys are clicked, and receives the text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/server/cursor.rs | 25 +++++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 81ad5bc3..0d034fdf 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -1395,6 +1395,19 @@ unsafe fn is_cloud_layer(layer_surface: *mut crate::layer_shell::LayerSurface) -
.starts_with("cce-cloud")
}
+/// Whether a click or touch on this layer surface may give it keyboard
+/// focus. Not when it asked for none: per wlr-layer-shell such a surface is
+/// never given keyboard focus, and the one that relies on it — an on-screen
+/// keyboard (cce-keyboard), typing into the window it was clicked over —
+/// cannot work if a click on its keys takes focus off that window.
+unsafe fn layer_takes_click_focus(layer_surface: *mut crate::layer_shell::LayerSurface) -> bool {
+ if layer_surface.is_null() || (*layer_surface).wlr_layer_surface.is_null() {
+ return false;
+ }
+ (*(*layer_surface).wlr_layer_surface).current.keyboard_interactive
+ != ffi::zwlr_layer_surface_v1_keyboard_interactivity_ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE
+}
+
unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
let cursor = &mut *crate::container_of!(listener, Cursor, button_listener);
let event = data as *mut ffi::wlr_pointer_button_event;
@@ -2246,9 +2259,11 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
}
}
}
- SceneNodeDataVal::LayerSurface(_) => {
+ SceneNodeDataVal::LayerSurface(layer_surface) => {
clicked_something = true;
- seat.focus(Focus::LayerSurface(result.surface));
+ if layer_takes_click_focus(layer_surface) {
+ seat.focus(Focus::LayerSurface(result.surface));
+ }
}
SceneNodeDataVal::ShellSurface(_) | SceneNodeDataVal::LockSurface(_) | SceneNodeDataVal::OverrideRedirect(_) => {
clicked_something = true;
@@ -3574,8 +3589,10 @@ unsafe extern "C" fn handle_touch_down(listener: *mut ffi::wl_listener, data: *m
let server = seat.server;
if let Some(result) = (*server).scene.at(lx, ly) {
match result.data {
- SceneNodeDataVal::LayerSurface(_) => {
- seat.focus(Focus::LayerSurface(result.surface));
+ SceneNodeDataVal::LayerSurface(layer_surface) => {
+ if layer_takes_click_focus(layer_surface) {
+ seat.focus(Focus::LayerSurface(result.surface));
+ }
}
_ => {}
}