[PATCH v2 12/12] macdrv: Improve GDI drawing performance.

Elaine Lefler elaineclefler at gmail.com
Thu Mar 24 23:46:26 CDT 2022


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_app.h    |   2 +
 dlls/winemac.drv/cocoa_app.m    |  59 +++++++---
 dlls/winemac.drv/cocoa_window.h |   3 +-
 dlls/winemac.drv/cocoa_window.m | 201 +++++++++++++++-----------------
 dlls/winemac.drv/macdrv.h       |   1 -
 dlls/winemac.drv/macdrv_cocoa.h |   5 +-
 dlls/winemac.drv/surface.c      | 124 +++++++++-----------
 dlls/winemac.drv/window.c       |   4 +-
 8 files changed, 204 insertions(+), 195 deletions(-)

diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h
index 52c91c0621f..b55c55f2995 100644
--- a/dlls/winemac.drv/cocoa_app.h
+++ b/dlls/winemac.drv/cocoa_app.h
@@ -124,6 +124,8 @@ @interface WineApplicationController : NSObject <NSApplicationDelegate>
 
     id<WineClipCursorHandler> clipCursorHandler;
 
+    NSMutableArray* wineWindows;
+
     NSImage* applicationIcon;
 
     BOOL beenActive;
diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m
index 8c525333e8d..e8b88286d24 100644
--- a/dlls/winemac.drv/cocoa_app.m
+++ b/dlls/winemac.drv/cocoa_app.m
@@ -47,6 +47,25 @@ + (void) setAllowsAutomaticWindowTabbing:(BOOL)allows;
 #endif
 
 
+ at interface NSWindow (WineNSPointExtensions)
+
+/* Reimplementation of -convertPointFromScreen: which isn't available on all
+ * supported macOS versions */
+- (NSPoint)wineConvertPointFromScreen:(NSPoint)point;
+
+ at end
+
+ at implementation NSWindow (WineNSPointExtensions)
+
+- (NSPoint)wineConvertPointFromScreen:(NSPoint)point
+{
+    NSPoint origin = self.frame.origin;
+    return NSMakePoint(point.x - origin.x, point.y - origin.y);
+}
+
+ at end
+
+
 /***********************************************************************
  *              WineLocalizedString
  *
@@ -204,6 +223,7 @@ - (void) dealloc
         [keyWindows release];
         [eventQueues release];
         [eventQueuesLock release];
+        [wineWindows release];
         if (requestsManipQueue) dispatch_release(requestsManipQueue);
         [requests release];
         if (requestSource)
@@ -571,7 +591,6 @@ - (WineWindow*) frontWineWindow
     - (void) adjustWindowLevels:(BOOL)active
     {
         NSArray* windowNumbers;
-        NSMutableArray* wineWindows;
         NSNumber* windowNumber;
         NSUInteger nextFloatingIndex = 0;
         __block NSInteger maxLevel = NSIntegerMin;
@@ -582,6 +601,8 @@ - (void) adjustWindowLevels:(BOOL)active
 
         if ([NSApp isHidden]) return;
 
+        [wineWindows release];
+
         windowNumbers = [NSWindow windowNumbersWithOptions:0];
         wineWindows = [[NSMutableArray alloc] initWithCapacity:[windowNumbers count]];
 
@@ -652,8 +673,6 @@ - (void) adjustWindowLevels:(BOOL)active
 
         NSEnableScreenUpdates();
 
-        [wineWindows release];
-
         // The above took care of the visible windows on the current space.  That
         // leaves windows on other spaces, minimized windows, and windows which
         // are not ordered in.  We want to leave windows on other spaces alone
@@ -1337,19 +1356,33 @@ - (void) handleMouseMove:(NSEvent*)anEvent
             targetWindow = (WineWindow*)[anEvent window];
         else
         {
-            /* Because of the way -[NSWindow setAcceptsMouseMovedEvents:] works, the
-               event indicates its window is the main window, even if the cursor is
-               over a different window.  Find the actual WineWindow that is under the
-               cursor and post the event as being for that window. */
+            /* Due to our use of NSTrackingArea and the way Cocoa directs mouse
+             * moves, the window receiving the event is probably not the one
+             * with the cursor. Find the window that actually has the cursor by
+             * hit-testing front to back. */
             CGPoint cgpoint = CGEventGetLocation([anEvent CGEvent]);
             NSPoint point = [self flippedMouseLocation:NSPointFromCGPoint(cgpoint)];
