769 lines
22 KiB
C
769 lines
22 KiB
C
/*
|
|
* 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-backend-private.h"
|
|
#include "clutter-enum-types.h"
|
|
#include "clutter-input-device.h"
|
|
#include "clutter-input-device-private.h"
|
|
#include "clutter-input-pointer-a11y-private.h"
|
|
#include "clutter-main.h"
|
|
#include "clutter-private.h"
|
|
#include "clutter-virtual-input-device.h"
|
|
|
|
static gboolean
|
|
is_secondary_click_enabled (ClutterInputDevice *device)
|
|
{
|
|
ClutterPointerA11ySettings settings;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
clutter_seat_get_pointer_a11y_settings (seat, &settings);
|
|
|
|
return (settings.controls & CLUTTER_A11Y_SECONDARY_CLICK_ENABLED);
|
|
}
|
|
|
|
static gboolean
|
|
is_dwell_click_enabled (ClutterInputDevice *device)
|
|
{
|
|
ClutterPointerA11ySettings settings;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
clutter_seat_get_pointer_a11y_settings (seat, &settings);
|
|
|
|
return (settings.controls & CLUTTER_A11Y_DWELL_ENABLED);
|
|
}
|
|
|
|
static unsigned int
|
|
get_secondary_click_delay (ClutterInputDevice *device)
|
|
{
|
|
ClutterPointerA11ySettings settings;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
clutter_seat_get_pointer_a11y_settings (seat, &settings);
|
|
|
|
return settings.secondary_click_delay;
|
|
}
|
|
|
|
static unsigned int
|
|
get_dwell_delay (ClutterInputDevice *device)
|
|
{
|
|
ClutterPointerA11ySettings settings;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
clutter_seat_get_pointer_a11y_settings (seat, &settings);
|
|
|
|
return settings.dwell_delay;
|
|
}
|
|
|
|
static unsigned int
|
|
get_dwell_threshold (ClutterInputDevice *device)
|
|
{
|
|
ClutterPointerA11ySettings settings;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
clutter_seat_get_pointer_a11y_settings (seat, &settings);
|
|
|
|
return settings.dwell_threshold;
|
|
}
|
|
|
|
static ClutterPointerA11yDwellMode
|
|
get_dwell_mode (ClutterInputDevice *device)
|
|
{
|
|
ClutterPointerA11ySettings settings;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
clutter_seat_get_pointer_a11y_settings (seat, &settings);
|
|
|
|
return settings.dwell_mode;
|
|
}
|
|
|
|
static ClutterPointerA11yDwellClickType
|
|
get_dwell_click_type (ClutterInputDevice *device)
|
|
{
|
|
ClutterPointerA11ySettings settings;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
clutter_seat_get_pointer_a11y_settings (seat, &settings);
|
|
|
|
return settings.dwell_click_type;
|
|
}
|
|
|
|
static ClutterPointerA11yDwellClickType
|
|
get_dwell_click_type_for_direction (ClutterInputDevice *device,
|
|
ClutterPointerA11yDwellDirection direction)
|
|
{
|
|
ClutterPointerA11ySettings settings;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
clutter_seat_get_pointer_a11y_settings (seat, &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;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
device->ptr_a11y_data->secondary_click_triggered = TRUE;
|
|
device->ptr_a11y_data->secondary_click_timer = 0;
|
|
|
|
g_signal_emit_by_name (seat,
|
|
"ptr-a11y-timeout-stopped",
|
|
device,
|
|
CLUTTER_A11Y_TIMEOUT_TYPE_SECONDARY_CLICK,
|
|
TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
start_secondary_click_timeout (ClutterInputDevice *device)
|
|
{
|
|
unsigned int delay = get_secondary_click_delay (device);
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
device->ptr_a11y_data->secondary_click_timer =
|
|
clutter_threads_add_timeout (delay, trigger_secondary_click, device);
|
|
|
|
g_signal_emit_by_name (seat,
|
|
"ptr-a11y-timeout-started",
|
|
device,
|
|
CLUTTER_A11Y_TIMEOUT_TYPE_SECONDARY_CLICK,
|
|
delay);
|
|
}
|
|
|
|
static void
|
|
stop_secondary_click_timeout (ClutterInputDevice *device)
|
|
{
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
if (device->ptr_a11y_data->secondary_click_timer)
|
|
{
|
|
g_clear_handle_id (&device->ptr_a11y_data->secondary_click_timer,
|
|
g_source_remove);
|
|
|
|
g_signal_emit_by_name (seat,
|
|
"ptr-a11y-timeout-stopped",
|
|
device,
|
|
CLUTTER_A11Y_TIMEOUT_TYPE_SECONDARY_CLICK,
|
|
FALSE);
|
|
}
|
|
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;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
clutter_seat_get_pointer_a11y_settings (seat, &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_seat_set_pointer_a11y_settings (seat, &settings);
|
|
|
|
g_signal_emit_by_name (seat,
|
|
"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);
|
|
ClutterSeat *seat = clutter_input_device_get_seat (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 (seat,
|
|
"ptr-a11y-timeout-stopped",
|
|
device,
|
|
CLUTTER_A11Y_TIMEOUT_TYPE_GESTURE,
|
|
TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
start_dwell_gesture_timeout (ClutterInputDevice *device)
|
|
{
|
|
unsigned int delay = get_dwell_delay (device);
|
|
ClutterSeat *seat = clutter_input_device_get_seat (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 (seat,
|
|
"ptr-a11y-timeout-started",
|
|
device,
|
|
CLUTTER_A11Y_TIMEOUT_TYPE_GESTURE,
|
|
delay);
|
|
}
|
|
|
|
static gboolean
|
|
trigger_dwell_click (gpointer data)
|
|
{
|
|
ClutterInputDevice *device = data;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
device->ptr_a11y_data->dwell_timer = 0;
|
|
|
|
g_signal_emit_by_name (seat,
|
|
"ptr-a11y-timeout-stopped",
|
|
device,
|
|
CLUTTER_A11Y_TIMEOUT_TYPE_DWELL,
|
|
TRUE);
|
|
|
|
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);
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
device->ptr_a11y_data->dwell_timer =
|
|
clutter_threads_add_timeout (delay, trigger_dwell_click, device);
|
|
|
|
g_signal_emit_by_name (seat,
|
|
"ptr-a11y-timeout-started",
|
|
device,
|
|
CLUTTER_A11Y_TIMEOUT_TYPE_DWELL,
|
|
delay);
|
|
}
|
|
|
|
static void
|
|
stop_dwell_timeout (ClutterInputDevice *device)
|
|
{
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
if (device->ptr_a11y_data->dwell_timer)
|
|
{
|
|
g_clear_handle_id (&device->ptr_a11y_data->dwell_timer, g_source_remove);
|
|
device->ptr_a11y_data->dwell_gesture_started = FALSE;
|
|
|
|
g_signal_emit_by_name (seat,
|
|
"ptr-a11y-timeout-stopped",
|
|
device,
|
|
CLUTTER_A11Y_TIMEOUT_TYPE_DWELL,
|
|
FALSE);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
trigger_dwell_position_timeout (gpointer data)
|
|
{
|
|
ClutterInputDevice *device = data;
|
|
|
|
device->ptr_a11y_data->dwell_position_timer = 0;
|
|
|
|
if (is_dwell_click_enabled (device))
|
|
{
|
|
if (!pointer_has_moved (device))
|
|
start_dwell_timeout (device);
|
|
}
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
start_dwell_position_timeout (ClutterInputDevice *device)
|
|
{
|
|
device->ptr_a11y_data->dwell_position_timer =
|
|
clutter_threads_add_timeout (100, trigger_dwell_position_timeout, device);
|
|
}
|
|
|
|
static void
|
|
stop_dwell_position_timeout (ClutterInputDevice *device)
|
|
{
|
|
g_clear_handle_id (&device->ptr_a11y_data->dwell_position_timer,
|
|
g_source_remove);
|
|
}
|
|
|
|
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;
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
core_pointer = clutter_seat_get_pointer (seat);
|
|
if (core_pointer == NULL)
|
|
return FALSE;
|
|
|
|
return (core_pointer == device);
|
|
}
|
|
|
|
void
|
|
_clutter_input_pointer_a11y_add_device (ClutterInputDevice *device)
|
|
{
|
|
ClutterSeat *seat = clutter_input_device_get_seat (device);
|
|
|
|
if (!is_device_core_pointer (device))
|
|
return;
|
|
|
|
device->accessibility_virtual_device =
|
|
clutter_seat_create_virtual_device (seat,
|
|
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_position_timeout (device);
|
|
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))
|
|
{
|
|
stop_dwell_position_timeout (device);
|
|
|
|
if (should_stop_dwell (device))
|
|
stop_dwell_timeout (device);
|
|
|
|
if (should_start_dwell (device))
|
|
start_dwell_position_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++;
|
|
|
|
stop_dwell_position_timeout (device);
|
|
|
|
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));
|
|
}
|
|
|
|
void
|
|
clutter_input_pointer_a11y_update (ClutterInputDevice *device,
|
|
const ClutterEvent *event)
|
|
{
|
|
|
|
ClutterMainContext *clutter_context;
|
|
ClutterBackend *backend;
|
|
|
|
g_return_if_fail (clutter_event_get_device (event) == device);
|
|
|
|
if (!_clutter_is_input_pointer_a11y_enabled (device))
|
|
return;
|
|
|
|
if ((event->any.flags & CLUTTER_EVENT_FLAG_SYNTHETIC) != 0)
|
|
return;
|
|
|
|
clutter_context = _clutter_context_get_default ();
|
|
backend = clutter_context->backend;
|
|
|
|
if (!clutter_backend_is_display_server (backend))
|
|
return;
|
|
|
|
if (event->type == CLUTTER_MOTION)
|
|
{
|
|
float x, y;
|
|
|
|
clutter_event_get_coords (event, &x, &y);
|
|
_clutter_input_pointer_a11y_on_motion_event (device, x, y);
|
|
}
|
|
else if (event->type == CLUTTER_BUTTON_PRESS ||
|
|
event->type == CLUTTER_BUTTON_RELEASE)
|
|
{
|
|
_clutter_input_pointer_a11y_on_button_event (device,
|
|
event->button.button,
|
|
event->type == CLUTTER_BUTTON_PRESS);
|
|
}
|
|
}
|