From fc0e996a8df6b3686d023d4dd6092aa57d363591 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sun, 17 Nov 2024 10:30:53 -0800 Subject: [PATCH] feat: Create XCBConnection from AsRawXcbConnection The goal here is to allow for an XCBConnection to be created safely from an object that already implements AsRawXcbConnection. This way, instead of using "from_raw_xcb_connection" and needing to use unsafe code, we can instead have it wrap aound something that already implements AsRawXcbConnection safely. This adds a generic parameter to XCBConnection that is set to a ZST by default. If this new feature is used this parameter is replaced by the type that is being wrapped. Similar to how owned display handles work in the winit ecosystem. Signed-off-by: John Nunley --- x11rb/src/xcb_ffi/mod.rs | 138 +++++++++++++++++++------------ x11rb/src/xcb_ffi/raw_ffi/mod.rs | 9 +- 2 files changed, 93 insertions(+), 54 deletions(-) diff --git a/x11rb/src/xcb_ffi/mod.rs b/x11rb/src/xcb_ffi/mod.rs index 287f2a12..31c9bec0 100644 --- a/x11rb/src/xcb_ffi/mod.rs +++ b/x11rb/src/xcb_ffi/mod.rs @@ -45,8 +45,8 @@ pub type BufWithFds = crate::connection::BufWithFds; /// interface to this C library. #[allow(clippy::upper_case_acronyms)] #[derive(Debug)] -pub struct XCBConnection { - conn: raw_ffi::XcbConnectionWrapper, +pub struct XCBConnection { + conn: Conn, setup: Setup, ext_mgr: Mutex, errors: pending_errors::PendingErrors, @@ -54,41 +54,6 @@ pub struct XCBConnection { } impl XCBConnection { - unsafe fn connection_error_from_connection( - c: *mut raw_ffi::xcb_connection_t, - ) -> ConnectionError { - Self::connection_error_from_c_error(raw_ffi::xcb_connection_has_error(c)) - } - - fn connection_error_from_c_error(error: c_int) -> ConnectionError { - use crate::xcb_ffi::raw_ffi::connection_errors::*; - - assert_ne!(error, 0); - match error { - ERROR => IOError::new(ErrorKind::Other, ConnectionError::UnknownError).into(), - EXT_NOTSUPPORTED => ConnectionError::UnsupportedExtension, - MEM_INSUFFICIENT => ConnectionError::InsufficientMemory, - REQ_LEN_EXCEED => ConnectionError::MaximumRequestLengthExceeded, - FDPASSING_FAILED => ConnectionError::FdPassingFailed, - _ => ConnectionError::UnknownError, - // Not possible here: PARSE_ERR, INVALID_SCREEN - } - } - - fn connect_error_from_c_error(error: c_int) -> ConnectError { - use crate::xcb_ffi::raw_ffi::connection_errors::*; - - assert_ne!(error, 0); - match error { - ERROR => IOError::new(ErrorKind::Other, ConnectionError::UnknownError).into(), - MEM_INSUFFICIENT => ConnectError::InsufficientMemory, - PARSE_ERR => DisplayParsingError::Unknown.into(), - INVALID_SCREEN => ConnectError::InvalidScreen, - _ => ConnectError::UnknownError, - // Not possible here: EXT_NOTSUPPORTED, REQ_LEN_EXCEED, FDPASSING_FAILED - } - } - /// Establish a new connection to an X11 server. /// /// If a `dpy_name` is provided, it describes the display that should be connected to, for @@ -136,9 +101,18 @@ impl XCBConnection { ptr: *mut c_void, should_drop: bool, ) -> Result { - let ptr = ptr as *mut raw_ffi::xcb_connection_t; - let conn = raw_ffi::XcbConnectionWrapper::new(ptr, should_drop); - let setup = raw_ffi::xcb_get_setup(ptr); + Self::from_raw_xcb_connection_inner(raw_ffi::XcbConnectionWrapper::new( + ptr.cast(), + should_drop, + )) + } +} + +impl XCBConnection { + unsafe fn from_raw_xcb_connection_inner( + conn: Conn, + ) -> Result, ConnectError> { + let setup = raw_ffi::xcb_get_setup(conn.as_raw_xcb_connection().cast()); Ok(XCBConnection { conn, setup: Self::parse_setup(setup)?, @@ -148,6 +122,64 @@ impl XCBConnection { }) } + unsafe fn connection_error_from_connection( + c: *mut raw_ffi::xcb_connection_t, + ) -> ConnectionError { + Self::connection_error_from_c_error(raw_ffi::xcb_connection_has_error(c)) + } + + fn connection_error_from_c_error(error: c_int) -> ConnectionError { + use crate::xcb_ffi::raw_ffi::connection_errors::*; + + assert_ne!(error, 0); + match error { + ERROR => IOError::new(ErrorKind::Other, ConnectionError::UnknownError).into(), + EXT_NOTSUPPORTED => ConnectionError::UnsupportedExtension, + MEM_INSUFFICIENT => ConnectionError::InsufficientMemory, + REQ_LEN_EXCEED => ConnectionError::MaximumRequestLengthExceeded, + FDPASSING_FAILED => ConnectionError::FdPassingFailed, + _ => ConnectionError::UnknownError, + // Not possible here: PARSE_ERR, INVALID_SCREEN + } + } + + fn connect_error_from_c_error(error: c_int) -> ConnectError { + use crate::xcb_ffi::raw_ffi::connection_errors::*; + + assert_ne!(error, 0); + match error { + ERROR => IOError::new(ErrorKind::Other, ConnectionError::UnknownError).into(), + MEM_INSUFFICIENT => ConnectError::InsufficientMemory, + PARSE_ERR => DisplayParsingError::Unknown.into(), + INVALID_SCREEN => ConnectError::InvalidScreen, + _ => ConnectError::UnknownError, + // Not possible here: EXT_NOTSUPPORTED, REQ_LEN_EXCEED, FDPASSING_FAILED + } + } + + /// Create an `XCBConnection` from an object that implements `AsRawXcbConnection`. + pub fn from_existing_connection(conn: Conn) -> Result { + let setup = unsafe { raw_ffi::xcb_get_setup(conn.as_raw_xcb_connection().cast()) }; + Ok(XCBConnection { + conn, + setup: unsafe { Self::parse_setup(setup) }?, + ext_mgr: Default::default(), + errors: Default::default(), + maximum_sequence_received: AtomicU64::new(0), + }) + } + + /// Get a reference to the inner managed connection. + /// + /// It is discouraged to use this inner connection for fetching events. + pub fn inner_connection(&self) -> &Conn { + &self.conn + } + + fn as_ptr(&self) -> *mut raw_ffi::xcb_connection_t { + self.conn.as_raw_xcb_connection().cast() + } + unsafe fn parse_setup(setup: *const raw_ffi::xcb_setup_t) -> Result { use std::slice::from_raw_parts; @@ -214,7 +246,7 @@ impl XCBConnection { let seqno = if fds.is_empty() { unsafe { raw_ffi::xcb_send_request64( - self.conn.as_ptr(), + self.as_ptr(), flags, &mut new_bufs_ffi[2], &protocol_request, @@ -229,7 +261,7 @@ impl XCBConnection { let fds_ptr = fds.as_mut_ptr(); unsafe { raw_ffi::xcb_send_request_with_fds64( - self.conn.as_ptr(), + self.as_ptr(), flags, &mut new_bufs_ffi[2], &protocol_request, @@ -244,7 +276,7 @@ impl XCBConnection { } }; if seqno == 0 { - unsafe { Err(Self::connection_error_from_connection(self.conn.as_ptr())) } + unsafe { Err(Self::connection_error_from_connection(self.as_ptr())) } } else { Ok(seqno) } @@ -253,7 +285,7 @@ impl XCBConnection { /// Check if the underlying XCB connection is in an error state. pub fn has_error(&self) -> Option { unsafe { - let error = raw_ffi::xcb_connection_has_error(self.conn.as_ptr()); + let error = raw_ffi::xcb_connection_has_error(self.as_ptr()); if error == 0 { None } else { @@ -267,7 +299,7 @@ impl XCBConnection { /// The returned pointer is valid for as long as the original object was not dropped. No /// ownerhsip is transferred. pub fn get_raw_xcb_connection(&self) -> *mut c_void { - self.conn.as_ptr() as _ + self.as_ptr().cast() } /// Check if a reply to the given request already received. @@ -280,7 +312,7 @@ impl XCBConnection { let mut reply = null_mut(); let mut error = null_mut(); let found = - raw_ffi::xcb_poll_for_reply64(self.conn.as_ptr(), sequence, &mut reply, &mut error); + raw_ffi::xcb_poll_for_reply64(self.as_ptr(), sequence, &mut reply, &mut error); if found == 0 { return Err(()); } @@ -355,7 +387,7 @@ impl XCBConnection { } } -impl RequestConnection for XCBConnection { +impl RequestConnection for XCBConnection { type Buf = CSlice; fn send_request_with_reply( @@ -401,7 +433,7 @@ impl RequestConnection for XCBConnection { match mode { DiscardMode::DiscardReplyAndError => unsafe { // libxcb can throw away everything for us - raw_ffi::xcb_discard_reply64(self.conn.as_ptr(), sequence); + raw_ffi::xcb_discard_reply64(self.as_ptr(), sequence); }, // We have to check for errors ourselves DiscardMode::DiscardReply => self.errors.discard_reply(sequence), @@ -434,9 +466,9 @@ impl RequestConnection for XCBConnection { ) -> Result, ConnectionError> { unsafe { let mut error = null_mut(); - let reply = raw_ffi::xcb_wait_for_reply64(self.conn.as_ptr(), sequence, &mut error); + let reply = raw_ffi::xcb_wait_for_reply64(self.as_ptr(), sequence, &mut error); match (reply.is_null(), error.is_null()) { - (true, true) => Err(Self::connection_error_from_connection(self.conn.as_ptr())), + (true, true) => Err(Self::connection_error_from_connection(self.as_ptr())), (false, true) => Ok(ReplyOrError::Reply(self.wrap_reply(reply as _, sequence))), (true, false) => Ok(ReplyOrError::Error(self.wrap_error(error as _, sequence))), // At least one of these pointers must be NULL. @@ -497,7 +529,7 @@ impl RequestConnection for XCBConnection { let cookie = raw_ffi::xcb_void_cookie_t { sequence: sequence as _, }; - let error = unsafe { raw_ffi::xcb_request_check(self.conn.as_ptr(), cookie) }; + let error = unsafe { raw_ffi::xcb_request_check(self.as_ptr(), cookie) }; if error.is_null() { Ok(None) } else { @@ -506,11 +538,11 @@ impl RequestConnection for XCBConnection { } fn maximum_request_bytes(&self) -> usize { - 4 * unsafe { raw_ffi::xcb_get_maximum_request_length(self.conn.as_ptr()) as usize } + 4 * unsafe { raw_ffi::xcb_get_maximum_request_length(self.as_ptr()) as usize } } fn prefetch_maximum_request_bytes(&self) { - unsafe { raw_ffi::xcb_prefetch_maximum_request_length(self.conn.as_ptr()) }; + unsafe { raw_ffi::xcb_prefetch_maximum_request_length(self.as_ptr()) }; } fn parse_error(&self, error: &[u8]) -> Result { diff --git a/x11rb/src/xcb_ffi/raw_ffi/mod.rs b/x11rb/src/xcb_ffi/raw_ffi/mod.rs index 0273e497..5d49253c 100644 --- a/x11rb/src/xcb_ffi/raw_ffi/mod.rs +++ b/x11rb/src/xcb_ffi/raw_ffi/mod.rs @@ -28,11 +28,18 @@ pub(crate) struct xcb_connection_t { } #[derive(Debug)] -pub(crate) struct XcbConnectionWrapper { +#[doc(hidden)] +pub struct XcbConnectionWrapper { ptr: NonNull, should_drop: bool, } +unsafe impl as_raw_xcb_connection::AsRawXcbConnection for XcbConnectionWrapper { + fn as_raw_xcb_connection(&self) -> *mut as_raw_xcb_connection::xcb_connection_t { + self.ptr.as_ptr().cast() + } +} + // libxcb is fully thread-safe (well, except for xcb_disconnect()), so the following is // actually fine and safe: unsafe impl Send for XcbConnectionWrapper {}