Wayland has 3 types of scrolling events:
- axis. Used for e.g., touchpad 2 finger smooth scrolling - axis_discrete. Used for mouse scroll wheels (i.e., notches) - axis_value120. Used for high resolution input devices
Wine currently only supports axis_discrete, meaning that 2 finger scroll events get ignored.
This commit tries to add basic support for axis scrolling events, by translating the smooth motion in scroll increments using some primitive assumptions about line height and number of lines to scroll.
-- v5: wip! winewayland.drv: Try to support smooth scroll events
From: Ray Strode rstrode@redhat.com
Wayland has 3 types of scrolling events:
- axis. Used for e.g., touchpad 2 finger smooth scrolling - axis_discrete. Used for mouse scroll wheels (i.e., notches) - axis_value120. Used for high resolution input devices
Wine currently only supports axis_discrete, meaning that 2 finger scroll events get ignored.
This commit tries to add basic support for axis scrolling events, by translating the smooth motion in scroll increments using some primitive assumptions about line height and number of lines to scroll. --- dlls/winewayland.drv/wayland_pointer.c | 85 ++++++++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 2 + 2 files changed, 87 insertions(+)
diff --git a/dlls/winewayland.drv/wayland_pointer.c b/dlls/winewayland.drv/wayland_pointer.c index fad75c8506c..c3153363865 100644 --- a/dlls/winewayland.drv/wayland_pointer.c +++ b/dlls/winewayland.drv/wayland_pointer.c @@ -191,10 +191,91 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { + struct wayland_pointer *pointer = &process_wayland.pointer; + INPUT input = {0}; + HWND hwnd; + /* wheel events aren't in pixel units but a fractional unit proportional + * to the number of lines to scroll. By default a value of WHEEL_DELTA means + * scroll the application content by 3 lines. + * + * When smooth scrolling with two fingers on a touchpad instead of with a + * wheel, we don't want to scroll by 3 lines, since that makes it hard to + * scroll to a precise line, and any lost speed can be made up for by doing + * a bigger swipe anyway. It's better to scroll a line at a time for small + * swipes. + * + * We also don't know exactly how big a "line" is in pixel units, which is + * what gets reported to this event handler. We can approximate it by + * saying a line is 18 pixels, and we're probably in the right ballpark + * most of the time. + * + * We also don't want to scroll the full swipe value in one go, because if + * it's a big swipe, it's likely to be several lines which can look + * discontinuous and jarring. For it to scroll relatively smoothly, we + * should report intermediate events every half line or so. + */ + const double pixels_per_line = 18.0; + const double lines_per_wheel_spin = 3.0; + const double wheel_delta_per_line = (WHEEL_DELTA / lines_per_wheel_spin); + const int32_t events_per_line = 2; + bool invert = false; + double lines_to_scroll; + double wheel_delta_for_value; + int32_t wheel_delta_per_event; + + double pixels_scrolled; + + if (!(hwnd = wayland_pointer_get_focused_hwnd())) return; + + if (pointer->scroll_frame_number == pointer->next_frame_number) return; + + input.type = INPUT_MOUSE; + + pixels_scrolled = wl_fixed_to_double(value); + + lines_to_scroll = pixels_scrolled / pixels_per_line; + wheel_delta_for_value = lines_to_scroll * wheel_delta_per_line; + + switch (axis) + { + case WL_POINTER_AXIS_VERTICAL_SCROLL: + input.mi.dwFlags = MOUSEEVENTF_WHEEL; + invert = true; + break; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + input.mi.dwFlags = MOUSEEVENTF_HWHEEL; + break; + default: break; + } + + TRACE("hwnd=%p axis=%u value=%lf\n", hwnd, axis, pixels_scrolled); + + wheel_delta_per_event = (int32_t) round(wheel_delta_per_line / events_per_line); + for (int32_t i = 0; i < ((int) lines_to_scroll) * events_per_line; i++) + { + if (wheel_delta_for_value < wheel_delta_for_event) break; + + wheel_delta_for_value -= wheel_delta_per_event; + + if (wheel_delta_for_value < wheel_delta_for_event) + { + wheel_delta_per_event += (int32_t) round(wheel_delta_for_value); + wheel_delta_for_value = 0.0; + } + input.mi.mouseData = wheel_delta_per_event; + + if (invert) + input.mi.mouseData *= -1; + + __wine_send_input(hwnd, &input, NULL); + } }
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) { + struct wayland_pointer *pointer = &process_wayland.pointer; + + pointer->next_frame_number++; }
static void pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer, @@ -210,6 +291,7 @@ static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { + struct wayland_pointer *pointer = &process_wayland.pointer; INPUT input = {0}; HWND hwnd;
@@ -233,6 +315,7 @@ static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_point TRACE("hwnd=%p axis=%u discrete=%d\n", hwnd, axis, discrete);
__wine_send_input(hwnd, &input, NULL); + pointer->scroll_frame_number = pointer->next_frame_number; }
static const struct wl_pointer_listener pointer_listener = @@ -333,6 +416,7 @@ void wayland_pointer_init(struct wl_pointer *wl_pointer) pointer->wl_pointer = wl_pointer; pointer->focused_hwnd = NULL; pointer->enter_serial = 0; + pointer->next_frame_number = 0; pthread_mutex_unlock(&pointer->mutex); wl_pointer_add_listener(pointer->wl_pointer, &pointer_listener, NULL); } @@ -361,6 +445,7 @@ void wayland_pointer_deinit(void) pointer->wl_pointer = NULL; pointer->focused_hwnd = NULL; pointer->enter_serial = 0; + pointer->next_frame_number = 0; pthread_mutex_unlock(&pointer->mutex); }
diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 0883c43f1ff..fc7fd6ee3fc 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -99,6 +99,8 @@ struct wayland_pointer HWND constraint_hwnd; uint32_t enter_serial; uint32_t button_serial; + uint32_t scroll_frame_number; + uint32_t next_frame_number; struct wayland_cursor cursor; pthread_mutex_t mutex; };
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=141677
Your paranoid android.
=== debian11b (64 bit WoW report) ===
msi: action.c:4719: Test failed: Directory not created action.c:4806: Test failed: Directory not created action.c:5232: Test failed: Directory not created action.c:5333: Test failed: Directory not created action.c:5414: Test failed: Directory not created action.c:5566: Test failed: Directory not created action.c:5741: Test succeeded inside todo block: directory not removed action.c:5882: Test failed: directory removed action.c:5987: Test failed: directory removed automation.c:2486: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:3142: Test failed: Directory not created install.c:3165: Test failed: Directory not created install.c:3190: Test succeeded inside todo block: Directory created install.c:3214: Test failed: Directory not created install.c:3251: Test succeeded inside todo block: Directory is created install.c:3296: Test succeeded inside todo block: Directory is created install.c:3339: Test failed: Directory not created install.c:3372: Test failed: Directory not created install.c:3402: Test failed: Directory not created install.c:3464: Test failed: Directory not created install.c:3524: Test failed: Directory not created install.c:3602: Test failed: Directory not created install.c:3843: Test failed: Directory not created install.c:3882: Test succeeded inside todo block: Directory created install.c:3894: Test failed: Directory not created install.c:3910: Test failed: Directory not created install.c:4041: Test failed: Directory created install.c:4104: Test failed: Directory not created install.c:4122: Test failed: Directory not created install.c:4163: Test succeeded inside todo block: Directory created install.c:4173: Test succeeded inside todo block: Directory created install.c:4263: Test failed: Directory created install.c:4318: Test failed: Directory not created install.c:4334: Test failed: Directory not created install.c:4350: Test failed: Directory not created install.c:4838: Test failed: Directory created install.c:4852: Test failed: Directory created install.c:2588: Test failed: Directory not created install.c:4918: Test failed: Directory not created install.c:4960: Test failed: Directory not created install.c:4965: Test failed: Directory not created install.c:4970: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:5121: Test failed: Directory not created install.c:5136: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:5385: Test failed: Directory not present or not empty install.c:5447: Test failed: Directory not present or not empty install.c:2588: Test failed: Directory not created install.c:2588: Test failed: Directory not created install.c:2588: Test failed: Directory not created
thanks for testing, i just pushed a new (untested) algorithm but given what you're saying I think it will probably be worse. I guess we probably need increase the lines scrolled per swipe in a non-linear way