xwayland: Aggregate scroll axis events to fix kinetic scrolling

Pointer scroll events are collected in xwl_seat->pending_pointer_event
as they are received in the pointer_handle_axis and
pointer_handle_axis_discrete callbacks. They are dispatched together as a
single event when pointer_handle_frame is called which "Indicates the end of a
set of events that logically belong together" [1]. This patch also sends an
event with dx=0, dy=0 when pointer_handle_axis_stop is called, which is what
allows XWayland clients to recognise the end of a touchpad scroll.

[1] https://wayland.app/protocols/wayland#wl_pointer:event:frame

Signed-off-by: David Jacewicz <david.jacewicz27@protonmail.com>
Fixes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/926
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
David Jacewicz 2022-06-17 15:13:13 -04:00 committed by Olivier Fourdan
parent 4cfdc5af31
commit e37eeb7af2
2 changed files with 75 additions and 53 deletions

View File

@ -51,12 +51,6 @@
#include "xwayland-keyboard-grab-unstable-v1-client-protocol.h"
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
struct axis_discrete_pending {
struct xorg_list l;
uint32_t axis;
int32_t discrete;
};
struct sync_pending {
struct xorg_list l;
DeviceIntPtr pending_dev;
@ -669,6 +663,36 @@ dispatch_relative_motion(struct xwl_seat *xwl_seat)
POINTER_RAWONLY, &mask);
}
static void
dispatch_scroll_motion(struct xwl_seat *xwl_seat)
{
ValuatorMask mask;
const int divisor = 10;
wl_fixed_t dy = xwl_seat->pending_pointer_event.scroll_dy;
wl_fixed_t dx = xwl_seat->pending_pointer_event.scroll_dx;
wl_fixed_t discrete_dy = xwl_seat->pending_pointer_event.scroll_discrete_dy;
wl_fixed_t discrete_dx = xwl_seat->pending_pointer_event.scroll_discrete_dx;
valuator_mask_zero(&mask);
if (xwl_seat->pending_pointer_event.has_vertical_scroll)
valuator_mask_set_double(&mask,
3,
wl_fixed_to_double(dy) / divisor);
else if (xwl_seat->pending_pointer_event.has_vertical_scroll_discrete)
valuator_mask_set(&mask, 3, discrete_dy);
if (xwl_seat->pending_pointer_event.has_horizontal_scroll)
valuator_mask_set_double(&mask,
2,
wl_fixed_to_double(dx) / divisor);
else if (xwl_seat->pending_pointer_event.has_horizontal_scroll_discrete)
valuator_mask_set(&mask, 2, discrete_dx);
QueuePointerEvents(get_pointer_device(xwl_seat),
MotionNotify, 0, POINTER_RELATIVE, &mask);
}
static void
dispatch_pointer_motion_event(struct xwl_seat *xwl_seat)
{
@ -685,8 +709,18 @@ dispatch_pointer_motion_event(struct xwl_seat *xwl_seat)
dispatch_absolute_motion(xwl_seat);
}
if (xwl_seat->pending_pointer_event.has_vertical_scroll ||
xwl_seat->pending_pointer_event.has_horizontal_scroll ||
xwl_seat->pending_pointer_event.has_vertical_scroll_discrete ||
xwl_seat->pending_pointer_event.has_horizontal_scroll_discrete)
dispatch_scroll_motion(xwl_seat);
xwl_seat->pending_pointer_event.has_absolute = FALSE;
xwl_seat->pending_pointer_event.has_relative = FALSE;
xwl_seat->pending_pointer_event.has_vertical_scroll = FALSE;
xwl_seat->pending_pointer_event.has_horizontal_scroll = FALSE;
xwl_seat->pending_pointer_event.has_vertical_scroll_discrete = FALSE;
xwl_seat->pending_pointer_event.has_horizontal_scroll_discrete = FALSE;
}
static void
@ -743,42 +777,15 @@ pointer_handle_axis(void *data, struct wl_pointer *pointer,
uint32_t time, uint32_t axis, wl_fixed_t value)
{
struct xwl_seat *xwl_seat = data;
int index;
const int divisor = 10;
ValuatorMask mask;
struct axis_discrete_pending *pending = NULL;
struct axis_discrete_pending *iter;
switch (axis) {
case WL_POINTER_AXIS_VERTICAL_SCROLL:
index = 3;
break;
xwl_seat->pending_pointer_event.has_vertical_scroll = TRUE;
xwl_seat->pending_pointer_event.scroll_dy = value;
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
index = 2;
break;
default:
return;
xwl_seat->pending_pointer_event.has_horizontal_scroll = TRUE;
xwl_seat->pending_pointer_event.scroll_dx = value;
}
xorg_list_for_each_entry(iter, &xwl_seat->axis_discrete_pending, l) {
if (iter->axis == axis) {
pending = iter;
break;
}
}
valuator_mask_zero(&mask);
if (pending) {
valuator_mask_set(&mask, index, pending->discrete);
xorg_list_del(&pending->l);
free(pending);
} else {
valuator_mask_set_double(&mask, index, wl_fixed_to_double(value) / divisor);
}
QueuePointerEvents(get_pointer_device(xwl_seat),
MotionNotify, 0, POINTER_RELATIVE, &mask);
}
static void
@ -801,6 +808,18 @@ static void
pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis)
{
struct xwl_seat *xwl_seat = data;
switch (axis) {
case WL_POINTER_AXIS_VERTICAL_SCROLL:
xwl_seat->pending_pointer_event.has_vertical_scroll = TRUE;
xwl_seat->pending_pointer_event.scroll_dy = 0;
break;
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
xwl_seat->pending_pointer_event.has_horizontal_scroll = TRUE;
xwl_seat->pending_pointer_event.scroll_dx = 0;
break;
}
}
static void
@ -809,14 +828,16 @@ pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer,
{
struct xwl_seat *xwl_seat = data;
struct axis_discrete_pending *pending = malloc(sizeof *pending);
if (!pending)
return;
pending->axis = axis;
pending->discrete = discrete;
xorg_list_add(&pending->l, &xwl_seat->axis_discrete_pending);
switch (axis) {
case WL_POINTER_AXIS_VERTICAL_SCROLL:
xwl_seat->pending_pointer_event.has_vertical_scroll_discrete = TRUE;
xwl_seat->pending_pointer_event.scroll_discrete_dy = discrete;
break;
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
xwl_seat->pending_pointer_event.has_horizontal_scroll_discrete = TRUE;
xwl_seat->pending_pointer_event.scroll_discrete_dx = discrete;
break;
}
}
static const struct wl_pointer_listener pointer_listener = {
@ -1832,7 +1853,6 @@ create_input_device(struct xwl_screen *xwl_screen, uint32_t id, uint32_t version
wl_array_init(&xwl_seat->keys);
xorg_list_init(&xwl_seat->touches);
xorg_list_init(&xwl_seat->axis_discrete_pending);
xorg_list_init(&xwl_seat->sync_pending);
}
@ -1841,7 +1861,6 @@ xwl_seat_destroy(struct xwl_seat *xwl_seat)
{
struct xwl_touch *xwl_touch, *next_xwl_touch;
struct sync_pending *p, *npd;
struct axis_discrete_pending *ad, *ad_next;
xorg_list_for_each_entry_safe(xwl_touch, next_xwl_touch,
&xwl_seat->touches, link_touch) {
@ -1854,11 +1873,6 @@ xwl_seat_destroy(struct xwl_seat *xwl_seat)
free (p);
}
xorg_list_for_each_entry_safe(ad, ad_next, &xwl_seat->axis_discrete_pending, l) {
xorg_list_del(&ad->l);
free(ad);
}
release_tablet_manager_seat(xwl_seat);
release_grab(xwl_seat);

View File

@ -93,7 +93,6 @@ struct xwl_seat {
char *keymap;
struct wl_surface *keyboard_focus;
struct xorg_list axis_discrete_pending;
struct xorg_list sync_pending;
struct xwl_pointer_warp_emulator *pointer_warp_emulator;
@ -111,6 +110,15 @@ struct xwl_seat {
double dy;
double dx_unaccel;
double dy_unaccel;
wl_fixed_t scroll_dy;
wl_fixed_t scroll_dx;
int32_t scroll_discrete_dy;
int32_t scroll_discrete_dx;
Bool has_vertical_scroll;
Bool has_horizontal_scroll;
Bool has_vertical_scroll_discrete;
Bool has_horizontal_scroll_discrete;
} pending_pointer_event;
struct xorg_list tablets;