[gtk/wip/nacho/macos-pen-input] Support for macOS Pen / Eraser input #1551



commit 0c2d9da86d2fb72d81ac127713ee7e4790d98618
Author: Andreas Butti <andreas butti gmail com>
Date:   Sun Jan 27 20:16:33 2019 +0100

    Support for macOS Pen / Eraser input #1551
    
    This fix is based on this patch: https://bugzilla.gnome.org/show_bug.cgi?id=695701

 gdk/quartz/gdkdevice-core-quartz.c        |  37 ++++++
 gdk/quartz/gdkdevicemanager-core-quartz.c | 184 +++++++++++++++++++++++++++++-
 gdk/quartz/gdkdevicemanager-core-quartz.h |  10 ++
 gdk/quartz/gdkevents-quartz.c             |  82 +++++++++++--
 gdk/quartz/gdkinternal-quartz.h           |  23 ++++
 5 files changed, 325 insertions(+), 11 deletions(-)
---
diff --git a/gdk/quartz/gdkdevice-core-quartz.c b/gdk/quartz/gdkdevice-core-quartz.c
index 788cbe4b04..a8fd34f224 100644
--- a/gdk/quartz/gdkdevice-core-quartz.c
+++ b/gdk/quartz/gdkdevice-core-quartz.c
@@ -31,6 +31,10 @@
 struct _GdkQuartzDeviceCore
 {
   GdkDevice parent_instance;
+
+  gboolean active;
+  NSUInteger device_id;
+  unsigned long long unique_id;
 };
 
 struct _GdkQuartzDeviceCoreClass
