diff --git a/winit-x11/src/event_loop.rs b/winit-x11/src/event_loop.rs index 78c2c0acb..71eab87a7 100644 --- a/winit-x11/src/event_loop.rs +++ b/winit-x11/src/event_loop.rs @@ -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); diff --git a/winit-x11/src/xdisplay.rs b/winit-x11/src/xdisplay.rs index 9eb190bc7..33d6a8d83 100644 --- a/winit-x11/src/xdisplay.rs +++ b/winit-x11/src/xdisplay.rs @@ -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), + + /// A required X11 extension is not supported. + ExtensionNotSupported(&'static str), } impl From 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, } } } diff --git a/winit/src/changelog/unreleased.md b/winit/src/changelog/unreleased.md index 5381bd83e..390a69d4c 100644 --- a/winit/src/changelog/unreleased.md +++ b/winit/src/changelog/unreleased.md @@ -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