1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2019 Red Hat Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * Author: Carlos Garnacho <carlosg@gnome.org>
22  */
23 
24 #include "clutter-build-config.h"
25 
26 #include "clutter-input-device-tool.h"
27 #include "clutter-input-pointer-a11y-private.h"
28 #include "clutter-marshal.h"
29 #include "clutter-mutter.h"
30 #include "clutter-private.h"
31 #include "clutter-seat.h"
32 #include "clutter-seat-private.h"
33 #include "clutter-settings-private.h"
34 #include "clutter-virtual-input-device.h"
35 
36 enum
37 {
38   DEVICE_ADDED,
39   DEVICE_REMOVED,
40   KBD_A11Y_MASK_CHANGED,
41   KBD_A11Y_FLAGS_CHANGED,
42   PTR_A11Y_DWELL_CLICK_TYPE_CHANGED,
43   PTR_A11Y_TIMEOUT_STARTED,
44   PTR_A11Y_TIMEOUT_STOPPED,
45   IS_UNFOCUS_INHIBITED_CHANGED,
46   N_SIGNALS,
47 };
48 
49 static guint signals[N_SIGNALS] = { 0 };
50 
51 enum
52 {
53   PROP_0,
54   PROP_TOUCH_MODE,
55   N_PROPS
56 };
57 
58 static GParamSpec *props[N_PROPS];
59 
60 typedef struct _ClutterSeatPrivate ClutterSeatPrivate;
61 
62 struct _ClutterSeatPrivate
63 {
64   unsigned int inhibit_unfocus_count;
65 
66   /* Pointer a11y */
67   ClutterPointerA11ySettings pointer_a11y_settings;
68 };
69 
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(ClutterSeat,clutter_seat,G_TYPE_OBJECT)70 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterSeat, clutter_seat, G_TYPE_OBJECT)
71 
72 static void
73 clutter_seat_set_property (GObject      *object,
74                            guint         prop_id,
75                            const GValue *value,
76                            GParamSpec   *pspec)
77 {
78   switch (prop_id)
79     {
80     case PROP_TOUCH_MODE:
81     default:
82       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
83     }
84 }
85 
86 static void
clutter_seat_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)87 clutter_seat_get_property (GObject    *object,
88                            guint       prop_id,
89                            GValue     *value,
90                            GParamSpec *pspec)
91 {
92   switch (prop_id)
93     {
94     case PROP_TOUCH_MODE:
95       g_value_set_boolean (value, FALSE);
96       break;
97     default:
98       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
99     }
100 }
101 
102 static void
clutter_seat_constructed(GObject * object)103 clutter_seat_constructed (GObject *object)
104 {
105   ClutterSettings *settings = clutter_settings_get_default ();
106 
107   G_OBJECT_CLASS (clutter_seat_parent_class)->constructed (object);
108   clutter_settings_ensure_pointer_a11y_settings (settings,
109                                                  CLUTTER_SEAT (object));
110 }
111 
112 static void
clutter_seat_class_init(ClutterSeatClass * klass)113 clutter_seat_class_init (ClutterSeatClass *klass)
114 {
115   GObjectClass *object_class = G_OBJECT_CLASS (klass);
116 
117   object_class->set_property = clutter_seat_set_property;
118   object_class->get_property = clutter_seat_get_property;
119   object_class->constructed = clutter_seat_constructed;
120 
121   signals[DEVICE_ADDED] =
122     g_signal_new (I_("device-added"),
123                   G_TYPE_FROM_CLASS (object_class),
124                   G_SIGNAL_RUN_LAST,
125                   0, NULL, NULL, NULL,
126                   G_TYPE_NONE, 1,
127                   CLUTTER_TYPE_INPUT_DEVICE);
128 
129   signals[DEVICE_REMOVED] =
130     g_signal_new (I_("device-removed"),
131                   G_TYPE_FROM_CLASS (object_class),
132                   G_SIGNAL_RUN_LAST,
133                   0, NULL, NULL, NULL,
134                   G_TYPE_NONE, 1,
135                   CLUTTER_TYPE_INPUT_DEVICE);
136 
137   /**
138    * ClutterSeat::kbd-a11y-mods-state-changed:
139    * @seat: the #ClutterSeat that emitted the signal
140    * @latched_mask: the latched modifier mask from stickykeys
141    * @locked_mask:  the locked modifier mask from stickykeys
142    *
143    * The ::kbd-a11y-mods-state-changed signal is emitted each time either the
144    * latched modifiers mask or locked modifiers mask are changed as the
145    * result of keyboard accessibilty's sticky keys operations.
146    */
147   signals[KBD_A11Y_MASK_CHANGED] =
148     g_signal_new (I_("kbd-a11y-mods-state-changed"),
149                   G_TYPE_FROM_CLASS (klass),
150                   G_SIGNAL_RUN_LAST,
151                   0, NULL, NULL,
152                   _clutter_marshal_VOID__UINT_UINT,
153                   G_TYPE_NONE, 2,
154                   G_TYPE_UINT,
155                   G_TYPE_UINT);
156   g_signal_set_va_marshaller (signals[KBD_A11Y_MASK_CHANGED],
157                               G_TYPE_FROM_CLASS (object_class),
158                               _clutter_marshal_VOID__UINT_UINTv);
159 
160   /**
161    * ClutterSeat::kbd-a11y-flags-changed:
162    * @seat: the #ClutterSeat that emitted the signal
163    * @settings_flags: the new ClutterKeyboardA11yFlags configuration
164    * @changed_mask: the ClutterKeyboardA11yFlags changed
165    *
166    * The ::kbd-a11y-flags-changed signal is emitted each time the
167    * ClutterKeyboardA11yFlags configuration is changed as the result of
168    * keyboard accessibility operations.
169    */
170   signals[KBD_A11Y_FLAGS_CHANGED] =
171     g_signal_new (I_("kbd-a11y-flags-changed"),
172                   G_TYPE_FROM_CLASS (klass),
173                   G_SIGNAL_RUN_LAST,
174                   0, NULL, NULL,
175                   _clutter_marshal_VOID__UINT_UINT,
176                   G_TYPE_NONE, 2,
177                   G_TYPE_UINT,
178                   G_TYPE_UINT);
179   g_signal_set_va_marshaller (signals[KBD_A11Y_FLAGS_CHANGED],
180                               G_TYPE_FROM_CLASS (object_class),
181                               _clutter_marshal_VOID__UINT_UINTv);
182 
183   /**
184    * ClutterSeat::ptr-a11y-dwell-click-type-changed:
185    * @seat: the #ClutterSeat that emitted the signal
186    * @click_type: the new #ClutterPointerA11yDwellClickType mode
187    *
188    * The ::ptr-a11y-dwell-click-type-changed signal is emitted each time
189    * the ClutterPointerA11yDwellClickType mode is changed as the result
190    * of pointer accessibility operations.
191    */
192   signals[PTR_A11Y_DWELL_CLICK_TYPE_CHANGED] =
193     g_signal_new (I_("ptr-a11y-dwell-click-type-changed"),
194                   G_TYPE_FROM_CLASS (klass),
195                   G_SIGNAL_RUN_LAST,
196                   0, NULL, NULL, NULL,
197                   G_TYPE_NONE, 1,
198                   CLUTTER_TYPE_POINTER_A11Y_DWELL_CLICK_TYPE);
199 
200   /**
201    * ClutterSeat::ptr-a11y-timeout-started:
202    * @seat: the #ClutterSeat that emitted the signal
203    * @device: the core pointer #ClutterInputDevice
204    * @timeout_type: the type of timeout #ClutterPointerA11yTimeoutType
205    * @delay: the delay in ms before secondary-click is triggered.
206    *
207    * The ::ptr-a11y-timeout-started signal is emitted when a
208    * pointer accessibility timeout delay is started, so that upper
209    * layers can notify the user with some visual feedback.
210    */
211   signals[PTR_A11Y_TIMEOUT_STARTED] =
212     g_signal_new (I_("ptr-a11y-timeout-started"),
213                   G_TYPE_FROM_CLASS (klass),
214                   G_SIGNAL_RUN_LAST,
215                   0, NULL, NULL,
216                   _clutter_marshal_VOID__OBJECT_FLAGS_UINT,
217                   G_TYPE_NONE, 3,
218                   CLUTTER_TYPE_INPUT_DEVICE,
219                   CLUTTER_TYPE_POINTER_A11Y_TIMEOUT_TYPE,
220                   G_TYPE_UINT);
221   g_signal_set_va_marshaller (signals[PTR_A11Y_TIMEOUT_STARTED],
222                               G_TYPE_FROM_CLASS (object_class),
223                               _clutter_marshal_VOID__OBJECT_FLAGS_UINTv);
224 
225   /**
226    * ClutterSeat::ptr-a11y-timeout-stopped:
227    * @seat: the #ClutterSeat that emitted the signal
228    * @device: the core pointer #ClutterInputDevice
229    * @timeout_type: the type of timeout #ClutterPointerA11yTimeoutType
230    * @clicked: %TRUE if the timeout finished and triggered a click
231    *
232    * The ::ptr-a11y-timeout-stopped signal is emitted when a running
233    * pointer accessibility timeout delay is stopped, either because
234    * it's triggered at the end of the delay or cancelled, so that
235    * upper layers can notify the user with some visual feedback.
236    */
237   signals[PTR_A11Y_TIMEOUT_STOPPED] =
238     g_signal_new (I_("ptr-a11y-timeout-stopped"),
239                   G_TYPE_FROM_CLASS (klass),
240                   G_SIGNAL_RUN_LAST,
241                   0, NULL, NULL,
242                   _clutter_marshal_VOID__OBJECT_FLAGS_BOOLEAN,
243                   G_TYPE_NONE, 3,
244                   CLUTTER_TYPE_INPUT_DEVICE,
245                   CLUTTER_TYPE_POINTER_A11Y_TIMEOUT_TYPE,
246                   G_TYPE_BOOLEAN);
247   g_signal_set_va_marshaller (signals[PTR_A11Y_TIMEOUT_STOPPED],
248                               G_TYPE_FROM_CLASS (object_class),
249                               _clutter_marshal_VOID__OBJECT_FLAGS_BOOLEANv);
250 
251   /**
252    * ClutterSeat::is-unfocus-inhibited-changed:
253    * @seat: the #ClutterSeat that emitted the signal
254    *
255    * The ::is-unfocus-inhibited-changed signal is emitted when the
256    * property to inhibit the unsetting of the focus-surface of the
257    * #ClutterSeat changed. To get the current state of this property,
258    * use clutter_seat_is_unfocus_inhibited().
259    */
260   signals[IS_UNFOCUS_INHIBITED_CHANGED] =
261     g_signal_new (I_("is-unfocus-inhibited-changed"),
262                   G_TYPE_FROM_CLASS (klass),
263                   G_SIGNAL_RUN_LAST,
264                   0, NULL, NULL, NULL,
265                   G_TYPE_NONE, 0);
266 
267   /**
268    * ClutterSeat:touch-mode:
269    *
270    * The current touch-mode of the #ClutterSeat, it is set to %TRUE if the
271    * requirements documented in clutter_seat_get_touch_mode() are fulfilled.
272    **/
273   props[PROP_TOUCH_MODE] =
274     g_param_spec_boolean ("touch-mode",
275                           P_("Touch mode"),
276                           P_("Touch mode"),
277                           FALSE,
278                           CLUTTER_PARAM_READABLE);
279 
280   g_object_class_install_properties (object_class, N_PROPS, props);
281 }
282 
283 static void
clutter_seat_init(ClutterSeat * seat)284 clutter_seat_init (ClutterSeat *seat)
285 {
286 }
287 
288 /**
289  * clutter_seat_get_pointer:
290  * @seat: a #ClutterSeat
291  *
292  * Returns the logical pointer
293  *
294  * Returns: (transfer none): the logical pointer
295  **/
296 ClutterInputDevice *
clutter_seat_get_pointer(ClutterSeat * seat)297 clutter_seat_get_pointer (ClutterSeat *seat)
298 {
299   g_return_val_if_fail (CLUTTER_IS_SEAT (seat), NULL);
300 
301   return CLUTTER_SEAT_GET_CLASS (seat)->get_pointer (seat);
302 }
303 
304 /**
305  * clutter_seat_get_keyboard:
306  * @seat: a #ClutterSeat
307  *
308  * Returns the logical keyboard
309  *
310  * Returns: (transfer none): the logical keyboard
311  **/
312 ClutterInputDevice *
clutter_seat_get_keyboard(ClutterSeat * seat)313 clutter_seat_get_keyboard (ClutterSeat *seat)
314 {
315   g_return_val_if_fail (CLUTTER_IS_SEAT (seat), NULL);
316 
317   return CLUTTER_SEAT_GET_CLASS (seat)->get_keyboard (seat);
318 }
319 
320 /**
321  * clutter_seat_peek_devices: (skip)
322  **/
323 const GList *
clutter_seat_peek_devices(ClutterSeat * seat)324 clutter_seat_peek_devices (ClutterSeat *seat)
325 {
326   g_return_val_if_fail (CLUTTER_IS_SEAT (seat), NULL);
327 
328   return CLUTTER_SEAT_GET_CLASS (seat)->peek_devices (seat);
329 }
330 
331 /**
332  * clutter_seat_list_devices:
333  * @seat: a #ClutterSeat
334  *
335  * Returns the list of HW devices
336  *
337  * Returns: (transfer container) (element-type Clutter.InputDevice): A list
338  *   of #ClutterInputDevice. The elements of the returned list are owned by
339  *   Clutter and may not be freed, the returned list should be freed using
340  *   g_list_free() when done.
341  **/
342 GList *
clutter_seat_list_devices(ClutterSeat * seat)343 clutter_seat_list_devices (ClutterSeat *seat)
344 {
345   g_return_val_if_fail (CLUTTER_IS_SEAT (seat), NULL);
346 
347   return g_list_copy ((GList *)clutter_seat_peek_devices (seat));
348 }
349 
350 void
clutter_seat_bell_notify(ClutterSeat * seat)351 clutter_seat_bell_notify (ClutterSeat *seat)
352 {
353   CLUTTER_SEAT_GET_CLASS (seat)->bell_notify (seat);
354 }
355 
356 /**
357  * clutter_seat_get_keymap:
358  * @seat: a #ClutterSeat
359  *
360  * Returns the seat keymap
361  *
362  * Returns: (transfer none): the seat keymap
363  **/
364 ClutterKeymap *
clutter_seat_get_keymap(ClutterSeat * seat)365 clutter_seat_get_keymap (ClutterSeat *seat)
366 {
367   return CLUTTER_SEAT_GET_CLASS (seat)->get_keymap (seat);
368 }
369 
370 void
clutter_seat_ensure_a11y_state(ClutterSeat * seat)371 clutter_seat_ensure_a11y_state (ClutterSeat *seat)
372 {
373   ClutterInputDevice *core_pointer;
374 
375   core_pointer = clutter_seat_get_pointer (seat);
376 
377   if (core_pointer)
378     {
379       if (_clutter_is_input_pointer_a11y_enabled (core_pointer))
380         _clutter_input_pointer_a11y_add_device (core_pointer);
381     }
382 }
383 
384 static gboolean
are_pointer_a11y_settings_equal(ClutterPointerA11ySettings * a,ClutterPointerA11ySettings * b)385 are_pointer_a11y_settings_equal (ClutterPointerA11ySettings *a,
386                                  ClutterPointerA11ySettings *b)
387 {
388   return (memcmp (a, b, sizeof (ClutterPointerA11ySettings)) == 0);
389 }
390 
391 static void
clutter_seat_enable_pointer_a11y(ClutterSeat * seat)392 clutter_seat_enable_pointer_a11y (ClutterSeat *seat)
393 {
394   ClutterInputDevice *core_pointer;
395 
396   core_pointer = clutter_seat_get_pointer (seat);
397 
398   _clutter_input_pointer_a11y_add_device (core_pointer);
399 }
400 
401 static void
clutter_seat_disable_pointer_a11y(ClutterSeat * seat)402 clutter_seat_disable_pointer_a11y (ClutterSeat *seat)
403 {
404   ClutterInputDevice *core_pointer;
405 
406   core_pointer = clutter_seat_get_pointer (seat);
407 
408   _clutter_input_pointer_a11y_remove_device (core_pointer);
409 }
410 
411 /**
412  * clutter_seat_set_pointer_a11y_settings:
413  * @seat: a #ClutterSeat
414  * @settings: a pointer to a #ClutterPointerA11ySettings
415  *
416  * Sets the pointer accessibility settings
417  **/
418 void
clutter_seat_set_pointer_a11y_settings(ClutterSeat * seat,ClutterPointerA11ySettings * settings)419 clutter_seat_set_pointer_a11y_settings (ClutterSeat                *seat,
420                                         ClutterPointerA11ySettings *settings)
421 {
422   ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat);
423 
424   g_return_if_fail (CLUTTER_IS_SEAT (seat));
425 
426   if (are_pointer_a11y_settings_equal (&priv->pointer_a11y_settings, settings))
427     return;
428 
429   if (priv->pointer_a11y_settings.controls == 0 && settings->controls != 0)
430     clutter_seat_enable_pointer_a11y (seat);
431   else if (priv->pointer_a11y_settings.controls != 0 && settings->controls == 0)
432     clutter_seat_disable_pointer_a11y (seat);
433 
434   priv->pointer_a11y_settings = *settings;
435 }
436 
437 /**
438  * clutter_seat_get_pointer_a11y_settings:
439  * @seat: a #ClutterSeat
440  * @settings: a pointer to a #ClutterPointerA11ySettings
441  *
442  * Gets the current pointer accessibility settings
443  **/
444 void
clutter_seat_get_pointer_a11y_settings(ClutterSeat * seat,ClutterPointerA11ySettings * settings)445 clutter_seat_get_pointer_a11y_settings (ClutterSeat                *seat,
446                                         ClutterPointerA11ySettings *settings)
447 {
448   ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat);
449 
450   g_return_if_fail (CLUTTER_IS_SEAT (seat));
451 
452   *settings = priv->pointer_a11y_settings;
453 }
454 
455 /**
456  * clutter_seat_set_pointer_a11y_dwell_click_type:
457  * @seat: a #ClutterSeat
458  * @click_type: type of click as #ClutterPointerA11yDwellClickType
459  *
460  * Sets the dwell click type
461  **/
462 void
clutter_seat_set_pointer_a11y_dwell_click_type(ClutterSeat * seat,ClutterPointerA11yDwellClickType click_type)463 clutter_seat_set_pointer_a11y_dwell_click_type (ClutterSeat                      *seat,
464                                                 ClutterPointerA11yDwellClickType  click_type)
465 {
466   ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat);
467 
468   g_return_if_fail (CLUTTER_IS_SEAT (seat));
469 
470   priv->pointer_a11y_settings.dwell_click_type = click_type;
471 }
472 
473 /**
474  * clutter_seat_inhibit_unfocus:
475  * @seat: a #ClutterSeat
476  *
477  * Inhibits unsetting of the pointer focus-surface for the #ClutterSeat @seat,
478  * this allows to keep using the pointer even when it's hidden.
479  *
480  * This property is refcounted, so clutter_seat_uninhibit_unfocus() must be
481  * called the exact same number of times as clutter_seat_inhibit_unfocus()
482  * was called before.
483  **/
484 void
clutter_seat_inhibit_unfocus(ClutterSeat * seat)485 clutter_seat_inhibit_unfocus (ClutterSeat *seat)
486 {
487   ClutterSeatPrivate *priv;
488 
489   g_return_if_fail (CLUTTER_IS_SEAT (seat));
490 
491   priv = clutter_seat_get_instance_private (seat);
492 
493   priv->inhibit_unfocus_count++;
494 
495   if (priv->inhibit_unfocus_count == 1)
496     g_signal_emit (G_OBJECT (seat), signals[IS_UNFOCUS_INHIBITED_CHANGED], 0);
497 }
498 
499 /**
500  * clutter_seat_uninhibit_unfocus:
501  * @seat: a #ClutterSeat
502  *
503  * Disables the inhibiting of unsetting of the pointer focus-surface
504  * previously enabled by calling clutter_seat_inhibit_unfocus().
505  *
506  * This property is refcounted, so clutter_seat_uninhibit_unfocus() must be
507  * called the exact same number of times as clutter_seat_inhibit_unfocus()
508  * was called before.
509  **/
510 void
clutter_seat_uninhibit_unfocus(ClutterSeat * seat)511 clutter_seat_uninhibit_unfocus (ClutterSeat *seat)
512 {
513   ClutterSeatPrivate *priv;
514 
515   g_return_if_fail (CLUTTER_IS_SEAT (seat));
516 
517   priv = clutter_seat_get_instance_private (seat);
518 
519   if (priv->inhibit_unfocus_count == 0)
520     {
521       g_warning ("Called clutter_seat_uninhibit_unfocus without inhibiting before");
522       return;
523     }
524 
525   priv->inhibit_unfocus_count--;
526 
527   if (priv->inhibit_unfocus_count == 0)
528     g_signal_emit (G_OBJECT (seat), signals[IS_UNFOCUS_INHIBITED_CHANGED], 0);
529 }
530 
531 /**
532  * clutter_seat_is_unfocus_inhibited:
533  * @seat: a #ClutterSeat
534  *
535  * Gets whether unsetting of the pointer focus-surface is inhibited
536  * for the #ClutterSeat @seat.
537  *
538  * Returns: %TRUE if unsetting is inhibited, %FALSE otherwise
539  **/
540 gboolean
clutter_seat_is_unfocus_inhibited(ClutterSeat * seat)541 clutter_seat_is_unfocus_inhibited (ClutterSeat *seat)
542 {
543   ClutterSeatPrivate *priv;
544 
545   g_return_val_if_fail (CLUTTER_IS_SEAT (seat), FALSE);
546 
547   priv = clutter_seat_get_instance_private (seat);
548 
549   return priv->inhibit_unfocus_count > 0;
550 }
551 
552 /**
553  * clutter_seat_create_virtual_device:
554  * @seat: a #ClutterSeat
555  * @device_type: the type of the virtual device
556  *
557  * Creates a virtual input device.
558  *
559  * Returns: (transfer full): a newly created virtual device
560  **/
561 ClutterVirtualInputDevice *
clutter_seat_create_virtual_device(ClutterSeat * seat,ClutterInputDeviceType device_type)562 clutter_seat_create_virtual_device (ClutterSeat            *seat,
563                                     ClutterInputDeviceType  device_type)
564 {
565   ClutterSeatClass *seat_class;
566 
567   g_return_val_if_fail (CLUTTER_IS_SEAT (seat), NULL);
568 
569   seat_class = CLUTTER_SEAT_GET_CLASS (seat);
570   return seat_class->create_virtual_device (seat, device_type);
571 }
572 
573 /**
574  * clutter_seat_get_supported_virtual_device_types: (skip)
575  **/
576 ClutterVirtualDeviceType
clutter_seat_get_supported_virtual_device_types(ClutterSeat * seat)577 clutter_seat_get_supported_virtual_device_types (ClutterSeat *seat)
578 {
579   ClutterSeatClass *seat_class;
580 
581   g_return_val_if_fail (CLUTTER_IS_SEAT (seat),
582                         CLUTTER_VIRTUAL_DEVICE_TYPE_NONE);
583 
584   seat_class = CLUTTER_SEAT_GET_CLASS (seat);
585   return seat_class->get_supported_virtual_device_types (seat);
586 }
587 
588 gboolean
clutter_seat_handle_event_post(ClutterSeat * seat,const ClutterEvent * event)589 clutter_seat_handle_event_post (ClutterSeat        *seat,
590                                 const ClutterEvent *event)
591 {
592   ClutterSeatClass *seat_class;
593   ClutterInputDevice *device;
594 
595   g_return_val_if_fail (CLUTTER_IS_SEAT (seat), FALSE);
596   g_return_val_if_fail (event, FALSE);
597 
598   seat_class = CLUTTER_SEAT_GET_CLASS (seat);
599 
600   if (seat_class->handle_event_post)
601     seat_class->handle_event_post (seat, event);
602 
603   device = clutter_event_get_source_device (event);
604   g_assert_true (CLUTTER_IS_INPUT_DEVICE (device));
605 
606   switch (event->type)
607     {
608       case CLUTTER_DEVICE_ADDED:
609         g_signal_emit (seat, signals[DEVICE_ADDED], 0, device);
610         break;
611       case CLUTTER_DEVICE_REMOVED:
612         g_signal_emit (seat, signals[DEVICE_REMOVED], 0, device);
613         g_object_run_dispose (G_OBJECT (device));
614         break;
615       default:
616         break;
617     }
618 
619   return TRUE;
620 }
621 
622 void
clutter_seat_warp_pointer(ClutterSeat * seat,int x,int y)623 clutter_seat_warp_pointer (ClutterSeat *seat,
624                            int          x,
625                            int          y)
626 {
627   g_return_if_fail (CLUTTER_IS_SEAT (seat));
628 
629   CLUTTER_SEAT_GET_CLASS (seat)->warp_pointer (seat, x, y);
630 }
631 
632 /**
633  * clutter_seat_get_touch_mode:
634  * @seat: a #ClutterSeat
635  *
636  * Gets the current touch-mode state of the #ClutterSeat @seat.
637  * The #ClutterSeat:touch-mode property is set to %TRUE if the following
638  * requirements are fulfilled:
639  *
640  *  - A touchscreen is available
641  *  - A tablet mode switch, if present, is enabled
642  *
643  * Returns: %TRUE if the device is a tablet that doesn't have an external
644  *   keyboard attached, %FALSE otherwise.
645  **/
646 gboolean
clutter_seat_get_touch_mode(ClutterSeat * seat)647 clutter_seat_get_touch_mode (ClutterSeat *seat)
648 {
649   gboolean touch_mode;
650 
651   g_return_val_if_fail (CLUTTER_IS_SEAT (seat), FALSE);
652 
653   g_object_get (G_OBJECT (seat), "touch-mode", &touch_mode, NULL);
654 
655   return touch_mode;
656 }
657 
658 /**
659  * clutter_seat_has_touchscreen: (skip)
660  **/
661 gboolean
clutter_seat_has_touchscreen(ClutterSeat * seat)662 clutter_seat_has_touchscreen (ClutterSeat *seat)
663 {
664   gboolean has_touchscreen = FALSE;
665   const GList *devices, *l;
666 
667   g_return_val_if_fail (CLUTTER_IS_SEAT (seat), FALSE);
668 
669   devices = clutter_seat_peek_devices (seat);
670   for (l = devices; l; l = l->next)
671     {
672       ClutterInputDevice *device = l->data;
673 
674       if (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_LOGICAL &&
675           clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE)
676         {
677           has_touchscreen = TRUE;
678           break;
679         }
680     }
681 
682   return has_touchscreen;
683 }
684 
685 gboolean
clutter_seat_query_state(ClutterSeat * seat,ClutterInputDevice * device,ClutterEventSequence * sequence,graphene_point_t * coords,ClutterModifierType * modifiers)686 clutter_seat_query_state (ClutterSeat          *seat,
687                           ClutterInputDevice   *device,
688                           ClutterEventSequence *sequence,
689                           graphene_point_t     *coords,
690                           ClutterModifierType  *modifiers)
691 {
692   g_return_val_if_fail (CLUTTER_IS_SEAT (seat), FALSE);
693   g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE);
694 
695   return CLUTTER_SEAT_GET_CLASS (seat)->query_state (seat,
696                                                      device,
697                                                      sequence,
698                                                      coords,
699                                                      modifiers);
700 }
701 
702 void
clutter_seat_destroy(ClutterSeat * seat)703 clutter_seat_destroy (ClutterSeat *seat)
704 {
705   g_object_run_dispose (G_OBJECT (seat));
706   g_object_unref (seat);
707 }
708