@@ -364,3 +368,36 @@ gdk_quartz_device_core_select_window_events (GdkDevice    *device,
 {
   /* The mask is set in the common code. */
 }
+
+void
+_gdk_quartz_device_core_set_active (GdkDevice  *device,
+                                    gboolean    active,
+                                    NSUInteger  device_id)
+{
+  GdkQuartzDeviceCore *self = GDK_QUARTZ_DEVICE_CORE (device);
+
+  self->active = active;
+  self->device_id = device_id;
+}
+
+gboolean
+_gdk_quartz_device_core_is_active (GdkDevice  *device,
+                                   NSUInteger  device_id)
+{
+  GdkQuartzDeviceCore *self = GDK_QUARTZ_DEVICE_CORE (device);
+
+  return (self->active && self->device_id == device_id);
+}
+
+void
+_gdk_quartz_device_core_set_unique (GdkDevice          *device,
+                                    unsigned long long  unique_id)
+{
+  GDK_QUARTZ_DEVICE_CORE (device)->unique_id = unique_id;
+}
+
+unsigned long long
+_gdk_quartz_device_core_get_unique (GdkDevice *device)
+{
+  return GDK_QUARTZ_DEVICE_CORE (device)->unique_id;
+}
diff --git a/gdk/quartz/gdkdevicemanager-core-quartz.c b/gdk/quartz/gdkdevicemanager-core-quartz.c
index d4756863c5..2d08e7f400 100644
--- a/gdk/quartz/gdkdevicemanager-core-quartz.c
+++ b/gdk/quartz/gdkdevicemanager-core-quartz.c
@@ -27,7 +27,20 @@
 #include "gdkquartzdevice-core.h"
 #include "gdkkeysyms.h"
 #include "gdkprivate-quartz.h"
+#include "gdkinternal-quartz.h"
 
+typedef enum
+{
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
+  GDK_QUARTZ_POINTER_DEVICE_TYPE_CURSOR = NSCursorPointingDevice,
+  GDK_QUARTZ_POINTER_DEVICE_TYPE_ERASER = NSEraserPointingDevice,
+  GDK_QUARTZ_POINTER_DEVICE_TYPE_PEN = NSPenPointingDevice,
+#else
+  GDK_QUARTZ_POINTER_DEVICE_TYPE_CURSOR = NSPointingDeviceTypeCursor,
+  GDK_QUARTZ_POINTER_DEVICE_TYPE_ERASER = NSPointingDeviceTypeEraser,
+  GDK_QUARTZ_POINTER_DEVICE_TYPE_PEN = NSPointingDeviceTypePen,
+#endif
+} GdkQuartzPointerDeviceType;
 
 #define HAS_FOCUS(toplevel)                           \
   ((toplevel)->has_focus || (toplevel)->has_pointer_focus)
@@ -87,6 +100,7 @@ create_core_keyboard (GdkDeviceManager *device_manager,
 static void
 gdk_quartz_device_manager_core_init (GdkQuartzDeviceManagerCore *device_manager)
 {
+  device_manager->known_tablet_devices = NULL;
 }
 
 static void
@@ -99,6 +113,8 @@ gdk_quartz_device_manager_core_finalize (GObject *object)
   g_object_unref (quartz_device_manager_core->core_pointer);
   g_object_unref (quartz_device_manager_core->core_keyboard);
 
+  g_list_free_full (quartz_device_manager_core->known_tablet_devices, g_object_unref);
+
   G_OBJECT_CLASS (gdk_quartz_device_manager_core_parent_class)->finalize (object);
 }
 
@@ -127,16 +143,25 @@ static GList *
 gdk_quartz_device_manager_core_list_devices (GdkDeviceManager *device_manager,
                                              GdkDeviceType     type)
 {
-  GdkQuartzDeviceManagerCore *quartz_device_manager_core;
+  GdkQuartzDeviceManagerCore *self;
   GList *devices = NULL;
+  GList *l;
+
+  self = GDK_QUARTZ_DEVICE_MANAGER_CORE (device_manager);
 
   if (type == GDK_DEVICE_TYPE_MASTER)
     {
-      quartz_device_manager_core = (GdkQuartzDeviceManagerCore *) device_manager;
-      devices = g_list_prepend (devices, quartz_device_manager_core->core_keyboard);
-      devices = g_list_prepend (devices, quartz_device_manager_core->core_pointer);
+      devices = g_list_prepend (devices, self->core_keyboard);
+      devices = g_list_prepend (devices, self->core_pointer);
+    }
+
+  for (l = self->known_tablet_devices; l; l = g_list_next (l))
+    {
+      devices = g_list_prepend (devices, GDK_DEVICE (l->data));
     }
 
+  devices = g_list_reverse (devices);
+
   return devices;
 }
 
@@ -148,3 +173,154 @@ gdk_quartz_device_manager_core_get_client_pointer (GdkDeviceManager *device_mana
   quartz_device_manager_core = (GdkQuartzDeviceManagerCore *) device_manager;
   return quartz_device_manager_core->core_pointer;
 }
+
+static GdkDevice *
+create_core_device (GdkDeviceManager *device_manager,
+                    const gchar      *device_name,
+                    GdkInputSource    source)
+{
+  GdkDisplay *display = gdk_device_manager_get_display (device_manager);
+  GdkDevice *device = g_object_new (GDK_TYPE_QUARTZ_DEVICE_CORE,
+                                    "name", device_name,
+                                    "type", GDK_DEVICE_TYPE_SLAVE,
+                                    "input-source", source,
+                                    "input-mode", GDK_MODE_DISABLED,
+                                    "has-cursor", FALSE,
+                                    "display", display,
+                                    "device-manager", device_manager,
+                                    NULL);
+
+  _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_PRESSURE, 0.0, 1.0, 0.001);
+  _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_XTILT, -1.0, 1.0, 0.001);
+  _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_YTILT, -1.0, 1.0, 0.001);
+
+  return device;
+}
+
+void
+_gdk_quartz_device_manager_register_device_for_ns_event (GdkDeviceManager *device_manager,
+                                                         NSEvent          *nsevent)
+{
+  GdkQuartzDeviceManagerCore *self = GDK_QUARTZ_DEVICE_MANAGER_CORE (device_manager);
+  GList *l = NULL;
+  GdkInputSource input_source = GDK_SOURCE_MOUSE;
+  GdkDevice *device = NULL;
+
+  /* Only handle device updates for proximity events */
+  if ([nsevent type] != GDK_QUARTZ_EVENT_TABLET_PROXIMITY &&
+      [nsevent subtype] != GDK_QUARTZ_EVENT_SUBTYPE_TABLET_PROXIMITY)
+    return;
+
+  if ([nsevent pointingDeviceType] == GDK_QUARTZ_POINTER_DEVICE_TYPE_PEN)
+    input_source = GDK_SOURCE_PEN;
+  else if ([nsevent pointingDeviceType] == GDK_QUARTZ_POINTER_DEVICE_TYPE_CURSOR)
+    input_source = GDK_SOURCE_CURSOR;
+  else if ([nsevent pointingDeviceType] == GDK_QUARTZ_POINTER_DEVICE_TYPE_ERASER)
+    input_source = GDK_SOURCE_ERASER;
+
+  for (l = self->known_tablet_devices; l; l = g_list_next (l))
+    {
+      GdkDevice *device_to_check = GDK_DEVICE (l->data);
+
+      if (input_source == gdk_device_get_source (device_to_check) &&
+          [nsevent uniqueID] == _gdk_quartz_device_core_get_unique (device_to_check))
+        {
+          device = device_to_check;
+          if ([nsevent isEnteringProximity])
+            {
+              if (!_gdk_quartz_device_core_is_active (device, [nsevent deviceID]))
+                self->num_active_devices++;
+
+              _gdk_quartz_device_core_set_active (device, TRUE, [nsevent deviceID]);
+            }
+          else
+            {
+              if (_gdk_quartz_device_core_is_active (device, [nsevent deviceID]))
+                self->num_active_devices--;
+
+              _gdk_quartz_device_core_set_active (device, FALSE, [nsevent deviceID]);
+            }
+        }
+    }
+
+  /* If we haven't seen this device before, add it */
+  if (!device)
+    {
+      GdkSeat *seat;
+
+      switch (input_source)
+        {
+        case GDK_SOURCE_PEN:
+          device = create_core_device (device_manager,
+                                        "Quartz Pen",
+                                        GDK_SOURCE_PEN);
+          break;
+        case GDK_SOURCE_CURSOR:
+          device = create_core_device (device_manager,
+                                        "Quartz Cursor",
+                                        GDK_SOURCE_CURSOR);
+          break;
+        case GDK_SOURCE_ERASER:
+          device = create_core_device (device_manager,
+                                        "Quartz Eraser",
+                                        GDK_SOURCE_ERASER);
+          break;
+        default:
+          g_warning ("GDK Quarz unknown input source: %i", input_source);
+          break;
+        }
+
+      _gdk_device_set_associated_device (GDK_DEVICE (device), self->core_pointer);
+      _gdk_device_add_slave (self->core_pointer, GDK_DEVICE (device));
+
+      seat = gdk_device_get_seat (self->core_pointer);
+      gdk_seat_default_add_slave (GDK_SEAT_DEFAULT (seat), device);
+
+      _gdk_quartz_device_core_set_unique (device, [nsevent uniqueID]);
+      _gdk_quartz_device_core_set_active (device, TRUE, [nsevent deviceID]);
+
+      self->known_tablet_devices = g_list_append (self->known_tablet_devices,
+                                                  device);
+
+      if ([nsevent isEnteringProximity])
+        {
+          if (!_gdk_quartz_device_core_is_active (device, [nsevent deviceID]))
+            self->num_active_devices++;
+          _gdk_quartz_device_core_set_active (device, TRUE, [nsevent deviceID]);
+        }
+    }
+
+  if (self->num_active_devices)
+    [NSEvent setMouseCoalescingEnabled: FALSE];
+  else
+    [NSEvent setMouseCoalescingEnabled: TRUE];
+}
+
+GdkDevice *
+_gdk_quartz_device_manager_core_device_for_ns_event (GdkDeviceManager *device_manager,
+                                                     NSEvent          *nsevent)
+{
+  GdkQuartzDeviceManagerCore *self = GDK_QUARTZ_DEVICE_MANAGER_CORE (device_manager);
+  GdkDevice *device = NULL;
+
+  if ([nsevent type] == GDK_QUARTZ_EVENT_TABLET_PROXIMITY ||
+      [nsevent subtype] == GDK_QUARTZ_EVENT_SUBTYPE_TABLET_PROXIMITY ||
+      [nsevent subtype] == GDK_QUARTZ_EVENT_SUBTYPE_TABLET_POINT)
+    {
+      /* Find the device based on deviceID */
+      GList *l = NULL;
+
+      for (l = self->known_tablet_devices; l && !device; l = g_list_next (l))
+        {
+          GdkDevice *device_to_check = GDK_DEVICE (l->data);
+
+          if (_gdk_quartz_device_core_is_active (device_to_check, [nsevent deviceID]))
+            device = device_to_check;
+        }
+    }
+
+  if (!device)
+    device = self->core_pointer;
+
+  return device;
+}
diff --git a/gdk/quartz/gdkdevicemanager-core-quartz.h b/gdk/quartz/gdkdevicemanager-core-quartz.h
index db054ad685..d8cf2d4443 100644
--- a/gdk/quartz/gdkdevicemanager-core-quartz.h
+++ b/gdk/quartz/gdkdevicemanager-core-quartz.h
@@ -23,6 +23,8 @@
 #include <gdkdevicemanagerprivate.h>
 #include "gdkquartzdevicemanager-core.h"
 
