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
32 #define WIN32_NO_STATUS
35 WINE_DEFAULT_DEBUG_CHANNEL(display
);
37 #define NEXT_DEVMODEW(mode) ((DEVMODEW *)((char *)((mode) + 1) + (mode)->dmDriverExtra))
39 struct display_mode_descriptor
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',
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
);
65 if (CFEqual(pixel_encoding
, CFSTR(kIO32BitFloatPixels
)))
67 else if (CFEqual(pixel_encoding
, CFSTR(kIO16BitFloatPixels
)))
69 else if (CFEqual(pixel_encoding
, CFSTR(kIO64BitDirectPixels
)))
71 else if (CFEqual(pixel_encoding
, CFSTR(kIO30BitDirectPixels
)))
73 else if (CFEqual(pixel_encoding
, CFSTR(IO32BitDirectPixels
)))
75 else if (CFEqual(pixel_encoding
, CFSTR(IO16BitDirectPixels
)))
77 else if (CFEqual(pixel_encoding
, CFSTR(IO8BitIndexedPixels
)))
79 else if (CFEqual(pixel_encoding
, CFSTR(IO4BitIndexedPixels
)))
81 else if (CFEqual(pixel_encoding
, CFSTR(IO2BitIndexedPixels
)))
83 else if (CFEqual(pixel_encoding
, CFSTR(IO1BitIndexedPixels
)))
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
)
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
;
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
)
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
)
149 char display_key_name
[19];
151 CGDisplayModeRef display_mode
;
154 CFStringRef pixel_encoding
;
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
)))
163 display_mode
= CGDisplayCopyDisplayMode(displayID
);
167 val
= CGDisplayModeGetWidth(display_mode
);
168 if (!set_setting_value(display_hkey
, "Width", val
))
170 val
= CGDisplayModeGetHeight(display_mode
);
171 if (!set_setting_value(display_hkey
, "Height", val
))
173 val
= CGDisplayModeGetRefreshRate(display_mode
) * 100;
174 if (!set_setting_value(display_hkey
, "RefreshRateTimes100", val
))
176 val
= CGDisplayModeGetIOFlags(display_mode
);
177 if (!set_setting_value(display_hkey
, "IOFlags", val
))
179 val
= CGDisplayModeGetPixelWidth(display_mode
);
180 if (!set_setting_value(display_hkey
, "PixelWidth", val
))
182 val
= CGDisplayModeGetPixelHeight(display_mode
);
183 if (!set_setting_value(display_hkey
, "PixelHeight", val
))
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
);
191 CFRelease(pixel_encoding
);
192 RtlInitUnicodeString(&str
, pixelencodingW
);
193 if (NtSetValueKey(display_hkey
, &str
, 0, REG_SZ
, (const BYTE
*)buf
, (len
+ 1) * sizeof(WCHAR
)))
200 if (display_mode
) CGDisplayModeRelease(display_mode
);
201 NtClose(display_hkey
);
205 reg_delete_tree(parent_hkey
, nameW
, asciiz_to_unicode(nameW
, display_key_name
) - sizeof(WCHAR
));
211 static void init_original_display_mode(void)
213 BOOL success
= FALSE
;
214 HKEY mac_driver_hkey
, parent_hkey
;
216 struct macdrv_display
*displays
= NULL
;
219 if (inited_original_display_mode
)
222 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver */
223 mac_driver_hkey
= reg_create_ascii_key(NULL
, "\\Registry\\Machine\\Software\\Wine\\Mac Driver",
225 if (!mac_driver_hkey
)
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
)))
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
)
241 if (macdrv_get_displays(&displays
, &num_displays
))
244 for (i
= 0; i
< num_displays
; i
++)
246 if (!write_display_settings(parent_hkey
, displays
[i
].displayID
))
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
);
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
;
269 asciiz_to_unicode(nameW
, name
);
270 if (query_reg_value(hkey
, nameW
, value
, sizeof(buffer
)) != sizeof(*val
) || value
->Type
!= REG_DWORD
)
272 *val
= *(DWORD
*)value
->Data
;
277 static void free_display_mode_descriptor(struct display_mode_descriptor
* desc
)
281 if (desc
->pixel_encoding
)
282 CFRelease(desc
->pixel_encoding
);
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
)];
297 KEY_VALUE_PARTIAL_INFORMATION
*value
= (void *)buffer
;
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
))))
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
))
317 desc
->refresh
= refresh100
/ 100.0;
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
)
330 desc
->pixel_encoding
= CFStringCreateWithCharacters(NULL
, (const UniChar
*)value
->Data
,
331 lstrlenW((const WCHAR
*)value
->Data
));
337 free_display_mode_descriptor(desc
);
343 static BOOL
display_mode_matches_descriptor(CGDisplayModeRef mode
, const struct display_mode_descriptor
* desc
)
347 CFStringRef mode_pixel_encoding
;
352 if (CGDisplayModeGetWidth(mode
) != desc
->width
||
353 CGDisplayModeGetHeight(mode
) != desc
->height
)
356 mode_io_flags
= CGDisplayModeGetIOFlags(mode
);
357 if ((desc
->io_flags
^ mode_io_flags
) & (kDisplayModeValidFlag
| kDisplayModeSafeFlag
| kDisplayModeStretchedFlag
|
358 kDisplayModeInterlacedFlag
| kDisplayModeTelevisionFlag
))
361 mode_refresh
= CGDisplayModeGetRefreshRate(mode
);
364 if (fabs(desc
->refresh
- mode_refresh
) > 0.1)
367 if (CGDisplayModeGetPixelWidth(mode
) != desc
->pixel_width
||
368 CGDisplayModeGetPixelHeight(mode
) != desc
->pixel_height
)
371 mode_pixel_encoding
= CGDisplayModeCopyPixelEncoding(mode
);
372 if (!CFEqual(mode_pixel_encoding
, desc
->pixel_encoding
))
374 CFRelease(mode_pixel_encoding
);
377 CFRelease(mode_pixel_encoding
);
383 static int get_default_bpp(void)
390 CGDisplayModeRef mode
= CGDisplayCopyDisplayMode(kCGDirectMainDisplay
);
393 cached
= display_mode_bits_per_pixel(mode
);
403 TRACE(" -> %d\n", ret
);
408 static CFDictionaryRef
create_mode_dict(CGDisplayModeRef display_mode
, BOOL is_original
)
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
)
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
[] = {
436 CFSTR("pixel_encoding"),
437 CFSTR("refresh_rate"),
439 const void* values
[ARRAY_SIZE(keys
)] = {
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
);
454 CFRelease(cf_height
);
455 CFRelease(cf_refresh
);
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
))
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
)
481 pixel_encoding
= CGDisplayModeCopyPixelEncoding(new_mode
);
484 BOOL bpp30
= CFEqual(pixel_encoding
, CFSTR(kIO30BitDirectPixels
));
485 CFRelease(pixel_encoding
);
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.
501 /* Prefer the original mode over any similar mode. */
502 if (display_mode_matches_descriptor(old_mode
, original_mode_desc
))
505 /* Prefer supported modes over similar unsupported ones. */
506 if (!new_is_supported
&& display_mode_is_supported(old_mode
))
509 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
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
)
523 if (!new_size_same
&& old_size_same
)
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
;
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
);
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
);
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
);
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 /***********************************************************************
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
;
595 CGDisplayModeRef
* mode_array
;
597 options
= CFDictionaryCreate(NULL
, (const void**)&kCGDisplayShowDuplicateLowResolutionModes
,
598 (const void**)&kCFBooleanTrue
, 1, &kCFTypeDictionaryKeyCallBacks
,
599 &kCFTypeDictionaryValueCallBacks
);
601 modes
= CGDisplayCopyAllDisplayModes(display
, options
);
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
);
624 free_display_mode_descriptor(desc
);
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
);
632 CFRelease(modes_by_size
);
636 CFIndex count
= CFArrayGetCount(modes
);
637 CFMutableArrayRef sorted_modes
= CFArrayCreateMutableCopy(NULL
, count
, modes
);
639 CFArraySortValues(sorted_modes
, CFRangeMake(0, count
), mode_compare
, NULL
);
647 void check_retina_status(void)
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
);
692 if (is_original
&& retina_enabled
)
701 if (devmode
->dmPelsWidth
!= width
)
703 if (devmode
->dmPelsHeight
!= height
)
705 if (devmode
->dmDisplayFrequency
!= (DWORD
)refresh_rate
)
707 if (!(devmode
->dmDisplayFlags
& DM_INTERLACED
) != !(io_flags
& kDisplayModeInterlacedFlag
))
709 if (!(devmode
->dmDisplayFixedOutput
== DMDFO_STRETCH
) != !(io_flags
& kDisplayModeStretchedFlag
))
712 if (best_display_mode
)
715 best_display_mode
= display_mode
;
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
;
734 struct macdrv_display
*macdrv_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
);
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");
765 if (is_detached_mode(mode
))
767 FIXME("Detaching adapters is currently unsupported.\n");
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-");
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
);
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
;
809 modes
= copy_display_modes(display_id
, TRUE
);
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
);
819 modes_has_8bpp
= TRUE
;
821 modes_has_16bpp
= TRUE
;
824 if (!(devmodes
= calloc(count
* 3, sizeof(DEVMODEW
))))
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;
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;
863 *modes_count
= count
+ synth_count
;
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
;
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
))
886 display_idx
= wcstol(devname
+ 11, &end
, 10) - 1;
887 if (display_idx
>= num_displays
)
889 macdrv_free_displays(displays
);
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
);
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
)
923 if (devmode
->dmDisplayFlags
& DM_INTERLACED
)
924 TRACE(" interlaced");
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
)
945 DDGAMMARAMP
*r
= ramp
;
946 struct macdrv_display
*displays
;
948 uint32_t mac_entries
;
949 int win_entries
= ARRAY_SIZE(r
->red
);
950 CGGammaValue
*red
, *green
, *blue
;
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");
962 mac_entries
= CGDisplayGammaTableCapacity(displays
[0].displayID
);
963 red
= malloc(mac_entries
* sizeof(red
[0]) * 3);
966 green
= red
+ mac_entries
;
967 blue
= green
+ mac_entries
;
969 err
= CGGetDisplayTransferByTable(displays
[0].displayID
, mac_entries
, red
, green
,
971 if (err
!= kCGErrorSuccess
)
973 WARN("failed to get Mac gamma table: %d\n", err
);
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;
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
];
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;
1019 macdrv_free_displays(displays
);
1023 /***********************************************************************
1024 * SetDeviceGammaRamp (MACDRV.@)
1026 BOOL
macdrv_SetDeviceGammaRamp(PHYSDEV dev
, LPVOID ramp
)
1028 DDGAMMARAMP
*r
= ramp
;
1029 struct macdrv_display
*displays
;
1031 int win_entries
= ARRAY_SIZE(r
->red
);
1032 CGGammaValue
*red
, *green
, *blue
;
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");
1044 if (macdrv_get_displays(&displays
, &num_displays
))
1046 WARN("failed to get Mac displays\n");
1050 red
= malloc(win_entries
* sizeof(red
[0]) * 3);
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
);
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
)};
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))
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
));
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
;
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");
1155 TRACE("GPU count: %d\n", gpu_count
);
1157 for (gpu
= gpus
; gpu
< gpus
+ gpu_count
; gpu
++)
1159 struct gdi_gpu gdi_gpu
=
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
=
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
);
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
);