diff options
Diffstat (limited to 'src/clients/hints.rs')
-rw-r--r-- | src/clients/hints.rs | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/src/clients/hints.rs b/src/clients/hints.rs new file mode 100644 index 0000000..0250401 --- /dev/null +++ b/src/clients/hints.rs @@ -0,0 +1,104 @@ +use xcb::{ + x::{self, ChangeProperty, GetProperty, Pixmap, PropMode, Window}, + Xid, XidNew, +}; + +use crate::conn_info::Connection; + +pub struct Xwm { + 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 Xwm { + /// Get the EWM hints for the given window, if they exist and are valid. + pub fn get(conn: &Connection<'_>, window: Window) -> Option<Self> { + // 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::<u32>() { + [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::<u32, i32>(*initial_state), + icon_pixmap: Pixmap::new(*icon_pixmap), + icon_window: Window::new(*icon_window), + icon_x: std::mem::transmute::<u32, i32>(*icon_x), + icon_y: std::mem::transmute::<u32, i32>(*icon_y), + icon_mask: Pixmap::new(*icon_mask), + window_group: *window_group, + }) + } + } + + /// Set these WM hints on the given window. + /// This function does not check for success, so `conn.flush()` should be called after. + 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::<i32, u32>(self.initial_state), + self.icon_pixmap.resource_id(), + self.icon_window.resource_id(), + std::mem::transmute::<i32, u32>(self.icon_x), + std::mem::transmute::<i32, u32>(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<bool> { + if (self.flags & 1 << 0) > 0 { + Some(self.input) + } else { + None + } + } +} |