/* GDK - The GIMP Drawing Kit * Copyright (C) 2009 Carlos Garnacho * * 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 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 . */ #include "config.h" #include #include "gdkdeviceprivate.h" #include "gdkdevicetool.h" #include "gdkdisplayprivate.h" #include "gdkintl.h" #include "gdkkeysprivate.h" /** * GdkDevice: * * The `GdkDevice` object represents an input device, such * as a keyboard, a mouse, or a touchpad. * * See the [class@Gdk.Seat] documentation for more information * about the various kinds of devices, and their relationships. */ typedef struct _GdkAxisInfo GdkAxisInfo; struct _GdkAxisInfo { GdkAxisUse use; double min_axis; double max_axis; double min_value; double max_value; double resolution; }; enum { CHANGED, TOOL_CHANGED, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0 }; static void gdk_device_finalize (GObject *object); static void gdk_device_dispose (GObject *object); static void gdk_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void gdk_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); G_DEFINE_ABSTRACT_TYPE (GdkDevice, gdk_device, G_TYPE_OBJECT) enum { PROP_0, PROP_DISPLAY, PROP_NAME, PROP_SOURCE, PROP_HAS_CURSOR, PROP_N_AXES, PROP_VENDOR_ID, PROP_PRODUCT_ID, PROP_SEAT, PROP_NUM_TOUCHES, PROP_TOOL, PROP_DIRECTION, PROP_HAS_BIDI_LAYOUTS, PROP_CAPS_LOCK_STATE, PROP_NUM_LOCK_STATE, PROP_SCROLL_LOCK_STATE, PROP_MODIFIER_STATE, LAST_PROP }; static GParamSpec *device_props[LAST_PROP] = { NULL, }; static void gdk_device_class_init (GdkDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gdk_device_finalize; object_class->dispose = gdk_device_dispose; object_class->set_property = gdk_device_set_property; object_class->get_property = gdk_device_get_property; /** * GdkDevice:display: (attributes org.gtk.Property.get=gdk_device_get_display) * * The `GdkDisplay` the `GdkDevice` pertains to. */ device_props[PROP_DISPLAY] = g_param_spec_object ("display", P_("Device Display"), P_("Display which the device belongs to"), GDK_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * GdkDevice:name: (attributes org.gtk.Property.get=gdk_device_get_name) * * The device name. */ device_props[PROP_NAME] = g_param_spec_string ("name", P_("Device name"), P_("Device name"), NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * GdkDevice:source: (attributes org.gtk.Property.get=gdk_device_get_source) * * Source type for the device. */ device_props[PROP_SOURCE] = g_param_spec_enum ("source", P_("Input source"), P_("Source type for the device"), GDK_TYPE_INPUT_SOURCE, GDK_SOURCE_MOUSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); /** * GdkDevice:has-cursor: (attributes org.gtk.Property.get=gdk_device_get_has_cursor) * * Whether the device is represented by a cursor on the screen. */ device_props[PROP_HAS_CURSOR] = g_param_spec_boolean ("has-cursor", P_("Whether the device has a cursor"), P_("Whether there is a visible cursor following device motion"), FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * GdkDevice:n-axes: * * Number of axes in the device. */ device_props[PROP_N_AXES] = g_param_spec_uint ("n-axes", P_("Number of axes in the device"), P_("Number of axes in the device"), 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkDevice:vendor-id: (attributes org.gtk.Property.get=gdk_device_get_vendor_id) * * Vendor ID of this device. * * See [method@Gdk.Device.get_vendor_id]. */ device_props[PROP_VENDOR_ID] = g_param_spec_string ("vendor-id", P_("Vendor ID"), P_("Vendor ID"), NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * GdkDevice:product-id: (attributes org.gtk.Property.get=gdk_device_get_product_id) * * Product ID of this device. * * See [method@Gdk.Device.get_product_id]. */ device_props[PROP_PRODUCT_ID] = g_param_spec_string ("product-id", P_("Product ID"), P_("Product ID"), NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * GdkDevice:seat: (attributes org.gtk.Property.get=gdk_device_get_seat) * * `GdkSeat` of this device. */ device_props[PROP_SEAT] = g_param_spec_object ("seat", P_("Seat"), P_("Seat"), GDK_TYPE_SEAT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * GdkDevice:num-touches: (attributes org.gtk.Property.get=gdk_device_get_num_touches) * * The maximal number of concurrent touches on a touch device. * * Will be 0 if the device is not a touch device or if the number * of touches is unknown. */ device_props[PROP_NUM_TOUCHES] = g_param_spec_uint ("num-touches", P_("Number of concurrent touches"), P_("Number of concurrent touches"), 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * GdkDevice:tool: (attributes org.gtk.Property.get=gdk_device_get_device_tool) * * The `GdkDeviceTool` that is currently used with this device. */ device_props[PROP_TOOL] = g_param_spec_object ("tool", P_("Tool"), P_("The tool that is currently used with this device"), GDK_TYPE_DEVICE_TOOL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkDevice:direction: (attributes org.gtk.Property.get=gdk_device_get_direction) * * The direction of the current layout. * * This is only relevant for keyboard devices. */ device_props[PROP_DIRECTION] = g_param_spec_enum ("direction", P_("Direction"), P_("The direction of the current layout of the keyboard"), PANGO_TYPE_DIRECTION, PANGO_DIRECTION_NEUTRAL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkDevice:has-bidi-layouts: (attributes org.gtk.Property.get=gdk_device_has_bidi_layouts) * * Whether the device has both right-to-left and left-to-right layouts. * * This is only relevant for keyboard devices. */ device_props[PROP_HAS_BIDI_LAYOUTS] = g_param_spec_boolean ("has-bidi-layouts", P_("Has bidi layouts"), P_("Whether the keyboard has bidi layouts"), FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkDevice:caps-lock-state: (attributes org.gtk.Property.get=gdk_device_get_caps_lock_state) * * Whether Caps Lock is on. * * This is only relevant for keyboard devices. */ device_props[PROP_CAPS_LOCK_STATE] = g_param_spec_boolean ("caps-lock-state", P_("Caps lock state"), P_("Whether the keyboard caps lock is on"), FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkDevice:num-lock-state: (attributes org.gtk.Property.get=gdk_device_get_num_lock_state) * * Whether Num Lock is on. * * This is only relevant for keyboard devices. */ device_props[PROP_NUM_LOCK_STATE] = g_param_spec_boolean ("num-lock-state", P_("Num lock state"), P_("Whether the keyboard num lock is on"), FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkDevice:scroll-lock-state: (attributes org.gtk.Property.get=gdk_device_get_scroll_lock_state) * * Whether Scroll Lock is on. * * This is only relevant for keyboard devices. */ device_props[PROP_SCROLL_LOCK_STATE] = g_param_spec_boolean ("scroll-lock-state", P_("Scroll lock state"), P_("Whether the keyboard scroll lock is on"), FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkDevice:modifier-state: (attributes org.gtk.Property.get=gdk_device_get_modifier_state) * * The current modifier state of the device. * * This is only relevant for keyboard devices. */ device_props[PROP_MODIFIER_STATE] = g_param_spec_flags ("modifier-state", P_("Modifier state"), P_("The modifier state of the keyboard"), GDK_TYPE_MODIFIER_TYPE, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, LAST_PROP, device_props); /** * GdkDevice::changed: * @device: the `GdkDevice` * * Emitted either when the number of either axes or keys changes. * * On X11 this will normally happen when the physical device * routing events through the logical device changes (for * example, user switches from the USB mouse to a tablet); in * that case the logical device will change to reflect the axes * and keys on the new physical device. */ signals[CHANGED] = g_signal_new (g_intern_static_string ("changed"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * GdkDevice::tool-changed: * @device: the `GdkDevice` * @tool: The new current tool * * Emitted on pen/eraser devices whenever tools enter or leave proximity. */ signals[TOOL_CHANGED] = g_signal_new (g_intern_static_string ("tool-changed"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GDK_TYPE_DEVICE_TOOL); } static void gdk_device_init (GdkDevice *device) { device->axes = g_array_new (FALSE, TRUE, sizeof (GdkAxisInfo)); } static void gdk_device_finalize (GObject *object) { GdkDevice *device = GDK_DEVICE (object); if (device->axes) { g_array_free (device->axes, TRUE); device->axes = NULL; } g_clear_pointer (&device->name, g_free); g_clear_pointer (&device->vendor_id, g_free); g_clear_pointer (&device->product_id, g_free); G_OBJECT_CLASS (gdk_device_parent_class)->finalize (object); } static void gdk_device_dispose (GObject *object) { GdkDevice *device = GDK_DEVICE (object); GdkDevice *associated = device->associated; if (associated) _gdk_device_remove_physical_device (associated, device); if (associated) { device->associated = NULL; if (associated->associated == device) _gdk_device_set_associated_device (associated, NULL); g_object_unref (associated); } g_clear_object (&device->last_tool); G_OBJECT_CLASS (gdk_device_parent_class)->dispose (object); } static void gdk_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GdkDevice *device = GDK_DEVICE (object); switch (prop_id) { case PROP_DISPLAY: device->display = g_value_get_object (value); break; case PROP_NAME: g_free (device->name); device->name = g_value_dup_string (value); break; case PROP_SOURCE: device->source = g_value_get_enum (value); break; case PROP_HAS_CURSOR: device->has_cursor = g_value_get_boolean (value); break; case PROP_VENDOR_ID: device->vendor_id = g_value_dup_string (value); break; case PROP_PRODUCT_ID: device->product_id = g_value_dup_string (value); break; case PROP_SEAT: device->seat = g_value_get_object (value); break; case PROP_NUM_TOUCHES: device->num_touches = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gdk_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GdkDevice *device = GDK_DEVICE (object); switch (prop_id) { case PROP_DISPLAY: g_value_set_object (value, device->display); break; case PROP_NAME: g_value_set_string (value, device->name); break; case PROP_SOURCE: g_value_set_enum (value, device->source); break; case PROP_HAS_CURSOR: g_value_set_boolean (value, device->has_cursor); break; case PROP_N_AXES: g_value_set_uint (value, device->axes->len); break; case PROP_VENDOR_ID: g_value_set_string (value, device->vendor_id); break; case PROP_PRODUCT_ID: g_value_set_string (value, device->product_id); break; case PROP_SEAT: g_value_set_object (value, device->seat); break; case PROP_NUM_TOUCHES: g_value_set_uint (value, device->num_touches); break; case PROP_TOOL: g_value_set_object (value, device->last_tool); break; case PROP_DIRECTION: g_value_set_enum (value, gdk_device_get_direction (device)); break; case PROP_HAS_BIDI_LAYOUTS: g_value_set_boolean (value, gdk_device_has_bidi_layouts (device)); break; case PROP_CAPS_LOCK_STATE: g_value_set_boolean (value, gdk_device_get_caps_lock_state (device)); break; case PROP_NUM_LOCK_STATE: g_value_set_boolean (value, gdk_device_get_num_lock_state (device)); break; case PROP_SCROLL_LOCK_STATE: g_value_set_boolean (value, gdk_device_get_scroll_lock_state (device)); break; case PROP_MODIFIER_STATE: g_value_set_flags (value, gdk_device_get_modifier_state (device)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /** * gdk_device_get_surface_at_position: * @device: pointer `GdkDevice` to query info to * @win_x: (out) (optional): return location for the X coordinate * of the device location relative to the surface origin * @win_y: (out) (optional): return location for the Y coordinate * of the device location relative to the surface origin * * Obtains the surface underneath @device, returning the location of the * device in @win_x and @win_y. * * Returns %NULL if the surface tree under @device is not known to GDK * (for example, belongs to another application). * * Returns: (nullable) (transfer none): the `GdkSurface` under the * device position */ GdkSurface * gdk_device_get_surface_at_position (GdkDevice *device, double *win_x, double *win_y) { double tmp_x, tmp_y; GdkSurface *surface; g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); g_return_val_if_fail (device->source != GDK_SOURCE_KEYBOARD, NULL); surface = _gdk_device_surface_at_position (device, &tmp_x, &tmp_y, NULL); if (win_x) *win_x = tmp_x; if (win_y) *win_y = tmp_y; return surface; } /** * gdk_device_get_name: * @device: a GdkDevice` * * The name of the device, suitable for showing in a user interface. * * Returns: a name */ const char * gdk_device_get_name (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); return device->name; } /** * gdk_device_get_has_cursor: (attributes org.gtk.Method.get_property=has-cursor) * @device: a `GdkDevice` * * Determines whether the pointer follows device motion. * * This is not meaningful for keyboard devices, which * don't have a pointer. * * Returns: %TRUE if the pointer follows device motion */ gboolean gdk_device_get_has_cursor (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE); return device->has_cursor; } /** * gdk_device_get_source: (attributes org.gtk.Method.get_property=source) * @device: a `GdkDevice` * * Determines the type of the device. * * Returns: a `GdkInputSource` */ GdkInputSource gdk_device_get_source (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), 0); return device->source; } /** * gdk_device_get_axis_use: * @device: a pointer `GdkDevice` * @index_: the index of the axi. * * Returns the axis use for @index_. * * Returns: a `GdkAxisUse` specifying how the axis is used. */ GdkAxisUse gdk_device_get_axis_use (GdkDevice *device, guint index_) { GdkAxisInfo *info; g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_AXIS_IGNORE); g_return_val_if_fail (device->source != GDK_SOURCE_KEYBOARD, GDK_AXIS_IGNORE); g_return_val_if_fail (index_ < device->axes->len, GDK_AXIS_IGNORE); info = &g_array_index (device->axes, GdkAxisInfo, index_); return info->use; } /** * gdk_device_get_display: (attributes org.gtk.Method.get_property=display) * @device: a `GdkDevice` * * Returns the `GdkDisplay` to which @device pertains. * * Returns: (transfer none): a `GdkDisplay` */ GdkDisplay * gdk_device_get_display (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); return device->display; } void _gdk_device_set_associated_device (GdkDevice *device, GdkDevice *associated) { g_return_if_fail (GDK_IS_DEVICE (device)); g_return_if_fail (associated == NULL || GDK_IS_DEVICE (associated)); g_set_object (&device->associated, associated); } /* * gdk_device_list_physical_devices: * @device: a logical `GdkDevice` * * Returns the list of physical devices attached to the given logical * `GdkDevice`. * * Returns: (nullable) (transfer container) (element-type GdkDevice): * the list of physical devices attached to a logical `GdkDevice` */ GList * gdk_device_list_physical_devices (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); return g_list_copy (device->physical_devices); } void _gdk_device_add_physical_device (GdkDevice *device, GdkDevice *physical) { if (!g_list_find (device->physical_devices, physical)) device->physical_devices = g_list_prepend (device->physical_devices, physical); } void _gdk_device_remove_physical_device (GdkDevice *device, GdkDevice *physical) { GList *elem; elem = g_list_find (device->physical_devices, physical); if (elem == NULL) return; device->physical_devices = g_list_delete_link (device->physical_devices, elem); } /* * gdk_device_get_n_axes: * @device: a pointer `GdkDevice` * * Returns the number of axes the device currently has. * * Returns: the number of axes. */ int gdk_device_get_n_axes (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), 0); g_return_val_if_fail (device->source != GDK_SOURCE_KEYBOARD, 0); return device->axes->len; } /* * gdk_device_get_axis: (skip) * @device: a `GdkDevice` * @axes: (array): pointer to an array of axes * @use: the use to look for * @value: (out): location to store the found value * * Interprets an array of `double` as axis values and get the value * for a given axis use. * * Returns: %TRUE if the given axis use was found, otherwise %FALSE */ gboolean gdk_device_get_axis (GdkDevice *device, double *axes, GdkAxisUse use, double *value) { int i; g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE); g_return_val_if_fail (device->source != GDK_SOURCE_KEYBOARD, FALSE); if (axes == NULL) return FALSE; g_return_val_if_fail (device->axes != NULL, FALSE); for (i = 0; i < device->axes->len; i++) { GdkAxisInfo axis_info; axis_info = g_array_index (device->axes, GdkAxisInfo, i); if (axis_info.use != use) continue; if (value) *value = axes[i]; return TRUE; } return FALSE; } static GdkEventMask get_native_grab_event_mask (GdkEventMask grab_mask) { /* Similar to the above but for pointer events only */ return GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_SCROLL_MASK | (grab_mask & ~(GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK)); } GdkGrabStatus gdk_device_grab (GdkDevice *device, GdkSurface *surface, gboolean owner_events, GdkEventMask event_mask, GdkCursor *cursor, guint32 time_) { GdkGrabStatus res; g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_GRAB_FAILED); g_return_val_if_fail (GDK_IS_SURFACE (surface), GDK_GRAB_FAILED); g_return_val_if_fail (gdk_surface_get_display (surface) == gdk_device_get_display (device), GDK_GRAB_FAILED); if (GDK_SURFACE_DESTROYED (surface)) return GDK_GRAB_NOT_VIEWABLE; res = GDK_DEVICE_GET_CLASS (device)->grab (device, surface, owner_events, get_native_grab_event_mask (event_mask), NULL, cursor, time_); if (res == GDK_GRAB_SUCCESS) { GdkDisplay *display; gulong serial; display = gdk_surface_get_display (surface); serial = _gdk_display_get_next_serial (display); _gdk_display_add_device_grab (display, device, surface, owner_events, event_mask, serial, time_, FALSE); } return res; } void gdk_device_ungrab (GdkDevice *device, guint32 time_) { g_return_if_fail (GDK_IS_DEVICE (device)); GDK_DEVICE_GET_CLASS (device)->ungrab (device, time_); } /* Private API */ void _gdk_device_reset_axes (GdkDevice *device) { int i; for (i = device->axes->len - 1; i >= 0; i--) g_array_remove_index (device->axes, i); g_object_notify_by_pspec (G_OBJECT (device), device_props[PROP_N_AXES]); } guint _gdk_device_add_axis (GdkDevice *device, GdkAxisUse use, double min_value, double max_value, double resolution) { GdkAxisInfo axis_info; guint pos; axis_info.use = use; axis_info.min_value = min_value; axis_info.max_value = max_value; axis_info.resolution = resolution; switch ((guint) use) { case GDK_AXIS_X: case GDK_AXIS_Y: axis_info.min_axis = 0; axis_info.max_axis = 0; break; case GDK_AXIS_XTILT: case GDK_AXIS_YTILT: axis_info.min_axis = -1; axis_info.max_axis = 1; break; default: axis_info.min_axis = 0; axis_info.max_axis = 1; break; } device->axes = g_array_append_val (device->axes, axis_info); pos = device->axes->len - 1; g_object_notify_by_pspec (G_OBJECT (device), device_props[PROP_N_AXES]); return pos; } void _gdk_device_get_axis_info (GdkDevice *device, guint index_, GdkAxisUse *use, double *min_value, double *max_value, double *resolution) { GdkAxisInfo *info; g_return_if_fail (GDK_IS_DEVICE (device)); g_return_if_fail (index_ < device->axes->len); info = &g_array_index (device->axes, GdkAxisInfo, index_); *use = info->use; *min_value = info->min_value; *max_value = info->max_value; *resolution = info->resolution; } static GdkAxisInfo * find_axis_info (GArray *array, GdkAxisUse use) { GdkAxisInfo *info; int i; for (i = 0; i < GDK_AXIS_LAST; i++) { info = &g_array_index (array, GdkAxisInfo, i); if (info->use == use) return info; } return NULL; } gboolean _gdk_device_translate_surface_coord (GdkDevice *device, GdkSurface *surface, guint index_, double value, double *axis_value) { GdkAxisInfo axis_info; GdkAxisInfo *axis_info_x, *axis_info_y; double device_width, device_height; double x_offset, y_offset; double x_scale, y_scale; double x_min, y_min; double x_resolution, y_resolution; double device_aspect; int surface_width, surface_height; if (index_ >= device->axes->len) return FALSE; axis_info = g_array_index (device->axes, GdkAxisInfo, index_); if (axis_info.use != GDK_AXIS_X && axis_info.use != GDK_AXIS_Y) return FALSE; if (axis_info.use == GDK_AXIS_X) { axis_info_x = &axis_info; axis_info_y = find_axis_info (device->axes, GDK_AXIS_Y); if (axis_info_y == NULL) return FALSE; } else { axis_info_x = find_axis_info (device->axes, GDK_AXIS_X); axis_info_y = &axis_info; if (axis_info_x == NULL) return FALSE; } device_width = axis_info_x->max_value - axis_info_x->min_value; device_height = axis_info_y->max_value - axis_info_y->min_value; x_min = axis_info_x->min_value; y_min = axis_info_y->min_value; surface_width = gdk_surface_get_width (surface); surface_height = gdk_surface_get_height (surface); x_resolution = axis_info_x->resolution; y_resolution = axis_info_y->resolution; /* * Some drivers incorrectly report the resolution of the device * as zero (in partiular linuxwacom < 0.5.3 with usb tablets). * This causes the device_aspect to become NaN and totally * breaks windowed mode. If this is the case, the best we can * do is to assume the resolution is non-zero is equal in both * directions (which is true for many devices). The absolute * value of the resolution doesn't matter since we only use the * ratio. */ if (x_resolution == 0 || y_resolution == 0) { x_resolution = 1; y_resolution = 1; } device_aspect = (device_height * y_resolution) / (device_width * x_resolution); if (device_aspect * surface_width >= surface_height) { /* device taller than surface */ x_scale = surface_width / device_width; y_scale = (x_scale * x_resolution) / y_resolution; x_offset = 0; y_offset = - (device_height * y_scale - surface_height) / 2; } else { /* surface taller than device */ y_scale = surface_height / device_height; x_scale = (y_scale * y_resolution) / x_resolution; y_offset = 0; x_offset = - (device_width * x_scale - surface_width) / 2; } if (axis_value) { if (axis_info.use == GDK_AXIS_X) *axis_value = x_offset + x_scale * (value - x_min); else *axis_value = y_offset + y_scale * (value - y_min); } return TRUE; } gboolean _gdk_device_translate_screen_coord (GdkDevice *device, GdkSurface *surface, double surface_root_x, double surface_root_y, double screen_width, double screen_height, guint index_, double value, double *axis_value) { GdkAxisInfo axis_info; double axis_width, scale, offset; if (index_ >= device->axes->len) return FALSE; axis_info = g_array_index (device->axes, GdkAxisInfo, index_); if (axis_info.use != GDK_AXIS_X && axis_info.use != GDK_AXIS_Y) return FALSE; axis_width = axis_info.max_value - axis_info.min_value; if (axis_info.use == GDK_AXIS_X) { if (axis_width > 0) scale = screen_width / axis_width; else scale = 1; offset = - surface_root_x; } else { if (axis_width > 0) scale = screen_height / axis_width; else scale = 1; offset = - surface_root_y; } if (axis_value) *axis_value = offset + scale * (value - axis_info.min_value); return TRUE; } gboolean _gdk_device_translate_axis (GdkDevice *device, guint index_, double value, double *axis_value) { GdkAxisInfo axis_info; double axis_width, out; if (index_ >= device->axes->len) return FALSE; axis_info = g_array_index (device->axes, GdkAxisInfo, index_); if (axis_info.use == GDK_AXIS_X || axis_info.use == GDK_AXIS_Y) return FALSE; axis_width = axis_info.max_value - axis_info.min_value; out = (axis_info.max_axis * (value - axis_info.min_value) + axis_info.min_axis * (axis_info.max_value - value)) / axis_width; if (axis_value) *axis_value = out; return TRUE; } GdkSurface * _gdk_device_surface_at_position (GdkDevice *device, double *win_x, double *win_y, GdkModifierType *mask) { return GDK_DEVICE_GET_CLASS (device)->surface_at_position (device, win_x, win_y, mask); } /** * gdk_device_get_vendor_id: (attributes org.gtk.Method.get_property=vendor-id) * @device: a physical `GdkDevice` * * Returns the vendor ID of this device. * * This ID is retrieved from the device, and does not change. * * This function, together with [method@Gdk.Device.get_product_id], * can be used to eg. compose `GSettings` paths to store settings * for this device. * * ```c * static GSettings * * get_device_settings (GdkDevice *device) * { * const char *vendor, *product; * GSettings *settings; * GdkDevice *device; * char *path; * * vendor = gdk_device_get_vendor_id (device); * product = gdk_device_get_product_id (device); * * path = g_strdup_printf ("/org/example/app/devices/%s:%s/", vendor, product); * settings = g_settings_new_with_path (DEVICE_SCHEMA, path); * g_free (path); * * return settings; * } * ``` * * Returns: (nullable): the vendor ID */ const char * gdk_device_get_vendor_id (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); return device->vendor_id; } /** * gdk_device_get_product_id: (attributes org.gtk.Method.get_property=product-id) * @device: a physical `GdkDevice` * * Returns the product ID of this device. * * This ID is retrieved from the device, and does not change. * See [method@Gdk.Device.get_vendor_id] for more information. * * Returns: (nullable): the product ID */ const char * gdk_device_get_product_id (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); return device->product_id; } void gdk_device_set_seat (GdkDevice *device, GdkSeat *seat) { g_return_if_fail (GDK_IS_DEVICE (device)); g_return_if_fail (!seat || GDK_IS_SEAT (seat)); if (device->seat == seat) return; device->seat = seat; g_object_notify (G_OBJECT (device), "seat"); } /** * gdk_device_get_seat: (attributes org.gtk.Method.get_property=seat) * @device: A `GdkDevice` * * Returns the `GdkSeat` the device belongs to. * * Returns: (transfer none): a `GdkSeat` */ GdkSeat * gdk_device_get_seat (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); return device->seat; } void gdk_device_update_tool (GdkDevice *device, GdkDeviceTool *tool) { g_return_if_fail (GDK_IS_DEVICE (device)); if (g_set_object (&device->last_tool, tool)) { g_object_notify (G_OBJECT (device), "tool"); g_signal_emit (device, signals[TOOL_CHANGED], 0, tool); } } /** * gdk_device_get_num_touches: * @device: a `GdkDevice` * * Retrieves the number of touch points associated to @device. * * Returns: the number of touch points */ guint gdk_device_get_num_touches (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), 0); return device->num_touches; } /** * gdk_device_get_device_tool: (attributes org.gtk.Method.get_property=tool) * @device: a `GdkDevice` * * Retrieves the current tool for @device. * * Returns: (transfer none) (nullable): the `GdkDeviceTool` */ GdkDeviceTool * gdk_device_get_device_tool (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); return device->last_tool; } /** * gdk_device_get_caps_lock_state: (attributes org.gtk.Method.get_property=caps-lock-state) * @device: a `GdkDevice` * * Retrieves whether the Caps Lock modifier of the keyboard is locked. * * This is only relevant for keyboard devices. * * Returns: %TRUE if Caps Lock is on for @device */ gboolean gdk_device_get_caps_lock_state (GdkDevice *device) { GdkKeymap *keymap = gdk_display_get_keymap (device->display); if (device->source == GDK_SOURCE_KEYBOARD) return gdk_keymap_get_caps_lock_state (keymap); return FALSE; } /** * gdk_device_get_num_lock_state: (attributes org.gtk.Method.get_property=num-lock-state) * @device: a ``GdkDevice` * * Retrieves whether the Num Lock modifier of the keyboard is locked. * * This is only relevant for keyboard devices. * * Returns: %TRUE if Num Lock is on for @device */ gboolean gdk_device_get_num_lock_state (GdkDevice *device) { GdkKeymap *keymap = gdk_display_get_keymap (device->display); if (device->source == GDK_SOURCE_KEYBOARD) return gdk_keymap_get_num_lock_state (keymap); return FALSE; } /** * gdk_device_get_scroll_lock_state: (attributes org.gtk.Method.get_property=scroll-lock-state) * @device: a `GdkDevice` * * Retrieves whether the Scroll Lock modifier of the keyboard is locked. * * This is only relevant for keyboard devices. * * Returns: %TRUE if Scroll Lock is on for @device */ gboolean gdk_device_get_scroll_lock_state (GdkDevice *device) { GdkKeymap *keymap = gdk_display_get_keymap (device->display); if (device->source == GDK_SOURCE_KEYBOARD) return gdk_keymap_get_scroll_lock_state (keymap); return FALSE; } /** * gdk_device_get_modifier_state: (attributes org.gtk.Method.get_property=modifier-state) * @device: a `GdkDevice` * * Retrieves the current modifier state of the keyboard. * * This is only relevant for keyboard devices. * * Returns: the current modifier state */ GdkModifierType gdk_device_get_modifier_state (GdkDevice *device) { GdkKeymap *keymap = gdk_display_get_keymap (device->display); if (device->source == GDK_SOURCE_KEYBOARD) return gdk_keymap_get_modifier_state (keymap); return 0; } /** * gdk_device_get_direction: (attributes org.gtk.Method.get_property=direction) * @device: a `GdkDevice` * * Returns the direction of effective layout of the keyboard. * * This is only relevant for keyboard devices. * * The direction of a layout is the direction of the majority * of its symbols. See [func@Pango.unichar_direction]. * * Returns: %PANGO_DIRECTION_LTR or %PANGO_DIRECTION_RTL * if it can determine the direction. %PANGO_DIRECTION_NEUTRAL * otherwise */ PangoDirection gdk_device_get_direction (GdkDevice *device) { GdkKeymap *keymap = gdk_display_get_keymap (device->display); if (device->source == GDK_SOURCE_KEYBOARD) return gdk_keymap_get_direction (keymap); return PANGO_DIRECTION_NEUTRAL; } /** * gdk_device_has_bidi_layouts: (attributes org.gtk.Method.get_property=has-bidi-layouts) * @device: a `GdkDevice` * * Determines if layouts for both right-to-left and * left-to-right languages are in use on the keyboard. * * This is only relevant for keyboard devices. * * Returns: %TRUE if there are layouts with both directions, %FALSE otherwise */ gboolean gdk_device_has_bidi_layouts (GdkDevice *device) { GdkKeymap *keymap = gdk_display_get_keymap (device->display); if (device->source == GDK_SOURCE_KEYBOARD) return gdk_keymap_have_bidi_layouts (keymap); return FALSE; } void gdk_device_set_timestamp (GdkDevice *device, guint32 timestamp) { device->timestamp = timestamp; } /** * gdk_device_get_timestamp: * @device: a `GdkDevice` * * Returns the timestamp of the last activity for this device. * * In practice, this means the timestamp of the last event that was * received from the OS for this device. (GTK may occasionally produce * events for a device that are not received from the OS, and will not * update the timestamp). * * Returns: the timestamp of the last activity for this device * * Since: 4.2 */ guint32 gdk_device_get_timestamp (GdkDevice *device) { g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_CURRENT_TIME); return device->timestamp; }