git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commitf8cc29e9b7d1528f9c992ac3c99a2e286b037d27
parent5145dd3e3f
authorLucas Galante <[email protected]>
date2026-06-23 14:06
Implement Super + two-finger scroll panning and include other local server/seat updates

 src/server/cursor.rs         | 23 +++++++++++++++++++++++
 src/server/seat.rs           |  5 ++++-
 src/server/window.rs         |  4 +---
 src/server/window_manager.rs | 19 +++++++++++++++++++
 4 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 2471e58..b39048d 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -873,6 +873,29 @@ unsafe extern "C" fn handle_axis(listener: *mut ffi::wl_listener, data: *mut std
         }
     }
 
+    let wlr_keyboard = ffi::river_wlr_seat_get_keyboard(seat.wlr_seat);
+    let modifiers = if !wlr_keyboard.is_null() {
+        ffi::wlr_keyboard_get_modifiers(wlr_keyboard)
+    } else {
+        0
+    };
+
+    if (modifiers & 0x40) != 0 {
+        let wm = &mut (*seat.server).wm;
+        let step = delta / wm.desk_zoom;
+        match (*event).orientation {
+            ffi::wl_pointer_axis_WL_POINTER_AXIS_VERTICAL_SCROLL => {
+                wm.desk_pan_y += step;
+            }
+            ffi::wl_pointer_axis_WL_POINTER_AXIS_HORIZONTAL_SCROLL => {
+                wm.desk_pan_x += step;
+            }
+            _ => {}
+        }
+        wm.dirty_windowing();
+        return;
+    }
+
     ffi::wlr_seat_pointer_notify_axis(
         seat.wlr_seat,
         (*event).time_msec,
diff --git a/src/server/seat.rs b/src/server/seat.rs
index 9e8e3a1..92740d3 100644
--- a/src/server/seat.rs
+++ b/src/server/seat.rs
@@ -31,7 +31,7 @@ pub struct SeatOp {
     pub start_win_virtual_y: f64,
 }
 
-#[derive(Clone, Copy, PartialEq)]
+#[derive(Clone, Copy, PartialEq, Debug)]
 pub enum Focus {
     None,
     LayerSurface(*mut ffi::wlr_surface),
@@ -304,6 +304,9 @@ impl Seat {
             return;
         }
 
+        let bt = std::backtrace::Backtrace::capture();
+        log::info!("[FocusDebug] Seat::focus changing from {:?} to {:?}. Backtrace:\n{}", self.focused, new_focus, bt);
+
         // If an exclusive layer surface is active and scheduled for focus,
         // block any window manager or other client focus requests (via focus_requested)
         // from stealing focus back to a regular window or shell surface.
diff --git a/src/server/window.rs b/src/server/window.rs
index 619cfb3..c67a79e 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -2722,7 +2722,5 @@ unsafe fn wl_listener_remove_safe(listener: *mut ffi::wl_listener) {
 
 unsafe extern "C" fn handle_window_commit(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
     let window = crate::container_of!(listener, Window, commit);
-    if (*window).scale != 1.0 {
-        (*window).render_finish();
-    }
+    (*window).render_finish();
 }
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 81fac4a..6cf1129 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -1833,6 +1833,25 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                     format!("error: unknown input command: {}\n", key)
                 }
             }
+            "pointer-move-to" => {
+                if parts.len() < 3 { return "error: usage: pointer-move-to <x> <y>\n".to_string(); }
+                if let (Ok(x), Ok(y)) = (parts[1].parse::<f64>(), parts[2].parse::<f64>()) {
+                    let seats_list = &mut (*self.server).input_manager.seats as *mut ffi::wl_list as *mut WlList;
+                    let mut curr_seat = (*seats_list).next;
+                    while curr_seat != seats_list {
+                        let next_seat = (*curr_seat).next;
+                        let seat = crate::container_of!(curr_seat, crate::seat::Seat, link);
+                        let cursor = &mut (*seat).cursor;
+                        ffi::wlr_cursor_warp_absolute(cursor.wlr_cursor, std::ptr::null_mut(), x, y);
+                        cursor.update_hovered();
+                        cursor.passthrough(crate::util::msec_timestamp());
+                        curr_seat = next_seat;
+                    }
+                    "ok\n".to_string()
+                } else {
+                    "error: invalid x or y\n".to_string()
+                }
+            }
             _ => format!("error: unknown command: {}\n", action),
         }
     }