winemac: Implement ClipCursor().
This commit is contained in:
parent
5c933c89ad
commit
9b0fdc645f
|
@ -59,7 +59,13 @@ @interface WineApplication : NSApplication <NSApplicationDelegate>
|
|||
NSTimer* cursorTimer;
|
||||
BOOL cursorHidden;
|
||||
|
||||
BOOL clippingCursor;
|
||||
CGRect cursorClipRect;
|
||||
CFMachPortRef cursorClippingEventTap;
|
||||
NSMutableArray* warpRecords;
|
||||
CGPoint synthesizedLocation;
|
||||
NSTimeInterval lastSetCursorPositionTime;
|
||||
NSTimeInterval lastEventTapEventTime;
|
||||
}
|
||||
|
||||
@property (nonatomic) CGEventSourceKeyboardType keyboardType;
|
||||
|
@ -79,6 +85,8 @@ - (void) windowGotFocus:(WineWindow*)window;
|
|||
|
||||
- (void) keyboardSelectionDidChange;
|
||||
|
||||
- (void) flipRect:(NSRect*)rect;
|
||||
|
||||
- (void) wineWindow:(WineWindow*)window
|
||||
ordered:(NSWindowOrderingMode)order
|
||||
relativeTo:(WineWindow*)otherWindow;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
*/
|
||||
|
||||
#import <Carbon/Carbon.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#import "cocoa_app.h"
|
||||
#import "cocoa_event.h"
|
||||
|
@ -28,6 +29,27 @@
|
|||
int macdrv_err_on;
|
||||
|
||||
|
||||
@interface WarpRecord : NSObject
|
||||
{
|
||||
CGEventTimestamp timeBefore, timeAfter;
|
||||
CGPoint from, to;
|
||||
}
|
||||
|
||||
@property (nonatomic) CGEventTimestamp timeBefore;
|
||||
@property (nonatomic) CGEventTimestamp timeAfter;
|
||||
@property (nonatomic) CGPoint from;
|
||||
@property (nonatomic) CGPoint to;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation WarpRecord
|
||||
|
||||
@synthesize timeBefore, timeAfter, from, to;
|
||||
|
||||
@end;
|
||||
|
||||
|
||||
@interface WineApplication ()
|
||||
|
||||
@property (readwrite, copy, nonatomic) NSEvent* lastFlagsChanged;
|
||||
|
@ -56,8 +78,10 @@ - (id) init
|
|||
|
||||
originalDisplayModes = [[NSMutableDictionary alloc] init];
|
||||
|
||||
warpRecords = [[NSMutableArray alloc] init];
|
||||
|
||||
if (!eventQueues || !eventQueuesLock || !keyWindows || !orderedWineWindows ||
|
||||
!originalDisplayModes)
|
||||
!originalDisplayModes || !warpRecords)
|
||||
{
|
||||
[self release];
|
||||
return nil;
|
||||
|
@ -68,6 +92,7 @@ - (id) init
|
|||
|
||||
- (void) dealloc
|
||||
{
|
||||
[warpRecords release];
|
||||
[cursorTimer release];
|
||||
[cursorFrames release];
|
||||
[originalDisplayModes release];
|
||||
|
@ -263,6 +288,14 @@ actually off by one (precisely because they were flipped from
|
|||
return point;
|
||||
}
|
||||
|
||||
- (void) flipRect:(NSRect*)rect
|
||||
{
|
||||
// We don't use -primaryScreenHeight here so there's no chance of having
|
||||
// out-of-date cached info. This method is called infrequently enough
|
||||
// that getting the screen height each time is not prohibitively expensive.
|
||||
rect->origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect);
|
||||
}
|
||||
|
||||
- (void) wineWindow:(WineWindow*)window
|
||||
ordered:(NSWindowOrderingMode)order
|
||||
relativeTo:(WineWindow*)otherWindow
|
||||
|
@ -548,17 +581,283 @@ - (void) setCursorWithFrames:(NSArray*)frames
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ---------- Cursor clipping methods ----------
|
||||
*
|
||||
* Neither Quartz nor Cocoa has an exact analog for Win32 cursor clipping.
|
||||
* For one simple case, clipping to a 1x1 rectangle, Quartz does have an
|
||||
* equivalent: CGAssociateMouseAndMouseCursorPosition(false). For the
|
||||
* general case, we leverage that. We disassociate mouse movements from
|
||||
* the cursor position and then move the cursor manually, keeping it within
|
||||
* the clipping rectangle.
|
||||
*
|
||||
* Moving the cursor manually isn't enough. We need to modify the event
|
||||
* stream so that the events have the new location, too. We need to do
|
||||
* this at a point before the events enter Cocoa, so that Cocoa will assign
|
||||
* the correct window to the event. So, we install a Quartz event tap to
|
||||
* do that.
|
||||
*
|
||||
* Also, there's a complication when we move the cursor. We use
|
||||
* CGWarpMouseCursorPosition(). That doesn't generate mouse movement
|
||||
* events, but the change of cursor position is incorporated into the
|
||||
* deltas of the next mouse move event. When the mouse is disassociated
|
||||
* from the cursor position, we need the deltas to only reflect actual
|
||||
* device movement, not programmatic changes. So, the event tap cancels
|
||||
* out the change caused by our calls to CGWarpMouseCursorPosition().
|
||||
*/
|
||||
- (void) clipCursorLocation:(CGPoint*)location
|
||||
{
|
||||
if (location->x < CGRectGetMinX(cursorClipRect))
|
||||
location->x = CGRectGetMinX(cursorClipRect);
|
||||
if (location->y < CGRectGetMinY(cursorClipRect))
|
||||
location->y = CGRectGetMinY(cursorClipRect);
|
||||
if (location->x > CGRectGetMaxX(cursorClipRect) - 1)
|
||||
location->x = CGRectGetMaxX(cursorClipRect) - 1;
|
||||
if (location->y > CGRectGetMaxY(cursorClipRect) - 1)
|
||||
location->y = CGRectGetMaxY(cursorClipRect) - 1;
|
||||
}
|
||||
|
||||
- (BOOL) warpCursorTo:(CGPoint*)newLocation from:(const CGPoint*)currentLocation
|
||||
{
|
||||
CGPoint oldLocation;
|
||||
|
||||
if (currentLocation)
|
||||
oldLocation = *currentLocation;
|
||||
else
|
||||
oldLocation = NSPointToCGPoint([self flippedMouseLocation:[NSEvent mouseLocation]]);
|
||||
|
||||
if (!CGPointEqualToPoint(oldLocation, *newLocation))
|
||||
{
|
||||
WarpRecord* warpRecord = [[[WarpRecord alloc] init] autorelease];
|
||||
CGError err;
|
||||
|
||||
warpRecord.from = oldLocation;
|
||||
warpRecord.timeBefore = [[NSProcessInfo processInfo] systemUptime] * NSEC_PER_SEC;
|
||||
|
||||
/* Actually move the cursor. */
|
||||
err = CGWarpMouseCursorPosition(*newLocation);
|
||||
if (err != kCGErrorSuccess)
|
||||
return FALSE;
|
||||
|
||||
warpRecord.timeAfter = [[NSProcessInfo processInfo] systemUptime] * NSEC_PER_SEC;
|
||||
*newLocation = NSPointToCGPoint([self flippedMouseLocation:[NSEvent mouseLocation]]);
|
||||
|
||||
if (!CGPointEqualToPoint(oldLocation, *newLocation))
|
||||
{
|
||||
warpRecord.to = *newLocation;
|
||||
[warpRecords addObject:warpRecord];
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
- (BOOL) isMouseMoveEventType:(CGEventType)type
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case kCGEventMouseMoved:
|
||||
case kCGEventLeftMouseDragged:
|
||||
case kCGEventRightMouseDragged:
|
||||
case kCGEventOtherMouseDragged:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
- (int) warpsFinishedByEventTime:(CGEventTimestamp)eventTime location:(CGPoint)eventLocation
|
||||
{
|
||||
int warpsFinished = 0;
|
||||
for (WarpRecord* warpRecord in warpRecords)
|
||||
{
|
||||
if (warpRecord.timeAfter < eventTime ||
|
||||
(warpRecord.timeBefore <= eventTime && CGPointEqualToPoint(eventLocation, warpRecord.to)))
|
||||
warpsFinished++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return warpsFinished;
|
||||
}
|
||||
|
||||
- (CGEventRef) eventTapWithProxy:(CGEventTapProxy)proxy
|
||||
type:(CGEventType)type
|
||||
event:(CGEventRef)event
|
||||
{
|
||||
CGEventTimestamp eventTime;
|
||||
CGPoint eventLocation, cursorLocation;
|
||||
|
||||
if (type == kCGEventTapDisabledByUserInput)
|
||||
return event;
|
||||
if (type == kCGEventTapDisabledByTimeout)
|
||||
{
|
||||
CGEventTapEnable(cursorClippingEventTap, TRUE);
|
||||
return event;
|
||||
}
|
||||
|
||||
if (!clippingCursor)
|
||||
return event;
|
||||
|
||||
eventTime = CGEventGetTimestamp(event);
|
||||
lastEventTapEventTime = eventTime / (double)NSEC_PER_SEC;
|
||||
|
||||
eventLocation = CGEventGetLocation(event);
|
||||
|
||||
cursorLocation = NSPointToCGPoint([self flippedMouseLocation:[NSEvent mouseLocation]]);
|
||||
|
||||
if ([self isMouseMoveEventType:type])
|
||||
{
|
||||
double deltaX, deltaY;
|
||||
int warpsFinished = [self warpsFinishedByEventTime:eventTime location:eventLocation];
|
||||
int i;
|
||||
|
||||
deltaX = CGEventGetDoubleValueField(event, kCGMouseEventDeltaX);
|
||||
deltaY = CGEventGetDoubleValueField(event, kCGMouseEventDeltaY);
|
||||
|
||||
for (i = 0; i < warpsFinished; i++)
|
||||
{
|
||||
WarpRecord* warpRecord = [warpRecords objectAtIndex:0];
|
||||
deltaX -= warpRecord.to.x - warpRecord.from.x;
|
||||
deltaY -= warpRecord.to.y - warpRecord.from.y;
|
||||
[warpRecords removeObjectAtIndex:0];
|
||||
}
|
||||
|
||||
if (warpsFinished)
|
||||
{
|
||||
CGEventSetDoubleValueField(event, kCGMouseEventDeltaX, deltaX);
|
||||
CGEventSetDoubleValueField(event, kCGMouseEventDeltaY, deltaY);
|
||||
}
|
||||
|
||||
synthesizedLocation.x += deltaX;
|
||||
synthesizedLocation.y += deltaY;
|
||||
}
|
||||
|
||||
// If the event is destined for another process, don't clip it. This may
|
||||
// happen if the user activates Exposé or Mission Control. In that case,
|
||||
// our app does not resign active status, so clipping is still in effect,
|
||||
// but the cursor should not actually be clipped.
|
||||
//
|
||||
// In addition, the fact that mouse moves may have been delivered to a
|
||||
// different process means we have to treat the next one we receive as
|
||||
// absolute rather than relative.
|
||||
if (CGEventGetIntegerValueField(event, kCGEventTargetUnixProcessID) == getpid())
|
||||
[self clipCursorLocation:&synthesizedLocation];
|
||||
else
|
||||
lastSetCursorPositionTime = lastEventTapEventTime;
|
||||
|
||||
[self warpCursorTo:&synthesizedLocation from:&cursorLocation];
|
||||
if (!CGPointEqualToPoint(eventLocation, synthesizedLocation))
|
||||
CGEventSetLocation(event, synthesizedLocation);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
CGEventRef WineAppEventTapCallBack(CGEventTapProxy proxy, CGEventType type,
|
||||
CGEventRef event, void *refcon)
|
||||
{
|
||||
WineApplication* app = refcon;
|
||||
return [app eventTapWithProxy:proxy type:type event:event];
|
||||
}
|
||||
|
||||
- (BOOL) installEventTap
|
||||
{
|
||||
ProcessSerialNumber psn;
|
||||
OSErr err;
|
||||
CGEventMask mask = CGEventMaskBit(kCGEventLeftMouseDown) |
|
||||
CGEventMaskBit(kCGEventLeftMouseUp) |
|
||||
CGEventMaskBit(kCGEventRightMouseDown) |
|
||||
CGEventMaskBit(kCGEventRightMouseUp) |
|
||||
CGEventMaskBit(kCGEventMouseMoved) |
|
||||
CGEventMaskBit(kCGEventLeftMouseDragged) |
|
||||
CGEventMaskBit(kCGEventRightMouseDragged) |
|
||||
CGEventMaskBit(kCGEventOtherMouseDown) |
|
||||
CGEventMaskBit(kCGEventOtherMouseUp) |
|
||||
CGEventMaskBit(kCGEventOtherMouseDragged) |
|
||||
CGEventMaskBit(kCGEventScrollWheel);
|
||||
CFRunLoopSourceRef source;
|
||||
void* appServices;
|
||||
OSErr (*pGetCurrentProcess)(ProcessSerialNumber* PSN);
|
||||
|
||||
if (cursorClippingEventTap)
|
||||
return TRUE;
|
||||
|
||||
// We need to get the Mac GetCurrentProcess() from the ApplicationServices
|
||||
// framework with dlsym() because the Win32 function of the same name
|
||||
// obscures it.
|
||||
appServices = dlopen("/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices", RTLD_LAZY);
|
||||
if (!appServices)
|
||||
return FALSE;
|
||||
|
||||
pGetCurrentProcess = dlsym(appServices, "GetCurrentProcess");
|
||||
if (!pGetCurrentProcess)
|
||||
{
|
||||
dlclose(appServices);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
err = pGetCurrentProcess(&psn);
|
||||
dlclose(appServices);
|
||||
if (err != noErr)
|
||||
return FALSE;
|
||||
|
||||
// We create an annotated session event tap rather than a process-specific
|
||||
// event tap because we need to programmatically move the cursor even when
|
||||
// mouse moves are directed to other processes. We disable our tap when
|
||||
// other processes are active, but things like Exposé are handled by other
|
||||
// processes even when we remain active.
|
||||
cursorClippingEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionDefault, mask, WineAppEventTapCallBack, self);
|
||||
if (!cursorClippingEventTap)
|
||||
return FALSE;
|
||||
|
||||
CGEventTapEnable(cursorClippingEventTap, FALSE);
|
||||
|
||||
source = CFMachPortCreateRunLoopSource(NULL, cursorClippingEventTap, 0);
|
||||
if (!source)
|
||||
{
|
||||
CFRelease(cursorClippingEventTap);
|
||||
cursorClippingEventTap = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
|
||||
CFRelease(source);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
- (BOOL) setCursorPosition:(CGPoint)pos
|
||||
{
|
||||
BOOL ret;
|
||||
|
||||
ret = (CGWarpMouseCursorPosition(pos) == kCGErrorSuccess);
|
||||
if (clippingCursor)
|
||||
{
|
||||
[self clipCursorLocation:&pos];
|
||||
|
||||
synthesizedLocation = pos;
|
||||
ret = [self warpCursorTo:&synthesizedLocation from:NULL];
|
||||
if (ret)
|
||||
{
|
||||
// We want to discard mouse-move events that have already been
|
||||
// through the event tap, because it's too late to account for
|
||||
// the setting of the cursor position with them. However, the
|
||||
// events that may be queued with times after that but before
|
||||
// the above warp can still be used. So, use the last event
|
||||
// tap event time so that -sendEvent: doesn't discard them.
|
||||
lastSetCursorPositionTime = lastEventTapEventTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = (CGWarpMouseCursorPosition(pos) == kCGErrorSuccess);
|
||||
if (ret)
|
||||
lastSetCursorPositionTime = [[NSProcessInfo processInfo] systemUptime];
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
WineEventQueue* queue;
|
||||
|
||||
lastSetCursorPositionTime = [[NSProcessInfo processInfo] systemUptime];
|
||||
|
||||
// Discard all pending mouse move events.
|
||||
[eventQueuesLock lock];
|
||||
for (queue in eventQueues)
|
||||
|
@ -573,6 +872,56 @@ - (BOOL) setCursorPosition:(CGPoint)pos
|
|||
return ret;
|
||||
}
|
||||
|
||||
- (void) activateCursorClipping
|
||||
{
|
||||
if (clippingCursor)
|
||||
{
|
||||
CGEventTapEnable(cursorClippingEventTap, TRUE);
|
||||
[self setCursorPosition:NSPointToCGPoint([self flippedMouseLocation:[NSEvent mouseLocation]])];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) deactivateCursorClipping
|
||||
{
|
||||
if (clippingCursor)
|
||||
{
|
||||
CGEventTapEnable(cursorClippingEventTap, FALSE);
|
||||
[warpRecords removeAllObjects];
|
||||
lastSetCursorPositionTime = [[NSProcessInfo processInfo] systemUptime];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) startClippingCursor:(CGRect)rect
|
||||
{
|
||||
CGError err;
|
||||
|
||||
if (!cursorClippingEventTap && ![self installEventTap])
|
||||
return FALSE;
|
||||
|
||||
err = CGAssociateMouseAndMouseCursorPosition(false);
|
||||
if (err != kCGErrorSuccess)
|
||||
return FALSE;
|
||||
|
||||
clippingCursor = TRUE;
|
||||
cursorClipRect = rect;
|
||||
if ([self isActive])
|
||||
[self activateCursorClipping];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
- (BOOL) stopClippingCursor
|
||||
{
|
||||
CGError err = CGAssociateMouseAndMouseCursorPosition(true);
|
||||
if (err != kCGErrorSuccess)
|
||||
return FALSE;
|
||||
|
||||
[self deactivateCursorClipping];
|
||||
clippingCursor = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ---------- NSApplication method overrides ----------
|
||||
|
@ -612,8 +961,9 @@ - (void) sendEvent:(NSEvent*)anEvent
|
|||
BOOL absolute = forceNextMouseMoveAbsolute || (targetWindow != lastTargetWindow);
|
||||
forceNextMouseMoveAbsolute = FALSE;
|
||||
|
||||
// If we recently warped the cursor, discard mouse move events until
|
||||
// we see an event which is later than that time.
|
||||
// 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
|
||||
// later than that time.
|
||||
if (lastSetCursorPositionTime)
|
||||
{
|
||||
if ([anEvent timestamp] <= lastSetCursorPositionTime)
|
||||
|
@ -651,6 +1001,8 @@ - (void) sendEvent:(NSEvent*)anEvent
|
|||
*/
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification
|
||||
{
|
||||
[self activateCursorClipping];
|
||||
|
||||
[orderedWineWindows enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop){
|
||||
WineWindow* window = obj;
|
||||
if ([window levelWhenActive] != [window level])
|
||||
|
@ -740,6 +1092,8 @@ - (void)applicationWillFinishLaunching:(NSNotification *)notification
|
|||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification
|
||||
{
|
||||
[self deactivateCursorClipping];
|
||||
|
||||
[orderedWineWindows enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop){
|
||||
WineWindow* window = obj;
|
||||
NSInteger level = window.floating ? NSFloatingWindowLevel : NSNormalWindowLevel;
|
||||
|
@ -946,3 +1300,45 @@ int macdrv_set_cursor_position(CGPoint pos)
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* macdrv_clip_cursor
|
||||
*
|
||||
* Sets the cursor cursor clipping rectangle. If the rectangle is equal
|
||||
* to or larger than the whole desktop region, the cursor is unclipped.
|
||||
* Returns zero on failure, non-zero on success.
|
||||
*/
|
||||
int macdrv_clip_cursor(CGRect rect)
|
||||
{
|
||||
__block int ret;
|
||||
|
||||
OnMainThread(^{
|
||||
BOOL clipping = FALSE;
|
||||
|
||||
if (!CGRectIsInfinite(rect))
|
||||
{
|
||||
NSRect nsrect = NSRectFromCGRect(rect);
|
||||
NSScreen* screen;
|
||||
|
||||
/* Convert the rectangle from top-down coords to bottom-up. */
|
||||
[NSApp flipRect:&nsrect];
|
||||
|
||||
clipping = FALSE;
|
||||
for (screen in [NSScreen screens])
|
||||
{
|
||||
if (!NSContainsRect(nsrect, [screen frame]))
|
||||
{
|
||||
clipping = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clipping)
|
||||
ret = [NSApp startClippingCursor:rect];
|
||||
else
|
||||
ret = [NSApp stopClippingCursor];
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -144,8 +144,6 @@ @interface WineWindow ()
|
|||
|
||||
@property (readwrite, nonatomic) NSInteger levelWhenActive;
|
||||
|
||||
+ (void) flipRect:(NSRect*)rect;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
@ -255,7 +253,7 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w
|
|||
WineContentView* contentView;
|
||||
NSTrackingArea* trackingArea;
|
||||
|
||||
[self flipRect:&window_frame];
|
||||
[NSApp flipRect:&window_frame];
|
||||
|
||||
window = [[[self alloc] initWithContentRect:window_frame
|
||||
styleMask:style_mask_for_features(wf)
|
||||
|
@ -309,11 +307,6 @@ - (void) dealloc
|
|||
[super dealloc];
|
||||
}
|
||||
|
||||
+ (void) flipRect:(NSRect*)rect
|
||||
{
|
||||
rect->origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect);
|
||||
}
|
||||
|
||||
- (void) adjustFeaturesForState
|
||||
{
|
||||
NSUInteger style = normalStyleMask;
|
||||
|
@ -524,7 +517,7 @@ - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
|
|||
|
||||
/* Origin is (left, top) in a top-down space. Need to convert it to
|
||||
(left, bottom) in a bottom-up space. */
|
||||
[[self class] flipRect:&contentRect];
|
||||
[NSApp flipRect:&contentRect];
|
||||
|
||||
if (on_screen)
|
||||
{
|
||||
|
@ -1094,7 +1087,7 @@ - (void)windowDidResize:(NSNotification *)notification
|
|||
macdrv_event event;
|
||||
NSRect frame = [self contentRectForFrameRect:[self frame]];
|
||||
|
||||
[[self class] flipRect:&frame];
|
||||
[NSApp flipRect:&frame];
|
||||
|
||||
/* Coalesce events by discarding any previous ones still in the queue. */
|
||||
[queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
|
||||
|
@ -1314,7 +1307,7 @@ void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
|
|||
NSRect frame;
|
||||
|
||||
frame = [window contentRectForFrameRect:[window frame]];
|
||||
[[window class] flipRect:&frame];
|
||||
[NSApp flipRect:&frame];
|
||||
*out_frame = NSRectToCGRect(frame);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@
|
|||
extern void macdrv_set_cursor(CFStringRef name, CFArrayRef frames) DECLSPEC_HIDDEN;
|
||||
extern int macdrv_get_cursor_position(CGPoint *pos) DECLSPEC_HIDDEN;
|
||||
extern int macdrv_set_cursor_position(CGPoint pos) DECLSPEC_HIDDEN;
|
||||
extern int macdrv_clip_cursor(CGRect rect) DECLSPEC_HIDDEN;
|
||||
|
||||
|
||||
/* display */
|
||||
|
|
|
@ -735,6 +735,32 @@ void CDECL macdrv_DestroyCursorIcon(HCURSOR cursor)
|
|||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* ClipCursor (MACDRV.@)
|
||||
*
|
||||
* Set the cursor clipping rectangle.
|
||||
*/
|
||||
BOOL CDECL macdrv_ClipCursor(LPCRECT clip)
|
||||
{
|
||||
CGRect rect;
|
||||
|
||||
TRACE("%s\n", wine_dbgstr_rect(clip));
|
||||
|
||||
if (clip)
|
||||
{
|
||||
rect = CGRectMake(clip->left, clip->top, max(1, clip->right - clip->left),
|
||||
max(1, clip->bottom - clip->top));
|
||||
}
|
||||
else
|
||||
rect = CGRectInfinite;
|
||||
|
||||
/* FIXME: This needs to be done not just in this process but in all of the
|
||||
ones for this WINEPREFIX. Broadcast a message to do that. */
|
||||
|
||||
return macdrv_clip_cursor(rect);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* GetCursorPos (MACDRV.@)
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
@ cdecl ActivateKeyboardLayout(long long) macdrv_ActivateKeyboardLayout
|
||||
@ cdecl Beep() macdrv_Beep
|
||||
@ cdecl ChangeDisplaySettingsEx(ptr ptr long long long) macdrv_ChangeDisplaySettingsEx
|
||||
@ cdecl ClipCursor(ptr) macdrv_ClipCursor
|
||||
@ cdecl CreateDesktopWindow(long) macdrv_CreateDesktopWindow
|
||||
@ cdecl CreateWindow(long) macdrv_CreateWindow
|
||||
@ cdecl DestroyCursorIcon(long) macdrv_DestroyCursorIcon
|
||||
|
|
Loading…
Reference in New Issue