Allows layered window to handle invisible-pixel clicks properly.
Signed-off-by: Elaine Lefler elaineclefler@gmail.com ---
v2: Was PATCH 3/3. Splitting into smaller patches as per feedback. --- dlls/winemac.drv/cocoa_window.h | 1 + dlls/winemac.drv/cocoa_window.m | 111 ++++++++++++++++++++++++-------- 2 files changed, 84 insertions(+), 28 deletions(-)
diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h index a83f2aa803b..b1f7e595ec9 100644 --- a/dlls/winemac.drv/cocoa_window.h +++ b/dlls/winemac.drv/cocoa_window.h @@ -53,6 +53,7 @@ @interface WineWindow : NSPanel <NSWindowDelegate> NSRect wineFrame; NSRect roundedWineFrame;
+ NSData* shapeData; BOOL shapeChangedSinceLastDraw;
BOOL colorKeyed; diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index bfa7e2fe8cc..1b66c97b19a 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -404,6 +404,7 @@ @interface WineWindow () @property (nonatomic) void* surface; @property (nonatomic) pthread_mutex_t* surface_mutex;
+@property (retain, nonatomic) NSData* shapeData; @property (nonatomic) BOOL shapeChangedSinceLastDraw; @property (readonly, nonatomic) BOOL needsTransparency;
@@ -416,8 +417,6 @@ @interface WineWindow ()
@property (readonly, copy, nonatomic) NSArray* childWineWindows;
- - (void) setShape:(CGPathRef)newShape; - - (void) updateForGLSubviews;
- (BOOL) becameEligibleParentOrChild; @@ -491,6 +490,42 @@ - (BOOL) isFlipped return YES; }
+ - (NSView*) hitTest:(NSPoint)point + { + WineWindow* window = (WineWindow*)[self window]; + NSPoint localPoint; + CGPoint cgPoint; + + if (window.contentView != self || !window.shapeData) + return [super hitTest:point]; + + localPoint = [self convertPoint:point fromView:self.superview]; + cgPoint = cgpoint_win_from_mac(NSPointToCGPoint(localPoint)); + + if (window.shapeData) + { + NSPoint nsPoint = NSPointFromCGPoint(cgPoint); + const CGRect* rects = (const CGRect*)window.shapeData.bytes; + NSUInteger count = window.shapeData.length / sizeof(*rects); + BOOL inShape = NO; + NSUInteger i; + + for (i = 0; i < count; i++) + { + if (NSMouseInRect(nsPoint, NSRectFromCGRect(rects[i]), NO)) + { + inShape = YES; + break; + } + } + + if (!inShape) + return nil; + } + + return [super hitTest:point]; + } + - (BOOL) wantsUpdateLayer { return YES /*!_everHadGLContext*/; @@ -958,7 +993,7 @@ @implementation WineWindow @synthesize disabled, noForeground, preventsAppActivation, floating, fullscreen, fakingClose, closing, latentParentWindow, hwnd, queue; @synthesize drawnSinceShown; @synthesize surface, surface_mutex; - @synthesize shapeChangedSinceLastDraw; + @synthesize shapeData, shapeChangedSinceLastDraw; @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue; @synthesize usePerPixelAlpha; @synthesize imeData, commandDone; @@ -1062,6 +1097,7 @@ - (void) dealloc [queue release]; [latentChildWindows release]; [latentParentWindow release]; + [shapeData release]; [super dealloc]; }
@@ -2032,23 +2068,47 @@ - (void) checkTransparency } }
- - (void) setShape:(CGPathRef)newShape + - (void) setShapeData:(NSData*)newShapeData { - CALayer* layer = [[self contentView] layer]; - CAShapeLayer* mask = (CAShapeLayer*)layer.mask; - if (CGPathEqualToPath(newShape, mask.path)) return; + if (shapeData == newShapeData) + return; + + if ([newShapeData length] == 0) + newShapeData = nil; + + if (shapeData) + { + CGRect boundingBox = CGPathGetBoundingBox([(CAShapeLayer*)self.contentView.layer.mask path]); + [shapeData release]; + [self.contentView setNeedsDisplayInRect:NSRectFromCGRect(boundingBox)]; + } + + if (newShapeData) + { + const CGRect* rects = (const CGRect*)newShapeData.bytes; + NSUInteger count = newShapeData.length / sizeof(*rects); + NSUInteger i; + + CGMutablePathRef path = CGPathCreateMutable(); + CAShapeLayer* maskLayer; + + for (i = 0; i < count; i++) + CGPathAddRect(path, nil, cgrect_mac_from_win(rects[i])); + + maskLayer = [CAShapeLayer layer]; + maskLayer.path = path; + self.contentView.layer.mask = maskLayer;
- if (newShape && !layer.mask) - layer.mask = mask = [CAShapeLayer layer]; - else if (!newShape) - layer.mask = mask = nil; + [self.contentView setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(path))];
- if (mask.path) - [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(mask.path))]; - if (newShape) - [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(newShape))]; + CFRelease(path); + } + else + { + self.contentView.layer.mask = nil; + }
- mask.path = newShape; + shapeData = [newShapeData retain]; self.shapeChangedSinceLastDraw = TRUE;
[self checkTransparency]; @@ -2250,8 +2310,7 @@ - (void) checkWineDisplayLink
- (BOOL) isEmptyShaped { - CAShapeLayer* mask = (CAShapeLayer*)[[self contentView] layer].mask; - return ([mask isEmptyShaped]); + return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero))); }
- (BOOL) canProvideSnapshot @@ -3471,19 +3530,15 @@ void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count) OnMainThread(^{ if (!rects || !count) { - [window setShape:NULL]; - [window checkEmptyShaped]; + window.shapeData = nil; } else { - CGMutablePathRef path; - unsigned int i; - - path = CGPathCreateMutable(); - for (i = 0; i < count; i++) - CGPathAddRect(path, NULL, cgrect_mac_from_win(rects[i])); - [window setShape:path]; - CGPathRelease(path); + size_t length = sizeof(*rects) * count; + if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length)) + { + window.shapeData = [NSData dataWithBytes:rects length:length]; + } } });