window management library
git clone https://git.lucas.co/cce-window-manager.git
camera: add pinch_zoom for continuous touchpad pinch
libinput reports pinch `scale` as absolute finger spread relative to the
gesture's begin, not a per-event delta — so the map is start_zoom * scale
off the zoom captured at pinch begin, clamped to the usual bounds.
Mapping off the *current* zoom would compound every update into runaway
growth. Consumed by the compositor's pinch handlers to zoom the desktop
camera when the gesture starts over the background.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/camera.rs | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/src/camera.rs b/src/camera.rs
index b57b843..117e977 100644
--- a/src/camera.rs
+++ b/src/camera.rs
@@ -47,6 +47,14 @@ pub fn wheel_zoom(zoom: f64, delta: f64) -> f64 {
(zoom * WHEEL_ZOOM_BASE.powf(-delta)).clamp(ZOOM_MIN, ZOOM_MAX)
}
+/// Continuous pinch zoom. libinput reports `scale` as the absolute finger
+/// spread relative to the gesture's begin (not a per-event delta), so the
+/// whole gesture maps off the zoom captured at pinch begin — never the
+/// current zoom, which would compound every update into runaway growth.
+pub fn pinch_zoom(start_zoom: f64, scale: f64) -> f64 {
+ (start_zoom * scale).clamp(ZOOM_MIN, ZOOM_MAX)
+}
+
/// Change zoom while keeping the virtual point under an output-local anchor
/// (`ax`, `ay` px from the output's top-left) fixed on screen — the wheel
/// zooms about the cursor, keyed zooms about the viewport center.
@@ -239,6 +247,18 @@ mod tests {
assert_eq!(keyed_zoom(3.7, 0.0), 1.0);
}
+ #[test]
+ fn pinch_zoom_maps_off_the_begin_zoom_and_clamps() {
+ // Absolute-scale semantics: spreading to 2x from zoom 1.5 lands on
+ // 3.0 no matter how many intermediate updates arrived.
+ assert_eq!(pinch_zoom(1.5, 2.0), 3.0);
+ assert_eq!(pinch_zoom(1.5, 1.0), 1.5); // begin-scale identity
+ assert_eq!(pinch_zoom(1.0, 0.5), 0.5);
+ // Clamped at both ends.
+ assert_eq!(pinch_zoom(8.0, 4.0), ZOOM_MAX);
+ assert_eq!(pinch_zoom(0.4, 0.1), ZOOM_MIN);
+ }
+
#[test]
fn zoom_about_anchor_pins_the_anchored_point() {
// Virtual point under the anchor before == after. Anchor (960, 540),