reg/tests: Test case sensitivity when creating and deleting registry keys.
[wine.git] / dlls / winemac.drv / display.c
blob49892ca3ef28634f220d1f35e43971fd4fa282d3
1 /*
2 * MACDRV display settings
4 * Copyright 2003 Alexander James Pasadyn
5 * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
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.
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.
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
22 #include "config.h"
24 #include "macdrv.h"
25 #include "winuser.h"
26 #include "winreg.h"
27 #include "ddrawi.h"
28 #include "wine/unicode.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(display);
33 struct display_mode_descriptor
35 DWORD width;
36 DWORD height;
37 DWORD pixel_width;
38 DWORD pixel_height;
39 DWORD io_flags;
40 double refresh;
41 CFStringRef pixel_encoding;
45 BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW devmode, DWORD flags);
48 static const char initial_mode_key[] = "Initial Display Mode";
49 static const WCHAR pixelencodingW[] = {'P','i','x','e','l','E','n','c','o','d','i','n','g',0};
52 static CFArrayRef modes;
53 static BOOL modes_has_8bpp, modes_has_16bpp;
54 static int default_mode_bpp;
55 static CRITICAL_SECTION modes_section;
56 static CRITICAL_SECTION_DEBUG critsect_debug =
58 0, 0, &modes_section,
59 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
60 0, 0, { (DWORD_PTR)(__FILE__ ": modes_section") }
62 static CRITICAL_SECTION modes_section = { &critsect_debug, -1, 0, 0, 0, 0 };
64 static BOOL inited_original_display_mode;
67 static inline HMONITOR display_id_to_monitor(CGDirectDisplayID display_id)
69 return (HMONITOR)(UINT_PTR)display_id;
72 static inline CGDirectDisplayID monitor_to_display_id(HMONITOR handle)
74 return (CGDirectDisplayID)(UINT_PTR)handle;
78 static BOOL get_display_device_reg_key(char *key, unsigned len)
80 static const char display_device_guid_prop[] = "__wine_display_device_guid";
81 static const char video_path[] = "System\\CurrentControlSet\\Control\\Video\\{";
82 static const char display0[] = "}\\0000";
83 ATOM guid_atom;
85 assert(len >= sizeof(video_path) + sizeof(display0) + 40);
87 guid_atom = HandleToULong(GetPropA(GetDesktopWindow(), display_device_guid_prop));
88 if (!guid_atom) return FALSE;
90 memcpy(key, video_path, sizeof(video_path));
92 if (!GlobalGetAtomNameA(guid_atom, key + strlen(key), 40))
93 return FALSE;
95 strcat(key, display0);
97 TRACE("display device key %s\n", wine_dbgstr_a(key));
98 return TRUE;
102 static BOOL read_registry_settings(DEVMODEW *dm)
104 char wine_mac_reg_key[128];
105 HKEY hkey;
106 DWORD type, size;
107 BOOL ret = TRUE;
109 dm->dmFields = 0;
111 if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
112 return FALSE;
114 if (RegOpenKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, KEY_READ, &hkey))
115 return FALSE;
117 #define query_value(name, data) \
118 size = sizeof(DWORD); \
119 if (RegQueryValueExA(hkey, name, 0, &type, (LPBYTE)(data), &size) || \
120 type != REG_DWORD || size != sizeof(DWORD)) \
121 ret = FALSE
123 query_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
124 dm->dmFields |= DM_BITSPERPEL;
125 query_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
126 dm->dmFields |= DM_PELSWIDTH;
127 query_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
128 dm->dmFields |= DM_PELSHEIGHT;
129 query_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
130 dm->dmFields |= DM_DISPLAYFREQUENCY;
131 query_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
132 dm->dmFields |= DM_DISPLAYFLAGS;
133 query_value("DefaultSettings.XPanning", &dm->dmPosition.x);
134 query_value("DefaultSettings.YPanning", &dm->dmPosition.y);
135 query_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
136 query_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
138 #undef query_value
140 RegCloseKey(hkey);
141 return ret;
145 static BOOL write_registry_settings(const DEVMODEW *dm)
147 char wine_mac_reg_key[128];
148 HKEY hkey;
149 BOOL ret = TRUE;
151 if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
152 return FALSE;
154 if (RegCreateKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, NULL,
155 REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL))
156 return FALSE;
158 #define set_value(name, data) \
159 if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
160 ret = FALSE
162 set_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
163 set_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
164 set_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
165 set_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
166 set_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
167 set_value("DefaultSettings.XPanning", &dm->dmPosition.x);
168 set_value("DefaultSettings.YPanning", &dm->dmPosition.y);
169 set_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
170 set_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
172 #undef set_value
174 RegCloseKey(hkey);
175 return ret;
179 static BOOL write_display_settings(HKEY parent_hkey, CGDirectDisplayID displayID)
181 BOOL ret = FALSE;
182 char display_key_name[19];
183 HKEY display_hkey;
184 CGDisplayModeRef display_mode;
185 DWORD val;
186 CFStringRef pixel_encoding;
187 size_t len;
188 WCHAR* buf = NULL;
190 snprintf(display_key_name, sizeof(display_key_name), "Display 0x%08x", CGDisplayUnitNumber(displayID));
191 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
192 if (RegCreateKeyExA(parent_hkey, display_key_name, 0, NULL,
193 REG_OPTION_VOLATILE, KEY_WRITE, NULL, &display_hkey, NULL))
194 return FALSE;
196 display_mode = CGDisplayCopyDisplayMode(displayID);
197 if (!display_mode)
198 goto fail;
200 val = CGDisplayModeGetWidth(display_mode);
201 if (RegSetValueExA(display_hkey, "Width", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
202 goto fail;
203 val = CGDisplayModeGetHeight(display_mode);
204 if (RegSetValueExA(display_hkey, "Height", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
205 goto fail;
206 val = CGDisplayModeGetRefreshRate(display_mode) * 100;
207 if (RegSetValueExA(display_hkey, "RefreshRateTimes100", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
208 goto fail;
209 val = CGDisplayModeGetIOFlags(display_mode);
210 if (RegSetValueExA(display_hkey, "IOFlags", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
211 goto fail;
213 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
214 if (CGDisplayModeGetPixelWidth != NULL && CGDisplayModeGetPixelHeight != NULL)
216 val = CGDisplayModeGetPixelWidth(display_mode);
217 if (RegSetValueExA(display_hkey, "PixelWidth", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
218 goto fail;
219 val = CGDisplayModeGetPixelHeight(display_mode);
220 if (RegSetValueExA(display_hkey, "PixelHeight", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
221 goto fail;
223 #endif
225 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
226 len = CFStringGetLength(pixel_encoding);
227 buf = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
228 CFStringGetCharacters(pixel_encoding, CFRangeMake(0, len), (UniChar*)buf);
229 buf[len] = 0;
230 CFRelease(pixel_encoding);
231 if (RegSetValueExW(display_hkey, pixelencodingW, 0, REG_SZ, (const BYTE*)buf, (len + 1) * sizeof(WCHAR)))
232 goto fail;
234 ret = TRUE;
236 fail:
237 HeapFree(GetProcessHeap(), 0, buf);
238 if (display_mode) CGDisplayModeRelease(display_mode);
239 RegCloseKey(display_hkey);
240 if (!ret)
241 RegDeleteKeyA(parent_hkey, display_key_name);
242 return ret;
246 static void init_original_display_mode(void)
248 BOOL success = FALSE;
249 HKEY mac_driver_hkey, parent_hkey;
250 DWORD disposition;
251 struct macdrv_display *displays = NULL;
252 int num_displays, i;
254 if (inited_original_display_mode)
255 return;
257 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver */
258 if (RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Mac Driver", 0, NULL,
259 0, KEY_ALL_ACCESS, NULL, &mac_driver_hkey, NULL))
260 return;
262 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode */
263 if (RegCreateKeyExA(mac_driver_hkey, initial_mode_key, 0, NULL,
264 REG_OPTION_VOLATILE, KEY_WRITE, NULL, &parent_hkey, &disposition))
266 parent_hkey = NULL;
267 goto fail;
270 /* If we didn't create a new key, then it already existed. Something already stored
271 the initial display mode since Wine was started. We don't want to overwrite it. */
272 if (disposition != REG_CREATED_NEW_KEY)
273 goto done;
275 if (macdrv_get_displays(&displays, &num_displays))
276 goto fail;
278 for (i = 0; i < num_displays; i++)
280 if (!write_display_settings(parent_hkey, displays[i].displayID))
281 goto fail;
284 done:
285 success = TRUE;
287 fail:
288 macdrv_free_displays(displays);
289 RegCloseKey(parent_hkey);
290 if (!success && parent_hkey)
291 RegDeleteTreeA(mac_driver_hkey, initial_mode_key);
292 RegCloseKey(mac_driver_hkey);
293 if (success)
294 inited_original_display_mode = TRUE;
298 static BOOL read_dword(HKEY hkey, const char* name, DWORD* val)
300 DWORD type, size = sizeof(*val);
301 if (RegQueryValueExA(hkey, name, 0, &type, (BYTE*)val, &size) || type != REG_DWORD || size != sizeof(*val))
302 return FALSE;
303 return TRUE;
307 static void free_display_mode_descriptor(struct display_mode_descriptor* desc)
309 if (desc)
311 if (desc->pixel_encoding)
312 CFRelease(desc->pixel_encoding);
313 HeapFree(GetProcessHeap(), 0, desc);
318 static struct display_mode_descriptor* create_original_display_mode_descriptor(CGDirectDisplayID displayID)
320 static const char display_key_format[] = "Software\\Wine\\Mac Driver\\Initial Display Mode\\Display 0x%08x";
321 struct display_mode_descriptor* ret = NULL;
322 struct display_mode_descriptor* desc;
323 char display_key[sizeof(display_key_format) + 10];
324 HKEY hkey;
325 DWORD type, size;
326 DWORD refresh100;
327 WCHAR* pixel_encoding = NULL, *end;
329 init_original_display_mode();
331 snprintf(display_key, sizeof(display_key), display_key_format, CGDisplayUnitNumber(displayID));
332 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
333 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, display_key, 0, KEY_READ, &hkey))
334 return NULL;
336 desc = HeapAlloc(GetProcessHeap(), 0, sizeof(*desc));
337 desc->pixel_encoding = NULL;
339 if (!read_dword(hkey, "Width", &desc->width) ||
340 !read_dword(hkey, "Height", &desc->height) ||
341 !read_dword(hkey, "RefreshRateTimes100", &refresh100) ||
342 !read_dword(hkey, "IOFlags", &desc->io_flags))
343 goto done;
344 if (refresh100)
345 desc->refresh = refresh100 / 100.0;
346 else
347 desc->refresh = 60;
349 if (!read_dword(hkey, "PixelWidth", &desc->pixel_width) ||
350 !read_dword(hkey, "PixelHeight", &desc->pixel_height))
352 desc->pixel_width = desc->width;
353 desc->pixel_height = desc->height;
356 size = 0;
357 if (RegQueryValueExW(hkey, pixelencodingW, 0, &type, NULL, &size) || type != REG_SZ)
358 goto done;
359 pixel_encoding = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
360 if (RegQueryValueExW(hkey, pixelencodingW, 0, &type, (BYTE*)pixel_encoding, &size) || type != REG_SZ)
361 goto done;
362 if ((end = memchrW(pixel_encoding, 0, size)))
363 size = end - pixel_encoding;
364 desc->pixel_encoding = CFStringCreateWithCharacters(NULL, (const UniChar*)pixel_encoding, size);
366 ret = desc;
368 done:
369 if (!ret)
370 free_display_mode_descriptor(desc);
371 HeapFree(GetProcessHeap(), 0, pixel_encoding);
372 RegCloseKey(hkey);
373 return ret;
377 static BOOL display_mode_matches_descriptor(CGDisplayModeRef mode, const struct display_mode_descriptor* desc)
379 DWORD mode_io_flags;
380 double mode_refresh;
381 CFStringRef mode_pixel_encoding;
383 if (!desc)
384 return FALSE;
386 if (CGDisplayModeGetWidth(mode) != desc->width ||
387 CGDisplayModeGetHeight(mode) != desc->height)
388 return FALSE;
390 mode_io_flags = CGDisplayModeGetIOFlags(mode);
391 if ((desc->io_flags ^ mode_io_flags) & (kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeStretchedFlag |
392 kDisplayModeInterlacedFlag | kDisplayModeTelevisionFlag))
393 return FALSE;
395 mode_refresh = CGDisplayModeGetRefreshRate(mode);
396 if (!mode_refresh)
397 mode_refresh = 60;
398 if (fabs(desc->refresh - mode_refresh) > 0.1)
399 return FALSE;
401 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
402 if (CGDisplayModeGetPixelWidth != NULL && CGDisplayModeGetPixelHeight != NULL)
404 if (CGDisplayModeGetPixelWidth(mode) != desc->pixel_width ||
405 CGDisplayModeGetPixelHeight(mode) != desc->pixel_height)
406 return FALSE;
408 else
409 #endif
410 if (CGDisplayModeGetWidth(mode) != desc->pixel_width ||
411 CGDisplayModeGetHeight(mode) != desc->pixel_height)
412 return FALSE;
414 mode_pixel_encoding = CGDisplayModeCopyPixelEncoding(mode);
415 if (!CFEqual(mode_pixel_encoding, desc->pixel_encoding))
417 CFRelease(mode_pixel_encoding);
418 return FALSE;
420 CFRelease(mode_pixel_encoding);
422 return TRUE;
426 static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode)
428 CFStringRef pixel_encoding;
429 int bits_per_pixel = 0;
431 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
432 if (pixel_encoding)
434 if (CFEqual(pixel_encoding, CFSTR(kIO32BitFloatPixels)))
435 bits_per_pixel = 128;
436 else if (CFEqual(pixel_encoding, CFSTR(kIO16BitFloatPixels)))
437 bits_per_pixel = 64;
438 else if (CFEqual(pixel_encoding, CFSTR(kIO64BitDirectPixels)))
439 bits_per_pixel = 64;
440 else if (CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels)))
441 bits_per_pixel = 30;
442 else if (CFEqual(pixel_encoding, CFSTR(IO32BitDirectPixels)))
443 bits_per_pixel = 32;
444 else if (CFEqual(pixel_encoding, CFSTR(IO16BitDirectPixels)))
445 bits_per_pixel = 16;
446 else if (CFEqual(pixel_encoding, CFSTR(IO8BitIndexedPixels)))
447 bits_per_pixel = 8;
448 else if (CFEqual(pixel_encoding, CFSTR(IO4BitIndexedPixels)))
449 bits_per_pixel = 4;
450 else if (CFEqual(pixel_encoding, CFSTR(IO2BitIndexedPixels)))
451 bits_per_pixel = 2;
452 else if (CFEqual(pixel_encoding, CFSTR(IO1BitIndexedPixels)))
453 bits_per_pixel = 1;
455 CFRelease(pixel_encoding);
458 return bits_per_pixel;
462 static int get_default_bpp(void)
464 int ret;
466 EnterCriticalSection(&modes_section);
468 if (!default_mode_bpp)
470 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
471 if (mode)
473 default_mode_bpp = display_mode_bits_per_pixel(mode);
474 CFRelease(mode);
477 if (!default_mode_bpp)
478 default_mode_bpp = 32;
481 ret = default_mode_bpp;
483 LeaveCriticalSection(&modes_section);
485 TRACE(" -> %d\n", ret);
486 return ret;
490 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
491 static CFDictionaryRef create_mode_dict(CGDisplayModeRef display_mode, BOOL is_original)
493 CFDictionaryRef ret;
494 SInt32 io_flags = CGDisplayModeGetIOFlags(display_mode);
495 SInt64 width = CGDisplayModeGetWidth(display_mode);
496 SInt64 height = CGDisplayModeGetHeight(display_mode);
497 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
498 CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
499 CFNumberRef cf_io_flags, cf_width, cf_height, cf_refresh;
501 if (retina_enabled && is_original)
503 width *= 2;
504 height *= 2;
507 io_flags &= kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeInterlacedFlag |
508 kDisplayModeStretchedFlag | kDisplayModeTelevisionFlag;
509 cf_io_flags = CFNumberCreate(NULL, kCFNumberSInt32Type, &io_flags);
510 cf_width = CFNumberCreate(NULL, kCFNumberSInt64Type, &width);
511 cf_height = CFNumberCreate(NULL, kCFNumberSInt64Type, &height);
512 cf_refresh = CFNumberCreate(NULL, kCFNumberDoubleType, &refresh_rate);
515 static const CFStringRef keys[] = {
516 CFSTR("io_flags"),
517 CFSTR("width"),
518 CFSTR("height"),
519 CFSTR("pixel_encoding"),
520 CFSTR("refresh_rate"),
522 const void* values[sizeof(keys) / sizeof(keys[0])] = {
523 cf_io_flags,
524 cf_width,
525 cf_height,
526 pixel_encoding,
527 cf_refresh,
530 ret = CFDictionaryCreate(NULL, (const void**)keys, (const void**)values, sizeof(keys) / sizeof(keys[0]),
531 &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
534 CFRelease(pixel_encoding);
535 CFRelease(cf_io_flags);
536 CFRelease(cf_width);
537 CFRelease(cf_height);
538 CFRelease(cf_refresh);
540 return ret;
542 #endif
545 /***********************************************************************
546 * copy_display_modes
548 * Wrapper around CGDisplayCopyAllDisplayModes() to include additional
549 * modes on Retina-capable systems, but filter those which would confuse
550 * Windows apps (basically duplicates at different DPIs).
552 * For example, some Retina Macs support a 1920x1200 mode, but it's not
553 * returned from CGDisplayCopyAllDisplayModes() without special options.
554 * This is especially bad if that's the user's default mode, since then
555 * no "available" mode matches the initial settings.
557 static CFArrayRef copy_display_modes(CGDirectDisplayID display)
559 CFArrayRef modes = NULL;
561 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
562 if (&kCGDisplayShowDuplicateLowResolutionModes != NULL &&
563 CGDisplayModeGetPixelWidth != NULL && CGDisplayModeGetPixelHeight != NULL)
565 CFDictionaryRef options;
566 struct display_mode_descriptor* desc;
567 CFMutableDictionaryRef modes_by_size;
568 CFIndex i, count;
569 CGDisplayModeRef* mode_array;
571 options = CFDictionaryCreate(NULL, (const void**)&kCGDisplayShowDuplicateLowResolutionModes,
572 (const void**)&kCFBooleanTrue, 1, &kCFTypeDictionaryKeyCallBacks,
573 &kCFTypeDictionaryValueCallBacks);
575 modes = CGDisplayCopyAllDisplayModes(display, options);
576 if (options)
577 CFRelease(options);
578 if (!modes)
579 return NULL;
581 desc = create_original_display_mode_descriptor(display);
583 modes_by_size = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
584 count = CFArrayGetCount(modes);
585 for (i = 0; i < count; i++)
587 BOOL better = TRUE;
588 CGDisplayModeRef new_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
589 BOOL new_is_original = display_mode_matches_descriptor(new_mode, desc);
590 CFDictionaryRef key = create_mode_dict(new_mode, new_is_original);
592 /* If a given mode is the user's default, then always list it in preference to any similar
593 modes that may exist. */
594 if (new_is_original)
595 better = TRUE;
596 else
598 CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(new_mode);
599 CGDisplayModeRef old_mode;
601 if (pixel_encoding)
603 BOOL bpp30 = CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels));
604 CFRelease(pixel_encoding);
605 if (bpp30)
607 /* This is an odd pixel encoding. It seems it's only returned
608 when using kCGDisplayShowDuplicateLowResolutionModes. It's
609 32bpp in terms of the actual raster layout, but it's 10
610 bits per component. I think that no Windows program is
611 likely to need it and they will probably be confused by it.
612 Skip it. */
613 CFRelease(key);
614 continue;
618 old_mode = (CGDisplayModeRef)CFDictionaryGetValue(modes_by_size, key);
619 if (old_mode)
621 BOOL old_is_original = display_mode_matches_descriptor(old_mode, desc);
623 if (old_is_original)
624 better = FALSE;
625 else
627 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
628 is scaled. */
629 size_t width_points = CGDisplayModeGetWidth(new_mode);
630 size_t height_points = CGDisplayModeGetHeight(new_mode);
631 size_t new_width_pixels = CGDisplayModeGetPixelWidth(new_mode);
632 size_t new_height_pixels = CGDisplayModeGetPixelHeight(new_mode);
633 size_t old_width_pixels = CGDisplayModeGetPixelWidth(old_mode);
634 size_t old_height_pixels = CGDisplayModeGetPixelHeight(old_mode);
635 BOOL new_size_same = (new_width_pixels == width_points && new_height_pixels == height_points);
636 BOOL old_size_same = (old_width_pixels == width_points && old_height_pixels == height_points);
638 if (new_size_same && !old_size_same)
639 better = TRUE;
640 else if (!new_size_same && old_size_same)
641 better = FALSE;
642 else
644 /* Otherwise, prefer the mode with the smaller pixel size. */
645 if (old_width_pixels < new_width_pixels || old_height_pixels < new_height_pixels)
646 better = FALSE;
652 if (better)
653 CFDictionarySetValue(modes_by_size, key, new_mode);
655 CFRelease(key);
658 free_display_mode_descriptor(desc);
659 CFRelease(modes);
661 count = CFDictionaryGetCount(modes_by_size);
662 mode_array = HeapAlloc(GetProcessHeap(), 0, count * sizeof(mode_array[0]));
663 CFDictionaryGetKeysAndValues(modes_by_size, NULL, (const void **)mode_array);
664 modes = CFArrayCreate(NULL, (const void **)mode_array, count, &kCFTypeArrayCallBacks);
665 HeapFree(GetProcessHeap(), 0, mode_array);
666 CFRelease(modes_by_size);
668 else
669 #endif
670 modes = CGDisplayCopyAllDisplayModes(display, NULL);
672 return modes;
676 void check_retina_status(void)
678 if (retina_enabled)
680 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(kCGDirectMainDisplay);
681 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
682 BOOL new_value = display_mode_matches_descriptor(mode, desc);
684 CGDisplayModeRelease(mode);
685 free_display_mode_descriptor(desc);
687 if (new_value != retina_on)
688 macdrv_set_cocoa_retina_mode(new_value);
693 /***********************************************************************
694 * ChangeDisplaySettingsEx (MACDRV.@)
697 LONG CDECL macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
698 HWND hwnd, DWORD flags, LPVOID lpvoid)
700 LONG ret = DISP_CHANGE_BADMODE;
701 int bpp;
702 DEVMODEW dm;
703 BOOL def_mode = TRUE;
704 struct macdrv_display *displays;
705 int num_displays;
706 CFArrayRef display_modes;
707 struct display_mode_descriptor* desc;
708 CFIndex count, i, safe, best;
709 CGDisplayModeRef best_display_mode;
710 uint32_t best_io_flags;
711 BOOL best_is_original;
713 TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid);
715 init_original_display_mode();
717 if (devmode)
719 /* this is the minimal dmSize that XP accepts */
720 if (devmode->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
721 return DISP_CHANGE_FAILED;
723 if (devmode->dmSize >= FIELD_OFFSET(DEVMODEW, dmFields) + sizeof(devmode->dmFields))
725 if (((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel) ||
726 ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth) ||
727 ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight) ||
728 ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency))
729 def_mode = FALSE;
733 if (def_mode)
735 if (!macdrv_EnumDisplaySettingsEx(devname, ENUM_REGISTRY_SETTINGS, &dm, 0))
737 ERR("Default mode not found!\n");
738 return DISP_CHANGE_BADMODE;
741 TRACE("Return to original display mode\n");
742 devmode = &dm;
745 if ((devmode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
747 WARN("devmode doesn't specify the resolution: %04x\n", devmode->dmFields);
748 return DISP_CHANGE_BADMODE;
751 if (macdrv_get_displays(&displays, &num_displays))
752 return DISP_CHANGE_FAILED;
754 display_modes = copy_display_modes(displays[0].displayID);
755 if (!display_modes)
757 macdrv_free_displays(displays);
758 return DISP_CHANGE_FAILED;
761 bpp = get_default_bpp();
762 if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel != bpp)
763 TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, devmode->dmBitsPerPel);
765 TRACE("looking for %dx%dx%dbpp @%d Hz",
766 (devmode->dmFields & DM_PELSWIDTH ? devmode->dmPelsWidth : 0),
767 (devmode->dmFields & DM_PELSHEIGHT ? devmode->dmPelsHeight : 0),
768 bpp,
769 (devmode->dmFields & DM_DISPLAYFREQUENCY ? devmode->dmDisplayFrequency : 0));
770 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
771 TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un");
772 if (devmode->dmFields & DM_DISPLAYFLAGS)
773 TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
774 TRACE("\n");
776 desc = create_original_display_mode_descriptor(displays[0].displayID);
778 safe = -1;
779 best_display_mode = NULL;
780 count = CFArrayGetCount(display_modes);
781 for (i = 0; i < count; i++)
783 CGDisplayModeRef display_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(display_modes, i);
784 BOOL is_original = display_mode_matches_descriptor(display_mode, desc);
785 uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
786 int mode_bpp = display_mode_bits_per_pixel(display_mode);
787 size_t width = CGDisplayModeGetWidth(display_mode);
788 size_t height = CGDisplayModeGetHeight(display_mode);
790 if (is_original && retina_enabled)
792 width *= 2;
793 height *= 2;
796 if (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag))
797 continue;
799 safe++;
801 if (bpp != mode_bpp)
802 continue;
804 if (devmode->dmFields & DM_PELSWIDTH)
806 if (devmode->dmPelsWidth != width)
807 continue;
809 if (devmode->dmFields & DM_PELSHEIGHT)
811 if (devmode->dmPelsHeight != height)
812 continue;
814 if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != 0)
816 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
817 if (!refresh_rate)
818 refresh_rate = 60;
819 if (devmode->dmDisplayFrequency != (DWORD)refresh_rate)
820 continue;
822 if (devmode->dmFields & DM_DISPLAYFLAGS)
824 if (!(devmode->dmDisplayFlags & DM_INTERLACED) != !(io_flags & kDisplayModeInterlacedFlag))
825 continue;
827 else if (best_display_mode)
829 if (io_flags & kDisplayModeInterlacedFlag && !(best_io_flags & kDisplayModeInterlacedFlag))
830 continue;
831 else if (!(io_flags & kDisplayModeInterlacedFlag) && best_io_flags & kDisplayModeInterlacedFlag)
832 goto better;
834 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
836 if (!(devmode->dmDisplayFixedOutput == DMDFO_STRETCH) != !(io_flags & kDisplayModeStretchedFlag))
837 continue;
839 else if (best_display_mode)
841 if (io_flags & kDisplayModeStretchedFlag && !(best_io_flags & kDisplayModeStretchedFlag))
842 continue;
843 else if (!(io_flags & kDisplayModeStretchedFlag) && best_io_flags & kDisplayModeStretchedFlag)
844 goto better;
847 if (best_display_mode)
848 continue;
850 better:
851 best_display_mode = display_mode;
852 best = safe;
853 best_io_flags = io_flags;
854 best_is_original = is_original;
857 if (best_display_mode)
859 /* we have a valid mode */
860 TRACE("Requested display settings match mode %ld\n", best);
862 if ((flags & CDS_UPDATEREGISTRY) && !write_registry_settings(devmode))
864 WARN("Failed to update registry\n");
865 ret = DISP_CHANGE_NOTUPDATED;
867 else if (flags & (CDS_TEST | CDS_NORESET))
868 ret = DISP_CHANGE_SUCCESSFUL;
869 else if (macdrv_set_display_mode(&displays[0], best_display_mode))
871 int mode_bpp = display_mode_bits_per_pixel(best_display_mode);
872 size_t width = CGDisplayModeGetWidth(best_display_mode);
873 size_t height = CGDisplayModeGetHeight(best_display_mode);
875 if (best_is_original && retina_enabled)
877 width *= 2;
878 height *= 2;
881 SendMessageW(GetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
882 MAKELPARAM(width, height));
883 ret = DISP_CHANGE_SUCCESSFUL;
885 else
887 WARN("Failed to set display mode\n");
888 ret = DISP_CHANGE_FAILED;
891 else
893 /* no valid modes found */
894 ERR("No matching mode found %ux%ux%d @%u!\n", devmode->dmPelsWidth, devmode->dmPelsHeight,
895 bpp, devmode->dmDisplayFrequency);
898 free_display_mode_descriptor(desc);
899 CFRelease(display_modes);
900 macdrv_free_displays(displays);
902 return ret;
906 /***********************************************************************
907 * EnumDisplayMonitors (MACDRV.@)
909 BOOL CDECL macdrv_EnumDisplayMonitors(HDC hdc, LPRECT rect, MONITORENUMPROC proc, LPARAM lparam)
911 struct macdrv_display *displays;
912 int num_displays;
913 int i;
914 BOOL ret = TRUE;
916 TRACE("%p, %s, %p, %#lx\n", hdc, wine_dbgstr_rect(rect), proc, lparam);
918 if (hdc)
920 POINT origin;
921 RECT limit;
923 if (!GetDCOrgEx(hdc, &origin)) return FALSE;
924 if (GetClipBox(hdc, &limit) == ERROR) return FALSE;
926 if (rect && !IntersectRect(&limit, &limit, rect)) return TRUE;
928 if (macdrv_get_displays(&displays, &num_displays))
929 return FALSE;
931 for (i = 0; i < num_displays; i++)
933 RECT monrect = rect_from_cgrect(displays[i].frame);
934 OffsetRect(&monrect, -origin.x, -origin.y);
935 if (IntersectRect(&monrect, &monrect, &limit))
937 HMONITOR monitor = display_id_to_monitor(displays[i].displayID);
938 TRACE("monitor %d handle %p @ %s\n", i, monitor, wine_dbgstr_rect(&monrect));
939 if (!proc(monitor, hdc, &monrect, lparam))
941 ret = FALSE;
942 break;
947 else
949 if (macdrv_get_displays(&displays, &num_displays))
950 return FALSE;
952 for (i = 0; i < num_displays; i++)
954 RECT monrect = rect_from_cgrect(displays[i].frame);
955 RECT unused;
956 if (!rect || IntersectRect(&unused, &monrect, rect))
958 HMONITOR monitor = display_id_to_monitor(displays[i].displayID);
959 TRACE("monitor %d handle %p @ %s\n", i, monitor, wine_dbgstr_rect(&monrect));
960 if (!proc(monitor, 0, &monrect, lparam))
962 ret = FALSE;
963 break;
969 macdrv_free_displays(displays);
971 return ret;
975 /***********************************************************************
976 * EnumDisplaySettingsEx (MACDRV.@)
979 BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode,
980 LPDEVMODEW devmode, DWORD flags)
982 static const WCHAR dev_name[CCHDEVICENAME] =
983 { 'W','i','n','e',' ','M','a','c',' ','d','r','i','v','e','r',0 };
984 struct macdrv_display *displays = NULL;
985 int num_displays;
986 CGDisplayModeRef display_mode;
987 int display_mode_bpp;
988 BOOL synthesized = FALSE;
989 double rotation;
990 uint32_t io_flags;
992 TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname), mode, devmode, devmode->dmSize, flags);
994 init_original_display_mode();
996 memcpy(devmode->dmDeviceName, dev_name, sizeof(dev_name));
997 devmode->dmSpecVersion = DM_SPECVERSION;
998 devmode->dmDriverVersion = DM_SPECVERSION;
999 devmode->dmSize = FIELD_OFFSET(DEVMODEW, dmICMMethod);
1000 devmode->dmDriverExtra = 0;
1001 memset(&devmode->dmFields, 0, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields));
1003 if (mode == ENUM_REGISTRY_SETTINGS)
1005 TRACE("mode %d (registry) -- getting default mode\n", mode);
1006 return read_registry_settings(devmode);
1009 if (macdrv_get_displays(&displays, &num_displays))
1010 goto failed;
1012 if (mode == ENUM_CURRENT_SETTINGS)
1014 TRACE("mode %d (current) -- getting current mode\n", mode);
1015 display_mode = CGDisplayCopyDisplayMode(displays[0].displayID);
1016 display_mode_bpp = display_mode_bits_per_pixel(display_mode);
1018 else
1020 DWORD count, i;
1022 EnterCriticalSection(&modes_section);
1024 if (mode == 0 || !modes)
1026 if (modes) CFRelease(modes);
1027 modes = copy_display_modes(displays[0].displayID);
1028 modes_has_8bpp = modes_has_16bpp = FALSE;
1030 if (modes)
1032 count = CFArrayGetCount(modes);
1033 for (i = 0; i < count && !(modes_has_8bpp && modes_has_16bpp); i++)
1035 CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
1036 int bpp = display_mode_bits_per_pixel(mode);
1037 if (bpp == 8)
1038 modes_has_8bpp = TRUE;
1039 else if (bpp == 16)
1040 modes_has_16bpp = TRUE;
1045 display_mode = NULL;
1046 if (modes)
1048 int default_bpp = get_default_bpp();
1049 DWORD seen_modes = 0;
1051 count = CFArrayGetCount(modes);
1052 for (i = 0; i < count; i++)
1054 CGDisplayModeRef candidate = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
1056 io_flags = CGDisplayModeGetIOFlags(candidate);
1057 if (!(flags & EDS_RAWMODE) &&
1058 (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag)))
1059 continue;
1061 seen_modes++;
1062 if (seen_modes > mode)
1064 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1065 display_mode_bpp = display_mode_bits_per_pixel(display_mode);
1066 break;
1069 /* We only synthesize modes from those having the default bpp. */
1070 if (display_mode_bits_per_pixel(candidate) != default_bpp)
1071 continue;
1073 if (!modes_has_8bpp)
1075 seen_modes++;
1076 if (seen_modes > mode)
1078 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1079 display_mode_bpp = 8;
1080 synthesized = TRUE;
1081 break;
1085 if (!modes_has_16bpp)
1087 seen_modes++;
1088 if (seen_modes > mode)
1090 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1091 display_mode_bpp = 16;
1092 synthesized = TRUE;
1093 break;
1099 LeaveCriticalSection(&modes_section);
1102 if (!display_mode)
1103 goto failed;
1105 /* We currently only report modes for the primary display, so it's at (0, 0). */
1106 devmode->dmPosition.x = 0;
1107 devmode->dmPosition.y = 0;
1108 devmode->dmFields |= DM_POSITION;
1110 rotation = CGDisplayRotation(displays[0].displayID);
1111 devmode->dmDisplayOrientation = ((int)((rotation / 90) + 0.5)) % 4;
1112 devmode->dmFields |= DM_DISPLAYORIENTATION;
1114 io_flags = CGDisplayModeGetIOFlags(display_mode);
1115 if (io_flags & kDisplayModeStretchedFlag)
1116 devmode->dmDisplayFixedOutput = DMDFO_STRETCH;
1117 else
1118 devmode->dmDisplayFixedOutput = DMDFO_CENTER;
1119 devmode->dmFields |= DM_DISPLAYFIXEDOUTPUT;
1121 devmode->dmBitsPerPel = display_mode_bpp;
1122 if (devmode->dmBitsPerPel)
1123 devmode->dmFields |= DM_BITSPERPEL;
1125 devmode->dmPelsWidth = CGDisplayModeGetWidth(display_mode);
1126 devmode->dmPelsHeight = CGDisplayModeGetHeight(display_mode);
1127 if (retina_enabled)
1129 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(displays[0].displayID);
1130 if (display_mode_matches_descriptor(display_mode, desc))
1132 devmode->dmPelsWidth *= 2;
1133 devmode->dmPelsHeight *= 2;
1135 free_display_mode_descriptor(desc);
1137 devmode->dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
1139 devmode->dmDisplayFlags = 0;
1140 if (io_flags & kDisplayModeInterlacedFlag)
1141 devmode->dmDisplayFlags |= DM_INTERLACED;
1142 devmode->dmFields |= DM_DISPLAYFLAGS;
1144 devmode->dmDisplayFrequency = CGDisplayModeGetRefreshRate(display_mode);
1145 if (!devmode->dmDisplayFrequency)
1146 devmode->dmDisplayFrequency = 60;
1147 devmode->dmFields |= DM_DISPLAYFREQUENCY;
1149 CFRelease(display_mode);
1150 macdrv_free_displays(displays);
1152 TRACE("mode %d -- %dx%dx%dbpp @%d Hz", mode,
1153 devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel,
1154 devmode->dmDisplayFrequency);
1155 if (devmode->dmDisplayOrientation)
1156 TRACE(" rotated %u degrees", devmode->dmDisplayOrientation * 90);
1157 if (devmode->dmDisplayFixedOutput == DMDFO_STRETCH)
1158 TRACE(" stretched");
1159 if (devmode->dmDisplayFlags & DM_INTERLACED)
1160 TRACE(" interlaced");
1161 if (synthesized)
1162 TRACE(" (synthesized)");
1163 TRACE("\n");
1165 return TRUE;
1167 failed:
1168 TRACE("mode %d -- not present\n", mode);
1169 if (displays) macdrv_free_displays(displays);
1170 SetLastError(ERROR_NO_MORE_FILES);
1171 return FALSE;
1175 /***********************************************************************
1176 * GetDeviceGammaRamp (MACDRV.@)
1178 BOOL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1180 BOOL ret = FALSE;
1181 DDGAMMARAMP *r = ramp;
1182 struct macdrv_display *displays;
1183 int num_displays;
1184 uint32_t mac_entries;
1185 int win_entries = sizeof(r->red) / sizeof(r->red[0]);
1186 CGGammaValue *red, *green, *blue;
1187 CGError err;
1188 int win_entry;
1190 TRACE("dev %p ramp %p\n", dev, ramp);
1192 if (macdrv_get_displays(&displays, &num_displays))
1194 WARN("failed to get Mac displays\n");
1195 return FALSE;
1198 mac_entries = CGDisplayGammaTableCapacity(displays[0].displayID);
1199 red = HeapAlloc(GetProcessHeap(), 0, mac_entries * sizeof(red[0]) * 3);
1200 if (!red)
1201 goto done;
1202 green = red + mac_entries;
1203 blue = green + mac_entries;
1205 err = CGGetDisplayTransferByTable(displays[0].displayID, mac_entries, red, green,
1206 blue, &mac_entries);
1207 if (err != kCGErrorSuccess)
1209 WARN("failed to get Mac gamma table: %d\n", err);
1210 goto done;
1213 if (mac_entries == win_entries)
1215 for (win_entry = 0; win_entry < win_entries; win_entry++)
1217 r->red[win_entry] = red[win_entry] * 65535 + 0.5;
1218 r->green[win_entry] = green[win_entry] * 65535 + 0.5;
1219 r->blue[win_entry] = blue[win_entry] * 65535 + 0.5;
1222 else
1224 for (win_entry = 0; win_entry < win_entries; win_entry++)
1226 double mac_pos = win_entry * (mac_entries - 1) / (double)(win_entries - 1);
1227 int mac_entry = mac_pos;
1228 double red_value, green_value, blue_value;
1230 if (mac_entry == mac_entries - 1)
1232 red_value = red[mac_entry];
1233 green_value = green[mac_entry];
1234 blue_value = blue[mac_entry];
1236 else
1238 double distance = mac_pos - mac_entry;
1240 red_value = red[mac_entry] * (1 - distance) + red[mac_entry + 1] * distance;
1241 green_value = green[mac_entry] * (1 - distance) + green[mac_entry + 1] * distance;
1242 blue_value = blue[mac_entry] * (1 - distance) + blue[mac_entry + 1] * distance;
1245 r->red[win_entry] = red_value * 65535 + 0.5;
1246 r->green[win_entry] = green_value * 65535 + 0.5;
1247 r->blue[win_entry] = blue_value * 65535 + 0.5;
1251 ret = TRUE;
1253 done:
1254 HeapFree(GetProcessHeap(), 0, red);
1255 macdrv_free_displays(displays);
1256 return ret;
1260 /***********************************************************************
1261 * GetMonitorInfo (MACDRV.@)
1263 BOOL CDECL macdrv_GetMonitorInfo(HMONITOR monitor, LPMONITORINFO info)
1265 static const WCHAR adapter_name[] = { '\\','\\','.','\\','D','I','S','P','L','A','Y','1',0 };
1266 struct macdrv_display *displays;
1267 int num_displays;
1268 CGDirectDisplayID display_id;
1269 int i;
1271 TRACE("%p, %p\n", monitor, info);
1273 if (macdrv_get_displays(&displays, &num_displays))
1275 ERR("couldn't get display list\n");
1276 SetLastError(ERROR_GEN_FAILURE);
1277 return FALSE;
1280 display_id = monitor_to_display_id(monitor);
1281 for (i = 0; i < num_displays; i++)
1283 if (displays[i].displayID == display_id)
1284 break;
1287 if (i < num_displays)
1289 info->rcMonitor = rect_from_cgrect(displays[i].frame);
1290 info->rcWork = rect_from_cgrect(displays[i].work_frame);
1292 info->dwFlags = (i == 0) ? MONITORINFOF_PRIMARY : 0;
1294 if (info->cbSize >= sizeof(MONITORINFOEXW))
1295 lstrcpyW(((MONITORINFOEXW*)info)->szDevice, adapter_name);
1297 TRACE(" -> rcMonitor %s rcWork %s dwFlags %08x\n", wine_dbgstr_rect(&info->rcMonitor),
1298 wine_dbgstr_rect(&info->rcWork), info->dwFlags);
1300 else
1302 ERR("invalid monitor handle\n");
1303 SetLastError(ERROR_INVALID_HANDLE);
1306 macdrv_free_displays(displays);
1307 return (i < num_displays);
1311 /***********************************************************************
1312 * SetDeviceGammaRamp (MACDRV.@)
1314 BOOL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1316 DDGAMMARAMP *r = ramp;
1317 struct macdrv_display *displays;
1318 int num_displays;
1319 int win_entries = sizeof(r->red) / sizeof(r->red[0]);
1320 CGGammaValue *red, *green, *blue;
1321 int i;
1322 CGError err = kCGErrorFailure;
1324 TRACE("dev %p ramp %p\n", dev, ramp);
1326 if (!allow_set_gamma)
1328 TRACE("disallowed by registry setting\n");
1329 return FALSE;
1332 if (macdrv_get_displays(&displays, &num_displays))
1334 WARN("failed to get Mac displays\n");
1335 return FALSE;
1338 red = HeapAlloc(GetProcessHeap(), 0, win_entries * sizeof(red[0]) * 3);
1339 if (!red)
1340 goto done;
1341 green = red + win_entries;
1342 blue = green + win_entries;
1344 for (i = 0; i < win_entries; i++)
1346 red[i] = r->red[i] / 65535.0;
1347 green[i] = r->green[i] / 65535.0;
1348 blue[i] = r->blue[i] / 65535.0;
1351 err = CGSetDisplayTransferByTable(displays[0].displayID, win_entries, red, green, blue);
1352 if (err != kCGErrorSuccess)
1353 WARN("failed to set display gamma table: %d\n", err);
1355 done:
1356 HeapFree(GetProcessHeap(), 0, red);
1357 macdrv_free_displays(displays);
1358 return (err == kCGErrorSuccess);
1362 /***********************************************************************
1363 * macdrv_displays_changed
1365 * Handler for DISPLAYS_CHANGED events.
1367 void macdrv_displays_changed(const macdrv_event *event)
1369 HWND hwnd = GetDesktopWindow();
1371 /* A system display change will get delivered to all GUI-attached threads,
1372 so the desktop-window-owning thread will get it and all others should
1373 ignore it. A synthesized display change event due to activation
1374 will only get delivered to the activated process. So, it needs to
1375 process it (by sending it to the desktop window). */
1376 if (event->displays_changed.activating ||
1377 GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId())
1379 CGDirectDisplayID mainDisplay = CGMainDisplayID();
1380 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(mainDisplay);
1381 size_t width = CGDisplayModeGetWidth(mode);
1382 size_t height = CGDisplayModeGetHeight(mode);
1383 int mode_bpp = display_mode_bits_per_pixel(mode);
1384 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(mainDisplay);
1385 BOOL is_original = display_mode_matches_descriptor(mode, desc);
1387 free_display_mode_descriptor(desc);
1388 CGDisplayModeRelease(mode);
1390 if (is_original && retina_enabled)
1392 width *= 2;
1393 height *= 2;
1396 SendMessageW(hwnd, WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
1397 MAKELPARAM(width, height));