winit-x11: replace xfixes with x11rb in set_hittest

The xfixes implementation is not that reliable and rather simple to
replace, so use x11rb to implement the same functionality.

Fixes #4120.
Co-authored-by: avitran0 <holyhades64@gmail.com>
This commit is contained in:
Silico_Biomancer
2025-11-25 15:06:39 +13:00
committed by Kirill Chibisov
parent 7035dd554f
commit 3eb731f8b5
2 changed files with 22 additions and 6 deletions

View File

@@ -43,3 +43,4 @@ changelog entry.
### Fixed
- On macOS, fixed crash when dragging non-file content onto window.
- On X11, fix `set_hittest` not working on some window managers.

View File

@@ -8,9 +8,8 @@ use std::{cmp, env};
use tracing::{debug, info, warn};
use x11rb::connection::Connection;
use x11rb::properties::{WmHints, WmSizeHints, WmSizeHintsSpecification};
use x11rb::protocol::shape::SK;
use x11rb::protocol::xfixes::{ConnectionExt, RegionWrapper};
use x11rb::protocol::xproto::{self, ConnectionExt as _, Rectangle};
use x11rb::protocol::shape::{ConnectionExt as ShapeExt, SK, SO};
use x11rb::protocol::xproto::{self, ClipOrdering, ConnectionExt as _, Rectangle};
use x11rb::protocol::{randr, xinput};
use crate::cursor::{Cursor, CustomCursor as RootCustomCursor};
@@ -1622,6 +1621,15 @@ impl UnownedWindow {
#[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
// Implement cursor hittest for X11 by either setting an empty or full window input shape.
// In X11, every window has two "shapes":
// * Bounding shape: defines the visible outline of the window.
// * Input shape: defines the region of the window that receives pointer/keyboard events.
// If the input shape is the full window rectangle, the window behaves normally.
// If the input shape is empty, the window is completely clickthrough.
// Here, we implement hit test by mapping `hittest = true` to "restore a full input shape"
// and `hittest = false` to "clear the input shape" (empty list of rectangles).
let mut rectangles: Vec<Rectangle> = Vec::new();
if hittest {
let size = self.inner_size();
@@ -1632,11 +1640,18 @@ impl UnownedWindow {
height: size.height as u16,
})
}
let region = RegionWrapper::create_region(self.xconn.xcb_connection(), &rectangles)
.map_err(|_e| ExternalError::Ignored)?;
self.xconn
.xcb_connection()
.xfixes_set_window_shape_region(self.xwindow, SK::INPUT, 0, 0, region.region())
.shape_rectangles(
SO::SET,
SK::INPUT,
ClipOrdering::UNSORTED,
self.xwindow,
0,
0,
&rectangles,
)
.map_err(|_e| ExternalError::Ignored)?;
self.shared_state_lock().cursor_hittest = Some(hittest);
Ok(())