[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