From 3e3d982185c62020b7478f433486ade19378ed6e Mon Sep 17 00:00:00 2001 From: Ken Thomases Date: Tue, 7 May 2013 03:00:49 -0500 Subject: [PATCH] winemac: Prefer absolute mouse moves unless cursor is pinned by clipping or desktop edges. This fixes an issue with sub-pixel-precise pointing devices on Lion and later. Wine's notion of the cursor position would get out of sync with the actual position because deltas don't convey the actual movement distance. --- dlls/winemac.drv/cocoa_app.h | 1 + dlls/winemac.drv/cocoa_app.m | 80 +++++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h index 5e4d181d622..3ce8a83ccb9 100644 --- a/dlls/winemac.drv/cocoa_app.h +++ b/dlls/winemac.drv/cocoa_app.h @@ -57,6 +57,7 @@ @interface WineApplicationController : NSObject CGFloat primaryScreenHeight; BOOL primaryScreenHeightValid; + NSMutableData* screenFrameCGRects; WineWindow* lastTargetWindow; BOOL forceNextMouseMoveAbsolute; diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index e8cca28f0f4..b4e15d0ae37 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -170,6 +170,7 @@ - (id) init - (void) dealloc { + [screenFrameCGRects release]; [applicationIcon release]; [warpRecords release]; [cursorTimer release]; @@ -377,10 +378,29 @@ - (CGFloat) primaryScreenHeight if (!primaryScreenHeightValid) { NSArray* screens = [NSScreen screens]; - if ([screens count]) + NSUInteger count = [screens count]; + if (count) { + NSUInteger size; + CGRect* rect; + NSScreen* screen; + primaryScreenHeight = NSHeight([[screens objectAtIndex:0] frame]); primaryScreenHeightValid = TRUE; + + size = count * sizeof(CGRect); + if (!screenFrameCGRects) + screenFrameCGRects = [[NSMutableData alloc] initWithLength:size]; + else + [screenFrameCGRects setLength:size]; + + rect = [screenFrameCGRects mutableBytes]; + for (screen in screens) + { + CGRect temp = NSRectToCGRect([screen frame]); + temp.origin.y = primaryScreenHeight - CGRectGetMaxY(temp); + *rect++ = temp; + } } else return 1280; /* arbitrary value */ @@ -1157,9 +1177,9 @@ - (void) handleMouseMove:(NSEvent*)anEvent if ([targetWindow isKindOfClass:[WineWindow class]]) { + CGPoint point = CGEventGetLocation([anEvent CGEvent]); macdrv_event* event; - BOOL absolute = forceNextMouseMoveAbsolute || (targetWindow != lastTargetWindow); - forceNextMouseMoveAbsolute = FALSE; + BOOL absolute; // If we recently warped the cursor (other than in our cursor-clipping // event tap), discard mouse move events until we see an event which is @@ -1170,13 +1190,63 @@ - (void) handleMouseMove:(NSEvent*)anEvent return; lastSetCursorPositionTime = 0; + forceNextMouseMoveAbsolute = TRUE; + } + + if (forceNextMouseMoveAbsolute || targetWindow != lastTargetWindow) + { absolute = TRUE; + forceNextMouseMoveAbsolute = FALSE; + } + else + { + // Send absolute move events if the cursor is in the interior of + // its range. Only send relative moves if the cursor is pinned to + // the boundaries of where it can go. We compute the position + // that's one additional point in the direction of movement. If + // that is outside of the clipping rect or desktop region (the + // union of the screen frames), then we figure the cursor would + // have moved outside if it could but it was pinned. + CGPoint computedPoint = point; + CGFloat deltaX = [anEvent deltaX]; + CGFloat deltaY = [anEvent deltaY]; + + if (deltaX > 0.001) + computedPoint.x++; + else if (deltaX < -0.001) + computedPoint.x--; + + if (deltaY > 0.001) + computedPoint.y++; + else if (deltaY < -0.001) + computedPoint.y--; + + // Assume cursor is pinned for now + absolute = FALSE; + if (!clippingCursor || CGRectContainsPoint(cursorClipRect, computedPoint)) + { + const CGRect* rects; + NSUInteger count, i; + + // Caches screenFrameCGRects if necessary + [self primaryScreenHeight]; + + rects = [screenFrameCGRects bytes]; + count = [screenFrameCGRects length] / sizeof(rects[0]); + + for (i = 0; i < count; i++) + { + if (CGRectContainsPoint(rects[i], computedPoint)) + { + absolute = TRUE; + break; + } + } + } } if (absolute) { - CGPoint point = CGEventGetLocation([anEvent CGEvent]); - if (clippingCursor) [self clipCursorLocation:&point];