use crate::error::{self, ProtocolError};
use crate::event::{self, Event};
use crate::ext::{Extension, ExtensionData};
#[cfg(feature = "dl")]
use crate::ffi::dl::xcb_get_funcs;
#[cfg(feature = "dl")]
use crate::ffi::dl::OpenError;
#[cfg(feature = "dl")]
use crate::ffi::XcbLib;
#[cfg(feature = "xlib_xcb_dl")]
use crate::ffi::XlibXcbLib;
#[cfg(feature = "present")]
use crate::present;
use crate::x::{Atom, Keysym, Setup, Timestamp};
#[cfg(feature = "xinput")]
use crate::xinput;
use crate::{cache_extensions_data, ffi::*};
#[cfg(all(feature = "xlib_xcb", not(feature = "xlib_xcb_dl")))]
use x11::xlib;
#[cfg(feature = "xlib_xcb_dl")]
use x11_dl::xlib;
use bitflags::bitflags;
use libc::{c_char, c_int};
use std::cell::RefCell;
use std::ffi::{c_void, CStr, CString};
use std::fmt::{self, Display, Formatter};
use std::marker::{self, PhantomData};
use std::mem;
use std::os::fd::{IntoRawFd, OwnedFd};
use std::os::unix::prelude::{AsRawFd, RawFd};
use std::ptr;
#[cfg(feature = "dl")]
#[cfg(feature = "dl")]
use std::rc::Rc;
use std::result;
use std::slice;
pub trait Xid {
fn none() -> Self;
fn resource_id(&self) -> u32;
fn is_none(&self) -> bool {
self.resource_id() == 0
}
}
pub trait XidNew: Xid {
fn new(res_id: u32) -> Self;
}
pub trait Raw<T>: Sized {
unsafe fn from_raw(raw: *mut T) -> Self;
fn into_raw(self) -> *mut T {
let raw = self.as_raw();
mem::forget(self);
raw
}
fn as_raw(&self) -> *mut T;
}
pub trait BaseEvent: Raw<xcb_generic_event_t> {
const EXTENSION: Option<Extension>;
const NUMBER: u32;
fn is_from_send_event(&self) -> bool {
let ev = self.as_raw();
let response_type = unsafe { (*ev).response_type };
(response_type & 0x80) != 0
}
}
pub trait GeEvent: Raw<xcb_ge_generic_event_t> {
const EXTENSION: Extension;
const NUMBER: u32;
}
pub trait BaseError: Raw<xcb_generic_error_t> {
const EXTENSION: Option<Extension>;
const NUMBER: u32;
}
pub(crate) trait ResolveWireEvent: Sized {
unsafe fn resolve_wire_event(first_event: u8, event: *mut xcb_generic_event_t) -> Option<Self>;
}
pub(crate) trait ResolveWireGeEvent: Sized {
unsafe fn resolve_wire_ge_event(event: *mut xcb_ge_generic_event_t) -> Self;
}
pub(crate) trait ResolveWireError: Sized {
unsafe fn resolve_wire_error(first_error: u8, error: *mut xcb_generic_error_t) -> Option<Self>;
}
pub(crate) trait WiredOut {
fn wire_len(&self) -> usize;
fn serialize(&self, wire_buf: &mut [u8]) -> usize;
}
pub(crate) trait WiredIn {
type Params: Copy;
unsafe fn compute_wire_len(ptr: *const u8, params: Self::Params) -> usize;
unsafe fn unserialize(ptr: *const u8, params: Self::Params, offset: &mut usize) -> Self;
}
macro_rules! impl_wired_simple {
($typ:ty) => {
impl WiredOut for $typ {
fn wire_len(&self) -> usize {
mem::size_of::<Self>()
}
fn serialize(&self, wire_buf: &mut [u8]) -> usize {
debug_assert!(wire_buf.len() >= mem::size_of::<Self>());
let buf_size = self.wire_len();
let src =
unsafe { slice::from_raw_parts(self as *const Self as *const u8, buf_size) };
wire_buf[..buf_size].copy_from_slice(src);
buf_size
}
}
impl WiredIn for $typ {
type Params = ();
unsafe fn compute_wire_len(_ptr: *const u8, _params: Self::Params) -> usize {
mem::size_of::<Self>()
}
unsafe fn unserialize(
ptr: *const u8,
_params: Self::Params,
offset: &mut usize,
) -> Self {
*offset += mem::size_of::<Self>();
*(ptr as *const Self)
}
}
};
}
impl_wired_simple!(u8);
impl_wired_simple!(u16);
impl_wired_simple!(u32);
impl_wired_simple!(u64);
impl_wired_simple!(i8);
impl_wired_simple!(i16);
impl_wired_simple!(i32);
impl_wired_simple!(f32);
impl<T: Xid> WiredOut for T {
fn wire_len(&self) -> usize {
4
}
fn serialize(&self, wire_buf: &mut [u8]) -> usize {
debug_assert!(wire_buf.len() >= 4);
let buf_size = self.wire_len();
let resource_id = self.resource_id();
let src =
unsafe { slice::from_raw_parts(&resource_id as *const u32 as *const u8, buf_size) };
wire_buf[..buf_size].copy_from_slice(src);
buf_size
}
}
impl<T: XidNew> WiredIn for T {
type Params = ();
unsafe fn compute_wire_len(_ptr: *const u8, _params: Self::Params) -> usize {
4
}
unsafe fn unserialize(ptr: *const u8, _params: Self::Params, offset: &mut usize) -> Self {
*offset += 4;
let xid = *(ptr as *const u32);
T::new(xid)
}
}
pub trait Reply {
unsafe fn from_raw(raw: *const u8) -> Self;
unsafe fn into_raw(self) -> *const u8;
unsafe fn as_raw(&self) -> *const u8;
}
pub trait Cookie {
unsafe fn from_sequence(seq: u64) -> Self;
fn sequence(&self) -> u64;
}
pub unsafe trait CookieChecked: Cookie {}
pub unsafe trait CookieWithReplyChecked: CookieChecked {
type Reply: Reply;
}
pub unsafe trait CookieWithReplyUnchecked: Cookie {
type Reply: Reply;
}
#[derive(Debug)]
pub struct VoidCookie {
seq: u64,
}
impl Cookie for VoidCookie {
unsafe fn from_sequence(seq: u64) -> Self {
VoidCookie { seq }
}
fn sequence(&self) -> u64 {
self.seq
}
}
#[derive(Debug)]
pub struct VoidCookieChecked {
seq: u64,
}
impl Cookie for VoidCookieChecked {
unsafe fn from_sequence(seq: u64) -> Self {
VoidCookieChecked { seq }
}
fn sequence(&self) -> u64 {
self.seq
}
}
unsafe impl CookieChecked for VoidCookieChecked {}
pub unsafe trait RawRequest {
fn raw_request(&self, conn: &Connection, checked: bool) -> u64;
}
pub trait Request: RawRequest {
type Cookie: Cookie;
const IS_VOID: bool;
}
pub trait RequestWithoutReply: Request {}
pub trait RequestWithReply: Request {
type Reply: Reply;
type Cookie: CookieWithReplyChecked<Reply = Self::Reply>;
type CookieUnchecked: CookieWithReplyUnchecked<Reply = Self::Reply>;
}
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
#[derive(Debug)]
pub enum EventQueueOwner {
Xcb,
Xlib,
}
#[derive(Copy, Clone, Debug)]
pub struct AuthInfo<'a> {
pub name: &'a str,
pub data: &'a str,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DisplayInfo {
pub host: String,
pub display: i32,
pub screen: i32,
}
pub fn parse_display(name: &str) -> Option<DisplayInfo> {
let name = CString::new(name).unwrap();
let mut hostp: *mut c_char = ptr::null_mut();
let mut display = 0i32;
let mut screen = 0i32;
let success = unsafe {
#[cfg(feature = "dl")]
let lib = crate::ffi::XcbLib::open();
#[cfg(feature = "dl")]
let xcb_parse_display = match lib {
Ok(lib) => lib.xcb_parse_display,
Err(_) => {
return None;
}
};
xcb_parse_display(
name.as_ptr(),
&mut hostp as *mut _,
&mut display as *mut _,
&mut screen as *mut _,
)
};
if success != 0 {
let host = unsafe { CStr::from_ptr(hostp as *const _) }
.to_str()
.unwrap()
.to_string();
unsafe {
libc::free(hostp as *mut _);
}
Some(DisplayInfo {
host,
display,
screen,
})
} else {
None
}
}
#[cfg(feature = "dl")]
pub fn unload_libraries() -> result::Result<(), OpenError> {
#[cfg(feature = "dl")]
crate::ffi::XcbLib::unload()?;
#[cfg(feature = "xlib_xcb_dl")]
crate::ffi::XlibXcbLib::unload()?;
Ok(())
}
#[deprecated(note = "Broken API: use `SpecialEvent` instead")]
#[cfg(any(feature = "xinput", feature = "present"))]
#[derive(Debug)]
pub struct SpecialEventId {
raw: *mut xcb_special_event_t,
stamp: Timestamp,
}
#[allow(deprecated)]
#[cfg(any(feature = "xinput", feature = "present"))]
impl SpecialEventId {
pub fn stamp(&self) -> Timestamp {
self.stamp
}
}
#[cfg(any(feature = "xinput", feature = "present"))]
#[derive(Debug)]
pub struct SpecialEvent {
raw: *mut xcb_special_event_t,
}
#[cfg(any(feature = "xinput", feature = "present"))]
unsafe impl Send for SpecialEvent {}
#[cfg(any(feature = "xinput", feature = "present"))]
unsafe impl Sync for SpecialEvent {}
#[derive(Debug)]
pub enum ConnError {
Connection,
ClosedExtNotSupported,
ClosedMemInsufficient,
ClosedReqLenExceed,
ClosedParseErr,
ClosedInvalidScreen,
ClosedFdPassingFailed,
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
XOpenDisplay,
#[cfg(feature = "dl")]
LibrariesNotLoaded,
}
impl ConnError {
fn to_str(&self) -> &str {
match *self {
ConnError::Connection => "Connection error, possible I/O error",
ConnError::ClosedExtNotSupported => "Connection closed, X extension not supported",
ConnError::ClosedMemInsufficient => "Connection closed, insufficient memory",
ConnError::ClosedReqLenExceed => {
"Connection closed, exceeded request length that server accepts."
}
ConnError::ClosedParseErr => "Connection closed, error during parsing display string",
ConnError::ClosedInvalidScreen => {
"Connection closed, the server does not have a screen matching the display"
}
ConnError::ClosedFdPassingFailed => {
"Connection closed, some file descriptor passing operation failed"
}
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
ConnError::XOpenDisplay => {
"XOpenDisplay failed to open a display. Check the $DISPLAY env var"
}
#[cfg(feature = "dl")]
ConnError::LibrariesNotLoaded => "Libraries are not loaded",
}
}
}
impl Display for ConnError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.to_str().fmt(f)
}
}
impl std::error::Error for ConnError {
fn description(&self) -> &str {
self.to_str()
}
}
#[cfg(feature = "dl")]
impl From<OpenError> for ConnError {
fn from(_: OpenError) -> Self {
ConnError::LibrariesNotLoaded
}
}
pub type ConnResult<T> = result::Result<T, ConnError>;
pub type ProtocolResult<T> = result::Result<T, ProtocolError>;
#[derive(Debug)]
pub enum Error {
Connection(ConnError),
Protocol(ProtocolError),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Error::Connection(_) => f.write_str("xcb connection error"),
Error::Protocol(_) => f.write_str("xcb protocol error"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Connection(err) => Some(err),
Error::Protocol(err) => Some(err),
}
}
}
impl From<ConnError> for Error {
fn from(err: ConnError) -> Error {
Error::Connection(err)
}
}
impl From<ProtocolError> for Error {
fn from(err: ProtocolError) -> Error {
Error::Protocol(err)
}
}
pub type Result<T> = result::Result<T, Error>;
pub struct Connection {
c: *mut xcb_connection_t,
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
dpy: *mut xlib::Display,
ext_data: Vec<ExtensionData>,
#[cfg(feature = "debug_atom_names")]
dbg_atom_names: bool,
#[cfg(feature = "dl")]
pub(crate) lib: XcbLib,
should_drop: bool,
}
unsafe impl Send for Connection {}
unsafe impl Sync for Connection {}
#[cfg(feature = "dl")]
macro_rules! xcb_get_conn_funcs {
($self:expr, $($name:ident),*) => {
$(
let $name = $self.lib.$name;
)*
};
}
#[cfg(feature = "dl")]
pub(crate) use xcb_get_conn_funcs;
impl Connection {
pub fn connect(display_name: Option<&str>) -> ConnResult<(Connection, i32)> {
Self::connect_with_extensions(display_name, &[], &[])
}
pub fn connect_with_extensions(
display_name: Option<&str>,
mandatory: &[Extension],
optional: &[Extension],
) -> ConnResult<(Connection, i32)> {
#[cfg(feature = "dl")]
xcb_get_funcs!(xcb_connect);
let mut screen_num: c_int = 0;
let displayname = display_name.map(|s| CString::new(s).unwrap());
unsafe {
let conn = if let Some(display) = displayname {
xcb_connect(display.as_ptr(), &mut screen_num)
} else {
xcb_connect(ptr::null(), &mut screen_num)
};
check_connection_error(conn)?;
let conn = Self::from_raw_conn_and_extensions(conn, mandatory, optional);
conn.has_error().map(|_| (conn, screen_num as i32))
}
}
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
pub fn connect_with_xlib_display() -> ConnResult<(Connection, i32)> {
#[cfg(all(feature = "xlib_xcb", not(feature = "xlib_xcb_dl")))]
let (xopen_display, xdefault_screen, xget_xcbconnection) =
(xlib::XOpenDisplay, xlib::XDefaultScreen, XGetXCBConnection);
#[cfg(feature = "xlib_xcb_dl")]
let (xopen_display, xdefault_screen, _xlib_lib, xget_xcbconnection, _xlib_xcb_lib) = {
let xlib_lib = xlib::Xlib::open().map_err(|_| ConnError::LibrariesNotLoaded)?;
let xlib_xcb_lib = XlibXcbLib::open().map_err(|_| ConnError::LibrariesNotLoaded)?;
(
xlib_lib.XOpenDisplay,
xlib_lib.XDefaultScreen,
xlib_lib,
xlib_xcb_lib.XGetXCBConnection,
xlib_xcb_lib,
)
};
unsafe {
let dpy = xopen_display(ptr::null());
if dpy.is_null() {
return Err(ConnError::XOpenDisplay);
}
check_connection_error(xget_xcbconnection(dpy))?;
let conn = Self::from_xlib_display(dpy);
conn.has_error()
.map(|_| (conn, xdefault_screen(dpy) as i32))
}
}
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
pub fn connect_with_xlib_display_and_extensions(
mandatory: &[Extension],
optional: &[Extension],
) -> ConnResult<(Connection, i32)> {
#[cfg(all(feature = "xlib_xcb", not(feature = "xlib_xcb_dl")))]
let (xopen_display, xdefault_screen, xget_xcbconnection) =
(xlib::XOpenDisplay, xlib::XDefaultScreen, XGetXCBConnection);
#[cfg(feature = "xlib_xcb_dl")]
let (xopen_display, xdefault_screen, _xlib_lib, xget_xcbconnection, _xlib_xcb_lib) = {
let xlib_lib = xlib::Xlib::open().map_err(|_| ConnError::LibrariesNotLoaded)?;
let xlib_xcb_lib = XlibXcbLib::open().map_err(|_| ConnError::LibrariesNotLoaded)?;
(
xlib_lib.XOpenDisplay,
xlib_lib.XDefaultScreen,
xlib_lib,
xlib_xcb_lib.XGetXCBConnection,
xlib_xcb_lib,
)
};
unsafe {
let dpy = xopen_display(ptr::null());
if dpy.is_null() {
return Err(ConnError::XOpenDisplay);
}
check_connection_error(xget_xcbconnection(dpy))?;
let conn = Self::from_xlib_display_and_extensions(dpy, mandatory, optional);
conn.has_error()
.map(|_| (conn, xdefault_screen(dpy) as i32))
}
}
#[deprecated(note = "unsound API, use `connect_with_fd` instead")]
#[doc(hidden)]
pub fn connect_to_fd(fd: RawFd, auth_info: Option<AuthInfo<'_>>) -> ConnResult<Self> {
#[allow(deprecated)]
Self::connect_to_fd_with_extensions(fd, auth_info, &[], &[])
}
#[deprecated(note = "unsound API, use `connect_with_fd_and_extensions` instead")]
#[doc(hidden)]
pub fn connect_to_fd_with_extensions(
fd: RawFd,
auth_info: Option<AuthInfo<'_>>,
mandatory: &[Extension],
optional: &[Extension],
) -> ConnResult<Self> {
#[cfg(feature = "dl")]
xcb_get_funcs!(xcb_connect_to_fd);
let mut auth_info = auth_info.map(|auth_info| {
let auth_name = CString::new(auth_info.name).unwrap();
let auth_data = CString::new(auth_info.data).unwrap();
let auth_info = xcb_auth_info_t {
namelen: auth_name.as_bytes().len() as _,
name: auth_name.as_ptr() as *mut _,
datalen: auth_data.as_bytes().len() as _,
data: auth_data.as_ptr() as *mut _,
};
(auth_info, auth_name, auth_data)
});
let ai_ptr = if let Some(auth_info) = auth_info.as_mut() {
&mut auth_info.0 as *mut _
} else {
ptr::null_mut()
};
let conn = unsafe {
let conn = xcb_connect_to_fd(fd, ai_ptr);
check_connection_error(conn)?;
Self::from_raw_conn_and_extensions(conn, mandatory, optional)
};
conn.has_error().map(|_| conn)
}
pub fn connect_with_fd(fd: OwnedFd, auth_info: Option<AuthInfo<'_>>) -> ConnResult<Self> {
Self::connect_with_fd_and_extensions(fd, auth_info, &[], &[])
}
pub fn connect_with_fd_and_extensions(
fd: OwnedFd,
auth_info: Option<AuthInfo<'_>>,
mandatory: &[Extension],
optional: &[Extension],
) -> ConnResult<Self> {
#[cfg(feature = "dl")]
xcb_get_funcs!(xcb_connect_to_fd);
let mut auth_info = auth_info.map(|auth_info| {
let auth_name = CString::new(auth_info.name).unwrap();
let auth_data = CString::new(auth_info.data).unwrap();
let auth_info = xcb_auth_info_t {
namelen: auth_name.as_bytes().len() as _,
name: auth_name.as_ptr() as *mut _,
datalen: auth_data.as_bytes().len() as _,
data: auth_data.as_ptr() as *mut _,
};
(auth_info, auth_name, auth_data)
});
let ai_ptr = if let Some(auth_info) = auth_info.as_mut() {
&mut auth_info.0 as *mut _
} else {
ptr::null_mut()
};
let fd = fd.into_raw_fd();
let conn = unsafe {
let conn = xcb_connect_to_fd(fd, ai_ptr);
check_connection_error(conn)?;
Self::from_raw_conn_and_extensions(conn, mandatory, optional)
};
conn.has_error().map(|_| conn)
}
pub fn connect_to_display_with_auth_info(
display_name: Option<&str>,
auth_info: AuthInfo<'_>,
) -> ConnResult<(Connection, i32)> {
Self::connect_to_display_with_auth_info_and_extensions(display_name, auth_info, &[], &[])
}
pub fn connect_to_display_with_auth_info_and_extensions(
display_name: Option<&str>,
auth_info: AuthInfo<'_>,
mandatory: &[Extension],
optional: &[Extension],
) -> ConnResult<(Connection, i32)> {
#[cfg(feature = "dl")]
xcb_get_funcs!(xcb_connect_to_display_with_auth_info);
let mut screen_num: c_int = 0;
let display_name = display_name.map(|s| CString::new(s).unwrap());
unsafe {
let display_name = if let Some(display_name) = &display_name {
display_name.as_ptr()
} else {
ptr::null()
};
let auth_name = CString::new(auth_info.name).unwrap();
let auth_data = CString::new(auth_info.data).unwrap();
let mut auth_info = xcb_auth_info_t {
namelen: auth_name.as_bytes().len() as _,
name: auth_name.as_ptr() as *mut _,
datalen: auth_data.as_bytes().len() as _,
data: auth_data.as_ptr() as *mut _,
};
let conn = xcb_connect_to_display_with_auth_info(
display_name,
&mut auth_info as *mut _,
&mut screen_num as *mut _,
);
check_connection_error(conn)?;
let conn = Self::from_raw_conn_and_extensions(conn, mandatory, optional);
conn.has_error().map(|_| (conn, screen_num as i32))
}
}
pub unsafe fn from_raw_conn(conn: *mut xcb_connection_t) -> Connection {
Self::from_raw_conn_and_extensions(conn, &[], &[])
}
pub unsafe fn from_raw_conn_and_extensions(
conn: *mut xcb_connection_t,
mandatory: &[Extension],
optional: &[Extension],
) -> Connection {
assert!(!conn.is_null());
assert!(check_connection_error(conn).is_ok());
#[cfg(feature = "debug_atom_names")]
let dbg_atom_names = {
if dan::DAN_CONN.is_null() {
dan::DAN_CONN = conn;
true
} else {
false
}
};
let ext_data = cache_extensions_data(conn, mandatory, optional);
return Connection {
c: conn,
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
dpy: ptr::null_mut(),
ext_data,
#[cfg(feature = "debug_atom_names")]
dbg_atom_names,
#[cfg(feature = "dl")]
lib: XcbLib::open().expect("xcb library not loaded"),
should_drop: true,
};
}
pub unsafe fn from_raw_conn_and_extensions_no_drop(
conn: *mut xcb_connection_t,
mandatory: &[Extension],
optional: &[Extension],
) -> Connection {
assert!(!conn.is_null());
assert!(check_connection_error(conn).is_ok());
#[cfg(feature = "debug_atom_names")]
let dbg_atom_names = {
if dan::DAN_CONN.is_null() {
dan::DAN_CONN = conn;
true
} else {
false
}
};
let ext_data = cache_extensions_data(conn, mandatory, optional);
return Connection {
c: conn,
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
dpy: ptr::null_mut(),
ext_data,
#[cfg(feature = "debug_atom_names")]
dbg_atom_names,
#[cfg(feature = "dl")]
lib: XcbLib::open().expect("xcb library not loaded"),
should_drop: false,
};
}
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
pub unsafe fn from_xlib_display(dpy: *mut xlib::Display) -> Connection {
Self::from_xlib_display_and_extensions(dpy, &[], &[])
}
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
pub unsafe fn from_xlib_display_and_extensions(
dpy: *mut xlib::Display,
mandatory: &[Extension],
optional: &[Extension],
) -> Connection {
assert!(!dpy.is_null(), "attempt connect with null display");
#[cfg(all(feature = "xlib_xcb", not(feature = "xlib_xcb_dl")))]
let xget_xcbconnection = XGetXCBConnection;
#[cfg(feature = "xlib_xcb_dl")]
let (xget_xcbconnection, _lib) = {
let lib = XlibXcbLib::open().expect("X11-xcb library not loaded");
(lib.XGetXCBConnection, lib)
};
let c = xget_xcbconnection(dpy);
assert!(check_connection_error(c).is_ok());
#[cfg(feature = "debug_atom_names")]
let dbg_atom_names = {
if dan::DAN_CONN.is_null() {
dan::DAN_CONN = c;
true
} else {
false
}
};
let ext_data = cache_extensions_data(c, mandatory, optional);
return Connection {
c,
dpy,
ext_data,
#[cfg(feature = "debug_atom_names")]
dbg_atom_names,
#[cfg(feature = "dl")]
lib: XcbLib::open().expect("xcb library not loaded"),
should_drop: true,
};
}
pub fn active_extensions(&self) -> impl Iterator<Item = Extension> + '_ {
self.ext_data.iter().map(|eed| eed.ext)
}
pub fn active_extensions_data(&self) -> &[ExtensionData] {
&self.ext_data
}
pub fn get_raw_conn(&self) -> *mut xcb_connection_t {
self.c
}
pub fn into_raw_conn(self) -> *mut xcb_connection_t {
let c = self.c;
mem::forget(self);
c
}
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
pub fn get_raw_dpy(&self) -> *mut xlib::Display {
self.dpy
}
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
pub fn set_event_queue_owner(&self, owner: EventQueueOwner) {
debug_assert!(!self.dpy.is_null());
unsafe {
#[cfg(all(feature = "xlib_xcb", not(feature = "xlib_xcb_dl")))]
let xset_eventqueueowner = XSetEventQueueOwner;
#[cfg(feature = "xlib_xcb_dl")]
let xset_eventqueueowner = XlibXcbLib::open()
.expect("X11-xcb library not loaded")
.XSetEventQueueOwner;
xset_eventqueueowner(
self.dpy,
match owner {
EventQueueOwner::Xcb => XCBOwnsEventQueue,
EventQueueOwner::Xlib => XlibOwnsEventQueue,
},
);
}
}
pub fn get_maximum_request_length(&self) -> u32 {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_get_maximum_request_length);
unsafe { xcb_get_maximum_request_length(self.c) }
}
pub fn prefetch_maximum_request_length(&self) {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_prefetch_maximum_request_length);
unsafe {
xcb_prefetch_maximum_request_length(self.c);
}
}
pub fn generate_id<T: XidNew>(&self) -> T {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_generate_id);
XidNew::new(unsafe { xcb_generate_id(self.c) })
}
pub fn flush(&self) -> ConnResult<()> {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_flush);
unsafe {
let ret = xcb_flush(self.c);
if ret > 0 {
Ok(())
} else {
self.has_error()?;
unreachable!()
}
}
}
pub unsafe fn resolve_event(&self, ev: &mut xcb_generic_event_t) -> Event {
event::resolve_event(ev, &self.ext_data)
}
pub unsafe fn resolve_error(&self, err: &mut xcb_generic_error_t) -> error::ProtocolError {
error::resolve_error(err, &self.ext_data)
}
pub fn wait_for_event(&self) -> Result<Event> {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_wait_for_event);
unsafe {
let ev = xcb_wait_for_event(self.c);
self.handle_wait_for_event(ev)
}
}
pub fn poll_for_event(&self) -> Result<Option<Event>> {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_poll_for_event);
unsafe {
let ev = xcb_poll_for_event(self.c);
self.handle_poll_for_event(ev)
}
}
pub fn poll_for_queued_event(&self) -> ProtocolResult<Option<Event>> {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_poll_for_queued_event);
unsafe {
let ev = xcb_poll_for_queued_event(self.c);
if ev.is_null() {
Ok(None)
} else if is_error(ev) {
Err(error::resolve_error(ev as *mut _, &self.ext_data))
} else {
Ok(Some(event::resolve_event(ev, &self.ext_data)))
}
}
}
#[deprecated(note = "Broken API: use `register_for_special_event` instead")]
#[cfg(any(feature = "xinput", feature = "present"))]
#[allow(deprecated)]
pub fn register_for_special_xge<XGE: GeEvent>(&self) -> SpecialEventId {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_register_for_special_xge);
unsafe {
let ext: *mut xcb_extension_t = match XGE::EXTENSION {
#[cfg(feature = "xinput")]
Extension::Input => ptr::addr_of_mut!(xinput::FFI_EXT),
#[cfg(feature = "present")]
Extension::Present => ptr::addr_of_mut!(present::FFI_EXT),
_ => unreachable!("only Input and Present have XGE events"),
};
let mut stamp: Timestamp = 0;
let raw = xcb_register_for_special_xge(self.c, ext, XGE::NUMBER, &mut stamp as *mut _);
SpecialEventId { raw, stamp }
}
}
#[deprecated(note = "use `unregister_for_special_event` instead")]
#[cfg(any(feature = "xinput", feature = "present"))]
#[allow(deprecated)]
pub fn unregister_for_special_xge(&self, se: SpecialEventId) {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_unregister_for_special_event);
unsafe {
xcb_unregister_for_special_event(self.c, se.raw);
}
}
#[deprecated(note = "Broken API: use `wait_for_special_event2` instead")]
#[cfg(any(feature = "xinput", feature = "present"))]
#[allow(deprecated)]
pub fn wait_for_special_event(&self, se: SpecialEventId) -> Result<Event> {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_wait_for_special_event);
unsafe {
let ev = xcb_wait_for_special_event(self.c, se.raw);
self.handle_wait_for_event(ev)
}
}
#[deprecated(note = "Broken API: use `poll_for_special_event2` instead")]
#[cfg(any(feature = "xinput", feature = "present"))]
#[allow(deprecated)]
pub fn poll_for_special_event(&self, se: SpecialEventId) -> Result<Option<Event>> {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_poll_for_special_event);
unsafe {
let ev = xcb_poll_for_special_event(self.c, se.raw);
self.handle_poll_for_event(ev)
}
}
#[cfg(any(feature = "xinput", feature = "present"))]
pub fn register_for_special_event<EID: Xid>(
&self,
extension: Extension,
eid: EID,
) -> SpecialEvent {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_register_for_special_xge);
unsafe {
let ext: *mut xcb_extension_t = match extension {
#[cfg(feature = "xinput")]
Extension::Input => ptr::addr_of_mut!(xinput::FFI_EXT),
#[cfg(feature = "present")]
Extension::Present => ptr::addr_of_mut!(present::FFI_EXT),
_ => unreachable!("only Input and Present have XGE events"),
};
let raw = xcb_register_for_special_xge(self.c, ext, eid.resource_id(), ptr::null_mut());
SpecialEvent { raw }
}
}
#[cfg(any(feature = "xinput", feature = "present"))]
pub fn unregister_for_special_event(&self, se: SpecialEvent) {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_unregister_for_special_event);
unsafe {
xcb_unregister_for_special_event(self.c, se.raw);
}
}
#[cfg(any(feature = "xinput", feature = "present"))]
pub fn wait_for_special_event2(&self, se: &SpecialEvent) -> Result<Event> {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_wait_for_special_event);
unsafe {
let ev = xcb_wait_for_special_event(self.c, se.raw);
self.handle_wait_for_event(ev)
}
}
#[cfg(any(feature = "xinput", feature = "present"))]
pub fn poll_for_special_event2(&self, se: &SpecialEvent) -> Result<Option<Event>> {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_poll_for_special_event);
unsafe {
let ev = xcb_poll_for_special_event(self.c, se.raw);
self.handle_poll_for_event(ev)
}
}
fn discard_reply<C: Cookie>(&self, cookie: C) {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_discard_reply64);
unsafe {
xcb_discard_reply64(self.c, cookie.sequence());
}
}
pub fn get_setup(&self) -> &Setup {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_get_setup);
unsafe {
let ptr = xcb_get_setup(self.c);
let mut _offset = 0;
<&Setup as WiredIn>::unserialize(ptr, (), &mut _offset)
}
}
pub fn has_error(&self) -> ConnResult<()> {
unsafe { check_connection_error(self.c) }
}
pub fn send_request<R>(&self, req: &R) -> R::Cookie
where
R: Request,
{
unsafe { R::Cookie::from_sequence(req.raw_request(self, !R::IS_VOID)) }
}
pub fn send_request_checked<R>(&self, req: &R) -> VoidCookieChecked
where
R: RequestWithoutReply,
{
unsafe { VoidCookieChecked::from_sequence(req.raw_request(self, true)) }
}
pub fn send_request_unchecked<R>(&self, req: &R) -> R::CookieUnchecked
where
R: RequestWithReply,
{
unsafe { R::CookieUnchecked::from_sequence(req.raw_request(self, false)) }
}
pub fn check_request(&self, cookie: VoidCookieChecked) -> ProtocolResult<()> {
let cookie = xcb_void_cookie_t {
seq: cookie.sequence() as u32,
};
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_request_check);
let error = unsafe { xcb_request_check(self.c, cookie) };
if error.is_null() {
Ok(())
} else {
unsafe {
let res = error::resolve_error(error, &self.ext_data);
Err(res)
}
}
}
pub fn send_and_check_request<R>(&self, req: &R) -> ProtocolResult<()>
where
R: RequestWithoutReply,
{
self.check_request(self.send_request_checked(req))
}
pub fn wait_for_reply<C>(&self, cookie: C) -> Result<C::Reply>
where
C: CookieWithReplyChecked,
{
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_wait_for_reply64);
unsafe {
let mut error: *mut xcb_generic_error_t = ptr::null_mut();
let reply = xcb_wait_for_reply64(self.c, cookie.sequence(), &mut error as *mut _);
self.handle_reply_checked::<C>(reply, error)
}
}
pub fn wait_for_reply_unchecked<C>(&self, cookie: C) -> ConnResult<Option<C::Reply>>
where
C: CookieWithReplyUnchecked,
{
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_wait_for_reply64);
unsafe {
let reply = xcb_wait_for_reply64(self.c, cookie.sequence(), ptr::null_mut());
self.handle_reply_unchecked::<C>(reply)
}
}
pub fn poll_for_reply<C>(&self, cookie: &C) -> Option<Result<C::Reply>>
where
C: CookieWithReplyChecked,
{
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_poll_for_reply64);
unsafe {
let mut error: *mut xcb_generic_error_t = ptr::null_mut();
let mut reply: *mut c_void = ptr::null_mut();
let received = xcb_poll_for_reply64(
self.c,
cookie.sequence(),
&mut reply as *mut _,
&mut error as *mut _,
);
match received {
0 => None,
1 => Some(self.handle_reply_checked::<C>(reply, error)),
_ => panic!("unexpected return value from xcb_poll_for_reply64"),
}
}
}
pub fn poll_for_reply_unchecked<C>(&self, cookie: &C) -> Option<ConnResult<Option<C::Reply>>>
where
C: CookieWithReplyUnchecked,
{
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_poll_for_reply64);
unsafe {
let mut reply: *mut c_void = ptr::null_mut();
let received = xcb_poll_for_reply64(
self.c,
cookie.sequence(),
&mut reply as *mut _,
ptr::null_mut(),
);
match received {
0 => None,
1 => Some(self.handle_reply_unchecked::<C>(reply)),
_ => panic!("unexpected return value from xcb_poll_for_reply64"),
}
}
}
#[cfg(feature = "libxcb_v1_14")]
pub fn total_read(&self) -> usize {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_total_read);
unsafe { xcb_total_read(self.c) as usize }
}
#[cfg(feature = "libxcb_v1_14")]
pub fn total_written(&self) -> usize {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_total_written);
unsafe { xcb_total_written(self.c) as usize }
}
}
impl Connection {
unsafe fn handle_wait_for_event(&self, ev: *mut xcb_generic_event_t) -> Result<Event> {
if ev.is_null() {
self.has_error()?;
panic!("xcb_wait_for_event returned null with I/O error");
} else if is_error(ev) {
Err(error::resolve_error(ev as *mut _, &self.ext_data).into())
} else {
Ok(event::resolve_event(ev, &self.ext_data))
}
}
unsafe fn handle_poll_for_event(&self, ev: *mut xcb_generic_event_t) -> Result<Option<Event>> {
if ev.is_null() {
self.has_error()?;
Ok(None)
} else if is_error(ev) {
Err(error::resolve_error(ev as *mut _, &self.ext_data).into())
} else {
Ok(Some(event::resolve_event(ev, &self.ext_data)))
}
}
unsafe fn handle_reply_checked<C>(
&self,
reply: *mut c_void,
error: *mut xcb_generic_error_t,
) -> Result<C::Reply>
where
C: CookieWithReplyChecked,
{
match (reply.is_null(), error.is_null()) {
(true, true) => {
self.has_error()?;
unreachable!("xcb_wait_for_reply64 returned null without I/O error");
}
(true, false) => {
let error = error::resolve_error(error, &self.ext_data);
Err(error.into())
}
(false, true) => Ok(C::Reply::from_raw(reply as *const u8)),
(false, false) => unreachable!("xcb_wait_for_reply64 returned two pointers"),
}
}
unsafe fn handle_reply_unchecked<C>(&self, reply: *mut c_void) -> ConnResult<Option<C::Reply>>
where
C: CookieWithReplyUnchecked,
{
if reply.is_null() {
self.has_error()?;
Ok(None)
} else {
Ok(Some(C::Reply::from_raw(reply as *const u8)))
}
}
}
impl AsRef<Connection> for Connection {
fn as_ref(&self) -> &Connection {
self
}
}
impl AsRawFd for Connection {
fn as_raw_fd(&self) -> RawFd {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_get_file_descriptor);
unsafe { xcb_get_file_descriptor(self.c) }
}
}
#[cfg(feature = "as-raw-xcb-connection")]
unsafe impl as_raw_xcb_connection::AsRawXcbConnection for Connection {
fn as_raw_xcb_connection(&self) -> *mut as_raw_xcb_connection::xcb_connection_t {
self.get_raw_conn().cast()
}
}
impl Drop for Connection {
fn drop(&mut self) {
#[cfg(feature = "debug_atom_names")]
if self.dbg_atom_names {
unsafe {
dan::DAN_CONN = ptr::null_mut();
}
}
if self.should_drop {
#[cfg(not(any(feature = "xlib_xcb", feature = "xlib_xcb_dl")))]
unsafe {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_disconnect);
xcb_disconnect(self.c);
}
#[cfg(any(feature = "xlib_xcb", feature = "xlib_xcb_dl"))]
unsafe {
if self.dpy.is_null() {
#[cfg(feature = "dl")]
xcb_get_conn_funcs!(self, xcb_disconnect);
xcb_disconnect(self.c);
} else {
#[cfg(all(feature = "xlib_xcb", not(feature = "xlib_xcb_dl")))]
let xclose_display = xlib::XCloseDisplay;
#[cfg(feature = "xlib_xcb_dl")]
let (xclose_display, _lib) = {
let lib = xlib::Xlib::open().expect("X11-xcb library not loaded");
(lib.XCloseDisplay, lib)
};
xclose_display(self.dpy);
}
}
}
}
}
#[cfg(feature = "debug_atom_names")]
mod dan {
use super::{Connection, Xid};
use crate::ffi::base::xcb_connection_t;
use crate::x;
use std::fmt;
use std::mem;
use std::ptr;
use std::str;
pub(crate) static mut DAN_CONN: *mut xcb_connection_t = ptr::null_mut();
impl fmt::Debug for x::Atom {
#[allow(clippy::print_in_format_impl)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.resource_id() == 0 {
return f.write_str("ATOM_NONE");
}
let conn = unsafe { Connection::from_raw_conn(DAN_CONN) };
let cookie = conn.send_request(&x::GetAtomName { atom: *self });
let reply = conn.wait_for_reply(cookie).map_err(|err| {
eprintln!(
"Error during fmt::Debug of x::Atom (fetching atom name): {:#?}",
err
);
fmt::Error
})?;
let name = reply.name().to_utf8();
f.write_fmt(format_args!("Atom(\"{}\" ; {})", name, self.resource_id()))?;
mem::forget(conn);
Ok(())
}
}
}
unsafe fn check_connection_error(conn: *mut xcb_connection_t) -> ConnResult<()> {
#[cfg(feature = "dl")]
xcb_get_funcs!(xcb_connection_has_error);
match xcb_connection_has_error(conn) {
0 => Ok(()),
XCB_CONN_ERROR => Err(ConnError::Connection),
XCB_CONN_CLOSED_EXT_NOTSUPPORTED => Err(ConnError::ClosedExtNotSupported),
XCB_CONN_CLOSED_MEM_INSUFFICIENT => Err(ConnError::ClosedMemInsufficient),
XCB_CONN_CLOSED_REQ_LEN_EXCEED => Err(ConnError::ClosedReqLenExceed),
XCB_CONN_CLOSED_PARSE_ERR => Err(ConnError::ClosedParseErr),
XCB_CONN_CLOSED_INVALID_SCREEN => Err(ConnError::ClosedInvalidScreen),
XCB_CONN_CLOSED_FDPASSING_FAILED => Err(ConnError::ClosedFdPassingFailed),
code => unreachable!("unexpected error code from XCB: {}", code),
}
}
unsafe fn is_error(ev: *mut xcb_generic_event_t) -> bool {
debug_assert!(!ev.is_null());
(*ev).response_type == 0
}
bitflags! {
pub(crate) struct RequestFlags: u32 {
const NONE = 0;
const CHECKED = 1;
const RAW = 2;
const DISCARD_REPLY = 4;
const REPLY_FDS = 8;
}
}
pub(crate) fn align_pad(base: usize, align: usize) -> usize {
debug_assert!(align.is_power_of_two(), "`align` must be a power of two");
let base = base as isize;
let align = align as isize;
(-base & (align - 1)) as usize
}
#[test]
fn test_align_pad() {
assert_eq!(align_pad(0, 1), 0);
assert_eq!(align_pad(1234, 1), 0);
assert_eq!(align_pad(1235, 1), 0);
assert_eq!(align_pad(0, 2), 0);
assert_eq!(align_pad(1233, 2), 1);
assert_eq!(align_pad(1234, 2), 0);
assert_eq!(align_pad(0, 4), 0);
assert_eq!(align_pad(12, 4), 0);
assert_eq!(align_pad(13, 4), 3);
assert_eq!(align_pad(14, 4), 2);
assert_eq!(align_pad(15, 4), 1);
assert_eq!(align_pad(16, 4), 0);
}