[mutter] clutter: Add pointer accessibility features
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter] clutter: Add pointer accessibility features
- Date: Thu, 6 Jun 2019 11:34:36 +0000 (UTC)
commit db11a37a68f469e374c3e353cf20dca701f053ab
Author: Olivier Fourdan <ofourdan redhat com>
Date: Wed Mar 6 09:04:37 2019 +0100
clutter: Add pointer accessibility features
Add support for click assist, namely simulated secondary click (on a
long primary button press) and hover click support (simulate a click when
the pointer remains static for some time).
https://gitlab.gnome.org/GNOME/mutter/merge_requests/512
clutter/clutter/clutter-device-manager-private.h | 17 +
clutter/clutter/clutter-device-manager.c | 88 +++
clutter/clutter/clutter-device-manager.h | 34 ++
.../clutter/clutter-input-pointer-a11y-private.h | 42 ++
clutter/clutter/clutter-input-pointer-a11y.c | 669 +++++++++++++++++++++
clutter/clutter/meson.build | 2 +
6 files changed, 852 insertions(+)
---
diff --git a/clutter/clutter/clutter-device-manager-private.h
b/clutter/clutter/clutter-device-manager-private.h
index d08e66ba4..712dd404a 100644
--- a/clutter/clutter/clutter-device-manager-private.h
+++ b/clutter/clutter/clutter-device-manager-private.h
@@ -69,6 +69,22 @@ typedef struct _ClutterTouchInfo
gfloat current_y;
} ClutterTouchInfo;
+typedef struct _ClutterPtrA11yData
+{
+ int n_btn_pressed;
+ float current_x;
+ float current_y;
+
+ float dwell_x;
+ float dwell_y;
+ gboolean dwell_drag_started;
+ gboolean dwell_gesture_started;
+ guint dwell_timer;
+
+ guint secondary_click_timer;
+ gboolean secondary_click_triggered;
+} ClutterPtrA11yData;
+
struct _ClutterInputDevice
{
GObject parent_instance;
@@ -146,6 +162,7 @@ struct _ClutterInputDevice
/* Accessiblity */
ClutterVirtualInputDevice *accessibility_virtual_device;
+ ClutterPtrA11yData *ptr_a11y_data;
};
typedef void (*ClutterEmitInputDeviceEvent) (ClutterEvent *event,
diff --git a/clutter/clutter/clutter-device-manager.c b/clutter/clutter/clutter-device-manager.c
index 4e527182b..fd485a0ae 100644
--- a/clutter/clutter/clutter-device-manager.c
+++ b/clutter/clutter/clutter-device-manager.c
@@ -47,6 +47,7 @@
#include "clutter-stage-private.h"
#include "clutter-virtual-input-device.h"
#include "clutter-input-device-tool.h"
+#include "clutter-input-pointer-a11y-private.h"
struct _ClutterDeviceManagerPrivate
{
@@ -55,6 +56,8 @@ struct _ClutterDeviceManagerPrivate
/* Keyboard a11y */
ClutterKbdA11ySettings kbd_a11y_settings;
+ /* Pointer a11y */
+ ClutterPointerA11ySettings pointer_a11y_settings;
};
enum
@@ -643,3 +646,88 @@ clutter_device_manager_get_kbd_a11y_settings (ClutterDeviceManager *device_man
*settings = device_manager->priv->kbd_a11y_settings;
}
+
+static gboolean
+are_pointer_a11y_settings_equal (ClutterPointerA11ySettings *a,
+ ClutterPointerA11ySettings *b)
+{
+ return (memcmp (a, b, sizeof (ClutterPointerA11ySettings)) == 0);
+}
+
+static void
+clutter_device_manager_enable_pointer_a11y (ClutterDeviceManager *device_manager)
+{
+ ClutterInputDevice *core_pointer;
+
+ core_pointer = clutter_device_manager_get_core_device (device_manager,
+ CLUTTER_POINTER_DEVICE);
+
+ _clutter_input_pointer_a11y_add_device (core_pointer);
+}
+
+static void
+clutter_device_manager_disable_pointer_a11y (ClutterDeviceManager *device_manager)
+{
+ ClutterInputDevice *core_pointer;
+
+ core_pointer = clutter_device_manager_get_core_device (device_manager,
+ CLUTTER_POINTER_DEVICE);
+
+ _clutter_input_pointer_a11y_remove_device (core_pointer);
+}
+
+/**
+ * clutter_device_manager_set_pointer_a11y_settings:
+ * @device_manager: a #ClutterDeviceManager
+ * @settings: a pointer to a #ClutterPointerA11ySettings
+ *
+ * Sets the pointer accessibility settings
+ **/
+void
+clutter_device_manager_set_pointer_a11y_settings (ClutterDeviceManager *device_manager,
+ ClutterPointerA11ySettings *settings)
+{
+ g_return_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager));
+
+ if (are_pointer_a11y_settings_equal (&device_manager->priv->pointer_a11y_settings, settings))
+ return;
+
+ if (device_manager->priv->pointer_a11y_settings.controls == 0 && settings->controls != 0)
+ clutter_device_manager_enable_pointer_a11y (device_manager);
+ else if (device_manager->priv->pointer_a11y_settings.controls != 0 && settings->controls == 0)
+ clutter_device_manager_disable_pointer_a11y (device_manager);
+
+ device_manager->priv->pointer_a11y_settings = *settings;
+}
+
+/**
+ * clutter_device_manager_get_pointer_a11y_settings:
+ * @device_manager: a #ClutterDeviceManager
+ * @settings: a pointer to a #ClutterPointerA11ySettings
+ *
+ * Gets the current pointer accessibility settings
+ **/
+void
+clutter_device_manager_get_pointer_a11y_settings (ClutterDeviceManager *device_manager,
+ ClutterPointerA11ySettings *settings)
+{
+ g_return_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager));
+
+ *settings = device_manager->priv->pointer_a11y_settings;
+}
+
+/**
+ * clutter_device_manager_set_pointer_a11y_dwell_click_type:
+ * @device_manager: a #ClutterDeviceManager
+ * @click_type: type of click as #ClutterPointerA11yDwellClickType
+ *
+ * Sets the dwell click type
+ **/
+void
+clutter_device_manager_set_pointer_a11y_dwell_click_type (ClutterDeviceManager *device_manager,
+ ClutterPointerA11yDwellClickType click_type)
+{
+ g_return_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager));
+
+ device_manager->priv->pointer_a11y_settings.dwell_click_type = click_type;
+}
diff --git a/clutter/clutter/clutter-device-manager.h b/clutter/clutter/clutter-device-manager.h
index 1cbf0307b..5fbd3952b 100644
--- a/clutter/clutter/clutter-device-manager.h
+++ b/clutter/clutter/clutter-device-manager.h
@@ -73,6 +73,27 @@ typedef struct _ClutterKbdA11ySettings
gint mousekeys_accel_time;
} ClutterKbdA11ySettings;
+/**
+ * ClutterPointerA11ySettings:
+ *
+ * The #ClutterPointerA11ySettings structure contains pointer accessibility
+ * settings
+ *
+ */
+typedef struct _ClutterPointerA11ySettings
+{
+ ClutterPointerA11yFlags controls;
+ ClutterPointerA11yDwellClickType dwell_click_type;
+ ClutterPointerA11yDwellMode dwell_mode;
+ ClutterPointerA11yDwellDirection dwell_gesture_single;
+ ClutterPointerA11yDwellDirection dwell_gesture_double;
+ ClutterPointerA11yDwellDirection dwell_gesture_drag;
+ ClutterPointerA11yDwellDirection dwell_gesture_secondary;
+ gint secondary_click_delay;
+ gint dwell_delay;
+ gint dwell_threshold;
+} ClutterPointerA11ySettings;
+
/**
* ClutterDeviceManager:
*
@@ -152,10 +173,23 @@ ClutterVirtualDeviceType clutter_device_manager_get_supported_virtual_device_typ
CLUTTER_EXPORT
void clutter_device_manager_set_kbd_a11y_settings (ClutterDeviceManager *device_manager,
ClutterKbdA11ySettings *settings);
+
CLUTTER_EXPORT
void clutter_device_manager_get_kbd_a11y_settings (ClutterDeviceManager *device_manager,
ClutterKbdA11ySettings *settings);
+CLUTTER_EXPORT
+void clutter_device_manager_set_pointer_a11y_settings (ClutterDeviceManager *device_manager,
+ ClutterPointerA11ySettings *settings);
+
+CLUTTER_EXPORT
+void clutter_device_manager_get_pointer_a11y_settings (ClutterDeviceManager *device_manager,
+ ClutterPointerA11ySettings *settings);
+
+CLUTTER_EXPORT
+void clutter_device_manager_set_pointer_a11y_dwell_click_type (ClutterDeviceManager
*device_manager,
+ ClutterPointerA11yDwellClickType click_type);
+
G_END_DECLS
#endif /* __CLUTTER_DEVICE_MANAGER_H__ */
diff --git a/clutter/clutter/clutter-input-pointer-a11y-private.h
b/clutter/clutter/clutter-input-pointer-a11y-private.h
new file mode 100644
index 000000000..6648909fc
--- /dev/null
+++ b/clutter/clutter/clutter-input-pointer-a11y-private.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Olivier Fourdan <ofourdan redhat com>
+ */
+
+#ifndef __CLUTTER_INPUT_POINTER_A11Y_H__
+#define __CLUTTER_INPUT_POINTER_A11Y_H__
+
+#include <clutter/clutter-types.h>
+#include "clutter-enum-types.h"
+
+G_BEGIN_DECLS
+
+void _clutter_input_pointer_a11y_add_device (ClutterInputDevice *device);
+void _clutter_input_pointer_a11y_remove_device (ClutterInputDevice *device);
+void _clutter_input_pointer_a11y_on_motion_event (ClutterInputDevice *device,
+ float x,
+ float y);
+void _clutter_input_pointer_a11y_on_button_event (ClutterInputDevice *device,
+ int button,
+ gboolean pressed);
+gboolean _clutter_is_input_pointer_a11y_enabled (ClutterInputDevice *device);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_INPUT_POINTER_A11Y_H__ */
diff --git a/clutter/clutter/clutter-input-pointer-a11y.c b/clutter/clutter/clutter-input-pointer-a11y.c
new file mode 100644
index 000000000..e55e0a6a9
--- /dev/null
+++ b/clutter/clutter/clutter-input-pointer-a11y.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2019 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Olivier Fourdan <ofourdan redhat com>
+ *
+ * This reimplements in Clutter the same behavior as mousetweaks original
+ * implementation by Gerd Kohlberger <gerdko gmail com>
+ * mousetweaks Copyright (C) 2007-2010 Gerd Kohlberger <gerdko gmail com>
+ */
+
+#include "clutter-build-config.h"
+
+#include "clutter-device-manager.h"
+#include "clutter-device-manager-private.h"
+#include "clutter-enum-types.h"
+#include "clutter-input-device.h"
+#include "clutter-input-pointer-a11y-private.h"
+#include "clutter-main.h"
+#include "clutter-virtual-input-device.h"
+
+static gboolean
+is_secondary_click_enabled (ClutterInputDevice *device)
+{
+ ClutterPointerA11ySettings settings;
+
+ clutter_device_manager_get_pointer_a11y_settings (device->device_manager, &settings);
+
+ return (settings.controls & CLUTTER_A11Y_SECONDARY_CLICK_ENABLED);
+}
+
+static gboolean
+is_dwell_click_enabled (ClutterInputDevice *device)
+{
+ ClutterPointerA11ySettings settings;
+
+ clutter_device_manager_get_pointer_a11y_settings (device->device_manager, &settings);
+
+ return (settings.controls & CLUTTER_A11Y_DWELL_ENABLED);
+}
+
+static unsigned int
+get_secondary_click_delay (ClutterInputDevice *device)
+{
+ ClutterPointerA11ySettings settings;
+
+ clutter_device_manager_get_pointer_a11y_settings (device->device_manager, &settings);
+
+ return settings.secondary_click_delay;
+}
+
+static unsigned int
+get_dwell_delay (ClutterInputDevice *device)
+{
+ ClutterPointerA11ySettings settings;
+
+ clutter_device_manager_get_pointer_a11y_settings (device->device_manager, &settings);
+
+ return settings.dwell_delay;
+}
+
+static unsigned int
+get_dwell_threshold (ClutterInputDevice *device)
+{
+ ClutterPointerA11ySettings settings;
+
+ clutter_device_manager_get_pointer_a11y_settings (device->device_manager, &settings);
+
+ return settings.dwell_threshold;
+}
+
+static ClutterPointerA11yDwellMode
+get_dwell_mode (ClutterInputDevice *device)
+{
+ ClutterPointerA11ySettings settings;
+
+ clutter_device_manager_get_pointer_a11y_settings (device->device_manager, &settings);
+
+ return settings.dwell_mode;
+}
+
+static ClutterPointerA11yDwellClickType
+get_dwell_click_type (ClutterInputDevice *device)
+{
+ ClutterPointerA11ySettings settings;
+
+ clutter_device_manager_get_pointer_a11y_settings (device->device_manager, &settings);
+#
+ return settings.dwell_click_type;
+}
+
+static ClutterPointerA11yDwellClickType
+get_dwell_click_type_for_direction (ClutterInputDevice *device,
+ ClutterPointerA11yDwellDirection direction)
+{
+ ClutterPointerA11ySettings settings;
+
+ clutter_device_manager_get_pointer_a11y_settings (device->device_manager, &settings);
+
+ if (direction == settings.dwell_gesture_single)
+ return CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY;
+ else if (direction == settings.dwell_gesture_double)
+ return CLUTTER_A11Y_DWELL_CLICK_TYPE_DOUBLE;
+ else if (direction == settings.dwell_gesture_drag)
+ return CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG;
+ else if (direction == settings.dwell_gesture_secondary)
+ return CLUTTER_A11Y_DWELL_CLICK_TYPE_SECONDARY;
+
+ return CLUTTER_A11Y_DWELL_CLICK_TYPE_NONE;
+}
+
+static void
+emit_button_press (ClutterInputDevice *device,
+ gint button)
+{
+ clutter_virtual_input_device_notify_button (device->accessibility_virtual_device,
+ g_get_monotonic_time (),
+ button,
+ CLUTTER_BUTTON_STATE_PRESSED);
+}
+
+static void
+emit_button_release (ClutterInputDevice *device,
+ gint button)
+{
+ clutter_virtual_input_device_notify_button (device->accessibility_virtual_device,
+ g_get_monotonic_time (),
+ button,
+ CLUTTER_BUTTON_STATE_RELEASED);
+}
+
+static void
+emit_button_click (ClutterInputDevice *device,
+ gint button)
+{
+ emit_button_press (device, button);
+ emit_button_release (device, button);
+}
+
+static void
+restore_dwell_position (ClutterInputDevice *device)
+{
+ clutter_virtual_input_device_notify_absolute_motion (device->accessibility_virtual_device,
+ g_get_monotonic_time (),
+ device->ptr_a11y_data->dwell_x,
+ device->ptr_a11y_data->dwell_y);
+}
+
+static gboolean
+trigger_secondary_click (gpointer data)
+{
+ ClutterInputDevice *device = data;
+
+ device->ptr_a11y_data->secondary_click_triggered = TRUE;
+ device->ptr_a11y_data->secondary_click_timer = 0;
+
+ g_signal_emit_by_name (device->device_manager,
+ "ptr-a11y-timeout-stopped",
+ device,
+ CLUTTER_A11Y_TIMEOUT_TYPE_SECONDARY_CLICK);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+start_secondary_click_timeout (ClutterInputDevice *device)
+{
+ unsigned int delay = get_secondary_click_delay (device);
+
+ device->ptr_a11y_data->secondary_click_timer =
+ clutter_threads_add_timeout (delay, trigger_secondary_click, device);
+
+ g_signal_emit_by_name (device->device_manager,
+ "ptr-a11y-timeout-started",
+ device,
+ CLUTTER_A11Y_TIMEOUT_TYPE_SECONDARY_CLICK,
+ delay);
+}
+
+static void
+stop_secondary_click_timeout (ClutterInputDevice *device)
+{
+ if (device->ptr_a11y_data->secondary_click_timer)
+ {
+ g_source_remove (device->ptr_a11y_data->secondary_click_timer);
+ device->ptr_a11y_data->secondary_click_timer = 0;
+
+ g_signal_emit_by_name (device->device_manager,
+ "ptr-a11y-timeout-stopped",
+ device,
+ CLUTTER_A11Y_TIMEOUT_TYPE_SECONDARY_CLICK);
+ }
+ device->ptr_a11y_data->secondary_click_triggered = FALSE;
+}
+
+static gboolean
+pointer_has_moved (ClutterInputDevice *device)
+{
+ float dx, dy;
+ gint threshold;
+
+ dx = device->ptr_a11y_data->dwell_x - device->ptr_a11y_data->current_x;
+ dy = device->ptr_a11y_data->dwell_y - device->ptr_a11y_data->current_y;
+ threshold = get_dwell_threshold (device);
+
+ /* Pythagorean theorem */
+ return ((dx * dx) + (dy * dy)) > (threshold * threshold);
+}
+
+static gboolean
+is_secondary_click_pending (ClutterInputDevice *device)
+{
+ return device->ptr_a11y_data->secondary_click_timer != 0;
+}
+
+static gboolean
+is_secondary_click_triggered (ClutterInputDevice *device)
+{
+ return device->ptr_a11y_data->secondary_click_triggered;
+}
+
+static gboolean
+is_dwell_click_pending (ClutterInputDevice *device)
+{
+ return device->ptr_a11y_data->dwell_timer != 0;
+}
+
+static gboolean
+is_dwell_dragging (ClutterInputDevice *device)
+{
+ return device->ptr_a11y_data->dwell_drag_started;
+}
+
+static gboolean
+is_dwell_gesturing (ClutterInputDevice *device)
+{
+ return device->ptr_a11y_data->dwell_gesture_started;
+}
+
+static gboolean
+has_button_pressed (ClutterInputDevice *device)
+{
+ return device->ptr_a11y_data->n_btn_pressed > 0;
+}
+
+static gboolean
+should_start_secondary_click_timeout (ClutterInputDevice *device)
+{
+ return !is_dwell_dragging (device);
+}
+
+static gboolean
+should_start_dwell (ClutterInputDevice *device)
+{
+ /* We should trigger a dwell if we've not already started one, and if
+ * no button is currently pressed or we are in the middle of a dwell
+ * drag action.
+ */
+ return !is_dwell_click_pending (device) &&
+ (is_dwell_dragging (device) ||
+ !has_button_pressed (device));
+}
+
+static gboolean
+should_stop_dwell (ClutterInputDevice *device)
+{
+ /* We should stop a dwell if the motion exceeds the threshold, unless
+ * we've started a gesture, because we want to keep the original dwell
+ * location to both detect a gesture and restore the original pointer
+ * location once the gesture is finished.
+ */
+ return pointer_has_moved (device) &&
+ !is_dwell_gesturing (device);
+}
+
+
+static gboolean
+should_update_dwell_position (ClutterInputDevice *device)
+{
+ return !is_dwell_gesturing (device) &&
+ !is_dwell_click_pending (device) &&
+ !is_secondary_click_pending (device);
+}
+
+static void
+update_dwell_click_type (ClutterInputDevice *device)
+{
+ ClutterPointerA11ySettings settings;
+ ClutterPointerA11yDwellClickType dwell_click_type;
+
+ clutter_device_manager_get_pointer_a11y_settings (device->device_manager, &settings);
+
+ dwell_click_type = settings.dwell_click_type;
+ switch (dwell_click_type)
+ {
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_DOUBLE:
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_SECONDARY:
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_MIDDLE:
+ dwell_click_type = CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY;
+ break;
+
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG:
+ if (!is_dwell_dragging (device))
+ dwell_click_type = CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY;
+ break;
+
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY:
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_NONE:
+ default:
+ break;
+ }
+
+ if (dwell_click_type != settings.dwell_click_type)
+ {
+ settings.dwell_click_type = dwell_click_type;
+ clutter_device_manager_set_pointer_a11y_settings (device->device_manager,
+ &settings);
+
+ g_signal_emit_by_name (device->device_manager,
+ "ptr-a11y-dwell-click-type-changed",
+ dwell_click_type);
+ }
+}
+
+static void
+emit_dwell_click (ClutterInputDevice *device,
+ ClutterPointerA11yDwellClickType dwell_click_type)
+{
+ switch (dwell_click_type)
+ {
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY:
+ emit_button_click (device, CLUTTER_BUTTON_PRIMARY);
+ break;
+
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_DOUBLE:
+ emit_button_click (device, CLUTTER_BUTTON_PRIMARY);
+ emit_button_click (device, CLUTTER_BUTTON_PRIMARY);
+ break;
+
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG:
+ if (is_dwell_dragging (device))
+ {
+ emit_button_release (device, CLUTTER_BUTTON_PRIMARY);
+ device->ptr_a11y_data->dwell_drag_started = FALSE;
+ }
+ else
+ {
+ emit_button_press (device, CLUTTER_BUTTON_PRIMARY);
+ device->ptr_a11y_data->dwell_drag_started = TRUE;
+ }
+ break;
+
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_SECONDARY:
+ emit_button_click (device, CLUTTER_BUTTON_SECONDARY);
+ break;
+
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_MIDDLE:
+ emit_button_click (device, CLUTTER_BUTTON_MIDDLE);
+ break;
+
+ case CLUTTER_A11Y_DWELL_CLICK_TYPE_NONE:
+ default:
+ break;
+ }
+}
+
+static ClutterPointerA11yDwellDirection
+get_dwell_direction (ClutterInputDevice *device)
+{
+ float dx, dy;
+
+ dx = ABS (device->ptr_a11y_data->dwell_x - device->ptr_a11y_data->current_x);
+ dy = ABS (device->ptr_a11y_data->dwell_y - device->ptr_a11y_data->current_y);
+
+ /* The pointer hasn't moved */
+ if (!pointer_has_moved (device))
+ return CLUTTER_A11Y_DWELL_DIRECTION_NONE;
+
+ if (device->ptr_a11y_data->dwell_x < device->ptr_a11y_data->current_x)
+ {
+ if (dx > dy)
+ return CLUTTER_A11Y_DWELL_DIRECTION_LEFT;
+ }
+ else
+ {
+ if (dx > dy)
+ return CLUTTER_A11Y_DWELL_DIRECTION_RIGHT;
+ }
+
+ if (device->ptr_a11y_data->dwell_y < device->ptr_a11y_data->current_y)
+ return CLUTTER_A11Y_DWELL_DIRECTION_UP;
+
+ return CLUTTER_A11Y_DWELL_DIRECTION_DOWN;
+}
+
+static gboolean
+trigger_clear_dwell_gesture (gpointer data)
+{
+ ClutterInputDevice *device = data;
+
+ device->ptr_a11y_data->dwell_timer = 0;
+ device->ptr_a11y_data->dwell_gesture_started = FALSE;
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+trigger_dwell_gesture (gpointer data)
+{
+ ClutterInputDevice *device = data;
+ ClutterPointerA11yDwellDirection direction;
+ unsigned int delay = get_dwell_delay (device);
+
+ restore_dwell_position (device);
+ direction = get_dwell_direction (device);
+ emit_dwell_click (device,
+ get_dwell_click_type_for_direction (device,
+ direction));
+
+ /* Do not clear the gesture right away, otherwise we'll start another one */
+ device->ptr_a11y_data->dwell_timer =
+ clutter_threads_add_timeout (delay, trigger_clear_dwell_gesture, device);
+
+ g_signal_emit_by_name (device->device_manager,
+ "ptr-a11y-timeout-stopped",
+ device,
+ CLUTTER_A11Y_TIMEOUT_TYPE_GESTURE);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+start_dwell_gesture_timeout (ClutterInputDevice *device)
+{
+ unsigned int delay = get_dwell_delay (device);
+
+ device->ptr_a11y_data->dwell_timer =
+ clutter_threads_add_timeout (delay, trigger_dwell_gesture, device);
+ device->ptr_a11y_data->dwell_gesture_started = TRUE;
+
+ g_signal_emit_by_name (device->device_manager,
+ "ptr-a11y-timeout-started",
+ device,
+ CLUTTER_A11Y_TIMEOUT_TYPE_GESTURE,
+ delay);
+}
+
+static gboolean
+trigger_dwell_click (gpointer data)
+{
+ ClutterInputDevice *device = data;
+
+ device->ptr_a11y_data->dwell_timer = 0;
+
+ g_signal_emit_by_name (device->device_manager,
+ "ptr-a11y-timeout-stopped",
+ device,
+ CLUTTER_A11Y_TIMEOUT_TYPE_DWELL);
+
+ if (get_dwell_mode (device) == CLUTTER_A11Y_DWELL_MODE_GESTURE)
+ {
+ if (is_dwell_dragging (device))
+ emit_dwell_click (device, CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG);
+ else
+ start_dwell_gesture_timeout (device);
+ }
+ else
+ {
+ emit_dwell_click (device, get_dwell_click_type (device));
+ update_dwell_click_type (device);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+start_dwell_timeout (ClutterInputDevice *device)
+{
+ unsigned int delay = get_dwell_delay (device);
+
+ device->ptr_a11y_data->dwell_timer =
+ clutter_threads_add_timeout (delay, trigger_dwell_click, device);
+
+ g_signal_emit_by_name (device->device_manager,
+ "ptr-a11y-timeout-started",
+ device,
+ CLUTTER_A11Y_TIMEOUT_TYPE_DWELL,
+ delay);
+}
+
+static void
+stop_dwell_timeout (ClutterInputDevice *device)
+{
+ if (device->ptr_a11y_data->dwell_timer)
+ {
+ g_source_remove (device->ptr_a11y_data->dwell_timer);
+ device->ptr_a11y_data->dwell_timer = 0;
+ device->ptr_a11y_data->dwell_gesture_started = FALSE;
+
+ g_signal_emit_by_name (device->device_manager,
+ "ptr-a11y-timeout-stopped",
+ device,
+ CLUTTER_A11Y_TIMEOUT_TYPE_DWELL);
+ }
+}
+
+static void
+update_dwell_position (ClutterInputDevice *device)
+{
+ device->ptr_a11y_data->dwell_x = device->ptr_a11y_data->current_x;
+ device->ptr_a11y_data->dwell_y = device->ptr_a11y_data->current_y;
+}
+
+static void
+update_current_position (ClutterInputDevice *device,
+ float x,
+ float y)
+{
+ device->ptr_a11y_data->current_x = x;
+ device->ptr_a11y_data->current_y = y;
+}
+
+static gboolean
+is_device_core_pointer (ClutterInputDevice *device)
+{
+ ClutterInputDevice *core_pointer;
+
+ core_pointer = clutter_device_manager_get_core_device (device->device_manager,
+ CLUTTER_POINTER_DEVICE);
+ if (core_pointer == NULL)
+ return FALSE;
+
+ return (core_pointer == device);
+}
+
+void
+_clutter_input_pointer_a11y_add_device (ClutterInputDevice *device)
+{
+ if (!is_device_core_pointer (device))
+ return;
+
+ device->accessibility_virtual_device =
+ clutter_device_manager_create_virtual_device (device->device_manager,
+ CLUTTER_POINTER_DEVICE);
+
+ device->ptr_a11y_data = g_new0 (ClutterPtrA11yData, 1);
+}
+
+void
+_clutter_input_pointer_a11y_remove_device (ClutterInputDevice *device)
+{
+ if (!is_device_core_pointer (device))
+ return;
+
+ /* Terminate a drag if started */
+ if (is_dwell_dragging (device))
+ emit_dwell_click (device, CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG);
+
+ stop_dwell_timeout (device);
+ stop_secondary_click_timeout (device);
+
+ g_clear_pointer (&device->ptr_a11y_data, g_free);
+}
+
+void
+_clutter_input_pointer_a11y_on_motion_event (ClutterInputDevice *device,
+ float x,
+ float y)
+{
+ if (!is_device_core_pointer (device))
+ return;
+
+ if (!_clutter_is_input_pointer_a11y_enabled (device))
+ return;
+
+ update_current_position (device, x, y);
+
+ if (is_secondary_click_enabled (device))
+ {
+ if (pointer_has_moved (device))
+ stop_secondary_click_timeout (device);
+ }
+
+ if (is_dwell_click_enabled (device))
+ {
+ if (should_stop_dwell (device))
+ stop_dwell_timeout (device);
+ else if (should_start_dwell (device))
+ start_dwell_timeout (device);
+ }
+
+ if (should_update_dwell_position (device))
+ update_dwell_position (device);
+}
+
+void
+_clutter_input_pointer_a11y_on_button_event (ClutterInputDevice *device,
+ int button,
+ gboolean pressed)
+{
+ if (!is_device_core_pointer (device))
+ return;
+
+ if (!_clutter_is_input_pointer_a11y_enabled (device))
+ return;
+
+ if (pressed)
+ {
+ device->ptr_a11y_data->n_btn_pressed++;
+
+ if (is_dwell_click_enabled (device))
+ stop_dwell_timeout (device);
+
+ if (is_dwell_dragging (device))
+ stop_dwell_timeout (device);
+
+ if (is_secondary_click_enabled (device))
+ {
+ if (button == CLUTTER_BUTTON_PRIMARY)
+ {
+ if (should_start_secondary_click_timeout (device))
+ start_secondary_click_timeout (device);
+ }
+ else if (is_secondary_click_pending (device))
+ {
+ stop_secondary_click_timeout (device);
+ }
+ }
+ }
+ else
+ {
+ if (has_button_pressed (device))
+ device->ptr_a11y_data->n_btn_pressed--;
+
+ if (is_secondary_click_triggered (device))
+ {
+ emit_button_click (device, CLUTTER_BUTTON_SECONDARY);
+ stop_secondary_click_timeout (device);
+ }
+
+ if (is_secondary_click_pending (device))
+ stop_secondary_click_timeout (device);
+
+ if (is_dwell_dragging (device))
+ emit_dwell_click (device, CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG);
+ }
+}
+
+gboolean
+_clutter_is_input_pointer_a11y_enabled (ClutterInputDevice *device)
+{
+ g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE);
+
+ return (is_secondary_click_enabled (device) || is_dwell_click_enabled (device));
+}
diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build
index abc251413..6210333a1 100644
--- a/clutter/clutter/meson.build
+++ b/clutter/clutter/meson.build
@@ -133,6 +133,7 @@ clutter_sources = [
'clutter-input-device-tool.c',
'clutter-input-focus.c',
'clutter-input-method.c',
+ 'clutter-input-pointer-a11y.c',
'clutter-virtual-input-device.c',
'clutter-interval.c',
'clutter-keyframe-transition.c',
@@ -195,6 +196,7 @@ clutter_private_headers = [
'clutter-id-pool.h',
'clutter-input-focus-private.h',
'clutter-input-method-private.h',
+ 'clutter-input-pointer-a11y-private.h',
'clutter-master-clock.h',
'clutter-master-clock-default.h',
'clutter-offscreen-effect-private.h',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]