1/* GDK - The GIMP Drawing Kit
2 * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include <math.h>
21
22#include "gdkdeviceprivate.h"
23#include "gdkdevicetool.h"
24#include "gdkdisplayprivate.h"
25#include "gdkintl.h"
26#include "gdkkeysprivate.h"
27
28/**
29 * GdkDevice:
30 *
31 * The `GdkDevice` object represents an input device, such
32 * as a keyboard, a mouse, or a touchpad.
33 *
34 * See the [class@Gdk.Seat] documentation for more information
35 * about the various kinds of devices, and their relationships.
36 */
37
38typedef struct _GdkAxisInfo GdkAxisInfo;
39
40struct _GdkAxisInfo
41{
42 GdkAxisUse use;
43 double min_axis;
44 double max_axis;
45 double min_value;
46 double max_value;
47 double resolution;
48};
49
50enum {
51 CHANGED,
52 TOOL_CHANGED,
53 LAST_SIGNAL
54};
55
56static guint signals [LAST_SIGNAL] = { 0 };
57
58
59static void gdk_device_finalize (GObject *object);
60static void gdk_device_dispose (GObject *object);
61static void gdk_device_set_property (GObject *object,
62 guint prop_id,
63 const GValue *value,
64 GParamSpec *pspec);
65static void gdk_device_get_property (GObject *object,
66 guint prop_id,
67 GValue *value,
68 GParamSpec *pspec);
69
70
71G_DEFINE_ABSTRACT_TYPE (GdkDevice, gdk_device, G_TYPE_OBJECT)
72
73enum {
74 PROP_0,
75 PROP_DISPLAY,
76 PROP_NAME,
77 PROP_SOURCE,
78 PROP_HAS_CURSOR,
79 PROP_N_AXES,
80 PROP_VENDOR_ID,
81 PROP_PRODUCT_ID,
82 PROP_SEAT,
83 PROP_NUM_TOUCHES,
84 PROP_TOOL,
85 PROP_DIRECTION,
86 PROP_HAS_BIDI_LAYOUTS,
87 PROP_CAPS_LOCK_STATE,
88 PROP_NUM_LOCK_STATE,
89 PROP_SCROLL_LOCK_STATE,
90 PROP_MODIFIER_STATE,
91 LAST_PROP
92};
93
94static GParamSpec *device_props[LAST_PROP] = { NULL, };
95
96static void
97gdk_device_class_init (GdkDeviceClass *klass)
98{
99 GObjectClass *object_class = G_OBJECT_CLASS (klass);
100
101 object_class->finalize = gdk_device_finalize;
102 object_class->dispose = gdk_device_dispose;
103 object_class->set_property = gdk_device_set_property;
104 object_class->get_property = gdk_device_get_property;
105
106 /**
107 * GdkDevice:display: (attributes org.gtk.Property.get=gdk_device_get_display)
108 *
109 * The `GdkDisplay` the `GdkDevice` pertains to.
110 */
111 device_props[PROP_DISPLAY] =
112 g_param_spec_object (name: "display",
113 P_("Device Display"),
114 P_("Display which the device belongs to"),
115 GDK_TYPE_DISPLAY,
116 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
117
118 /**
119 * GdkDevice:name: (attributes org.gtk.Property.get=gdk_device_get_name)
120 *
121 * The device name.
122 */
123 device_props[PROP_NAME] =
124 g_param_spec_string (name: "name",
125 P_("Device name"),
126 P_("Device name"),
127 NULL,
128 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
129 G_PARAM_STATIC_STRINGS);
130
131 /**
132 * GdkDevice:source: (attributes org.gtk.Property.get=gdk_device_get_source)
133 *
134 * Source type for the device.
135 */
136 device_props[PROP_SOURCE] =
137 g_param_spec_enum (name: "source",
138 P_("Input source"),
139 P_("Source type for the device"),
140 enum_type: GDK_TYPE_INPUT_SOURCE,
141 default_value: GDK_SOURCE_MOUSE,
142 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
143 G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
144
145 /**
146 * GdkDevice:has-cursor: (attributes org.gtk.Property.get=gdk_device_get_has_cursor)
147 *
148 * Whether the device is represented by a cursor on the screen.
149 */
150 device_props[PROP_HAS_CURSOR] =
151 g_param_spec_boolean (name: "has-cursor",
152 P_("Whether the device has a cursor"),
153 P_("Whether there is a visible cursor following device motion"),
154 FALSE,
155 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
156 G_PARAM_STATIC_STRINGS);
157
158 /**
159 * GdkDevice:n-axes:
160 *
161 * Number of axes in the device.
162 */
163 device_props[PROP_N_AXES] =
164 g_param_spec_uint (name: "n-axes",
165 P_("Number of axes in the device"),
166 P_("Number of axes in the device"),
167 minimum: 0, G_MAXUINT,
168 default_value: 0,
169 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
170
171 /**
172 * GdkDevice:vendor-id: (attributes org.gtk.Property.get=gdk_device_get_vendor_id)
173 *
174 * Vendor ID of this device.
175 *
176 * See [method@Gdk.Device.get_vendor_id].
177 */
178 device_props[PROP_VENDOR_ID] =
179 g_param_spec_string (name: "vendor-id",
180 P_("Vendor ID"),
181 P_("Vendor ID"),
182 NULL,
183 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
184 G_PARAM_STATIC_STRINGS);
185
186 /**
187 * GdkDevice:product-id: (attributes org.gtk.Property.get=gdk_device_get_product_id)
188 *
189 * Product ID of this device.
190 *
191 * See [method@Gdk.Device.get_product_id].
192 */
193 device_props[PROP_PRODUCT_ID] =
194 g_param_spec_string (name: "product-id",
195 P_("Product ID"),
196 P_("Product ID"),
197 NULL,
198 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
199 G_PARAM_STATIC_STRINGS);
200
201 /**
202 * GdkDevice:seat: (attributes org.gtk.Property.get=gdk_device_get_seat)
203 *
204 * `GdkSeat` of this device.
205 */
206 device_props[PROP_SEAT] =
207 g_param_spec_object (name: "seat",
208 P_("Seat"),
209 P_("Seat"),
210 GDK_TYPE_SEAT,
211 flags: G_PARAM_READWRITE |
212 G_PARAM_STATIC_STRINGS);
213
214 /**
215 * GdkDevice:num-touches: (attributes org.gtk.Property.get=gdk_device_get_num_touches)
216 *
217 * The maximal number of concurrent touches on a touch device.
218 *
219 * Will be 0 if the device is not a touch device or if the number
220 * of touches is unknown.
221 */
222 device_props[PROP_NUM_TOUCHES] =
223 g_param_spec_uint (name: "num-touches",
224 P_("Number of concurrent touches"),
225 P_("Number of concurrent touches"),
226 minimum: 0, G_MAXUINT,
227 default_value: 0,
228 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
229 G_PARAM_STATIC_STRINGS);
230
231 /**
232 * GdkDevice:tool: (attributes org.gtk.Property.get=gdk_device_get_device_tool)
233 *
234 * The `GdkDeviceTool` that is currently used with this device.
235 */
236 device_props[PROP_TOOL] =
237 g_param_spec_object (name: "tool",
238 P_("Tool"),
239 P_("The tool that is currently used with this device"),
240 GDK_TYPE_DEVICE_TOOL,
241 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
242
243 /**
244 * GdkDevice:direction: (attributes org.gtk.Property.get=gdk_device_get_direction)
245 *
246 * The direction of the current layout.
247 *
248 * This is only relevant for keyboard devices.
249 */
250 device_props[PROP_DIRECTION] =
251 g_param_spec_enum (name: "direction",
252 P_("Direction"),
253 P_("The direction of the current layout of the keyboard"),
254 enum_type: PANGO_TYPE_DIRECTION, default_value: PANGO_DIRECTION_NEUTRAL,
255 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
256
257 /**
258 * GdkDevice:has-bidi-layouts: (attributes org.gtk.Property.get=gdk_device_has_bidi_layouts)
259 *
260 * Whether the device has both right-to-left and left-to-right layouts.
261 *
262 * This is only relevant for keyboard devices.
263 */
264 device_props[PROP_HAS_BIDI_LAYOUTS] =
265 g_param_spec_boolean (name: "has-bidi-layouts",
266 P_("Has bidi layouts"),
267 P_("Whether the keyboard has bidi layouts"),
268 FALSE,
269 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
270
271 /**
272 * GdkDevice:caps-lock-state: (attributes org.gtk.Property.get=gdk_device_get_caps_lock_state)
273 *
274 * Whether Caps Lock is on.
275 *
276 * This is only relevant for keyboard devices.
277 */
278 device_props[PROP_CAPS_LOCK_STATE] =
279 g_param_spec_boolean (name: "caps-lock-state",
280 P_("Caps lock state"),
281 P_("Whether the keyboard caps lock is on"),
282 FALSE,
283 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
284
285 /**
286 * GdkDevice:num-lock-state: (attributes org.gtk.Property.get=gdk_device_get_num_lock_state)
287 *
288 * Whether Num Lock is on.
289 *
290 * This is only relevant for keyboard devices.
291 */
292 device_props[PROP_NUM_LOCK_STATE] =
293 g_param_spec_boolean (name: "num-lock-state",
294 P_("Num lock state"),
295 P_("Whether the keyboard num lock is on"),
296 FALSE,
297 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
298
299 /**
300 * GdkDevice:scroll-lock-state: (attributes org.gtk.Property.get=gdk_device_get_scroll_lock_state)
301 *
302 * Whether Scroll Lock is on.
303 *
304 * This is only relevant for keyboard devices.
305 */
306 device_props[PROP_SCROLL_LOCK_STATE] =
307 g_param_spec_boolean (name: "scroll-lock-state",
308 P_("Scroll lock state"),
309 P_("Whether the keyboard scroll lock is on"),
310 FALSE,
311 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
312
313 /**
314 * GdkDevice:modifier-state: (attributes org.gtk.Property.get=gdk_device_get_modifier_state)
315 *
316 * The current modifier state of the device.
317 *
318 * This is only relevant for keyboard devices.
319 */
320 device_props[PROP_MODIFIER_STATE] =
321 g_param_spec_flags (name: "modifier-state",
322 P_("Modifier state"),
323 P_("The modifier state of the keyboard"),
324 flags_type: GDK_TYPE_MODIFIER_TYPE, default_value: 0,
325 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
326
327 g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: device_props);
328
329 /**
330 * GdkDevice::changed:
331 * @device: the `GdkDevice`
332 *
333 * Emitted either when the number of either axes or keys changes.
334 *
335 * On X11 this will normally happen when the physical device
336 * routing events through the logical device changes (for
337 * example, user switches from the USB mouse to a tablet); in
338 * that case the logical device will change to reflect the axes
339 * and keys on the new physical device.
340 */
341 signals[CHANGED] =
342 g_signal_new (signal_name: g_intern_static_string (string: "changed"),
343 G_TYPE_FROM_CLASS (object_class),
344 signal_flags: G_SIGNAL_RUN_LAST,
345 class_offset: 0, NULL, NULL,
346 NULL,
347 G_TYPE_NONE, n_params: 0);
348
349 /**
350 * GdkDevice::tool-changed:
351 * @device: the `GdkDevice`
352 * @tool: The new current tool
353 *
354 * Emitted on pen/eraser devices whenever tools enter or leave proximity.
355 */
356 signals[TOOL_CHANGED] =
357 g_signal_new (signal_name: g_intern_static_string (string: "tool-changed"),
358 G_TYPE_FROM_CLASS (object_class),
359 signal_flags: G_SIGNAL_RUN_LAST,
360 class_offset: 0, NULL, NULL,
361 NULL,
362 G_TYPE_NONE, n_params: 1, GDK_TYPE_DEVICE_TOOL);
363}
364
365static void
366gdk_device_init (GdkDevice *device)
367{
368 device->axes = g_array_new (FALSE, TRUE, element_size: sizeof (GdkAxisInfo));
369}
370
371static void
372gdk_device_finalize (GObject *object)
373{
374 GdkDevice *device = GDK_DEVICE (object);
375
376 if (device->axes)
377 {
378 g_array_free (array: device->axes, TRUE);
379 device->axes = NULL;
380 }
381
382 g_clear_pointer (&device->name, g_free);
383 g_clear_pointer (&device->vendor_id, g_free);
384 g_clear_pointer (&device->product_id, g_free);
385
386 G_OBJECT_CLASS (gdk_device_parent_class)->finalize (object);
387}
388
389static void
390gdk_device_dispose (GObject *object)
391{
392 GdkDevice *device = GDK_DEVICE (object);
393 GdkDevice *associated = device->associated;
394
395 if (associated)
396 _gdk_device_remove_physical_device (device: associated, physical: device);
397
398 if (associated)
399 {
400 device->associated = NULL;
401
402 if (associated->associated == device)
403 _gdk_device_set_associated_device (device: associated, NULL);
404
405 g_object_unref (object: associated);
406 }
407
408 g_clear_object (&device->last_tool);
409
410 G_OBJECT_CLASS (gdk_device_parent_class)->dispose (object);
411}
412
413static void
414gdk_device_set_property (GObject *object,
415 guint prop_id,
416 const GValue *value,
417 GParamSpec *pspec)
418{
419 GdkDevice *device = GDK_DEVICE (object);
420
421 switch (prop_id)
422 {
423 case PROP_DISPLAY:
424 device->display = g_value_get_object (value);
425 break;
426 case PROP_NAME:
427 g_free (mem: device->name);
428
429 device->name = g_value_dup_string (value);
430 break;
431 case PROP_SOURCE:
432 device->source = g_value_get_enum (value);
433 break;
434 case PROP_HAS_CURSOR:
435 device->has_cursor = g_value_get_boolean (value);
436 break;
437 case PROP_VENDOR_ID:
438 device->vendor_id = g_value_dup_string (value);
439 break;
440 case PROP_PRODUCT_ID:
441 device->product_id = g_value_dup_string (value);
442 break;
443 case PROP_SEAT:
444 device->seat = g_value_get_object (value);
445 break;
446 case PROP_NUM_TOUCHES:
447 device->num_touches = g_value_get_uint (value);
448 break;
449 default:
450 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
451 break;
452 }
453}
454
455static void
456gdk_device_get_property (GObject *object,
457 guint prop_id,
458 GValue *value,
459 GParamSpec *pspec)
460{
461 GdkDevice *device = GDK_DEVICE (object);
462
463 switch (prop_id)
464 {
465 case PROP_DISPLAY:
466 g_value_set_object (value, v_object: device->display);
467 break;
468 case PROP_NAME:
469 g_value_set_string (value, v_string: device->name);
470 break;
471 case PROP_SOURCE:
472 g_value_set_enum (value, v_enum: device->source);
473 break;
474 case PROP_HAS_CURSOR:
475 g_value_set_boolean (value, v_boolean: device->has_cursor);
476 break;
477 case PROP_N_AXES:
478 g_value_set_uint (value, v_uint: device->axes->len);
479 break;
480 case PROP_VENDOR_ID:
481 g_value_set_string (value, v_string: device->vendor_id);
482 break;
483 case PROP_PRODUCT_ID:
484 g_value_set_string (value, v_string: device->product_id);
485 break;
486 case PROP_SEAT:
487 g_value_set_object (value, v_object: device->seat);
488 break;
489 case PROP_NUM_TOUCHES:
490 g_value_set_uint (value, v_uint: device->num_touches);
491 break;
492 case PROP_TOOL:
493 g_value_set_object (value, v_object: device->last_tool);
494 break;
495 case PROP_DIRECTION:
496 g_value_set_enum (value, v_enum: gdk_device_get_direction (device));
497 break;
498 case PROP_HAS_BIDI_LAYOUTS:
499 g_value_set_boolean (value, v_boolean: gdk_device_has_bidi_layouts (device));
500 break;
501 case PROP_CAPS_LOCK_STATE:
502 g_value_set_boolean (value, v_boolean: gdk_device_get_caps_lock_state (device));
503 break;
504 case PROP_NUM_LOCK_STATE:
505 g_value_set_boolean (value, v_boolean: gdk_device_get_num_lock_state (device));
506 break;
507 case PROP_SCROLL_LOCK_STATE:
508 g_value_set_boolean (value, v_boolean: gdk_device_get_scroll_lock_state (device));
509 break;
510 case PROP_MODIFIER_STATE:
511 g_value_set_flags (value, v_flags: gdk_device_get_modifier_state (device));
512 break;
513 default:
514 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
515 break;
516 }
517}
518
519/**
520 * gdk_device_get_surface_at_position:
521 * @device: pointer `GdkDevice` to query info to
522 * @win_x: (out) (optional): return location for the X coordinate
523 * of the device location relative to the surface origin
524 * @win_y: (out) (optional): return location for the Y coordinate
525 * of the device location relative to the surface origin
526 *
527 * Obtains the surface underneath @device, returning the location of the
528 * device in @win_x and @win_y.
529 *
530 * Returns %NULL if the surface tree under @device is not known to GDK
531 * (for example, belongs to another application).
532 *
533 * Returns: (nullable) (transfer none): the `GdkSurface` under the
534 * device position
535 */
536GdkSurface *
537gdk_device_get_surface_at_position (GdkDevice *device,
538 double *win_x,
539 double *win_y)
540{
541 double tmp_x, tmp_y;
542 GdkSurface *surface;
543
544 g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
545 g_return_val_if_fail (device->source != GDK_SOURCE_KEYBOARD, NULL);
546
547 surface = _gdk_device_surface_at_position (device, win_x: &tmp_x, win_y: &tmp_y, NULL);
548
549 if (win_x)
550 *win_x = tmp_x;
551 if (win_y)
552 *win_y = tmp_y;
553
554 return surface;
555}
556
557/**
558 * gdk_device_get_name:
559 * @device: a GdkDevice`
560 *
561 * The name of the device, suitable for showing in a user interface.
562 *
563 * Returns: a name
564 */
565const char *
566gdk_device_get_name (GdkDevice *device)
567{
568 g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
569
570 return device->name;
571}
572
573/**
574 * gdk_device_get_has_cursor: (attributes org.gtk.Method.get_property=has-cursor)
575 * @device: a `GdkDevice`
576 *
577 * Determines whether the pointer follows device motion.
578 *
579 * This is not meaningful for keyboard devices, which
580 * don't have a pointer.
581 *
582 * Returns: %TRUE if the pointer follows device motion
583 */
584gboolean
585gdk_device_get_has_cursor (GdkDevice *device)
586{
587 g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
588
589 return device->has_cursor;
590}
591
592/**
593 * gdk_device_get_source: (attributes org.gtk.Method.get_property=source)
594 * @device: a `GdkDevice`
595 *
596 * Determines the type of the device.
597 *
598 * Returns: a `GdkInputSource`
599 */
600GdkInputSource
601gdk_device_get_source (GdkDevice *device)
602{
603 g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
604
605 return device->source;
606}
607
608/**
609 * gdk_device_get_axis_use:
610 * @device: a pointer `GdkDevice`
611 * @index_: the index of the axi.
612 *
613 * Returns the axis use for @index_.
614 *
615 * Returns: a `GdkAxisUse` specifying how the axis is used.
616 */
617GdkAxisUse
618gdk_device_get_axis_use (GdkDevice *device,
619 guint index_)
620{
621 GdkAxisInfo *info;
622
623 g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_AXIS_IGNORE);
624 g_return_val_if_fail (device->source != GDK_SOURCE_KEYBOARD, GDK_AXIS_IGNORE);
625 g_return_val_if_fail (index_ < device->axes->len, GDK_AXIS_IGNORE);
626
627 info = &g_array_index (device->axes, GdkAxisInfo, index_);
628
629 return info->use;
630}
631
632/**
633 * gdk_device_get_display: (attributes org.gtk.Method.get_property=display)
634 * @device: a `GdkDevice`
635 *
636 * Returns the `GdkDisplay` to which @device pertains.
637 *
638 * Returns: (transfer none): a `GdkDisplay`
639 */
640GdkDisplay *
641gdk_device_get_display (GdkDevice *device)
642{
643 g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
644
645 return device->display;
646}
647
648void
649_gdk_device_set_associated_device (GdkDevice *device,
650 GdkDevice *associated)
651{
652 g_return_if_fail (GDK_IS_DEVICE (device));
653 g_return_if_fail (associated == NULL || GDK_IS_DEVICE (associated));
654
655 g_set_object (&device->associated, associated);
656}
657
658/*
659 * gdk_device_list_physical_devices:
660 * @device: a logical `GdkDevice`
661 *
662 * Returns the list of physical devices attached to the given logical
663 * `GdkDevice`.
664 *
665 * Returns: (nullable) (transfer container) (element-type GdkDevice):
666 * the list of physical devices attached to a logical `GdkDevice`
667 */
668GList *
669gdk_device_list_physical_devices (GdkDevice *device)
670{
671 g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
672
673 return g_list_copy (list: device->physical_devices);
674}
675
676void
677_gdk_device_add_physical_device (GdkDevice *device,
678 GdkDevice *physical)
679{
680 if (!g_list_find (list: device->physical_devices, data: physical))
681 device->physical_devices = g_list_prepend (list: device->physical_devices, data: physical);
682}
683
684void
685_gdk_device_remove_physical_device (GdkDevice *device,
686 GdkDevice *physical)
687{
688 GList *elem;
689
690 elem = g_list_find (list: device->physical_devices, data: physical);
691 if (elem == NULL)
692 return;
693
694 device->physical_devices = g_list_delete_link (list: device->physical_devices, link_: elem);
695}
696
697/*
698 * gdk_device_get_n_axes:
699 * @device: a pointer `GdkDevice`
700 *
701 * Returns the number of axes the device currently has.
702 *
703 * Returns: the number of axes.
704 */
705int
706gdk_device_get_n_axes (GdkDevice *device)
707{
708 g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
709 g_return_val_if_fail (device->source != GDK_SOURCE_KEYBOARD, 0);
710
711 return device->axes->len;
712}
713
714/*
715 * gdk_device_get_axis: (skip)
716 * @device: a `GdkDevice`
717 * @axes: (array): pointer to an array of axes
718 * @use: the use to look for
719 * @value: (out): location to store the found value
720 *
721 * Interprets an array of `double` as axis values and get the value
722 * for a given axis use.
723 *
724 * Returns: %TRUE if the given axis use was found, otherwise %FALSE
725 */
726gboolean
727gdk_device_get_axis (GdkDevice *device,
728 double *axes,
729 GdkAxisUse use,
730 double *value)
731{
732 int i;
733
734 g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
735 g_return_val_if_fail (device->source != GDK_SOURCE_KEYBOARD, FALSE);
736
737 if (axes == NULL)
738 return FALSE;
739
740 g_return_val_if_fail (device->axes != NULL, FALSE);
741
742 for (i = 0; i < device->axes->len; i++)
743 {
744 GdkAxisInfo axis_info;
745
746 axis_info = g_array_index (device->axes, GdkAxisInfo, i);
747
748 if (axis_info.use != use)
749 continue;
750
751 if (value)
752 *value = axes[i];
753
754 return TRUE;
755 }
756
757 return FALSE;
758}
759
760static GdkEventMask
761get_native_grab_event_mask (GdkEventMask grab_mask)
762{
763 /* Similar to the above but for pointer events only */
764 return
765 GDK_POINTER_MOTION_MASK |
766 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
767 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
768 GDK_SCROLL_MASK |
769 (grab_mask &
770 ~(GDK_BUTTON_MOTION_MASK |
771 GDK_BUTTON1_MOTION_MASK |
772 GDK_BUTTON2_MOTION_MASK |
773 GDK_BUTTON3_MOTION_MASK));
774}
775
776GdkGrabStatus
777gdk_device_grab (GdkDevice *device,
778 GdkSurface *surface,
779 gboolean owner_events,
780 GdkEventMask event_mask,
781 GdkCursor *cursor,
782 guint32 time_)
783{
784 GdkGrabStatus res;
785
786 g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_GRAB_FAILED);
787 g_return_val_if_fail (GDK_IS_SURFACE (surface), GDK_GRAB_FAILED);
788 g_return_val_if_fail (gdk_surface_get_display (surface) == gdk_device_get_display (device), GDK_GRAB_FAILED);
789
790 if (GDK_SURFACE_DESTROYED (surface))
791 return GDK_GRAB_NOT_VIEWABLE;
792
793 res = GDK_DEVICE_GET_CLASS (device)->grab (device,
794 surface,
795 owner_events,
796 get_native_grab_event_mask (grab_mask: event_mask),
797 NULL,
798 cursor,
799 time_);
800
801 if (res == GDK_GRAB_SUCCESS)
802 {
803 GdkDisplay *display;
804 gulong serial;
805
806 display = gdk_surface_get_display (surface);
807 serial = _gdk_display_get_next_serial (display);
808
809 _gdk_display_add_device_grab (display,
810 device,
811 surface,
812 owner_events,
813 event_mask,
814 serial_start: serial,
815 time: time_,
816 FALSE);
817 }
818
819 return res;
820}
821
822void
823gdk_device_ungrab (GdkDevice *device,
824 guint32 time_)
825{
826 g_return_if_fail (GDK_IS_DEVICE (device));
827
828 GDK_DEVICE_GET_CLASS (device)->ungrab (device, time_);
829}
830
831/* Private API */
832void
833_gdk_device_reset_axes (GdkDevice *device)
834{
835 int i;
836
837 for (i = device->axes->len - 1; i >= 0; i--)
838 g_array_remove_index (array: device->axes, index_: i);
839
840 g_object_notify_by_pspec (G_OBJECT (device), pspec: device_props[PROP_N_AXES]);
841}
842
843guint
844_gdk_device_add_axis (GdkDevice *device,
845 GdkAxisUse use,
846 double min_value,
847 double max_value,
848 double resolution)
849{
850 GdkAxisInfo axis_info;
851 guint pos;
852
853 axis_info.use = use;
854 axis_info.min_value = min_value;
855 axis_info.max_value = max_value;
856 axis_info.resolution = resolution;
857
858 switch ((guint) use)
859 {
860 case GDK_AXIS_X:
861 case GDK_AXIS_Y:
862 axis_info.min_axis = 0;
863 axis_info.max_axis = 0;
864 break;
865 case GDK_AXIS_XTILT:
866 case GDK_AXIS_YTILT:
867 axis_info.min_axis = -1;
868 axis_info.max_axis = 1;
869 break;
870 default:
871 axis_info.min_axis = 0;
872 axis_info.max_axis = 1;
873 break;
874 }
875
876 device->axes = g_array_append_val (device->axes, axis_info);
877 pos = device->axes->len - 1;
878
879 g_object_notify_by_pspec (G_OBJECT (device), pspec: device_props[PROP_N_AXES]);
880
881 return pos;
882}
883
884void
885_gdk_device_get_axis_info (GdkDevice *device,
886 guint index_,
887 GdkAxisUse *use,
888 double *min_value,
889 double *max_value,
890 double *resolution)
891{
892 GdkAxisInfo *info;
893
894 g_return_if_fail (GDK_IS_DEVICE (device));
895 g_return_if_fail (index_ < device->axes->len);
896
897 info = &g_array_index (device->axes, GdkAxisInfo, index_);
898
899 *use = info->use;
900 *min_value = info->min_value;
901 *max_value = info->max_value;
902 *resolution = info->resolution;
903}
904
905static GdkAxisInfo *
906find_axis_info (GArray *array,
907 GdkAxisUse use)
908{
909 GdkAxisInfo *info;
910 int i;
911
912 for (i = 0; i < GDK_AXIS_LAST; i++)
913 {
914 info = &g_array_index (array, GdkAxisInfo, i);
915
916 if (info->use == use)
917 return info;
918 }
919
920 return NULL;
921}
922
923gboolean
924_gdk_device_translate_surface_coord (GdkDevice *device,
925 GdkSurface *surface,
926 guint index_,
927 double value,
928 double *axis_value)
929{
930 GdkAxisInfo axis_info;
931 GdkAxisInfo *axis_info_x, *axis_info_y;
932 double device_width, device_height;
933 double x_offset, y_offset;
934 double x_scale, y_scale;
935 double x_min, y_min;
936 double x_resolution, y_resolution;
937 double device_aspect;
938 int surface_width, surface_height;
939
940 if (index_ >= device->axes->len)
941 return FALSE;
942
943 axis_info = g_array_index (device->axes, GdkAxisInfo, index_);
944
945 if (axis_info.use != GDK_AXIS_X &&
946 axis_info.use != GDK_AXIS_Y)
947 return FALSE;
948
949 if (axis_info.use == GDK_AXIS_X)
950 {
951 axis_info_x = &axis_info;
952 axis_info_y = find_axis_info (array: device->axes, use: GDK_AXIS_Y);
953 if (axis_info_y == NULL)
954 return FALSE;
955 }
956 else
957 {
958 axis_info_x = find_axis_info (array: device->axes, use: GDK_AXIS_X);
959 axis_info_y = &axis_info;
960 if (axis_info_x == NULL)
961 return FALSE;
962 }
963
964 device_width = axis_info_x->max_value - axis_info_x->min_value;
965 device_height = axis_info_y->max_value - axis_info_y->min_value;
966
967 x_min = axis_info_x->min_value;
968 y_min = axis_info_y->min_value;
969
970 surface_width = gdk_surface_get_width (surface);
971 surface_height = gdk_surface_get_height (surface);
972
973 x_resolution = axis_info_x->resolution;
974 y_resolution = axis_info_y->resolution;
975
976 /*
977 * Some drivers incorrectly report the resolution of the device
978 * as zero (in partiular linuxwacom < 0.5.3 with usb tablets).
979 * This causes the device_aspect to become NaN and totally
980 * breaks windowed mode. If this is the case, the best we can
981 * do is to assume the resolution is non-zero is equal in both
982 * directions (which is true for many devices). The absolute
983 * value of the resolution doesn't matter since we only use the
984 * ratio.
985 */
986 if (x_resolution == 0 || y_resolution == 0)
987 {
988 x_resolution = 1;
989 y_resolution = 1;
990 }
991
992 device_aspect = (device_height * y_resolution) /
993 (device_width * x_resolution);
994
995 if (device_aspect * surface_width >= surface_height)
996 {
997 /* device taller than surface */
998 x_scale = surface_width / device_width;
999 y_scale = (x_scale * x_resolution) / y_resolution;
1000
1001 x_offset = 0;
1002 y_offset = - (device_height * y_scale - surface_height) / 2;
1003 }
1004 else
1005 {
1006 /* surface taller than device */
1007 y_scale = surface_height / device_height;
1008 x_scale = (y_scale * y_resolution) / x_resolution;
1009
1010 y_offset = 0;
1011 x_offset = - (device_width * x_scale - surface_width) / 2;
1012 }
1013
1014 if (axis_value)
1015 {
1016 if (axis_info.use == GDK_AXIS_X)
1017 *axis_value = x_offset + x_scale * (value - x_min);
1018 else
1019 *axis_value = y_offset + y_scale * (value - y_min);
1020 }
1021
1022 return TRUE;
1023}
1024
1025gboolean
1026_gdk_device_translate_screen_coord (GdkDevice *device,
1027 GdkSurface *surface,
1028 double surface_root_x,
1029 double surface_root_y,
1030 double screen_width,
1031 double screen_height,
1032 guint index_,
1033 double value,
1034 double *axis_value)
1035{
1036 GdkAxisInfo axis_info;
1037 double axis_width, scale, offset;
1038
1039 if (index_ >= device->axes->len)
1040 return FALSE;
1041
1042 axis_info = g_array_index (device->axes, GdkAxisInfo, index_);
1043
1044 if (axis_info.use != GDK_AXIS_X &&
1045 axis_info.use != GDK_AXIS_Y)
1046 return FALSE;
1047
1048 axis_width = axis_info.max_value - axis_info.min_value;
1049
1050 if (axis_info.use == GDK_AXIS_X)
1051 {
1052 if (axis_width > 0)
1053 scale = screen_width / axis_width;
1054 else
1055 scale = 1;
1056
1057 offset = - surface_root_x;
1058 }
1059 else
1060 {
1061 if (axis_width > 0)
1062 scale = screen_height / axis_width;
1063 else
1064 scale = 1;
1065
1066 offset = - surface_root_y;
1067 }
1068
1069 if (axis_value)
1070 *axis_value = offset + scale * (value - axis_info.min_value);
1071
1072 return TRUE;
1073}
1074
1075gboolean
1076_gdk_device_translate_axis (GdkDevice *device,
1077 guint index_,
1078 double value,
1079 double *axis_value)
1080{
1081 GdkAxisInfo axis_info;
1082 double axis_width, out;
1083
1084 if (index_ >= device->axes->len)
1085 return FALSE;
1086
1087 axis_info = g_array_index (device->axes, GdkAxisInfo, index_);
1088
1089 if (axis_info.use == GDK_AXIS_X ||
1090 axis_info.use == GDK_AXIS_Y)
1091 return FALSE;
1092
1093 axis_width = axis_info.max_value - axis_info.min_value;
1094 out = (axis_info.max_axis * (value - axis_info.min_value) +
1095 axis_info.min_axis * (axis_info.max_value - value)) / axis_width;
1096
1097 if (axis_value)
1098 *axis_value = out;
1099
1100 return TRUE;
1101}
1102
1103GdkSurface *
1104_gdk_device_surface_at_position (GdkDevice *device,
1105 double *win_x,
1106 double *win_y,
1107 GdkModifierType *mask)
1108{
1109 return GDK_DEVICE_GET_CLASS (device)->surface_at_position (device,
1110 win_x,
1111 win_y,
1112 mask);
1113}
1114
1115/**
1116 * gdk_device_get_vendor_id: (attributes org.gtk.Method.get_property=vendor-id)
1117 * @device: a physical `GdkDevice`
1118 *
1119 * Returns the vendor ID of this device.
1120 *
1121 * This ID is retrieved from the device, and does not change.
1122 *
1123 * This function, together with [method@Gdk.Device.get_product_id],
1124 * can be used to eg. compose `GSettings` paths to store settings
1125 * for this device.
1126 *
1127 * ```c
1128 * static GSettings *
1129 * get_device_settings (GdkDevice *device)
1130 * {
1131 * const char *vendor, *product;
1132 * GSettings *settings;
1133 * GdkDevice *device;
1134 * char *path;
1135 *
1136 * vendor = gdk_device_get_vendor_id (device);
1137 * product = gdk_device_get_product_id (device);
1138 *
1139 * path = g_strdup_printf ("/org/example/app/devices/%s:%s/", vendor, product);
1140 * settings = g_settings_new_with_path (DEVICE_SCHEMA, path);
1141 * g_free (path);
1142 *
1143 * return settings;
1144 * }
1145 * ```
1146 *
1147 * Returns: (nullable): the vendor ID
1148 */
1149const char *
1150gdk_device_get_vendor_id (GdkDevice *device)
1151{
1152 g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
1153
1154 return device->vendor_id;
1155}
1156
1157/**
1158 * gdk_device_get_product_id: (attributes org.gtk.Method.get_property=product-id)
1159 * @device: a physical `GdkDevice`
1160 *
1161 * Returns the product ID of this device.
1162 *
1163 * This ID is retrieved from the device, and does not change.
1164 * See [method@Gdk.Device.get_vendor_id] for more information.
1165 *
1166 * Returns: (nullable): the product ID
1167 */
1168const char *
1169gdk_device_get_product_id (GdkDevice *device)
1170{
1171 g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
1172
1173 return device->product_id;
1174}
1175
1176void
1177gdk_device_set_seat (GdkDevice *device,
1178 GdkSeat *seat)
1179{
1180 g_return_if_fail (GDK_IS_DEVICE (device));
1181 g_return_if_fail (!seat || GDK_IS_SEAT (seat));
1182
1183 if (device->seat == seat)
1184 return;
1185
1186 device->seat = seat;
1187 g_object_notify (G_OBJECT (device), property_name: "seat");
1188}
1189
1190/**
1191 * gdk_device_get_seat: (attributes org.gtk.Method.get_property=seat)
1192 * @device: A `GdkDevice`
1193 *
1194 * Returns the `GdkSeat` the device belongs to.
1195 *
1196 * Returns: (transfer none): a `GdkSeat`
1197 */
1198GdkSeat *
1199gdk_device_get_seat (GdkDevice *device)
1200{
1201 g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
1202
1203 return device->seat;
1204}
1205
1206void
1207gdk_device_update_tool (GdkDevice *device,
1208 GdkDeviceTool *tool)
1209{
1210 g_return_if_fail (GDK_IS_DEVICE (device));
1211
1212 if (g_set_object (&device->last_tool, tool))
1213 {
1214 g_object_notify (G_OBJECT (device), property_name: "tool");
1215 g_signal_emit (instance: device, signal_id: signals[TOOL_CHANGED], detail: 0, tool);
1216 }
1217}
1218
1219/**
1220 * gdk_device_get_num_touches:
1221 * @device: a `GdkDevice`
1222 *
1223 * Retrieves the number of touch points associated to @device.
1224 *
1225 * Returns: the number of touch points
1226 */
1227guint
1228gdk_device_get_num_touches (GdkDevice *device)
1229{
1230 g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
1231
1232 return device->num_touches;
1233}
1234
1235/**
1236 * gdk_device_get_device_tool: (attributes org.gtk.Method.get_property=tool)
1237 * @device: a `GdkDevice`
1238 *
1239 * Retrieves the current tool for @device.
1240 *
1241 * Returns: (transfer none) (nullable): the `GdkDeviceTool`
1242 */
1243GdkDeviceTool *
1244gdk_device_get_device_tool (GdkDevice *device)
1245{
1246 g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
1247
1248 return device->last_tool;
1249}
1250
1251/**
1252 * gdk_device_get_caps_lock_state: (attributes org.gtk.Method.get_property=caps-lock-state)
1253 * @device: a `GdkDevice`
1254 *
1255 * Retrieves whether the Caps Lock modifier of the keyboard is locked.
1256 *
1257 * This is only relevant for keyboard devices.
1258 *
1259 * Returns: %TRUE if Caps Lock is on for @device
1260 */
1261gboolean
1262gdk_device_get_caps_lock_state (GdkDevice *device)
1263{
1264 GdkKeymap *keymap = gdk_display_get_keymap (display: device->display);
1265
1266 if (device->source == GDK_SOURCE_KEYBOARD)
1267 return gdk_keymap_get_caps_lock_state (keymap);
1268
1269 return FALSE;
1270}
1271
1272/**
1273 * gdk_device_get_num_lock_state: (attributes org.gtk.Method.get_property=num-lock-state)
1274 * @device: a ``GdkDevice`
1275 *
1276 * Retrieves whether the Num Lock modifier of the keyboard is locked.
1277 *
1278 * This is only relevant for keyboard devices.
1279 *
1280 * Returns: %TRUE if Num Lock is on for @device
1281 */
1282gboolean
1283gdk_device_get_num_lock_state (GdkDevice *device)
1284{
1285 GdkKeymap *keymap = gdk_display_get_keymap (display: device->display);
1286
1287 if (device->source == GDK_SOURCE_KEYBOARD)
1288 return gdk_keymap_get_num_lock_state (keymap);
1289
1290 return FALSE;
1291}
1292
1293/**
1294 * gdk_device_get_scroll_lock_state: (attributes org.gtk.Method.get_property=scroll-lock-state)
1295 * @device: a `GdkDevice`
1296 *
1297 * Retrieves whether the Scroll Lock modifier of the keyboard is locked.
1298 *
1299 * This is only relevant for keyboard devices.
1300 *
1301 * Returns: %TRUE if Scroll Lock is on for @device
1302 */
1303gboolean
1304gdk_device_get_scroll_lock_state (GdkDevice *device)
1305{
1306 GdkKeymap *keymap = gdk_display_get_keymap (display: device->display);
1307
1308 if (device->source == GDK_SOURCE_KEYBOARD)
1309 return gdk_keymap_get_scroll_lock_state (keymap);
1310
1311 return FALSE;
1312}
1313
1314/**
1315 * gdk_device_get_modifier_state: (attributes org.gtk.Method.get_property=modifier-state)
1316 * @device: a `GdkDevice`
1317 *
1318 * Retrieves the current modifier state of the keyboard.
1319 *
1320 * This is only relevant for keyboard devices.
1321 *
1322 * Returns: the current modifier state
1323 */
1324GdkModifierType
1325gdk_device_get_modifier_state (GdkDevice *device)
1326{
1327 GdkKeymap *keymap = gdk_display_get_keymap (display: device->display);
1328
1329 if (device->source == GDK_SOURCE_KEYBOARD)
1330 return gdk_keymap_get_modifier_state (keymap);
1331
1332 return 0;
1333}
1334
1335/**
1336 * gdk_device_get_direction: (attributes org.gtk.Method.get_property=direction)
1337 * @device: a `GdkDevice`
1338 *
1339 * Returns the direction of effective layout of the keyboard.
1340 *
1341 * This is only relevant for keyboard devices.
1342 *
1343 * The direction of a layout is the direction of the majority
1344 * of its symbols. See [func@Pango.unichar_direction].
1345 *
1346 * Returns: %PANGO_DIRECTION_LTR or %PANGO_DIRECTION_RTL
1347 * if it can determine the direction. %PANGO_DIRECTION_NEUTRAL
1348 * otherwise
1349 */
1350PangoDirection
1351gdk_device_get_direction (GdkDevice *device)
1352{
1353 GdkKeymap *keymap = gdk_display_get_keymap (display: device->display);
1354
1355 if (device->source == GDK_SOURCE_KEYBOARD)
1356 return gdk_keymap_get_direction (keymap);
1357
1358 return PANGO_DIRECTION_NEUTRAL;
1359}
1360
1361/**
1362 * gdk_device_has_bidi_layouts: (attributes org.gtk.Method.get_property=has-bidi-layouts)
1363 * @device: a `GdkDevice`
1364 *
1365 * Determines if layouts for both right-to-left and
1366 * left-to-right languages are in use on the keyboard.
1367 *
1368 * This is only relevant for keyboard devices.
1369 *
1370 * Returns: %TRUE if there are layouts with both directions, %FALSE otherwise
1371 */
1372gboolean
1373gdk_device_has_bidi_layouts (GdkDevice *device)
1374{
1375 GdkKeymap *keymap = gdk_display_get_keymap (display: device->display);
1376
1377 if (device->source == GDK_SOURCE_KEYBOARD)
1378 return gdk_keymap_have_bidi_layouts (keymap);
1379
1380 return FALSE;
1381}
1382
1383void
1384gdk_device_set_timestamp (GdkDevice *device,
1385 guint32 timestamp)
1386{
1387 device->timestamp = timestamp;
1388}
1389
1390/**
1391 * gdk_device_get_timestamp:
1392 * @device: a `GdkDevice`
1393 *
1394 * Returns the timestamp of the last activity for this device.
1395 *
1396 * In practice, this means the timestamp of the last event that was
1397 * received from the OS for this device. (GTK may occasionally produce
1398 * events for a device that are not received from the OS, and will not
1399 * update the timestamp).
1400 *
1401 * Returns: the timestamp of the last activity for this device
1402 *
1403 * Since: 4.2
1404 */
1405guint32
1406gdk_device_get_timestamp (GdkDevice *device)
1407{
1408 g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_CURRENT_TIME);
1409
1410 return device->timestamp;
1411}
1412

source code of gtk/gdk/gdkdevice.c