mirror of
https://github.com/brl/mutter.git
synced 2024-11-28 02:50:41 -05:00
70e44c281c
It's slightly racy to have the main thread update the views and warp the pointer, since the input thread may not be aware yet of the new viewport layout. Make the input thread clamp the pointer so it remains onscreen instead, when the new viewports are obtained. Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/64 Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2237>
3659 lines
120 KiB
C
3659 lines
120 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author: Damien Lespiau <damien.lespiau@intel.com>
|
|
* Author: Jonas Ådahl <jadahl@gmail.com>
|
|
* Author: Carlos Garnacho <carlosg@gnome.org>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <libinput.h>
|
|
#include <linux/input.h>
|
|
#include <math.h>
|
|
|
|
#include "backends/meta-cursor-tracker-private.h"
|
|
#include "backends/native/meta-backend-native-private.h"
|
|
#include "backends/native/meta-barrier-native.h"
|
|
#include "backends/native/meta-device-pool.h"
|
|
#include "backends/native/meta-input-thread.h"
|
|
#include "backends/native/meta-virtual-input-device-native.h"
|
|
#include "clutter/clutter-mutter.h"
|
|
#include "core/bell.h"
|
|
|
|
#include "meta-private-enum-types.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;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_SEAT,
|
|
PROP_SEAT_ID,
|
|
PROP_FLAGS,
|
|
N_PROPS,
|
|
};
|
|
|
|
static GParamSpec *props[N_PROPS] = { NULL };
|
|
|
|
enum
|
|
{
|
|
KBD_A11Y_FLAGS_CHANGED,
|
|
KBD_A11Y_MODS_STATE_CHANGED,
|
|
TOUCH_MODE,
|
|
BELL,
|
|
MODS_STATE_CHANGED,
|
|
N_SIGNALS
|
|
};
|
|
|
|
static guint signals[N_SIGNALS] = { 0 };
|
|
|
|
typedef struct _MetaSeatImplPrivate
|
|
{
|
|
GHashTable *device_files;
|
|
} MetaSeatImplPrivate;
|
|
|
|
static void meta_seat_impl_initable_iface_init (GInitableIface *iface);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (MetaSeatImpl, meta_seat_impl, G_TYPE_OBJECT,
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
|
|
meta_seat_impl_initable_iface_init)
|
|
G_ADD_PRIVATE (MetaSeatImpl))
|
|
|
|
static void process_events (MetaSeatImpl *seat_impl);
|
|
void meta_seat_impl_constrain_pointer (MetaSeatImpl *seat_impl,
|
|
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_impl,
|
|
ClutterInputDevice *device,
|
|
float x,
|
|
float y,
|
|
float *dx,
|
|
float *dy);
|
|
void meta_seat_impl_clear_repeat_source (MetaSeatImpl *seat_impl);
|
|
|
|
void
|
|
meta_seat_impl_run_input_task (MetaSeatImpl *seat_impl,
|
|
GTask *task,
|
|
GSourceFunc dispatch_func)
|
|
{
|
|
GSource *source;
|
|
|
|
source = g_idle_source_new ();
|
|
g_source_set_priority (source, G_PRIORITY_HIGH);
|
|
g_source_set_callback (source,
|
|
dispatch_func,
|
|
g_object_ref (task),
|
|
g_object_unref);
|
|
g_source_attach (source, seat_impl->input_context);
|
|
g_source_unref (source);
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_sync_leds_in_impl (MetaSeatImpl *seat_impl)
|
|
{
|
|
GSList *iter;
|
|
MetaInputDeviceNative *device_native;
|
|
int caps_lock, num_lock, scroll_lock;
|
|
enum libinput_led leds = 0;
|
|
|
|
caps_lock = xkb_state_led_index_is_active (seat_impl->xkb,
|
|
seat_impl->caps_lock_led);
|
|
num_lock = xkb_state_led_index_is_active (seat_impl->xkb,
|
|
seat_impl->num_lock_led);
|
|
scroll_lock = xkb_state_led_index_is_active (seat_impl->xkb,
|
|
seat_impl->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_impl->devices; iter; iter = iter->next)
|
|
{
|
|
device_native = iter->data;
|
|
meta_input_device_native_update_leds_in_impl (device_native, leds);
|
|
}
|
|
}
|
|
|
|
MetaTouchState *
|
|
meta_seat_impl_lookup_touch_state_in_impl (MetaSeatImpl *seat_impl,
|
|
int seat_slot)
|
|
{
|
|
if (!seat_impl->touch_states)
|
|
return NULL;
|
|
|
|
return g_hash_table_lookup (seat_impl->touch_states,
|
|
GINT_TO_POINTER (seat_slot));
|
|
}
|
|
|
|
static void
|
|
meta_touch_state_free (MetaTouchState *state)
|
|
{
|
|
g_free (state);
|
|
}
|
|
|
|
MetaTouchState *
|
|
meta_seat_impl_acquire_touch_state_in_impl (MetaSeatImpl *seat_impl,
|
|
int seat_slot)
|
|
{
|
|
MetaTouchState *touch_state;
|
|
|
|
if (!seat_impl->touch_states)
|
|
{
|
|
seat_impl->touch_states =
|
|
g_hash_table_new_full (NULL, NULL, NULL,
|
|
(GDestroyNotify) meta_touch_state_free);
|
|
}
|
|
|
|
g_assert (!g_hash_table_contains (seat_impl->touch_states,
|
|
GINT_TO_POINTER (seat_slot)));
|
|
|
|
touch_state = g_new0 (MetaTouchState, 1);
|
|
*touch_state = (MetaTouchState) {
|
|
.seat_impl = seat_impl,
|
|
.seat_slot = seat_slot,
|
|
};
|
|
|
|
g_hash_table_insert (seat_impl->touch_states, GINT_TO_POINTER (seat_slot),
|
|
touch_state);
|
|
|
|
return touch_state;
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_release_touch_state_in_impl (MetaSeatImpl *seat_impl,
|
|
int seat_slot)
|
|
{
|
|
if (!seat_impl->touch_states)
|
|
return;
|
|
g_hash_table_remove (seat_impl->touch_states, GINT_TO_POINTER (seat_slot));
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_clear_repeat_source (MetaSeatImpl *seat_impl)
|
|
{
|
|
if (seat_impl->repeat_source)
|
|
{
|
|
g_source_destroy (seat_impl->repeat_source);
|
|
g_clear_pointer (&seat_impl->repeat_source, g_source_unref);
|
|
}
|
|
|
|
g_clear_object (&seat_impl->repeat_device);
|
|
}
|
|
|
|
static void
|
|
dispatch_libinput (MetaSeatImpl *seat_impl)
|
|
{
|
|
libinput_dispatch (seat_impl->libinput);
|
|
process_events (seat_impl);
|
|
}
|
|
|
|
static gboolean
|
|
keyboard_repeat (gpointer data)
|
|
{
|
|
MetaSeatImpl *seat_impl = data;
|
|
|
|
/* There might be events queued in libinput that could cancel the
|
|
repeat timer. */
|
|
if (seat_impl->libinput)
|
|
{
|
|
dispatch_libinput (seat_impl);
|
|
if (!seat_impl->repeat_source)
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
g_return_val_if_fail (seat_impl->repeat_device != NULL, G_SOURCE_REMOVE);
|
|
|
|
meta_seat_impl_notify_key_in_impl (seat_impl,
|
|
seat_impl->repeat_device,
|
|
g_source_get_time (seat_impl->repeat_source),
|
|
seat_impl->repeat_key,
|
|
AUTOREPEAT_VALUE,
|
|
FALSE);
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static void
|
|
queue_event (MetaSeatImpl *seat_impl,
|
|
ClutterEvent *event)
|
|
{
|
|
_clutter_event_push (event, FALSE);
|
|
}
|
|
|
|
static int
|
|
update_button_count (MetaSeatImpl *seat_impl,
|
|
uint32_t button,
|
|
uint32_t state)
|
|
{
|
|
if (state)
|
|
{
|
|
return ++seat_impl->button_count[button];
|
|
}
|
|
else
|
|
{
|
|
/* Handle cases where we newer saw the initial pressed event. */
|
|
if (seat_impl->button_count[button] == 0)
|
|
{
|
|
meta_topic (META_DEBUG_INPUT,
|
|
"Counting release of key 0x%x and count is already 0",
|
|
button);
|
|
return 0;
|
|
}
|
|
|
|
return --seat_impl->button_count[button];
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_queue_main_thread_idle (MetaSeatImpl *seat_impl,
|
|
GSourceFunc func,
|
|
gpointer user_data,
|
|
GDestroyNotify destroy_notify)
|
|
{
|
|
GSource *source;
|
|
|
|
source = g_idle_source_new ();
|
|
g_source_set_priority (source, G_PRIORITY_HIGH);
|
|
g_source_set_callback (source, func, user_data, destroy_notify);
|
|
|
|
g_source_attach (source, seat_impl->main_context);
|
|
g_source_unref (source);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
MetaSeatImpl *seat_impl;
|
|
guint signal_id;
|
|
GArray *args;
|
|
} MetaSeatSignalData;
|
|
|
|
static gboolean
|
|
emit_signal_in_main (MetaSeatSignalData *data)
|
|
{
|
|
g_signal_emitv ((GValue *) data->args->data,
|
|
data->signal_id,
|
|
0, NULL);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
signal_data_free (MetaSeatSignalData *data)
|
|
{
|
|
g_array_unref (data->args);
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
emit_signal (MetaSeatImpl *seat_impl,
|
|
guint signal_id,
|
|
GValue *args,
|
|
int n_args)
|
|
{
|
|
MetaSeatSignalData *emit_signal_data;
|
|
GArray *array;
|
|
GValue self = G_VALUE_INIT;
|
|
|
|
g_value_init (&self, META_TYPE_SEAT_IMPL);
|
|
g_value_set_object (&self, seat_impl);
|
|
|
|
array = g_array_new (FALSE, FALSE, sizeof (GValue));
|
|
g_array_append_val (array, self);
|
|
if (args && n_args > 0)
|
|
g_array_append_vals (array, args, n_args);
|
|
|
|
emit_signal_data = g_new0 (MetaSeatSignalData, 1);
|
|
emit_signal_data->seat_impl = seat_impl;
|
|
emit_signal_data->signal_id = signal_id;
|
|
emit_signal_data->args = array;
|
|
|
|
meta_seat_impl_queue_main_thread_idle (seat_impl,
|
|
(GSourceFunc) emit_signal_in_main,
|
|
emit_signal_data,
|
|
(GDestroyNotify) signal_data_free);
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_notify_key_in_impl (MetaSeatImpl *seat_impl,
|
|
ClutterInputDevice *device,
|
|
uint64_t time_us,
|
|
uint32_t key,
|
|
uint32_t state,
|
|
gboolean update_keys)
|
|
{
|
|
ClutterEvent *event = NULL;
|
|
enum xkb_state_component changed_state;
|
|
uint32_t keycode;
|
|
|
|
if (state != AUTOREPEAT_VALUE)
|
|
{
|
|
/* Drop any repeated button press (for example from virtual devices. */
|
|
int count = update_button_count (seat_impl, 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",
|
|
state ? "press" : "release", key, count, state);
|
|
return;
|
|
}
|
|
}
|
|
|
|
event = meta_key_event_new_from_evdev (device,
|
|
seat_impl->core_keyboard,
|
|
seat_impl->xkb,
|
|
seat_impl->button_state,
|
|
us2ms (time_us), key, state);
|
|
event->key.evdev_code = key;
|
|
|
|
keycode = meta_xkb_evdev_to_keycode (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_impl->xkb, keycode,
|
|
state ? XKB_KEY_DOWN : XKB_KEY_UP);
|
|
}
|
|
else
|
|
{
|
|
changed_state = 0;
|
|
clutter_event_set_flags (event, CLUTTER_EVENT_FLAG_REPEATED);
|
|
}
|
|
|
|
if (!meta_input_device_native_process_kbd_a11y_event_in_impl (seat_impl->core_keyboard,
|
|
event))
|
|
queue_event (seat_impl, event);
|
|
else
|
|
clutter_event_free (event);
|
|
|
|
if (update_keys && (changed_state & XKB_STATE_LEDS))
|
|
{
|
|
MetaInputDeviceNative *keyboard_native;
|
|
gboolean numlock_active;
|
|
|
|
meta_keymap_native_update_in_impl (seat_impl->keymap,
|
|
seat_impl,
|
|
seat_impl->xkb);
|
|
meta_seat_impl_sync_leds_in_impl (seat_impl);
|
|
|
|
numlock_active =
|
|
xkb_state_mod_name_is_active (seat_impl->xkb, XKB_MOD_NAME_NUM,
|
|
XKB_STATE_MODS_LATCHED |
|
|
XKB_STATE_MODS_LOCKED);
|
|
meta_input_settings_maybe_save_numlock_state (seat_impl->input_settings,
|
|
numlock_active);
|
|
|
|
keyboard_native = META_INPUT_DEVICE_NATIVE (seat_impl->core_keyboard);
|
|
meta_input_device_native_a11y_maybe_notify_toggle_keys_in_impl (keyboard_native);
|
|
}
|
|
|
|
if (state == 0 || /* key release */
|
|
!seat_impl->repeat ||
|
|
!xkb_keymap_key_repeats (xkb_state_get_keymap (seat_impl->xkb),
|
|
keycode))
|
|
{
|
|
seat_impl->repeat_count = 0;
|
|
meta_seat_impl_clear_repeat_source (seat_impl);
|
|
return;
|
|
}
|
|
|
|
if (state == 1) /* key press */
|
|
seat_impl->repeat_count = 0;
|
|
|
|
seat_impl->repeat_count += 1;
|
|
seat_impl->repeat_key = key;
|
|
|
|
switch (seat_impl->repeat_count)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
{
|
|
uint32_t interval;
|
|
|
|
meta_seat_impl_clear_repeat_source (seat_impl);
|
|
seat_impl->repeat_device = g_object_ref (device);
|
|
|
|
if (seat_impl->repeat_count == 1)
|
|
interval = seat_impl->repeat_delay;
|
|
else
|
|
interval = seat_impl->repeat_interval;
|
|
|
|
seat_impl->repeat_source = g_timeout_source_new (interval);
|
|
g_source_set_priority (seat_impl->repeat_source, CLUTTER_PRIORITY_EVENTS);
|
|
g_source_set_callback (seat_impl->repeat_source,
|
|
keyboard_repeat, seat_impl, NULL);
|
|
g_source_attach (seat_impl->repeat_source, seat_impl->input_context);
|
|
return;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
static ClutterEvent *
|
|
new_absolute_motion_event (MetaSeatImpl *seat_impl,
|
|
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_impl,
|
|
seat_impl->core_pointer,
|
|
time_us,
|
|
seat_impl->pointer_x,
|
|
seat_impl->pointer_y,
|
|
&x, &y);
|
|
}
|
|
else
|
|
{
|
|
/* This may happen early at startup */
|
|
if (seat_impl->viewports)
|
|
{
|
|
meta_input_device_native_translate_coordinates_in_impl (input_device,
|
|
seat_impl->viewports,
|
|
&x,
|
|
&y);
|
|
}
|
|
}
|
|
|
|
event->motion.time_us = time_us;
|
|
event->motion.time = us2ms (time_us);
|
|
meta_xkb_translate_state (event, seat_impl->xkb, seat_impl->button_state);
|
|
event->motion.x = x;
|
|
event->motion.y = y;
|
|
|
|
event->motion.axes = axes;
|
|
clutter_event_set_device (event, seat_impl->core_pointer);
|
|
clutter_event_set_source_device (event, input_device);
|
|
|
|
g_rw_lock_writer_lock (&seat_impl->state_lock);
|
|
|
|
if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE)
|
|
{
|
|
MetaInputDeviceNative *device_native =
|
|
META_INPUT_DEVICE_NATIVE (input_device);
|
|
|
|
clutter_event_set_device_tool (event, device_native->last_tool);
|
|
clutter_event_set_device (event, input_device);
|
|
meta_input_device_native_set_coords_in_impl (META_INPUT_DEVICE_NATIVE (input_device),
|
|
x, y);
|
|
}
|
|
else
|
|
{
|
|
clutter_event_set_device (event, seat_impl->core_pointer);
|
|
meta_input_device_native_set_coords_in_impl (META_INPUT_DEVICE_NATIVE (seat_impl->core_pointer),
|
|
x, y);
|
|
}
|
|
|
|
if (clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE)
|
|
{
|
|
seat_impl->pointer_x = x;
|
|
seat_impl->pointer_y = y;
|
|
}
|
|
|
|
g_rw_lock_writer_unlock (&seat_impl->state_lock);
|
|
|
|
return event;
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_notify_relative_motion_in_impl (MetaSeatImpl *seat_impl,
|
|
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_impl,
|
|
input_device,
|
|
seat_impl->pointer_x,
|
|
seat_impl->pointer_y,
|
|
&dx,
|
|
&dy);
|
|
|
|
new_x = seat_impl->pointer_x + dx;
|
|
new_y = seat_impl->pointer_y + dy;
|
|
event = new_absolute_motion_event (seat_impl, input_device,
|
|
time_us, new_x, new_y, NULL);
|
|
|
|
event->motion.flags |= CLUTTER_EVENT_FLAG_RELATIVE_MOTION;
|
|
event->motion.dx = dx;
|
|
event->motion.dy = dy;
|
|
event->motion.dx_unaccel = dx_unaccel;
|
|
event->motion.dy_unaccel = dy_unaccel;
|
|
|
|
queue_event (seat_impl, event);
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_notify_absolute_motion_in_impl (MetaSeatImpl *seat_impl,
|
|
ClutterInputDevice *input_device,
|
|
uint64_t time_us,
|
|
float x,
|
|
float y,
|
|
double *axes)
|
|
{
|
|
ClutterEvent *event;
|
|
|
|
event = new_absolute_motion_event (seat_impl, input_device, time_us, x, y, axes);
|
|
|
|
queue_event (seat_impl, event);
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_notify_button_in_impl (MetaSeatImpl *seat_impl,
|
|
ClutterInputDevice *input_device,
|
|
uint64_t time_us,
|
|
uint32_t button,
|
|
uint32_t state)
|
|
{
|
|
MetaInputDeviceNative *device_native = (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_impl, 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",
|
|
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_impl->button_state |= maskmap[button_nr - 1];
|
|
else
|
|
seat_impl->button_state &= ~maskmap[button_nr - 1];
|
|
}
|
|
|
|
event->button.time = us2ms (time_us);
|
|
meta_xkb_translate_state (event, seat_impl->xkb, seat_impl->button_state);
|
|
event->button.button = button_nr;
|
|
|
|
if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE)
|
|
{
|
|
meta_input_device_native_get_coords_in_impl (device_native,
|
|
&event->button.x,
|
|
&event->button.y);
|
|
}
|
|
else
|
|
{
|
|
meta_input_device_native_get_coords_in_impl (META_INPUT_DEVICE_NATIVE (seat_impl->core_pointer),
|
|
&event->button.x,
|
|
&event->button.y);
|
|
}
|
|
|
|
clutter_event_set_device (event, seat_impl->core_pointer);
|
|
clutter_event_set_source_device (event, input_device);
|
|
|
|
if (device_native->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_in_impl (device_native->last_tool,
|
|
button_nr);
|
|
if (mapped_button != 0)
|
|
button = mapped_button;
|
|
}
|
|
|
|
event->button.evdev_code = button;
|
|
|
|
if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE)
|
|
{
|
|
clutter_event_set_device_tool (event, device_native->last_tool);
|
|
clutter_event_set_device (event, input_device);
|
|
}
|
|
else
|
|
{
|
|
clutter_event_set_device (event, seat_impl->core_pointer);
|
|
}
|
|
|
|
queue_event (seat_impl, event);
|
|
}
|
|
|
|
static MetaSeatImpl *
|
|
seat_impl_from_device (ClutterInputDevice *device)
|
|
{
|
|
MetaInputDeviceNative *device_native = META_INPUT_DEVICE_NATIVE (device);
|
|
|
|
return meta_input_device_native_get_seat_impl (device_native);
|
|
}
|
|
|
|
static void
|
|
notify_scroll (ClutterInputDevice *input_device,
|
|
uint64_t time_us,
|
|
double dx,
|
|
double dy,
|
|
ClutterScrollSource scroll_source,
|
|
ClutterScrollFinishFlags flags,
|
|
gboolean emulated)
|
|
{
|
|
MetaSeatImpl *seat_impl;
|
|
ClutterEvent *event = NULL;
|
|
double scroll_factor;
|
|
|
|
seat_impl = seat_impl_from_device (input_device);
|
|
|
|
event = clutter_event_new (CLUTTER_SCROLL);
|
|
|
|
event->scroll.time = us2ms (time_us);
|
|
meta_xkb_translate_state (event, seat_impl->xkb, seat_impl->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_impl->pointer_x;
|
|
event->scroll.y = seat_impl->pointer_y;
|
|
clutter_event_set_device (event, seat_impl->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_impl, event);
|
|
}
|
|
|
|
static void
|
|
notify_discrete_scroll (ClutterInputDevice *input_device,
|
|
uint64_t time_us,
|
|
ClutterScrollDirection direction,
|
|
ClutterScrollSource scroll_source,
|
|
gboolean emulated)
|
|
{
|
|
MetaSeatImpl *seat_impl;
|
|
ClutterEvent *event = NULL;
|
|
|
|
if (direction == CLUTTER_SCROLL_SMOOTH)
|
|
return;
|
|
|
|
seat_impl = seat_impl_from_device (input_device);
|
|
|
|
event = clutter_event_new (CLUTTER_SCROLL);
|
|
|
|
event->scroll.time = us2ms (time_us);
|
|
meta_xkb_translate_state (event, seat_impl->xkb, seat_impl->button_state);
|
|
|
|
event->scroll.direction = direction;
|
|
|
|
event->scroll.x = seat_impl->pointer_x;
|
|
event->scroll.y = seat_impl->pointer_y;
|
|
clutter_event_set_device (event, seat_impl->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_impl, event);
|
|
}
|
|
|
|
static void
|
|
check_notify_discrete_scroll (MetaSeatImpl *seat_impl,
|
|
ClutterInputDevice *device,
|
|
uint64_t time_us,
|
|
ClutterScrollSource scroll_source)
|
|
{
|
|
int i, n_xscrolls, n_yscrolls;
|
|
|
|
n_xscrolls = floor ((fabs (seat_impl->accum_scroll_dx) + DBL_EPSILON) /
|
|
DISCRETE_SCROLL_STEP);
|
|
n_yscrolls = floor ((fabs (seat_impl->accum_scroll_dy) + DBL_EPSILON) /
|
|
DISCRETE_SCROLL_STEP);
|
|
|
|
for (i = 0; i < n_xscrolls; i++)
|
|
{
|
|
notify_discrete_scroll (device, time_us,
|
|
seat_impl->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_impl->accum_scroll_dy > 0 ?
|
|
CLUTTER_SCROLL_DOWN : CLUTTER_SCROLL_UP,
|
|
scroll_source, TRUE);
|
|
}
|
|
|
|
seat_impl->accum_scroll_dx =
|
|
fmodf (seat_impl->accum_scroll_dx, DISCRETE_SCROLL_STEP);
|
|
seat_impl->accum_scroll_dy =
|
|
fmodf (seat_impl->accum_scroll_dy, DISCRETE_SCROLL_STEP);
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_notify_scroll_continuous_in_impl (MetaSeatImpl *seat_impl,
|
|
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_impl->accum_scroll_dx = 0;
|
|
else
|
|
seat_impl->accum_scroll_dx += dx;
|
|
|
|
if (finish_flags & CLUTTER_SCROLL_FINISHED_VERTICAL)
|
|
seat_impl->accum_scroll_dy = 0;
|
|
else
|
|
seat_impl->accum_scroll_dy += dy;
|
|
|
|
notify_scroll (input_device, time_us, dx, dy, scroll_source,
|
|
finish_flags, FALSE);
|
|
check_notify_discrete_scroll (seat_impl, 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_in_impl (MetaSeatImpl *seat_impl,
|
|
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_in_impl (MetaSeatImpl *seat_impl,
|
|
ClutterInputDevice *input_device,
|
|
ClutterEventType evtype,
|
|
uint64_t time_us,
|
|
int slot,
|
|
double x,
|
|
double y)
|
|
{
|
|
ClutterEvent *event = NULL;
|
|
|
|
event = clutter_event_new (evtype);
|
|
|
|
event->touch.time = us2ms (time_us);
|
|
event->touch.x = x;
|
|
event->touch.y = y;
|
|
meta_input_device_native_translate_coordinates_in_impl (input_device,
|
|
seat_impl->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_impl->xkb, seat_impl->button_state);
|
|
|
|
if (evtype == CLUTTER_TOUCH_BEGIN ||
|
|
evtype == CLUTTER_TOUCH_UPDATE)
|
|
event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
|
|
|
|
clutter_event_set_device (event, seat_impl->core_pointer);
|
|
clutter_event_set_source_device (event, input_device);
|
|
|
|
queue_event (seat_impl, event);
|
|
}
|
|
|
|
/*
|
|
* MetaEventSource for reading input devices
|
|
*/
|
|
|
|
static gboolean
|
|
meta_event_prepare (GSource *g_source,
|
|
int *timeout_ms)
|
|
{
|
|
MetaEventSource *source = (MetaEventSource *) g_source;
|
|
MetaSeatImpl *seat_impl = source->seat_impl;
|
|
|
|
*timeout_ms = -1;
|
|
|
|
switch (libinput_next_event_type (seat_impl->libinput))
|
|
{
|
|
case LIBINPUT_EVENT_NONE:
|
|
return FALSE;
|
|
default:
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
meta_event_check (GSource *source)
|
|
{
|
|
MetaEventSource *event_source = (MetaEventSource *) source;
|
|
gboolean retval;
|
|
|
|
retval = !!(event_source->event_poll_fd.revents & G_IO_IN);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
constrain_to_barriers (MetaSeatImpl *seat_impl,
|
|
ClutterInputDevice *device,
|
|
uint32_t time,
|
|
float *new_x,
|
|
float *new_y)
|
|
{
|
|
meta_barrier_manager_native_process_in_impl (seat_impl->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)
|
|
{
|
|
float cx, cy;
|
|
int i, n_views;
|
|
|
|
meta_input_device_native_get_coords_in_impl (META_INPUT_DEVICE_NATIVE (device),
|
|
&cx, &cy);
|
|
|
|
/* 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_info (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_impl,
|
|
ClutterInputDevice *core_pointer,
|
|
uint64_t time_us,
|
|
float x,
|
|
float y,
|
|
float *new_x,
|
|
float *new_y)
|
|
{
|
|
/* Constrain to barriers */
|
|
constrain_to_barriers (seat_impl, core_pointer,
|
|
us2ms (time_us),
|
|
new_x, new_y);
|
|
|
|
/* Bar to constraints */
|
|
if (seat_impl->pointer_constraint)
|
|
{
|
|
meta_pointer_constraint_impl_constrain (seat_impl->pointer_constraint,
|
|
core_pointer,
|
|
us2ms (time_us),
|
|
x, y,
|
|
new_x, new_y);
|
|
}
|
|
|
|
if (seat_impl->viewports)
|
|
{
|
|
/* if we're moving inside a monitor, we're fine */
|
|
if (meta_viewport_info_get_view_at (seat_impl->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_impl->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_info (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_impl,
|
|
ClutterInputDevice *device,
|
|
float x,
|
|
float y,
|
|
float *dx,
|
|
float *dy)
|
|
{
|
|
int view, dest_view;
|
|
float new_dx, new_dy, scale;
|
|
|
|
if (!seat_impl->viewports)
|
|
return;
|
|
if (meta_viewport_info_is_views_scaled (seat_impl->viewports))
|
|
return;
|
|
|
|
view = meta_viewport_info_get_view_at (seat_impl->viewports, x, y);
|
|
if (view < 0)
|
|
return;
|
|
|
|
meta_viewport_info_get_view_info (seat_impl->viewports, view, NULL, &scale);
|
|
new_dx = (*dx) * scale;
|
|
new_dy = (*dy) * scale;
|
|
|
|
dest_view = meta_viewport_info_get_view_at (seat_impl->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_impl->viewports, view,
|
|
x, y, &new_dx, &new_dy);
|
|
}
|
|
|
|
*dx = new_dx;
|
|
*dy = new_dy;
|
|
}
|
|
|
|
static void
|
|
notify_absolute_motion_in_impl (ClutterInputDevice *input_device,
|
|
uint64_t time_us,
|
|
float x,
|
|
float y,
|
|
double *axes)
|
|
{
|
|
MetaSeatImpl *seat_impl;
|
|
ClutterEvent *event;
|
|
|
|
seat_impl = seat_impl_from_device (input_device);
|
|
event = new_absolute_motion_event (seat_impl, input_device, time_us, x, y, axes);
|
|
|
|
queue_event (seat_impl, event);
|
|
}
|
|
|
|
static void
|
|
notify_relative_tool_motion_in_impl (ClutterInputDevice *input_device,
|
|
uint64_t time_us,
|
|
float dx,
|
|
float dy,
|
|
double *axes)
|
|
{
|
|
MetaInputDeviceNative *device_native;
|
|
ClutterEvent *event;
|
|
MetaSeatImpl *seat_impl;
|
|
float x, y;
|
|
|
|
device_native = META_INPUT_DEVICE_NATIVE (input_device);
|
|
seat_impl = seat_impl_from_device (input_device);
|
|
x = device_native->pointer_x + dx;
|
|
y = device_native->pointer_y + dy;
|
|
|
|
meta_seat_impl_filter_relative_motion (seat_impl,
|
|
input_device,
|
|
seat_impl->pointer_x,
|
|
seat_impl->pointer_y,
|
|
&dx,
|
|
&dy);
|
|
|
|
event = new_absolute_motion_event (seat_impl, input_device, time_us,
|
|
x, y, axes);
|
|
|
|
event->motion.flags |= CLUTTER_EVENT_FLAG_RELATIVE_MOTION;
|
|
event->motion.dx = dx;
|
|
event->motion.dy = dy;
|
|
|
|
queue_event (seat_impl, event);
|
|
}
|
|
|
|
static void
|
|
notify_pinch_gesture_event (ClutterInputDevice *input_device,
|
|
ClutterTouchpadGesturePhase phase,
|
|
uint64_t time_us,
|
|
double dx,
|
|
double dy,
|
|
double dx_unaccel,
|
|
double dy_unaccel,
|
|
double angle_delta,
|
|
double scale,
|
|
uint32_t n_fingers)
|
|
{
|
|
MetaSeatImpl *seat_impl;
|
|
ClutterEvent *event = NULL;
|
|
|
|
seat_impl = seat_impl_from_device (input_device);
|
|
|
|
event = clutter_event_new (CLUTTER_TOUCHPAD_PINCH);
|
|
|
|
meta_input_device_native_get_coords_in_impl (META_INPUT_DEVICE_NATIVE (seat_impl->core_pointer),
|
|
&event->touchpad_pinch.x,
|
|
&event->touchpad_pinch.y);
|
|
|
|
event->touchpad_pinch.phase = phase;
|
|
event->touchpad_pinch.time = us2ms (time_us);
|
|
event->touchpad_pinch.dx = dx;
|
|
event->touchpad_pinch.dy = dy;
|
|
event->touchpad_pinch.dx_unaccel = dx_unaccel;
|
|
event->touchpad_pinch.dy_unaccel = dy_unaccel;
|
|
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_impl->xkb, seat_impl->button_state);
|
|
|
|
clutter_event_set_device (event, seat_impl->core_pointer);
|
|
clutter_event_set_source_device (event, input_device);
|
|
|
|
queue_event (seat_impl, event);
|
|
}
|
|
|
|
static void
|
|
notify_swipe_gesture_event (ClutterInputDevice *input_device,
|
|
ClutterTouchpadGesturePhase phase,
|
|
uint64_t time_us,
|
|
uint32_t n_fingers,
|
|
double dx,
|
|
double dy,
|
|
double dx_unaccel,
|
|
double dy_unaccel)
|
|
{
|
|
MetaSeatImpl *seat_impl;
|
|
ClutterEvent *event = NULL;
|
|
|
|
seat_impl = seat_impl_from_device (input_device);
|
|
|
|
event = clutter_event_new (CLUTTER_TOUCHPAD_SWIPE);
|
|
|
|
event->touchpad_swipe.phase = phase;
|
|
event->touchpad_swipe.time = us2ms (time_us);
|
|
|
|
meta_input_device_native_get_coords_in_impl (META_INPUT_DEVICE_NATIVE (seat_impl->core_pointer),
|
|
&event->touchpad_swipe.x,
|
|
&event->touchpad_swipe.y);
|
|
event->touchpad_swipe.dx = dx;
|
|
event->touchpad_swipe.dy = dy;
|
|
event->touchpad_swipe.dx_unaccel = dx_unaccel;
|
|
event->touchpad_swipe.dy_unaccel = dy_unaccel;
|
|
event->touchpad_swipe.n_fingers = n_fingers;
|
|
|
|
meta_xkb_translate_state (event, seat_impl->xkb, seat_impl->button_state);
|
|
|
|
clutter_event_set_device (event, seat_impl->core_pointer);
|
|
clutter_event_set_source_device (event, input_device);
|
|
|
|
queue_event (seat_impl, event);
|
|
}
|
|
|
|
static void
|
|
notify_hold_gesture_event (ClutterInputDevice *input_device,
|
|
ClutterTouchpadGesturePhase phase,
|
|
uint64_t time_us,
|
|
uint32_t n_fingers)
|
|
{
|
|
MetaSeatImpl *seat_impl;
|
|
ClutterEvent *event = NULL;
|
|
|
|
seat_impl = seat_impl_from_device (input_device);
|
|
|
|
event = clutter_event_new (CLUTTER_TOUCHPAD_HOLD);
|
|
|
|
event->touchpad_hold.phase = phase;
|
|
event->touchpad_hold.time = us2ms (time_us);
|
|
event->touchpad_hold.n_fingers = n_fingers;
|
|
|
|
meta_input_device_native_get_coords_in_impl (META_INPUT_DEVICE_NATIVE (seat_impl->core_pointer),
|
|
&event->touchpad_hold.x,
|
|
&event->touchpad_hold.y);
|
|
|
|
meta_xkb_translate_state (event, seat_impl->xkb, seat_impl->button_state);
|
|
|
|
clutter_event_set_device (event, seat_impl->core_pointer);
|
|
clutter_event_set_source_device (event, input_device);
|
|
|
|
queue_event (seat_impl, event);
|
|
}
|
|
|
|
static void
|
|
notify_proximity (ClutterInputDevice *input_device,
|
|
uint64_t time_us,
|
|
gboolean in)
|
|
{
|
|
MetaInputDeviceNative *device_native;
|
|
MetaSeatImpl *seat_impl;
|
|
ClutterEvent *event = NULL;
|
|
|
|
device_native = META_INPUT_DEVICE_NATIVE (input_device);
|
|
seat_impl = seat_impl_from_device (input_device);
|
|
|
|
if (in)
|
|
event = clutter_event_new (CLUTTER_PROXIMITY_IN);
|
|
else
|
|
event = clutter_event_new (CLUTTER_PROXIMITY_OUT);
|
|
|
|
event->proximity.time = us2ms (time_us);
|
|
clutter_event_set_device_tool (event, device_native->last_tool);
|
|
clutter_event_set_device (event, seat_impl->core_pointer);
|
|
clutter_event_set_source_device (event, input_device);
|
|
|
|
queue_event (seat_impl, 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_impl;
|
|
ClutterEvent *event;
|
|
|
|
seat_impl = seat_impl_from_device (input_device);
|
|
|
|
if (pressed)
|
|
event = clutter_event_new (CLUTTER_PAD_BUTTON_PRESS);
|
|
else
|
|
event = clutter_event_new (CLUTTER_PAD_BUTTON_RELEASE);
|
|
|
|
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_impl, 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_impl;
|
|
ClutterEvent *event;
|
|
|
|
seat_impl = seat_impl_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);
|
|
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_impl, 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_impl;
|
|
ClutterEvent *event;
|
|
|
|
seat_impl = seat_impl_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);
|
|
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_impl, event);
|
|
}
|
|
|
|
static gboolean
|
|
meta_event_dispatch (GSource *g_source,
|
|
GSourceFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
MetaEventSource *source = (MetaEventSource *) g_source;
|
|
MetaSeatImpl *seat_impl;
|
|
|
|
seat_impl = source->seat_impl;
|
|
|
|
dispatch_libinput (seat_impl);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GSourceFuncs event_funcs = {
|
|
meta_event_prepare,
|
|
meta_event_check,
|
|
meta_event_dispatch,
|
|
NULL
|
|
};
|
|
|
|
static MetaEventSource *
|
|
meta_event_source_new (MetaSeatImpl *seat_impl)
|
|
{
|
|
GSource *source;
|
|
MetaEventSource *event_source;
|
|
int fd;
|
|
|
|
source = g_source_new (&event_funcs, sizeof (MetaEventSource));
|
|
event_source = (MetaEventSource *) source;
|
|
|
|
/* setup the source */
|
|
event_source->seat_impl = seat_impl;
|
|
|
|
fd = libinput_get_fd (seat_impl->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, seat_impl->input_context);
|
|
|
|
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_impl)
|
|
{
|
|
GSList *l;
|
|
|
|
for (l = seat_impl->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 inline gboolean
|
|
device_type_is_pointer (ClutterInputDeviceType device_type)
|
|
{
|
|
return device_type == CLUTTER_POINTER_DEVICE ||
|
|
device_type == CLUTTER_TOUCHPAD_DEVICE;
|
|
}
|
|
|
|
static gboolean
|
|
has_pointer (MetaSeatImpl *seat_impl)
|
|
{
|
|
GSList *l;
|
|
|
|
for (l = seat_impl->devices; l; l = l->next)
|
|
{
|
|
ClutterInputDeviceType device_type;
|
|
|
|
device_type = clutter_input_device_get_device_type (l->data);
|
|
if (device_type_is_pointer (device_type))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
device_is_tablet_switch (MetaInputDeviceNative *device_native)
|
|
{
|
|
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))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
has_tablet_switch (MetaSeatImpl *seat_impl)
|
|
{
|
|
GSList *l;
|
|
|
|
for (l = seat_impl->devices; l; l = l->next)
|
|
{
|
|
MetaInputDeviceNative *device_native = META_INPUT_DEVICE_NATIVE (l->data);
|
|
|
|
if (device_is_tablet_switch (device_native))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
update_touch_mode (MetaSeatImpl *seat_impl)
|
|
{
|
|
gboolean touch_mode;
|
|
|
|
/* No touch mode if we don't have a touchscreen, easy */
|
|
if (!seat_impl->has_touchscreen)
|
|
touch_mode = FALSE;
|
|
/* If we have a tablet mode switch, honor it being unset */
|
|
else if (seat_impl->has_tablet_switch && !seat_impl->tablet_mode_switch_state)
|
|
touch_mode = FALSE;
|
|
/* If tablet mode is enabled, go for it */
|
|
else if (seat_impl->has_tablet_switch && seat_impl->tablet_mode_switch_state)
|
|
touch_mode = TRUE;
|
|
/* If there is no tablet mode switch (eg. kiosk machines),
|
|
* assume touch-mode is mutually exclusive with pointers.
|
|
*/
|
|
else
|
|
touch_mode = !seat_impl->has_pointer;
|
|
|
|
if (seat_impl->touch_mode != touch_mode)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
|
|
g_value_init (&value, G_TYPE_BOOLEAN);
|
|
g_value_set_boolean (&value, touch_mode);
|
|
seat_impl->touch_mode = touch_mode;
|
|
emit_signal (seat_impl, signals[TOUCH_MODE], &value, 1);
|
|
g_value_unset (&value);
|
|
}
|
|
}
|
|
|
|
static ClutterInputDevice *
|
|
evdev_add_device (MetaSeatImpl *seat_impl,
|
|
struct libinput_device *libinput_device)
|
|
{
|
|
ClutterInputDeviceType type;
|
|
ClutterInputDevice *device;
|
|
gboolean is_touchscreen, is_tablet_switch, is_pointer;
|
|
|
|
device = meta_input_device_native_new_in_impl (seat_impl, libinput_device);
|
|
|
|
seat_impl->devices = g_slist_prepend (seat_impl->devices, device);
|
|
meta_seat_impl_sync_leds_in_impl (seat_impl);
|
|
|
|
/* Clutter assumes that device types are exclusive in the
|
|
* ClutterInputDevice API */
|
|
type = meta_input_device_native_determine_type_in_impl (libinput_device);
|
|
|
|
is_touchscreen = type == CLUTTER_TOUCHSCREEN_DEVICE;
|
|
is_tablet_switch =
|
|
device_is_tablet_switch (META_INPUT_DEVICE_NATIVE (device));
|
|
is_pointer = device_type_is_pointer (type);
|
|
|
|
seat_impl->has_touchscreen |= is_touchscreen;
|
|
seat_impl->has_tablet_switch |= is_tablet_switch;
|
|
seat_impl->has_pointer |= is_pointer;
|
|
|
|
if (is_touchscreen || is_tablet_switch || is_pointer)
|
|
update_touch_mode (seat_impl);
|
|
|
|
return device;
|
|
}
|
|
|
|
static void
|
|
evdev_remove_device (MetaSeatImpl *seat_impl,
|
|
MetaInputDeviceNative *device_native)
|
|
{
|
|
ClutterInputDevice *device;
|
|
ClutterInputDeviceType device_type;
|
|
gboolean is_touchscreen, is_tablet_switch, is_pointer;
|
|
|
|
device = CLUTTER_INPUT_DEVICE (device_native);
|
|
seat_impl->devices = g_slist_remove (seat_impl->devices, device);
|
|
|
|
device_type = clutter_input_device_get_device_type (device);
|
|
|
|
is_touchscreen = device_type == CLUTTER_TOUCHSCREEN_DEVICE;
|
|
is_tablet_switch = device_is_tablet_switch (device_native);
|
|
is_pointer = device_type_is_pointer (device_type);
|
|
|
|
if (is_touchscreen)
|
|
seat_impl->has_touchscreen = has_touchscreen (seat_impl);
|
|
if (is_tablet_switch)
|
|
seat_impl->has_tablet_switch = has_tablet_switch (seat_impl);
|
|
if (is_pointer)
|
|
seat_impl->has_pointer = has_pointer (seat_impl);
|
|
|
|
if (is_touchscreen || is_tablet_switch || is_pointer)
|
|
update_touch_mode (seat_impl);
|
|
|
|
if (seat_impl->repeat_source && seat_impl->repeat_device == device)
|
|
meta_seat_impl_clear_repeat_source (seat_impl);
|
|
|
|
meta_input_device_native_detach_libinput_in_impl (device_native);
|
|
|
|
g_object_unref (device);
|
|
}
|
|
|
|
static gboolean
|
|
process_base_event (MetaSeatImpl *seat_impl,
|
|
struct libinput_event *event)
|
|
{
|
|
ClutterInputDevice *device;
|
|
ClutterEvent *device_event = NULL;
|
|
struct libinput_device *libinput_device;
|
|
MetaInputSettings *input_settings;
|
|
|
|
input_settings = seat_impl->input_settings;
|
|
|
|
switch (libinput_event_get_type (event))
|
|
{
|
|
case LIBINPUT_EVENT_DEVICE_ADDED:
|
|
libinput_device = libinput_event_get_device (event);
|
|
|
|
device = evdev_add_device (seat_impl, libinput_device);
|
|
device_event = clutter_event_new (CLUTTER_DEVICE_ADDED);
|
|
clutter_event_set_device (device_event, device);
|
|
meta_input_settings_add_device (input_settings, 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);
|
|
meta_input_settings_remove_device (input_settings, device);
|
|
evdev_remove_device (seat_impl,
|
|
META_INPUT_DEVICE_NATIVE (device));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (device_event)
|
|
{
|
|
queue_event (seat_impl, 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 (MetaSeatImpl *seat_impl,
|
|
ClutterInputDevice *input_device,
|
|
struct libinput_tablet_tool *libinput_tool)
|
|
{
|
|
MetaInputDeviceNative *evdev_device = META_INPUT_DEVICE_NATIVE (input_device);
|
|
ClutterInputDeviceTool *tool = NULL;
|
|
MetaInputSettings *input_settings;
|
|
|
|
if (libinput_tool)
|
|
{
|
|
if (!seat_impl->tools)
|
|
{
|
|
seat_impl->tools =
|
|
g_hash_table_new_full (NULL, NULL, NULL,
|
|
(GDestroyNotify) g_object_unref);
|
|
}
|
|
|
|
tool = g_hash_table_lookup (seat_impl->tools, libinput_tool);
|
|
|
|
if (!tool)
|
|
{
|
|
ClutterInputDeviceToolType tool_type;
|
|
uint64_t tool_serial;
|
|
|
|
tool_serial = libinput_tablet_tool_get_serial (libinput_tool);
|
|
tool_type = translate_tool_type (libinput_tool);
|
|
tool = meta_input_device_tool_native_new (libinput_tool,
|
|
tool_serial, tool_type);
|
|
g_hash_table_insert (seat_impl->tools, libinput_tool, tool);
|
|
}
|
|
}
|
|
|
|
if (evdev_device->last_tool != tool)
|
|
{
|
|
evdev_device->last_tool = tool;
|
|
input_settings = seat_impl->input_settings;
|
|
meta_input_settings_notify_tool_change (input_settings, input_device, tool);
|
|
}
|
|
}
|
|
|
|
static double *
|
|
translate_tablet_axes (struct libinput_event_tablet_tool *tablet_event,
|
|
ClutterInputDeviceTool *tool)
|
|
{
|
|
double *axes = g_new0 (double, CLUTTER_INPUT_AXIS_LAST);
|
|
struct libinput_tablet_tool *libinput_tool;
|
|
double value;
|
|
|
|
libinput_tool = libinput_event_tablet_tool_get_tool (tablet_event);
|
|
|
|
value = libinput_event_tablet_tool_get_x (tablet_event);
|
|
axes[CLUTTER_INPUT_AXIS_X] = value;
|
|
value = libinput_event_tablet_tool_get_y (tablet_event);
|
|
axes[CLUTTER_INPUT_AXIS_Y] = value;
|
|
|
|
if (libinput_tablet_tool_has_distance (libinput_tool))
|
|
{
|
|
value = libinput_event_tablet_tool_get_distance (tablet_event);
|
|
axes[CLUTTER_INPUT_AXIS_DISTANCE] = 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_in_impl (tool, value);
|
|
axes[CLUTTER_INPUT_AXIS_PRESSURE] = value;
|
|
}
|
|
|
|
if (libinput_tablet_tool_has_tilt (libinput_tool))
|
|
{
|
|
value = libinput_event_tablet_tool_get_tilt_x (tablet_event);
|
|
axes[CLUTTER_INPUT_AXIS_XTILT] = value;
|
|
value = libinput_event_tablet_tool_get_tilt_y (tablet_event);
|
|
axes[CLUTTER_INPUT_AXIS_YTILT] = value;
|
|
}
|
|
|
|
if (libinput_tablet_tool_has_rotation (libinput_tool))
|
|
{
|
|
value = libinput_event_tablet_tool_get_rotation (tablet_event);
|
|
axes[CLUTTER_INPUT_AXIS_ROTATION] = value;
|
|
}
|
|
|
|
if (libinput_tablet_tool_has_slider (libinput_tool))
|
|
{
|
|
value = libinput_event_tablet_tool_get_slider_position (tablet_event);
|
|
axes[CLUTTER_INPUT_AXIS_SLIDER] = value;
|
|
}
|
|
|
|
if (libinput_tablet_tool_has_wheel (libinput_tool))
|
|
{
|
|
value = libinput_event_tablet_tool_get_wheel_delta (tablet_event);
|
|
axes[CLUTTER_INPUT_AXIS_WHEEL] = value;
|
|
}
|
|
|
|
return axes;
|
|
}
|
|
|
|
static void
|
|
notify_continuous_axis (MetaSeatImpl *seat_impl,
|
|
ClutterInputDevice *device,
|
|
uint64_t time_us,
|
|
ClutterScrollSource scroll_source,
|
|
struct libinput_event_pointer *axis_event)
|
|
{
|
|
double 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_in_impl (seat_impl, device, time_us,
|
|
dx, dy,
|
|
scroll_source, finish_flags);
|
|
}
|
|
|
|
static void
|
|
notify_discrete_axis (MetaSeatImpl *seat_impl,
|
|
ClutterInputDevice *device,
|
|
uint64_t time_us,
|
|
ClutterScrollSource scroll_source,
|
|
struct libinput_event_pointer *axis_event)
|
|
{
|
|
double 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_in_impl (seat_impl, device,
|
|
time_us,
|
|
discrete_dx, discrete_dy,
|
|
scroll_source);
|
|
}
|
|
|
|
static void
|
|
handle_pointer_scroll (MetaSeatImpl *seat_impl,
|
|
struct libinput_event *event)
|
|
{
|
|
struct libinput_device *libinput_device = libinput_event_get_device (event);
|
|
ClutterInputDevice *device;
|
|
uint64_t time_us;
|
|
enum libinput_pointer_axis_source source;
|
|
struct libinput_event_pointer *axis_event =
|
|
libinput_event_get_pointer_event (event);
|
|
ClutterScrollSource scroll_source;
|
|
|
|
device = libinput_device_get_user_data (libinput_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_impl, 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_impl, device, time_us, scroll_source,
|
|
axis_event);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
process_tablet_axis (MetaSeatImpl *seat_impl,
|
|
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);
|
|
|
|
meta_viewport_info_get_extents (seat_impl->viewports,
|
|
&stage_width, &stage_height);
|
|
|
|
time = libinput_event_tablet_tool_get_time_usec (tablet_event);
|
|
|
|
if (meta_input_device_native_get_mapping_mode_in_impl (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_in_impl (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_in_impl (device, time, x, y, axes);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
process_device_event (MetaSeatImpl *seat_impl,
|
|
struct libinput_event *event)
|
|
{
|
|
gboolean handled = TRUE;
|
|
struct libinput_device *libinput_device = libinput_event_get_device(event);
|
|
ClutterInputDevice *device;
|
|
MetaInputDeviceNative *device_native;
|
|
|
|
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",
|
|
key_state == LIBINPUT_KEY_STATE_PRESSED ? "press" : "release",
|
|
key, seat_key_count);
|
|
break;
|
|
}
|
|
|
|
meta_seat_impl_notify_key_in_impl (seat_impl,
|
|
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_in_impl (seat_impl,
|
|
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_impl->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_in_impl (seat_impl,
|
|
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",
|
|
button_state == LIBINPUT_BUTTON_STATE_PRESSED ? "press" : "release",
|
|
button, seat_button_count);
|
|
break;
|
|
}
|
|
|
|
meta_seat_impl_notify_button_in_impl (seat_impl, device,
|
|
time_us, button, button_state);
|
|
break;
|
|
}
|
|
|
|
case LIBINPUT_EVENT_POINTER_AXIS:
|
|
{
|
|
handle_pointer_scroll (seat_impl, event);
|
|
break;
|
|
}
|
|
|
|
case LIBINPUT_EVENT_TOUCH_DOWN:
|
|
{
|
|
int seat_slot;
|
|
uint64_t time_us;
|
|
double x, y;
|
|
float stage_width, stage_height;
|
|
MetaTouchState *touch_state;
|
|
struct libinput_event_touch *touch_event =
|
|
libinput_event_get_touch_event (event);
|
|
|
|
device = libinput_device_get_user_data (libinput_device);
|
|
device_native = META_INPUT_DEVICE_NATIVE (device);
|
|
|
|
meta_viewport_info_get_extents (seat_impl->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);
|
|
|
|
g_rw_lock_writer_lock (&seat_impl->state_lock);
|
|
|
|
touch_state = meta_seat_impl_acquire_touch_state_in_impl (seat_impl, seat_slot);
|
|
touch_state->coords.x = x;
|
|
touch_state->coords.y = y;
|
|
|
|
g_rw_lock_writer_unlock (&seat_impl->state_lock);
|
|
|
|
meta_seat_impl_notify_touch_event_in_impl (seat_impl, 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;
|
|
MetaTouchState *touch_state;
|
|
struct libinput_event_touch *touch_event =
|
|
libinput_event_get_touch_event (event);
|
|
|
|
device = libinput_device_get_user_data (libinput_device);
|
|
device_native = META_INPUT_DEVICE_NATIVE (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_in_impl (seat_impl, seat_slot);
|
|
if (!touch_state)
|
|
break;
|
|
|
|
meta_seat_impl_notify_touch_event_in_impl (seat_impl, device,
|
|
CLUTTER_TOUCH_END, time_us,
|
|
touch_state->seat_slot,
|
|
touch_state->coords.x,
|
|
touch_state->coords.y);
|
|
|
|
g_rw_lock_writer_lock (&seat_impl->state_lock);
|
|
meta_seat_impl_release_touch_state_in_impl (seat_impl, seat_slot);
|
|
g_rw_lock_writer_unlock (&seat_impl->state_lock);
|
|
break;
|
|
}
|
|
|
|
case LIBINPUT_EVENT_TOUCH_MOTION:
|
|
{
|
|
int seat_slot;
|
|
uint64_t time_us;
|
|
double x, y;
|
|
float stage_width, stage_height;
|
|
MetaTouchState *touch_state;
|
|
struct libinput_event_touch *touch_event =
|
|
libinput_event_get_touch_event (event);
|
|
|
|
device = libinput_device_get_user_data (libinput_device);
|
|
device_native = META_INPUT_DEVICE_NATIVE (device);
|
|
|
|
meta_viewport_info_get_extents (seat_impl->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);
|
|
|
|
g_rw_lock_writer_lock (&seat_impl->state_lock);
|
|
touch_state = meta_seat_impl_lookup_touch_state_in_impl (seat_impl, seat_slot);
|
|
if (touch_state)
|
|
{
|
|
touch_state->coords.x = x;
|
|
touch_state->coords.y = y;
|
|
}
|
|
g_rw_lock_writer_unlock (&seat_impl->state_lock);
|
|
|
|
if (!touch_state)
|
|
break;
|
|
|
|
meta_seat_impl_notify_touch_event_in_impl (seat_impl, 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;
|
|
struct libinput_event_touch *touch_event =
|
|
libinput_event_get_touch_event (event);
|
|
|
|
device = libinput_device_get_user_data (libinput_device);
|
|
device_native = META_INPUT_DEVICE_NATIVE (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_in_impl (seat_impl, seat_slot);
|
|
if (!touch_state)
|
|
break;
|
|
|
|
meta_seat_impl_notify_touch_event_in_impl (touch_state->seat_impl,
|
|
CLUTTER_INPUT_DEVICE (device_native),
|
|
CLUTTER_TOUCH_CANCEL,
|
|
time_us,
|
|
touch_state->seat_slot,
|
|
touch_state->coords.x,
|
|
touch_state->coords.y);
|
|
|
|
meta_seat_impl_release_touch_state_in_impl (seat_impl, 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, 0, 0, n_fingers);
|
|
break;
|
|
}
|
|
case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
|
|
{
|
|
struct libinput_event_gesture *gesture_event =
|
|
libinput_event_get_gesture_event (event);
|
|
double angle_delta, scale, dx, dy, dx_unaccel, dy_unaccel;
|
|
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);
|
|
dx_unaccel = libinput_event_gesture_get_dx_unaccelerated (gesture_event);
|
|
dy_unaccel = libinput_event_gesture_get_dy_unaccelerated (gesture_event);
|
|
|
|
notify_pinch_gesture_event (device,
|
|
CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE,
|
|
time_us, dx, dy, dx_unaccel, dy_unaccel,
|
|
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, 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, dx_unaccel, dy_unaccel;
|
|
|
|
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);
|
|
dx_unaccel = libinput_event_gesture_get_dx_unaccelerated (gesture_event);
|
|
dy_unaccel = libinput_event_gesture_get_dy_unaccelerated (gesture_event);
|
|
|
|
notify_swipe_gesture_event (device,
|
|
CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE,
|
|
time_us, n_fingers, dx, dy, dx_unaccel, dy_unaccel);
|
|
break;
|
|
}
|
|
case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
|
|
case LIBINPUT_EVENT_GESTURE_HOLD_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_HOLD_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_hold_gesture_event (device, phase, time_us, n_fingers);
|
|
break;
|
|
}
|
|
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
|
|
{
|
|
process_tablet_axis (seat_impl, 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 (seat_impl, device, libinput_tool);
|
|
notify_proximity (device, time, in);
|
|
if (!in)
|
|
input_device_update_tool (seat_impl, 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_impl, 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_in_impl (seat_impl, 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_impl, event);
|
|
|
|
meta_seat_impl_notify_button_in_impl (seat_impl, device,
|
|
time_us, BTN_TOUCH, button_state);
|
|
if (!button_state)
|
|
process_tablet_axis (seat_impl, 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_impl->tablet_mode_switch_state = (state == LIBINPUT_SWITCH_STATE_ON);
|
|
update_touch_mode (seat_impl);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
handled = FALSE;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
static void
|
|
process_event (MetaSeatImpl *seat_impl,
|
|
struct libinput_event *event)
|
|
{
|
|
if (process_base_event (seat_impl, event))
|
|
return;
|
|
if (process_device_event (seat_impl, event))
|
|
return;
|
|
}
|
|
|
|
static void
|
|
process_events (MetaSeatImpl *seat_impl)
|
|
{
|
|
struct libinput_event *event;
|
|
|
|
while ((event = libinput_get_event (seat_impl->libinput)))
|
|
{
|
|
process_event(seat_impl, event);
|
|
libinput_event_destroy(event);
|
|
}
|
|
}
|
|
|
|
static int
|
|
open_restricted (const char *path,
|
|
int open_flags,
|
|
void *user_data)
|
|
{
|
|
MetaSeatImpl *seat_impl = user_data;
|
|
MetaSeatImplPrivate *priv = meta_seat_impl_get_instance_private (seat_impl);
|
|
MetaBackend *backend = meta_seat_native_get_backend (seat_impl->seat_native);
|
|
MetaDevicePool *device_pool =
|
|
meta_backend_native_get_device_pool (META_BACKEND_NATIVE (backend));
|
|
MetaDeviceFileFlags flags;
|
|
g_autoptr (GError) error = NULL;
|
|
MetaDeviceFile *device_file;
|
|
int fd;
|
|
|
|
flags = META_DEVICE_FILE_FLAG_NONE;
|
|
if (!(open_flags & (O_RDWR | O_WRONLY)))
|
|
flags |= META_DEVICE_FILE_FLAG_READ_ONLY;
|
|
|
|
if (!g_str_has_prefix (path, "/sys/"))
|
|
flags |= META_DEVICE_FILE_FLAG_TAKE_CONTROL;
|
|
|
|
device_file = meta_device_pool_open (device_pool, path, flags, &error);
|
|
if (!device_file)
|
|
{
|
|
g_warning ("Could not open device %s: %s", path, error->message);
|
|
return -1;
|
|
}
|
|
|
|
fd = meta_device_file_get_fd (device_file);
|
|
g_hash_table_insert (priv->device_files, GINT_TO_POINTER (fd), device_file);
|
|
|
|
return fd;
|
|
}
|
|
|
|
static void
|
|
close_restricted (int fd,
|
|
void *user_data)
|
|
{
|
|
MetaSeatImpl *seat_impl = user_data;
|
|
MetaSeatImplPrivate *priv = meta_seat_impl_get_instance_private (seat_impl);
|
|
|
|
g_hash_table_remove (priv->device_files, GINT_TO_POINTER (fd));
|
|
}
|
|
|
|
static const struct libinput_interface libinput_interface = {
|
|
open_restricted,
|
|
close_restricted
|
|
};
|
|
|
|
static void
|
|
kbd_a11y_changed_cb (MetaInputSettings *input_settings,
|
|
MetaKbdA11ySettings *a11y_settings,
|
|
MetaSeatImpl *seat_impl)
|
|
{
|
|
MetaInputDeviceNative *keyboard;
|
|
|
|
keyboard = META_INPUT_DEVICE_NATIVE (seat_impl->core_keyboard);
|
|
meta_input_device_native_apply_kbd_a11y_settings_in_impl (keyboard, a11y_settings);
|
|
}
|
|
|
|
static void
|
|
meta_seat_impl_set_keyboard_numlock_in_impl (MetaSeatImpl *seat_impl,
|
|
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;
|
|
|
|
keymap = seat_impl->keymap;
|
|
xkb_keymap = meta_keymap_native_get_keyboard_map_in_impl (keymap);
|
|
|
|
numlock = (1 << xkb_keymap_mod_get_index (xkb_keymap, "Mod2"));
|
|
|
|
depressed_mods =
|
|
xkb_state_serialize_mods (seat_impl->xkb, XKB_STATE_MODS_DEPRESSED);
|
|
latched_mods =
|
|
xkb_state_serialize_mods (seat_impl->xkb, XKB_STATE_MODS_LATCHED);
|
|
locked_mods =
|
|
xkb_state_serialize_mods (seat_impl->xkb, XKB_STATE_MODS_LOCKED);
|
|
group_mods =
|
|
xkb_state_serialize_layout (seat_impl->xkb, XKB_STATE_LAYOUT_EFFECTIVE);
|
|
|
|
if (numlock_state)
|
|
locked_mods |= numlock;
|
|
else
|
|
locked_mods &= ~numlock;
|
|
|
|
xkb_state_update_mask (seat_impl->xkb,
|
|
depressed_mods,
|
|
latched_mods,
|
|
locked_mods,
|
|
0, 0,
|
|
group_mods);
|
|
|
|
meta_seat_impl_sync_leds_in_impl (seat_impl);
|
|
meta_keymap_native_update_in_impl (seat_impl->keymap,
|
|
seat_impl,
|
|
seat_impl->xkb);
|
|
}
|
|
|
|
static gboolean
|
|
init_libinput (MetaSeatImpl *seat_impl,
|
|
GError **error)
|
|
{
|
|
MetaEventSource *source;
|
|
struct udev *udev;
|
|
struct libinput *libinput;
|
|
|
|
udev = udev_new ();
|
|
if (G_UNLIKELY (udev == NULL))
|
|
{
|
|
g_warning ("Failed to create udev object");
|
|
seat_impl->input_thread_initialized = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
libinput = libinput_udev_create_context (&libinput_interface,
|
|
seat_impl, udev);
|
|
udev_unref (udev);
|
|
|
|
if (libinput == NULL)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create the libinput object.");
|
|
return FALSE;
|
|
}
|
|
|
|
if (libinput_udev_assign_seat (libinput, seat_impl->seat_id) == -1)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to assign a seat to the libinput object.");
|
|
libinput_unref (seat_impl->libinput);
|
|
return FALSE;
|
|
}
|
|
|
|
seat_impl->libinput = libinput;
|
|
source = meta_event_source_new (seat_impl);
|
|
seat_impl->event_source = source;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gpointer
|
|
input_thread (MetaSeatImpl *seat_impl)
|
|
{
|
|
MetaSeatImplPrivate *priv = meta_seat_impl_get_instance_private (seat_impl);
|
|
struct xkb_keymap *xkb_keymap;
|
|
|
|
g_main_context_push_thread_default (seat_impl->input_context);
|
|
|
|
priv->device_files =
|
|
g_hash_table_new_full (NULL, NULL,
|
|
NULL,
|
|
(GDestroyNotify) meta_device_file_release);
|
|
|
|
if (!(seat_impl->flags & META_SEAT_NATIVE_FLAG_NO_LIBINPUT))
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (!init_libinput (seat_impl, &error))
|
|
{
|
|
g_critical ("Failed to initialize seat: %s", error->message);
|
|
seat_impl->input_thread_initialized = TRUE;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
seat_impl->input_settings = meta_input_settings_native_new_in_impl (seat_impl);
|
|
g_signal_connect_object (seat_impl->input_settings, "kbd-a11y-changed",
|
|
G_CALLBACK (kbd_a11y_changed_cb), seat_impl, 0);
|
|
|
|
seat_impl->keymap = g_object_new (META_TYPE_KEYMAP_NATIVE, NULL);
|
|
|
|
xkb_keymap = meta_keymap_native_get_keyboard_map_in_impl (seat_impl->keymap);
|
|
|
|
if (xkb_keymap)
|
|
{
|
|
seat_impl->xkb = xkb_state_new (xkb_keymap);
|
|
|
|
seat_impl->caps_lock_led =
|
|
xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS);
|
|
seat_impl->num_lock_led =
|
|
xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM);
|
|
seat_impl->scroll_lock_led =
|
|
xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL);
|
|
}
|
|
|
|
if (meta_input_settings_maybe_restore_numlock_state (seat_impl->input_settings))
|
|
meta_seat_impl_set_keyboard_numlock_in_impl (seat_impl, TRUE);
|
|
|
|
seat_impl->has_touchscreen = has_touchscreen (seat_impl);
|
|
seat_impl->has_tablet_switch = has_tablet_switch (seat_impl);
|
|
update_touch_mode (seat_impl);
|
|
|
|
g_mutex_lock (&seat_impl->init_mutex);
|
|
seat_impl->input_thread_initialized = TRUE;
|
|
g_cond_signal (&seat_impl->init_cond);
|
|
g_mutex_unlock (&seat_impl->init_mutex);
|
|
|
|
seat_impl->input_loop = g_main_loop_new (seat_impl->input_context, FALSE);
|
|
g_main_loop_run (seat_impl->input_loop);
|
|
g_main_loop_unref (seat_impl->input_loop);
|
|
|
|
g_main_context_pop_thread_default (seat_impl->input_context);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
meta_seat_impl_initable_init (GInitable *initable,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
MetaSeatImpl *seat_impl = META_SEAT_IMPL (initable);
|
|
|
|
seat_impl->input_context = g_main_context_new ();
|
|
seat_impl->main_context = g_main_context_ref_thread_default ();
|
|
g_assert (seat_impl->main_context == g_main_context_default ());
|
|
|
|
seat_impl->input_thread =
|
|
g_thread_try_new ("Mutter Input Thread",
|
|
(GThreadFunc) input_thread,
|
|
initable,
|
|
error);
|
|
if (!seat_impl->input_thread)
|
|
return FALSE;
|
|
|
|
/* Initialize thread synchronously */
|
|
g_mutex_lock (&seat_impl->init_mutex);
|
|
while (!seat_impl->input_thread_initialized)
|
|
g_cond_wait (&seat_impl->init_cond, &seat_impl->init_mutex);
|
|
g_mutex_unlock (&seat_impl->init_mutex);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
meta_seat_impl_constructed (GObject *object)
|
|
{
|
|
MetaSeatImpl *seat_impl = META_SEAT_IMPL (object);
|
|
ClutterInputDevice *device;
|
|
|
|
device = meta_input_device_native_new_virtual (
|
|
seat_impl, CLUTTER_POINTER_DEVICE,
|
|
CLUTTER_INPUT_MODE_LOGICAL);
|
|
seat_impl->pointer_x = INITIAL_POINTER_X;
|
|
seat_impl->pointer_y = INITIAL_POINTER_Y;
|
|
meta_input_device_native_set_coords_in_impl (META_INPUT_DEVICE_NATIVE (device),
|
|
seat_impl->pointer_x,
|
|
seat_impl->pointer_y);
|
|
seat_impl->core_pointer = device;
|
|
|
|
device = meta_input_device_native_new_virtual (
|
|
seat_impl, CLUTTER_KEYBOARD_DEVICE,
|
|
CLUTTER_INPUT_MODE_LOGICAL);
|
|
seat_impl->core_keyboard = device;
|
|
|
|
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_native = g_value_get_object (value);
|
|
break;
|
|
case PROP_SEAT_ID:
|
|
seat_impl->seat_id = g_value_dup_string (value);
|
|
break;
|
|
case PROP_FLAGS:
|
|
seat_impl->flags = g_value_get_flags (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_native);
|
|
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 gboolean
|
|
destroy_in_impl (GTask *task)
|
|
{
|
|
MetaSeatImpl *seat_impl = g_task_get_source_object (task);
|
|
MetaSeatImplPrivate *priv = meta_seat_impl_get_instance_private (seat_impl);
|
|
gboolean numlock_active;
|
|
|
|
g_slist_foreach (seat_impl->devices,
|
|
(GFunc) meta_input_device_native_detach_libinput_in_impl,
|
|
NULL);
|
|
g_slist_free_full (seat_impl->devices, g_object_unref);
|
|
seat_impl->devices = NULL;
|
|
|
|
g_clear_pointer (&seat_impl->libinput, libinput_unref);
|
|
g_clear_pointer (&seat_impl->tools, g_hash_table_unref);
|
|
g_clear_pointer (&seat_impl->touch_states, g_hash_table_destroy);
|
|
g_clear_pointer (&seat_impl->event_source, meta_event_source_free);
|
|
|
|
numlock_active =
|
|
xkb_state_mod_name_is_active (seat_impl->xkb, XKB_MOD_NAME_NUM,
|
|
XKB_STATE_MODS_LATCHED |
|
|
XKB_STATE_MODS_LOCKED);
|
|
meta_input_settings_maybe_save_numlock_state (seat_impl->input_settings,
|
|
numlock_active);
|
|
|
|
g_clear_pointer (&seat_impl->xkb, xkb_state_unref);
|
|
|
|
meta_seat_impl_clear_repeat_source (seat_impl);
|
|
|
|
g_clear_pointer (&priv->device_files, g_hash_table_destroy);
|
|
|
|
g_main_loop_quit (seat_impl->input_loop);
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_destroy (MetaSeatImpl *seat_impl)
|
|
{
|
|
if (seat_impl->libinput)
|
|
{
|
|
GTask *task;
|
|
|
|
task = g_task_new (seat_impl, NULL, NULL, NULL);
|
|
meta_seat_impl_run_input_task (seat_impl, task,
|
|
(GSourceFunc) destroy_in_impl);
|
|
g_object_unref (task);
|
|
|
|
g_thread_join (seat_impl->input_thread);
|
|
g_assert (!seat_impl->libinput);
|
|
}
|
|
|
|
g_object_unref (seat_impl);
|
|
}
|
|
|
|
static void
|
|
meta_seat_impl_finalize (GObject *object)
|
|
{
|
|
MetaSeatImpl *seat_impl = META_SEAT_IMPL (object);
|
|
|
|
g_assert (!seat_impl->libinput);
|
|
g_assert (!seat_impl->tools);
|
|
g_assert (!seat_impl->event_source);
|
|
|
|
g_free (seat_impl->seat_id);
|
|
|
|
g_rw_lock_clear (&seat_impl->state_lock);
|
|
|
|
G_OBJECT_CLASS (meta_seat_impl_parent_class)->finalize (object);
|
|
}
|
|
|
|
ClutterInputDevice *
|
|
meta_seat_impl_get_pointer (MetaSeatImpl *seat_impl)
|
|
{
|
|
return seat_impl->core_pointer;
|
|
}
|
|
|
|
ClutterInputDevice *
|
|
meta_seat_impl_get_keyboard (MetaSeatImpl *seat_impl)
|
|
{
|
|
return seat_impl->core_keyboard;
|
|
}
|
|
|
|
GSList *
|
|
meta_seat_impl_get_devices_in_impl (MetaSeatImpl *seat_impl)
|
|
{
|
|
return g_slist_copy_deep (seat_impl->devices,
|
|
(GCopyFunc) g_object_ref,
|
|
NULL);
|
|
}
|
|
|
|
MetaKeymapNative *
|
|
meta_seat_impl_get_keymap (MetaSeatImpl *seat_impl)
|
|
{
|
|
return g_object_ref (seat_impl->keymap);
|
|
}
|
|
|
|
static gboolean
|
|
warp_pointer_in_impl (GTask *task)
|
|
{
|
|
MetaSeatImpl *seat_impl = g_task_get_source_object (task);
|
|
graphene_point_t *point;
|
|
|
|
point = g_task_get_task_data (task);
|
|
notify_absolute_motion_in_impl (seat_impl->core_pointer, 0,
|
|
point->x, point->y, NULL);
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_warp_pointer (MetaSeatImpl *seat_impl,
|
|
int x,
|
|
int y)
|
|
{
|
|
graphene_point_t *point;
|
|
GTask *task;
|
|
|
|
point = graphene_point_alloc ();
|
|
point->x = x;
|
|
point->y = y;
|
|
|
|
task = g_task_new (seat_impl, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, point, (GDestroyNotify) graphene_point_free);
|
|
meta_seat_impl_run_input_task (seat_impl, task,
|
|
(GSourceFunc) warp_pointer_in_impl);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
gboolean
|
|
meta_seat_impl_query_state (MetaSeatImpl *seat_impl,
|
|
ClutterInputDevice *device,
|
|
ClutterEventSequence *sequence,
|
|
graphene_point_t *coords,
|
|
ClutterModifierType *modifiers)
|
|
{
|
|
MetaInputDeviceNative *device_native = META_INPUT_DEVICE_NATIVE (device);
|
|
gboolean retval = FALSE;
|
|
ClutterModifierType mods = 0;
|
|
|
|
g_rw_lock_reader_lock (&seat_impl->state_lock);
|
|
|
|
if (sequence)
|
|
{
|
|
MetaTouchState *touch_state;
|
|
int slot;
|
|
|
|
slot = clutter_event_sequence_get_slot (sequence);
|
|
touch_state = meta_seat_impl_lookup_touch_state_in_impl (seat_impl, slot);
|
|
if (!touch_state)
|
|
goto out;
|
|
|
|
if (coords)
|
|
{
|
|
coords->x = touch_state->coords.x;
|
|
coords->y = touch_state->coords.y;
|
|
}
|
|
|
|
if (seat_impl->xkb)
|
|
mods = meta_xkb_translate_modifiers (seat_impl->xkb, 0);
|
|
|
|
retval = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (coords)
|
|
{
|
|
coords->x = device_native->pointer_x;
|
|
coords->y = device_native->pointer_y;
|
|
}
|
|
|
|
if (seat_impl->xkb)
|
|
{
|
|
mods = meta_xkb_translate_modifiers (seat_impl->xkb,
|
|
seat_impl->button_state);
|
|
}
|
|
|
|
retval = TRUE;
|
|
}
|
|
|
|
if (modifiers)
|
|
*modifiers = mods;
|
|
|
|
out:
|
|
g_rw_lock_reader_unlock (&seat_impl->state_lock);
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
meta_seat_impl_initable_iface_init (GInitableIface *iface)
|
|
{
|
|
iface->init = meta_seat_impl_initable_init;
|
|
}
|
|
|
|
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->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);
|
|
|
|
props[PROP_FLAGS] =
|
|
g_param_spec_flags ("flags",
|
|
"Flags",
|
|
"Flags",
|
|
META_TYPE_SEAT_NATIVE_FLAG,
|
|
META_SEAT_NATIVE_FLAG_NONE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY);
|
|
|
|
signals[KBD_A11Y_FLAGS_CHANGED] =
|
|
g_signal_new ("kbd-a11y-flags-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL, NULL,
|
|
G_TYPE_NONE, 2,
|
|
G_TYPE_UINT, G_TYPE_UINT);
|
|
signals[KBD_A11Y_MODS_STATE_CHANGED] =
|
|
g_signal_new ("kbd-a11y-mods-state-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL, NULL,
|
|
G_TYPE_NONE, 2,
|
|
G_TYPE_UINT, G_TYPE_UINT);
|
|
signals[TOUCH_MODE] =
|
|
g_signal_new ("touch-mode",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__BOOLEAN,
|
|
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
|
|
signals[BELL] =
|
|
g_signal_new ("bell",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
signals[MODS_STATE_CHANGED] =
|
|
g_signal_new ("mods-state-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
g_object_class_install_properties (object_class, N_PROPS, props);
|
|
}
|
|
|
|
static void
|
|
meta_seat_impl_init (MetaSeatImpl *seat_impl)
|
|
{
|
|
g_rw_lock_init (&seat_impl->state_lock);
|
|
|
|
seat_impl->repeat = TRUE;
|
|
seat_impl->repeat_delay = 250; /* ms */
|
|
seat_impl->repeat_interval = 33; /* ms */
|
|
|
|
g_mutex_init (&seat_impl->init_mutex);
|
|
g_cond_init (&seat_impl->init_cond);
|
|
|
|
seat_impl->barrier_manager = meta_barrier_manager_native_new ();
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_update_xkb_state_in_impl (MetaSeatImpl *seat_impl)
|
|
{
|
|
xkb_mod_mask_t latched_mods = 0;
|
|
xkb_mod_mask_t locked_mods = 0;
|
|
struct xkb_keymap *xkb_keymap;
|
|
|
|
g_rw_lock_writer_lock (&seat_impl->state_lock);
|
|
|
|
xkb_keymap = meta_keymap_native_get_keyboard_map_in_impl (seat_impl->keymap);
|
|
|
|
if (seat_impl->xkb)
|
|
{
|
|
latched_mods = xkb_state_serialize_mods (seat_impl->xkb,
|
|
XKB_STATE_MODS_LATCHED);
|
|
locked_mods = xkb_state_serialize_mods (seat_impl->xkb,
|
|
XKB_STATE_MODS_LOCKED);
|
|
xkb_state_unref (seat_impl->xkb);
|
|
}
|
|
|
|
seat_impl->xkb = xkb_state_new (xkb_keymap);
|
|
|
|
xkb_state_update_mask (seat_impl->xkb,
|
|
0, /* depressed */
|
|
latched_mods,
|
|
locked_mods,
|
|
0, 0, seat_impl->layout_idx);
|
|
|
|
seat_impl->caps_lock_led =
|
|
xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS);
|
|
seat_impl->num_lock_led =
|
|
xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM);
|
|
seat_impl->scroll_lock_led =
|
|
xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL);
|
|
|
|
meta_seat_impl_sync_leds_in_impl (seat_impl);
|
|
meta_keymap_native_update_in_impl (seat_impl->keymap,
|
|
seat_impl,
|
|
seat_impl->xkb);
|
|
|
|
g_rw_lock_writer_unlock (&seat_impl->state_lock);
|
|
}
|
|
|
|
static gboolean
|
|
release_devices (GTask *task)
|
|
{
|
|
MetaSeatImpl *seat_impl = g_task_get_source_object (task);
|
|
|
|
if (seat_impl->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");
|
|
}
|
|
else
|
|
{
|
|
libinput_suspend (seat_impl->libinput);
|
|
process_events (seat_impl);
|
|
|
|
seat_impl->released = TRUE;
|
|
}
|
|
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
/**
|
|
* 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_impl)
|
|
{
|
|
GTask *task;
|
|
|
|
g_return_if_fail (META_IS_SEAT_IMPL (seat_impl));
|
|
|
|
task = g_task_new (seat_impl, NULL, NULL, NULL);
|
|
meta_seat_impl_run_input_task (seat_impl, task,
|
|
(GSourceFunc) release_devices);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gboolean
|
|
reclaim_devices (GTask *task)
|
|
{
|
|
MetaSeatImpl *seat_impl = g_task_get_source_object (task);
|
|
|
|
if (seat_impl->released)
|
|
{
|
|
libinput_resume (seat_impl->libinput);
|
|
meta_seat_impl_update_xkb_state_in_impl (seat_impl);
|
|
process_events (seat_impl);
|
|
|
|
seat_impl->released = FALSE;
|
|
}
|
|
else
|
|
{
|
|
g_warning ("Spurious call to meta_seat_impl_reclaim_devices() without "
|
|
"previous call to meta_seat_impl_release_devices");
|
|
}
|
|
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
/**
|
|
* 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_impl)
|
|
{
|
|
GTask *task;
|
|
|
|
g_return_if_fail (META_IS_SEAT_IMPL (seat_impl));
|
|
|
|
task = g_task_new (seat_impl, NULL, NULL, NULL);
|
|
meta_seat_impl_run_input_task (seat_impl, task, (GSourceFunc) reclaim_devices);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gboolean
|
|
set_keyboard_map (GTask *task)
|
|
{
|
|
MetaSeatImpl *seat_impl = g_task_get_source_object (task);
|
|
struct xkb_keymap *xkb_keymap = g_task_get_task_data (task);
|
|
MetaKeymapNative *keymap;
|
|
|
|
keymap = seat_impl->keymap;
|
|
meta_keymap_native_set_keyboard_map_in_impl (keymap, xkb_keymap);
|
|
|
|
meta_seat_impl_update_xkb_state_in_impl (seat_impl);
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
/**
|
|
* meta_seat_impl_set_keyboard_map: (skip)
|
|
* @seat_impl: 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_impl,
|
|
struct xkb_keymap *xkb_keymap)
|
|
{
|
|
GTask *task;
|
|
|
|
g_return_if_fail (META_IS_SEAT_IMPL (seat_impl));
|
|
g_return_if_fail (xkb_keymap != NULL);
|
|
|
|
task = g_task_new (seat_impl, NULL, NULL, NULL);
|
|
g_task_set_task_data (task,
|
|
xkb_keymap_ref (xkb_keymap),
|
|
(GDestroyNotify) xkb_keymap_unref);
|
|
meta_seat_impl_run_input_task (seat_impl, task, (GSourceFunc) set_keyboard_map);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gboolean
|
|
set_keyboard_layout_index (GTask *task)
|
|
{
|
|
MetaSeatImpl *seat_impl = g_task_get_source_object (task);
|
|
xkb_layout_index_t idx = GPOINTER_TO_UINT (g_task_get_task_data (task));
|
|
xkb_mod_mask_t depressed_mods;
|
|
xkb_mod_mask_t latched_mods;
|
|
xkb_mod_mask_t locked_mods;
|
|
struct xkb_state *state;
|
|
|
|
g_rw_lock_writer_lock (&seat_impl->state_lock);
|
|
|
|
state = seat_impl->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);
|
|
meta_keymap_native_update_in_impl (seat_impl->keymap,
|
|
seat_impl,
|
|
seat_impl->xkb);
|
|
|
|
seat_impl->layout_idx = idx;
|
|
|
|
g_rw_lock_writer_unlock (&seat_impl->state_lock);
|
|
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
/**
|
|
* meta_seat_impl_set_keyboard_layout_index: (skip)
|
|
* @seat_impl: 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_impl,
|
|
xkb_layout_index_t idx)
|
|
{
|
|
GTask *task;
|
|
|
|
g_return_if_fail (META_IS_SEAT_IMPL (seat_impl));
|
|
|
|
task = g_task_new (seat_impl, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, GUINT_TO_POINTER (idx), NULL);
|
|
meta_seat_impl_run_input_task (seat_impl, task,
|
|
(GSourceFunc) set_keyboard_layout_index);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
/**
|
|
* meta_seat_impl_set_keyboard_repeat_in_impl:
|
|
* @seat_impl: 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_in_impl (MetaSeatImpl *seat_impl,
|
|
gboolean repeat,
|
|
uint32_t delay,
|
|
uint32_t interval)
|
|
{
|
|
g_return_if_fail (META_IS_SEAT_IMPL (seat_impl));
|
|
|
|
seat_impl->repeat = repeat;
|
|
seat_impl->repeat_delay = delay;
|
|
seat_impl->repeat_interval = interval;
|
|
}
|
|
|
|
struct xkb_state *
|
|
meta_seat_impl_get_xkb_state_in_impl (MetaSeatImpl *seat_impl)
|
|
{
|
|
return seat_impl->xkb;
|
|
}
|
|
|
|
MetaBarrierManagerNative *
|
|
meta_seat_impl_get_barrier_manager (MetaSeatImpl *seat_impl)
|
|
{
|
|
return seat_impl->barrier_manager;
|
|
}
|
|
|
|
static gboolean
|
|
set_pointer_constraint (GTask *task)
|
|
{
|
|
MetaSeatImpl *seat_impl = g_task_get_source_object (task);
|
|
MetaPointerConstraintImpl *constraint_impl = g_task_get_task_data (task);
|
|
|
|
if (!g_set_object (&seat_impl->pointer_constraint, constraint_impl))
|
|
return G_SOURCE_REMOVE;
|
|
|
|
if (constraint_impl)
|
|
{
|
|
meta_pointer_constraint_impl_ensure_constrained (constraint_impl,
|
|
seat_impl->core_pointer);
|
|
}
|
|
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_set_pointer_constraint (MetaSeatImpl *seat_impl,
|
|
MetaPointerConstraintImpl *constraint_impl)
|
|
{
|
|
GTask *task;
|
|
|
|
g_return_if_fail (META_IS_SEAT_IMPL (seat_impl));
|
|
|
|
task = g_task_new (seat_impl, NULL, NULL, NULL);
|
|
if (constraint_impl)
|
|
g_task_set_task_data (task, g_object_ref (constraint_impl), g_object_unref);
|
|
meta_seat_impl_run_input_task (seat_impl, task,
|
|
(GSourceFunc) set_pointer_constraint);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
ensure_pointer_onscreen (MetaSeatImpl *seat_impl)
|
|
{
|
|
int i, candidate = -1;
|
|
int nearest_monitor_x, nearest_monitor_y, min_distance = G_MAXINT;
|
|
cairo_rectangle_int_t monitor_rect;
|
|
graphene_point_t coords;
|
|
|
|
if (!meta_seat_impl_query_state (seat_impl,
|
|
seat_impl->core_pointer, NULL,
|
|
&coords, NULL))
|
|
return;
|
|
|
|
/* Pointer is in a view */
|
|
if (meta_viewport_info_get_view_at (seat_impl->viewports,
|
|
coords.x, coords.y) >= 0)
|
|
return;
|
|
|
|
/* Find nearest view */
|
|
for (i = 0; i < meta_viewport_info_get_num_views (seat_impl->viewports); i++)
|
|
{
|
|
meta_viewport_info_get_view_info (seat_impl->viewports, i,
|
|
&monitor_rect, NULL);
|
|
nearest_monitor_x = MIN (ABS (coords.x - monitor_rect.x),
|
|
ABS (coords.x -
|
|
monitor_rect.x + monitor_rect.width));
|
|
nearest_monitor_y = MIN (ABS (coords.y - monitor_rect.y),
|
|
ABS (coords.y -
|
|
monitor_rect.y + monitor_rect.height));
|
|
if (nearest_monitor_x < min_distance ||
|
|
nearest_monitor_y < min_distance)
|
|
{
|
|
min_distance = MIN (nearest_monitor_x, nearest_monitor_y);
|
|
candidate = i;
|
|
}
|
|
}
|
|
|
|
if (candidate < 0)
|
|
return;
|
|
|
|
/* Calculate new coordinates on nearest view */
|
|
meta_viewport_info_get_view_info (seat_impl->viewports,
|
|
candidate,
|
|
&monitor_rect, NULL);
|
|
coords.x = CLAMP (coords.x, monitor_rect.x,
|
|
monitor_rect.x + monitor_rect.width - 1);
|
|
coords.y = CLAMP (coords.y, monitor_rect.y,
|
|
monitor_rect.y + monitor_rect.height - 1);
|
|
|
|
notify_absolute_motion_in_impl (seat_impl->core_pointer, 0,
|
|
coords.x, coords.y, NULL);
|
|
}
|
|
|
|
static gboolean
|
|
set_viewports (GTask *task)
|
|
{
|
|
MetaSeatImpl *seat_impl = g_task_get_source_object (task);
|
|
MetaViewportInfo *viewports = g_task_get_task_data (task);
|
|
|
|
g_set_object (&seat_impl->viewports, viewports);
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
ensure_pointer_onscreen (seat_impl);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_set_viewports (MetaSeatImpl *seat_impl,
|
|
MetaViewportInfo *viewports)
|
|
{
|
|
GTask *task;
|
|
|
|
g_return_if_fail (META_IS_SEAT_IMPL (seat_impl));
|
|
|
|
task = g_task_new (seat_impl, NULL, NULL, NULL);
|
|
g_task_set_task_data (task, g_object_ref (viewports), g_object_unref);
|
|
meta_seat_impl_run_input_task (seat_impl, task,
|
|
(GSourceFunc) set_viewports);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
MetaSeatImpl *
|
|
meta_seat_impl_new (MetaSeatNative *seat_native,
|
|
const char *seat_id,
|
|
MetaSeatNativeFlag flags)
|
|
{
|
|
return g_initable_new (META_TYPE_SEAT_IMPL,
|
|
NULL, NULL,
|
|
"seat", seat_native,
|
|
"seat-id", seat_id,
|
|
"flags", flags,
|
|
NULL);
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_notify_kbd_a11y_flags_changed_in_impl (MetaSeatImpl *seat_impl,
|
|
MetaKeyboardA11yFlags new_flags,
|
|
MetaKeyboardA11yFlags what_changed)
|
|
{
|
|
MetaInputSettings *input_settings;
|
|
GValue values[] = { G_VALUE_INIT, G_VALUE_INIT };
|
|
|
|
input_settings = seat_impl->input_settings;
|
|
meta_input_settings_notify_kbd_a11y_change (input_settings,
|
|
new_flags, what_changed);
|
|
g_value_init (&values[0], G_TYPE_UINT);
|
|
g_value_set_uint (&values[0], new_flags);
|
|
g_value_init (&values[1], G_TYPE_UINT);
|
|
g_value_set_uint (&values[1], what_changed);
|
|
|
|
emit_signal (seat_impl, signals[KBD_A11Y_FLAGS_CHANGED],
|
|
values, G_N_ELEMENTS (values));
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_notify_kbd_a11y_mods_state_changed_in_impl (MetaSeatImpl *seat_impl,
|
|
xkb_mod_mask_t new_latched_mods,
|
|
xkb_mod_mask_t new_locked_mods)
|
|
{
|
|
GValue values[] = { G_VALUE_INIT, G_VALUE_INIT };
|
|
|
|
g_value_init (&values[0], G_TYPE_UINT);
|
|
g_value_set_uint (&values[0], new_latched_mods);
|
|
g_value_init (&values[1], G_TYPE_UINT);
|
|
g_value_set_uint (&values[1], new_locked_mods);
|
|
|
|
emit_signal (seat_impl, signals[KBD_A11Y_MODS_STATE_CHANGED],
|
|
values, G_N_ELEMENTS (values));
|
|
}
|
|
|
|
void
|
|
meta_seat_impl_notify_bell_in_impl (MetaSeatImpl *seat_impl)
|
|
{
|
|
emit_signal (seat_impl, signals[BELL], NULL, 0);
|
|
}
|
|
|
|
MetaInputSettings *
|
|
meta_seat_impl_get_input_settings (MetaSeatImpl *seat_impl)
|
|
{
|
|
return seat_impl->input_settings;
|
|
}
|