mirror of
https://github.com/zed-industries/zed.git
synced 2026-07-09 16:00:35 +00:00
gpui: Add input region support for Wayland windows (#60161)
# Objective Wayland windows have no way to restrict which parts of the surface accept pointer and touch input. This adds support for setting an input region, so events outside it pass through to whatever is below the window. This is useful for shaped or partially click-through windows. Clicks in green area can pass through the window, clicks in red area do not: <img width="1057" height="395" alt="image" src="https://github.com/user-attachments/assets/2039af62-e43b-4834-b877-edad2a8f5ccf" /> ## Solution Add `Window::set_input_region`, which takes `Option<&[Bounds<Pixels>]>`: - `Some(rects)` restricts pointer and touch input to the union of the rectangles, in window coordinates. - `Some(&[])` is an empty region, so the window receives no input at all and is fully click-through. - `None` resets the region to the default, so the whole window receives input again. On Wayland this maps to `wl_surface.set_input_region`, building a `wl_region` from the rectangles or clearing it for `None`, and commits so the change applies immediately rather than waiting for the next frame. The method is a no-op on other platforms. ## Testing Tested on Linux with Wayland. - Tested in my own GPUI application, which uses a fullscreen layer for overlays while allowing clicks outside of the rendered elements to be passed through to the underlying windows. - No automated test was added, since this calls through to the compositor and is checked by observing input routing. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - N/A
This commit is contained in:
parent
fcd0f76952
commit
a29c0d41f4
3 changed files with 43 additions and 0 deletions
|
|
@ -702,6 +702,7 @@ pub trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
|||
fn show_window_menu(&self, _position: Point<Pixels>) {}
|
||||
fn start_window_move(&self) {}
|
||||
fn start_window_resize(&self, _edge: ResizeEdge) {}
|
||||
fn set_input_region(&self, _region: Option<&[Bounds<Pixels>]>) {}
|
||||
fn window_decorations(&self) -> Decorations {
|
||||
Decorations::Server
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Pixels>]>) {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -1489,6 +1489,38 @@ impl PlatformWindow for WaylandWindow {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_input_region(&self, region: Option<&[Bounds<Pixels>]>) {
|
||||
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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue