From 549865a41a8108b1cb6c2513f5b1e1ab6f58e065 Mon Sep 17 00:00:00 2001
From: pfg <pfg@pfg.pw>
Date: Thu, 11 Apr 2024 20:58:14 -0400
Subject: [PATCH] wayland support, needs review

---
 docs/news.md         |  7 ++---
 include/GLFW/glfw3.h |  4 +--
 src/CMakeLists.txt   |  1 +
 src/cocoa_window.m   |  4 ++-
 src/wl_init.c        | 16 +++++++++++
 src/wl_platform.h    |  6 ++++
 src/wl_window.c      | 65 ++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 96 insertions(+), 7 deletions(-)

diff --git a/docs/news.md b/docs/news.md
index c832fa16..7a422b14 100644
--- a/docs/news.md
+++ b/docs/news.md
@@ -14,12 +14,11 @@ values over 8. For compatibility with older versions, the
 @ref GLFW_UNLIMITED_MOUSE_BUTTONS input mode needs to be set to make use of
 this.
 
-### Support for trackpad zoom and rotate on macOS
+### Support for trackpad zoom and rotate on macOS and Wayland
 
-Trackpad zoom and rotate events are now supported on macOS using
+Trackpad zoom and rotate events are now supported on macOS and Wayland using
 [glfwSetTrackpadZoomCallback](@ref glfwSetTrackpadZoomCallback) and [glfwSetTrackpadRotateCallback](@ref glfwSetTrackpadRotateCallback). These
-events have not yet been implemented and currently will not emit anything on
-Windows, X11, and Wayland.
+events will not yet emit anything on Windows or X11.
 
 ## Caveats {#caveats}
 
diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h
index bebaf8e4..4152b648 100644
--- a/include/GLFW/glfw3.h
+++ b/include/GLFW/glfw3.h
@@ -1880,7 +1880,7 @@ typedef void (* GLFWscrollfun)(GLFWwindow* window, double xoffset, double yoffse
  *  @endcode
  *
  *  @param[in] window The window that received the event.
- *  @param[in] scale The manigification amount
+ *  @param[in] scale The manigification amount, as a scale factor
  *
  *  @sa @ref glfwSetTrackpadZoomCallback
  *
@@ -1897,7 +1897,7 @@ typedef void (* GLFWtrackpadzoomfun)(GLFWwindow* window, double scale);
  *  @endcode
  *
  *  @param[in] window The window that received the event.
- *  @param[in] angle The rotation amount
+ *  @param[in] angle The rotation amount, in degrees
  *
  *  @sa @ref glfwSetTrackpadRotateCallback
  *
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 463b898d..cbcc7b87 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -104,6 +104,7 @@ if (GLFW_BUILD_WAYLAND)
     generate_wayland_protocol("fractional-scale-v1.xml")
     generate_wayland_protocol("xdg-activation-v1.xml")
     generate_wayland_protocol("xdg-decoration-unstable-v1.xml")
+    generate_wayland_protocol("pointer-gestures-unstable-v1.xml")
 endif()
 
 if (WIN32 AND GLFW_BUILD_SHARED_LIBRARY)
diff --git a/src/cocoa_window.m b/src/cocoa_window.m
index 05cb2e51..ea84d9ab 100644
--- a/src/cocoa_window.m
+++ b/src/cocoa_window.m
@@ -618,8 +618,10 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
 {
     double magnification = [event magnification];
 
+    // 1.0 is added to convert the magnification value to a scale factor,
+    // as suggested in apple documentation
     if (fabs(magnification) > 0.0)
-        _glfwInputTrackpadZoom(window, magnification);
+        _glfwInputTrackpadZoom(window, magnification + 1.0);
 }
 
 - (void)rotateWithEvent:(NSEvent *)event
diff --git a/src/wl_init.c b/src/wl_init.c
index 76054bc6..3c63ad84 100644
--- a/src/wl_init.c
+++ b/src/wl_init.c
@@ -49,6 +49,7 @@
 #include "fractional-scale-v1-client-protocol.h"
 #include "xdg-activation-v1-client-protocol.h"
 #include "idle-inhibit-unstable-v1-client-protocol.h"
+#include "pointer-gestures-unstable-v1-client-protocol.h"
 
 // NOTE: Versions of wayland-scanner prior to 1.17.91 named every global array of
 //       wl_interface pointers 'types', making it impossible to combine several unmodified
@@ -91,6 +92,10 @@
 #include "idle-inhibit-unstable-v1-client-protocol-code.h"
 #undef types
 
+#define types _glfw_pointer_gestures_types
+#include "wayland-pointer-gestures-unstable-v1-client-protocol-code.h"
+#undef types
+
 static void wmBaseHandlePing(void* userData,
                              struct xdg_wm_base* wmBase,
                              uint32_t serial)
@@ -207,6 +212,13 @@ static void registryHandleGlobal(void* userData,
             wl_registry_bind(registry, name,
                              &wp_fractional_scale_manager_v1_interface,
                              1);
+    else if (strcmp(interface, "zwp_pointer_gestures_v1") == 0)
+    {
+        _glfw.wl.pointerGestures =
+            wl_registry_bind(registry, name,
+                             &zwp_pointer_gestures_v1_interface,
+                             1);
+        _glfwAddPointerGesturesListeners(_glfw.wl.pointerGestures);
     }
 }
 
@@ -984,6 +996,10 @@ void _glfwTerminateWayland(void)
         xdg_activation_v1_destroy(_glfw.wl.activationManager);
     if (_glfw.wl.fractionalScaleManager)
         wp_fractional_scale_manager_v1_destroy(_glfw.wl.fractionalScaleManager);
+    if (_glfw.wl.pinchGesture)
+        zwp_pointer_gesture_pinch_v1_destroy(_glfw.wl.pinchGesture);
+    if (_glfw.wl.pointerGestures)
+        zwp_pointer_gestures_v1_destroy(_glfw.wl.pointerGestures);
     if (_glfw.wl.registry)
         wl_registry_destroy(_glfw.wl.registry);
     if (_glfw.wl.display)
diff --git a/src/wl_platform.h b/src/wl_platform.h
index f3e8cba2..71da0913 100644
--- a/src/wl_platform.h
+++ b/src/wl_platform.h
@@ -117,6 +117,7 @@ struct wl_output;
 #define zwp_pointer_constraints_v1_interface _glfw_zwp_pointer_constraints_v1_interface
 #define zwp_relative_pointer_v1_interface _glfw_zwp_relative_pointer_v1_interface
 #define zwp_relative_pointer_manager_v1_interface _glfw_zwp_relative_pointer_manager_v1_interface
+#define zwp_pointer_gestures_v1_interface _glfw_zwp_pointer_gestures_v1_interface
 #define wp_viewport_interface _glfw_wp_viewport_interface
 #define wp_viewporter_interface _glfw_wp_viewporter_interface
 #define xdg_toplevel_interface _glfw_xdg_toplevel_interface
@@ -435,6 +436,8 @@ typedef struct _GLFWlibraryWayland
     struct zwp_idle_inhibit_manager_v1*     idleInhibitManager;
     struct xdg_activation_v1*               activationManager;
     struct wp_fractional_scale_manager_v1*  fractionalScaleManager;
+    struct zwp_pointer_gestures_v1*      pointerGestures;
+    struct zwp_pointer_gesture_pinch_v1* pinchGesture;
 
     _GLFWofferWayland*          offers;
     unsigned int                offerCount;
@@ -466,6 +469,8 @@ typedef struct _GLFWlibraryWayland
     short int                   scancodes[GLFW_KEY_LAST + 1];
     char                        keynames[GLFW_KEY_LAST + 1][5];
 
+    double                       pinchGesturePreviousScale;
+
     struct {
         void*                   handle;
         struct xkb_context*     context;
@@ -686,4 +691,5 @@ void _glfwUpdateBufferScaleFromOutputsWayland(_GLFWwindow* window);
 
 void _glfwAddSeatListenerWayland(struct wl_seat* seat);
 void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device);
+void _glfwAddPointerGesturesListeners(struct zwp_pointer_gestures_v1* pointer_gestures);
 
diff --git a/src/wl_window.c b/src/wl_window.c
index 2e842aaa..09493134 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -51,6 +51,7 @@
 #include "xdg-activation-v1-client-protocol.h"
 #include "idle-inhibit-unstable-v1-client-protocol.h"
 #include "fractional-scale-v1-client-protocol.h"
+#include "pointer-gestures-unstable-v1-client-protocol.h"
 
 #define GLFW_BORDER_SIZE    4
 #define GLFW_CAPTION_HEIGHT 24
@@ -1631,6 +1632,53 @@ static const struct wl_pointer_listener pointerListener =
     pointerHandleAxis,
 };
 
