summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-06-15 22:11:59 +0100
committertcmal <me@aria.rip>2024-06-15 22:11:59 +0100
commitc3e98e34ed7d42ef4271339de88f7131e7647442 (patch)
tree2065275616a3ad4d727369c87890dee10553e44b /src
parent9db364a195cb5c0e8fb82e52e607d373ddb0e13c (diff)
implement wm hints
Diffstat (limited to 'src')
-rw-r--r--src/clients.rs108
-rw-r--r--src/main.rs19
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(()),
+ }
}
}