-            NSInteger windowUnderNumber;
+            WineWindow* window;
 
-            windowUnderNumber = [NSWindow windowNumberAtPoint:point
-                                  belowWindowWithWindowNumber:0];
-            targetWindow = (WineWindow*)[NSApp windowWithWindowNumber:windowUnderNumber];
-            if (!NSMouseInRect(point, [targetWindow contentRectForFrameRect:[targetWindow frame]], NO))
-                targetWindow = nil;
+            targetWindow = nil;
+
+            for (window in wineWindows)
+            {
+                NSPoint windowPoint = [window wineConvertPointFromScreen:point];
+                BOOL isHit = ([window.contentView hitTest:windowPoint] != nil);
+
+                /* Windows with transparency must be instructed to ignore
+                 * mouse-downs when the hovered pixel is not visible. The
+                 * window's tracking area still reports events. */
+                if (window.needsTransparency)
+                    [window setIgnoresMouseEvents:!isHit && NSMouseInRect(windowPoint, window.contentView.frame, NO)];
+
+                if (isHit)
+                {
+                    targetWindow = (WineWindow*)window;
+                    break;
+                }
+            }
         }
 
         if ([targetWindow isKindOfClass:[WineWindow class]])
diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h
index 596e3c52b3e..675abcc659b 100644
--- a/dlls/winemac.drv/cocoa_window.h
+++ b/dlls/winemac.drv/cocoa_window.h
@@ -45,7 +45,6 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
     WineEventQueue* queue;
 
     void* surface;
-    pthread_mutex_t* surface_mutex;
 
     CGDirectDisplayID _lastDisplayID;
     NSTimeInterval _lastDisplayTime;
@@ -95,6 +94,8 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
 @property (readonly, nonatomic) BOOL noForeground;
 @property (readonly, nonatomic) BOOL preventsAppActivation;
 @property (readonly, nonatomic) BOOL floating;
+ at property (readonly, nonatomic) BOOL needsTransparency;
+ at property (readonly, nonatomic) BOOL needsLayerTransparency;
 @property (readonly, getter=isFullscreen, nonatomic) BOOL fullscreen;
 @property (readonly, getter=isFakingClose, nonatomic) BOOL fakingClose;
 @property (readonly, nonatomic) NSRect wine_fractionalFrame;
diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m
index 656a5ba2283..74a84aab630 100644
--- a/dlls/winemac.drv/cocoa_window.m
+++ b/dlls/winemac.drv/cocoa_window.m
@@ -309,32 +309,6 @@ static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTi
 @end
 
 
-#ifndef MAC_OS_X_VERSION_10_14
- at protocol NSViewLayerContentScaleDelegate <NSObject>
- at optional
-
-    - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window;
-
- at end
-#endif
-
-
- at interface CAShapeLayer (WineShapeMaskExtensions)
-
- at property(readonly, nonatomic, getter=isEmptyShaped) BOOL emptyShaped;
-
- at end
-
- at implementation CAShapeLayer (WineShapeMaskExtensions)
-
-    - (BOOL) isEmptyShaped
-    {
-        return CGRectEqualToRect(CGPathGetBoundingBox(self.path), CGRectZero);
-    }
-
- at end
-
-
 @interface WineBaseView : NSView
 @end
 
@@ -351,10 +325,11 @@ - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device;
 #endif
 
 
- at interface WineContentView : WineBaseView <NSTextInputClient, NSViewLayerContentScaleDelegate>
+ at interface WineContentView : WineBaseView <NSTextInputClient>
 {
     NSMutableArray* glContexts;
     NSMutableArray* pendingGlContexts;
+    BOOL _shouldBeHidden;
     BOOL _everHadGLContext;
     BOOL _cachedHasGLDescendant;
     BOOL _cachedHasGLDescendantValid;
@@ -363,7 +338,6 @@ @interface WineContentView : WineBaseView <NSTextInputClient, NSViewLayerContent
     NSMutableAttributedString* markedText;
     NSRange markedTextSelection;
 
-    BOOL _retinaMode;
     int backingSize[2];
 
 #ifdef HAVE_METAL_METAL_H
@@ -402,11 +376,9 @@ @interface WineWindow ()
 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
 
 @property (nonatomic) void* surface;
- at property (nonatomic) pthread_mutex_t* surface_mutex;
 
 @property (retain, nonatomic) NSData* shapeData;
 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
- at property (readonly, nonatomic) BOOL needsTransparency;
 
 @property (nonatomic) BOOL colorKeyed;
 @property (nonatomic) uint8_t colorKeyRed, colorKeyGreen, colorKeyBlue;
@@ -490,13 +462,32 @@ - (BOOL) isFlipped
         return YES;
     }
 
