dxdiagn: Remove DECLSPEC_HIDDEN usage.
[wine.git] / dlls / winemac.drv / display.c
blob14c09649211dc7ca81d87a6f7afad24766579972
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 static void display_get_current_mode(struct macdrv_display *display, DEVMODEW *devmode)
869 CGDisplayModeRef display_mode;
870 CGDirectDisplayID display_id;
872 display_id = display->displayID;
873 display_mode = CGDisplayCopyDisplayMode(display_id);
875 devmode->dmPosition.x = CGRectGetMinX(display->frame);
876 devmode->dmPosition.y = CGRectGetMinY(display->frame);
877 devmode->dmFields |= DM_POSITION;
879 display_mode_to_devmode(display_id, display_mode, devmode);
880 if (retina_enabled)
882 struct display_mode_descriptor *desc = create_original_display_mode_descriptor(display_id);
883 if (display_mode_matches_descriptor(display_mode, desc))
885 devmode->dmPelsWidth *= 2;
886 devmode->dmPelsHeight *= 2;
888 free_display_mode_descriptor(desc);
891 CFRelease(display_mode);
894 /***********************************************************************
895 * GetCurrentDisplaySettings (MACDRV.@)
898 BOOL macdrv_GetCurrentDisplaySettings(LPCWSTR devname, BOOL is_primary, LPDEVMODEW devmode)
900 struct macdrv_display *displays = NULL;
901 int num_displays, display_idx;
902 WCHAR *end;
904 TRACE("%s, %u, %p + %hu\n", debugstr_w(devname), is_primary, devmode, devmode->dmSize);
906 init_original_display_mode();
908 if (macdrv_get_displays(&displays, &num_displays))
909 return FALSE;
911 display_idx = wcstol(devname + 11, &end, 10) - 1;
912 if (display_idx >= num_displays)
914 macdrv_free_displays(displays);
915 return FALSE;
918 display_get_current_mode(&displays[display_idx], devmode);
919 macdrv_free_displays(displays);
921 TRACE("current mode -- %dx%d-%dx%dx%dbpp @%d Hz",
922 (int)devmode->dmPosition.x, (int)devmode->dmPosition.y,
923 (int)devmode->dmPelsWidth, (int)devmode->dmPelsHeight, (int)devmode->dmBitsPerPel,
924 (int)devmode->dmDisplayFrequency);
925 if (devmode->dmDisplayOrientation)
926 TRACE(" rotated %u degrees", (unsigned int)devmode->dmDisplayOrientation * 90);
927 if (devmode->dmDisplayFixedOutput == DMDFO_STRETCH)
928 TRACE(" stretched");
929 if (devmode->dmDisplayFlags & DM_INTERLACED)
930 TRACE(" interlaced");
931 TRACE("\n");
933 return TRUE;
936 /***********************************************************************
937 * GetDisplayDepth (MACDRV.@)
940 INT macdrv_GetDisplayDepth(LPCWSTR name, BOOL is_primary)
942 return get_default_bpp();
945 /***********************************************************************
946 * GetDeviceGammaRamp (MACDRV.@)
948 BOOL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
950 BOOL ret = FALSE;
951 DDGAMMARAMP *r = ramp;
952 struct macdrv_display *displays;
953 int num_displays;
954 uint32_t mac_entries;
955 int win_entries = ARRAY_SIZE(r->red);
956 CGGammaValue *red, *green, *blue;
957 CGError err;
958 int win_entry;
960 TRACE("dev %p ramp %p\n", dev, ramp);
962 if (macdrv_get_displays(&displays, &num_displays))
964 WARN("failed to get Mac displays\n");
965 return FALSE;
968 mac_entries = CGDisplayGammaTableCapacity(displays[0].displayID);
969 red = malloc(mac_entries * sizeof(red[0]) * 3);
970 if (!red)
971 goto done;
972 green = red + mac_entries;
973 blue = green + mac_entries;
975 err = CGGetDisplayTransferByTable(displays[0].displayID, mac_entries, red, green,
976 blue, &mac_entries);
977 if (err != kCGErrorSuccess)
979 WARN("failed to get Mac gamma table: %d\n", err);
980 goto done;
983 if (mac_entries == win_entries)
985 for (win_entry = 0; win_entry < win_entries; win_entry++)
987 r->red[win_entry] = red[win_entry] * 65535 + 0.5;
988 r->green[win_entry] = green[win_entry] * 65535 + 0.5;
989 r->blue[win_entry] = blue[win_entry] * 65535 + 0.5;
992 else
994 for (win_entry = 0; win_entry < win_entries; win_entry++)
996 double mac_pos = win_entry * (mac_entries - 1) / (double)(win_entries - 1);
997 int mac_entry = mac_pos;
998 double red_value, green_value, blue_value;
1000 if (mac_entry == mac_entries - 1)
1002 red_value = red[mac_entry];
1003 green_value = green[mac_entry];
1004 blue_value = blue[mac_entry];
1006 else
1008 double distance = mac_pos - mac_entry;
1010 red_value = red[mac_entry] * (1 - distance) + red[mac_entry + 1] * distance;
1011 green_value = green[mac_entry] * (1 - distance) + green[mac_entry + 1] * distance;
1012 blue_value = blue[mac_entry] * (1 - distance) + blue[mac_entry + 1] * distance;
1015 r->red[win_entry] = red_value * 65535 + 0.5;
1016 r->green[win_entry] = green_value * 65535 + 0.5;
1017 r->blue[win_entry] = blue_value * 65535 + 0.5;
1021 ret = TRUE;
1023 done:
1024 free(red);
1025 macdrv_free_displays(displays);
1026 return ret;
1029 /***********************************************************************
1030 * SetDeviceGammaRamp (MACDRV.@)
1032 BOOL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1034 DDGAMMARAMP *r = ramp;
1035 struct macdrv_display *displays;
1036 int num_displays;
1037 int win_entries = ARRAY_SIZE(r->red);
1038 CGGammaValue *red, *green, *blue;
1039 int i;
1040 CGError err = kCGErrorFailure;
1042 TRACE("dev %p ramp %p\n", dev, ramp);
1044 if (!allow_set_gamma)
1046 TRACE("disallowed by registry setting\n");
1047 return FALSE;
1050 if (macdrv_get_displays(&displays, &num_displays))
1052 WARN("failed to get Mac displays\n");
1053 return FALSE;
1056 red = malloc(win_entries * sizeof(red[0]) * 3);
1057 if (!red)
1058 goto done;
1059 green = red + win_entries;
1060 blue = green + win_entries;
1062 for (i = 0; i < win_entries; i++)
1064 red[i] = r->red[i] / 65535.0;
1065 green[i] = r->green[i] / 65535.0;
1066 blue[i] = r->blue[i] / 65535.0;
1069 err = CGSetDisplayTransferByTable(displays[0].displayID, win_entries, red, green, blue);
1070 if (err != kCGErrorSuccess)
1071 WARN("failed to set display gamma table: %d\n", err);
1073 done:
1074 free(red);
1075 macdrv_free_displays(displays);
1076 return (err == kCGErrorSuccess);
1079 /***********************************************************************
1080 * macdrv_displays_changed
1082 * Handler for DISPLAYS_CHANGED events.
1084 void macdrv_displays_changed(const macdrv_event *event)
1086 HWND hwnd = NtUserGetDesktopWindow();
1088 /* A system display change will get delivered to all GUI-attached threads,
1089 so the desktop-window-owning thread will get it and all others should
1090 ignore it. A synthesized display change event due to activation
1091 will only get delivered to the activated process. So, it needs to
1092 process it (by sending it to the desktop window). */
1093 if (event->displays_changed.activating ||
1094 NtUserGetWindowThread(hwnd, NULL) == GetCurrentThreadId())
1096 macdrv_init_display_devices(TRUE);
1097 macdrv_resize_desktop();
1101 static BOOL force_display_devices_refresh;
1103 static BOOL is_same_devmode(const DEVMODEW *a, const DEVMODEW *b)
1105 return a->dmDisplayOrientation == b->dmDisplayOrientation &&
1106 a->dmDisplayFixedOutput == b->dmDisplayFixedOutput &&
1107 a->dmBitsPerPel == b->dmBitsPerPel &&
1108 a->dmPelsWidth == b->dmPelsWidth &&
1109 a->dmPelsHeight == b->dmPelsHeight &&
1110 a->dmDisplayFlags == b->dmDisplayFlags &&
1111 a->dmDisplayFrequency == b->dmDisplayFrequency;
1114 static const char *debugstr_devmodew(const DEVMODEW *devmode)
1116 char position[32] = {0};
1118 if (devmode->dmFields & DM_POSITION)
1120 snprintf(position, sizeof(position), " at (%d,%d)",
1121 (int)devmode->dmPosition.x, (int)devmode->dmPosition.y);
1124 return wine_dbg_sprintf("%ux%u %ubits %uHz rotated %u degrees %sstretched %sinterlaced%s",
1125 (unsigned int)devmode->dmPelsWidth,
1126 (unsigned int)devmode->dmPelsHeight,
1127 (unsigned int)devmode->dmBitsPerPel,
1128 (unsigned int)devmode->dmDisplayFrequency,
1129 (unsigned int)devmode->dmDisplayOrientation * 90,
1130 devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un",
1131 devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-",
1132 position);
1135 BOOL macdrv_UpdateDisplayDevices( const struct gdi_device_manager *device_manager, BOOL force, void *param )
1137 struct macdrv_adapter *adapters, *adapter;
1138 struct macdrv_monitor *monitors, *monitor;
1139 struct macdrv_gpu *gpus, *gpu;
1140 struct macdrv_display *displays, *display;
1141 INT gpu_count, adapter_count, monitor_count, mode_count, display_count;
1142 DEVMODEW *mode, *modes;
1143 DWORD len;
1145 if (!force && !force_display_devices_refresh) return TRUE;
1146 force_display_devices_refresh = FALSE;
1148 if (macdrv_get_displays(&displays, &display_count))
1150 displays = NULL;
1151 display_count = 0;
1154 /* Initialize GPUs */
1155 if (macdrv_get_gpus(&gpus, &gpu_count))
1157 ERR("could not get GPUs\n");
1158 return FALSE;
1160 TRACE("GPU count: %d\n", gpu_count);
1162 for (gpu = gpus; gpu < gpus + gpu_count; gpu++)
1164 struct gdi_gpu gdi_gpu =
1166 .id = gpu->id,
1167 .vendor_id = gpu->vendor_id,
1168 .device_id = gpu->device_id,
1169 .subsys_id = gpu->subsys_id,
1170 .revision_id = gpu->revision_id,
1172 RtlUTF8ToUnicodeN(gdi_gpu.name, sizeof(gdi_gpu.name), &len, gpu->name, strlen(gpu->name));
1173 device_manager->add_gpu(&gdi_gpu, param);
1175 /* Initialize adapters */
1176 if (macdrv_get_adapters(gpu->id, &adapters, &adapter_count)) break;
1177 TRACE("GPU: %llx %s, adapter count: %d\n", gpu->id, debugstr_a(gpu->name), adapter_count);
1179 for (adapter = adapters; adapter < adapters + adapter_count; adapter++)
1181 DEVMODEW current_mode = { .dmSize = sizeof(current_mode) };
1182 struct gdi_adapter gdi_adapter =
1184 .id = adapter->id,
1185 .state_flags = adapter->state_flags,
1187 device_manager->add_adapter( &gdi_adapter, param );
1189 if (macdrv_get_monitors(adapter->id, &monitors, &monitor_count)) break;
1190 TRACE("adapter: %#x, monitor count: %d\n", adapter->id, monitor_count);
1192 /* Initialize monitors */
1193 for (monitor = monitors; monitor < monitors + monitor_count; monitor++)
1195 struct gdi_monitor gdi_monitor =
1197 .rc_monitor = rect_from_cgrect(monitor->rc_monitor),
1198 .rc_work = rect_from_cgrect(monitor->rc_work),
1199 .state_flags = monitor->state_flags,
1201 device_manager->add_monitor( &gdi_monitor, param );
1204 /* Get the current mode */
1205 if (displays)
1207 for (display = displays; display < displays + display_count; display++)
1209 if (display->displayID == adapter->id)
1211 display_get_current_mode(display, &current_mode);
1212 break;
1217 if (!(modes = display_get_modes(adapter->id, &mode_count))) break;
1218 TRACE("adapter: %#x, mode count: %d\n", adapter->id, mode_count);
1220 /* Initialize modes */
1221 for (mode = modes; mode < modes + mode_count; mode++)
1223 if (is_same_devmode(mode, &current_mode))
1225 TRACE("current mode: %s\n", debugstr_devmodew(&current_mode));
1226 device_manager->add_mode( &current_mode, TRUE, param );
1228 else
1230 TRACE("mode: %s\n", debugstr_devmodew(mode));
1231 device_manager->add_mode( mode, FALSE, param );
1235 free(modes);
1236 macdrv_free_monitors(monitors);
1239 macdrv_free_adapters(adapters);
1242 macdrv_free_gpus(gpus);
1243 macdrv_free_displays(displays);
1244 return TRUE;
1247 /***********************************************************************
1248 * macdrv_init_display_devices
1250 * Initialize display device registry data.
1252 void macdrv_init_display_devices(BOOL force)
1254 UINT32 num_path, num_mode;
1256 if (force) force_display_devices_refresh = TRUE;
1257 /* trigger refresh in win32u */
1258 NtUserGetDisplayConfigBufferSizes( QDC_ONLY_ACTIVE_PATHS, &num_path, &num_mode );