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.
This commit is contained in:
Ken Thomases 2014-05-14 20:15:35 -05:00 committed by Alexandre Julliard
parent 757c57634e
commit 451915100a
4 changed files with 69 additions and 18 deletions

View File

@ -66,6 +66,9 @@ enum {
double mouseMoveDeltaX, mouseMoveDeltaY;
NSUInteger unmatchedMouseDowns;
NSTimeInterval lastScrollTime;
double accumScrollX, accumScrollY;
NSMutableDictionary* originalDisplayModes;
NSMutableDictionary* latentDisplayModes;
BOOL displaysCapturedForFullscreen;

View File

@ -1725,7 +1725,7 @@ int macdrv_err_on;
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 @@ int macdrv_err_on;
/* 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)

View File

@ -148,6 +148,7 @@ extern int left_option_is_alt DECLSPEC_HIDDEN;
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;

View File

@ -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);
}