mirror of
https://github.com/rust-windowing/winit.git
synced 2026-09-03 23:30:05 -04:00
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:
@@ -33,7 +33,7 @@ use winit_core::window::{Theme, Window as CoreWindow, WindowAttributes, WindowId
|
|||||||
use x11rb::connection::RequestConnection;
|
use x11rb::connection::RequestConnection;
|
||||||
use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError};
|
use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError};
|
||||||
use x11rb::protocol::xinput::{self, ConnectionExt as _};
|
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::x11_utils::X11Error as LogicalError;
|
||||||
use x11rb::xcb_ffi::ReplyOrIdError;
|
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() {
|
let xconn = match X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner()).as_ref() {
|
||||||
Ok(xconn) => xconn.clone(),
|
Ok(xconn) => xconn.clone(),
|
||||||
|
Err(XNotSupported::ExtensionNotSupported(reason)) => {
|
||||||
|
return Err(NotSupportedError::new(reason).into());
|
||||||
|
},
|
||||||
Err(err) => return Err(os_error!(err.clone()).into()),
|
Err(err) => return Err(os_error!(err.clone()).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -268,27 +271,40 @@ impl EventLoop {
|
|||||||
|
|
||||||
let ime = ime.ok().map(RefCell::new);
|
let ime = ime.ok().map(RefCell::new);
|
||||||
|
|
||||||
let randr_event_offset =
|
let randr_event_offset = xconn.select_xrandr_input(root).map_err(|err| match err {
|
||||||
xconn.select_xrandr_input(root).expect("Failed to query XRandR extension");
|
X11Error::MissingExtension(_) => EventLoopError::NotSupported(NotSupportedError::new(
|
||||||
|
"the X11 backend requires XRandR 1.2 or newer",
|
||||||
|
)),
|
||||||
|
error => os_error!(error).into(),
|
||||||
|
})?;
|
||||||
|
|
||||||
let xi2ext = xconn
|
let xi2ext = xconn
|
||||||
.xcb_connection()
|
.xcb_connection()
|
||||||
.extension_information(xinput::X11_EXTENSION_NAME)
|
.extension_information(xinput::X11_EXTENSION_NAME)
|
||||||
.expect("Failed to query XInput extension")
|
.map_err(|err| os_error!(X11Error::from(err)))?
|
||||||
.expect("X server missing XInput extension");
|
.ok_or_else(|| {
|
||||||
|
NotSupportedError::new("the X11 backend requires XInput 2.0 or newer")
|
||||||
|
})?;
|
||||||
let xkbext = xconn
|
let xkbext = xconn
|
||||||
.xcb_connection()
|
.xcb_connection()
|
||||||
.extension_information(xkb::X11_EXTENSION_NAME)
|
.extension_information(xkb::X11_EXTENSION_NAME)
|
||||||
.expect("Failed to query XKB extension")
|
.map_err(|err| os_error!(X11Error::from(err)))?
|
||||||
.expect("X server missing XKB extension");
|
.ok_or_else(|| NotSupportedError::new("the X11 backend requires XKB 1.0 or newer"))?;
|
||||||
|
|
||||||
// Check for XInput2 support.
|
// Check for XInput2 support.
|
||||||
xconn
|
xconn
|
||||||
.xcb_connection()
|
.xcb_connection()
|
||||||
.xinput_xi_query_version(2, 3)
|
.xinput_xi_query_version(2, 3)
|
||||||
.expect("Failed to send XInput2 query version request")
|
.map_err(|err| os_error!(X11Error::from(err)))?
|
||||||
.reply()
|
.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);
|
xconn.update_cached_wm_info(root);
|
||||||
|
|
||||||
@@ -338,8 +354,8 @@ impl EventLoop {
|
|||||||
.expect("Failed to register the event loop waker source");
|
.expect("Failed to register the event loop waker source");
|
||||||
let event_loop_proxy = EventLoopProxy::new(user_waker);
|
let event_loop_proxy = EventLoopProxy::new(user_waker);
|
||||||
|
|
||||||
let xkb_context =
|
let xkb_context = Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection())
|
||||||
Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection()).unwrap();
|
.map_err(|_| NotSupportedError::new("the X11 backend requires XKB 1.0 or newer"))?;
|
||||||
|
|
||||||
let mut xmodmap = util::ModifierKeymap::new();
|
let mut xmodmap = util::ModifierKeymap::new();
|
||||||
xmodmap.reload_from_x_connection(&xconn);
|
xmodmap.reload_from_x_connection(&xconn);
|
||||||
@@ -411,7 +427,7 @@ impl EventLoop {
|
|||||||
| xkb::EventType::MAP_NOTIFY
|
| xkb::EventType::MAP_NOTIFY
|
||||||
| xkb::EventType::STATE_NOTIFY,
|
| xkb::EventType::STATE_NOTIFY,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.map_err(|err| os_error!(err))?;
|
||||||
|
|
||||||
event_processor.init_device(ALL_DEVICES);
|
event_processor.init_device(ALL_DEVICES);
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ use std::{fmt, ptr};
|
|||||||
|
|
||||||
use rwh_06::HasDisplayHandle;
|
use rwh_06::HasDisplayHandle;
|
||||||
use winit_core::cursor::CursorIcon;
|
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::randr::ConnectionExt as _;
|
||||||
use x11rb::protocol::render;
|
|
||||||
use x11rb::protocol::xproto::{self, ConnectionExt};
|
use x11rb::protocol::xproto::{self, ConnectionExt};
|
||||||
|
use x11rb::protocol::{ErrorKind, randr, render};
|
||||||
use x11rb::resource_manager;
|
use x11rb::resource_manager;
|
||||||
use x11rb::xcb_ffi::XCBConnection;
|
use x11rb::xcb_ffi::XCBConnection;
|
||||||
|
|
||||||
@@ -119,11 +120,28 @@ impl XConnection {
|
|||||||
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
|
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
|
||||||
|
|
||||||
// Load the RandR version.
|
// 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
|
let randr_version = xcb
|
||||||
.randr_query_version(1, 3)
|
.randr_query_version(1, 3)
|
||||||
.expect("failed to request XRandR version")
|
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?
|
||||||
.reply()
|
.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);
|
let xsettings_screen = Self::new_xsettings_screen(&xcb, default_screen);
|
||||||
if xsettings_screen.is_none() {
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum XNotSupported {
|
pub enum XNotSupported {
|
||||||
/// Failed to load one or several shared libraries.
|
/// 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.
|
/// We encountered an error while converting the connection to XCB.
|
||||||
XcbConversionError(Arc<dyn Error + Send + Sync + 'static>),
|
XcbConversionError(Arc<dyn Error + Send + Sync + 'static>),
|
||||||
|
|
||||||
|
/// A required X11 extension is not supported.
|
||||||
|
ExtensionNotSupported(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ffi::OpenError> for XNotSupported {
|
impl From<ffi::OpenError> for XNotSupported {
|
||||||
@@ -364,6 +385,7 @@ impl XNotSupported {
|
|||||||
XNotSupported::LibraryOpenError(_) => "Failed to load one of xlib's shared libraries",
|
XNotSupported::LibraryOpenError(_) => "Failed to load one of xlib's shared libraries",
|
||||||
XNotSupported::XOpenDisplayFailed => "Failed to open connection to X server",
|
XNotSupported::XOpenDisplayFailed => "Failed to open connection to X server",
|
||||||
XNotSupported::XcbConversionError(_) => "Failed to convert Xlib connection to XCB",
|
XNotSupported::XcbConversionError(_) => "Failed to convert Xlib connection to XCB",
|
||||||
|
XNotSupported::ExtensionNotSupported(reason) => reason,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ changelog entry.
|
|||||||
|
|
||||||
### Fixed
|
### 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
|
- 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
|
tools such as Punto Switcher. The `WM_INPUTLANGCHANGE` message is now handled
|
||||||
to refresh the cached keyboard layout, while still deferring to
|
to refresh the cached keyboard layout, while still deferring to
|
||||||
|
|||||||
Reference in New Issue
Block a user