diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2019-03-29 22:03:27 +0100 |
---|---|---|
committer | Jonas Ådahl <jadahl@gmail.com> | 2019-08-24 08:59:08 +0000 |
commit | 8b03d9ecc39a35451ae4a315216536a4597433f5 (patch) | |
tree | 8f8a07ad8736bce31f49e95d38d2c2b659e1907c /src/backends/native/meta-virtual-input-device-native.c | |
parent | ea54ce7d960e43ca8be99a0c789be259a4571a08 (diff) | |
download | mutter-8b03d9ecc39a35451ae4a315216536a4597433f5.tar.gz |
clutter: Move evdev input to src/backends/native
The end goal is to have all clutter backend code in src/backends. Input
is the larger chunk of it, which is now part of our specific
MutterClutterBackendNative, this extends to device manager, input devices,
tools and keymap.
This was supposed to be nice and incremental, but there's no sane way
to cut this through. As a result of the refactor, a number of private
Clutter functions are now exported for external backends to be possible.
https://gitlab.gnome.org/GNOME/mutter/merge_requests/672
Diffstat (limited to 'src/backends/native/meta-virtual-input-device-native.c')
-rw-r--r-- | src/backends/native/meta-virtual-input-device-native.c | 701 |
1 files changed, 701 insertions, 0 deletions
diff --git a/src/backends/native/meta-virtual-input-device-native.c b/src/backends/native/meta-virtual-input-device-native.c new file mode 100644 index 000000000..9ecc657c6 --- /dev/null +++ b/src/backends/native/meta-virtual-input-device-native.c @@ -0,0 +1,701 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author: Jonas Ådahl <jadahl@gmail.com> + */ + +#include "config.h" + +#include <glib-object.h> +#include <linux/input.h> + +#include "backends/native/meta-input-device-native.h" +#include "backends/native/meta-keymap-native.h" +#include "backends/native/meta-seat-native.h" +#include "backends/native/meta-virtual-input-device-native.h" +#include "clutter/clutter-mutter.h" + +enum +{ + PROP_0, + + PROP_SEAT, + + PROP_LAST +}; + +static GParamSpec *obj_props[PROP_LAST]; + +struct _MetaVirtualInputDeviceNative +{ + ClutterVirtualInputDevice parent; + + ClutterInputDevice *device; + MetaSeatNative *seat; + int button_count[KEY_CNT]; +}; + +G_DEFINE_TYPE (MetaVirtualInputDeviceNative, + meta_virtual_input_device_native, + CLUTTER_TYPE_VIRTUAL_INPUT_DEVICE) + +typedef enum _EvdevButtonType +{ + EVDEV_BUTTON_TYPE_NONE, + EVDEV_BUTTON_TYPE_KEY, + EVDEV_BUTTON_TYPE_BUTTON, +} EvdevButtonType; + +static int +update_button_count (MetaVirtualInputDeviceNative *virtual_evdev, + uint32_t button, + uint32_t state) +{ + if (state) + return ++virtual_evdev->button_count[button]; + else + return --virtual_evdev->button_count[button]; +} + +static EvdevButtonType +get_button_type (uint16_t code) +{ + switch (code) + { + case BTN_TOOL_PEN: + case BTN_TOOL_RUBBER: + case BTN_TOOL_BRUSH: + case BTN_TOOL_PENCIL: + case BTN_TOOL_AIRBRUSH: + case BTN_TOOL_MOUSE: + case BTN_TOOL_LENS: + case BTN_TOOL_QUINTTAP: + case BTN_TOOL_DOUBLETAP: + case BTN_TOOL_TRIPLETAP: + case BTN_TOOL_QUADTAP: + case BTN_TOOL_FINGER: + case BTN_TOUCH: + return EVDEV_BUTTON_TYPE_NONE; + } + + if (code >= KEY_ESC && code <= KEY_MICMUTE) + return EVDEV_BUTTON_TYPE_KEY; + if (code >= BTN_MISC && code <= BTN_GEAR_UP) + return EVDEV_BUTTON_TYPE_BUTTON; + if (code >= KEY_OK && code <= KEY_LIGHTS_TOGGLE) + return EVDEV_BUTTON_TYPE_KEY; + if (code >= BTN_DPAD_UP && code <= BTN_DPAD_RIGHT) + return EVDEV_BUTTON_TYPE_BUTTON; + if (code >= KEY_ALS_TOGGLE && code <= KEY_KBDINPUTASSIST_CANCEL) + return EVDEV_BUTTON_TYPE_KEY; + if (code >= BTN_TRIGGER_HAPPY && code <= BTN_TRIGGER_HAPPY40) + return EVDEV_BUTTON_TYPE_BUTTON; + return EVDEV_BUTTON_TYPE_NONE; +} + +static void +release_pressed_buttons (ClutterVirtualInputDevice *virtual_device) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + int code; + uint64_t time_us; + + time_us = g_get_monotonic_time (); + + for (code = 0; code < G_N_ELEMENTS (virtual_evdev->button_count); code++) + { + if (virtual_evdev->button_count[code] == 0) + continue; + + switch (get_button_type (code)) + { + case EVDEV_BUTTON_TYPE_KEY: + clutter_virtual_input_device_notify_key (virtual_device, + time_us, + code, + CLUTTER_KEY_STATE_RELEASED); + break; + case EVDEV_BUTTON_TYPE_BUTTON: + clutter_virtual_input_device_notify_button (virtual_device, + time_us, + code, + CLUTTER_BUTTON_STATE_RELEASED); + break; + case EVDEV_BUTTON_TYPE_NONE: + g_assert_not_reached (); + } + } +} + +static void +meta_virtual_input_device_native_notify_relative_motion (ClutterVirtualInputDevice *virtual_device, + uint64_t time_us, + double dx, + double dy) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + + 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); +} + +static void +meta_virtual_input_device_native_notify_absolute_motion (ClutterVirtualInputDevice *virtual_device, + uint64_t time_us, + double x, + double y) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + + 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); +} + +static int +translate_to_evdev_button (int clutter_button) +{ + switch (clutter_button) + { + case CLUTTER_BUTTON_PRIMARY: + return BTN_LEFT; + case CLUTTER_BUTTON_SECONDARY: + return BTN_RIGHT; + case CLUTTER_BUTTON_MIDDLE: + return BTN_MIDDLE; + default: + /* + * For compatibility reasons, all additional buttons go after the old + * 4-7 scroll ones. + */ + return clutter_button + (BTN_LEFT - 1) - 4; + } +} + +static void +meta_virtual_input_device_native_notify_button (ClutterVirtualInputDevice *virtual_device, + uint64_t time_us, + uint32_t button, + ClutterButtonState button_state) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + int button_count; + int evdev_button; + + if (time_us == CLUTTER_CURRENT_TIME) + time_us = g_get_monotonic_time (); + + evdev_button = translate_to_evdev_button (button); + + if (get_button_type (evdev_button) != EVDEV_BUTTON_TYPE_BUTTON) + { + g_warning ("Unknown/invalid virtual device button 0x%x pressed", + evdev_button); + return; + } + + button_count = update_button_count (virtual_evdev, evdev_button, button_state); + if (button_count < 0 || button_count > 1) + { + g_warning ("Received multiple virtual 0x%x button %s (ignoring)", evdev_button, + button_state == CLUTTER_BUTTON_STATE_PRESSED ? "presses" : "releases"); + update_button_count (virtual_evdev, evdev_button, 1 - button_state); + return; + } + + meta_seat_native_notify_button (virtual_evdev->seat, + virtual_evdev->device, + time_us, + evdev_button, + button_state); +} + +static void +meta_virtual_input_device_native_notify_key (ClutterVirtualInputDevice *virtual_device, + uint64_t time_us, + uint32_t key, + ClutterKeyState key_state) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + int key_count; + + if (time_us == CLUTTER_CURRENT_TIME) + time_us = g_get_monotonic_time (); + + if (get_button_type (key) != EVDEV_BUTTON_TYPE_KEY) + { + g_warning ("Unknown/invalid virtual device key 0x%x pressed\n", key); + return; + } + + key_count = update_button_count (virtual_evdev, key, key_state); + if (key_count < 0 || key_count > 1) + { + g_warning ("Received multiple virtual 0x%x key %s (ignoring)", key, + key_state == CLUTTER_KEY_STATE_PRESSED ? "presses" : "releases"); + update_button_count (virtual_evdev, key, 1 - key_state); + return; + } + + meta_seat_native_notify_key (virtual_evdev->seat, + virtual_evdev->device, + time_us, + key, + key_state, + TRUE); +} + +static gboolean +pick_keycode_for_keyval_in_current_group (ClutterVirtualInputDevice *virtual_device, + guint keyval, + guint *keycode_out, + guint *level_out) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + ClutterKeymap *keymap; + struct xkb_keymap *xkb_keymap; + struct xkb_state *state; + guint keycode, layout; + xkb_keycode_t min_keycode, max_keycode; + + keymap = clutter_backend_get_keymap (clutter_get_default_backend ()); + xkb_keymap = meta_keymap_native_get_keyboard_map (META_KEYMAP_NATIVE (keymap)); + state = virtual_evdev->seat->xkb; + + layout = xkb_state_serialize_layout (state, XKB_STATE_LAYOUT_EFFECTIVE); + min_keycode = xkb_keymap_min_keycode (xkb_keymap); + max_keycode = xkb_keymap_max_keycode (xkb_keymap); + for (keycode = min_keycode; keycode < max_keycode; keycode++) + { + gint num_levels, level; + num_levels = xkb_keymap_num_levels_for_key (xkb_keymap, keycode, layout); + for (level = 0; level < num_levels; level++) + { + const xkb_keysym_t *syms; + gint num_syms, sym; + num_syms = xkb_keymap_key_get_syms_by_level (xkb_keymap, keycode, layout, level, &syms); + for (sym = 0; sym < num_syms; sym++) + { + if (syms[sym] == keyval) + { + *keycode_out = keycode; + if (level_out) + *level_out = level; + return TRUE; + } + } + } + } + + return FALSE; +} + +static void +apply_level_modifiers (ClutterVirtualInputDevice *virtual_device, + uint64_t time_us, + uint32_t level, + uint32_t key_state) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + guint keysym, keycode, evcode; + + if (level == 0) + return; + + if (level == 1) + { + keysym = XKB_KEY_Shift_L; + } + else if (level == 2) + { + keysym = XKB_KEY_ISO_Level3_Shift; + } + else + { + g_warning ("Unhandled level: %d\n", level); + return; + } + + if (!pick_keycode_for_keyval_in_current_group (virtual_device, keysym, + &keycode, NULL)) + return; + + clutter_input_device_keycode_to_evdev (virtual_evdev->device, + keycode, &evcode); + meta_seat_native_notify_key (virtual_evdev->seat, + virtual_evdev->device, + time_us, + evcode, + key_state, + TRUE); +} + +static void +meta_virtual_input_device_native_notify_keyval (ClutterVirtualInputDevice *virtual_device, + uint64_t time_us, + uint32_t keyval, + ClutterKeyState key_state) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + int key_count; + guint keycode = 0, level = 0, evcode = 0; + + if (time_us == CLUTTER_CURRENT_TIME) + time_us = g_get_monotonic_time (); + + if (!pick_keycode_for_keyval_in_current_group (virtual_device, + keyval, &keycode, &level)) + { + g_warning ("No keycode found for keyval %x in current group", keyval); + return; + } + + clutter_input_device_keycode_to_evdev (virtual_evdev->device, + keycode, &evcode); + + if (get_button_type (evcode) != EVDEV_BUTTON_TYPE_KEY) + { + g_warning ("Unknown/invalid virtual device key 0x%x pressed\n", evcode); + return; + } + + key_count = update_button_count (virtual_evdev, evcode, key_state); + if (key_count < 0 || key_count > 1) + { + g_warning ("Received multiple virtual 0x%x key %s (ignoring)", keycode, + key_state == CLUTTER_KEY_STATE_PRESSED ? "presses" : "releases"); + update_button_count (virtual_evdev, evcode, 1 - key_state); + return; + } + + 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); + + if (!key_state) + apply_level_modifiers (virtual_device, time_us, level, key_state); +} + +static void +direction_to_discrete (ClutterScrollDirection direction, + double *discrete_dx, + double *discrete_dy) +{ + switch (direction) + { + case CLUTTER_SCROLL_UP: + *discrete_dx = 0.0; + *discrete_dy = -1.0; + break; + case CLUTTER_SCROLL_DOWN: + *discrete_dx = 0.0; + *discrete_dy = 1.0; + break; + case CLUTTER_SCROLL_LEFT: + *discrete_dx = -1.0; + *discrete_dy = 0.0; + break; + case CLUTTER_SCROLL_RIGHT: + *discrete_dx = 1.0; + *discrete_dy = 0.0; + break; + case CLUTTER_SCROLL_SMOOTH: + g_assert_not_reached (); + break; + } +} + +static void +meta_virtual_input_device_native_notify_discrete_scroll (ClutterVirtualInputDevice *virtual_device, + uint64_t time_us, + ClutterScrollDirection direction, + ClutterScrollSource scroll_source) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + double discrete_dx = 0.0, discrete_dy = 0.0; + + if (time_us == CLUTTER_CURRENT_TIME) + time_us = g_get_monotonic_time (); + + 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); +} + +static void +meta_virtual_input_device_native_notify_scroll_continuous (ClutterVirtualInputDevice *virtual_device, + uint64_t time_us, + double dx, + double dy, + ClutterScrollSource scroll_source, + ClutterScrollFinishFlags finish_flags) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + + 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); +} + +static void +meta_virtual_input_device_native_notify_touch_down (ClutterVirtualInputDevice *virtual_device, + uint64_t time_us, + int device_slot, + double x, + double y) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + MetaInputDeviceNative *device_evdev = + META_INPUT_DEVICE_NATIVE (virtual_evdev->device); + MetaTouchState *touch_state; + + if (time_us == CLUTTER_CURRENT_TIME) + time_us = g_get_monotonic_time (); + + touch_state = meta_input_device_native_acquire_touch_state (device_evdev, + 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); +} + +static void +meta_virtual_input_device_native_notify_touch_motion (ClutterVirtualInputDevice *virtual_device, + uint64_t time_us, + int device_slot, + double x, + double y) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + MetaInputDeviceNative *device_evdev = + META_INPUT_DEVICE_NATIVE (virtual_evdev->device); + MetaTouchState *touch_state; + + if (time_us == CLUTTER_CURRENT_TIME) + time_us = g_get_monotonic_time (); + + touch_state = meta_input_device_native_lookup_touch_state (device_evdev, + 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); +} + +static void +meta_virtual_input_device_native_notify_touch_up (ClutterVirtualInputDevice *virtual_device, + uint64_t time_us, + int device_slot) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); + MetaInputDeviceNative *device_evdev = + META_INPUT_DEVICE_NATIVE (virtual_evdev->device); + MetaTouchState *touch_state; + + if (time_us == CLUTTER_CURRENT_TIME) + time_us = g_get_monotonic_time (); + + touch_state = meta_input_device_native_lookup_touch_state (device_evdev, + 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_input_device_native_release_touch_state (device_evdev, touch_state); +} + +static void +meta_virtual_input_device_native_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (object); + + switch (prop_id) + { + case PROP_SEAT: + g_value_set_pointer (value, virtual_evdev->seat); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_virtual_input_device_native_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (object); + + switch (prop_id) + { + case PROP_SEAT: + virtual_evdev->seat = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_virtual_input_device_native_constructed (GObject *object) +{ + ClutterVirtualInputDevice *virtual_device = + CLUTTER_VIRTUAL_INPUT_DEVICE (object); + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (object); + ClutterDeviceManager *manager; + ClutterInputDeviceType device_type; + ClutterStage *stage; + + manager = clutter_virtual_input_device_get_manager (virtual_device); + device_type = clutter_virtual_input_device_get_device_type (virtual_device); + + virtual_evdev->device = + meta_input_device_native_new_virtual (manager, + virtual_evdev->seat, + device_type, + CLUTTER_INPUT_MODE_SLAVE); + + stage = meta_device_manager_native_get_stage (META_DEVICE_MANAGER_NATIVE (manager)); + _clutter_input_device_set_stage (virtual_evdev->device, stage); +} + +static void +meta_virtual_input_device_native_finalize (GObject *object) +{ + ClutterVirtualInputDevice *virtual_device = + CLUTTER_VIRTUAL_INPUT_DEVICE (object); + MetaVirtualInputDeviceNative *virtual_evdev = + META_VIRTUAL_INPUT_DEVICE_NATIVE (object); + GObjectClass *object_class; + + release_pressed_buttons (virtual_device); + g_clear_object (&virtual_evdev->device); + + object_class = + G_OBJECT_CLASS (meta_virtual_input_device_native_parent_class); + object_class->finalize (object); +} + +static void +meta_virtual_input_device_native_init (MetaVirtualInputDeviceNative *virtual_device_evdev) +{ +} + +static void +meta_virtual_input_device_native_class_init (MetaVirtualInputDeviceNativeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ClutterVirtualInputDeviceClass *virtual_input_device_class = + CLUTTER_VIRTUAL_INPUT_DEVICE_CLASS (klass); + + object_class->get_property = meta_virtual_input_device_native_get_property; + object_class->set_property = meta_virtual_input_device_native_set_property; + object_class->constructed = meta_virtual_input_device_native_constructed; + object_class->finalize = meta_virtual_input_device_native_finalize; + + virtual_input_device_class->notify_relative_motion = meta_virtual_input_device_native_notify_relative_motion; + virtual_input_device_class->notify_absolute_motion = meta_virtual_input_device_native_notify_absolute_motion; + virtual_input_device_class->notify_button = meta_virtual_input_device_native_notify_button; + virtual_input_device_class->notify_key = meta_virtual_input_device_native_notify_key; + virtual_input_device_class->notify_keyval = meta_virtual_input_device_native_notify_keyval; + virtual_input_device_class->notify_discrete_scroll = meta_virtual_input_device_native_notify_discrete_scroll; + virtual_input_device_class->notify_scroll_continuous = meta_virtual_input_device_native_notify_scroll_continuous; + virtual_input_device_class->notify_touch_down = meta_virtual_input_device_native_notify_touch_down; + virtual_input_device_class->notify_touch_motion = meta_virtual_input_device_native_notify_touch_motion; + virtual_input_device_class->notify_touch_up = meta_virtual_input_device_native_notify_touch_up; + + obj_props[PROP_SEAT] = g_param_spec_pointer ("seat", + P_("Seat"), + P_("Seat"), + CLUTTER_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_properties (object_class, PROP_LAST, obj_props); +} |