use super::raw;
use crate::{Extent, Position, XDisplay, XError, lets};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub(crate) struct XWindowState {
pub(crate) x: i16,
pub(crate) y: i16,
pub(crate) width: u16,
pub(crate) height: u16,
pub(crate) needs_redraw: bool,
}
#[doc = crate::_tags!(unix uid guard)]
#[doc = crate::_doc_location!("sys/device/display/x11")]
#[derive(Debug)]
pub struct XWindow {
pub(super) display: *mut raw::xcb_connection_t,
pub(super) win: u32,
pub(super) gc: u32,
}
#[rustfmt::skip]
impl XWindow {
pub fn new(display: &mut XDisplay, x: i16, y: i16, width: u16, height: u16, border_width: u16)
-> Result<Self, XError> {
let conn = display.conn;
let win: u32 = unsafe { raw::xcb_generate_id(conn) }; let screen = unsafe { &*display.screen };
let values: [u32; 2] = [screen.black_pixel,
raw::XCB_EVENT_MASK_KEY_PRESS
| raw::XCB_EVENT_MASK_KEY_RELEASE
| raw::XCB_EVENT_MASK_BUTTON_PRESS
| raw::XCB_EVENT_MASK_BUTTON_RELEASE
| raw::XCB_EVENT_MASK_POINTER_MOTION
| raw::XCB_EVENT_MASK_BUTTON_MOTION
| raw::XCB_EVENT_MASK_ENTER_WINDOW
| raw::XCB_EVENT_MASK_LEAVE_WINDOW
| raw::XCB_EVENT_MASK_FOCUS_CHANGE
| raw::XCB_EVENT_MASK_EXPOSURE
| raw::XCB_EVENT_MASK_STRUCTURE_NOTIFY
| raw::XCB_EVENT_MASK_VISIBILITY_CHANGE
| raw::XCB_EVENT_MASK_PROPERTY_CHANGE
];
let mask: u32 = raw::XCB_CW_BACK_PIXEL | raw::XCB_CW_EVENT_MASK;
unsafe {
raw::xcb_create_window(conn, screen.root_depth, win, screen.root, x, y, width, height,
border_width, raw::XCB_WINDOW_CLASS_INPUT_OUTPUT, screen.root_visual, mask,
values.as_ptr());
}
let hints = raw::XSizeHints::new().set_position(x, y);
hints.set_on(conn, win, display.atoms.wm_normal_hints);
let gc: u32 = unsafe { raw::xcb_generate_id(conn) };
let gc_values: [u32; 1] = [screen.black_pixel];
unsafe { raw::xcb_create_gc(conn, gc, win, raw::XCB_GC_FOREGROUND, gc_values.as_ptr()); }
unsafe { raw::xcb_map_window(conn, win); }
display.atoms.set_property_atom(conn, win,
display.atoms.wm_protocols, display.atoms.wm_delete_window);
let state = XWindowState { x, y, width, height, needs_redraw: false };
display.window_register(win, state);
let window = Self { display: conn, win, gc };
display.flush();
Ok(window)
}
pub fn destroy(&self, display: &mut XDisplay) -> bool {
display.window_destroy(self.id(), self.gc)
}
pub const fn id(&self) -> u32 { self.win }
pub fn extent(&self, display: &XDisplay) -> Extent<u16, 2> {
display.window_extent(self.id()).expect("current window ID is valid")
}
pub fn position(&self, display: &XDisplay) -> Position<i16, 2> {
display.window_position(self.id()).expect("current window ID is valid")
}
pub fn needs_redraw(&self, display: &XDisplay) -> bool {
display.window_needs_redraw(self.id())
}
pub fn clear_redraw(&self, display: &mut XDisplay) {
display.window_needs_redraw(self.id());
}
pub fn put_image_bytes(&self, width: u16, height: u16, depth: u8, data: &[u8]) {
lets![dst_x=0, dst_y=0, left_pad=0];
unsafe { raw::xcb_put_image(self.display, raw::XCB_IMAGE_FORMAT_Z_PIXMAP, self.win, self.gc,
width, height, dst_x, dst_y, left_pad, depth, data.len() as u32, data.as_ptr()); }
}
pub fn put_image_u32(&self, width: u16, height: u16, depth: u8, data: &[u32]) {
lets![dst_x=0, dst_y=0, left_pad=0];
unsafe {
let bytes = core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4);
raw::xcb_put_image(self.display, raw::XCB_IMAGE_FORMAT_Z_PIXMAP, self.win, self.gc,
width, height, dst_x, dst_y, left_pad, depth, data.len() as u32 * 4, bytes.as_ptr());
}
}
pub fn fill_rect(&self, pos: Position<i16, 2>, ext: Extent<u16, 2>) {
let rect = raw::xcb_rectangle_t::new(pos, ext);
unsafe { raw::xcb_poly_fill_rectangle(self.display, self.win, self.gc, 1, &rect as *const _); }
}
}