From 407f661c3393992b35b85d4cf9d18066fec72734 Mon Sep 17 00:00:00 2001
From: NoOneBtw <noonebtw@gmail.com>
Date: Mon, 3 May 2021 04:13:41 +0200
Subject: [PATCH] reworked move/resize windows (amend: actually works)

---
 src/state.rs | 292 +++++++++++++++++++++++++--------------------------
 src/xlib.rs  |  14 ++-
 2 files changed, 155 insertions(+), 151 deletions(-)

diff --git a/src/state.rs b/src/state.rs
index f6fe9bd..da1311c 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -2,7 +2,10 @@ use std::rc::Rc;
 
 use log::{error, info};
 
-use x11::xlib::{self, ShiftMask, Window, XButtonEvent, XEvent, XKeyEvent, XMotionEvent};
+use x11::xlib::{
+    self, ShiftMask, Window, XButtonEvent, XButtonPressedEvent, XButtonReleasedEvent, XEvent,
+    XKeyEvent, XMotionEvent,
+};
 use xlib::{
     ButtonPressMask, ButtonReleaseMask, Mod1Mask, PointerMotionMask, XConfigureRequestEvent,
     XCrossingEvent, XDestroyWindowEvent, XMapRequestEvent, XUnmapEvent,
@@ -16,8 +19,7 @@ use crate::{
 
 pub struct WindowManager {
     clients: ClientState,
-    move_window: Option<MoveWindow>,
-    resize_window: Option<MoveWindow>,
+    move_resize_window: MoveResizeInfo,
     keybinds: Vec<KeyBinding>,
     xlib: XLib,
 }
@@ -28,9 +30,22 @@ pub enum Direction {
     Right,
 }
 
-struct MoveWindow {
-    key: Window,
-    cached_cursor_position: (i32, i32),
+enum MoveResizeInfo {
+    Move(MoveInfoInner),
+    Resize(ResizeInfoInner),
+    None,
+}
+
+struct MoveInfoInner {
+    window: Window,
+    starting_cursor_pos: (i32, i32),
+    starting_window_pos: (i32, i32),
+}
+
+struct ResizeInfoInner {
+    window: Window,
+    starting_cursor_pos: (i32, i32),
+    starting_window_size: (i32, i32),
 }
 
 #[derive(Clone)]
@@ -46,8 +61,7 @@ impl WindowManager {
 
         Self {
             clients,
-            move_window: None,
-            resize_window: None,
+            move_resize_window: MoveResizeInfo::None,
             keybinds: Vec::new(),
             xlib,
         }
@@ -142,17 +156,15 @@ impl WindowManager {
         loop {
             let event = self.xlib.next_event();
 
-            self.handle_toggle_floating(&event);
-            self.handle_move_window(&event);
-            self.handle_resize_client(&event);
-
             match event.get_type() {
                 xlib::MapRequest => self.map_request(&event),
                 xlib::UnmapNotify => self.unmap_notify(&event),
                 xlib::ConfigureRequest => self.configure_request(&event),
                 xlib::EnterNotify => self.enter_notify(&event),
                 xlib::DestroyNotify => self.destroy_notify(&event),
-                xlib::ButtonPress => self.button_notify(&event),
+                xlib::ButtonPress => self.button_press(event.as_ref()),
+                xlib::ButtonRelease => self.button_release(event.as_ref()),
+                xlib::MotionNotify => self.motion_notify(event.as_ref()),
                 xlib::KeyPress => self.handle_keybinds(event.as_ref()),
                 _ => {}
             }
@@ -212,137 +224,6 @@ impl WindowManager {
         self.arrange_clients();
     }
 
-    fn handle_move_window(&mut self, event: &XEvent) {
-        let clean_mask = self.xlib.get_clean_mask();
-
-        match event.get_type() {
-            xlib::ButtonPress => {
-                let event: &XButtonEvent = event.as_ref();
-
-                if self.move_window.is_none()
-                    && event.button == 1
-                    && event.state & clean_mask == Mod1Mask & clean_mask
-                    && self.clients.contains(&event.subwindow)
-                {
-                    // if client is tiled, set to floating
-                    if self.clients.set_floating(&event.subwindow) {
-                        self.arrange_clients();
-                    }
-
-                    self.move_window = Some(MoveWindow {
-                        key: event.subwindow,
-                        cached_cursor_position: (event.x, event.y),
-                    });
-                }
-            }
-
-            // reset on release
-            xlib::ButtonRelease => {
-                let event: &XButtonEvent = event.as_ref();
-
-                if event.button == 1 && self.move_window.is_some() {
-                    self.move_window = None;
-                }
-            }
-
-            xlib::MotionNotify => {
-                //let event = self.xlib.squash_event(xlib::MotionNotify);
-                let event: &XMotionEvent = event.as_ref();
-
-                if let Some(move_window) = &mut self.move_window {
-                    let (x, y) = (
-                        event.x - move_window.cached_cursor_position.0,
-                        event.y - move_window.cached_cursor_position.1,
-                    );
-
-                    move_window.cached_cursor_position = (event.x, event.y);
-
-                    if let Some(client) = self.clients.get_mut(&move_window.key).into_option() {
-                        let position = &mut client.position;
-                        position.0 += x;
-                        position.1 += y;
-
-                        self.xlib.move_client(client);
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
-
-    fn handle_resize_client(&mut self, event: &XEvent) {
-        let clean_mask = self.xlib.get_clean_mask();
-
-        match event.get_type() {
-            xlib::ButtonPress => {
-                let event: &XButtonEvent = event.as_ref();
-
-                if self.resize_window.is_none()
-                    && event.button == 3
-                    && event.state & clean_mask == Mod1Mask & clean_mask
-                    && self.clients.contains(&event.subwindow)
-                {
-                    // if client is tiled, set to floating
-                    if self.clients.set_floating(&event.subwindow) {
-                        self.arrange_clients();
-                    }
-
-                    let client = self.clients.get(&event.subwindow).unwrap();
-
-                    let position = {
-                        (
-                            client.position.0 + client.size.0,
-                            client.position.1 + client.size.1,
-                        )
-                    };
-
-                    self.xlib.move_cursor(client.window, position);
-                    self.xlib.grab_cursor();
-
-                    self.resize_window = Some(MoveWindow {
-                        key: event.subwindow,
-                        cached_cursor_position: position,
-                    });
-                }
-            }
-
-            // reset on release
-            xlib::ButtonRelease => {
-                let event: &XButtonEvent = event.as_ref();
-
-                if event.button == 3 && self.resize_window.is_some() {
-                    self.resize_window = None;
-                    self.xlib.release_cursor();
-                }
-            }
-
-            xlib::MotionNotify => {
-                let event = self.xlib.squash_event(xlib::MotionNotify);
-                let event: &XMotionEvent = event.as_ref();
-
-                if let Some(resize_window) = &mut self.resize_window {
-                    info!("MotionNotify-resize");
-                    let (x, y) = (
-                        event.x - resize_window.cached_cursor_position.0,
-                        event.y - resize_window.cached_cursor_position.1,
-                    );
-
-                    resize_window.cached_cursor_position = (event.x, event.y);
-
-                    if let Some(client) = self.clients.get_mut(&resize_window.key).into_option() {
-                        let size = &mut client.size;
-
-                        size.0 = std::cmp::max(1, size.0 + x);
-                        size.1 = std::cmp::max(1, size.1 + y);
-
-                        self.xlib.resize_client(client);
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
-
     fn rotate_virtual_screen(&mut self, dir: Direction) {
         info!("rotateing VS: {:?}", dir);
 
@@ -471,15 +352,128 @@ impl WindowManager {
         self.focus_client(&event.window);
     }
 
-    fn button_notify(&mut self, event: &XEvent) {
-        let event: &XButtonEvent = event.as_ref();
+    /// ensure event.subwindow refers to a valid client.
+    fn start_move_resize_window(&mut self, event: &XButtonPressedEvent) {
+        let window = event.subwindow;
 
-        self.focus_client(&event.subwindow);
-        if let Some(client) = self.clients.get(&event.subwindow).into_option() {
-            self.xlib.raise_client(client);
+        match event.button {
+            1 => {
+                if self.clients.set_floating(&window) {
+                    self.arrange_clients();
+                }
+
+                self.move_resize_window = MoveResizeInfo::Move(MoveInfoInner {
+                    window,
+                    starting_cursor_pos: (event.x, event.y),
+                    starting_window_pos: self.clients.get(&window).unwrap().position,
+                });
+            }
+            3 => {
+                if self.clients.set_floating(&window) {
+                    self.arrange_clients();
+                }
+
+                let client = self.clients.get(&window).unwrap();
+
+                let corner_pos = {
+                    (
+                        client.position.0 + client.size.0,
+                        client.position.1 + client.size.1,
+                    )
+                };
+
+                self.xlib.move_cursor(None, corner_pos);
+                self.xlib.grab_cursor();
+
+                self.move_resize_window = MoveResizeInfo::Resize(ResizeInfoInner {
+                    window,
+                    starting_cursor_pos: corner_pos,
+                    starting_window_size: client.size,
+                });
+            }
+            _ => {}
         }
     }
 
+    fn end_move_resize_window(&mut self, event: &XButtonReleasedEvent) {
+        if event.button == 1 || event.button == 3 {
+            self.move_resize_window = MoveResizeInfo::None;
+        }
+        if event.button == 3 {
+            self.xlib.release_cursor();
+        }
+    }
+
+    fn do_move_resize_window(&mut self, event: &XMotionEvent) {
+        match &self.move_resize_window {
+            MoveResizeInfo::Move(info) => {
+                let (x, y) = (
+                    event.x - info.starting_cursor_pos.0,
+                    event.y - info.starting_cursor_pos.1,
+                );
+
+                if let Some(client) = self.clients.get_mut(&info.window).into_option() {
+                    let position = &mut client.position;
+
+                    position.0 = info.starting_window_pos.0 + x;
+                    position.1 = info.starting_window_pos.1 + y;
+
+                    self.xlib.move_client(client);
+                }
+            }
+            MoveResizeInfo::Resize(info) => {
+                let (x, y) = (
+                    event.x - info.starting_cursor_pos.0,
+                    event.y - info.starting_cursor_pos.1,
+                );
+
+                if let Some(client) = self.clients.get_mut(&info.window).into_option() {
+                    let size = &mut client.size;
+
+                    size.0 = std::cmp::max(1, info.starting_window_size.0 + x);
+                    size.1 = std::cmp::max(1, info.starting_window_size.1 + y);
+
+                    self.xlib.resize_client(client);
+                }
+            }
+            _ => {}
+        }
+    }
+
+    fn button_press(&mut self, event: &XButtonPressedEvent) {
+        self.focus_client(&event.subwindow);
+
+        match event.button {
+            1 | 3 => match self.move_resize_window {
+                MoveResizeInfo::None
+                    if self.xlib.are_masks_equal(event.state, Mod1Mask)
+                        && self.clients.contains(&event.subwindow) =>
+                {
+                    self.start_move_resize_window(event)
+                }
+                _ => {}
+            },
+            2 => {
+                self.clients.toggle_floating(&event.subwindow);
+                self.arrange_clients();
+            }
+            _ => {}
+        }
+    }
+
+    fn button_release(&mut self, event: &XButtonReleasedEvent) {
+        match self.move_resize_window {
+            MoveResizeInfo::None => {}
+            _ => {
+                self.end_move_resize_window(event);
+            }
+        }
+    }
+
+    fn motion_notify(&mut self, event: &XMotionEvent) {
+        self.do_move_resize_window(event);
+    }
+
     pub fn spawn(&self, command: &str, args: &[&str]) {
         info!("spawn: {:?} {:?}", command, args.join(" "));
         match std::process::Command::new(command).args(args).spawn() {
diff --git a/src/xlib.rs b/src/xlib.rs
index 968c6a6..dd2757f 100644
--- a/src/xlib.rs
+++ b/src/xlib.rs
@@ -461,9 +461,9 @@ impl XLib {
         }
     }
 
-    pub fn move_cursor(&self, window: Window, position: (i32, i32)) {
+    pub fn move_cursor(&self, window: Option<Window>, position: (i32, i32)) {
         unsafe {
-            XWarpPointer(self.dpy(), 0, window, 0, 0, 0, 0, position.0, position.1);
+            XWarpPointer(self.dpy(), 0, self.root, 0, 0, 0, 0, position.0, position.1);
         }
     }
 
@@ -558,6 +558,16 @@ impl XLib {
         !(self.get_numlock_mask() | LockMask)
             & (ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
     }
+
+    pub fn clean_mask(&self, mask: u32) -> u32 {
+        mask & self.get_clean_mask()
+    }
+
+    pub fn are_masks_equal(&self, rhs: u32, lhs: u32) -> bool {
+        let clean = self.get_clean_mask();
+
+        rhs & clean == lhs & clean
+    }
 }
 
 impl Display {