diff --git a/glfw/internal.h b/glfw/internal.h index 66faf6034..edf77742b 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -887,6 +887,11 @@ MonitorGeometry _glfwPlatformGetMonitorGeometry(_GLFWmonitor* monitor); bool _glfwPlatformGrabKeyboard(bool grab); void glfw_handle_scroll_event_for_momentum(_GLFWwindow *w, const GLFWScrollEvent *ev, bool stopped, bool is_finger_based); #define glfw_cancel_momentum_scroll() glfw_handle_scroll_event_for_momentum(NULL, NULL, false, false) +#ifdef _GLFW_X11 +#define momentum_scroll_gesture_detection_timeout_ms 50 +#else +#define momentum_scroll_gesture_detection_timeout_ms 0 +#endif char* _glfw_strdup(const char* source); diff --git a/glfw/momentum-scroll.c b/glfw/momentum-scroll.c index 3af379140..b0a558e6f 100644 --- a/glfw/momentum-scroll.c +++ b/glfw/momentum-scroll.c @@ -147,6 +147,7 @@ send_momentum_event(bool is_start) { m = GLFW_MOMENTUM_PHASE_ENDED; if (s.timer_id) glfwRemoveTimer(s.timer_id); s.timer_id = 0; + s.state = NONE; } GLFWScrollEvent e = { .offset_type=GLFW_SCROLL_OFFEST_HIGHRES, .momentum_type=m, .unscaled.x=s.velocity.x, .unscaled.y=s.velocity.y, @@ -179,12 +180,14 @@ void glfw_handle_scroll_event_for_momentum( _GLFWwindow *w, const GLFWScrollEvent *ev, bool stopped, bool is_finger_based ) { + const bool is_synthetic_momentum_start_event = stopped && momentum_scroll_gesture_detection_timeout_ms; if (!w) { cancel_existing_scroll(true); return; } if (!is_finger_based || ev->offset_type != GLFW_SCROLL_OFFEST_HIGHRES || s.friction < 0 || s.friction >= 1) { _glfwInputScroll(w, ev); return; } monotonic_t now = monotonic(); + if (is_synthetic_momentum_start_event) now -= ms_to_monotonic_t(momentum_scroll_gesture_detection_timeout_ms); if (s.state == PHYSICAL_EVENT_IN_PROGRESS) { s.physical_event.displacement.x += ev->unscaled.x; s.physical_event.displacement.y += ev->unscaled.y; @@ -200,13 +203,15 @@ glfw_handle_scroll_event_for_momentum( else if (ev->unscaled.x > 0) s.scale = ev->x_offset / ev->unscaled.x; if (s.window_id && s.window_id != w->id) cancel_existing_scroll(true); if (s.state != PHYSICAL_EVENT_IN_PROGRESS) cancel_existing_scroll(false); - // Check for change in direction - double ldx, ldy; last_sample_delta(&ldx, &ldy); - if (ldx * ev->x_offset < 0 || ldy * ev->y_offset < 0) cancel_existing_scroll(true); + if (!is_synthetic_momentum_start_event) { + // Check for change in direction + double ldx, ldy; last_sample_delta(&ldx, &ldy); + if (ldx * ev->x_offset < 0 || ldy * ev->y_offset < 0) cancel_existing_scroll(true); + } s.window_id = w->id; s.keyboard_modifiers = ev->keyboard_modifiers; if (ev->offset_type == GLFW_SCROLL_OFFEST_HIGHRES) { - add_sample(ev->unscaled.x, ev->unscaled.y, now); + if (!is_synthetic_momentum_start_event) add_sample(ev->unscaled.x, ev->unscaled.y, now); if (stopped) s.state = is_suitable_for_momentum() ? MOMENTUM_IN_PROGRESS : NONE; else s.state = PHYSICAL_EVENT_IN_PROGRESS; } else { diff --git a/glfw/x11_window.c b/glfw/x11_window.c index 7df048057..86150b85e 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -52,7 +52,6 @@ static struct { unsigned long long timer_id; GLFWid window_id; - bool is_finger_based; GLFWScrollEvent last_event; } x11_momentum_scroll_state = {0}; @@ -61,13 +60,13 @@ x11_scroll_stop_timer_callback(unsigned long long timer_id UNUSED, void *data UN x11_momentum_scroll_state.timer_id = 0; _GLFWwindow *w = _glfwWindowForId(x11_momentum_scroll_state.window_id); if (w) { - glfw_handle_scroll_event_for_momentum( - w, &x11_momentum_scroll_state.last_event, true, x11_momentum_scroll_state.is_finger_based); + x11_momentum_scroll_state.last_event.y_offset = 0; x11_momentum_scroll_state.last_event.x_offset = 0; + x11_momentum_scroll_state.last_event.unscaled.x = 0; x11_momentum_scroll_state.last_event.unscaled.y = 0; + glfw_handle_scroll_event_for_momentum(w, &x11_momentum_scroll_state.last_event, true, true); } else { // Window no longer exists, cancel any ongoing momentum glfw_cancel_momentum_scroll(); } - x11_momentum_scroll_state.window_id = 0; } static void @@ -1375,16 +1374,16 @@ handle_xi_motion_event(_GLFWwindow *window, XIDeviceEvent *de) { if (d->is_highres && d->is_finger_based && type == GLFW_SCROLL_OFFEST_HIGHRES) { // Reset the timer on each scroll event x11_cancel_momentum_scroll_timer(); - + // Store the event for later use when timer fires x11_momentum_scroll_state.window_id = window->id; - x11_momentum_scroll_state.is_finger_based = true; x11_momentum_scroll_state.last_event = ev; - - // Start timer (100ms after last scroll event) + + // Start timer x11_momentum_scroll_state.timer_id = glfwAddTimer( - ms_to_monotonic_t(100), false, x11_scroll_stop_timer_callback, NULL, NULL); - + ms_to_monotonic_t(momentum_scroll_gesture_detection_timeout_ms), false, + x11_scroll_stop_timer_callback, NULL, NULL); + // Send the scroll event through momentum handler glfw_handle_scroll_event_for_momentum(window, &ev, false, true); } else { @@ -1613,16 +1612,18 @@ static void processEvent(XEvent *event) { const int mods = translateState(event->xbutton.state); - // Cancel momentum scrolling on any button press - x11_cancel_momentum_scroll_timer(); - glfw_cancel_momentum_scroll(); +#define cancel_momentum() x11_cancel_momentum_scroll_timer(); glfw_cancel_momentum_scroll() - if (event->xbutton.button == Button1) + if (event->xbutton.button == Button1) { + cancel_momentum(); _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods); - else if (event->xbutton.button == Button2) + } else if (event->xbutton.button == Button2) { + cancel_momentum(); _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods); - else if (event->xbutton.button == Button3) + } else if (event->xbutton.button == Button3) { + cancel_momentum(); _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods); + } // Modern X provides scroll events as mouse button presses // Only use these if smooth scrolling is not available @@ -1649,6 +1650,7 @@ static void processEvent(XEvent *event) else { + cancel_momentum(); // Additional buttons after 7 are treated as regular buttons // We subtract 4 to fill the gap left by scroll input above _glfwInputMouseClick(window, @@ -1664,12 +1666,10 @@ static void processEvent(XEvent *event) { const int mods = translateState(event->xbutton.state); - // Cancel momentum scrolling on any button release - x11_cancel_momentum_scroll_timer(); - glfw_cancel_momentum_scroll(); if (event->xbutton.button == Button1) { + cancel_momentum(); _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, @@ -1677,6 +1677,7 @@ static void processEvent(XEvent *event) } else if (event->xbutton.button == Button2) { + cancel_momentum(); _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_RELEASE, @@ -1684,6 +1685,7 @@ static void processEvent(XEvent *event) } else if (event->xbutton.button == Button3) { + cancel_momentum(); _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_RELEASE, @@ -1691,6 +1693,7 @@ static void processEvent(XEvent *event) } else if (event->xbutton.button > Button7) { + cancel_momentum(); // Additional buttons after 7 are treated as regular buttons // We subtract 4 to fill the gap left by scroll input above _glfwInputMouseClick(window, @@ -1700,6 +1703,7 @@ static void processEvent(XEvent *event) } return; +#undef cancel_momentum } case EnterNotify: