use core::ffi::{CStr, c_char, c_int, c_long, c_uint, c_void};
use crate::linux::{CLOCK_MONOTONIC, Nfds};
use core::ptr;
use std::sync::OnceLock;
#[repr(C)]
pub struct Connection {
_opaque: [u8; 0],
}
#[repr(C)]
pub struct CursorContext {
_opaque: [u8; 0],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Cookie {
pub sequence: c_uint,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct GenericEvent {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub pad: [u32; 7],
pub full_sequence: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct GenericError {
pub response_type: u8,
pub error_code: u8,
pub sequence: u16,
pub resource_id: u32,
pub minor_code: u16,
pub major_code: u8,
pub pad0: u8,
pub pad: [u32; 5],
pub full_sequence: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Setup {
pub status: u8,
pub pad0: u8,
pub protocol_major_version: u16,
pub protocol_minor_version: u16,
pub length: u16,
pub release_number: u32,
pub resource_id_base: u32,
pub resource_id_mask: u32,
pub motion_buffer_size: u32,
pub vendor_len: u16,
pub maximum_request_length: u16,
pub roots_len: u8,
pub pixmap_formats_len: u8,
pub image_byte_order: u8,
pub bitmap_format_bit_order: u8,
pub bitmap_format_scanline_unit: u8,
pub bitmap_format_scanline_pad: u8,
pub min_keycode: u8,
pub max_keycode: u8,
pub pad1: [u8; 4],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Rectangle {
pub x: i16,
pub y: i16,
pub width: u16,
pub height: u16,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Screen {
pub root: u32,
pub default_colormap: u32,
pub white_pixel: u32,
pub black_pixel: u32,
pub current_input_masks: u32,
pub width_in_pixels: u16,
pub height_in_pixels: u16,
pub width_in_millimeters: u16,
pub height_in_millimeters: u16,
pub min_installed_maps: u16,
pub max_installed_maps: u16,
pub root_visual: u32,
pub backing_stores: u8,
pub save_unders: u8,
pub root_depth: u8,
pub allowed_depths_len: u8,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ScreenIterator {
pub data: *mut Screen,
pub rem: c_int,
pub index: c_int,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct InternAtomReply {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub length: u32,
pub atom: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct GetPropertyReply {
pub response_type: u8,
pub format: u8,
pub sequence: u16,
pub length: u32,
pub type_: u32,
pub bytes_after: u32,
pub value_len: u32,
pub pad0: [u8; 12],
}
#[cfg(feature = "x11-e2e")]
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct QueryTreeReply {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub length: u32,
pub root: u32,
pub parent: u32,
pub children_len: u16,
pub pad1: [u8; 14],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct GetGeometryReply {
pub response_type: u8,
pub depth: u8,
pub sequence: u16,
pub length: u32,
pub root: u32,
pub x: i16,
pub y: i16,
pub width: u16,
pub height: u16,
pub border_width: u16,
pub pad0: [u8; 2],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct TranslateCoordinatesReply {
pub response_type: u8,
pub same_screen: u8,
pub sequence: u16,
pub length: u32,
pub child: u32,
pub dst_x: i16,
pub dst_y: i16,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct QueryPointerReply {
pub response_type: u8,
pub same_screen: u8,
pub sequence: u16,
pub length: u32,
pub root: u32,
pub child: u32,
pub root_x: i16,
pub root_y: i16,
pub win_x: i16,
pub win_y: i16,
pub mask: u16,
pub pad0: [u8; 2],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct XkbUseExtensionReply {
pub response_type: u8,
pub supported: u8,
pub sequence: u16,
pub length: u32,
pub server_major: u16,
pub server_minor: u16,
pub pad0: [u8; 20],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ShapeQueryVersionReply {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub length: u32,
pub major_version: u16,
pub minor_version: u16,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct XkbPerClientFlagsReply {
pub response_type: u8,
pub device_id: u8,
pub sequence: u16,
pub length: u32,
pub supported: u32,
pub value: u32,
pub auto_ctrls: u32,
pub auto_ctrls_values: u32,
pub pad0: [u8; 8],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct GetSelectionOwnerReply {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub length: u32,
pub owner: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct GrabPointerReply {
pub response_type: u8,
pub status: u8,
pub sequence: u16,
pub length: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct QueryExtensionReply {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub length: u32,
pub present: u8,
pub major_opcode: u8,
pub first_event: u8,
pub first_error: u8,
}
#[repr(C)]
pub struct Extension {
_opaque: [u8; 0],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct RandrQueryVersionReply {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub length: u32,
pub major_version: u32,
pub minor_version: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct RandrScreenResourcesReply {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub length: u32,
pub timestamp: u32,
pub config_timestamp: u32,
pub num_crtcs: u16,
pub num_outputs: u16,
pub num_modes: u16,
pub names_len: u16,
pub pad1: [u8; 8],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct RandrModeInfo {
pub id: u32,
pub width: u16,
pub height: u16,
pub dot_clock: u32,
pub hsync_start: u16,
pub hsync_end: u16,
pub htotal: u16,
pub hskew: u16,
pub vsync_start: u16,
pub vsync_end: u16,
pub vtotal: u16,
pub name_len: u16,
pub mode_flags: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct RandrCrtcInfoReply {
pub response_type: u8,
pub status: u8,
pub sequence: u16,
pub length: u32,
pub timestamp: u32,
pub x: i16,
pub y: i16,
pub width: u16,
pub height: u16,
pub mode: u32,
pub rotation: u16,
pub rotations: u16,
pub num_outputs: u16,
pub num_possible_outputs: u16,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct RandrOutputInfoReply {
pub response_type: u8,
pub status: u8,
pub sequence: u16,
pub length: u32,
pub timestamp: u32,
pub crtc: u32,
pub mm_width: u32,
pub mm_height: u32,
pub connection: u8,
pub subpixel_order: u8,
pub num_crtcs: u16,
pub num_modes: u16,
pub num_preferred: u16,
pub num_clones: u16,
pub name_len: u16,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct RandrOutputPrimaryReply {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub length: u32,
pub output: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct XiQueryVersionReply {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub length: u32,
pub major_version: u16,
pub minor_version: u16,
pub pad1: [u8; 20],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct XiEventMask {
pub deviceid: u16,
pub mask_len: u16,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct XiRawEvent {
pub response_type: u8,
pub extension: u8,
pub sequence: u16,
pub length: u32,
pub event_type: u16,
pub deviceid: u16,
pub time: u32,
pub detail: u32,
pub sourceid: u16,
pub valuators_len: u16,
pub flags: u32,
pub pad0: [u8; 4],
pub full_sequence: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct InputEvent {
pub response_type: u8,
pub detail: u8,
pub sequence: u16,
pub time: u32,
pub root: u32,
pub event: u32,
pub child: u32,
pub root_x: i16,
pub root_y: i16,
pub event_x: i16,
pub event_y: i16,
pub state: u16,
pub same_screen: u8,
pub pad0: u8,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct FocusEvent {
pub response_type: u8,
pub detail: u8,
pub sequence: u16,
pub event: u32,
pub mode: u8,
pub pad0: [u8; 3],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ConfigureNotifyEvent {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub event: u32,
pub window: u32,
pub above_sibling: u32,
pub x: i16,
pub y: i16,
pub width: u16,
pub height: u16,
pub border_width: u16,
pub override_redirect: u8,
pub pad1: u8,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct WindowNotifyEvent {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub event: u32,
pub window: u32,
pub flag: u8,
pub pad1: [u8; 3],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct SyntheticWindowNotify {
pub event: WindowNotifyEvent,
pub pad: [u8; 16],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct PropertyNotifyEvent {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub window: u32,
pub atom: u32,
pub time: u32,
pub state: u8,
pub pad1: [u8; 3],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct SelectionClearEvent {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub time: u32,
pub owner: u32,
pub selection: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct SelectionRequestEvent {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub time: u32,
pub owner: u32,
pub requestor: u32,
pub selection: u32,
pub target: u32,
pub property: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SelectionNotifyEvent {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub time: u32,
pub requestor: u32,
pub selection: u32,
pub target: u32,
pub property: u32,
pub pad1: [u8; 8],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct ClientMessageEvent {
pub response_type: u8,
pub format: u8,
pub sequence: u16,
pub window: u32,
pub type_: u32,
pub data: [u32; 5],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct MappingNotifyEvent {
pub response_type: u8,
pub pad0: u8,
pub sequence: u16,
pub request: u8,
pub first_keycode: u8,
pub count: u8,
pub pad1: u8,
}
pub mod event {
pub const KEY_PRESS: u8 = 2;
pub const KEY_RELEASE: u8 = 3;
pub const BUTTON_PRESS: u8 = 4;
pub const BUTTON_RELEASE: u8 = 5;
pub const MOTION_NOTIFY: u8 = 6;
pub const ENTER_NOTIFY: u8 = 7;
pub const LEAVE_NOTIFY: u8 = 8;
pub const FOCUS_IN: u8 = 9;
pub const FOCUS_OUT: u8 = 10;
pub const DESTROY_NOTIFY: u8 = 17;
pub const UNMAP_NOTIFY: u8 = 18;
pub const MAP_NOTIFY: u8 = 19;
pub const CONFIGURE_NOTIFY: u8 = 22;
pub const PROPERTY_NOTIFY: u8 = 28;
pub const SELECTION_CLEAR: u8 = 29;
pub const SELECTION_REQUEST: u8 = 30;
pub const SELECTION_NOTIFY: u8 = 31;
pub const MAPPING_NOTIFY: u8 = 34;
pub const GE_GENERIC: u8 = 35;
pub const CLIENT_MESSAGE: u8 = 33;
}
pub mod event_mask {
pub const KEY_PRESS: u32 = 1;
pub const KEY_RELEASE: u32 = 1 << 1;
pub const BUTTON_PRESS: u32 = 1 << 2;
pub const BUTTON_RELEASE: u32 = 1 << 3;
pub const ENTER_WINDOW: u32 = 1 << 4;
pub const LEAVE_WINDOW: u32 = 1 << 5;
pub const POINTER_MOTION: u32 = 1 << 6;
pub const EXPOSURE: u32 = 1 << 15;
pub const STRUCTURE_NOTIFY: u32 = 1 << 17;
pub const FOCUS_CHANGE: u32 = 1 << 21;
pub const PROPERTY_CHANGE: u32 = 1 << 22;
}
pub mod gc {
pub const FOREGROUND: u32 = 1 << 2;
}
pub mod cw {
pub const BACK_PIXEL: u32 = 1 << 1;
pub const EVENT_MASK: u32 = 1 << 11;
}
pub mod config_window {
pub const X: u16 = 1;
pub const Y: u16 = 1 << 1;
#[cfg(feature = "x11-e2e")]
pub const WIDTH: u16 = 1 << 2;
#[cfg(feature = "x11-e2e")]
pub const HEIGHT: u16 = 1 << 3;
}
pub mod value {
pub const COPY_FROM_PARENT: u32 = 0;
pub const INPUT_OUTPUT: u16 = 1;
pub const PROP_MODE_REPLACE: u8 = 0;
pub const PROP_MODE_APPEND: u8 = 2;
pub const NONE: u32 = 0;
pub const CURRENT_TIME: u32 = 0;
pub const GRAB_MODE_ASYNC: u8 = 1;
pub const GRAB_SUCCESS: u8 = 0;
pub const ATOM_ATOM: u32 = 4;
pub const ATOM_CARDINAL: u32 = 6;
pub const ATOM_INTEGER: u32 = 19;
pub const ATOM_STRING: u32 = 31;
pub const ATOM_WM_NAME: u32 = 39;
pub const ATOM_WM_NORMAL_HINTS: u32 = 40;
pub const ATOM_WM_SIZE_HINTS: u32 = 41;
pub const ATOM_WM_CLASS: u32 = 67;
pub const ATOM_WM_HINTS: u32 = 35;
pub const ATOM_RESOURCE_MANAGER: u32 = 23;
pub const XI_ALL_MASTER_DEVICES: u16 = 1;
pub const XI_RAW_MOTION: u16 = 17;
pub const XI_RAW_MOTION_MASK: u32 = 1 << 17;
pub const RANDR_SCREEN_CHANGE_MASK: u16 = 1;
pub const RANDR_CRTC_CHANGE_MASK: u16 = 1 << 1;
pub const RANDR_OUTPUT_CHANGE_MASK: u16 = 1 << 2;
pub const XKB_USE_CORE_KBD: u16 = 0x0100;
pub const XKB_DETECTABLE_AUTO_REPEAT: u32 = 1;
pub const XKB_MAJOR: u16 = 1;
pub const XKB_MINOR: u16 = 0;
pub const SHAPE_SET: u8 = 0;
pub const SHAPE_INPUT: u8 = 2;
pub const CLIP_UNSORTED: u8 = 0;
pub const PIXMAP_NONE: u32 = 0;
}
unsafe extern "C" {
fn dlopen(filename: *const c_char, flags: c_int) -> *mut c_void;
fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
fn dlerror() -> *const c_char;
fn free(ptr: *mut c_void);
fn poll(fds: *mut PollFd, nfds: Nfds, timeout: c_int) -> c_int;
fn clock_gettime(clock_id: c_int, tp: *mut Timespec) -> c_int;
}
#[repr(C)]
struct PollFd {
fd: c_int,
events: i16,
revents: i16,
}
#[repr(C)]
struct Timespec {
tv_sec: c_long,
tv_nsec: c_long,
}
const RTLD_NOW: c_int = 2;
const POLLIN: i16 = 0x001;
mod prototype {
#[cfg(feature = "x11-e2e")]
use super::QueryTreeReply;
use super::{
Connection, Cookie, CursorContext, Extension, GenericError, GenericEvent, GetGeometryReply,
GetPropertyReply, GetSelectionOwnerReply, GrabPointerReply, InternAtomReply,
QueryExtensionReply, QueryPointerReply, RandrCrtcInfoReply, RandrOutputInfoReply,
RandrOutputPrimaryReply, RandrQueryVersionReply, RandrScreenResourcesReply, Rectangle,
Screen, ScreenIterator, Setup, ShapeQueryVersionReply, TranslateCoordinatesReply,
XiEventMask, XiQueryVersionReply, XkbPerClientFlagsReply, XkbUseExtensionReply, c_char,
c_int, c_void,
};
pub type Connect = unsafe extern "C" fn(*const c_char, *mut c_int) -> *mut Connection;
pub type Disconnect = unsafe extern "C" fn(*mut Connection);
pub type ConnInt = unsafe extern "C" fn(*mut Connection) -> c_int;
pub type GenerateId = unsafe extern "C" fn(*mut Connection) -> u32;
pub type GetSetup = unsafe extern "C" fn(*mut Connection) -> *const Setup;
pub type SetupRootsIterator = unsafe extern "C" fn(*const Setup) -> ScreenIterator;
pub type ScreenNext = unsafe extern "C" fn(*mut ScreenIterator);
pub type PollForEvent = unsafe extern "C" fn(*mut Connection) -> *mut GenericEvent;
pub type GetExtensionData =
unsafe extern "C" fn(*mut Connection, *mut Extension) -> *const QueryExtensionReply;
pub type CreateWindow = unsafe extern "C" fn(
*mut Connection,
u8,
u32,
u32,
i16,
i16,
u16,
u16,
u16,
u16,
u32,
u32,
*const c_void,
) -> Cookie;
pub type WindowRequest = unsafe extern "C" fn(*mut Connection, u32) -> Cookie;
pub type ChangeWindowAttributes =
unsafe extern "C" fn(*mut Connection, u32, u32, *const c_void) -> Cookie;
pub type ConfigureWindow =
unsafe extern "C" fn(*mut Connection, u32, u16, *const c_void) -> Cookie;
pub type ChangeProperty =
unsafe extern "C" fn(*mut Connection, u8, u32, u32, u32, u8, u32, *const c_void) -> Cookie;
pub type DeleteProperty = unsafe extern "C" fn(*mut Connection, u32, u32) -> Cookie;
pub type GetProperty =
unsafe extern "C" fn(*mut Connection, u8, u32, u32, u32, u32, u32) -> Cookie;
pub type GetPropertyReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut GetPropertyReply;
pub type GetPropertyValue = unsafe extern "C" fn(*const GetPropertyReply) -> *mut c_void;
pub type GetPropertyValueLength = unsafe extern "C" fn(*const GetPropertyReply) -> c_int;
pub type InternAtom = unsafe extern "C" fn(*mut Connection, u8, u16, *const c_char) -> Cookie;
pub type InternAtomReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut InternAtomReply;
pub type DrawableRequest = unsafe extern "C" fn(*mut Connection, u32) -> Cookie;
pub type GetGeometryReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut GetGeometryReply;
pub type TranslateCoordinates =
unsafe extern "C" fn(*mut Connection, u32, u32, i16, i16) -> Cookie;
pub type TranslateCoordinatesReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
)
-> *mut TranslateCoordinatesReply;
#[cfg(feature = "x11-e2e")]
pub type QueryTree = unsafe extern "C" fn(*mut Connection, u32) -> Cookie;
#[cfg(feature = "x11-e2e")]
pub type QueryTreeReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut QueryTreeReply;
#[cfg(feature = "x11-e2e")]
pub type QueryTreeChildren = unsafe extern "C" fn(*const QueryTreeReply) -> *mut u32;
#[cfg(feature = "x11-e2e")]
pub type QueryTreeChildrenLength = unsafe extern "C" fn(*const QueryTreeReply) -> c_int;
pub type SendEvent =
unsafe extern "C" fn(*mut Connection, u8, u32, u32, *const c_char) -> Cookie;
pub type SetSelectionOwner = unsafe extern "C" fn(*mut Connection, u32, u32, u32) -> Cookie;
pub type GetSelectionOwnerReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut GetSelectionOwnerReply;
pub type ConvertSelection =
unsafe extern "C" fn(*mut Connection, u32, u32, u32, u32, u32) -> Cookie;
pub type GrabPointer =
unsafe extern "C" fn(*mut Connection, u8, u32, u16, u8, u8, u32, u32, u32) -> Cookie;
pub type GrabPointerReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut GrabPointerReply;
pub type UngrabPointer = unsafe extern "C" fn(*mut Connection, u32) -> Cookie;
pub type WarpPointer =
unsafe extern "C" fn(*mut Connection, u32, u32, i16, i16, u16, u16, i16, i16) -> Cookie;
pub type QueryPointerReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut QueryPointerReply;
pub type CreatePixmap = unsafe extern "C" fn(*mut Connection, u8, u32, u32, u16, u16) -> Cookie;
pub type CreateCursor = unsafe extern "C" fn(
*mut Connection,
u32,
u32,
u32,
u16,
u16,
u16,
u16,
u16,
u16,
u16,
u16,
) -> Cookie;
pub type CreateGc =
unsafe extern "C" fn(*mut Connection, u32, u32, u32, *const c_void) -> Cookie;
pub type PolyFillRectangle =
unsafe extern "C" fn(*mut Connection, u32, u32, u32, *const Rectangle) -> Cookie;
pub type FreeResource = unsafe extern "C" fn(*mut Connection, u32) -> Cookie;
pub type XkbUseExtension = unsafe extern "C" fn(*mut Connection, u16, u16) -> Cookie;
pub type XkbUseExtensionReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut XkbUseExtensionReply;
pub type XkbPerClientFlags =
unsafe extern "C" fn(*mut Connection, u16, u32, u32, u32, u32, u32) -> Cookie;
pub type XkbPerClientFlagsReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut XkbPerClientFlagsReply;
pub type RandrQueryVersion = unsafe extern "C" fn(*mut Connection, u32, u32) -> Cookie;
pub type RandrQueryVersionReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut RandrQueryVersionReply;
pub type RandrWindowRequest = unsafe extern "C" fn(*mut Connection, u32) -> Cookie;
pub type RandrScreenResourcesReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
)
-> *mut RandrScreenResourcesReply;
pub type RandrResourcesCrtcs =
unsafe extern "C" fn(*const RandrScreenResourcesReply) -> *mut u32;
pub type RandrResourcesModes =
unsafe extern "C" fn(*const RandrScreenResourcesReply) -> *mut super::RandrModeInfo;
pub type RandrInfoRequest = unsafe extern "C" fn(*mut Connection, u32, u32) -> Cookie;
pub type RandrCrtcInfoReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut RandrCrtcInfoReply;
pub type RandrCrtcOutputs = unsafe extern "C" fn(*const RandrCrtcInfoReply) -> *mut u32;
pub type RandrOutputInfoReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut RandrOutputInfoReply;
pub type RandrOutputName = unsafe extern "C" fn(*const RandrOutputInfoReply) -> *mut u8;
pub type RandrOutputPrimaryReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut RandrOutputPrimaryReply;
pub type RandrSelectInput = unsafe extern "C" fn(*mut Connection, u32, u16) -> Cookie;
pub type XiQueryVersion = unsafe extern "C" fn(*mut Connection, u16, u16) -> Cookie;
pub type XiQueryVersionReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut XiQueryVersionReply;
pub type XiSelectEvents =
unsafe extern "C" fn(*mut Connection, u32, u16, *const XiEventMask) -> Cookie;
pub type CursorContextNew =
unsafe extern "C" fn(*mut Connection, *mut Screen, *mut *mut CursorContext) -> c_int;
pub type CursorLoadCursor = unsafe extern "C" fn(*mut CursorContext, *const c_char) -> u32;
pub type CursorContextFree = unsafe extern "C" fn(*mut CursorContext);
pub type ShapeQueryVersion = unsafe extern "C" fn(*mut Connection) -> Cookie;
pub type ShapeQueryVersionReplyFn = unsafe extern "C" fn(
*mut Connection,
Cookie,
*mut *mut GenericError,
) -> *mut ShapeQueryVersionReply;
pub type ShapeRectangles = unsafe extern "C" fn(
*mut Connection,
u8,
u8,
u8,
u32,
i16,
i16,
u32,
*const Rectangle,
) -> Cookie;
pub type ShapeMask =
unsafe extern "C" fn(*mut Connection, u8, u8, u32, i16, i16, u32) -> Cookie;
}
pub struct Lib {
pub connect: prototype::Connect,
pub disconnect: prototype::Disconnect,
pub connection_has_error: prototype::ConnInt,
pub flush: prototype::ConnInt,
pub get_file_descriptor: prototype::ConnInt,
pub generate_id: prototype::GenerateId,
pub get_setup: prototype::GetSetup,
pub setup_roots_iterator: prototype::SetupRootsIterator,
pub screen_next: prototype::ScreenNext,
pub poll_for_event: prototype::PollForEvent,
pub get_extension_data: prototype::GetExtensionData,
pub create_window: prototype::CreateWindow,
pub destroy_window: prototype::WindowRequest,
pub map_window: prototype::WindowRequest,
pub unmap_window: prototype::WindowRequest,
pub change_window_attributes: prototype::ChangeWindowAttributes,
pub configure_window: prototype::ConfigureWindow,
pub change_property: prototype::ChangeProperty,
pub delete_property: prototype::DeleteProperty,
pub get_property: prototype::GetProperty,
pub get_property_reply: prototype::GetPropertyReplyFn,
pub get_property_value: prototype::GetPropertyValue,
pub get_property_value_length: prototype::GetPropertyValueLength,
#[cfg(feature = "x11-e2e")]
pub query_tree: prototype::QueryTree,
#[cfg(feature = "x11-e2e")]
pub query_tree_reply: prototype::QueryTreeReplyFn,
#[cfg(feature = "x11-e2e")]
pub query_tree_children: prototype::QueryTreeChildren,
#[cfg(feature = "x11-e2e")]
pub query_tree_children_length: prototype::QueryTreeChildrenLength,
pub intern_atom: prototype::InternAtom,
pub intern_atom_reply: prototype::InternAtomReplyFn,
pub get_geometry: prototype::DrawableRequest,
pub get_geometry_reply: prototype::GetGeometryReplyFn,
pub translate_coordinates: prototype::TranslateCoordinates,
pub translate_coordinates_reply: prototype::TranslateCoordinatesReplyFn,
pub send_event: prototype::SendEvent,
pub set_selection_owner: prototype::SetSelectionOwner,
pub get_selection_owner: prototype::DrawableRequest,
pub get_selection_owner_reply: prototype::GetSelectionOwnerReplyFn,
pub convert_selection: prototype::ConvertSelection,
pub grab_pointer: prototype::GrabPointer,
pub grab_pointer_reply: prototype::GrabPointerReplyFn,
pub ungrab_pointer: prototype::UngrabPointer,
pub warp_pointer: prototype::WarpPointer,
pub query_pointer: prototype::DrawableRequest,
pub query_pointer_reply: prototype::QueryPointerReplyFn,
pub create_pixmap: prototype::CreatePixmap,
pub create_cursor: prototype::CreateCursor,
pub free_pixmap: prototype::FreeResource,
pub free_cursor: prototype::FreeResource,
pub create_gc: prototype::CreateGc,
pub poly_fill_rectangle: prototype::PolyFillRectangle,
pub free_gc: prototype::FreeResource,
}
impl core::fmt::Debug for Lib {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("Lib(libxcb)")
}
}
pub struct RandrLib {
pub id: usize,
pub query_version: prototype::RandrQueryVersion,
pub query_version_reply: prototype::RandrQueryVersionReplyFn,
pub get_screen_resources_current: prototype::RandrWindowRequest,
pub get_screen_resources_current_reply: prototype::RandrScreenResourcesReplyFn,
pub resources_crtcs: prototype::RandrResourcesCrtcs,
pub resources_modes: prototype::RandrResourcesModes,
pub get_crtc_info: prototype::RandrInfoRequest,
pub get_crtc_info_reply: prototype::RandrCrtcInfoReplyFn,
pub crtc_outputs: prototype::RandrCrtcOutputs,
pub get_output_info: prototype::RandrInfoRequest,
pub get_output_info_reply: prototype::RandrOutputInfoReplyFn,
pub output_name: prototype::RandrOutputName,
pub get_output_primary: prototype::RandrWindowRequest,
pub get_output_primary_reply: prototype::RandrOutputPrimaryReplyFn,
pub select_input: prototype::RandrSelectInput,
}
impl core::fmt::Debug for RandrLib {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("RandrLib(libxcb-randr)")
}
}
pub struct XiLib {
pub id: usize,
pub query_version: prototype::XiQueryVersion,
pub query_version_reply: prototype::XiQueryVersionReplyFn,
pub select_events: prototype::XiSelectEvents,
}
impl core::fmt::Debug for XiLib {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("XiLib(libxcb-xinput)")
}
}
pub struct XkbLib {
pub use_extension: prototype::XkbUseExtension,
pub use_extension_reply: prototype::XkbUseExtensionReplyFn,
pub per_client_flags: prototype::XkbPerClientFlags,
pub per_client_flags_reply: prototype::XkbPerClientFlagsReplyFn,
}
impl core::fmt::Debug for XkbLib {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("XkbLib(libxcb-xkb)")
}
}
pub struct CursorLib {
pub context_new: prototype::CursorContextNew,
pub load_cursor: prototype::CursorLoadCursor,
pub context_free: prototype::CursorContextFree,
}
impl core::fmt::Debug for CursorLib {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("CursorLib(libxcb-cursor)")
}
}
pub struct ShapeLib {
pub id: usize,
pub query_version: prototype::ShapeQueryVersion,
pub query_version_reply: prototype::ShapeQueryVersionReplyFn,
pub rectangles: prototype::ShapeRectangles,
pub mask: prototype::ShapeMask,
}
impl core::fmt::Debug for ShapeLib {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("ShapeLib(libxcb-shape)")
}
}
#[derive(Debug)]
pub struct Ext {
pub randr: Option<&'static RandrLib>,
pub xi: Option<&'static XiLib>,
pub xkb: Option<&'static XkbLib>,
pub cursor: Option<&'static CursorLib>,
pub shape: Option<&'static ShapeLib>,
}
const SONAMES: [&CStr; 2] = [c"libxcb.so.1", c"libxcb.so"];
const RANDR_SONAMES: [&CStr; 2] = [c"libxcb-randr.so.0", c"libxcb-randr.so"];
const XI_SONAMES: [&CStr; 2] = [c"libxcb-xinput.so.0", c"libxcb-xinput.so"];
const XKB_SONAMES: [&CStr; 2] = [c"libxcb-xkb.so.1", c"libxcb-xkb.so"];
const CURSOR_SONAMES: [&CStr; 2] = [c"libxcb-cursor.so.0", c"libxcb-cursor.so"];
const SHAPE_SONAMES: [&CStr; 2] = [c"libxcb-shape.so.0", c"libxcb-shape.so"];
static LIB: OnceLock<Result<Lib, String>> = OnceLock::new();
static RANDR: OnceLock<Option<RandrLib>> = OnceLock::new();
static XI: OnceLock<Option<XiLib>> = OnceLock::new();
static XKB: OnceLock<Option<XkbLib>> = OnceLock::new();
static CURSOR: OnceLock<Option<CursorLib>> = OnceLock::new();
static SHAPE: OnceLock<Option<ShapeLib>> = OnceLock::new();
#[inline]
#[must_use]
pub const fn extension_ptr(id: usize) -> *mut Extension {
id as *mut Extension
}
pub fn load() -> Result<&'static Lib, &'static str> {
match LIB.get_or_init(load_uncached) {
Ok(lib) => Ok(lib),
Err(message) => Err(message.as_str()),
}
}
#[must_use]
pub fn load_ext() -> Ext {
Ext {
randr: RANDR.get_or_init(load_randr).as_ref(),
xi: XI.get_or_init(load_xi).as_ref(),
xkb: XKB.get_or_init(load_xkb).as_ref(),
cursor: CURSOR.get_or_init(load_cursor).as_ref(),
shape: SHAPE.get_or_init(load_shape).as_ref(),
}
}
fn open_library(sonames: &[&CStr]) -> *mut c_void {
for soname in sonames {
let handle = unsafe { dlopen(soname.as_ptr(), RTLD_NOW) };
if !handle.is_null() {
return handle;
}
}
ptr::null_mut()
}
fn last_dl_error() -> String {
unsafe {
let message = dlerror();
if message.is_null() {
"not found".to_string()
} else {
CStr::from_ptr(message).to_string_lossy().into_owned()
}
}
}
fn load_uncached() -> Result<Lib, String> {
let handle = open_library(&SONAMES);
if handle.is_null() {
return Err(format!("cannot load libxcb.so.1: {}", last_dl_error()));
}
macro_rules! symbol {
($name:literal, $ty:ty) => {{
let raw = unsafe { dlsym(handle, concat!($name, "\0").as_ptr().cast()) };
if raw.is_null() {
return Err(format!("libxcb.so.1 has no symbol {}", $name));
}
unsafe { core::mem::transmute::<*mut c_void, $ty>(raw) }
}};
}
Ok(Lib {
connect: symbol!("xcb_connect", prototype::Connect),
disconnect: symbol!("xcb_disconnect", prototype::Disconnect),
connection_has_error: symbol!("xcb_connection_has_error", prototype::ConnInt),
flush: symbol!("xcb_flush", prototype::ConnInt),
get_file_descriptor: symbol!("xcb_get_file_descriptor", prototype::ConnInt),
generate_id: symbol!("xcb_generate_id", prototype::GenerateId),
get_setup: symbol!("xcb_get_setup", prototype::GetSetup),
setup_roots_iterator: symbol!("xcb_setup_roots_iterator", prototype::SetupRootsIterator),
screen_next: symbol!("xcb_screen_next", prototype::ScreenNext),
poll_for_event: symbol!("xcb_poll_for_event", prototype::PollForEvent),
get_extension_data: symbol!("xcb_get_extension_data", prototype::GetExtensionData),
create_window: symbol!("xcb_create_window", prototype::CreateWindow),
destroy_window: symbol!("xcb_destroy_window", prototype::WindowRequest),
map_window: symbol!("xcb_map_window", prototype::WindowRequest),
unmap_window: symbol!("xcb_unmap_window", prototype::WindowRequest),
change_window_attributes: symbol!(
"xcb_change_window_attributes",
prototype::ChangeWindowAttributes
),
configure_window: symbol!("xcb_configure_window", prototype::ConfigureWindow),
change_property: symbol!("xcb_change_property", prototype::ChangeProperty),
delete_property: symbol!("xcb_delete_property", prototype::DeleteProperty),
get_property: symbol!("xcb_get_property", prototype::GetProperty),
get_property_reply: symbol!("xcb_get_property_reply", prototype::GetPropertyReplyFn),
get_property_value: symbol!("xcb_get_property_value", prototype::GetPropertyValue),
get_property_value_length: symbol!(
"xcb_get_property_value_length",
prototype::GetPropertyValueLength
),
#[cfg(feature = "x11-e2e")]
query_tree: symbol!("xcb_query_tree", prototype::QueryTree),
#[cfg(feature = "x11-e2e")]
query_tree_reply: symbol!("xcb_query_tree_reply", prototype::QueryTreeReplyFn),
#[cfg(feature = "x11-e2e")]
query_tree_children: symbol!("xcb_query_tree_children", prototype::QueryTreeChildren),
#[cfg(feature = "x11-e2e")]
query_tree_children_length: symbol!(
"xcb_query_tree_children_length",
prototype::QueryTreeChildrenLength
),
intern_atom: symbol!("xcb_intern_atom", prototype::InternAtom),
intern_atom_reply: symbol!("xcb_intern_atom_reply", prototype::InternAtomReplyFn),
get_geometry: symbol!("xcb_get_geometry", prototype::DrawableRequest),
get_geometry_reply: symbol!("xcb_get_geometry_reply", prototype::GetGeometryReplyFn),
translate_coordinates: symbol!(
"xcb_translate_coordinates",
prototype::TranslateCoordinates
),
translate_coordinates_reply: symbol!(
"xcb_translate_coordinates_reply",
prototype::TranslateCoordinatesReplyFn
),
send_event: symbol!("xcb_send_event", prototype::SendEvent),
set_selection_owner: symbol!("xcb_set_selection_owner", prototype::SetSelectionOwner),
get_selection_owner: symbol!("xcb_get_selection_owner", prototype::DrawableRequest),
get_selection_owner_reply: symbol!(
"xcb_get_selection_owner_reply",
prototype::GetSelectionOwnerReplyFn
),
convert_selection: symbol!("xcb_convert_selection", prototype::ConvertSelection),
grab_pointer: symbol!("xcb_grab_pointer", prototype::GrabPointer),
grab_pointer_reply: symbol!("xcb_grab_pointer_reply", prototype::GrabPointerReplyFn),
ungrab_pointer: symbol!("xcb_ungrab_pointer", prototype::UngrabPointer),
warp_pointer: symbol!("xcb_warp_pointer", prototype::WarpPointer),
query_pointer: symbol!("xcb_query_pointer", prototype::DrawableRequest),
query_pointer_reply: symbol!("xcb_query_pointer_reply", prototype::QueryPointerReplyFn),
create_pixmap: symbol!("xcb_create_pixmap", prototype::CreatePixmap),
create_cursor: symbol!("xcb_create_cursor", prototype::CreateCursor),
free_pixmap: symbol!("xcb_free_pixmap", prototype::FreeResource),
free_cursor: symbol!("xcb_free_cursor", prototype::FreeResource),
create_gc: symbol!("xcb_create_gc", prototype::CreateGc),
poly_fill_rectangle: symbol!("xcb_poly_fill_rectangle", prototype::PolyFillRectangle),
free_gc: symbol!("xcb_free_gc", prototype::FreeResource),
})
}
fn load_randr() -> Option<RandrLib> {
let handle = open_library(&RANDR_SONAMES);
if handle.is_null() {
crcbl_core::log::warn!(
"libxcb-randr is not available: one monitor covering the whole screen, \
and no refresh rate"
);
return None;
}
macro_rules! symbol {
($name:literal, $ty:ty) => {{
let raw = unsafe { dlsym(handle, concat!($name, "\0").as_ptr().cast()) };
if raw.is_null() {
crcbl_core::log::warn!("libxcb-randr has no symbol {}; degrading", $name);
return None;
}
unsafe { core::mem::transmute::<*mut c_void, $ty>(raw) }
}};
}
let id = unsafe { dlsym(handle, c"xcb_randr_id".as_ptr()) } as usize;
if id == 0 {
crcbl_core::log::warn!("libxcb-randr has no xcb_randr_id; degrading");
return None;
}
Some(RandrLib {
id,
query_version: symbol!("xcb_randr_query_version", prototype::RandrQueryVersion),
query_version_reply: symbol!(
"xcb_randr_query_version_reply",
prototype::RandrQueryVersionReplyFn
),
get_screen_resources_current: symbol!(
"xcb_randr_get_screen_resources_current",
prototype::RandrWindowRequest
),
get_screen_resources_current_reply: symbol!(
"xcb_randr_get_screen_resources_current_reply",
prototype::RandrScreenResourcesReplyFn
),
resources_crtcs: symbol!(
"xcb_randr_get_screen_resources_current_crtcs",
prototype::RandrResourcesCrtcs
),
resources_modes: symbol!(
"xcb_randr_get_screen_resources_current_modes",
prototype::RandrResourcesModes
),
get_crtc_info: symbol!("xcb_randr_get_crtc_info", prototype::RandrInfoRequest),
get_crtc_info_reply: symbol!(
"xcb_randr_get_crtc_info_reply",
prototype::RandrCrtcInfoReplyFn
),
crtc_outputs: symbol!(
"xcb_randr_get_crtc_info_outputs",
prototype::RandrCrtcOutputs
),
get_output_info: symbol!("xcb_randr_get_output_info", prototype::RandrInfoRequest),
get_output_info_reply: symbol!(
"xcb_randr_get_output_info_reply",
prototype::RandrOutputInfoReplyFn
),
output_name: symbol!("xcb_randr_get_output_info_name", prototype::RandrOutputName),
get_output_primary: symbol!(
"xcb_randr_get_output_primary",
prototype::RandrWindowRequest
),
get_output_primary_reply: symbol!(
"xcb_randr_get_output_primary_reply",
prototype::RandrOutputPrimaryReplyFn
),
select_input: symbol!("xcb_randr_select_input", prototype::RandrSelectInput),
})
}
fn load_xi() -> Option<XiLib> {
let handle = open_library(&XI_SONAMES);
if handle.is_null() {
crcbl_core::log::warn!(
"libxcb-xinput is not available: no unaccelerated pointer motion, so \
ShellCaps::RAW_POINTER_MOTION stays clear"
);
return None;
}
macro_rules! symbol {
($name:literal, $ty:ty) => {{
let raw = unsafe { dlsym(handle, concat!($name, "\0").as_ptr().cast()) };
if raw.is_null() {
crcbl_core::log::warn!("libxcb-xinput has no symbol {}; degrading", $name);
return None;
}
unsafe { core::mem::transmute::<*mut c_void, $ty>(raw) }
}};
}
let id = unsafe { dlsym(handle, c"xcb_input_id".as_ptr()) } as usize;
if id == 0 {
crcbl_core::log::warn!("libxcb-xinput has no xcb_input_id; degrading");
return None;
}
Some(XiLib {
id,
query_version: symbol!("xcb_input_xi_query_version", prototype::XiQueryVersion),
query_version_reply: symbol!(
"xcb_input_xi_query_version_reply",
prototype::XiQueryVersionReplyFn
),
select_events: symbol!("xcb_input_xi_select_events", prototype::XiSelectEvents),
})
}
fn load_xkb() -> Option<XkbLib> {
let handle = open_library(&XKB_SONAMES);
if handle.is_null() {
crcbl_core::log::warn!(
"libxcb-xkb is not available: auto-repeat falls back to the release/press \
lookahead, which misreads a repeat whose two halves arrive in different reads"
);
return None;
}
macro_rules! symbol {
($name:literal, $ty:ty) => {{
let raw = unsafe { dlsym(handle, concat!($name, "\0").as_ptr().cast()) };
if raw.is_null() {
crcbl_core::log::warn!("libxcb-xkb has no symbol {}; degrading", $name);
return None;
}
unsafe { core::mem::transmute::<*mut c_void, $ty>(raw) }
}};
}
Some(XkbLib {
use_extension: symbol!("xcb_xkb_use_extension", prototype::XkbUseExtension),
use_extension_reply: symbol!(
"xcb_xkb_use_extension_reply",
prototype::XkbUseExtensionReplyFn
),
per_client_flags: symbol!("xcb_xkb_per_client_flags", prototype::XkbPerClientFlags),
per_client_flags_reply: symbol!(
"xcb_xkb_per_client_flags_reply",
prototype::XkbPerClientFlagsReplyFn
),
})
}
fn load_cursor() -> Option<CursorLib> {
let handle = open_library(&CURSOR_SONAMES);
if handle.is_null() {
crcbl_core::log::warn!(
"libxcb-cursor is not available: cursor shapes are recorded and not applied, so \
ShellCaps::CURSOR_SHAPE stays clear"
);
return None;
}
macro_rules! symbol {
($name:literal, $ty:ty) => {{
let raw = unsafe { dlsym(handle, concat!($name, "\0").as_ptr().cast()) };
if raw.is_null() {
crcbl_core::log::warn!("libxcb-cursor has no symbol {}; degrading", $name);
return None;
}
unsafe { core::mem::transmute::<*mut c_void, $ty>(raw) }
}};
}
Some(CursorLib {
context_new: symbol!("xcb_cursor_context_new", prototype::CursorContextNew),
load_cursor: symbol!("xcb_cursor_load_cursor", prototype::CursorLoadCursor),
context_free: symbol!("xcb_cursor_context_free", prototype::CursorContextFree),
})
}
fn load_shape() -> Option<ShapeLib> {
let handle = open_library(&SHAPE_SONAMES);
if handle.is_null() {
crcbl_core::log::warn!(
"libxcb-shape is not available: an input region is not applied, so \
ShellCaps::INPUT_REGION stays clear"
);
return None;
}
macro_rules! symbol {
($name:literal, $ty:ty) => {{
let raw = unsafe { dlsym(handle, concat!($name, "\0").as_ptr().cast()) };
if raw.is_null() {
crcbl_core::log::warn!("libxcb-shape has no symbol {}; degrading", $name);
return None;
}
unsafe { core::mem::transmute::<*mut c_void, $ty>(raw) }
}};
}
let id = unsafe { dlsym(handle, c"xcb_shape_id".as_ptr()) } as usize;
if id == 0 {
crcbl_core::log::warn!("libxcb-shape has no xcb_shape_id; degrading");
return None;
}
Some(ShapeLib {
id,
query_version: symbol!("xcb_shape_query_version", prototype::ShapeQueryVersion),
query_version_reply: symbol!(
"xcb_shape_query_version_reply",
prototype::ShapeQueryVersionReplyFn
),
rectangles: symbol!("xcb_shape_rectangles", prototype::ShapeRectangles),
mask: symbol!("xcb_shape_mask", prototype::ShapeMask),
})
}
#[inline]
pub unsafe fn free_reply<T>(ptr: *mut T) {
if !ptr.is_null() {
unsafe { free(ptr.cast::<c_void>()) };
}
}
#[must_use]
pub fn poll_readable(fd: c_int, timeout_ms: c_int) -> bool {
let mut fds = PollFd {
fd,
events: POLLIN,
revents: 0,
};
let result = unsafe { poll(&raw mut fds, 1, timeout_ms) };
result > 0 && fds.revents & POLLIN != 0
}
#[must_use]
pub fn poll_readable_or(primary: c_int, other: c_int, timeout_ms: c_int) -> bool {
let mut fds = [primary, other].map(|fd| PollFd {
fd,
events: POLLIN,
revents: 0,
});
let count = Nfds::try_from(fds.len()).unwrap_or(1);
let result = unsafe { poll(fds.as_mut_ptr(), count, timeout_ms) };
result > 0 && fds[0].revents & POLLIN != 0
}
#[must_use]
pub fn monotonic_nanos() -> u64 {
let mut ts = Timespec {
tv_sec: 0,
tv_nsec: 0,
};
if unsafe { clock_gettime(CLOCK_MONOTONIC, &raw mut ts) } != 0 {
return 0;
}
let seconds = u64::try_from(ts.tv_sec).unwrap_or(0);
let nanos = u64::try_from(ts.tv_nsec).unwrap_or(0);
seconds * 1_000_000_000 + nanos
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_wire_structures_match_the_c_layout() {
assert_eq!(
size_of::<GenericEvent>(),
36,
"32 wire bytes + full_sequence"
);
assert_eq!(size_of::<GenericError>(), 36);
assert_eq!(size_of::<Setup>(), 40);
assert_eq!(size_of::<Screen>(), 40);
assert_eq!(size_of::<GetPropertyReply>(), 32);
assert_eq!(size_of::<GetGeometryReply>(), 24);
assert_eq!(size_of::<QueryPointerReply>(), 28);
assert_eq!(size_of::<TranslateCoordinatesReply>(), 16);
assert_eq!(size_of::<GetSelectionOwnerReply>(), 12);
assert_eq!(size_of::<XkbUseExtensionReply>(), 32);
assert_eq!(size_of::<XkbPerClientFlagsReply>(), 32);
assert_eq!(size_of::<ShapeQueryVersionReply>(), 12);
assert_eq!(size_of::<InternAtomReply>(), 12);
assert_eq!(size_of::<GrabPointerReply>(), 8);
assert_eq!(size_of::<QueryExtensionReply>(), 12);
assert_eq!(size_of::<RandrModeInfo>(), 32);
assert_eq!(size_of::<RandrCrtcInfoReply>(), 32);
assert_eq!(size_of::<RandrOutputInfoReply>(), 36);
assert_eq!(
size_of::<XiRawEvent>(),
36,
"a GeGeneric's fixed part, with full_sequence inserted at 32"
);
assert_eq!(size_of::<InputEvent>(), 32);
assert_eq!(size_of::<ConfigureNotifyEvent>(), 28);
assert_eq!(size_of::<PropertyNotifyEvent>(), 20);
assert_eq!(size_of::<SelectionRequestEvent>(), 28);
assert_eq!(
size_of::<SelectionNotifyEvent>(),
32,
"24 on the wire, padded because SendEvent reads 32"
);
assert_eq!(size_of::<ClientMessageEvent>(), 32, "SendEvent wants 32");
assert_eq!(size_of::<FocusEvent>(), 12);
assert_eq!(size_of::<WindowNotifyEvent>(), 16);
assert_eq!(
size_of::<SyntheticWindowNotify>(),
32,
"16 on the wire, padded because SendEvent reads 32"
);
assert_eq!(size_of::<SelectionClearEvent>(), 16);
assert_eq!(size_of::<MappingNotifyEvent>(), 8);
assert_eq!(size_of::<Cookie>(), size_of::<c_uint>());
assert_eq!(align_of::<Cookie>(), align_of::<c_uint>());
}
#[test]
fn the_monotonic_clock_advances() {
let first = monotonic_nanos();
assert!(first > 0, "CLOCK_MONOTONIC is available on Linux");
assert!(monotonic_nanos() >= first);
}
#[test]
fn polling_a_closed_descriptor_does_not_block() {
assert!(!poll_readable(-1, 0));
}
#[test]
fn freeing_a_null_reply_is_a_no_op() {
unsafe { free_reply::<GenericEvent>(ptr::null_mut()) };
}
}