+    - (BOOL) wantsDefaultClipping
+    {
+        /* Don't need this, we already limit our drawing to the dirty region */
+        return NO;
+    }
+
+    - (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
+    {
+        /* Returning NSNull ensures the layer will never animate itself */
+        return [NSNull null];
+    }
+
+    - (BOOL) isOpaque
+    {
+        WineWindow* window = (WineWindow*)[self window];
+        return window.contentView == self && !window.needsLayerTransparency;
+    }
+
     - (NSView*) hitTest:(NSPoint)point
     {
         WineWindow* window = (WineWindow*)[self window];
         NSPoint localPoint;
         CGPoint cgPoint;
 
-        if (window.contentView != self || !window.shapeData)
+        if (window.contentView != self
+                || (!window.shapeData && !window.needsLayerTransparency))
             return [super hitTest:point];
 
         localPoint = [self convertPoint:point fromView:self.superview];
@@ -523,72 +514,76 @@ - (NSView*) hitTest:(NSPoint)point
                 return nil;
         }
 
-        return [super hitTest:point];
-    }
+        if (window.needsLayerTransparency)
+        {
+            /* Transparent pixels are not supposed to be clickable, but due to
+             * contentView.layer.drawsAsynchronously, Cocoa does not enforce it.
+             * Therefore, we must perform our own per-pixel hit test. */
+            if (!surface_hit_test(window.surface, cgPoint, window.colorKeyed,
+                                  window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue))
+                return nil;
+        }
 
-    - (BOOL) wantsUpdateLayer
-    {
-        return YES /*!_everHadGLContext*/;
+        return [super hitTest:point];
     }
 
-    - (void) updateLayer
+    - (void) drawRect:(NSRect)rect
     {
         WineWindow* window = (WineWindow*)[self window];
-        CGImageRef image = NULL;
-        CGRect imageRect;
-        CALayer* layer = [self layer];
+        CGRect imageRect = cgrect_win_from_mac(NSRectToCGRect(rect));
+        CGImageRef image;
 
-        if ([window contentView] != self)
-            return;
+        for (WineOpenGLContext* context in pendingGlContexts)
+        {
+            if (!clearedGlSurface)
+            {
+                context.shouldClearToBlack = TRUE;
+                clearedGlSurface = TRUE;
+            }
+            context.needsUpdate = TRUE;
+        }
+        [glContexts addObjectsFromArray:pendingGlContexts];
+        [pendingGlContexts removeAllObjects];
 
-        if (window.closing || !window.surface || !window.surface_mutex)
+        if ([window contentView] != self)
             return;
 
-        pthread_mutex_lock(window.surface_mutex);
-        if (get_surface_blit_rects(window.surface, NULL, NULL))
+        if ((image = create_surface_image(window.surface, &imageRect, window.colorKeyed,
+                        window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue)) != NULL)
         {
-            imageRect = layer.bounds;
-            imageRect.origin.x *= layer.contentsScale;
-            imageRect.origin.y *= layer.contentsScale;
-            imageRect.size.width *= layer.contentsScale;
-            imageRect.size.height *= layer.contentsScale;
-            image = create_surface_image(window.surface, &imageRect, window.colorKeyed,
-                                         window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue);
-        }
-        pthread_mutex_unlock(window.surface_mutex);
+            CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+            CGContextSetBlendMode(context, kCGBlendModeCopy);
+            /* HQ interpolation should be used with retina to prevent artifacts
+             * on mixed DPI. Not needed for standard DPI. */
+            CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
+
+            CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
+            CGImageRelease(image);
 
-        if (image)
-        {
-            layer.contents = (id)image;
-            CFRelease(image);
             [window windowDidDrawContent];
+        }
 
-            // If the window may be transparent, then we have to invalidate the
-            // shadow every time we draw.  Also, if this is the first time we've
-            // drawn since changing from transparent to opaque.
-            if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
-            {
-                window.shapeChangedSinceLastDraw = FALSE;
-                [window invalidateShadow];
-            }
+        /* If the window may be transparent, then we have to invalidate the
+         * shadow every time we draw.  Also, if this is the first time we've
+         * drawn since changing from transparent to opaque. */
+        if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
+        {
+            window.shapeChangedSinceLastDraw = FALSE;
+            [window invalidateShadow];
         }
     }
 
