win32u: Don't report cloned monitors in EnumDisplayMonitors().
[wine.git] / dlls / winemac.drv / display.c
blob19b9a9cdd9d412442c27e85efc73f080a7277813
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;
63 static HANDLE get_display_device_init_mutex(void)
65 static const WCHAR init_mutexW[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t'};
66 UNICODE_STRING name = { sizeof(init_mutexW), sizeof(init_mutexW), (WCHAR *)init_mutexW };
67 OBJECT_ATTRIBUTES attr;
68 HANDLE mutex = 0;
70 InitializeObjectAttributes(&attr, &name, OBJ_OPENIF, NULL, NULL);
71 NtCreateMutant(&mutex, MUTEX_ALL_ACCESS, &attr, FALSE);
72 if (mutex) NtWaitForSingleObject(mutex, FALSE, NULL);
73 return mutex;
76 static void release_display_device_init_mutex(HANDLE mutex)
78 NtReleaseMutant(mutex, NULL);
79 NtClose(mutex);
82 static HKEY get_display_device_reg_key(const WCHAR *device_name)
84 static const WCHAR display[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y'};
85 static const WCHAR video_key[] = {
86 '\\','R','e','g','i','s','t','r','y',
87 '\\','M','a','c','h','i','n','e',
88 '\\','H','A','R','D','W','A','R','E',
89 '\\','D','E','V','I','C','E','M','A','P',
90 '\\','V','I','D','E','O'};
91 static const WCHAR current_config_key[] = {
92 '\\','R','e','g','i','s','t','r','y',
93 '\\','M','a','c','h','i','n','e',
94 '\\','S','y','s','t','e','m',
95 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
96 '\\','H','a','r','d','w','a','r','e',' ','P','r','o','f','i','l','e','s',
97 '\\','C','u','r','r','e','n','t'};
98 WCHAR value_name[MAX_PATH], buffer[4096], *end_ptr;
99 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
100 DWORD adapter_index, size;
101 char adapter_name[100];
102 HKEY hkey;
104 /* Device name has to be \\.\DISPLAY%d */
105 if (wcsnicmp(device_name, display, ARRAY_SIZE(display)))
106 return FALSE;
108 /* Parse \\.\DISPLAY* */
109 adapter_index = wcstol(device_name + ARRAY_SIZE(display), &end_ptr, 10) - 1;
110 if (*end_ptr)
111 return FALSE;
113 /* Open \Device\Video* in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */
114 if (!(hkey = reg_open_key(NULL, video_key, sizeof(video_key)))) return FALSE;
115 sprintf(adapter_name, "\\Device\\Video%d", adapter_index);
116 asciiz_to_unicode(value_name, adapter_name);
117 size = query_reg_value(hkey, value_name, value, sizeof(buffer));
118 NtClose(hkey);
119 if (!size || value->Type != REG_SZ) return FALSE;
121 /* Replace \Registry\Machine\ prefix with HKEY_CURRENT_CONFIG */
122 memmove(buffer + ARRAYSIZE(current_config_key), (const WCHAR *)value->Data + 17,
123 size - 17 * sizeof(WCHAR));
124 memcpy(buffer, current_config_key, sizeof(current_config_key));
125 TRACE("display device %s registry settings key %s.\n", wine_dbgstr_w(device_name),
126 wine_dbgstr_w(buffer));
127 return reg_open_key(NULL, buffer, lstrlenW(buffer) * sizeof(WCHAR));
131 static BOOL query_display_setting(HKEY hkey, const char *name, DWORD *ret)
133 char buffer[1024];
134 WCHAR nameW[128];
135 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
137 asciiz_to_unicode(nameW, name);
138 if (query_reg_value(hkey, nameW, value, sizeof(buffer)) != sizeof(DWORD) ||
139 value->Type != REG_DWORD)
140 return FALSE;
142 *ret = *(DWORD *)value->Data;
143 return TRUE;
147 static BOOL read_registry_settings(const WCHAR *device_name, DEVMODEW *dm)
149 HANDLE mutex;
150 HKEY hkey;
151 BOOL ret = TRUE;
153 dm->dmFields = 0;
155 mutex = get_display_device_init_mutex();
156 if (!(hkey = get_display_device_reg_key(device_name)))
158 release_display_device_init_mutex(mutex);
159 return FALSE;
162 ret &= query_display_setting(hkey, "DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
163 dm->dmFields |= DM_BITSPERPEL;
164 ret &= query_display_setting(hkey, "DefaultSettings.XResolution", &dm->dmPelsWidth);
165 dm->dmFields |= DM_PELSWIDTH;
166 ret &= query_display_setting(hkey, "DefaultSettings.YResolution", &dm->dmPelsHeight);
167 dm->dmFields |= DM_PELSHEIGHT;
168 ret &= query_display_setting(hkey, "DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
169 dm->dmFields |= DM_DISPLAYFREQUENCY;
170 ret &= query_display_setting(hkey, "DefaultSettings.Flags", &dm->dmDisplayFlags);
171 dm->dmFields |= DM_DISPLAYFLAGS;
172 ret &= query_display_setting(hkey, "DefaultSettings.XPanning", (DWORD *)&dm->dmPosition.x);
173 ret &= query_display_setting(hkey, "DefaultSettings.YPanning", (DWORD *)&dm->dmPosition.y);
174 dm->dmFields |= DM_POSITION;
175 ret &= query_display_setting(hkey, "DefaultSettings.Orientation", &dm->dmDisplayOrientation);
176 dm->dmFields |= DM_DISPLAYORIENTATION;
177 ret &= query_display_setting(hkey, "DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
179 NtClose(hkey);
180 release_display_device_init_mutex(mutex);
181 return ret;
185 static BOOL set_setting_value(HKEY hkey, const char *name, DWORD val)
187 WCHAR nameW[128];
188 UNICODE_STRING str = { asciiz_to_unicode(nameW, name) - sizeof(WCHAR), sizeof(nameW), nameW };
189 return !NtSetValueKey(hkey, &str, 0, REG_DWORD, &val, sizeof(val));
193 static BOOL write_registry_settings(const WCHAR *device_name, const DEVMODEW *dm)
195 HANDLE mutex;
196 HKEY hkey;
197 BOOL ret = TRUE;
199 mutex = get_display_device_init_mutex();
200 if (!(hkey = get_display_device_reg_key(device_name)))
202 release_display_device_init_mutex(mutex);
203 return FALSE;
206 ret &= set_setting_value(hkey, "DefaultSettings.BitsPerPel", dm->dmBitsPerPel);
207 ret &= set_setting_value(hkey, "DefaultSettings.XResolution", dm->dmPelsWidth);
208 ret &= set_setting_value(hkey, "DefaultSettings.YResolution", dm->dmPelsHeight);
209 ret &= set_setting_value(hkey, "DefaultSettings.VRefresh", dm->dmDisplayFrequency);
210 ret &= set_setting_value(hkey, "DefaultSettings.Flags", dm->dmDisplayFlags);
211 ret &= set_setting_value(hkey, "DefaultSettings.XPanning", dm->dmPosition.x);
212 ret &= set_setting_value(hkey, "DefaultSettings.YPanning", dm->dmPosition.y);
213 ret &= set_setting_value(hkey, "DefaultSettings.Orientation", dm->dmDisplayOrientation);
214 ret &= set_setting_value(hkey, "DefaultSettings.FixedOutput", dm->dmDisplayFixedOutput);
216 NtClose(hkey);
217 release_display_device_init_mutex(mutex);
218 return ret;
222 static BOOL write_display_settings(HKEY parent_hkey, CGDirectDisplayID displayID)
224 BOOL ret = FALSE;
225 char display_key_name[19];
226 HKEY display_hkey;
227 CGDisplayModeRef display_mode;
228 UNICODE_STRING str;
229 DWORD val;
230 CFStringRef pixel_encoding;
231 size_t len;
232 WCHAR* buf = NULL;
234 snprintf(display_key_name, sizeof(display_key_name), "Display 0x%08x", CGDisplayUnitNumber(displayID));
235 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
236 if (!(display_hkey = reg_create_ascii_key(parent_hkey, display_key_name, REG_OPTION_VOLATILE, NULL)))
237 return FALSE;
239 display_mode = CGDisplayCopyDisplayMode(displayID);
240 if (!display_mode)
241 goto fail;
243 val = CGDisplayModeGetWidth(display_mode);
244 if (!set_setting_value(display_hkey, "Width", val))
245 goto fail;
246 val = CGDisplayModeGetHeight(display_mode);
247 if (!set_setting_value(display_hkey, "Height", val))
248 goto fail;
249 val = CGDisplayModeGetRefreshRate(display_mode) * 100;
250 if (!set_setting_value(display_hkey, "RefreshRateTimes100", val))
251 goto fail;
252 val = CGDisplayModeGetIOFlags(display_mode);
253 if (!set_setting_value(display_hkey, "IOFlags", val))
254 goto fail;
256 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
257 if (&CGDisplayModeGetPixelWidth != NULL && &CGDisplayModeGetPixelHeight != NULL)
259 val = CGDisplayModeGetPixelWidth(display_mode);
260 if (!set_setting_value(display_hkey, "PixelWidth", val))
261 goto fail;
262 val = CGDisplayModeGetPixelHeight(display_mode);
263 if (!set_setting_value(display_hkey, "PixelHeight", val))
264 goto fail;
266 #endif
268 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
269 len = CFStringGetLength(pixel_encoding);
270 buf = malloc((len + 1) * sizeof(WCHAR));
271 CFStringGetCharacters(pixel_encoding, CFRangeMake(0, len), (UniChar*)buf);
272 buf[len] = 0;
273 CFRelease(pixel_encoding);
274 RtlInitUnicodeString(&str, pixelencodingW);
275 if (NtSetValueKey(display_hkey, &str, 0, REG_SZ, (const BYTE*)buf, (len + 1) * sizeof(WCHAR)))
276 goto fail;
278 ret = TRUE;
280 fail:
281 free(buf);
282 if (display_mode) CGDisplayModeRelease(display_mode);
283 NtClose(display_hkey);
284 if (!ret)
286 WCHAR nameW[64];
287 reg_delete_tree(parent_hkey, nameW, asciiz_to_unicode(nameW, display_key_name) - sizeof(WCHAR));
289 return ret;
293 static void init_original_display_mode(void)
295 BOOL success = FALSE;
296 HKEY mac_driver_hkey, parent_hkey;
297 DWORD disposition;
298 struct macdrv_display *displays = NULL;
299 int num_displays, i;
301 if (inited_original_display_mode)
302 return;
304 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver */
305 mac_driver_hkey = reg_create_ascii_key(NULL, "\\Registry\\Machine\\Software\\Wine\\Mac Driver",
306 0, NULL);
307 if (!mac_driver_hkey)
308 return;
310 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode */
311 if (!(parent_hkey = reg_create_key(mac_driver_hkey, initial_mode_keyW, sizeof(initial_mode_keyW),
312 REG_OPTION_VOLATILE, &disposition)))
314 parent_hkey = NULL;
315 goto fail;
318 /* If we didn't create a new key, then it already existed. Something already stored
319 the initial display mode since Wine was started. We don't want to overwrite it. */
320 if (disposition != REG_CREATED_NEW_KEY)
321 goto done;
323 if (macdrv_get_displays(&displays, &num_displays))
324 goto fail;
326 for (i = 0; i < num_displays; i++)
328 if (!write_display_settings(parent_hkey, displays[i].displayID))
329 goto fail;
332 done:
333 success = TRUE;
335 fail:
336 macdrv_free_displays(displays);
337 NtClose(parent_hkey);
338 if (!success && parent_hkey)
339 reg_delete_tree(mac_driver_hkey, initial_mode_keyW, sizeof(initial_mode_keyW));
340 NtClose(mac_driver_hkey);
341 if (success)
342 inited_original_display_mode = TRUE;
346 static BOOL read_dword(HKEY hkey, const char* name, DWORD* val)
348 char buffer[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(*val)])];
349 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
350 WCHAR nameW[64];
351 asciiz_to_unicode(nameW, name);
352 if (query_reg_value(hkey, nameW, value, sizeof(buffer)) != sizeof(*val) || value->Type != REG_DWORD)
353 return FALSE;
354 *val = *(DWORD *)value->Data;
355 return TRUE;
359 static void free_display_mode_descriptor(struct display_mode_descriptor* desc)
361 if (desc)
363 if (desc->pixel_encoding)
364 CFRelease(desc->pixel_encoding);
365 free(desc);
370 static struct display_mode_descriptor* create_original_display_mode_descriptor(CGDirectDisplayID displayID)
372 static const char display_key_format[] =
373 "\\Registry\\Machine\\Software\\Wine\\Mac Driver\\Initial Display Mode\\Display 0x%08x";
374 struct display_mode_descriptor* ret = NULL;
375 struct display_mode_descriptor* desc;
376 char display_key[sizeof(display_key_format) + 10];
377 WCHAR nameW[ARRAYSIZE(display_key)];
378 char buffer[4096];
379 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
380 HKEY hkey;
381 DWORD refresh100;
383 init_original_display_mode();
385 snprintf(display_key, sizeof(display_key), display_key_format, CGDisplayUnitNumber(displayID));
386 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
387 if (!(hkey = reg_open_key(NULL, nameW, asciiz_to_unicode(nameW, display_key))))
388 return NULL;
390 desc = malloc(sizeof(*desc));
391 desc->pixel_encoding = NULL;
393 if (!read_dword(hkey, "Width", &desc->width) ||
394 !read_dword(hkey, "Height", &desc->height) ||
395 !read_dword(hkey, "RefreshRateTimes100", &refresh100) ||
396 !read_dword(hkey, "IOFlags", &desc->io_flags))
397 goto done;
398 if (refresh100)
399 desc->refresh = refresh100 / 100.0;
400 else
401 desc->refresh = 60;
403 if (!read_dword(hkey, "PixelWidth", &desc->pixel_width) ||
404 !read_dword(hkey, "PixelHeight", &desc->pixel_height))
406 desc->pixel_width = desc->width;
407 desc->pixel_height = desc->height;
410 if (!query_reg_value(hkey, pixelencodingW, value, sizeof(buffer)) || value->Type != REG_SZ)
411 goto done;
412 desc->pixel_encoding = CFStringCreateWithCharacters(NULL, (const UniChar*)value->Data,
413 lstrlenW((const WCHAR *)value->Data));
415 ret = desc;
417 done:
418 if (!ret)
419 free_display_mode_descriptor(desc);
420 NtClose(hkey);
421 return ret;
425 static BOOL display_mode_matches_descriptor(CGDisplayModeRef mode, const struct display_mode_descriptor* desc)
427 DWORD mode_io_flags;
428 double mode_refresh;
429 CFStringRef mode_pixel_encoding;
431 if (!desc)
432 return FALSE;
434 if (CGDisplayModeGetWidth(mode) != desc->width ||
435 CGDisplayModeGetHeight(mode) != desc->height)
436 return FALSE;
438 mode_io_flags = CGDisplayModeGetIOFlags(mode);
439 if ((desc->io_flags ^ mode_io_flags) & (kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeStretchedFlag |
440 kDisplayModeInterlacedFlag | kDisplayModeTelevisionFlag))
441 return FALSE;
443 mode_refresh = CGDisplayModeGetRefreshRate(mode);
444 if (!mode_refresh)
445 mode_refresh = 60;
446 if (fabs(desc->refresh - mode_refresh) > 0.1)
447 return FALSE;
449 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
450 if (&CGDisplayModeGetPixelWidth != NULL && &CGDisplayModeGetPixelHeight != NULL)
452 if (CGDisplayModeGetPixelWidth(mode) != desc->pixel_width ||
453 CGDisplayModeGetPixelHeight(mode) != desc->pixel_height)
454 return FALSE;
456 else
457 #endif
458 if (CGDisplayModeGetWidth(mode) != desc->pixel_width ||
459 CGDisplayModeGetHeight(mode) != desc->pixel_height)
460 return FALSE;
462 mode_pixel_encoding = CGDisplayModeCopyPixelEncoding(mode);
463 if (!CFEqual(mode_pixel_encoding, desc->pixel_encoding))
465 CFRelease(mode_pixel_encoding);
466 return FALSE;
468 CFRelease(mode_pixel_encoding);
470 return TRUE;
474 static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode)
476 CFStringRef pixel_encoding;
477 int bits_per_pixel = 0;
479 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
480 if (pixel_encoding)
482 if (CFEqual(pixel_encoding, CFSTR(kIO32BitFloatPixels)))
483 bits_per_pixel = 128;
484 else if (CFEqual(pixel_encoding, CFSTR(kIO16BitFloatPixels)))
485 bits_per_pixel = 64;
486 else if (CFEqual(pixel_encoding, CFSTR(kIO64BitDirectPixels)))
487 bits_per_pixel = 64;
488 else if (CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels)))
489 bits_per_pixel = 30;
490 else if (CFEqual(pixel_encoding, CFSTR(IO32BitDirectPixels)))
491 bits_per_pixel = 32;
492 else if (CFEqual(pixel_encoding, CFSTR(IO16BitDirectPixels)))
493 bits_per_pixel = 16;
494 else if (CFEqual(pixel_encoding, CFSTR(IO8BitIndexedPixels)))
495 bits_per_pixel = 8;
496 else if (CFEqual(pixel_encoding, CFSTR(IO4BitIndexedPixels)))
497 bits_per_pixel = 4;
498 else if (CFEqual(pixel_encoding, CFSTR(IO2BitIndexedPixels)))
499 bits_per_pixel = 2;
500 else if (CFEqual(pixel_encoding, CFSTR(IO1BitIndexedPixels)))
501 bits_per_pixel = 1;
503 CFRelease(pixel_encoding);
506 return bits_per_pixel;
510 static int get_default_bpp(void)
512 int ret;
514 if (!default_mode_bpp)
516 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
517 if (mode)
519 default_mode_bpp = display_mode_bits_per_pixel(mode);
520 CFRelease(mode);
523 if (!default_mode_bpp)
524 default_mode_bpp = 32;
527 ret = default_mode_bpp;
529 TRACE(" -> %d\n", ret);
530 return ret;
534 static BOOL display_mode_is_supported(CGDisplayModeRef display_mode)
536 uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
537 return (io_flags & kDisplayModeValidFlag) && (io_flags & kDisplayModeSafeFlag);
541 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
542 static CFDictionaryRef create_mode_dict(CGDisplayModeRef display_mode, BOOL is_original)
544 CFDictionaryRef ret;
545 SInt32 io_flags = CGDisplayModeGetIOFlags(display_mode);
546 SInt64 width = CGDisplayModeGetWidth(display_mode);
547 SInt64 height = CGDisplayModeGetHeight(display_mode);
548 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
549 CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
550 CFNumberRef cf_io_flags, cf_width, cf_height, cf_refresh;
552 if (retina_enabled && is_original)
554 width *= 2;
555 height *= 2;
558 io_flags &= kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeInterlacedFlag |
559 kDisplayModeStretchedFlag | kDisplayModeTelevisionFlag;
560 cf_io_flags = CFNumberCreate(NULL, kCFNumberSInt32Type, &io_flags);
561 cf_width = CFNumberCreate(NULL, kCFNumberSInt64Type, &width);
562 cf_height = CFNumberCreate(NULL, kCFNumberSInt64Type, &height);
563 cf_refresh = CFNumberCreate(NULL, kCFNumberDoubleType, &refresh_rate);
566 static const CFStringRef keys[] = {
567 CFSTR("io_flags"),
568 CFSTR("width"),
569 CFSTR("height"),
570 CFSTR("pixel_encoding"),
571 CFSTR("refresh_rate"),
573 const void* values[ARRAY_SIZE(keys)] = {
574 cf_io_flags,
575 cf_width,
576 cf_height,
577 pixel_encoding,
578 cf_refresh,
581 ret = CFDictionaryCreate(NULL, (const void**)keys, (const void**)values, ARRAY_SIZE(keys),
582 &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
585 CFRelease(pixel_encoding);
586 CFRelease(cf_io_flags);
587 CFRelease(cf_width);
588 CFRelease(cf_height);
589 CFRelease(cf_refresh);
591 return ret;
595 static BOOL mode_is_preferred(CGDisplayModeRef new_mode, CGDisplayModeRef old_mode,
596 struct display_mode_descriptor *original_mode_desc,
597 BOOL include_unsupported)
599 BOOL new_is_supported;
600 CFStringRef pixel_encoding;
601 size_t width_points, height_points;
602 size_t old_width_pixels, old_height_pixels, new_width_pixels, new_height_pixels;
603 BOOL old_size_same, new_size_same;
605 /* If a given mode is the user's default, then always list it in preference to any similar
606 modes that may exist. */
607 if (display_mode_matches_descriptor(new_mode, original_mode_desc))
608 return TRUE;
610 /* Skip unsupported modes unless told to do otherwise. */
611 new_is_supported = display_mode_is_supported(new_mode);
612 if (!new_is_supported && !include_unsupported)
613 return FALSE;
615 pixel_encoding = CGDisplayModeCopyPixelEncoding(new_mode);
616 if (pixel_encoding)
618 BOOL bpp30 = CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels));
619 CFRelease(pixel_encoding);
620 if (bpp30)
622 /* This is an odd pixel encoding. It seems it's only returned
623 when using kCGDisplayShowDuplicateLowResolutionModes. It's
624 32bpp in terms of the actual raster layout, but it's 10
625 bits per component. I think that no Windows program is
626 likely to need it and they will probably be confused by it.
627 Skip it. */
628 return FALSE;
632 if (!old_mode)
633 return TRUE;
635 /* Prefer the original mode over any similar mode. */
636 if (display_mode_matches_descriptor(old_mode, original_mode_desc))
637 return FALSE;
639 /* Prefer supported modes over similar unsupported ones. */
640 if (!new_is_supported && display_mode_is_supported(old_mode))
641 return FALSE;
643 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
644 is scaled. */
645 width_points = CGDisplayModeGetWidth(new_mode);
646 height_points = CGDisplayModeGetHeight(new_mode);
647 new_width_pixels = CGDisplayModeGetPixelWidth(new_mode);
648 new_height_pixels = CGDisplayModeGetPixelHeight(new_mode);
649 old_width_pixels = CGDisplayModeGetPixelWidth(old_mode);
650 old_height_pixels = CGDisplayModeGetPixelHeight(old_mode);
651 new_size_same = (new_width_pixels == width_points && new_height_pixels == height_points);
652 old_size_same = (old_width_pixels == width_points && old_height_pixels == height_points);
654 if (new_size_same && !old_size_same)
655 return TRUE;
657 if (!new_size_same && old_size_same)
658 return FALSE;
660 /* Otherwise, prefer the mode with the smaller pixel size. */
661 return new_width_pixels < old_width_pixels && new_height_pixels < old_height_pixels;
663 #endif
666 static CFComparisonResult mode_compare(const void *p1, const void *p2, void *context)
668 CGDisplayModeRef a = (CGDisplayModeRef)p1, b = (CGDisplayModeRef)p2;
669 size_t a_val, b_val;
670 double a_refresh_rate, b_refresh_rate;
672 /* Sort by bpp descending, */
673 a_val = display_mode_bits_per_pixel(a);
674 b_val = display_mode_bits_per_pixel(b);
675 if (a_val < b_val)
676 return kCFCompareGreaterThan;
677 else if (a_val > b_val)
678 return kCFCompareLessThan;
680 /* then width ascending, */
681 a_val = CGDisplayModeGetWidth(a);
682 b_val = CGDisplayModeGetWidth(b);
683 if (a_val < b_val)
684 return kCFCompareLessThan;
685 else if (a_val > b_val)
686 return kCFCompareGreaterThan;
688 /* then height ascending, */
689 a_val = CGDisplayModeGetHeight(a);
690 b_val = CGDisplayModeGetHeight(b);
691 if (a_val < b_val)
692 return kCFCompareLessThan;
693 else if (a_val > b_val)
694 return kCFCompareGreaterThan;
696 /* then refresh rate descending. */
697 a_refresh_rate = CGDisplayModeGetRefreshRate(a);
698 b_refresh_rate = CGDisplayModeGetRefreshRate(b);
699 if (a_refresh_rate < b_refresh_rate)
700 return kCFCompareGreaterThan;
701 else if (a_refresh_rate > b_refresh_rate)
702 return kCFCompareLessThan;
704 return kCFCompareEqualTo;
708 /***********************************************************************
709 * copy_display_modes
711 * Wrapper around CGDisplayCopyAllDisplayModes() to include additional
712 * modes on Retina-capable systems, but filter those which would confuse
713 * Windows apps (basically duplicates at different DPIs).
715 * For example, some Retina Macs support a 1920x1200 mode, but it's not
716 * returned from CGDisplayCopyAllDisplayModes() without special options.
717 * This is especially bad if that's the user's default mode, since then
718 * no "available" mode matches the initial settings.
720 * If include_unsupported is FALSE, display modes with IO flags that
721 * indicate that they are invalid or unsafe are filtered.
723 static CFArrayRef copy_display_modes(CGDirectDisplayID display, BOOL include_unsupported)
725 CFArrayRef modes = NULL;
727 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
728 if (&CGDisplayModeGetPixelWidth != NULL && &CGDisplayModeGetPixelHeight != NULL)
730 CFDictionaryRef options;
731 struct display_mode_descriptor* desc;
732 CFMutableDictionaryRef modes_by_size;
733 CFIndex i, count;
734 CGDisplayModeRef* mode_array;
736 options = CFDictionaryCreate(NULL, (const void**)&kCGDisplayShowDuplicateLowResolutionModes,
737 (const void**)&kCFBooleanTrue, 1, &kCFTypeDictionaryKeyCallBacks,
738 &kCFTypeDictionaryValueCallBacks);
740 modes = CGDisplayCopyAllDisplayModes(display, options);
741 if (options)
742 CFRelease(options);
743 if (!modes)
744 return NULL;
746 desc = create_original_display_mode_descriptor(display);
748 modes_by_size = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
749 count = CFArrayGetCount(modes);
750 for (i = 0; i < count; i++)
752 CGDisplayModeRef new_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
753 BOOL new_is_original = display_mode_matches_descriptor(new_mode, desc);
754 CFDictionaryRef key = create_mode_dict(new_mode, new_is_original);
755 CGDisplayModeRef old_mode = (CGDisplayModeRef)CFDictionaryGetValue(modes_by_size, key);
757 if (mode_is_preferred(new_mode, old_mode, desc, include_unsupported))
758 CFDictionarySetValue(modes_by_size, key, new_mode);
760 CFRelease(key);
763 free_display_mode_descriptor(desc);
764 CFRelease(modes);
766 count = CFDictionaryGetCount(modes_by_size);
767 mode_array = malloc(count * sizeof(mode_array[0]));
768 CFDictionaryGetKeysAndValues(modes_by_size, NULL, (const void **)mode_array);
769 modes = CFArrayCreate(NULL, (const void **)mode_array, count, &kCFTypeArrayCallBacks);
770 free(mode_array);
771 CFRelease(modes_by_size);
773 else
774 #endif
775 modes = CGDisplayCopyAllDisplayModes(display, NULL);
777 if (modes)
779 CFIndex count = CFArrayGetCount(modes);
780 CFMutableArrayRef sorted_modes = CFArrayCreateMutableCopy(NULL, count, modes);
781 CFRelease(modes);
782 CFArraySortValues(sorted_modes, CFRangeMake(0, count), mode_compare, NULL);
783 return sorted_modes;
786 return NULL;
790 void check_retina_status(void)
792 if (retina_enabled)
794 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(kCGDirectMainDisplay);
795 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
796 BOOL new_value = display_mode_matches_descriptor(mode, desc);
798 CGDisplayModeRelease(mode);
799 free_display_mode_descriptor(desc);
801 if (new_value != retina_on)
802 macdrv_set_cocoa_retina_mode(new_value);
806 static BOOL get_primary_adapter(WCHAR *name)
808 DISPLAY_DEVICEW dd;
809 DWORD i;
811 dd.cb = sizeof(dd);
812 for (i = 0; !NtUserEnumDisplayDevices(NULL, i, &dd, 0); ++i)
814 if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
816 lstrcpyW(name, dd.DeviceName);
817 return TRUE;
821 return FALSE;
824 static BOOL is_detached_mode(const DEVMODEW *mode)
826 return mode->dmFields & DM_POSITION &&
827 mode->dmFields & DM_PELSWIDTH &&
828 mode->dmFields & DM_PELSHEIGHT &&
829 mode->dmPelsWidth == 0 &&
830 mode->dmPelsHeight == 0;
833 /***********************************************************************
834 * ChangeDisplaySettingsEx (MACDRV.@)
837 LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
838 HWND hwnd, DWORD flags, LPVOID lpvoid)
840 WCHAR primary_adapter[CCHDEVICENAME];
841 LONG ret = DISP_CHANGE_BADMODE;
842 DEVMODEW default_mode;
843 int bpp;
844 struct macdrv_display *displays;
845 int num_displays;
846 CFArrayRef display_modes;
847 struct display_mode_descriptor* desc;
848 CFIndex count, i, best;
849 CGDisplayModeRef best_display_mode;
850 uint32_t best_io_flags;
851 BOOL best_is_original;
853 TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid);
855 init_original_display_mode();
857 if (!get_primary_adapter(primary_adapter))
858 return DISP_CHANGE_FAILED;
860 if (!devname && !devmode)
862 UNICODE_STRING str;
863 memset(&default_mode, 0, sizeof(default_mode));
864 default_mode.dmSize = sizeof(default_mode);
865 RtlInitUnicodeString(&str, primary_adapter);
866 if (!NtUserEnumDisplaySettings(&str, ENUM_REGISTRY_SETTINGS, &default_mode, 0))
868 ERR("Default mode not found for %s!\n", wine_dbgstr_w(primary_adapter));
869 return DISP_CHANGE_BADMODE;
872 devname = primary_adapter;
873 devmode = &default_mode;
876 if (is_detached_mode(devmode))
878 FIXME("Detaching adapters is currently unsupported.\n");
879 return DISP_CHANGE_SUCCESSFUL;
882 if (macdrv_get_displays(&displays, &num_displays))
883 return DISP_CHANGE_FAILED;
885 display_modes = copy_display_modes(displays[0].displayID, FALSE);
886 if (!display_modes)
888 macdrv_free_displays(displays);
889 return DISP_CHANGE_FAILED;
892 pthread_mutex_lock(&modes_mutex);
893 bpp = get_default_bpp();
894 pthread_mutex_unlock(&modes_mutex);
895 if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel != bpp)
896 TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, devmode->dmBitsPerPel);
898 TRACE("looking for %dx%dx%dbpp @%d Hz",
899 (devmode->dmFields & DM_PELSWIDTH ? devmode->dmPelsWidth : 0),
900 (devmode->dmFields & DM_PELSHEIGHT ? devmode->dmPelsHeight : 0),
901 bpp,
902 (devmode->dmFields & DM_DISPLAYFREQUENCY ? devmode->dmDisplayFrequency : 0));
903 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
904 TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un");
905 if (devmode->dmFields & DM_DISPLAYFLAGS)
906 TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
907 TRACE("\n");
909 desc = create_original_display_mode_descriptor(displays[0].displayID);
911 best_display_mode = NULL;
912 count = CFArrayGetCount(display_modes);
913 for (i = 0; i < count; i++)
915 CGDisplayModeRef display_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(display_modes, i);
916 BOOL is_original = display_mode_matches_descriptor(display_mode, desc);
917 uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
918 int mode_bpp = display_mode_bits_per_pixel(display_mode);
919 size_t width = CGDisplayModeGetWidth(display_mode);
920 size_t height = CGDisplayModeGetHeight(display_mode);
922 if (is_original && retina_enabled)
924 width *= 2;
925 height *= 2;
928 if (bpp != mode_bpp)
929 continue;
931 if (devmode->dmFields & DM_PELSWIDTH)
933 if (devmode->dmPelsWidth != width)
934 continue;
936 if (devmode->dmFields & DM_PELSHEIGHT)
938 if (devmode->dmPelsHeight != height)
939 continue;
941 if ((devmode->dmFields & DM_DISPLAYFREQUENCY) &&
942 devmode->dmDisplayFrequency != 0 &&
943 devmode->dmDisplayFrequency != 1)
945 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
946 if (!refresh_rate)
947 refresh_rate = 60;
948 if (devmode->dmDisplayFrequency != (DWORD)refresh_rate)
949 continue;
951 if (devmode->dmFields & DM_DISPLAYFLAGS)
953 if (!(devmode->dmDisplayFlags & DM_INTERLACED) != !(io_flags & kDisplayModeInterlacedFlag))
954 continue;
956 else if (best_display_mode)
958 if (io_flags & kDisplayModeInterlacedFlag && !(best_io_flags & kDisplayModeInterlacedFlag))
959 continue;
960 else if (!(io_flags & kDisplayModeInterlacedFlag) && best_io_flags & kDisplayModeInterlacedFlag)
961 goto better;
963 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
965 if (!(devmode->dmDisplayFixedOutput == DMDFO_STRETCH) != !(io_flags & kDisplayModeStretchedFlag))
966 continue;
968 else if (best_display_mode)
970 if (io_flags & kDisplayModeStretchedFlag && !(best_io_flags & kDisplayModeStretchedFlag))
971 continue;
972 else if (!(io_flags & kDisplayModeStretchedFlag) && best_io_flags & kDisplayModeStretchedFlag)
973 goto better;
976 if (best_display_mode)
977 continue;
979 better:
980 best_display_mode = display_mode;
981 best = i;
982 best_io_flags = io_flags;
983 best_is_original = is_original;
986 if (best_display_mode)
988 /* we have a valid mode */
989 TRACE("Requested display settings match mode %ld\n", best);
991 if ((flags & CDS_UPDATEREGISTRY) && !write_registry_settings(devname, devmode))
993 WARN("Failed to update registry\n");
994 ret = DISP_CHANGE_NOTUPDATED;
996 else if (flags & (CDS_TEST | CDS_NORESET))
997 ret = DISP_CHANGE_SUCCESSFUL;
998 else if (wcsicmp(primary_adapter, devname))
1000 FIXME("Changing non-primary adapter settings is currently unsupported.\n");
1001 ret = DISP_CHANGE_SUCCESSFUL;
1003 else if (macdrv_set_display_mode(&displays[0], best_display_mode))
1005 int mode_bpp = display_mode_bits_per_pixel(best_display_mode);
1006 size_t width = CGDisplayModeGetWidth(best_display_mode);
1007 size_t height = CGDisplayModeGetHeight(best_display_mode);
1009 macdrv_init_display_devices(TRUE);
1011 if (best_is_original && retina_enabled)
1013 width *= 2;
1014 height *= 2;
1017 send_message(NtUserGetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
1018 MAKELPARAM(width, height));
1019 ret = DISP_CHANGE_SUCCESSFUL;
1021 else
1023 WARN("Failed to set display mode\n");
1024 ret = DISP_CHANGE_FAILED;
1027 else
1029 /* no valid modes found */
1030 ERR("No matching mode found %ux%ux%d @%u!\n", devmode->dmPelsWidth, devmode->dmPelsHeight,
1031 bpp, devmode->dmDisplayFrequency);
1034 free_display_mode_descriptor(desc);
1035 CFRelease(display_modes);
1036 macdrv_free_displays(displays);
1038 return ret;
1041 /***********************************************************************
1042 * EnumDisplaySettingsEx (MACDRV.@)
1045 BOOL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, DEVMODEW *devmode, DWORD flags)
1047 static const WCHAR dev_name[CCHDEVICENAME] =
1048 { 'W','i','n','e',' ','M','a','c',' ','d','r','i','v','e','r',0 };
1049 struct macdrv_display *displays = NULL;
1050 int num_displays;
1051 CGDisplayModeRef display_mode;
1052 int display_mode_bpp;
1053 BOOL synthesized = FALSE;
1054 double rotation;
1055 uint32_t io_flags;
1057 TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname), mode, devmode, devmode->dmSize, flags);
1059 init_original_display_mode();
1061 memcpy(devmode->dmDeviceName, dev_name, sizeof(dev_name));
1062 devmode->dmSpecVersion = DM_SPECVERSION;
1063 devmode->dmDriverVersion = DM_SPECVERSION;
1064 devmode->dmSize = FIELD_OFFSET(DEVMODEW, dmICMMethod);
1065 devmode->dmDriverExtra = 0;
1066 memset(&devmode->dmFields, 0, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields));
1068 if (mode == ENUM_REGISTRY_SETTINGS)
1070 TRACE("mode %d (registry) -- getting default mode\n", mode);
1071 return read_registry_settings(devname, devmode);
1074 if (macdrv_get_displays(&displays, &num_displays))
1075 goto failed;
1077 if (mode == ENUM_CURRENT_SETTINGS)
1079 TRACE("mode %d (current) -- getting current mode\n", mode);
1080 display_mode = CGDisplayCopyDisplayMode(displays[0].displayID);
1081 display_mode_bpp = display_mode_bits_per_pixel(display_mode);
1083 else
1085 DWORD count, i;
1087 pthread_mutex_lock(&modes_mutex);
1089 if (mode == 0 || !modes)
1091 if (modes) CFRelease(modes);
1092 modes = copy_display_modes(displays[0].displayID, (flags & EDS_RAWMODE) != 0);
1093 modes_has_8bpp = modes_has_16bpp = FALSE;
1095 if (modes)
1097 count = CFArrayGetCount(modes);
1098 for (i = 0; i < count && !(modes_has_8bpp && modes_has_16bpp); i++)
1100 CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
1101 int bpp = display_mode_bits_per_pixel(mode);
1102 if (bpp == 8)
1103 modes_has_8bpp = TRUE;
1104 else if (bpp == 16)
1105 modes_has_16bpp = TRUE;
1110 display_mode = NULL;
1111 if (modes)
1113 int default_bpp;
1114 DWORD seen_modes = 0;
1116 count = CFArrayGetCount(modes);
1117 for (i = 0; i < count; i++)
1119 CGDisplayModeRef candidate = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
1121 seen_modes++;
1122 if (seen_modes > mode)
1124 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1125 display_mode_bpp = display_mode_bits_per_pixel(display_mode);
1126 break;
1130 default_bpp = get_default_bpp();
1132 /* If all the real modes are exhausted, synthesize lower bpp modes. */
1133 if (!display_mode && (!modes_has_16bpp || !modes_has_8bpp))
1135 /* We want to synthesize higher depths first. */
1136 int synth_bpps[] = { modes_has_16bpp ? 0 : 16, modes_has_8bpp ? 0 : 8 };
1137 size_t synth_bpp_idx;
1138 for (synth_bpp_idx = 0; synth_bpp_idx < 2; synth_bpp_idx++)
1140 int synth_bpp = synth_bpps[synth_bpp_idx];
1141 if (synth_bpp == 0)
1142 continue;
1144 for (i = 0; i < count; i++)
1146 CGDisplayModeRef candidate = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
1147 /* We only synthesize modes from those having the default bpp. */
1148 if (display_mode_bits_per_pixel(candidate) != default_bpp)
1149 continue;
1151 seen_modes++;
1152 if (seen_modes > mode)
1154 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1155 display_mode_bpp = synth_bpp;
1156 synthesized = TRUE;
1157 break;
1161 if (display_mode)
1162 break;
1167 pthread_mutex_unlock(&modes_mutex);
1170 if (!display_mode)
1171 goto failed;
1173 /* We currently only report modes for the primary display, so it's at (0, 0). */
1174 devmode->dmPosition.x = 0;
1175 devmode->dmPosition.y = 0;
1176 devmode->dmFields |= DM_POSITION;
1178 rotation = CGDisplayRotation(displays[0].displayID);
1179 devmode->dmDisplayOrientation = ((int)((rotation / 90) + 0.5)) % 4;
1180 devmode->dmFields |= DM_DISPLAYORIENTATION;
1182 io_flags = CGDisplayModeGetIOFlags(display_mode);
1183 if (io_flags & kDisplayModeStretchedFlag)
1184 devmode->dmDisplayFixedOutput = DMDFO_STRETCH;
1185 else
1186 devmode->dmDisplayFixedOutput = DMDFO_CENTER;
1187 devmode->dmFields |= DM_DISPLAYFIXEDOUTPUT;
1189 devmode->dmBitsPerPel = display_mode_bpp;
1190 if (devmode->dmBitsPerPel)
1191 devmode->dmFields |= DM_BITSPERPEL;
1193 devmode->dmPelsWidth = CGDisplayModeGetWidth(display_mode);
1194 devmode->dmPelsHeight = CGDisplayModeGetHeight(display_mode);
1195 if (retina_enabled)
1197 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(displays[0].displayID);
1198 if (display_mode_matches_descriptor(display_mode, desc))
1200 devmode->dmPelsWidth *= 2;
1201 devmode->dmPelsHeight *= 2;
1203 free_display_mode_descriptor(desc);
1205 devmode->dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
1207 devmode->dmDisplayFlags = 0;
1208 if (io_flags & kDisplayModeInterlacedFlag)
1209 devmode->dmDisplayFlags |= DM_INTERLACED;
1210 devmode->dmFields |= DM_DISPLAYFLAGS;
1212 devmode->dmDisplayFrequency = CGDisplayModeGetRefreshRate(display_mode);
1213 if (!devmode->dmDisplayFrequency)
1214 devmode->dmDisplayFrequency = 60;
1215 devmode->dmFields |= DM_DISPLAYFREQUENCY;
1217 CFRelease(display_mode);
1218 macdrv_free_displays(displays);
1220 TRACE("mode %d -- %dx%dx%dbpp @%d Hz", mode,
1221 devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel,
1222 devmode->dmDisplayFrequency);
1223 if (devmode->dmDisplayOrientation)
1224 TRACE(" rotated %u degrees", devmode->dmDisplayOrientation * 90);
1225 if (devmode->dmDisplayFixedOutput == DMDFO_STRETCH)
1226 TRACE(" stretched");
1227 if (devmode->dmDisplayFlags & DM_INTERLACED)
1228 TRACE(" interlaced");
1229 if (synthesized)
1230 TRACE(" (synthesized)");
1231 TRACE("\n");
1233 return TRUE;
1235 failed:
1236 TRACE("mode %d -- not present\n", mode);
1237 if (displays) macdrv_free_displays(displays);
1238 SetLastError(ERROR_NO_MORE_FILES);
1239 return FALSE;
1243 /***********************************************************************
1244 * GetDeviceGammaRamp (MACDRV.@)
1246 BOOL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1248 BOOL ret = FALSE;
1249 DDGAMMARAMP *r = ramp;
1250 struct macdrv_display *displays;
1251 int num_displays;
1252 uint32_t mac_entries;
1253 int win_entries = ARRAY_SIZE(r->red);
1254 CGGammaValue *red, *green, *blue;
1255 CGError err;
1256 int win_entry;
1258 TRACE("dev %p ramp %p\n", dev, ramp);
1260 if (macdrv_get_displays(&displays, &num_displays))
1262 WARN("failed to get Mac displays\n");
1263 return FALSE;
1266 mac_entries = CGDisplayGammaTableCapacity(displays[0].displayID);
1267 red = malloc(mac_entries * sizeof(red[0]) * 3);
1268 if (!red)
1269 goto done;
1270 green = red + mac_entries;
1271 blue = green + mac_entries;
1273 err = CGGetDisplayTransferByTable(displays[0].displayID, mac_entries, red, green,
1274 blue, &mac_entries);
1275 if (err != kCGErrorSuccess)
1277 WARN("failed to get Mac gamma table: %d\n", err);
1278 goto done;
1281 if (mac_entries == win_entries)
1283 for (win_entry = 0; win_entry < win_entries; win_entry++)
1285 r->red[win_entry] = red[win_entry] * 65535 + 0.5;
1286 r->green[win_entry] = green[win_entry] * 65535 + 0.5;
1287 r->blue[win_entry] = blue[win_entry] * 65535 + 0.5;
1290 else
1292 for (win_entry = 0; win_entry < win_entries; win_entry++)
1294 double mac_pos = win_entry * (mac_entries - 1) / (double)(win_entries - 1);
1295 int mac_entry = mac_pos;
1296 double red_value, green_value, blue_value;
1298 if (mac_entry == mac_entries - 1)
1300 red_value = red[mac_entry];
1301 green_value = green[mac_entry];
1302 blue_value = blue[mac_entry];
1304 else
1306 double distance = mac_pos - mac_entry;
1308 red_value = red[mac_entry] * (1 - distance) + red[mac_entry + 1] * distance;
1309 green_value = green[mac_entry] * (1 - distance) + green[mac_entry + 1] * distance;
1310 blue_value = blue[mac_entry] * (1 - distance) + blue[mac_entry + 1] * distance;
1313 r->red[win_entry] = red_value * 65535 + 0.5;
1314 r->green[win_entry] = green_value * 65535 + 0.5;
1315 r->blue[win_entry] = blue_value * 65535 + 0.5;
1319 ret = TRUE;
1321 done:
1322 free(red);
1323 macdrv_free_displays(displays);
1324 return ret;
1327 /***********************************************************************
1328 * SetDeviceGammaRamp (MACDRV.@)
1330 BOOL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1332 DDGAMMARAMP *r = ramp;
1333 struct macdrv_display *displays;
1334 int num_displays;
1335 int win_entries = ARRAY_SIZE(r->red);
1336 CGGammaValue *red, *green, *blue;
1337 int i;
1338 CGError err = kCGErrorFailure;
1340 TRACE("dev %p ramp %p\n", dev, ramp);
1342 if (!allow_set_gamma)
1344 TRACE("disallowed by registry setting\n");
1345 return FALSE;
1348 if (macdrv_get_displays(&displays, &num_displays))
1350 WARN("failed to get Mac displays\n");
1351 return FALSE;
1354 red = malloc(win_entries * sizeof(red[0]) * 3);
1355 if (!red)
1356 goto done;
1357 green = red + win_entries;
1358 blue = green + win_entries;
1360 for (i = 0; i < win_entries; i++)
1362 red[i] = r->red[i] / 65535.0;
1363 green[i] = r->green[i] / 65535.0;
1364 blue[i] = r->blue[i] / 65535.0;
1367 err = CGSetDisplayTransferByTable(displays[0].displayID, win_entries, red, green, blue);
1368 if (err != kCGErrorSuccess)
1369 WARN("failed to set display gamma table: %d\n", err);
1371 done:
1372 free(red);
1373 macdrv_free_displays(displays);
1374 return (err == kCGErrorSuccess);
1377 /***********************************************************************
1378 * init_registry_display_settings
1380 * Initialize registry display settings when new display devices are added.
1382 static void init_registry_display_settings(void)
1384 DEVMODEW dm = {.dmSize = sizeof(dm)};
1385 DISPLAY_DEVICEW dd = {sizeof(dd)};
1386 UNICODE_STRING str;
1387 DWORD i = 0;
1388 LONG ret;
1390 while (!NtUserEnumDisplayDevices(NULL, i++, &dd, 0))
1392 RtlInitUnicodeString(&str, dd.DeviceName);
1394 /* Skip if the device already has registry display settings */
1395 if (NtUserEnumDisplaySettings(&str, ENUM_REGISTRY_SETTINGS, &dm, 0))
1396 continue;
1398 if (!NtUserEnumDisplaySettings(&str, ENUM_CURRENT_SETTINGS, &dm, 0))
1400 ERR("Failed to query current display settings for %s.\n", wine_dbgstr_w(dd.DeviceName));
1401 continue;
1404 TRACE("Device %s current display mode %ux%u %ubits %uHz at %d,%d.\n",
1405 wine_dbgstr_w(dd.DeviceName), dm.dmPelsWidth, dm.dmPelsHeight, dm.dmBitsPerPel,
1406 dm.dmDisplayFrequency, dm.dmPosition.x, dm.dmPosition.y);
1408 ret = NtUserChangeDisplaySettings(&str, &dm, NULL,
1409 CDS_GLOBAL | CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
1410 if (ret != DISP_CHANGE_SUCCESSFUL)
1411 ERR("Failed to save registry display settings for %s, returned %d.\n",
1412 wine_dbgstr_w(dd.DeviceName), ret);
1416 /***********************************************************************
1417 * macdrv_displays_changed
1419 * Handler for DISPLAYS_CHANGED events.
1421 void macdrv_displays_changed(const macdrv_event *event)
1423 HWND hwnd = NtUserGetDesktopWindow();
1425 /* A system display change will get delivered to all GUI-attached threads,
1426 so the desktop-window-owning thread will get it and all others should
1427 ignore it. A synthesized display change event due to activation
1428 will only get delivered to the activated process. So, it needs to
1429 process it (by sending it to the desktop window). */
1430 if (event->displays_changed.activating ||
1431 NtUserGetWindowThread(hwnd, NULL) == GetCurrentThreadId())
1433 CGDirectDisplayID mainDisplay = CGMainDisplayID();
1434 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(mainDisplay);
1435 size_t width = CGDisplayModeGetWidth(mode);
1436 size_t height = CGDisplayModeGetHeight(mode);
1437 int mode_bpp = display_mode_bits_per_pixel(mode);
1438 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(mainDisplay);
1439 BOOL is_original = display_mode_matches_descriptor(mode, desc);
1441 free_display_mode_descriptor(desc);
1442 CGDisplayModeRelease(mode);
1444 macdrv_init_display_devices(TRUE);
1445 init_registry_display_settings();
1447 if (is_original && retina_enabled)
1449 width *= 2;
1450 height *= 2;
1453 send_message(hwnd, WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
1454 MAKELPARAM(width, height));
1458 static BOOL force_display_devices_refresh;
1460 void macdrv_UpdateDisplayDevices( const struct gdi_device_manager *device_manager,
1461 BOOL force, void *param )
1463 struct macdrv_adapter *adapters, *adapter;
1464 struct macdrv_monitor *monitors, *monitor;
1465 struct macdrv_gpu *gpus, *gpu;
1466 INT gpu_count, adapter_count, monitor_count;
1467 DWORD len;
1469 if (!force && !force_display_devices_refresh) return;
1470 force_display_devices_refresh = FALSE;
1472 /* Initialize GPUs */
1473 if (macdrv_get_gpus(&gpus, &gpu_count))
1475 ERR("could not get GPUs\n");
1476 return;
1478 TRACE("GPU count: %d\n", gpu_count);
1480 for (gpu = gpus; gpu < gpus + gpu_count; gpu++)
1482 struct gdi_gpu gdi_gpu =
1484 .id = gpu->id,
1485 .vendor_id = gpu->vendor_id,
1486 .device_id = gpu->device_id,
1487 .subsys_id = gpu->subsys_id,
1488 .revision_id = gpu->revision_id,
1490 RtlUTF8ToUnicodeN(gdi_gpu.name, sizeof(gdi_gpu.name), &len, gpu->name, strlen(gpu->name));
1491 device_manager->add_gpu(&gdi_gpu, param);
1493 /* Initialize adapters */
1494 if (macdrv_get_adapters(gpu->id, &adapters, &adapter_count)) break;
1495 TRACE("GPU: %llx %s, adapter count: %d\n", gpu->id, debugstr_a(gpu->name), adapter_count);
1497 for (adapter = adapters; adapter < adapters + adapter_count; adapter++)
1499 struct gdi_adapter gdi_adapter =
1501 .id = adapter->id,
1502 .state_flags = adapter->state_flags,
1504 device_manager->add_adapter( &gdi_adapter, param );
1506 if (macdrv_get_monitors(adapter->id, &monitors, &monitor_count)) break;
1507 TRACE("adapter: %#x, monitor count: %d\n", adapter->id, monitor_count);
1509 /* Initialize monitors */
1510 for (monitor = monitors; monitor < monitors + monitor_count; monitor++)
1512 struct gdi_monitor gdi_monitor =
1514 .rc_monitor = rect_from_cgrect(monitor->rc_monitor),
1515 .rc_work = rect_from_cgrect(monitor->rc_work),
1516 .state_flags = monitor->state_flags,
1518 RtlUTF8ToUnicodeN(gdi_monitor.name, sizeof(gdi_monitor.name), &len,
1519 monitor->name, strlen(monitor->name));
1520 TRACE("monitor: %s\n", debugstr_a(monitor->name));
1521 device_manager->add_monitor( &gdi_monitor, param );
1524 macdrv_free_monitors(monitors);
1527 macdrv_free_adapters(adapters);
1530 macdrv_free_gpus(gpus);
1533 /***********************************************************************
1534 * macdrv_init_display_devices
1536 * Initialize display device registry data.
1538 void macdrv_init_display_devices(BOOL force)
1540 UINT32 num_path, num_mode;
1542 if (force) force_display_devices_refresh = TRUE;
1543 /* trigger refresh in win32u */
1544 NtUserGetDisplayConfigBufferSizes( QDC_ONLY_ACTIVE_PATHS, &num_path, &num_mode );