[Bf-blender-cvs] [8bb211a7716] master: GHOST/Wayland: multi-touch gesture support

Campbell Barton noreply at git.blender.org
Sat Oct 22 09:02:26 CEST 2022


Commit: 8bb211a771642144ac75ba6be0c1897c836a6276
Author: Campbell Barton
Date:   Sat Oct 22 16:49:09 2022 +1100
Branches: master
https://developer.blender.org/rB8bb211a771642144ac75ba6be0c1897c836a6276

GHOST/Wayland: multi-touch gesture support

Add support for zoom & rotate gestures, hold and swipe may
be used in the future although swipe maps to 2D smooth-scroll for
Gnome & KDE.

Tested to work with Apple track-pad & Wacom tablet on Gnome & KDE.

===================================================================

M	intern/ghost/CMakeLists.txt
M	intern/ghost/intern/GHOST_SystemWayland.cpp
M	intern/ghost/intern/GHOST_SystemWayland.h

===================================================================

diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index c05f2a327b1..fb10530bfae 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -372,6 +372,10 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
     generate_protocol_bindings(
       "${WAYLAND_PROTOCOLS_DIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
     )
+    # Pointer-gestures (multi-touch).
+    generate_protocol_bindings(
+      "${WAYLAND_PROTOCOLS_DIR}/unstable/pointer-gestures/pointer-gestures-unstable-v1.xml"
+    )
     # Tablet.
     generate_protocol_bindings(
       "${WAYLAND_PROTOCOLS_DIR}/unstable/tablet/tablet-unstable-v2.xml"
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 39f392e62b9..442e51d4f7c 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -51,6 +51,7 @@
 
 /* Generated by `wayland-scanner`. */
 #include <pointer-constraints-unstable-v1-client-protocol.h>
+#include <pointer-gestures-unstable-v1-client-protocol.h>
 #include <primary-selection-unstable-v1-client-protocol.h>
 #include <relative-pointer-unstable-v1-client-protocol.h>
 #include <tablet-unstable-v2-client-protocol.h>
@@ -461,6 +462,39 @@ struct GWL_SeatStatePointerScroll {
   enum wl_pointer_axis_source axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
 };
 
+/**
+ * Utility struct to access rounded values from a scaled `wl_fixed_t`,
+ * without loosing information.
+ *
+ * As the rounded result  is rounded to a lower precision integer,
+ * the high precision value is accumulated and converted to an integer to
+ * prevent the accumulation of rounded values giving an inaccurate result.
+ *
+ * \note This is simple but doesn't read well when expanded multiple times inline.
+ */
+struct GWL_ScaledFixedT {
+  wl_fixed_t value = 0;
+  wl_fixed_t factor = 1;
+};
+
+static int gwl_scaled_fixed_t_add_and_calc_rounded_delta(GWL_ScaledFixedT *sf,
+                                                         const wl_fixed_t add)
+{
+  const int result_prev = wl_fixed_to_int(sf->value * sf->factor);
+  sf->value += add;
+  const int result_curr = wl_fixed_to_int(sf->value * sf->factor);
+  return result_curr - result_prev;
+}
+
+/**
+ * Gesture state.
+ * This is needed so the gesture values can be converted to deltas.
+ */
+struct GWL_SeatStatePointerGesture_Pinch {
+  GWL_ScaledFixedT scale;
+  GWL_ScaledFixedT rotation;
+};
+
 /**
  * State of the keyboard (in #GWL_Seat).
  */
@@ -570,6 +604,10 @@ struct GWL_Seat {
   struct wl_keyboard *wl_keyboard = nullptr;
   struct zwp_tablet_seat_v2 *wp_tablet_seat = nullptr;
 
+  struct zwp_pointer_gesture_hold_v1 *wp_pointer_gesture_hold = nullptr;
+  struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch = nullptr;
+  struct zwp_pointer_gesture_swipe_v1 *wp_pointer_gesture_swipe = nullptr;
+
   /** All currently active tablet tools (needed for changing the cursor). */
   std::unordered_set<zwp_tablet_tool_v2 *> tablet_tools;
 
@@ -578,6 +616,7 @@ struct GWL_Seat {
 
   GWL_SeatStatePointer pointer;
   GWL_SeatStatePointerScroll pointer_scroll;
+  GWL_SeatStatePointerGesture_Pinch pointer_gesture_pinch;
 
   /** Mostly this can be interchanged with `pointer` however it can't be locked/confined. */
   GWL_SeatStatePointer tablet;
@@ -683,6 +722,7 @@ struct GWL_Display {
   struct zwp_tablet_manager_v2 *wp_tablet_manager = nullptr;
   struct zwp_relative_pointer_manager_v1 *wp_relative_pointer_manager = nullptr;
   struct zwp_pointer_constraints_v1 *wp_pointer_constraints = nullptr;
+  struct zwp_pointer_gestures_v1 *wp_pointer_gestures = nullptr;
 
   struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_device_manager = nullptr;
 
@@ -844,8 +884,8 @@ static void display_destroy(GWL_Display *display)
     zwp_pointer_constraints_v1_destroy(display->wp_pointer_constraints);
   }
 
-  if (display->wp_primary_selection_device_manager) {
-    zwp_primary_selection_device_manager_v1_destroy(display->wp_primary_selection_device_manager);
+  if (display->wp_pointer_gestures) {
+    zwp_pointer_gestures_v1_destroy(display->wp_pointer_gestures);
   }
 
   if (display->wl_compositor) {
@@ -2269,6 +2309,223 @@ static const struct wl_pointer_listener pointer_listener = {
 
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \name Listener (Pointer Gesture: Hold), #zwp_pointer_gesture_hold_v1_listener
+ * \{ */
+
+static CLG_LogRef LOG_WL_POINTER_GESTURE_HOLD = {"ghost.wl.handle.pointer_gesture.hold"};
+#define LOG (&LOG_WL_POINTER_GESTURE_HOLD)
+
+static void gesture_hold_handle_begin(
+    void * /*data*/,
+    struct zwp_pointer_gesture_hold_v1 * /*zwp_pointer_gesture_hold_v1*/,
+    uint32_t /*serial*/,
+    uint32_t /*time*/,
+    struct wl_surface * /*surface*/,
+    uint32_t fingers)
+{
+  CLOG_INFO(LOG, 2, "begin (fingers=%u)", fingers);
+}
+
+static void gesture_hold_handle_end(
+    void * /*data*/,
+    struct zwp_pointer_gesture_hold_v1 * /*zwp_pointer_gesture_hold_v1*/,
+    uint32_t /*serial*/,
+    uint32_t /*time*/,
+    int32_t cancelled)
+{
+  CLOG_INFO(LOG, 2, "end (cancelled=%i)", cancelled);
+}
+
+static const struct zwp_pointer_gesture_hold_v1_listener gesture_hold_listener = {
+    gesture_hold_handle_begin,
+    gesture_hold_handle_end,
+};
+
+#undef LOG
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Pointer Gesture: Pinch), #zwp_pointer_gesture_pinch_v1_listener
+ * \{ */
+
+static CLG_LogRef LOG_WL_POINTER_GESTURE_PINCH = {"ghost.wl.handle.pointer_gesture.pinch"};
+#define LOG (&LOG_WL_POINTER_GESTURE_PINCH)
+
+static void gesture_pinch_handle_begin(void *data,
+                                       struct zwp_pointer_gesture_pinch_v1 * /*pinch*/,
+                                       uint32_t /*serial*/,
+                                       uint32_t /*time*/,
+                                       struct wl_surface * /*surface*/,
+                                       uint32_t fingers)
+{
+  CLOG_INFO(LOG, 2, "begin (fingers=%u)", fingers);
+  GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+  /* Reset defaults. */
+  seat->pointer_gesture_pinch = GWL_SeatStatePointerGesture_Pinch{};
+
+  GHOST_WindowWayland *win = nullptr;
+  if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
+    win = ghost_wl_surface_user_data(wl_surface_focus);
+  }
+  const wl_fixed_t win_scale = win ? win->scale() : 1;
+
+  /* NOTE(@campbellbarton): Scale factors match Blender's operators & default preferences.
+   * For these values to work correctly, operator logic will need to be changed not to scale input
+   * by the region size (as with 3D view zoom) or preference for 3D view orbit sensitivity.
+   *
+   * By working "correctly" I mean that a rotation action where the users fingers rotate to
+   * opposite locations should always rotate the viewport 180d, since users will expect the
+   * physical location of their fingers to match the viewport.
+   * Similarly with zoom, the scale value from the pinch action can be mapped to a zoom level
+   * although unlike rotation, an inexact mapping is less noticeable.
+   * Users may even prefer the zoom level to be scaled - which could be a preference. */
+  seat->pointer_gesture_pinch.scale.value = wl_fixed_from_int(1);
+  /* The value 300 matches a value used in clip & image zoom operators.
+   * It seems OK for the 3D view too. */
+  seat->pointer_gesture_pinch.scale.factor = 300 * win_scale;
+  /* The value 5 is used on macOS and roughly maps 1:1 with turntable rotation,
+   * although preferences can scale the sensitivity (which would be skipped ideally). */
+  seat->pointer_gesture_pinch.rotation.factor = 5 * win_scale;
+}
+
+static void gesture_pinch_handle_update(void *data,
+                                        struct zwp_pointer_gesture_pinch_v1 * /*pinch*/,
+                                        uint32_t /*time*/,
+                                        wl_fixed_t dx,
+                                        wl_fixed_t dy,
+                                        wl_fixed_t scale,
+                                        wl_fixed_t rotation)
+{
+  CLOG_INFO(LOG,
+            2,
+            "update (dx=%.3f, dy=%.3f, scale=%.3f, rotation=%.3f)",
+            wl_fixed_to_double(dx),
+            wl_fixed_to_double(dy),
+            wl_fixed_to_double(scale),
+            wl_fixed_to_double(rotation));
+
+  GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+
+  GHOST_WindowWayland *win = nullptr;
+
+  if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
+    win = ghost_wl_surface_user_data(wl_surface_focus);
+  }
+
+  /* Scale defaults to `wl_fixed_from_int(1)` which may change while pinching.
+   * This needs to be converted to a delta. */
+  const wl_fixed_t scale_delta = scale - seat->pointer_gesture_pinch.scale.value;
+  const int scale_as_delta_px = gwl_scaled_fixed_t_add_and_calc_rounded_delta(
+      &seat->pointer_gesture_pinch.scale, scale_delta);
+
+  /* Rotation in degrees, unlike scale this is a delta. */
+  const int rotation_as_delta_px = gwl_scaled_fixed_t_add_and_calc_rounded_delta(
+      &seat->pointer_gesture_pinch.rotation, rotation);
+
+  if (win) {
+    const wl_fixed_t win_scale = win->scale();
+    const int32_t event_xy[2] = {
+        wl_fixed_to_int(win_scale * seat->pointer.xy[0]),
+        wl_fixed_to_int(win_scale * seat->pointer.xy[1]),
+    };
+    if (scale_as_delta_px) {
+      seat->system->pushEvent(new GHOST_EventTrackpad(seat->system->getMilliSeconds(),
+                                                      win,
+                                                      GHOST_kTrackpadEventMagnify,
+                                                      event_xy[0],
+                                                      event_xy[1],
+                                                      scale_as_delta_px,
+                                                      0,
+                                                      false));
+    }
+
+    if (rotation_as_delta_px) {
+      seat->system->pushEvent(new GHOST_EventTrackpad(seat->system->getMilliSeconds(),
+           

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list