/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corp. * Copyright (C) 2014 Jonas Ådahl * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Damien Lespiau * Author: Jonas Ådahl */ #include "config.h" #include "backends/native/meta-seat-native.h" #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-keymap-utils.h" #include "backends/native/meta-barrier-native.h" #include "backends/native/meta-input-thread.h" #include "backends/native/meta-keymap-native.h" #include "backends/native/meta-virtual-input-device-native.h" #include "clutter/clutter-mutter.h" #include "core/bell.h" enum { PROP_0, PROP_SEAT_ID, N_PROPS, /* This property is overridden */ PROP_TOUCH_MODE, }; static GParamSpec *props[N_PROPS] = { NULL }; G_DEFINE_TYPE (MetaSeatNative, meta_seat_native, CLUTTER_TYPE_SEAT) static gboolean meta_seat_native_handle_event_post (ClutterSeat *seat, const ClutterEvent *event) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); ClutterInputDevice *device = clutter_event_get_source_device (event); ClutterEventType event_type = event->type; if (event_type == CLUTTER_PROXIMITY_IN) { MetaCursorRendererNative *cursor_renderer_native; if (!seat_native->tablet_cursors) { seat_native->tablet_cursors = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); } cursor_renderer_native = meta_cursor_renderer_native_new (meta_get_backend (), device); g_hash_table_insert (seat_native->tablet_cursors, device, cursor_renderer_native); return TRUE; } else if (event_type == CLUTTER_PROXIMITY_OUT) { if (seat_native->tablet_cursors) g_hash_table_remove (seat_native->tablet_cursors, device); return TRUE; } else if (event_type == CLUTTER_DEVICE_ADDED) { if (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_LOGICAL) seat_native->devices = g_list_prepend (seat_native->devices, g_object_ref (device)); } else if (event_type == CLUTTER_DEVICE_REMOVED) { GList *l = g_list_find (seat_native->devices, device); if (l) { seat_native->devices = g_list_delete_link (seat_native->devices, l); g_object_unref (device); } } return FALSE; } static void proxy_kbd_a11y_flags_changed (MetaSeatImpl *seat_impl, MetaKeyboardA11yFlags new_flags, MetaKeyboardA11yFlags what_changed, MetaSeatNative *seat_native) { g_signal_emit_by_name (seat_native, "kbd-a11y-flags-changed", new_flags, what_changed); } 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) { g_signal_emit_by_name (seat_native, "kbd-a11y-mods-state-changed", new_latched_mods, new_locked_mods); } static void proxy_touch_mode_changed (MetaSeatImpl *seat_impl, gboolean enabled, MetaSeatNative *seat_native) { seat_native->touch_mode = enabled; g_object_notify (G_OBJECT (seat_native), "touch-mode"); } static void proxy_bell (MetaSeatImpl *seat_impl, MetaSeatNative *seat_native) { clutter_seat_bell_notify (CLUTTER_SEAT (seat_native)); } static void proxy_mods_state_changed (MetaSeatImpl *seat_impl, ClutterSeat *seat) { ClutterKeymap *keymap; keymap = clutter_seat_get_keymap (seat); g_signal_emit_by_name (keymap, "state-changed"); } static void meta_seat_native_constructed (GObject *object) { MetaSeatNative *seat = META_SEAT_NATIVE (object); seat->impl = meta_seat_impl_new (seat, seat->seat_id); g_signal_connect (seat->impl, "kbd-a11y-flags-changed", G_CALLBACK (proxy_kbd_a11y_flags_changed), seat); g_signal_connect (seat->impl, "kbd-a11y-mods-state-changed", G_CALLBACK (proxy_kbd_a11y_mods_state_changed), seat); g_signal_connect (seat->impl, "touch-mode", G_CALLBACK (proxy_touch_mode_changed), seat); g_signal_connect (seat->impl, "bell", G_CALLBACK (proxy_bell), seat); g_signal_connect (seat->impl, "mods-state-changed", G_CALLBACK (proxy_mods_state_changed), seat); seat->core_pointer = meta_seat_impl_get_pointer (seat->impl); seat->core_keyboard = meta_seat_impl_get_keyboard (seat->impl); meta_seat_native_set_keyboard_map (seat, "us", "", ""); if (G_OBJECT_CLASS (meta_seat_native_parent_class)->constructed) G_OBJECT_CLASS (meta_seat_native_parent_class)->constructed (object); } static void meta_seat_native_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaSeatNative *seat_native = META_SEAT_NATIVE (object); switch (prop_id) { case PROP_SEAT_ID: seat_native->seat_id = g_value_dup_string (value); break; case PROP_TOUCH_MODE: default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_seat_native_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaSeatNative *seat_native = META_SEAT_NATIVE (object); switch (prop_id) { case PROP_SEAT_ID: g_value_set_string (value, seat_native->seat_id); break; case PROP_TOUCH_MODE: g_value_set_boolean (value, seat_native->touch_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_seat_native_finalize (GObject *object) { MetaSeatNative *seat = META_SEAT_NATIVE (object); GList *iter; if (seat->xkb_keymap) xkb_keymap_unref (seat->xkb_keymap); g_clear_object (&seat->core_pointer); g_clear_object (&seat->core_keyboard); g_clear_object (&seat->impl); for (iter = seat->devices; iter; iter = g_list_next (iter)) { ClutterInputDevice *device = iter->data; g_object_unref (device); } g_list_free (seat->devices); g_hash_table_destroy (seat->reserved_virtual_slots); g_clear_pointer (&seat->tablet_cursors, g_hash_table_unref); g_object_unref (seat->cursor_renderer); g_free (seat->seat_id); G_OBJECT_CLASS (meta_seat_native_parent_class)->finalize (object); } static ClutterInputDevice * meta_seat_native_get_pointer (ClutterSeat *seat) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); return seat_native->core_pointer; } static ClutterInputDevice * meta_seat_native_get_keyboard (ClutterSeat *seat) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); return seat_native->core_keyboard; } static const GList * meta_seat_native_peek_devices (ClutterSeat *seat) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); return (const GList *) seat_native->devices; } static void meta_seat_native_bell_notify (ClutterSeat *seat) { MetaDisplay *display = meta_get_display (); meta_bell_notify (display, NULL); } static ClutterKeymap * meta_seat_native_get_keymap (ClutterSeat *seat) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); if (!seat_native->keymap) seat_native->keymap = meta_seat_impl_get_keymap (seat_native->impl); return CLUTTER_KEYMAP (seat_native->keymap); } static guint bump_virtual_touch_slot_base (MetaSeatNative *seat_native) { while (TRUE) { if (seat_native->virtual_touch_slot_base < 0x100) seat_native->virtual_touch_slot_base = 0x100; seat_native->virtual_touch_slot_base += CLUTTER_VIRTUAL_INPUT_DEVICE_MAX_TOUCH_SLOTS; if (!g_hash_table_lookup (seat_native->reserved_virtual_slots, GUINT_TO_POINTER (seat_native->virtual_touch_slot_base))) break; } return seat_native->virtual_touch_slot_base; } static ClutterVirtualInputDevice * meta_seat_native_create_virtual_device (ClutterSeat *seat, ClutterInputDeviceType device_type) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); guint slot_base; slot_base = bump_virtual_touch_slot_base (seat_native); g_hash_table_add (seat_native->reserved_virtual_slots, GUINT_TO_POINTER (slot_base)); return g_object_new (META_TYPE_VIRTUAL_INPUT_DEVICE_NATIVE, "seat", seat, "slot-base", slot_base, "device-type", device_type, NULL); } void meta_seat_native_release_touch_slots (MetaSeatNative *seat, guint base_slot) { g_hash_table_remove (seat->reserved_virtual_slots, GUINT_TO_POINTER (base_slot)); } static ClutterVirtualDeviceType meta_seat_native_get_supported_virtual_device_types (ClutterSeat *seat) { return (CLUTTER_VIRTUAL_DEVICE_TYPE_KEYBOARD | CLUTTER_VIRTUAL_DEVICE_TYPE_POINTER | CLUTTER_VIRTUAL_DEVICE_TYPE_TOUCHSCREEN); } static void meta_seat_native_warp_pointer (ClutterSeat *seat, int x, int y) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); meta_seat_impl_warp_pointer (seat_native->impl, x, y); } static gboolean meta_seat_native_query_state (ClutterSeat *seat, ClutterInputDevice *device, ClutterEventSequence *sequence, graphene_point_t *coords, ClutterModifierType *modifiers) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); return meta_seat_impl_query_state (seat_native->impl, device, sequence, coords, modifiers); } static void meta_seat_native_class_init (MetaSeatNativeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterSeatClass *seat_class = CLUTTER_SEAT_CLASS (klass); object_class->constructed = meta_seat_native_constructed; object_class->set_property = meta_seat_native_set_property; object_class->get_property = meta_seat_native_get_property; object_class->finalize = meta_seat_native_finalize; seat_class->get_pointer = meta_seat_native_get_pointer; seat_class->get_keyboard = meta_seat_native_get_keyboard; seat_class->peek_devices = meta_seat_native_peek_devices; seat_class->bell_notify = meta_seat_native_bell_notify; seat_class->get_keymap = meta_seat_native_get_keymap; seat_class->create_virtual_device = meta_seat_native_create_virtual_device; seat_class->get_supported_virtual_device_types = meta_seat_native_get_supported_virtual_device_types; seat_class->warp_pointer = meta_seat_native_warp_pointer; seat_class->handle_event_post = meta_seat_native_handle_event_post; seat_class->query_state = meta_seat_native_query_state; props[PROP_SEAT_ID] = g_param_spec_string ("seat-id", "Seat ID", "Seat ID", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, N_PROPS, props); g_object_class_override_property (object_class, PROP_TOUCH_MODE, "touch-mode"); } static void meta_seat_native_init (MetaSeatNative *seat) { seat->reserved_virtual_slots = g_hash_table_new (NULL, NULL); } /** * meta_seat_native_release_devices: * * Releases all the evdev devices that Clutter is currently managing. This api * is typically used when switching away from the Clutter application when * switching tty. The devices can be reclaimed later with a call to * meta_seat_native_reclaim_devices(). * * This function should only be called after clutter has been initialized. */ void meta_seat_native_release_devices (MetaSeatNative *seat) { g_return_if_fail (META_IS_SEAT_NATIVE (seat)); if (seat->released) { g_warning ("meta_seat_native_release_devices() shouldn't be called " "multiple times without a corresponding call to " "meta_seat_native_reclaim_devices() first"); return; } meta_seat_impl_release_devices (seat->impl); seat->released = TRUE; } /** * meta_seat_native_reclaim_devices: * * This causes Clutter to re-probe for evdev devices. This is must only be * called after a corresponding call to meta_seat_native_release_devices() * was previously used to release all evdev devices. This API is typically * used when a clutter application using evdev has regained focus due to * switching ttys. * * This function should only be called after clutter has been initialized. */ void meta_seat_native_reclaim_devices (MetaSeatNative *seat) { if (!seat->released) { g_warning ("Spurious call to meta_seat_native_reclaim_devices() without " "previous call to meta_seat_native_release_devices"); return; } meta_seat_impl_reclaim_devices (seat->impl); seat->released = FALSE; } static struct xkb_keymap * create_keymap (const char *layouts, const char *variants, const char *options) { struct xkb_rule_names names; struct xkb_keymap *keymap; struct xkb_context *context; names.rules = DEFAULT_XKB_RULES_FILE; names.model = DEFAULT_XKB_MODEL; names.layout = layouts; names.variant = variants; names.options = options; context = meta_create_xkb_context (); keymap = xkb_keymap_new_from_names (context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); xkb_context_unref (context); return keymap; } /** * meta_seat_native_set_keyboard_map: (skip) * @seat: the #ClutterSeat created by the evdev backend * @keymap: the new keymap * * Instructs @evdev to use the specified keyboard map. This will cause * the backend to drop the state and create a new one with the new * map. To avoid state being lost, callers should ensure that no key * is pressed when calling this function. */ void meta_seat_native_set_keyboard_map (MetaSeatNative *seat, const char *layouts, const char *variants, const char *options) { struct xkb_keymap *keymap, *impl_keymap; keymap = create_keymap (layouts, variants, options); impl_keymap = create_keymap (layouts, variants, options); if (keymap == NULL) { g_warning ("Unable to load configured keymap: rules=%s, model=%s, layout=%s, variant=%s, options=%s", DEFAULT_XKB_RULES_FILE, DEFAULT_XKB_MODEL, layouts, variants, options); return; } if (seat->xkb_keymap) xkb_keymap_unref (seat->xkb_keymap); seat->xkb_keymap = keymap; meta_seat_impl_set_keyboard_map (seat->impl, impl_keymap); xkb_keymap_unref (impl_keymap); } /** * meta_seat_native_get_keyboard_map: (skip) * @seat: the #ClutterSeat created by the evdev backend * * Retrieves the #xkb_keymap in use by the evdev backend. * * Return value: the #xkb_keymap. */ struct xkb_keymap * meta_seat_native_get_keyboard_map (MetaSeatNative *seat) { g_return_val_if_fail (META_IS_SEAT_NATIVE (seat), NULL); return seat->xkb_keymap; } /** * meta_seat_native_set_keyboard_layout_index: (skip) * @seat: the #ClutterSeat created by the evdev backend * @idx: the xkb layout index to set * * Sets the xkb layout index on the backend's #xkb_state . */ void meta_seat_native_set_keyboard_layout_index (MetaSeatNative *seat, xkb_layout_index_t idx) { g_return_if_fail (META_IS_SEAT_NATIVE (seat)); seat->xkb_layout_index = idx; meta_seat_impl_set_keyboard_layout_index (seat->impl, idx); } /** * meta_seat_native_get_keyboard_layout_index: (skip) */ xkb_layout_index_t meta_seat_native_get_keyboard_layout_index (MetaSeatNative *seat) { return seat->xkb_layout_index; } MetaBarrierManagerNative * meta_seat_native_get_barrier_manager (MetaSeatNative *seat) { return meta_seat_impl_get_barrier_manager (seat->impl); } void meta_seat_native_set_pointer_constraint (MetaSeatNative *seat, MetaPointerConstraintImpl *constraint_impl) { meta_seat_impl_set_pointer_constraint (seat->impl, constraint_impl); } MetaCursorRenderer * meta_seat_native_maybe_ensure_cursor_renderer (MetaSeatNative *seat_native, ClutterInputDevice *device) { if (device == seat_native->core_pointer) { if (!seat_native->cursor_renderer) { MetaCursorRendererNative *cursor_renderer_native; cursor_renderer_native = meta_cursor_renderer_native_new (meta_get_backend (), seat_native->core_pointer); seat_native->cursor_renderer = META_CURSOR_RENDERER (cursor_renderer_native); } return seat_native->cursor_renderer; } if (seat_native->tablet_cursors && clutter_input_device_get_device_type (device) == CLUTTER_TABLET_DEVICE) return g_hash_table_lookup (seat_native->tablet_cursors, device); return NULL; } void meta_seat_native_set_viewports (MetaSeatNative *seat, MetaViewportInfo *viewports) { meta_seat_impl_set_viewports (seat->impl, viewports); }