+#import <Cocoa/Cocoa.h>
+
 G_BEGIN_DECLS
 
 struct _GdkQuartzDeviceManagerCore
@@ -30,6 +32,8 @@ struct _GdkQuartzDeviceManagerCore
   GdkDeviceManager parent_object;
   GdkDevice *core_pointer;
   GdkDevice *core_keyboard;
+  GList *known_tablet_devices;
+  guint num_active_devices;
 };
 
 struct _GdkQuartzDeviceManagerCoreClass
@@ -37,6 +41,12 @@ struct _GdkQuartzDeviceManagerCoreClass
   GdkDeviceManagerClass parent_class;
 };
 
+void       _gdk_quartz_device_manager_register_device_for_ns_event (GdkDeviceManager *device_manager,
+                                                                    NSEvent          *nsevent);
+
+GdkDevice *_gdk_quartz_device_manager_core_device_for_ns_event (GdkDeviceManager *device_manager,
+                                                                NSEvent          *ns_event);
+
 G_END_DECLS
 
 #endif /* __GDK_QUARTZ_DEVICE_MANAGER__ */
diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
index b0e6c0549e..83bda5b8a5 100644
--- a/gdk/quartz/gdkevents-quartz.c
+++ b/gdk/quartz/gdkevents-quartz.c
@@ -42,6 +42,7 @@
 #define GRIP_WIDTH 15
 #define GRIP_HEIGHT 15
 #define GDK_LION_RESIZE 5
