diff options
-rw-r--r-- | src/clients.rs | 108 | ||||
-rw-r--r-- | src/main.rs | 19 |
2 files changed, 121 insertions, 6 deletions
diff --git a/src/clients.rs b/src/clients.rs index 2892c9d..e9efeab 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -5,11 +5,11 @@ use xcb::{ x::{ self, ChangeProperty, ChangeWindowAttributes, ConfigWindow, ConfigureNotifyEvent, ConfigureRequestEvent, ConfigureWindow, Cw, DestroyNotifyEvent, Drawable, EventMask, - GetGeometry, GetWindowAttributes, MapRequestEvent, MapWindow, SendEvent, SendEventDest, - UnmapNotifyEvent, UnmapWindow, Window, + GetGeometry, GetProperty, GetWindowAttributes, MapRequestEvent, MapWindow, Pixmap, + SendEvent, SendEventDest, UnmapNotifyEvent, UnmapWindow, Window, }, xinerama::{self, ScreenInfo}, - BaseEvent, Connection, Extension, Xid, + BaseEvent, Connection, Extension, Xid, XidNew, }; impl WM<'_> { @@ -180,6 +180,8 @@ impl WM<'_> { height: 0, border_width: BORDER_WIDTH, mapped: false, + urgent: false, + never_focus: false, }, ); @@ -201,6 +203,7 @@ impl WM<'_> { // TODO: updatewindowtype // TODO: updatesizehints // TODO: updatewmhints + c.sync_properties(conn, true); // XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); c.set_event_mask( @@ -317,6 +320,11 @@ impl ClientState { None } + /// Get the position of the currently focused client. This position may be invalid. + pub fn focused(&self) -> (usize, usize) { + self.focused + } + /// Get a mutable reference to the currently focused client, if it exists. pub fn focused_mut(&mut self) -> Option<&mut Client> { self.client_mut(self.focused.0, self.focused.1) @@ -392,6 +400,9 @@ pub struct Client { height: u16, border_width: u16, mapped: bool, + + urgent: bool, + never_focus: bool, } impl Client { @@ -495,6 +506,97 @@ impl Client { value_list: &[Cw::EventMask(event_mask)], }); } + + /// Sync the non-geometry related properties with EWMH hints + /// This function does not check for success, so conn.flush() should be called after. + pub fn sync_properties(&mut self, conn: &Connection, focused: bool) { + let Some(mut hints) = XWMHints::get(conn, self.window) else { + return; + }; + + if focused && hints.is_urgent() { + hints.set_urgent(false); + hints.set(conn, self.window); + } else { + self.urgent = hints.is_urgent(); + } + + self.never_focus = if hints.input_valid() { + !hints.input + } else { + false + }; + } +} + +struct XWMHints { + 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 XWMHints { + 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: 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, + }) + } + } + + pub fn set(&self, conn: &Connection, window: Window) { + todo!() + } + + pub fn is_urgent(&self) -> bool { + (self.flags & (1 << 8)) > 0 + } + + pub fn set_urgent(&mut self, urgent: bool) { + self.flags &= (1 << 8) + } + + fn input_valid(&self) -> bool { + (self.flags & (1 << 0)) > 0 + } } impl Default for MonitorInfo { diff --git a/src/main.rs b/src/main.rs index 25b4bb9..374b403 100644 --- a/src/main.rs +++ b/src/main.rs @@ -221,9 +221,22 @@ impl WM<'_> { } /// Handle a property notify event, by doing *todo* - fn handle_property_notify(&self, _e: PropertyNotifyEvent) -> Result<()> { - println!("TODO: handle_property_notify"); - Ok(()) + fn handle_property_notify(&mut self, e: PropertyNotifyEvent) -> Result<()> { + match e.atom() { + x::ATOM_WM_HINTS => { + let Some(p) = self.clients.find_client_pos(e.window()) else { + return Ok(()); + }; + let focused = p == self.clients.focused(); + self.clients.client_mut(p.0, p.1).and_then(|c| { + c.sync_properties(self.conn, focused); + Some(()) + }); + + Ok(()) + } + _ => Ok(()), + } } } |