oxwm

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

Added send monitor function and handled mouse cursor events like dwm does, to drag/resize events causing bugs on multi monitor setups.

Commit
4ca96ad9c80c9faa659f29b782c00c7e829be436
Parent
e932c2c
Author
tonybtw <tonybtw@tonybtw.com>
Date
2025-12-01 23:17:03

Diff

diff --git a/src/window_manager.rs b/src/window_manager.rs
index abf318d..9813496 100644
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@ -930,6 +930,37 @@ impl WindowManager {
         best_monitor
     }
 
+    fn sendmon(&mut self, window: Window, target_monitor_index: usize) -> WmResult<()> {
+        let current_monitor_index = self.clients
+            .get(&window)
+            .map(|c| c.monitor_index);
+
+        if let Some(current_idx) = current_monitor_index {
+            if current_idx == target_monitor_index {
+                return Ok(());
+            }
+        }
+
+        self.unfocus(window)?;
+        self.detach(window);
+        self.detach_stack(window);
+
+        if let Some(client) = self.clients.get_mut(&window) {
+            client.monitor_index = target_monitor_index;
+            if let Some(target_monitor) = self.monitors.get(target_monitor_index) {
+                client.tags = target_monitor.tagset[target_monitor.selected_tags_index];
+            }
+        }
+
+        self.attach_aside(window, target_monitor_index);
+        self.attach_stack(window, target_monitor_index);
+
+        self.focus(None)?;
+        self.apply_layout()?;
+
+        Ok(())
+    }
+
     fn dir_to_monitor(&self, direction: i32) -> Option<usize> {
         if self.monitors.len() <= 1 {
             return None;
@@ -2323,62 +2354,146 @@ impl WindowManager {
     }
 
     fn move_mouse(&mut self, window: Window) -> WmResult<()> {
-        self.floating_windows.insert(window);
+        let is_fullscreen = self.clients
+            .get(&window)
+            .map(|c| c.is_fullscreen)
+            .unwrap_or(false);
 
-        let geometry = self.connection.get_geometry(window)?.reply()?;
+        if is_fullscreen {
+            return Ok(());
+        }
 
-        self.connection
-            .grab_pointer(
-                false,
-                self.root,
-                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
-                GrabMode::ASYNC,
-                GrabMode::ASYNC,
-                x11rb::NONE,
-                x11rb::NONE,
-                x11rb::CURRENT_TIME,
-            )?
-            .reply()?;
+        let client_info = self.clients.get(&window).map(|c| {
+            (c.x_position, c.y_position, c.width, c.height, c.is_floating, c.monitor_index)
+        });
+
+        let Some((orig_x, orig_y, width, height, was_floating, monitor_idx)) = client_info else {
+            return Ok(());
+        };
+
+        let monitor = self.monitors.get(monitor_idx).cloned();
+        let Some(monitor) = monitor else {
+            return Ok(());
+        };
+
+        let snap = 32;
+        let is_normie = self.layout.name() == "normie";
+
+        self.connection.grab_pointer(
+            false,
+            self.root,
+            (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE |
+             EventMask::BUTTON_PRESS).into(),
+            GrabMode::ASYNC,
+            GrabMode::ASYNC,
+            x11rb::NONE,
+            x11rb::NONE,
+            x11rb::CURRENT_TIME,
+        )?.reply()?;
 
         let pointer = self.connection.query_pointer(self.root)?.reply()?;
-        let (start_x, start_y) = (pointer.root_x, pointer.root_y);
+        let (start_x, start_y) = (pointer.root_x as i32, pointer.root_y as i32);
 
-        self.connection.configure_window(
-            window,
-            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
-        )?;
+        let mut last_time = 0u32;
 
         loop {
             let event = self.connection.wait_for_event()?;
             match event {
+                Event::ConfigureRequest(_) | Event::MapRequest(_) | Event::Expose(_) => {}
                 Event::MotionNotify(e) => {
-                    let new_x = geometry.x + (e.root_x - start_x);
-                    let new_y = geometry.y + (e.root_y - start_y);
-                    self.connection.configure_window(
-                        window,
-                        &ConfigureWindowAux::new().x(new_x as i32).y(new_y as i32),
-                    )?;
-                    self.connection.flush()?;
+                    if e.time.wrapping_sub(last_time) <= 16 {
+                        continue;
+                    }
+                    last_time = e.time;
+
+                    let mut new_x = orig_x as i32 + (e.root_x as i32 - start_x);
+                    let mut new_y = orig_y as i32 + (e.root_y as i32 - start_y);
+
+                    if (monitor.window_area_x - new_x).abs() < snap {
+                        new_x = monitor.window_area_x;
+                    } else if ((monitor.window_area_x + monitor.window_area_width) - (new_x + width as i32)).abs() < snap {
+                        new_x = monitor.window_area_x + monitor.window_area_width - width as i32;
+                    }
+
+                    if (monitor.window_area_y - new_y).abs() < snap {
+                        new_y = monitor.window_area_y;
+                    } else if ((monitor.window_area_y + monitor.window_area_height) - (new_y + height as i32)).abs() < snap {
+                        new_y = monitor.window_area_y + monitor.window_area_height - height as i32;
+                    }
+
+                    if !was_floating && !is_normie &&
+                       ((new_x - orig_x as i32).abs() > snap || (new_y - orig_y as i32).abs() > snap) {
+                        self.toggle_floating()?;
+                    }
+
+                    let should_resize = is_normie || self.clients
+                        .get(&window)
+                        .map(|c| c.is_floating)
+                        .unwrap_or(false);
+
+                    if should_resize {
+                        if let Some(client) = self.clients.get_mut(&window) {
+                            client.x_position = new_x as i16;
+                            client.y_position = new_y as i16;
+                        }
+
+                        self.connection.configure_window(
+                            window,
+                            &ConfigureWindowAux::new()
+                                .x(new_x)
+                                .y(new_y),
+                        )?;
+                        self.connection.flush()?;
+                    }
                 }
                 Event::ButtonRelease(_) => break,
                 _ => {}
             }
         }
 
-        self.connection
-            .ungrab_pointer(x11rb::CURRENT_TIME)?
-            .check()?;
-        self.connection
-            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
-            .check()?;
+        self.connection.ungrab_pointer(x11rb::CURRENT_TIME)?.check()?;
+
+        let final_client = self.clients.get(&window).map(|c| {
+            (c.x_position, c.y_position, c.width, c.height)
+        });
+
+        if let Some((x, y, w, h)) = final_client {
+            let new_monitor = self.rect_to_monitor(x as i32, y as i32, w as i32, h as i32);
+            if new_monitor != monitor_idx {
+                self.sendmon(window, new_monitor)?;
+                self.selected_monitor = new_monitor;
+                self.focus(None)?;
+            }
+        }
 
         Ok(())
     }
 
     fn resize_mouse(&mut self, window: Window) -> WmResult<()> {
-        self.floating_windows.insert(window);
+        let is_fullscreen = self.clients
+            .get(&window)
+            .map(|c| c.is_fullscreen)
+            .unwrap_or(false);
 
-        let geometry = self.connection.get_geometry(window)?.reply()?;
+        if is_fullscreen {
+            return Ok(());
+        }
+
+        let client_info = self.clients.get(&window).map(|c| {
+            (c.x_position, c.y_position, c.width, c.height, c.border_width, c.is_floating, c.monitor_index)
+        });
+
+        let Some((orig_x, orig_y, orig_width, orig_height, border_width, was_floating, monitor_idx)) = client_info else {
+            return Ok(());
+        };
+
+        let monitor = self.monitors.get(monitor_idx).cloned();
+        let Some(monitor) = monitor else {
+            return Ok(());
+        };
+
+        let snap = 32;
+        let is_normie = self.layout.name() == "normie";
 
         self.connection.warp_pointer(
             x11rb::NONE,
@@ -2387,54 +2502,115 @@ impl WindowManager {
             0,
             0,
             0,
-            geometry.width as i16,
-            geometry.height as i16,
+            (orig_width + border_width - 1) as i16,
+            (orig_height + border_width - 1) as i16,
         )?;
 
-        self.connection
-            .grab_pointer(
-                false,
-                self.root,
-                (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE).into(),
-                GrabMode::ASYNC,
-                GrabMode::ASYNC,
-                x11rb::NONE,
-                x11rb::NONE,
-                x11rb::CURRENT_TIME,
-            )?
-            .reply()?;
+        self.connection.grab_pointer(
+            false,
+            self.root,
+            (EventMask::POINTER_MOTION | EventMask::BUTTON_RELEASE |
+             EventMask::BUTTON_PRESS).into(),
+            GrabMode::ASYNC,
+            GrabMode::ASYNC,
+            x11rb::NONE,
+            x11rb::NONE,
+            x11rb::CURRENT_TIME,
+        )?.reply()?;
 
-        self.connection.configure_window(
-            window,
-            &ConfigureWindowAux::new().stack_mode(StackMode::ABOVE),
-        )?;
+        let mut last_time = 0u32;
 
         loop {
             let event = self.connection.wait_for_event()?;
             match event {
+                Event::ConfigureRequest(_) | Event::MapRequest(_) | Event::Expose(_) => {}
                 Event::MotionNotify(e) => {
-                    let new_width = (e.root_x - geometry.x).max(1) as u32;
-                    let new_height = (e.root_y - geometry.y).max(1) as u32;
+                    if e.time.wrapping_sub(last_time) <= 16 {
+                        continue;
+                    }
+                    last_time = e.time;
+
+                    let new_width = ((e.root_x as i32 - orig_x as i32 - 2 * border_width as i32 + 1).max(1)) as u32;
+                    let new_height = ((e.root_y as i32 - orig_y as i32 - 2 * border_width as i32 + 1).max(1)) as u32;
+
+                    let in_monitor_bounds =
+                        monitor.window_area_x + new_width as i32 >= monitor.window_area_x &&
+                        monitor.window_area_x + new_width as i32 <= monitor.window_area_x + monitor.window_area_width &&
+                        monitor.window_area_y + new_height as i32 >= monitor.window_area_y &&
+                        monitor.window_area_y + new_height as i32 <= monitor.window_area_y + monitor.window_area_height;
+
+                    if in_monitor_bounds {
+                        if !was_floating && !is_normie &&
+                           ((new_width as i32 - orig_width as i32).abs() > snap ||
+                            (new_height as i32 - orig_height as i32).abs() > snap) {
+                            self.toggle_floating()?;
+                        }
+                    }
 
-                    self.connection.configure_window(
-                        window,
-                        &ConfigureWindowAux::new()
-                            .width(new_width)
-                            .height(new_height),
-                    )?;
-                    self.connection.flush()?;
+                    let should_resize = is_normie || self.clients
+                        .get(&window)
+                        .map(|c| c.is_floating)
+                        .unwrap_or(false);
+
+                    if should_resize {
+                        if let Some(client) = self.clients.get(&window) {
+                            let (hint_width, hint_height) = self.apply_size_hints(
+                                client,
+                                new_width as i32,
+                                new_height as i32,
+                            );
+
+                            if let Some(client_mut) = self.clients.get_mut(&window) {
+                                client_mut.width = hint_width as u16;
+                                client_mut.height = hint_height as u16;
+                            }
+
+                            self.connection.configure_window(
+                                window,
+                                &ConfigureWindowAux::new()
+                                    .width(hint_width as u32)
+                                    .height(hint_height as u32),
+                            )?;
+                            self.connection.flush()?;
+                        }
+                    }
                 }
                 Event::ButtonRelease(_) => break,
                 _ => {}
             }
         }
 
-        self.connection
-            .ungrab_pointer(x11rb::CURRENT_TIME)?
-            .check()?;
-        self.connection
-            .allow_events(Allow::REPLAY_POINTER, x11rb::CURRENT_TIME)?
-            .check()?;
+        let final_client = self.clients.get(&window).map(|c| {
+            (c.width, c.border_width)
+        });
+
+        if let Some((w, bw)) = final_client {
+            self.connection.warp_pointer(
+                x11rb::NONE,
+                window,
+                0,
+                0,
+                0,
+                0,
+                (w + bw - 1) as i16,
+                (w + bw - 1) as i16,
+            )?;
+        }
+
+        self.connection.ungrab_pointer(x11rb::CURRENT_TIME)?.check()?;
+
+        let final_client_pos = self.clients.get(&window).map(|c| {
+            (c.x_position, c.y_position, c.width, c.height)
+        });
+
+        if let Some((x, y, w, h)) = final_client_pos {
+            let new_monitor = self.rect_to_monitor(x as i32, y as i32, w as i32, h as i32);
+            if new_monitor != monitor_idx {
+                self.sendmon(window, new_monitor)?;
+                self.selected_monitor = new_monitor;
+                self.focus(None)?;
+            }
+        }
 
         Ok(())
     }