From ac4eef7eb3eeaa7cd0b24027dc43e134b587866f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 8 Apr 2024 18:45:38 +0530 Subject: [PATCH] Another try at pointer frame support on Wayland This time I think I got the wheel handling correct. At least works with my wheel mouse. --- glfw/wl_init.c | 176 ++++++++++++++++++++++++++------------------- glfw/wl_platform.h | 27 +++++-- 2 files changed, 125 insertions(+), 78 deletions(-) diff --git a/glfw/wl_init.c b/glfw/wl_init.c index bc7a86471..42dd080ab 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -35,7 +35,6 @@ #include "wl_text_input.h" #include "wayland-text-input-unstable-v3-client-protocol.h" -#include #include #include #include @@ -152,94 +151,121 @@ static void pointerHandleButton(void* data UNUSED, #undef x #undef y -static void pointerHandleAxis(void* data UNUSED, - struct wl_pointer* pointer UNUSED, - uint32_t time UNUSED, - uint32_t axis, - wl_fixed_t value) -{ +#define info (window->wl.pointer_curr_axis_info) + +static void +pointer_handle_axis_common(enum _GLFWWaylandAxisEvent type, uint32_t axis, wl_fixed_t value) { _GLFWwindow* window = _glfw.wl.pointerFocus; - double x = 0.0, y = 0.0; if (!window || window->wl.decorations.focus != CENTRAL_WINDOW) return; + float fval = (float) wl_fixed_to_double(value); - assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || - axis == WL_POINTER_AXIS_VERTICAL_SCROLL); +#define CASE(type, type_const, axis, fval) \ + case type_const: \ + if (info.type.axis##_axis_type == AXIS_EVENT_UNKNOWN) { \ + info.type.axis##_axis_type = type_const; info.type.axis = 0.f; } \ + info.type.axis += fval; break; - if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - if (window->wl.axis_discrete_count.x) { - window->wl.axis_discrete_count.x--; - return; + if (window) { + switch ((enum wl_pointer_axis)axis) { + case WL_POINTER_AXIS_VERTICAL_SCROLL: + switch (type) { + case AXIS_EVENT_UNKNOWN: break; + CASE(discrete, AXIS_EVENT_DISCRETE, y, -fval); // wheel event + CASE(discrete, AXIS_EVENT_VALUE120, y, -fval); // wheel event higher res than plain discrete + CASE(continuous, AXIS_EVENT_CONTINUOUS, y, -fval); // touchpad, etc. high res + } + break; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + switch (type) { + case AXIS_EVENT_UNKNOWN: break; + CASE(discrete, AXIS_EVENT_DISCRETE, x, fval); // wheel event + CASE(discrete, AXIS_EVENT_VALUE120, x, fval); // wheel event higher res than plain discrete + CASE(continuous, AXIS_EVENT_CONTINUOUS, x, fval); // touchpad, etc. high res + } + break; } - x = -wl_fixed_to_double(value) * _glfwWaylandWindowScale(window); } - else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - if (window->wl.axis_discrete_count.y) { - window->wl.axis_discrete_count.y--; - return; - } - y = -wl_fixed_to_double(value) * _glfwWaylandWindowScale(window); - } - - _glfwInputScroll(window, x, y, 1, _glfw.wl.xkb.states.modifiers); +#undef CASE } -static void pointerHandleFrame(void* data UNUSED, - struct wl_pointer* pointer UNUSED) -{ +static void +pointer_handle_axis(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t time, uint32_t axis, wl_fixed_t value) { _GLFWwindow* window = _glfw.wl.pointerFocus; - if (!window || window->wl.decorations.focus != CENTRAL_WINDOW) return; - window->wl.axis_discrete_count.x = 0; - window->wl.axis_discrete_count.y = 0; + if (!window) return; + if (!info.timestamp_ns) info.timestamp_ns = ms_to_monotonic_t(time); + pointer_handle_axis_common(AXIS_EVENT_CONTINUOUS, axis, value); } -static void pointerHandleAxisSource(void* data UNUSED, - struct wl_pointer* pointer UNUSED, - uint32_t source UNUSED) -{ -} - -static void pointerHandleAxisStop(void *data UNUSED, - struct wl_pointer *wl_pointer UNUSED, - uint32_t time UNUSED, - uint32_t axis UNUSED) -{ -} - - -static void pointerHandleAxisDiscrete(void *data UNUSED, - struct wl_pointer *wl_pointer UNUSED, - uint32_t axis, - int32_t discrete) -{ +static void +pointer_handle_frame(void *data UNUSED, struct wl_pointer *pointer UNUSED) { _GLFWwindow* window = _glfw.wl.pointerFocus; - double x = 0.0, y = 0.0; - if (!window || window->wl.decorations.focus != CENTRAL_WINDOW) return; + if (!window) return; + float x = 0, y = 0; + int highres = 0; - assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || - axis == WL_POINTER_AXIS_VERTICAL_SCROLL); - - if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - x = -discrete; - window->wl.axis_discrete_count.x++; - } - else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - y = -discrete; - window->wl.axis_discrete_count.y++; + if (info.discrete.y_axis_type != AXIS_EVENT_UNKNOWN) { + y = info.discrete.y; + if (info.discrete.y_axis_type == AXIS_EVENT_VALUE120) y /= 120.f; + } else if (info.continuous.y_axis_type != AXIS_EVENT_UNKNOWN) { + highres = 1; + y = info.continuous.y; } - _glfwInputScroll(window, x, y, 0, _glfw.wl.xkb.states.modifiers); + if (info.discrete.x_axis_type != AXIS_EVENT_UNKNOWN) { + x = info.discrete.x; + if (info.discrete.x_axis_type == AXIS_EVENT_VALUE120) x /= 120.f; + } else if (info.continuous.x_axis_type != AXIS_EVENT_UNKNOWN) { + highres = 1; + x = info.continuous.x; + } + /* clear pointer_curr_axis_info for next frame */ + memset(&info, 0, sizeof(info)); + + if (x != 0.0f || y != 0.0f) { + float scale = _glfwWaylandWindowScale(window); + y *= scale; x *= scale; + _glfwInputScroll(window, x, y, highres, _glfw.wl.xkb.states.modifiers); + } } +static void +pointer_handle_axis_source(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t source UNUSED) { } + +static void +pointer_handle_axis_stop(void *data UNUSED, struct wl_pointer *wl_pointer UNUSED, uint32_t time UNUSED, uint32_t axis UNUSED) { } + + +static void +pointer_handle_axis_discrete(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t axis, int32_t discrete) { + pointer_handle_axis_common(AXIS_EVENT_DISCRETE, axis, wl_fixed_from_int(discrete)); +} + +static void +pointer_handle_axis_value120(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t axis, int32_t value120) { + pointer_handle_axis_common(AXIS_EVENT_VALUE120, axis, wl_fixed_from_int(value120)); +} + +static void +pointer_handle_axis_relative_direction(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t axis UNUSED, uint32_t axis_relative_direction UNUSED) { } + +#undef info static const struct wl_pointer_listener pointerListener = { - pointerHandleEnter, - pointerHandleLeave, - pointerHandleMotion, - pointerHandleButton, - pointerHandleAxis, - pointerHandleFrame, - pointerHandleAxisSource, - pointerHandleAxisStop, - pointerHandleAxisDiscrete, + .enter = pointerHandleEnter, + .leave = pointerHandleLeave, + .motion = pointerHandleMotion, + .button = pointerHandleButton, + + .axis = pointer_handle_axis, + .frame = pointer_handle_frame, + .axis_source = pointer_handle_axis_source, + .axis_stop = pointer_handle_axis_stop, + .axis_discrete = pointer_handle_axis_discrete, +#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION + .axis_value120 = pointer_handle_axis_value120, +#endif +#ifdef WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION + .axis_relative_direction = pointer_handle_axis_relative_direction, +#endif }; static void keyboardHandleKeymap(void* data UNUSED, @@ -473,7 +499,13 @@ static void registryHandleGlobal(void* data UNUSED, { if (!_glfw.wl.seat) { - _glfw.wl.seatVersion = MIN(5u, version); +#ifdef WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION + _glfw.wl.seatVersion = MIN(WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION, (int)version); +#elif defined(WL_POINTER_AXIS_VALUE120_SINCE_VERSION) + _glfw.wl.seatVersion = MIN(WL_POINTER_AXIS_VALUE120_SINCE_VERSION, (int)version); +#else + _glfw.wl.seatVersion = MIN(WL_POINTER_AXIS_DISCRETE_SINCE_VERSION, version); +#endif _glfw.wl.seat = wl_registry_bind(registry, name, &wl_seat_interface, _glfw.wl.seatVersion); diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 0c7c20db8..b2a8b7f52 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -146,6 +146,13 @@ enum WaylandWindowPendingState { PENDING_STATE_DECORATION = 2 }; +enum _GLFWWaylandAxisEvent { + AXIS_EVENT_UNKNOWN = 0, + AXIS_EVENT_CONTINUOUS = 1, + AXIS_EVENT_DISCRETE = 2, + AXIS_EVENT_VALUE120 = 3 +}; + // Wayland-specific per-window data // typedef struct _GLFWwindowWayland @@ -178,6 +185,20 @@ typedef struct _GLFWwindowWayland struct zwlr_layer_surface_v1* zwlr_layer_surface_v1; } layer_shell; + /* information about axis events on current frame */ + struct + { + struct { + enum _GLFWWaylandAxisEvent x_axis_type; + float x; + enum _GLFWWaylandAxisEvent y_axis_type; + float y; + } discrete, continuous; + + /* Event timestamp in nanoseconds */ + monotonic_t timestamp_ns; + } pointer_curr_axis_info; + _GLFWcursor* currentCursor; double cursorPosX, cursorPosY, allCursorPosX, allCursorPosY; @@ -255,12 +276,6 @@ typedef struct _GLFWwindowWayland bool maximize_on_first_show; - // counters for ignoring axis events following axis_discrete events in the - // same frame along the same axis - struct { - unsigned int x, y; - } axis_discrete_count; - uint32_t pending_state; struct { int width, height;