92a90774a4
Previously, when scroll was received in a remote session, it was handled
as continuous scroll.
This generated issues with clients without high-resolution scroll
support as the code path in charge of accumulating scroll until 120 is
reached was not used and therefore discrete scroll events were not being
generated.
Handle scroll generated in a remote session as discrete scroll when the
source is CLUTTER_SCROLL_SOURCE_WHEEL to fix this issue.
Fix https://gitlab.gnome.org/GNOME/mutter/-/issues/2473
Fixes: 9dd6268d13
("wayland/pointer: Send high-resolution scroll data")
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2664>
1125 lines
38 KiB
C
1125 lines
38 KiB
C
/*
|
|
* 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-thread.h"
|
|
#include "backends/native/meta-seat-native.h"
|
|
#include "backends/native/meta-virtual-input-device-native.h"
|
|
#include "clutter/clutter-mutter.h"
|
|
#include "meta/util.h"
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_SEAT,
|
|
PROP_SLOT_BASE,
|
|
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *obj_props[PROP_LAST];
|
|
|
|
typedef struct _ImplState ImplState;
|
|
|
|
struct _ImplState
|
|
{
|
|
ClutterInputDevice *device;
|
|
int button_count[KEY_CNT];
|
|
};
|
|
|
|
struct _MetaVirtualInputDeviceNative
|
|
{
|
|
ClutterVirtualInputDevice parent;
|
|
|
|
MetaSeatNative *seat;
|
|
guint slot_base;
|
|
ImplState *impl_state;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
uint64_t time_us;
|
|
double x;
|
|
double y;
|
|
} MetaVirtualEventMotion;
|
|
|
|
typedef struct
|
|
{
|
|
uint64_t time_us;
|
|
uint32_t button;
|
|
ClutterButtonState button_state;
|
|
} MetaVirtualEventButton;
|
|
|
|
typedef struct
|
|
{
|
|
uint64_t time_us;
|
|
double dx;
|
|
double dy;
|
|
ClutterScrollDirection direction;
|
|
ClutterScrollSource scroll_source;
|
|
ClutterScrollFinishFlags finish_flags;
|
|
} MetaVirtualEventScroll;
|
|
|
|
typedef struct
|
|
{
|
|
uint64_t time_us;
|
|
uint32_t key;
|
|
ClutterKeyState key_state;
|
|
} MetaVirtualEventKey;
|
|
|
|
typedef struct
|
|
{
|
|
uint64_t time_us;
|
|
int device_slot;
|
|
double x;
|
|
double y;
|
|
} MetaVirtualEventTouch;
|
|
|
|
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_in_impl (MetaVirtualInputDeviceNative *virtual_evdev,
|
|
uint32_t button,
|
|
uint32_t state)
|
|
{
|
|
if (state)
|
|
return ++virtual_evdev->impl_state->button_count[button];
|
|
else
|
|
return --virtual_evdev->impl_state->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 gboolean
|
|
release_device_in_impl (GTask *task)
|
|
{
|
|
ImplState *impl_state = g_task_get_task_data (task);
|
|
MetaInputDeviceNative *device_native;
|
|
MetaSeatImpl *seat_impl;
|
|
int code;
|
|
uint64_t time_us;
|
|
ClutterEvent *device_event;
|
|
|
|
device_native = META_INPUT_DEVICE_NATIVE (impl_state->device);
|
|
seat_impl = meta_input_device_native_get_seat_impl (device_native);
|
|
time_us = g_get_monotonic_time ();
|
|
|
|
meta_topic (META_DEBUG_INPUT,
|
|
"Releasing pressed buttons while destroying virtual input device "
|
|
"(device %p)", device_native);
|
|
|
|
for (code = 0; code < G_N_ELEMENTS (impl_state->button_count); code++)
|
|
{
|
|
if (impl_state->button_count[code] == 0)
|
|
continue;
|
|
|
|
switch (get_button_type (code))
|
|
{
|
|
case EVDEV_BUTTON_TYPE_KEY:
|
|
meta_seat_impl_notify_key_in_impl (seat_impl,
|
|
impl_state->device,
|
|
time_us,
|
|
code,
|
|
CLUTTER_KEY_STATE_RELEASED,
|
|
TRUE);
|
|
break;
|
|
case EVDEV_BUTTON_TYPE_BUTTON:
|
|
meta_seat_impl_notify_button_in_impl (seat_impl,
|
|
impl_state->device,
|
|
time_us,
|
|
code,
|
|
CLUTTER_BUTTON_STATE_RELEASED);
|
|
break;
|
|
case EVDEV_BUTTON_TYPE_NONE:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
device_event = clutter_event_new (CLUTTER_DEVICE_REMOVED);
|
|
clutter_event_set_device (device_event, impl_state->device);
|
|
_clutter_event_push (device_event, FALSE);
|
|
|
|
g_clear_object (&impl_state->device);
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static gboolean
|
|
notify_relative_motion_in_impl (GTask *task)
|
|
{
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
g_task_get_source_object (task);
|
|
MetaSeatImpl *seat = virtual_evdev->seat->impl;
|
|
MetaVirtualEventMotion *event = g_task_get_task_data (task);
|
|
|
|
if (event->time_us == CLUTTER_CURRENT_TIME)
|
|
event->time_us = g_get_monotonic_time ();
|
|
|
|
meta_seat_impl_notify_relative_motion_in_impl (seat,
|
|
virtual_evdev->impl_state->device,
|
|
event->time_us,
|
|
event->x, event->y,
|
|
event->x, event->y);
|
|
g_task_return_boolean (task, TRUE);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
meta_virtual_input_device_native_notify_relative_motion (ClutterVirtualInputDevice *virtual_device,
|
|
uint64_t time_us,
|
|
double dx,
|
|
double dy)
|
|
{
|
|
MetaVirtualEventMotion *event;
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device);
|
|
GTask *task;
|
|
|
|
g_return_if_fail (virtual_evdev->impl_state->device != NULL);
|
|
|
|
event = g_new0 (MetaVirtualEventMotion, 1);
|
|
event->time_us = time_us;
|
|
event->x = dx;
|
|
event->y = dy;
|
|
|
|
task = g_task_new (virtual_device, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, event, g_free);
|
|
meta_seat_impl_run_input_task (virtual_evdev->seat->impl, task,
|
|
(GSourceFunc) notify_relative_motion_in_impl);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gboolean
|
|
notify_absolute_motion_in_impl (GTask *task)
|
|
{
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
g_task_get_source_object (task);
|
|
MetaSeatImpl *seat = virtual_evdev->seat->impl;
|
|
MetaVirtualEventMotion *event = g_task_get_task_data (task);
|
|
|
|
if (event->time_us == CLUTTER_CURRENT_TIME)
|
|
event->time_us = g_get_monotonic_time ();
|
|
|
|
meta_seat_impl_notify_absolute_motion_in_impl (seat,
|
|
virtual_evdev->impl_state->device,
|
|
event->time_us,
|
|
event->x, event->y,
|
|
NULL);
|
|
g_task_return_boolean (task, TRUE);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
meta_virtual_input_device_native_notify_absolute_motion (ClutterVirtualInputDevice *virtual_device,
|
|
uint64_t time_us,
|
|
double x,
|
|
double y)
|
|
{
|
|
MetaVirtualEventMotion *event;
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device);
|
|
GTask *task;
|
|
|
|
g_return_if_fail (virtual_evdev->impl_state->device != NULL);
|
|
|
|
event = g_new0 (MetaVirtualEventMotion, 1);
|
|
event->time_us = time_us;
|
|
event->x = x;
|
|
event->y = y;
|
|
|
|
task = g_task_new (virtual_device, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, event, g_free);
|
|
meta_seat_impl_run_input_task (virtual_evdev->seat->impl, task,
|
|
(GSourceFunc) notify_absolute_motion_in_impl);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
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 gboolean
|
|
notify_button_in_impl (GTask *task)
|
|
{
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
g_task_get_source_object (task);
|
|
MetaSeatImpl *seat = virtual_evdev->seat->impl;
|
|
MetaVirtualEventButton *event = g_task_get_task_data (task);
|
|
int button_count;
|
|
int evdev_button;
|
|
|
|
if (event->time_us == CLUTTER_CURRENT_TIME)
|
|
event->time_us = g_get_monotonic_time ();
|
|
|
|
evdev_button = translate_to_evdev_button (event->button);
|
|
|
|
if (get_button_type (evdev_button) != EVDEV_BUTTON_TYPE_BUTTON)
|
|
{
|
|
g_warning ("Unknown/invalid virtual device button 0x%x pressed",
|
|
evdev_button);
|
|
goto out;
|
|
}
|
|
|
|
button_count = update_button_count_in_impl (virtual_evdev, evdev_button,
|
|
event->button_state);
|
|
if (button_count < 0 || button_count > 1)
|
|
{
|
|
g_warning ("Received multiple virtual 0x%x button %s (ignoring)", evdev_button,
|
|
event->button_state == CLUTTER_BUTTON_STATE_PRESSED ?
|
|
"presses" : "releases");
|
|
update_button_count_in_impl (virtual_evdev, evdev_button, 1 - event->button_state);
|
|
goto out;
|
|
}
|
|
|
|
meta_topic (META_DEBUG_INPUT,
|
|
"Emitting virtual button-%s of button 0x%x (device %p)",
|
|
event->button_state == CLUTTER_BUTTON_STATE_PRESSED ?
|
|
"press" : "release",
|
|
evdev_button, virtual_evdev);
|
|
|
|
meta_seat_impl_notify_button_in_impl (seat,
|
|
virtual_evdev->impl_state->device,
|
|
event->time_us,
|
|
evdev_button,
|
|
event->button_state);
|
|
out:
|
|
g_task_return_boolean (task, TRUE);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
meta_virtual_input_device_native_notify_button (ClutterVirtualInputDevice *virtual_device,
|
|
uint64_t time_us,
|
|
uint32_t button,
|
|
ClutterButtonState button_state)
|
|
{
|
|
MetaVirtualEventButton *event;
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device);
|
|
GTask *task;
|
|
|
|
g_return_if_fail (virtual_evdev->impl_state->device != NULL);
|
|
|
|
event = g_new0 (MetaVirtualEventButton, 1);
|
|
event->time_us = time_us;
|
|
event->button = button;
|
|
event->button_state = button_state;
|
|
|
|
task = g_task_new (virtual_device, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, event, g_free);
|
|
meta_seat_impl_run_input_task (virtual_evdev->seat->impl, task,
|
|
(GSourceFunc) notify_button_in_impl);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gboolean
|
|
notify_key_in_impl (GTask *task)
|
|
{
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
g_task_get_source_object (task);
|
|
MetaSeatImpl *seat = virtual_evdev->seat->impl;
|
|
MetaVirtualEventKey *event = g_task_get_task_data (task);
|
|
int key_count;
|
|
|
|
if (event->time_us == CLUTTER_CURRENT_TIME)
|
|
event->time_us = g_get_monotonic_time ();
|
|
|
|
if (get_button_type (event->key) != EVDEV_BUTTON_TYPE_KEY)
|
|
{
|
|
g_warning ("Unknown/invalid virtual device key 0x%x pressed", event->key);
|
|
goto out;
|
|
}
|
|
|
|
key_count = update_button_count_in_impl (virtual_evdev, event->key, event->key_state);
|
|
if (key_count < 0 || key_count > 1)
|
|
{
|
|
g_warning ("Received multiple virtual 0x%x key %s (ignoring)", event->key,
|
|
event->key_state == CLUTTER_KEY_STATE_PRESSED ?
|
|
"presses" : "releases");
|
|
update_button_count_in_impl (virtual_evdev, event->key, 1 - event->key_state);
|
|
goto out;
|
|
}
|
|
|
|
meta_topic (META_DEBUG_INPUT,
|
|
"Emitting virtual key-%s of key 0x%x (device %p)",
|
|
event->key_state == CLUTTER_KEY_STATE_PRESSED ? "press" : "release",
|
|
event->key, virtual_evdev);
|
|
|
|
meta_seat_impl_notify_key_in_impl (seat,
|
|
virtual_evdev->impl_state->device,
|
|
event->time_us,
|
|
event->key,
|
|
event->key_state,
|
|
TRUE);
|
|
|
|
out:
|
|
g_task_return_boolean (task, TRUE);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
meta_virtual_input_device_native_notify_key (ClutterVirtualInputDevice *virtual_device,
|
|
uint64_t time_us,
|
|
uint32_t key,
|
|
ClutterKeyState key_state)
|
|
{
|
|
MetaVirtualEventKey *event;
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device);
|
|
GTask *task;
|
|
|
|
g_return_if_fail (virtual_evdev->impl_state->device != NULL);
|
|
|
|
event = g_new0 (MetaVirtualEventKey, 1);
|
|
event->time_us = time_us;
|
|
event->key = key;
|
|
event->key_state = key_state;
|
|
|
|
task = g_task_new (virtual_device, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, event, g_free);
|
|
meta_seat_impl_run_input_task (virtual_evdev->seat->impl, task,
|
|
(GSourceFunc) notify_key_in_impl);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gboolean
|
|
pick_keycode_for_keyval_in_current_group_in_impl (ClutterVirtualInputDevice *virtual_device,
|
|
guint keyval,
|
|
guint *keycode_out,
|
|
guint *level_out)
|
|
{
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device);
|
|
ClutterBackend *backend;
|
|
ClutterKeymap *keymap;
|
|
struct xkb_keymap *xkb_keymap;
|
|
struct xkb_state *state;
|
|
guint keycode, layout;
|
|
xkb_keycode_t min_keycode, max_keycode;
|
|
|
|
backend = clutter_get_default_backend ();
|
|
keymap = clutter_seat_get_keymap (clutter_backend_get_default_seat (backend));
|
|
xkb_keymap = meta_keymap_native_get_keyboard_map_in_impl (META_KEYMAP_NATIVE (keymap));
|
|
state = meta_seat_impl_get_xkb_state_in_impl (virtual_evdev->seat->impl);
|
|
|
|
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_in_impl (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", level);
|
|
return;
|
|
}
|
|
|
|
if (!pick_keycode_for_keyval_in_current_group_in_impl (virtual_device, keysym,
|
|
&keycode, NULL))
|
|
return;
|
|
|
|
evcode = meta_xkb_keycode_to_evdev (keycode);
|
|
|
|
meta_topic (META_DEBUG_INPUT,
|
|
"Emitting virtual key-%s of modifier key 0x%x (device %p)",
|
|
key_state == CLUTTER_KEY_STATE_PRESSED ? "press" : "release",
|
|
evcode, virtual_device);
|
|
|
|
meta_seat_impl_notify_key_in_impl (virtual_evdev->seat->impl,
|
|
virtual_evdev->impl_state->device,
|
|
time_us,
|
|
evcode,
|
|
key_state,
|
|
TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
notify_keyval_in_impl (GTask *task)
|
|
{
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
g_task_get_source_object (task);
|
|
ClutterVirtualInputDevice *virtual_device =
|
|
CLUTTER_VIRTUAL_INPUT_DEVICE (virtual_evdev);
|
|
MetaSeatImpl *seat = virtual_evdev->seat->impl;
|
|
MetaVirtualEventKey *event = g_task_get_task_data (task);
|
|
int key_count;
|
|
guint keycode = 0, level = 0, evcode = 0;
|
|
|
|
if (event->time_us == CLUTTER_CURRENT_TIME)
|
|
event->time_us = g_get_monotonic_time ();
|
|
|
|
if (!pick_keycode_for_keyval_in_current_group_in_impl (virtual_device,
|
|
event->key,
|
|
&keycode, &level))
|
|
{
|
|
g_warning ("No keycode found for keyval %x in current group", event->key);
|
|
goto out;
|
|
}
|
|
|
|
evcode = meta_xkb_keycode_to_evdev (keycode);
|
|
|
|
if (get_button_type (evcode) != EVDEV_BUTTON_TYPE_KEY)
|
|
{
|
|
g_warning ("Unknown/invalid virtual device key 0x%x pressed", evcode);
|
|
goto out;
|
|
}
|
|
|
|
key_count = update_button_count_in_impl (virtual_evdev, evcode, event->key_state);
|
|
if (key_count < 0 || key_count > 1)
|
|
{
|
|
g_warning ("Received multiple virtual 0x%x key %s (ignoring)", evcode,
|
|
event->key_state == CLUTTER_KEY_STATE_PRESSED ?
|
|
"presses" : "releases");
|
|
update_button_count_in_impl (virtual_evdev, evcode, 1 - event->key_state);
|
|
goto out;
|
|
}
|
|
|
|
meta_topic (META_DEBUG_INPUT,
|
|
"Emitting virtual key-%s of key 0x%x with modifier level %d, "
|
|
"press count %d (device %p)",
|
|
event->key_state == CLUTTER_KEY_STATE_PRESSED ? "press" : "release",
|
|
evcode, level, key_count, virtual_evdev);
|
|
|
|
if (event->key_state)
|
|
{
|
|
apply_level_modifiers_in_impl (virtual_device, event->time_us,
|
|
level, event->key_state);
|
|
}
|
|
|
|
meta_seat_impl_notify_key_in_impl (seat,
|
|
virtual_evdev->impl_state->device,
|
|
event->time_us,
|
|
evcode,
|
|
event->key_state,
|
|
TRUE);
|
|
|
|
if (!event->key_state)
|
|
{
|
|
apply_level_modifiers_in_impl (virtual_device, event->time_us,
|
|
level, event->key_state);
|
|
}
|
|
|
|
out:
|
|
g_task_return_boolean (task, TRUE);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
meta_virtual_input_device_native_notify_keyval (ClutterVirtualInputDevice *virtual_device,
|
|
uint64_t time_us,
|
|
uint32_t keyval,
|
|
ClutterKeyState key_state)
|
|
{
|
|
MetaVirtualEventKey *event;
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device);
|
|
GTask *task;
|
|
|
|
g_return_if_fail (virtual_evdev->impl_state->device != NULL);
|
|
|
|
event = g_new0 (MetaVirtualEventKey, 1);
|
|
event->time_us = time_us;
|
|
event->key = keyval;
|
|
event->key_state = key_state;
|
|
|
|
task = g_task_new (virtual_device, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, event, g_free);
|
|
meta_seat_impl_run_input_task (virtual_evdev->seat->impl, task,
|
|
(GSourceFunc) notify_keyval_in_impl);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
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 gboolean
|
|
notify_discrete_scroll_in_impl (GTask *task)
|
|
{
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
g_task_get_source_object (task);
|
|
MetaSeatImpl *seat = virtual_evdev->seat->impl;
|
|
MetaVirtualEventScroll *event = g_task_get_task_data (task);
|
|
double discrete_dx = 0.0, discrete_dy = 0.0;
|
|
|
|
if (event->time_us == CLUTTER_CURRENT_TIME)
|
|
event->time_us = g_get_monotonic_time ();
|
|
|
|
direction_to_discrete (event->direction, &discrete_dx, &discrete_dy);
|
|
|
|
meta_seat_impl_notify_discrete_scroll_in_impl (seat,
|
|
virtual_evdev->impl_state->device,
|
|
event->time_us,
|
|
discrete_dx * 120.0,
|
|
discrete_dy * 120.0,
|
|
event->scroll_source);
|
|
|
|
g_task_return_boolean (task, TRUE);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
meta_virtual_input_device_native_notify_discrete_scroll (ClutterVirtualInputDevice *virtual_device,
|
|
uint64_t time_us,
|
|
ClutterScrollDirection direction,
|
|
ClutterScrollSource scroll_source)
|
|
{
|
|
MetaVirtualEventScroll *event;
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device);
|
|
GTask *task;
|
|
|
|
g_return_if_fail (virtual_evdev->impl_state->device != NULL);
|
|
|
|
event = g_new0 (MetaVirtualEventScroll, 1);
|
|
event->time_us = time_us;
|
|
event->direction = direction;
|
|
event->scroll_source = scroll_source;
|
|
|
|
task = g_task_new (virtual_device, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, event, g_free);
|
|
meta_seat_impl_run_input_task (virtual_evdev->seat->impl, task,
|
|
(GSourceFunc) notify_discrete_scroll_in_impl);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gboolean
|
|
notify_scroll_continuous_in_impl (GTask *task)
|
|
{
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
g_task_get_source_object (task);
|
|
MetaSeatImpl *seat = virtual_evdev->seat->impl;
|
|
MetaVirtualEventScroll *event = g_task_get_task_data (task);
|
|
|
|
if (event->time_us == CLUTTER_CURRENT_TIME)
|
|
event->time_us = g_get_monotonic_time ();
|
|
|
|
if (event->scroll_source == CLUTTER_SCROLL_SOURCE_WHEEL)
|
|
{
|
|
meta_seat_impl_notify_discrete_scroll_in_impl (seat,
|
|
virtual_evdev->impl_state->device,
|
|
event->time_us,
|
|
event->dx * (120.0 / 10.0),
|
|
event->dy * (120.0 / 10.0),
|
|
event->scroll_source);
|
|
}
|
|
else
|
|
{
|
|
meta_seat_impl_notify_scroll_continuous_in_impl (seat,
|
|
virtual_evdev->impl_state->device,
|
|
event->time_us,
|
|
event->dx, event->dy,
|
|
event->scroll_source,
|
|
CLUTTER_SCROLL_FINISHED_NONE);
|
|
}
|
|
|
|
g_task_return_boolean (task, TRUE);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
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)
|
|
{
|
|
MetaVirtualEventScroll *event;
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device);
|
|
GTask *task;
|
|
|
|
g_return_if_fail (virtual_evdev->impl_state->device != NULL);
|
|
|
|
event = g_new0 (MetaVirtualEventScroll, 1);
|
|
event->time_us = time_us;
|
|
event->dx = dx;
|
|
event->dy = dy;
|
|
event->scroll_source = scroll_source;
|
|
event->finish_flags = finish_flags;
|
|
|
|
task = g_task_new (virtual_device, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, event, g_free);
|
|
meta_seat_impl_run_input_task (virtual_evdev->seat->impl, task,
|
|
(GSourceFunc) notify_scroll_continuous_in_impl);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gboolean
|
|
notify_touch_down_in_impl (GTask *task)
|
|
{
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
g_task_get_source_object (task);
|
|
MetaSeatImpl *seat = virtual_evdev->seat->impl;
|
|
MetaVirtualEventTouch *event = g_task_get_task_data (task);
|
|
MetaTouchState *touch_state;
|
|
|
|
if (event->time_us == CLUTTER_CURRENT_TIME)
|
|
event->time_us = g_get_monotonic_time ();
|
|
|
|
touch_state = meta_seat_impl_acquire_touch_state_in_impl (seat,
|
|
event->device_slot);
|
|
if (!touch_state)
|
|
goto out;
|
|
|
|
touch_state->coords.x = event->x;
|
|
touch_state->coords.y = event->y;
|
|
|
|
meta_seat_impl_notify_touch_event_in_impl (seat,
|
|
virtual_evdev->impl_state->device,
|
|
CLUTTER_TOUCH_BEGIN,
|
|
event->time_us,
|
|
touch_state->seat_slot,
|
|
touch_state->coords.x,
|
|
touch_state->coords.y);
|
|
|
|
out:
|
|
g_task_return_boolean (task, TRUE);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
meta_virtual_input_device_native_notify_touch_down (ClutterVirtualInputDevice *virtual_device,
|
|
uint64_t time_us,
|
|
int device_slot,
|
|
double x,
|
|
double y)
|
|
{
|
|
MetaVirtualEventTouch *event;
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device);
|
|
GTask *task;
|
|
|
|
g_return_if_fail (virtual_evdev->impl_state->device != NULL);
|
|
|
|
event = g_new0 (MetaVirtualEventTouch, 1);
|
|
event->time_us = time_us;
|
|
event->device_slot = virtual_evdev->slot_base + (guint) device_slot;
|
|
event->x = x;
|
|
event->y = y;
|
|
|
|
task = g_task_new (virtual_device, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, event, g_free);
|
|
meta_seat_impl_run_input_task (virtual_evdev->seat->impl, task,
|
|
(GSourceFunc) notify_touch_down_in_impl);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gboolean
|
|
notify_touch_motion_in_impl (GTask *task)
|
|
{
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
g_task_get_source_object (task);
|
|
MetaSeatImpl *seat = virtual_evdev->seat->impl;
|
|
MetaVirtualEventTouch *event = g_task_get_task_data (task);
|
|
MetaTouchState *touch_state;
|
|
|
|
if (event->time_us == CLUTTER_CURRENT_TIME)
|
|
event->time_us = g_get_monotonic_time ();
|
|
|
|
touch_state = meta_seat_impl_lookup_touch_state_in_impl (seat,
|
|
event->device_slot);
|
|
if (!touch_state)
|
|
goto out;
|
|
|
|
touch_state->coords.x = event->x;
|
|
touch_state->coords.y = event->y;
|
|
|
|
meta_seat_impl_notify_touch_event_in_impl (seat,
|
|
virtual_evdev->impl_state->device,
|
|
CLUTTER_TOUCH_UPDATE,
|
|
event->time_us,
|
|
touch_state->seat_slot,
|
|
touch_state->coords.x,
|
|
touch_state->coords.y);
|
|
|
|
out:
|
|
g_task_return_boolean (task, TRUE);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
meta_virtual_input_device_native_notify_touch_motion (ClutterVirtualInputDevice *virtual_device,
|
|
uint64_t time_us,
|
|
int device_slot,
|
|
double x,
|
|
double y)
|
|
{
|
|
MetaVirtualEventTouch *event;
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device);
|
|
GTask *task;
|
|
|
|
g_return_if_fail (virtual_evdev->impl_state->device != NULL);
|
|
|
|
event = g_new0 (MetaVirtualEventTouch, 1);
|
|
event->time_us = time_us;
|
|
event->device_slot = virtual_evdev->slot_base + (guint) device_slot;
|
|
event->x = x;
|
|
event->y = y;
|
|
|
|
task = g_task_new (virtual_device, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, event, g_free);
|
|
meta_seat_impl_run_input_task (virtual_evdev->seat->impl, task,
|
|
(GSourceFunc) notify_touch_motion_in_impl);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gboolean
|
|
notify_touch_up_in_impl (GTask *task)
|
|
{
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
g_task_get_source_object (task);
|
|
MetaSeatImpl *seat = virtual_evdev->seat->impl;
|
|
MetaVirtualEventTouch *event = g_task_get_task_data (task);
|
|
MetaTouchState *touch_state;
|
|
|
|
if (event->time_us == CLUTTER_CURRENT_TIME)
|
|
event->time_us = g_get_monotonic_time ();
|
|
|
|
touch_state = meta_seat_impl_lookup_touch_state_in_impl (seat,
|
|
event->device_slot);
|
|
if (!touch_state)
|
|
goto out;
|
|
|
|
meta_seat_impl_notify_touch_event_in_impl (seat,
|
|
virtual_evdev->impl_state->device,
|
|
CLUTTER_TOUCH_END,
|
|
event->time_us,
|
|
touch_state->seat_slot,
|
|
touch_state->coords.x,
|
|
touch_state->coords.y);
|
|
|
|
meta_seat_impl_release_touch_state_in_impl (virtual_evdev->seat->impl,
|
|
touch_state->seat_slot);
|
|
|
|
out:
|
|
g_task_return_boolean (task, TRUE);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
meta_virtual_input_device_native_notify_touch_up (ClutterVirtualInputDevice *virtual_device,
|
|
uint64_t time_us,
|
|
int device_slot)
|
|
{
|
|
MetaVirtualEventTouch *event;
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device);
|
|
GTask *task;
|
|
|
|
g_return_if_fail (virtual_evdev->impl_state->device != NULL);
|
|
|
|
event = g_new0 (MetaVirtualEventTouch, 1);
|
|
event->time_us = time_us;
|
|
event->device_slot = virtual_evdev->slot_base + (guint) device_slot;
|
|
|
|
task = g_task_new (virtual_device, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, event, g_free);
|
|
meta_seat_impl_run_input_task (virtual_evdev->seat->impl, task,
|
|
(GSourceFunc) notify_touch_up_in_impl);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
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;
|
|
case PROP_SLOT_BASE:
|
|
g_value_set_uint (value, virtual_evdev->slot_base);
|
|
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;
|
|
case PROP_SLOT_BASE:
|
|
virtual_evdev->slot_base = g_value_get_uint (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);
|
|
ClutterInputDeviceType device_type;
|
|
ClutterEvent *device_event = NULL;
|
|
|
|
device_type = clutter_virtual_input_device_get_device_type (virtual_device);
|
|
|
|
meta_topic (META_DEBUG_INPUT,
|
|
"Creating new virtual input device of type %d (%p)",
|
|
device_type, virtual_device);
|
|
|
|
virtual_evdev->impl_state = g_new0 (ImplState, 1);
|
|
virtual_evdev->impl_state->device =
|
|
meta_input_device_native_new_virtual (virtual_evdev->seat->impl,
|
|
device_type,
|
|
CLUTTER_INPUT_MODE_PHYSICAL);
|
|
|
|
device_event = clutter_event_new (CLUTTER_DEVICE_ADDED);
|
|
clutter_event_set_device (device_event, virtual_evdev->impl_state->device);
|
|
_clutter_event_push (device_event, FALSE);
|
|
}
|
|
|
|
static void
|
|
impl_state_free (ImplState *impl_state)
|
|
{
|
|
g_warn_if_fail (!impl_state->device);
|
|
g_free (impl_state);
|
|
}
|
|
|
|
static void
|
|
meta_virtual_input_device_native_dispose (GObject *object)
|
|
{
|
|
ClutterVirtualInputDevice *virtual_device =
|
|
CLUTTER_VIRTUAL_INPUT_DEVICE (object);
|
|
MetaVirtualInputDeviceNative *virtual_evdev =
|
|
META_VIRTUAL_INPUT_DEVICE_NATIVE (object);
|
|
GObjectClass *object_class =
|
|
G_OBJECT_CLASS (meta_virtual_input_device_native_parent_class);
|
|
|
|
if (virtual_evdev->impl_state)
|
|
{
|
|
GTask *task;
|
|
|
|
task = g_task_new (virtual_device, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, virtual_evdev->impl_state,
|
|
(GDestroyNotify) impl_state_free);
|
|
meta_seat_impl_run_input_task (virtual_evdev->seat->impl, task,
|
|
(GSourceFunc) release_device_in_impl);
|
|
g_object_unref (task);
|
|
|
|
virtual_evdev->impl_state = NULL;
|
|
}
|
|
|
|
meta_seat_native_release_touch_slots (virtual_evdev->seat,
|
|
virtual_evdev->slot_base);
|
|
|
|
object_class->dispose (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->dispose = meta_virtual_input_device_native_dispose;
|
|
|
|
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",
|
|
"Seat",
|
|
"Seat",
|
|
CLUTTER_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY);
|
|
obj_props[PROP_SLOT_BASE] = g_param_spec_uint ("slot-base",
|
|
"Slot base",
|
|
"Base for touch slots",
|
|
0, G_MAXUINT, 0,
|
|
CLUTTER_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY);
|
|
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
|
|
}
|