/*
* 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
* 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
*/
#include "config.h"
#include <gdk/gdk.h>
#include "gdkdeviceprivate.h"
#include "gdkdevicetoolprivate.h"
#include "gdkmacosdevice.h"
#include "gdkmacosseat-private.h"
#include "gdkprivate.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)
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)
{
return GDK_GRAB_SUCCESS;
}
static void
gdk_macos_seat_ungrab (GdkSeat *seat,
GdkSurface *surface)
{
}
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)
{
GdkMacosSeat *self;
g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (display), NULL);
self = g_object_new (GDK_TYPE_MACOS_SEAT,
"display" , display,
NULL);
self->display = display;
init_devices (self);
return GDK_SEAT (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);
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]);
}
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]);
}
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_memdup2 (tablet->axes, sizeof (double ) * GDK_AXIS_LAST);
}
Messung V0.5 in Prozent C=98 H=95 G=96
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland