From 06adde5c6b5b59199558789f838eb77ec1f0a28e Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 7 Aug 2020 15:13:51 +0200 Subject: [PATCH] backends/native: Spin MetaSeatImpl off MetaSeatNative Move most of the functional bits (those meant to run on a standalone thread) to a MetaSeatImpl object. This object is managed by the MetaSeatImpl and not exposed outside the friend MetaSeatNative/MetaInputDeviceNative/ MetaInputSettings classes. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1403 --- .../native/meta-backend-native-types.h | 1 + .../native/meta-input-device-native.c | 78 +- .../native/meta-input-device-native.h | 8 +- .../native/meta-input-settings-native.c | 4 +- src/backends/native/meta-keymap-native.c | 4 +- src/backends/native/meta-launcher.c | 6 +- src/backends/native/meta-seat-impl.c | 3068 +++++++++++++++++ src/backends/native/meta-seat-impl.h | 257 ++ src/backends/native/meta-seat-native.c | 2784 +-------------- src/backends/native/meta-seat-native.h | 145 +- .../native/meta-virtual-input-device-native.c | 172 +- src/meson.build | 2 + 12 files changed, 3530 insertions(+), 2999 deletions(-) create mode 100644 src/backends/native/meta-seat-impl.c create mode 100644 src/backends/native/meta-seat-impl.h diff --git a/src/backends/native/meta-backend-native-types.h b/src/backends/native/meta-backend-native-types.h index 3112e915d..bfc506788 100644 --- a/src/backends/native/meta-backend-native-types.h +++ b/src/backends/native/meta-backend-native-types.h @@ -22,5 +22,6 @@ #define META_BACKEND_NATIVE_TYPES_H typedef struct _MetaBackendNative MetaBackendNative; +typedef struct _MetaSeatNative MetaSeatNative; #endif /* META_BACKEND_NATIVE_TYPES_H */ diff --git a/src/backends/native/meta-input-device-native.c b/src/backends/native/meta-input-device-native.c index dc788f186..f5e21de60 100644 --- a/src/backends/native/meta-input-device-native.c +++ b/src/backends/native/meta-input-device-native.c @@ -70,7 +70,7 @@ meta_input_device_native_finalize (GObject *object) backend = clutter_get_default_backend (); seat = clutter_backend_get_default_seat (backend); - meta_seat_native_release_device_id (META_SEAT_NATIVE (seat), device); + meta_seat_impl_release_device_id (META_SEAT_NATIVE (seat)->impl, device); clear_slow_keys (device_evdev); stop_bounce_keys (device_evdev); @@ -222,7 +222,7 @@ meta_input_device_native_is_grouped (ClutterInputDevice *device, static void meta_input_device_native_bell_notify (MetaInputDeviceNative *device) { - clutter_seat_bell_notify (CLUTTER_SEAT (device->seat)); + clutter_seat_bell_notify (CLUTTER_SEAT (device->seat_impl->seat)); } static void @@ -423,7 +423,7 @@ key_event_is_modifier (ClutterEvent *event) static void notify_stickykeys_mask (MetaInputDeviceNative *device) { - g_signal_emit_by_name (device->seat, + g_signal_emit_by_name (device->seat_impl->seat, "kbd-a11y-mods-state-changed", device->stickykeys_latched_mask, device->stickykeys_locked_mask); @@ -434,15 +434,17 @@ update_internal_xkb_state (MetaInputDeviceNative *device, xkb_mod_mask_t new_latched_mask, xkb_mod_mask_t new_locked_mask) { - MetaSeatNative *seat = device->seat; + MetaSeatImpl *seat_impl = device->seat_impl; xkb_mod_mask_t depressed_mods; xkb_mod_mask_t latched_mods; xkb_mod_mask_t locked_mods; xkb_mod_mask_t group_mods; + struct xkb_state *xkb_state; - depressed_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_DEPRESSED); - latched_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LATCHED); - locked_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LOCKED); + xkb_state = meta_seat_impl_get_xkb_state (seat_impl); + depressed_mods = xkb_state_serialize_mods (xkb_state, XKB_STATE_MODS_DEPRESSED); + latched_mods = xkb_state_serialize_mods (xkb_state, XKB_STATE_MODS_LATCHED); + locked_mods = xkb_state_serialize_mods (xkb_state, XKB_STATE_MODS_LOCKED); latched_mods &= ~device->stickykeys_latched_mask; locked_mods &= ~device->stickykeys_locked_mask; @@ -453,9 +455,9 @@ update_internal_xkb_state (MetaInputDeviceNative *device, latched_mods |= device->stickykeys_latched_mask; locked_mods |= device->stickykeys_locked_mask; - group_mods = xkb_state_serialize_layout (seat->xkb, XKB_STATE_LAYOUT_EFFECTIVE); + group_mods = xkb_state_serialize_layout (xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); - xkb_state_update_mask (seat->xkb, + xkb_state_update_mask (xkb_state, depressed_mods, latched_mods, locked_mods, @@ -469,23 +471,25 @@ update_stickykeys_event (ClutterEvent *event, xkb_mod_mask_t new_latched_mask, xkb_mod_mask_t new_locked_mask) { - MetaSeatNative *seat = device->seat; + MetaSeatImpl *seat_impl = device->seat_impl; xkb_mod_mask_t effective_mods; xkb_mod_mask_t latched_mods; xkb_mod_mask_t locked_mods; + struct xkb_state *xkb_state; update_internal_xkb_state (device, new_latched_mask, new_locked_mask); - effective_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_EFFECTIVE); - latched_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LATCHED); - locked_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LOCKED); + xkb_state = meta_seat_impl_get_xkb_state (seat_impl); + effective_mods = xkb_state_serialize_mods (xkb_state, XKB_STATE_MODS_EFFECTIVE); + latched_mods = xkb_state_serialize_mods (xkb_state, XKB_STATE_MODS_LATCHED); + locked_mods = xkb_state_serialize_mods (xkb_state, XKB_STATE_MODS_LOCKED); _clutter_event_set_state_full (event, - seat->button_state, + seat_impl->button_state, device->stickykeys_depressed_mask, latched_mods, locked_mods, - effective_mods | seat->button_state); + effective_mods | seat_impl->button_state); } static void @@ -549,10 +553,11 @@ static void handle_stickykeys_press (ClutterEvent *event, MetaInputDeviceNative *device) { - MetaSeatNative *seat = device->seat; + MetaSeatImpl *seat_impl = device->seat_impl; xkb_mod_mask_t depressed_mods; xkb_mod_mask_t new_latched_mask; xkb_mod_mask_t new_locked_mask; + struct xkb_state *xkb_state; if (!key_event_is_modifier (event)) return; @@ -564,7 +569,8 @@ handle_stickykeys_press (ClutterEvent *event, return; } - depressed_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_DEPRESSED); + xkb_state = meta_seat_impl_get_xkb_state (seat_impl); + depressed_mods = xkb_state_serialize_mods (xkb_state, XKB_STATE_MODS_DEPRESSED); /* Ignore the lock modifier mask, that one cannot be sticky, yet the * CAPS_LOCK key itself counts as a modifier as it might be remapped * to some other modifier which can be sticky. @@ -597,10 +603,12 @@ static void handle_stickykeys_release (ClutterEvent *event, MetaInputDeviceNative *device) { - MetaSeatNative *seat = device->seat; + MetaSeatImpl *seat_impl = device->seat_impl; + struct xkb_state *xkb_state; + xkb_state = meta_seat_impl_get_xkb_state (seat_impl); device->stickykeys_depressed_mask = - xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_DEPRESSED); + xkb_state_serialize_mods (xkb_state, XKB_STATE_MODS_DEPRESSED); if (key_event_is_modifier (event)) { @@ -838,8 +846,12 @@ emulate_pointer_motion (MetaInputDeviceNative *device_evdev, static gboolean is_numlock_active (MetaInputDeviceNative *device) { - MetaSeatNative *seat = device->seat; - return xkb_state_mod_name_is_active (seat->xkb, + MetaSeatImpl *seat_impl = device->seat_impl; + struct xkb_state *xkb_state; + + xkb_state = meta_seat_impl_get_xkb_state (seat_impl); + + return xkb_state_mod_name_is_active (xkb_state, "Mod2", XKB_STATE_MODS_LOCKED); } @@ -859,7 +871,7 @@ enable_mousekeys (MetaInputDeviceNative *device_evdev) return; device->accessibility_virtual_device = - clutter_seat_create_virtual_device (CLUTTER_SEAT (device_evdev->seat), + clutter_seat_create_virtual_device (clutter_input_device_get_seat (device), CLUTTER_POINTER_DEVICE); } @@ -1276,7 +1288,7 @@ meta_input_device_native_init (MetaInputDeviceNative *self) * it with the provided seat. */ ClutterInputDevice * -meta_input_device_native_new (MetaSeatNative *seat, +meta_input_device_native_new (MetaSeatImpl *seat_impl, struct libinput_device *libinput_device) { MetaInputDeviceNative *device; @@ -1289,7 +1301,7 @@ meta_input_device_native_new (MetaSeatNative *seat, type = meta_input_device_native_determine_type (libinput_device); vendor = g_strdup_printf ("%.4x", libinput_device_get_id_vendor (libinput_device)); product = g_strdup_printf ("%.4x", libinput_device_get_id_product (libinput_device)); - device_id = meta_seat_native_acquire_device_id (seat); + device_id = meta_seat_impl_acquire_device_id (seat_impl); node_path = g_strdup_printf ("/dev/input/%s", libinput_device_get_sysname (libinput_device)); if (libinput_device_has_capability (libinput_device, @@ -1311,10 +1323,10 @@ meta_input_device_native_new (MetaSeatNative *seat, "n-strips", n_strips, "n-mode-groups", n_groups, "device-node", node_path, - "seat", seat, + "seat", seat_impl->seat, NULL); - device->seat = seat; + device->seat_impl = seat_impl; device->libinput_device = libinput_device; libinput_device_set_user_data (libinput_device, device); @@ -1337,7 +1349,7 @@ meta_input_device_native_new (MetaSeatNative *seat, * Create a new virtual ClutterInputDevice of the given type. */ ClutterInputDevice * -meta_input_device_native_new_virtual (MetaSeatNative *seat, +meta_input_device_native_new_virtual (MetaSeatImpl *seat_impl, ClutterInputDeviceType type, ClutterInputMode mode) { @@ -1361,24 +1373,24 @@ meta_input_device_native_new_virtual (MetaSeatNative *seat, break; }; - device_id = meta_seat_native_acquire_device_id (seat); + device_id = meta_seat_impl_acquire_device_id (seat_impl); device = g_object_new (META_TYPE_INPUT_DEVICE_NATIVE, "id", device_id, "name", name, "device-type", type, "device-mode", mode, - "seat", seat, + "seat", seat_impl->seat, NULL); - device->seat = seat; + device->seat_impl = seat_impl; return CLUTTER_INPUT_DEVICE (device); } -MetaSeatNative * -meta_input_device_native_get_seat (MetaInputDeviceNative *device) +MetaSeatImpl * +meta_input_device_native_get_seat_impl (MetaInputDeviceNative *device) { - return device->seat; + return device->seat_impl; } void diff --git a/src/backends/native/meta-input-device-native.h b/src/backends/native/meta-input-device-native.h index 31db4a392..e5fd488e0 100644 --- a/src/backends/native/meta-input-device-native.h +++ b/src/backends/native/meta-input-device-native.h @@ -69,7 +69,7 @@ struct _MetaInputDeviceNative ClutterInputDevice parent; struct libinput_device *libinput_device; - MetaSeatNative *seat; + MetaSeatImpl *seat_impl; ClutterInputDeviceTool *last_tool; cairo_matrix_t device_matrix; @@ -111,14 +111,14 @@ struct _MetaInputDeviceNativeClass GType meta_input_device_native_get_type (void) G_GNUC_CONST; -ClutterInputDevice * meta_input_device_native_new (MetaSeatNative *seat, +ClutterInputDevice * meta_input_device_native_new (MetaSeatImpl *seat_impl, struct libinput_device *libinput_device); -ClutterInputDevice * meta_input_device_native_new_virtual (MetaSeatNative *seat, +ClutterInputDevice * meta_input_device_native_new_virtual (MetaSeatImpl *seat_impl, ClutterInputDeviceType type, ClutterInputMode mode); -MetaSeatNative * meta_input_device_native_get_seat (MetaInputDeviceNative *device); +MetaSeatImpl * meta_input_device_native_get_seat_impl (MetaInputDeviceNative *device); void meta_input_device_native_update_leds (MetaInputDeviceNative *device, enum libinput_led leds); diff --git a/src/backends/native/meta-input-settings-native.c b/src/backends/native/meta-input-settings-native.c index 7bf6eefb4..aaeb9bc7b 100644 --- a/src/backends/native/meta-input-settings-native.c +++ b/src/backends/native/meta-input-settings-native.c @@ -389,8 +389,8 @@ meta_input_settings_native_set_keyboard_repeat (MetaInputSettings *settings, ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); - meta_seat_native_set_keyboard_repeat (META_SEAT_NATIVE (seat), - enabled, delay, interval); + meta_seat_impl_set_keyboard_repeat (META_SEAT_NATIVE (seat)->impl, + enabled, delay, interval); } static void diff --git a/src/backends/native/meta-keymap-native.c b/src/backends/native/meta-keymap-native.c index f55cd059f..034f1e6f4 100644 --- a/src/backends/native/meta-keymap-native.c +++ b/src/backends/native/meta-keymap-native.c @@ -58,7 +58,7 @@ meta_keymap_native_get_num_lock_state (ClutterKeymap *keymap) ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); - xkb_state = meta_seat_native_get_xkb_state (META_SEAT_NATIVE (seat)); + xkb_state = meta_seat_impl_get_xkb_state (META_SEAT_NATIVE (seat)->impl); return xkb_state_mod_name_is_active (xkb_state, XKB_MOD_NAME_NUM, @@ -73,7 +73,7 @@ meta_keymap_native_get_caps_lock_state (ClutterKeymap *keymap) ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); - xkb_state = meta_seat_native_get_xkb_state (META_SEAT_NATIVE (seat)); + xkb_state = meta_seat_impl_get_xkb_state (META_SEAT_NATIVE (seat)->impl); return xkb_state_mod_name_is_active (xkb_state, XKB_MOD_NAME_CAPS, diff --git a/src/backends/native/meta-launcher.c b/src/backends/native/meta-launcher.c index 61fe19be0..bb05f3382 100644 --- a/src/backends/native/meta-launcher.c +++ b/src/backends/native/meta-launcher.c @@ -528,9 +528,9 @@ meta_launcher_new (GError **error) meta_clutter_backend_native_set_seat_id (self->seat_id); - meta_seat_native_set_device_callbacks (on_evdev_device_open, - on_evdev_device_close, - self); + meta_seat_impl_set_device_callbacks (on_evdev_device_open, + on_evdev_device_close, + self); g_signal_connect (self->session_proxy, "notify::active", G_CALLBACK (on_active_changed), self); diff --git a/src/backends/native/meta-seat-impl.c b/src/backends/native/meta-seat-impl.c new file mode 100644 index 000000000..85db01d72 --- /dev/null +++ b/src/backends/native/meta-seat-impl.c @@ -0,0 +1,3068 @@ +/* + * 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-impl.h" + +#include +#include +#include +#include +#include + +#include "backends/meta-cursor-tracker-private.h" +#include "backends/native/meta-barrier-native.h" +#include "backends/native/meta-event-native.h" +#include "backends/native/meta-input-device-native.h" +#include "backends/native/meta-input-device-tool-native.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" + +/* + * Clutter makes the assumption that two core devices have ID's 2 and 3 (core + * pointer and core keyboard). + * + * Since the two first devices that will ever be created will be the virtual + * pointer and virtual keyboard of the first seat, we fulfill the made + * assumptions by having the first device having ID 2 and following 3. + */ +#define INITIAL_DEVICE_ID 2 + +/* Try to keep the pointer inside the stage. Hopefully no one is using + * this backend with stages smaller than this. */ +#define INITIAL_POINTER_X 16 +#define INITIAL_POINTER_Y 16 + +#define AUTOREPEAT_VALUE 2 + +#define DISCRETE_SCROLL_STEP 10.0 + +#ifndef BTN_STYLUS3 +#define BTN_STYLUS3 0x149 /* Linux 4.15 */ +#endif + +struct _MetaEventSource +{ + GSource source; + + MetaSeatImpl *seat_impl; + GPollFD event_poll_fd; +}; + +static MetaOpenDeviceCallback device_open_callback; +static MetaCloseDeviceCallback device_close_callback; +static gpointer device_callback_data; + +#ifdef CLUTTER_ENABLE_DEBUG +static const char *device_type_str[] = { + "pointer", /* CLUTTER_POINTER_DEVICE */ + "keyboard", /* CLUTTER_KEYBOARD_DEVICE */ + "extension", /* CLUTTER_EXTENSION_DEVICE */ + "joystick", /* CLUTTER_JOYSTICK_DEVICE */ + "tablet", /* CLUTTER_TABLET_DEVICE */ + "touchpad", /* CLUTTER_TOUCHPAD_DEVICE */ + "touchscreen", /* CLUTTER_TOUCHSCREEN_DEVICE */ + "pen", /* CLUTTER_PEN_DEVICE */ + "eraser", /* CLUTTER_ERASER_DEVICE */ + "cursor", /* CLUTTER_CURSOR_DEVICE */ + "pad", /* CLUTTER_PAD_DEVICE */ +}; +#endif /* CLUTTER_ENABLE_DEBUG */ + +enum +{ + PROP_0, + PROP_SEAT, + PROP_SEAT_ID, + N_PROPS, +}; + +static GParamSpec *props[N_PROPS] = { NULL }; + +G_DEFINE_TYPE (MetaSeatImpl, meta_seat_impl, G_TYPE_OBJECT) + +static void process_events (MetaSeatImpl *seat); +void meta_seat_impl_constrain_pointer (MetaSeatImpl *seat, + ClutterInputDevice *core_pointer, + uint64_t time_us, + float x, + float y, + float *new_x, + float *new_y); +void meta_seat_impl_filter_relative_motion (MetaSeatImpl *seat, + ClutterInputDevice *device, + float x, + float y, + float *dx, + float *dy); +void meta_seat_impl_clear_repeat_timer (MetaSeatImpl *seat); + +void +meta_seat_impl_set_libinput_seat (MetaSeatImpl *seat, + struct libinput_seat *libinput_seat) +{ + g_assert (seat->libinput_seat == NULL); + + libinput_seat_ref (libinput_seat); + libinput_seat_set_user_data (libinput_seat, seat); + seat->libinput_seat = libinput_seat; +} + +void +meta_seat_impl_sync_leds (MetaSeatImpl *seat) +{ + GSList *iter; + MetaInputDeviceNative *device_evdev; + int caps_lock, num_lock, scroll_lock; + enum libinput_led leds = 0; + + caps_lock = xkb_state_led_index_is_active (seat->xkb, seat->caps_lock_led); + num_lock = xkb_state_led_index_is_active (seat->xkb, seat->num_lock_led); + scroll_lock = xkb_state_led_index_is_active (seat->xkb, seat->scroll_lock_led); + + if (caps_lock) + leds |= LIBINPUT_LED_CAPS_LOCK; + if (num_lock) + leds |= LIBINPUT_LED_NUM_LOCK; + if (scroll_lock) + leds |= LIBINPUT_LED_SCROLL_LOCK; + + for (iter = seat->devices; iter; iter = iter->next) + { + device_evdev = iter->data; + meta_input_device_native_update_leds (device_evdev, leds); + } +} + +MetaTouchState * +meta_seat_impl_lookup_touch_state (MetaSeatImpl *seat, + int seat_slot) +{ + if (!seat->touch_states) + return NULL; + return g_hash_table_lookup (seat->touch_states, GINT_TO_POINTER (seat_slot)); +} + +static void +meta_touch_state_free (MetaTouchState *state) +{ + g_slice_free (MetaTouchState, state); +} + +MetaTouchState * +meta_seat_impl_acquire_touch_state (MetaSeatImpl *seat, + int seat_slot) +{ + MetaTouchState *touch_state; + + if (!seat->touch_states) + { + seat->touch_states = + g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) meta_touch_state_free); + } + + g_assert (!g_hash_table_contains (seat->touch_states, + GINT_TO_POINTER (seat_slot))); + + touch_state = g_slice_new0 (MetaTouchState); + *touch_state = (MetaTouchState) { + .seat = seat, + .seat_slot = seat_slot, + }; + + g_hash_table_insert (seat->touch_states, GINT_TO_POINTER (seat_slot), + touch_state); + + return touch_state; +} + +void +meta_seat_impl_release_touch_state (MetaSeatImpl *seat, + int seat_slot) +{ + if (!seat->touch_states) + return; + g_hash_table_remove (seat->touch_states, GINT_TO_POINTER (seat_slot)); +} + +void +meta_seat_impl_clear_repeat_timer (MetaSeatImpl *seat) +{ + if (seat->repeat_timer) + { + g_clear_handle_id (&seat->repeat_timer, g_source_remove); + g_clear_object (&seat->repeat_device); + } +} + +static void +dispatch_libinput (MetaSeatImpl *seat) +{ + libinput_dispatch (seat->libinput); + process_events (seat); +} + +static gboolean +keyboard_repeat (gpointer data) +{ + MetaSeatImpl *seat = data; + GSource *source; + + /* There might be events queued in libinput that could cancel the + repeat timer. */ + dispatch_libinput (seat); + if (!seat->repeat_timer) + return G_SOURCE_REMOVE; + + g_return_val_if_fail (seat->repeat_device != NULL, G_SOURCE_REMOVE); + source = g_main_context_find_source_by_id (NULL, seat->repeat_timer); + + meta_seat_impl_notify_key (seat, + seat->repeat_device, + g_source_get_time (source), + seat->repeat_key, + AUTOREPEAT_VALUE, + FALSE); + + return G_SOURCE_CONTINUE; +} + +static void +queue_event (MetaSeatImpl *seat, + ClutterEvent *event) +{ + _clutter_event_push (event, FALSE); +} + +static int +update_button_count (MetaSeatImpl *seat, + uint32_t button, + uint32_t state) +{ + if (state) + { + return ++seat->button_count[button]; + } + else + { + /* Handle cases where we newer saw the initial pressed event. */ + if (seat->button_count[button] == 0) + { + meta_topic (META_DEBUG_INPUT, + "Counting release of key 0x%x and count is already 0\n", + button); + return 0; + } + + return --seat->button_count[button]; + } +} + +void +meta_seat_impl_notify_key (MetaSeatImpl *seat, + ClutterInputDevice *device, + uint64_t time_us, + uint32_t key, + uint32_t state, + gboolean update_keys) +{ + ClutterEvent *event = NULL; + enum xkb_state_component changed_state; + + if (state != AUTOREPEAT_VALUE) + { + /* Drop any repeated button press (for example from virtual devices. */ + int count = update_button_count (seat, key, state); + if ((state && count > 1) || + (!state && count != 0)) + { + meta_topic (META_DEBUG_INPUT, + "Dropping repeated %s of key 0x%x, count %d, state %d\n", + state ? "press" : "release", key, count, state); + return; + } + } + + event = meta_key_event_new_from_evdev (device, + seat->core_keyboard, + seat->xkb, + seat->button_state, + us2ms (time_us), key, state); + meta_event_native_set_event_code (event, key); + + /* We must be careful and not pass multiple releases to xkb, otherwise it gets + confused and locks the modifiers */ + if (state != AUTOREPEAT_VALUE) + { + changed_state = xkb_state_update_key (seat->xkb, + event->key.hardware_keycode, + state ? XKB_KEY_DOWN : XKB_KEY_UP); + } + else + { + changed_state = 0; + clutter_event_set_flags (event, CLUTTER_EVENT_FLAG_REPEATED); + } + + queue_event (seat, event); + + if (update_keys && (changed_state & XKB_STATE_LEDS)) + { + g_signal_emit_by_name (seat->keymap, "state-changed"); + meta_seat_impl_sync_leds (seat); + meta_input_device_native_a11y_maybe_notify_toggle_keys (META_INPUT_DEVICE_NATIVE (seat->core_keyboard)); + } + + if (state == 0 || /* key release */ + !seat->repeat || + !xkb_keymap_key_repeats (xkb_state_get_keymap (seat->xkb), + event->key.hardware_keycode)) + { + meta_seat_impl_clear_repeat_timer (seat); + return; + } + + if (state == 1) /* key press */ + seat->repeat_count = 0; + + seat->repeat_count += 1; + seat->repeat_key = key; + + switch (seat->repeat_count) + { + case 1: + case 2: + { + uint32_t interval; + + meta_seat_impl_clear_repeat_timer (seat); + seat->repeat_device = g_object_ref (device); + + if (seat->repeat_count == 1) + interval = seat->repeat_delay; + else + interval = seat->repeat_interval; + + seat->repeat_timer = + clutter_threads_add_timeout_full (CLUTTER_PRIORITY_EVENTS, + interval, + keyboard_repeat, + seat, + NULL); + return; + } + default: + return; + } +} + +static ClutterEvent * +new_absolute_motion_event (MetaSeatImpl *seat, + ClutterInputDevice *input_device, + uint64_t time_us, + float x, + float y, + double *axes) +{ + ClutterEvent *event; + + event = clutter_event_new (CLUTTER_MOTION); + + if (clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE) + { + meta_seat_impl_constrain_pointer (seat, + seat->core_pointer, + time_us, + seat->pointer_x, + seat->pointer_y, + &x, &y); + } + + meta_event_native_set_time_usec (event, time_us); + event->motion.time = us2ms (time_us); + meta_xkb_translate_state (event, seat->xkb, seat->button_state); + event->motion.x = x; + event->motion.y = y; + + /* This may happen early at startup */ + if (seat->viewports) + { + meta_input_device_native_translate_coordinates (input_device, + seat->viewports, + &event->motion.x, + &event->motion.y); + } + + event->motion.axes = axes; + clutter_event_set_device (event, seat->core_pointer); + clutter_event_set_source_device (event, input_device); + + if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) + { + MetaInputDeviceNative *device_evdev = + META_INPUT_DEVICE_NATIVE (input_device); + + clutter_event_set_device_tool (event, device_evdev->last_tool); + clutter_event_set_device (event, input_device); + meta_input_device_native_update_coords (META_INPUT_DEVICE_NATIVE (input_device), + x, y); + } + else + { + clutter_event_set_device (event, seat->core_pointer); + meta_input_device_native_update_coords (META_INPUT_DEVICE_NATIVE (seat->core_pointer), + x, y); + } + + if (clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE) + { + seat->pointer_x = x; + seat->pointer_y = y; + } + + return event; +} + +void +meta_seat_impl_notify_relative_motion (MetaSeatImpl *seat, + ClutterInputDevice *input_device, + uint64_t time_us, + float dx, + float dy, + float dx_unaccel, + float dy_unaccel) +{ + float new_x, new_y; + ClutterEvent *event; + + meta_seat_impl_filter_relative_motion (seat, + input_device, + seat->pointer_x, + seat->pointer_y, + &dx, + &dy); + + new_x = seat->pointer_x + dx; + new_y = seat->pointer_y + dy; + event = new_absolute_motion_event (seat, input_device, + time_us, new_x, new_y, NULL); + + meta_event_native_set_relative_motion (event, + dx, dy, + dx_unaccel, dy_unaccel); + + queue_event (seat, event); +} + +void +meta_seat_impl_notify_absolute_motion (MetaSeatImpl *seat, + ClutterInputDevice *input_device, + uint64_t time_us, + float x, + float y, + double *axes) +{ + ClutterEvent *event; + + event = new_absolute_motion_event (seat, input_device, time_us, x, y, axes); + + queue_event (seat, event); +} + +void +meta_seat_impl_notify_button (MetaSeatImpl *seat, + ClutterInputDevice *input_device, + uint64_t time_us, + uint32_t button, + uint32_t state) +{ + MetaInputDeviceNative *device_evdev = (MetaInputDeviceNative *) input_device; + ClutterEvent *event = NULL; + int button_nr; + static int maskmap[8] = + { + CLUTTER_BUTTON1_MASK, CLUTTER_BUTTON3_MASK, CLUTTER_BUTTON2_MASK, + CLUTTER_BUTTON4_MASK, CLUTTER_BUTTON5_MASK, 0, 0, 0 + }; + int button_count; + + /* Drop any repeated button press (for example from virtual devices. */ + button_count = update_button_count (seat, button, state); + if ((state && button_count > 1) || + (!state && button_count != 0)) + { + meta_topic (META_DEBUG_INPUT, + "Dropping repeated %s of button 0x%x, count %d\n", + state ? "press" : "release", button, button_count); + return; + } + + /* The evdev button numbers don't map sequentially to clutter button + * numbers (the right and middle mouse buttons are in the opposite + * order) so we'll map them directly with a switch statement */ + switch (button) + { + case BTN_LEFT: + case BTN_TOUCH: + button_nr = CLUTTER_BUTTON_PRIMARY; + break; + + case BTN_RIGHT: + case BTN_STYLUS: + button_nr = CLUTTER_BUTTON_SECONDARY; + break; + + case BTN_MIDDLE: + case BTN_STYLUS2: + button_nr = CLUTTER_BUTTON_MIDDLE; + break; + + case 0x149: /* BTN_STYLUS3 */ + button_nr = 8; + break; + + default: + /* For compatibility reasons, all additional buttons go after the old 4-7 scroll ones */ + if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) + button_nr = button - BTN_TOOL_PEN + 4; + else + button_nr = button - (BTN_LEFT - 1) + 4; + break; + } + + if (button_nr < 1 || button_nr > 12) + { + g_warning ("Unhandled button event 0x%x", button); + return; + } + + if (state) + event = clutter_event_new (CLUTTER_BUTTON_PRESS); + else + event = clutter_event_new (CLUTTER_BUTTON_RELEASE); + + if (button_nr < G_N_ELEMENTS (maskmap)) + { + /* Update the modifiers */ + if (state) + seat->button_state |= maskmap[button_nr - 1]; + else + seat->button_state &= ~maskmap[button_nr - 1]; + } + + meta_event_native_set_time_usec (event, time_us); + event->button.time = us2ms (time_us); + meta_xkb_translate_state (event, seat->xkb, seat->button_state); + event->button.button = button_nr; + + if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) + { + graphene_point_t point; + + clutter_input_device_get_coords (input_device, NULL, &point); + event->button.x = point.x; + event->button.y = point.y; + } + else + { + event->button.x = seat->pointer_x; + event->button.y = seat->pointer_y; + } + + clutter_event_set_device (event, seat->core_pointer); + clutter_event_set_source_device (event, input_device); + + if (device_evdev->last_tool) + { + /* Apply the button event code as per the tool mapping */ + uint32_t mapped_button; + + mapped_button = meta_input_device_tool_native_get_button_code (device_evdev->last_tool, + button_nr); + if (mapped_button != 0) + button = mapped_button; + } + + meta_event_native_set_event_code (event, button); + + if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) + { + clutter_event_set_device_tool (event, device_evdev->last_tool); + clutter_event_set_device (event, input_device); + } + else + { + clutter_event_set_device (event, seat->core_pointer); + } + + queue_event (seat, event); +} + +static MetaSeatImpl * +seat_from_device (ClutterInputDevice *device) +{ + MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (device); + + return meta_input_device_native_get_seat_impl (device_evdev); +} + +static void +notify_scroll (ClutterInputDevice *input_device, + uint64_t time_us, + double dx, + double dy, + ClutterScrollSource scroll_source, + ClutterScrollFinishFlags flags, + gboolean emulated) +{ + MetaSeatImpl *seat; + ClutterEvent *event = NULL; + double scroll_factor; + + seat = seat_from_device (input_device); + + event = clutter_event_new (CLUTTER_SCROLL); + + meta_event_native_set_time_usec (event, time_us); + event->scroll.time = us2ms (time_us); + meta_xkb_translate_state (event, seat->xkb, seat->button_state); + + /* libinput pointer axis events are in pointer motion coordinate space. + * To convert to Xi2 discrete step coordinate space, multiply the factor + * 1/10. */ + event->scroll.direction = CLUTTER_SCROLL_SMOOTH; + scroll_factor = 1.0 / DISCRETE_SCROLL_STEP; + clutter_event_set_scroll_delta (event, + scroll_factor * dx, + scroll_factor * dy); + + event->scroll.x = seat->pointer_x; + event->scroll.y = seat->pointer_y; + clutter_event_set_device (event, seat->core_pointer); + clutter_event_set_source_device (event, input_device); + event->scroll.scroll_source = scroll_source; + event->scroll.finish_flags = flags; + + _clutter_event_set_pointer_emulated (event, emulated); + + queue_event (seat, event); +} + +static void +notify_discrete_scroll (ClutterInputDevice *input_device, + uint64_t time_us, + ClutterScrollDirection direction, + ClutterScrollSource scroll_source, + gboolean emulated) +{ + MetaSeatImpl *seat; + ClutterEvent *event = NULL; + + if (direction == CLUTTER_SCROLL_SMOOTH) + return; + + seat = seat_from_device (input_device); + + event = clutter_event_new (CLUTTER_SCROLL); + + meta_event_native_set_time_usec (event, time_us); + event->scroll.time = us2ms (time_us); + meta_xkb_translate_state (event, seat->xkb, seat->button_state); + + event->scroll.direction = direction; + + event->scroll.x = seat->pointer_x; + event->scroll.y = seat->pointer_y; + clutter_event_set_device (event, seat->core_pointer); + clutter_event_set_source_device (event, input_device); + event->scroll.scroll_source = scroll_source; + + _clutter_event_set_pointer_emulated (event, emulated); + + queue_event (seat, event); +} + +static void +check_notify_discrete_scroll (MetaSeatImpl *seat, + ClutterInputDevice *device, + uint64_t time_us, + ClutterScrollSource scroll_source) +{ + int i, n_xscrolls, n_yscrolls; + + n_xscrolls = floor (fabs (seat->accum_scroll_dx) / DISCRETE_SCROLL_STEP); + n_yscrolls = floor (fabs (seat->accum_scroll_dy) / DISCRETE_SCROLL_STEP); + + for (i = 0; i < n_xscrolls; i++) + { + notify_discrete_scroll (device, time_us, + seat->accum_scroll_dx > 0 ? + CLUTTER_SCROLL_RIGHT : CLUTTER_SCROLL_LEFT, + scroll_source, TRUE); + } + + for (i = 0; i < n_yscrolls; i++) + { + notify_discrete_scroll (device, time_us, + seat->accum_scroll_dy > 0 ? + CLUTTER_SCROLL_DOWN : CLUTTER_SCROLL_UP, + scroll_source, TRUE); + } + + seat->accum_scroll_dx = fmodf (seat->accum_scroll_dx, DISCRETE_SCROLL_STEP); + seat->accum_scroll_dy = fmodf (seat->accum_scroll_dy, DISCRETE_SCROLL_STEP); +} + +void +meta_seat_impl_notify_scroll_continuous (MetaSeatImpl *seat, + ClutterInputDevice *input_device, + uint64_t time_us, + double dx, + double dy, + ClutterScrollSource scroll_source, + ClutterScrollFinishFlags finish_flags) +{ + if (finish_flags & CLUTTER_SCROLL_FINISHED_HORIZONTAL) + seat->accum_scroll_dx = 0; + else + seat->accum_scroll_dx += dx; + + if (finish_flags & CLUTTER_SCROLL_FINISHED_VERTICAL) + seat->accum_scroll_dy = 0; + else + seat->accum_scroll_dy += dy; + + notify_scroll (input_device, time_us, dx, dy, scroll_source, + finish_flags, FALSE); + check_notify_discrete_scroll (seat, input_device, time_us, scroll_source); +} + +static ClutterScrollDirection +discrete_to_direction (double discrete_dx, + double discrete_dy) +{ + if (discrete_dx > 0) + return CLUTTER_SCROLL_RIGHT; + else if (discrete_dx < 0) + return CLUTTER_SCROLL_LEFT; + else if (discrete_dy > 0) + return CLUTTER_SCROLL_DOWN; + else if (discrete_dy < 0) + return CLUTTER_SCROLL_UP; + else + g_assert_not_reached (); + return 0; +} + +void +meta_seat_impl_notify_discrete_scroll (MetaSeatImpl *seat, + ClutterInputDevice *input_device, + uint64_t time_us, + double discrete_dx, + double discrete_dy, + ClutterScrollSource scroll_source) +{ + notify_scroll (input_device, time_us, + discrete_dx * DISCRETE_SCROLL_STEP, + discrete_dy * DISCRETE_SCROLL_STEP, + scroll_source, CLUTTER_SCROLL_FINISHED_NONE, + TRUE); + notify_discrete_scroll (input_device, time_us, + discrete_to_direction (discrete_dx, discrete_dy), + scroll_source, FALSE); + +} + +void +meta_seat_impl_notify_touch_event (MetaSeatImpl *seat, + ClutterInputDevice *input_device, + ClutterEventType evtype, + uint64_t time_us, + int slot, + double x, + double y) +{ + ClutterEvent *event = NULL; + + event = clutter_event_new (evtype); + + meta_event_native_set_time_usec (event, time_us); + event->touch.time = us2ms (time_us); + event->touch.x = x; + event->touch.y = y; + meta_input_device_native_translate_coordinates (input_device, + seat->viewports, + &event->touch.x, + &event->touch.y); + + /* "NULL" sequences are special cased in clutter */ + event->touch.sequence = GINT_TO_POINTER (MAX (1, slot + 1)); + meta_xkb_translate_state (event, seat->xkb, seat->button_state); + + if (evtype == CLUTTER_TOUCH_BEGIN || + evtype == CLUTTER_TOUCH_UPDATE) + event->touch.modifier_state |= CLUTTER_BUTTON1_MASK; + + clutter_event_set_device (event, seat->core_pointer); + clutter_event_set_source_device (event, input_device); + + queue_event (seat, event); +} + + +/* + * MetaEventSource for reading input devices + */ +static gboolean +meta_event_prepare (GSource *source, + gint *timeout) +{ + gboolean retval; + + *timeout = -1; + retval = clutter_events_pending (); + + return retval; +} + +static gboolean +meta_event_check (GSource *source) +{ + MetaEventSource *event_source = (MetaEventSource *) source; + gboolean retval; + + retval = ((event_source->event_poll_fd.revents & G_IO_IN) || + clutter_events_pending ()); + + return retval; +} + +static void +constrain_to_barriers (MetaSeatImpl *seat, + ClutterInputDevice *device, + guint32 time, + float *new_x, + float *new_y) +{ + meta_barrier_manager_native_process (seat->barrier_manager, + device, + time, + new_x, new_y); +} + +/* + * The pointer constrain code is mostly a rip-off of the XRandR code from Xorg. + * (from xserver/randr/rrcrtc.c, RRConstrainCursorHarder) + * + * Copyright © 2006 Keith Packard + * Copyright 2010 Red Hat, Inc + * + */ + +static void +constrain_all_screen_monitors (ClutterInputDevice *device, + MetaViewportInfo *viewports, + float *x, + float *y) +{ + graphene_point_t current; + float cx, cy; + int i, n_views; + + clutter_input_device_get_coords (device, NULL, ¤t); + + cx = current.x; + cy = current.y; + + /* if we're trying to escape, clamp to the CRTC we're coming from */ + + n_views = meta_viewport_info_get_num_views (viewports); + + for (i = 0; i < n_views; i++) + { + int left, right, top, bottom; + cairo_rectangle_int_t rect; + + meta_viewport_info_get_view (viewports, i, &rect, NULL); + + left = rect.x; + right = left + rect.width; + top = rect.y; + bottom = top + rect.height; + + if ((cx >= left) && (cx < right) && (cy >= top) && (cy < bottom)) + { + if (*x < left) + *x = left; + if (*x >= right) + *x = right - 1; + if (*y < top) + *y = top; + if (*y >= bottom) + *y = bottom - 1; + + return; + } + } +} + +void +meta_seat_impl_constrain_pointer (MetaSeatImpl *seat, + ClutterInputDevice *core_pointer, + uint64_t time_us, + float x, + float y, + float *new_x, + float *new_y) +{ + /* Constrain to barriers */ + constrain_to_barriers (seat, core_pointer, + us2ms (time_us), + new_x, new_y); + + /* Bar to constraints */ + if (seat->pointer_constraint) + { + meta_pointer_constraint_impl_constrain (seat->pointer_constraint, + core_pointer, + us2ms (time_us), + x, y, + new_x, new_y); + } + + if (seat->viewports) + { + /* if we're moving inside a monitor, we're fine */ + if (meta_viewport_info_get_view_at (seat->viewports, *new_x, *new_y) > 0) + return; + + /* if we're trying to escape, clamp to the CRTC we're coming from */ + constrain_all_screen_monitors (core_pointer, seat->viewports, new_x, new_y); + } +} + +static void +relative_motion_across_outputs (MetaViewportInfo *viewports, + int view, + float cur_x, + float cur_y, + float *dx_inout, + float *dy_inout) +{ + int cur_view = view; + float x = cur_x, y = cur_y; + float target_x = cur_x, target_y = cur_y; + float dx = *dx_inout, dy = *dy_inout; + MetaDisplayDirection direction = -1; + + while (cur_view >= 0) + { + MetaLine2 left, right, top, bottom, motion; + MetaVector2 intersection; + cairo_rectangle_int_t rect; + float scale; + + meta_viewport_info_get_view (viewports, cur_view, &rect, &scale); + + motion = (MetaLine2) { + .a = { x, y }, + .b = { x + (dx * scale), y + (dy * scale) } + }; + left = (MetaLine2) { + { rect.x, rect.y }, + { rect.x, rect.y + rect.height } + }; + right = (MetaLine2) { + { rect.x + rect.width, rect.y }, + { rect.x + rect.width, rect.y + rect.height } + }; + top = (MetaLine2) { + { rect.x, rect.y }, + { rect.x + rect.width, rect.y } + }; + bottom = (MetaLine2) { + { rect.x, rect.y + rect.height }, + { rect.x + rect.width, rect.y + rect.height } + }; + + target_x = motion.b.x; + target_y = motion.b.y; + + if (direction != META_DISPLAY_RIGHT && + meta_line2_intersects_with (&motion, &left, &intersection)) + direction = META_DISPLAY_LEFT; + else if (direction != META_DISPLAY_LEFT && + meta_line2_intersects_with (&motion, &right, &intersection)) + direction = META_DISPLAY_RIGHT; + else if (direction != META_DISPLAY_DOWN && + meta_line2_intersects_with (&motion, &top, &intersection)) + direction = META_DISPLAY_UP; + else if (direction != META_DISPLAY_UP && + meta_line2_intersects_with (&motion, &bottom, &intersection)) + direction = META_DISPLAY_DOWN; + else + /* We reached the dest logical monitor */ + break; + + x = intersection.x; + y = intersection.y; + dx -= intersection.x - motion.a.x; + dy -= intersection.y - motion.a.y; + + cur_view = meta_viewport_info_get_neighbor (viewports, cur_view, + direction); + } + + *dx_inout = target_x - cur_x; + *dy_inout = target_y - cur_y; +} + +void +meta_seat_impl_filter_relative_motion (MetaSeatImpl *seat, + ClutterInputDevice *device, + float x, + float y, + float *dx, + float *dy) +{ + int view = -1, dest_view; + float new_dx, new_dy, scale; + + if (meta_is_stage_views_scaled ()) + return; + + if (seat->viewports) + view = meta_viewport_info_get_view_at (seat->viewports, x, y); + if (view < 0) + return; + + meta_viewport_info_get_view (seat->viewports, view, NULL, &scale); + new_dx = (*dx) * scale; + new_dy = (*dy) * scale; + + dest_view = meta_viewport_info_get_view_at (seat->viewports, + x + new_dx, + y + new_dy); + if (dest_view >= 0 && dest_view != view) + { + /* If we are crossing monitors, attempt to bisect the distance on each + * axis and apply the relative scale for each of them. + */ + new_dx = *dx; + new_dy = *dy; + relative_motion_across_outputs (seat->viewports, view, + x, y, &new_dx, &new_dy); + } + + *dx = new_dx; + *dy = new_dy; +} + +static void +notify_absolute_motion (ClutterInputDevice *input_device, + uint64_t time_us, + float x, + float y, + double *axes) +{ + MetaSeatImpl *seat; + ClutterEvent *event; + + seat = seat_from_device (input_device); + event = new_absolute_motion_event (seat, input_device, time_us, x, y, axes); + + queue_event (seat, event); +} + +static void +notify_relative_tool_motion (ClutterInputDevice *input_device, + uint64_t time_us, + float dx, + float dy, + double *axes) +{ + MetaInputDeviceNative *device_evdev; + ClutterEvent *event; + MetaSeatImpl *seat; + gfloat x, y; + + device_evdev = META_INPUT_DEVICE_NATIVE (input_device); + seat = seat_from_device (input_device); + x = device_evdev->pointer_x + dx; + y = device_evdev->pointer_y + dy; + + meta_seat_impl_filter_relative_motion (seat, + input_device, + seat->pointer_x, + seat->pointer_y, + &dx, + &dy); + + event = new_absolute_motion_event (seat, input_device, time_us, x, y, axes); + meta_event_native_set_relative_motion (event, dx, dy, 0, 0); + + queue_event (seat, event); +} + +static void +notify_pinch_gesture_event (ClutterInputDevice *input_device, + ClutterTouchpadGesturePhase phase, + uint64_t time_us, + double dx, + double dy, + double angle_delta, + double scale, + uint32_t n_fingers) +{ + MetaSeatImpl *seat; + ClutterEvent *event = NULL; + graphene_point_t pos; + + seat = seat_from_device (input_device); + + event = clutter_event_new (CLUTTER_TOUCHPAD_PINCH); + + clutter_input_device_get_coords (seat->core_pointer, NULL, &pos); + + meta_event_native_set_time_usec (event, time_us); + event->touchpad_pinch.phase = phase; + event->touchpad_pinch.time = us2ms (time_us); + event->touchpad_pinch.x = pos.x; + event->touchpad_pinch.y = pos.y; + event->touchpad_pinch.dx = dx; + event->touchpad_pinch.dy = dy; + event->touchpad_pinch.angle_delta = angle_delta; + event->touchpad_pinch.scale = scale; + event->touchpad_pinch.n_fingers = n_fingers; + + meta_xkb_translate_state (event, seat->xkb, seat->button_state); + + clutter_event_set_device (event, seat->core_pointer); + clutter_event_set_source_device (event, input_device); + + queue_event (seat, event); +} + +static void +notify_swipe_gesture_event (ClutterInputDevice *input_device, + ClutterTouchpadGesturePhase phase, + uint64_t time_us, + uint32_t n_fingers, + double dx, + double dy) +{ + MetaSeatImpl *seat; + ClutterEvent *event = NULL; + graphene_point_t pos; + + seat = seat_from_device (input_device); + + event = clutter_event_new (CLUTTER_TOUCHPAD_SWIPE); + + meta_event_native_set_time_usec (event, time_us); + event->touchpad_swipe.phase = phase; + event->touchpad_swipe.time = us2ms (time_us); + + clutter_input_device_get_coords (seat->core_pointer, NULL, &pos); + event->touchpad_swipe.x = pos.x; + event->touchpad_swipe.y = pos.y; + event->touchpad_swipe.dx = dx; + event->touchpad_swipe.dy = dy; + event->touchpad_swipe.n_fingers = n_fingers; + + meta_xkb_translate_state (event, seat->xkb, seat->button_state); + + clutter_event_set_device (event, seat->core_pointer); + clutter_event_set_source_device (event, input_device); + + queue_event (seat, event); +} + +static void +notify_proximity (ClutterInputDevice *input_device, + uint64_t time_us, + gboolean in) +{ + MetaInputDeviceNative *device_evdev; + MetaSeatImpl *seat; + ClutterEvent *event = NULL; + + device_evdev = META_INPUT_DEVICE_NATIVE (input_device); + seat = seat_from_device (input_device); + + if (in) + event = clutter_event_new (CLUTTER_PROXIMITY_IN); + else + event = clutter_event_new (CLUTTER_PROXIMITY_OUT); + + meta_event_native_set_time_usec (event, time_us); + + event->proximity.time = us2ms (time_us); + clutter_event_set_device_tool (event, device_evdev->last_tool); + clutter_event_set_device (event, seat->core_pointer); + clutter_event_set_source_device (event, input_device); + + queue_event (seat, event); +} + +static void +notify_pad_button (ClutterInputDevice *input_device, + uint64_t time_us, + uint32_t button, + uint32_t mode_group, + uint32_t mode, + uint32_t pressed) +{ + MetaSeatImpl *seat; + ClutterEvent *event; + + seat = seat_from_device (input_device); + + if (pressed) + event = clutter_event_new (CLUTTER_PAD_BUTTON_PRESS); + else + event = clutter_event_new (CLUTTER_PAD_BUTTON_RELEASE); + + meta_event_native_set_time_usec (event, time_us); + event->pad_button.button = button; + event->pad_button.group = mode_group; + event->pad_button.mode = mode; + clutter_event_set_device (event, input_device); + clutter_event_set_source_device (event, input_device); + clutter_event_set_time (event, us2ms (time_us)); + + queue_event (seat, event); +} + +static void +notify_pad_strip (ClutterInputDevice *input_device, + uint64_t time_us, + uint32_t strip_number, + uint32_t strip_source, + uint32_t mode_group, + uint32_t mode, + double value) +{ + ClutterInputDevicePadSource source; + MetaSeatImpl *seat; + ClutterEvent *event; + + seat = seat_from_device (input_device); + + if (strip_source == LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER) + source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER; + else + source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN; + + event = clutter_event_new (CLUTTER_PAD_STRIP); + meta_event_native_set_time_usec (event, time_us); + event->pad_strip.strip_source = source; + event->pad_strip.strip_number = strip_number; + event->pad_strip.value = value; + event->pad_strip.group = mode_group; + event->pad_strip.mode = mode; + clutter_event_set_device (event, input_device); + clutter_event_set_source_device (event, input_device); + clutter_event_set_time (event, us2ms (time_us)); + + queue_event (seat, event); +} + +static void +notify_pad_ring (ClutterInputDevice *input_device, + uint64_t time_us, + uint32_t ring_number, + uint32_t ring_source, + uint32_t mode_group, + uint32_t mode, + double angle) +{ + ClutterInputDevicePadSource source; + MetaSeatImpl *seat; + ClutterEvent *event; + + seat = seat_from_device (input_device); + + if (ring_source == LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER) + source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER; + else + source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN; + + event = clutter_event_new (CLUTTER_PAD_RING); + meta_event_native_set_time_usec (event, time_us); + event->pad_ring.ring_source = source; + event->pad_ring.ring_number = ring_number; + event->pad_ring.angle = angle; + event->pad_ring.group = mode_group; + event->pad_ring.mode = mode; + clutter_event_set_device (event, input_device); + clutter_event_set_source_device (event, input_device); + clutter_event_set_time (event, us2ms (time_us)); + + queue_event (seat, event); +} + +static gboolean +meta_event_dispatch (GSource *g_source, + GSourceFunc callback, + gpointer user_data) +{ + MetaEventSource *source = (MetaEventSource *) g_source; + MetaSeatImpl *seat; + + seat = source->seat_impl; + + /* Don't queue more events if we haven't finished handling the previous batch + */ + if (clutter_events_pending ()) + goto queue_event; + + dispatch_libinput (seat); + + queue_event: + + return TRUE; +} +static GSourceFuncs event_funcs = { + meta_event_prepare, + meta_event_check, + meta_event_dispatch, + NULL +}; + +static MetaEventSource * +meta_event_source_new (MetaSeatImpl *seat) +{ + GSource *source; + MetaEventSource *event_source; + gint fd; + + source = g_source_new (&event_funcs, sizeof (MetaEventSource)); + event_source = (MetaEventSource *) source; + + /* setup the source */ + event_source->seat_impl = seat; + + fd = libinput_get_fd (seat->libinput); + event_source->event_poll_fd.fd = fd; + event_source->event_poll_fd.events = G_IO_IN; + + /* and finally configure and attach the GSource */ + g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS); + g_source_add_poll (source, &event_source->event_poll_fd); + g_source_set_can_recurse (source, TRUE); + g_source_attach (source, NULL); + + return event_source; +} + +static void +meta_event_source_free (MetaEventSource *source) +{ + GSource *g_source = (GSource *) source; + + /* ignore the return value of close, it's not like we can do something + * about it */ + close (source->event_poll_fd.fd); + + g_source_destroy (g_source); + g_source_unref (g_source); +} + +static gboolean +has_touchscreen (MetaSeatImpl *seat) +{ + GSList *l; + + for (l = seat->devices; l; l = l->next) + { + ClutterInputDeviceType device_type; + + device_type = clutter_input_device_get_device_type (l->data); + + if (device_type == CLUTTER_TOUCHSCREEN_DEVICE) + return TRUE; + } + + return FALSE; +} + +static void +update_touch_mode (MetaSeatImpl *seat) +{ + gboolean touch_mode; + + /* No touch mode if we don't have a touchscreen, easy */ + if (!seat->has_touchscreen) + touch_mode = FALSE; + /* If we have a tablet mode switch, honor it being unset */ + else if (seat->has_tablet_switch && !seat->tablet_mode_switch_state) + touch_mode = FALSE; + /* If tablet mode is enabled, or if there is no tablet mode switch + * (eg. kiosk machines), assume touch-mode. + */ + else + touch_mode = TRUE; + + if (seat->touch_mode != touch_mode) + { + seat->touch_mode = touch_mode; + g_object_notify (G_OBJECT (seat->seat), "touch-mode"); + } +} + +static ClutterInputDevice * +evdev_add_device (MetaSeatImpl *seat, + struct libinput_device *libinput_device) +{ + ClutterInputDeviceType type; + ClutterInputDevice *device, *master = NULL; + gboolean check_touch_mode = FALSE; + + device = meta_input_device_native_new (seat, libinput_device); + + seat->devices = g_slist_prepend (seat->devices, device); + + /* Clutter assumes that device types are exclusive in the + * ClutterInputDevice API */ + type = meta_input_device_native_determine_type (libinput_device); + + if (type == CLUTTER_KEYBOARD_DEVICE) + master = seat->core_keyboard; + else if (type == CLUTTER_POINTER_DEVICE) + master = seat->core_pointer; + + if (master) + { + _clutter_input_device_set_associated_device (device, master); + _clutter_input_device_add_slave (master, device); + } + + if (type == CLUTTER_TOUCHSCREEN_DEVICE) + { + seat->has_touchscreen = TRUE; + check_touch_mode = TRUE; + } + + if (libinput_device_has_capability (libinput_device, + LIBINPUT_DEVICE_CAP_SWITCH) && + libinput_device_switch_has_switch (libinput_device, + LIBINPUT_SWITCH_TABLET_MODE)) + { + seat->has_tablet_switch = TRUE; + check_touch_mode = TRUE; + } + + if (check_touch_mode) + update_touch_mode (seat); + + return device; +} + +static void +evdev_remove_device (MetaSeatImpl *seat, + MetaInputDeviceNative *device_evdev) +{ + ClutterInputDevice *device; + ClutterInputDeviceType device_type; + + device = CLUTTER_INPUT_DEVICE (device_evdev); + seat->devices = g_slist_remove (seat->devices, device); + + device_type = clutter_input_device_get_device_type (device); + + if (device_type == CLUTTER_TOUCHSCREEN_DEVICE) + { + seat->has_touchscreen = has_touchscreen (seat); + update_touch_mode (seat); + } + + if (seat->repeat_timer && seat->repeat_device == device) + meta_seat_impl_clear_repeat_timer (seat); + + g_object_run_dispose (G_OBJECT (device)); + g_object_unref (device); +} + +static gboolean +process_base_event (MetaSeatImpl *seat, + struct libinput_event *event) +{ + ClutterInputDevice *device; + ClutterEvent *device_event; + struct libinput_device *libinput_device; + + switch (libinput_event_get_type (event)) + { + case LIBINPUT_EVENT_DEVICE_ADDED: + libinput_device = libinput_event_get_device (event); + + device = evdev_add_device (seat, libinput_device); + device_event = clutter_event_new (CLUTTER_DEVICE_ADDED); + clutter_event_set_device (device_event, device); + break; + + case LIBINPUT_EVENT_DEVICE_REMOVED: + libinput_device = libinput_event_get_device (event); + + device = libinput_device_get_user_data (libinput_device); + device_event = clutter_event_new (CLUTTER_DEVICE_REMOVED); + clutter_event_set_device (device_event, device); + evdev_remove_device (seat, + META_INPUT_DEVICE_NATIVE (device)); + break; + + default: + device_event = NULL; + } + + if (device_event) + { + queue_event (seat, device_event); + return TRUE; + } + + return FALSE; +} + +static ClutterScrollSource +translate_scroll_source (enum libinput_pointer_axis_source source) +{ + switch (source) + { + case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: + return CLUTTER_SCROLL_SOURCE_WHEEL; + case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: + return CLUTTER_SCROLL_SOURCE_FINGER; + case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: + return CLUTTER_SCROLL_SOURCE_CONTINUOUS; + default: + return CLUTTER_SCROLL_SOURCE_UNKNOWN; + } +} + +static ClutterInputDeviceToolType +translate_tool_type (struct libinput_tablet_tool *libinput_tool) +{ + enum libinput_tablet_tool_type tool; + + tool = libinput_tablet_tool_get_type (libinput_tool); + + switch (tool) + { + case LIBINPUT_TABLET_TOOL_TYPE_PEN: + return CLUTTER_INPUT_DEVICE_TOOL_PEN; + case LIBINPUT_TABLET_TOOL_TYPE_ERASER: + return CLUTTER_INPUT_DEVICE_TOOL_ERASER; + case LIBINPUT_TABLET_TOOL_TYPE_BRUSH: + return CLUTTER_INPUT_DEVICE_TOOL_BRUSH; + case LIBINPUT_TABLET_TOOL_TYPE_PENCIL: + return CLUTTER_INPUT_DEVICE_TOOL_PENCIL; + case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH: + return CLUTTER_INPUT_DEVICE_TOOL_AIRBRUSH; + case LIBINPUT_TABLET_TOOL_TYPE_MOUSE: + return CLUTTER_INPUT_DEVICE_TOOL_MOUSE; + case LIBINPUT_TABLET_TOOL_TYPE_LENS: + return CLUTTER_INPUT_DEVICE_TOOL_LENS; + default: + return CLUTTER_INPUT_DEVICE_TOOL_NONE; + } +} + +static void +input_device_update_tool (ClutterInputDevice *input_device, + struct libinput_tablet_tool *libinput_tool) +{ + MetaInputDeviceNative *evdev_device = META_INPUT_DEVICE_NATIVE (input_device); + MetaSeatImpl *seat = seat_from_device (input_device); + ClutterInputDeviceTool *tool = NULL; + ClutterInputDeviceToolType tool_type; + uint64_t tool_serial; + + if (libinput_tool) + { + tool_serial = libinput_tablet_tool_get_serial (libinput_tool); + tool_type = translate_tool_type (libinput_tool); + tool = clutter_input_device_lookup_tool (input_device, + tool_serial, tool_type); + + if (!tool) + { + tool = meta_input_device_tool_native_new (libinput_tool, + tool_serial, tool_type); + clutter_input_device_add_tool (input_device, tool); + } + } + + if (evdev_device->last_tool != tool) + { + evdev_device->last_tool = tool; + g_signal_emit_by_name (seat->seat, "tool-changed", input_device, tool); + } +} + +static gdouble * +translate_tablet_axes (struct libinput_event_tablet_tool *tablet_event, + ClutterInputDeviceTool *tool) +{ + GArray *axes = g_array_new (FALSE, FALSE, sizeof (gdouble)); + struct libinput_tablet_tool *libinput_tool; + gdouble value; + + libinput_tool = libinput_event_tablet_tool_get_tool (tablet_event); + + value = libinput_event_tablet_tool_get_x (tablet_event); + g_array_append_val (axes, value); + value = libinput_event_tablet_tool_get_y (tablet_event); + g_array_append_val (axes, value); + + if (libinput_tablet_tool_has_distance (libinput_tool)) + { + value = libinput_event_tablet_tool_get_distance (tablet_event); + g_array_append_val (axes, value); + } + + if (libinput_tablet_tool_has_pressure (libinput_tool)) + { + value = libinput_event_tablet_tool_get_pressure (tablet_event); + value = meta_input_device_tool_native_translate_pressure (tool, value); + g_array_append_val (axes, value); + } + + if (libinput_tablet_tool_has_tilt (libinput_tool)) + { + value = libinput_event_tablet_tool_get_tilt_x (tablet_event); + g_array_append_val (axes, value); + value = libinput_event_tablet_tool_get_tilt_y (tablet_event); + g_array_append_val (axes, value); + } + + if (libinput_tablet_tool_has_rotation (libinput_tool)) + { + value = libinput_event_tablet_tool_get_rotation (tablet_event); + g_array_append_val (axes, value); + } + + if (libinput_tablet_tool_has_slider (libinput_tool)) + { + value = libinput_event_tablet_tool_get_slider_position (tablet_event); + g_array_append_val (axes, value); + } + + if (libinput_tablet_tool_has_wheel (libinput_tool)) + { + value = libinput_event_tablet_tool_get_wheel_delta (tablet_event); + g_array_append_val (axes, value); + } + + if (axes->len == 0) + { + g_array_free (axes, TRUE); + return NULL; + } + else + return (gdouble *) g_array_free (axes, FALSE); +} + +static void +notify_continuous_axis (MetaSeatImpl *seat, + ClutterInputDevice *device, + uint64_t time_us, + ClutterScrollSource scroll_source, + struct libinput_event_pointer *axis_event) +{ + gdouble dx = 0.0, dy = 0.0; + ClutterScrollFinishFlags finish_flags = CLUTTER_SCROLL_FINISHED_NONE; + + if (libinput_event_pointer_has_axis (axis_event, + LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) + { + dx = libinput_event_pointer_get_axis_value ( + axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); + + if (fabs (dx) < DBL_EPSILON) + finish_flags |= CLUTTER_SCROLL_FINISHED_HORIZONTAL; + } + if (libinput_event_pointer_has_axis (axis_event, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) + { + dy = libinput_event_pointer_get_axis_value ( + axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); + + if (fabs (dy) < DBL_EPSILON) + finish_flags |= CLUTTER_SCROLL_FINISHED_VERTICAL; + } + + meta_seat_impl_notify_scroll_continuous (seat, device, time_us, + dx, dy, + scroll_source, finish_flags); +} + +static void +notify_discrete_axis (MetaSeatImpl *seat, + ClutterInputDevice *device, + uint64_t time_us, + ClutterScrollSource scroll_source, + struct libinput_event_pointer *axis_event) +{ + gdouble discrete_dx = 0.0, discrete_dy = 0.0; + + if (libinput_event_pointer_has_axis (axis_event, + LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) + { + discrete_dx = libinput_event_pointer_get_axis_value_discrete ( + axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); + } + if (libinput_event_pointer_has_axis (axis_event, + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) + { + discrete_dy = libinput_event_pointer_get_axis_value_discrete ( + axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); + } + + meta_seat_impl_notify_discrete_scroll (seat, device, + time_us, + discrete_dx, discrete_dy, + scroll_source); +} + +static void +process_tablet_axis (MetaSeatImpl *seat, + struct libinput_event *event) +{ + struct libinput_device *libinput_device = libinput_event_get_device (event); + uint64_t time; + double x, y, dx, dy, *axes; + float stage_width, stage_height; + ClutterInputDevice *device; + struct libinput_event_tablet_tool *tablet_event = + libinput_event_get_tablet_tool_event (event); + MetaInputDeviceNative *evdev_device; + + device = libinput_device_get_user_data (libinput_device); + evdev_device = META_INPUT_DEVICE_NATIVE (device); + + axes = translate_tablet_axes (tablet_event, + evdev_device->last_tool); + if (!axes) + return; + + meta_viewport_info_get_extents (seat->viewports, &stage_width, &stage_height); + + time = libinput_event_tablet_tool_get_time_usec (tablet_event); + + if (meta_input_device_native_get_mapping_mode (device) == META_INPUT_DEVICE_MAPPING_RELATIVE || + clutter_input_device_tool_get_tool_type (evdev_device->last_tool) == CLUTTER_INPUT_DEVICE_TOOL_MOUSE || + clutter_input_device_tool_get_tool_type (evdev_device->last_tool) == CLUTTER_INPUT_DEVICE_TOOL_LENS) + { + dx = libinput_event_tablet_tool_get_dx (tablet_event); + dy = libinput_event_tablet_tool_get_dy (tablet_event); + notify_relative_tool_motion (device, time, dx, dy, axes); + } + else + { + x = libinput_event_tablet_tool_get_x_transformed (tablet_event, stage_width); + y = libinput_event_tablet_tool_get_y_transformed (tablet_event, stage_height); + notify_absolute_motion (device, time, x, y, axes); + } +} + +static gboolean +process_device_event (MetaSeatImpl *seat, + struct libinput_event *event) +{ + gboolean handled = TRUE; + struct libinput_device *libinput_device = libinput_event_get_device(event); + ClutterInputDevice *device; + MetaInputDeviceNative *device_evdev; + + switch (libinput_event_get_type (event)) + { + case LIBINPUT_EVENT_KEYBOARD_KEY: + { + uint32_t key, key_state, seat_key_count; + uint64_t time_us; + struct libinput_event_keyboard *key_event = + libinput_event_get_keyboard_event (event); + + device = libinput_device_get_user_data (libinput_device); + time_us = libinput_event_keyboard_get_time_usec (key_event); + key = libinput_event_keyboard_get_key (key_event); + key_state = libinput_event_keyboard_get_key_state (key_event) == + LIBINPUT_KEY_STATE_PRESSED; + seat_key_count = + libinput_event_keyboard_get_seat_key_count (key_event); + + /* Ignore key events that are not seat wide state changes. */ + if ((key_state == LIBINPUT_KEY_STATE_PRESSED && + seat_key_count != 1) || + (key_state == LIBINPUT_KEY_STATE_RELEASED && + seat_key_count != 0)) + { + meta_topic (META_DEBUG_INPUT, + "Dropping key-%s of key 0x%x because seat-wide " + "key count is %d\n", + key_state == LIBINPUT_KEY_STATE_PRESSED ? "press" : "release", + key, seat_key_count); + break; + } + + meta_seat_impl_notify_key (seat_from_device (device), + device, + time_us, key, key_state, TRUE); + + break; + } + + case LIBINPUT_EVENT_POINTER_MOTION: + { + struct libinput_event_pointer *pointer_event = + libinput_event_get_pointer_event (event); + uint64_t time_us; + double dx; + double dy; + double dx_unaccel; + double dy_unaccel; + + device = libinput_device_get_user_data (libinput_device); + time_us = libinput_event_pointer_get_time_usec (pointer_event); + dx = libinput_event_pointer_get_dx (pointer_event); + dy = libinput_event_pointer_get_dy (pointer_event); + dx_unaccel = libinput_event_pointer_get_dx_unaccelerated (pointer_event); + dy_unaccel = libinput_event_pointer_get_dy_unaccelerated (pointer_event); + + meta_seat_impl_notify_relative_motion (seat_from_device (device), + device, + time_us, + dx, dy, + dx_unaccel, dy_unaccel); + + break; + } + + case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: + { + uint64_t time_us; + double x, y; + float stage_width, stage_height; + struct libinput_event_pointer *motion_event = + libinput_event_get_pointer_event (event); + device = libinput_device_get_user_data (libinput_device); + + meta_viewport_info_get_extents (seat->viewports, + &stage_width, &stage_height); + + time_us = libinput_event_pointer_get_time_usec (motion_event); + x = libinput_event_pointer_get_absolute_x_transformed (motion_event, + stage_width); + y = libinput_event_pointer_get_absolute_y_transformed (motion_event, + stage_height); + + meta_seat_impl_notify_absolute_motion (seat_from_device (device), + device, + time_us, + x, y, + NULL); + + break; + } + + case LIBINPUT_EVENT_POINTER_BUTTON: + { + uint32_t button, button_state, seat_button_count; + uint64_t time_us; + struct libinput_event_pointer *button_event = + libinput_event_get_pointer_event (event); + device = libinput_device_get_user_data (libinput_device); + + time_us = libinput_event_pointer_get_time_usec (button_event); + button = libinput_event_pointer_get_button (button_event); + button_state = libinput_event_pointer_get_button_state (button_event) == + LIBINPUT_BUTTON_STATE_PRESSED; + seat_button_count = + libinput_event_pointer_get_seat_button_count (button_event); + + /* Ignore button events that are not seat wide state changes. */ + if ((button_state == LIBINPUT_BUTTON_STATE_PRESSED && + seat_button_count != 1) || + (button_state == LIBINPUT_BUTTON_STATE_RELEASED && + seat_button_count != 0)) + { + meta_topic (META_DEBUG_INPUT, + "Dropping button-%s of button 0x%x because seat-wide " + "button count is %d\n", + button_state == LIBINPUT_BUTTON_STATE_PRESSED ? "press" : "release", + button, seat_button_count); + break; + } + + meta_seat_impl_notify_button (seat_from_device (device), device, + time_us, button, button_state); + break; + } + + case LIBINPUT_EVENT_POINTER_AXIS: + { + uint64_t time_us; + enum libinput_pointer_axis_source source; + struct libinput_event_pointer *axis_event = + libinput_event_get_pointer_event (event); + MetaSeatImpl *seat; + ClutterScrollSource scroll_source; + + device = libinput_device_get_user_data (libinput_device); + seat = seat_from_device (device); + + time_us = libinput_event_pointer_get_time_usec (axis_event); + source = libinput_event_pointer_get_axis_source (axis_event); + scroll_source = translate_scroll_source (source); + + /* libinput < 0.8 sent wheel click events with value 10. Since 0.8 + the value is the angle of the click in degrees. To keep + backwards-compat with existing clients, we just send multiples of + the click count. */ + + switch (scroll_source) + { + case CLUTTER_SCROLL_SOURCE_WHEEL: + notify_discrete_axis (seat, device, time_us, scroll_source, + axis_event); + break; + case CLUTTER_SCROLL_SOURCE_FINGER: + case CLUTTER_SCROLL_SOURCE_CONTINUOUS: + case CLUTTER_SCROLL_SOURCE_UNKNOWN: + notify_continuous_axis (seat, device, time_us, scroll_source, + axis_event); + break; + } + break; + } + + case LIBINPUT_EVENT_TOUCH_DOWN: + { + int seat_slot; + uint64_t time_us; + double x, y; + float stage_width, stage_height; + MetaSeatImpl *seat; + MetaTouchState *touch_state; + struct libinput_event_touch *touch_event = + libinput_event_get_touch_event (event); + + device = libinput_device_get_user_data (libinput_device); + device_evdev = META_INPUT_DEVICE_NATIVE (device); + seat = seat_from_device (device); + + meta_viewport_info_get_extents (seat->viewports, + &stage_width, &stage_height); + + seat_slot = libinput_event_touch_get_seat_slot (touch_event); + time_us = libinput_event_touch_get_time_usec (touch_event); + x = libinput_event_touch_get_x_transformed (touch_event, + stage_width); + y = libinput_event_touch_get_y_transformed (touch_event, + stage_height); + + touch_state = meta_seat_impl_acquire_touch_state (seat, seat_slot); + touch_state->coords.x = x; + touch_state->coords.y = y; + + meta_seat_impl_notify_touch_event (seat, device, + CLUTTER_TOUCH_BEGIN, + time_us, + touch_state->seat_slot, + touch_state->coords.x, + touch_state->coords.y); + break; + } + + case LIBINPUT_EVENT_TOUCH_UP: + { + int seat_slot; + uint64_t time_us; + MetaSeatImpl *seat; + MetaTouchState *touch_state; + struct libinput_event_touch *touch_event = + libinput_event_get_touch_event (event); + + device = libinput_device_get_user_data (libinput_device); + device_evdev = META_INPUT_DEVICE_NATIVE (device); + seat = seat_from_device (device); + + seat_slot = libinput_event_touch_get_seat_slot (touch_event); + time_us = libinput_event_touch_get_time_usec (touch_event); + touch_state = meta_seat_impl_lookup_touch_state (seat, seat_slot); + if (!touch_state) + break; + + meta_seat_impl_notify_touch_event (seat, device, + CLUTTER_TOUCH_END, time_us, + touch_state->seat_slot, + touch_state->coords.x, + touch_state->coords.y); + meta_seat_impl_release_touch_state (seat, seat_slot); + break; + } + + case LIBINPUT_EVENT_TOUCH_MOTION: + { + int seat_slot; + uint64_t time_us; + double x, y; + float stage_width, stage_height; + MetaSeatImpl *seat; + MetaTouchState *touch_state; + struct libinput_event_touch *touch_event = + libinput_event_get_touch_event (event); + + device = libinput_device_get_user_data (libinput_device); + device_evdev = META_INPUT_DEVICE_NATIVE (device); + seat = seat_from_device (device); + + meta_viewport_info_get_extents (seat->viewports, + &stage_width, &stage_height); + + seat_slot = libinput_event_touch_get_seat_slot (touch_event); + time_us = libinput_event_touch_get_time_usec (touch_event); + x = libinput_event_touch_get_x_transformed (touch_event, + stage_width); + y = libinput_event_touch_get_y_transformed (touch_event, + stage_height); + + touch_state = meta_seat_impl_lookup_touch_state (seat, seat_slot); + if (!touch_state) + break; + + touch_state->coords.x = x; + touch_state->coords.y = y; + + meta_seat_impl_notify_touch_event (seat, device, + CLUTTER_TOUCH_UPDATE, + time_us, + touch_state->seat_slot, + touch_state->coords.x, + touch_state->coords.y); + break; + } + case LIBINPUT_EVENT_TOUCH_CANCEL: + { + int seat_slot; + MetaTouchState *touch_state; + uint64_t time_us; + MetaSeatImpl *seat; + struct libinput_event_touch *touch_event = + libinput_event_get_touch_event (event); + + device = libinput_device_get_user_data (libinput_device); + device_evdev = META_INPUT_DEVICE_NATIVE (device); + seat = seat_from_device (device); + time_us = libinput_event_touch_get_time_usec (touch_event); + + seat_slot = libinput_event_touch_get_seat_slot (touch_event); + touch_state = meta_seat_impl_lookup_touch_state (seat, seat_slot); + if (!touch_state) + break; + + meta_seat_impl_notify_touch_event (touch_state->seat, + CLUTTER_INPUT_DEVICE (device_evdev), + CLUTTER_TOUCH_CANCEL, + time_us, + touch_state->seat_slot, + touch_state->coords.x, + touch_state->coords.y); + + meta_seat_impl_release_touch_state (seat, seat_slot); + break; + } + case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: + case LIBINPUT_EVENT_GESTURE_PINCH_END: + { + struct libinput_event_gesture *gesture_event = + libinput_event_get_gesture_event (event); + ClutterTouchpadGesturePhase phase; + uint32_t n_fingers; + uint64_t time_us; + + if (libinput_event_get_type (event) == LIBINPUT_EVENT_GESTURE_PINCH_BEGIN) + phase = CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN; + else + phase = libinput_event_gesture_get_cancelled (gesture_event) ? + CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL : CLUTTER_TOUCHPAD_GESTURE_PHASE_END; + + n_fingers = libinput_event_gesture_get_finger_count (gesture_event); + device = libinput_device_get_user_data (libinput_device); + time_us = libinput_event_gesture_get_time_usec (gesture_event); + notify_pinch_gesture_event (device, phase, time_us, 0, 0, 0, 0, n_fingers); + break; + } + case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: + { + struct libinput_event_gesture *gesture_event = + libinput_event_get_gesture_event (event); + gdouble angle_delta, scale, dx, dy; + uint32_t n_fingers; + uint64_t time_us; + + n_fingers = libinput_event_gesture_get_finger_count (gesture_event); + device = libinput_device_get_user_data (libinput_device); + time_us = libinput_event_gesture_get_time_usec (gesture_event); + angle_delta = libinput_event_gesture_get_angle_delta (gesture_event); + scale = libinput_event_gesture_get_scale (gesture_event); + dx = libinput_event_gesture_get_dx (gesture_event); + dy = libinput_event_gesture_get_dy (gesture_event); + + notify_pinch_gesture_event (device, + CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE, + time_us, dx, dy, angle_delta, scale, n_fingers); + break; + } + case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: + case LIBINPUT_EVENT_GESTURE_SWIPE_END: + { + struct libinput_event_gesture *gesture_event = + libinput_event_get_gesture_event (event); + ClutterTouchpadGesturePhase phase; + uint32_t n_fingers; + uint64_t time_us; + + device = libinput_device_get_user_data (libinput_device); + time_us = libinput_event_gesture_get_time_usec (gesture_event); + n_fingers = libinput_event_gesture_get_finger_count (gesture_event); + + if (libinput_event_get_type (event) == LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN) + phase = CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN; + else + phase = libinput_event_gesture_get_cancelled (gesture_event) ? + CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL : CLUTTER_TOUCHPAD_GESTURE_PHASE_END; + + notify_swipe_gesture_event (device, phase, time_us, n_fingers, 0, 0); + break; + } + case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: + { + struct libinput_event_gesture *gesture_event = + libinput_event_get_gesture_event (event); + uint32_t n_fingers; + uint64_t time_us; + double dx, dy; + + device = libinput_device_get_user_data (libinput_device); + time_us = libinput_event_gesture_get_time_usec (gesture_event); + n_fingers = libinput_event_gesture_get_finger_count (gesture_event); + dx = libinput_event_gesture_get_dx (gesture_event); + dy = libinput_event_gesture_get_dy (gesture_event); + + notify_swipe_gesture_event (device, + CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE, + time_us, n_fingers, dx, dy); + break; + } + case LIBINPUT_EVENT_TABLET_TOOL_AXIS: + { + process_tablet_axis (seat, event); + break; + } + case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: + { + uint64_t time; + struct libinput_event_tablet_tool *tablet_event = + libinput_event_get_tablet_tool_event (event); + struct libinput_tablet_tool *libinput_tool = NULL; + enum libinput_tablet_tool_proximity_state state; + gboolean in; + + state = libinput_event_tablet_tool_get_proximity_state (tablet_event); + time = libinput_event_tablet_tool_get_time_usec (tablet_event); + device = libinput_device_get_user_data (libinput_device); + in = state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN; + + libinput_tool = libinput_event_tablet_tool_get_tool (tablet_event); + + if (in) + input_device_update_tool (device, libinput_tool); + notify_proximity (device, time, in); + if (!in) + input_device_update_tool (device, NULL); + + break; + } + case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: + { + uint64_t time_us; + uint32_t button_state; + struct libinput_event_tablet_tool *tablet_event = + libinput_event_get_tablet_tool_event (event); + uint32_t tablet_button; + + process_tablet_axis (seat, event); + + device = libinput_device_get_user_data (libinput_device); + time_us = libinput_event_tablet_tool_get_time_usec (tablet_event); + tablet_button = libinput_event_tablet_tool_get_button (tablet_event); + + button_state = libinput_event_tablet_tool_get_button_state (tablet_event) == + LIBINPUT_BUTTON_STATE_PRESSED; + + meta_seat_impl_notify_button (seat_from_device (device), device, + time_us, tablet_button, button_state); + break; + } + case LIBINPUT_EVENT_TABLET_TOOL_TIP: + { + uint64_t time_us; + uint32_t button_state; + struct libinput_event_tablet_tool *tablet_event = + libinput_event_get_tablet_tool_event (event); + + device = libinput_device_get_user_data (libinput_device); + time_us = libinput_event_tablet_tool_get_time_usec (tablet_event); + + button_state = libinput_event_tablet_tool_get_tip_state (tablet_event) == + LIBINPUT_TABLET_TOOL_TIP_DOWN; + + /* To avoid jumps on tip, notify axes before the tip down event + but after the tip up event */ + if (button_state) + process_tablet_axis (seat, event); + + meta_seat_impl_notify_button (seat_from_device (device), device, + time_us, BTN_TOUCH, button_state); + if (!button_state) + process_tablet_axis (seat, event); + break; + } + case LIBINPUT_EVENT_TABLET_PAD_BUTTON: + { + uint64_t time; + uint32_t button_state, button, group, mode; + struct libinput_tablet_pad_mode_group *mode_group; + struct libinput_event_tablet_pad *pad_event = + libinput_event_get_tablet_pad_event (event); + + device = libinput_device_get_user_data (libinput_device); + time = libinput_event_tablet_pad_get_time_usec (pad_event); + + mode_group = libinput_event_tablet_pad_get_mode_group (pad_event); + group = libinput_tablet_pad_mode_group_get_index (mode_group); + mode = libinput_event_tablet_pad_get_mode (pad_event); + + button = libinput_event_tablet_pad_get_button_number (pad_event); + button_state = libinput_event_tablet_pad_get_button_state (pad_event) == + LIBINPUT_BUTTON_STATE_PRESSED; + notify_pad_button (device, time, button, group, mode, button_state); + break; + } + case LIBINPUT_EVENT_TABLET_PAD_STRIP: + { + uint64_t time; + uint32_t number, source, group, mode; + struct libinput_tablet_pad_mode_group *mode_group; + struct libinput_event_tablet_pad *pad_event = + libinput_event_get_tablet_pad_event (event); + double value; + + device = libinput_device_get_user_data (libinput_device); + time = libinput_event_tablet_pad_get_time_usec (pad_event); + number = libinput_event_tablet_pad_get_strip_number (pad_event); + value = libinput_event_tablet_pad_get_strip_position (pad_event); + source = libinput_event_tablet_pad_get_strip_source (pad_event); + + mode_group = libinput_event_tablet_pad_get_mode_group (pad_event); + group = libinput_tablet_pad_mode_group_get_index (mode_group); + mode = libinput_event_tablet_pad_get_mode (pad_event); + + notify_pad_strip (device, time, number, source, group, mode, value); + break; + } + case LIBINPUT_EVENT_TABLET_PAD_RING: + { + uint64_t time; + uint32_t number, source, group, mode; + struct libinput_tablet_pad_mode_group *mode_group; + struct libinput_event_tablet_pad *pad_event = + libinput_event_get_tablet_pad_event (event); + double angle; + + device = libinput_device_get_user_data (libinput_device); + time = libinput_event_tablet_pad_get_time_usec (pad_event); + number = libinput_event_tablet_pad_get_ring_number (pad_event); + angle = libinput_event_tablet_pad_get_ring_position (pad_event); + source = libinput_event_tablet_pad_get_ring_source (pad_event); + + mode_group = libinput_event_tablet_pad_get_mode_group (pad_event); + group = libinput_tablet_pad_mode_group_get_index (mode_group); + mode = libinput_event_tablet_pad_get_mode (pad_event); + + notify_pad_ring (device, time, number, source, group, mode, angle); + break; + } + case LIBINPUT_EVENT_SWITCH_TOGGLE: + { + struct libinput_event_switch *switch_event = + libinput_event_get_switch_event (event); + enum libinput_switch sw = + libinput_event_switch_get_switch (switch_event); + enum libinput_switch_state state = + libinput_event_switch_get_switch_state (switch_event); + + if (sw == LIBINPUT_SWITCH_TABLET_MODE) + { + seat->tablet_mode_switch_state = (state == LIBINPUT_SWITCH_STATE_ON); + update_touch_mode (seat); + } + break; + } + default: + handled = FALSE; + } + + return handled; +} + +static void +process_event (MetaSeatImpl *seat, + struct libinput_event *event) +{ + if (process_base_event (seat, event)) + return; + if (process_device_event (seat, event)) + return; +} + +static void +process_events (MetaSeatImpl *seat) +{ + struct libinput_event *event; + + while ((event = libinput_get_event (seat->libinput))) + { + process_event(seat, event); + libinput_event_destroy(event); + } +} + +static int +open_restricted (const char *path, + int flags, + void *user_data) +{ + gint fd; + + if (device_open_callback) + { + GError *error = NULL; + + fd = device_open_callback (path, flags, device_callback_data, &error); + + if (fd < 0) + { + g_warning ("Could not open device %s: %s", path, error->message); + g_error_free (error); + } + } + else + { + fd = open (path, O_RDWR | O_NONBLOCK); + if (fd < 0) + { + g_warning ("Could not open device %s: %s", path, strerror (errno)); + } + } + + return fd; +} + +static void +close_restricted (int fd, + void *user_data) +{ + if (device_close_callback) + device_close_callback (fd, device_callback_data); + else + close (fd); +} + +static const struct libinput_interface libinput_interface = { + open_restricted, + close_restricted +}; + +static void +meta_seat_impl_constructed (GObject *object) +{ + MetaSeatImpl *seat = META_SEAT_IMPL (object); + ClutterInputDevice *device; + MetaEventSource *source; + struct udev *udev; + struct xkb_keymap *xkb_keymap; + + device = meta_input_device_native_new_virtual ( + seat, CLUTTER_POINTER_DEVICE, + CLUTTER_INPUT_MODE_MASTER); + seat->pointer_x = INITIAL_POINTER_X; + seat->pointer_y = INITIAL_POINTER_Y; + meta_input_device_native_update_coords (META_INPUT_DEVICE_NATIVE (device), + seat->pointer_x, seat->pointer_y); + seat->core_pointer = device; + + device = meta_input_device_native_new_virtual ( + seat, CLUTTER_KEYBOARD_DEVICE, + CLUTTER_INPUT_MODE_MASTER); + seat->core_keyboard = device; + + udev = udev_new (); + if (G_UNLIKELY (udev == NULL)) + { + g_warning ("Failed to create udev object"); + return; + } + + seat->libinput = libinput_udev_create_context (&libinput_interface, + seat, udev); + if (seat->libinput == NULL) + { + g_critical ("Failed to create the libinput object."); + return; + } + + if (libinput_udev_assign_seat (seat->libinput, seat->seat_id) == -1) + { + g_critical ("Failed to assign a seat to the libinput object."); + libinput_unref (seat->libinput); + seat->libinput = NULL; + return; + } + + udev_unref (udev); + + seat->udev_client = g_udev_client_new ((const gchar *[]) { "input", NULL }); + + dispatch_libinput (seat); + + source = meta_event_source_new (seat); + seat->event_source = source; + + seat->keymap = g_object_new (META_TYPE_KEYMAP_NATIVE, NULL); + xkb_keymap = meta_keymap_native_get_keyboard_map (seat->keymap); + + if (xkb_keymap) + { + seat->xkb = xkb_state_new (xkb_keymap); + + seat->caps_lock_led = + xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS); + seat->num_lock_led = + xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM); + seat->scroll_lock_led = + xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL); + } + + seat->has_touchscreen = has_touchscreen (seat); + update_touch_mode (seat); + + if (G_OBJECT_CLASS (meta_seat_impl_parent_class)->constructed) + G_OBJECT_CLASS (meta_seat_impl_parent_class)->constructed (object); +} + +static void +meta_seat_impl_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaSeatImpl *seat_impl = META_SEAT_IMPL (object); + + switch (prop_id) + { + case PROP_SEAT: + seat_impl->seat = g_value_get_object (value); + break; + case PROP_SEAT_ID: + seat_impl->seat_id = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +meta_seat_impl_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaSeatImpl *seat_impl = META_SEAT_IMPL (object); + + switch (prop_id) + { + case PROP_SEAT: + g_value_set_object (value, seat_impl->seat); + break; + case PROP_SEAT_ID: + g_value_set_string (value, seat_impl->seat_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +meta_seat_impl_dispose (GObject *object) +{ + MetaSeatImpl *seat = META_SEAT_IMPL (object); + + if (seat->libinput) + { + libinput_unref (seat->libinput); + seat->libinput = NULL; + } + + G_OBJECT_CLASS (meta_seat_impl_parent_class)->dispose (object); +} + +static void +meta_seat_impl_finalize (GObject *object) +{ + MetaSeatImpl *seat = META_SEAT_IMPL (object); + GSList *iter; + + for (iter = seat->devices; iter; iter = g_slist_next (iter)) + { + ClutterInputDevice *device = iter->data; + + g_object_unref (device); + } + g_slist_free (seat->devices); + + if (seat->touch_states) + g_hash_table_destroy (seat->touch_states); + + g_object_unref (seat->udev_client); + + meta_event_source_free (seat->event_source); + + xkb_state_unref (seat->xkb); + + meta_seat_impl_clear_repeat_timer (seat); + + if (seat->libinput_seat) + libinput_seat_unref (seat->libinput_seat); + + g_list_free (seat->free_device_ids); + + g_free (seat->seat_id); + + G_OBJECT_CLASS (meta_seat_impl_parent_class)->finalize (object); +} + +ClutterInputDevice * +meta_seat_impl_get_pointer (MetaSeatImpl *seat) +{ + MetaSeatImpl *seat_impl = META_SEAT_IMPL (seat); + + return seat_impl->core_pointer; +} + +ClutterInputDevice * +meta_seat_impl_get_keyboard (MetaSeatImpl *seat) +{ + MetaSeatImpl *seat_impl = META_SEAT_IMPL (seat); + + return seat_impl->core_keyboard; +} + +GSList * +meta_seat_impl_get_devices (MetaSeatImpl *seat) +{ + MetaSeatImpl *seat_impl = META_SEAT_IMPL (seat); + + return g_slist_copy_deep (seat_impl->devices, + (GCopyFunc) g_object_ref, + NULL); +} + +MetaKeymapNative * +meta_seat_impl_get_keymap (MetaSeatImpl *seat) +{ + return g_object_ref (seat->keymap); +} + +ClutterVirtualInputDevice * +meta_seat_impl_create_virtual_device (MetaSeatImpl *seat, + ClutterInputDeviceType device_type) +{ + return g_object_new (META_TYPE_VIRTUAL_INPUT_DEVICE_NATIVE, + "seat", seat->seat, + "device-type", device_type, + NULL); +} + +void +meta_seat_impl_warp_pointer (MetaSeatImpl *seat, + int x, + int y) +{ + notify_absolute_motion (seat->core_pointer, 0, x, y, NULL); +} + +gboolean +meta_seat_impl_query_state (MetaSeatImpl *seat, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + graphene_point_t *coords, + ClutterModifierType *modifiers) +{ + MetaInputDeviceNative *device_native = META_INPUT_DEVICE_NATIVE (device); + MetaSeatImpl *seat_native = META_SEAT_IMPL (seat); + + if (sequence) + { + MetaTouchState *touch_state; + int slot; + + slot = meta_event_native_sequence_get_slot (sequence); + touch_state = meta_seat_impl_lookup_touch_state (seat_native, slot); + if (!touch_state) + return FALSE; + + if (coords) + { + coords->x = touch_state->coords.x; + coords->y = touch_state->coords.y; + } + + if (modifiers) + *modifiers = meta_xkb_translate_modifiers (seat_native->xkb, 0); + + return TRUE; + } + else + { + if (coords) + { + coords->x = device_native->pointer_x; + coords->y = device_native->pointer_y; + } + + if (modifiers) + { + *modifiers = meta_xkb_translate_modifiers (seat_native->xkb, + seat_native->button_state); + } + + return TRUE; + } +} + +static void +meta_seat_impl_class_init (MetaSeatImplClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = meta_seat_impl_constructed; + object_class->set_property = meta_seat_impl_set_property; + object_class->get_property = meta_seat_impl_get_property; + object_class->dispose = meta_seat_impl_dispose; + object_class->finalize = meta_seat_impl_finalize; + + props[PROP_SEAT] = + g_param_spec_object ("seat", + "Seat", + "Seat", + META_TYPE_SEAT_NATIVE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + 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); +} + +static void +meta_seat_impl_init (MetaSeatImpl *seat) +{ + seat->device_id_next = INITIAL_DEVICE_ID; + + seat->repeat = TRUE; + seat->repeat_delay = 250; /* ms */ + seat->repeat_interval = 33; /* ms */ + + seat->barrier_manager = meta_barrier_manager_native_new (); +} + +/** + * meta_seat_impl_set_device_callbacks: (skip) + * @open_callback: the user replacement for open() + * @close_callback: the user replacement for close() + * @user_data: user data for @callback + * + * Through this function, the application can set a custom callback + * to be invoked when Clutter is about to open an evdev device. It can do + * so if special handling is needed, for example to circumvent permission + * problems. + * + * Setting @callback to %NULL will reset the default behavior. + * + * For reliable effects, this function must be called before clutter_init(). + */ +void +meta_seat_impl_set_device_callbacks (MetaOpenDeviceCallback open_callback, + MetaCloseDeviceCallback close_callback, + gpointer user_data) +{ + device_open_callback = open_callback; + device_close_callback = close_callback; + device_callback_data = user_data; +} + +void +meta_seat_impl_update_xkb_state (MetaSeatImpl *seat) +{ + xkb_mod_mask_t latched_mods; + xkb_mod_mask_t locked_mods; + struct xkb_keymap *xkb_keymap; + + xkb_keymap = meta_keymap_native_get_keyboard_map (seat->keymap); + + latched_mods = xkb_state_serialize_mods (seat->xkb, + XKB_STATE_MODS_LATCHED); + locked_mods = xkb_state_serialize_mods (seat->xkb, + XKB_STATE_MODS_LOCKED); + xkb_state_unref (seat->xkb); + seat->xkb = xkb_state_new (xkb_keymap); + + xkb_state_update_mask (seat->xkb, + 0, /* depressed */ + latched_mods, + locked_mods, + 0, 0, seat->layout_idx); + + seat->caps_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS); + seat->num_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM); + seat->scroll_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL); + + meta_seat_impl_sync_leds (seat); +} + +gint +meta_seat_impl_acquire_device_id (MetaSeatImpl *seat) +{ + GList *first; + gint next_id; + + if (seat->free_device_ids == NULL) + { + gint i; + + /* We ran out of free ID's, so append 10 new ones. */ + for (i = 0; i < 10; i++) + seat->free_device_ids = + g_list_append (seat->free_device_ids, + GINT_TO_POINTER (seat->device_id_next++)); + } + + first = g_list_first (seat->free_device_ids); + next_id = GPOINTER_TO_INT (first->data); + seat->free_device_ids = g_list_delete_link (seat->free_device_ids, first); + + return next_id; +} + +static int +compare_ids (gconstpointer a, + gconstpointer b) +{ + return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b); +} + +void +meta_seat_impl_release_device_id (MetaSeatImpl *seat, + ClutterInputDevice *device) +{ + gint device_id; + + device_id = clutter_input_device_get_device_id (device); + seat->free_device_ids = g_list_insert_sorted (seat->free_device_ids, + GINT_TO_POINTER (device_id), + compare_ids); +} + +/** + * meta_seat_impl_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_impl_reclaim_devices(). + * + * This function should only be called after clutter has been initialized. + */ +void +meta_seat_impl_release_devices (MetaSeatImpl *seat) +{ + g_return_if_fail (META_IS_SEAT_IMPL (seat)); + + if (seat->released) + { + g_warning ("meta_seat_impl_release_devices() shouldn't be called " + "multiple times without a corresponding call to " + "meta_seat_impl_reclaim_devices() first"); + return; + } + + libinput_suspend (seat->libinput); + process_events (seat); + + seat->released = TRUE; +} + +/** + * meta_seat_impl_reclaim_devices: + * + * This causes Clutter to re-probe for evdev devices. This is must only be + * called after a corresponding call to meta_seat_impl_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_impl_reclaim_devices (MetaSeatImpl *seat) +{ + if (!seat->released) + { + g_warning ("Spurious call to meta_seat_impl_reclaim_devices() without " + "previous call to meta_seat_impl_release_devices"); + return; + } + + libinput_resume (seat->libinput); + meta_seat_impl_update_xkb_state (seat); + process_events (seat); + + seat->released = FALSE; +} + +/** + * meta_seat_impl_set_keyboard_map: (skip) + * @seat: the #ClutterSeat created by the evdev backend + * @keymap: the new keymap + * + * Instructs @evdev to use the speficied 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_impl_set_keyboard_map (MetaSeatImpl *seat, + struct xkb_keymap *xkb_keymap) +{ + MetaKeymapNative *keymap; + + g_return_if_fail (META_IS_SEAT_IMPL (seat)); + + keymap = seat->keymap; + meta_keymap_native_set_keyboard_map (keymap, xkb_keymap); + + meta_seat_impl_update_xkb_state (seat); +} + +/** + * meta_seat_impl_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_impl_get_keyboard_map (MetaSeatImpl *seat) +{ + g_return_val_if_fail (META_IS_SEAT_IMPL (seat), NULL); + + return xkb_state_get_keymap (seat->xkb); +} + +/** + * meta_seat_impl_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_impl_set_keyboard_layout_index (MetaSeatImpl *seat, + xkb_layout_index_t idx) +{ + xkb_mod_mask_t depressed_mods; + xkb_mod_mask_t latched_mods; + xkb_mod_mask_t locked_mods; + struct xkb_state *state; + + g_return_if_fail (META_IS_SEAT_IMPL (seat)); + + state = seat->xkb; + + depressed_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_DEPRESSED); + latched_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_LATCHED); + locked_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED); + + xkb_state_update_mask (state, depressed_mods, latched_mods, locked_mods, 0, 0, idx); + + seat->layout_idx = idx; +} + +/** + * meta_seat_impl_get_keyboard_layout_index: (skip) + */ +xkb_layout_index_t +meta_seat_impl_get_keyboard_layout_index (MetaSeatImpl *seat) +{ + return seat->layout_idx; +} + +/** + * meta_seat_impl_set_keyboard_numlock: (skip) + * @seat: the #ClutterSeat created by the evdev backend + * @numlock_set: TRUE to set NumLock ON, FALSE otherwise. + * + * Sets the NumLock state on the backend's #xkb_state . + */ +void +meta_seat_impl_set_keyboard_numlock (MetaSeatImpl *seat, + gboolean numlock_state) +{ + xkb_mod_mask_t depressed_mods; + xkb_mod_mask_t latched_mods; + xkb_mod_mask_t locked_mods; + xkb_mod_mask_t group_mods; + xkb_mod_mask_t numlock; + struct xkb_keymap *xkb_keymap; + MetaKeymapNative *keymap; + + g_return_if_fail (META_IS_SEAT_IMPL (seat)); + + keymap = seat->keymap; + xkb_keymap = meta_keymap_native_get_keyboard_map (keymap); + + numlock = (1 << xkb_keymap_mod_get_index (xkb_keymap, "Mod2")); + + depressed_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_DEPRESSED); + latched_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LATCHED); + locked_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LOCKED); + group_mods = xkb_state_serialize_layout (seat->xkb, XKB_STATE_LAYOUT_EFFECTIVE); + + if (numlock_state) + locked_mods |= numlock; + else + locked_mods &= ~numlock; + + xkb_state_update_mask (seat->xkb, + depressed_mods, + latched_mods, + locked_mods, + 0, 0, + group_mods); + + meta_seat_impl_sync_leds (seat); +} + +/** + * meta_seat_impl_set_keyboard_repeat: + * @seat: the #ClutterSeat created by the evdev backend + * @repeat: whether to enable or disable keyboard repeat events + * @delay: the delay in ms between the hardware key press event and + * the first synthetic event + * @interval: the period in ms between consecutive synthetic key + * press events + * + * Enables or disables sythetic key press events, allowing for initial + * delay and interval period to be specified. + */ +void +meta_seat_impl_set_keyboard_repeat (MetaSeatImpl *seat, + gboolean repeat, + uint32_t delay, + uint32_t interval) +{ + g_return_if_fail (META_IS_SEAT_IMPL (seat)); + + seat->repeat = repeat; + seat->repeat_delay = delay; + seat->repeat_interval = interval; +} + +struct xkb_state * +meta_seat_impl_get_xkb_state (MetaSeatImpl *seat) +{ + return seat->xkb; +} + +MetaBarrierManagerNative * +meta_seat_impl_get_barrier_manager (MetaSeatImpl *seat) +{ + return seat->barrier_manager; +} + +void +meta_seat_impl_set_pointer_constraint (MetaSeatImpl *seat, + MetaPointerConstraintImpl *impl) +{ + if (g_set_object (&seat->pointer_constraint, impl)) + { + if (impl) + meta_pointer_constraint_impl_ensure_constrained (impl, seat->core_pointer); + } +} + +void +meta_seat_impl_set_viewports (MetaSeatImpl *seat, + MetaViewportInfo *viewports) +{ + g_set_object (&seat->viewports, viewports); +} + +MetaSeatImpl * +meta_seat_impl_new (MetaSeatNative *seat, + const gchar *seat_id) +{ + return g_object_new (META_TYPE_SEAT_IMPL, + "seat", seat, + "seat-id", seat_id, + NULL); +} diff --git a/src/backends/native/meta-seat-impl.h b/src/backends/native/meta-seat-impl.h new file mode 100644 index 000000000..10a66426e --- /dev/null +++ b/src/backends/native/meta-seat-impl.h @@ -0,0 +1,257 @@ +/* + * 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 + */ + +#ifndef META_SEAT_IMPL_H +#define META_SEAT_IMPL_H + +#include +#include +#include + +#include "backends/meta-viewport-info.h" +#include "backends/native/meta-backend-native-types.h" +#include "backends/native/meta-barrier-native.h" +#include "backends/native/meta-cursor-renderer-native.h" +#include "backends/native/meta-keymap-native.h" +#include "backends/native/meta-pointer-constraint-native.h" +#include "backends/native/meta-xkb-utils.h" +#include "clutter/clutter.h" + +typedef struct _MetaTouchState MetaTouchState; +typedef struct _MetaSeatImpl MetaSeatImpl; +typedef struct _MetaEventSource MetaEventSource; + +struct _MetaTouchState +{ + MetaSeatImpl *seat; + + int device_slot; + int seat_slot; + graphene_point_t coords; +}; + +struct _MetaSeatImpl +{ + GObject parent_instance; + + MetaSeatNative *seat; + char *seat_id; + MetaEventSource *event_source; + struct libinput *libinput; + struct libinput_seat *libinput_seat; + + GSList *devices; + + ClutterInputDevice *core_pointer; + ClutterInputDevice *core_keyboard; + + GHashTable *touch_states; + GHashTable *cursor_renderers; + + struct xkb_state *xkb; + xkb_led_index_t caps_lock_led; + xkb_led_index_t num_lock_led; + xkb_led_index_t scroll_lock_led; + xkb_layout_index_t layout_idx; + uint32_t button_state; + int button_count[KEY_CNT]; + + int device_id_next; + GList *free_device_ids; + + MetaBarrierManagerNative *barrier_manager; + MetaPointerConstraintImpl *pointer_constraint; + + MetaKeymapNative *keymap; + + MetaViewportInfo *viewports; + + GUdevClient *udev_client; + guint tablet_mode_switch_state : 1; + guint has_touchscreen : 1; + guint has_tablet_switch : 1; + guint touch_mode : 1; + + /* keyboard repeat */ + gboolean repeat; + uint32_t repeat_delay; + uint32_t repeat_interval; + uint32_t repeat_key; + uint32_t repeat_count; + uint32_t repeat_timer; + ClutterInputDevice *repeat_device; + + float pointer_x; + float pointer_y; + + /* Emulation of discrete scroll events out of smooth ones */ + float accum_scroll_dx; + float accum_scroll_dy; + + gboolean released; +}; + +#define META_TYPE_SEAT_IMPL meta_seat_impl_get_type () +G_DECLARE_FINAL_TYPE (MetaSeatImpl, meta_seat_impl, + META, SEAT_IMPL, GObject) + +MetaSeatImpl * meta_seat_impl_new (MetaSeatNative *seat, + const gchar *seat_id); + +void meta_seat_impl_notify_key (MetaSeatImpl *seat, + ClutterInputDevice *device, + uint64_t time_us, + uint32_t key, + uint32_t state, + gboolean update_keys); + +void meta_seat_impl_notify_relative_motion (MetaSeatImpl *seat_evdev, + ClutterInputDevice *input_device, + uint64_t time_us, + float dx, + float dy, + float dx_unaccel, + float dy_unaccel); + +void meta_seat_impl_notify_absolute_motion (MetaSeatImpl *seat_evdev, + ClutterInputDevice *input_device, + uint64_t time_us, + float x, + float y, + double *axes); + +void meta_seat_impl_notify_button (MetaSeatImpl *seat, + ClutterInputDevice *input_device, + uint64_t time_us, + uint32_t button, + uint32_t state); + +void meta_seat_impl_notify_scroll_continuous (MetaSeatImpl *seat, + ClutterInputDevice *input_device, + uint64_t time_us, + double dx, + double dy, + ClutterScrollSource source, + ClutterScrollFinishFlags flags); + +void meta_seat_impl_notify_discrete_scroll (MetaSeatImpl *seat, + ClutterInputDevice *input_device, + uint64_t time_us, + double discrete_dx, + double discrete_dy, + ClutterScrollSource source); + +void meta_seat_impl_notify_touch_event (MetaSeatImpl *seat, + ClutterInputDevice *input_device, + ClutterEventType evtype, + uint64_t time_us, + int slot, + double x, + double y); + +void meta_seat_impl_set_libinput_seat (MetaSeatImpl *seat, + struct libinput_seat *libinput_seat); + +void meta_seat_impl_sync_leds (MetaSeatImpl *seat); + +MetaTouchState * meta_seat_impl_acquire_touch_state (MetaSeatImpl *seat, + int seat_slot); +MetaTouchState * meta_seat_impl_lookup_touch_state (MetaSeatImpl *seat, + int seat_slot); + +void meta_seat_impl_release_touch_state (MetaSeatImpl *seat, + int seat_slot); + +gint meta_seat_impl_acquire_device_id (MetaSeatImpl *seat); +void meta_seat_impl_release_device_id (MetaSeatImpl *seat, + ClutterInputDevice *device); + +void meta_seat_impl_update_xkb_state (MetaSeatImpl *seat); + +/** + * MetaOpenDeviceCallback: + * @path: the device path + * @flags: flags to be passed to open + * + * This callback will be called when Clutter needs to access an input + * device. It should return an open file descriptor for the file at @path, + * or -1 if opening failed. + */ +typedef int (* MetaOpenDeviceCallback) (const char *path, + int flags, + gpointer user_data, + GError **error); +typedef void (* MetaCloseDeviceCallback) (int fd, + gpointer user_data); + +void meta_seat_impl_set_device_callbacks (MetaOpenDeviceCallback open_callback, + MetaCloseDeviceCallback close_callback, + gpointer user_data); + +void meta_seat_impl_release_devices (MetaSeatImpl *seat); +void meta_seat_impl_reclaim_devices (MetaSeatImpl *seat); + +struct xkb_state * meta_seat_impl_get_xkb_state (MetaSeatImpl *seat); + +void meta_seat_impl_set_keyboard_map (MetaSeatImpl *seat, + struct xkb_keymap *keymap); + +struct xkb_keymap * meta_seat_impl_get_keyboard_map (MetaSeatImpl *seat); + +void meta_seat_impl_set_keyboard_layout_index (MetaSeatImpl *seat, + xkb_layout_index_t idx); + +xkb_layout_index_t meta_seat_impl_get_keyboard_layout_index (MetaSeatImpl *seat); + +void meta_seat_impl_set_keyboard_numlock (MetaSeatImpl *seat, + gboolean numlock_state); + +void meta_seat_impl_set_keyboard_repeat (MetaSeatImpl *seat, + gboolean repeat, + uint32_t delay, + uint32_t interval); + +MetaBarrierManagerNative * meta_seat_impl_get_barrier_manager (MetaSeatImpl *seat); + +void meta_seat_impl_set_pointer_constraint (MetaSeatImpl *seat, + MetaPointerConstraintImpl *impl); +void meta_seat_impl_set_viewports (MetaSeatImpl *seat, + MetaViewportInfo *viewports); + +void meta_seat_impl_warp_pointer (MetaSeatImpl *seat, + int x, + int y); +ClutterVirtualInputDevice * +meta_seat_impl_create_virtual_device (MetaSeatImpl *seat, + ClutterInputDeviceType device_type); +gboolean meta_seat_impl_query_state (MetaSeatImpl *seat, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + graphene_point_t *coords, + ClutterModifierType *modifiers); +ClutterInputDevice * meta_seat_impl_get_pointer (MetaSeatImpl *seat); +ClutterInputDevice * meta_seat_impl_get_keyboard (MetaSeatImpl *seat); +GSList * meta_seat_impl_get_devices (MetaSeatImpl *seat); + +MetaKeymapNative * meta_seat_impl_get_keymap (MetaSeatImpl *seat); + +#endif /* META_SEAT_IMPL_H */ diff --git a/src/backends/native/meta-seat-native.c b/src/backends/native/meta-seat-native.c index 08c26a677..d06de2b17 100644 --- a/src/backends/native/meta-seat-native.c +++ b/src/backends/native/meta-seat-native.c @@ -44,57 +44,6 @@ #include "clutter/clutter-mutter.h" #include "core/bell.h" -/* - * Clutter makes the assumption that two core devices have ID's 2 and 3 (core - * pointer and core keyboard). - * - * Since the two first devices that will ever be created will be the virtual - * pointer and virtual keyboard of the first seat, we fulfill the made - * assumptions by having the first device having ID 2 and following 3. - */ -#define INITIAL_DEVICE_ID 2 - -/* Try to keep the pointer inside the stage. Hopefully no one is using - * this backend with stages smaller than this. */ -#define INITIAL_POINTER_X 16 -#define INITIAL_POINTER_Y 16 - -#define AUTOREPEAT_VALUE 2 - -#define DISCRETE_SCROLL_STEP 10.0 - -#ifndef BTN_STYLUS3 -#define BTN_STYLUS3 0x149 /* Linux 4.15 */ -#endif - -struct _MetaEventSource -{ - GSource source; - - MetaSeatNative *seat; - GPollFD event_poll_fd; -}; - -static MetaOpenDeviceCallback device_open_callback; -static MetaCloseDeviceCallback device_close_callback; -static gpointer device_callback_data; - -#ifdef CLUTTER_ENABLE_DEBUG -static const char *device_type_str[] = { - "pointer", /* CLUTTER_POINTER_DEVICE */ - "keyboard", /* CLUTTER_KEYBOARD_DEVICE */ - "extension", /* CLUTTER_EXTENSION_DEVICE */ - "joystick", /* CLUTTER_JOYSTICK_DEVICE */ - "tablet", /* CLUTTER_TABLET_DEVICE */ - "touchpad", /* CLUTTER_TOUCHPAD_DEVICE */ - "touchscreen", /* CLUTTER_TOUCHSCREEN_DEVICE */ - "pen", /* CLUTTER_PEN_DEVICE */ - "eraser", /* CLUTTER_ERASER_DEVICE */ - "cursor", /* CLUTTER_CURSOR_DEVICE */ - "pad", /* CLUTTER_PAD_DEVICE */ -}; -#endif /* CLUTTER_ENABLE_DEBUG */ - enum { PROP_0, @@ -105,2432 +54,69 @@ enum PROP_TOUCH_MODE, }; -GParamSpec *props[N_PROPS] = { NULL }; +static GParamSpec *props[N_PROPS] = { NULL }; G_DEFINE_TYPE (MetaSeatNative, meta_seat_native, CLUTTER_TYPE_SEAT) -static void process_events (MetaSeatNative *seat); - -void -meta_seat_native_set_libinput_seat (MetaSeatNative *seat, - struct libinput_seat *libinput_seat) -{ - g_assert (seat->libinput_seat == NULL); - - libinput_seat_ref (libinput_seat); - libinput_seat_set_user_data (libinput_seat, seat); - seat->libinput_seat = libinput_seat; -} - -void -meta_seat_native_sync_leds (MetaSeatNative *seat) -{ - GSList *iter; - MetaInputDeviceNative *device_evdev; - int caps_lock, num_lock, scroll_lock; - enum libinput_led leds = 0; - - caps_lock = xkb_state_led_index_is_active (seat->xkb, seat->caps_lock_led); - num_lock = xkb_state_led_index_is_active (seat->xkb, seat->num_lock_led); - scroll_lock = xkb_state_led_index_is_active (seat->xkb, seat->scroll_lock_led); - - if (caps_lock) - leds |= LIBINPUT_LED_CAPS_LOCK; - if (num_lock) - leds |= LIBINPUT_LED_NUM_LOCK; - if (scroll_lock) - leds |= LIBINPUT_LED_SCROLL_LOCK; - - for (iter = seat->devices; iter; iter = iter->next) - { - device_evdev = iter->data; - meta_input_device_native_update_leds (device_evdev, leds); - } -} - -MetaTouchState * -meta_seat_native_lookup_touch_state (MetaSeatNative *seat, - int seat_slot) -{ - if (!seat->touch_states) - return NULL; - return g_hash_table_lookup (seat->touch_states, GINT_TO_POINTER (seat_slot)); -} - -static void -meta_touch_state_free (MetaTouchState *state) -{ - g_slice_free (MetaTouchState, state); -} - -MetaTouchState * -meta_seat_native_acquire_touch_state (MetaSeatNative *seat, - int seat_slot) -{ - MetaTouchState *touch_state; - - if (!seat->touch_states) - { - seat->touch_states = - g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify) meta_touch_state_free); - } - - g_assert (!g_hash_table_contains (seat->touch_states, - GINT_TO_POINTER (seat_slot))); - - touch_state = g_slice_new0 (MetaTouchState); - *touch_state = (MetaTouchState) { - .seat = seat, - .seat_slot = seat_slot, - }; - - g_hash_table_insert (seat->touch_states, GINT_TO_POINTER (seat_slot), - touch_state); - - return touch_state; -} - -void -meta_seat_native_release_touch_state (MetaSeatNative *seat, - int seat_slot) -{ - if (!seat->touch_states) - return; - g_hash_table_remove (seat->touch_states, GINT_TO_POINTER (seat_slot)); -} - -void -meta_seat_native_clear_repeat_timer (MetaSeatNative *seat) -{ - if (seat->repeat_timer) - { - g_clear_handle_id (&seat->repeat_timer, g_source_remove); - g_clear_object (&seat->repeat_device); - } -} - -static void -dispatch_libinput (MetaSeatNative *seat) -{ - libinput_dispatch (seat->libinput); - process_events (seat); -} - -static gboolean -keyboard_repeat (gpointer data) -{ - MetaSeatNative *seat = data; - GSource *source; - - /* There might be events queued in libinput that could cancel the - repeat timer. */ - dispatch_libinput (seat); - if (!seat->repeat_timer) - return G_SOURCE_REMOVE; - - g_return_val_if_fail (seat->repeat_device != NULL, G_SOURCE_REMOVE); - source = g_main_context_find_source_by_id (NULL, seat->repeat_timer); - - meta_seat_native_notify_key (seat, - seat->repeat_device, - g_source_get_time (source), - seat->repeat_key, - AUTOREPEAT_VALUE, - FALSE); - - return G_SOURCE_CONTINUE; -} - -static void -queue_event (MetaSeatNative *seat, - ClutterEvent *event) -{ - _clutter_event_push (event, FALSE); -} - -static int -update_button_count (MetaSeatNative *seat, - uint32_t button, - uint32_t state) -{ - if (state) - { - return ++seat->button_count[button]; - } - else - { - /* Handle cases where we newer saw the initial pressed event. */ - if (seat->button_count[button] == 0) - { - meta_topic (META_DEBUG_INPUT, - "Counting release of key 0x%x and count is already 0\n", - button); - return 0; - } - - return --seat->button_count[button]; - } -} - -void -meta_seat_native_notify_key (MetaSeatNative *seat, - ClutterInputDevice *device, - uint64_t time_us, - uint32_t key, - uint32_t state, - gboolean update_keys) -{ - ClutterEvent *event = NULL; - enum xkb_state_component changed_state; - - if (state != AUTOREPEAT_VALUE) - { - /* Drop any repeated button press (for example from virtual devices. */ - int count = update_button_count (seat, key, state); - if ((state && count > 1) || - (!state && count != 0)) - { - meta_topic (META_DEBUG_INPUT, - "Dropping repeated %s of key 0x%x, count %d, state %d\n", - state ? "press" : "release", key, count, state); - return; - } - } - - event = meta_key_event_new_from_evdev (device, - seat->core_keyboard, - seat->xkb, - seat->button_state, - us2ms (time_us), key, state); - meta_event_native_set_event_code (event, key); - - /* We must be careful and not pass multiple releases to xkb, otherwise it gets - confused and locks the modifiers */ - if (state != AUTOREPEAT_VALUE) - { - changed_state = xkb_state_update_key (seat->xkb, - event->key.hardware_keycode, - state ? XKB_KEY_DOWN : XKB_KEY_UP); - } - else - { - changed_state = 0; - clutter_event_set_flags (event, CLUTTER_EVENT_FLAG_REPEATED); - } - - queue_event (seat, event); - - if (update_keys && (changed_state & XKB_STATE_LEDS)) - { - g_signal_emit_by_name (seat->keymap, "state-changed"); - meta_seat_native_sync_leds (seat); - meta_input_device_native_a11y_maybe_notify_toggle_keys (META_INPUT_DEVICE_NATIVE (seat->core_keyboard)); - } - - if (state == 0 || /* key release */ - !seat->repeat || - !xkb_keymap_key_repeats (xkb_state_get_keymap (seat->xkb), - event->key.hardware_keycode)) - { - meta_seat_native_clear_repeat_timer (seat); - return; - } - - if (state == 1) /* key press */ - seat->repeat_count = 0; - - seat->repeat_count += 1; - seat->repeat_key = key; - - switch (seat->repeat_count) - { - case 1: - case 2: - { - uint32_t interval; - - meta_seat_native_clear_repeat_timer (seat); - seat->repeat_device = g_object_ref (device); - - if (seat->repeat_count == 1) - interval = seat->repeat_delay; - else - interval = seat->repeat_interval; - - seat->repeat_timer = - clutter_threads_add_timeout_full (CLUTTER_PRIORITY_EVENTS, - interval, - keyboard_repeat, - seat, - NULL); - return; - } - default: - return; - } -} - -static ClutterEvent * -new_absolute_motion_event (MetaSeatNative *seat, - ClutterInputDevice *input_device, - uint64_t time_us, - float x, - float y, - double *axes) -{ - ClutterEvent *event; - - event = clutter_event_new (CLUTTER_MOTION); - - if (clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE) - { - meta_seat_native_constrain_pointer (seat, - seat->core_pointer, - time_us, - seat->pointer_x, - seat->pointer_y, - &x, &y); - } - - meta_event_native_set_time_usec (event, time_us); - event->motion.time = us2ms (time_us); - meta_xkb_translate_state (event, seat->xkb, seat->button_state); - event->motion.x = x; - event->motion.y = y; - - /* This may happen early at startup */ - if (seat->viewports) - { - meta_input_device_native_translate_coordinates (input_device, - seat->viewports, - &event->motion.x, - &event->motion.y); - } - - event->motion.axes = axes; - clutter_event_set_device (event, seat->core_pointer); - clutter_event_set_source_device (event, input_device); - - if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) - { - MetaInputDeviceNative *device_evdev = - META_INPUT_DEVICE_NATIVE (input_device); - - clutter_event_set_device_tool (event, device_evdev->last_tool); - clutter_event_set_device (event, input_device); - meta_input_device_native_update_coords (META_INPUT_DEVICE_NATIVE (input_device), - x, y); - } - else - { - clutter_event_set_device (event, seat->core_pointer); - meta_input_device_native_update_coords (META_INPUT_DEVICE_NATIVE (seat->core_pointer), - x, y); - } - - if (clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE) - { - seat->pointer_x = x; - seat->pointer_y = y; - } - - return event; -} - -void -meta_seat_native_notify_relative_motion (MetaSeatNative *seat, - ClutterInputDevice *input_device, - uint64_t time_us, - float dx, - float dy, - float dx_unaccel, - float dy_unaccel) -{ - float new_x, new_y; - ClutterEvent *event; - - meta_seat_native_filter_relative_motion (seat, - input_device, - seat->pointer_x, - seat->pointer_y, - &dx, - &dy); - - new_x = seat->pointer_x + dx; - new_y = seat->pointer_y + dy; - event = new_absolute_motion_event (seat, input_device, - time_us, new_x, new_y, NULL); - - meta_event_native_set_relative_motion (event, - dx, dy, - dx_unaccel, dy_unaccel); - - queue_event (seat, event); -} - -void -meta_seat_native_notify_absolute_motion (MetaSeatNative *seat, - ClutterInputDevice *input_device, - uint64_t time_us, - float x, - float y, - double *axes) -{ - ClutterEvent *event; - - event = new_absolute_motion_event (seat, input_device, time_us, x, y, axes); - - queue_event (seat, event); -} - -void -meta_seat_native_notify_button (MetaSeatNative *seat, - ClutterInputDevice *input_device, - uint64_t time_us, - uint32_t button, - uint32_t state) -{ - MetaInputDeviceNative *device_evdev = (MetaInputDeviceNative *) input_device; - ClutterEvent *event = NULL; - int button_nr; - static int maskmap[8] = - { - CLUTTER_BUTTON1_MASK, CLUTTER_BUTTON3_MASK, CLUTTER_BUTTON2_MASK, - CLUTTER_BUTTON4_MASK, CLUTTER_BUTTON5_MASK, 0, 0, 0 - }; - int button_count; - - /* Drop any repeated button press (for example from virtual devices. */ - button_count = update_button_count (seat, button, state); - if ((state && button_count > 1) || - (!state && button_count != 0)) - { - meta_topic (META_DEBUG_INPUT, - "Dropping repeated %s of button 0x%x, count %d\n", - state ? "press" : "release", button, button_count); - return; - } - - /* The evdev button numbers don't map sequentially to clutter button - * numbers (the right and middle mouse buttons are in the opposite - * order) so we'll map them directly with a switch statement */ - switch (button) - { - case BTN_LEFT: - case BTN_TOUCH: - button_nr = CLUTTER_BUTTON_PRIMARY; - break; - - case BTN_RIGHT: - case BTN_STYLUS: - button_nr = CLUTTER_BUTTON_SECONDARY; - break; - - case BTN_MIDDLE: - case BTN_STYLUS2: - button_nr = CLUTTER_BUTTON_MIDDLE; - break; - - case 0x149: /* BTN_STYLUS3 */ - button_nr = 8; - break; - - default: - /* For compatibility reasons, all additional buttons go after the old 4-7 scroll ones */ - if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) - button_nr = button - BTN_TOOL_PEN + 4; - else - button_nr = button - (BTN_LEFT - 1) + 4; - break; - } - - if (button_nr < 1 || button_nr > 12) - { - g_warning ("Unhandled button event 0x%x", button); - return; - } - - if (state) - event = clutter_event_new (CLUTTER_BUTTON_PRESS); - else - event = clutter_event_new (CLUTTER_BUTTON_RELEASE); - - if (button_nr < G_N_ELEMENTS (maskmap)) - { - /* Update the modifiers */ - if (state) - seat->button_state |= maskmap[button_nr - 1]; - else - seat->button_state &= ~maskmap[button_nr - 1]; - } - - meta_event_native_set_time_usec (event, time_us); - event->button.time = us2ms (time_us); - meta_xkb_translate_state (event, seat->xkb, seat->button_state); - event->button.button = button_nr; - - if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) - { - graphene_point_t point; - - clutter_input_device_get_coords (input_device, NULL, &point); - event->button.x = point.x; - event->button.y = point.y; - } - else - { - event->button.x = seat->pointer_x; - event->button.y = seat->pointer_y; - } - - clutter_event_set_device (event, seat->core_pointer); - clutter_event_set_source_device (event, input_device); - - if (device_evdev->last_tool) - { - /* Apply the button event code as per the tool mapping */ - uint32_t mapped_button; - - mapped_button = meta_input_device_tool_native_get_button_code (device_evdev->last_tool, - button_nr); - if (mapped_button != 0) - button = mapped_button; - } - - meta_event_native_set_event_code (event, button); - - if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) - { - clutter_event_set_device_tool (event, device_evdev->last_tool); - clutter_event_set_device (event, input_device); - } - else - { - clutter_event_set_device (event, seat->core_pointer); - } - - queue_event (seat, event); -} - -static void -notify_scroll (ClutterInputDevice *input_device, - uint64_t time_us, - double dx, - double dy, - ClutterScrollSource scroll_source, - ClutterScrollFinishFlags flags, - gboolean emulated) -{ - MetaInputDeviceNative *device_evdev; - MetaSeatNative *seat; - ClutterEvent *event = NULL; - double scroll_factor; - - device_evdev = META_INPUT_DEVICE_NATIVE (input_device); - seat = meta_input_device_native_get_seat (device_evdev); - - event = clutter_event_new (CLUTTER_SCROLL); - - meta_event_native_set_time_usec (event, time_us); - event->scroll.time = us2ms (time_us); - meta_xkb_translate_state (event, seat->xkb, seat->button_state); - - /* libinput pointer axis events are in pointer motion coordinate space. - * To convert to Xi2 discrete step coordinate space, multiply the factor - * 1/10. */ - event->scroll.direction = CLUTTER_SCROLL_SMOOTH; - scroll_factor = 1.0 / DISCRETE_SCROLL_STEP; - clutter_event_set_scroll_delta (event, - scroll_factor * dx, - scroll_factor * dy); - - event->scroll.x = seat->pointer_x; - event->scroll.y = seat->pointer_y; - clutter_event_set_device (event, seat->core_pointer); - clutter_event_set_source_device (event, input_device); - event->scroll.scroll_source = scroll_source; - event->scroll.finish_flags = flags; - - _clutter_event_set_pointer_emulated (event, emulated); - - queue_event (seat, event); -} - -static void -notify_discrete_scroll (ClutterInputDevice *input_device, - uint64_t time_us, - ClutterScrollDirection direction, - ClutterScrollSource scroll_source, - gboolean emulated) -{ - MetaInputDeviceNative *device_evdev; - MetaSeatNative *seat; - ClutterEvent *event = NULL; - - if (direction == CLUTTER_SCROLL_SMOOTH) - return; - - device_evdev = META_INPUT_DEVICE_NATIVE (input_device); - seat = meta_input_device_native_get_seat (device_evdev); - - event = clutter_event_new (CLUTTER_SCROLL); - - meta_event_native_set_time_usec (event, time_us); - event->scroll.time = us2ms (time_us); - meta_xkb_translate_state (event, seat->xkb, seat->button_state); - - event->scroll.direction = direction; - - event->scroll.x = seat->pointer_x; - event->scroll.y = seat->pointer_y; - clutter_event_set_device (event, seat->core_pointer); - clutter_event_set_source_device (event, input_device); - event->scroll.scroll_source = scroll_source; - - _clutter_event_set_pointer_emulated (event, emulated); - - queue_event (seat, event); -} - -static void -check_notify_discrete_scroll (MetaSeatNative *seat, - ClutterInputDevice *device, - uint64_t time_us, - ClutterScrollSource scroll_source) -{ - int i, n_xscrolls, n_yscrolls; - - n_xscrolls = floor (fabs (seat->accum_scroll_dx) / DISCRETE_SCROLL_STEP); - n_yscrolls = floor (fabs (seat->accum_scroll_dy) / DISCRETE_SCROLL_STEP); - - for (i = 0; i < n_xscrolls; i++) - { - notify_discrete_scroll (device, time_us, - seat->accum_scroll_dx > 0 ? - CLUTTER_SCROLL_RIGHT : CLUTTER_SCROLL_LEFT, - scroll_source, TRUE); - } - - for (i = 0; i < n_yscrolls; i++) - { - notify_discrete_scroll (device, time_us, - seat->accum_scroll_dy > 0 ? - CLUTTER_SCROLL_DOWN : CLUTTER_SCROLL_UP, - scroll_source, TRUE); - } - - seat->accum_scroll_dx = fmodf (seat->accum_scroll_dx, DISCRETE_SCROLL_STEP); - seat->accum_scroll_dy = fmodf (seat->accum_scroll_dy, DISCRETE_SCROLL_STEP); -} - -void -meta_seat_native_notify_scroll_continuous (MetaSeatNative *seat, - ClutterInputDevice *input_device, - uint64_t time_us, - double dx, - double dy, - ClutterScrollSource scroll_source, - ClutterScrollFinishFlags finish_flags) -{ - if (finish_flags & CLUTTER_SCROLL_FINISHED_HORIZONTAL) - seat->accum_scroll_dx = 0; - else - seat->accum_scroll_dx += dx; - - if (finish_flags & CLUTTER_SCROLL_FINISHED_VERTICAL) - seat->accum_scroll_dy = 0; - else - seat->accum_scroll_dy += dy; - - notify_scroll (input_device, time_us, dx, dy, scroll_source, - finish_flags, FALSE); - check_notify_discrete_scroll (seat, input_device, time_us, scroll_source); -} - -static ClutterScrollDirection -discrete_to_direction (double discrete_dx, - double discrete_dy) -{ - if (discrete_dx > 0) - return CLUTTER_SCROLL_RIGHT; - else if (discrete_dx < 0) - return CLUTTER_SCROLL_LEFT; - else if (discrete_dy > 0) - return CLUTTER_SCROLL_DOWN; - else if (discrete_dy < 0) - return CLUTTER_SCROLL_UP; - else - g_assert_not_reached (); - return 0; -} - -void -meta_seat_native_notify_discrete_scroll (MetaSeatNative *seat, - ClutterInputDevice *input_device, - uint64_t time_us, - double discrete_dx, - double discrete_dy, - ClutterScrollSource scroll_source) -{ - notify_scroll (input_device, time_us, - discrete_dx * DISCRETE_SCROLL_STEP, - discrete_dy * DISCRETE_SCROLL_STEP, - scroll_source, CLUTTER_SCROLL_FINISHED_NONE, - TRUE); - notify_discrete_scroll (input_device, time_us, - discrete_to_direction (discrete_dx, discrete_dy), - scroll_source, FALSE); - -} - -void -meta_seat_native_notify_touch_event (MetaSeatNative *seat, - ClutterInputDevice *input_device, - ClutterEventType evtype, - uint64_t time_us, - int slot, - double x, - double y) -{ - ClutterEvent *event = NULL; - - event = clutter_event_new (evtype); - - meta_event_native_set_time_usec (event, time_us); - event->touch.time = us2ms (time_us); - event->touch.x = x; - event->touch.y = y; - meta_input_device_native_translate_coordinates (input_device, - seat->viewports, - &event->touch.x, - &event->touch.y); - - /* "NULL" sequences are special cased in clutter */ - event->touch.sequence = GINT_TO_POINTER (MAX (1, slot + 1)); - meta_xkb_translate_state (event, seat->xkb, seat->button_state); - - if (evtype == CLUTTER_TOUCH_BEGIN || - evtype == CLUTTER_TOUCH_UPDATE) - event->touch.modifier_state |= CLUTTER_BUTTON1_MASK; - - clutter_event_set_device (event, seat->core_pointer); - clutter_event_set_source_device (event, input_device); - - queue_event (seat, event); -} - - -/* - * MetaEventSource for reading input devices - */ -static gboolean -meta_event_prepare (GSource *source, - gint *timeout) -{ - gboolean retval; - - *timeout = -1; - retval = clutter_events_pending (); - - return retval; -} - -static gboolean -meta_event_check (GSource *source) -{ - MetaEventSource *event_source = (MetaEventSource *) source; - gboolean retval; - - retval = ((event_source->event_poll_fd.revents & G_IO_IN) || - clutter_events_pending ()); - - return retval; -} - -static void -constrain_to_barriers (MetaSeatNative *seat, - ClutterInputDevice *device, - guint32 time, - float *new_x, - float *new_y) -{ - meta_barrier_manager_native_process (seat->barrier_manager, - device, - time, - new_x, new_y); -} - -/* - * The pointer constrain code is mostly a rip-off of the XRandR code from Xorg. - * (from xserver/randr/rrcrtc.c, RRConstrainCursorHarder) - * - * Copyright © 2006 Keith Packard - * Copyright 2010 Red Hat, Inc - * - */ - -static void -constrain_all_screen_monitors (ClutterInputDevice *device, - MetaViewportInfo *viewports, - float *x, - float *y) -{ - graphene_point_t current; - float cx, cy; - int i, n_views; - - clutter_input_device_get_coords (device, NULL, ¤t); - - cx = current.x; - cy = current.y; - - /* if we're trying to escape, clamp to the CRTC we're coming from */ - - n_views = meta_viewport_info_get_num_views (viewports); - - for (i = 0; i < n_views; i++) - { - int left, right, top, bottom; - cairo_rectangle_int_t rect; - - meta_viewport_info_get_view (viewports, i, &rect, NULL); - - left = rect.x; - right = left + rect.width; - top = rect.y; - bottom = top + rect.height; - - if ((cx >= left) && (cx < right) && (cy >= top) && (cy < bottom)) - { - if (*x < left) - *x = left; - if (*x >= right) - *x = right - 1; - if (*y < top) - *y = top; - if (*y >= bottom) - *y = bottom - 1; - - return; - } - } -} - -void -meta_seat_native_constrain_pointer (MetaSeatNative *seat, - ClutterInputDevice *core_pointer, - uint64_t time_us, - float x, - float y, - float *new_x, - float *new_y) -{ - /* Constrain to barriers */ - constrain_to_barriers (seat, core_pointer, - us2ms (time_us), - new_x, new_y); - - /* Bar to constraints */ - if (seat->pointer_constraint) - { - meta_pointer_constraint_impl_constrain (seat->pointer_constraint, - core_pointer, - us2ms (time_us), - x, y, - new_x, new_y); - } - - if (seat->viewports) - { - /* if we're moving inside a monitor, we're fine */ - if (meta_viewport_info_get_view_at (seat->viewports, *new_x, *new_y) > 0) - return; - - /* if we're trying to escape, clamp to the CRTC we're coming from */ - constrain_all_screen_monitors (core_pointer, seat->viewports, new_x, new_y); - } -} - -static void -relative_motion_across_outputs (MetaViewportInfo *viewports, - int view, - float cur_x, - float cur_y, - float *dx_inout, - float *dy_inout) -{ - int cur_view = view; - float x = cur_x, y = cur_y; - float target_x = cur_x, target_y = cur_y; - float dx = *dx_inout, dy = *dy_inout; - MetaDisplayDirection direction = -1; - - while (cur_view >= 0) - { - MetaLine2 left, right, top, bottom, motion; - MetaVector2 intersection; - cairo_rectangle_int_t rect; - float scale; - - meta_viewport_info_get_view (viewports, cur_view, &rect, &scale); - - motion = (MetaLine2) { - .a = { x, y }, - .b = { x + (dx * scale), y + (dy * scale) } - }; - left = (MetaLine2) { - { rect.x, rect.y }, - { rect.x, rect.y + rect.height } - }; - right = (MetaLine2) { - { rect.x + rect.width, rect.y }, - { rect.x + rect.width, rect.y + rect.height } - }; - top = (MetaLine2) { - { rect.x, rect.y }, - { rect.x + rect.width, rect.y } - }; - bottom = (MetaLine2) { - { rect.x, rect.y + rect.height }, - { rect.x + rect.width, rect.y + rect.height } - }; - - target_x = motion.b.x; - target_y = motion.b.y; - - if (direction != META_DISPLAY_RIGHT && - meta_line2_intersects_with (&motion, &left, &intersection)) - direction = META_DISPLAY_LEFT; - else if (direction != META_DISPLAY_LEFT && - meta_line2_intersects_with (&motion, &right, &intersection)) - direction = META_DISPLAY_RIGHT; - else if (direction != META_DISPLAY_DOWN && - meta_line2_intersects_with (&motion, &top, &intersection)) - direction = META_DISPLAY_UP; - else if (direction != META_DISPLAY_UP && - meta_line2_intersects_with (&motion, &bottom, &intersection)) - direction = META_DISPLAY_DOWN; - else - /* We reached the dest logical monitor */ - break; - - x = intersection.x; - y = intersection.y; - dx -= intersection.x - motion.a.x; - dy -= intersection.y - motion.a.y; - - cur_view = meta_viewport_info_get_neighbor (viewports, cur_view, - direction); - } - - *dx_inout = target_x - cur_x; - *dy_inout = target_y - cur_y; -} - -void -meta_seat_native_filter_relative_motion (MetaSeatNative *seat, - ClutterInputDevice *device, - float x, - float y, - float *dx, - float *dy) -{ - int view = -1, dest_view; - float new_dx, new_dy, scale; - - if (meta_is_stage_views_scaled ()) - return; - - if (seat->viewports) - view = meta_viewport_info_get_view_at (seat->viewports, x, y); - if (view < 0) - return; - - meta_viewport_info_get_view (seat->viewports, view, NULL, &scale); - new_dx = (*dx) * scale; - new_dy = (*dy) * scale; - - dest_view = meta_viewport_info_get_view_at (seat->viewports, - x + new_dx, - y + new_dy); - if (dest_view >= 0 && dest_view != view) - { - /* If we are crossing monitors, attempt to bisect the distance on each - * axis and apply the relative scale for each of them. - */ - new_dx = *dx; - new_dy = *dy; - relative_motion_across_outputs (seat->viewports, view, - x, y, &new_dx, &new_dy); - } - - *dx = new_dx; - *dy = new_dy; -} - -static void -notify_absolute_motion (ClutterInputDevice *input_device, - uint64_t time_us, - float x, - float y, - double *axes) -{ - MetaSeatNative *seat; - ClutterEvent *event; - - seat = meta_input_device_native_get_seat (META_INPUT_DEVICE_NATIVE (input_device)); - event = new_absolute_motion_event (seat, input_device, time_us, x, y, axes); - - queue_event (seat, event); -} - -static void -notify_relative_tool_motion (ClutterInputDevice *input_device, - uint64_t time_us, - float dx, - float dy, - double *axes) -{ - MetaInputDeviceNative *device_evdev; - ClutterEvent *event; - MetaSeatNative *seat; - gfloat x, y; - - device_evdev = META_INPUT_DEVICE_NATIVE (input_device); - seat = meta_input_device_native_get_seat (device_evdev); - x = device_evdev->pointer_x + dx; - y = device_evdev->pointer_y + dy; - - meta_seat_native_filter_relative_motion (seat, - input_device, - seat->pointer_x, - seat->pointer_y, - &dx, - &dy); - - event = new_absolute_motion_event (seat, input_device, time_us, x, y, axes); - meta_event_native_set_relative_motion (event, dx, dy, 0, 0); - - queue_event (seat, event); -} - -static void -notify_pinch_gesture_event (ClutterInputDevice *input_device, - ClutterTouchpadGesturePhase phase, - uint64_t time_us, - double dx, - double dy, - double angle_delta, - double scale, - uint32_t n_fingers) -{ - MetaInputDeviceNative *device_evdev; - MetaSeatNative *seat; - ClutterEvent *event = NULL; - graphene_point_t pos; - - device_evdev = META_INPUT_DEVICE_NATIVE (input_device); - seat = meta_input_device_native_get_seat (device_evdev); - - event = clutter_event_new (CLUTTER_TOUCHPAD_PINCH); - - clutter_input_device_get_coords (seat->core_pointer, NULL, &pos); - - meta_event_native_set_time_usec (event, time_us); - event->touchpad_pinch.phase = phase; - event->touchpad_pinch.time = us2ms (time_us); - event->touchpad_pinch.x = pos.x; - event->touchpad_pinch.y = pos.y; - event->touchpad_pinch.dx = dx; - event->touchpad_pinch.dy = dy; - event->touchpad_pinch.angle_delta = angle_delta; - event->touchpad_pinch.scale = scale; - event->touchpad_pinch.n_fingers = n_fingers; - - meta_xkb_translate_state (event, seat->xkb, seat->button_state); - - clutter_event_set_device (event, seat->core_pointer); - clutter_event_set_source_device (event, input_device); - - queue_event (seat, event); -} - -static void -notify_swipe_gesture_event (ClutterInputDevice *input_device, - ClutterTouchpadGesturePhase phase, - uint64_t time_us, - uint32_t n_fingers, - double dx, - double dy) -{ - MetaInputDeviceNative *device_evdev; - MetaSeatNative *seat; - ClutterEvent *event = NULL; - graphene_point_t pos; - - device_evdev = META_INPUT_DEVICE_NATIVE (input_device); - seat = meta_input_device_native_get_seat (device_evdev); - - event = clutter_event_new (CLUTTER_TOUCHPAD_SWIPE); - - meta_event_native_set_time_usec (event, time_us); - event->touchpad_swipe.phase = phase; - event->touchpad_swipe.time = us2ms (time_us); - - clutter_input_device_get_coords (seat->core_pointer, NULL, &pos); - event->touchpad_swipe.x = pos.x; - event->touchpad_swipe.y = pos.y; - event->touchpad_swipe.dx = dx; - event->touchpad_swipe.dy = dy; - event->touchpad_swipe.n_fingers = n_fingers; - - meta_xkb_translate_state (event, seat->xkb, seat->button_state); - - clutter_event_set_device (event, seat->core_pointer); - clutter_event_set_source_device (event, input_device); - - queue_event (seat, event); -} - -static void -notify_proximity (ClutterInputDevice *input_device, - uint64_t time_us, - gboolean in) -{ - MetaInputDeviceNative *device_evdev; - MetaSeatNative *seat; - ClutterEvent *event = NULL; - - device_evdev = META_INPUT_DEVICE_NATIVE (input_device); - seat = meta_input_device_native_get_seat (device_evdev); - - if (in) - event = clutter_event_new (CLUTTER_PROXIMITY_IN); - else - event = clutter_event_new (CLUTTER_PROXIMITY_OUT); - - meta_event_native_set_time_usec (event, time_us); - - event->proximity.time = us2ms (time_us); - clutter_event_set_device_tool (event, device_evdev->last_tool); - clutter_event_set_device (event, seat->core_pointer); - clutter_event_set_source_device (event, input_device); - - queue_event (seat, event); -} - -static void -notify_pad_button (ClutterInputDevice *input_device, - uint64_t time_us, - uint32_t button, - uint32_t mode_group, - uint32_t mode, - uint32_t pressed) -{ - MetaInputDeviceNative *device_evdev; - MetaSeatNative *seat; - ClutterEvent *event; - - device_evdev = META_INPUT_DEVICE_NATIVE (input_device); - seat = meta_input_device_native_get_seat (device_evdev); - - if (pressed) - event = clutter_event_new (CLUTTER_PAD_BUTTON_PRESS); - else - event = clutter_event_new (CLUTTER_PAD_BUTTON_RELEASE); - - meta_event_native_set_time_usec (event, time_us); - event->pad_button.button = button; - event->pad_button.group = mode_group; - event->pad_button.mode = mode; - clutter_event_set_device (event, input_device); - clutter_event_set_source_device (event, input_device); - clutter_event_set_time (event, us2ms (time_us)); - - queue_event (seat, event); -} - -static void -notify_pad_strip (ClutterInputDevice *input_device, - uint64_t time_us, - uint32_t strip_number, - uint32_t strip_source, - uint32_t mode_group, - uint32_t mode, - double value) -{ - ClutterInputDevicePadSource source; - MetaInputDeviceNative *device_evdev; - MetaSeatNative *seat; - ClutterEvent *event; - - device_evdev = META_INPUT_DEVICE_NATIVE (input_device); - seat = meta_input_device_native_get_seat (device_evdev); - - if (strip_source == LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER) - source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER; - else - source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN; - - event = clutter_event_new (CLUTTER_PAD_STRIP); - meta_event_native_set_time_usec (event, time_us); - event->pad_strip.strip_source = source; - event->pad_strip.strip_number = strip_number; - event->pad_strip.value = value; - event->pad_strip.group = mode_group; - event->pad_strip.mode = mode; - clutter_event_set_device (event, input_device); - clutter_event_set_source_device (event, input_device); - clutter_event_set_time (event, us2ms (time_us)); - - queue_event (seat, event); -} - -static void -notify_pad_ring (ClutterInputDevice *input_device, - uint64_t time_us, - uint32_t ring_number, - uint32_t ring_source, - uint32_t mode_group, - uint32_t mode, - double angle) -{ - ClutterInputDevicePadSource source; - MetaInputDeviceNative *device_evdev; - MetaSeatNative *seat; - ClutterEvent *event; - - device_evdev = META_INPUT_DEVICE_NATIVE (input_device); - seat = meta_input_device_native_get_seat (device_evdev); - - if (ring_source == LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER) - source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER; - else - source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN; - - event = clutter_event_new (CLUTTER_PAD_RING); - meta_event_native_set_time_usec (event, time_us); - event->pad_ring.ring_source = source; - event->pad_ring.ring_number = ring_number; - event->pad_ring.angle = angle; - event->pad_ring.group = mode_group; - event->pad_ring.mode = mode; - clutter_event_set_device (event, input_device); - clutter_event_set_source_device (event, input_device); - clutter_event_set_time (event, us2ms (time_us)); - - queue_event (seat, event); -} - -static gboolean -meta_event_dispatch (GSource *g_source, - GSourceFunc callback, - gpointer user_data) -{ - MetaEventSource *source = (MetaEventSource *) g_source; - MetaSeatNative *seat; - - seat = source->seat; - - /* Don't queue more events if we haven't finished handling the previous batch - */ - if (clutter_events_pending ()) - goto queue_event; - - dispatch_libinput (seat); - - queue_event: - - return TRUE; -} -static GSourceFuncs event_funcs = { - meta_event_prepare, - meta_event_check, - meta_event_dispatch, - NULL -}; - -static MetaEventSource * -meta_event_source_new (MetaSeatNative *seat) -{ - GSource *source; - MetaEventSource *event_source; - gint fd; - - source = g_source_new (&event_funcs, sizeof (MetaEventSource)); - event_source = (MetaEventSource *) source; - - /* setup the source */ - event_source->seat = seat; - - fd = libinput_get_fd (seat->libinput); - event_source->event_poll_fd.fd = fd; - event_source->event_poll_fd.events = G_IO_IN; - - /* and finally configure and attach the GSource */ - g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS); - g_source_add_poll (source, &event_source->event_poll_fd); - g_source_set_can_recurse (source, TRUE); - g_source_attach (source, NULL); - - return event_source; -} - -static void -meta_event_source_free (MetaEventSource *source) -{ - GSource *g_source = (GSource *) source; - - /* ignore the return value of close, it's not like we can do something - * about it */ - close (source->event_poll_fd.fd); - - g_source_destroy (g_source); - g_source_unref (g_source); -} - -static gboolean -has_touchscreen (MetaSeatNative *seat) -{ - GSList *l; - - for (l = seat->devices; l; l = l->next) - { - ClutterInputDeviceType device_type; - - device_type = clutter_input_device_get_device_type (l->data); - - if (device_type == CLUTTER_TOUCHSCREEN_DEVICE) - return TRUE; - } - - return FALSE; -} - -static void -update_touch_mode (MetaSeatNative *seat) -{ - gboolean touch_mode; - - /* No touch mode if we don't have a touchscreen, easy */ - if (!seat->has_touchscreen) - touch_mode = FALSE; - /* If we have a tablet mode switch, honor it being unset */ - else if (seat->has_tablet_switch && !seat->tablet_mode_switch_state) - touch_mode = FALSE; - /* If tablet mode is enabled, or if there is no tablet mode switch - * (eg. kiosk machines), assume touch-mode. - */ - else - touch_mode = TRUE; - - if (seat->touch_mode != touch_mode) - { - seat->touch_mode = touch_mode; - g_object_notify (G_OBJECT (seat), "touch-mode"); - } -} - -static ClutterInputDevice * -evdev_add_device (MetaSeatNative *seat, - struct libinput_device *libinput_device) -{ - ClutterInputDeviceType type; - ClutterInputDevice *device, *master = NULL; - - device = meta_input_device_native_new (seat, libinput_device); - - seat->devices = g_slist_prepend (seat->devices, device); - - /* Clutter assumes that device types are exclusive in the - * ClutterInputDevice API */ - type = meta_input_device_native_determine_type (libinput_device); - - if (type == CLUTTER_KEYBOARD_DEVICE) - master = seat->core_keyboard; - else if (type == CLUTTER_POINTER_DEVICE) - master = seat->core_pointer; - - if (master) - { - _clutter_input_device_set_associated_device (device, master); - _clutter_input_device_add_slave (master, device); - } - - return device; -} - -static void -evdev_remove_device (MetaSeatNative *seat, - MetaInputDeviceNative *device_evdev) -{ - ClutterInputDevice *device; - - device = CLUTTER_INPUT_DEVICE (device_evdev); - seat->devices = g_slist_remove (seat->devices, device); - - g_object_unref (device); -} - static gboolean meta_seat_native_handle_event_post (ClutterSeat *seat, const ClutterEvent *event) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); - ClutterInputDevice *device = event->device.device; - MetaInputDeviceNative *device_native = META_INPUT_DEVICE_NATIVE (device); - gboolean check_touch_mode; + ClutterInputDevice *device = clutter_event_get_source_device (event); + ClutterEventType event_type = event->type; - check_touch_mode = - clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE; - - switch (event->type) + if (event_type == CLUTTER_PROXIMITY_IN) { - case CLUTTER_DEVICE_ADDED: - seat_native->has_touchscreen = check_touch_mode; + MetaCursorRendererNative *renderer; - if (libinput_device_has_capability (device_native->libinput_device, - LIBINPUT_DEVICE_CAP_SWITCH) && - libinput_device_switch_has_switch (device_native->libinput_device, - LIBINPUT_SWITCH_TABLET_MODE)) - { - seat_native->has_tablet_switch = TRUE; - check_touch_mode = TRUE; - } - break; + if (!seat_native->tablet_cursors) + { + seat_native->tablet_cursors = g_hash_table_new_full (NULL, NULL, NULL, + g_object_unref); + } - case CLUTTER_DEVICE_REMOVED: - if (check_touch_mode) - seat_native->has_touchscreen = has_touchscreen (seat_native); - - if (seat_native->repeat_timer && seat_native->repeat_device == device) - meta_seat_native_clear_repeat_timer (seat_native); - break; - - default: - break; - } - - if (check_touch_mode) - update_touch_mode (seat_native); - - return TRUE; -} - -static gboolean -process_base_event (MetaSeatNative *seat, - struct libinput_event *event) -{ - ClutterInputDevice *device; - ClutterEvent *device_event; - struct libinput_device *libinput_device; - - switch (libinput_event_get_type (event)) - { - case LIBINPUT_EVENT_DEVICE_ADDED: - libinput_device = libinput_event_get_device (event); - - device = evdev_add_device (seat, libinput_device); - device_event = clutter_event_new (CLUTTER_DEVICE_ADDED); - clutter_event_set_device (device_event, device); - break; - - case LIBINPUT_EVENT_DEVICE_REMOVED: - libinput_device = libinput_event_get_device (event); - - device = libinput_device_get_user_data (libinput_device); - device_event = clutter_event_new (CLUTTER_DEVICE_REMOVED); - clutter_event_set_device (device_event, device); - evdev_remove_device (seat, - META_INPUT_DEVICE_NATIVE (device)); - break; - - default: - device_event = NULL; - } - - if (device_event) - { - queue_event (seat, device_event); + renderer = meta_cursor_renderer_native_new (meta_get_backend (), device); + g_hash_table_insert (seat_native->tablet_cursors, device, renderer); 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_MASTER) + 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 ClutterScrollSource -translate_scroll_source (enum libinput_pointer_axis_source source) -{ - switch (source) - { - case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: - return CLUTTER_SCROLL_SOURCE_WHEEL; - case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: - return CLUTTER_SCROLL_SOURCE_FINGER; - case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: - return CLUTTER_SCROLL_SOURCE_CONTINUOUS; - default: - return CLUTTER_SCROLL_SOURCE_UNKNOWN; - } -} - -static ClutterInputDeviceToolType -translate_tool_type (struct libinput_tablet_tool *libinput_tool) -{ - enum libinput_tablet_tool_type tool; - - tool = libinput_tablet_tool_get_type (libinput_tool); - - switch (tool) - { - case LIBINPUT_TABLET_TOOL_TYPE_PEN: - return CLUTTER_INPUT_DEVICE_TOOL_PEN; - case LIBINPUT_TABLET_TOOL_TYPE_ERASER: - return CLUTTER_INPUT_DEVICE_TOOL_ERASER; - case LIBINPUT_TABLET_TOOL_TYPE_BRUSH: - return CLUTTER_INPUT_DEVICE_TOOL_BRUSH; - case LIBINPUT_TABLET_TOOL_TYPE_PENCIL: - return CLUTTER_INPUT_DEVICE_TOOL_PENCIL; - case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH: - return CLUTTER_INPUT_DEVICE_TOOL_AIRBRUSH; - case LIBINPUT_TABLET_TOOL_TYPE_MOUSE: - return CLUTTER_INPUT_DEVICE_TOOL_MOUSE; - case LIBINPUT_TABLET_TOOL_TYPE_LENS: - return CLUTTER_INPUT_DEVICE_TOOL_LENS; - default: - return CLUTTER_INPUT_DEVICE_TOOL_NONE; - } -} - -static void -input_device_update_tool (ClutterInputDevice *input_device, - struct libinput_tablet_tool *libinput_tool) -{ - MetaInputDeviceNative *evdev_device = META_INPUT_DEVICE_NATIVE (input_device); - MetaSeatNative *seat = meta_input_device_native_get_seat (evdev_device); - ClutterInputDeviceTool *tool = NULL; - ClutterInputDeviceToolType tool_type; - uint64_t tool_serial; - - if (libinput_tool) - { - tool_serial = libinput_tablet_tool_get_serial (libinput_tool); - tool_type = translate_tool_type (libinput_tool); - tool = clutter_input_device_lookup_tool (input_device, - tool_serial, tool_type); - - if (!tool) - { - tool = meta_input_device_tool_native_new (libinput_tool, - tool_serial, tool_type); - clutter_input_device_add_tool (input_device, tool); - } - } - - if (evdev_device->last_tool != tool) - { - evdev_device->last_tool = tool; - g_signal_emit_by_name (seat, "tool-changed", input_device, tool); - } -} - -static gdouble * -translate_tablet_axes (struct libinput_event_tablet_tool *tablet_event, - ClutterInputDeviceTool *tool) -{ - GArray *axes = g_array_new (FALSE, FALSE, sizeof (gdouble)); - struct libinput_tablet_tool *libinput_tool; - gdouble value; - - libinput_tool = libinput_event_tablet_tool_get_tool (tablet_event); - - value = libinput_event_tablet_tool_get_x (tablet_event); - g_array_append_val (axes, value); - value = libinput_event_tablet_tool_get_y (tablet_event); - g_array_append_val (axes, value); - - if (libinput_tablet_tool_has_distance (libinput_tool)) - { - value = libinput_event_tablet_tool_get_distance (tablet_event); - g_array_append_val (axes, value); - } - - if (libinput_tablet_tool_has_pressure (libinput_tool)) - { - value = libinput_event_tablet_tool_get_pressure (tablet_event); - value = meta_input_device_tool_native_translate_pressure (tool, value); - g_array_append_val (axes, value); - } - - if (libinput_tablet_tool_has_tilt (libinput_tool)) - { - value = libinput_event_tablet_tool_get_tilt_x (tablet_event); - g_array_append_val (axes, value); - value = libinput_event_tablet_tool_get_tilt_y (tablet_event); - g_array_append_val (axes, value); - } - - if (libinput_tablet_tool_has_rotation (libinput_tool)) - { - value = libinput_event_tablet_tool_get_rotation (tablet_event); - g_array_append_val (axes, value); - } - - if (libinput_tablet_tool_has_slider (libinput_tool)) - { - value = libinput_event_tablet_tool_get_slider_position (tablet_event); - g_array_append_val (axes, value); - } - - if (libinput_tablet_tool_has_wheel (libinput_tool)) - { - value = libinput_event_tablet_tool_get_wheel_delta (tablet_event); - g_array_append_val (axes, value); - } - - if (axes->len == 0) - { - g_array_free (axes, TRUE); - return NULL; - } - else - return (gdouble *) g_array_free (axes, FALSE); -} - -static MetaSeatNative * -seat_from_device (ClutterInputDevice *device) -{ - MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (device); - - return meta_input_device_native_get_seat (device_evdev); -} - -static void -notify_continuous_axis (MetaSeatNative *seat, - ClutterInputDevice *device, - uint64_t time_us, - ClutterScrollSource scroll_source, - struct libinput_event_pointer *axis_event) -{ - gdouble dx = 0.0, dy = 0.0; - ClutterScrollFinishFlags finish_flags = CLUTTER_SCROLL_FINISHED_NONE; - - if (libinput_event_pointer_has_axis (axis_event, - LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) - { - dx = libinput_event_pointer_get_axis_value ( - axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); - - if (fabs (dx) < DBL_EPSILON) - finish_flags |= CLUTTER_SCROLL_FINISHED_HORIZONTAL; - } - if (libinput_event_pointer_has_axis (axis_event, - LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) - { - dy = libinput_event_pointer_get_axis_value ( - axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); - - if (fabs (dy) < DBL_EPSILON) - finish_flags |= CLUTTER_SCROLL_FINISHED_VERTICAL; - } - - meta_seat_native_notify_scroll_continuous (seat, device, time_us, - dx, dy, - scroll_source, finish_flags); -} - -static void -notify_discrete_axis (MetaSeatNative *seat, - ClutterInputDevice *device, - uint64_t time_us, - ClutterScrollSource scroll_source, - struct libinput_event_pointer *axis_event) -{ - gdouble discrete_dx = 0.0, discrete_dy = 0.0; - - if (libinput_event_pointer_has_axis (axis_event, - LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) - { - discrete_dx = libinput_event_pointer_get_axis_value_discrete ( - axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); - } - if (libinput_event_pointer_has_axis (axis_event, - LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) - { - discrete_dy = libinput_event_pointer_get_axis_value_discrete ( - axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); - } - - meta_seat_native_notify_discrete_scroll (seat, device, - time_us, - discrete_dx, discrete_dy, - scroll_source); -} - -static void -process_tablet_axis (MetaSeatNative *seat, - struct libinput_event *event) -{ - struct libinput_device *libinput_device = libinput_event_get_device (event); - uint64_t time; - double x, y, dx, dy, *axes; - float stage_width, stage_height; - ClutterInputDevice *device; - struct libinput_event_tablet_tool *tablet_event = - libinput_event_get_tablet_tool_event (event); - MetaInputDeviceNative *evdev_device; - - device = libinput_device_get_user_data (libinput_device); - evdev_device = META_INPUT_DEVICE_NATIVE (device); - - axes = translate_tablet_axes (tablet_event, - evdev_device->last_tool); - if (!axes) - return; - - meta_viewport_info_get_extents (seat->viewports, &stage_width, &stage_height); - - time = libinput_event_tablet_tool_get_time_usec (tablet_event); - - if (meta_input_device_native_get_mapping_mode (device) == META_INPUT_DEVICE_MAPPING_RELATIVE || - clutter_input_device_tool_get_tool_type (evdev_device->last_tool) == CLUTTER_INPUT_DEVICE_TOOL_MOUSE || - clutter_input_device_tool_get_tool_type (evdev_device->last_tool) == CLUTTER_INPUT_DEVICE_TOOL_LENS) - { - dx = libinput_event_tablet_tool_get_dx (tablet_event); - dy = libinput_event_tablet_tool_get_dy (tablet_event); - notify_relative_tool_motion (device, time, dx, dy, axes); - } - else - { - x = libinput_event_tablet_tool_get_x_transformed (tablet_event, stage_width); - y = libinput_event_tablet_tool_get_y_transformed (tablet_event, stage_height); - notify_absolute_motion (device, time, x, y, axes); - } -} - -static void -update_tablet_cursor_state (MetaSeatNative *seat, - ClutterInputDevice *device, - gboolean in) -{ - if (in) - { - MetaCursorRendererNative *renderer; - - if (!seat->tablet_cursors) - { - seat->tablet_cursors = g_hash_table_new_full (NULL, NULL, NULL, - g_object_unref); - } - - renderer = meta_cursor_renderer_native_new (meta_get_backend (), device); - g_hash_table_insert (seat->tablet_cursors, device, renderer); - } - else - { - if (seat->tablet_cursors) - g_hash_table_remove (seat->tablet_cursors, device); - } -} - -static gboolean -process_device_event (MetaSeatNative *seat, - struct libinput_event *event) -{ - gboolean handled = TRUE; - struct libinput_device *libinput_device = libinput_event_get_device(event); - ClutterInputDevice *device; - MetaInputDeviceNative *device_evdev; - - switch (libinput_event_get_type (event)) - { - case LIBINPUT_EVENT_KEYBOARD_KEY: - { - uint32_t key, key_state, seat_key_count; - uint64_t time_us; - struct libinput_event_keyboard *key_event = - libinput_event_get_keyboard_event (event); - - device = libinput_device_get_user_data (libinput_device); - time_us = libinput_event_keyboard_get_time_usec (key_event); - key = libinput_event_keyboard_get_key (key_event); - key_state = libinput_event_keyboard_get_key_state (key_event) == - LIBINPUT_KEY_STATE_PRESSED; - seat_key_count = - libinput_event_keyboard_get_seat_key_count (key_event); - - /* Ignore key events that are not seat wide state changes. */ - if ((key_state == LIBINPUT_KEY_STATE_PRESSED && - seat_key_count != 1) || - (key_state == LIBINPUT_KEY_STATE_RELEASED && - seat_key_count != 0)) - { - meta_topic (META_DEBUG_INPUT, - "Dropping key-%s of key 0x%x because seat-wide " - "key count is %d\n", - key_state == LIBINPUT_KEY_STATE_PRESSED ? "press" : "release", - key, seat_key_count); - break; - } - - meta_seat_native_notify_key (seat_from_device (device), - device, - time_us, key, key_state, TRUE); - - break; - } - - case LIBINPUT_EVENT_POINTER_MOTION: - { - struct libinput_event_pointer *pointer_event = - libinput_event_get_pointer_event (event); - uint64_t time_us; - double dx; - double dy; - double dx_unaccel; - double dy_unaccel; - - device = libinput_device_get_user_data (libinput_device); - time_us = libinput_event_pointer_get_time_usec (pointer_event); - dx = libinput_event_pointer_get_dx (pointer_event); - dy = libinput_event_pointer_get_dy (pointer_event); - dx_unaccel = libinput_event_pointer_get_dx_unaccelerated (pointer_event); - dy_unaccel = libinput_event_pointer_get_dy_unaccelerated (pointer_event); - - meta_seat_native_notify_relative_motion (seat_from_device (device), - device, - time_us, - dx, dy, - dx_unaccel, dy_unaccel); - - break; - } - - case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: - { - uint64_t time_us; - double x, y; - float stage_width, stage_height; - struct libinput_event_pointer *motion_event = - libinput_event_get_pointer_event (event); - device = libinput_device_get_user_data (libinput_device); - - meta_viewport_info_get_extents (seat->viewports, - &stage_width, &stage_height); - - time_us = libinput_event_pointer_get_time_usec (motion_event); - x = libinput_event_pointer_get_absolute_x_transformed (motion_event, - stage_width); - y = libinput_event_pointer_get_absolute_y_transformed (motion_event, - stage_height); - - meta_seat_native_notify_absolute_motion (seat_from_device (device), - device, - time_us, - x, y, - NULL); - - break; - } - - case LIBINPUT_EVENT_POINTER_BUTTON: - { - uint32_t button, button_state, seat_button_count; - uint64_t time_us; - struct libinput_event_pointer *button_event = - libinput_event_get_pointer_event (event); - device = libinput_device_get_user_data (libinput_device); - - time_us = libinput_event_pointer_get_time_usec (button_event); - button = libinput_event_pointer_get_button (button_event); - button_state = libinput_event_pointer_get_button_state (button_event) == - LIBINPUT_BUTTON_STATE_PRESSED; - seat_button_count = - libinput_event_pointer_get_seat_button_count (button_event); - - /* Ignore button events that are not seat wide state changes. */ - if ((button_state == LIBINPUT_BUTTON_STATE_PRESSED && - seat_button_count != 1) || - (button_state == LIBINPUT_BUTTON_STATE_RELEASED && - seat_button_count != 0)) - { - meta_topic (META_DEBUG_INPUT, - "Dropping button-%s of button 0x%x because seat-wide " - "button count is %d\n", - button_state == LIBINPUT_BUTTON_STATE_PRESSED ? "press" : "release", - button, seat_button_count); - break; - } - - meta_seat_native_notify_button (seat_from_device (device), device, - time_us, button, button_state); - break; - } - - case LIBINPUT_EVENT_POINTER_AXIS: - { - uint64_t time_us; - enum libinput_pointer_axis_source source; - struct libinput_event_pointer *axis_event = - libinput_event_get_pointer_event (event); - MetaSeatNative *seat; - ClutterScrollSource scroll_source; - - device = libinput_device_get_user_data (libinput_device); - seat = meta_input_device_native_get_seat (META_INPUT_DEVICE_NATIVE (device)); - - time_us = libinput_event_pointer_get_time_usec (axis_event); - source = libinput_event_pointer_get_axis_source (axis_event); - scroll_source = translate_scroll_source (source); - - /* libinput < 0.8 sent wheel click events with value 10. Since 0.8 - the value is the angle of the click in degrees. To keep - backwards-compat with existing clients, we just send multiples of - the click count. */ - - switch (scroll_source) - { - case CLUTTER_SCROLL_SOURCE_WHEEL: - notify_discrete_axis (seat, device, time_us, scroll_source, - axis_event); - break; - case CLUTTER_SCROLL_SOURCE_FINGER: - case CLUTTER_SCROLL_SOURCE_CONTINUOUS: - case CLUTTER_SCROLL_SOURCE_UNKNOWN: - notify_continuous_axis (seat, device, time_us, scroll_source, - axis_event); - break; - } - break; - } - - case LIBINPUT_EVENT_TOUCH_DOWN: - { - int seat_slot; - uint64_t time_us; - double x, y; - float stage_width, stage_height; - MetaSeatNative *seat; - MetaTouchState *touch_state; - struct libinput_event_touch *touch_event = - libinput_event_get_touch_event (event); - - device = libinput_device_get_user_data (libinput_device); - device_evdev = META_INPUT_DEVICE_NATIVE (device); - seat = meta_input_device_native_get_seat (device_evdev); - - meta_viewport_info_get_extents (seat->viewports, - &stage_width, &stage_height); - - seat_slot = libinput_event_touch_get_seat_slot (touch_event); - time_us = libinput_event_touch_get_time_usec (touch_event); - x = libinput_event_touch_get_x_transformed (touch_event, - stage_width); - y = libinput_event_touch_get_y_transformed (touch_event, - stage_height); - - touch_state = meta_seat_native_acquire_touch_state (seat, seat_slot); - touch_state->coords.x = x; - touch_state->coords.y = y; - - meta_seat_native_notify_touch_event (seat, device, - CLUTTER_TOUCH_BEGIN, - time_us, - touch_state->seat_slot, - touch_state->coords.x, - touch_state->coords.y); - break; - } - - case LIBINPUT_EVENT_TOUCH_UP: - { - int seat_slot; - uint64_t time_us; - MetaSeatNative *seat; - MetaTouchState *touch_state; - struct libinput_event_touch *touch_event = - libinput_event_get_touch_event (event); - - device = libinput_device_get_user_data (libinput_device); - device_evdev = META_INPUT_DEVICE_NATIVE (device); - seat = meta_input_device_native_get_seat (device_evdev); - - seat_slot = libinput_event_touch_get_seat_slot (touch_event); - time_us = libinput_event_touch_get_time_usec (touch_event); - touch_state = meta_seat_native_lookup_touch_state (seat, seat_slot); - if (!touch_state) - break; - - meta_seat_native_notify_touch_event (seat, device, - CLUTTER_TOUCH_END, time_us, - touch_state->seat_slot, - touch_state->coords.x, - touch_state->coords.y); - meta_seat_native_release_touch_state (seat, seat_slot); - break; - } - - case LIBINPUT_EVENT_TOUCH_MOTION: - { - int seat_slot; - uint64_t time_us; - double x, y; - float stage_width, stage_height; - MetaSeatNative *seat; - MetaTouchState *touch_state; - struct libinput_event_touch *touch_event = - libinput_event_get_touch_event (event); - - device = libinput_device_get_user_data (libinput_device); - device_evdev = META_INPUT_DEVICE_NATIVE (device); - seat = meta_input_device_native_get_seat (device_evdev); - - meta_viewport_info_get_extents (seat->viewports, - &stage_width, &stage_height); - - seat_slot = libinput_event_touch_get_seat_slot (touch_event); - time_us = libinput_event_touch_get_time_usec (touch_event); - x = libinput_event_touch_get_x_transformed (touch_event, - stage_width); - y = libinput_event_touch_get_y_transformed (touch_event, - stage_height); - - touch_state = meta_seat_native_lookup_touch_state (seat, seat_slot); - if (!touch_state) - break; - - touch_state->coords.x = x; - touch_state->coords.y = y; - - meta_seat_native_notify_touch_event (seat, device, - CLUTTER_TOUCH_UPDATE, - time_us, - touch_state->seat_slot, - touch_state->coords.x, - touch_state->coords.y); - break; - } - case LIBINPUT_EVENT_TOUCH_CANCEL: - { - int seat_slot; - MetaTouchState *touch_state; - uint64_t time_us; - MetaSeatNative *seat; - struct libinput_event_touch *touch_event = - libinput_event_get_touch_event (event); - - device = libinput_device_get_user_data (libinput_device); - device_evdev = META_INPUT_DEVICE_NATIVE (device); - seat = meta_input_device_native_get_seat (device_evdev); - time_us = libinput_event_touch_get_time_usec (touch_event); - - seat_slot = libinput_event_touch_get_seat_slot (touch_event); - touch_state = meta_seat_native_lookup_touch_state (seat, seat_slot); - if (!touch_state) - break; - - meta_seat_native_notify_touch_event (touch_state->seat, - CLUTTER_INPUT_DEVICE (device_evdev), - CLUTTER_TOUCH_CANCEL, - time_us, - touch_state->seat_slot, - touch_state->coords.x, - touch_state->coords.y); - - meta_seat_native_release_touch_state (seat, seat_slot); - break; - } - case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: - case LIBINPUT_EVENT_GESTURE_PINCH_END: - { - struct libinput_event_gesture *gesture_event = - libinput_event_get_gesture_event (event); - ClutterTouchpadGesturePhase phase; - uint32_t n_fingers; - uint64_t time_us; - - if (libinput_event_get_type (event) == LIBINPUT_EVENT_GESTURE_PINCH_BEGIN) - phase = CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN; - else - phase = libinput_event_gesture_get_cancelled (gesture_event) ? - CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL : CLUTTER_TOUCHPAD_GESTURE_PHASE_END; - - n_fingers = libinput_event_gesture_get_finger_count (gesture_event); - device = libinput_device_get_user_data (libinput_device); - time_us = libinput_event_gesture_get_time_usec (gesture_event); - notify_pinch_gesture_event (device, phase, time_us, 0, 0, 0, 0, n_fingers); - break; - } - case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: - { - struct libinput_event_gesture *gesture_event = - libinput_event_get_gesture_event (event); - gdouble angle_delta, scale, dx, dy; - uint32_t n_fingers; - uint64_t time_us; - - n_fingers = libinput_event_gesture_get_finger_count (gesture_event); - device = libinput_device_get_user_data (libinput_device); - time_us = libinput_event_gesture_get_time_usec (gesture_event); - angle_delta = libinput_event_gesture_get_angle_delta (gesture_event); - scale = libinput_event_gesture_get_scale (gesture_event); - dx = libinput_event_gesture_get_dx (gesture_event); - dy = libinput_event_gesture_get_dy (gesture_event); - - notify_pinch_gesture_event (device, - CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE, - time_us, dx, dy, angle_delta, scale, n_fingers); - break; - } - case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: - case LIBINPUT_EVENT_GESTURE_SWIPE_END: - { - struct libinput_event_gesture *gesture_event = - libinput_event_get_gesture_event (event); - ClutterTouchpadGesturePhase phase; - uint32_t n_fingers; - uint64_t time_us; - - device = libinput_device_get_user_data (libinput_device); - time_us = libinput_event_gesture_get_time_usec (gesture_event); - n_fingers = libinput_event_gesture_get_finger_count (gesture_event); - - if (libinput_event_get_type (event) == LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN) - phase = CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN; - else - phase = libinput_event_gesture_get_cancelled (gesture_event) ? - CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL : CLUTTER_TOUCHPAD_GESTURE_PHASE_END; - - notify_swipe_gesture_event (device, phase, time_us, n_fingers, 0, 0); - break; - } - case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: - { - struct libinput_event_gesture *gesture_event = - libinput_event_get_gesture_event (event); - uint32_t n_fingers; - uint64_t time_us; - double dx, dy; - - device = libinput_device_get_user_data (libinput_device); - time_us = libinput_event_gesture_get_time_usec (gesture_event); - n_fingers = libinput_event_gesture_get_finger_count (gesture_event); - dx = libinput_event_gesture_get_dx (gesture_event); - dy = libinput_event_gesture_get_dy (gesture_event); - - notify_swipe_gesture_event (device, - CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE, - time_us, n_fingers, dx, dy); - break; - } - case LIBINPUT_EVENT_TABLET_TOOL_AXIS: - { - process_tablet_axis (seat, event); - break; - } - case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: - { - uint64_t time; - struct libinput_event_tablet_tool *tablet_event = - libinput_event_get_tablet_tool_event (event); - struct libinput_tablet_tool *libinput_tool = NULL; - enum libinput_tablet_tool_proximity_state state; - gboolean in; - - state = libinput_event_tablet_tool_get_proximity_state (tablet_event); - time = libinput_event_tablet_tool_get_time_usec (tablet_event); - device = libinput_device_get_user_data (libinput_device); - in = state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN; - - libinput_tool = libinput_event_tablet_tool_get_tool (tablet_event); - - if (in) - input_device_update_tool (device, libinput_tool); - notify_proximity (device, time, in); - if (!in) - input_device_update_tool (device, NULL); - - update_tablet_cursor_state (seat, device, in); - break; - } - case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: - { - uint64_t time_us; - uint32_t button_state; - struct libinput_event_tablet_tool *tablet_event = - libinput_event_get_tablet_tool_event (event); - uint32_t tablet_button; - - process_tablet_axis (seat, event); - - device = libinput_device_get_user_data (libinput_device); - time_us = libinput_event_tablet_tool_get_time_usec (tablet_event); - tablet_button = libinput_event_tablet_tool_get_button (tablet_event); - - button_state = libinput_event_tablet_tool_get_button_state (tablet_event) == - LIBINPUT_BUTTON_STATE_PRESSED; - - meta_seat_native_notify_button (seat_from_device (device), device, - time_us, tablet_button, button_state); - break; - } - case LIBINPUT_EVENT_TABLET_TOOL_TIP: - { - uint64_t time_us; - uint32_t button_state; - struct libinput_event_tablet_tool *tablet_event = - libinput_event_get_tablet_tool_event (event); - - device = libinput_device_get_user_data (libinput_device); - time_us = libinput_event_tablet_tool_get_time_usec (tablet_event); - - button_state = libinput_event_tablet_tool_get_tip_state (tablet_event) == - LIBINPUT_TABLET_TOOL_TIP_DOWN; - - /* To avoid jumps on tip, notify axes before the tip down event - but after the tip up event */ - if (button_state) - process_tablet_axis (seat, event); - - meta_seat_native_notify_button (seat_from_device (device), device, - time_us, BTN_TOUCH, button_state); - if (!button_state) - process_tablet_axis (seat, event); - break; - } - case LIBINPUT_EVENT_TABLET_PAD_BUTTON: - { - uint64_t time; - uint32_t button_state, button, group, mode; - struct libinput_tablet_pad_mode_group *mode_group; - struct libinput_event_tablet_pad *pad_event = - libinput_event_get_tablet_pad_event (event); - - device = libinput_device_get_user_data (libinput_device); - time = libinput_event_tablet_pad_get_time_usec (pad_event); - - mode_group = libinput_event_tablet_pad_get_mode_group (pad_event); - group = libinput_tablet_pad_mode_group_get_index (mode_group); - mode = libinput_event_tablet_pad_get_mode (pad_event); - - button = libinput_event_tablet_pad_get_button_number (pad_event); - button_state = libinput_event_tablet_pad_get_button_state (pad_event) == - LIBINPUT_BUTTON_STATE_PRESSED; - notify_pad_button (device, time, button, group, mode, button_state); - break; - } - case LIBINPUT_EVENT_TABLET_PAD_STRIP: - { - uint64_t time; - uint32_t number, source, group, mode; - struct libinput_tablet_pad_mode_group *mode_group; - struct libinput_event_tablet_pad *pad_event = - libinput_event_get_tablet_pad_event (event); - double value; - - device = libinput_device_get_user_data (libinput_device); - time = libinput_event_tablet_pad_get_time_usec (pad_event); - number = libinput_event_tablet_pad_get_strip_number (pad_event); - value = libinput_event_tablet_pad_get_strip_position (pad_event); - source = libinput_event_tablet_pad_get_strip_source (pad_event); - - mode_group = libinput_event_tablet_pad_get_mode_group (pad_event); - group = libinput_tablet_pad_mode_group_get_index (mode_group); - mode = libinput_event_tablet_pad_get_mode (pad_event); - - notify_pad_strip (device, time, number, source, group, mode, value); - break; - } - case LIBINPUT_EVENT_TABLET_PAD_RING: - { - uint64_t time; - uint32_t number, source, group, mode; - struct libinput_tablet_pad_mode_group *mode_group; - struct libinput_event_tablet_pad *pad_event = - libinput_event_get_tablet_pad_event (event); - double angle; - - device = libinput_device_get_user_data (libinput_device); - time = libinput_event_tablet_pad_get_time_usec (pad_event); - number = libinput_event_tablet_pad_get_ring_number (pad_event); - angle = libinput_event_tablet_pad_get_ring_position (pad_event); - source = libinput_event_tablet_pad_get_ring_source (pad_event); - - mode_group = libinput_event_tablet_pad_get_mode_group (pad_event); - group = libinput_tablet_pad_mode_group_get_index (mode_group); - mode = libinput_event_tablet_pad_get_mode (pad_event); - - notify_pad_ring (device, time, number, source, group, mode, angle); - break; - } - case LIBINPUT_EVENT_SWITCH_TOGGLE: - { - struct libinput_event_switch *switch_event = - libinput_event_get_switch_event (event); - enum libinput_switch sw = - libinput_event_switch_get_switch (switch_event); - enum libinput_switch_state state = - libinput_event_switch_get_switch_state (switch_event); - - if (sw == LIBINPUT_SWITCH_TABLET_MODE) - { - seat->tablet_mode_switch_state = (state == LIBINPUT_SWITCH_STATE_ON); - update_touch_mode (seat); - } - break; - } - default: - handled = FALSE; - } - - return handled; -} - -static void -process_event (MetaSeatNative *seat, - struct libinput_event *event) -{ - if (process_base_event (seat, event)) - return; - if (process_device_event (seat, event)) - return; -} - -static void -process_events (MetaSeatNative *seat) -{ - struct libinput_event *event; - - while ((event = libinput_get_event (seat->libinput))) - { - process_event(seat, event); - libinput_event_destroy(event); - } -} - -static int -open_restricted (const char *path, - int flags, - void *user_data) -{ - gint fd; - - if (device_open_callback) - { - GError *error = NULL; - - fd = device_open_callback (path, flags, device_callback_data, &error); - - if (fd < 0) - { - g_warning ("Could not open device %s: %s", path, error->message); - g_error_free (error); - } - } - else - { - fd = open (path, O_RDWR | O_NONBLOCK); - if (fd < 0) - { - g_warning ("Could not open device %s: %s", path, strerror (errno)); - } - } - - return fd; -} - -static void -close_restricted (int fd, - void *user_data) -{ - if (device_close_callback) - device_close_callback (fd, device_callback_data); - else - close (fd); -} - -static const struct libinput_interface libinput_interface = { - open_restricted, - close_restricted -}; - static void meta_seat_native_constructed (GObject *object) { MetaSeatNative *seat = META_SEAT_NATIVE (object); - ClutterInputDevice *device; - MetaEventSource *source; - struct udev *udev; - struct xkb_keymap *xkb_keymap; - device = meta_input_device_native_new_virtual ( - seat, CLUTTER_POINTER_DEVICE, - CLUTTER_INPUT_MODE_MASTER); - seat->pointer_x = INITIAL_POINTER_X; - seat->pointer_y = INITIAL_POINTER_Y; - meta_input_device_native_update_coords (META_INPUT_DEVICE_NATIVE (device), - seat->pointer_x, seat->pointer_y); - seat->core_pointer = device; - - device = meta_input_device_native_new_virtual ( - seat, CLUTTER_KEYBOARD_DEVICE, - CLUTTER_INPUT_MODE_MASTER); - seat->core_keyboard = device; + seat->impl = meta_seat_impl_new (seat, seat->seat_id); + seat->core_pointer = meta_seat_impl_get_pointer (seat->impl); + seat->core_keyboard = meta_seat_impl_get_keyboard (seat->impl); seat->kms_cursor_renderer = meta_kms_cursor_renderer_new (meta_get_backend ()); - udev = udev_new (); - if (G_UNLIKELY (udev == NULL)) - { - g_warning ("Failed to create udev object"); - return; - } - - seat->libinput = libinput_udev_create_context (&libinput_interface, - seat, udev); - if (seat->libinput == NULL) - { - g_critical ("Failed to create the libinput object."); - return; - } - - if (libinput_udev_assign_seat (seat->libinput, seat->seat_id) == -1) - { - g_critical ("Failed to assign a seat to the libinput object."); - libinput_unref (seat->libinput); - seat->libinput = NULL; - return; - } - - udev_unref (udev); - - seat->udev_client = g_udev_client_new ((const gchar *[]) { "input", NULL }); - - dispatch_libinput (seat); - - source = meta_event_source_new (seat); - seat->event_source = source; - - seat->keymap = g_object_new (META_TYPE_KEYMAP_NATIVE, NULL); - xkb_keymap = meta_keymap_native_get_keyboard_map (seat->keymap); - - if (xkb_keymap) - { - seat->xkb = xkb_state_new (xkb_keymap); - - seat->caps_lock_led = - xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS); - seat->num_lock_led = - xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM); - seat->scroll_lock_led = - xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL); - } - - seat->has_touchscreen = has_touchscreen (seat); - update_touch_mode (seat); - if (G_OBJECT_CLASS (meta_seat_native_parent_class)->constructed) G_OBJECT_CLASS (meta_seat_native_parent_class)->constructed (object); } @@ -2568,32 +154,21 @@ meta_seat_native_get_property (GObject *object, g_value_set_string (value, seat_native->seat_id); break; case PROP_TOUCH_MODE: - g_value_set_boolean (value, seat_native->touch_mode); + g_value_set_boolean (value, seat_native->impl->touch_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } -static void -meta_seat_native_dispose (GObject *object) -{ - MetaSeatNative *seat = META_SEAT_NATIVE (object); - - if (seat->libinput) - { - libinput_unref (seat->libinput); - seat->libinput = NULL; - } - - G_OBJECT_CLASS (meta_seat_native_parent_class)->dispose (object); -} - static void meta_seat_native_finalize (GObject *object) { MetaSeatNative *seat = META_SEAT_NATIVE (object); - GSList *iter; + + g_clear_object (&seat->core_pointer); + g_clear_object (&seat->core_keyboard); + g_clear_object (&seat->impl); for (iter = seat->devices; iter; iter = g_slist_next (iter)) { @@ -2603,23 +178,8 @@ meta_seat_native_finalize (GObject *object) } g_slist_free (seat->devices); - if (seat->touch_states) - g_hash_table_destroy (seat->touch_states); - g_clear_pointer (&seat->tablet_cursors, g_hash_table_unref); g_object_unref (seat->cursor_renderer); - g_object_unref (seat->udev_client); - - meta_event_source_free (seat->event_source); - - xkb_state_unref (seat->xkb); - - meta_seat_native_clear_repeat_timer (seat); - - if (seat->libinput_seat) - libinput_seat_unref (seat->libinput_seat); - - g_list_free (seat->free_device_ids); g_free (seat->seat_id); @@ -2663,6 +223,9 @@ 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); } @@ -2743,8 +306,7 @@ meta_seat_native_warp_pointer (ClutterSeat *seat, meta_backend_get_cursor_renderer (backend); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); - notify_absolute_motion (seat_native->core_pointer, 0, x, y, NULL); - + meta_seat_impl_warp_pointer (seat_native->impl, x, y); meta_cursor_renderer_update_position (cursor_renderer); meta_cursor_tracker_update_position (cursor_tracker); } @@ -2756,46 +318,10 @@ meta_seat_native_query_state (ClutterSeat *seat, graphene_point_t *coords, ClutterModifierType *modifiers) { - MetaInputDeviceNative *device_native = META_INPUT_DEVICE_NATIVE (device); MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); - if (sequence) - { - MetaTouchState *touch_state; - int slot; - - slot = meta_event_native_sequence_get_slot (sequence); - touch_state = meta_seat_native_lookup_touch_state (seat_native, slot); - if (!touch_state) - return FALSE; - - if (coords) - { - coords->x = touch_state->coords.x; - coords->y = touch_state->coords.y; - } - - if (modifiers) - *modifiers = meta_xkb_translate_modifiers (seat_native->xkb, 0); - - return TRUE; - } - else - { - if (coords) - { - coords->x = device_native->pointer_x; - coords->y = device_native->pointer_y; - } - - if (modifiers) - { - *modifiers = meta_xkb_translate_modifiers (seat_native->xkb, - seat_native->button_state); - } - - return TRUE; - } + return meta_seat_impl_query_state (seat_native->impl, device, sequence, + coords, modifiers); } static void @@ -2807,7 +333,6 @@ meta_seat_native_class_init (MetaSeatNativeClass *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->dispose = meta_seat_native_dispose; object_class->finalize = meta_seat_native_finalize; seat_class->get_pointer = meta_seat_native_get_pointer; @@ -2841,110 +366,6 @@ meta_seat_native_class_init (MetaSeatNativeClass *klass) static void meta_seat_native_init (MetaSeatNative *seat) { - seat->device_id_next = INITIAL_DEVICE_ID; - - seat->repeat = TRUE; - seat->repeat_delay = 250; /* ms */ - seat->repeat_interval = 33; /* ms */ - - seat->barrier_manager = meta_barrier_manager_native_new (); -} - -/** - * meta_seat_native_set_device_callbacks: (skip) - * @open_callback: the user replacement for open() - * @close_callback: the user replacement for close() - * @user_data: user data for @callback - * - * Through this function, the application can set a custom callback - * to be invoked when Clutter is about to open an evdev device. It can do - * so if special handling is needed, for example to circumvent permission - * problems. - * - * Setting @callback to %NULL will reset the default behavior. - * - * For reliable effects, this function must be called before clutter_init(). - */ -void -meta_seat_native_set_device_callbacks (MetaOpenDeviceCallback open_callback, - MetaCloseDeviceCallback close_callback, - gpointer user_data) -{ - device_open_callback = open_callback; - device_close_callback = close_callback; - device_callback_data = user_data; -} - -void -meta_seat_native_update_xkb_state (MetaSeatNative *seat) -{ - xkb_mod_mask_t latched_mods; - xkb_mod_mask_t locked_mods; - struct xkb_keymap *xkb_keymap; - - xkb_keymap = meta_keymap_native_get_keyboard_map (seat->keymap); - - latched_mods = xkb_state_serialize_mods (seat->xkb, - XKB_STATE_MODS_LATCHED); - locked_mods = xkb_state_serialize_mods (seat->xkb, - XKB_STATE_MODS_LOCKED); - xkb_state_unref (seat->xkb); - seat->xkb = xkb_state_new (xkb_keymap); - - xkb_state_update_mask (seat->xkb, - 0, /* depressed */ - latched_mods, - locked_mods, - 0, 0, seat->layout_idx); - - seat->caps_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS); - seat->num_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM); - seat->scroll_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL); - - meta_seat_native_sync_leds (seat); -} - -gint -meta_seat_native_acquire_device_id (MetaSeatNative *seat) -{ - GList *first; - gint next_id; - - if (seat->free_device_ids == NULL) - { - gint i; - - /* We ran out of free ID's, so append 10 new ones. */ - for (i = 0; i < 10; i++) - seat->free_device_ids = - g_list_append (seat->free_device_ids, - GINT_TO_POINTER (seat->device_id_next++)); - } - - first = g_list_first (seat->free_device_ids); - next_id = GPOINTER_TO_INT (first->data); - seat->free_device_ids = g_list_delete_link (seat->free_device_ids, first); - - return next_id; -} - -static int -compare_ids (gconstpointer a, - gconstpointer b) -{ - return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b); -} - -void -meta_seat_native_release_device_id (MetaSeatNative *seat, - ClutterInputDevice *device) -{ - gint device_id; - - device_id = clutter_input_device_get_device_id (device); - seat->free_device_ids = g_list_insert_sorted (seat->free_device_ids, - GINT_TO_POINTER (device_id), - compare_ids); } /** @@ -2970,9 +391,7 @@ meta_seat_native_release_devices (MetaSeatNative *seat) return; } - libinput_suspend (seat->libinput); - process_events (seat); - + meta_seat_impl_release_devices (seat->impl); seat->released = TRUE; } @@ -2997,10 +416,7 @@ meta_seat_native_reclaim_devices (MetaSeatNative *seat) return; } - libinput_resume (seat->libinput); - meta_seat_native_update_xkb_state (seat); - process_events (seat); - + meta_seat_impl_reclaim_devices (seat->impl); seat->released = FALSE; } @@ -3018,15 +434,9 @@ void meta_seat_native_set_keyboard_map (MetaSeatNative *seat, struct xkb_keymap *xkb_keymap) { - ClutterKeymap *keymap; - g_return_if_fail (META_IS_SEAT_NATIVE (seat)); - keymap = clutter_seat_get_keymap (CLUTTER_SEAT (seat)); - meta_keymap_native_set_keyboard_map (META_KEYMAP_NATIVE (keymap), - xkb_keymap); - - meta_seat_native_update_xkb_state (seat); + meta_seat_impl_set_keyboard_map (seat->impl, xkb_keymap); } /** @@ -3042,7 +452,7 @@ meta_seat_native_get_keyboard_map (MetaSeatNative *seat) { g_return_val_if_fail (META_IS_SEAT_NATIVE (seat), NULL); - return xkb_state_get_keymap (seat->xkb); + return meta_seat_impl_get_keyboard_map (seat->impl); } /** @@ -3056,22 +466,9 @@ void meta_seat_native_set_keyboard_layout_index (MetaSeatNative *seat, xkb_layout_index_t idx) { - xkb_mod_mask_t depressed_mods; - xkb_mod_mask_t latched_mods; - xkb_mod_mask_t locked_mods; - struct xkb_state *state; - g_return_if_fail (META_IS_SEAT_NATIVE (seat)); - state = seat->xkb; - - depressed_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_DEPRESSED); - latched_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_LATCHED); - locked_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED); - - xkb_state_update_mask (state, depressed_mods, latched_mods, locked_mods, 0, 0, idx); - - seat->layout_idx = idx; + meta_seat_impl_set_keyboard_layout_index (seat->impl, idx); } /** @@ -3080,7 +477,7 @@ meta_seat_native_set_keyboard_layout_index (MetaSeatNative *seat, xkb_layout_index_t meta_seat_native_get_keyboard_layout_index (MetaSeatNative *seat) { - return seat->layout_idx; + return meta_seat_impl_get_keyboard_layout_index (seat->impl); } /** @@ -3094,87 +491,20 @@ void meta_seat_native_set_keyboard_numlock (MetaSeatNative *seat, gboolean numlock_state) { - xkb_mod_mask_t depressed_mods; - xkb_mod_mask_t latched_mods; - xkb_mod_mask_t locked_mods; - xkb_mod_mask_t group_mods; - xkb_mod_mask_t numlock; - struct xkb_keymap *xkb_keymap; - ClutterKeymap *keymap; - - g_return_if_fail (META_IS_SEAT_NATIVE (seat)); - - keymap = clutter_seat_get_keymap (CLUTTER_SEAT (seat)); - xkb_keymap = meta_keymap_native_get_keyboard_map (META_KEYMAP_NATIVE (keymap)); - - numlock = (1 << xkb_keymap_mod_get_index (xkb_keymap, "Mod2")); - - depressed_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_DEPRESSED); - latched_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LATCHED); - locked_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LOCKED); - group_mods = xkb_state_serialize_layout (seat->xkb, XKB_STATE_LAYOUT_EFFECTIVE); - - if (numlock_state) - locked_mods |= numlock; - else - locked_mods &= ~numlock; - - xkb_state_update_mask (seat->xkb, - depressed_mods, - latched_mods, - locked_mods, - 0, 0, - group_mods); - - meta_seat_native_sync_leds (seat); -} - -/** - * meta_seat_native_set_keyboard_repeat: - * @seat: the #ClutterSeat created by the evdev backend - * @repeat: whether to enable or disable keyboard repeat events - * @delay: the delay in ms between the hardware key press event and - * the first synthetic event - * @interval: the period in ms between consecutive synthetic key - * press events - * - * Enables or disables sythetic key press events, allowing for initial - * delay and interval period to be specified. - */ -void -meta_seat_native_set_keyboard_repeat (MetaSeatNative *seat, - gboolean repeat, - uint32_t delay, - uint32_t interval) -{ - g_return_if_fail (META_IS_SEAT_NATIVE (seat)); - - seat->repeat = repeat; - seat->repeat_delay = delay; - seat->repeat_interval = interval; -} - -struct xkb_state * -meta_seat_native_get_xkb_state (MetaSeatNative *seat) -{ - return seat->xkb; + meta_seat_impl_set_keyboard_numlock (seat->impl, numlock_state); } MetaBarrierManagerNative * meta_seat_native_get_barrier_manager (MetaSeatNative *seat) { - return seat->barrier_manager; + return meta_seat_impl_get_barrier_manager (seat->impl); } void meta_seat_native_set_pointer_constraint (MetaSeatNative *seat, MetaPointerConstraintImpl *impl) { - if (g_set_object (&seat->pointer_constraint, impl)) - { - if (impl) - meta_pointer_constraint_impl_ensure_constrained (impl, seat->core_pointer); - } + meta_seat_impl_set_pointer_constraint (seat->impl, impl); } MetaCursorRenderer * @@ -3209,5 +539,5 @@ void meta_seat_native_set_viewports (MetaSeatNative *seat, MetaViewportInfo *viewports) { - g_set_object (&seat->viewports, viewports); + meta_seat_impl_set_viewports (seat->impl, viewports); } diff --git a/src/backends/native/meta-seat-native.h b/src/backends/native/meta-seat-native.h index 8e98d0c6e..8bc50856c 100644 --- a/src/backends/native/meta-seat-native.h +++ b/src/backends/native/meta-seat-native.h @@ -32,82 +32,29 @@ #include "backends/native/meta-cursor-renderer-native.h" #include "backends/native/meta-keymap-native.h" #include "backends/native/meta-pointer-constraint-native.h" +#include "backends/native/meta-seat-impl.h" #include "backends/native/meta-xkb-utils.h" #include "clutter/clutter.h" -typedef struct _MetaTouchState MetaTouchState; typedef struct _MetaSeatNative MetaSeatNative; -typedef struct _MetaEventSource MetaEventSource; - -struct _MetaTouchState -{ - MetaSeatNative *seat; - - int device_slot; - int seat_slot; - graphene_point_t coords; -}; struct _MetaSeatNative { ClutterSeat parent_instance; + MetaSeatImpl *impl; char *seat_id; - MetaEventSource *event_source; - struct libinput *libinput; - struct libinput_seat *libinput_seat; - GSList *devices; + GList *devices; ClutterInputDevice *core_pointer; ClutterInputDevice *core_keyboard; - GHashTable *touch_states; - GHashTable *cursor_renderers; - - struct xkb_state *xkb; - xkb_led_index_t caps_lock_led; - xkb_led_index_t num_lock_led; - xkb_led_index_t scroll_lock_led; - xkb_layout_index_t layout_idx; - uint32_t button_state; - int button_count[KEY_CNT]; - - int device_id_next; - GList *free_device_ids; - - MetaBarrierManagerNative *barrier_manager; - MetaPointerConstraintImpl *pointer_constraint; - MetaKeymapNative *keymap; MetaCursorRenderer *cursor_renderer; MetaKmsCursorRenderer *kms_cursor_renderer; GHashTable *tablet_cursors; - MetaViewportInfo *viewports; - - GUdevClient *udev_client; - guint tablet_mode_switch_state : 1; - guint has_touchscreen : 1; - guint has_tablet_switch : 1; - guint touch_mode : 1; - - /* keyboard repeat */ - gboolean repeat; - uint32_t repeat_delay; - uint32_t repeat_interval; - uint32_t repeat_key; - uint32_t repeat_count; - uint32_t repeat_timer; - ClutterInputDevice *repeat_device; - - float pointer_x; - float pointer_y; - - /* Emulation of discrete scroll events out of smooth ones */ - float accum_scroll_dx; - float accum_scroll_dy; - gboolean released; }; @@ -115,95 +62,11 @@ struct _MetaSeatNative G_DECLARE_FINAL_TYPE (MetaSeatNative, meta_seat_native, META, SEAT_NATIVE, ClutterSeat) -void meta_seat_native_notify_key (MetaSeatNative *seat, - ClutterInputDevice *device, - uint64_t time_us, - uint32_t key, - uint32_t state, - gboolean update_keys); - -void meta_seat_native_notify_relative_motion (MetaSeatNative *seat_evdev, - ClutterInputDevice *input_device, - uint64_t time_us, - float dx, - float dy, - float dx_unaccel, - float dy_unaccel); - -void meta_seat_native_notify_absolute_motion (MetaSeatNative *seat_evdev, - ClutterInputDevice *input_device, - uint64_t time_us, - float x, - float y, - double *axes); - -void meta_seat_native_notify_button (MetaSeatNative *seat, - ClutterInputDevice *input_device, - uint64_t time_us, - uint32_t button, - uint32_t state); - -void meta_seat_native_notify_scroll_continuous (MetaSeatNative *seat, - ClutterInputDevice *input_device, - uint64_t time_us, - double dx, - double dy, - ClutterScrollSource source, - ClutterScrollFinishFlags flags); - -void meta_seat_native_notify_discrete_scroll (MetaSeatNative *seat, - ClutterInputDevice *input_device, - uint64_t time_us, - double discrete_dx, - double discrete_dy, - ClutterScrollSource source); - -void meta_seat_native_notify_touch_event (MetaSeatNative *seat, - ClutterInputDevice *input_device, - ClutterEventType evtype, - uint64_t time_us, - int slot, - double x, - double y); - void meta_seat_native_set_libinput_seat (MetaSeatNative *seat, struct libinput_seat *libinput_seat); void meta_seat_native_sync_leds (MetaSeatNative *seat); -MetaTouchState * meta_seat_native_acquire_touch_state (MetaSeatNative *seat, - int seat_slot); -MetaTouchState * meta_seat_native_lookup_touch_state (MetaSeatNative *seat, - int seat_slot); - -void meta_seat_native_release_touch_state (MetaSeatNative *seat, - int seat_slot); - -void meta_seat_native_clear_repeat_timer (MetaSeatNative *seat); - -gint meta_seat_native_acquire_device_id (MetaSeatNative *seat); -void meta_seat_native_release_device_id (MetaSeatNative *seat, - ClutterInputDevice *device); - -void meta_seat_native_update_xkb_state (MetaSeatNative *seat); - -void meta_seat_native_constrain_pointer (MetaSeatNative *seat, - ClutterInputDevice *core_pointer, - uint64_t time_us, - float x, - float y, - float *new_x, - float *new_y); - -void meta_seat_native_filter_relative_motion (MetaSeatNative *seat, - ClutterInputDevice *device, - float x, - float y, - float *dx, - float *dy); - -void meta_seat_native_dispatch (MetaSeatNative *seat); - /** * MetaOpenDeviceCallback: * @path: the device path @@ -227,8 +90,6 @@ void meta_seat_native_set_device_callbacks (MetaOpenDeviceCallback open_callba void meta_seat_native_release_devices (MetaSeatNative *seat); void meta_seat_native_reclaim_devices (MetaSeatNative *seat); -struct xkb_state * meta_seat_native_get_xkb_state (MetaSeatNative *seat); - void meta_seat_native_set_keyboard_map (MetaSeatNative *seat, struct xkb_keymap *keymap); diff --git a/src/backends/native/meta-virtual-input-device-native.c b/src/backends/native/meta-virtual-input-device-native.c index 2033f245b..778ac8aed 100644 --- a/src/backends/native/meta-virtual-input-device-native.c +++ b/src/backends/native/meta-virtual-input-device-native.c @@ -129,19 +129,19 @@ release_pressed_buttons (ClutterVirtualInputDevice *virtual_device) switch (get_button_type (code)) { case EVDEV_BUTTON_TYPE_KEY: - meta_seat_native_notify_key (virtual_evdev->seat, - virtual_evdev->device, - time_us, - code, - CLUTTER_KEY_STATE_RELEASED, - TRUE); + meta_seat_impl_notify_key (virtual_evdev->seat->impl, + virtual_evdev->device, + time_us, + code, + CLUTTER_KEY_STATE_RELEASED, + TRUE); break; case EVDEV_BUTTON_TYPE_BUTTON: - meta_seat_native_notify_button (virtual_evdev->seat, - virtual_evdev->device, - time_us, - code, - CLUTTER_BUTTON_STATE_RELEASED); + meta_seat_impl_notify_button (virtual_evdev->seat->impl, + virtual_evdev->device, + time_us, + code, + CLUTTER_BUTTON_STATE_RELEASED); break; case EVDEV_BUTTON_TYPE_NONE: g_assert_not_reached (); @@ -165,11 +165,11 @@ meta_virtual_input_device_native_notify_relative_motion (ClutterVirtualInputDevi if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); - meta_seat_native_notify_relative_motion (virtual_evdev->seat, - virtual_evdev->device, - time_us, - dx, dy, - dx, dy); + meta_seat_impl_notify_relative_motion (virtual_evdev->seat->impl, + virtual_evdev->device, + time_us, + dx, dy, + dx, dy); } static void @@ -186,11 +186,11 @@ meta_virtual_input_device_native_notify_absolute_motion (ClutterVirtualInputDevi if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); - meta_seat_native_notify_absolute_motion (virtual_evdev->seat, - virtual_evdev->device, - time_us, - x, y, - NULL); + meta_seat_impl_notify_absolute_motion (virtual_evdev->seat->impl, + virtual_evdev->device, + time_us, + x, y, + NULL); } static int @@ -252,11 +252,11 @@ meta_virtual_input_device_native_notify_button (ClutterVirtualInputDevice *virtu button_state == CLUTTER_BUTTON_STATE_PRESSED ? "press" : "release", evdev_button, virtual_device); - meta_seat_native_notify_button (virtual_evdev->seat, - virtual_evdev->device, - time_us, - evdev_button, - button_state); + meta_seat_impl_notify_button (virtual_evdev->seat->impl, + virtual_evdev->device, + time_us, + evdev_button, + button_state); } static void @@ -294,12 +294,12 @@ meta_virtual_input_device_native_notify_key (ClutterVirtualInputDevice *virtual_ key_state == CLUTTER_KEY_STATE_PRESSED ? "press" : "release", key, virtual_device); - meta_seat_native_notify_key (virtual_evdev->seat, - virtual_evdev->device, - time_us, - key, - key_state, - TRUE); + meta_seat_impl_notify_key (virtual_evdev->seat->impl, + virtual_evdev->device, + time_us, + key, + key_state, + TRUE); } static gboolean @@ -320,7 +320,7 @@ pick_keycode_for_keyval_in_current_group (ClutterVirtualInputDevice *virtual_dev backend = clutter_get_default_backend (); keymap = clutter_seat_get_keymap (clutter_backend_get_default_seat (backend)); xkb_keymap = meta_keymap_native_get_keyboard_map (META_KEYMAP_NATIVE (keymap)); - state = virtual_evdev->seat->xkb; + state = meta_seat_impl_get_xkb_state (virtual_evdev->seat->impl); layout = xkb_state_serialize_layout (state, XKB_STATE_LAYOUT_EFFECTIVE); min_keycode = xkb_keymap_min_keycode (xkb_keymap); @@ -389,12 +389,12 @@ apply_level_modifiers (ClutterVirtualInputDevice *virtual_device, key_state == CLUTTER_KEY_STATE_PRESSED ? "press" : "release", evcode, virtual_device); - meta_seat_native_notify_key (virtual_evdev->seat, - virtual_evdev->device, - time_us, - evcode, - key_state, - TRUE); + meta_seat_impl_notify_key (virtual_evdev->seat->impl, + virtual_evdev->device, + time_us, + evcode, + key_state, + TRUE); } static void @@ -447,12 +447,12 @@ meta_virtual_input_device_native_notify_keyval (ClutterVirtualInputDevice *virtu if (key_state) apply_level_modifiers (virtual_device, time_us, level, key_state); - meta_seat_native_notify_key (virtual_evdev->seat, - virtual_evdev->device, - time_us, - evcode, - key_state, - TRUE); + meta_seat_impl_notify_key (virtual_evdev->seat->impl, + virtual_evdev->device, + time_us, + evcode, + key_state, + TRUE); if (!key_state) apply_level_modifiers (virtual_device, time_us, level, key_state); @@ -504,11 +504,11 @@ meta_virtual_input_device_native_notify_discrete_scroll (ClutterVirtualInputDevi direction_to_discrete (direction, &discrete_dx, &discrete_dy); - meta_seat_native_notify_discrete_scroll (virtual_evdev->seat, - virtual_evdev->device, - time_us, - discrete_dx, discrete_dy, - scroll_source); + meta_seat_impl_notify_discrete_scroll (virtual_evdev->seat->impl, + virtual_evdev->device, + time_us, + discrete_dx, discrete_dy, + scroll_source); } static void @@ -527,12 +527,12 @@ meta_virtual_input_device_native_notify_scroll_continuous (ClutterVirtualInputDe if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); - meta_seat_native_notify_scroll_continuous (virtual_evdev->seat, - virtual_evdev->device, - time_us, - dx, dy, - scroll_source, - CLUTTER_SCROLL_FINISHED_NONE); + meta_seat_impl_notify_scroll_continuous (virtual_evdev->seat->impl, + virtual_evdev->device, + time_us, + dx, dy, + scroll_source, + CLUTTER_SCROLL_FINISHED_NONE); } static void @@ -551,21 +551,21 @@ meta_virtual_input_device_native_notify_touch_down (ClutterVirtualInputDevice *v if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); - touch_state = meta_seat_native_acquire_touch_state (virtual_evdev->seat, - device_slot); + touch_state = meta_seat_impl_acquire_touch_state (virtual_evdev->seat->impl, + device_slot); if (!touch_state) return; touch_state->coords.x = x; touch_state->coords.y = y; - meta_seat_native_notify_touch_event (virtual_evdev->seat, - virtual_evdev->device, - CLUTTER_TOUCH_BEGIN, - time_us, - touch_state->seat_slot, - touch_state->coords.x, - touch_state->coords.y); + meta_seat_impl_notify_touch_event (virtual_evdev->seat->impl, + virtual_evdev->device, + CLUTTER_TOUCH_BEGIN, + time_us, + touch_state->seat_slot, + touch_state->coords.x, + touch_state->coords.y); } static void @@ -584,21 +584,21 @@ meta_virtual_input_device_native_notify_touch_motion (ClutterVirtualInputDevice if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); - touch_state = meta_seat_native_lookup_touch_state (virtual_evdev->seat, - device_slot); + touch_state = meta_seat_impl_lookup_touch_state (virtual_evdev->seat->impl, + device_slot); if (!touch_state) return; touch_state->coords.x = x; touch_state->coords.y = y; - meta_seat_native_notify_touch_event (virtual_evdev->seat, - virtual_evdev->device, - CLUTTER_TOUCH_BEGIN, - time_us, - touch_state->seat_slot, - touch_state->coords.x, - touch_state->coords.y); + meta_seat_impl_notify_touch_event (virtual_evdev->seat->impl, + virtual_evdev->device, + CLUTTER_TOUCH_BEGIN, + time_us, + touch_state->seat_slot, + touch_state->coords.x, + touch_state->coords.y); } static void @@ -615,21 +615,21 @@ meta_virtual_input_device_native_notify_touch_up (ClutterVirtualInputDevice *vir if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); - touch_state = meta_seat_native_lookup_touch_state (virtual_evdev->seat, - device_slot); + touch_state = meta_seat_impl_lookup_touch_state (virtual_evdev->seat->impl, + device_slot); if (!touch_state) return; - meta_seat_native_notify_touch_event (virtual_evdev->seat, - virtual_evdev->device, - CLUTTER_TOUCH_BEGIN, - time_us, - touch_state->seat_slot, - touch_state->coords.x, - touch_state->coords.y); + meta_seat_impl_notify_touch_event (virtual_evdev->seat->impl, + virtual_evdev->device, + CLUTTER_TOUCH_BEGIN, + time_us, + touch_state->seat_slot, + touch_state->coords.x, + touch_state->coords.y); - meta_seat_native_release_touch_state (virtual_evdev->seat, - touch_state->seat_slot); + meta_seat_impl_release_touch_state (virtual_evdev->seat->impl, + touch_state->seat_slot); } static void @@ -688,7 +688,7 @@ meta_virtual_input_device_native_constructed (GObject *object) device_type, virtual_device); virtual_evdev->device = - meta_input_device_native_new_virtual (virtual_evdev->seat, + meta_input_device_native_new_virtual (virtual_evdev->seat->impl, device_type, CLUTTER_INPUT_MODE_SLAVE); diff --git a/src/meson.build b/src/meson.build index b24f12d9c..48369d6ce 100644 --- a/src/meson.build +++ b/src/meson.build @@ -705,6 +705,8 @@ if have_native_backend 'backends/native/meta-renderer-native-gles3.c', 'backends/native/meta-renderer-native-gles3.h', 'backends/native/meta-renderer-native.h', + 'backends/native/meta-seat-impl.c', + 'backends/native/meta-seat-impl.h', 'backends/native/meta-seat-native.c', 'backends/native/meta-seat-native.h', 'backends/native/meta-stage-native.c',