gardesk/gar / 77eff09

Browse files

Fix picom v12+ startup and improve cursor handling

- Add --backend glx to picom commands (required for v12+, no default)
- Add cursor_move (fleur) for floating window drag operations
- Add clear_window_cursor() to prevent grab/window cursor conflicts
- Pass appropriate cursor to grab_pointer() during drag/resize
- Restore edge cursor after drag release
- Add cursor_for_edge() helper for resize cursor selection
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
77eff09dfa9e861cb10ce3120d1c4cd01193596b
Parents
9239558
Tree
dacf2c7

4 changed files

StatusFile+-
M gar-session.sh 2 1
M gar/src/x11/connection.rs 21 6
M gar/src/x11/events.rs 86 37
M start-gar.sh 2 1
gar-session.shmodified
@@ -8,8 +8,9 @@
88
 #   --output HDMI-1 --mode 2560x1440 --pos 1920x0
99
 
1010
 # Launch compositor before WM (for proper screen repainting)
11
+# --backend glx is required for picom v12+ (no longer has a default)
1112
 if command -v picom &> /dev/null; then
12
-    picom -b --use-ewmh-active-win &
13
+    picom -b --backend glx --use-ewmh-active-win &
1314
     sleep 0.1
1415
 fi
1516
 
gar/src/x11/connection.rsmodified
@@ -83,8 +83,9 @@ pub struct Connection {
8383
     // Struts (reserved screen areas for docks/panels)
8484
     pub net_wm_strut: Atom,
8585
     pub net_wm_strut_partial: Atom,
86
-    // Cursors for resize operations
86
+    // Cursors for resize/move operations
8787
     pub cursor_normal: u32,
88
+    pub cursor_move: u32,
8889
     pub cursor_top_left: u32,
8990
     pub cursor_top_right: u32,
9091
     pub cursor_bottom_left: u32,
@@ -155,6 +156,7 @@ impl Connection {
155156
         // Create cursors for pointer and resize operations
156157
         let (
157158
             cursor_normal,
159
+            cursor_move,
158160
             cursor_top_left,
159161
             cursor_top_right,
160162
             cursor_bottom_left,
@@ -209,6 +211,7 @@ impl Connection {
209211
             net_wm_strut,
210212
             net_wm_strut_partial,
211213
             cursor_normal,
214
+            cursor_move,
212215
             cursor_top_left,
213216
             cursor_top_right,
214217
             cursor_bottom_left,
@@ -283,13 +286,13 @@ impl Connection {
283286
     }
284287
 
285288
     /// Create all cursors used by the window manager.
286
-    fn create_cursors(conn: &RustConnection) -> Result<(u32, u32, u32, u32, u32, u32, u32, u32, u32), Error> {
289
+    fn create_cursors(conn: &RustConnection) -> Result<(u32, u32, u32, u32, u32, u32, u32, u32, u32, u32), Error> {
287290
         // Open the cursor font
288291
         let font: Font = conn.generate_id()?;
289292
         conn.open_font(font, b"cursor")?;
290293
 
291294
         // Cursor glyph numbers from the cursor font:
292
-        // left_ptr = 68, top_left_corner = 134, top_right_corner = 136
295
+        // left_ptr = 68, fleur = 52, top_left_corner = 134, top_right_corner = 136
293296
         // bottom_left_corner = 12, bottom_right_corner = 14
294297
         // left_side = 70, right_side = 96, top_side = 138, bottom_side = 16
295298
 
@@ -308,6 +311,7 @@ impl Connection {
308311
         };
309312
 
310313
         let cursor_normal = create(68)?;       // left_ptr
314
+        let cursor_move = create(52)?;         // fleur (move cursor)
311315
         let cursor_top_left = create(134)?;    // top_left_corner
312316
         let cursor_top_right = create(136)?;   // top_right_corner
313317
         let cursor_bottom_left = create(12)?;  // bottom_left_corner
@@ -322,6 +326,7 @@ impl Connection {
322326
 
323327
         Ok((
324328
             cursor_normal,
329
+            cursor_move,
325330
             cursor_top_left,
326331
             cursor_top_right,
327332
             cursor_bottom_left,
@@ -437,11 +442,20 @@ impl Connection {
437442
 
438443
     /// Set the cursor for a window.
439444
     pub fn set_window_cursor(&self, window: Window, cursor: u32) -> Result<(), Error> {
445
+        tracing::debug!("set_window_cursor: window={} cursor={}", window, cursor);
440446
         let change = ChangeWindowAttributesAux::new().cursor(cursor);
441447
         self.conn.change_window_attributes(window, &change)?;
442448
         Ok(())
443449
     }
444450
 
451
+    /// Clear the cursor attribute from a window, letting the application's cursor show.
452
+    pub fn clear_window_cursor(&self, window: Window) -> Result<(), Error> {
453
+        tracing::debug!("clear_window_cursor: window={}", window);
454
+        let change = ChangeWindowAttributesAux::new().cursor(x11rb::NONE);
455
+        self.conn.change_window_attributes(window, &change)?;
456
+        Ok(())
457
+    }
458
+
445459
     /// Set input focus to a window.
446460
     pub fn set_focus(&self, window: Window) -> Result<(), Error> {
447461
         self.conn
@@ -528,8 +542,9 @@ impl Connection {
528542
         Ok(())
529543
     }
530544
 
531
-    /// Grab the pointer for drag operations.
532
-    pub fn grab_pointer(&self, _window: Window) -> Result<(), Error> {
545
+    /// Grab the pointer for drag operations with optional cursor override.
546
+    pub fn grab_pointer(&self, cursor: Option<u32>) -> Result<(), Error> {
547
+        let cursor_id = cursor.unwrap_or(x11rb::NONE);
533548
         let reply = self.conn.grab_pointer(
534549
             false,
535550
             self.root,
@@ -537,7 +552,7 @@ impl Connection {
537552
             GrabMode::ASYNC,
538553
             GrabMode::ASYNC,
539554
             x11rb::NONE,
540
-            x11rb::NONE,
555
+            cursor_id,
541556
             CURRENT_TIME,
542557
         )?.reply()?;
543558
         tracing::debug!("grab_pointer result: {:?}", reply.status);
gar/src/x11/events.rsmodified
@@ -134,7 +134,7 @@ impl WindowManager {
134134
             let should_float = rule_actions.floating.unwrap_or_else(|| self.conn.should_float(window));
135135
 
136136
             // Subscribe to events on the window
137
-            // Floating windows get POINTER_MOTION for edge resize cursors
137
+            // Floating windows get POINTER_MOTION for edge resize cursor feedback
138138
             let base_events = EventMask::ENTER_WINDOW
139139
                 | EventMask::FOCUS_CHANGE
140140
                 | EventMask::PROPERTY_CHANGE
@@ -175,6 +175,19 @@ impl WindowManager {
175175
     }
176176
 
177177
     pub fn handle_event(&mut self, event: Event) -> Result<()> {
178
+        // Log events to debug cursor flicker
179
+        match &event {
180
+            Event::MotionNotify(e) => {
181
+                tracing::info!("MOTION: window={} pos=({},{})", e.event, e.root_x, e.root_y);
182
+            }
183
+            Event::EnterNotify(e) => {
184
+                tracing::info!("ENTER: window={}", e.event);
185
+            }
186
+            Event::ButtonPress(e) => {
187
+                tracing::info!("BUTTON: window={} btn={}", e.event, e.detail);
188
+            }
189
+            _ => {}
190
+        }
178191
         match event {
179192
             Event::MapRequest(e) => self.handle_map_request(e)?,
180193
             Event::ConfigureRequest(e) => self.handle_configure_request(e)?,
@@ -256,7 +269,7 @@ impl WindowManager {
256269
         let should_float = rule_actions.floating.unwrap_or_else(|| self.conn.should_float(window));
257270
 
258271
         // Subscribe to events on the window
259
-        // Floating windows get POINTER_MOTION for edge resize cursors
272
+        // Floating windows get POINTER_MOTION for edge resize cursor feedback
260273
         let base_events = EventMask::ENTER_WINDOW
261274
             | EventMask::FOCUS_CHANGE
262275
             | EventMask::PROPERTY_CHANGE
@@ -449,8 +462,10 @@ impl WindowManager {
449462
                     start_y: event.root_y,
450463
                     start_geometry: geometry,
451464
                 });
452
-                // Grab pointer for motion events
453
-                self.conn.grab_pointer(target)?;
465
+                // Clear window cursor to prevent conflict with grab cursor
466
+                self.conn.clear_window_cursor(target)?;
467
+                // Grab pointer for motion events (use fleur/move cursor)
468
+                self.conn.grab_pointer(Some(self.conn.cursor_move))?;
454469
                 return Ok(());
455470
             } else if event.detail == 3 {
456471
                 // Mod+Button3 = Resize (quadrant-based edge detection)
@@ -463,7 +478,10 @@ impl WindowManager {
463478
                     start_geometry: geometry,
464479
                     edge,
465480
                 });
466
-                self.conn.grab_pointer(target)?;
481
+                // Clear window cursor to prevent conflict with grab cursor
482
+                self.conn.clear_window_cursor(target)?;
483
+                let cursor = self.cursor_for_edge(edge);
484
+                self.conn.grab_pointer(Some(cursor))?;
467485
                 return Ok(());
468486
             }
469487
         }
@@ -492,7 +510,10 @@ impl WindowManager {
492510
                     edge,
493511
                 });
494512
                 tracing::debug!("Grabbing pointer for resize, geometry={:?}", geometry);
495
-                self.conn.grab_pointer(window)?;
513
+                // Clear window cursor to prevent conflict with grab cursor
514
+                self.conn.clear_window_cursor(window)?;
515
+                let cursor = self.cursor_for_edge(edge);
516
+                self.conn.grab_pointer(Some(cursor))?;
496517
                 self.conn.flush()?;
497518
                 return Ok(());
498519
             }
@@ -549,11 +570,19 @@ impl WindowManager {
549570
     fn handle_button_release(&mut self, event: ButtonReleaseEvent) -> Result<()> {
550571
         tracing::debug!("ButtonRelease: button={}, window={}, in_drag={}",
551572
             event.detail, event.event, self.drag_state.is_some());
552
-        if self.drag_state.is_some() {
573
+        if let Some(drag) = self.drag_state.take() {
553574
             tracing::debug!("Ending drag operation");
554
-            self.drag_state = None;
555575
             self.conn.ungrab_pointer()?;
556576
             self.conn.flush()?;
577
+
578
+            // Restore edge cursor if pointer is still over the dragged floating window
579
+            let window = match drag {
580
+                DragState::Move { window, .. } | DragState::Resize { window, .. } => window,
581
+            };
582
+            if self.is_floating(window) {
583
+                // Use the release event position to update cursor
584
+                self.update_edge_cursor(window, event.root_x, event.root_y)?;
585
+            }
557586
         }
558587
         Ok(())
559588
     }
@@ -565,11 +594,13 @@ impl WindowManager {
565594
             let is_managed = self.windows.contains_key(&window);
566595
             let is_float = is_managed && self.is_floating(window);
567596
 
597
+            // Debug: log all motion events to track down cursor flicker
598
+            tracing::debug!(
599
+                "MotionNotify: window={} managed={} floating={} root_xy=({},{}) event_xy=({},{})",
600
+                window, is_managed, is_float, event.root_x, event.root_y, event.event_x, event.event_y
601
+            );
602
+
568603
             if is_float {
569
-                tracing::trace!(
570
-                    "Motion on floating window {}: root({},{}) event({},{})",
571
-                    window, event.root_x, event.root_y, event.event_x, event.event_y
572
-                );
573604
                 self.update_edge_cursor(window, event.root_x, event.root_y)?;
574605
             }
575606
             return Ok(());
@@ -620,9 +651,11 @@ impl WindowManager {
620651
     /// Handle pointer motion for edge cursor changes on floating windows.
621652
     fn update_edge_cursor(&mut self, window: u32, root_x: i16, root_y: i16) -> Result<()> {
622653
         if !self.is_floating(window) {
623
-            // Not a floating window, ensure cursor is normal
624
-            if self.current_edge_cursor.is_some() {
625
-                self.conn.set_window_cursor(window, self.conn.cursor_normal)?;
654
+            // Not a floating window, clear any edge cursor state
655
+            if let Some((old_window, old_edge)) = self.current_edge_cursor {
656
+                if old_edge != ResizeEdge::None {
657
+                    self.conn.clear_window_cursor(old_window)?;
658
+                }
626659
                 self.current_edge_cursor = None;
627660
             }
628661
             return Ok(());
@@ -637,28 +670,29 @@ impl WindowManager {
637670
             return Ok(()); // No change needed
638671
         }
639672
 
640
-        // Get the appropriate cursor for this edge
641
-        let cursor = match edge {
642
-            ResizeEdge::TopLeft => self.conn.cursor_top_left,
643
-            ResizeEdge::Top => self.conn.cursor_top,
644
-            ResizeEdge::TopRight => self.conn.cursor_top_right,
645
-            ResizeEdge::Left => self.conn.cursor_left,
646
-            ResizeEdge::Right => self.conn.cursor_right,
647
-            ResizeEdge::BottomLeft => self.conn.cursor_bottom_left,
648
-            ResizeEdge::Bottom => self.conn.cursor_bottom,
649
-            ResizeEdge::BottomRight => self.conn.cursor_bottom_right,
650
-            ResizeEdge::None => self.conn.cursor_normal,
651
-        };
652
-
653
-        // Set cursor on the window
654
-        self.conn.set_window_cursor(window, cursor)?;
655
-        self.conn.flush()?;
656
-
657673
         if edge != ResizeEdge::None {
658
-            tracing::debug!("Set resize cursor {:?} for floating window {} edge", edge, window);
674
+            let cursor = match edge {
675
+                ResizeEdge::TopLeft => self.conn.cursor_top_left,
676
+                ResizeEdge::Top => self.conn.cursor_top,
677
+                ResizeEdge::TopRight => self.conn.cursor_top_right,
678
+                ResizeEdge::Left => self.conn.cursor_left,
679
+                ResizeEdge::Right => self.conn.cursor_right,
680
+                ResizeEdge::BottomLeft => self.conn.cursor_bottom_left,
681
+                ResizeEdge::Bottom => self.conn.cursor_bottom,
682
+                ResizeEdge::BottomRight => self.conn.cursor_bottom_right,
683
+                ResizeEdge::None => unreachable!(),
684
+            };
685
+            self.conn.set_window_cursor(window, cursor)?;
686
+            self.conn.flush()?;
659687
             self.current_edge_cursor = Some((window, edge));
688
+        } else if let Some((old_window, old_edge)) = current {
689
+            if old_edge != ResizeEdge::None {
690
+                self.conn.clear_window_cursor(old_window)?;
691
+                self.conn.flush()?;
692
+            }
693
+            self.current_edge_cursor = Some((window, ResizeEdge::None));
660694
         } else {
661
-            self.current_edge_cursor = None;
695
+            self.current_edge_cursor = Some((window, ResizeEdge::None));
662696
         }
663697
 
664698
         Ok(())
@@ -1696,6 +1730,21 @@ impl WindowManager {
16961730
             .unwrap_or(false)
16971731
     }
16981732
 
1733
+    /// Get the appropriate cursor for a resize edge.
1734
+    fn cursor_for_edge(&self, edge: ResizeEdge) -> u32 {
1735
+        match edge {
1736
+            ResizeEdge::TopLeft => self.conn.cursor_top_left,
1737
+            ResizeEdge::Top => self.conn.cursor_top,
1738
+            ResizeEdge::TopRight => self.conn.cursor_top_right,
1739
+            ResizeEdge::Left => self.conn.cursor_left,
1740
+            ResizeEdge::Right => self.conn.cursor_right,
1741
+            ResizeEdge::BottomLeft => self.conn.cursor_bottom_left,
1742
+            ResizeEdge::Bottom => self.conn.cursor_bottom,
1743
+            ResizeEdge::BottomRight => self.conn.cursor_bottom_right,
1744
+            ResizeEdge::None => self.conn.cursor_normal,
1745
+        }
1746
+    }
1747
+
16991748
     fn get_floating_geometry(&self, window: u32) -> Rect {
17001749
         self.windows
17011750
             .get(&window)
@@ -1953,7 +2002,7 @@ impl WindowManager {
19532002
                 win.floating = false;
19542003
             }
19552004
 
1956
-            // Remove POINTER_MOTION event mask (no longer need edge detection)
2005
+            // Standard event mask for tiled window
19572006
             self.conn.select_input(
19582007
                 window,
19592008
                 EventMask::ENTER_WINDOW
@@ -1964,7 +2013,7 @@ impl WindowManager {
19642013
 
19652014
             // Clear edge cursor state if this window had one
19662015
             if self.current_edge_cursor.map(|(w, _)| w) == Some(window) {
1967
-                self.conn.set_window_cursor(window, self.conn.cursor_normal)?;
2016
+                self.conn.clear_window_cursor(window)?;
19682017
                 self.current_edge_cursor = None;
19692018
             }
19702019
 
@@ -2010,7 +2059,7 @@ impl WindowManager {
20102059
                 win.floating_geometry = geometry;
20112060
             }
20122061
 
2013
-            // Add POINTER_MOTION event mask for edge detection
2062
+            // Event mask for floating window (includes POINTER_MOTION for edge cursor)
20142063
             self.conn.select_input(
20152064
                 window,
20162065
                 EventMask::ENTER_WINDOW
start-gar.shmodified
@@ -86,8 +86,9 @@ xrandr \\
8686
 
8787
 # Launch compositor before WM (for proper screen repainting)
8888
 # --use-ewmh-active-win uses _NET_ACTIVE_WINDOW for focus detection
89
+# --backend glx is required for picom v12+ (no longer has a default)
8990
 if command -v picom > /dev/null 2>&1; then
90
-    picom -b --use-ewmh-active-win &
91
+    picom -b --backend glx --use-ewmh-active-win &
9192
     sleep 0.1
9293
 fi
9394