Replace parts of the Xlib backend with x11-rb

This commit is contained in:
John Nunley 2023-07-12 00:59:12 -07:00 committed by GitHub
parent 5379d60e4d
commit d7ec899d69
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1550 additions and 1395 deletions

View file

@ -1,5 +1,6 @@
#![cfg(x11_platform)]
mod atoms;
mod dnd;
mod event_processor;
pub mod ffi;
@ -25,10 +26,13 @@ use std::{
cell::{Cell, RefCell},
collections::{HashMap, HashSet, VecDeque},
ffi::CStr,
fmt,
mem::{self, MaybeUninit},
ops::Deref,
os::raw::*,
os::unix::io::RawFd,
os::{
raw::*,
unix::io::{AsRawFd, RawFd},
},
ptr,
rc::Rc,
slice,
@ -37,8 +41,20 @@ use std::{
};
use libc::{self, setlocale, LC_CTYPE};
use atoms::*;
use raw_window_handle::{RawDisplayHandle, XlibDisplayHandle};
use x11rb::protocol::{
xinput,
xproto::{self, ConnectionExt},
};
use x11rb::x11_utils::X11Error as LogicalError;
use x11rb::{
errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError},
xcb_ffi::ReplyOrIdError,
};
use self::{
dnd::{Dnd, DndState},
event_processor::EventProcessor,
@ -60,10 +76,10 @@ type X11Source = Generic<RawFd>;
pub struct EventLoopWindowTarget<T> {
xconn: Arc<XConnection>,
wm_delete_window: ffi::Atom,
net_wm_ping: ffi::Atom,
wm_delete_window: xproto::Atom,
net_wm_ping: xproto::Atom,
ime_sender: ImeSender,
root: ffi::Window,
root: xproto::Window,
ime: RefCell<Ime>,
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
redraw_sender: Sender<WindowId>,
@ -106,11 +122,11 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
impl<T: 'static> EventLoop<T> {
pub(crate) fn new(xconn: Arc<XConnection>) -> EventLoop<T> {
let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) };
let root = xconn.default_root().root;
let atoms = xconn.atoms();
let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") };
let net_wm_ping = unsafe { xconn.get_atom_unchecked(b"_NET_WM_PING\0") };
let wm_delete_window = atoms[WM_DELETE_WINDOW];
let net_wm_ping = atoms[_NET_WM_PING];
let dnd = Dnd::new(Arc::clone(&xconn))
.expect("Failed to call XInternAtoms when initializing drag and drop");
@ -149,7 +165,7 @@ impl<T: 'static> EventLoop<T> {
});
let randr_event_offset = xconn
.select_xrandr_input(root)
.select_xrandr_input(root as ffi::Window)
.expect("Failed to query XRandR extension");
let xi2ext = unsafe {
@ -223,7 +239,11 @@ impl<T: 'static> EventLoop<T> {
let handle = event_loop.handle();
// Create the X11 event dispatcher.
let source = X11Source::new(xconn.x11_fd, calloop::Interest::READ, calloop::Mode::Level);
let source = X11Source::new(
xconn.xcb_connection().as_raw_fd(),
calloop::Interest::READ,
calloop::Mode::Level,
);
handle
.insert_source(source, |_, _, _| Ok(calloop::PostAction::Continue))
.expect("Failed to register the X11 event dispatcher");
@ -254,8 +274,7 @@ impl<T: 'static> EventLoop<T> {
.expect("Failed to register the redraw event channel with the event loop");
let kb_state =
KbdState::from_x11_xkb(unsafe { (xconn.xlib_xcb.XGetXCBConnection)(xconn.display) })
.unwrap();
KbdState::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection()).unwrap();
let window_target = EventLoopWindowTarget {
ime,
@ -299,8 +318,12 @@ impl<T: 'static> EventLoop<T> {
// (The request buffer is flushed during `init_device`)
get_xtarget(&target)
.xconn
.select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask)
.queue();
.select_xinput_events(
root,
ffi::XIAllDevices as _,
x11rb::protocol::xinput::XIEventMask::HIERARCHY,
)
.expect_then_ignore_error("Failed to register for XInput2 device hotplug events");
get_xtarget(&target)
.xconn
@ -308,8 +331,7 @@ impl<T: 'static> EventLoop<T> {
0x100, // Use the "core keyboard device"
ffi::XkbNewKeyboardNotifyMask | ffi::XkbStateNotifyMask,
)
.unwrap()
.queue();
.unwrap();
event_processor.init_device(ffi::XIAllDevices);
@ -592,25 +614,24 @@ impl<T> EventLoopWindowTarget<T> {
let device_events = self.device_events.get() == DeviceEvents::Always
|| (focus && self.device_events.get() == DeviceEvents::WhenFocused);
let mut mask = 0;
let mut mask = xinput::XIEventMask::from(0u32);
if device_events {
mask = ffi::XI_RawMotionMask
| ffi::XI_RawButtonPressMask
| ffi::XI_RawButtonReleaseMask
| ffi::XI_RawKeyPressMask
| ffi::XI_RawKeyReleaseMask;
mask = xinput::XIEventMask::RAW_MOTION
| xinput::XIEventMask::RAW_BUTTON_PRESS
| xinput::XIEventMask::RAW_BUTTON_RELEASE
| xinput::XIEventMask::RAW_KEY_PRESS
| xinput::XIEventMask::RAW_KEY_RELEASE;
}
self.xconn
.select_xinput_events(self.root, ffi::XIAllMasterDevices, mask)
.queue();
.select_xinput_events(self.root, ffi::XIAllMasterDevices as _, mask)
.expect_then_ignore_error("Failed to update device event filter");
}
pub fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
let mut display_handle = XlibDisplayHandle::empty();
display_handle.display = self.xconn.display as *mut _;
display_handle.screen =
unsafe { (self.xconn.xlib.XDefaultScreen)(self.xconn.display as *mut _) };
display_handle.screen = self.xconn.default_screen_index() as c_int;
RawDisplayHandle::Xlib(display_handle)
}
}
@ -702,14 +723,133 @@ impl Drop for Window {
fn drop(&mut self) {
let window = self.deref();
let xconn = &window.xconn;
unsafe {
(xconn.xlib.XDestroyWindow)(xconn.display, window.id().0 as ffi::Window);
// If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about.
let _ = xconn.check_errors();
if let Ok(c) = xconn
.xcb_connection()
.destroy_window(window.id().0 as xproto::Window)
{
c.ignore_error();
}
}
}
/// Generic sum error type for X11 errors.
#[derive(Debug)]
pub enum X11Error {
/// An error from the Xlib library.
Xlib(XError),
/// An error that occurred while trying to connect to the X server.
Connect(ConnectError),
/// An error that occurred over the connection medium.
Connection(ConnectionError),
/// An error that occurred logically on the X11 end.
X11(LogicalError),
/// The XID range has been exhausted.
XidsExhausted(IdsExhausted),
/// Got `null` from an Xlib function without a reason.
UnexpectedNull(&'static str),
}
impl fmt::Display for X11Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X11Error::Xlib(e) => write!(f, "Xlib error: {}", e),
X11Error::Connect(e) => write!(f, "X11 connection error: {}", e),
X11Error::Connection(e) => write!(f, "X11 connection error: {}", e),
X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {}", e),
X11Error::X11(e) => write!(f, "X11 error: {:?}", e),
X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {}", s),
}
}
}
impl std::error::Error for X11Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
X11Error::Xlib(e) => Some(e),
X11Error::Connect(e) => Some(e),
X11Error::Connection(e) => Some(e),
X11Error::XidsExhausted(e) => Some(e),
_ => None,
}
}
}
impl From<XError> for X11Error {
fn from(e: XError) -> Self {
X11Error::Xlib(e)
}
}
impl From<ConnectError> for X11Error {
fn from(e: ConnectError) -> Self {
X11Error::Connect(e)
}
}
impl From<ConnectionError> for X11Error {
fn from(e: ConnectionError) -> Self {
X11Error::Connection(e)
}
}
impl From<LogicalError> for X11Error {
fn from(e: LogicalError) -> Self {
X11Error::X11(e)
}
}
impl From<ReplyError> for X11Error {
fn from(value: ReplyError) -> Self {
match value {
ReplyError::ConnectionError(e) => e.into(),
ReplyError::X11Error(e) => e.into(),
}
}
}
impl From<ime::ImeContextCreationError> for X11Error {
fn from(value: ime::ImeContextCreationError) -> Self {
match value {
ime::ImeContextCreationError::XError(e) => e.into(),
ime::ImeContextCreationError::Null => Self::UnexpectedNull("XOpenIM"),
}
}
}
impl From<ReplyOrIdError> for X11Error {
fn from(value: ReplyOrIdError) -> Self {
match value {
ReplyOrIdError::ConnectionError(e) => e.into(),
ReplyOrIdError::X11Error(e) => e.into(),
ReplyOrIdError::IdsExhausted => Self::XidsExhausted(IdsExhausted),
}
}
}
/// The underlying x11rb connection that we are using.
type X11rbConnection = x11rb::xcb_ffi::XCBConnection;
/// Type alias for a void cookie.
type VoidCookie<'a> = x11rb::cookie::VoidCookie<'a, X11rbConnection>;
/// Extension trait for `Result<VoidCookie, E>`.
trait CookieResultExt {
/// Unwrap the send error and ignore the result.
fn expect_then_ignore_error(self, msg: &str);
}
impl<'a, E: fmt::Debug> CookieResultExt for Result<VoidCookie<'a>, E> {
fn expect_then_ignore_error(self, msg: &str) {
self.expect(msg).ignore_error()
}
}
/// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to
/// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
struct GenericEventCookie<'a> {
@ -745,7 +885,7 @@ struct XExtension {
first_error_id: c_int,
}
fn mkwid(w: ffi::Window) -> crate::window::WindowId {
fn mkwid(w: xproto::Window) -> crate::window::WindowId {
crate::window::WindowId(crate::platform_impl::platform::WindowId(w as _))
}
fn mkdid(w: c_int) -> crate::event::DeviceId {