oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git

updated atom caching to eliminate 6 round trips to X server per operation, lowered cpu usage by updating polling frequency to 100ms for updating status blocks, changed focus updates to only update necessary windows/monitors by implementing dwms approach to go from O(n) to O(1) runtime.

Commit
3f51577d231357ca8029b1342091432df0c578cf
Parent
e4b5586
Author
tonybtw <tonybtw@tonybtw.com>
Date
2025-10-19 04:44:55

Diff

diff --git a/src/window_manager.rs b/src/window_manager.rs
index 9291a16..44fa5de 100644
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@ -19,6 +19,37 @@ pub fn tag_mask(tag: usize) -> TagMask {
     1 << tag
 }
 
+struct AtomCache {
+    net_current_desktop: Atom,
+    net_client_info: Atom,
+    wm_state: Atom,
+}
+
+impl AtomCache {
+    fn new(connection: &RustConnection) -> WmResult<Self> {
+        let net_current_desktop = connection
+            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
+            .reply()?
+            .atom;
+
+        let net_client_info = connection
+            .intern_atom(false, b"_NET_CLIENT_INFO")?
+            .reply()?
+            .atom;
+
+        let wm_state = connection
+            .intern_atom(false, b"WM_STATE")?
+            .reply()?
+            .atom;
+
+        Ok(Self {
+            net_current_desktop,
+            net_client_info,
+            wm_state,
+        })
+    }
+}
+
 pub struct WindowManager {
     config: Config,
     connection: RustConnection,
@@ -35,6 +66,8 @@ pub struct WindowManager {
     bars: Vec<Bar>,
     monitors: Vec<Monitor>,
     selected_monitor: usize,
+    atoms: AtomCache,
+    previous_focused: Option<Window>,
 }
 
 type WmResult<T> = Result<T, WmError>;
@@ -116,6 +149,8 @@ impl WindowManager {
 
         let gaps_enabled = config.gaps_enabled;
 
+        let atoms = AtomCache::new(&connection)?;
+
         let mut window_manager = Self {
             config,
             connection,
@@ -132,6 +167,8 @@ impl WindowManager {
             bars,
             monitors,
             selected_monitor: 0,
+            atoms,
+            previous_focused: None,
         };
 
         window_manager.scan_existing_windows()?;
@@ -176,17 +213,8 @@ impl WindowManager {
 
     fn scan_existing_windows(&mut self) -> WmResult<()> {
         let tree = self.connection.query_tree(self.root)?.reply()?;
-        let net_client_info = self
-            .connection
-            .intern_atom(false, b"_NET_CLIENT_INFO")?
-            .reply()?
-            .atom;
-
-        let wm_state_atom = self
-            .connection
-            .intern_atom(false, b"WM_STATE")?
-            .reply()?
-            .atom;
+        let net_client_info = self.atoms.net_client_info;
+        let wm_state_atom = self.atoms.wm_state;
 
         for &window in &tree.children {
             if self.bars.iter().any(|bar| bar.window() == window) {
@@ -271,11 +299,7 @@ impl WindowManager {
     }
 
     fn save_client_tag(&self, window: Window, tag: TagMask) -> WmResult<()> {
-        let net_client_info = self
-            .connection
-            .intern_atom(false, b"_NET_CLIENT_INFO")?
-            .reply()?
-            .atom;
+        let net_client_info = self.atoms.net_client_info;
 
         let bytes = tag.to_ne_bytes().to_vec();
 
@@ -294,11 +318,7 @@ impl WindowManager {
     }
 
     fn set_wm_state(&self, window: Window, state: u32) -> WmResult<()> {
-        let wm_state_atom = self
-            .connection
-            .intern_atom(false, b"WM_STATE")?
-            .reply()?
-            .atom;
+        let wm_state_atom = self.atoms.wm_state;
 
         let data = [state, 0u32];
         let bytes: Vec<u8> = data.iter().flat_map(|&v| v.to_ne_bytes()).collect();
@@ -324,21 +344,21 @@ impl WindowManager {
         self.update_bar()?;
 
         loop {
-            if let Some(bar) = self.bars.get_mut(self.selected_monitor) {
-                bar.update_blocks();
-            }
-
-            if let Ok(Some(event)) = self.connection.poll_for_event() {
+            while let Some(event) = self.connection.poll_for_event()? {
                 if let Some(should_restart) = self.handle_event(event)? {
                     return Ok(should_restart);
                 }
             }
 
+            if let Some(bar) = self.bars.get_mut(self.selected_monitor) {
+                bar.update_blocks();
+            }
+
             if self.bars.iter().any(|bar| bar.needs_redraw()) {
                 self.update_bar()?;
             }
 
-            std::thread::sleep(std::time::Duration::from_millis(10));
+            std::thread::sleep(std::time::Duration::from_millis(100));
         }
     }
 
@@ -370,7 +390,6 @@ impl WindowManager {
                 }
 
                 self.apply_layout()?;
-                self.update_focus_visuals()?;
             } else {
                 self.fullscreen_window = Some(focused);
 
@@ -596,11 +615,7 @@ impl WindowManager {
     }
 
     fn save_selected_tags(&self) -> WmResult<()> {
-        let net_current_desktop = self
-            .connection
-            .intern_atom(false, b"_NET_CURRENT_DESKTOP")?
-            .reply()?
-            .atom;
+        let net_current_desktop = self.atoms.net_current_desktop;
 
         let selected_tags = self.monitors.get(self.selected_monitor).map(|m| m.selected_tags).unwrap_or(tag_mask(0));
         let desktop = selected_tags.trailing_zeros();
@@ -668,6 +683,8 @@ impl WindowManager {
     }
 
     pub fn set_focus(&mut self, window: Window) -> WmResult<()> {
+        let old_focused = self.previous_focused;
+
         if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
             monitor.focused_window = Some(window);
         }
@@ -676,30 +693,36 @@ impl WindowManager {
             .set_input_focus(InputFocus::POINTER_ROOT, window, x11rb::CURRENT_TIME)?;
         self.connection.flush()?;
 
-        self.update_focus_visuals()?;
+        self.update_focus_visuals(old_focused, window)?;
+        self.previous_focused = Some(window);
         Ok(())
     }
 
-    fn update_focus_visuals(&self) -> WmResult<()> {
-        let focused = self.monitors.get(self.selected_monitor).and_then(|m| m.focused_window);
+    fn update_focus_visuals(&self, old_focused: Option<Window>, new_focused: Window) -> WmResult<()> {
+        if let Some(old_win) = old_focused {
+            if old_win != new_focused {
+                self.connection.configure_window(
+                    old_win,
+                    &ConfigureWindowAux::new().border_width(self.config.border_width),
+                )?;
 
-        for &window in &self.windows {
-            let (border_color, border_width) = if focused == Some(window) {
-                (self.config.border_focused, self.config.border_width)
-            } else {
-                (self.config.border_unfocused, self.config.border_width)
-            };
+                self.connection.change_window_attributes(
+                    old_win,
+                    &ChangeWindowAttributesAux::new().border_pixel(self.config.border_unfocused),
+                )?;
+            }
+        }
 
-            self.connection.configure_window(
-                window,
-                &ConfigureWindowAux::new().border_width(border_width),
-            )?;
+        self.connection.configure_window(
+            new_focused,
+            &ConfigureWindowAux::new().border_width(self.config.border_width),
+        )?;
+
+        self.connection.change_window_attributes(
+            new_focused,
+            &ChangeWindowAttributesAux::new().border_pixel(self.config.border_focused),
+        )?;
 
-            self.connection.change_window_attributes(
-                window,
-                &ChangeWindowAttributesAux::new().border_pixel(border_color),
-            )?;
-        }
         self.connection.flush()?;
         Ok(())
     }