winemac: Associate an event queue with each thread that creates windows and with...
[wine] / dlls / winemac.drv / window.c
1 /*
2  * MACDRV windowing driver
3  *
4  * Copyright 1993, 1994, 1995, 1996, 2001 Alexandre Julliard
5  * Copyright 1993 David Metcalfe
6  * Copyright 1995, 1996 Alex Korobka
7  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25
26 #include "macdrv.h"
27 #include "winuser.h"
28 #include "wine/unicode.h"
29 #include "wine/server.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(macdrv);
32
33
34 static CRITICAL_SECTION win_data_section;
35 static CRITICAL_SECTION_DEBUG critsect_debug =
36 {
37     0, 0, &win_data_section,
38     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
39       0, 0, { (DWORD_PTR)(__FILE__ ": win_data_section") }
40 };
41 static CRITICAL_SECTION win_data_section = { &critsect_debug, -1, 0, 0, 0, 0 };
42
43 static CFMutableDictionaryRef win_datas;
44
45
46 /***********************************************************************
47  *              get_cocoa_window_features
48  */
49 static void get_cocoa_window_features(struct macdrv_win_data *data,
50                                       DWORD style, DWORD ex_style,
51                                       struct macdrv_window_features* wf)
52 {
53     memset(wf, 0, sizeof(*wf));
54
55     if (IsRectEmpty(&data->window_rect)) return;
56
57     if ((style & WS_CAPTION) == WS_CAPTION && !(ex_style & WS_EX_LAYERED))
58     {
59         wf->shadow = 1;
60         if (!data->shaped)
61         {
62             wf->title_bar = 1;
63             if (style & WS_SYSMENU) wf->close_button = 1;
64             if (style & WS_MINIMIZEBOX) wf->minimize_button = 1;
65             if (style & WS_MAXIMIZEBOX) wf->resizable = 1;
66             if (ex_style & WS_EX_TOOLWINDOW) wf->utility = 1;
67         }
68     }
69     if (ex_style & WS_EX_DLGMODALFRAME) wf->shadow = 1;
70     else if (style & WS_THICKFRAME)
71     {
72         wf->shadow = 1;
73         if (!data->shaped) wf->resizable = 1;
74     }
75     else if ((style & (WS_DLGFRAME|WS_BORDER)) == WS_DLGFRAME) wf->shadow = 1;
76 }
77
78
79 /*******************************************************************
80  *              can_activate_window
81  *
82  * Check if we can activate the specified window.
83  */
84 static inline BOOL can_activate_window(HWND hwnd)
85 {
86     LONG style = GetWindowLongW(hwnd, GWL_STYLE);
87     RECT rect;
88
89     if (!(style & WS_VISIBLE)) return FALSE;
90     if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
91     if (style & WS_MINIMIZE) return FALSE;
92     if (GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_NOACTIVATE) return FALSE;
93     if (hwnd == GetDesktopWindow()) return FALSE;
94     if (GetWindowRect(hwnd, &rect) && IsRectEmpty(&rect)) return FALSE;
95     return !(style & WS_DISABLED);
96 }
97
98
99 /***********************************************************************
100  *              get_cocoa_window_state
101  */
102 static void get_cocoa_window_state(struct macdrv_win_data *data,
103                                    DWORD style, DWORD ex_style,
104                                    struct macdrv_window_state* state)
105 {
106     memset(state, 0, sizeof(*state));
107     state->disabled = (style & WS_DISABLED) != 0;
108     state->no_activate = !can_activate_window(data->hwnd);
109     state->floating = (ex_style & WS_EX_TOPMOST) != 0;
110     state->excluded_by_expose = state->excluded_by_cycle =
111         !(ex_style & WS_EX_APPWINDOW) &&
112         (GetWindow(data->hwnd, GW_OWNER) || (ex_style & (WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE)));
113 }
114
115
116 /***********************************************************************
117  *              get_mac_rect_offset
118  *
119  * Helper for macdrv_window_to_mac_rect and macdrv_mac_to_window_rect.
120  */
121 static void get_mac_rect_offset(struct macdrv_win_data *data, DWORD style, RECT *rect)
122 {
123     DWORD ex_style, style_mask = 0, ex_style_mask = 0;
124
125     rect->top = rect->bottom = rect->left = rect->right = 0;
126
127     ex_style = GetWindowLongW(data->hwnd, GWL_EXSTYLE);
128
129     if (!data->shaped)
130     {
131         struct macdrv_window_features wf;
132         get_cocoa_window_features(data, style, ex_style, &wf);
133
134         if (wf.title_bar) style_mask |= WS_CAPTION;
135         if (wf.shadow)
136         {
137             style_mask |= WS_DLGFRAME | WS_THICKFRAME;
138             ex_style_mask |= WS_EX_DLGMODALFRAME;
139         }
140     }
141
142     AdjustWindowRectEx(rect, style & style_mask, FALSE, ex_style & ex_style_mask);
143
144     TRACE("%p/%p style %08x ex_style %08x shaped %d -> %s\n", data->hwnd, data->cocoa_window,
145           style, ex_style, data->shaped, wine_dbgstr_rect(rect));
146 }
147
148
149 /***********************************************************************
150  *              show_window
151  */
152 static void show_window(struct macdrv_win_data *data)
153 {
154     TRACE("win %p/%p\n", data->hwnd, data->cocoa_window);
155
156     data->on_screen = macdrv_order_cocoa_window(data->cocoa_window, NULL, NULL);
157 }
158
159
160 /***********************************************************************
161  *              hide_window
162  */
163 static void hide_window(struct macdrv_win_data *data)
164 {
165     TRACE("win %p/%p\n", data->hwnd, data->cocoa_window);
166
167     macdrv_hide_cocoa_window(data->cocoa_window);
168     data->on_screen = FALSE;
169 }
170
171
172 /***********************************************************************
173  *              macdrv_window_to_mac_rect
174  *
175  * Convert a rect from client to Mac window coordinates
176  */
177 static void macdrv_window_to_mac_rect(struct macdrv_win_data *data, DWORD style, RECT *rect)
178 {
179     RECT rc;
180
181     if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return;
182     if (IsRectEmpty(rect)) return;
183
184     get_mac_rect_offset(data, style, &rc);
185
186     rect->left   -= rc.left;
187     rect->right  -= rc.right;
188     rect->top    -= rc.top;
189     rect->bottom -= rc.bottom;
190     if (rect->top >= rect->bottom) rect->bottom = rect->top + 1;
191     if (rect->left >= rect->right) rect->right = rect->left + 1;
192 }
193
194
195 /***********************************************************************
196  *              macdrv_mac_to_window_rect
197  *
198  * Opposite of macdrv_window_to_mac_rect
199  */
200 static void macdrv_mac_to_window_rect(struct macdrv_win_data *data, RECT *rect)
201 {
202     RECT rc;
203     DWORD style = GetWindowLongW(data->hwnd, GWL_STYLE);
204
205     if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return;
206     if (IsRectEmpty(rect)) return;
207
208     get_mac_rect_offset(data, style, &rc);
209
210     rect->left   += rc.left;
211     rect->right  += rc.right;
212     rect->top    += rc.top;
213     rect->bottom += rc.bottom;
214     if (rect->top >= rect->bottom) rect->bottom = rect->top + 1;
215     if (rect->left >= rect->right) rect->right = rect->left + 1;
216 }
217
218
219 /***********************************************************************
220  *              constrain_window_frame
221  *
222  * Alter a window frame rectangle to fit within a) Cocoa's documented
223  * limits, and b) sane sizes, like twice the desktop rect.
224  */
225 static void constrain_window_frame(CGRect* frame)
226 {
227     CGRect desktop_rect = macdrv_get_desktop_rect();
228     int max_width, max_height;
229
230     max_width = min(32000, 2 * CGRectGetWidth(desktop_rect));
231     max_height = min(32000, 2 * CGRectGetHeight(desktop_rect));
232
233     if (frame->origin.x < -16000) frame->origin.x = -16000;
234     if (frame->origin.y < -16000) frame->origin.y = -16000;
235     if (frame->origin.x > 16000) frame->origin.x = 16000;
236     if (frame->origin.y > 16000) frame->origin.y = 16000;
237     if (frame->size.width > max_width) frame->size.width = max_width;
238     if (frame->size.height > max_height) frame->size.height = max_height;
239 }
240
241
242 /***********************************************************************
243  *              alloc_win_data
244  */
245 static struct macdrv_win_data *alloc_win_data(HWND hwnd)
246 {
247     struct macdrv_win_data *data;
248
249     if ((data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data))))
250     {
251         data->hwnd = hwnd;
252         data->color_key = CLR_INVALID;
253         EnterCriticalSection(&win_data_section);
254         if (!win_datas)
255             win_datas = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
256         CFDictionarySetValue(win_datas, hwnd, data);
257     }
258     return data;
259 }
260
261
262 /***********************************************************************
263  *              get_win_data
264  *
265  * Lock and return the data structure associated with a window.
266  */
267 static struct macdrv_win_data *get_win_data(HWND hwnd)
268 {
269     struct macdrv_win_data *data;
270
271     if (!hwnd) return NULL;
272     EnterCriticalSection(&win_data_section);
273     if (win_datas && (data = (struct macdrv_win_data*)CFDictionaryGetValue(win_datas, hwnd)))
274         return data;
275     LeaveCriticalSection(&win_data_section);
276     return NULL;
277 }
278
279
280 /***********************************************************************
281  *              release_win_data
282  *
283  * Release the data returned by get_win_data.
284  */
285 static void release_win_data(struct macdrv_win_data *data)
286 {
287     if (data) LeaveCriticalSection(&win_data_section);
288 }
289
290
291 /***********************************************************************
292  *              macdrv_get_cocoa_window
293  *
294  * Return the Mac window associated with the full area of a window
295  */
296 static macdrv_window macdrv_get_cocoa_window(HWND hwnd)
297 {
298     struct macdrv_win_data *data = get_win_data(hwnd);
299     macdrv_window ret = data ? data->cocoa_window : NULL;
300     release_win_data(data);
301     return ret;
302 }
303
304
305 /***********************************************************************
306  *              set_cocoa_window_properties
307  *
308  * Set the window properties for a Cocoa window based on its Windows
309  * properties.
310  */
311 static void set_cocoa_window_properties(struct macdrv_win_data *data)
312 {
313     DWORD style, ex_style;
314     HWND owner;
315     macdrv_window owner_win;
316     struct macdrv_window_features wf;
317     struct macdrv_window_state state;
318
319     style = GetWindowLongW(data->hwnd, GWL_STYLE);
320     ex_style = GetWindowLongW(data->hwnd, GWL_EXSTYLE);
321
322     owner = GetWindow(data->hwnd, GW_OWNER);
323     owner_win = macdrv_get_cocoa_window(owner);
324     macdrv_set_cocoa_parent_window(data->cocoa_window, owner_win);
325
326     get_cocoa_window_features(data, style, ex_style, &wf);
327     macdrv_set_cocoa_window_features(data->cocoa_window, &wf);
328
329     get_cocoa_window_state(data, style, ex_style, &state);
330     macdrv_set_cocoa_window_state(data->cocoa_window, &state);
331 }
332
333
334 /***********************************************************************
335  *              sync_window_region
336  *
337  * Update the window region.
338  */
339 static void sync_window_region(struct macdrv_win_data *data, HRGN win_region)
340 {
341     HRGN hrgn = win_region;
342     RGNDATA *region_data;
343     const CGRect* rects;
344     int count;
345
346     if (!data->cocoa_window) return;
347     data->shaped = FALSE;
348
349     if (hrgn == (HRGN)1)  /* hack: win_region == 1 means retrieve region from server */
350     {
351         if (!(hrgn = CreateRectRgn(0, 0, 0, 0))) return;
352         if (GetWindowRgn(data->hwnd, hrgn) == ERROR)
353         {
354             DeleteObject(hrgn);
355             hrgn = 0;
356         }
357     }
358
359     if (hrgn && GetWindowLongW(data->hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
360         MirrorRgn(data->hwnd, hrgn);
361     if (hrgn)
362     {
363         OffsetRgn(hrgn, data->window_rect.left - data->whole_rect.left,
364                   data->window_rect.top - data->whole_rect.top);
365     }
366     region_data = get_region_data(hrgn, 0);
367     if (region_data)
368     {
369         rects = (CGRect*)region_data->Buffer;
370         count = region_data->rdh.nCount;
371         /* Special case optimization.  If the region entirely encloses the Cocoa
372            window, it's the same as there being no region.  It's potentially
373            hard/slow to test this for arbitrary regions, so we just check for
374            very simple regions. */
375         if (count == 1 && CGRectContainsRect(rects[0], cgrect_from_rect(data->whole_rect)))
376         {
377             TRACE("optimizing for simple region that contains Cocoa content rect\n");
378             rects = NULL;
379             count = 0;
380         }
381     }
382     else
383     {
384         rects = NULL;
385         count = 0;
386     }
387
388     TRACE("win %p/%p win_region %p rects %p count %d\n", data->hwnd, data->cocoa_window, win_region, rects, count);
389     macdrv_set_window_shape(data->cocoa_window, rects, count);
390
391     HeapFree(GetProcessHeap(), 0, region_data);
392     data->shaped = (region_data != NULL);
393
394     if (hrgn && hrgn != win_region) DeleteObject(hrgn);
395 }
396
397
398 /***********************************************************************
399  *              add_bounds_rect
400  */
401 static inline void add_bounds_rect(RECT *bounds, const RECT *rect)
402 {
403     if (rect->left >= rect->right || rect->top >= rect->bottom) return;
404     bounds->left   = min(bounds->left, rect->left);
405     bounds->top    = min(bounds->top, rect->top);
406     bounds->right  = max(bounds->right, rect->right);
407     bounds->bottom = max(bounds->bottom, rect->bottom);
408 }
409
410
411 /***********************************************************************
412  *              sync_window_opacity
413  */
414 static void sync_window_opacity(struct macdrv_win_data *data, COLORREF key, BYTE alpha,
415                                 BOOL per_pixel_alpha, DWORD flags)
416 {
417     CGFloat opacity = 1.0;
418     BOOL needs_flush = FALSE;
419
420     if (flags & LWA_ALPHA) opacity = alpha / 255.0;
421
422     TRACE("setting window %p/%p alpha to %g\n", data->hwnd, data->cocoa_window, opacity);
423     macdrv_set_window_alpha(data->cocoa_window, opacity);
424
425     if (flags & LWA_COLORKEY)
426     {
427         /* FIXME: treat PALETTEINDEX and DIBINDEX as black */
428         if ((key & (1 << 24)) || key >> 16 == 0x10ff)
429             key = RGB(0, 0, 0);
430     }
431     else
432         key = CLR_INVALID;
433
434     if (data->color_key != key)
435     {
436         if (key == CLR_INVALID)
437         {
438             TRACE("clearing color-key for window %p/%p\n", data->hwnd, data->cocoa_window);
439             macdrv_clear_window_color_key(data->cocoa_window);
440         }
441         else
442         {
443             TRACE("setting color-key for window %p/%p to RGB %d,%d,%d\n", data->hwnd, data->cocoa_window,
444                   GetRValue(key), GetGValue(key), GetBValue(key));
445             macdrv_set_window_color_key(data->cocoa_window, GetRValue(key), GetGValue(key), GetBValue(key));
446         }
447
448         data->color_key = key;
449         needs_flush = TRUE;
450     }
451
452     if (!data->per_pixel_alpha != !per_pixel_alpha)
453     {
454         macdrv_window_use_per_pixel_alpha(data->cocoa_window, per_pixel_alpha);
455         data->per_pixel_alpha = per_pixel_alpha;
456         needs_flush = TRUE;
457     }
458
459     if (needs_flush && data->surface)
460     {
461         RECT *bounds;
462         RECT rect;
463
464         rect = data->whole_rect;
465         OffsetRect(&rect, -data->whole_rect.left, -data->whole_rect.top);
466         data->surface->funcs->lock(data->surface);
467         bounds = data->surface->funcs->get_bounds(data->surface);
468         add_bounds_rect(bounds, &rect);
469         data->surface->funcs->unlock(data->surface);
470     }
471 }
472
473
474 /**********************************************************************
475  *              create_cocoa_window
476  *
477  * Create the whole Mac window for a given window
478  */
479 static void create_cocoa_window(struct macdrv_win_data *data)
480 {
481     struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
482     WCHAR text[1024];
483     struct macdrv_window_features wf;
484     CGRect frame;
485     DWORD style, ex_style;
486     HRGN win_rgn;
487     COLORREF key;
488     BYTE alpha;
489     DWORD layered_flags;
490
491     if ((win_rgn = CreateRectRgn(0, 0, 0, 0)) &&
492         GetWindowRgn(data->hwnd, win_rgn) == ERROR)
493     {
494         DeleteObject(win_rgn);
495         win_rgn = 0;
496     }
497     data->shaped = (win_rgn != 0);
498
499     style = GetWindowLongW(data->hwnd, GWL_STYLE);
500     ex_style = GetWindowLongW(data->hwnd, GWL_EXSTYLE);
501
502     data->whole_rect = data->window_rect;
503     macdrv_window_to_mac_rect(data, style, &data->whole_rect);
504
505     memset(&wf, 0, sizeof(wf));
506     get_cocoa_window_features(data, style, ex_style, &wf);
507
508     frame = cgrect_from_rect(data->whole_rect);
509     constrain_window_frame(&frame);
510
511     TRACE("creating %p window %s whole %s client %s\n", data->hwnd, wine_dbgstr_rect(&data->window_rect),
512           wine_dbgstr_rect(&data->whole_rect), wine_dbgstr_rect(&data->client_rect));
513
514     data->cocoa_window = macdrv_create_cocoa_window(&wf, frame, thread_data->queue);
515     if (!data->cocoa_window) goto done;
516
517     set_cocoa_window_properties(data);
518
519     /* set the window text */
520     if (!InternalGetWindowText(data->hwnd, text, sizeof(text)/sizeof(WCHAR))) text[0] = 0;
521     macdrv_set_cocoa_window_title(data->cocoa_window, text, strlenW(text));
522
523     /* set the window region */
524     if (win_rgn) sync_window_region(data, win_rgn);
525
526     /* set the window opacity */
527     if (!GetLayeredWindowAttributes(data->hwnd, &key, &alpha, &layered_flags)) layered_flags = 0;
528     sync_window_opacity(data, key, alpha, FALSE, layered_flags);
529
530 done:
531     if (win_rgn) DeleteObject(win_rgn);
532 }
533
534
535 /**********************************************************************
536  *              destroy_cocoa_window
537  *
538  * Destroy the whole Mac window for a given window.
539  */
540 static void destroy_cocoa_window(struct macdrv_win_data *data)
541 {
542     if (!data->cocoa_window) return;
543
544     TRACE("win %p Cocoa win %p\n", data->hwnd, data->cocoa_window);
545
546     macdrv_destroy_cocoa_window(data->cocoa_window);
547     data->cocoa_window = 0;
548     data->on_screen = FALSE;
549     data->color_key = CLR_INVALID;
550     if (data->surface) window_surface_release(data->surface);
551     data->surface = NULL;
552 }
553
554
555 /***********************************************************************
556  *              macdrv_create_win_data
557  *
558  * Create a Mac data window structure for an existing window.
559  */
560 static struct macdrv_win_data *macdrv_create_win_data(HWND hwnd, const RECT *window_rect,
561                                                       const RECT *client_rect)
562 {
563     struct macdrv_win_data *data;
564     HWND parent;
565
566     if (GetWindowThreadProcessId(hwnd, NULL) != GetCurrentThreadId()) return NULL;
567
568     if (!(parent = GetAncestor(hwnd, GA_PARENT)))  /* desktop */
569     {
570         macdrv_init_thread_data();
571         return NULL;
572     }
573
574     /* don't create win data for HWND_MESSAGE windows */
575     if (parent != GetDesktopWindow() && !GetAncestor(parent, GA_PARENT)) return NULL;
576
577     if (!(data = alloc_win_data(hwnd))) return NULL;
578
579     data->whole_rect = data->window_rect = *window_rect;
580     data->client_rect = *client_rect;
581
582     if (parent == GetDesktopWindow())
583     {
584         create_cocoa_window(data);
585         TRACE("win %p/%p window %s whole %s client %s\n",
586                hwnd, data->cocoa_window, wine_dbgstr_rect(&data->window_rect),
587                wine_dbgstr_rect(&data->whole_rect), wine_dbgstr_rect(&data->client_rect));
588     }
589
590     return data;
591 }
592
593
594 /***********************************************************************
595  *              get_region_data
596  *
597  * Calls GetRegionData on the given region and converts the rectangle
598  * array to CGRect format. The returned buffer must be freed by
599  * caller using HeapFree(GetProcessHeap(),...).
600  * If hdc_lptodp is not 0, the rectangles are converted through LPtoDP.
601  */
602 RGNDATA *get_region_data(HRGN hrgn, HDC hdc_lptodp)
603 {
604     RGNDATA *data;
605     DWORD size;
606     int i;
607     RECT *rect;
608     CGRect *cgrect;
609
610     if (!hrgn || !(size = GetRegionData(hrgn, 0, NULL))) return NULL;
611     if (sizeof(CGRect) > sizeof(RECT))
612     {
613         /* add extra size for CGRect array */
614         int count = (size - sizeof(RGNDATAHEADER)) / sizeof(RECT);
615         size += count * (sizeof(CGRect) - sizeof(RECT));
616     }
617     if (!(data = HeapAlloc(GetProcessHeap(), 0, size))) return NULL;
618     if (!GetRegionData(hrgn, size, data))
619     {
620         HeapFree(GetProcessHeap(), 0, data);
621         return NULL;
622     }
623
624     rect = (RECT *)data->Buffer;
625     cgrect = (CGRect *)data->Buffer;
626     if (hdc_lptodp)  /* map to device coordinates */
627     {
628         LPtoDP(hdc_lptodp, (POINT *)rect, data->rdh.nCount * 2);
629         for (i = 0; i < data->rdh.nCount; i++)
630         {
631             if (rect[i].right < rect[i].left)
632             {
633                 INT tmp = rect[i].right;
634                 rect[i].right = rect[i].left;
635                 rect[i].left = tmp;
636             }
637             if (rect[i].bottom < rect[i].top)
638             {
639                 INT tmp = rect[i].bottom;
640                 rect[i].bottom = rect[i].top;
641                 rect[i].top = tmp;
642             }
643         }
644     }
645
646     if (sizeof(CGRect) > sizeof(RECT))
647     {
648         /* need to start from the end */
649         for (i = data->rdh.nCount-1; i >= 0; i--)
650             cgrect[i] = cgrect_from_rect(rect[i]);
651     }
652     else
653     {
654         for (i = 0; i < data->rdh.nCount; i++)
655             cgrect[i] = cgrect_from_rect(rect[i]);
656     }
657     return data;
658 }
659
660
661 /***********************************************************************
662  *              sync_window_position
663  *
664  * Synchronize the Mac window position with the Windows one
665  */
666 static void sync_window_position(struct macdrv_win_data *data, UINT swp_flags)
667 {
668     CGRect frame = cgrect_from_rect(data->whole_rect);
669     constrain_window_frame(&frame);
670
671     data->on_screen = macdrv_set_cocoa_window_frame(data->cocoa_window, &frame);
672     if (data->shaped) sync_window_region(data, (HRGN)1);
673
674     TRACE("win %p/%p pos %s\n", data->hwnd, data->cocoa_window,
675           wine_dbgstr_rect(&data->whole_rect));
676
677     if (data->on_screen && (!(swp_flags & SWP_NOZORDER) || (swp_flags & SWP_SHOWWINDOW)))
678     {
679         HWND next = NULL;
680         macdrv_window prev_window = NULL;
681         macdrv_window next_window = NULL;
682
683         /* find window that this one must be after */
684         HWND prev = GetWindow(data->hwnd, GW_HWNDPREV);
685         while (prev && !((GetWindowLongW(prev, GWL_STYLE) & WS_VISIBLE) &&
686                          (prev_window = macdrv_get_cocoa_window(prev))))
687             prev = GetWindow(prev, GW_HWNDPREV);
688         if (!prev_window)
689         {
690             /* find window that this one must be before */
691             next = GetWindow(data->hwnd, GW_HWNDNEXT);
692             while (next && !((GetWindowLongW(next, GWL_STYLE) & WS_VISIBLE) &&
693                              (next_window = macdrv_get_cocoa_window(next))))
694                 next = GetWindow(next, GW_HWNDNEXT);
695         }
696
697         data->on_screen = macdrv_order_cocoa_window(data->cocoa_window, prev_window, next_window);
698
699         TRACE("win %p/%p below %p/%p above %p/%p\n",
700               data->hwnd, data->cocoa_window, prev, prev_window, next, next_window);
701     }
702 }
703
704
705 /***********************************************************************
706  *              move_window_bits
707  *
708  * Move the window bits when a window is moved.
709  */
710 static void move_window_bits(HWND hwnd, macdrv_window window, const RECT *old_rect, const RECT *new_rect,
711                              const RECT *old_client_rect, const RECT *new_client_rect,
712                              const RECT *new_window_rect)
713 {
714     RECT src_rect = *old_rect;
715     RECT dst_rect = *new_rect;
716     HDC hdc_src, hdc_dst;
717     HRGN rgn;
718     HWND parent = 0;
719
720     if (!window)
721     {
722         OffsetRect(&dst_rect, -new_window_rect->left, -new_window_rect->top);
723         parent = GetAncestor(hwnd, GA_PARENT);
724         hdc_src = GetDCEx(parent, 0, DCX_CACHE);
725         hdc_dst = GetDCEx(hwnd, 0, DCX_CACHE | DCX_WINDOW);
726     }
727     else
728     {
729         OffsetRect(&dst_rect, -new_client_rect->left, -new_client_rect->top);
730         /* make src rect relative to the old position of the window */
731         OffsetRect(&src_rect, -old_client_rect->left, -old_client_rect->top);
732         if (dst_rect.left == src_rect.left && dst_rect.top == src_rect.top) return;
733         hdc_src = hdc_dst = GetDCEx(hwnd, 0, DCX_CACHE);
734     }
735
736     rgn = CreateRectRgnIndirect(&dst_rect);
737     SelectClipRgn(hdc_dst, rgn);
738     DeleteObject(rgn);
739     ExcludeUpdateRgn(hdc_dst, hwnd);
740
741     TRACE("copying bits for win %p/%p %s -> %s\n", hwnd, window,
742           wine_dbgstr_rect(&src_rect), wine_dbgstr_rect(&dst_rect));
743     BitBlt(hdc_dst, dst_rect.left, dst_rect.top,
744            dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top,
745            hdc_src, src_rect.left, src_rect.top, SRCCOPY);
746
747     ReleaseDC(hwnd, hdc_dst);
748     if (hdc_src != hdc_dst) ReleaseDC(parent, hdc_src);
749 }
750
751
752 /**********************************************************************
753  *              CreateDesktopWindow   (MACDRV.@)
754  */
755 BOOL CDECL macdrv_CreateDesktopWindow(HWND hwnd)
756 {
757     unsigned int width, height;
758
759     TRACE("%p\n", hwnd);
760
761     /* retrieve the real size of the desktop */
762     SERVER_START_REQ(get_window_rectangles)
763     {
764         req->handle = wine_server_user_handle(hwnd);
765         req->relative = COORDS_CLIENT;
766         wine_server_call(req);
767         width  = reply->window.right;
768         height = reply->window.bottom;
769     }
770     SERVER_END_REQ;
771
772     if (!width && !height)  /* not initialized yet */
773     {
774         CGRect rect = macdrv_get_desktop_rect();
775
776         SERVER_START_REQ(set_window_pos)
777         {
778             req->handle        = wine_server_user_handle(hwnd);
779             req->previous      = 0;
780             req->swp_flags     = SWP_NOZORDER;
781             req->window.left   = CGRectGetMinX(rect);
782             req->window.top    = CGRectGetMinY(rect);
783             req->window.right  = CGRectGetMaxX(rect);
784             req->window.bottom = CGRectGetMaxY(rect);
785             req->client        = req->window;
786             wine_server_call(req);
787         }
788         SERVER_END_REQ;
789     }
790
791     return TRUE;
792 }
793
794
795 /**********************************************************************
796  *              CreateWindow   (MACDRV.@)
797  */
798 BOOL CDECL macdrv_CreateWindow(HWND hwnd)
799 {
800     return TRUE;
801 }
802
803
804 /***********************************************************************
805  *              DestroyWindow   (MACDRV.@)
806  */
807 void CDECL macdrv_DestroyWindow(HWND hwnd)
808 {
809     struct macdrv_win_data *data;
810
811     TRACE("%p\n", hwnd);
812
813     if (!(data = get_win_data(hwnd))) return;
814
815     destroy_cocoa_window(data);
816
817     CFDictionaryRemoveValue(win_datas, hwnd);
818     release_win_data(data);
819     HeapFree(GetProcessHeap(), 0, data);
820 }
821
822
823 /***********************************************************************
824  *              SetLayeredWindowAttributes  (MACDRV.@)
825  *
826  * Set transparency attributes for a layered window.
827  */
828 void CDECL macdrv_SetLayeredWindowAttributes(HWND hwnd, COLORREF key, BYTE alpha, DWORD flags)
829 {
830     struct macdrv_win_data *data = get_win_data(hwnd);
831
832     TRACE("hwnd %p key %#08x alpha %#02x flags %x\n", hwnd, key, alpha, flags);
833
834     if (data)
835     {
836         data->layered = TRUE;
837         if (data->cocoa_window)
838         {
839             sync_window_opacity(data, key, alpha, FALSE, flags);
840             /* since layered attributes are now set, can now show the window */
841             if ((GetWindowLongW(hwnd, GWL_STYLE) & WS_VISIBLE) && !data->on_screen)
842                 show_window(data);
843         }
844         release_win_data(data);
845     }
846     else
847         FIXME("setting layered attributes on window %p of other process not supported\n", hwnd);
848 }
849
850
851 /*****************************************************************
852  *              SetParent   (MACDRV.@)
853  */
854 void CDECL macdrv_SetParent(HWND hwnd, HWND parent, HWND old_parent)
855 {
856     struct macdrv_win_data *data;
857
858     TRACE("%p, %p, %p\n", hwnd, parent, old_parent);
859
860     if (parent == old_parent) return;
861     if (!(data = get_win_data(hwnd))) return;
862
863     if (parent != GetDesktopWindow()) /* a child window */
864     {
865         if (old_parent == GetDesktopWindow())
866         {
867             /* destroy the old Mac window */
868             destroy_cocoa_window(data);
869         }
870     }
871     else  /* new top level window */
872         create_cocoa_window(data);
873     release_win_data(data);
874 }
875
876
877 /***********************************************************************
878  *              SetWindowRgn  (MACDRV.@)
879  *
880  * Assign specified region to window (for non-rectangular windows)
881  */
882 int CDECL macdrv_SetWindowRgn(HWND hwnd, HRGN hrgn, BOOL redraw)
883 {
884     struct macdrv_win_data *data;
885
886     TRACE("%p, %p, %d\n", hwnd, hrgn, redraw);
887
888     if ((data = get_win_data(hwnd)))
889     {
890         sync_window_region(data, hrgn);
891         release_win_data(data);
892     }
893     else
894     {
895         DWORD procid;
896
897         GetWindowThreadProcessId(hwnd, &procid);
898         if (procid != GetCurrentProcessId())
899             SendMessageW(hwnd, WM_MACDRV_SET_WIN_REGION, 0, 0);
900     }
901
902     return TRUE;
903 }
904
905
906 /***********************************************************************
907  *              SetWindowStyle   (MACDRV.@)
908  *
909  * Update the state of the Cocoa window to reflect a style change
910  */
911 void CDECL macdrv_SetWindowStyle(HWND hwnd, INT offset, STYLESTRUCT *style)
912 {
913     struct macdrv_win_data *data;
914
915     TRACE("%p, %d, %p\n", hwnd, offset, style);
916
917     if (hwnd == GetDesktopWindow()) return;
918     if (!(data = get_win_data(hwnd))) return;
919
920     if (data->cocoa_window)
921     {
922         DWORD changed = style->styleNew ^ style->styleOld;
923
924         set_cocoa_window_properties(data);
925
926         if (offset == GWL_EXSTYLE && (changed & WS_EX_LAYERED)) /* changing WS_EX_LAYERED resets attributes */
927         {
928             data->layered = FALSE;
929             sync_window_opacity(data, 0, 0, FALSE, 0);
930             if (data->surface) set_surface_use_alpha(data->surface, FALSE);
931         }
932     }
933
934     release_win_data(data);
935 }
936
937
938 /*****************************************************************
939  *              SetWindowText   (MACDRV.@)
940  */
941 void CDECL macdrv_SetWindowText(HWND hwnd, LPCWSTR text)
942 {
943     macdrv_window win;
944
945     TRACE("%p, %s\n", hwnd, debugstr_w(text));
946
947     if ((win = macdrv_get_cocoa_window(hwnd)))
948         macdrv_set_cocoa_window_title(win, text, strlenW(text));
949 }
950
951
952 /***********************************************************************
953  *              UpdateLayeredWindow   (MACDRV.@)
954  */
955 BOOL CDECL macdrv_UpdateLayeredWindow(HWND hwnd, const UPDATELAYEREDWINDOWINFO *info,
956                                       const RECT *window_rect)
957 {
958     struct window_surface *surface;
959     struct macdrv_win_data *data;
960     BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, 0 };
961     BYTE alpha;
962     char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
963     BITMAPINFO *bmi = (BITMAPINFO *)buffer;
964     void *src_bits, *dst_bits;
965     RECT rect;
966     HDC hdc = 0;
967     HBITMAP dib;
968     BOOL ret = FALSE;
969
970     if (!(data = get_win_data(hwnd))) return FALSE;
971
972     data->layered = TRUE;
973
974     rect = *window_rect;
975     OffsetRect(&rect, -window_rect->left, -window_rect->top);
976
977     surface = data->surface;
978     if (!surface || memcmp(&surface->rect, &rect, sizeof(RECT)))
979     {
980         data->surface = create_surface(data->cocoa_window, &rect, TRUE);
981         set_window_surface(data->cocoa_window, data->surface);
982         if (surface) window_surface_release(surface);
983         surface = data->surface;
984     }
985     else set_surface_use_alpha(surface, TRUE);
986
987     if (surface) window_surface_add_ref(surface);
988     release_win_data(data);
989
990     if (!surface) return FALSE;
991     if (!info->hdcSrc)
992     {
993         window_surface_release(surface);
994         return TRUE;
995     }
996
997     if (info->dwFlags & ULW_ALPHA)
998     {
999         /* Apply SourceConstantAlpha via window alpha, not blend. */
1000         alpha = info->pblend->SourceConstantAlpha;
1001         blend = *info->pblend;
1002         blend.SourceConstantAlpha = 0xff;
1003     }
1004     else
1005         alpha = 0xff;
1006
1007     dst_bits = surface->funcs->get_info(surface, bmi);
1008
1009     if (!(dib = CreateDIBSection(info->hdcDst, bmi, DIB_RGB_COLORS, &src_bits, NULL, 0))) goto done;
1010     if (!(hdc = CreateCompatibleDC(0))) goto done;
1011
1012     SelectObject(hdc, dib);
1013     if (info->prcDirty)
1014     {
1015         IntersectRect(&rect, &rect, info->prcDirty);
1016         surface->funcs->lock(surface);
1017         memcpy(src_bits, dst_bits, bmi->bmiHeader.biSizeImage);
1018         surface->funcs->unlock(surface);
1019         PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, BLACKNESS);
1020     }
1021     if (!(ret = GdiAlphaBlend(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
1022                               info->hdcSrc,
1023                               rect.left + (info->pptSrc ? info->pptSrc->x : 0),
1024                               rect.top + (info->pptSrc ? info->pptSrc->y : 0),
1025                               rect.right - rect.left, rect.bottom - rect.top,
1026                               blend)))
1027         goto done;
1028
1029     if ((data = get_win_data(hwnd)))
1030     {
1031         if (surface == data->surface)
1032         {
1033             surface->funcs->lock(surface);
1034             memcpy(dst_bits, src_bits, bmi->bmiHeader.biSizeImage);
1035             add_bounds_rect(surface->funcs->get_bounds(surface), &rect);
1036             surface->funcs->unlock(surface);
1037             surface->funcs->flush(surface);
1038         }
1039
1040         /* The ULW flags are a superset of the LWA flags. */
1041         sync_window_opacity(data, info->crKey, alpha, TRUE, info->dwFlags);
1042
1043         release_win_data(data);
1044     }
1045
1046 done:
1047     window_surface_release(surface);
1048     if (hdc) DeleteDC(hdc);
1049     if (dib) DeleteObject(dib);
1050     return ret;
1051 }
1052
1053
1054 /**********************************************************************
1055  *              WindowMessage   (MACDRV.@)
1056  */
1057 LRESULT CDECL macdrv_WindowMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
1058 {
1059     struct macdrv_win_data *data;
1060
1061     TRACE("%p, %u, %u, %lu\n", hwnd, msg, (unsigned)wp, lp);
1062
1063     switch(msg)
1064     {
1065     case WM_MACDRV_SET_WIN_REGION:
1066         if ((data = get_win_data(hwnd)))
1067         {
1068             sync_window_region(data, (HRGN)1);
1069             release_win_data(data);
1070         }
1071         return 0;
1072     }
1073
1074     FIXME("unrecognized window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, wp, lp);
1075     return 0;
1076 }
1077
1078
1079 static inline RECT get_surface_rect(const RECT *visible_rect)
1080 {
1081     RECT rect;
1082     RECT desktop_rect = rect_from_cgrect(macdrv_get_desktop_rect());
1083
1084     IntersectRect(&rect, visible_rect, &desktop_rect);
1085     OffsetRect(&rect, -visible_rect->left, -visible_rect->top);
1086     rect.left &= ~127;
1087     rect.top  &= ~127;
1088     rect.right  = max(rect.left + 128, (rect.right + 127) & ~127);
1089     rect.bottom = max(rect.top + 128, (rect.bottom + 127) & ~127);
1090     return rect;
1091 }
1092
1093
1094 /***********************************************************************
1095  *              WindowPosChanging   (MACDRV.@)
1096  */
1097 void CDECL macdrv_WindowPosChanging(HWND hwnd, HWND insert_after, UINT swp_flags,
1098                                     const RECT *window_rect, const RECT *client_rect,
1099                                     RECT *visible_rect, struct window_surface **surface)
1100 {
1101     struct macdrv_win_data *data = get_win_data(hwnd);
1102     DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
1103     RECT surface_rect;
1104
1105     TRACE("%p after %p swp %04x window %s client %s visible %s surface %p\n", hwnd, insert_after,
1106           swp_flags, wine_dbgstr_rect(window_rect), wine_dbgstr_rect(client_rect),
1107           wine_dbgstr_rect(visible_rect), surface);
1108
1109     if (!data && !(data = macdrv_create_win_data(hwnd, window_rect, client_rect))) return;
1110
1111     *visible_rect = *window_rect;
1112     macdrv_window_to_mac_rect(data, style, visible_rect);
1113     TRACE("visible_rect %s -> %s\n", wine_dbgstr_rect(window_rect),
1114           wine_dbgstr_rect(visible_rect));
1115
1116     /* create the window surface if necessary */
1117     if (!data->cocoa_window) goto done;
1118     if (swp_flags & SWP_HIDEWINDOW) goto done;
1119
1120     if (*surface) window_surface_release(*surface);
1121     *surface = NULL;
1122
1123     surface_rect = get_surface_rect(visible_rect);
1124     if (data->surface)
1125     {
1126         if (!memcmp(&data->surface->rect, &surface_rect, sizeof(surface_rect)))
1127         {
1128             /* existing surface is good enough */
1129             window_surface_add_ref(data->surface);
1130             *surface = data->surface;
1131             goto done;
1132         }
1133     }
1134     else if (!(swp_flags & SWP_SHOWWINDOW) && !(style & WS_VISIBLE)) goto done;
1135
1136     *surface = create_surface(data->cocoa_window, &surface_rect, FALSE);
1137
1138 done:
1139     release_win_data(data);
1140 }
1141
1142
1143 /***********************************************************************
1144  *              WindowPosChanged   (MACDRV.@)
1145  */
1146 void CDECL macdrv_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags,
1147                                    const RECT *window_rect, const RECT *client_rect,
1148                                    const RECT *visible_rect, const RECT *valid_rects,
1149                                    struct window_surface *surface)
1150 {
1151     struct macdrv_win_data *data;
1152     DWORD new_style = GetWindowLongW(hwnd, GWL_STYLE);
1153     RECT old_window_rect, old_whole_rect, old_client_rect;
1154
1155     if (!(data = get_win_data(hwnd))) return;
1156
1157     old_window_rect = data->window_rect;
1158     old_whole_rect  = data->whole_rect;
1159     old_client_rect = data->client_rect;
1160     data->window_rect = *window_rect;
1161     data->whole_rect  = *visible_rect;
1162     data->client_rect = *client_rect;
1163     if (surface)
1164         window_surface_add_ref(surface);
1165     set_window_surface(data->cocoa_window, surface);
1166     if (data->surface) window_surface_release(data->surface);
1167     data->surface = surface;
1168
1169     TRACE("win %p/%p window %s whole %s client %s style %08x flags %08x surface %p\n",
1170            hwnd, data->cocoa_window, wine_dbgstr_rect(window_rect),
1171            wine_dbgstr_rect(visible_rect), wine_dbgstr_rect(client_rect),
1172            new_style, swp_flags, surface);
1173
1174     if (!IsRectEmpty(&valid_rects[0]))
1175     {
1176         macdrv_window window = data->cocoa_window;
1177         int x_offset = old_whole_rect.left - data->whole_rect.left;
1178         int y_offset = old_whole_rect.top - data->whole_rect.top;
1179
1180         /* if all that happened is that the whole window moved, copy everything */
1181         if (!(swp_flags & SWP_FRAMECHANGED) &&
1182             old_whole_rect.right   - data->whole_rect.right   == x_offset &&
1183             old_whole_rect.bottom  - data->whole_rect.bottom  == y_offset &&
1184             old_client_rect.left   - data->client_rect.left   == x_offset &&
1185             old_client_rect.right  - data->client_rect.right  == x_offset &&
1186             old_client_rect.top    - data->client_rect.top    == y_offset &&
1187             old_client_rect.bottom - data->client_rect.bottom == y_offset &&
1188             !memcmp(&valid_rects[0], &data->client_rect, sizeof(RECT)))
1189         {
1190             /* A Cocoa window's bits are moved automatically */
1191             if (!window && (x_offset != 0 || y_offset != 0))
1192             {
1193                 release_win_data(data);
1194                 move_window_bits(hwnd, window, &old_whole_rect, visible_rect,
1195                                  &old_client_rect, client_rect, window_rect);
1196                 if (!(data = get_win_data(hwnd))) return;
1197             }
1198         }
1199         else
1200         {
1201             release_win_data(data);
1202             move_window_bits(hwnd, window, &valid_rects[1], &valid_rects[0],
1203                              &old_client_rect, client_rect, window_rect);
1204             if (!(data = get_win_data(hwnd))) return;
1205         }
1206     }
1207
1208     if (!data->cocoa_window) goto done;
1209
1210     if (data->on_screen)
1211     {
1212         if ((swp_flags & SWP_HIDEWINDOW) && !(new_style & WS_VISIBLE))
1213             hide_window(data);
1214     }
1215
1216     if (new_style & WS_VISIBLE)
1217     {
1218         if (!data->on_screen || (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)))
1219             set_cocoa_window_properties(data);
1220
1221         /* layered windows are not shown until their attributes are set */
1222         if (!data->on_screen &&
1223             (data->layered || !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED)))
1224             show_window(data);
1225     }
1226
1227     sync_window_position(data, swp_flags);
1228     set_cocoa_window_properties(data);
1229
1230 done:
1231     release_win_data(data);
1232 }