vbscript: Fix memory leak in owned safearray iterator.
[wine.git] / dlls / winemac.drv / display.c
blobdab81cc8ffb250fd4790d635d6b447cab443fa56
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);
37 #define NEXT_DEVMODEW(mode) ((DEVMODEW *)((char *)((mode) + 1) + (mode)->dmDriverExtra))
39 struct display_mode_descriptor
41 DWORD width;
42 DWORD height;
43 DWORD pixel_width;
44 DWORD pixel_height;
45 DWORD io_flags;
46 double refresh;
47 CFStringRef pixel_encoding;
50 static const WCHAR initial_mode_keyW[] = {'I','n','i','t','i','a','l',' ','D','i','s','p','l','a','y',
51 ' ','M','o','d','e'};
52 static const WCHAR pixelencodingW[] = {'P','i','x','e','l','E','n','c','o','d','i','n','g',0};
54 static BOOL inited_original_display_mode;
57 static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode)
59 CFStringRef pixel_encoding;
60 int bits_per_pixel = 0;
62 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
63 if (pixel_encoding)
65 if (CFEqual(pixel_encoding, CFSTR(kIO32BitFloatPixels)))
66 bits_per_pixel = 128;
67 else if (CFEqual(pixel_encoding, CFSTR(kIO16BitFloatPixels)))
68 bits_per_pixel = 64;
69 else if (CFEqual(pixel_encoding, CFSTR(kIO64BitDirectPixels)))
70 bits_per_pixel = 64;
71 else if (CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels)))
72 bits_per_pixel = 30;
73 else if (CFEqual(pixel_encoding, CFSTR(IO32BitDirectPixels)))
74 bits_per_pixel = 32;
75 else if (CFEqual(pixel_encoding, CFSTR(IO16BitDirectPixels)))
76 bits_per_pixel = 16;
77 else if (CFEqual(pixel_encoding, CFSTR(IO8BitIndexedPixels)))
78 bits_per_pixel = 8;
79 else if (CFEqual(pixel_encoding, CFSTR(IO4BitIndexedPixels)))
80 bits_per_pixel = 4;
81 else if (CFEqual(pixel_encoding, CFSTR(IO2BitIndexedPixels)))
82 bits_per_pixel = 2;
83 else if (CFEqual(pixel_encoding, CFSTR(IO1BitIndexedPixels)))
84 bits_per_pixel = 1;
86 CFRelease(pixel_encoding);
89 return bits_per_pixel;
93 static BOOL display_mode_is_supported(CGDisplayModeRef display_mode)
95 uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
96 return (io_flags & kDisplayModeValidFlag) && (io_flags & kDisplayModeSafeFlag);
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 if (!display_mode_is_supported(display_mode))
128 devmode->dmDisplayFlags |= WINE_DM_UNSUPPORTED;
129 devmode->dmFields |= DM_DISPLAYFLAGS;
131 devmode->dmDisplayFrequency = CGDisplayModeGetRefreshRate(display_mode);
132 if (!devmode->dmDisplayFrequency)
133 devmode->dmDisplayFrequency = 60;
134 devmode->dmFields |= DM_DISPLAYFREQUENCY;
138 static BOOL set_setting_value(HKEY hkey, const char *name, DWORD val)
140 WCHAR nameW[128];
141 UNICODE_STRING str = { asciiz_to_unicode(nameW, name) - sizeof(WCHAR), sizeof(nameW), nameW };
142 return !NtSetValueKey(hkey, &str, 0, REG_DWORD, &val, sizeof(val));
146 static BOOL write_display_settings(HKEY parent_hkey, CGDirectDisplayID displayID)
148 BOOL ret = FALSE;
149 char display_key_name[19];
150 HKEY display_hkey;
151 CGDisplayModeRef display_mode;
152 UNICODE_STRING str;
153 DWORD val;
154 CFStringRef pixel_encoding;
155 size_t len;
156 WCHAR* buf = NULL;
158 snprintf(display_key_name, sizeof(display_key_name), "Display 0x%08x", CGDisplayUnitNumber(displayID));
159 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
160 if (!(display_hkey = reg_create_ascii_key(parent_hkey, display_key_name, REG_OPTION_VOLATILE, NULL)))
161 return FALSE;
163 display_mode = CGDisplayCopyDisplayMode(displayID);
164 if (!display_mode)
165 goto fail;
167 val = CGDisplayModeGetWidth(display_mode);
168 if (!set_setting_value(display_hkey, "Width", val))
169 goto fail;
170 val = CGDisplayModeGetHeight(display_mode);
171 if (!set_setting_value(display_hkey, "Height", val))
172 goto fail;
173 val = CGDisplayModeGetRefreshRate(display_mode) * 100;
174 if (!set_setting_value(display_hkey, "RefreshRateTimes100", val))
175 goto fail;
176 val = CGDisplayModeGetIOFlags(display_mode);
177 if (!set_setting_value(display_hkey, "IOFlags", val))
178 goto fail;
179 val = CGDisplayModeGetPixelWidth(display_mode);
180 if (!set_setting_value(display_hkey, "PixelWidth", val))
181 goto fail;
182 val = CGDisplayModeGetPixelHeight(display_mode);
183 if (!set_setting_value(display_hkey, "PixelHeight", val))
184 goto fail;
186 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
187 len = CFStringGetLength(pixel_encoding);
188 buf = malloc((len + 1) * sizeof(WCHAR));
189 CFStringGetCharacters(pixel_encoding, CFRangeMake(0, len), (UniChar*)buf);
190 buf[len] = 0;
191 CFRelease(pixel_encoding);
192 RtlInitUnicodeString(&str, pixelencodingW);
193 if (NtSetValueKey(display_hkey, &str, 0, REG_SZ, (const BYTE*)buf, (len + 1) * sizeof(WCHAR)))
194 goto fail;
196 ret = TRUE;
198 fail:
199 free(buf);
200 if (display_mode) CGDisplayModeRelease(display_mode);
201 NtClose(display_hkey);
202 if (!ret)
204 WCHAR nameW[64];
205 reg_delete_tree(parent_hkey, nameW, asciiz_to_unicode(nameW, display_key_name) - sizeof(WCHAR));
207 return ret;
211 static void init_original_display_mode(void)
213 BOOL success = FALSE;
214 HKEY mac_driver_hkey, parent_hkey;
215 DWORD disposition;
216 struct macdrv_display *displays = NULL;
217 int num_displays, i;
219 if (inited_original_display_mode)
220 return;
222 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver */
223 mac_driver_hkey = reg_create_ascii_key(NULL, "\\Registry\\Machine\\Software\\Wine\\Mac Driver",
224 0, NULL);
225 if (!mac_driver_hkey)
226 return;
228 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode */
229 if (!(parent_hkey = reg_create_key(mac_driver_hkey, initial_mode_keyW, sizeof(initial_mode_keyW),
230 REG_OPTION_VOLATILE, &disposition)))
232 parent_hkey = NULL;
233 goto fail;
236 /* If we didn't create a new key, then it already existed. Something already stored
237 the initial display mode since Wine was started. We don't want to overwrite it. */
238 if (disposition != REG_CREATED_NEW_KEY)
239 goto done;
241 if (macdrv_get_displays(&displays, &num_displays))
242 goto fail;
244 for (i = 0; i < num_displays; i++)
246 if (!write_display_settings(parent_hkey, displays[i].displayID))
247 goto fail;
250 done:
251 success = TRUE;
253 fail:
254 macdrv_free_displays(displays);
255 NtClose(parent_hkey);
256 if (!success && parent_hkey)
257 reg_delete_tree(mac_driver_hkey, initial_mode_keyW, sizeof(initial_mode_keyW));
258 NtClose(mac_driver_hkey);
259 if (success)
260 inited_original_display_mode = TRUE;
264 static BOOL read_dword(HKEY hkey, const char* name, DWORD* val)
266 char buffer[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(*val)])];
267 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
268 WCHAR nameW[64];
269 asciiz_to_unicode(nameW, name);
270 if (query_reg_value(hkey, nameW, value, sizeof(buffer)) != sizeof(*val) || value->Type != REG_DWORD)
271 return FALSE;
272 *val = *(DWORD *)value->Data;
273 return TRUE;
277 static void free_display_mode_descriptor(struct display_mode_descriptor* desc)
279 if (desc)
281 if (desc->pixel_encoding)
282 CFRelease(desc->pixel_encoding);
283 free(desc);
288 static struct display_mode_descriptor* create_original_display_mode_descriptor(CGDirectDisplayID displayID)
290 static const char display_key_format[] =
291 "\\Registry\\Machine\\Software\\Wine\\Mac Driver\\Initial Display Mode\\Display 0x%08x";
292 struct display_mode_descriptor* ret = NULL;
293 struct display_mode_descriptor* desc;
294 char display_key[sizeof(display_key_format) + 10];
295 WCHAR nameW[ARRAYSIZE(display_key)];
296 char buffer[4096];
297 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
298 HKEY hkey;
299 DWORD refresh100;
301 init_original_display_mode();
303 snprintf(display_key, sizeof(display_key), display_key_format, CGDisplayUnitNumber(displayID));
304 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
305 if (!(hkey = reg_open_key(NULL, nameW, asciiz_to_unicode(nameW, display_key) - sizeof(WCHAR))))
306 return NULL;
308 desc = malloc(sizeof(*desc));
309 desc->pixel_encoding = NULL;
311 if (!read_dword(hkey, "Width", &desc->width) ||
312 !read_dword(hkey, "Height", &desc->height) ||
313 !read_dword(hkey, "RefreshRateTimes100", &refresh100) ||
314 !read_dword(hkey, "IOFlags", &desc->io_flags))
315 goto done;
316 if (refresh100)
317 desc->refresh = refresh100 / 100.0;
318 else
319 desc->refresh = 60;
321 if (!read_dword(hkey, "PixelWidth", &desc->pixel_width) ||
322 !read_dword(hkey, "PixelHeight", &desc->pixel_height))
324 desc->pixel_width = desc->width;
325 desc->pixel_height = desc->height;
328 if (!query_reg_value(hkey, pixelencodingW, value, sizeof(buffer)) || value->Type != REG_SZ)
329 goto done;
330 desc->pixel_encoding = CFStringCreateWithCharacters(NULL, (const UniChar*)value->Data,
331 lstrlenW((const WCHAR *)value->Data));
333 ret = desc;
335 done:
336 if (!ret)
337 free_display_mode_descriptor(desc);
338 NtClose(hkey);
339 return ret;
343 static BOOL display_mode_matches_descriptor(CGDisplayModeRef mode, const struct display_mode_descriptor* desc)
345 DWORD mode_io_flags;
346 double mode_refresh;
347 CFStringRef mode_pixel_encoding;
349 if (!desc)
350 return FALSE;
352 if (CGDisplayModeGetWidth(mode) != desc->width ||
353 CGDisplayModeGetHeight(mode) != desc->height)
354 return FALSE;
356 mode_io_flags = CGDisplayModeGetIOFlags(mode);
357 if ((desc->io_flags ^ mode_io_flags) & (kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeStretchedFlag |
358 kDisplayModeInterlacedFlag | kDisplayModeTelevisionFlag))
359 return FALSE;
361 mode_refresh = CGDisplayModeGetRefreshRate(mode);
362 if (!mode_refresh)
363 mode_refresh = 60;
364 if (fabs(desc->refresh - mode_refresh) > 0.1)
365 return FALSE;
367 if (CGDisplayModeGetPixelWidth(mode) != desc->pixel_width ||
368 CGDisplayModeGetPixelHeight(mode) != desc->pixel_height)
369 return FALSE;
371 mode_pixel_encoding = CGDisplayModeCopyPixelEncoding(mode);
372 if (!CFEqual(mode_pixel_encoding, desc->pixel_encoding))
374 CFRelease(mode_pixel_encoding);
375 return FALSE;
377 CFRelease(mode_pixel_encoding);
379 return TRUE;
383 static int get_default_bpp(void)
385 static int cached;
386 int ret;
388 if (!cached)
390 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
391 if (mode)
393 cached = display_mode_bits_per_pixel(mode);
394 CFRelease(mode);
397 if (!cached)
398 cached = 32;
401 ret = cached;
403 TRACE(" -> %d\n", ret);
404 return ret;
408 static CFDictionaryRef create_mode_dict(CGDisplayModeRef display_mode, BOOL is_original)
410 CFDictionaryRef ret;
411 SInt32 io_flags = CGDisplayModeGetIOFlags(display_mode);
412 SInt64 width = CGDisplayModeGetWidth(display_mode);
413 SInt64 height = CGDisplayModeGetHeight(display_mode);
414 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
415 CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
416 CFNumberRef cf_io_flags, cf_width, cf_height, cf_refresh;
418 if (retina_enabled && is_original)
420 width *= 2;
421 height *= 2;
424 io_flags &= kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeInterlacedFlag |
425 kDisplayModeStretchedFlag | kDisplayModeTelevisionFlag;
426 cf_io_flags = CFNumberCreate(NULL, kCFNumberSInt32Type, &io_flags);
427 cf_width = CFNumberCreate(NULL, kCFNumberSInt64Type, &width);
428 cf_height = CFNumberCreate(NULL, kCFNumberSInt64Type, &height);
429 cf_refresh = CFNumberCreate(NULL, kCFNumberDoubleType, &refresh_rate);
432 static const CFStringRef keys[] = {
433 CFSTR("io_flags"),
434 CFSTR("width"),
435 CFSTR("height"),
436 CFSTR("pixel_encoding"),
437 CFSTR("refresh_rate"),
439 const void* values[ARRAY_SIZE(keys)] = {
440 cf_io_flags,
441 cf_width,
442 cf_height,
443 pixel_encoding,
444 cf_refresh,
447 ret = CFDictionaryCreate(NULL, (const void**)keys, (const void**)values, ARRAY_SIZE(keys),
448 &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
451 CFRelease(pixel_encoding);
452 CFRelease(cf_io_flags);
453 CFRelease(cf_width);
454 CFRelease(cf_height);
455 CFRelease(cf_refresh);
457 return ret;
461 static BOOL mode_is_preferred(CGDisplayModeRef new_mode, CGDisplayModeRef old_mode,
462 struct display_mode_descriptor *original_mode_desc,
463 BOOL include_unsupported)
465 BOOL new_is_supported;
466 CFStringRef pixel_encoding;
467 size_t width_points, height_points;
468 size_t old_width_pixels, old_height_pixels, new_width_pixels, new_height_pixels;
469 BOOL old_size_same, new_size_same;
471 /* If a given mode is the user's default, then always list it in preference to any similar
472 modes that may exist. */
473 if (display_mode_matches_descriptor(new_mode, original_mode_desc))
474 return TRUE;
476 /* Skip unsupported modes unless told to do otherwise. */
477 new_is_supported = display_mode_is_supported(new_mode);
478 if (!new_is_supported && !include_unsupported)
479 return FALSE;
481 pixel_encoding = CGDisplayModeCopyPixelEncoding(new_mode);
482 if (pixel_encoding)
484 BOOL bpp30 = CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels));
485 CFRelease(pixel_encoding);
486 if (bpp30)
488 /* This is an odd pixel encoding. It seems it's only returned
489 when using kCGDisplayShowDuplicateLowResolutionModes. It's
490 32bpp in terms of the actual raster layout, but it's 10
491 bits per component. I think that no Windows program is
492 likely to need it and they will probably be confused by it.
493 Skip it. */
494 return FALSE;
498 if (!old_mode)
499 return TRUE;
501 /* Prefer the original mode over any similar mode. */
502 if (display_mode_matches_descriptor(old_mode, original_mode_desc))
503 return FALSE;
505 /* Prefer supported modes over similar unsupported ones. */
506 if (!new_is_supported && display_mode_is_supported(old_mode))
507 return FALSE;
509 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
510 is scaled. */
511 width_points = CGDisplayModeGetWidth(new_mode);
512 height_points = CGDisplayModeGetHeight(new_mode);
513 new_width_pixels = CGDisplayModeGetPixelWidth(new_mode);
514 new_height_pixels = CGDisplayModeGetPixelHeight(new_mode);
515 old_width_pixels = CGDisplayModeGetPixelWidth(old_mode);
516 old_height_pixels = CGDisplayModeGetPixelHeight(old_mode);
517 new_size_same = (new_width_pixels == width_points && new_height_pixels == height_points);
518 old_size_same = (old_width_pixels == width_points && old_height_pixels == height_points);
520 if (new_size_same && !old_size_same)
521 return TRUE;
523 if (!new_size_same && old_size_same)
524 return FALSE;
526 /* Otherwise, prefer the mode with the smaller pixel size. */
527 return new_width_pixels < old_width_pixels && new_height_pixels < old_height_pixels;
531 static CFComparisonResult mode_compare(const void *p1, const void *p2, void *context)
533 CGDisplayModeRef a = (CGDisplayModeRef)p1, b = (CGDisplayModeRef)p2;
534 size_t a_val, b_val;
535 double a_refresh_rate, b_refresh_rate;
537 /* Sort by bpp descending, */
538 a_val = display_mode_bits_per_pixel(a);
539 b_val = display_mode_bits_per_pixel(b);
540 if (a_val < b_val)
541 return kCFCompareGreaterThan;
542 else if (a_val > b_val)
543 return kCFCompareLessThan;
545 /* then width ascending, */
546 a_val = CGDisplayModeGetWidth(a);
547 b_val = CGDisplayModeGetWidth(b);
548 if (a_val < b_val)
549 return kCFCompareLessThan;
550 else if (a_val > b_val)
551 return kCFCompareGreaterThan;
553 /* then height ascending, */
554 a_val = CGDisplayModeGetHeight(a);
555 b_val = CGDisplayModeGetHeight(b);
556 if (a_val < b_val)
557 return kCFCompareLessThan;
558 else if (a_val > b_val)
559 return kCFCompareGreaterThan;
561 /* then refresh rate descending. */
562 a_refresh_rate = CGDisplayModeGetRefreshRate(a);
563 b_refresh_rate = CGDisplayModeGetRefreshRate(b);
564 if (a_refresh_rate < b_refresh_rate)
565 return kCFCompareGreaterThan;
566 else if (a_refresh_rate > b_refresh_rate)
567 return kCFCompareLessThan;
569 return kCFCompareEqualTo;
573 /***********************************************************************
574 * copy_display_modes
576 * Wrapper around CGDisplayCopyAllDisplayModes() to include additional
577 * modes on Retina-capable systems, but filter those which would confuse
578 * Windows apps (basically duplicates at different DPIs).
580 * For example, some Retina Macs support a 1920x1200 mode, but it's not
581 * returned from CGDisplayCopyAllDisplayModes() without special options.
582 * This is especially bad if that's the user's default mode, since then
583 * no "available" mode matches the initial settings.
585 * If include_unsupported is FALSE, display modes with IO flags that
586 * indicate that they are invalid or unsafe are filtered.
588 static CFArrayRef copy_display_modes(CGDirectDisplayID display, BOOL include_unsupported)
590 CFArrayRef modes = NULL;
591 CFDictionaryRef options;
592 struct display_mode_descriptor* desc;
593 CFMutableDictionaryRef modes_by_size;
594 CFIndex i, count;
595 CGDisplayModeRef* mode_array;
597 options = CFDictionaryCreate(NULL, (const void**)&kCGDisplayShowDuplicateLowResolutionModes,
598 (const void**)&kCFBooleanTrue, 1, &kCFTypeDictionaryKeyCallBacks,
599 &kCFTypeDictionaryValueCallBacks);
601 modes = CGDisplayCopyAllDisplayModes(display, options);
602 if (options)
603 CFRelease(options);
604 if (!modes)
605 return NULL;
607 desc = create_original_display_mode_descriptor(display);
609 modes_by_size = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
610 count = CFArrayGetCount(modes);
611 for (i = 0; i < count; i++)
613 CGDisplayModeRef new_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
614 BOOL new_is_original = display_mode_matches_descriptor(new_mode, desc);
615 CFDictionaryRef key = create_mode_dict(new_mode, new_is_original);
616 CGDisplayModeRef old_mode = (CGDisplayModeRef)CFDictionaryGetValue(modes_by_size, key);
618 if (mode_is_preferred(new_mode, old_mode, desc, include_unsupported))
619 CFDictionarySetValue(modes_by_size, key, new_mode);
621 CFRelease(key);
624 free_display_mode_descriptor(desc);
625 CFRelease(modes);
627 count = CFDictionaryGetCount(modes_by_size);
628 mode_array = malloc(count * sizeof(mode_array[0]));
629 CFDictionaryGetKeysAndValues(modes_by_size, NULL, (const void **)mode_array);
630 modes = CFArrayCreate(NULL, (const void **)mode_array, count, &kCFTypeArrayCallBacks);
631 free(mode_array);
632 CFRelease(modes_by_size);
634 if (modes)
636 CFIndex count = CFArrayGetCount(modes);
637 CFMutableArrayRef sorted_modes = CFArrayCreateMutableCopy(NULL, count, modes);
638 CFRelease(modes);
639 CFArraySortValues(sorted_modes, CFRangeMake(0, count), mode_compare, NULL);
640 return sorted_modes;
643 return NULL;
647 void check_retina_status(void)
649 if (retina_enabled)
651 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(kCGDirectMainDisplay);
652 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
653 BOOL new_value = display_mode_matches_descriptor(mode, desc);
655 CGDisplayModeRelease(mode);
656 free_display_mode_descriptor(desc);
658 if (new_value != retina_on)
659 macdrv_set_cocoa_retina_mode(new_value);
663 static BOOL is_detached_mode(const DEVMODEW *mode)
665 return mode->dmFields & DM_POSITION &&
666 mode->dmFields & DM_PELSWIDTH &&
667 mode->dmFields & DM_PELSHEIGHT &&
668 mode->dmPelsWidth == 0 &&
669 mode->dmPelsHeight == 0;
672 static CGDisplayModeRef find_best_display_mode(DEVMODEW *devmode, CFArrayRef display_modes, int bpp, struct display_mode_descriptor *desc)
674 CFIndex count, i, best;
675 CGDisplayModeRef best_display_mode;
677 best_display_mode = NULL;
679 count = CFArrayGetCount(display_modes);
680 for (i = 0; i < count; i++)
682 CGDisplayModeRef display_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(display_modes, i);
683 BOOL is_original = display_mode_matches_descriptor(display_mode, desc);
684 uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
685 int mode_bpp = display_mode_bits_per_pixel(display_mode);
686 size_t width = CGDisplayModeGetWidth(display_mode);
687 size_t height = CGDisplayModeGetHeight(display_mode);
688 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
689 if (!refresh_rate)
690 refresh_rate = 60;
692 if (is_original && retina_enabled)
694 width *= 2;
695 height *= 2;
698 if (bpp != mode_bpp)
699 continue;
701 if (devmode->dmPelsWidth != width)
702 continue;
703 if (devmode->dmPelsHeight != height)
704 continue;
705 if (devmode->dmDisplayFrequency != (DWORD)refresh_rate)
706 continue;
707 if (!(devmode->dmDisplayFlags & DM_INTERLACED) != !(io_flags & kDisplayModeInterlacedFlag))
708 continue;
709 if (!(devmode->dmDisplayFixedOutput == DMDFO_STRETCH) != !(io_flags & kDisplayModeStretchedFlag))
710 continue;
712 if (best_display_mode)
713 continue;
715 best_display_mode = display_mode;
716 best = i;
719 if (best_display_mode)
720 TRACE("Requested display settings match mode %ld\n", best);
722 return best_display_mode;
725 /***********************************************************************
726 * ChangeDisplaySettings (MACDRV.@)
729 LONG macdrv_ChangeDisplaySettings(LPDEVMODEW displays, LPCWSTR primary_name, HWND hwnd, DWORD flags, LPVOID lpvoid)
731 LONG ret = DISP_CHANGE_SUCCESSFUL;
732 DEVMODEW *mode;
733 int bpp;
734 struct macdrv_display *macdrv_displays;
735 int num_displays;
736 CFArrayRef display_modes;
737 struct display_mode_descriptor *desc;
738 CGDisplayModeRef best_display_mode;
740 TRACE("%p %s %p 0x%08x %p\n", displays, debugstr_w(primary_name), hwnd, (unsigned int)flags, lpvoid);
742 init_original_display_mode();
744 if (macdrv_get_displays(&macdrv_displays, &num_displays))
745 return DISP_CHANGE_FAILED;
747 display_modes = copy_display_modes(macdrv_displays[0].displayID, FALSE);
748 if (!display_modes)
750 macdrv_free_displays(macdrv_displays);
751 return DISP_CHANGE_FAILED;
754 bpp = get_default_bpp();
756 desc = create_original_display_mode_descriptor(macdrv_displays[0].displayID);
758 for (mode = displays; mode->dmSize && !ret; mode = NEXT_DEVMODEW(mode))
760 if (wcsicmp(primary_name, mode->dmDeviceName))
762 FIXME("Changing non-primary adapter settings is currently unsupported.\n");
763 continue;
765 if (is_detached_mode(mode))
767 FIXME("Detaching adapters is currently unsupported.\n");
768 continue;
771 if (mode->dmBitsPerPel != bpp)
772 TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, (int)mode->dmBitsPerPel);
774 TRACE("looking for %dx%dx%dbpp @%d Hz", (int)mode->dmPelsWidth, (int)mode->dmPelsHeight,
775 bpp, (int)mode->dmDisplayFrequency);
776 TRACE(" %sstretched", mode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un");
777 TRACE(" %sinterlaced", mode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
778 TRACE("\n");
780 if (!(best_display_mode = find_best_display_mode(mode, display_modes, bpp, desc)))
782 ERR("No matching mode found %ux%ux%d @%u!\n", (unsigned int)mode->dmPelsWidth, (unsigned int)mode->dmPelsHeight,
783 bpp, (unsigned int)mode->dmDisplayFrequency);
784 ret = DISP_CHANGE_BADMODE;
786 else if (!macdrv_set_display_mode(&macdrv_displays[0], best_display_mode))
788 WARN("Failed to set display mode\n");
789 ret = DISP_CHANGE_FAILED;
793 free_display_mode_descriptor(desc);
794 CFRelease(display_modes);
795 macdrv_free_displays(macdrv_displays);
797 return ret;
801 static DEVMODEW *display_get_modes(CGDirectDisplayID display_id, int *modes_count)
803 int default_bpp = get_default_bpp(), synth_count = 0, count, i;
804 BOOL modes_has_8bpp = FALSE, modes_has_16bpp = FALSE;
805 struct display_mode_descriptor *desc;
806 DEVMODEW *devmodes;
807 CFArrayRef modes;
809 modes = copy_display_modes(display_id, TRUE);
810 if (!modes)
811 return NULL;
813 count = CFArrayGetCount(modes);
814 for (i = 0; i < count && !(modes_has_8bpp && modes_has_16bpp); i++)
816 CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
817 int bpp = display_mode_bits_per_pixel(mode);
818 if (bpp == 8)
819 modes_has_8bpp = TRUE;
820 else if (bpp == 16)
821 modes_has_16bpp = TRUE;
824 if (!(devmodes = calloc(count * 3, sizeof(DEVMODEW))))
826 CFRelease(modes);
827 return NULL;
830 desc = create_original_display_mode_descriptor(display_id);
831 for (i = 0; i < count; i++)
833 CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
834 display_mode_to_devmode(display_id, mode, devmodes + i);
836 if (retina_enabled && display_mode_matches_descriptor(mode, desc))
838 devmodes[i].dmPelsWidth *= 2;
839 devmodes[i].dmPelsHeight *= 2;
842 free_display_mode_descriptor(desc);
844 for (i = 0; !modes_has_16bpp && i < count; i++)
846 /* We only synthesize modes from those having the default bpp. */
847 if (devmodes[i].dmBitsPerPel != default_bpp) continue;
848 devmodes[count + synth_count] = devmodes[i];
849 devmodes[count + synth_count].dmBitsPerPel = 16;
850 synth_count++;
853 for (i = 0; !modes_has_8bpp && i < count; i++)
855 /* We only synthesize modes from those having the default bpp. */
856 if (devmodes[i].dmBitsPerPel != default_bpp) continue;
857 devmodes[count + synth_count] = devmodes[i];
858 devmodes[count + synth_count].dmBitsPerPel = 8;
859 synth_count++;
862 CFRelease(modes);
863 *modes_count = count + synth_count;
864 return devmodes;
867 /***********************************************************************
868 * GetCurrentDisplaySettings (MACDRV.@)
871 BOOL macdrv_GetCurrentDisplaySettings(LPCWSTR devname, BOOL is_primary, LPDEVMODEW devmode)
873 struct macdrv_display *displays = NULL;
874 int num_displays, display_idx;
875 CGDisplayModeRef display_mode;
876 CGDirectDisplayID display_id;
877 WCHAR *end;
879 TRACE("%s, %u, %p + %hu\n", debugstr_w(devname), is_primary, devmode, devmode->dmSize);
881 init_original_display_mode();
883 if (macdrv_get_displays(&displays, &num_displays))
884 return FALSE;
886 display_idx = wcstol(devname + 11, &end, 10) - 1;
887 if (display_idx >= num_displays)
889 macdrv_free_displays(displays);
890 return FALSE;
893 display_id = displays[display_idx].displayID;
894 display_mode = CGDisplayCopyDisplayMode(display_id);
896 devmode->dmPosition.x = CGRectGetMinX(displays[display_idx].frame);
897 devmode->dmPosition.y = CGRectGetMinY(displays[display_idx].frame);
898 devmode->dmFields |= DM_POSITION;
900 display_mode_to_devmode(display_id, display_mode, devmode);
901 if (retina_enabled)
903 struct display_mode_descriptor *desc = create_original_display_mode_descriptor(display_id);
904 if (display_mode_matches_descriptor(display_mode, desc))
906 devmode->dmPelsWidth *= 2;
907 devmode->dmPelsHeight *= 2;
909 free_display_mode_descriptor(desc);
912 CFRelease(display_mode);
913 macdrv_free_displays(displays);
915 TRACE("current mode -- %dx%d-%dx%dx%dbpp @%d Hz",
916 (int)devmode->dmPosition.x, (int)devmode->dmPosition.y,
917 (int)devmode->dmPelsWidth, (int)devmode->dmPelsHeight, (int)devmode->dmBitsPerPel,
918 (int)devmode->dmDisplayFrequency);
919 if (devmode->dmDisplayOrientation)
920 TRACE(" rotated %u degrees", (unsigned int)devmode->dmDisplayOrientation * 90);
921 if (devmode->dmDisplayFixedOutput == DMDFO_STRETCH)
922 TRACE(" stretched");
923 if (devmode->dmDisplayFlags & DM_INTERLACED)
924 TRACE(" interlaced");
925 TRACE("\n");
927 return TRUE;
930 /***********************************************************************
931 * GetDisplayDepth (MACDRV.@)
934 INT macdrv_GetDisplayDepth(LPCWSTR name, BOOL is_primary)
936 return get_default_bpp();
939 /***********************************************************************
940 * GetDeviceGammaRamp (MACDRV.@)
942 BOOL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
944 BOOL ret = FALSE;
945 DDGAMMARAMP *r = ramp;
946 struct macdrv_display *displays;
947 int num_displays;
948 uint32_t mac_entries;
949 int win_entries = ARRAY_SIZE(r->red);
950 CGGammaValue *red, *green, *blue;
951 CGError err;
952 int win_entry;
954 TRACE("dev %p ramp %p\n", dev, ramp);
956 if (macdrv_get_displays(&displays, &num_displays))
958 WARN("failed to get Mac displays\n");
959 return FALSE;
962 mac_entries = CGDisplayGammaTableCapacity(displays[0].displayID);
963 red = malloc(mac_entries * sizeof(red[0]) * 3);
964 if (!red)
965 goto done;
966 green = red + mac_entries;
967 blue = green + mac_entries;
969 err = CGGetDisplayTransferByTable(displays[0].displayID, mac_entries, red, green,
970 blue, &mac_entries);
971 if (err != kCGErrorSuccess)
973 WARN("failed to get Mac gamma table: %d\n", err);
974 goto done;
977 if (mac_entries == win_entries)
979 for (win_entry = 0; win_entry < win_entries; win_entry++)
981 r->red[win_entry] = red[win_entry] * 65535 + 0.5;
982 r->green[win_entry] = green[win_entry] * 65535 + 0.5;
983 r->blue[win_entry] = blue[win_entry] * 65535 + 0.5;
986 else
988 for (win_entry = 0; win_entry < win_entries; win_entry++)
990 double mac_pos = win_entry * (mac_entries - 1) / (double)(win_entries - 1);
991 int mac_entry = mac_pos;
992 double red_value, green_value, blue_value;
994 if (mac_entry == mac_entries - 1)
996 red_value = red[mac_entry];
997 green_value = green[mac_entry];
998 blue_value = blue[mac_entry];
1000 else
1002 double distance = mac_pos - mac_entry;
1004 red_value = red[mac_entry] * (1 - distance) + red[mac_entry + 1] * distance;
1005 green_value = green[mac_entry] * (1 - distance) + green[mac_entry + 1] * distance;
1006 blue_value = blue[mac_entry] * (1 - distance) + blue[mac_entry + 1] * distance;
1009 r->red[win_entry] = red_value * 65535 + 0.5;
1010 r->green[win_entry] = green_value * 65535 + 0.5;
1011 r->blue[win_entry] = blue_value * 65535 + 0.5;
1015 ret = TRUE;
1017 done:
1018 free(red);
1019 macdrv_free_displays(displays);
1020 return ret;
1023 /***********************************************************************
1024 * SetDeviceGammaRamp (MACDRV.@)
1026 BOOL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1028 DDGAMMARAMP *r = ramp;
1029 struct macdrv_display *displays;
1030 int num_displays;
1031 int win_entries = ARRAY_SIZE(r->red);
1032 CGGammaValue *red, *green, *blue;
1033 int i;
1034 CGError err = kCGErrorFailure;
1036 TRACE("dev %p ramp %p\n", dev, ramp);
1038 if (!allow_set_gamma)
1040 TRACE("disallowed by registry setting\n");
1041 return FALSE;
1044 if (macdrv_get_displays(&displays, &num_displays))
1046 WARN("failed to get Mac displays\n");
1047 return FALSE;
1050 red = malloc(win_entries * sizeof(red[0]) * 3);
1051 if (!red)
1052 goto done;
1053 green = red + win_entries;
1054 blue = green + win_entries;
1056 for (i = 0; i < win_entries; i++)
1058 red[i] = r->red[i] / 65535.0;
1059 green[i] = r->green[i] / 65535.0;
1060 blue[i] = r->blue[i] / 65535.0;
1063 err = CGSetDisplayTransferByTable(displays[0].displayID, win_entries, red, green, blue);
1064 if (err != kCGErrorSuccess)
1065 WARN("failed to set display gamma table: %d\n", err);
1067 done:
1068 free(red);
1069 macdrv_free_displays(displays);
1070 return (err == kCGErrorSuccess);
1073 /***********************************************************************
1074 * init_registry_display_settings
1076 * Initialize registry display settings when new display devices are added.
1078 static void init_registry_display_settings(void)
1080 DEVMODEW dm = {.dmSize = sizeof(dm)};
1081 DISPLAY_DEVICEW dd = {sizeof(dd)};
1082 UNICODE_STRING str;
1083 DWORD i = 0;
1084 int ret;
1086 while (!NtUserEnumDisplayDevices(NULL, i++, &dd, 0))
1088 RtlInitUnicodeString(&str, dd.DeviceName);
1090 /* Skip if the device already has registry display settings */
1091 if (NtUserEnumDisplaySettings(&str, ENUM_REGISTRY_SETTINGS, &dm, 0))
1092 continue;
1094 if (!NtUserEnumDisplaySettings(&str, ENUM_CURRENT_SETTINGS, &dm, 0))
1096 ERR("Failed to query current display settings for %s.\n", wine_dbgstr_w(dd.DeviceName));
1097 continue;
1100 TRACE("Device %s current display mode %ux%u %ubits %uHz at %d,%d.\n",
1101 wine_dbgstr_w(dd.DeviceName), (unsigned int)dm.dmPelsWidth, (unsigned int)dm.dmPelsHeight,
1102 (unsigned int)dm.dmBitsPerPel, (unsigned int)dm.dmDisplayFrequency, (int)dm.dmPosition.x, (int)dm.dmPosition.y);
1104 ret = NtUserChangeDisplaySettings(&str, &dm, NULL,
1105 CDS_GLOBAL | CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
1106 if (ret != DISP_CHANGE_SUCCESSFUL)
1107 ERR("Failed to save registry display settings for %s, returned %d.\n",
1108 wine_dbgstr_w(dd.DeviceName), ret);
1112 /***********************************************************************
1113 * macdrv_displays_changed
1115 * Handler for DISPLAYS_CHANGED events.
1117 void macdrv_displays_changed(const macdrv_event *event)
1119 HWND hwnd = NtUserGetDesktopWindow();
1121 /* A system display change will get delivered to all GUI-attached threads,
1122 so the desktop-window-owning thread will get it and all others should
1123 ignore it. A synthesized display change event due to activation
1124 will only get delivered to the activated process. So, it needs to
1125 process it (by sending it to the desktop window). */
1126 if (event->displays_changed.activating ||
1127 NtUserGetWindowThread(hwnd, NULL) == GetCurrentThreadId())
1129 macdrv_init_display_devices(TRUE);
1130 init_registry_display_settings();
1131 macdrv_resize_desktop();
1135 static BOOL force_display_devices_refresh;
1137 BOOL macdrv_UpdateDisplayDevices( const struct gdi_device_manager *device_manager, BOOL force, void *param )
1139 struct macdrv_adapter *adapters, *adapter;
1140 struct macdrv_monitor *monitors, *monitor;
1141 struct macdrv_gpu *gpus, *gpu;
1142 INT gpu_count, adapter_count, monitor_count, mode_count;
1143 DEVMODEW *mode, *modes;
1144 DWORD len;
1146 if (!force && !force_display_devices_refresh) return TRUE;
1147 force_display_devices_refresh = FALSE;
1149 /* Initialize GPUs */
1150 if (macdrv_get_gpus(&gpus, &gpu_count))
1152 ERR("could not get GPUs\n");
1153 return FALSE;
1155 TRACE("GPU count: %d\n", gpu_count);
1157 for (gpu = gpus; gpu < gpus + gpu_count; gpu++)
1159 struct gdi_gpu gdi_gpu =
1161 .id = gpu->id,
1162 .vendor_id = gpu->vendor_id,
1163 .device_id = gpu->device_id,
1164 .subsys_id = gpu->subsys_id,
1165 .revision_id = gpu->revision_id,
1167 RtlUTF8ToUnicodeN(gdi_gpu.name, sizeof(gdi_gpu.name), &len, gpu->name, strlen(gpu->name));
1168 device_manager->add_gpu(&gdi_gpu, param);
1170 /* Initialize adapters */
1171 if (macdrv_get_adapters(gpu->id, &adapters, &adapter_count)) break;
1172 TRACE("GPU: %llx %s, adapter count: %d\n", gpu->id, debugstr_a(gpu->name), adapter_count);
1174 for (adapter = adapters; adapter < adapters + adapter_count; adapter++)
1176 struct gdi_adapter gdi_adapter =
1178 .id = adapter->id,
1179 .state_flags = adapter->state_flags,
1181 device_manager->add_adapter( &gdi_adapter, param );
1183 if (macdrv_get_monitors(adapter->id, &monitors, &monitor_count)) break;
1184 TRACE("adapter: %#x, monitor count: %d\n", adapter->id, monitor_count);
1186 /* Initialize monitors */
1187 for (monitor = monitors; monitor < monitors + monitor_count; monitor++)
1189 struct gdi_monitor gdi_monitor =
1191 .rc_monitor = rect_from_cgrect(monitor->rc_monitor),
1192 .rc_work = rect_from_cgrect(monitor->rc_work),
1193 .state_flags = monitor->state_flags,
1195 RtlUTF8ToUnicodeN(gdi_monitor.name, sizeof(gdi_monitor.name), &len,
1196 monitor->name, strlen(monitor->name));
1197 TRACE("monitor: %s\n", debugstr_a(monitor->name));
1198 device_manager->add_monitor( &gdi_monitor, param );
1201 if (!(modes = display_get_modes(adapter->id, &mode_count))) break;
1202 TRACE("adapter: %#x, mode count: %d\n", adapter->id, mode_count);
1204 /* Initialize modes */
1205 for (mode = modes; mode < modes + mode_count; mode++)
1207 TRACE("mode: %dx%dx%dbpp @%d Hz, %sstretched %sinterlaced\n", (int)mode->dmPelsWidth, (int)mode->dmPelsHeight,
1208 (int)mode->dmBitsPerPel, (int)mode->dmDisplayFrequency,
1209 mode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un",
1210 mode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
1211 device_manager->add_mode( mode, param );
1214 macdrv_free_monitors(monitors);
1217 macdrv_free_adapters(adapters);
1220 macdrv_free_gpus(gpus);
1221 return TRUE;
1224 /***********************************************************************
1225 * macdrv_init_display_devices
1227 * Initialize display device registry data.
1229 void macdrv_init_display_devices(BOOL force)
1231 UINT32 num_path, num_mode;
1233 if (force) force_display_devices_refresh = TRUE;
1234 /* trigger refresh in win32u */
1235 NtUserGetDisplayConfigBufferSizes( QDC_ONLY_ACTIVE_PATHS, &num_path, &num_mode );