diff --git a/src/gl/x11.rs b/src/gl/x11.rs index c9b0a22b..53f60fee 100644 --- a/src/gl/x11.rs +++ b/src/gl/x11.rs @@ -6,10 +6,8 @@ use std::rc::Rc; use x11_dl::error::OpenError; use x11_dl::glx::GLXContext; -mod errors; -mod glx; -use crate::gl::x11::glx::GlxFbConfig; -use glx::Glx; +use crate::wrappers::glx::*; +use crate::wrappers::xlib::{XErrorHandler, XLibError}; #[derive(Debug)] pub enum CreationFailedError { @@ -18,12 +16,12 @@ pub enum CreationFailedError { GetProcAddressFailed, MakeCurrentFailed, ContextCreationFailed, - X11Error(errors::XLibError), + X11Error(XLibError), OpenError(OpenError), } -impl From for GlError { - fn from(e: errors::XLibError) -> Self { +impl From for GlError { + fn from(e: XLibError) -> Self { GlError::CreationFailed(CreationFailedError::X11Error(e)) } } @@ -64,12 +62,14 @@ impl GlContext { /// only then create the OpenGL context. /// /// Use [Self::get_fb_config_and_visual] to create both of these things. - pub unsafe fn create( + pub fn create( window: c_ulong, connection: Rc, config: FbConfig, ) -> Result { let glx = Glx::open()?; - errors::XErrorHandler::handle(&connection, |error_handler| { + let xlib_connection = connection.conn.xlib_connection(); + + XErrorHandler::handle(xlib_connection, |error_handler| { let Some(create_context) = glx.get_glx_create_context_attribs_arb() else { return Err(GlError::CreationFailed(CreationFailedError::GetProcAddressFailed)); }; @@ -79,7 +79,7 @@ impl GlContext { }; let context = create_context.call( - &connection, + xlib_connection, &config.gl_config, config.fb_config, error_handler, @@ -88,16 +88,22 @@ impl GlContext { // Create context object here so that error or panic will properly free the context let context = GlContext { glx, window, connection: Rc::clone(&connection), context }; - context.glx.with_current_context( - &connection, - window, - context.context, - error_handler, - || { - swap_interval(connection.dpy, window, config.gl_config.vsync as i32); - error_handler.check() - }, - )??; + unsafe { + context.glx.with_current_context( + xlib_connection, + window, + context.context, + error_handler, + || { + swap_interval( + xlib_connection.as_raw(), + window, + config.gl_config.vsync as i32, + ); + error_handler.check() + }, + )??; + } Ok(context) }) @@ -107,16 +113,19 @@ impl GlContext { /// This needs to be passed to [Self::create] along with a handle to a window that was created /// using the visual also returned from this function. pub fn get_fb_config_and_visual( - conn: &XcbConnection, config: GlConfig, + connection: &XcbConnection, config: GlConfig, ) -> Result<(FbConfig, WindowConfig), GlError> { let glx = Glx::open()?; - errors::XErrorHandler::handle(conn, |error_handler| { - let fb_config = glx.choose_best_fb_config(conn, &config, error_handler)?; + let xlib_connection = connection.conn.xlib_connection(); + + XErrorHandler::handle(xlib_connection, |error_handler| { + let fb_config = glx.choose_best_fb_config(xlib_connection, &config, error_handler)?; // Now that we have a matching framebuffer config, we need to know which visual matches // this config so the window is compatible with the OpenGL context we're about to create - let visual = glx.get_visual_from_fb_config(conn, fb_config, error_handler)?; + let visual = + glx.get_visual_from_fb_config(xlib_connection, fb_config, error_handler)?; Ok(( FbConfig { fb_config, gl_config: config }, @@ -126,16 +135,21 @@ impl GlContext { } pub unsafe fn make_current(&self) { - errors::XErrorHandler::handle(&self.connection, |error_handler| { + XErrorHandler::handle(self.connection.conn.xlib_connection(), |error_handler| { self.glx - .make_current(&self.connection, self.window, self.context, error_handler) + .make_current( + self.connection.conn.xlib_connection(), + self.window, + self.context, + error_handler, + ) .unwrap(); }) } pub unsafe fn make_not_current(&self) { - errors::XErrorHandler::handle(&self.connection, |error_handler| { - self.glx.clear_current(&self.connection, error_handler).unwrap(); + XErrorHandler::handle(self.connection.conn.xlib_connection(), |error_handler| { + self.glx.clear_current(self.connection.conn.xlib_connection(), error_handler).unwrap(); }) } @@ -149,14 +163,16 @@ impl GlContext { } pub fn swap_buffers(&self) { - errors::XErrorHandler::handle(&self.connection, |error_handler| { - self.glx.swap_buffers(&self.connection, self.window, error_handler).unwrap() + XErrorHandler::handle(self.connection.conn.xlib_connection(), |error_handler| { + self.glx + .swap_buffers(self.connection.conn.xlib_connection(), self.window, error_handler) + .unwrap() }) } } impl Drop for GlContext { fn drop(&mut self) { - unsafe { self.glx.destroy_context(&self.connection, self.context) } + unsafe { self.glx.destroy_context(self.connection.conn.xlib_connection(), self.context) } } } diff --git a/src/lib.rs b/src/lib.rs index 7ec7971d..29a7129a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,3 +22,5 @@ pub use mouse_cursor::MouseCursor; pub use window::*; pub use window_info::*; pub use window_open_options::*; + +pub(crate) mod wrappers; diff --git a/src/wrappers.rs b/src/wrappers.rs new file mode 100644 index 00000000..a392a348 --- /dev/null +++ b/src/wrappers.rs @@ -0,0 +1,20 @@ +#![allow(unsafe_code)] + +//! A set of safe wrappers around C or platform APIs. +//! +//! This module is designed to contain all unsafe code necessary for baseview to work, where safe +//! APIs do not yet exist in the Rust ecosystem (e.g. Win32, Xlib, GLX), or are not practical to use. +//! +//! These wrappers are not designed to fully encapsulate their respective APIs, only the bits used +//! by baseview. +//! +//! However, all of these APIs should always be sound (i.e. no UB can be triggered by safe code). +//! Otherwise, this should be considered a bug and reported accordingly. + +/// Wrappers and utilities around Xlib. (provided by x11_dl) +#[cfg(target_os = "linux")] +pub mod xlib; + +/// Wrappers and utilities around GLX +#[cfg(all(target_os = "linux", feature = "opengl"))] +pub mod glx; diff --git a/src/gl/x11/glx.rs b/src/wrappers/glx.rs similarity index 80% rename from src/gl/x11/glx.rs rename to src/wrappers/glx.rs index d794469f..be6ba4aa 100644 --- a/src/gl/x11/glx.rs +++ b/src/wrappers/glx.rs @@ -1,7 +1,7 @@ +use super::xlib::*; use crate::gl::platform::CreationFailedError; -use crate::gl::x11::errors::XErrorHandler; use crate::gl::{GlConfig, GlError, Profile}; -use crate::x11::XcbConnection; + use std::ffi::{c_ulong, c_void, CStr}; use std::os::raw::c_int; use std::ptr::NonNull; @@ -58,17 +58,17 @@ impl Glx { } pub fn choose_best_fb_config( - &self, connection: &XcbConnection, config: &GlConfig, error_handler: &XErrorHandler, + &self, connection: &XlibConnection, config: &GlConfig, error_handler: &XErrorHandler, ) -> Result { let fb_attribs = Self::get_fb_attribs(config); let mut nelements = 0; - // SAFETY: XcbConnection guarantees the inner dpy is valid. + // SAFETY: XlibConnection guarantees the inner dpy is valid. // The fb_attribs and nelements pointers come from references and are therefore valid. let result = unsafe { (self.inner.glXChooseFBConfig)( - connection.dpy, - connection.screen, + connection.as_raw(), + connection.default_screen_index(), fb_attribs.as_ptr(), &mut nelements, ) @@ -85,16 +85,17 @@ impl Glx { // SAFETY: for the same reasons above, glXChooseFBConfig returned a valid array // that we must free ourselves. - unsafe { (connection.xlib.XFree)(result.cast()) }; + unsafe { (connection.xlib().XFree)(result.cast()) }; Ok(GlxFbConfig(first_result)) } pub fn get_visual_from_fb_config( - &self, connection: &XcbConnection, fb_config: GlxFbConfig, error_handler: &XErrorHandler, + &self, connection: &XlibConnection, fb_config: GlxFbConfig, error_handler: &XErrorHandler, ) -> Result { - // SAFETY: XcbConnection guarantees the inner dpy is valid. - let result = unsafe { (self.inner.glXGetVisualFromFBConfig)(connection.dpy, fb_config.0) }; + // SAFETY: XlibConnection guarantees the inner dpy is valid. + let result = + unsafe { (self.inner.glXGetVisualFromFBConfig)(connection.as_raw(), fb_config.0) }; error_handler.check()?; if result.is_null() { @@ -106,16 +107,16 @@ impl Glx { let visual = unsafe { result.read() }; // SAFETY: for the same reasons above, glXGetVisualFromFBConfig returned a valid array // that we must free ourselves. - unsafe { (connection.xlib.XFree)(result.cast()) }; + unsafe { (connection.xlib().XFree)(result.cast()) }; Ok(visual) } pub fn swap_buffers( - &self, connection: &XcbConnection, window_id: c_ulong, error_handler: &XErrorHandler, + &self, connection: &XlibConnection, window_id: c_ulong, error_handler: &XErrorHandler, ) -> Result<(), GlError> { - // SAFETY: XcbConnection guarantees the inner dpy is valid. - unsafe { (self.inner.glXSwapBuffers)(connection.dpy, window_id) }; + // SAFETY: XlibConnection guarantees the inner dpy is valid. + unsafe { (self.inner.glXSwapBuffers)(connection.as_raw(), window_id) }; Ok(error_handler.check()?) } @@ -142,16 +143,17 @@ impl Glx { })) } - pub unsafe fn destroy_context(&self, connection: &XcbConnection, context: GLXContext) { - // SAFETY: - unsafe { (self.inner.glXDestroyContext)(connection.dpy, context) }; + pub unsafe fn destroy_context(&self, connection: &XlibConnection, context: GLXContext) { + // SAFETY: XlibConnection guarantees the inner dpy is valid. + unsafe { (self.inner.glXDestroyContext)(connection.as_raw(), context) }; } pub unsafe fn make_current( - &self, connection: &XcbConnection, window_id: c_ulong, context: GLXContext, + &self, connection: &XlibConnection, window_id: c_ulong, context: GLXContext, error_handler: &XErrorHandler, ) -> Result<(), GlError> { - let res = unsafe { (self.inner.glXMakeCurrent)(connection.dpy, window_id, context) }; + // SAFETY: XlibConnection guarantees the inner dpy is valid. + let res = unsafe { (self.inner.glXMakeCurrent)(connection.as_raw(), window_id, context) }; error_handler.check()?; if res == 0 { @@ -162,13 +164,13 @@ impl Glx { } pub unsafe fn clear_current( - &self, connection: &XcbConnection, error_handler: &XErrorHandler, + &self, connection: &XlibConnection, error_handler: &XErrorHandler, ) -> Result<(), GlError> { self.make_current(connection, 0, core::ptr::null_mut(), error_handler) } pub unsafe fn with_current_context( - &self, connection: &XcbConnection, window_id: c_ulong, context: GLXContext, + &self, connection: &XlibConnection, window_id: c_ulong, context: GLXContext, error_handler: &XErrorHandler, closure: impl FnOnce() -> T, ) -> Result { self.make_current(connection, window_id, context, error_handler)?; @@ -186,7 +188,7 @@ impl Glx { pub struct ContextClearOnDrop<'a> { glx: &'a Glx, - connection: &'a XcbConnection, + connection: &'a XlibConnection, error_handler: &'a XErrorHandler<'a>, } @@ -217,13 +219,19 @@ impl GlxCreateContextAttribsARB { } pub fn call( - &self, connection: &XcbConnection, gl_config: &GlConfig, glx_fb_config: GlxFbConfig, + &self, connection: &XlibConnection, gl_config: &GlConfig, glx_fb_config: GlxFbConfig, error_handler: &XErrorHandler, ) -> Result { let ctx_attribs = Self::get_ctx_attribs(gl_config); let context = unsafe { - self.0(connection.dpy, glx_fb_config.0, std::ptr::null_mut(), 1, ctx_attribs.as_ptr()) + self.0( + connection.as_raw(), + glx_fb_config.0, + std::ptr::null_mut(), + 1, + ctx_attribs.as_ptr(), + ) }; error_handler.check()?; diff --git a/src/wrappers/xlib.rs b/src/wrappers/xlib.rs new file mode 100644 index 00000000..e8db61d3 --- /dev/null +++ b/src/wrappers/xlib.rs @@ -0,0 +1,12 @@ +#[cfg(feature = "opengl")] +mod error_handler; +mod xlib_connection; +mod xlib_xcb; + +pub use xlib_xcb::XlibXcbConnection; + +#[cfg(feature = "opengl")] +pub use self::{ + error_handler::{XErrorHandler, XLibError}, + xlib_connection::XlibConnection, +}; diff --git a/src/gl/x11/errors.rs b/src/wrappers/xlib/error_handler.rs similarity index 79% rename from src/gl/x11/errors.rs rename to src/wrappers/xlib/error_handler.rs index f1808861..3a07bd5b 100644 --- a/src/gl/x11/errors.rs +++ b/src/wrappers/xlib/error_handler.rs @@ -1,8 +1,7 @@ -use std::ffi::CStr; use std::fmt::{Debug, Display, Formatter}; use x11_dl::xlib; -use crate::x11::XcbConnection; +use super::xlib_connection::XlibConnection; use std::cell::Cell; use std::error::Error; use std::os::raw::{c_int, c_uchar, c_ulong}; @@ -17,7 +16,7 @@ thread_local! { /// A helper struct for safe X11 error handling pub struct XErrorHandler<'a> { - conn: &'a XcbConnection, + conn: &'a XlibConnection, error: &'a Cell>, } @@ -25,7 +24,7 @@ impl<'a> XErrorHandler<'a> { /// Syncs and checks if any previous X11 calls from the given display returned an error pub fn check(&self) -> Result<(), XLibError> { // Flush all possible previous errors - unsafe { (self.conn.xlib.XSync)(self.conn.dpy, 0) }; + self.conn.sync(); let error = self.error.take(); @@ -37,7 +36,7 @@ impl<'a> XErrorHandler<'a> { /// Sets up a temporary X11 error handler for the duration of the given closure, and allows /// that closure to check on the latest X11 error at any time. - pub fn handle T>(conn: &XcbConnection, handler: F) -> T { + pub fn handle T>(conn: &XlibConnection, handler: F) -> T { /// # Safety /// The given display and error pointers *must* be valid for the duration of this function. unsafe extern "C" fn error_handler( @@ -60,19 +59,19 @@ impl<'a> XErrorHandler<'a> { } // Flush all possible previous errors - unsafe { (conn.xlib.XSync)(conn.dpy, 0) }; + conn.sync(); CURRENT_X11_ERROR.with(|error| { // Make sure to clear any errors from the last call to this function error.set(None); - let old_handler = unsafe { (conn.xlib.XSetErrorHandler)(Some(error_handler)) }; + let old_handler = conn.set_error_handler(Some(error_handler)); let panic_result = std::panic::catch_unwind(AssertUnwindSafe(|| { let mut h = XErrorHandler { conn, error }; handler(&mut h) })); // Whatever happened, restore old error handler - unsafe { (conn.xlib.XSetErrorHandler)(old_handler) }; + conn.set_error_handler(old_handler); match panic_result { Ok(v) => v, @@ -112,26 +111,11 @@ pub struct XLibError { } impl XLibError { - fn from_inner(inner: CaughtXLibError, conn: &XcbConnection) -> Self { - Self { display_name: Self::get_display_name(&inner, conn), inner } - } - - fn get_display_name(error: &CaughtXLibError, conn: &XcbConnection) -> Box { + fn from_inner(inner: CaughtXLibError, conn: &XlibConnection) -> Self { let mut buf = [0; 255]; - unsafe { - (conn.xlib.XGetErrorText)( - conn.dpy, - error.error_code.into(), - buf.as_mut_ptr().cast(), - (buf.len() - 1) as i32, - ) - }; - - *buf.last_mut().unwrap() = 0; - // SAFETY: whatever XGetErrorText did or not, we guaranteed there is a nul byte at the end of the buffer - let cstr = unsafe { CStr::from_ptr(buf.as_mut_ptr().cast()) }; - - cstr.to_string_lossy().into() + let cstr = conn.get_error_text(&mut buf, inner.error_code); + + Self { display_name: cstr.to_string_lossy().into(), inner } } } diff --git a/src/wrappers/xlib/xlib_connection.rs b/src/wrappers/xlib/xlib_connection.rs new file mode 100644 index 00000000..b43a7c53 --- /dev/null +++ b/src/wrappers/xlib/xlib_connection.rs @@ -0,0 +1,133 @@ +use std::error::Error; +use std::fmt::Formatter; +use std::os::raw::c_int; +use std::ptr::NonNull; +use x11_dl::xlib::{Display, Xlib}; +use x11_dl::xlib_xcb::{XEventQueueOwner, Xlib_xcb}; + +/// An owned Xlib Display connection. +/// +/// This type guarantees the inner display connection object to be alive and valid, as long as this +/// is alive. +/// +/// It will also always close the display connection on drop. +pub struct XlibConnection { + display: NonNull, + xlib: Box, + default_screen: c_int, +} + +impl XlibConnection { + pub fn open() -> Result> { + let xlib = Box::new(Xlib::open()?); + + // SAFETY: It's always safe to call XOpenDisplay with a NULL display_name + let ptr = unsafe { (xlib.XOpenDisplay)(core::ptr::null()) }; + + let Some(display) = NonNull::new(ptr) else { return Err(DisplayOpenFailedError.into()) }; + + let mut this = Self { display, xlib, default_screen: 0 }; + + this.default_screen = this.fetch_default_screen(); + + Ok(this) + } + + pub fn set_xcb_queue_owner(&self, xlib_xcb: &Xlib_xcb) { + // SAFETY: This type ensures the display pointer is always valid. + unsafe { + (xlib_xcb.XSetEventQueueOwner)( + self.display.as_ptr(), + XEventQueueOwner::XCBOwnsEventQueue, + ) + } + } + + /// Returns the index of the default screen for this X server. + pub fn default_screen_index(&self) -> c_int { + self.default_screen + } + + /// Safe wrapper for XDefaultScreen + fn fetch_default_screen(&self) -> c_int { + // SAFETY: This type ensures the display pointer is always valid. + unsafe { (self.xlib.XDefaultScreen)(self.display.as_ptr()) } + } + + pub fn as_raw(&self) -> *mut Display { + self.display.as_ptr() + } +} + +#[cfg(feature = "opengl")] +impl XlibConnection { + pub fn xlib(&self) -> &Xlib { + &self.xlib + } + + /// Calls XSync(0) + pub fn sync(&self) { + // SAFETY: This type ensures the display pointer is always valid. + unsafe { (self.xlib.XSync)(self.display.as_ptr(), 0) }; + } + + pub fn get_error_text( + &self, buf: &mut [u8], error_code: core::ffi::c_uchar, + ) -> &core::ffi::CStr { + if buf.is_empty() { + return c""; + } + + // PANIC: we just checked above that buf.len > 0 + let buf_len = buf.len() - 1; + let Ok(buf_len) = buf_len.try_into() else { + // Buffers should never get that big, something went horribly wrong. + return c""; + }; + + // SAFETY: This type ensures the display pointer is always valid. + // Moreover, the buffer pointer is guaranteed to be valid for writes for the given length, as it comes from the given mutable slice. + unsafe { + (self.xlib.XGetErrorText)( + self.as_raw(), + error_code.into(), + buf.as_mut_ptr().cast(), + buf_len, + ) + }; + + // PANIC: we checked above that buf.len > 0 + *buf.last_mut().unwrap() = 0; + + // SAFETY: whatever XGetErrorText did or not, we guaranteed there is a nul byte at the end of the buffer + unsafe { std::ffi::CStr::from_ptr(buf.as_mut_ptr().cast()) } + } + + pub fn set_error_handler( + &self, new_error_handler: Option, + ) -> Option { + // SAFETY: XSetErrorHandler is always safe to call as long as the function pointer is valid, + // which this guarantees + unsafe { (self.xlib.XSetErrorHandler)(new_error_handler) } + } +} + +#[cfg(feature = "opengl")] +type ErrorHandler = unsafe extern "C" fn(*mut Display, *mut x11_dl::xlib::XErrorEvent) -> c_int; + +impl Drop for XlibConnection { + fn drop(&mut self) { + // SAFETY: This type guarantees the display pointer is valid. + // This being `Drop` also prevents any double-free. + unsafe { (self.xlib.XCloseDisplay)(self.display.as_ptr()) }; + } +} + +#[derive(Debug)] +struct DisplayOpenFailedError; +impl std::fmt::Display for DisplayOpenFailedError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("Failed to open X11 display connection: XOpenDisplay() failed") + } +} +impl Error for DisplayOpenFailedError {} diff --git a/src/wrappers/xlib/xlib_xcb.rs b/src/wrappers/xlib/xlib_xcb.rs new file mode 100644 index 00000000..88e84396 --- /dev/null +++ b/src/wrappers/xlib/xlib_xcb.rs @@ -0,0 +1,74 @@ +use crate::wrappers::xlib::xlib_connection::XlibConnection; +use std::error::Error; +use std::ops::Deref; +use std::os::raw::c_int; +use x11_dl::xlib::Display; +use x11_dl::xlib_xcb::Xlib_xcb; +use x11rb::xcb_ffi::XCBConnection; + +/// A Xlib/XCB connection object. +/// +/// This exposes both a raw Xlib display pointer, and a x11rb XCBConnection object. +/// +/// This allows us to interface with the same connection using Xlib (needed for GLX, and for FFI), +/// as well as XCB (needed to preserve our sanity). +/// +/// (Note: The term Xlib/XCB means "Xlib over XCB", not "Xlib or XCB"). +pub struct XlibXcbConnection { + // SAFETY: Drop order matters here! We *MUST* Drop the XCBConnection first, as it essentially + // borrows the Xlib/XCB connection + xcb_connection: XCBConnection, + xlib_connection: XlibConnection, +} + +impl XlibXcbConnection { + pub fn open() -> Result> { + let xlib_xcb = Xlib_xcb::open()?; + // Open the connection to the X11 server as a Xlib/XCB connection object + let xlib_connection = XlibConnection::open()?; + // Set the XCB end of the Xlib/XCB connection object as the queue owner. + // From now on, we'll use XCB (i.e. X11rb) to interface with the event queue + xlib_connection.set_xcb_queue_owner(&xlib_xcb); + + // Extract the XCB connection object pointer from the Xlib/XCB connection object + // SAFETY: This is always safe to call as long as the OwnedDisplayConnection is alive + let xcb_connection = unsafe { (xlib_xcb.XGetXCBConnection)(xlib_connection.as_raw()) }; + + // The XGetXCBConnection function is not documented to ever be able to return NULL. + // Still, this is cheap to check, just in case. + assert!(!xcb_connection.is_null()); + + // Wrap the XCB connection object in a x11rb connection object + // SAFETY: The xcb_connection pointer should be valid. We also enforce the drop order in this + let xcb_connection = + unsafe { XCBConnection::from_raw_xcb_connection(xcb_connection, false)? }; + + Ok(Self { xcb_connection, xlib_connection }) + } + + pub fn default_screen(&self) -> c_int { + self.xlib_connection.default_screen_index() + } + + pub fn xlib_display(&self) -> *mut Display { + self.xlib_connection.as_raw() + } + + pub fn xcb_connection(&self) -> &XCBConnection { + &self.xcb_connection + } + + #[cfg(feature = "opengl")] + pub fn xlib_connection(&self) -> &XlibConnection { + &self.xlib_connection + } +} + +// For convenience +impl Deref for XlibXcbConnection { + type Target = XCBConnection; + + fn deref(&self) -> &Self::Target { + &self.xcb_connection + } +} diff --git a/src/x11/window.rs b/src/x11/window.rs index 7ab44cae..6414486d 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -264,10 +264,9 @@ impl<'a> Window<'a> { let window = window_id as c_ulong; // Because of the visual negotation we had to take some extra steps to create this context - let context = unsafe { + let context = platform::GlContext::create(window, Rc::clone(&xcb_connection), fb_config) - } - .expect("Could not create OpenGL context"); + .expect("Could not create OpenGL context"); GlContext::new(context) }); @@ -364,11 +363,11 @@ unsafe impl<'a> HasRawWindowHandle for Window<'a> { unsafe impl<'a> HasRawDisplayHandle for Window<'a> { fn raw_display_handle(&self) -> RawDisplayHandle { - let display = self.inner.xcb_connection.dpy; + let display = self.inner.xcb_connection.conn.xlib_display(); let mut handle = XlibDisplayHandle::empty(); handle.display = display as *mut c_void; - handle.screen = self.inner.xcb_connection.screen; + handle.screen = self.inner.xcb_connection.conn.default_screen(); RawDisplayHandle::Xlib(handle) } diff --git a/src/x11/xcb_connection.rs b/src/x11/xcb_connection.rs index c8725917..68ed2db8 100644 --- a/src/x11/xcb_connection.rs +++ b/src/x11/xcb_connection.rs @@ -1,20 +1,14 @@ use std::cell::RefCell; use std::collections::hash_map::{Entry, HashMap}; use std::error::Error; -use std::ffi::c_int; -use std::sync::Arc; -use x11_dl::xlib::Xlib; -use x11_dl::xlib_xcb::Xlib_xcb; -use x11_dl::{xlib::Display, xlib_xcb}; use x11rb::connection::Connection; use x11rb::cursor::Handle as CursorHandle; use x11rb::protocol::xproto::{Cursor, Screen}; use x11rb::resource_manager; -use x11rb::xcb_ffi::XCBConnection; - -use crate::MouseCursor; use super::cursor; +use crate::wrappers::xlib::XlibXcbConnection; +use crate::MouseCursor; x11rb::atom_manager! { pub Atoms: AtomsCookie { @@ -27,10 +21,7 @@ x11rb::atom_manager! { /// /// Keeps track of the xcb connection itself and the xlib display ID that was used to connect. pub struct XcbConnection { - pub(crate) xlib: Arc, - pub(crate) dpy: *mut Display, - pub(crate) conn: XCBConnection, - pub(crate) screen: c_int, + pub(crate) conn: XlibXcbConnection, pub(crate) atoms: Atoms, pub(crate) resources: resource_manager::Database, pub(crate) cursor_handle: CursorHandle, @@ -39,27 +30,16 @@ pub struct XcbConnection { impl XcbConnection { pub fn new() -> Result> { - let xlib = Xlib::open()?; - let dpy = unsafe { (xlib.XOpenDisplay)(std::ptr::null()) }; - assert!(!dpy.is_null()); - let xlib_xcb = Xlib_xcb::open()?; - let xcb_connection = unsafe { (xlib_xcb.XGetXCBConnection)(dpy) }; - assert!(!xcb_connection.is_null()); - let screen = unsafe { (xlib.XDefaultScreen)(dpy) }; - let conn = unsafe { XCBConnection::from_raw_xcb_connection(xcb_connection, false)? }; - unsafe { - (xlib_xcb.XSetEventQueueOwner)(dpy, xlib_xcb::XEventQueueOwner::XCBOwnsEventQueue) - }; + let conn = XlibXcbConnection::open()?; + let screen = conn.default_screen(); + let xcb_conn = conn.xcb_connection(); - let atoms = Atoms::new(&conn)?.reply()?; - let resources = resource_manager::new_from_default(&conn)?; - let cursor_handle = CursorHandle::new(&conn, screen as usize, &resources)?.reply()?; + let atoms = Atoms::new(xcb_conn)?.reply()?; + let resources = resource_manager::new_from_default(xcb_conn)?; + let cursor_handle = CursorHandle::new(xcb_conn, screen as usize, &resources)?.reply()?; Ok(Self { - xlib: Arc::new(xlib), - dpy, conn, - screen, atoms, resources, cursor_handle, @@ -118,7 +98,7 @@ impl XcbConnection { Entry::Vacant(entry) => { let cursor = cursor::get_xcursor( &self.conn, - self.screen as usize, + self.conn.default_screen() as usize, &self.cursor_handle, cursor, )?; @@ -129,14 +109,6 @@ impl XcbConnection { } pub fn screen(&self) -> &Screen { - &self.conn.setup().roots[self.screen as usize] - } -} - -impl Drop for XcbConnection { - fn drop(&mut self) { - unsafe { - (self.xlib.XCloseDisplay)(self.dpy); - } + &self.conn.setup().roots[self.conn.default_screen() as usize] } }