-    - (void) viewWillDraw
+    - (void) setHidden:(BOOL)hidden
     {
-        [super viewWillDraw];
-
-        for (WineOpenGLContext* context in pendingGlContexts)
+        if (self.window.contentView == self)
         {
-            if (!clearedGlSurface)
-            {
-                context.shouldClearToBlack = TRUE;
-                clearedGlSurface = TRUE;
-            }
-            context.needsUpdate = TRUE;
+            [super setHidden:hidden];
+            return;
         }
-        [glContexts addObjectsFromArray:pendingGlContexts];
-        [pendingGlContexts removeAllObjects];
+
+        /* Client views should always remain hidden, unless we have OpenGL */
+        [super setHidden:hidden || !_everHadGLContext];
+        _shouldBeHidden = hidden;
     }
 
     - (void) addGLContext:(WineOpenGLContext*)context
@@ -617,7 +612,10 @@ - (void) addGLContext:(WineOpenGLContext*)context
 
         _everHadGLContext = YES;
         if (!hadContext)
+        {
+            [super setHidden:_shouldBeHidden];
             [self invalidateHasGLDescendant];
+        }
         [(WineWindow*)[self window] updateForGLSubviews];
     }
 
@@ -721,18 +719,9 @@ - (void) setRetinaMode:(int)mode
         [self setWantsBestResolutionOpenGLSurface:mode];
         [self updateGLContexts];
 
-        _retinaMode = !!mode;
-        [self layer].contentsScale = mode ? 2.0 : 1.0;
-        [self layer].minificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
-        [self layer].magnificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
         [super setRetinaMode:mode];
     }
 
-    - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window
-    {
-        return (_retinaMode || newScale == 1.0);
-    }
-
     - (void) viewDidHide
     {
         [super viewDidHide];
@@ -992,7 +981,7 @@ @implementation WineWindow
 
     @synthesize disabled, noForeground, preventsAppActivation, floating, fullscreen, fakingClose, closing, latentParentWindow, hwnd, queue;
     @synthesize drawnSinceShown;
-    @synthesize surface, surface_mutex;
+    @synthesize surface;
     @synthesize shapeData, shapeChangedSinceLastDraw;
     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
     @synthesize usePerPixelAlpha;
@@ -1026,7 +1015,6 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w
         [window disableCursorRects];
         [window setShowsResizeIndicator:NO];
         [window setHasShadow:wf->shadow];
-        [window setAcceptsMouseMovedEvents:YES];
         [window setDelegate:window];
         [window setBackgroundColor:[NSColor clearColor]];
         [window setOpaque:NO];
@@ -1045,12 +1033,10 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w
         if (!contentView)
             return nil;
         [contentView setWantsLayer:YES];
-        [contentView layer].minificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
-        [contentView layer].magnificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
-        [contentView layer].contentsScale = retina_on ? 2.0 : 1.0;
+        [contentView.layer setDrawsAsynchronously:YES];
         [contentView setAutoresizesSubviews:NO];
 
-        /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
+        /* We use tracking areas instead of setAcceptsMouseMovedEvents:YES
            because they give us mouse moves in the background. */
         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
                                                      options:(NSTrackingMouseMoved |
@@ -2044,28 +2030,37 @@ - (void) setDisabled:(BOOL)newValue
         }
     }
 
+    - (BOOL) needsLayerTransparency
+    {
+        return self.colorKeyed || self.usePerPixelAlpha;
+    }
+
     - (BOOL) needsTransparency
     {
-        return self.contentView.layer.mask || self.colorKeyed || self.usePerPixelAlpha ||
+        return self.shapeData || self.needsLayerTransparency ||
                 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
     }
 
     - (void) checkTransparency
     {
-        if (![self isOpaque] && !self.needsTransparency)
+        if (!self.opaque && !self.needsTransparency)
         {
             self.shapeChangedSinceLastDraw = TRUE;
-            [[self contentView] setNeedsDisplay:YES];
+            [self.contentView setNeedsDisplay:YES];
             [self setBackgroundColor:[NSColor windowBackgroundColor]];
             [self setOpaque:YES];
+            /* Ensure WineApplicationController hasn't cut off mouse events */
+            [self setIgnoresMouseEvents:NO];
         }
-        else if ([self isOpaque] && self.needsTransparency)
+        else if (self.opaque && self.needsTransparency)
         {
             self.shapeChangedSinceLastDraw = TRUE;
-            [[self contentView] setNeedsDisplay:YES];
+            [self.contentView setNeedsDisplay:YES];
             [self setBackgroundColor:[NSColor clearColor]];
             [self setOpaque:NO];
         }
+
+        [self.contentView.layer setOpaque:!self.needsLayerTransparency];
     }
 
     - (void) setShapeData:(NSData*)newShapeData
