diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 5765fd80aff..13fcbcee7a7 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -702,6 +702,7 @@ pub trait PlatformWindow: HasWindowHandle + HasDisplayHandle { fn show_window_menu(&self, _position: Point) {} fn start_window_move(&self) {} fn start_window_resize(&self, _edge: ResizeEdge) {} + fn set_input_region(&self, _region: Option<&[Bounds]>) {} fn window_decorations(&self) -> Decorations { Decorations::Server } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 198fab268e8..fcf10ac5576 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1999,6 +1999,16 @@ impl Window { self.platform_window.start_window_resize(edge); } + /// Linux (wayland) only: Set the window's input region, the area that receives pointer + /// and touch input. Events outside it pass through to whatever is below the window. + /// + /// - `Some(rects)` restricts input to the union of `rects`, in window coordinates. + /// - `Some(&[])` is an empty region, so the window receives no pointer or touch input. + /// - `None` resets the region to the default, so the whole window receives input again. + pub fn set_input_region(&self, region: Option<&[Bounds]>) { + self.platform_window.set_input_region(region); + } + /// Return the `WindowBounds` to indicate that how a window should be opened /// after it has been closed pub fn window_bounds(&self) -> WindowBounds { diff --git a/crates/gpui_linux/src/linux/wayland/window.rs b/crates/gpui_linux/src/linux/wayland/window.rs index da759cfebc7..731569a025f 100644 --- a/crates/gpui_linux/src/linux/wayland/window.rs +++ b/crates/gpui_linux/src/linux/wayland/window.rs @@ -1489,6 +1489,38 @@ impl PlatformWindow for WaylandWindow { } } + fn set_input_region(&self, region: Option<&[Bounds]>) { + let state = self.borrow(); + match region { + // No region means the whole surface receives input. + None => state.surface.set_input_region(None), + // A region restricts input to its rectangles. An empty region + // receives no input at all. + Some(rects) => { + let wl_region = state + .globals + .compositor + .create_region(&state.globals.qh, ()); + for rect in rects { + let rect = rect.map(|pixels| f32::from(pixels) as i32); + wl_region.add( + rect.origin.x, + rect.origin.y, + rect.size.width, + rect.size.height, + ); + } + state.surface.set_input_region(Some(&wl_region)); + wl_region.destroy(); + } + } + + // Commit so the new input region applies immediately. Otherwise it + // waits for the next frame, which could be the very click we want to + // allow passing through. + state.surface.commit(); + } + fn window_decorations(&self) -> Decorations { let state = self.borrow(); match state.decorations {