1 /*
2 * Clutter.
3 *
4 * An OpenGL based 'interactive canvas' library.
5 *
6 * Copyright (C) 2010 Intel Corp.
7 * Copyright (C) 2014 Jonas Ådahl
8 * Copyright (C) 2016 Red Hat Inc.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 *
23 * Author: Damien Lespiau <damien.lespiau@intel.com>
24 * Author: Jonas Ådahl <jadahl@gmail.com>
25 */
26
27 #include "config.h"
28
29 #include "backends/native/meta-seat-native.h"
30
31 #include "backends/meta-cursor-tracker-private.h"
32 #include "backends/meta-keymap-utils.h"
33 #include "backends/native/meta-barrier-native.h"
34 #include "backends/native/meta-input-thread.h"
35 #include "backends/native/meta-keymap-native.h"
36 #include "backends/native/meta-virtual-input-device-native.h"
37 #include "clutter/clutter-mutter.h"
38 #include "core/bell.h"
39
40 #include "meta-private-enum-types.h"
41
42 enum
43 {
44 PROP_0,
45 PROP_SEAT_ID,
46 PROP_FLAGS,
47 PROP_BACKEND,
48 N_PROPS,
49
50 /* This property is overridden */
51 PROP_TOUCH_MODE,
52 };
53
54 static GParamSpec *props[N_PROPS] = { NULL };
55
G_DEFINE_TYPE(MetaSeatNative,meta_seat_native,CLUTTER_TYPE_SEAT)56 G_DEFINE_TYPE (MetaSeatNative, meta_seat_native, CLUTTER_TYPE_SEAT)
57
58 static gboolean
59 meta_seat_native_handle_event_post (ClutterSeat *seat,
60 const ClutterEvent *event)
61 {
62 MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
63 ClutterInputDevice *device = clutter_event_get_source_device (event);
64 ClutterEventType event_type = event->type;
65
66 if (event_type == CLUTTER_PROXIMITY_IN)
67 {
68 MetaCursorRenderer *cursor_renderer;
69
70 if (!seat_native->tablet_cursors)
71 {
72 seat_native->tablet_cursors = g_hash_table_new_full (NULL, NULL, NULL,
73 g_object_unref);
74 }
75
76 cursor_renderer = meta_cursor_renderer_new (meta_get_backend (), device);
77 g_hash_table_insert (seat_native->tablet_cursors,
78 device, cursor_renderer);
79 return TRUE;
80 }
81 else if (event_type == CLUTTER_PROXIMITY_OUT)
82 {
83 if (seat_native->tablet_cursors)
84 g_hash_table_remove (seat_native->tablet_cursors, device);
85 return TRUE;
86 }
87 else if (event_type == CLUTTER_DEVICE_ADDED)
88 {
89 if (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_LOGICAL)
90 seat_native->devices = g_list_prepend (seat_native->devices, g_object_ref (device));
91 }
92 else if (event_type == CLUTTER_DEVICE_REMOVED)
93 {
94 GList *l = g_list_find (seat_native->devices, device);
95
96 if (l)
97 {
98 seat_native->devices = g_list_delete_link (seat_native->devices, l);
99 g_object_unref (device);
100 }
101 }
102
103 return FALSE;
104 }
105
106 static void
proxy_kbd_a11y_flags_changed(MetaSeatImpl * seat_impl,MetaKeyboardA11yFlags new_flags,MetaKeyboardA11yFlags what_changed,MetaSeatNative * seat_native)107 proxy_kbd_a11y_flags_changed (MetaSeatImpl *seat_impl,
108 MetaKeyboardA11yFlags new_flags,
109 MetaKeyboardA11yFlags what_changed,
110 MetaSeatNative *seat_native)
111 {
112 g_signal_emit_by_name (seat_native,
113 "kbd-a11y-flags-changed",
114 new_flags, what_changed);
115 }
116
117 static void
proxy_kbd_a11y_mods_state_changed(MetaSeatImpl * seat_impl,xkb_mod_mask_t new_latched_mods,xkb_mod_mask_t new_locked_mods,MetaSeatNative * seat_native)118 proxy_kbd_a11y_mods_state_changed (MetaSeatImpl *seat_impl,
119 xkb_mod_mask_t new_latched_mods,
120 xkb_mod_mask_t new_locked_mods,
121 MetaSeatNative *seat_native)
122 {
123 g_signal_emit_by_name (seat_native,
124 "kbd-a11y-mods-state-changed",
125 new_latched_mods,
126 new_locked_mods);
127 }
128
129 static void
proxy_touch_mode_changed(MetaSeatImpl * seat_impl,gboolean enabled,MetaSeatNative * seat_native)130 proxy_touch_mode_changed (MetaSeatImpl *seat_impl,
131 gboolean enabled,
132 MetaSeatNative *seat_native)
133 {
134 seat_native->touch_mode = enabled;
135 g_object_notify (G_OBJECT (seat_native), "touch-mode");
136 }
137
138 static void
proxy_bell(MetaSeatImpl * seat_impl,MetaSeatNative * seat_native)139 proxy_bell (MetaSeatImpl *seat_impl,
140 MetaSeatNative *seat_native)
141 {
142 clutter_seat_bell_notify (CLUTTER_SEAT (seat_native));
143 }
144
145 static void
proxy_mods_state_changed(MetaSeatImpl * seat_impl,ClutterSeat * seat)146 proxy_mods_state_changed (MetaSeatImpl *seat_impl,
147 ClutterSeat *seat)
148 {
149 ClutterKeymap *keymap;
150
151 keymap = clutter_seat_get_keymap (seat);
152 g_signal_emit_by_name (keymap, "state-changed");
153 }
154
155 static void
meta_seat_native_constructed(GObject * object)156 meta_seat_native_constructed (GObject *object)
157 {
158 MetaSeatNative *seat = META_SEAT_NATIVE (object);
159
160 seat->impl = meta_seat_impl_new (seat, seat->seat_id, seat->flags);
161 g_signal_connect (seat->impl, "kbd-a11y-flags-changed",
162 G_CALLBACK (proxy_kbd_a11y_flags_changed), seat);
163 g_signal_connect (seat->impl, "kbd-a11y-mods-state-changed",
164 G_CALLBACK (proxy_kbd_a11y_mods_state_changed), seat);
165 g_signal_connect (seat->impl, "touch-mode",
166 G_CALLBACK (proxy_touch_mode_changed), seat);
167 g_signal_connect (seat->impl, "bell",
168 G_CALLBACK (proxy_bell), seat);
169 g_signal_connect (seat->impl, "mods-state-changed",
170 G_CALLBACK (proxy_mods_state_changed), seat);
171
172 seat->core_pointer = meta_seat_impl_get_pointer (seat->impl);
173 seat->core_keyboard = meta_seat_impl_get_keyboard (seat->impl);
174
175 meta_seat_native_set_keyboard_map (seat, "us", "", "");
176
177 if (G_OBJECT_CLASS (meta_seat_native_parent_class)->constructed)
178 G_OBJECT_CLASS (meta_seat_native_parent_class)->constructed (object);
179 }
180
181 static void
meta_seat_native_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)182 meta_seat_native_set_property (GObject *object,
183 guint prop_id,
184 const GValue *value,
185 GParamSpec *pspec)
186 {
187 MetaSeatNative *seat_native = META_SEAT_NATIVE (object);
188
189 switch (prop_id)
190 {
191 case PROP_SEAT_ID:
192 seat_native->seat_id = g_value_dup_string (value);
193 break;
194 case PROP_FLAGS:
195 seat_native->flags = g_value_get_flags (value);
196 break;
197 case PROP_BACKEND:
198 seat_native->backend = g_value_get_object (value);
199 break;
200 case PROP_TOUCH_MODE:
201 default:
202 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
203 }
204 }
205
206 static void
meta_seat_native_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)207 meta_seat_native_get_property (GObject *object,
208 guint prop_id,
209 GValue *value,
210 GParamSpec *pspec)
211 {
212 MetaSeatNative *seat_native = META_SEAT_NATIVE (object);
213
214 switch (prop_id)
215 {
216 case PROP_SEAT_ID:
217 g_value_set_string (value, seat_native->seat_id);
218 break;
219 case PROP_TOUCH_MODE:
220 g_value_set_boolean (value, seat_native->touch_mode);
221 break;
222 case PROP_FLAGS:
223 g_value_set_flags (value, seat_native->flags);
224 break;
225 case PROP_BACKEND:
226 g_value_set_object (value, seat_native->backend);
227 break;
228 default:
229 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
230 }
231 }
232
233 static void
meta_seat_native_dispose(GObject * object)234 meta_seat_native_dispose (GObject *object)
235 {
236 MetaSeatNative *seat = META_SEAT_NATIVE (object);
237
238 g_clear_pointer (&seat->xkb_keymap, xkb_keymap_unref);
239 g_clear_object (&seat->core_pointer);
240 g_clear_object (&seat->core_keyboard);
241 g_clear_pointer (&seat->impl, meta_seat_impl_destroy);
242 g_list_free_full (g_steal_pointer (&seat->devices), g_object_unref);
243 g_clear_pointer (&seat->reserved_virtual_slots, g_hash_table_destroy);
244 g_clear_pointer (&seat->tablet_cursors, g_hash_table_unref);
245 g_clear_object (&seat->cursor_renderer);
246
247 g_clear_pointer (&seat->seat_id, g_free);
248
249 G_OBJECT_CLASS (meta_seat_native_parent_class)->dispose (object);
250 }
251
252 static ClutterInputDevice *
meta_seat_native_get_pointer(ClutterSeat * seat)253 meta_seat_native_get_pointer (ClutterSeat *seat)
254 {
255 MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
256
257 return seat_native->core_pointer;
258 }
259
260 static ClutterInputDevice *
meta_seat_native_get_keyboard(ClutterSeat * seat)261 meta_seat_native_get_keyboard (ClutterSeat *seat)
262 {
263 MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
264
265 return seat_native->core_keyboard;
266 }
267
268 static const GList *
meta_seat_native_peek_devices(ClutterSeat * seat)269 meta_seat_native_peek_devices (ClutterSeat *seat)
270 {
271 MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
272
273 return (const GList *) seat_native->devices;
274 }
275
276 static void
meta_seat_native_bell_notify(ClutterSeat * seat)277 meta_seat_native_bell_notify (ClutterSeat *seat)
278 {
279 MetaDisplay *display = meta_get_display ();
280
281 meta_bell_notify (display, NULL);
282 }
283
284 static ClutterKeymap *
meta_seat_native_get_keymap(ClutterSeat * seat)285 meta_seat_native_get_keymap (ClutterSeat *seat)
286 {
287 MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
288
289 if (!seat_native->keymap)
290 seat_native->keymap = meta_seat_impl_get_keymap (seat_native->impl);
291
292 return CLUTTER_KEYMAP (seat_native->keymap);
293 }
294
295 static guint
bump_virtual_touch_slot_base(MetaSeatNative * seat_native)296 bump_virtual_touch_slot_base (MetaSeatNative *seat_native)
297 {
298 while (TRUE)
299 {
300 if (seat_native->virtual_touch_slot_base < 0x100)
301 seat_native->virtual_touch_slot_base = 0x100;
302
303 seat_native->virtual_touch_slot_base +=
304 CLUTTER_VIRTUAL_INPUT_DEVICE_MAX_TOUCH_SLOTS;
305
306 if (!g_hash_table_lookup (seat_native->reserved_virtual_slots,
307 GUINT_TO_POINTER (seat_native->virtual_touch_slot_base)))
308 break;
309 }
310
311 return seat_native->virtual_touch_slot_base;
312 }
313
314 static ClutterVirtualInputDevice *
meta_seat_native_create_virtual_device(ClutterSeat * seat,ClutterInputDeviceType device_type)315 meta_seat_native_create_virtual_device (ClutterSeat *seat,
316 ClutterInputDeviceType device_type)
317 {
318 MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
319 guint slot_base;
320
321 slot_base = bump_virtual_touch_slot_base (seat_native);
322 g_hash_table_add (seat_native->reserved_virtual_slots,
323 GUINT_TO_POINTER (slot_base));
324
325 return g_object_new (META_TYPE_VIRTUAL_INPUT_DEVICE_NATIVE,
326 "seat", seat,
327 "slot-base", slot_base,
328 "device-type", device_type,
329 NULL);
330 }
331
332 void
meta_seat_native_release_touch_slots(MetaSeatNative * seat,guint base_slot)333 meta_seat_native_release_touch_slots (MetaSeatNative *seat,
334 guint base_slot)
335 {
336 g_hash_table_remove (seat->reserved_virtual_slots,
337 GUINT_TO_POINTER (base_slot));
338 }
339
340 static ClutterVirtualDeviceType
meta_seat_native_get_supported_virtual_device_types(ClutterSeat * seat)341 meta_seat_native_get_supported_virtual_device_types (ClutterSeat *seat)
342 {
343 return (CLUTTER_VIRTUAL_DEVICE_TYPE_KEYBOARD |
344 CLUTTER_VIRTUAL_DEVICE_TYPE_POINTER |
345 CLUTTER_VIRTUAL_DEVICE_TYPE_TOUCHSCREEN);
346 }
347
348 static void
meta_seat_native_warp_pointer(ClutterSeat * seat,int x,int y)349 meta_seat_native_warp_pointer (ClutterSeat *seat,
350 int x,
351 int y)
352 {
353 MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
354
355 meta_seat_impl_warp_pointer (seat_native->impl, x, y);
356 }
357
358 static gboolean
meta_seat_native_query_state(ClutterSeat * seat,ClutterInputDevice * device,ClutterEventSequence * sequence,graphene_point_t * coords,ClutterModifierType * modifiers)359 meta_seat_native_query_state (ClutterSeat *seat,
360 ClutterInputDevice *device,
361 ClutterEventSequence *sequence,
362 graphene_point_t *coords,
363 ClutterModifierType *modifiers)
364 {
365 MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
366
367 return meta_seat_impl_query_state (seat_native->impl, device, sequence,
368 coords, modifiers);
369 }
370
371 static void
meta_seat_native_class_init(MetaSeatNativeClass * klass)372 meta_seat_native_class_init (MetaSeatNativeClass *klass)
373 {
374 GObjectClass *object_class = G_OBJECT_CLASS (klass);
375 ClutterSeatClass *seat_class = CLUTTER_SEAT_CLASS (klass);
376
377 object_class->constructed = meta_seat_native_constructed;
378 object_class->set_property = meta_seat_native_set_property;
379 object_class->get_property = meta_seat_native_get_property;
380 object_class->dispose = meta_seat_native_dispose;
381
382 seat_class->get_pointer = meta_seat_native_get_pointer;
383 seat_class->get_keyboard = meta_seat_native_get_keyboard;
384 seat_class->peek_devices = meta_seat_native_peek_devices;
385 seat_class->bell_notify = meta_seat_native_bell_notify;
386 seat_class->get_keymap = meta_seat_native_get_keymap;
387 seat_class->create_virtual_device = meta_seat_native_create_virtual_device;
388 seat_class->get_supported_virtual_device_types = meta_seat_native_get_supported_virtual_device_types;
389 seat_class->warp_pointer = meta_seat_native_warp_pointer;
390 seat_class->handle_event_post = meta_seat_native_handle_event_post;
391 seat_class->query_state = meta_seat_native_query_state;
392
393 props[PROP_SEAT_ID] =
394 g_param_spec_string ("seat-id",
395 "Seat ID",
396 "Seat ID",
397 NULL,
398 G_PARAM_READWRITE |
399 G_PARAM_CONSTRUCT_ONLY);
400
401 props[PROP_FLAGS] =
402 g_param_spec_flags ("flags",
403 "Flags",
404 "Flags",
405 META_TYPE_SEAT_NATIVE_FLAG,
406 META_SEAT_NATIVE_FLAG_NONE,
407 G_PARAM_READWRITE |
408 G_PARAM_CONSTRUCT_ONLY);
409
410 props[PROP_BACKEND] =
411 g_param_spec_object ("backend",
412 "Backend",
413 "Backend",
414 META_TYPE_BACKEND,
415 G_PARAM_READWRITE |
416 G_PARAM_CONSTRUCT_ONLY);
417
418 g_object_class_install_properties (object_class, N_PROPS, props);
419
420 g_object_class_override_property (object_class, PROP_TOUCH_MODE,
421 "touch-mode");
422 }
423
424 static void
meta_seat_native_init(MetaSeatNative * seat)425 meta_seat_native_init (MetaSeatNative *seat)
426 {
427 seat->reserved_virtual_slots = g_hash_table_new (NULL, NULL);
428 }
429
430 /**
431 * meta_seat_native_release_devices:
432 *
433 * Releases all the evdev devices that Clutter is currently managing. This api
434 * is typically used when switching away from the Clutter application when
435 * switching tty. The devices can be reclaimed later with a call to
436 * meta_seat_native_reclaim_devices().
437 *
438 * This function should only be called after clutter has been initialized.
439 */
440 void
meta_seat_native_release_devices(MetaSeatNative * seat)441 meta_seat_native_release_devices (MetaSeatNative *seat)
442 {
443 g_return_if_fail (META_IS_SEAT_NATIVE (seat));
444
445 if (seat->released)
446 {
447 g_warning ("meta_seat_native_release_devices() shouldn't be called "
448 "multiple times without a corresponding call to "
449 "meta_seat_native_reclaim_devices() first");
450 return;
451 }
452
453 meta_seat_impl_release_devices (seat->impl);
454 seat->released = TRUE;
455 }
456
457 /**
458 * meta_seat_native_reclaim_devices:
459 *
460 * This causes Clutter to re-probe for evdev devices. This is must only be
461 * called after a corresponding call to meta_seat_native_release_devices()
462 * was previously used to release all evdev devices. This API is typically
463 * used when a clutter application using evdev has regained focus due to
464 * switching ttys.
465 *
466 * This function should only be called after clutter has been initialized.
467 */
468 void
meta_seat_native_reclaim_devices(MetaSeatNative * seat)469 meta_seat_native_reclaim_devices (MetaSeatNative *seat)
470 {
471 if (!seat->released)
472 {
473 g_warning ("Spurious call to meta_seat_native_reclaim_devices() without "
474 "previous call to meta_seat_native_release_devices");
475 return;
476 }
477
478 meta_seat_impl_reclaim_devices (seat->impl);
479 seat->released = FALSE;
480 }
481
482 static struct xkb_keymap *
create_keymap(const char * layouts,const char * variants,const char * options)483 create_keymap (const char *layouts,
484 const char *variants,
485 const char *options)
486 {
487 struct xkb_rule_names names;
488 struct xkb_keymap *keymap;
489 struct xkb_context *context;
490
491 names.rules = DEFAULT_XKB_RULES_FILE;
492 names.model = DEFAULT_XKB_MODEL;
493 names.layout = layouts;
494 names.variant = variants;
495 names.options = options;
496
497 context = meta_create_xkb_context ();
498 keymap = xkb_keymap_new_from_names (context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
499 xkb_context_unref (context);
500
501 return keymap;
502 }
503
504 /**
505 * meta_seat_native_set_keyboard_map: (skip)
506 * @seat: the #ClutterSeat created by the evdev backend
507 * @keymap: the new keymap
508 *
509 * Instructs @evdev to use the specified keyboard map. This will cause
510 * the backend to drop the state and create a new one with the new
511 * map. To avoid state being lost, callers should ensure that no key
512 * is pressed when calling this function.
513 */
514 void
meta_seat_native_set_keyboard_map(MetaSeatNative * seat,const char * layouts,const char * variants,const char * options)515 meta_seat_native_set_keyboard_map (MetaSeatNative *seat,
516 const char *layouts,
517 const char *variants,
518 const char *options)
519 {
520 struct xkb_keymap *keymap, *impl_keymap;
521
522 keymap = create_keymap (layouts, variants, options);
523 impl_keymap = create_keymap (layouts, variants, options);
524
525 if (keymap == NULL)
526 {
527 g_warning ("Unable to load configured keymap: rules=%s, model=%s, layout=%s, variant=%s, options=%s",
528 DEFAULT_XKB_RULES_FILE, DEFAULT_XKB_MODEL, layouts,
529 variants, options);
530 return;
531 }
532
533 if (seat->xkb_keymap)
534 xkb_keymap_unref (seat->xkb_keymap);
535 seat->xkb_keymap = keymap;
536
537 meta_seat_impl_set_keyboard_map (seat->impl, impl_keymap);
538 xkb_keymap_unref (impl_keymap);
539 }
540
541 /**
542 * meta_seat_native_get_keyboard_map: (skip)
543 * @seat: the #ClutterSeat created by the evdev backend
544 *
545 * Retrieves the #xkb_keymap in use by the evdev backend.
546 *
547 * Return value: the #xkb_keymap.
548 */
549 struct xkb_keymap *
meta_seat_native_get_keyboard_map(MetaSeatNative * seat)550 meta_seat_native_get_keyboard_map (MetaSeatNative *seat)
551 {
552 g_return_val_if_fail (META_IS_SEAT_NATIVE (seat), NULL);
553
554 return seat->xkb_keymap;
555 }
556
557 /**
558 * meta_seat_native_set_keyboard_layout_index: (skip)
559 * @seat: the #ClutterSeat created by the evdev backend
560 * @idx: the xkb layout index to set
561 *
562 * Sets the xkb layout index on the backend's #xkb_state .
563 */
564 void
meta_seat_native_set_keyboard_layout_index(MetaSeatNative * seat,xkb_layout_index_t idx)565 meta_seat_native_set_keyboard_layout_index (MetaSeatNative *seat,
566 xkb_layout_index_t idx)
567 {
568 g_return_if_fail (META_IS_SEAT_NATIVE (seat));
569
570 seat->xkb_layout_index = idx;
571 meta_seat_impl_set_keyboard_layout_index (seat->impl, idx);
572 }
573
574 /**
575 * meta_seat_native_get_keyboard_layout_index: (skip)
576 */
577 xkb_layout_index_t
meta_seat_native_get_keyboard_layout_index(MetaSeatNative * seat)578 meta_seat_native_get_keyboard_layout_index (MetaSeatNative *seat)
579 {
580 return seat->xkb_layout_index;
581 }
582
583 MetaBarrierManagerNative *
meta_seat_native_get_barrier_manager(MetaSeatNative * seat)584 meta_seat_native_get_barrier_manager (MetaSeatNative *seat)
585 {
586 return meta_seat_impl_get_barrier_manager (seat->impl);
587 }
588
589 MetaBackend *
meta_seat_native_get_backend(MetaSeatNative * seat_native)590 meta_seat_native_get_backend (MetaSeatNative *seat_native)
591 {
592 return seat_native->backend;
593 }
594
595 void
meta_seat_native_set_pointer_constraint(MetaSeatNative * seat,MetaPointerConstraintImpl * constraint_impl)596 meta_seat_native_set_pointer_constraint (MetaSeatNative *seat,
597 MetaPointerConstraintImpl *constraint_impl)
598 {
599 meta_seat_impl_set_pointer_constraint (seat->impl, constraint_impl);
600 }
601
602 MetaCursorRenderer *
meta_seat_native_maybe_ensure_cursor_renderer(MetaSeatNative * seat_native,ClutterInputDevice * device)603 meta_seat_native_maybe_ensure_cursor_renderer (MetaSeatNative *seat_native,
604 ClutterInputDevice *device)
605 {
606 if (device == seat_native->core_pointer)
607 {
608 if (!seat_native->cursor_renderer)
609 {
610 MetaCursorRendererNative *cursor_renderer_native;
611
612 cursor_renderer_native =
613 meta_cursor_renderer_native_new (meta_get_backend (),
614 seat_native->core_pointer);
615 seat_native->cursor_renderer =
616 META_CURSOR_RENDERER (cursor_renderer_native);
617 }
618
619 return seat_native->cursor_renderer;
620 }
621
622 if (seat_native->tablet_cursors &&
623 clutter_input_device_get_device_type (device) == CLUTTER_TABLET_DEVICE)
624 return g_hash_table_lookup (seat_native->tablet_cursors, device);
625
626 return NULL;
627 }
628
629 void
meta_seat_native_set_viewports(MetaSeatNative * seat,MetaViewportInfo * viewports)630 meta_seat_native_set_viewports (MetaSeatNative *seat,
631 MetaViewportInfo *viewports)
632 {
633 meta_seat_impl_set_viewports (seat->impl, viewports);
634 }
635