@@ -2712,8 +2707,6 @@ - (void) setRetinaMode:(int)mode
 
         [transform scaleBy:scale];
 
-        [[self contentView] layer].mask.contentsScale = mode ? 2.0 : 1.0;
-
         for (WineBaseView* subview in [self.contentView subviews])
         {
             if ([subview isKindOfClass:[WineBaseView class]])
@@ -3319,6 +3312,7 @@ void macdrv_destroy_cocoa_window(macdrv_window w)
     WineWindow* window = (WineWindow*)w;
 
     OnMainThread(^{
+        window.surface = nil;
         window.closing = TRUE;
         [window doOrderOut];
         [window close];
@@ -3485,14 +3479,13 @@ void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
 /***********************************************************************
  *              macdrv_set_window_surface
  */
-void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
+void macdrv_set_window_surface(macdrv_window w, void *surface)
 {
     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
     WineWindow* window = (WineWindow*)w;
 
     OnMainThread(^{
         window.surface = surface;
-        window.surface_mutex = mutex;
     });
 
     [pool release];
@@ -3659,9 +3652,7 @@ macdrv_view macdrv_create_view(CGRect rect)
 
         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
         [view setWantsLayer:YES];
-        [view layer].minificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
-        [view layer].magnificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
-        [view layer].contentsScale = retina_on ? 2.0 : 1.0;
+        [view.layer setDrawsAsynchronously:YES];
         [view setAutoresizesSubviews:NO];
         [view setAutoresizingMask:NSViewNotSizable];
         [view setHidden:YES];
diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h
index 3eecb26a01e..7b410fbdef0 100644
--- a/dlls/winemac.drv/macdrv.h
+++ b/dlls/winemac.drv/macdrv.h
@@ -209,7 +209,6 @@ extern DWORD CDECL macdrv_MsgWaitForMultipleObjectsEx(DWORD count, const HANDLE
 extern void activate_on_following_focus(void) DECLSPEC_HIDDEN;
 extern struct window_surface *create_surface(macdrv_window window, const RECT *rect,
                                              struct window_surface *old_surface, BOOL use_alpha) DECLSPEC_HIDDEN;
-extern void set_window_surface(macdrv_window window, struct window_surface *window_surface) DECLSPEC_HIDDEN;
 extern void set_surface_use_alpha(struct window_surface *window_surface, BOOL use_alpha) DECLSPEC_HIDDEN;
 extern void surface_clip_to_visible_rect(struct window_surface *window_surface, const RECT *visible_rect) DECLSPEC_HIDDEN;
 
diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h
index b0eb86133c4..34769771fa7 100644
--- a/dlls/winemac.drv/macdrv_cocoa.h
+++ b/dlls/winemac.drv/macdrv_cocoa.h
@@ -580,10 +580,11 @@ extern void macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
 extern void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame) DECLSPEC_HIDDEN;
 extern void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame) DECLSPEC_HIDDEN;
 extern void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent) DECLSPEC_HIDDEN;
-extern void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex) DECLSPEC_HIDDEN;
+extern void macdrv_set_window_surface(macdrv_window w, void *surface) DECLSPEC_HIDDEN;
 extern CGImageRef create_surface_image(void *window_surface, CGRect *dirty_area, int color_keyed,
                                        uint8_t key_red, uint8_t key_green, uint8_t key_blue) DECLSPEC_HIDDEN;
-extern int get_surface_blit_rects(void *window_surface, const CGRect **rects, int *count) DECLSPEC_HIDDEN;
+extern int surface_hit_test(void *window_surface, CGPoint point, int color_keyed,
+                            uint8_t key_red, uint8_t key_green, uint8_t key_blue) DECLSPEC_HIDDEN;
 extern void macdrv_window_needs_display(macdrv_window w, CGRect rect) DECLSPEC_HIDDEN;
 extern void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count) DECLSPEC_HIDDEN;
 extern void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha) DECLSPEC_HIDDEN;
