srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2025-02-04 23:56:00 +0200
committersrdusr <[email protected]>2025-02-04 23:56:00 +0200
commit379466fe866f006700df9ace16c12ad78adb9a83 (patch)
treed9c5ae943328256df73223e3f4f310cc60decd80 /crates
parent31942ff440c6039b943b4975714dca25372f80c5 (diff)
downloadsrdwm-379466fe866f006700df9ace16c12ad78adb9a83.tar.gz
srdwm-379466fe866f006700df9ace16c12ad78adb9a83.zip
Fix layer-surface hit-testing giving up after the topmost bbox match fails
layer_surface_under_layers used smithay's LayerMap::layer_under(), which returns only the single topmost surface (by z-order) whose *bounding box* contains the point - not its real input region. If that one surface's input region excluded the point, the old code gave up on the whole layer-kind instead of falling through to whatever real surface is stacked underneath it. Concretely: any other surface on the same layer-kind with a bbox overlapping the target - a mapped-but-mostly-transparent backdrop/dismiss popup, concretely - would silently swallow every hover and click meant for whatever's underneath, with no way to reach it at all. Same failure shape as an already-fixed AGS-side bug (Overview's own bbox-wide input-region fallback), just compositor-side and not limited to that one instance. Now walks every candidate on a layer-kind topmost-first and tries the next one down when a candidate's actual input region doesn't cover the point, instead of stopping at the first bounding-box match.
Diffstat (limited to 'crates')
-rw-r--r--crates/wayland/src/input.rs29
1 files changed, 25 insertions, 4 deletions
diff --git a/crates/wayland/src/input.rs b/crates/wayland/src/input.rs
index fcffc19..f737957 100644
--- a/crates/wayland/src/input.rs
+++ b/crates/wayland/src/input.rs
@@ -67,10 +67,31 @@ pub(crate) fn layer_surface_under_layers(state: &CompState, pos: Point<f64, Logi
let local = pos - origin.to_f64();
let map = layer_map_for_output(&entry.output);
for layer_kind in layers {
- let Some(layer) = map.layer_under(layer_kind, local) else { continue };
- let Some(geo) = map.layer_geometry(layer) else { continue };
- if let Some((surface, surface_loc)) = layer.surface_under(local - geo.loc.to_f64(), WindowSurfaceType::ALL) {
- return Some((surface, origin + geo.loc + surface_loc));
+ // Not `map.layer_under(layer_kind, local)` - that hands back only
+ // the single topmost surface whose *bounding box* contains `local`,
+ // and if that one surface's own input region excludes the point
+ // (its `surface_under` below returns `None`), the old code gave up
+ // on this whole layer-kind rather than trying whatever real,
+ // clickable surface is stacked underneath it. A bbox-only pick is
+ // exactly wrong the moment two surfaces on the same layer-kind
+ // overlap - a transparent, mapped-but-mostly-empty surface (a
+ // backdrop-dismiss popup, concretely: `Overview`'s own bbox-wide
+ // fallback region was exactly this shape before it was fixed
+ // AGS-side) sitting in front of a real one in z-order would
+ // silently swallow every click and even every hover/motion event
+ // meant for the surface underneath, with no way to reach it at
+ // all. Walking every candidate on this layer-kind, topmost first
+ // (`.rev()`, matching `layer_under`'s own z-order convention), and
+ // falling through to the next when a candidate's real input region
+ // doesn't cover the point, is what `layer_under` alone can't do.
+ for layer in map.layers_on(layer_kind).rev() {
+ let Some(geo) = map.layer_geometry(layer) else { continue };
+ if !geo.to_f64().contains(local) {
+ continue;
+ }
+ if let Some((surface, surface_loc)) = layer.surface_under(local - geo.loc.to_f64(), WindowSurfaceType::ALL) {
+ return Some((surface, origin + geo.loc + surface_loc));
+ }
}
}
None