gardesk/gar / 255874d

Browse files

fix: use stored geometry for pointer warp to avoid X11 race

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
255874d55cba4400e14c12c8c4a51aa7890ac50b
Parents
46d12e5
Tree
c23a6e5

5 changed files

StatusFile+-
M Cargo.lock 2 2
M gar/src/core/mod.rs 75 53
M gar/src/core/window.rs 3 0
M gar/src/x11/connection.rs 5 0
M gar/src/x11/events.rs 2 0
Cargo.lockmodified
@@ -202,7 +202,7 @@ checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
202202
 
203203
 [[package]]
204204
 name = "gar"
205
-version = "0.2.0"
205
+version = "0.3.0"
206206
 dependencies = [
207207
  "dirs",
208208
  "libc",
@@ -217,7 +217,7 @@ dependencies = [
217217
 
218218
 [[package]]
219219
 name = "garctl"
220
-version = "0.2.0"
220
+version = "0.3.0"
221221
 dependencies = [
222222
  "clap",
223223
  "serde",
gar/src/core/mod.rsmodified
@@ -700,11 +700,22 @@ impl WindowManager {
700700
 
701701
         // Warp pointer to center of focused window (mouse follows focus)
702702
         if warp_pointer {
703
-            if let Err(e) = self.conn.warp_pointer_to_window(window) {
704
-                tracing::warn!("Failed to warp pointer: {}", e);
703
+            // Use stored geometry instead of querying X11 (avoids race with ConfigureWindow)
704
+            if let Some(win) = self.windows.get(&window) {
705
+                let g = &win.current_geometry;
706
+                let center_x = g.x + (g.width as i16 / 2);
707
+                let center_y = g.y + (g.height as i16 / 2);
708
+                if let Err(e) = self.conn.warp_pointer(center_x, center_y) {
709
+                    tracing::warn!("Failed to warp pointer: {}", e);
710
+                }
711
+            } else {
712
+                // Fallback to querying X11 if window not in our map
713
+                if let Err(e) = self.conn.warp_pointer_to_window(window) {
714
+                    tracing::warn!("Failed to warp pointer: {}", e);
715
+                }
705716
             }
706
-            // Record warp time to suppress EnterNotify feedback loop
707717
             self.last_warp = std::time::Instant::now();
718
+            self.conn.flush()?;
708719
         }
709720
 
710721
         Ok(())
@@ -981,6 +992,11 @@ impl WindowManager {
981992
                         border_width,
982993
                     )?;
983994
                 }
995
+
996
+                // Store the actual geometry for pointer warping
997
+                if let Some(win) = self.windows.get_mut(window) {
998
+                    win.current_geometry = Rect::new(gapped_x, gapped_y, final_width.max(1), final_height.max(1));
999
+                }
9841000
             }
9851001
 
9861002
             // 2. Configure floating windows and stack them above tiled
@@ -988,59 +1004,65 @@ impl WindowManager {
9881004
 
9891005
             for window_id in floating_ids {
9901006
                 // Get the window's floating geometry from our state
991
-                if let Some(win) = self.windows.get(&window_id) {
992
-                    let geom = win.floating_geometry;
993
-                    let adjusted_width = geom.width.saturating_sub(2 * border_width as u16);
994
-                    let adjusted_height = geom.height.saturating_sub(2 * border_width as u16);
995
-
996
-                    let has_frame = win.frame.is_some();
997
-
998
-                    if has_frame && titlebar_enabled {
999
-                        // Configure frame for floating window
1000
-                        let client_height = adjusted_height.saturating_sub(titlebar_height);
1001
-                        self.frames.configure_frame(
1002
-                            &self.conn.conn,
1003
-                            window_id,
1004
-                            geom.x,
1005
-                            geom.y,
1006
-                            adjusted_width.max(1),
1007
-                            client_height.max(1),
1008
-                            titlebar_height,
1009
-                            border_width as u16,
1010
-                        )?;
1011
-
1012
-                        // Raise frame to top of stack
1013
-                        if let Some(frame) = self.frames.frame_for_client(window_id) {
1014
-                            let aux = ConfigureWindowAux::new().stack_mode(StackMode::ABOVE);
1015
-                            self.conn.conn.configure_window(frame, &aux)?;
1016
-                        }
1017
-
1018
-                        tracing::debug!(
1019
-                            "apply_layout: FLOATING+FRAME window={} at ({}, {}) size {}x{} (raising)",
1020
-                            window_id, geom.x, geom.y, adjusted_width.max(1), adjusted_height.max(1)
1021
-                        );
1022
-                    } else {
1023
-                        tracing::debug!(
1024
-                            "apply_layout: FLOATING window={} at ({}, {}) size {}x{} (raising)",
1025
-                            window_id, geom.x, geom.y, adjusted_width.max(1), adjusted_height.max(1)
1026
-                        );
1027
-
1028
-                        // Configure geometry
1029
-                        self.conn.configure_window(
1030
-                            window_id,
1031
-                            geom.x,
1032
-                            geom.y,
1033
-                            adjusted_width.max(1),
1034
-                            adjusted_height.max(1),
1035
-                            border_width,
1036
-                        )?;
1037
-
1038
-                        // Raise to top of stack (each subsequent window goes above the previous)
1007
+                let (geom, has_frame) = match self.windows.get(&window_id) {
1008
+                    Some(win) => (win.floating_geometry, win.frame.is_some()),
1009
+                    None => {
1010
+                        tracing::warn!("apply_layout: floating window {} not in windows map!", window_id);
1011
+                        continue;
1012
+                    }
1013
+                };
1014
+
1015
+                let adjusted_width = geom.width.saturating_sub(2 * border_width as u16);
1016
+                let adjusted_height = geom.height.saturating_sub(2 * border_width as u16);
1017
+
1018
+                if has_frame && titlebar_enabled {
1019
+                    // Configure frame for floating window
1020
+                    let client_height = adjusted_height.saturating_sub(titlebar_height);
1021
+                    self.frames.configure_frame(
1022
+                        &self.conn.conn,
1023
+                        window_id,
1024
+                        geom.x,
1025
+                        geom.y,
1026
+                        adjusted_width.max(1),
1027
+                        client_height.max(1),
1028
+                        titlebar_height,
1029
+                        border_width as u16,
1030
+                    )?;
1031
+
1032
+                    // Raise frame to top of stack
1033
+                    if let Some(frame) = self.frames.frame_for_client(window_id) {
10391034
                         let aux = ConfigureWindowAux::new().stack_mode(StackMode::ABOVE);
1040
-                        self.conn.conn.configure_window(window_id, &aux)?;
1035
+                        self.conn.conn.configure_window(frame, &aux)?;
10411036
                     }
1037
+
1038
+                    tracing::debug!(
1039
+                        "apply_layout: FLOATING+FRAME window={} at ({}, {}) size {}x{} (raising)",
1040
+                        window_id, geom.x, geom.y, adjusted_width.max(1), adjusted_height.max(1)
1041
+                    );
10421042
                 } else {
1043
-                    tracing::warn!("apply_layout: floating window {} not in windows map!", window_id);
1043
+                    tracing::debug!(
1044
+                        "apply_layout: FLOATING window={} at ({}, {}) size {}x{} (raising)",
1045
+                        window_id, geom.x, geom.y, adjusted_width.max(1), adjusted_height.max(1)
1046
+                    );
1047
+
1048
+                    // Configure geometry
1049
+                    self.conn.configure_window(
1050
+                        window_id,
1051
+                        geom.x,
1052
+                        geom.y,
1053
+                        adjusted_width.max(1),
1054
+                        adjusted_height.max(1),
1055
+                        border_width,
1056
+                    )?;
1057
+
1058
+                    // Raise to top of stack (each subsequent window goes above the previous)
1059
+                    let aux = ConfigureWindowAux::new().stack_mode(StackMode::ABOVE);
1060
+                    self.conn.conn.configure_window(window_id, &aux)?;
1061
+                }
1062
+
1063
+                // Store the actual geometry for pointer warping
1064
+                if let Some(win) = self.windows.get_mut(&window_id) {
1065
+                    win.current_geometry = Rect::new(geom.x, geom.y, adjusted_width.max(1), adjusted_height.max(1));
10441066
                 }
10451067
             }
10461068
         }
gar/src/core/window.rsmodified
@@ -7,6 +7,8 @@ pub struct Window {
77
     pub id: XWindow,
88
     /// Geometry for floating mode (position and size when floating)
99
     pub floating_geometry: Rect,
10
+    /// Current actual geometry (updated by apply_layout, used for pointer warping)
11
+    pub current_geometry: Rect,
1012
     pub mapped: bool,
1113
     pub focused: bool,
1214
     pub floating: bool,
@@ -30,6 +32,7 @@ impl Window {
3032
         Self {
3133
             id,
3234
             floating_geometry: Rect::new(0, 0, 640, 480),
35
+            current_geometry: Rect::new(0, 0, 640, 480),
3336
             mapped: false,
3437
             focused: false,
3538
             floating: false,
gar/src/x11/connection.rsmodified
@@ -516,6 +516,11 @@ impl Connection {
516516
         let center_x = (geom.width / 2) as i16;
517517
         let center_y = (geom.height / 2) as i16;
518518
 
519
+        tracing::debug!(
520
+            "Warping pointer to window {}: geom={}x{}+{}+{}, center=({},{})",
521
+            window, geom.width, geom.height, geom.x, geom.y, center_x, center_y
522
+        );
523
+
519524
         self.conn.warp_pointer(
520525
             x11rb::NONE,  // src_window (none = don't check source)
521526
             window,       // dst_window
gar/src/x11/events.rsmodified
@@ -571,6 +571,8 @@ impl WindowManager {
571571
 
572572
         // Apply layout to all windows
573573
         self.apply_layout()?;
574
+        // Flush to ensure ConfigureWindow requests are processed before we query geometry
575
+        self.conn.flush()?;
574576
 
575577
         // Focus the new window if on a visible workspace
576578
         if target_visible {