#![allow(unused)]
#[cfg(ffi_xcb_shm··)]
use super::XShmCaps;
use super::{_raw, KeyRepeatFilter, XAtoms, XError, XEvent, XImageFormat, XWindowState, XkbState};
use crate::{ConstInit, Extent, Libc, Position, Ptr, Vec, c_int, is, lets, vec_ as vec};
use crate::{
Event, EventButton, EventButtonState, EventButtons, EventKind, EventMouse, EventQueue,
EventWheel, EventWheelUnit, EventWindow, KeyMods,
};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub(crate) struct XWindowConfigureDelta {
pub(crate) moved: bool,
pub(crate) resized: bool,
}
#[doc = crate::_tags!(unix runtime guard)]
#[doc = crate::_doc_meta!{
location("sys/device/display/x11"),
#[cfg(target_pointer_width = "64")]
test_size_of(XDisplay = 240|1920; niche Option),
}]
#[derive(Debug)]
pub struct XDisplay {
pub(super) conn: *mut _raw::xcb_connection_t,
pub(super) screen: *const _raw::xcb_screen_t,
screen_num: c_int,
pub(super) depth: u8,
pub(super) image_format: XImageFormat,
#[cfg(ffi_xcb_shm··)]
pub(super) shm_caps: Option<XShmCaps>,
xkb: XkbState,
pub(super) atoms: XAtoms,
pub(super) pending: Option<*mut _raw::xcb_generic_event_t>,
pub(super) repeat_filter: KeyRepeatFilter,
pub(super) windows: Vec<(u32, XWindowState)>,
queue: EventQueue<2>,
}
#[rustfmt::skip]
impl XDisplay {
pub fn open() -> Result<Self, XError> {
let mut screen_num = 0;
let conn = unsafe { _raw::xcb_connect(Ptr::null(), &mut screen_num as *mut c_int) };
is! { conn.is_null(), return Err(XError::ConnectionFailed) }
if unsafe { _raw::xcb_connection_has_error(conn) } != 0 {
unsafe { _raw::xcb_disconnect(conn) };
return Err(XError::ConnectionFailed);
}
let setup = unsafe { _raw::xcb_get_setup(conn) };
if setup.is_null() {
unsafe { _raw::xcb_disconnect(conn) };
return Err(XError::SetupFailed);
}
let iter = unsafe { _raw::xcb_setup_roots_iterator(setup) };
if iter.data.is_null() {
unsafe { _raw::xcb_disconnect(conn) };
return Err(XError::NoScreensFound);
}
let screen: *const _raw::xcb_screen_t = iter.data;
let depth = unsafe { (*screen).root_depth };
let image_format = Self::query_image_format(setup, depth)?;
#[cfg(ffi_xcb_shm··)]
let shm_caps = Self::query_shm_caps(conn);
lets![mut major = 0, mut minor = 0, mut ev = 0, mut err = 0];
let ok = unsafe {
_raw::xkb_x11_setup_xkb_extension(
conn, 1, 0, 0, &mut major, &mut minor, &mut ev, &mut err,
)
};
if ok <= 0 {
unsafe { _raw::xcb_disconnect(conn) };
return Err(XError::ExtensionUnavailable("xkb-setup"));
}
let xkb = XkbState::new(conn)?;
let atoms = XAtoms::new(conn);
let pending = None;
let repeat_filter = KeyRepeatFilter::new();
let windows = vec![];
let queue = EventQueue::new();
Ok(Self { conn, screen, screen_num, depth, image_format,
#[cfg(ffi_xcb_shm··)]
shm_caps,
xkb, pending, repeat_filter, atoms, windows, queue, })
}
pub fn depth(&self) -> u8 {
self.depth
}
pub const fn bits_per_pixel(&self) -> u8 { self.image_format.bits_per_pixel }
pub const fn scanline_pad_bits(&self) -> u8 { self.image_format.scanline_pad_bits }
pub const fn bytes_per_line(&self, width: u16) -> u32 {
self.image_format.bytes_per_line(width)
}
#[cfg(ffi_xcb_shm··)]
pub const fn has_shm(&self) -> bool { self.shm_caps.is_some() }
#[inline(always)]
pub fn poll_event(&mut self) -> Event {
is![let Some(ev) = self.queue.pop(), return ev]; is![let Some(raw) = self.poll_raw_event(), return self.handle_raw_event(raw)];
Event::None
}
#[inline(always)]
pub fn wait_event(&mut self) -> Event {
is![let Some(ev) = self.queue.pop(), return ev]; is![let Some(raw) = self.wait_raw_event(), self.handle_raw_event(raw), Event::None]
}
#[inline(always)]
pub fn flush(&self) { unsafe { _raw::xcb_flush(self.conn); } }
}
impl XDisplay {
pub(crate) fn conn(&self) -> *mut _raw::xcb_connection_t {
self.conn
}
pub(crate) fn screen(&self) -> &_raw::xcb_screen_t {
unsafe { &*self.screen }
}
fn has_window(&self, window_id: u32) -> bool {
self.windows.iter().any(|&(id, _)| id == window_id)
}
fn window_state(&self, window_id: u32) -> Option<&XWindowState> {
self.windows.iter().find(|&&(id, _)| id == window_id).map(|(_, state)| state)
}
fn window_state_mut(&mut self, window_id: u32) -> Option<&mut XWindowState> {
self.windows.iter_mut().find(|(id, _)| *id == window_id).map(|(_, state)| state)
}
pub(crate) fn window_register(&mut self, window_id: u32, state: XWindowState) {
debug_assert!(!self.has_window(window_id), "window already registered: {window_id}");
self.windows.push((window_id, state));
}
fn window_unregister(&mut self, window_id: u32) -> Option<XWindowState> {
let index = self.windows.iter().position(|&(id, _)| id == window_id)?;
Some(self.windows.swap_remove(index).1)
}
pub(crate) fn window_destroy(&mut self, window_id: u32, gc: u32) -> bool {
let Some(_) = self.window_unregister(window_id) else {
return false;
};
unsafe {
_raw::xcb_free_gc(self.conn, gc);
_raw::xcb_destroy_window(self.conn, window_id);
}
self.flush();
true
}
pub(crate) fn window_extent(&self, window_id: u32) -> Option<Extent<u16, 2>> {
let state = self.window_state(window_id)?;
Some(Extent::new([state.width, state.height]))
}
pub(crate) fn window_position(&self, window_id: u32) -> Option<Position<i16, 2>> {
let state = self.window_state(window_id)?;
Some(Position::new([state.x, state.y]))
}
pub(crate) fn window_needs_redraw(&self, window_id: u32) -> bool {
if let Some(state) = self.window_state(window_id) {
state.needs_redraw
} else {
false
}
}
fn window_mark_redraw(&mut self, window_id: u32) -> bool {
if let Some(state) = self.window_state_mut(window_id) {
state.needs_redraw = true;
true
} else {
false
}
}
pub(crate) fn window_clear_redraw(&mut self, window_id: u32) -> bool {
if let Some(state) = self.window_state_mut(window_id) {
state.needs_redraw = false;
true
} else {
false
}
}
fn window_update_configure(
&mut self,
window_id: u32,
x: i16,
y: i16,
width: u16,
height: u16,
) -> Option<XWindowConfigureDelta> {
let state = self.window_state_mut(window_id)?;
let moved = state.x != x || state.y != y;
let resized = state.width != width || state.height != height;
state.x = x;
state.y = y;
state.width = width;
state.height = height;
is! { resized, state.needs_redraw = true }
Some(XWindowConfigureDelta { moved, resized })
}
fn query_image_format(
setup: *const _raw::xcb_setup_t,
depth: u8,
) -> Result<XImageFormat, XError> {
let mut it = unsafe { _raw::xcb_setup_pixmap_formats_iterator(setup) };
while it.rem > 0 && !it.data.is_null() {
let fmt = unsafe { &*it.data };
if fmt.depth == depth {
return Ok(XImageFormat::new(fmt.depth, fmt.bits_per_pixel, fmt.scanline_pad));
}
unsafe { _raw::xcb_format_next(&mut it) };
}
Err(XError::Other("no pixmap format for root depth"))
}
#[cfg(ffi_xcb_shm··)]
fn query_shm_caps(conn: *mut _raw::xcb_connection_t) -> Option<XShmCaps> {
unsafe {
let cookie = _raw::xcb_shm_query_version(conn);
let mut err = core::ptr::null_mut();
let reply = _raw::xcb_shm_query_version_reply(conn, cookie, &mut err);
if !err.is_null() || reply.is_null() {
return None;
}
let caps = XShmCaps {
major_version: (*reply).major_version,
minor_version: (*reply).minor_version,
shared_pixmaps: (*reply).shared_pixmaps != 0,
pixmap_format: (*reply).pixmap_format,
};
Libc::free(reply.cast());
Some(caps)
}
}
#[rustfmt::skip]
fn handle_raw_event(&mut self, xev: XEvent) -> Event {
use {EventKind as Kind, EventWindow as Win};
if let Some(ev) = xev.as_raw_key() {
let keycode = unsafe { ev.detail };
let time_ms = unsafe { ev.time };
if xev.is_key_release() {
let real = self.classify_release(keycode, time_ms);
is! { !real, return Event::default() } }
let key = xev.to_event_key(&self.xkb, &mut self.repeat_filter);
return Event::from_window(ev.event, Kind::Key(key), xev.timestamp());
} else if let Some(ev) = xev.as_raw_button() {
let x = ev.event_x.into();
let y = ev.event_y.into();
let timestamp = xev.timestamp();
let buttons = XEvent::map_button_mask(ev.state);
let mods = self.xkb.key_mods(ev.state);
let unit = EventWheelUnit::Step;
match ev.detail {
4 => { return Event::from_window(ev.event, Kind::Wheel(
EventWheel::new(0, -1, unit, x, y, buttons, mods)), timestamp); }
5 => { return Event::from_window(ev.event, Kind::Wheel(
EventWheel::new(0, 1, unit, x, y, buttons, mods)), timestamp); }
6 => { return Event::from_window(ev.event, Kind::Wheel(
EventWheel::new(-1, 0, unit, x, y, buttons, mods)), timestamp); }
7 => { return Event::from_window(ev.event, Kind::Wheel(
EventWheel::new(1, 0, unit, x, y, buttons, mods)), timestamp); }
_ => {
let button = XEvent::map_button(ev.detail);
let state = xev.map_button_state();
return Event::from_window(ev.event, Kind::Mouse(
EventMouse { x, y, button: Some(button), state, buttons, mods }),
timestamp,
);
}
}
} else if let Some(ev) = xev.as_raw_motion() {
let buttons = XEvent::map_button_mask(ev.state);
let button = EventButton::from_one_bit_mask(buttons);
let mods = self.xkb.key_mods(ev.state);
return Event::from_window(
ev.event,
Kind::Mouse(EventMouse {
x: ev.event_x.into(),
y: ev.event_y.into(),
button,
state: EventButtonState::Moved,
buttons,
mods,
}),
xev.timestamp(),
);
} else if let Some(cm) = xev.as_raw_client_message() {
let proto = unsafe { cm.data.data32[0] };
if proto == self.atoms.wm_delete_window {
return Event::from_window(
cm.window,
Kind::Window(Win::CloseRequested),
xev.timestamp(),
);
}
} else if let Some(ev) = xev.as_raw_enter() {
return Event::from_window(ev.event, Kind::Window(Win::Entered), xev.timestamp());
} else if let Some(ev) = xev.as_raw_leave() {
return Event::from_window(ev.event, Kind::Window(Win::Left), xev.timestamp());
} else if let Some(ev) = xev.as_raw_expose() {
self.window_mark_redraw(ev.window);
return Event::from_window(
ev.window,
Kind::Window(Win::RedrawRequested),
xev.timestamp(),
);
} else if let Some(ev) = xev.as_raw_configure() {
let window_id = ev.window;
let (x, y, w, h) = (ev.x, ev.y, ev.width, ev.height);
let ts = xev.timestamp();
if let Some(delta) = self.window_update_configure(window_id, x, y, w, h) {
if delta.moved {
let position = Position::<i32, 2>::new([x as i32, y as i32]);
self.queue.push(Event::from_window(
window_id,
EventKind::Window(EventWindow::Moved(Some(position))),
ts,
));
}
if delta.resized {
let extent = Extent::<u32, 2>::new([w as u32, h as u32]);
self.queue.push(Event::from_window(
window_id,
EventKind::Window(EventWindow::Resized(Some(extent))),
ts,
));
}
}
return Event::None;
} else {
}
Event::None
}
fn next_raw_event(&mut self) -> Option<*mut _raw::xcb_generic_event_t> {
is! { let Some(ev) = self.pending.take(), return Some(ev) }
let ev = unsafe { _raw::xcb_poll_for_event(self.conn) };
if ev.is_null() { None } else { Some(ev) }
}
fn peek_raw_event(&mut self) -> Option<*mut _raw::xcb_generic_event_t> {
if self.pending.is_none() {
let ev = unsafe { _raw::xcb_poll_for_event(self.conn) };
is! { !ev.is_null(), self.pending = Some(ev) }
}
self.pending
}
fn poll_raw_event(&mut self) -> Option<XEvent> {
let ev = self.next_raw_event()?;
Some(XEvent { raw: ev })
}
fn wait_raw_event(&mut self) -> Option<XEvent> {
is! { let Some(ev) = self.pending.take(), return Some(XEvent { raw: ev }) }
let ev = unsafe { _raw::xcb_wait_for_event(self.conn) };
if ev.is_null() { None } else { Some(XEvent { raw: ev }) }
}
fn classify_release(&mut self, keycode: u8, timestamp: u32) -> bool {
if let Some(next) = self.peek_raw_event() {
unsafe {
let ty = (*next).response_type & 0x7F;
if ty == _raw::xcb_event_code::XCB_KEY_PRESS as u8 {
let kpev = next as *const _raw::xcb_key_press_event_t;
is! { (*kpev).detail == keycode && (*kpev).time == timestamp, return false }
}
}
}
true
}
}
impl Drop for XDisplay {
fn drop(&mut self) {
unsafe { _raw::xcb_disconnect(self.conn) }
}
}