x11: fix crash when XInput 2 is unavailable

EventLoop::new() currently panics while handling the XIQueryVersion
reply on servers that only support XI1, such as x2goagent.

Propagate XInput initialization failures through the existing error path
and return NotSupported when the server does not support XI2.
This commit is contained in:
mTvare
2026-09-03 19:46:25 +05:30
committed by GitHub
parent 729985877a
commit 2700ac9a69
3 changed files with 57 additions and 17 deletions

View File

@@ -33,7 +33,7 @@ use winit_core::window::{Theme, Window as CoreWindow, WindowAttributes, WindowId
use x11rb::connection::RequestConnection;
use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError};
use x11rb::protocol::xinput::{self, ConnectionExt as _};
use x11rb::protocol::{xkb, xproto};
use x11rb::protocol::{ErrorKind, xkb, xproto};
use x11rb::x11_utils::X11Error as LogicalError;
use x11rb::xcb_ffi::ReplyOrIdError;
@@ -221,6 +221,9 @@ impl EventLoop {
let xconn = match X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner()).as_ref() {
Ok(xconn) => xconn.clone(),
Err(XNotSupported::ExtensionNotSupported(reason)) => {
return Err(NotSupportedError::new(reason).into());
},
Err(err) => return Err(os_error!(err.clone()).into()),
};
@@ -268,27 +271,40 @@ impl EventLoop {
let ime = ime.ok().map(RefCell::new);
let randr_event_offset =
xconn.select_xrandr_input(root).expect("Failed to query XRandR extension");
let randr_event_offset = xconn.select_xrandr_input(root).map_err(|err| match err {
X11Error::MissingExtension(_) => EventLoopError::NotSupported(NotSupportedError::new(
"the X11 backend requires XRandR 1.2 or newer",
)),
error => os_error!(error).into(),
})?;
let xi2ext = xconn
.xcb_connection()
.extension_information(xinput::X11_EXTENSION_NAME)
.expect("Failed to query XInput extension")
.expect("X server missing XInput extension");
.map_err(|err| os_error!(X11Error::from(err)))?
.ok_or_else(|| {
NotSupportedError::new("the X11 backend requires XInput 2.0 or newer")
})?;
let xkbext = xconn
.xcb_connection()
.extension_information(xkb::X11_EXTENSION_NAME)
.expect("Failed to query XKB extension")
.expect("X server missing XKB extension");
.map_err(|err| os_error!(X11Error::from(err)))?
.ok_or_else(|| NotSupportedError::new("the X11 backend requires XKB 1.0 or newer"))?;
// Check for XInput2 support.
xconn
.xcb_connection()
.xinput_xi_query_version(2, 3)
.expect("Failed to send XInput2 query version request")
.map_err(|err| os_error!(X11Error::from(err)))?
.reply()
.expect("Error while checking for XInput2 query version reply");
.map_err(|err| match err {
ReplyError::X11Error(error) if error.error_kind == ErrorKind::Request => {
EventLoopError::NotSupported(NotSupportedError::new(
"the X11 backend requires XInput 2.0 or newer",
))
},
error => os_error!(X11Error::from(error)).into(),
})?;
xconn.update_cached_wm_info(root);
@@ -338,8 +354,8 @@ impl EventLoop {
.expect("Failed to register the event loop waker source");
let event_loop_proxy = EventLoopProxy::new(user_waker);
let xkb_context =
Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection()).unwrap();
let xkb_context = Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection())
.map_err(|_| NotSupportedError::new("the X11 backend requires XKB 1.0 or newer"))?;
let mut xmodmap = util::ModifierKeymap::new();
xmodmap.reload_from_x_connection(&xconn);
@@ -411,7 +427,7 @@ impl EventLoop {
| xkb::EventType::MAP_NOTIFY
| xkb::EventType::STATE_NOTIFY,
)
.unwrap();
.map_err(|err| os_error!(err))?;
event_processor.init_device(ALL_DEVICES);

View File

@@ -7,10 +7,11 @@ use std::{fmt, ptr};
use rwh_06::HasDisplayHandle;
use winit_core::cursor::CursorIcon;
use x11rb::connection::Connection;
use x11rb::connection::{Connection, RequestConnection};
use x11rb::errors::ReplyError;
use x11rb::protocol::randr::ConnectionExt as _;
use x11rb::protocol::render;
use x11rb::protocol::xproto::{self, ConnectionExt};
use x11rb::protocol::{ErrorKind, randr, render};
use x11rb::resource_manager;
use x11rb::xcb_ffi::XCBConnection;
@@ -119,11 +120,28 @@ impl XConnection {
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
// Load the RandR version.
xcb.extension_information(randr::X11_EXTENSION_NAME)
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?
.ok_or(XNotSupported::ExtensionNotSupported(
"the X11 backend requires XRandR 1.2 or newer",
))?;
let randr_version = xcb
.randr_query_version(1, 3)
.expect("failed to request XRandR version")
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?
.reply()
.expect("failed to query XRandR version");
.map_err(|e| match e {
ReplyError::X11Error(error) if error.error_kind == ErrorKind::Request => {
XNotSupported::ExtensionNotSupported(
"the X11 backend requires XRandR 1.2 or newer",
)
},
error => XNotSupported::XcbConversionError(Arc::new(error)),
})?;
if (randr_version.major_version, randr_version.minor_version) < (1, 2) {
return Err(XNotSupported::ExtensionNotSupported(
"the X11 backend requires XRandR 1.2 or newer",
));
}
let xsettings_screen = Self::new_xsettings_screen(&xcb, default_screen);
if xsettings_screen.is_none() {
@@ -338,7 +356,7 @@ impl fmt::Display for XError {
}
}
/// Error returned if this system doesn't have XLib or can't create an X connection.
/// Error returned if this system doesn't support the X11 backend.
#[derive(Clone, Debug)]
pub enum XNotSupported {
/// Failed to load one or several shared libraries.
@@ -349,6 +367,9 @@ pub enum XNotSupported {
/// We encountered an error while converting the connection to XCB.
XcbConversionError(Arc<dyn Error + Send + Sync + 'static>),
/// A required X11 extension is not supported.
ExtensionNotSupported(&'static str),
}
impl From<ffi::OpenError> for XNotSupported {
@@ -364,6 +385,7 @@ impl XNotSupported {
XNotSupported::LibraryOpenError(_) => "Failed to load one of xlib's shared libraries",
XNotSupported::XOpenDisplayFailed => "Failed to open connection to X server",
XNotSupported::XcbConversionError(_) => "Failed to convert Xlib connection to XCB",
XNotSupported::ExtensionNotSupported(reason) => reason,
}
}
}

View File

@@ -111,6 +111,8 @@ changelog entry.
### Fixed
- On X11, return `NotSupported` instead of panicking when XInput 2.0, XRandR
1.2, or XKB 1.0 is unavailable.
- On Windows, fix a freeze that occurs when the keyboard layout is switched by
tools such as Punto Switcher. The `WM_INPUTLANGCHANGE` message is now handled
to refresh the cached keyboard layout, while still deferring to