gtk/gdk/gdkdevice.c
Sophie Herold a546ae32d7 Remove all nicks and blurbs from param specs
Those property features don't seem to be in use anywhere.
They are redundant since the docs cover the same information
and more. They also created unnecessary translation work.

Closes #4904
2022-05-11 18:16:29 +02:00

1380 lines
37 KiB
C

/* GDK - The GIMP Drawing Kit
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <math.h>
#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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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", NULL, NULL,
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;
}