+static void pointerGesturesHandlePinchBegin(void *userData,
+                                            struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
+                                            uint32_t serial,
+                                            uint32_t time,
+                                            struct wl_surface *surface,
+                                            uint32_t fingers)
+{
+    _glfw.wl.pinchGesturePreviousScale = 1.0;
+}
+
+static void pointerGesturesHandlePinchMotion(void *userData,
+                                             struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
+                                             uint32_t time,
+		                                     wl_fixed_t dx,
+		                                     wl_fixed_t dy,
+		                                     wl_fixed_t scale,
+		                                     wl_fixed_t rotation)
+{
+    _GLFWwindow* window = _glfw.wl.pointerFocus;
+
+    double zoom_value = wl_fixed_to_double(scale);
+    double prev_zoom_value = _glfw.wl.pinchGesturePreviousScale;
+    double zoom_delta = zoom_value / prev_zoom_value;
+    _glfw.wl.pinchGesturePreviousScale = zoom_value;
+
+    double rotation_value = wl_fixed_to_double(rotation);
+
+    _glfwInputTrackpadZoom(window, zoom_delta);
+    _glfwInputTrackpadRotate(window, rotation_value);
+}
+
+static void pointerGesturesHandlePinchEnd(void *userData,
+                                          struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
+		                                  uint32_t serial,
+		                                  uint32_t time,
+		                                  int32_t cancelled)
+{
+    _glfw.wl.pinchGesturePreviousScale = 1.0;
+}
+
+static const struct zwp_pointer_gesture_pinch_v1_listener pinchGestureListener =
+{
+    pointerGesturesHandlePinchBegin,
+    pointerGesturesHandlePinchMotion,
+    pointerGesturesHandlePinchEnd,
+};
+
 static void keyboardHandleKeymap(void* userData,
                                  struct wl_keyboard* keyboard,
                                  uint32_t format,
@@ -1885,6 +1933,8 @@ static void seatHandleCapabilities(void* userData,
     {
         _glfw.wl.pointer = wl_seat_get_pointer(seat);
         wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL);
+
+        _glfwAddPointerGesturesListeners(_glfw.wl.pointerGestures);
     }
     else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer)
     {
@@ -2120,6 +2170,21 @@ void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device)
     wl_data_device_add_listener(device, &dataDeviceListener, NULL);
 }
 
+void _glfwAddPointerGesturesListeners(struct zwp_pointer_gestures_v1* pointer_gestures)
+{
+    if (_glfw.wl.pinchGesture) return;
+    if (!_glfw.wl.pointer) return;
+
+    _glfw.wl.pinchGesture =
+        zwp_pointer_gestures_v1_get_pinch_gesture(
+            pointer_gestures,
+            _glfw.wl.pointer);
+    zwp_pointer_gesture_pinch_v1_add_listener(_glfw.wl.pinchGesture,
+                                              &pinchGestureListener,
+                                              NULL);
+    // zwp_pointer_gestures_v1
+}
+
 
 //////////////////////////////////////////////////////////////////////////
 //////                       GLFW platform API                      //////