diff --git a/dlls/winemac.drv/surface.c b/dlls/winemac.drv/surface.c
index eed31229473..0eb1f09d417 100644
--- a/dlls/winemac.drv/surface.c
+++ b/dlls/winemac.drv/surface.c
@@ -64,7 +64,6 @@ struct macdrv_window_surface
     HRGN                    region;
     HRGN                    drawn;
     BOOL                    use_alpha;
-    RGNDATA                *blit_data;
     struct shadow_surface  *shadow;
     BYTE                   *bits;
     pthread_mutex_t         mutex;
@@ -317,27 +316,6 @@ static void shadow_cfdata_dealloc(void *ptr, void *info)
     shadow_bitmap_return(info);
 }
 
-/***********************************************************************
- *              update_blit_data
- */
-static void update_blit_data(struct macdrv_window_surface *surface)
-{
-    HeapFree(GetProcessHeap(), 0, surface->blit_data);
-    surface->blit_data = NULL;
-
-    if (surface->drawn)
-    {
-        HRGN blit = CreateRectRgn(0, 0, 0, 0);
-
-        if (CombineRgn(blit, surface->drawn, 0, RGN_COPY) > NULLREGION &&
-            (!surface->region || CombineRgn(blit, blit, surface->region, RGN_AND) > NULLREGION) &&
-            OffsetRgn(blit, surface->header.rect.left, surface->header.rect.top) > NULLREGION)
-            surface->blit_data = get_region_data(blit, 0);
-
-        DeleteObject(blit);
-    }
-}
-
 /***********************************************************************
  *              macdrv_surface_lock
  */
@@ -401,7 +379,6 @@ static void macdrv_surface_set_region(struct window_surface *window_surface, HRG
         if (surface->region) DeleteObject(surface->region);
         surface->region = 0;
     }
-    update_blit_data(surface);
 
     window_surface->funcs->unlock(window_surface);
 }
@@ -433,7 +410,6 @@ static void macdrv_surface_flush(struct window_surface *window_surface)
         else
             surface->drawn = region;
     }
-    update_blit_data(surface);
     reset_bounds(&surface->bounds);
 
     window_surface->funcs->unlock(window_surface);
@@ -459,7 +435,7 @@ static void macdrv_surface_destroy(struct window_surface *window_surface)
     }
     if (surface->bits && surface->bits != MAP_FAILED)
         munmap(surface->bits, surface->info.bmiHeader.biSizeImage);
-    HeapFree(GetProcessHeap(), 0, surface->blit_data);
+
     pthread_mutex_destroy(&surface->mutex);
     HeapFree(GetProcessHeap(), 0, surface);
 }
@@ -515,7 +491,7 @@ struct window_surface *create_surface(macdrv_window window, const RECT *rect,
 
     surface->info.bmiHeader.biSize        = sizeof(surface->info.bmiHeader);
     surface->info.bmiHeader.biWidth       = width;
-    surface->info.bmiHeader.biHeight      = -height; /* top-down */
+    surface->info.bmiHeader.biHeight      = height; /* bottom-up */
     surface->info.bmiHeader.biPlanes      = 1;
     surface->info.bmiHeader.biBitCount    = 32;
     surface->info.bmiHeader.biSizeImage   = get_dib_image_size(&surface->info);
@@ -542,7 +518,6 @@ struct window_surface *create_surface(macdrv_window window, const RECT *rect,
             surface->drawn = 0;
         }
     }
-    update_blit_data(surface);
     surface->use_alpha = use_alpha;
     surface->shadow = shadow_surface_create(surface);
     if (!surface->shadow) goto failed;
@@ -574,48 +549,6 @@ void set_surface_use_alpha(struct window_surface *window_surface, BOOL use_alpha
     if (surface) surface->use_alpha = use_alpha;
 }
 
