From 451915100a61a2e5f3e848d0c6ab7fb477b08d56 Mon Sep 17 00:00:00 2001 From: Ken Thomases Date: Wed, 14 May 2014 20:15:35 -0500 Subject: [PATCH] winemac: Add the ability to disable high-resolution scrolling. The Mac driver can generate scroll wheel events with values which are not integral multiples of WHEEL_DELTA. Apps should handle that by scrolling a corresponding non-integral multiple of what they'd do for a WHEEL_DELTA-valued scroll or, if they can't, then at least accumulate scroll distance until its magnitude exceeds WHEEL_DELTA and do a "chunky" scroll. However, many apps don't do that properly. They may scroll way too far/fast or even in the opposite direction. If the registry setting UsePreciseScrolling is set to "n", the Mac driver will do that accumulation and chunking itself to work around such broken app behavior. --- dlls/winemac.drv/cocoa_app.h | 3 ++ dlls/winemac.drv/cocoa_app.m | 79 +++++++++++++++++++++++++-------- dlls/winemac.drv/macdrv_cocoa.h | 1 + dlls/winemac.drv/macdrv_main.c | 4 ++ 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h index 0f8926da0fe..fc6c1ecbce8 100644 --- a/dlls/winemac.drv/cocoa_app.h +++ b/dlls/winemac.drv/cocoa_app.h @@ -66,6 +66,9 @@ @interface WineApplicationController : NSObject double mouseMoveDeltaX, mouseMoveDeltaY; NSUInteger unmatchedMouseDowns; + NSTimeInterval lastScrollTime; + double accumScrollX, accumScrollY; + NSMutableDictionary* originalDisplayModes; NSMutableDictionary* latentDisplayModes; BOOL displaysCapturedForFullscreen; diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index e5594d1d395..d454513d34d 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -1725,7 +1725,7 @@ - (void) handleScrollWheel:(NSEvent*)theEvent if (process) { macdrv_event* event; - CGFloat x, y; + double x, y; BOOL continuous = FALSE; event = macdrv_create_event(MOUSE_SCROLL, window); @@ -1768,26 +1768,69 @@ - (void) handleScrollWheel:(NSEvent*)theEvent /* The x,y values so far are in pixels. Win32 expects to receive some fraction of WHEEL_DELTA == 120. By my estimation, that's roughly 6 times the pixel value. */ - event->mouse_scroll.x_scroll = 6 * x; - event->mouse_scroll.y_scroll = 6 * y; + x *= 6; + y *= 6; - if (!continuous) + if (use_precise_scrolling) { - /* For non-continuous "clicky" wheels, if there was any motion, make - sure there was at least WHEEL_DELTA motion. This is so, at slow - speeds where the system's acceleration curve is actually reducing the - scroll distance, the user is sure to get some action out of each click. - For example, this is important for rotating though weapons in a - first-person shooter. */ - if (0 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 120) - event->mouse_scroll.x_scroll = 120; - else if (-120 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 0) - event->mouse_scroll.x_scroll = -120; + event->mouse_scroll.x_scroll = x; + event->mouse_scroll.y_scroll = y; - if (0 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 120) - event->mouse_scroll.y_scroll = 120; - else if (-120 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 0) - event->mouse_scroll.y_scroll = -120; + if (!continuous) + { + /* For non-continuous "clicky" wheels, if there was any motion, make + sure there was at least WHEEL_DELTA motion. This is so, at slow + speeds where the system's acceleration curve is actually reducing the + scroll distance, the user is sure to get some action out of each click. + For example, this is important for rotating though weapons in a + first-person shooter. */ + if (0 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 120) + event->mouse_scroll.x_scroll = 120; + else if (-120 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 0) + event->mouse_scroll.x_scroll = -120; + + if (0 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 120) + event->mouse_scroll.y_scroll = 120; + else if (-120 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 0) + event->mouse_scroll.y_scroll = -120; + } + } + else + { + /* If it's been a while since the last scroll event or if the scrolling has + reversed direction, reset the accumulated scroll value. */ + if ([theEvent timestamp] - lastScrollTime > 1) + accumScrollX = accumScrollY = 0; + else + { + /* The accumulated scroll value is in the opposite direction/sign of the last + scroll. That's because it's the "debt" resulting from over-scrolling in + that direction. We accumulate by adding in the scroll amount and then, if + it has the same sign as the scroll value, we subtract any whole or partial + WHEEL_DELTAs, leaving it 0 or the opposite sign. So, the user switched + scroll direction if the accumulated debt and the new scroll value have the + same sign. */ + if ((accumScrollX < 0 && x < 0) || (accumScrollX > 0 && x > 0)) + accumScrollX = 0; + if ((accumScrollY < 0 && y < 0) || (accumScrollY > 0 && y > 0)) + accumScrollY = 0; + } + lastScrollTime = [theEvent timestamp]; + + accumScrollX += x; + accumScrollY += y; + + if (accumScrollX > 0 && x > 0) + event->mouse_scroll.x_scroll = 120 * ceil(accumScrollX / 120); + if (accumScrollX < 0 && x < 0) + event->mouse_scroll.x_scroll = 120 * -ceil(-accumScrollX / 120); + if (accumScrollY > 0 && y > 0) + event->mouse_scroll.y_scroll = 120 * ceil(accumScrollY / 120); + if (accumScrollY < 0 && y < 0) + event->mouse_scroll.y_scroll = 120 * -ceil(-accumScrollY / 120); + + accumScrollX -= event->mouse_scroll.x_scroll; + accumScrollY -= event->mouse_scroll.y_scroll; } if (event->mouse_scroll.x_scroll || event->mouse_scroll.y_scroll) diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index d3d3ffc256c..30b6b644924 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -148,6 +148,7 @@ extern int right_option_is_alt DECLSPEC_HIDDEN; extern int allow_immovable_windows DECLSPEC_HIDDEN; extern int cursor_clipping_locks_windows DECLSPEC_HIDDEN; +extern int use_precise_scrolling DECLSPEC_HIDDEN; extern int macdrv_start_cocoa_app(unsigned long long tickcount) DECLSPEC_HIDDEN; extern void macdrv_window_rejected_focus(const struct macdrv_event *event) DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c index 300b1a7b1cb..38352a82bb6 100644 --- a/dlls/winemac.drv/macdrv_main.c +++ b/dlls/winemac.drv/macdrv_main.c @@ -56,6 +56,7 @@ BOOL allow_software_rendering = FALSE; BOOL disable_window_decorations = FALSE; int allow_immovable_windows = TRUE; int cursor_clipping_locks_windows = TRUE; +int use_precise_scrolling = TRUE; HMODULE macdrv_module = 0; @@ -179,6 +180,9 @@ static void setup_options(void) if (!get_config_key(hkey, appkey, "CursorClippingLocksWindows", buffer, sizeof(buffer))) cursor_clipping_locks_windows = IS_OPTION_TRUE(buffer[0]); + if (!get_config_key(hkey, appkey, "UsePreciseScrolling", buffer, sizeof(buffer))) + use_precise_scrolling = IS_OPTION_TRUE(buffer[0]); + if (appkey) RegCloseKey(appkey); if (hkey) RegCloseKey(hkey); }