From c1303bd642e57d53ccbc8f6eb450c2a45bf4d3ef Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Thu, 14 Mar 2019 14:12:34 +0100 Subject: [PATCH] clutter/x11: Hook up pointer accessibility Pointer accessibility features requires to receive all pointer events regardless of X11 grabs. Add XI2 raw events mask and hook up the pointer accessibility handlers to the raw motion and button press/release events. https://gitlab.gnome.org/GNOME/mutter/merge_requests/512 --- .../clutter/x11/clutter-device-manager-xi2.c | 78 ++++++++++++++++++- .../clutter/x11/clutter-input-device-xi2.c | 77 ++++++++++++++++++ .../clutter/x11/clutter-input-device-xi2.h | 3 + 3 files changed, 157 insertions(+), 1 deletion(-) diff --git a/clutter/clutter/x11/clutter-device-manager-xi2.c b/clutter/clutter/x11/clutter-device-manager-xi2.c index 87da4b050..6d63ed9bd 100644 --- a/clutter/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/clutter/x11/clutter-device-manager-xi2.c @@ -30,6 +30,7 @@ #include "clutter-backend-x11.h" #include "clutter-input-device-xi2.h" #include "clutter-input-device-tool-xi2.h" +#include "clutter-input-pointer-a11y-private.h" #include "clutter-virtual-input-device-x11.h" #include "clutter-stage-x11.h" @@ -1273,6 +1274,60 @@ translate_pad_event (ClutterEvent *event, return TRUE; } +static void +handle_raw_event (ClutterDeviceManagerXI2 *manager_xi2, + XEvent *xevent) +{ + ClutterInputDevice *device; + XGenericEventCookie *cookie; + XIEvent *xi_event; + XIRawEvent *xev; + float x,y; + + cookie = &xevent->xcookie; + xi_event = (XIEvent *) cookie->data; + xev = (XIRawEvent *) xi_event; + + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->deviceid)); + if (device == NULL) + return; + + if (!_clutter_is_input_pointer_a11y_enabled (device)) + return; + + switch (cookie->evtype) + { + case XI_RawMotion: + CLUTTER_NOTE (EVENT, + "raw motion: device:%d '%s'", + device->id, + device->device_name); + /* We don't get actual pointer location with raw events, and we cannot + * rely on `clutter_input_device_get_coords()` either because of + * unreparented toplevels (like all client-side decoration windows), + * so we need to explicitely query the pointer here... + */ + if (clutter_input_device_xi2_get_pointer_location (device, &x, &y)) + _clutter_input_pointer_a11y_on_motion_event (device, x, y); + break; + case XI_RawButtonPress: + case XI_RawButtonRelease: + CLUTTER_NOTE (EVENT, + "raw button %s: device:%d '%s' button %i", + cookie->evtype == XI_RawButtonPress + ? "press " + : "release", + device->id, + device->device_name, + xev->detail); + _clutter_input_pointer_a11y_on_button_event (device, + xev->detail, + (cookie->evtype == XI_RawButtonPress)); + break; + } +} + static ClutterTranslateReturn clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, gpointer native, @@ -1303,6 +1358,14 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, if (!xi_event) return CLUTTER_TRANSLATE_REMOVE; + if (cookie->evtype == XI_RawMotion || + cookie->evtype == XI_RawButtonPress || + cookie->evtype == XI_RawButtonRelease) + { + handle_raw_event (manager_xi2, xevent); + return CLUTTER_TRANSLATE_REMOVE; + } + if (!(xi_event->evtype == XI_HierarchyChanged || xi_event->evtype == XI_DeviceChanged || xi_event->evtype == XI_PropertyEvent)) @@ -2031,7 +2094,7 @@ clutter_device_manager_xi2_constructed (GObject *gobject) GHashTable *masters, *slaves; XIDeviceInfo *info; XIEventMask event_mask; - unsigned char mask[2] = { 0, }; + unsigned char mask[(XI_LASTEVENT + 7) / 8] = { 0, }; int n_devices, i; backend_x11 = @@ -2083,6 +2146,19 @@ clutter_device_manager_xi2_constructed (GObject *gobject) event_mask.mask_len = sizeof (mask); event_mask.mask = mask; + clutter_device_manager_xi2_select_events (manager, + clutter_x11_get_root_window (), + &event_mask); + + memset(mask, 0, sizeof (mask)); + XISetMask (mask, XI_RawMotion); + XISetMask (mask, XI_RawButtonPress); + XISetMask (mask, XI_RawButtonRelease); + + event_mask.deviceid = XIAllMasterDevices; + event_mask.mask_len = sizeof (mask); + event_mask.mask = mask; + clutter_device_manager_xi2_select_events (manager, clutter_x11_get_root_window (), &event_mask); diff --git a/clutter/clutter/x11/clutter-input-device-xi2.c b/clutter/clutter/x11/clutter-input-device-xi2.c index 1254aca3a..0033166c5 100644 --- a/clutter/clutter/x11/clutter-input-device-xi2.c +++ b/clutter/clutter/x11/clutter-input-device-xi2.c @@ -46,6 +46,11 @@ struct _ClutterInputDeviceXI2 gint device_id; ClutterInputDeviceTool *current_tool; + guint inhibit_pointer_query_timer; + gboolean query_status; + float current_x; + float current_y; + #ifdef HAVE_LIBWACOM WacomDevice *wacom_device; GArray *group_modes; @@ -114,6 +119,9 @@ clutter_input_device_xi2_finalize (GObject *object) g_array_unref (device_xi2->group_modes); #endif + if (device_xi2->inhibit_pointer_query_timer) + g_source_remove (device_xi2->inhibit_pointer_query_timer); + G_OBJECT_CLASS (clutter_input_device_xi2_parent_class)->finalize (object); } @@ -293,6 +301,75 @@ clutter_input_device_xi2_get_current_tool (ClutterInputDevice *device) return device_xi2->current_tool; } +static gboolean +clutter_input_device_xi2_query_pointer_location (ClutterInputDeviceXI2 *device_xi2) +{ + Window xroot_window, xchild_window; + double xroot_x, xroot_y, xwin_x, xwin_y; + XIButtonState button_state; + XIModifierState mod_state; + XIGroupState group_state; + int result; + + clutter_x11_trap_x_errors (); + result = XIQueryPointer (clutter_x11_get_default_display (), + device_xi2->device_id, + clutter_x11_get_root_window (), + &xroot_window, + &xchild_window, + &xroot_x, &xroot_y, + &xwin_x, &xwin_y, + &button_state, + &mod_state, + &group_state); + clutter_x11_untrap_x_errors (); + + if (!result) + return FALSE; + + device_xi2->current_x = (float) xroot_x; + device_xi2->current_y = (float) xroot_y; + + return TRUE; +} + +static gboolean +clear_inhibit_pointer_query_cb (gpointer data) +{ + ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (data); + + device_xi2->inhibit_pointer_query_timer = 0; + + return G_SOURCE_REMOVE; +} + +gboolean +clutter_input_device_xi2_get_pointer_location (ClutterInputDevice *device, + float *x, + float *y) + +{ + ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device); + + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE_XI2 (device_xi2), FALSE); + g_return_val_if_fail (device->device_type == CLUTTER_POINTER_DEVICE, FALSE); + + /* Throttle XServer queries and roundtrips using an idle timeout */ + if (device_xi2->inhibit_pointer_query_timer == 0) + { + device_xi2->query_status = + clutter_input_device_xi2_query_pointer_location (device_xi2); + device_xi2->inhibit_pointer_query_timer = + clutter_threads_add_idle (clear_inhibit_pointer_query_cb, device_xi2); + } + + *x = device_xi2->current_x; + *y = device_xi2->current_y; + + return device_xi2->query_status; +} + #ifdef HAVE_LIBWACOM void clutter_input_device_xi2_ensure_wacom_info (ClutterInputDevice *device, diff --git a/clutter/clutter/x11/clutter-input-device-xi2.h b/clutter/clutter/x11/clutter-input-device-xi2.h index 2194e1ba2..8ec709a2e 100644 --- a/clutter/clutter/x11/clutter-input-device-xi2.h +++ b/clutter/clutter/x11/clutter-input-device-xi2.h @@ -48,6 +48,9 @@ void _clutter_input_device_xi2_translate_state (ClutterEvent *event, void clutter_input_device_xi2_update_tool (ClutterInputDevice *device, ClutterInputDeviceTool *tool); ClutterInputDeviceTool * clutter_input_device_xi2_get_current_tool (ClutterInputDevice *device); +gboolean clutter_input_device_xi2_get_pointer_location (ClutterInputDevice *device, + float *x, + float *y); #ifdef HAVE_LIBWACOM void clutter_input_device_xi2_ensure_wacom_info (ClutterInputDevice *device,