From 7d6ebfa742eac87c9f5ac5e228c0c5f68e785f9f Mon Sep 17 00:00:00 2001 From: Ken Thomases Date: Mon, 14 Jan 2013 20:23:55 -0600 Subject: [PATCH] winemac: Implement window surface rendering. --- dlls/winemac.drv/Makefile.in | 1 + dlls/winemac.drv/cocoa_window.h | 3 + dlls/winemac.drv/cocoa_window.m | 82 ++++++++ dlls/winemac.drv/macdrv.h | 4 + dlls/winemac.drv/macdrv_cocoa.h | 6 + dlls/winemac.drv/surface.c | 340 ++++++++++++++++++++++++++++++++ dlls/winemac.drv/window.c | 107 +++++++++- 7 files changed, 542 insertions(+), 1 deletion(-) create mode 100644 dlls/winemac.drv/surface.c diff --git a/dlls/winemac.drv/Makefile.in b/dlls/winemac.drv/Makefile.in index d3d1d884702..c858d280df5 100644 --- a/dlls/winemac.drv/Makefile.in +++ b/dlls/winemac.drv/Makefile.in @@ -6,6 +6,7 @@ C_SRCS = \ display.c \ gdi.c \ macdrv_main.c \ + surface.c \ window.c OBJC_SRCS = \ diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h index 8533e2abfe6..720a89be6c9 100644 --- a/dlls/winemac.drv/cocoa_window.h +++ b/dlls/winemac.drv/cocoa_window.h @@ -28,6 +28,9 @@ @interface WineWindow : NSPanel BOOL noActivate; BOOL floating; WineWindow* latentParentWindow; + + void* surface; + pthread_mutex_t* surface_mutex; } @end diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 49dd31bd840..46f5576e146 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -65,6 +65,9 @@ @interface WineWindow () @property (nonatomic) BOOL floating; @property (retain, nonatomic) NSWindow* latentParentWindow; +@property (nonatomic) void* surface; +@property (nonatomic) pthread_mutex_t* surface_mutex; + + (void) flipRect:(NSRect*)rect; @end @@ -77,12 +80,56 @@ - (BOOL) isFlipped return YES; } + - (void) drawRect:(NSRect)rect + { + WineWindow* window = (WineWindow*)[self window]; + + if (window.surface && window.surface_mutex && + !pthread_mutex_lock(window.surface_mutex)) + { + const CGRect* rects; + int count; + + if (!get_surface_region_rects(window.surface, &rects, &count) || count) + { + CGRect imageRect; + CGImageRef image; + + imageRect = NSRectToCGRect(rect); + image = create_surface_image(window.surface, &imageRect, FALSE); + + if (image) + { + CGContextRef context; + + if (rects && count) + { + NSBezierPath* surfaceClip = [NSBezierPath bezierPath]; + int i; + for (i = 0; i < count; i++) + [surfaceClip appendBezierPathWithRect:NSRectFromCGRect(rects[i])]; + [surfaceClip addClip]; + } + + context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + CGContextSetBlendMode(context, kCGBlendModeCopy); + CGContextDrawImage(context, imageRect, image); + + CGImageRelease(image); + } + } + + pthread_mutex_unlock(window.surface_mutex); + } + } + @end @implementation WineWindow @synthesize disabled, noActivate, floating, latentParentWindow; + @synthesize surface, surface_mutex; + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf windowFrame:(NSRect)window_frame @@ -108,6 +155,7 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w [window disableCursorRects]; [window setShowsResizeIndicator:NO]; [window setHasShadow:wf->shadow]; + [window setColorSpace:[NSColorSpace genericRGBColorSpace]]; [window setDelegate:window]; contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease]; @@ -477,3 +525,37 @@ void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent) [window setMacDrvParentWindow:(WineWindow*)parent]; }); } + +/*********************************************************************** + * macdrv_set_window_surface + */ +void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + WineWindow* window = (WineWindow*)w; + + OnMainThread(^{ + window.surface = surface; + window.surface_mutex = mutex; + }); + + [pool release]; +} + +/*********************************************************************** + * macdrv_window_needs_display + * + * Mark a window as needing display in a specified rect (in non-client + * area coordinates). + */ +void macdrv_window_needs_display(macdrv_window w, CGRect rect) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + WineWindow* window = (WineWindow*)w; + + OnMainThreadAsync(^{ + [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)]; + }); + + [pool release]; +} diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index b10573b7730..a5d1b9adab7 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -83,7 +83,11 @@ static inline RECT rect_from_cgrect(CGRect cgrect) RECT whole_rect; /* Mac window rectangle for the whole window relative to parent */ RECT client_rect; /* client area relative to parent */ BOOL on_screen : 1; /* is window ordered in? */ + struct window_surface *surface; }; +extern RGNDATA *get_region_data(HRGN hrgn, HDC hdc_lptodp) DECLSPEC_HIDDEN; +extern struct window_surface *create_surface(macdrv_window window, const RECT *rect) DECLSPEC_HIDDEN; +extern void set_window_surface(macdrv_window window, struct window_surface *window_surface) DECLSPEC_HIDDEN; #endif /* __WINE_MACDRV_H */ diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index fc2b0ac35d7..5ae30a5e59f 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -85,6 +85,8 @@ #undef UnionRect #undef DPRINTF +#include + #ifndef DECLSPEC_HIDDEN # if defined(__MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__) @@ -147,5 +149,9 @@ extern int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev, extern void macdrv_hide_cocoa_window(macdrv_window w) DECLSPEC_HIDDEN; extern int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_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 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 void macdrv_window_needs_display(macdrv_window w, CGRect rect) DECLSPEC_HIDDEN; #endif /* __WINE_MACDRV_COCOA_H */ diff --git a/dlls/winemac.drv/surface.c b/dlls/winemac.drv/surface.c new file mode 100644 index 00000000000..391657d54bf --- /dev/null +++ b/dlls/winemac.drv/surface.c @@ -0,0 +1,340 @@ +/* + * Mac driver window surface implementation + * + * Copyright 1993, 1994, 2011 Alexandre Julliard + * Copyright 2006 Damjan Jovanovic + * Copyright 2012, 2013 Ken Thomases for CodeWeavers, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" + +#include "macdrv.h" +#include "winuser.h" + +WINE_DEFAULT_DEBUG_CHANNEL(macdrv); + + +/* only for use on sanitized BITMAPINFO structures */ +static inline int get_dib_info_size(const BITMAPINFO *info, UINT coloruse) +{ + if (info->bmiHeader.biCompression == BI_BITFIELDS) + return sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD); + if (coloruse == DIB_PAL_COLORS) + return sizeof(BITMAPINFOHEADER) + info->bmiHeader.biClrUsed * sizeof(WORD); + return FIELD_OFFSET(BITMAPINFO, bmiColors[info->bmiHeader.biClrUsed]); +} + +static inline int get_dib_stride(int width, int bpp) +{ + return ((width * bpp + 31) >> 3) & ~3; +} + +static inline int get_dib_image_size(const BITMAPINFO *info) +{ + return get_dib_stride(info->bmiHeader.biWidth, info->bmiHeader.biBitCount) + * abs(info->bmiHeader.biHeight); +} + +static inline void reset_bounds(RECT *bounds) +{ + bounds->left = bounds->top = INT_MAX; + bounds->right = bounds->bottom = INT_MIN; +} + + +struct macdrv_window_surface +{ + struct window_surface header; + macdrv_window window; + RECT bounds; + RGNDATA *region_data; + BYTE *bits; + pthread_mutex_t mutex; + BITMAPINFO info; /* variable size, must be last */ +}; + +static struct macdrv_window_surface *get_mac_surface(struct window_surface *surface) +{ + return (struct macdrv_window_surface *)surface; +} + +/*********************************************************************** + * macdrv_surface_lock + */ +static void macdrv_surface_lock(struct window_surface *window_surface) +{ + struct macdrv_window_surface *surface = get_mac_surface(window_surface); + + pthread_mutex_lock(&surface->mutex); +} + +/*********************************************************************** + * macdrv_surface_unlock + */ +static void macdrv_surface_unlock(struct window_surface *window_surface) +{ + struct macdrv_window_surface *surface = get_mac_surface(window_surface); + + pthread_mutex_unlock(&surface->mutex); +} + +/*********************************************************************** + * macdrv_surface_get_bitmap_info + */ +static void *macdrv_surface_get_bitmap_info(struct window_surface *window_surface, + BITMAPINFO *info) +{ + struct macdrv_window_surface *surface = get_mac_surface(window_surface); + + memcpy(info, &surface->info, get_dib_info_size(&surface->info, DIB_RGB_COLORS)); + return surface->bits; +} + +/*********************************************************************** + * macdrv_surface_get_bounds + */ +static RECT *macdrv_surface_get_bounds(struct window_surface *window_surface) +{ + struct macdrv_window_surface *surface = get_mac_surface(window_surface); + + return &surface->bounds; +} + +/*********************************************************************** + * macdrv_surface_set_region + */ +static void macdrv_surface_set_region(struct window_surface *window_surface, HRGN region) +{ + struct macdrv_window_surface *surface = get_mac_surface(window_surface); + + TRACE("updating surface %p with %p\n", surface, region); + + 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); + } +} + +/*********************************************************************** + * macdrv_surface_flush + */ +static void macdrv_surface_flush(struct window_surface *window_surface) +{ + struct macdrv_window_surface *surface = get_mac_surface(window_surface); + CGRect rect; + + window_surface->funcs->lock(window_surface); + + TRACE("flushing %p %s bounds %s bits %p\n", surface, wine_dbgstr_rect(&surface->header.rect), + wine_dbgstr_rect(&surface->bounds), surface->bits); + + rect = cgrect_from_rect(surface->bounds); + rect = CGRectOffset(rect, surface->header.rect.left, surface->header.rect.top); + reset_bounds(&surface->bounds); + + window_surface->funcs->unlock(window_surface); + + if (!CGRectIsEmpty(rect)) + macdrv_window_needs_display(surface->window, rect); +} + +/*********************************************************************** + * macdrv_surface_destroy + */ +static void macdrv_surface_destroy(struct window_surface *window_surface) +{ + struct macdrv_window_surface *surface = get_mac_surface(window_surface); + + TRACE("freeing %p bits %p\n", surface, surface->bits); + HeapFree(GetProcessHeap(), 0, surface->bits); + pthread_mutex_destroy(&surface->mutex); + HeapFree(GetProcessHeap(), 0, surface); +} + +static const struct window_surface_funcs macdrv_surface_funcs = +{ + macdrv_surface_lock, + macdrv_surface_unlock, + macdrv_surface_get_bitmap_info, + macdrv_surface_get_bounds, + macdrv_surface_set_region, + macdrv_surface_flush, + macdrv_surface_destroy, +}; + +/*********************************************************************** + * create_surface + */ +struct window_surface *create_surface(macdrv_window window, const RECT *rect) +{ + struct macdrv_window_surface *surface; + int width = rect->right - rect->left, height = rect->bottom - rect->top; + DWORD *colors; + pthread_mutexattr_t attr; + int err; + + surface = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + FIELD_OFFSET(struct macdrv_window_surface, info.bmiColors[3])); + if (!surface) return NULL; + + err = pthread_mutexattr_init(&attr); + if (!err) + { + err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (!err) + err = pthread_mutex_init(&surface->mutex, &attr); + pthread_mutexattr_destroy(&attr); + } + if (err) + { + HeapFree(GetProcessHeap(), 0, surface); + return NULL; + } + + surface->info.bmiHeader.biSize = sizeof(surface->info.bmiHeader); + surface->info.bmiHeader.biWidth = width; + 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); + surface->info.bmiHeader.biCompression = BI_RGB; + surface->info.bmiHeader.biClrUsed = 0; + + colors = (DWORD *)((char *)&surface->info + surface->info.bmiHeader.biSize); + colors[0] = 0x00ff0000; + colors[1] = 0x0000ff00; + colors[2] = 0x000000ff; + + surface->header.funcs = &macdrv_surface_funcs; + surface->header.rect = *rect; + surface->header.ref = 1; + surface->window = window; + reset_bounds(&surface->bounds); + surface->bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, surface->info.bmiHeader.biSizeImage); + if (!surface->bits) goto failed; + + TRACE("created %p for %p %s bits %p-%p\n", surface, window, wine_dbgstr_rect(rect), + surface->bits, surface->bits + surface->info.bmiHeader.biSizeImage); + + return &surface->header; + +failed: + macdrv_surface_destroy(&surface->header); + return NULL; +} + +/*********************************************************************** + * 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_region_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. + * + * 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) +{ + struct macdrv_window_surface *surface = get_mac_surface(window_surface); + + if (surface->region_data) + { + *rects = (const CGRect*)surface->region_data->Buffer; + *count = surface->region_data->rdh.nCount; + } + else + { + *rects = NULL; + *count = 0; + } + + return (surface->region_data != NULL); +} + +/*********************************************************************** + * create_surface_image + * + * Caller must hold the surface lock. On input, *rect is the requested + * image rect, relative to the window whole_rect, a.k.a. visible_rect. + * On output, it's been intersected with that part backed by the surface + * and is the actual size of the returned image. copy_data indicates if + * the caller will keep the returned image beyond the point where the + * surface bits can be guaranteed to remain valid and unchanged. If so, + * the bits are copied instead of merely referenced by the image. + * + * IMPORTANT: This function is called from non-Wine threads, so it + * must not use Win32 or Wine functions, including debug + * logging. + */ +CGImageRef create_surface_image(void *window_surface, CGRect *rect, int copy_data) +{ + CGImageRef cgimage = NULL; + struct macdrv_window_surface *surface = get_mac_surface(window_surface); + int width, height; + + width = surface->header.rect.right - surface->header.rect.left; + height = surface->header.rect.bottom - surface->header.rect.top; + *rect = CGRectIntersection(cgrect_from_rect(surface->header.rect), *rect); + if (!CGRectIsEmpty(*rect)) + { + CGRect visrect; + CGColorSpaceRef colorspace; + CGDataProviderRef provider; + int bytes_per_row, offset, size; + + visrect = CGRectOffset(*rect, -surface->header.rect.left, -surface->header.rect.top); + + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + bytes_per_row = get_dib_stride(width, 32); + offset = CGRectGetMinX(visrect) * 4 + (height - CGRectGetMaxY(visrect)) * bytes_per_row; + size = min(CGRectGetHeight(visrect) * bytes_per_row, + surface->info.bmiHeader.biSizeImage - offset); + + if (copy_data) + { + CFDataRef data = CFDataCreate(NULL, (UInt8*)surface->bits + offset, size); + provider = CGDataProviderCreateWithCFData(data); + CFRelease(data); + } + else + provider = CGDataProviderCreateWithData(NULL, surface->bits + offset, size, NULL); + + cgimage = CGImageCreate(CGRectGetWidth(visrect), CGRectGetHeight(visrect), + 8, 32, bytes_per_row, colorspace, + kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, + provider, NULL, FALSE, kCGRenderingIntentDefault); + CGDataProviderRelease(provider); + CGColorSpaceRelease(colorspace); + } + + return cgimage; +} diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c index 4344da5a031..1485a810d05 100644 --- a/dlls/winemac.drv/window.c +++ b/dlls/winemac.drv/window.c @@ -376,6 +376,8 @@ static void destroy_cocoa_window(struct macdrv_win_data *data) macdrv_destroy_cocoa_window(data->cocoa_window); data->cocoa_window = 0; data->on_screen = FALSE; + if (data->surface) window_surface_release(data->surface); + data->surface = NULL; } @@ -415,6 +417,73 @@ static struct macdrv_win_data *macdrv_create_win_data(HWND hwnd, const RECT *win } +/*********************************************************************** + * get_region_data + * + * Calls GetRegionData on the given region and converts the rectangle + * array to CGRect format. The returned buffer must be freed by + * caller using HeapFree(GetProcessHeap(),...). + * If hdc_lptodp is not 0, the rectangles are converted through LPtoDP. + */ +RGNDATA *get_region_data(HRGN hrgn, HDC hdc_lptodp) +{ + RGNDATA *data; + DWORD size; + int i; + RECT *rect; + CGRect *cgrect; + + if (!hrgn || !(size = GetRegionData(hrgn, 0, NULL))) return NULL; + if (sizeof(CGRect) > sizeof(RECT)) + { + /* add extra size for CGRect array */ + int count = (size - sizeof(RGNDATAHEADER)) / sizeof(RECT); + size += count * (sizeof(CGRect) - sizeof(RECT)); + } + if (!(data = HeapAlloc(GetProcessHeap(), 0, size))) return NULL; + if (!GetRegionData(hrgn, size, data)) + { + HeapFree(GetProcessHeap(), 0, data); + return NULL; + } + + rect = (RECT *)data->Buffer; + cgrect = (CGRect *)data->Buffer; + if (hdc_lptodp) /* map to device coordinates */ + { + LPtoDP(hdc_lptodp, (POINT *)rect, data->rdh.nCount * 2); + for (i = 0; i < data->rdh.nCount; i++) + { + if (rect[i].right < rect[i].left) + { + INT tmp = rect[i].right; + rect[i].right = rect[i].left; + rect[i].left = tmp; + } + if (rect[i].bottom < rect[i].top) + { + INT tmp = rect[i].bottom; + rect[i].bottom = rect[i].top; + rect[i].top = tmp; + } + } + } + + if (sizeof(CGRect) > sizeof(RECT)) + { + /* need to start from the end */ + for (i = data->rdh.nCount-1; i >= 0; i--) + cgrect[i] = cgrect_from_rect(rect[i]); + } + else + { + for (i = 0; i < data->rdh.nCount; i++) + cgrect[i] = cgrect_from_rect(rect[i]); + } + return data; +} + + /*********************************************************************** * sync_window_position * @@ -637,6 +706,21 @@ void CDECL macdrv_SetWindowText(HWND hwnd, LPCWSTR text) } +static inline RECT get_surface_rect(const RECT *visible_rect) +{ + RECT rect; + RECT desktop_rect = rect_from_cgrect(macdrv_get_desktop_rect()); + + IntersectRect(&rect, visible_rect, &desktop_rect); + OffsetRect(&rect, -visible_rect->left, -visible_rect->top); + rect.left &= ~127; + rect.top &= ~127; + rect.right = max(rect.left + 128, (rect.right + 127) & ~127); + rect.bottom = max(rect.top + 128, (rect.bottom + 127) & ~127); + return rect; +} + + /*********************************************************************** * WindowPosChanging (MACDRV.@) */ @@ -646,6 +730,7 @@ void CDECL macdrv_WindowPosChanging(HWND hwnd, HWND insert_after, UINT swp_flags { struct macdrv_win_data *data = get_win_data(hwnd); DWORD style = GetWindowLongW(hwnd, GWL_STYLE); + RECT surface_rect; TRACE("%p after %p swp %04x window %s client %s visible %s surface %p\n", hwnd, insert_after, swp_flags, wine_dbgstr_rect(window_rect), wine_dbgstr_rect(client_rect), @@ -658,13 +743,28 @@ void CDECL macdrv_WindowPosChanging(HWND hwnd, HWND insert_after, UINT swp_flags TRACE("visible_rect %s -> %s\n", wine_dbgstr_rect(window_rect), wine_dbgstr_rect(visible_rect)); - /* release the window surface if necessary */ + /* create the window surface if necessary */ if (!data->cocoa_window) goto done; if (swp_flags & SWP_HIDEWINDOW) goto done; if (*surface) window_surface_release(*surface); *surface = NULL; + surface_rect = get_surface_rect(visible_rect); + if (data->surface) + { + if (!memcmp(&data->surface->rect, &surface_rect, sizeof(surface_rect))) + { + /* existing surface is good enough */ + window_surface_add_ref(data->surface); + *surface = data->surface; + goto done; + } + } + else if (!(swp_flags & SWP_SHOWWINDOW) && !(style & WS_VISIBLE)) goto done; + + *surface = create_surface(data->cocoa_window, &surface_rect); + done: release_win_data(data); } @@ -690,6 +790,11 @@ void CDECL macdrv_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags, data->window_rect = *window_rect; data->whole_rect = *visible_rect; data->client_rect = *client_rect; + if (surface) + window_surface_add_ref(surface); + set_window_surface(data->cocoa_window, surface); + if (data->surface) window_surface_release(data->surface); + data->surface = surface; TRACE("win %p/%p window %s whole %s client %s style %08x flags %08x surface %p\n", hwnd, data->cocoa_window, wine_dbgstr_rect(window_rect),