From a767ee99fa59a15980847bc9af980a860919f114 Mon Sep 17 00:00:00 2001
From: Ken Thomases <ken@codeweavers.com>
Date: Thu, 28 Mar 2013 23:34:06 -0500
Subject: [PATCH] winemac: Track drawn surface region to reduce black flicker
 for new or resized windows.

---
 dlls/winemac.drv/cocoa_window.m | 10 +---
 dlls/winemac.drv/macdrv.h       |  3 +-
 dlls/winemac.drv/macdrv_cocoa.h |  2 +-
 dlls/winemac.drv/surface.c      | 84 ++++++++++++++++++++++++++-------
 dlls/winemac.drv/window.c       |  4 +-
 5 files changed, 74 insertions(+), 29 deletions(-)

diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m
index 279f81fb2e3..1d5c551c9d9 100644
--- a/dlls/winemac.drv/cocoa_window.m
+++ b/dlls/winemac.drv/cocoa_window.m
@@ -186,18 +186,10 @@ - (void) drawRect:(NSRect)rect
         if (window.surface && window.surface_mutex &&
             !pthread_mutex_lock(window.surface_mutex))
         {
-            CGRect bounds;
             const CGRect* rects;
             int count;
 
-            if (!get_surface_region_rects(window.surface, &rects, &count))
-            {
-                bounds = NSRectToCGRect([self bounds]);
-                rects = &bounds;
-                count = 1;
-            }
-
-            if (count)
+            if (get_surface_blit_rects(window.surface, &rects, &count) && count)
             {
                 CGContextRef context;
                 int i;
diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h
index 970a228c5dc..f8c45ab21a5 100644
--- a/dlls/winemac.drv/macdrv.h
+++ b/dlls/winemac.drv/macdrv.h
@@ -137,7 +137,8 @@ static inline RECT rect_from_cgrect(CGRect cgrect)
 extern void release_win_data(struct macdrv_win_data *data) DECLSPEC_HIDDEN;
 extern macdrv_window macdrv_get_cocoa_window(HWND hwnd, BOOL require_on_screen) DECLSPEC_HIDDEN;
 extern RGNDATA *get_region_data(HRGN hrgn, HDC hdc_lptodp) DECLSPEC_HIDDEN;
-extern struct window_surface *create_surface(macdrv_window window, const RECT *rect, BOOL use_alpha) 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;
 
diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h
index c014fbe3cfc..2d749698f1f 100644
--- a/dlls/winemac.drv/macdrv_cocoa.h
+++ b/dlls/winemac.drv/macdrv_cocoa.h
@@ -319,7 +319,7 @@ extern int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
 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 CGImageRef create_surface_image(void *window_surface, CGRect *rect, int copy_data) DECLSPEC_HIDDEN;
-extern int get_surface_region_rects(void *window_surface, const CGRect **rects, int *count) DECLSPEC_HIDDEN;
+extern int get_surface_blit_rects(void *window_surface, const CGRect **rects, int *count) 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 29fc7f885eb..564d004f711 100644
--- a/dlls/winemac.drv/surface.c
+++ b/dlls/winemac.drv/surface.c
@@ -61,8 +61,10 @@ struct macdrv_window_surface
     struct window_surface   header;
     macdrv_window           window;
     RECT                    bounds;
+    HRGN                    region;
+    HRGN                    drawn;
     BOOL                    use_alpha;
-    RGNDATA                *region_data;
+    RGNDATA                *blit_data;
     BYTE                   *bits;
     pthread_mutex_t         mutex;
     BITMAPINFO              info;   /* variable size, must be last */
@@ -73,6 +75,27 @@ static struct macdrv_window_surface *get_mac_surface(struct window_surface *surf
     return (struct macdrv_window_surface *)surface;
 }
 
+/***********************************************************************
+ *              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
  */
@@ -126,15 +149,17 @@ static void macdrv_surface_set_region(struct window_surface *window_surface, HRG
 
     window_surface->funcs->lock(window_surface);
 
-    HeapFree(GetProcessHeap(), 0, surface->region_data);
-    surface->region_data = NULL;
-
     if (region)
     {
-        int rc = OffsetRgn(region, surface->header.rect.left, surface->header.rect.top);
-        if (rc != ERROR)
-            surface->region_data = get_region_data(region, 0);
+        if (!surface->region) surface->region = CreateRectRgn(0, 0, 0, 0);
+        CombineRgn(surface->region, region, 0, RGN_COPY);
     }
+    else
+    {
+        if (surface->region) DeleteObject(surface->region);
+        surface->region = 0;
+    }
+    update_blit_data(surface);
 
     window_surface->funcs->unlock(window_surface);
 }
@@ -146,6 +171,7 @@ static void macdrv_surface_flush(struct window_surface *window_surface)
 {
     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
     CGRect rect;
+    HRGN region;
 
     window_surface->funcs->lock(window_surface);
 
@@ -154,6 +180,18 @@ static void macdrv_surface_flush(struct window_surface *window_surface)
 
     rect = cgrect_from_rect(surface->bounds);
     rect = CGRectOffset(rect, surface->header.rect.left, surface->header.rect.top);
+
+    if (!IsRectEmpty(&surface->bounds) && (region = CreateRectRgnIndirect(&surface->bounds)))
+    {
+        if (surface->drawn)
+        {
+            CombineRgn(surface->drawn, surface->drawn, region, RGN_OR);
+            DeleteObject(region);
+        }
+        else
+            surface->drawn = region;
+    }
+    update_blit_data(surface);
     reset_bounds(&surface->bounds);
 
     window_surface->funcs->unlock(window_surface);
@@ -189,9 +227,11 @@ static const struct window_surface_funcs macdrv_surface_funcs =
 /***********************************************************************
  *              create_surface
  */
-struct window_surface *create_surface(macdrv_window window, const RECT *rect, BOOL use_alpha)
+struct window_surface *create_surface(macdrv_window window, const RECT *rect,
+                                      struct window_surface *old_surface, BOOL use_alpha)
 {
     struct macdrv_window_surface *surface;
+    struct macdrv_window_surface *old_mac_surface = get_mac_surface(old_surface);
     int width = rect->right - rect->left, height = rect->bottom - rect->top;
     DWORD *colors;
     pthread_mutexattr_t attr;
@@ -234,6 +274,17 @@ struct window_surface *create_surface(macdrv_window window, const RECT *rect, BO
     surface->header.ref   = 1;
     surface->window = window;
     reset_bounds(&surface->bounds);
+    if (old_mac_surface && old_mac_surface->drawn)
+    {
+        surface->drawn = CreateRectRgnIndirect(rect);
+        OffsetRgn(surface->drawn, -rect->left, -rect->top);
+        if (CombineRgn(surface->drawn, surface->drawn, old_mac_surface->drawn, RGN_AND) <= NULLREGION)
+        {
+            DeleteObject(surface->drawn);
+            surface->drawn = 0;
+        }
+    }
+    update_blit_data(surface);
     surface->use_alpha = use_alpha;
     surface->bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, surface->info.bmiHeader.biSizeImage);
     if (!surface->bits) goto failed;
@@ -267,24 +318,25 @@ void set_window_surface(macdrv_window window, struct window_surface *window_surf
 }
 
 /***********************************************************************
- *              get_surface_region_rects
+ *              get_surface_blit_rects
  *
  * Caller must hold the surface lock.  Indirectly returns the surface
- * region rects.  Returns zero if the surface has no region set (it is
- * unclipped); returns non-zero if the surface does have a region set.
+ * 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_region_rects(void *window_surface, const CGRect **rects, int *count)
+int get_surface_blit_rects(void *window_surface, const CGRect **rects, int *count)
 {
     struct macdrv_window_surface *surface = get_mac_surface(window_surface);
 
-    if (surface->region_data)
+    if (surface->blit_data)
     {
-        *rects = (const CGRect*)surface->region_data->Buffer;
-        *count = surface->region_data->rdh.nCount;
+        *rects = (const CGRect*)surface->blit_data->Buffer;
+        *count = surface->blit_data->rdh.nCount;
     }
     else
     {
@@ -292,7 +344,7 @@ int get_surface_region_rects(void *window_surface, const CGRect **rects, int *co
         *count = 0;
     }
 
-    return (surface->region_data != NULL);
+    return (surface->blit_data != NULL);
 }
 
 /***********************************************************************
diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c
index 07c4643e3e3..9932d9976ef 100644
--- a/dlls/winemac.drv/window.c
+++ b/dlls/winemac.drv/window.c
@@ -1101,7 +1101,7 @@ BOOL CDECL macdrv_UpdateLayeredWindow(HWND hwnd, const UPDATELAYEREDWINDOWINFO *
     surface = data->surface;
     if (!surface || memcmp(&surface->rect, &rect, sizeof(RECT)))
     {
-        data->surface = create_surface(data->cocoa_window, &rect, TRUE);
+        data->surface = create_surface(data->cocoa_window, &rect, NULL, TRUE);
         set_window_surface(data->cocoa_window, data->surface);
         if (surface) window_surface_release(surface);
         surface = data->surface;
@@ -1291,7 +1291,7 @@ void CDECL macdrv_WindowPosChanging(HWND hwnd, HWND insert_after, UINT swp_flags
     }
     else if (!(swp_flags & SWP_SHOWWINDOW) && !(style & WS_VISIBLE)) goto done;
 
-    *surface = create_surface(data->cocoa_window, &surface_rect, FALSE);
+    *surface = create_surface(data->cocoa_window, &surface_rect, data->surface, FALSE);
 
 done:
     release_win_data(data);