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