use xcb::{ x::{self, Atom, ChangeProperty, GetProperty, Pixmap, PropMode, Window}, Xid, XidNew, }; use crate::conn_info::Connection; /// EWM Hints set on a window. pub struct Ewm { flags: u32, input: bool, initial_state: i32, icon_pixmap: Pixmap, icon_window: Window, icon_x: i32, icon_y: i32, icon_mask: Pixmap, window_group: u32, } impl Ewm { /// Get the EWM hints for the given window, if they exist and are valid. pub fn get(conn: &Connection<'_>, window: Window) -> Option { // https://github.com/mirror/libX11/blob/ff8706a5eae25b8bafce300527079f68a201d27f/src/GetHints.c#L106 // https://github.com/mirror/libX11/blob/master/src/Xatomtype.h#L111 let hints = conn .wait_for_reply(conn.send_request(&GetProperty { window, delete: false, property: x::ATOM_WM_HINTS, r#type: x::ATOM_WM_HINTS, long_offset: 0, long_length: 9, })) .ok()?; if hints.r#type() != x::ATOM_WM_HINTS || hints.length() < 8 || hints.format() != 32 { return None; } let [flags, input, initial_state, icon_pixmap, icon_window, icon_x, icon_y, icon_mask, window_group] = match hints.value::() { [f, i, is, ip, iw, ix, iy, im, wg] => [f, i, is, ip, iw, ix, iy, im, wg], [f, i, is, ip, iw, ix, iy, im] => [f, i, is, ip, iw, ix, iy, im, &0], _ => unreachable!(), }; unsafe { Some(Self { flags: *flags, input: *input > 0, initial_state: std::mem::transmute::(*initial_state), icon_pixmap: Pixmap::new(*icon_pixmap), icon_window: Window::new(*icon_window), icon_x: std::mem::transmute::(*icon_x), icon_y: std::mem::transmute::(*icon_y), icon_mask: Pixmap::new(*icon_mask), window_group: *window_group, }) } } /// Set these WM hints on the given window. pub fn apply(&self, conn: &Connection<'_>, window: Window) { conn.send_request(&ChangeProperty { mode: PropMode::Replace, window, property: x::ATOM_WM_HINTS, r#type: x::ATOM_WM_HINTS, data: unsafe { &[ self.flags, u32::from(self.input), std::mem::transmute::(self.initial_state), self.icon_pixmap.resource_id(), self.icon_window.resource_id(), std::mem::transmute::(self.icon_x), std::mem::transmute::(self.icon_y), self.icon_mask.resource_id(), self.window_group, ] }, }); } /// If the window is flagged as urgent pub const fn is_urgent(&self) -> bool { (self.flags & 1 << 8) > 0 } /// Set the urgent flag. [`Self::apply`] should be called afterwards. pub fn set_urgent(&mut self, urgent: bool) { self.flags &= u32::from(urgent) << 8; } /// Whether the window has the `input` flag set, unset, or not specified. pub const fn input(&self) -> Option { if (self.flags & 1 << 0) > 0 { Some(self.input) } else { None } } } /// Gets the `WM_TRANSIENT_FOR` hint set on a window pub fn transient_for(conn: &Connection, window: Window) -> Option { let hints = conn .wait_for_reply(conn.send_request(&GetProperty { window, delete: false, property: x::ATOM_WM_TRANSIENT_FOR, r#type: x::ATOM_WINDOW, long_offset: 0, long_length: 1, })) .ok()?; if hints.r#type() != x::ATOM_WINDOW || hints.length() < 1 || hints.format() != 32 { return None; } Some(hints.value::()[0]) } /// Check if the given window is flagged as a dialog with the `NET_WM_WINDOW_TYPE` property pub fn is_dialog(conn: &Connection, window: Window) -> bool { let Ok(hints) = conn.wait_for_reply(conn.send_request(&GetProperty { window, delete: false, property: conn.atoms.net_wm_window_type, r#type: x::ATOM_ATOM, long_offset: 0, long_length: 1, })) else { return false; }; hints.r#type() == x::ATOM_ATOM && hints.length() == 1 && hints.format() == 32 && hints.value::()[0] == conn.atoms.net_wm_window_type_dialog } /// Check if the given window wants to be fullscreen, using the `NET_WM_STATE` property pub fn is_fullscreen(conn: &Connection, window: Window) -> bool { let Ok(hints) = conn.wait_for_reply(conn.send_request(&GetProperty { window, delete: false, property: conn.atoms.net_wm_state, r#type: x::ATOM_ATOM, long_offset: 0, long_length: 9999, })) else { return false; }; hints.r#type() == x::ATOM_ATOM && hints.format() == 32 && hints .value::() .iter() .any(|a| *a == conn.atoms.net_wm_fullscreen) }