]> Pileus Git - ~andy/gtk/blob - gdk/gdkdevice.c
Merge branch 'windows_list'
[~andy/gtk] / gdk / gdkdevice.c
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21 #include "gdkdevice.h"
22 #include "gdkdeviceprivate.h"
23 #include "gdkintl.h"
24 #include "gdkinternals.h"
25
26
27 typedef struct _GdkAxisInfo GdkAxisInfo;
28
29 struct _GdkAxisInfo
30 {
31   GdkAtom label;
32   GdkAxisUse use;
33
34   gdouble min_axis;
35   gdouble max_axis;
36
37   gdouble min_value;
38   gdouble max_value;
39   gdouble resolution;
40 };
41
42 struct _GdkDevicePrivate
43 {
44   GdkDeviceManager *device_manager;
45   GdkDisplay *display;
46   GdkDevice *associated;
47   GdkDeviceType type;
48   GArray *axes;
49 };
50
51 static void gdk_device_dispose      (GObject      *object);
52 static void gdk_device_set_property (GObject      *object,
53                                      guint         prop_id,
54                                      const GValue *value,
55                                      GParamSpec   *pspec);
56 static void gdk_device_get_property (GObject      *object,
57                                      guint         prop_id,
58                                      GValue       *value,
59                                      GParamSpec   *pspec);
60
61
62 G_DEFINE_ABSTRACT_TYPE (GdkDevice, gdk_device, G_TYPE_OBJECT)
63
64 enum {
65   PROP_0,
66   PROP_DISPLAY,
67   PROP_DEVICE_MANAGER,
68   PROP_NAME,
69   PROP_ASSOCIATED_DEVICE,
70   PROP_TYPE,
71   PROP_INPUT_SOURCE,
72   PROP_INPUT_MODE,
73   PROP_HAS_CURSOR,
74   PROP_N_AXES
75 };
76
77
78 static void
79 gdk_device_class_init (GdkDeviceClass *klass)
80 {
81   GObjectClass *object_class = G_OBJECT_CLASS (klass);
82
83   object_class->dispose = gdk_device_dispose;
84   object_class->set_property = gdk_device_set_property;
85   object_class->get_property = gdk_device_get_property;
86
87   /**
88    * GdkDevice:display:
89    *
90    * The #GdkDisplay the #GdkDevice pertains to.
91    *
92    * Since: 3.0
93    */
94   g_object_class_install_property (object_class,
95                                    PROP_DISPLAY,
96                                    g_param_spec_object ("display",
97                                                         P_("Device Display"),
98                                                         P_("Display to which the device belongs to"),
99                                                         GDK_TYPE_DISPLAY,
100                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
101                                                         G_PARAM_STATIC_STRINGS));
102   /**
103    * GdkDevice:device-manager:
104    *
105    * The #GdkDeviceManager the #GdkDevice pertains to.
106    *
107    * Since: 3.0
108    */
109   g_object_class_install_property (object_class,
110                                    PROP_DEVICE_MANAGER,
111                                    g_param_spec_object ("device-manager",
112                                                         P_("Device manager"),
113                                                         P_("Device manager to which the device belongs to"),
114                                                         GDK_TYPE_DEVICE_MANAGER,
115                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
116                                                         G_PARAM_STATIC_STRINGS));
117   /**
118    * GdkDevice:name:
119    *
120    * The device name.
121    *
122    * Since: 3.0
123    */
124   g_object_class_install_property (object_class,
125                                    PROP_NAME,
126                                    g_param_spec_string ("name",
127                                                         P_("Device name"),
128                                                         P_("Device name"),
129                                                         NULL,
130                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
131                                                         G_PARAM_STATIC_STRINGS));
132   /**
133    * GdkDevice:type:
134    *
135    * Device role in the device manager.
136    *
137    * Since: 3.0
138    */
139   g_object_class_install_property (object_class,
140                                    PROP_TYPE,
141                                    g_param_spec_enum ("type",
142                                                       P_("Device type"),
143                                                       P_("Device role in the device manager"),
144                                                       GDK_TYPE_DEVICE_TYPE,
145                                                       GDK_DEVICE_TYPE_MASTER,
146                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
147                                                       G_PARAM_STATIC_STRINGS));
148   /**
149    * GdkDevice:associated-device:
150    *
151    * Associated pointer or keyboard to this device, if any. Devices of type #GDK_DEVICE_TYPE_MASTER
152    * always come in keyboard/pointer pairs. Other device types will have a %NULL associated device.
153    *
154    * Since: 3.0
155    */
156   g_object_class_install_property (object_class,
157                                    PROP_ASSOCIATED_DEVICE,
158                                    g_param_spec_object ("associated-device",
159                                                         P_("Associated device"),
160                                                         P_("Associated pointer or keyboard to this device"),
161                                                         GDK_TYPE_DEVICE,
162                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
163   /**
164    * GdkDevice:input-source:
165    *
166    * Source type for the device.
167    *
168    * Since: 3.0
169    */
170   g_object_class_install_property (object_class,
171                                    PROP_INPUT_SOURCE,
172                                    g_param_spec_enum ("input-source",
173                                                       P_("Input source"),
174                                                       P_("Source type for the device"),
175                                                       GDK_TYPE_INPUT_SOURCE,
176                                                       GDK_SOURCE_MOUSE,
177                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
178                                                       G_PARAM_STATIC_STRINGS));
179   /**
180    * GdkDevice:input-mode:
181    *
182    * Input mode for the device.
183    *
184    * Since: 3.0
185    */
186   g_object_class_install_property (object_class,
187                                    PROP_INPUT_MODE,
188                                    g_param_spec_enum ("input-mode",
189                                                       P_("Input mode for the device"),
190                                                       P_("Input mode for the device"),
191                                                       GDK_TYPE_INPUT_MODE,
192                                                       GDK_MODE_DISABLED,
193                                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
194   /**
195    * GdkDevice:has-cursor:
196    *
197    * Whether the device is represented by a cursor on the screen. Devices of type
198    * %GDK_DEVICE_TYPE_MASTER will have %TRUE here.
199    *
200    * Since: 3.0
201    */
202   g_object_class_install_property (object_class,
203                                    PROP_HAS_CURSOR,
204                                    g_param_spec_boolean ("has-cursor",
205                                                          P_("Whether the device has cursor"),
206                                                          P_("Whether there is a visible cursor following device motion"),
207                                                          FALSE,
208                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
209                                                          G_PARAM_STATIC_STRINGS));
210   /**
211    * GdkDevice:n-axes:
212    *
213    * Number of axes in the device.
214    *
215    * Since: 3.0
216    */
217   g_object_class_install_property (object_class,
218                                    PROP_N_AXES,
219                                    g_param_spec_uint ("n-axes",
220                                                       P_("Number of axes in the device"),
221                                                       P_("Number of axes in the device"),
222                                                       0, G_MAXUINT, 0,
223                                                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
224
225   g_type_class_add_private (object_class, sizeof (GdkDevicePrivate));
226 }
227
228 static void
229 gdk_device_init (GdkDevice *device)
230 {
231   GdkDevicePrivate *priv;
232
233   device->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
234                                                      GDK_TYPE_DEVICE,
235                                                      GdkDevicePrivate);
236
237   priv->axes = g_array_new (FALSE, TRUE, sizeof (GdkAxisInfo));
238 }
239
240 static void
241 gdk_device_dispose (GObject *object)
242 {
243   GdkDevicePrivate *priv;
244   GdkDevice *device;
245
246   device = GDK_DEVICE (object);
247   priv = device->priv;
248
249   if (priv->associated)
250     {
251       _gdk_device_set_associated_device (priv->associated, NULL);
252       g_object_unref (priv->associated);
253       priv->associated = NULL;
254     }
255
256   if (priv->axes)
257     {
258       g_array_free (priv->axes, TRUE);
259       priv->axes = NULL;
260     }
261
262   g_free (device->name);
263   g_free (device->keys);
264   g_free (device->axes);
265
266   device->name = NULL;
267   device->keys = NULL;
268   device->axes = NULL;
269
270   G_OBJECT_CLASS (gdk_device_parent_class)->dispose (object);
271 }
272
273 static void
274 gdk_device_set_property (GObject      *object,
275                          guint         prop_id,
276                          const GValue *value,
277                          GParamSpec   *pspec)
278 {
279   GdkDevice *device = GDK_DEVICE (object);
280   GdkDevicePrivate *priv = device->priv;
281
282   switch (prop_id)
283     {
284     case PROP_DISPLAY:
285       priv->display = g_value_get_object (value);
286       break;
287     case PROP_DEVICE_MANAGER:
288       priv->device_manager = g_value_get_object (value);
289       break;
290     case PROP_NAME:
291       if (device->name)
292         g_free (device->name);
293
294       device->name = g_value_dup_string (value);
295       break;
296     case PROP_TYPE:
297       priv->type = g_value_get_enum (value);
298       break;
299     case PROP_INPUT_SOURCE:
300       device->source = g_value_get_enum (value);
301       break;
302     case PROP_INPUT_MODE:
303       gdk_device_set_mode (device, g_value_get_enum (value));
304       break;
305     case PROP_HAS_CURSOR:
306       device->has_cursor = g_value_get_boolean (value);
307       break;
308     default:
309       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
310       break;
311     }
312 }
313
314 static void
315 gdk_device_get_property (GObject    *object,
316                          guint       prop_id,
317                          GValue     *value,
318                          GParamSpec *pspec)
319 {
320   GdkDevice *device = GDK_DEVICE (object);
321   GdkDevicePrivate *priv = device->priv;
322
323   switch (prop_id)
324     {
325     case PROP_DISPLAY:
326       g_value_set_object (value, priv->display);
327       break;
328     case PROP_DEVICE_MANAGER:
329       g_value_set_object (value, priv->device_manager);
330       break;
331     case PROP_ASSOCIATED_DEVICE:
332       g_value_set_object (value, priv->associated);
333       break;
334     case PROP_NAME:
335       g_value_set_string (value,
336                           device->name);
337       break;
338     case PROP_TYPE:
339       g_value_set_enum (value, priv->type);
340       break;
341     case PROP_INPUT_SOURCE:
342       g_value_set_enum (value, device->source);
343       break;
344     case PROP_INPUT_MODE:
345       g_value_set_enum (value, device->mode);
346       break;
347     case PROP_HAS_CURSOR:
348       g_value_set_boolean (value,
349                            device->has_cursor);
350       break;
351     case PROP_N_AXES:
352       g_value_set_uint (value, priv->axes->len);
353       break;
354     default:
355       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
356       break;
357     }
358 }
359
360 /**
361  * gdk_device_get_state:
362  * @device: a #GdkDevice.
363  * @window: a #GdkWindow.
364  * @axes: an array of doubles to store the values of the axes of @device in,
365  * or %NULL.
366  * @mask: location to store the modifiers, or %NULL.
367  *
368  * Gets the current state of a device relative to @window.
369  */
370 void
371 gdk_device_get_state (GdkDevice       *device,
372                       GdkWindow       *window,
373                       gdouble         *axes,
374                       GdkModifierType *mask)
375 {
376   g_return_if_fail (GDK_IS_DEVICE (device));
377   g_return_if_fail (GDK_IS_WINDOW (window));
378
379   if (GDK_DEVICE_GET_CLASS (device)->get_state)
380     GDK_DEVICE_GET_CLASS (device)->get_state (device, window, axes, mask);
381 }
382
383 /**
384  * gdk_device_get_history:
385  * @device: a #GdkDevice
386  * @window: the window with respect to which which the event coordinates will be reported
387  * @start: starting timestamp for range of events to return
388  * @stop: ending timestamp for the range of events to return
389  * @events: (array length=n_events) (out) (transfer none): location to store a newly-allocated array of #GdkTimeCoord, or %NULL
390  * @n_events: location to store the length of @events, or %NULL
391  *
392  * Obtains the motion history for a device; given a starting and
393  * ending timestamp, return all events in the motion history for
394  * the device in the given range of time. Some windowing systems
395  * do not support motion history, in which case, %FALSE will
396  * be returned. (This is not distinguishable from the case where
397  * motion history is supported and no events were found.)
398  *
399  * Return value: %TRUE if the windowing system supports motion history and
400  *  at least one event was found.
401  **/
402 gboolean
403 gdk_device_get_history (GdkDevice      *device,
404                         GdkWindow      *window,
405                         guint32         start,
406                         guint32         stop,
407                         GdkTimeCoord ***events,
408                         guint          *n_events)
409 {
410   g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
411   g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
412
413   if (n_events)
414     *n_events = 0;
415
416   if (events)
417     *events = NULL;
418
419   if (GDK_WINDOW_DESTROYED (window))
420     return FALSE;
421
422   if (!GDK_DEVICE_GET_CLASS (device)->get_history)
423     return FALSE;
424
425   return GDK_DEVICE_GET_CLASS (device)->get_history (device, window,
426                                                      start, stop,
427                                                      events, n_events);
428 }
429
430 GdkTimeCoord **
431 _gdk_device_allocate_history (GdkDevice *device,
432                               guint      n_events)
433 {
434   GdkTimeCoord **result = g_new (GdkTimeCoord *, n_events);
435   gint i;
436
437   for (i = 0; i < n_events; i++)
438     result[i] = g_malloc (sizeof (GdkTimeCoord) -
439                           sizeof (double) * (GDK_MAX_TIMECOORD_AXES - device->num_axes));
440   return result;
441 }
442
443 /**
444  * gdk_device_free_history:
445  * @events: (inout) (transfer none): an array of #GdkTimeCoord.
446  * @n_events: the length of the array.
447  *
448  * Frees an array of #GdkTimeCoord that was returned by gdk_device_get_history().
449  */
450 void
451 gdk_device_free_history (GdkTimeCoord **events,
452                          gint           n_events)
453 {
454   gint i;
455
456   for (i = 0; i < n_events; i++)
457     g_free (events[i]);
458
459   g_free (events);
460 }
461
462 /**
463  * gdk_device_get_name:
464  * @device: a #GdkDevice
465  *
466  * Determines the name of the device.
467  *
468  * Return value: a name
469  *
470  * Since: 2.20
471  **/
472 const gchar *
473 gdk_device_get_name (GdkDevice *device)
474 {
475   g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
476
477   return device->name;
478 }
479
480 /**
481  * gdk_device_get_has_cursor:
482  * @device: a #GdkDevice
483  *
484  * Determines whether the pointer follows device motion.
485  *
486  * Return value: %TRUE if the pointer follows device motion
487  *
488  * Since: 2.20
489  **/
490 gboolean
491 gdk_device_get_has_cursor (GdkDevice *device)
492 {
493   g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
494
495   return device->has_cursor;
496 }
497
498 /**
499  * gdk_device_get_source:
500  * @device: a #GdkDevice
501  *
502  * Determines the type of the device.
503  *
504  * Return value: a #GdkInputSource
505  *
506  * Since: 2.20
507  **/
508 GdkInputSource
509 gdk_device_get_source (GdkDevice *device)
510 {
511   g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
512
513   return device->source;
514 }
515
516 /**
517  * gdk_device_set_source:
518  * @device: a #GdkDevice.
519  * @source: the source type.
520  *
521  * Sets the source type for an input device.
522  **/
523 void
524 gdk_device_set_source (GdkDevice      *device,
525                        GdkInputSource  source)
526 {
527   g_return_if_fail (GDK_IS_DEVICE (device));
528
529   device->source = source;
530 }
531
532 /**
533  * gdk_device_get_mode:
534  * @device: a #GdkDevice
535  *
536  * Determines the mode of the device.
537  *
538  * Return value: a #GdkInputSource
539  *
540  * Since: 2.20
541  **/
542 GdkInputMode
543 gdk_device_get_mode (GdkDevice *device)
544 {
545   g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
546
547   return device->mode;
548 }
549
550 /**
551  * gdk_device_set_mode:
552  * @device: a #GdkDevice.
553  * @mode: the input mode.
554  *
555  * Sets a the mode of an input device. The mode controls if the
556  * device is active and whether the device's range is mapped to the
557  * entire screen or to a single window.
558  *
559  * Returns: %TRUE if the mode was successfully changed.
560  **/
561 gboolean
562 gdk_device_set_mode (GdkDevice    *device,
563                      GdkInputMode  mode)
564 {
565   g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
566
567   if (device->mode == mode)
568     return TRUE;
569
570   if (mode == GDK_MODE_DISABLED &&
571       gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER)
572     return FALSE;
573
574   /* FIXME: setting has_cursor when mode is window? */
575
576   device->mode = mode;
577   g_object_notify (G_OBJECT (device), "input-mode");
578
579   if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER)
580     _gdk_input_check_extension_events (device);
581
582   return TRUE;
583 }
584
585 /**
586  * gdk_device_get_key:
587  * @device: a #GdkDevice.
588  * @index_: the index of the macro button to get.
589  * @keyval: return value for the keyval.
590  * @modifiers: return value for modifiers.
591  *
592  * If @index_ has a valid keyval, this function will return %TRUE
593  * and fill in @keyval and @modifiers with the keyval settings.
594  *
595  * Returns: %TRUE if keyval is set for @index.
596  *
597  * Since: 2.20
598  **/
599 gboolean
600 gdk_device_get_key (GdkDevice       *device,
601                     guint            index_,
602                     guint           *keyval,
603                     GdkModifierType *modifiers)
604 {
605   g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
606   g_return_val_if_fail (index_ < device->num_keys, FALSE);
607
608   if (!device->keys[index_].keyval &&
609       !device->keys[index_].modifiers)
610     return FALSE;
611
612   if (keyval)
613     *keyval = device->keys[index_].keyval;
614
615   if (modifiers)
616     *modifiers = device->keys[index_].modifiers;
617
618   return TRUE;
619 }
620
621 /**
622  * gdk_device_set_key:
623  * @device: a #GdkDevice
624  * @index_: the index of the macro button to set
625  * @keyval: the keyval to generate
626  * @modifiers: the modifiers to set
627  *
628  * Specifies the X key event to generate when a macro button of a device
629  * is pressed.
630  **/
631 void
632 gdk_device_set_key (GdkDevice      *device,
633                     guint           index_,
634                     guint           keyval,
635                     GdkModifierType modifiers)
636 {
637   g_return_if_fail (GDK_IS_DEVICE (device));
638   g_return_if_fail (index_ < device->num_keys);
639
640   device->keys[index_].keyval = keyval;
641   device->keys[index_].modifiers = modifiers;
642 }
643
644 /**
645  * gdk_device_get_axis_use:
646  * @device: a #GdkDevice.
647  * @index_: the index of the axis.
648  *
649  * Returns the axis use for @index_.
650  *
651  * Returns: a #GdkAxisUse specifying how the axis is used.
652  *
653  * Since: 2.20
654  **/
655 GdkAxisUse
656 gdk_device_get_axis_use (GdkDevice *device,
657                          guint      index_)
658 {
659   g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_AXIS_IGNORE);
660   g_return_val_if_fail (index_ < device->num_axes, GDK_AXIS_IGNORE);
661
662   return device->axes[index_].use;
663 }
664
665 /**
666  * gdk_device_set_axis_use:
667  * @device: a #GdkDevice
668  * @index_: the index of the axis
669  * @use: specifies how the axis is used
670  *
671  * Specifies how an axis of a device is used.
672  **/
673 void
674 gdk_device_set_axis_use (GdkDevice   *device,
675                          guint        index_,
676                          GdkAxisUse   use)
677 {
678   GdkDevicePrivate *priv;
679   GdkAxisInfo *info;
680
681   g_return_if_fail (GDK_IS_DEVICE (device));
682   g_return_if_fail (index_ < device->num_axes);
683
684   priv = device->priv;
685   info = &g_array_index (priv->axes, GdkAxisInfo, index_);
686   info->use = use;
687
688   device->axes[index_].use = use;
689
690   switch (use)
691     {
692     case GDK_AXIS_X:
693     case GDK_AXIS_Y:
694       device->axes[index_].min = info->min_axis = 0;
695       device->axes[index_].max = info->max_axis = 0;
696       break;
697     case GDK_AXIS_XTILT:
698     case GDK_AXIS_YTILT:
699       device->axes[index_].min = info->min_axis = -1;
700       device->axes[index_].max = info->max_axis = 1;
701       break;
702     default:
703       device->axes[index_].min = info->min_axis = 0;
704       device->axes[index_].max = info->max_axis = 1;
705       break;
706     }
707 }
708
709 /**
710  * gdk_device_get_display:
711  * @device: a #GdkDevice
712  *
713  * Returns the #GdkDisplay to which @device pertains.
714  *
715  * Returns: a #GdkDisplay. This memory is owned by GTK+,
716  *          and must not be freed or unreffed.
717  *
718  * Since: 3.0
719  **/
720 GdkDisplay *
721 gdk_device_get_display (GdkDevice *device)
722 {
723   GdkDevicePrivate *priv;
724
725   g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
726
727   priv = device->priv;
728
729   return priv->display;
730 }
731
732 /**
733  * gdk_device_get_associated_device:
734  * @device: a #GdkDevice
735  *
736  * Returns the associated device to @device, if @device is of type
737  * %GDK_DEVICE_TYPE_MASTER, it will return the paired pointer or
738  * keyboard.
739  *
740  * If @device is of type %GDK_DEVICE_TYPE_SLAVE, it will return
741  * the master device to which @device is attached to.
742  *
743  * If @device is of type %GDK_DEVICE_TYPE_FLOATING, %NULL will be
744  * returned, as there is no associated device.
745  *
746  * Returns: The associated device, or %NULL
747  *
748  * Since: 3.0
749  **/
750 GdkDevice *
751 gdk_device_get_associated_device (GdkDevice *device)
752 {
753   GdkDevicePrivate *priv;
754
755   g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
756
757   priv = device->priv;
758
759   return priv->associated;
760 }
761
762 void
763 _gdk_device_set_associated_device (GdkDevice *device,
764                                    GdkDevice *associated)
765 {
766   GdkDevicePrivate *priv;
767
768   g_return_if_fail (GDK_IS_DEVICE (device));
769   g_return_if_fail (GDK_IS_DEVICE (associated));
770
771   priv = device->priv;
772
773   if (priv->associated == associated)
774     return;
775
776   if (priv->associated)
777     {
778       g_object_unref (priv->associated);
779       priv->associated = NULL;
780     }
781
782   if (associated)
783     priv->associated = g_object_ref (associated);
784 }
785
786 /**
787  * gdk_device_get_device_type:
788  * @device: a #GdkDevice
789  *
790  * Returns the device type for @device.
791  *
792  * Returns: the #GdkDeviceType for @device.
793  *
794  * Since: 3.0
795  **/
796 GdkDeviceType
797 gdk_device_get_device_type (GdkDevice *device)
798 {
799   GdkDevicePrivate *priv;
800
801   g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_DEVICE_TYPE_MASTER);
802
803   priv = device->priv;
804
805   return priv->type;
806 }
807
808 /**
809  * gdk_device_get_n_axes:
810  * @device: a #GdkDevice
811  *
812  * Returns the number of axes the device currently has.
813  *
814  * Returns: the number of axes.
815  *
816  * Since: 3.0
817  **/
818 guint
819 gdk_device_get_n_axes (GdkDevice *device)
820 {
821   g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
822
823   return device->num_axes;
824 }
825
826 /**
827  * gdk_device_list_axes:
828  * @device: a #GdkDevice
829  *
830  * Returns a #GList of #GdkAtom<!-- -->s, containing the labels for
831  * the axes that @device currently has.
832  *
833  * Returns: A #GList of #GdkAtom<!-- -->s, free with g_list_free().
834  *
835  * Since: 3.0
836  **/
837 GList *
838 gdk_device_list_axes (GdkDevice *device)
839 {
840   GdkDevicePrivate *priv;
841   GList *axes = NULL;
842   gint i;
843
844   priv = device->priv;
845
846   for (i = 0; i < priv->axes->len; i++)
847     {
848       GdkAxisInfo axis_info;
849
850       axis_info = g_array_index (priv->axes, GdkAxisInfo, i);
851       axes = g_list_prepend (axes, GDK_ATOM_TO_POINTER (axis_info.label));
852     }
853
854   return g_list_reverse (axes);
855 }
856
857 /**
858  * gdk_device_get_axis_value:
859  * @device: a #GdkDevice.
860  * @axes: pointer to an array of axes
861  * @axis_label: #GdkAtom with the axis label.
862  * @value: location to store the found value.
863  *
864  * Interprets an array of double as axis values for a given device,
865  * and locates the value in the array for a given axis label, as returned
866  * by gdk_device_list_axes()
867  *
868  * Returns: %TRUE if the given axis use was found, otherwise %FALSE.
869  *
870  * Since: 3.0
871  **/
872 gboolean
873 gdk_device_get_axis_value (GdkDevice *device,
874                            gdouble   *axes,
875                            GdkAtom    axis_label,
876                            gdouble   *value)
877 {
878   GdkDevicePrivate *priv;
879   gint i;
880
881   g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
882
883   if (axes == NULL)
884     return FALSE;
885
886   priv = device->priv;
887
888   for (i = 0; i < priv->axes->len; i++)
889     {
890       GdkAxisInfo axis_info;
891
892       axis_info = g_array_index (priv->axes, GdkAxisInfo, i);
893
894       if (axis_info.label != axis_label)
895         continue;
896
897       if (value)
898         *value = axes[i];
899
900       return TRUE;
901     }
902
903   return FALSE;
904 }
905
906 /**
907  * gdk_device_get_axis:
908  * @device: a #GdkDevice
909  * @axes: pointer to an array of axes
910  * @use: the use to look for
911  * @value: location to store the found value.
912  *
913  * Interprets an array of double as axis values for a given device,
914  * and locates the value in the array for a given axis use.
915  *
916  * Return value: %TRUE if the given axis use was found, otherwise %FALSE
917  **/
918 gboolean
919 gdk_device_get_axis (GdkDevice  *device,
920                      gdouble    *axes,
921                      GdkAxisUse  use,
922                      gdouble    *value)
923 {
924   GdkDevicePrivate *priv;
925   gint i;
926
927   g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
928
929   if (axes == NULL)
930     return FALSE;
931
932   priv = device->priv;
933
934   g_return_val_if_fail (priv->axes != NULL, FALSE);
935
936   for (i = 0; i < priv->axes->len; i++)
937     {
938       GdkAxisInfo axis_info;
939
940       axis_info = g_array_index (priv->axes, GdkAxisInfo, i);
941
942       if (axis_info.use != use)
943         continue;
944
945       if (value)
946         *value = axes[i];
947
948       return TRUE;
949     }
950
951   return FALSE;
952 }
953
954 static GdkEventMask
955 get_native_grab_event_mask (GdkEventMask grab_mask)
956 {
957   /* Similar to the above but for pointer events only */
958   return
959     GDK_POINTER_MOTION_MASK |
960     GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
961     GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
962     GDK_SCROLL_MASK |
963     (grab_mask &
964      ~(GDK_POINTER_MOTION_HINT_MASK |
965        GDK_BUTTON_MOTION_MASK |
966        GDK_BUTTON1_MOTION_MASK |
967        GDK_BUTTON2_MOTION_MASK |
968        GDK_BUTTON3_MOTION_MASK));
969 }
970
971 /**
972  * gdk_device_grab:
973  * @device: a #GdkDevice
974  * @window: the #GdkWindow which will own the grab (the grab window)
975  * @grab_ownership: specifies the grab ownership.
976  * @owner_events: if %FALSE then all device events are reported with respect to
977  *                @window and are only reported if selected by @event_mask. If
978  *                %TRUE then pointer events for this application are reported
979  *                as normal, but pointer events outside this application are
980  *                reported with respect to @window and only if selected by
981  *                @event_mask. In either mode, unreported events are discarded.
982  * @event_mask: specifies the event mask, which is used in accordance with
983  *              @owner_events.
984  * @cursor: the cursor to display while the grab is active if the device is
985  *          a pointer. If this is %NULL then the normal cursors are used for
986  *          @window and its descendants, and the cursor for @window is used
987  *          elsewhere.
988  * @time_: the timestamp of the event which led to this pointer grab. This
989  *         usually comes from the #GdkEvent struct, though %GDK_CURRENT_TIME
990  *         can be used if the time isn't known.
991  *
992  * Grabs the device so that all events coming from this device are passed to
993  * this application until the device is ungrabbed with gdk_device_ungrab(),
994  * or the window becomes unviewable. This overrides any previous grab on the device
995  * by this client.
996  *
997  * Device grabs are used for operations which need complete control over the
998  * given device events (either pointer or keyboard). For example in GTK+ this
999  * is used for Drag and Drop operations, popup menus and such.
1000  *
1001  * Note that if the event mask of an X window has selected both button press
1002  * and button release events, then a button press event will cause an automatic
1003  * pointer grab until the button is released. X does this automatically since
1004  * most applications expect to receive button press and release events in pairs.
1005  * It is equivalent to a pointer grab on the window with @owner_events set to
1006  * %TRUE.
1007  *
1008  * If you set up anything at the time you take the grab that needs to be
1009  * cleaned up when the grab ends, you should handle the #GdkEventGrabBroken
1010  * events that are emitted when the grab ends unvoluntarily.
1011  *
1012  * Returns: %GDK_GRAB_SUCCESS if the grab was successful.
1013  *
1014  * Since: 3.0
1015  **/
1016 GdkGrabStatus
1017 gdk_device_grab (GdkDevice        *device,
1018                  GdkWindow        *window,
1019                  GdkGrabOwnership  grab_ownership,
1020                  gboolean          owner_events,
1021                  GdkEventMask      event_mask,
1022                  GdkCursor        *cursor,
1023                  guint32           time_)
1024 {
1025   GdkGrabStatus res;
1026   GdkWindow *native;
1027
1028   g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
1029   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
1030
1031   if (_gdk_native_windows)
1032     native = window;
1033   else
1034     native = gdk_window_get_toplevel (window);
1035
1036   while (((GdkWindowObject *) native)->window_type == GDK_WINDOW_OFFSCREEN)
1037     {
1038       native = gdk_offscreen_window_get_embedder (native);
1039
1040       if (native == NULL ||
1041           (!_gdk_window_has_impl (native) &&
1042            !gdk_window_is_viewable (native)))
1043         return GDK_GRAB_NOT_VIEWABLE;
1044
1045       native = gdk_window_get_toplevel (native);
1046     }
1047
1048   res = _gdk_windowing_device_grab (device,
1049                                     window,
1050                                     native,
1051                                     owner_events,
1052                                     get_native_grab_event_mask (event_mask),
1053                                     NULL,
1054                                     cursor,
1055                                     time_);
1056
1057   if (res == GDK_GRAB_SUCCESS)
1058     {
1059       GdkDisplay *display;
1060       gulong serial;
1061
1062       display = gdk_drawable_get_display (window);
1063       serial = _gdk_windowing_window_get_next_serial (display);
1064
1065       _gdk_display_add_device_grab (display,
1066                                     device,
1067                                     window,
1068                                     native,
1069                                     grab_ownership,
1070                                     owner_events,
1071                                     event_mask,
1072                                     serial,
1073                                     time_,
1074                                     FALSE);
1075     }
1076
1077   return res;
1078 }
1079
1080 /* Private API */
1081 void
1082 _gdk_device_reset_axes (GdkDevice *device)
1083 {
1084   GdkDevicePrivate *priv;
1085   gint i;
1086
1087   priv = device->priv;
1088
1089   for (i = priv->axes->len - 1; i >= 0; i--)
1090     g_array_remove_index (priv->axes, i);
1091
1092   g_object_notify (G_OBJECT (device), "n-axes");
1093
1094   /* This is done for backwards compatibility */
1095   g_free (device->axes);
1096   device->axes = NULL;
1097 }
1098
1099 guint
1100 _gdk_device_add_axis (GdkDevice   *device,
1101                       GdkAtom      label_atom,
1102                       GdkAxisUse   use,
1103                       gdouble      min_value,
1104                       gdouble      max_value,
1105                       gdouble      resolution)
1106 {
1107   GdkDevicePrivate *priv;
1108   GdkAxisInfo axis_info;
1109   guint pos;
1110
1111   priv = device->priv;
1112
1113   axis_info.use = use;
1114   axis_info.label = label_atom;
1115   axis_info.min_value = min_value;
1116   axis_info.max_value = max_value;
1117   axis_info.resolution = resolution;
1118
1119   switch (use)
1120     {
1121     case GDK_AXIS_X:
1122     case GDK_AXIS_Y:
1123       axis_info.min_axis = 0;
1124       axis_info.max_axis = 0;
1125       break;
1126     case GDK_AXIS_XTILT:
1127     case GDK_AXIS_YTILT:
1128       axis_info.min_axis = -1;
1129       axis_info.max_axis = 1;
1130       break;
1131     default:
1132       axis_info.min_axis = 0;
1133       axis_info.max_axis = 1;
1134       break;
1135     }
1136
1137   priv->axes = g_array_append_val (priv->axes, axis_info);
1138   device->num_axes = priv->axes->len;
1139   pos = device->num_axes - 1;
1140
1141   /* This is done for backwards compatibility, since the public
1142    * struct doesn't actually store the device data.
1143    */
1144   device->axes = g_realloc (device->axes, sizeof (GdkDeviceAxis) * priv->axes->len);
1145   device->axes[pos].use = axis_info.use;
1146   device->axes[pos].min = axis_info.min_axis;
1147   device->axes[pos].max = axis_info.max_axis;
1148
1149   g_object_notify (G_OBJECT (device), "n-axes");
1150
1151   return pos;
1152 }
1153
1154 void
1155 _gdk_device_set_keys (GdkDevice *device,
1156                       guint      num_keys)
1157 {
1158   if (device->keys)
1159     g_free (device->keys);
1160
1161   device->num_keys = num_keys;
1162   device->keys = g_new0 (GdkDeviceKey, num_keys);
1163 }
1164
1165 static GdkAxisInfo *
1166 find_axis_info (GArray     *array,
1167                 GdkAxisUse  use)
1168 {
1169   GdkAxisInfo *info;
1170   gint i;
1171
1172   for (i = 0; i < GDK_AXIS_LAST; i++)
1173     {
1174       info = &g_array_index (array, GdkAxisInfo, i);
1175
1176       if (info->use == use)
1177         return info;
1178     }
1179
1180   return NULL;
1181 }
1182
1183 GdkAxisUse
1184 _gdk_device_get_axis_use (GdkDevice *device,
1185                           guint      index_)
1186 {
1187   GdkDevicePrivate *priv;
1188   GdkAxisInfo info;
1189
1190   priv = device->priv;
1191
1192   info = g_array_index (priv->axes, GdkAxisInfo, index_);
1193   return info.use;
1194 }
1195
1196 gboolean
1197 _gdk_device_translate_window_coord (GdkDevice *device,
1198                                     GdkWindow *window,
1199                                     guint      index_,
1200                                     gdouble    value,
1201                                     gdouble   *axis_value)
1202 {
1203   GdkDevicePrivate *priv;
1204   GdkAxisInfo axis_info;
1205   GdkAxisInfo *axis_info_x, *axis_info_y;
1206   gdouble device_width, device_height;
1207   gdouble x_offset, y_offset;
1208   gdouble x_scale, y_scale;
1209   gdouble x_min, y_min;
1210   gdouble x_resolution, y_resolution;
1211   gdouble device_aspect;
1212   gint window_width, window_height;
1213   GdkWindowObject *window_private;
1214
1215   priv = device->priv;
1216
1217   if (index_ >= priv->axes->len)
1218     return FALSE;
1219
1220   axis_info = g_array_index (priv->axes, GdkAxisInfo, index_);
1221
1222   if (axis_info.use != GDK_AXIS_X &&
1223       axis_info.use != GDK_AXIS_Y)
1224     return FALSE;
1225
1226   if (axis_info.use == GDK_AXIS_X)
1227     {
1228       axis_info_x = &axis_info;
1229       axis_info_y = find_axis_info (priv->axes, GDK_AXIS_Y);
1230     }
1231   else
1232     {
1233       axis_info_x = find_axis_info (priv->axes, GDK_AXIS_X);
1234       axis_info_y = &axis_info;
1235     }
1236
1237   device_width = axis_info_x->max_value - axis_info_x->min_value;
1238   device_height = axis_info_y->max_value - axis_info_y->min_value;
1239
1240   if (device_width > 0)
1241     x_min = axis_info_x->min_value;
1242   else
1243     {
1244       device_width = gdk_screen_get_width (gdk_drawable_get_screen (window));
1245       x_min = 0;
1246     }
1247
1248   if (device_height > 0)
1249     y_min = axis_info_y->min_value;
1250   else
1251     {
1252       device_height = gdk_screen_get_height (gdk_drawable_get_screen (window));
1253       y_min = 0;
1254     }
1255
1256   window_private = (GdkWindowObject *) window;
1257   gdk_drawable_get_size (window, &window_width, &window_height);
1258
1259   x_resolution = axis_info_x->resolution;
1260   y_resolution = axis_info_y->resolution;
1261
1262   /*
1263    * Some drivers incorrectly report the resolution of the device
1264    * as zero (in partiular linuxwacom < 0.5.3 with usb tablets).
1265    * This causes the device_aspect to become NaN and totally
1266    * breaks windowed mode.  If this is the case, the best we can
1267    * do is to assume the resolution is non-zero is equal in both
1268    * directions (which is true for many devices).  The absolute
1269    * value of the resolution doesn't matter since we only use the
1270    * ratio.
1271    */
1272   if (x_resolution == 0 || y_resolution == 0)
1273     {
1274       x_resolution = 1;
1275       y_resolution = 1;
1276     }
1277
1278   device_aspect = (device_height * y_resolution) /
1279     (device_width * x_resolution);
1280
1281   if (device_aspect * window_width >= window_height)
1282     {
1283       /* device taller than window */
1284       x_scale = window_width / device_width;
1285       y_scale = (x_scale * x_resolution) / y_resolution;
1286
1287       x_offset = 0;
1288       y_offset = - (device_height * y_scale - window_height) / 2;
1289     }
1290   else
1291     {
1292       /* window taller than device */
1293       y_scale = window_height / device_height;
1294       x_scale = (y_scale * y_resolution) / x_resolution;
1295
1296       y_offset = 0;
1297       x_offset = - (device_width * x_scale - window_width) / 2;
1298     }
1299
1300   if (axis_value)
1301     {
1302       if (axis_info.use == GDK_AXIS_X)
1303         *axis_value = x_offset + x_scale * (value - x_min);
1304       else
1305         *axis_value = y_offset + y_scale * (value - y_min);
1306     }
1307
1308   return TRUE;
1309 }
1310
1311 gboolean
1312 _gdk_device_translate_screen_coord (GdkDevice *device,
1313                                     GdkWindow *window,
1314                                     gint       window_root_x,
1315                                     gint       window_root_y,
1316                                     guint      index_,
1317                                     gdouble    value,
1318                                     gdouble   *axis_value)
1319 {
1320   GdkDevicePrivate *priv;
1321   GdkAxisInfo axis_info;
1322   gdouble axis_width, scale, offset;
1323   GdkWindowObject *window_private;
1324
1325   if (device->mode != GDK_MODE_SCREEN)
1326     return FALSE;
1327
1328   priv = device->priv;
1329
1330   if (index_ >= priv->axes->len)
1331     return FALSE;
1332
1333   axis_info = g_array_index (priv->axes, GdkAxisInfo, index_);
1334
1335   if (axis_info.use != GDK_AXIS_X &&
1336       axis_info.use != GDK_AXIS_Y)
1337     return FALSE;
1338
1339   axis_width = axis_info.max_value - axis_info.min_value;
1340   window_private = (GdkWindowObject *) window;
1341
1342   if (axis_info.use == GDK_AXIS_X)
1343     {
1344       if (axis_width > 0)
1345         scale = gdk_screen_get_width (gdk_drawable_get_screen (window)) / axis_width;
1346       else
1347         scale = 1;
1348
1349       offset = - window_root_x - window_private->abs_x;
1350     }
1351   else
1352     {
1353       if (axis_width > 0)
1354         scale = gdk_screen_get_height (gdk_drawable_get_screen (window)) / axis_width;
1355       else
1356         scale = 1;
1357
1358       offset = - window_root_y - window_private->abs_y;
1359     }
1360
1361   if (axis_value)
1362     *axis_value = offset + scale * (value - axis_info.min_value);
1363
1364   return TRUE;
1365 }
1366
1367 gboolean
1368 _gdk_device_translate_axis (GdkDevice *device,
1369                             guint      index_,
1370                             gdouble    value,
1371                             gdouble   *axis_value)
1372 {
1373   GdkDevicePrivate *priv;
1374   GdkAxisInfo axis_info;
1375   gdouble axis_width, out;
1376
1377   priv = device->priv;
1378
1379   if (index_ >= priv->axes->len)
1380     return FALSE;
1381
1382   axis_info = g_array_index (priv->axes, GdkAxisInfo, index_);
1383
1384   if (axis_info.use == GDK_AXIS_X ||
1385       axis_info.use == GDK_AXIS_Y)
1386     return FALSE;
1387
1388   axis_width = axis_info.max_value - axis_info.min_value;
1389   out = (axis_info.max_axis * (value - axis_info.min_value) +
1390          axis_info.min_axis * (axis_info.max_value - value)) / axis_width;
1391
1392   if (axis_value)
1393     *axis_value = out;
1394
1395   return TRUE;
1396 }