Skip to content

Commit

Permalink
feat: Create XCBConnection from AsRawXcbConnection
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
notgull committed Nov 17, 2024
1 parent b1349ba commit 7b8072e
Showing 1 changed file with 75 additions and 37 deletions.
112 changes: 75 additions & 37 deletions x11rb/src/xcb_ffi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,50 +45,18 @@ pub type BufWithFds = crate::connection::BufWithFds<Buffer>;
/// interface to this C library.
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug)]
pub struct XCBConnection {
pub struct XCBConnection<Conn = Managed> {
conn: raw_ffi::XcbConnectionWrapper,
setup: Setup,
ext_mgr: Mutex<ExtensionManager>,
errors: pending_errors::PendingErrors,
maximum_sequence_received: AtomicU64,

/// Keeps the underlying connection alive if needed.
_manager: Conn,
}

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
Expand All @@ -115,6 +83,7 @@ impl XCBConnection {
ext_mgr: Default::default(),
errors: Default::default(),
maximum_sequence_received: AtomicU64::new(0),
_manager: Managed { _private: () },
};
Ok((conn, screen as usize))
}
Expand All @@ -136,6 +105,16 @@ impl XCBConnection {
ptr: *mut c_void,
should_drop: bool,
) -> Result<XCBConnection, ConnectError> {
Self::from_raw_xcb_connection_inner(Managed { _private: () }, ptr, should_drop)
}
}

impl<Conn> XCBConnection<Conn> {
unsafe fn from_raw_xcb_connection_inner(
manager: Conn,
ptr: *mut c_void,
should_drop: bool,
) -> Result<XCBConnection<Conn>, ConnectError> {
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);
Expand All @@ -145,9 +124,60 @@ impl XCBConnection {
ext_mgr: Default::default(),
errors: Default::default(),
maximum_sequence_received: AtomicU64::new(0),
_manager: manager,
})
}

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(connection: Conn) -> Result<Self, ConnectError>
where
Conn: as_raw_xcb_connection::AsRawXcbConnection,
{
// SAFETY: `connection` is a valid connection object.
let ptr = connection.as_raw_xcb_connection();
unsafe { Self::from_raw_xcb_connection_inner(connection, ptr.cast(), false) }
}

/// Get a reference to the inner managed connection.
pub fn inner_connection(&self) -> &Conn {
&self._manager
}

unsafe fn parse_setup(setup: *const raw_ffi::xcb_setup_t) -> Result<Setup, ParseError> {
use std::slice::from_raw_parts;

Expand Down Expand Up @@ -355,7 +385,15 @@ impl XCBConnection {
}
}

impl RequestConnection for XCBConnection {
/// Represents a connection source that indicates `x11rb` is responsible for
/// the underlying connection object.
#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct Managed {
_private: (),
}

impl<Conn> RequestConnection for XCBConnection<Conn> {
type Buf = CSlice;

fn send_request_with_reply<R>(
Expand Down

0 comments on commit 7b8072e

Please sign in to comment.