From 3eb731f8b516df1fb7917ee76275f18cf7997101 Mon Sep 17 00:00:00 2001 From: Silico_Biomancer Date: Tue, 25 Nov 2025 15:06:39 +1300 Subject: [PATCH] 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 --- src/changelog/unreleased.md | 1 + src/platform_impl/linux/x11/window.rs | 27 +++++++++++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index eee5740d8..58f0de13d 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -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. diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 2fd069fc5..7e15d5f24 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -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 click‑through. + // 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 = 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(())