From ecbed15d47fc29fc0da133c255fb7e4cb62e4129 Mon Sep 17 00:00:00 2001 From: Andreas Butti Date: Sun, 27 Jan 2019 20:16:33 +0100 Subject: [PATCH] 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 | 59 ++++++- gdk/quartz/gdkinternal-quartz.h | 23 +++ 5 files changed, 302 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..44ff25e4c6 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 #include "gdkquartzdevicemanager-core.h" +#import + 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..3a8e4cc665 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) @@ -557,7 +558,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 +1020,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 +1048,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 +1069,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 +1088,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 +1112,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 +1148,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 +1506,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);