summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Hergert <christian@hergert.me>2021-01-13 16:13:14 +0000
committerChristian Hergert <christian@hergert.me>2021-01-13 16:13:14 +0000
commit6c21a7be0b188f344352cd17b35d8faf2bcec3dd (patch)
tree2aa0316316e2f1fff7ef36bd1368a6bfb657bcd8
parenta6f2bcb220813e43506c6edf684cf4f3bff798e7 (diff)
parentf5efb15cbae1ae1c6aca07066a4a5720dc84706a (diff)
downloadgtk+-6c21a7be0b188f344352cd17b35d8faf2bcec3dd.tar.gz
Merge branch 'nacho/macos-stylus' into 'master'
Support stylus devices on macos See merge request GNOME/gtk!3058
-rw-r--r--gdk/macos/gdkmacosdisplay-translate.c48
-rw-r--r--gdk/macos/gdkmacosseat-private.h11
-rw-r--r--gdk/macos/gdkmacosseat.c606
-rw-r--r--gdk/macos/gdkmacosseat.h40
-rw-r--r--gdk/macos/meson.build1
5 files changed, 679 insertions, 27 deletions
diff --git a/gdk/macos/gdkmacosdisplay-translate.c b/gdk/macos/gdkmacosdisplay-translate.c
index e6551581a2..2a825879a5 100644
--- a/gdk/macos/gdkmacosdisplay-translate.c
+++ b/gdk/macos/gdkmacosdisplay-translate.c
@@ -28,6 +28,7 @@
#include "gdkmacosdisplay-private.h"
#include "gdkmacoskeymap-private.h"
#include "gdkmacossurface-private.h"
+#include "gdkmacosseat-private.h"
#define GDK_MOD2_MASK (1 << 4)
#define GRIP_WIDTH 15
@@ -205,6 +206,9 @@ fill_button_event (GdkMacosDisplay *display,
GdkSeat *seat;
GdkEventType type;
GdkModifierType state;
+ GdkDevice *pointer = NULL;
+ GdkDeviceTool *tool = NULL;
+ double *axes = NULL;
g_assert (GDK_IS_MACOS_DISPLAY (display));
g_assert (GDK_IS_MACOS_SURFACE (surface));
@@ -241,16 +245,22 @@ fill_button_event (GdkMacosDisplay *display,
y < 0 || y > GDK_SURFACE (surface)->height))
return NULL;
+ if (([nsevent subtype] == NSEventSubtypeTabletPoint) &&
+ _gdk_macos_seat_get_tablet (GDK_MACOS_SEAT (seat), &pointer, &tool))
+ axes = _gdk_macos_seat_get_tablet_axes_from_nsevent (GDK_MACOS_SEAT (seat), nsevent);
+ else
+ pointer = gdk_seat_get_pointer (seat);
+
return gdk_button_event_new (type,
GDK_SURFACE (surface),
- gdk_seat_get_pointer (seat),
- NULL,
+ pointer,
+ tool,
get_time_from_ns_event (nsevent),
state,
get_mouse_button_from_ns_event (nsevent),
x,
y,
- NULL);
+ axes);
}
static GdkEvent *
@@ -557,6 +567,9 @@ fill_motion_event (GdkMacosDisplay *display,
{
GdkSeat *seat;
GdkModifierType state;
+ GdkDevice *pointer = NULL;
+ GdkDeviceTool *tool = NULL;
+ double *axes = NULL;
g_assert (GDK_IS_MACOS_SURFACE (surface));
g_assert (nsevent != NULL);
@@ -566,14 +579,20 @@ fill_motion_event (GdkMacosDisplay *display,
state = get_keyboard_modifiers_from_ns_event (nsevent) |
_gdk_macos_display_get_current_mouse_modifiers (display);
+ if (([nsevent subtype] == NSEventSubtypeTabletPoint) &&
+ _gdk_macos_seat_get_tablet (GDK_MACOS_SEAT (seat), &pointer, &tool))
+ axes = _gdk_macos_seat_get_tablet_axes_from_nsevent (GDK_MACOS_SEAT (seat), nsevent);
+ else
+ pointer = gdk_seat_get_pointer (seat);
+
return gdk_motion_event_new (GDK_SURFACE (surface),
- gdk_seat_get_pointer (seat),
- NULL,
+ pointer,
+ tool,
get_time_from_ns_event (nsevent),
state,
x,
y,
- NULL);
+ axes);
}
static GdkEvent *
@@ -1051,6 +1070,23 @@ _gdk_macos_display_translate (GdkMacosDisplay *self,
return NULL;
}
+ /* We need to register the proximity event from any point on the screen
+ * to properly register the devices
+ * FIXME: is there a better way to detect if a tablet has been plugged?
+ */
+ if (event_type == NSEventTypeTabletProximity)
+ {
+ GdkSeat *seat = gdk_display_get_default_seat (GDK_DISPLAY (self));
+
+ _gdk_macos_seat_handle_tablet_tool_event (GDK_MACOS_SEAT (seat), nsevent);
+
+ /* FIXME: we might want to cache this proximity event and propagate it
+ * but proximity events in gdk work at a window level while on macos
+ * works at a screen level. For now we just skip them.
+ */
+ return NULL;
+ }
+
if (!(surface = find_surface_for_ns_event (self, nsevent, &x, &y)))
return NULL;
diff --git a/gdk/macos/gdkmacosseat-private.h b/gdk/macos/gdkmacosseat-private.h
index 57e5605018..889051e74a 100644
--- a/gdk/macos/gdkmacosseat-private.h
+++ b/gdk/macos/gdkmacosseat-private.h
@@ -23,6 +23,7 @@
#include <AppKit/AppKit.h>
#include "gdkmacosdisplay.h"
+#include "gdkmacosseat.h"
#include "gdkseatprivate.h"
@@ -30,6 +31,16 @@ G_BEGIN_DECLS
GdkSeat *_gdk_macos_seat_new (GdkMacosDisplay *display);
+void _gdk_macos_seat_handle_tablet_tool_event (GdkMacosSeat *seat,
+ NSEvent *nsevent);
+
+gboolean _gdk_macos_seat_get_tablet (GdkMacosSeat *seat,
+ GdkDevice **device,
+ GdkDeviceTool **tool);
+
+double *_gdk_macos_seat_get_tablet_axes_from_nsevent (GdkMacosSeat *seat,
+ NSEvent *nsevent);
+
G_END_DECLS
#endif /* __GDK_MACOS_SEAT_PRIVATE_H__ */
diff --git a/gdk/macos/gdkmacosseat.c b/gdk/macos/gdkmacosseat.c
index e239c6cb3c..a7b7c59915 100644
--- a/gdk/macos/gdkmacosseat.c
+++ b/gdk/macos/gdkmacosseat.c
@@ -1,5 +1,6 @@
/*
* Copyright © 2020 Red Hat, Inc.
+ * Copyright © 2021 Amazon.com, Inc. and its affiliates. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -22,42 +23,605 @@
#include <gdk/gdk.h>
#include "gdkdeviceprivate.h"
-#include "gdkseatdefaultprivate.h"
+#include "gdkdevicetoolprivate.h"
#include "gdkmacosdevice.h"
#include "gdkmacosseat-private.h"
+typedef struct
+{
+ NSUInteger device_id;
+ char *name;
+
+ GdkDevice *logical_device;
+ GdkDevice *stylus_device;
+ GdkSeat *seat;
+
+ GdkDeviceTool *current_tool;
+
+ int axis_indices[GDK_AXIS_LAST];
+ double axes[GDK_AXIS_LAST];
+} GdkMacosTabletData;
+
+struct _GdkMacosSeat
+{
+ GdkSeat parent_instance;
+
+ GdkMacosDisplay *display;
+
+ GdkDevice *logical_pointer;
+ GdkDevice *logical_keyboard;
+
+ GdkMacosTabletData *current_tablet;
+ GPtrArray *tablets;
+ GPtrArray *tools;
+};
+
+struct _GdkMacosSeatClass
+{
+ GdkSeatClass parent_class;
+};
+
+G_DEFINE_TYPE (GdkMacosSeat, gdk_macos_seat, GDK_TYPE_SEAT)
+
+#define KEYBOARD_EVENTS (GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | \
+ GDK_FOCUS_CHANGE_MASK)
+#define TOUCH_EVENTS (GDK_TOUCH_MASK)
+#define POINTER_EVENTS (GDK_POINTER_MOTION_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_BUTTON_RELEASE_MASK | \
+ GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK | \
+ GDK_ENTER_NOTIFY_MASK | \
+ GDK_LEAVE_NOTIFY_MASK | \
+ GDK_PROXIMITY_IN_MASK | \
+ GDK_PROXIMITY_OUT_MASK)
+
+static void
+gdk_macos_tablet_data_free (gpointer user_data)
+{
+ GdkMacosTabletData *tablet = user_data;
+
+ gdk_seat_device_removed (GDK_SEAT (tablet->seat), tablet->stylus_device);
+ gdk_seat_device_removed (GDK_SEAT (tablet->seat), tablet->logical_device);
+
+ _gdk_device_set_associated_device (tablet->logical_device, NULL);
+ _gdk_device_set_associated_device (tablet->stylus_device, NULL);
+
+ g_object_unref (tablet->logical_device);
+ g_object_unref (tablet->stylus_device);
+
+ g_free (tablet->name);
+ g_free (tablet);
+}
+
+static void
+gdk_macos_seat_dispose (GObject *object)
+{
+ GdkMacosSeat *self = GDK_MACOS_SEAT (object);
+
+ if (self->logical_pointer)
+ {
+ gdk_seat_device_removed (GDK_SEAT (self), self->logical_pointer);
+ g_clear_object (&self->logical_pointer);
+ }
+
+ if (self->logical_keyboard)
+ {
+ gdk_seat_device_removed (GDK_SEAT (self), self->logical_keyboard);
+ g_clear_object (&self->logical_pointer);
+ }
+
+ g_clear_pointer (&self->tablets, g_ptr_array_unref);
+ g_clear_pointer (&self->tools, g_ptr_array_unref);
+
+ G_OBJECT_CLASS (gdk_macos_seat_parent_class)->dispose (object);
+}
+
+static GdkSeatCapabilities
+gdk_macos_seat_get_capabilities (GdkSeat *seat)
+{
+ GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
+ GdkSeatCapabilities caps = 0;
+
+ if (self->logical_pointer)
+ caps |= GDK_SEAT_CAPABILITY_POINTER;
+ if (self->logical_keyboard)
+ caps |= GDK_SEAT_CAPABILITY_KEYBOARD;
+
+ return caps;
+}
+
+static GdkGrabStatus
+gdk_macos_seat_grab (GdkSeat *seat,
+ GdkSurface *surface,
+ GdkSeatCapabilities capabilities,
+ gboolean owner_events,
+ GdkCursor *cursor,
+ GdkEvent *event,
+ GdkSeatGrabPrepareFunc prepare_func,
+ gpointer prepare_func_data)
+{
+ GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
+ guint32 evtime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
+ GdkGrabStatus status = GDK_GRAB_SUCCESS;
+ gboolean was_visible;
+
+ was_visible = gdk_surface_get_mapped (surface);
+
+ if (prepare_func)
+ (prepare_func) (seat, surface, prepare_func_data);
+
+ if (!gdk_surface_get_mapped (surface))
+ {
+ g_critical ("Surface %p has not been mapped in GdkSeatGrabPrepareFunc",
+ surface);
+ return GDK_GRAB_NOT_VIEWABLE;
+ }
+
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+
+ if (capabilities & GDK_SEAT_CAPABILITY_ALL_POINTING)
+ {
+ /* ALL_POINTING spans 3 capabilities; get the mask for the ones we have */
+ GdkEventMask pointer_evmask = 0;
+
+ /* We let tablet styli take over the pointer cursor */
+ if (capabilities & (GDK_SEAT_CAPABILITY_POINTER |
+ GDK_SEAT_CAPABILITY_TABLET_STYLUS))
+ {
+ pointer_evmask |= POINTER_EVENTS;
+ }
+
+ if (capabilities & GDK_SEAT_CAPABILITY_TOUCH)
+ pointer_evmask |= TOUCH_EVENTS;
+
+ status = gdk_device_grab (self->logical_pointer, surface,
+ owner_events,
+ pointer_evmask, cursor,
+ evtime);
+ }
+
+ if (status == GDK_GRAB_SUCCESS &&
+ capabilities & GDK_SEAT_CAPABILITY_KEYBOARD)
+ {
+ status = gdk_device_grab (self->logical_keyboard, surface,
+ owner_events,
+ KEYBOARD_EVENTS, cursor,
+ evtime);
+
+ if (status != GDK_GRAB_SUCCESS)
+ {
+ if (capabilities & ~GDK_SEAT_CAPABILITY_KEYBOARD)
+ gdk_device_ungrab (self->logical_pointer, evtime);
+ }
+ }
+
+ if (status != GDK_GRAB_SUCCESS && !was_visible)
+ gdk_surface_hide (surface);
+
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+
+ return status;
+}
+
+static void
+gdk_macos_seat_ungrab (GdkSeat *seat)
+{
+ GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
+
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ gdk_device_ungrab (self->logical_pointer, GDK_CURRENT_TIME);
+ gdk_device_ungrab (self->logical_keyboard, GDK_CURRENT_TIME);
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+}
+
+static GdkDevice *
+gdk_macos_seat_get_logical_device (GdkSeat *seat,
+ GdkSeatCapabilities capability)
+{
+ GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
+
+ /* There must be only one flag set */
+ switch ((guint) capability)
+ {
+ case GDK_SEAT_CAPABILITY_POINTER:
+ case GDK_SEAT_CAPABILITY_TOUCH:
+ return self->logical_pointer;
+ case GDK_SEAT_CAPABILITY_KEYBOARD:
+ return self->logical_keyboard;
+ default:
+ g_warning ("Unhandled capability %x", capability);
+ break;
+ }
+
+ return NULL;
+}
+
+static GList *
+gdk_macos_seat_get_devices (GdkSeat *seat,
+ GdkSeatCapabilities capabilities)
+{
+ GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
+ GList *physical_devices = NULL;
+
+ if (self->logical_pointer && (capabilities & GDK_SEAT_CAPABILITY_POINTER))
+ physical_devices = g_list_prepend (physical_devices, self->logical_pointer);
+
+ if (self->logical_keyboard && (capabilities & GDK_SEAT_CAPABILITY_KEYBOARD))
+ physical_devices = g_list_prepend (physical_devices, self->logical_keyboard);
+
+ if (capabilities & GDK_SEAT_CAPABILITY_TABLET_STYLUS)
+ {
+ for (guint i = 0; i < self->tablets->len; i++)
+ {
+ GdkMacosTabletData *tablet = g_ptr_array_index (self->tablets, i);
+
+ physical_devices = g_list_prepend (physical_devices, tablet->stylus_device);
+ }
+ }
+
+ return physical_devices;
+}
+
+static GList *
+gdk_macos_seat_get_tools (GdkSeat *seat)
+{
+ GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
+ GdkDeviceTool *tool;
+ GList *tools = NULL;
+
+ for (guint i = 0; i < self->tools->len; i++)
+ {
+ tool = g_ptr_array_index (self->tools, i);
+ tools = g_list_prepend (tools, tool);
+ }
+
+ return tools;
+}
+
+static void
+gdk_macos_seat_class_init (GdkMacosSeatClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdkSeatClass *seat_class = GDK_SEAT_CLASS (klass);
+
+ object_class->dispose = gdk_macos_seat_dispose;
+
+ seat_class->get_capabilities = gdk_macos_seat_get_capabilities;
+ seat_class->grab = gdk_macos_seat_grab;
+ seat_class->ungrab = gdk_macos_seat_ungrab;
+ seat_class->get_logical_device = gdk_macos_seat_get_logical_device;
+ seat_class->get_devices = gdk_macos_seat_get_devices;
+ seat_class->get_tools = gdk_macos_seat_get_tools;
+}
+
+static void
+gdk_macos_seat_init (GdkMacosSeat *self)
+{
+ self->tablets = g_ptr_array_new_with_free_func (gdk_macos_tablet_data_free);
+ self->tools = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+}
+
+static void
+init_devices (GdkMacosSeat *self)
+{
+ /* pointer */
+ self->logical_pointer = g_object_new (GDK_TYPE_MACOS_DEVICE,
+ "name", "Core Pointer",
+ "source", GDK_SOURCE_MOUSE,
+ "has-cursor", TRUE,
+ "display", self->display,
+ "seat", self,
+ NULL);
+
+ /* keyboard */
+ self->logical_keyboard = g_object_new (GDK_TYPE_MACOS_DEVICE,
+ "name", "Core Keyboard",
+ "source", GDK_SOURCE_KEYBOARD,
+ "has-cursor", FALSE,
+ "display", self->display,
+ "seat", self,
+ NULL);
+
+ /* link both */
+ _gdk_device_set_associated_device (self->logical_pointer, self->logical_keyboard);
+ _gdk_device_set_associated_device (self->logical_keyboard, self->logical_pointer);
+
+ gdk_seat_device_added (GDK_SEAT (self), self->logical_pointer);
+ gdk_seat_device_added (GDK_SEAT (self), self->logical_keyboard);
+}
+
GdkSeat *
_gdk_macos_seat_new (GdkMacosDisplay *display)
{
- GdkDevice *core_keyboard;
- GdkDevice *core_pointer;
- GdkSeat *seat;
+ GdkMacosSeat *self;
g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (display), NULL);
- core_pointer = g_object_new (GDK_TYPE_MACOS_DEVICE,
- "name", "Core Pointer",
- "source", GDK_SOURCE_MOUSE,
- "has-cursor", TRUE,
- "display", display,
- NULL);
- core_keyboard = g_object_new (GDK_TYPE_MACOS_DEVICE,
- "name", "Core Keyboard",
- "source", GDK_SOURCE_KEYBOARD,
+ self = g_object_new (GDK_TYPE_MACOS_SEAT,
+ "display", display,
+ NULL);
+
+ self->display = display;
+
+ init_devices (self);
+
+ return g_steal_pointer (&self);
+}
+
+static GdkDeviceToolType
+get_device_tool_type_from_nsevent (NSEvent *nsevent)
+{
+ GdkDeviceToolType tool_type;
+
+ switch ([nsevent pointingDeviceType])
+ {
+ case NSPointingDeviceTypePen:
+ tool_type = GDK_DEVICE_TOOL_TYPE_PEN;
+ break;
+ case NSPointingDeviceTypeEraser:
+ tool_type = GDK_DEVICE_TOOL_TYPE_ERASER;
+ break;
+ case NSPointingDeviceTypeCursor:
+ tool_type = GDK_DEVICE_TOOL_TYPE_MOUSE;
+ break;
+ case NSPointingDeviceTypeUnknown:
+ default:
+ tool_type = GDK_DEVICE_TOOL_TYPE_UNKNOWN;
+ }
+
+ return tool_type;
+}
+
+static GdkAxisFlags
+get_device_tool_axes_from_nsevent (NSEvent *nsevent)
+{
+ /* TODO: do we need to be smarter about the capabilities? */
+ return GDK_AXIS_FLAG_XTILT | GDK_AXIS_FLAG_YTILT | GDK_AXIS_FLAG_PRESSURE |
+ GDK_AXIS_FLAG_ROTATION;
+}
+
+static GdkMacosTabletData *
+create_tablet_data_from_nsevent (GdkMacosSeat *self,
+ NSEvent *nsevent)
+{
+ GdkMacosTabletData *tablet;
+ GdkDisplay *display = gdk_seat_get_display (GDK_SEAT (self));
+ GdkDevice *logical_device, *stylus_device;
+ char *logical_name;
+ char *vid, *pid;
+
+ tablet = g_new0 (GdkMacosTabletData, 1);
+ tablet->seat = GDK_SEAT (self);
+ tablet->device_id = [nsevent deviceID];
+ /* FIXME: find a better name */
+ tablet->name = g_strdup_printf ("Tablet %lu", [nsevent deviceID]);
+
+ vid = g_strdup_printf ("%.4lx", [nsevent vendorID]);
+ pid = g_strdup_printf ("%.4lx", [nsevent tabletID]);
+
+ logical_name = g_strdup_printf ("Logical pointer for %s", tablet->name);
+ logical_device = g_object_new (GDK_TYPE_MACOS_DEVICE,
+ "name", logical_name,
+ "source", GDK_SOURCE_MOUSE,
+ "has-cursor", TRUE,
+ "display", display,
+ "seat", self,
+ NULL);
+
+ stylus_device = g_object_new (GDK_TYPE_MACOS_DEVICE,
+ "name", tablet->name,
+ "source", GDK_SOURCE_PEN,
"has-cursor", FALSE,
"display", display,
+ "seat", self,
+ "vendor-id", vid,
+ "product-id", pid,
NULL);
- _gdk_device_set_associated_device (GDK_DEVICE (core_pointer),
- GDK_DEVICE (core_keyboard));
- _gdk_device_set_associated_device (GDK_DEVICE (core_keyboard),
- GDK_DEVICE (core_pointer));
+ tablet->logical_device = logical_device;
+ tablet->stylus_device = stylus_device;
+
+ _gdk_device_set_associated_device (logical_device, self->logical_keyboard);
+ _gdk_device_set_associated_device (stylus_device, logical_device);
+
+ gdk_seat_device_added (GDK_SEAT (self), logical_device);
+ gdk_seat_device_added (GDK_SEAT (self), stylus_device);
+
+ g_free (logical_name);
+ g_free (vid);
+ g_free (pid);
+
+ return tablet;
+}
+
+static GdkMacosTabletData *
+get_tablet_data_from_nsevent (GdkMacosSeat *self,
+ NSEvent *nsevent)
+{
+ GdkMacosTabletData *tablet = NULL;
+
+ for (guint i = 0; i < self->tablets->len; i++)
+ {
+ GdkMacosTabletData *t = g_ptr_array_index (self->tablets, i);
+
+ if (t->device_id == [nsevent deviceID])
+ {
+ tablet = t;
+ break;
+ }
+ }
+
+ if (!tablet)
+ tablet = create_tablet_data_from_nsevent (self, nsevent);
+
+ return tablet;
+}
+
+static void
+device_tablet_clone_tool_axes (GdkMacosTabletData *tablet,
+ GdkDeviceTool *tool)
+{
+ int axis_pos;
+
+ g_object_freeze_notify (G_OBJECT (tablet->stylus_device));
+ _gdk_device_reset_axes (tablet->stylus_device);
+
+ _gdk_device_add_axis (tablet->stylus_device, GDK_AXIS_X, 0, 0, 0);
+ _gdk_device_add_axis (tablet->stylus_device, GDK_AXIS_Y, 0, 0, 0);
+
+ if (tool->tool_axes & (GDK_AXIS_FLAG_XTILT | GDK_AXIS_FLAG_YTILT))
+ {
+ axis_pos = _gdk_device_add_axis (tablet->stylus_device,
+ GDK_AXIS_XTILT, -1.0, 1.0, 0);
+ tablet->axis_indices[GDK_AXIS_XTILT] = axis_pos;
+
+ axis_pos = _gdk_device_add_axis (tablet->stylus_device,
+ GDK_AXIS_YTILT, -1.0, 1.0, 0);
+ tablet->axis_indices[GDK_AXIS_YTILT] = axis_pos;
+ }
+
+ if (tool->tool_axes & GDK_AXIS_FLAG_PRESSURE)
+ {
+ axis_pos = _gdk_device_add_axis (tablet->stylus_device,
+ GDK_AXIS_PRESSURE, 0.0, 1.0, 0);
+ tablet->axis_indices[GDK_AXIS_PRESSURE] = axis_pos;
+ }
+
+ if (tool->tool_axes & GDK_AXIS_FLAG_ROTATION)
+ {
+ axis_pos = _gdk_device_add_axis (tablet->stylus_device,
+ GDK_AXIS_ROTATION, 0.0, 1.0, 0);
+ tablet->axis_indices[GDK_AXIS_ROTATION] = axis_pos;
+ }
+
+ g_object_thaw_notify (G_OBJECT (tablet->stylus_device));
+}
+
+static void
+mimic_device_axes (GdkDevice *logical,
+ GdkDevice *physical)
+{
+ double axis_min, axis_max, axis_resolution;
+ GdkAxisUse axis_use;
+ int axis_count;
+
+ g_object_freeze_notify (G_OBJECT (logical));
+ _gdk_device_reset_axes (logical);
+ axis_count = gdk_device_get_n_axes (physical);
+
+ for (int i = 0; i < axis_count; i++)
+ {
+ _gdk_device_get_axis_info (physical, i, &axis_use, &axis_min,
+ &axis_max, &axis_resolution);
+ _gdk_device_add_axis (logical, axis_use, axis_min,
+ axis_max, axis_resolution);
+ }
+
+ g_object_thaw_notify (G_OBJECT (logical));
+}
+
+void
+_gdk_macos_seat_handle_tablet_tool_event (GdkMacosSeat *seat,
+ NSEvent *nsevent)
+{
+ GdkDeviceToolType tool_type;
+ GdkMacosTabletData *tablet;
+ GdkDeviceTool *tool;
+
+ g_return_if_fail (GDK_IS_MACOS_SEAT (seat));
+ g_return_if_fail (nsevent != NULL);
+
+ tablet = get_tablet_data_from_nsevent (seat, nsevent);
+
+ tool_type = get_device_tool_type_from_nsevent (nsevent);
+
+ if (tool_type == GDK_DEVICE_TOOL_TYPE_UNKNOWN)
+ {
+ g_warning ("Unknown device tool detected");
+ return;
+ }
+
+ tool = gdk_seat_get_tool (GDK_SEAT (seat), [nsevent tabletID], [nsevent deviceID], tool_type);
+
+ if ([nsevent isEnteringProximity])
+ {
+ if (!tool)
+ {
+ tool = gdk_device_tool_new ([nsevent tabletID], [nsevent vendorID], tool_type,
+ get_device_tool_axes_from_nsevent (nsevent));
+ g_ptr_array_add (seat->tools, tool);
+ }
+
+ gdk_device_update_tool (tablet->stylus_device, tool);
+ tablet->current_tool = tool;
+ device_tablet_clone_tool_axes (tablet, tool);
+ mimic_device_axes (tablet->logical_device, tablet->stylus_device);
+ seat->current_tablet = tablet;
+ }
+ else
+ {
+ gdk_device_update_tool (tablet->stylus_device, NULL);
+ tablet->current_tool = NULL;
+ seat->current_tablet = NULL;
+ }
+}
+
+gboolean
+_gdk_macos_seat_get_tablet (GdkMacosSeat *seat,
+ GdkDevice **logical_device,
+ GdkDeviceTool **tool)
+{
+ g_return_val_if_fail (GDK_IS_MACOS_SEAT (seat), FALSE);
+
+ if (!seat->current_tablet)
+ return FALSE;
+
+ *logical_device = seat->current_tablet->logical_device;
+ *tool = seat->current_tablet->current_tool;
+
+ return TRUE;
+}
+
+double *
+_gdk_macos_seat_get_tablet_axes_from_nsevent (GdkMacosSeat *seat,
+ NSEvent *nsevent)
+{
+ GdkMacosTabletData *tablet;
+ int axis_index;
+
+ g_return_val_if_fail (GDK_IS_MACOS_SEAT (seat), NULL);
+ g_return_val_if_fail (nsevent != NULL, NULL);
+
+ tablet = seat->current_tablet;
+ if (!tablet || !tablet->current_tool)
+ return NULL;
+
+ if (tablet->current_tool->tool_axes & (GDK_AXIS_FLAG_XTILT | GDK_AXIS_FLAG_YTILT))
+ {
+ axis_index = tablet->axis_indices[GDK_AXIS_XTILT];
+ _gdk_device_translate_axis (tablet->stylus_device, axis_index,
+ [nsevent tilt].x, &tablet->axes[GDK_AXIS_XTILT]);
+
+ axis_index = tablet->axis_indices[GDK_AXIS_YTILT];
+ _gdk_device_translate_axis (tablet->stylus_device, axis_index,
+ [nsevent tilt].y, &tablet->axes[GDK_AXIS_YTILT]);
+ }
- seat = gdk_seat_default_new_for_logical_pair (core_pointer, core_keyboard);
+ if (tablet->current_tool->tool_axes & GDK_AXIS_FLAG_PRESSURE)
+ {
+ axis_index = tablet->axis_indices[GDK_AXIS_PRESSURE];
+ _gdk_device_translate_axis (tablet->stylus_device, axis_index,
+ [nsevent pressure], &tablet->axes[GDK_AXIS_PRESSURE]);
+ }
- g_object_unref (core_pointer);
- g_object_unref (core_keyboard);
+ if (tablet->current_tool->tool_axes & GDK_AXIS_FLAG_ROTATION)
+ {
+ axis_index = tablet->axis_indices[GDK_AXIS_ROTATION];
+ _gdk_device_translate_axis (tablet->stylus_device, axis_index,
+ [nsevent rotation], &tablet->axes[GDK_AXIS_ROTATION]);
+ }
- return g_steal_pointer (&seat);
+ return g_memdup (tablet->axes,
+ sizeof (double) * GDK_AXIS_LAST);
}
diff --git a/gdk/macos/gdkmacosseat.h b/gdk/macos/gdkmacosseat.h
new file mode 100644
index 0000000000..e09c01c995
--- /dev/null
+++ b/gdk/macos/gdkmacosseat.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2021 Amazon.com, Inc. and its affiliates. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#if !defined (__GDKMACOS_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gdk/macos/gdkmacos.h> can be included directly."
+#endif
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_MACOS_SEAT (gdk_macos_seat_get_type ())
+#define GDK_MACOS_SEAT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_MACOS_SEAT, GdkMacosSeat))
+#define GDK_IS_MACOS_SEAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_MACOS_SEAT))
+
+typedef struct _GdkMacosSeat GdkMacosSeat;
+typedef struct _GdkMacosSeatClass GdkMacosSeatClass;
+
+GDK_AVAILABLE_IN_ALL
+GType gdk_macos_seat_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build
index 1b8d205055..943fb84457 100644
--- a/gdk/macos/meson.build
+++ b/gdk/macos/meson.build
@@ -33,6 +33,7 @@ gdk_macos_public_headers = files([
'gdkmacosglcontext.h',
'gdkmacoskeymap.h',
'gdkmacosmonitor.h',
+ 'gdkmacosseat.h',
'gdkmacossurface.h',
])