From 506be129e17e8cdfc511c3d0f6e0bb202ee0c17f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 28 Mar 2024 15:32:59 +0530 Subject: [PATCH] Wayland: Add support for pointer frame events. Code taken with thanks from SDL --- glfw/glfw3.h | 1 + glfw/wl_init.c | 271 +++++++++++++++++++++++++++++-------------- glfw/wl_platform.h | 24 +++- kitty/glfw-wrapper.h | 1 + 4 files changed, 208 insertions(+), 89 deletions(-) diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 312665645..dc64f5261 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -316,6 +316,7 @@ extern "C" { #define GLFW_HAT_RIGHT_DOWN (GLFW_HAT_RIGHT | GLFW_HAT_DOWN) #define GLFW_HAT_LEFT_UP (GLFW_HAT_LEFT | GLFW_HAT_UP) #define GLFW_HAT_LEFT_DOWN (GLFW_HAT_LEFT | GLFW_HAT_DOWN) +#define GLFW_WAYLAND_WHEEL_AXIS_UNIT 10 /*! @} */ /*! @defgroup keys Keyboard keys diff --git a/glfw/wl_init.c b/glfw/wl_init.c index f4fb7b9bd..5f03f2a8e 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -80,7 +80,7 @@ findWindowFromDecorationSurface(struct wl_surface* surface, _GLFWdecorationSideW return window; } -static void pointerHandleEnter(void* data UNUSED, +static void pointer_handle_enter(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t serial, struct wl_surface* surface, @@ -109,7 +109,7 @@ static void pointerHandleEnter(void* data UNUSED, _glfwInputCursorEnter(window, true); } -static void pointerHandleLeave(void* data UNUSED, +static void pointer_handle_leave(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t serial, struct wl_surface* surface UNUSED) @@ -171,7 +171,7 @@ static void setCursor(GLFWCursorShape shape, _GLFWwindow* window) #define x window->wl.allCursorPosX #define y window->wl.allCursorPosY -static void pointerHandleMotion(void* data UNUSED, +static void pointer_handle_motion(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t time UNUSED, wl_fixed_t sx, @@ -229,7 +229,7 @@ static void pointerHandleMotion(void* data UNUSED, setCursor(cursorShape, window); } -static void pointerHandleButton(void* data UNUSED, +static void pointer_handle_button(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t serial, uint32_t time UNUSED, @@ -328,97 +328,200 @@ 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) -{ +static void +pointer_handle_axis_common(enum _GLFWWaylandAxisEvent type, uint32_t axis, wl_fixed_t value) { + enum wl_pointer_axis a = axis; _GLFWwindow* window = _glfw.wl.pointerFocus; - double x = 0.0, y = 0.0; - if (!window) - return; +#define input (&window->wl) - assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || - axis == WL_POINTER_AXIS_VERTICAL_SCROLL); - - if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - if (window->wl.axis_discrete_count.x) { - window->wl.axis_discrete_count.x--; - return; - } - 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); -} - -static void pointerHandleFrame(void* data UNUSED, - struct wl_pointer* pointer UNUSED) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; if (window) { - window->wl.axis_discrete_count.x = 0; - window->wl.axis_discrete_count.y = 0; + switch (a) { + case WL_POINTER_AXIS_VERTICAL_SCROLL: + switch (type) { + case AXIS_EVENT_VALUE120: + /* + * High resolution scroll event. The spec doesn't state that axis_value120 + * events are limited to one per frame, so the values are accumulated. + */ + if (input->pointer_curr_axis_info.y_axis_type != AXIS_EVENT_VALUE120) { + input->pointer_curr_axis_info.y_axis_type = AXIS_EVENT_VALUE120; + input->pointer_curr_axis_info.y = 0.0f; + } + input->pointer_curr_axis_info.y += 0 - (float)wl_fixed_to_double(value); + break; + case AXIS_EVENT_DISCRETE: + /* + * This is a discrete axis event, so we process it and set the + * flag to ignore future continuous axis events in this frame. + */ + if (input->pointer_curr_axis_info.y_axis_type != AXIS_EVENT_DISCRETE) { + input->pointer_curr_axis_info.y_axis_type = AXIS_EVENT_DISCRETE; + input->pointer_curr_axis_info.y = 0 - (float)wl_fixed_to_double(value); + } + break; + case AXIS_EVENT_CONTINUOUS: + /* Only process continuous events if no discrete events have been received. */ + if (input->pointer_curr_axis_info.y_axis_type == AXIS_EVENT_CONTINUOUS) { + input->pointer_curr_axis_info.y = 0 - (float)wl_fixed_to_double(value); + } + break; + } + break; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + switch (type) { + case AXIS_EVENT_VALUE120: + /* + * High resolution scroll event. The spec doesn't state that axis_value120 + * events are limited to one per frame, so the values are accumulated. + */ + if (input->pointer_curr_axis_info.x_axis_type != AXIS_EVENT_VALUE120) { + input->pointer_curr_axis_info.x_axis_type = AXIS_EVENT_VALUE120; + input->pointer_curr_axis_info.x = 0.0f; + } + input->pointer_curr_axis_info.x += (float)wl_fixed_to_double(value); + break; + case AXIS_EVENT_DISCRETE: + /* + * This is a discrete axis event, so we process it and set the + * flag to ignore future continuous axis events in this frame. + */ + if (input->pointer_curr_axis_info.x_axis_type != AXIS_EVENT_DISCRETE) { + input->pointer_curr_axis_info.x_axis_type = AXIS_EVENT_DISCRETE; + input->pointer_curr_axis_info.x = (float)wl_fixed_to_double(value); + } + break; + case AXIS_EVENT_CONTINUOUS: + /* Only process continuous events if no discrete events have been received. */ + if (input->pointer_curr_axis_info.x_axis_type == AXIS_EVENT_CONTINUOUS) { + input->pointer_curr_axis_info.x = (float)wl_fixed_to_double(value); + } + break; + } + break; + } } } -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_axis_common_v1(uint32_t time UNUSED, uint32_t axis, wl_fixed_t value) { _GLFWwindow* window = _glfw.wl.pointerFocus; - double x = 0.0, y = 0.0; - if (!window) - return; + enum wl_pointer_axis a = axis; + float x, y; - assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || - axis == WL_POINTER_AXIS_VERTICAL_SCROLL); + if (window) { + switch (a) { + case WL_POINTER_AXIS_VERTICAL_SCROLL: + x = 0; + y = 0 - (float)wl_fixed_to_double(value); + break; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + x = (float)wl_fixed_to_double(value); + y = 0; + break; + default: + return; + } - if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - x = -discrete; - window->wl.axis_discrete_count.x++; + x /= GLFW_WAYLAND_WHEEL_AXIS_UNIT; + y /= GLFW_WAYLAND_WHEEL_AXIS_UNIT; + + _glfwInputScroll(window, x, y, 0, _glfw.wl.xkb.states.modifiers); } - else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - y = -discrete; - window->wl.axis_discrete_count.y++; - } - - _glfwInputScroll(window, x, y, 0, _glfw.wl.xkb.states.modifiers); } -static const struct wl_pointer_listener pointerListener = { - pointerHandleEnter, - pointerHandleLeave, - pointerHandleMotion, - pointerHandleButton, - pointerHandleAxis, - pointerHandleFrame, - pointerHandleAxisSource, - pointerHandleAxisStop, - pointerHandleAxisDiscrete, +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) return; + + if (_glfw.wl.seatVersion >= WL_POINTER_FRAME_SINCE_VERSION) { + if (!input->pointer_curr_axis_info.timestamp_ns) input->pointer_curr_axis_info.timestamp_ns = ms_to_monotonic_t(time); + pointer_handle_axis_common(AXIS_EVENT_CONTINUOUS, axis, value); + } else { + pointer_handle_axis_common_v1(time, axis, value); + } +} + + +static void +pointer_handle_frame(void *data UNUSED, struct wl_pointer *pointer UNUSED) { + _GLFWwindow* window = _glfw.wl.pointerFocus; + if (!window) return; + float x, y; + + switch (input->pointer_curr_axis_info.x_axis_type) { + case AXIS_EVENT_CONTINUOUS: + x = input->pointer_curr_axis_info.x; + break; + case AXIS_EVENT_DISCRETE: + x = input->pointer_curr_axis_info.x; + break; + case AXIS_EVENT_VALUE120: + x = input->pointer_curr_axis_info.x / 120.0f; + break; + default: + x = 0.0f; + break; + } + + switch (input->pointer_curr_axis_info.y_axis_type) { + case AXIS_EVENT_CONTINUOUS: + y = input->pointer_curr_axis_info.y; + break; + case AXIS_EVENT_DISCRETE: + y = input->pointer_curr_axis_info.y; + break; + case AXIS_EVENT_VALUE120: + y = input->pointer_curr_axis_info.y / 120.0f; + break; + default: + y = 0.0f; + break; + } + + /* clear pointer_curr_axis_info for next frame */ + memset(&input->pointer_curr_axis_info, 0, sizeof(input->pointer_curr_axis_info)); + + if (x != 0.0f || y != 0.0f) { + float scale = _glfwWaylandWindowScale(window); + y *= scale; x *= scale; + _glfwInputScroll(window, x, y, 1, _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 input +static const struct wl_pointer_listener pointer_listener = { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, + pointer_handle_frame, /* Version 5 */ + pointer_handle_axis_source, /* Version 5 */ + pointer_handle_axis_stop, /* Version 5 */ + pointer_handle_axis_discrete, /* Version 5 */ + pointer_handle_axis_value120, /* Version 8 */ + pointer_handle_axis_relative_direction /* Version 9 */ }; static void keyboardHandleKeymap(void* data UNUSED, @@ -572,7 +675,7 @@ static void seatHandleCapabilities(void* data UNUSED, if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer) { _glfw.wl.pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL); + wl_pointer_add_listener(_glfw.wl.pointer, &pointer_listener, NULL); if (_glfw.wl.wp_cursor_shape_manager_v1) { if (_glfw.wl.wp_cursor_shape_device_v1) wp_cursor_shape_device_v1_destroy(_glfw.wl.wp_cursor_shape_device_v1); _glfw.wl.wp_cursor_shape_device_v1 = NULL; diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 0c9c6075b..81c241187 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -147,6 +147,12 @@ enum WaylandWindowPendingState { PENDING_STATE_DECORATION = 2 }; +enum _GLFWWaylandAxisEvent { + AXIS_EVENT_CONTINUOUS = 0, + AXIS_EVENT_DISCRETE, + AXIS_EVENT_VALUE120 +}; + // Wayland-specific per-window data // typedef struct _GLFWwindowWayland @@ -178,6 +184,19 @@ typedef struct _GLFWwindowWayland struct zwlr_layer_surface_v1* zwlr_layer_surface_v1; } layer_shell; + /* information about axis events on current frame */ + struct + { + enum _GLFWWaylandAxisEvent x_axis_type; + float x; + + enum _GLFWWaylandAxisEvent y_axis_type; + float y; + + /* Event timestamp in nanoseconds */ + monotonic_t timestamp_ns; + } pointer_curr_axis_info; + _GLFWcursor* currentCursor; double cursorPosX, cursorPosY, allCursorPosX, allCursorPosY; @@ -242,11 +261,6 @@ typedef struct _GLFWwindowWayland } user_requested_content_size; 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 { diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index ad09a7e63..577583e0f 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -54,6 +54,7 @@ #define GLFW_HAT_RIGHT_DOWN (GLFW_HAT_RIGHT | GLFW_HAT_DOWN) #define GLFW_HAT_LEFT_UP (GLFW_HAT_LEFT | GLFW_HAT_UP) #define GLFW_HAT_LEFT_DOWN (GLFW_HAT_LEFT | GLFW_HAT_DOWN) +#define GLFW_WAYLAND_WHEEL_AXIS_UNIT 10 /*! @} */ /*! @defgroup keys Keyboard keys