+#define TABLET_AXES 5
 
 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060
 #define NSEventTypeRotate 13
@@ -173,7 +174,7 @@ gdk_event_apply_filters (NSEvent *nsevent,
 {
   GList *tmp_list;
   GdkFilterReturn result;
-  
+
   tmp_list = *filters;
 
   while (tmp_list)
@@ -408,6 +409,29 @@ is_mouse_button_press_event (NSEventType type)
   return FALSE;
 }
 
+static gboolean
+is_mouse_event (NSEventType type)
+{
+  switch (type)
+    {
+    case GDK_QUARTZ_LEFT_MOUSE_DOWN:
+    case GDK_QUARTZ_RIGHT_MOUSE_DOWN:
+    case GDK_QUARTZ_OTHER_MOUSE_DOWN:
+    case GDK_QUARTZ_LEFT_MOUSE_UP:
+    case GDK_QUARTZ_RIGHT_MOUSE_UP:
+    case GDK_QUARTZ_OTHER_MOUSE_UP:
+    case GDK_QUARTZ_MOUSE_MOVED:
+    case GDK_QUARTZ_LEFT_MOUSE_DRAGGED:
+    case GDK_QUARTZ_RIGHT_MOUSE_DRAGGED:
+    case GDK_QUARTZ_OTHER_MOUSE_DRAGGED:
+    case GDK_QUARTZ_MOUSE_ENTERED:
+    case GDK_QUARTZ_MOUSE_EXITED:
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 static GdkWindow *
 get_toplevel_from_ns_event (NSEvent *nsevent,
                             NSPoint *screen_point,
@@ -557,7 +581,7 @@ generate_motion_event (GdkWindow *window)
   event->motion.state = _gdk_quartz_events_get_current_keyboard_modifiers () |
                         _gdk_quartz_events_get_current_mouse_modifiers ();
   event->motion.is_hint = FALSE;
-  event->motion.device = gdk_seat_get_pointer (seat);
+  gdk_event_set_device (event, gdk_seat_get_pointer (seat));
   gdk_event_set_seat (event, seat);
 
   append_event (event, TRUE);
@@ -1019,6 +1043,8 @@ fill_button_event (GdkWindow *window,
                    gint       y_root)
 {
   GdkEventType type;
+  GdkDevice *event_device = NULL;
+  gdouble *axes = NULL;
   gint state;
   GdkSeat *seat = gdk_display_get_default_seat (_gdk_display);
 
@@ -1045,6 +1071,20 @@ fill_button_event (GdkWindow *window,
       g_assert_not_reached ();
     }
 
+  event_device = _gdk_quartz_device_manager_core_device_for_ns_event (gdk_display_get_device_manager 
(_gdk_display),
+                                                                      nsevent);
+
+  if ([nsevent subtype] == GDK_QUARTZ_EVENT_SUBTYPE_TABLET_POINT)
+    {
+      axes = g_new (gdouble, TABLET_AXES);
+
+      axes[0] = x;
+      axes[1] = y;
+      axes[2] = [nsevent pressure];
+      axes[3] = [nsevent tilt].x;
+      axes[4] = [nsevent tilt].y;
+    }
+
   event->any.type = type;
   event->button.window = window;
   event->button.time = get_time_from_ns_event (nsevent);
@@ -1052,11 +1092,12 @@ fill_button_event (GdkWindow *window,
   event->button.y = y;
   event->button.x_root = x_root;
   event->button.y_root = y_root;
-  /* FIXME event->axes */
+  event->button.axes = axes;
   event->button.state = state;
   event->button.button = get_mouse_button_from_ns_event (nsevent);
 
-  event->button.device = gdk_seat_get_pointer (seat);
+  gdk_event_set_device (event, gdk_seat_get_pointer (seat));
+  gdk_event_set_source_device (event, event_device);
   gdk_event_set_seat (event, seat);
 }
 
@@ -1070,6 +1111,22 @@ fill_motion_event (GdkWindow *window,
                    gint       y_root)
 {
   GdkSeat *seat = gdk_display_get_default_seat (_gdk_display);
+  GdkDevice *event_device = NULL;
+  gdouble *axes = NULL;
+
+  event_device = _gdk_quartz_device_manager_core_device_for_ns_event (gdk_display_get_device_manager 
(_gdk_display),
+                                                                      nsevent);
+
+  if ([nsevent subtype] == GDK_QUARTZ_EVENT_SUBTYPE_TABLET_POINT)
+    {
+      axes = g_new (gdouble, TABLET_AXES);
+
+      axes[0] = x;
+      axes[1] = y;
+      axes[2] = [nsevent pressure];
+      axes[3] = [nsevent tilt].x;
+      axes[4] = [nsevent tilt].y;
+    }
 
   event->any.type = GDK_MOTION_NOTIFY;
   event->motion.window = window;
@@ -1078,11 +1135,13 @@ fill_motion_event (GdkWindow *window,
   event->motion.y = y;
   event->motion.x_root = x_root;
   event->motion.y_root = y_root;
-  /* FIXME event->axes */
+  event->motion.axes = axes;
   event->motion.state = get_keyboard_modifiers_from_ns_event (nsevent) |
                         _gdk_quartz_events_get_current_mouse_modifiers ();
   event->motion.is_hint = FALSE;
-  event->motion.device = gdk_seat_get_pointer (seat);
+  gdk_event_set_device (event, gdk_seat_get_pointer (seat));
+  gdk_event_set_source_device (event, event_device);
+
   gdk_event_set_seat (event, seat);
 }
 
@@ -1112,9 +1171,9 @@ fill_scroll_event (GdkWindow          *window,
   event->scroll.y_root = y_root;
   event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent);
   event->scroll.direction = direction;
-  event->scroll.device = gdk_seat_get_pointer (seat);
   event->scroll.delta_x = delta_x;
   event->scroll.delta_y = delta_y;
+  gdk_event_set_device (event, gdk_seat_get_pointer (seat));
   gdk_event_set_seat (event, seat);
 }
 
@@ -1470,6 +1529,15 @@ gdk_event_translate (GdkEvent *event,
         }
     }
 
+  /* We need to register the proximity event from any point on the screen
+   * to properly register the devices
+   */
+  if (event_type == GDK_QUARTZ_EVENT_TABLET_PROXIMITY)
+    {
+      _gdk_quartz_device_manager_register_device_for_ns_event (gdk_display_get_device_manager (_gdk_display),
+                                                               nsevent);
+    }
+
   nswindow = [nsevent window];
 
   /* Ignore events for windows not created by GDK. */
diff --git a/gdk/quartz/gdkinternal-quartz.h b/gdk/quartz/gdkinternal-quartz.h
index 2f47e0a71f..e40ae492c7 100644
--- a/gdk/quartz/gdkinternal-quartz.h
+++ b/gdk/quartz/gdkinternal-quartz.h
@@ -75,6 +75,16 @@ typedef enum {
   GDK_QUARTZ_EVENT_SUBTYPE_EVENTLOOP
 } GdkQuartzEventSubType;
 
+#if MACOS_X_VERSION_MIN_SUPPORTED >= GDK_OSX_SIERRA
+#define GDK_QUARTZ_EVENT_TABLET_PROXIMITY NSEventTypeTabletProximity
+#define GDK_QUARTZ_EVENT_SUBTYPE_TABLET_PROXIMITY NSEventSubtypeTabletProximity
+#define GDK_QUARTZ_EVENT_SUBTYPE_TABLET_POINT NSEventSubtypeTabletPoint
+#else
+#define GDK_QUARTZ_EVENT_TABLET_PROXIMITY NSTabletProximity
+#define GDK_QUARTZ_EVENT_SUBTYPE_TABLET_PROXIMITY NSTabletProximityEventSubtype
+#define GDK_QUARTZ_EVENT_SUBTYPE_TABLET_POINT NSTabletPointEventSubtype
+#endif
+
 void         _gdk_quartz_events_update_focus_window    (GdkWindow *new_window,
                                                         gboolean   got_focus);
 void         _gdk_quartz_events_send_map_event         (GdkWindow *window);
@@ -84,6 +94,19 @@ GdkModifierType _gdk_quartz_events_get_current_mouse_modifiers    (void);
 
 void         _gdk_quartz_events_break_all_grabs         (guint32    time);
 
+/* Devices */
+void       _gdk_quartz_device_core_set_active (GdkDevice  *device,
+                                               gboolean    active,
+                                               NSUInteger  device_id);
+
+gboolean   _gdk_quartz_device_core_is_active (GdkDevice  *device,
+                                              NSUInteger  device_id);
+
+void       _gdk_quartz_device_core_set_unique (GdkDevice          *device,
+                                               unsigned long long  unique_id);
+
+unsigned long long _gdk_quartz_device_core_get_unique (GdkDevice *device);
+
 /* Event loop */
 gboolean   _gdk_quartz_event_loop_check_pending (void);
 NSEvent *  _gdk_quartz_event_loop_get_pending   (void);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]