clutter/evdev: implement slow keys support

Delay emitting clutter key press events when slow key is enabled.

https://bugzilla.gnome.org/show_bug.cgi?id=788564
This commit is contained in:
Olivier Fourdan 2017-10-12 17:16:00 +02:00
parent 76b064cffc
commit fa28481736
2 changed files with 153 additions and 0 deletions

View File

@ -54,6 +54,16 @@ enum {
static GParamSpec *obj_props[N_PROPS] = { 0 };
typedef struct _SlowKeysEventPending
{
ClutterInputDeviceEvdev *device;
ClutterEvent *event;
ClutterEmitInputDeviceEvent emit_event_func;
guint timer;
} SlowKeysEventPending;
static void clear_slow_keys (ClutterInputDeviceEvdev *device);
static void
clutter_input_device_evdev_finalize (GObject *object)
{
@ -67,6 +77,8 @@ clutter_input_device_evdev_finalize (GObject *object)
_clutter_device_manager_evdev_release_device_id (manager_evdev, device);
clear_slow_keys (device_evdev);
G_OBJECT_CLASS (clutter_input_device_evdev_parent_class)->finalize (object);
}
@ -209,6 +221,130 @@ clutter_input_device_evdev_is_grouped (ClutterInputDevice *device,
libinput_device_get_device_group (other_libinput_device);
}
static void
clutter_input_device_evdev_bell_notify (void)
{
ClutterBackend *backend;
backend = clutter_get_default_backend ();
clutter_backend_bell_notify (backend);
}
static void
clutter_input_device_evdev_free_pending_slow_key (gpointer data)
{
SlowKeysEventPending *slow_keys_event = data;
clutter_event_free (slow_keys_event->event);
if (slow_keys_event->timer)
g_source_remove (slow_keys_event->timer);
g_free (slow_keys_event);
}
static void
clear_slow_keys (ClutterInputDeviceEvdev *device)
{
g_list_free_full (device->slow_keys_list, clutter_input_device_evdev_free_pending_slow_key);
g_list_free (device->slow_keys_list);
device->slow_keys_list = NULL;
}
static guint
get_slow_keys_delay (ClutterInputDevice *device)
{
ClutterKbdA11ySettings a11y_settings;
clutter_device_manager_get_kbd_a11y_settings (device->device_manager,
&a11y_settings);
/* Settings use int, we use uint, make sure we dont go negative */
return MAX (0, a11y_settings.slowkeys_delay);
}
static gboolean
trigger_slow_keys (gpointer data)
{
SlowKeysEventPending *slow_keys_event = data;
ClutterInputDeviceEvdev *device = slow_keys_event->device;
ClutterKeyEvent *key_event = (ClutterKeyEvent *) slow_keys_event->event;
/* Alter timestamp and emit the event */
key_event->time = us2ms (g_get_monotonic_time ());
slow_keys_event->emit_event_func (slow_keys_event->event,
CLUTTER_INPUT_DEVICE (device));
/* Then remote the pending event */
device->slow_keys_list = g_list_remove (device->slow_keys_list, slow_keys_event);
clutter_input_device_evdev_free_pending_slow_key (slow_keys_event);
if (device->a11y_flags & CLUTTER_A11Y_SLOW_KEYS_BEEP_ACCEPT)
clutter_input_device_evdev_bell_notify ();
return G_SOURCE_REMOVE;
}
static gint
find_pending_event_by_keycode (gconstpointer a,
gconstpointer b)
{
const SlowKeysEventPending *pa = a;
const ClutterKeyEvent *ka = (ClutterKeyEvent *) pa->event;
const ClutterKeyEvent *kb = b;
return kb->hardware_keycode - ka->hardware_keycode;
}
static void
start_slow_keys (ClutterEvent *event,
ClutterInputDeviceEvdev *device,
ClutterEmitInputDeviceEvent emit_event_func)
{
SlowKeysEventPending *slow_keys_event;
ClutterKeyEvent *key_event = (ClutterKeyEvent *) event;
/* Synthetic key events are for autorepeat, ignore those... */
if (key_event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC)
return;
slow_keys_event = g_new0 (SlowKeysEventPending, 1);
slow_keys_event->device = device;
slow_keys_event->event = clutter_event_copy (event);
slow_keys_event->emit_event_func = emit_event_func;
slow_keys_event->timer =
clutter_threads_add_timeout (get_slow_keys_delay (CLUTTER_INPUT_DEVICE (device)),
trigger_slow_keys,
slow_keys_event);
device->slow_keys_list = g_list_append (device->slow_keys_list, slow_keys_event);
if (device->a11y_flags & CLUTTER_A11Y_SLOW_KEYS_BEEP_PRESS)
clutter_input_device_evdev_bell_notify ();
}
static void
stop_slow_keys (ClutterEvent *event,
ClutterInputDeviceEvdev *device,
ClutterEmitInputDeviceEvent emit_event_func)
{
GList *item;
/* Check if we have a slow key event queued for this key event */
item = g_list_find_custom (device->slow_keys_list, event, find_pending_event_by_keycode);
if (item)
{
SlowKeysEventPending *slow_keys_event = item->data;
device->slow_keys_list = g_list_delete_link (device->slow_keys_list, item);
clutter_input_device_evdev_free_pending_slow_key (slow_keys_event);
if (device->a11y_flags & CLUTTER_A11Y_SLOW_KEYS_BEEP_REJECT)
clutter_input_device_evdev_bell_notify ();
return;
}
/* If no key press event was pending, just emit the key release as-is */
emit_event_func (event, CLUTTER_INPUT_DEVICE (device));
}
static void
clutter_input_device_evdev_process_kbd_a11y_event (ClutterEvent *event,
ClutterInputDevice *device,
@ -219,6 +355,17 @@ clutter_input_device_evdev_process_kbd_a11y_event (ClutterEvent *e
if (!device_evdev->a11y_flags & CLUTTER_A11Y_KEYBOARD_ENABLED)
goto emit_event;
if ((device_evdev->a11y_flags & CLUTTER_A11Y_SLOW_KEYS_ENABLED) &&
(get_slow_keys_delay (device) != 0))
{
if (event->type == CLUTTER_KEY_PRESS)
start_slow_keys (event, device_evdev, emit_event_func);
else if (event->type == CLUTTER_KEY_RELEASE)
stop_slow_keys (event, device_evdev, emit_event_func);
return;
}
emit_event:
emit_event_func (event, device);
}
@ -227,6 +374,11 @@ void
clutter_input_device_evdev_apply_kbd_a11y_settings (ClutterInputDeviceEvdev *device,
ClutterKbdA11ySettings *settings)
{
ClutterKeyboardA11yFlags changed_flags = (device->a11y_flags ^ settings->controls);
if (changed_flags & (CLUTTER_A11Y_KEYBOARD_ENABLED | CLUTTER_A11Y_SLOW_KEYS_ENABLED))
clear_slow_keys (device);
/* Keep our own copy of keyboard a11y features flags to see what changes */
device->a11y_flags = settings->controls;
}

View File

@ -73,6 +73,7 @@ struct _ClutterInputDeviceEvdev
/* Keyboard a11y */
ClutterKeyboardA11yFlags a11y_flags;
GList *slow_keys_list;
};
GType _clutter_input_device_evdev_get_type (void) G_GNUC_CONST;