Release 1.5.29.
[wine] / dlls / winemac.drv / display.c
1 /*
2  * MACDRV display settings
3  *
4  * Copyright 2003 Alexander James Pasadyn
5  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23
24 #include "macdrv.h"
25 #include "winuser.h"
26 #include "winreg.h"
27 #include "ddrawi.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(display);
30
31
32 BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW devmode, DWORD flags);
33
34
35 static CFArrayRef modes;
36 static BOOL modes_has_8bpp, modes_has_16bpp;
37 static int default_mode_bpp;
38 static CRITICAL_SECTION modes_section;
39 static CRITICAL_SECTION_DEBUG critsect_debug =
40 {
41     0, 0, &modes_section,
42     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
43       0, 0, { (DWORD_PTR)(__FILE__ ": modes_section") }
44 };
45 static CRITICAL_SECTION modes_section = { &critsect_debug, -1, 0, 0, 0, 0 };
46
47
48 static inline HMONITOR display_id_to_monitor(CGDirectDisplayID display_id)
49 {
50     return (HMONITOR)(UINT_PTR)display_id;
51 }
52
53 static inline CGDirectDisplayID monitor_to_display_id(HMONITOR handle)
54 {
55     return (CGDirectDisplayID)(UINT_PTR)handle;
56 }
57
58
59 static BOOL get_display_device_reg_key(char *key, unsigned len)
60 {
61     static const char display_device_guid_prop[] = "__wine_display_device_guid";
62     static const char video_path[] = "System\\CurrentControlSet\\Control\\Video\\{";
63     static const char display0[] = "}\\0000";
64     ATOM guid_atom;
65
66     assert(len >= sizeof(video_path) + sizeof(display0) + 40);
67
68     guid_atom = HandleToULong(GetPropA(GetDesktopWindow(), display_device_guid_prop));
69     if (!guid_atom) return FALSE;
70
71     memcpy(key, video_path, sizeof(video_path));
72
73     if (!GlobalGetAtomNameA(guid_atom, key + strlen(key), 40))
74         return FALSE;
75
76     strcat(key, display0);
77
78     TRACE("display device key %s\n", wine_dbgstr_a(key));
79     return TRUE;
80 }
81
82
83 static BOOL read_registry_settings(DEVMODEW *dm)
84 {
85     char wine_mac_reg_key[128];
86     HKEY hkey;
87     DWORD type, size;
88     BOOL ret = TRUE;
89
90     dm->dmFields = 0;
91
92     if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
93         return FALSE;
94
95     if (RegOpenKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, KEY_READ, &hkey))
96         return FALSE;
97
98 #define query_value(name, data) \
99     size = sizeof(DWORD); \
100     if (RegQueryValueExA(hkey, name, 0, &type, (LPBYTE)(data), &size) || \
101         type != REG_DWORD || size != sizeof(DWORD)) \
102         ret = FALSE
103
104     query_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
105     dm->dmFields |= DM_BITSPERPEL;
106     query_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
107     dm->dmFields |= DM_PELSWIDTH;
108     query_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
109     dm->dmFields |= DM_PELSHEIGHT;
110     query_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
111     dm->dmFields |= DM_DISPLAYFREQUENCY;
112     query_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
113     dm->dmFields |= DM_DISPLAYFLAGS;
114     query_value("DefaultSettings.XPanning", &dm->dmPosition.x);
115     query_value("DefaultSettings.YPanning", &dm->dmPosition.y);
116     query_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
117     query_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
118
119 #undef query_value
120
121     RegCloseKey(hkey);
122     return ret;
123 }
124
125
126 static BOOL write_registry_settings(const DEVMODEW *dm)
127 {
128     char wine_mac_reg_key[128];
129     HKEY hkey;
130     BOOL ret = TRUE;
131
132     if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
133         return FALSE;
134
135     if (RegCreateKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, NULL,
136                         REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL))
137         return FALSE;
138
139 #define set_value(name, data) \
140     if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
141         ret = FALSE
142
143     set_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
144     set_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
145     set_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
146     set_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
147     set_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
148     set_value("DefaultSettings.XPanning", &dm->dmPosition.x);
149     set_value("DefaultSettings.YPanning", &dm->dmPosition.y);
150     set_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
151     set_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
152
153 #undef set_value
154
155     RegCloseKey(hkey);
156     return ret;
157 }
158
159
160 static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode)
161 {
162     CFStringRef pixel_encoding;
163     int bits_per_pixel = 0;
164
165     pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
166     if (pixel_encoding)
167     {
168         if (CFEqual(pixel_encoding, CFSTR(kIO32BitFloatPixels)))
169             bits_per_pixel = 128;
170         else if (CFEqual(pixel_encoding, CFSTR(kIO16BitFloatPixels)))
171             bits_per_pixel = 64;
172         else if (CFEqual(pixel_encoding, CFSTR(kIO64BitDirectPixels)))
173             bits_per_pixel = 64;
174         else if (CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels)))
175             bits_per_pixel = 30;
176         else if (CFEqual(pixel_encoding, CFSTR(IO32BitDirectPixels)))
177             bits_per_pixel = 32;
178         else if (CFEqual(pixel_encoding, CFSTR(IO16BitDirectPixels)))
179             bits_per_pixel = 16;
180         else if (CFEqual(pixel_encoding, CFSTR(IO8BitIndexedPixels)))
181             bits_per_pixel = 8;
182         else if (CFEqual(pixel_encoding, CFSTR(IO4BitIndexedPixels)))
183             bits_per_pixel = 4;
184         else if (CFEqual(pixel_encoding, CFSTR(IO2BitIndexedPixels)))
185             bits_per_pixel = 2;
186         else if (CFEqual(pixel_encoding, CFSTR(IO1BitIndexedPixels)))
187             bits_per_pixel = 1;
188
189         CFRelease(pixel_encoding);
190     }
191
192     return bits_per_pixel;
193 }
194
195
196 static int get_default_bpp(void)
197 {
198     int ret;
199
200     EnterCriticalSection(&modes_section);
201
202     if (!default_mode_bpp)
203     {
204         CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
205         if (mode)
206         {
207             default_mode_bpp = display_mode_bits_per_pixel(mode);
208             CFRelease(mode);
209         }
210
211         if (!default_mode_bpp)
212             default_mode_bpp = 32;
213     }
214
215     ret = default_mode_bpp;
216
217     LeaveCriticalSection(&modes_section);
218
219     TRACE(" -> %d\n", ret);
220     return ret;
221 }
222
223
224 /***********************************************************************
225  *              ChangeDisplaySettingsEx  (MACDRV.@)
226  *
227  */
228 LONG CDECL macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
229                                           HWND hwnd, DWORD flags, LPVOID lpvoid)
230 {
231     LONG ret = DISP_CHANGE_BADMODE;
232     int bpp;
233     DEVMODEW dm;
234     BOOL def_mode = TRUE;
235     struct macdrv_display *displays;
236     int num_displays;
237     CFArrayRef display_modes;
238     CFIndex count, i, safe;
239
240     TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid);
241
242     if (devmode)
243     {
244         /* this is the minimal dmSize that XP accepts */
245         if (devmode->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
246             return DISP_CHANGE_FAILED;
247
248         if (devmode->dmSize >= FIELD_OFFSET(DEVMODEW, dmFields) + sizeof(devmode->dmFields))
249         {
250             if (((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel) ||
251                 ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth) ||
252                 ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight) ||
253                 ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency))
254                 def_mode = FALSE;
255         }
256     }
257
258     if (def_mode)
259     {
260         if (!macdrv_EnumDisplaySettingsEx(devname, ENUM_REGISTRY_SETTINGS, &dm, 0))
261         {
262             ERR("Default mode not found!\n");
263             return DISP_CHANGE_BADMODE;
264         }
265
266         TRACE("Return to original display mode\n");
267         devmode = &dm;
268     }
269
270     if ((devmode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
271     {
272         WARN("devmode doesn't specify the resolution: %04x\n", devmode->dmFields);
273         return DISP_CHANGE_BADMODE;
274     }
275
276     if (macdrv_get_displays(&displays, &num_displays))
277         return DISP_CHANGE_FAILED;
278
279     display_modes = CGDisplayCopyAllDisplayModes(displays[0].displayID, NULL);
280     if (!display_modes)
281     {
282         macdrv_free_displays(displays);
283         return DISP_CHANGE_FAILED;
284     }
285
286     bpp = get_default_bpp();
287     if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel != bpp)
288         TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, devmode->dmBitsPerPel);
289
290     TRACE("looking for %dx%dx%dbpp @%d Hz",
291           (devmode->dmFields & DM_PELSWIDTH ? devmode->dmPelsWidth : 0),
292           (devmode->dmFields & DM_PELSHEIGHT ? devmode->dmPelsHeight : 0),
293           bpp,
294           (devmode->dmFields & DM_DISPLAYFREQUENCY ? devmode->dmDisplayFrequency : 0));
295     if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
296         TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un");
297     if (devmode->dmFields & DM_DISPLAYFLAGS)
298         TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
299     TRACE("\n");
300
301     safe = -1;
302     count = CFArrayGetCount(display_modes);
303     for (i = 0; i < count; i++)
304     {
305         CGDisplayModeRef display_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(display_modes, i);
306         uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
307         int mode_bpp = display_mode_bits_per_pixel(display_mode);
308         size_t width = CGDisplayModeGetWidth(display_mode);
309         size_t height = CGDisplayModeGetHeight(display_mode);
310
311         if (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag))
312             continue;
313
314         safe++;
315
316         if (bpp != mode_bpp)
317             continue;
318
319         if (devmode->dmFields & DM_PELSWIDTH)
320         {
321             if (devmode->dmPelsWidth != width)
322                 continue;
323         }
324         if (devmode->dmFields & DM_PELSHEIGHT)
325         {
326             if (devmode->dmPelsHeight != height)
327                 continue;
328         }
329         if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != 0)
330         {
331             double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
332             if (!refresh_rate)
333                 refresh_rate = 60;
334             if (devmode->dmDisplayFrequency != (DWORD)refresh_rate)
335                 continue;
336         }
337         if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
338         {
339             if (!(devmode->dmDisplayFixedOutput == DMDFO_STRETCH) != !(io_flags & kDisplayModeStretchedFlag))
340                 continue;
341         }
342         if (devmode->dmFields & DM_DISPLAYFLAGS)
343         {
344             if (!(devmode->dmDisplayFlags & DM_INTERLACED) != !(io_flags & kDisplayModeInterlacedFlag))
345                 continue;
346         }
347
348         /* we have a valid mode */
349         TRACE("Requested display settings match mode %ld\n", safe);
350
351         if ((flags & CDS_UPDATEREGISTRY) && !write_registry_settings(devmode))
352         {
353             WARN("Failed to update registry\n");
354             ret = DISP_CHANGE_NOTUPDATED;
355             break;
356         }
357
358         if (flags & (CDS_TEST | CDS_NORESET))
359             ret = DISP_CHANGE_SUCCESSFUL;
360         else
361         {
362             if (macdrv_set_display_mode(&displays[0], display_mode))
363             {
364                 SendMessageW(GetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
365                              MAKELPARAM(width, height));
366                 ret = DISP_CHANGE_SUCCESSFUL;
367             }
368             else
369             {
370                 WARN("Failed to set display mode\n");
371                 ret = DISP_CHANGE_FAILED;
372             }
373         }
374
375         break;
376     }
377
378     CFRelease(display_modes);
379     macdrv_free_displays(displays);
380
381     if (i >= count)
382     {
383         /* no valid modes found */
384         ERR("No matching mode found %ux%ux%d @%u!\n", devmode->dmPelsWidth, devmode->dmPelsHeight,
385             bpp, devmode->dmDisplayFrequency);
386     }
387
388     return ret;
389 }
390
391
392 /***********************************************************************
393  *              EnumDisplayMonitors  (MACDRV.@)
394  */
395 BOOL CDECL macdrv_EnumDisplayMonitors(HDC hdc, LPRECT rect, MONITORENUMPROC proc, LPARAM lparam)
396 {
397     struct macdrv_display *displays;
398     int num_displays;
399     int i;
400     BOOL ret = TRUE;
401
402     TRACE("%p, %s, %p, %#lx\n", hdc, wine_dbgstr_rect(rect), proc, lparam);
403
404     if (hdc)
405     {
406         POINT origin;
407         RECT limit;
408
409         if (!GetDCOrgEx(hdc, &origin)) return FALSE;
410         if (GetClipBox(hdc, &limit) == ERROR) return FALSE;
411
412         if (rect && !IntersectRect(&limit, &limit, rect)) return TRUE;
413
414         if (macdrv_get_displays(&displays, &num_displays))
415             return FALSE;
416
417         for (i = 0; i < num_displays; i++)
418         {
419             RECT monrect = rect_from_cgrect(displays[i].frame);
420             OffsetRect(&monrect, -origin.x, -origin.y);
421             if (IntersectRect(&monrect, &monrect, &limit))
422             {
423                 HMONITOR monitor = display_id_to_monitor(displays[i].displayID);
424                 TRACE("monitor %d handle %p @ %s\n", i, monitor, wine_dbgstr_rect(&monrect));
425                 if (!proc(monitor, hdc, &monrect, lparam))
426                 {
427                     ret = FALSE;
428                     break;
429                 }
430             }
431         }
432     }
433     else
434     {
435         if (macdrv_get_displays(&displays, &num_displays))
436             return FALSE;
437
438         for (i = 0; i < num_displays; i++)
439         {
440             RECT monrect = rect_from_cgrect(displays[i].frame);
441             RECT unused;
442             if (!rect || IntersectRect(&unused, &monrect, rect))
443             {
444                 HMONITOR monitor = display_id_to_monitor(displays[i].displayID);
445                 TRACE("monitor %d handle %p @ %s\n", i, monitor, wine_dbgstr_rect(&monrect));
446                 if (!proc(monitor, 0, &monrect, lparam))
447                 {
448                     ret = FALSE;
449                     break;
450                 }
451             }
452         }
453     }
454
455     macdrv_free_displays(displays);
456
457     return ret;
458 }
459
460
461 /***********************************************************************
462  *              EnumDisplaySettingsEx  (MACDRV.@)
463  *
464  */
465 BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode,
466                                         LPDEVMODEW devmode, DWORD flags)
467 {
468     static const WCHAR dev_name[CCHDEVICENAME] =
469         { 'W','i','n','e',' ','M','a','c',' ','d','r','i','v','e','r',0 };
470     struct macdrv_display *displays = NULL;
471     int num_displays;
472     CGDisplayModeRef display_mode;
473     int display_mode_bpp;
474     BOOL synthesized = FALSE;
475     double rotation;
476     uint32_t io_flags;
477
478     TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname), mode, devmode, devmode->dmSize, flags);
479
480     memcpy(devmode->dmDeviceName, dev_name, sizeof(dev_name));
481     devmode->dmSpecVersion = DM_SPECVERSION;
482     devmode->dmDriverVersion = DM_SPECVERSION;
483     devmode->dmSize = FIELD_OFFSET(DEVMODEW, dmICMMethod);
484     devmode->dmDriverExtra = 0;
485     memset(&devmode->dmFields, 0, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields));
486
487     if (mode == ENUM_REGISTRY_SETTINGS)
488     {
489         TRACE("mode %d (registry) -- getting default mode\n", mode);
490         return read_registry_settings(devmode);
491     }
492
493     if (macdrv_get_displays(&displays, &num_displays))
494         goto failed;
495
496     if (mode == ENUM_CURRENT_SETTINGS)
497     {
498         TRACE("mode %d (current) -- getting current mode\n", mode);
499         display_mode = CGDisplayCopyDisplayMode(displays[0].displayID);
500         display_mode_bpp = display_mode_bits_per_pixel(display_mode);
501     }
502     else
503     {
504         DWORD count, i;
505
506         EnterCriticalSection(&modes_section);
507
508         if (mode == 0 || !modes)
509         {
510             if (modes) CFRelease(modes);
511             modes = CGDisplayCopyAllDisplayModes(displays[0].displayID, NULL);
512             modes_has_8bpp = modes_has_16bpp = FALSE;
513
514             if (modes)
515             {
516                 count = CFArrayGetCount(modes);
517                 for (i = 0; i < count && !(modes_has_8bpp && modes_has_16bpp); i++)
518                 {
519                     CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
520                     int bpp = display_mode_bits_per_pixel(mode);
521                     if (bpp == 8)
522                         modes_has_8bpp = TRUE;
523                     else if (bpp == 16)
524                         modes_has_16bpp = TRUE;
525                 }
526             }
527         }
528
529         display_mode = NULL;
530         if (modes)
531         {
532             int default_bpp = get_default_bpp();
533             DWORD seen_modes = 0;
534
535             count = CFArrayGetCount(modes);
536             for (i = 0; i < count; i++)
537             {
538                 CGDisplayModeRef candidate = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
539
540                 io_flags = CGDisplayModeGetIOFlags(candidate);
541                 if (!(flags & EDS_RAWMODE) &&
542                     (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag)))
543                     continue;
544
545                 seen_modes++;
546                 if (seen_modes > mode)
547                 {
548                     display_mode = (CGDisplayModeRef)CFRetain(candidate);
549                     display_mode_bpp = display_mode_bits_per_pixel(display_mode);
550                     break;
551                 }
552
553                 /* We only synthesize modes from those having the default bpp. */
554                 if (display_mode_bits_per_pixel(candidate) != default_bpp)
555                     continue;
556
557                 if (!modes_has_8bpp)
558                 {
559                     seen_modes++;
560                     if (seen_modes > mode)
561                     {
562                         display_mode = (CGDisplayModeRef)CFRetain(candidate);
563                         display_mode_bpp = 8;
564                         synthesized = TRUE;
565                         break;
566                     }
567                 }
568
569                 if (!modes_has_16bpp)
570                 {
571                     seen_modes++;
572                     if (seen_modes > mode)
573                     {
574                         display_mode = (CGDisplayModeRef)CFRetain(candidate);
575                         display_mode_bpp = 16;
576                         synthesized = TRUE;
577                         break;
578                     }
579                 }
580             }
581         }
582
583         LeaveCriticalSection(&modes_section);
584     }
585
586     if (!display_mode)
587         goto failed;
588
589     /* We currently only report modes for the primary display, so it's at (0, 0). */
590     devmode->dmPosition.x = 0;
591     devmode->dmPosition.y = 0;
592     devmode->dmFields |= DM_POSITION;
593
594     rotation = CGDisplayRotation(displays[0].displayID);
595     devmode->dmDisplayOrientation = ((int)((rotation / 90) + 0.5)) % 4;
596     devmode->dmFields |= DM_DISPLAYORIENTATION;
597
598     io_flags = CGDisplayModeGetIOFlags(display_mode);
599     if (io_flags & kDisplayModeStretchedFlag)
600         devmode->dmDisplayFixedOutput = DMDFO_STRETCH;
601     else
602         devmode->dmDisplayFixedOutput = DMDFO_CENTER;
603     devmode->dmFields |= DM_DISPLAYFIXEDOUTPUT;
604
605     devmode->dmBitsPerPel = display_mode_bpp;
606     if (devmode->dmBitsPerPel)
607         devmode->dmFields |= DM_BITSPERPEL;
608
609     devmode->dmPelsWidth = CGDisplayModeGetWidth(display_mode);
610     devmode->dmPelsHeight = CGDisplayModeGetHeight(display_mode);
611     devmode->dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
612
613     devmode->dmDisplayFlags = 0;
614     if (io_flags & kDisplayModeInterlacedFlag)
615         devmode->dmDisplayFlags |= DM_INTERLACED;
616     devmode->dmFields |= DM_DISPLAYFLAGS;
617
618     devmode->dmDisplayFrequency = CGDisplayModeGetRefreshRate(display_mode);
619     if (!devmode->dmDisplayFrequency)
620         devmode->dmDisplayFrequency = 60;
621     devmode->dmFields |= DM_DISPLAYFREQUENCY;
622
623     CFRelease(display_mode);
624     macdrv_free_displays(displays);
625
626     TRACE("mode %d -- %dx%dx%dbpp @%d Hz", mode,
627           devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel,
628           devmode->dmDisplayFrequency);
629     if (devmode->dmDisplayOrientation)
630         TRACE(" rotated %u degrees", devmode->dmDisplayOrientation * 90);
631     if (devmode->dmDisplayFixedOutput == DMDFO_STRETCH)
632         TRACE(" stretched");
633     if (devmode->dmDisplayFlags & DM_INTERLACED)
634         TRACE(" interlaced");
635     if (synthesized)
636         TRACE(" (synthesized)");
637     TRACE("\n");
638
639     return TRUE;
640
641 failed:
642     TRACE("mode %d -- not present\n", mode);
643     if (displays) macdrv_free_displays(displays);
644     SetLastError(ERROR_NO_MORE_FILES);
645     return FALSE;
646 }
647
648
649 /***********************************************************************
650  *              GetDeviceGammaRamp (MACDRV.@)
651  */
652 BOOL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
653 {
654     BOOL ret = FALSE;
655     DDGAMMARAMP *r = ramp;
656     struct macdrv_display *displays;
657     int num_displays;
658     uint32_t mac_entries;
659     int win_entries = sizeof(r->red) / sizeof(r->red[0]);
660     CGGammaValue *red, *green, *blue;
661     CGError err;
662     int win_entry;
663
664     TRACE("dev %p ramp %p\n", dev, ramp);
665
666     if (macdrv_get_displays(&displays, &num_displays))
667     {
668         WARN("failed to get Mac displays\n");
669         return FALSE;
670     }
671
672     mac_entries = CGDisplayGammaTableCapacity(displays[0].displayID);
673     red = HeapAlloc(GetProcessHeap(), 0, mac_entries * sizeof(red[0]) * 3);
674     if (!red)
675         goto done;
676     green = red + mac_entries;
677     blue = green + mac_entries;
678
679     err = CGGetDisplayTransferByTable(displays[0].displayID, mac_entries, red, green,
680                                       blue, &mac_entries);
681     if (err != kCGErrorSuccess)
682     {
683         WARN("failed to get Mac gamma table: %d\n", err);
684         goto done;
685     }
686
687     if (mac_entries == win_entries)
688     {
689         for (win_entry = 0; win_entry < win_entries; win_entry++)
690         {
691             r->red[win_entry]   = red[win_entry]   * 65535 + 0.5;
692             r->green[win_entry] = green[win_entry] * 65535 + 0.5;
693             r->blue[win_entry]  = blue[win_entry]  * 65535 + 0.5;
694         }
695     }
696     else
697     {
698         for (win_entry = 0; win_entry < win_entries; win_entry++)
699         {
700             double mac_pos = win_entry * (mac_entries - 1) / (double)(win_entries - 1);
701             int mac_entry = mac_pos;
702             double red_value, green_value, blue_value;
703
704             if (mac_entry == mac_entries - 1)
705             {
706                 red_value   = red[mac_entry];
707                 green_value = green[mac_entry];
708                 blue_value  = blue[mac_entry];
709             }
710             else
711             {
712                 double distance = mac_pos - mac_entry;
713
714                 red_value   = red[mac_entry]   * (1 - distance) + red[mac_entry + 1]   * distance;
715                 green_value = green[mac_entry] * (1 - distance) + green[mac_entry + 1] * distance;
716                 blue_value  = blue[mac_entry]  * (1 - distance) + blue[mac_entry + 1]  * distance;
717             }
718
719             r->red[win_entry]   = red_value   * 65535 + 0.5;
720             r->green[win_entry] = green_value * 65535 + 0.5;
721             r->blue[win_entry]  = blue_value  * 65535 + 0.5;
722         }
723     }
724
725     ret = TRUE;
726
727 done:
728     HeapFree(GetProcessHeap(), 0, red);
729     macdrv_free_displays(displays);
730     return ret;
731 }
732
733
734 /***********************************************************************
735  *              GetMonitorInfo  (MACDRV.@)
736  */
737 BOOL CDECL macdrv_GetMonitorInfo(HMONITOR monitor, LPMONITORINFO info)
738 {
739     static const WCHAR adapter_name[] = { '\\','\\','.','\\','D','I','S','P','L','A','Y','1',0 };
740     struct macdrv_display *displays;
741     int num_displays;
742     CGDirectDisplayID display_id;
743     int i;
744
745     TRACE("%p, %p\n", monitor, info);
746
747     if (macdrv_get_displays(&displays, &num_displays))
748     {
749         ERR("couldn't get display list\n");
750         SetLastError(ERROR_GEN_FAILURE);
751         return FALSE;
752     }
753
754     display_id = monitor_to_display_id(monitor);
755     for (i = 0; i < num_displays; i++)
756     {
757         if (displays[i].displayID == display_id)
758             break;
759     }
760
761     if (i < num_displays)
762     {
763         info->rcMonitor = rect_from_cgrect(displays[i].frame);
764         info->rcWork    = rect_from_cgrect(displays[i].work_frame);
765
766         info->dwFlags = (i == 0) ? MONITORINFOF_PRIMARY : 0;
767
768         if (info->cbSize >= sizeof(MONITORINFOEXW))
769             lstrcpyW(((MONITORINFOEXW*)info)->szDevice, adapter_name);
770
771         TRACE(" -> rcMonitor %s rcWork %s dwFlags %08x\n", wine_dbgstr_rect(&info->rcMonitor),
772               wine_dbgstr_rect(&info->rcWork), info->dwFlags);
773     }
774     else
775     {
776         ERR("invalid monitor handle\n");
777         SetLastError(ERROR_INVALID_HANDLE);
778     }
779
780     macdrv_free_displays(displays);
781     return (i < num_displays);
782 }
783
784
785 /***********************************************************************
786  *              SetDeviceGammaRamp (MACDRV.@)
787  */
788 BOOL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
789 {
790     DDGAMMARAMP *r = ramp;
791     struct macdrv_display *displays;
792     int num_displays;
793     int win_entries = sizeof(r->red) / sizeof(r->red[0]);
794     CGGammaValue *red, *green, *blue;
795     int i;
796     CGError err = kCGErrorFailure;
797
798     TRACE("dev %p ramp %p\n", dev, ramp);
799
800     if (macdrv_get_displays(&displays, &num_displays))
801     {
802         WARN("failed to get Mac displays\n");
803         return FALSE;
804     }
805
806     red = HeapAlloc(GetProcessHeap(), 0, win_entries * sizeof(red[0]) * 3);
807     if (!red)
808         goto done;
809     green = red + win_entries;
810     blue = green + win_entries;
811
812     for (i = 0; i < win_entries; i++)
813     {
814         red[i]      = r->red[i] / 65535.0;
815         green[i]    = r->green[i] / 65535.0;
816         blue[i]     = r->blue[i] / 65535.0;
817     }
818
819     err = CGSetDisplayTransferByTable(displays[0].displayID, win_entries, red, green, blue);
820     if (err != kCGErrorSuccess)
821         WARN("failed to set display gamma table: %d\n", err);
822
823 done:
824     HeapFree(GetProcessHeap(), 0, red);
825     macdrv_free_displays(displays);
826     return (err == kCGErrorSuccess);
827 }
828
829
830 /***********************************************************************
831  *              macdrv_displays_changed
832  *
833  * Handler for DISPLAYS_CHANGED events.
834  */
835 void macdrv_displays_changed(const macdrv_event *event)
836 {
837     HWND hwnd = GetDesktopWindow();
838
839     /* A system display change will get delivered to all GUI-attached threads,
840        so the desktop-window-owning thread will get it and all others should
841        ignore it.  A synthesized display change event due to activation
842        will only get delivered to the activated process.  So, it needs to
843        process it (by sending it to the desktop window). */
844     if (event->displays_changed.activating ||
845         GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId())
846     {
847         CGDirectDisplayID mainDisplay = CGMainDisplayID();
848         CGDisplayModeRef mode = CGDisplayCopyDisplayMode(mainDisplay);
849         size_t width = CGDisplayModeGetWidth(mode);
850         size_t height = CGDisplayModeGetHeight(mode);
851         int mode_bpp = display_mode_bits_per_pixel(mode);
852
853         CGDisplayModeRelease(mode);
854         SendMessageW(hwnd, WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
855                      MAKELPARAM(width, height));
856     }
857 }