[PATCH v2 10/12] macdrv: Hit test windows based on shape data.
Elaine Lefler
elaineclefler at gmail.com
Thu Mar 24 23:46:24 CDT 2022
Allows layered window to handle invisible-pixel clicks properly.
Signed-off-by: Elaine Lefler <elaineclefler at 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;
+ at 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];
+ }
}
});
--
2.32.0 (Apple Git-132)
More information about the wine-devel
mailing list