winemac: Use RtlSetLastWin32Error.
[wine.git] / dlls / winemac.drv / display.c
blob2d7b96561033bd2a962ff9734d54b238c8a13da8
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 #if 0
23 #pragma makedep unix
24 #endif
26 #include "config.h"
28 #include "macdrv.h"
29 #include "winuser.h"
30 #include "winreg.h"
31 #include "ddrawi.h"
32 #define WIN32_NO_STATUS
33 #include "winternl.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(display);
38 struct display_mode_descriptor
40 DWORD width;
41 DWORD height;
42 DWORD pixel_width;
43 DWORD pixel_height;
44 DWORD io_flags;
45 double refresh;
46 CFStringRef pixel_encoding;
50 BOOL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW devmode, DWORD flags);
52 static const WCHAR initial_mode_keyW[] = {'I','n','i','t','i','a','l',' ','D','i','s','p','l','a','y',
53 ' ','M','o','d','e'};
54 static const WCHAR pixelencodingW[] = {'P','i','x','e','l','E','n','c','o','d','i','n','g',0};
56 static CFArrayRef modes;
57 static BOOL modes_has_8bpp, modes_has_16bpp;
58 static int default_mode_bpp;
59 static pthread_mutex_t modes_mutex = PTHREAD_MUTEX_INITIALIZER;
61 static BOOL inited_original_display_mode;
64 static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode)
66 CFStringRef pixel_encoding;
67 int bits_per_pixel = 0;
69 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
70 if (pixel_encoding)
72 if (CFEqual(pixel_encoding, CFSTR(kIO32BitFloatPixels)))
73 bits_per_pixel = 128;
74 else if (CFEqual(pixel_encoding, CFSTR(kIO16BitFloatPixels)))
75 bits_per_pixel = 64;
76 else if (CFEqual(pixel_encoding, CFSTR(kIO64BitDirectPixels)))
77 bits_per_pixel = 64;
78 else if (CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels)))
79 bits_per_pixel = 30;
80 else if (CFEqual(pixel_encoding, CFSTR(IO32BitDirectPixels)))
81 bits_per_pixel = 32;
82 else if (CFEqual(pixel_encoding, CFSTR(IO16BitDirectPixels)))
83 bits_per_pixel = 16;
84 else if (CFEqual(pixel_encoding, CFSTR(IO8BitIndexedPixels)))
85 bits_per_pixel = 8;
86 else if (CFEqual(pixel_encoding, CFSTR(IO4BitIndexedPixels)))
87 bits_per_pixel = 4;
88 else if (CFEqual(pixel_encoding, CFSTR(IO2BitIndexedPixels)))
89 bits_per_pixel = 2;
90 else if (CFEqual(pixel_encoding, CFSTR(IO1BitIndexedPixels)))
91 bits_per_pixel = 1;
93 CFRelease(pixel_encoding);
96 return bits_per_pixel;
100 static void display_mode_to_devmode(CGDirectDisplayID display_id, CGDisplayModeRef display_mode, DEVMODEW *devmode)
102 uint32_t io_flags;
103 double rotation;
105 rotation = CGDisplayRotation(display_id);
106 devmode->dmDisplayOrientation = ((int)((rotation / 90) + 0.5)) % 4;
107 devmode->dmFields |= DM_DISPLAYORIENTATION;
109 io_flags = CGDisplayModeGetIOFlags(display_mode);
110 if (io_flags & kDisplayModeStretchedFlag)
111 devmode->dmDisplayFixedOutput = DMDFO_STRETCH;
112 else
113 devmode->dmDisplayFixedOutput = DMDFO_CENTER;
114 devmode->dmFields |= DM_DISPLAYFIXEDOUTPUT;
116 devmode->dmBitsPerPel = display_mode_bits_per_pixel(display_mode);
117 if (devmode->dmBitsPerPel)
118 devmode->dmFields |= DM_BITSPERPEL;
120 devmode->dmPelsWidth = CGDisplayModeGetWidth(display_mode);
121 devmode->dmPelsHeight = CGDisplayModeGetHeight(display_mode);
122 devmode->dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
124 devmode->dmDisplayFlags = 0;
125 if (io_flags & kDisplayModeInterlacedFlag)
126 devmode->dmDisplayFlags |= DM_INTERLACED;
127 devmode->dmFields |= DM_DISPLAYFLAGS;
129 devmode->dmDisplayFrequency = CGDisplayModeGetRefreshRate(display_mode);
130 if (!devmode->dmDisplayFrequency)
131 devmode->dmDisplayFrequency = 60;
132 devmode->dmFields |= DM_DISPLAYFREQUENCY;
136 static BOOL set_setting_value(HKEY hkey, const char *name, DWORD val)
138 WCHAR nameW[128];
139 UNICODE_STRING str = { asciiz_to_unicode(nameW, name) - sizeof(WCHAR), sizeof(nameW), nameW };
140 return !NtSetValueKey(hkey, &str, 0, REG_DWORD, &val, sizeof(val));
144 static BOOL write_display_settings(HKEY parent_hkey, CGDirectDisplayID displayID)
146 BOOL ret = FALSE;
147 char display_key_name[19];
148 HKEY display_hkey;
149 CGDisplayModeRef display_mode;
150 UNICODE_STRING str;
151 DWORD val;
152 CFStringRef pixel_encoding;
153 size_t len;
154 WCHAR* buf = NULL;
156 snprintf(display_key_name, sizeof(display_key_name), "Display 0x%08x", CGDisplayUnitNumber(displayID));
157 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
158 if (!(display_hkey = reg_create_ascii_key(parent_hkey, display_key_name, REG_OPTION_VOLATILE, NULL)))
159 return FALSE;
161 display_mode = CGDisplayCopyDisplayMode(displayID);
162 if (!display_mode)
163 goto fail;
165 val = CGDisplayModeGetWidth(display_mode);
166 if (!set_setting_value(display_hkey, "Width", val))
167 goto fail;
168 val = CGDisplayModeGetHeight(display_mode);
169 if (!set_setting_value(display_hkey, "Height", val))
170 goto fail;
171 val = CGDisplayModeGetRefreshRate(display_mode) * 100;
172 if (!set_setting_value(display_hkey, "RefreshRateTimes100", val))
173 goto fail;
174 val = CGDisplayModeGetIOFlags(display_mode);
175 if (!set_setting_value(display_hkey, "IOFlags", val))
176 goto fail;
178 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
179 if (&CGDisplayModeGetPixelWidth != NULL && &CGDisplayModeGetPixelHeight != NULL)
181 val = CGDisplayModeGetPixelWidth(display_mode);
182 if (!set_setting_value(display_hkey, "PixelWidth", val))
183 goto fail;
184 val = CGDisplayModeGetPixelHeight(display_mode);
185 if (!set_setting_value(display_hkey, "PixelHeight", val))
186 goto fail;
188 #endif
190 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
191 len = CFStringGetLength(pixel_encoding);
192 buf = malloc((len + 1) * sizeof(WCHAR));
193 CFStringGetCharacters(pixel_encoding, CFRangeMake(0, len), (UniChar*)buf);
194 buf[len] = 0;
195 CFRelease(pixel_encoding);
196 RtlInitUnicodeString(&str, pixelencodingW);
197 if (NtSetValueKey(display_hkey, &str, 0, REG_SZ, (const BYTE*)buf, (len + 1) * sizeof(WCHAR)))
198 goto fail;
200 ret = TRUE;
202 fail:
203 free(buf);
204 if (display_mode) CGDisplayModeRelease(display_mode);
205 NtClose(display_hkey);
206 if (!ret)
208 WCHAR nameW[64];
209 reg_delete_tree(parent_hkey, nameW, asciiz_to_unicode(nameW, display_key_name) - sizeof(WCHAR));
211 return ret;
215 static void init_original_display_mode(void)
217 BOOL success = FALSE;
218 HKEY mac_driver_hkey, parent_hkey;
219 DWORD disposition;
220 struct macdrv_display *displays = NULL;
221 int num_displays, i;
223 if (inited_original_display_mode)
224 return;
226 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver */
227 mac_driver_hkey = reg_create_ascii_key(NULL, "\\Registry\\Machine\\Software\\Wine\\Mac Driver",
228 0, NULL);
229 if (!mac_driver_hkey)
230 return;
232 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode */
233 if (!(parent_hkey = reg_create_key(mac_driver_hkey, initial_mode_keyW, sizeof(initial_mode_keyW),
234 REG_OPTION_VOLATILE, &disposition)))
236 parent_hkey = NULL;
237 goto fail;
240 /* If we didn't create a new key, then it already existed. Something already stored
241 the initial display mode since Wine was started. We don't want to overwrite it. */
242 if (disposition != REG_CREATED_NEW_KEY)
243 goto done;
245 if (macdrv_get_displays(&displays, &num_displays))
246 goto fail;
248 for (i = 0; i < num_displays; i++)
250 if (!write_display_settings(parent_hkey, displays[i].displayID))
251 goto fail;
254 done:
255 success = TRUE;
257 fail:
258 macdrv_free_displays(displays);
259 NtClose(parent_hkey);
260 if (!success && parent_hkey)
261 reg_delete_tree(mac_driver_hkey, initial_mode_keyW, sizeof(initial_mode_keyW));
262 NtClose(mac_driver_hkey);
263 if (success)
264 inited_original_display_mode = TRUE;
268 static BOOL read_dword(HKEY hkey, const char* name, DWORD* val)
270 char buffer[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(*val)])];
271 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
272 WCHAR nameW[64];
273 asciiz_to_unicode(nameW, name);
274 if (query_reg_value(hkey, nameW, value, sizeof(buffer)) != sizeof(*val) || value->Type != REG_DWORD)
275 return FALSE;
276 *val = *(DWORD *)value->Data;
277 return TRUE;
281 static void free_display_mode_descriptor(struct display_mode_descriptor* desc)
283 if (desc)
285 if (desc->pixel_encoding)
286 CFRelease(desc->pixel_encoding);
287 free(desc);
292 static struct display_mode_descriptor* create_original_display_mode_descriptor(CGDirectDisplayID displayID)
294 static const char display_key_format[] =
295 "\\Registry\\Machine\\Software\\Wine\\Mac Driver\\Initial Display Mode\\Display 0x%08x";
296 struct display_mode_descriptor* ret = NULL;
297 struct display_mode_descriptor* desc;
298 char display_key[sizeof(display_key_format) + 10];
299 WCHAR nameW[ARRAYSIZE(display_key)];
300 char buffer[4096];
301 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
302 HKEY hkey;
303 DWORD refresh100;
305 init_original_display_mode();
307 snprintf(display_key, sizeof(display_key), display_key_format, CGDisplayUnitNumber(displayID));
308 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
309 if (!(hkey = reg_open_key(NULL, nameW, asciiz_to_unicode(nameW, display_key))))
310 return NULL;
312 desc = malloc(sizeof(*desc));
313 desc->pixel_encoding = NULL;
315 if (!read_dword(hkey, "Width", &desc->width) ||
316 !read_dword(hkey, "Height", &desc->height) ||
317 !read_dword(hkey, "RefreshRateTimes100", &refresh100) ||
318 !read_dword(hkey, "IOFlags", &desc->io_flags))
319 goto done;
320 if (refresh100)
321 desc->refresh = refresh100 / 100.0;
322 else
323 desc->refresh = 60;
325 if (!read_dword(hkey, "PixelWidth", &desc->pixel_width) ||
326 !read_dword(hkey, "PixelHeight", &desc->pixel_height))
328 desc->pixel_width = desc->width;
329 desc->pixel_height = desc->height;
332 if (!query_reg_value(hkey, pixelencodingW, value, sizeof(buffer)) || value->Type != REG_SZ)
333 goto done;
334 desc->pixel_encoding = CFStringCreateWithCharacters(NULL, (const UniChar*)value->Data,
335 lstrlenW((const WCHAR *)value->Data));
337 ret = desc;
339 done:
340 if (!ret)
341 free_display_mode_descriptor(desc);
342 NtClose(hkey);
343 return ret;
347 static BOOL display_mode_matches_descriptor(CGDisplayModeRef mode, const struct display_mode_descriptor* desc)
349 DWORD mode_io_flags;
350 double mode_refresh;
351 CFStringRef mode_pixel_encoding;
353 if (!desc)
354 return FALSE;
356 if (CGDisplayModeGetWidth(mode) != desc->width ||
357 CGDisplayModeGetHeight(mode) != desc->height)
358 return FALSE;
360 mode_io_flags = CGDisplayModeGetIOFlags(mode);
361 if ((desc->io_flags ^ mode_io_flags) & (kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeStretchedFlag |
362 kDisplayModeInterlacedFlag | kDisplayModeTelevisionFlag))
363 return FALSE;
365 mode_refresh = CGDisplayModeGetRefreshRate(mode);
366 if (!mode_refresh)
367 mode_refresh = 60;
368 if (fabs(desc->refresh - mode_refresh) > 0.1)
369 return FALSE;
371 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
372 if (&CGDisplayModeGetPixelWidth != NULL && &CGDisplayModeGetPixelHeight != NULL)
374 if (CGDisplayModeGetPixelWidth(mode) != desc->pixel_width ||
375 CGDisplayModeGetPixelHeight(mode) != desc->pixel_height)
376 return FALSE;
378 else
379 #endif
380 if (CGDisplayModeGetWidth(mode) != desc->pixel_width ||
381 CGDisplayModeGetHeight(mode) != desc->pixel_height)
382 return FALSE;
384 mode_pixel_encoding = CGDisplayModeCopyPixelEncoding(mode);
385 if (!CFEqual(mode_pixel_encoding, desc->pixel_encoding))
387 CFRelease(mode_pixel_encoding);
388 return FALSE;
390 CFRelease(mode_pixel_encoding);
392 return TRUE;
396 static int get_default_bpp(void)
398 int ret;
400 if (!default_mode_bpp)
402 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
403 if (mode)
405 default_mode_bpp = display_mode_bits_per_pixel(mode);
406 CFRelease(mode);
409 if (!default_mode_bpp)
410 default_mode_bpp = 32;
413 ret = default_mode_bpp;
415 TRACE(" -> %d\n", ret);
416 return ret;
420 static BOOL display_mode_is_supported(CGDisplayModeRef display_mode)
422 uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
423 return (io_flags & kDisplayModeValidFlag) && (io_flags & kDisplayModeSafeFlag);
427 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
428 static CFDictionaryRef create_mode_dict(CGDisplayModeRef display_mode, BOOL is_original)
430 CFDictionaryRef ret;
431 SInt32 io_flags = CGDisplayModeGetIOFlags(display_mode);
432 SInt64 width = CGDisplayModeGetWidth(display_mode);
433 SInt64 height = CGDisplayModeGetHeight(display_mode);
434 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
435 CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
436 CFNumberRef cf_io_flags, cf_width, cf_height, cf_refresh;
438 if (retina_enabled && is_original)
440 width *= 2;
441 height *= 2;
444 io_flags &= kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeInterlacedFlag |
445 kDisplayModeStretchedFlag | kDisplayModeTelevisionFlag;
446 cf_io_flags = CFNumberCreate(NULL, kCFNumberSInt32Type, &io_flags);
447 cf_width = CFNumberCreate(NULL, kCFNumberSInt64Type, &width);
448 cf_height = CFNumberCreate(NULL, kCFNumberSInt64Type, &height);
449 cf_refresh = CFNumberCreate(NULL, kCFNumberDoubleType, &refresh_rate);
452 static const CFStringRef keys[] = {
453 CFSTR("io_flags"),
454 CFSTR("width"),
455 CFSTR("height"),
456 CFSTR("pixel_encoding"),
457 CFSTR("refresh_rate"),
459 const void* values[ARRAY_SIZE(keys)] = {
460 cf_io_flags,
461 cf_width,
462 cf_height,
463 pixel_encoding,
464 cf_refresh,
467 ret = CFDictionaryCreate(NULL, (const void**)keys, (const void**)values, ARRAY_SIZE(keys),
468 &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
471 CFRelease(pixel_encoding);
472 CFRelease(cf_io_flags);
473 CFRelease(cf_width);
474 CFRelease(cf_height);
475 CFRelease(cf_refresh);
477 return ret;
481 static BOOL mode_is_preferred(CGDisplayModeRef new_mode, CGDisplayModeRef old_mode,
482 struct display_mode_descriptor *original_mode_desc,
483 BOOL include_unsupported)
485 BOOL new_is_supported;
486 CFStringRef pixel_encoding;
487 size_t width_points, height_points;
488 size_t old_width_pixels, old_height_pixels, new_width_pixels, new_height_pixels;
489 BOOL old_size_same, new_size_same;
491 /* If a given mode is the user's default, then always list it in preference to any similar
492 modes that may exist. */
493 if (display_mode_matches_descriptor(new_mode, original_mode_desc))
494 return TRUE;
496 /* Skip unsupported modes unless told to do otherwise. */
497 new_is_supported = display_mode_is_supported(new_mode);
498 if (!new_is_supported && !include_unsupported)
499 return FALSE;
501 pixel_encoding = CGDisplayModeCopyPixelEncoding(new_mode);
502 if (pixel_encoding)
504 BOOL bpp30 = CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels));
505 CFRelease(pixel_encoding);
506 if (bpp30)
508 /* This is an odd pixel encoding. It seems it's only returned
509 when using kCGDisplayShowDuplicateLowResolutionModes. It's
510 32bpp in terms of the actual raster layout, but it's 10
511 bits per component. I think that no Windows program is
512 likely to need it and they will probably be confused by it.
513 Skip it. */
514 return FALSE;
518 if (!old_mode)
519 return TRUE;
521 /* Prefer the original mode over any similar mode. */
522 if (display_mode_matches_descriptor(old_mode, original_mode_desc))
523 return FALSE;
525 /* Prefer supported modes over similar unsupported ones. */
526 if (!new_is_supported && display_mode_is_supported(old_mode))
527 return FALSE;
529 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
530 is scaled. */
531 width_points = CGDisplayModeGetWidth(new_mode);
532 height_points = CGDisplayModeGetHeight(new_mode);
533 new_width_pixels = CGDisplayModeGetPixelWidth(new_mode);
534 new_height_pixels = CGDisplayModeGetPixelHeight(new_mode);
535 old_width_pixels = CGDisplayModeGetPixelWidth(old_mode);
536 old_height_pixels = CGDisplayModeGetPixelHeight(old_mode);
537 new_size_same = (new_width_pixels == width_points && new_height_pixels == height_points);
538 old_size_same = (old_width_pixels == width_points && old_height_pixels == height_points);
540 if (new_size_same && !old_size_same)
541 return TRUE;
543 if (!new_size_same && old_size_same)
544 return FALSE;
546 /* Otherwise, prefer the mode with the smaller pixel size. */
547 return new_width_pixels < old_width_pixels && new_height_pixels < old_height_pixels;
549 #endif
552 static CFComparisonResult mode_compare(const void *p1, const void *p2, void *context)
554 CGDisplayModeRef a = (CGDisplayModeRef)p1, b = (CGDisplayModeRef)p2;
555 size_t a_val, b_val;
556 double a_refresh_rate, b_refresh_rate;
558 /* Sort by bpp descending, */
559 a_val = display_mode_bits_per_pixel(a);
560 b_val = display_mode_bits_per_pixel(b);
561 if (a_val < b_val)
562 return kCFCompareGreaterThan;
563 else if (a_val > b_val)
564 return kCFCompareLessThan;
566 /* then width ascending, */
567 a_val = CGDisplayModeGetWidth(a);
568 b_val = CGDisplayModeGetWidth(b);
569 if (a_val < b_val)
570 return kCFCompareLessThan;
571 else if (a_val > b_val)
572 return kCFCompareGreaterThan;
574 /* then height ascending, */
575 a_val = CGDisplayModeGetHeight(a);
576 b_val = CGDisplayModeGetHeight(b);
577 if (a_val < b_val)
578 return kCFCompareLessThan;
579 else if (a_val > b_val)
580 return kCFCompareGreaterThan;
582 /* then refresh rate descending. */
583 a_refresh_rate = CGDisplayModeGetRefreshRate(a);
584 b_refresh_rate = CGDisplayModeGetRefreshRate(b);
585 if (a_refresh_rate < b_refresh_rate)
586 return kCFCompareGreaterThan;
587 else if (a_refresh_rate > b_refresh_rate)
588 return kCFCompareLessThan;
590 return kCFCompareEqualTo;
594 /***********************************************************************
595 * copy_display_modes
597 * Wrapper around CGDisplayCopyAllDisplayModes() to include additional
598 * modes on Retina-capable systems, but filter those which would confuse
599 * Windows apps (basically duplicates at different DPIs).
601 * For example, some Retina Macs support a 1920x1200 mode, but it's not
602 * returned from CGDisplayCopyAllDisplayModes() without special options.
603 * This is especially bad if that's the user's default mode, since then
604 * no "available" mode matches the initial settings.
606 * If include_unsupported is FALSE, display modes with IO flags that
607 * indicate that they are invalid or unsafe are filtered.
609 static CFArrayRef copy_display_modes(CGDirectDisplayID display, BOOL include_unsupported)
611 CFArrayRef modes = NULL;
613 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
614 if (&CGDisplayModeGetPixelWidth != NULL && &CGDisplayModeGetPixelHeight != NULL)
616 CFDictionaryRef options;
617 struct display_mode_descriptor* desc;
618 CFMutableDictionaryRef modes_by_size;
619 CFIndex i, count;
620 CGDisplayModeRef* mode_array;
622 options = CFDictionaryCreate(NULL, (const void**)&kCGDisplayShowDuplicateLowResolutionModes,
623 (const void**)&kCFBooleanTrue, 1, &kCFTypeDictionaryKeyCallBacks,
624 &kCFTypeDictionaryValueCallBacks);
626 modes = CGDisplayCopyAllDisplayModes(display, options);
627 if (options)
628 CFRelease(options);
629 if (!modes)
630 return NULL;
632 desc = create_original_display_mode_descriptor(display);
634 modes_by_size = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
635 count = CFArrayGetCount(modes);
636 for (i = 0; i < count; i++)
638 CGDisplayModeRef new_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
639 BOOL new_is_original = display_mode_matches_descriptor(new_mode, desc);
640 CFDictionaryRef key = create_mode_dict(new_mode, new_is_original);
641 CGDisplayModeRef old_mode = (CGDisplayModeRef)CFDictionaryGetValue(modes_by_size, key);
643 if (mode_is_preferred(new_mode, old_mode, desc, include_unsupported))
644 CFDictionarySetValue(modes_by_size, key, new_mode);
646 CFRelease(key);
649 free_display_mode_descriptor(desc);
650 CFRelease(modes);
652 count = CFDictionaryGetCount(modes_by_size);
653 mode_array = malloc(count * sizeof(mode_array[0]));
654 CFDictionaryGetKeysAndValues(modes_by_size, NULL, (const void **)mode_array);
655 modes = CFArrayCreate(NULL, (const void **)mode_array, count, &kCFTypeArrayCallBacks);
656 free(mode_array);
657 CFRelease(modes_by_size);
659 else
660 #endif
661 modes = CGDisplayCopyAllDisplayModes(display, NULL);
663 if (modes)
665 CFIndex count = CFArrayGetCount(modes);
666 CFMutableArrayRef sorted_modes = CFArrayCreateMutableCopy(NULL, count, modes);
667 CFRelease(modes);
668 CFArraySortValues(sorted_modes, CFRangeMake(0, count), mode_compare, NULL);
669 return sorted_modes;
672 return NULL;
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);
692 static BOOL get_primary_adapter(WCHAR *name)
694 DISPLAY_DEVICEW dd;
695 DWORD i;
697 dd.cb = sizeof(dd);
698 for (i = 0; !NtUserEnumDisplayDevices(NULL, i, &dd, 0); ++i)
700 if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
702 lstrcpyW(name, dd.DeviceName);
703 return TRUE;
707 return FALSE;
710 static BOOL is_detached_mode(const DEVMODEW *mode)
712 return mode->dmFields & DM_POSITION &&
713 mode->dmFields & DM_PELSWIDTH &&
714 mode->dmFields & DM_PELSHEIGHT &&
715 mode->dmPelsWidth == 0 &&
716 mode->dmPelsHeight == 0;
719 static CGDisplayModeRef find_best_display_mode(DEVMODEW *devmode, CFArrayRef display_modes, int bpp, struct display_mode_descriptor *desc)
721 CFIndex count, i, best;
722 CGDisplayModeRef best_display_mode;
723 uint32_t best_io_flags;
725 best_display_mode = NULL;
727 count = CFArrayGetCount(display_modes);
728 for (i = 0; i < count; i++)
730 CGDisplayModeRef display_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(display_modes, i);
731 BOOL is_original = display_mode_matches_descriptor(display_mode, desc);
732 uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
733 int mode_bpp = display_mode_bits_per_pixel(display_mode);
734 size_t width = CGDisplayModeGetWidth(display_mode);
735 size_t height = CGDisplayModeGetHeight(display_mode);
737 if (is_original && retina_enabled)
739 width *= 2;
740 height *= 2;
743 if (bpp != mode_bpp)
744 continue;
746 if (devmode->dmFields & DM_PELSWIDTH)
748 if (devmode->dmPelsWidth != width)
749 continue;
751 if (devmode->dmFields & DM_PELSHEIGHT)
753 if (devmode->dmPelsHeight != height)
754 continue;
756 if ((devmode->dmFields & DM_DISPLAYFREQUENCY) &&
757 devmode->dmDisplayFrequency != 0 &&
758 devmode->dmDisplayFrequency != 1)
760 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
761 if (!refresh_rate)
762 refresh_rate = 60;
763 if (devmode->dmDisplayFrequency != (DWORD)refresh_rate)
764 continue;
766 if (devmode->dmFields & DM_DISPLAYFLAGS)
768 if (!(devmode->dmDisplayFlags & DM_INTERLACED) != !(io_flags & kDisplayModeInterlacedFlag))
769 continue;
771 else if (best_display_mode)
773 if (io_flags & kDisplayModeInterlacedFlag && !(best_io_flags & kDisplayModeInterlacedFlag))
774 continue;
775 else if (!(io_flags & kDisplayModeInterlacedFlag) && best_io_flags & kDisplayModeInterlacedFlag)
776 goto better;
778 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
780 if (!(devmode->dmDisplayFixedOutput == DMDFO_STRETCH) != !(io_flags & kDisplayModeStretchedFlag))
781 continue;
783 else if (best_display_mode)
785 if (io_flags & kDisplayModeStretchedFlag && !(best_io_flags & kDisplayModeStretchedFlag))
786 continue;
787 else if (!(io_flags & kDisplayModeStretchedFlag) && best_io_flags & kDisplayModeStretchedFlag)
788 goto better;
791 if (best_display_mode)
792 continue;
794 better:
795 best_display_mode = display_mode;
796 best = i;
797 best_io_flags = io_flags;
800 if (best_display_mode)
801 TRACE("Requested display settings match mode %ld\n", best);
803 return best_display_mode;
806 /***********************************************************************
807 * ChangeDisplaySettingsEx (MACDRV.@)
810 LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
811 HWND hwnd, DWORD flags, LPVOID lpvoid)
813 WCHAR primary_adapter[CCHDEVICENAME];
814 LONG ret = DISP_CHANGE_BADMODE;
815 DEVMODEW default_mode;
816 int bpp;
817 struct macdrv_display *displays;
818 int num_displays;
819 CFArrayRef display_modes;
820 struct display_mode_descriptor *desc;
821 CGDisplayModeRef best_display_mode;
823 TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid);
825 init_original_display_mode();
827 if (!get_primary_adapter(primary_adapter))
828 return DISP_CHANGE_FAILED;
830 if (!devname && !devmode)
832 UNICODE_STRING str;
833 memset(&default_mode, 0, sizeof(default_mode));
834 default_mode.dmSize = sizeof(default_mode);
835 RtlInitUnicodeString(&str, primary_adapter);
836 if (!NtUserEnumDisplaySettings(&str, ENUM_REGISTRY_SETTINGS, &default_mode, 0))
838 ERR("Default mode not found for %s!\n", wine_dbgstr_w(primary_adapter));
839 return DISP_CHANGE_BADMODE;
842 devname = primary_adapter;
843 devmode = &default_mode;
846 if (is_detached_mode(devmode))
848 FIXME("Detaching adapters is currently unsupported.\n");
849 return DISP_CHANGE_SUCCESSFUL;
852 if (macdrv_get_displays(&displays, &num_displays))
853 return DISP_CHANGE_FAILED;
855 display_modes = copy_display_modes(displays[0].displayID, FALSE);
856 if (!display_modes)
858 macdrv_free_displays(displays);
859 return DISP_CHANGE_FAILED;
862 pthread_mutex_lock(&modes_mutex);
863 bpp = get_default_bpp();
864 pthread_mutex_unlock(&modes_mutex);
865 if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel != bpp)
866 TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, devmode->dmBitsPerPel);
868 TRACE("looking for %dx%dx%dbpp @%d Hz",
869 (devmode->dmFields & DM_PELSWIDTH ? devmode->dmPelsWidth : 0),
870 (devmode->dmFields & DM_PELSHEIGHT ? devmode->dmPelsHeight : 0),
871 bpp,
872 (devmode->dmFields & DM_DISPLAYFREQUENCY ? devmode->dmDisplayFrequency : 0));
873 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
874 TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un");
875 if (devmode->dmFields & DM_DISPLAYFLAGS)
876 TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
877 TRACE("\n");
879 desc = create_original_display_mode_descriptor(displays[0].displayID);
880 best_display_mode = find_best_display_mode(devmode, display_modes, bpp, desc);
882 if (best_display_mode)
884 if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL;
885 else if (wcsicmp(primary_adapter, devname))
887 FIXME("Changing non-primary adapter settings is currently unsupported.\n");
888 ret = DISP_CHANGE_SUCCESSFUL;
890 else if (macdrv_set_display_mode(&displays[0], best_display_mode))
892 int mode_bpp = display_mode_bits_per_pixel(best_display_mode);
893 size_t width = CGDisplayModeGetWidth(best_display_mode);
894 size_t height = CGDisplayModeGetHeight(best_display_mode);
896 macdrv_init_display_devices(TRUE);
898 if (retina_enabled && display_mode_matches_descriptor(best_display_mode, desc))
900 width *= 2;
901 height *= 2;
904 send_message(NtUserGetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
905 MAKELPARAM(width, height));
906 ret = DISP_CHANGE_SUCCESSFUL;
908 else
910 WARN("Failed to set display mode\n");
911 ret = DISP_CHANGE_FAILED;
914 else
916 /* no valid modes found */
917 ERR("No matching mode found %ux%ux%d @%u!\n", devmode->dmPelsWidth, devmode->dmPelsHeight,
918 bpp, devmode->dmDisplayFrequency);
921 free_display_mode_descriptor(desc);
922 CFRelease(display_modes);
923 macdrv_free_displays(displays);
925 return ret;
929 static DEVMODEW *display_get_modes(CGDirectDisplayID display_id, int *modes_count)
931 int default_bpp = get_default_bpp(), synth_count = 0, count, i;
932 BOOL modes_has_8bpp = FALSE, modes_has_16bpp = FALSE;
933 struct display_mode_descriptor *desc;
934 DEVMODEW *devmodes;
935 CFArrayRef modes;
937 modes = copy_display_modes(display_id, TRUE);
938 if (!modes)
939 return NULL;
941 count = CFArrayGetCount(modes);
942 for (i = 0; i < count && !(modes_has_8bpp && modes_has_16bpp); i++)
944 CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
945 int bpp = display_mode_bits_per_pixel(mode);
946 if (bpp == 8)
947 modes_has_8bpp = TRUE;
948 else if (bpp == 16)
949 modes_has_16bpp = TRUE;
952 if (!(devmodes = calloc(count * 3, sizeof(DEVMODEW))))
954 CFRelease(modes);
955 return NULL;
958 desc = create_original_display_mode_descriptor(display_id);
959 for (i = 0; i < count; i++)
961 CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
962 display_mode_to_devmode(display_id, mode, devmodes + i);
964 if (retina_enabled && display_mode_matches_descriptor(mode, desc))
966 devmodes[i].dmPelsWidth *= 2;
967 devmodes[i].dmPelsHeight *= 2;
970 free_display_mode_descriptor(desc);
972 for (i = 0; !modes_has_16bpp && i < count; i++)
974 /* We only synthesize modes from those having the default bpp. */
975 if (devmodes[i].dmBitsPerPel != default_bpp) continue;
976 devmodes[count + synth_count] = devmodes[i];
977 devmodes[count + synth_count].dmBitsPerPel = 16;
978 synth_count++;
981 for (i = 0; !modes_has_8bpp && i < count; i++)
983 /* We only synthesize modes from those having the default bpp. */
984 if (devmodes[i].dmBitsPerPel != default_bpp) continue;
985 devmodes[count + synth_count] = devmodes[i];
986 devmodes[count + synth_count].dmBitsPerPel = 8;
987 synth_count++;
990 CFRelease(modes);
991 *modes_count = count + synth_count;
992 return devmodes;
995 /***********************************************************************
996 * EnumDisplaySettingsEx (MACDRV.@)
999 BOOL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW devmode, DWORD flags)
1001 struct macdrv_display *displays = NULL;
1002 int num_displays;
1003 CGDisplayModeRef display_mode;
1004 int display_mode_bpp;
1005 BOOL synthesized = FALSE;
1006 DWORD count, i;
1008 TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname), mode, devmode, devmode->dmSize, flags);
1010 init_original_display_mode();
1012 if (macdrv_get_displays(&displays, &num_displays))
1013 goto failed;
1015 pthread_mutex_lock(&modes_mutex);
1017 if (mode == 0 || !modes)
1019 if (modes) CFRelease(modes);
1020 modes = copy_display_modes(displays[0].displayID, (flags & EDS_RAWMODE) != 0);
1021 modes_has_8bpp = modes_has_16bpp = FALSE;
1023 if (modes)
1025 count = CFArrayGetCount(modes);
1026 for (i = 0; i < count && !(modes_has_8bpp && modes_has_16bpp); i++)
1028 CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
1029 int bpp = display_mode_bits_per_pixel(mode);
1030 if (bpp == 8)
1031 modes_has_8bpp = TRUE;
1032 else if (bpp == 16)
1033 modes_has_16bpp = TRUE;
1038 display_mode = NULL;
1039 if (modes)
1041 int default_bpp;
1042 DWORD seen_modes = 0;
1044 count = CFArrayGetCount(modes);
1045 for (i = 0; i < count; i++)
1047 CGDisplayModeRef candidate = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
1049 seen_modes++;
1050 if (seen_modes > mode)
1052 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1053 display_mode_bpp = display_mode_bits_per_pixel(display_mode);
1054 break;
1058 default_bpp = get_default_bpp();
1060 /* If all the real modes are exhausted, synthesize lower bpp modes. */
1061 if (!display_mode && (!modes_has_16bpp || !modes_has_8bpp))
1063 /* We want to synthesize higher depths first. */
1064 int synth_bpps[] = { modes_has_16bpp ? 0 : 16, modes_has_8bpp ? 0 : 8 };
1065 size_t synth_bpp_idx;
1066 for (synth_bpp_idx = 0; synth_bpp_idx < 2; synth_bpp_idx++)
1068 int synth_bpp = synth_bpps[synth_bpp_idx];
1069 if (synth_bpp == 0)
1070 continue;
1072 for (i = 0; i < count; i++)
1074 CGDisplayModeRef candidate = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
1075 /* We only synthesize modes from those having the default bpp. */
1076 if (display_mode_bits_per_pixel(candidate) != default_bpp)
1077 continue;
1079 seen_modes++;
1080 if (seen_modes > mode)
1082 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1083 display_mode_bpp = synth_bpp;
1084 synthesized = TRUE;
1085 break;
1089 if (display_mode)
1090 break;
1095 pthread_mutex_unlock(&modes_mutex);
1097 if (!display_mode)
1098 goto failed;
1100 display_mode_to_devmode(displays[0].displayID, display_mode, devmode);
1101 devmode->dmBitsPerPel = display_mode_bpp;
1102 if (devmode->dmBitsPerPel)
1103 devmode->dmFields |= DM_BITSPERPEL;
1104 if (retina_enabled)
1106 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(displays[0].displayID);
1107 if (display_mode_matches_descriptor(display_mode, desc))
1109 devmode->dmPelsWidth *= 2;
1110 devmode->dmPelsHeight *= 2;
1112 free_display_mode_descriptor(desc);
1115 CFRelease(display_mode);
1116 macdrv_free_displays(displays);
1118 TRACE("mode %d -- %dx%dx%dbpp @%d Hz", mode,
1119 devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel,
1120 devmode->dmDisplayFrequency);
1121 if (devmode->dmDisplayOrientation)
1122 TRACE(" rotated %u degrees", devmode->dmDisplayOrientation * 90);
1123 if (devmode->dmDisplayFixedOutput == DMDFO_STRETCH)
1124 TRACE(" stretched");
1125 if (devmode->dmDisplayFlags & DM_INTERLACED)
1126 TRACE(" interlaced");
1127 if (synthesized)
1128 TRACE(" (synthesized)");
1129 TRACE("\n");
1131 return TRUE;
1133 failed:
1134 TRACE("mode %d -- not present\n", mode);
1135 if (displays) macdrv_free_displays(displays);
1136 RtlSetLastWin32Error(ERROR_NO_MORE_FILES);
1137 return FALSE;
1140 /***********************************************************************
1141 * GetCurrentDisplaySettings (MACDRV.@)
1144 BOOL macdrv_GetCurrentDisplaySettings(LPCWSTR devname, LPDEVMODEW devmode)
1146 struct macdrv_display *displays = NULL;
1147 int num_displays;
1148 CGDisplayModeRef display_mode;
1150 TRACE("%s, %p + %hu\n", debugstr_w(devname), devmode, devmode->dmSize);
1152 init_original_display_mode();
1154 if (macdrv_get_displays(&displays, &num_displays))
1155 return FALSE;
1157 display_mode = CGDisplayCopyDisplayMode(displays[0].displayID);
1159 /* We currently only report modes for the primary display, so it's at (0, 0). */
1160 devmode->dmPosition.x = 0;
1161 devmode->dmPosition.y = 0;
1162 devmode->dmFields |= DM_POSITION;
1164 display_mode_to_devmode(displays[0].displayID, display_mode, devmode);
1165 if (retina_enabled)
1167 struct display_mode_descriptor *desc = create_original_display_mode_descriptor(displays[0].displayID);
1168 if (display_mode_matches_descriptor(display_mode, desc))
1170 devmode->dmPelsWidth *= 2;
1171 devmode->dmPelsHeight *= 2;
1173 free_display_mode_descriptor(desc);
1176 CFRelease(display_mode);
1177 macdrv_free_displays(displays);
1179 TRACE("current mode -- %dx%dx%dbpp @%d Hz",
1180 devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel,
1181 devmode->dmDisplayFrequency);
1182 if (devmode->dmDisplayOrientation)
1183 TRACE(" rotated %u degrees", devmode->dmDisplayOrientation * 90);
1184 if (devmode->dmDisplayFixedOutput == DMDFO_STRETCH)
1185 TRACE(" stretched");
1186 if (devmode->dmDisplayFlags & DM_INTERLACED)
1187 TRACE(" interlaced");
1188 TRACE("\n");
1190 return TRUE;
1194 /***********************************************************************
1195 * GetDeviceGammaRamp (MACDRV.@)
1197 BOOL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1199 BOOL ret = FALSE;
1200 DDGAMMARAMP *r = ramp;
1201 struct macdrv_display *displays;
1202 int num_displays;
1203 uint32_t mac_entries;
1204 int win_entries = ARRAY_SIZE(r->red);
1205 CGGammaValue *red, *green, *blue;
1206 CGError err;
1207 int win_entry;
1209 TRACE("dev %p ramp %p\n", dev, ramp);
1211 if (macdrv_get_displays(&displays, &num_displays))
1213 WARN("failed to get Mac displays\n");
1214 return FALSE;
1217 mac_entries = CGDisplayGammaTableCapacity(displays[0].displayID);
1218 red = malloc(mac_entries * sizeof(red[0]) * 3);
1219 if (!red)
1220 goto done;
1221 green = red + mac_entries;
1222 blue = green + mac_entries;
1224 err = CGGetDisplayTransferByTable(displays[0].displayID, mac_entries, red, green,
1225 blue, &mac_entries);
1226 if (err != kCGErrorSuccess)
1228 WARN("failed to get Mac gamma table: %d\n", err);
1229 goto done;
1232 if (mac_entries == win_entries)
1234 for (win_entry = 0; win_entry < win_entries; win_entry++)
1236 r->red[win_entry] = red[win_entry] * 65535 + 0.5;
1237 r->green[win_entry] = green[win_entry] * 65535 + 0.5;
1238 r->blue[win_entry] = blue[win_entry] * 65535 + 0.5;
1241 else
1243 for (win_entry = 0; win_entry < win_entries; win_entry++)
1245 double mac_pos = win_entry * (mac_entries - 1) / (double)(win_entries - 1);
1246 int mac_entry = mac_pos;
1247 double red_value, green_value, blue_value;
1249 if (mac_entry == mac_entries - 1)
1251 red_value = red[mac_entry];
1252 green_value = green[mac_entry];
1253 blue_value = blue[mac_entry];
1255 else
1257 double distance = mac_pos - mac_entry;
1259 red_value = red[mac_entry] * (1 - distance) + red[mac_entry + 1] * distance;
1260 green_value = green[mac_entry] * (1 - distance) + green[mac_entry + 1] * distance;
1261 blue_value = blue[mac_entry] * (1 - distance) + blue[mac_entry + 1] * distance;
1264 r->red[win_entry] = red_value * 65535 + 0.5;
1265 r->green[win_entry] = green_value * 65535 + 0.5;
1266 r->blue[win_entry] = blue_value * 65535 + 0.5;
1270 ret = TRUE;
1272 done:
1273 free(red);
1274 macdrv_free_displays(displays);
1275 return ret;
1278 /***********************************************************************
1279 * SetDeviceGammaRamp (MACDRV.@)
1281 BOOL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1283 DDGAMMARAMP *r = ramp;
1284 struct macdrv_display *displays;
1285 int num_displays;
1286 int win_entries = ARRAY_SIZE(r->red);
1287 CGGammaValue *red, *green, *blue;
1288 int i;
1289 CGError err = kCGErrorFailure;
1291 TRACE("dev %p ramp %p\n", dev, ramp);
1293 if (!allow_set_gamma)
1295 TRACE("disallowed by registry setting\n");
1296 return FALSE;
1299 if (macdrv_get_displays(&displays, &num_displays))
1301 WARN("failed to get Mac displays\n");
1302 return FALSE;
1305 red = malloc(win_entries * sizeof(red[0]) * 3);
1306 if (!red)
1307 goto done;
1308 green = red + win_entries;
1309 blue = green + win_entries;
1311 for (i = 0; i < win_entries; i++)
1313 red[i] = r->red[i] / 65535.0;
1314 green[i] = r->green[i] / 65535.0;
1315 blue[i] = r->blue[i] / 65535.0;
1318 err = CGSetDisplayTransferByTable(displays[0].displayID, win_entries, red, green, blue);
1319 if (err != kCGErrorSuccess)
1320 WARN("failed to set display gamma table: %d\n", err);
1322 done:
1323 free(red);
1324 macdrv_free_displays(displays);
1325 return (err == kCGErrorSuccess);
1328 /***********************************************************************
1329 * init_registry_display_settings
1331 * Initialize registry display settings when new display devices are added.
1333 static void init_registry_display_settings(void)
1335 DEVMODEW dm = {.dmSize = sizeof(dm)};
1336 DISPLAY_DEVICEW dd = {sizeof(dd)};
1337 UNICODE_STRING str;
1338 DWORD i = 0;
1339 LONG ret;
1341 while (!NtUserEnumDisplayDevices(NULL, i++, &dd, 0))
1343 RtlInitUnicodeString(&str, dd.DeviceName);
1345 /* Skip if the device already has registry display settings */
1346 if (NtUserEnumDisplaySettings(&str, ENUM_REGISTRY_SETTINGS, &dm, 0))
1347 continue;
1349 if (!NtUserEnumDisplaySettings(&str, ENUM_CURRENT_SETTINGS, &dm, 0))
1351 ERR("Failed to query current display settings for %s.\n", wine_dbgstr_w(dd.DeviceName));
1352 continue;
1355 TRACE("Device %s current display mode %ux%u %ubits %uHz at %d,%d.\n",
1356 wine_dbgstr_w(dd.DeviceName), dm.dmPelsWidth, dm.dmPelsHeight, dm.dmBitsPerPel,
1357 dm.dmDisplayFrequency, dm.dmPosition.x, dm.dmPosition.y);
1359 ret = NtUserChangeDisplaySettings(&str, &dm, NULL,
1360 CDS_GLOBAL | CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
1361 if (ret != DISP_CHANGE_SUCCESSFUL)
1362 ERR("Failed to save registry display settings for %s, returned %d.\n",
1363 wine_dbgstr_w(dd.DeviceName), ret);
1367 /***********************************************************************
1368 * macdrv_displays_changed
1370 * Handler for DISPLAYS_CHANGED events.
1372 void macdrv_displays_changed(const macdrv_event *event)
1374 HWND hwnd = NtUserGetDesktopWindow();
1376 /* A system display change will get delivered to all GUI-attached threads,
1377 so the desktop-window-owning thread will get it and all others should
1378 ignore it. A synthesized display change event due to activation
1379 will only get delivered to the activated process. So, it needs to
1380 process it (by sending it to the desktop window). */
1381 if (event->displays_changed.activating ||
1382 NtUserGetWindowThread(hwnd, NULL) == GetCurrentThreadId())
1384 CGDirectDisplayID mainDisplay = CGMainDisplayID();
1385 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(mainDisplay);
1386 size_t width = CGDisplayModeGetWidth(mode);
1387 size_t height = CGDisplayModeGetHeight(mode);
1388 int mode_bpp = display_mode_bits_per_pixel(mode);
1389 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(mainDisplay);
1390 BOOL is_original = display_mode_matches_descriptor(mode, desc);
1392 free_display_mode_descriptor(desc);
1393 CGDisplayModeRelease(mode);
1395 macdrv_init_display_devices(TRUE);
1396 init_registry_display_settings();
1398 if (is_original && retina_enabled)
1400 width *= 2;
1401 height *= 2;
1404 send_message(hwnd, WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
1405 MAKELPARAM(width, height));
1409 static BOOL force_display_devices_refresh;
1411 BOOL macdrv_UpdateDisplayDevices( const struct gdi_device_manager *device_manager, BOOL force, void *param )
1413 struct macdrv_adapter *adapters, *adapter;
1414 struct macdrv_monitor *monitors, *monitor;
1415 struct macdrv_gpu *gpus, *gpu;
1416 INT gpu_count, adapter_count, monitor_count, mode_count;
1417 DEVMODEW *mode, *modes;
1418 DWORD len;
1420 if (!force && !force_display_devices_refresh) return TRUE;
1421 force_display_devices_refresh = FALSE;
1423 /* Initialize GPUs */
1424 if (macdrv_get_gpus(&gpus, &gpu_count))
1426 ERR("could not get GPUs\n");
1427 return FALSE;
1429 TRACE("GPU count: %d\n", gpu_count);
1431 for (gpu = gpus; gpu < gpus + gpu_count; gpu++)
1433 struct gdi_gpu gdi_gpu =
1435 .id = gpu->id,
1436 .vendor_id = gpu->vendor_id,
1437 .device_id = gpu->device_id,
1438 .subsys_id = gpu->subsys_id,
1439 .revision_id = gpu->revision_id,
1441 RtlUTF8ToUnicodeN(gdi_gpu.name, sizeof(gdi_gpu.name), &len, gpu->name, strlen(gpu->name));
1442 device_manager->add_gpu(&gdi_gpu, param);
1444 /* Initialize adapters */
1445 if (macdrv_get_adapters(gpu->id, &adapters, &adapter_count)) break;
1446 TRACE("GPU: %llx %s, adapter count: %d\n", gpu->id, debugstr_a(gpu->name), adapter_count);
1448 for (adapter = adapters; adapter < adapters + adapter_count; adapter++)
1450 struct gdi_adapter gdi_adapter =
1452 .id = adapter->id,
1453 .state_flags = adapter->state_flags,
1455 device_manager->add_adapter( &gdi_adapter, param );
1457 if (macdrv_get_monitors(adapter->id, &monitors, &monitor_count)) break;
1458 TRACE("adapter: %#x, monitor count: %d\n", adapter->id, monitor_count);
1460 /* Initialize monitors */
1461 for (monitor = monitors; monitor < monitors + monitor_count; monitor++)
1463 struct gdi_monitor gdi_monitor =
1465 .rc_monitor = rect_from_cgrect(monitor->rc_monitor),
1466 .rc_work = rect_from_cgrect(monitor->rc_work),
1467 .state_flags = monitor->state_flags,
1469 RtlUTF8ToUnicodeN(gdi_monitor.name, sizeof(gdi_monitor.name), &len,
1470 monitor->name, strlen(monitor->name));
1471 TRACE("monitor: %s\n", debugstr_a(monitor->name));
1472 device_manager->add_monitor( &gdi_monitor, param );
1475 if (!(modes = display_get_modes(adapter->id, &mode_count))) break;
1476 TRACE("adapter: %#x, mode count: %d\n", adapter->id, mode_count);
1478 /* Initialize modes */
1479 for (mode = modes; mode < modes + mode_count; mode++)
1481 TRACE("mode: %dx%dx%dbpp @%d Hz, %sstretched %sinterlaced\n", mode->dmPelsWidth, mode->dmPelsHeight,
1482 mode->dmBitsPerPel, mode->dmDisplayFrequency,
1483 mode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un",
1484 mode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
1485 device_manager->add_mode( mode, param );
1488 macdrv_free_monitors(monitors);
1491 macdrv_free_adapters(adapters);
1494 macdrv_free_gpus(gpus);
1495 return TRUE;
1498 /***********************************************************************
1499 * macdrv_init_display_devices
1501 * Initialize display device registry data.
1503 void macdrv_init_display_devices(BOOL force)
1505 UINT32 num_path, num_mode;
1507 if (force) force_display_devices_refresh = TRUE;
1508 /* trigger refresh in win32u */
1509 NtUserGetDisplayConfigBufferSizes( QDC_ONLY_ACTIVE_PATHS, &num_path, &num_mode );