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

commit3adf4c7125f31015afd4f457a7e903e49eeae045
parent2c7ed2c707
authorLucas Galante <[email protected]>
date2026-06-27 11:27
fix: layer popups above pinned windows & add smooth 3-finger swipe zoom

 src/server/cursor.rs         | 43 ++++++++++++++++++++++++++++++++++++++++++-
 src/server/window_manager.rs |  9 ++++++---
 2 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 1d4e9cd..1946d71 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -1007,7 +1007,7 @@ unsafe extern "C" fn handle_axis(listener: *mut ffi::wl_listener, data: *mut std
                     wm.desk_pan_x += (cx - phys_x) * (1.0 / old_zoom - 1.0 / new_zoom);
                     wm.desk_pan_y += (cy - phys_y) * (1.0 / old_zoom - 1.0 / new_zoom);
                     wm.desk_zoom = new_zoom;
-                    wm.mode = if new_zoom < 0.999 { crate::window_manager::WindowManagerMode::Overview } else { crate::window_manager::WindowManagerMode::Normal };
+                    wm.mode = if (new_zoom - 1.0).abs() > 0.001 { crate::window_manager::WindowManagerMode::Overview } else { crate::window_manager::WindowManagerMode::Normal };
                     wm.dirty_windowing();
                 }
             }
@@ -1369,6 +1369,47 @@ unsafe extern "C" fn handle_swipe_update(listener: *mut ffi::wl_listener, data:
     let seat = &mut *cursor.seat;
     seat.handle_activity();
 
+    if (*event).fingers == 3 {
+        let wm = &mut (*seat.server).wm;
+        wm.stop_panning_animation();
+        
+        let dy = (*event).dy;
+        let zoom_factor = 1.005_f64.powf(-dy);
+        let old_zoom = wm.desk_zoom;
+        let new_zoom = (old_zoom * zoom_factor).clamp(0.1, 10.0);
+        
+        if new_zoom != old_zoom {
+            let cx = cursor.x();
+            let cy = cursor.y();
+            let wlr_output = (*(*seat).server).om.output_at(cx, cy);
+            let (phys_x, phys_y) = if !wlr_output.is_null() {
+                let mut output_box = ffi::wlr_box { x: 0, y: 0, width: 0, height: 0 };
+                ffi::wlr_output_layout_get_box((*(*seat).server).om.output_layout, wlr_output, &mut output_box);
+                (output_box.x as f64, output_box.y as f64)
+            } else {
+                (0.0, 0.0)
+            };
+            wm.desk_pan_x += (cx - phys_x) * (1.0 / old_zoom - 1.0 / new_zoom);
+            wm.desk_pan_y += (cy - phys_y) * (1.0 / old_zoom - 1.0 / new_zoom);
+            wm.desk_zoom = new_zoom;
+            wm.mode = if (new_zoom - 1.0).abs() > 0.001 { crate::window_manager::WindowManagerMode::Overview } else { crate::window_manager::WindowManagerMode::Normal };
+            wm.dirty_windowing();
+        }
+        
+        let server = seat.server;
+        let pointer_gestures = (*server).input_manager.pointer_gestures;
+        if !pointer_gestures.is_null() {
+            ffi::wlr_pointer_gestures_v1_send_swipe_update(
+                pointer_gestures,
+                seat.wlr_seat,
+                (*event).time_msec,
+                (*event).dx,
+                (*event).dy,
+            );
+        }
+        return;
+    }
+
     if cursor.gesture_triggered {
         return;
     }
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index c510d26..a1c3cf9 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -224,7 +224,7 @@ impl WindowManager {
                 self.desk_pan_x = state.desk_pan_x;
                 self.desk_pan_y = state.desk_pan_y;
                 self.desk_zoom = state.desk_zoom;
-                self.mode = if state.desk_zoom < 0.999 { WindowManagerMode::Overview } else { WindowManagerMode::Normal };
+                self.mode = if (state.desk_zoom - 1.0).abs() > 0.001 { WindowManagerMode::Overview } else { WindowManagerMode::Normal };
                 self.global_layout = state.global_layout;
                 self.restore_queue = state.windows;
                 self.has_restored_focused_window = self.restore_queue.iter().any(|w| w.focused);
@@ -688,6 +688,9 @@ impl WindowManager {
                                 ffi::wlr_scene_node_reparent((*window).tree as *mut _, (*self.server).scene.layers.fullscreen);
                                 ffi::wlr_scene_node_raise_to_top((*window).tree as *mut _);
                                 found_fullscreen = true;
+                            } else if (*window).tiling_mode == crate::tiling::TilingMode::Popup {
+                                ffi::wlr_scene_node_reparent((*window).tree as *mut _, (*self.server).scene.layers.popups);
+                                ffi::wlr_scene_node_raise_to_top((*window).tree as *mut _);
                             } else if (*window).rendering_requested.circular {
                                 ffi::wlr_scene_node_reparent((*window).tree as *mut _, (*self.server).scene.layers.top);
                                 ffi::wlr_scene_node_raise_to_top((*window).tree as *mut _);
@@ -1657,7 +1660,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                 self.desk_pan_x = cx - (viewport_w / 2.0) / new_zoom;
                 self.desk_pan_y = cy - (viewport_h / 2.0) / new_zoom;
                 self.desk_zoom = new_zoom;
-                self.mode = if new_zoom < 0.999 { WindowManagerMode::Overview } else { WindowManagerMode::Normal };
+                self.mode = if (new_zoom - 1.0).abs() > 0.001 { WindowManagerMode::Overview } else { WindowManagerMode::Normal };
                 self.dirty_windowing();
             }
             Action::PanLeft | Action::PanRight | Action::PanUp | Action::PanDown => {
@@ -1898,7 +1901,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                     self.desk_pan_x = cx - (viewport_w / 2.0) / new_zoom;
                     self.desk_pan_y = cy - (viewport_h / 2.0) / new_zoom;
                     self.desk_zoom = new_zoom;
-                    self.mode = if new_zoom < 0.999 { WindowManagerMode::Overview } else { WindowManagerMode::Normal };
+                    self.mode = if (new_zoom - 1.0).abs() > 0.001 { WindowManagerMode::Overview } else { WindowManagerMode::Normal };
                     self.dirty_windowing();
                     return "ok\n".to_string();
                 }