-/***********************************************************************
- *              set_window_surface
- */
-void set_window_surface(macdrv_window window, struct window_surface *window_surface)
-{
-    struct macdrv_window_surface *surface = get_mac_surface(window_surface);
-    macdrv_set_window_surface(window, window_surface, surface ? &surface->mutex : NULL);
-}
-
-/***********************************************************************
- *              get_surface_blit_rects
- *
- * Caller must hold the surface lock.  Indirectly returns the surface
- * blit region rects.  Returns zero if the surface has nothing to blit;
- * returns non-zero if the surface does have rects to blit (drawn area
- * which isn't clipped away by a surface region).
- *
- * IMPORTANT: This function is called from non-Wine threads, so it
- *            must not use Win32 or Wine functions, including debug
- *            logging.
- */
-int get_surface_blit_rects(void *window_surface, const CGRect **rects, int *count)
-{
-    struct macdrv_window_surface *surface = get_mac_surface(window_surface);
-
-    if (rects && count)
-    {
-        if (surface->blit_data)
-        {
-            *rects = (const CGRect*)surface->blit_data->Buffer;
-            *count = surface->blit_data->rdh.nCount;
-        }
-        else
-        {
-            *rects = NULL;
-            *count = 0;
-        }
-    }
-
-    return (surface->blit_data != NULL && surface->blit_data->rdh.nCount > 0);
-}
-
 /***********************************************************************
  *              create_surface_image
  *
@@ -779,10 +712,59 @@ void surface_clip_to_visible_rect(struct window_surface *window_surface, const R
         {
             CombineRgn(surface->drawn, surface->drawn, region, RGN_AND);
             DeleteObject(region);
-
-            update_blit_data(surface);
         }
     }
 
     window_surface->funcs->unlock(window_surface);
 }
+
+/***********************************************************************
+ *              surface_hit_test
+ *
+ * Performs a per-pixel hit test on the given surface. Returns FALSE if
+ * the chosen pixel is transparent or keyed out, TRUE if the pixel is
+ * clickable.
+ *
+ * IMPORTANT: This function is called from non-Wine threads, so it
+ *            must not use Win32 or Wine functions, including debug
+ *            logging.
+ */
+int surface_hit_test(void *window_surface, CGPoint point, int color_keyed,
+                     uint8_t key_red, uint8_t key_green, uint8_t key_blue)
+{
+    struct macdrv_window_surface *surface = get_mac_surface(window_surface);
+    DWORD key = (key_red << 16) | (key_green << 8) | (key_blue);
+    DWORD pixel;
+    int surface_width, bytes_per_row;
+    /* Note: coordinates can be non-integers. Truncate. */
+    int point_x = point.x, point_y = point.y;
+    int retval = TRUE;
+
+    if (!surface)
+        return TRUE;
+
+    pthread_mutex_lock(&surface->mutex);
+    surface_width = surface->header.rect.right - surface->header.rect.left;
+    bytes_per_row = get_dib_stride(surface_width, 32);
+
+    if (!surface->use_alpha && !color_keyed)
+        /* Opaque surface always succeeds */
+        goto done;
+
+    if (point_x < surface->header.rect.left || point_x >= surface->header.rect.right
+            || point_y < surface->header.rect.top || point_y >= surface->header.rect.bottom)
+    {
+        retval = FALSE;
+        goto done;
+    }
+
+    pixel = *(DWORD*)(surface->bits + (point_x - surface->header.rect.left) * 4
+                      + (surface->header.rect.bottom - point_y) * bytes_per_row);
+
+    retval = !((color_keyed && (pixel & 0x00FFFFFF) == key)
+               || (surface->use_alpha && (pixel & 0xFF000000) == 0));
+
+done:
+    pthread_mutex_unlock(&surface->mutex);
+    return retval;
+}
diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c
index 9177f493a5f..dba87df37de 100644
--- a/dlls/winemac.drv/window.c
+++ b/dlls/winemac.drv/window.c
@@ -1899,7 +1899,7 @@ BOOL CDECL macdrv_UpdateLayeredWindow(HWND hwnd, const UPDATELAYEREDWINDOWINFO *
     if (!surface || !EqualRect(&surface->rect, &rect))
     {
         data->surface = create_surface(data->cocoa_window, &rect, NULL, TRUE);
-        set_window_surface(data->cocoa_window, data->surface);
+        macdrv_set_window_surface(data->cocoa_window, data->surface);
         if (surface) window_surface_release(surface);
         surface = data->surface;
         if (data->unminimized_surface)
@@ -2139,7 +2139,7 @@ void CDECL macdrv_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags,
         }
         else
         {
-            set_window_surface(data->cocoa_window, surface);
+            macdrv_set_window_surface(data->cocoa_window, surface);
             if (data->unminimized_surface)
             {
                 window_surface_release(data->unminimized_surface);
-- 
2.32.0 (Apple Git-132)




More information about the wine-devel mailing list