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
33 #define WIN32_NO_STATUS
35 #include "wine/unicode.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(display
);
40 struct display_mode_descriptor
48 CFStringRef pixel_encoding
;
52 BOOL CDECL
macdrv_EnumDisplaySettingsEx(LPCWSTR devname
, DWORD mode
, LPDEVMODEW devmode
, DWORD flags
);
54 /* Wine specific monitor properties */
55 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_STATEFLAGS
, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 2);
56 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_RCMONITOR
, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 3);
57 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_RCWORK
, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 4);
58 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_ADAPTERNAME
, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 5);
60 static const char initial_mode_key
[] = "Initial Display Mode";
61 static const WCHAR pixelencodingW
[] = {'P','i','x','e','l','E','n','c','o','d','i','n','g',0};
62 static const WCHAR driver_date_dataW
[] = {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0};
63 static const WCHAR driver_descW
[] = {'D','r','i','v','e','r','D','e','s','c',0};
64 static const WCHAR displayW
[] = {'D','I','S','P','L','A','Y',0};
65 static const WCHAR pciW
[] = {'P','C','I',0};
66 static const WCHAR video_idW
[] = {'V','i','d','e','o','I','D',0};
67 static const WCHAR symbolic_link_valueW
[]= {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
68 static const WCHAR gpu_idW
[] = {'G','P','U','I','D',0};
69 static const WCHAR mointor_id_fmtW
[] = {'M','o','n','i','t','o','r','I','D','%','d',0};
70 static const WCHAR adapter_name_fmtW
[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y','%','d',0};
71 static const WCHAR state_flagsW
[] = {'S','t','a','t','e','F','l','a','g','s',0};
72 static const WCHAR guid_fmtW
[] = {
73 '{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-','%','0','2','x','%','0','2','x','-',
74 '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0};
75 static const WCHAR gpu_instance_fmtW
[] = {
77 'V','E','N','_','%','0','4','X','&',
78 'D','E','V','_','%','0','4','X','&',
79 'S','U','B','S','Y','S','_','%','0','8','X','&',
80 'R','E','V','_','%','0','2','X','\\',
82 static const WCHAR gpu_hardware_id_fmtW
[] = {
84 'V','E','N','_','%','0','4','X','&',
85 'D','E','V','_','%','0','4','X','&',
86 'S','U','B','S','Y','S','_','0','0','0','0','0','0','0','0','&',
87 'R','E','V','_','0','0',0};
88 static const WCHAR video_keyW
[] = {
89 'H','A','R','D','W','A','R','E','\\',
90 'D','E','V','I','C','E','M','A','P','\\',
91 'V','I','D','E','O',0};
92 static const WCHAR adapter_key_fmtW
[] = {
93 'S','y','s','t','e','m','\\',
94 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
95 'C','o','n','t','r','o','l','\\',
96 'V','i','d','e','o','\\',
99 static const WCHAR device_video_fmtW
[] = {
100 '\\','D','e','v','i','c','e','\\',
101 'V','i','d','e','o','%','d',0};
102 static const WCHAR machine_prefixW
[] = {
103 '\\','R','e','g','i','s','t','r','y','\\',
104 'M','a','c','h','i','n','e','\\',0};
105 static const WCHAR nt_classW
[] = {
106 '\\','R','e','g','i','s','t','r','y','\\',
107 'M','a','c','h','i','n','e','\\',
108 'S','y','s','t','e','m','\\',
109 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
110 'C','o','n','t','r','o','l','\\',
111 'C','l','a','s','s','\\',0};
112 static const WCHAR monitor_instance_fmtW
[] = {
113 'D','I','S','P','L','A','Y','\\',
114 'D','e','f','a','u','l','t','_','M','o','n','i','t','o','r','\\',
115 '%','0','4','X','&','%','0','4','X',0};
116 static const WCHAR monitor_hardware_idW
[] = {
117 'M','O','N','I','T','O','R','\\',
118 'D','e','f','a','u','l','t','_','M','o','n','i','t','o','r',0,0};
121 static CFArrayRef modes
;
122 static BOOL modes_has_8bpp
, modes_has_16bpp
;
123 static int default_mode_bpp
;
124 static CRITICAL_SECTION modes_section
;
125 static CRITICAL_SECTION_DEBUG critsect_debug
=
127 0, 0, &modes_section
,
128 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
129 0, 0, { (DWORD_PTR
)(__FILE__
": modes_section") }
131 static CRITICAL_SECTION modes_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
133 static BOOL inited_original_display_mode
;
135 static BOOL
get_display_device_reg_key(char *key
, unsigned len
)
137 static const char display_device_guid_prop
[] = "__wine_display_device_guid";
138 static const char video_path
[] = "System\\CurrentControlSet\\Control\\Video\\{";
139 static const char display0
[] = "}\\0000";
142 assert(len
>= sizeof(video_path
) + sizeof(display0
) + 40);
144 guid_atom
= HandleToULong(GetPropA(GetDesktopWindow(), display_device_guid_prop
));
145 if (!guid_atom
) return FALSE
;
147 memcpy(key
, video_path
, sizeof(video_path
));
149 if (!GlobalGetAtomNameA(guid_atom
, key
+ strlen(key
), 40))
152 strcat(key
, display0
);
154 TRACE("display device key %s\n", wine_dbgstr_a(key
));
159 static BOOL
read_registry_settings(DEVMODEW
*dm
)
161 char wine_mac_reg_key
[128];
168 if (!get_display_device_reg_key(wine_mac_reg_key
, sizeof(wine_mac_reg_key
)))
171 if (RegOpenKeyExA(HKEY_CURRENT_CONFIG
, wine_mac_reg_key
, 0, KEY_READ
, &hkey
))
174 #define query_value(name, data) \
175 size = sizeof(DWORD); \
176 if (RegQueryValueExA(hkey, name, 0, &type, (LPBYTE)(data), &size) || \
177 type != REG_DWORD || size != sizeof(DWORD)) \
180 query_value("DefaultSettings.BitsPerPel", &dm
->dmBitsPerPel
);
181 dm
->dmFields
|= DM_BITSPERPEL
;
182 query_value("DefaultSettings.XResolution", &dm
->dmPelsWidth
);
183 dm
->dmFields
|= DM_PELSWIDTH
;
184 query_value("DefaultSettings.YResolution", &dm
->dmPelsHeight
);
185 dm
->dmFields
|= DM_PELSHEIGHT
;
186 query_value("DefaultSettings.VRefresh", &dm
->dmDisplayFrequency
);
187 dm
->dmFields
|= DM_DISPLAYFREQUENCY
;
188 query_value("DefaultSettings.Flags", &dm
->dmDisplayFlags
);
189 dm
->dmFields
|= DM_DISPLAYFLAGS
;
190 query_value("DefaultSettings.XPanning", &dm
->dmPosition
.x
);
191 query_value("DefaultSettings.YPanning", &dm
->dmPosition
.y
);
192 query_value("DefaultSettings.Orientation", &dm
->dmDisplayOrientation
);
193 query_value("DefaultSettings.FixedOutput", &dm
->dmDisplayFixedOutput
);
202 static BOOL
write_registry_settings(const DEVMODEW
*dm
)
204 char wine_mac_reg_key
[128];
208 if (!get_display_device_reg_key(wine_mac_reg_key
, sizeof(wine_mac_reg_key
)))
211 if (RegCreateKeyExA(HKEY_CURRENT_CONFIG
, wine_mac_reg_key
, 0, NULL
,
212 REG_OPTION_VOLATILE
, KEY_WRITE
, NULL
, &hkey
, NULL
))
215 #define set_value(name, data) \
216 if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
219 set_value("DefaultSettings.BitsPerPel", &dm
->dmBitsPerPel
);
220 set_value("DefaultSettings.XResolution", &dm
->dmPelsWidth
);
221 set_value("DefaultSettings.YResolution", &dm
->dmPelsHeight
);
222 set_value("DefaultSettings.VRefresh", &dm
->dmDisplayFrequency
);
223 set_value("DefaultSettings.Flags", &dm
->dmDisplayFlags
);
224 set_value("DefaultSettings.XPanning", &dm
->dmPosition
.x
);
225 set_value("DefaultSettings.YPanning", &dm
->dmPosition
.y
);
226 set_value("DefaultSettings.Orientation", &dm
->dmDisplayOrientation
);
227 set_value("DefaultSettings.FixedOutput", &dm
->dmDisplayFixedOutput
);
236 static BOOL
write_display_settings(HKEY parent_hkey
, CGDirectDisplayID displayID
)
239 char display_key_name
[19];
241 CGDisplayModeRef display_mode
;
243 CFStringRef pixel_encoding
;
247 snprintf(display_key_name
, sizeof(display_key_name
), "Display 0x%08x", CGDisplayUnitNumber(displayID
));
248 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
249 if (RegCreateKeyExA(parent_hkey
, display_key_name
, 0, NULL
,
250 REG_OPTION_VOLATILE
, KEY_WRITE
, NULL
, &display_hkey
, NULL
))
253 display_mode
= CGDisplayCopyDisplayMode(displayID
);
257 val
= CGDisplayModeGetWidth(display_mode
);
258 if (RegSetValueExA(display_hkey
, "Width", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
260 val
= CGDisplayModeGetHeight(display_mode
);
261 if (RegSetValueExA(display_hkey
, "Height", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
263 val
= CGDisplayModeGetRefreshRate(display_mode
) * 100;
264 if (RegSetValueExA(display_hkey
, "RefreshRateTimes100", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
266 val
= CGDisplayModeGetIOFlags(display_mode
);
267 if (RegSetValueExA(display_hkey
, "IOFlags", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
270 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
271 if (&CGDisplayModeGetPixelWidth
!= NULL
&& &CGDisplayModeGetPixelHeight
!= NULL
)
273 val
= CGDisplayModeGetPixelWidth(display_mode
);
274 if (RegSetValueExA(display_hkey
, "PixelWidth", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
276 val
= CGDisplayModeGetPixelHeight(display_mode
);
277 if (RegSetValueExA(display_hkey
, "PixelHeight", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
282 pixel_encoding
= CGDisplayModeCopyPixelEncoding(display_mode
);
283 len
= CFStringGetLength(pixel_encoding
);
284 buf
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
285 CFStringGetCharacters(pixel_encoding
, CFRangeMake(0, len
), (UniChar
*)buf
);
287 CFRelease(pixel_encoding
);
288 if (RegSetValueExW(display_hkey
, pixelencodingW
, 0, REG_SZ
, (const BYTE
*)buf
, (len
+ 1) * sizeof(WCHAR
)))
294 HeapFree(GetProcessHeap(), 0, buf
);
295 if (display_mode
) CGDisplayModeRelease(display_mode
);
296 RegCloseKey(display_hkey
);
298 RegDeleteKeyA(parent_hkey
, display_key_name
);
303 static void init_original_display_mode(void)
305 BOOL success
= FALSE
;
306 HKEY mac_driver_hkey
, parent_hkey
;
308 struct macdrv_display
*displays
= NULL
;
311 if (inited_original_display_mode
)
314 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver */
315 if (RegCreateKeyExA(HKEY_LOCAL_MACHINE
, "Software\\Wine\\Mac Driver", 0, NULL
,
316 0, KEY_ALL_ACCESS
, NULL
, &mac_driver_hkey
, NULL
))
319 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode */
320 if (RegCreateKeyExA(mac_driver_hkey
, initial_mode_key
, 0, NULL
,
321 REG_OPTION_VOLATILE
, KEY_WRITE
, NULL
, &parent_hkey
, &disposition
))
327 /* If we didn't create a new key, then it already existed. Something already stored
328 the initial display mode since Wine was started. We don't want to overwrite it. */
329 if (disposition
!= REG_CREATED_NEW_KEY
)
332 if (macdrv_get_displays(&displays
, &num_displays
))
335 for (i
= 0; i
< num_displays
; i
++)
337 if (!write_display_settings(parent_hkey
, displays
[i
].displayID
))
345 macdrv_free_displays(displays
);
346 RegCloseKey(parent_hkey
);
347 if (!success
&& parent_hkey
)
348 RegDeleteTreeA(mac_driver_hkey
, initial_mode_key
);
349 RegCloseKey(mac_driver_hkey
);
351 inited_original_display_mode
= TRUE
;
355 static BOOL
read_dword(HKEY hkey
, const char* name
, DWORD
* val
)
357 DWORD type
, size
= sizeof(*val
);
358 if (RegQueryValueExA(hkey
, name
, 0, &type
, (BYTE
*)val
, &size
) || type
!= REG_DWORD
|| size
!= sizeof(*val
))
364 static void free_display_mode_descriptor(struct display_mode_descriptor
* desc
)
368 if (desc
->pixel_encoding
)
369 CFRelease(desc
->pixel_encoding
);
370 HeapFree(GetProcessHeap(), 0, desc
);
375 static struct display_mode_descriptor
* create_original_display_mode_descriptor(CGDirectDisplayID displayID
)
377 static const char display_key_format
[] = "Software\\Wine\\Mac Driver\\Initial Display Mode\\Display 0x%08x";
378 struct display_mode_descriptor
* ret
= NULL
;
379 struct display_mode_descriptor
* desc
;
380 char display_key
[sizeof(display_key_format
) + 10];
384 WCHAR
* pixel_encoding
= NULL
, *end
;
386 init_original_display_mode();
388 snprintf(display_key
, sizeof(display_key
), display_key_format
, CGDisplayUnitNumber(displayID
));
389 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
390 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE
, display_key
, 0, KEY_READ
, &hkey
))
393 desc
= HeapAlloc(GetProcessHeap(), 0, sizeof(*desc
));
394 desc
->pixel_encoding
= NULL
;
396 if (!read_dword(hkey
, "Width", &desc
->width
) ||
397 !read_dword(hkey
, "Height", &desc
->height
) ||
398 !read_dword(hkey
, "RefreshRateTimes100", &refresh100
) ||
399 !read_dword(hkey
, "IOFlags", &desc
->io_flags
))
402 desc
->refresh
= refresh100
/ 100.0;
406 if (!read_dword(hkey
, "PixelWidth", &desc
->pixel_width
) ||
407 !read_dword(hkey
, "PixelHeight", &desc
->pixel_height
))
409 desc
->pixel_width
= desc
->width
;
410 desc
->pixel_height
= desc
->height
;
414 if (RegQueryValueExW(hkey
, pixelencodingW
, 0, &type
, NULL
, &size
) || type
!= REG_SZ
)
416 pixel_encoding
= HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
));
417 if (RegQueryValueExW(hkey
, pixelencodingW
, 0, &type
, (BYTE
*)pixel_encoding
, &size
) || type
!= REG_SZ
)
419 if ((end
= memchrW(pixel_encoding
, 0, size
)))
420 size
= end
- pixel_encoding
;
421 desc
->pixel_encoding
= CFStringCreateWithCharacters(NULL
, (const UniChar
*)pixel_encoding
, size
);
427 free_display_mode_descriptor(desc
);
428 HeapFree(GetProcessHeap(), 0, pixel_encoding
);
434 static BOOL
display_mode_matches_descriptor(CGDisplayModeRef mode
, const struct display_mode_descriptor
* desc
)
438 CFStringRef mode_pixel_encoding
;
443 if (CGDisplayModeGetWidth(mode
) != desc
->width
||
444 CGDisplayModeGetHeight(mode
) != desc
->height
)
447 mode_io_flags
= CGDisplayModeGetIOFlags(mode
);
448 if ((desc
->io_flags
^ mode_io_flags
) & (kDisplayModeValidFlag
| kDisplayModeSafeFlag
| kDisplayModeStretchedFlag
|
449 kDisplayModeInterlacedFlag
| kDisplayModeTelevisionFlag
))
452 mode_refresh
= CGDisplayModeGetRefreshRate(mode
);
455 if (fabs(desc
->refresh
- mode_refresh
) > 0.1)
458 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
459 if (&CGDisplayModeGetPixelWidth
!= NULL
&& &CGDisplayModeGetPixelHeight
!= NULL
)
461 if (CGDisplayModeGetPixelWidth(mode
) != desc
->pixel_width
||
462 CGDisplayModeGetPixelHeight(mode
) != desc
->pixel_height
)
467 if (CGDisplayModeGetWidth(mode
) != desc
->pixel_width
||
468 CGDisplayModeGetHeight(mode
) != desc
->pixel_height
)
471 mode_pixel_encoding
= CGDisplayModeCopyPixelEncoding(mode
);
472 if (!CFEqual(mode_pixel_encoding
, desc
->pixel_encoding
))
474 CFRelease(mode_pixel_encoding
);
477 CFRelease(mode_pixel_encoding
);
483 static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode
)
485 CFStringRef pixel_encoding
;
486 int bits_per_pixel
= 0;
488 pixel_encoding
= CGDisplayModeCopyPixelEncoding(display_mode
);
491 if (CFEqual(pixel_encoding
, CFSTR(kIO32BitFloatPixels
)))
492 bits_per_pixel
= 128;
493 else if (CFEqual(pixel_encoding
, CFSTR(kIO16BitFloatPixels
)))
495 else if (CFEqual(pixel_encoding
, CFSTR(kIO64BitDirectPixels
)))
497 else if (CFEqual(pixel_encoding
, CFSTR(kIO30BitDirectPixels
)))
499 else if (CFEqual(pixel_encoding
, CFSTR(IO32BitDirectPixels
)))
501 else if (CFEqual(pixel_encoding
, CFSTR(IO16BitDirectPixels
)))
503 else if (CFEqual(pixel_encoding
, CFSTR(IO8BitIndexedPixels
)))
505 else if (CFEqual(pixel_encoding
, CFSTR(IO4BitIndexedPixels
)))
507 else if (CFEqual(pixel_encoding
, CFSTR(IO2BitIndexedPixels
)))
509 else if (CFEqual(pixel_encoding
, CFSTR(IO1BitIndexedPixels
)))
512 CFRelease(pixel_encoding
);
515 return bits_per_pixel
;
519 static int get_default_bpp(void)
523 EnterCriticalSection(&modes_section
);
525 if (!default_mode_bpp
)
527 CGDisplayModeRef mode
= CGDisplayCopyDisplayMode(kCGDirectMainDisplay
);
530 default_mode_bpp
= display_mode_bits_per_pixel(mode
);
534 if (!default_mode_bpp
)
535 default_mode_bpp
= 32;
538 ret
= default_mode_bpp
;
540 LeaveCriticalSection(&modes_section
);
542 TRACE(" -> %d\n", ret
);
547 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
548 static CFDictionaryRef
create_mode_dict(CGDisplayModeRef display_mode
, BOOL is_original
)
551 SInt32 io_flags
= CGDisplayModeGetIOFlags(display_mode
);
552 SInt64 width
= CGDisplayModeGetWidth(display_mode
);
553 SInt64 height
= CGDisplayModeGetHeight(display_mode
);
554 double refresh_rate
= CGDisplayModeGetRefreshRate(display_mode
);
555 CFStringRef pixel_encoding
= CGDisplayModeCopyPixelEncoding(display_mode
);
556 CFNumberRef cf_io_flags
, cf_width
, cf_height
, cf_refresh
;
558 if (retina_enabled
&& is_original
)
564 io_flags
&= kDisplayModeValidFlag
| kDisplayModeSafeFlag
| kDisplayModeInterlacedFlag
|
565 kDisplayModeStretchedFlag
| kDisplayModeTelevisionFlag
;
566 cf_io_flags
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &io_flags
);
567 cf_width
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, &width
);
568 cf_height
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, &height
);
569 cf_refresh
= CFNumberCreate(NULL
, kCFNumberDoubleType
, &refresh_rate
);
572 static const CFStringRef keys
[] = {
576 CFSTR("pixel_encoding"),
577 CFSTR("refresh_rate"),
579 const void* values
[ARRAY_SIZE(keys
)] = {
587 ret
= CFDictionaryCreate(NULL
, (const void**)keys
, (const void**)values
, ARRAY_SIZE(keys
),
588 &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
591 CFRelease(pixel_encoding
);
592 CFRelease(cf_io_flags
);
594 CFRelease(cf_height
);
595 CFRelease(cf_refresh
);
602 /***********************************************************************
605 * Wrapper around CGDisplayCopyAllDisplayModes() to include additional
606 * modes on Retina-capable systems, but filter those which would confuse
607 * Windows apps (basically duplicates at different DPIs).
609 * For example, some Retina Macs support a 1920x1200 mode, but it's not
610 * returned from CGDisplayCopyAllDisplayModes() without special options.
611 * This is especially bad if that's the user's default mode, since then
612 * no "available" mode matches the initial settings.
614 static CFArrayRef
copy_display_modes(CGDirectDisplayID display
)
616 CFArrayRef modes
= NULL
;
618 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
619 if (&CGDisplayModeGetPixelWidth
!= NULL
&& &CGDisplayModeGetPixelHeight
!= NULL
)
621 CFDictionaryRef options
;
622 struct display_mode_descriptor
* desc
;
623 CFMutableDictionaryRef modes_by_size
;
625 CGDisplayModeRef
* mode_array
;
627 options
= CFDictionaryCreate(NULL
, (const void**)&kCGDisplayShowDuplicateLowResolutionModes
,
628 (const void**)&kCFBooleanTrue
, 1, &kCFTypeDictionaryKeyCallBacks
,
629 &kCFTypeDictionaryValueCallBacks
);
631 modes
= CGDisplayCopyAllDisplayModes(display
, options
);
637 desc
= create_original_display_mode_descriptor(display
);
639 modes_by_size
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
640 count
= CFArrayGetCount(modes
);
641 for (i
= 0; i
< count
; i
++)
644 CGDisplayModeRef new_mode
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(modes
, i
);
645 BOOL new_is_original
= display_mode_matches_descriptor(new_mode
, desc
);
646 CFDictionaryRef key
= create_mode_dict(new_mode
, new_is_original
);
648 /* If a given mode is the user's default, then always list it in preference to any similar
649 modes that may exist. */
654 CFStringRef pixel_encoding
= CGDisplayModeCopyPixelEncoding(new_mode
);
655 CGDisplayModeRef old_mode
;
659 BOOL bpp30
= CFEqual(pixel_encoding
, CFSTR(kIO30BitDirectPixels
));
660 CFRelease(pixel_encoding
);
663 /* This is an odd pixel encoding. It seems it's only returned
664 when using kCGDisplayShowDuplicateLowResolutionModes. It's
665 32bpp in terms of the actual raster layout, but it's 10
666 bits per component. I think that no Windows program is
667 likely to need it and they will probably be confused by it.
674 old_mode
= (CGDisplayModeRef
)CFDictionaryGetValue(modes_by_size
, key
);
677 BOOL old_is_original
= display_mode_matches_descriptor(old_mode
, desc
);
683 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
685 size_t width_points
= CGDisplayModeGetWidth(new_mode
);
686 size_t height_points
= CGDisplayModeGetHeight(new_mode
);
687 size_t new_width_pixels
= CGDisplayModeGetPixelWidth(new_mode
);
688 size_t new_height_pixels
= CGDisplayModeGetPixelHeight(new_mode
);
689 size_t old_width_pixels
= CGDisplayModeGetPixelWidth(old_mode
);
690 size_t old_height_pixels
= CGDisplayModeGetPixelHeight(old_mode
);
691 BOOL new_size_same
= (new_width_pixels
== width_points
&& new_height_pixels
== height_points
);
692 BOOL old_size_same
= (old_width_pixels
== width_points
&& old_height_pixels
== height_points
);
694 if (new_size_same
&& !old_size_same
)
696 else if (!new_size_same
&& old_size_same
)
700 /* Otherwise, prefer the mode with the smaller pixel size. */
701 if (old_width_pixels
< new_width_pixels
|| old_height_pixels
< new_height_pixels
)
709 CFDictionarySetValue(modes_by_size
, key
, new_mode
);
714 free_display_mode_descriptor(desc
);
717 count
= CFDictionaryGetCount(modes_by_size
);
718 mode_array
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(mode_array
[0]));
719 CFDictionaryGetKeysAndValues(modes_by_size
, NULL
, (const void **)mode_array
);
720 modes
= CFArrayCreate(NULL
, (const void **)mode_array
, count
, &kCFTypeArrayCallBacks
);
721 HeapFree(GetProcessHeap(), 0, mode_array
);
722 CFRelease(modes_by_size
);
726 modes
= CGDisplayCopyAllDisplayModes(display
, NULL
);
732 void check_retina_status(void)
736 struct display_mode_descriptor
* desc
= create_original_display_mode_descriptor(kCGDirectMainDisplay
);
737 CGDisplayModeRef mode
= CGDisplayCopyDisplayMode(kCGDirectMainDisplay
);
738 BOOL new_value
= display_mode_matches_descriptor(mode
, desc
);
740 CGDisplayModeRelease(mode
);
741 free_display_mode_descriptor(desc
);
743 if (new_value
!= retina_on
)
744 macdrv_set_cocoa_retina_mode(new_value
);
749 /***********************************************************************
750 * ChangeDisplaySettingsEx (MACDRV.@)
753 LONG CDECL
macdrv_ChangeDisplaySettingsEx(LPCWSTR devname
, LPDEVMODEW devmode
,
754 HWND hwnd
, DWORD flags
, LPVOID lpvoid
)
756 LONG ret
= DISP_CHANGE_BADMODE
;
758 struct macdrv_display
*displays
;
760 CFArrayRef display_modes
;
761 struct display_mode_descriptor
* desc
;
762 CFIndex count
, i
, safe
, best
;
763 CGDisplayModeRef best_display_mode
;
764 uint32_t best_io_flags
;
765 BOOL best_is_original
;
767 TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname
), devmode
, hwnd
, flags
, lpvoid
);
769 init_original_display_mode();
771 if (macdrv_get_displays(&displays
, &num_displays
))
772 return DISP_CHANGE_FAILED
;
774 display_modes
= copy_display_modes(displays
[0].displayID
);
777 macdrv_free_displays(displays
);
778 return DISP_CHANGE_FAILED
;
781 bpp
= get_default_bpp();
782 if ((devmode
->dmFields
& DM_BITSPERPEL
) && devmode
->dmBitsPerPel
!= bpp
)
783 TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp
, devmode
->dmBitsPerPel
);
785 TRACE("looking for %dx%dx%dbpp @%d Hz",
786 (devmode
->dmFields
& DM_PELSWIDTH
? devmode
->dmPelsWidth
: 0),
787 (devmode
->dmFields
& DM_PELSHEIGHT
? devmode
->dmPelsHeight
: 0),
789 (devmode
->dmFields
& DM_DISPLAYFREQUENCY
? devmode
->dmDisplayFrequency
: 0));
790 if (devmode
->dmFields
& DM_DISPLAYFIXEDOUTPUT
)
791 TRACE(" %sstretched", devmode
->dmDisplayFixedOutput
== DMDFO_STRETCH
? "" : "un");
792 if (devmode
->dmFields
& DM_DISPLAYFLAGS
)
793 TRACE(" %sinterlaced", devmode
->dmDisplayFlags
& DM_INTERLACED
? "" : "non-");
796 desc
= create_original_display_mode_descriptor(displays
[0].displayID
);
799 best_display_mode
= NULL
;
800 count
= CFArrayGetCount(display_modes
);
801 for (i
= 0; i
< count
; i
++)
803 CGDisplayModeRef display_mode
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(display_modes
, i
);
804 BOOL is_original
= display_mode_matches_descriptor(display_mode
, desc
);
805 uint32_t io_flags
= CGDisplayModeGetIOFlags(display_mode
);
806 int mode_bpp
= display_mode_bits_per_pixel(display_mode
);
807 size_t width
= CGDisplayModeGetWidth(display_mode
);
808 size_t height
= CGDisplayModeGetHeight(display_mode
);
810 if (is_original
&& retina_enabled
)
816 if (!(io_flags
& kDisplayModeValidFlag
) || !(io_flags
& kDisplayModeSafeFlag
))
824 if (devmode
->dmFields
& DM_PELSWIDTH
)
826 if (devmode
->dmPelsWidth
!= width
)
829 if (devmode
->dmFields
& DM_PELSHEIGHT
)
831 if (devmode
->dmPelsHeight
!= height
)
834 if ((devmode
->dmFields
& DM_DISPLAYFREQUENCY
) && devmode
->dmDisplayFrequency
!= 0)
836 double refresh_rate
= CGDisplayModeGetRefreshRate(display_mode
);
839 if (devmode
->dmDisplayFrequency
!= (DWORD
)refresh_rate
)
842 if (devmode
->dmFields
& DM_DISPLAYFLAGS
)
844 if (!(devmode
->dmDisplayFlags
& DM_INTERLACED
) != !(io_flags
& kDisplayModeInterlacedFlag
))
847 else if (best_display_mode
)
849 if (io_flags
& kDisplayModeInterlacedFlag
&& !(best_io_flags
& kDisplayModeInterlacedFlag
))
851 else if (!(io_flags
& kDisplayModeInterlacedFlag
) && best_io_flags
& kDisplayModeInterlacedFlag
)
854 if (devmode
->dmFields
& DM_DISPLAYFIXEDOUTPUT
)
856 if (!(devmode
->dmDisplayFixedOutput
== DMDFO_STRETCH
) != !(io_flags
& kDisplayModeStretchedFlag
))
859 else if (best_display_mode
)
861 if (io_flags
& kDisplayModeStretchedFlag
&& !(best_io_flags
& kDisplayModeStretchedFlag
))
863 else if (!(io_flags
& kDisplayModeStretchedFlag
) && best_io_flags
& kDisplayModeStretchedFlag
)
867 if (best_display_mode
)
871 best_display_mode
= display_mode
;
873 best_io_flags
= io_flags
;
874 best_is_original
= is_original
;
877 if (best_display_mode
)
879 /* we have a valid mode */
880 TRACE("Requested display settings match mode %ld\n", best
);
882 if ((flags
& CDS_UPDATEREGISTRY
) && !write_registry_settings(devmode
))
884 WARN("Failed to update registry\n");
885 ret
= DISP_CHANGE_NOTUPDATED
;
887 else if (flags
& (CDS_TEST
| CDS_NORESET
))
888 ret
= DISP_CHANGE_SUCCESSFUL
;
889 else if (macdrv_set_display_mode(&displays
[0], best_display_mode
))
891 int mode_bpp
= display_mode_bits_per_pixel(best_display_mode
);
892 size_t width
= CGDisplayModeGetWidth(best_display_mode
);
893 size_t height
= CGDisplayModeGetHeight(best_display_mode
);
895 if (best_is_original
&& retina_enabled
)
901 SendMessageW(GetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT
, mode_bpp
,
902 MAKELPARAM(width
, height
));
903 ret
= DISP_CHANGE_SUCCESSFUL
;
905 macdrv_init_display_devices(TRUE
);
909 WARN("Failed to set display mode\n");
910 ret
= DISP_CHANGE_FAILED
;
915 /* no valid modes found */
916 ERR("No matching mode found %ux%ux%d @%u!\n", devmode
->dmPelsWidth
, devmode
->dmPelsHeight
,
917 bpp
, devmode
->dmDisplayFrequency
);
920 free_display_mode_descriptor(desc
);
921 CFRelease(display_modes
);
922 macdrv_free_displays(displays
);
927 /***********************************************************************
928 * EnumDisplaySettingsEx (MACDRV.@)
931 BOOL CDECL
macdrv_EnumDisplaySettingsEx(LPCWSTR devname
, DWORD mode
,
932 LPDEVMODEW devmode
, DWORD flags
)
934 static const WCHAR dev_name
[CCHDEVICENAME
] =
935 { 'W','i','n','e',' ','M','a','c',' ','d','r','i','v','e','r',0 };
936 struct macdrv_display
*displays
= NULL
;
938 CGDisplayModeRef display_mode
;
939 int display_mode_bpp
;
940 BOOL synthesized
= FALSE
;
944 TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname
), mode
, devmode
, devmode
->dmSize
, flags
);
946 init_original_display_mode();
948 memcpy(devmode
->dmDeviceName
, dev_name
, sizeof(dev_name
));
949 devmode
->dmSpecVersion
= DM_SPECVERSION
;
950 devmode
->dmDriverVersion
= DM_SPECVERSION
;
951 devmode
->dmSize
= FIELD_OFFSET(DEVMODEW
, dmICMMethod
);
952 devmode
->dmDriverExtra
= 0;
953 memset(&devmode
->dmFields
, 0, devmode
->dmSize
- FIELD_OFFSET(DEVMODEW
, dmFields
));
955 if (mode
== ENUM_REGISTRY_SETTINGS
)
957 TRACE("mode %d (registry) -- getting default mode\n", mode
);
958 return read_registry_settings(devmode
);
961 if (macdrv_get_displays(&displays
, &num_displays
))
964 if (mode
== ENUM_CURRENT_SETTINGS
)
966 TRACE("mode %d (current) -- getting current mode\n", mode
);
967 display_mode
= CGDisplayCopyDisplayMode(displays
[0].displayID
);
968 display_mode_bpp
= display_mode_bits_per_pixel(display_mode
);
974 EnterCriticalSection(&modes_section
);
976 if (mode
== 0 || !modes
)
978 if (modes
) CFRelease(modes
);
979 modes
= copy_display_modes(displays
[0].displayID
);
980 modes_has_8bpp
= modes_has_16bpp
= FALSE
;
984 count
= CFArrayGetCount(modes
);
985 for (i
= 0; i
< count
&& !(modes_has_8bpp
&& modes_has_16bpp
); i
++)
987 CGDisplayModeRef mode
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(modes
, i
);
988 int bpp
= display_mode_bits_per_pixel(mode
);
990 modes_has_8bpp
= TRUE
;
992 modes_has_16bpp
= TRUE
;
1000 int default_bpp
= get_default_bpp();
1001 DWORD seen_modes
= 0;
1003 count
= CFArrayGetCount(modes
);
1004 for (i
= 0; i
< count
; i
++)
1006 CGDisplayModeRef candidate
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(modes
, i
);
1008 io_flags
= CGDisplayModeGetIOFlags(candidate
);
1009 if (!(flags
& EDS_RAWMODE
) &&
1010 (!(io_flags
& kDisplayModeValidFlag
) || !(io_flags
& kDisplayModeSafeFlag
)))
1014 if (seen_modes
> mode
)
1016 display_mode
= (CGDisplayModeRef
)CFRetain(candidate
);
1017 display_mode_bpp
= display_mode_bits_per_pixel(display_mode
);
1021 /* We only synthesize modes from those having the default bpp. */
1022 if (display_mode_bits_per_pixel(candidate
) != default_bpp
)
1025 if (!modes_has_8bpp
)
1028 if (seen_modes
> mode
)
1030 display_mode
= (CGDisplayModeRef
)CFRetain(candidate
);
1031 display_mode_bpp
= 8;
1037 if (!modes_has_16bpp
)
1040 if (seen_modes
> mode
)
1042 display_mode
= (CGDisplayModeRef
)CFRetain(candidate
);
1043 display_mode_bpp
= 16;
1051 LeaveCriticalSection(&modes_section
);
1057 /* We currently only report modes for the primary display, so it's at (0, 0). */
1058 devmode
->dmPosition
.x
= 0;
1059 devmode
->dmPosition
.y
= 0;
1060 devmode
->dmFields
|= DM_POSITION
;
1062 rotation
= CGDisplayRotation(displays
[0].displayID
);
1063 devmode
->dmDisplayOrientation
= ((int)((rotation
/ 90) + 0.5)) % 4;
1064 devmode
->dmFields
|= DM_DISPLAYORIENTATION
;
1066 io_flags
= CGDisplayModeGetIOFlags(display_mode
);
1067 if (io_flags
& kDisplayModeStretchedFlag
)
1068 devmode
->dmDisplayFixedOutput
= DMDFO_STRETCH
;
1070 devmode
->dmDisplayFixedOutput
= DMDFO_CENTER
;
1071 devmode
->dmFields
|= DM_DISPLAYFIXEDOUTPUT
;
1073 devmode
->dmBitsPerPel
= display_mode_bpp
;
1074 if (devmode
->dmBitsPerPel
)
1075 devmode
->dmFields
|= DM_BITSPERPEL
;
1077 devmode
->dmPelsWidth
= CGDisplayModeGetWidth(display_mode
);
1078 devmode
->dmPelsHeight
= CGDisplayModeGetHeight(display_mode
);
1081 struct display_mode_descriptor
* desc
= create_original_display_mode_descriptor(displays
[0].displayID
);
1082 if (display_mode_matches_descriptor(display_mode
, desc
))
1084 devmode
->dmPelsWidth
*= 2;
1085 devmode
->dmPelsHeight
*= 2;
1087 free_display_mode_descriptor(desc
);
1089 devmode
->dmFields
|= DM_PELSWIDTH
| DM_PELSHEIGHT
;
1091 devmode
->dmDisplayFlags
= 0;
1092 if (io_flags
& kDisplayModeInterlacedFlag
)
1093 devmode
->dmDisplayFlags
|= DM_INTERLACED
;
1094 devmode
->dmFields
|= DM_DISPLAYFLAGS
;
1096 devmode
->dmDisplayFrequency
= CGDisplayModeGetRefreshRate(display_mode
);
1097 if (!devmode
->dmDisplayFrequency
)
1098 devmode
->dmDisplayFrequency
= 60;
1099 devmode
->dmFields
|= DM_DISPLAYFREQUENCY
;
1101 CFRelease(display_mode
);
1102 macdrv_free_displays(displays
);
1104 TRACE("mode %d -- %dx%dx%dbpp @%d Hz", mode
,
1105 devmode
->dmPelsWidth
, devmode
->dmPelsHeight
, devmode
->dmBitsPerPel
,
1106 devmode
->dmDisplayFrequency
);
1107 if (devmode
->dmDisplayOrientation
)
1108 TRACE(" rotated %u degrees", devmode
->dmDisplayOrientation
* 90);
1109 if (devmode
->dmDisplayFixedOutput
== DMDFO_STRETCH
)
1110 TRACE(" stretched");
1111 if (devmode
->dmDisplayFlags
& DM_INTERLACED
)
1112 TRACE(" interlaced");
1114 TRACE(" (synthesized)");
1120 TRACE("mode %d -- not present\n", mode
);
1121 if (displays
) macdrv_free_displays(displays
);
1122 SetLastError(ERROR_NO_MORE_FILES
);
1127 /***********************************************************************
1128 * GetDeviceGammaRamp (MACDRV.@)
1130 BOOL CDECL
macdrv_GetDeviceGammaRamp(PHYSDEV dev
, LPVOID ramp
)
1133 DDGAMMARAMP
*r
= ramp
;
1134 struct macdrv_display
*displays
;
1136 uint32_t mac_entries
;
1137 int win_entries
= ARRAY_SIZE(r
->red
);
1138 CGGammaValue
*red
, *green
, *blue
;
1142 TRACE("dev %p ramp %p\n", dev
, ramp
);
1144 if (macdrv_get_displays(&displays
, &num_displays
))
1146 WARN("failed to get Mac displays\n");
1150 mac_entries
= CGDisplayGammaTableCapacity(displays
[0].displayID
);
1151 red
= HeapAlloc(GetProcessHeap(), 0, mac_entries
* sizeof(red
[0]) * 3);
1154 green
= red
+ mac_entries
;
1155 blue
= green
+ mac_entries
;
1157 err
= CGGetDisplayTransferByTable(displays
[0].displayID
, mac_entries
, red
, green
,
1158 blue
, &mac_entries
);
1159 if (err
!= kCGErrorSuccess
)
1161 WARN("failed to get Mac gamma table: %d\n", err
);
1165 if (mac_entries
== win_entries
)
1167 for (win_entry
= 0; win_entry
< win_entries
; win_entry
++)
1169 r
->red
[win_entry
] = red
[win_entry
] * 65535 + 0.5;
1170 r
->green
[win_entry
] = green
[win_entry
] * 65535 + 0.5;
1171 r
->blue
[win_entry
] = blue
[win_entry
] * 65535 + 0.5;
1176 for (win_entry
= 0; win_entry
< win_entries
; win_entry
++)
1178 double mac_pos
= win_entry
* (mac_entries
- 1) / (double)(win_entries
- 1);
1179 int mac_entry
= mac_pos
;
1180 double red_value
, green_value
, blue_value
;
1182 if (mac_entry
== mac_entries
- 1)
1184 red_value
= red
[mac_entry
];
1185 green_value
= green
[mac_entry
];
1186 blue_value
= blue
[mac_entry
];
1190 double distance
= mac_pos
- mac_entry
;
1192 red_value
= red
[mac_entry
] * (1 - distance
) + red
[mac_entry
+ 1] * distance
;
1193 green_value
= green
[mac_entry
] * (1 - distance
) + green
[mac_entry
+ 1] * distance
;
1194 blue_value
= blue
[mac_entry
] * (1 - distance
) + blue
[mac_entry
+ 1] * distance
;
1197 r
->red
[win_entry
] = red_value
* 65535 + 0.5;
1198 r
->green
[win_entry
] = green_value
* 65535 + 0.5;
1199 r
->blue
[win_entry
] = blue_value
* 65535 + 0.5;
1206 HeapFree(GetProcessHeap(), 0, red
);
1207 macdrv_free_displays(displays
);
1211 /***********************************************************************
1212 * SetDeviceGammaRamp (MACDRV.@)
1214 BOOL CDECL
macdrv_SetDeviceGammaRamp(PHYSDEV dev
, LPVOID ramp
)
1216 DDGAMMARAMP
*r
= ramp
;
1217 struct macdrv_display
*displays
;
1219 int win_entries
= ARRAY_SIZE(r
->red
);
1220 CGGammaValue
*red
, *green
, *blue
;
1222 CGError err
= kCGErrorFailure
;
1224 TRACE("dev %p ramp %p\n", dev
, ramp
);
1226 if (!allow_set_gamma
)
1228 TRACE("disallowed by registry setting\n");
1232 if (macdrv_get_displays(&displays
, &num_displays
))
1234 WARN("failed to get Mac displays\n");
1238 red
= HeapAlloc(GetProcessHeap(), 0, win_entries
* sizeof(red
[0]) * 3);
1241 green
= red
+ win_entries
;
1242 blue
= green
+ win_entries
;
1244 for (i
= 0; i
< win_entries
; i
++)
1246 red
[i
] = r
->red
[i
] / 65535.0;
1247 green
[i
] = r
->green
[i
] / 65535.0;
1248 blue
[i
] = r
->blue
[i
] / 65535.0;
1251 err
= CGSetDisplayTransferByTable(displays
[0].displayID
, win_entries
, red
, green
, blue
);
1252 if (err
!= kCGErrorSuccess
)
1253 WARN("failed to set display gamma table: %d\n", err
);
1256 HeapFree(GetProcessHeap(), 0, red
);
1257 macdrv_free_displays(displays
);
1258 return (err
== kCGErrorSuccess
);
1262 /***********************************************************************
1263 * macdrv_displays_changed
1265 * Handler for DISPLAYS_CHANGED events.
1267 void macdrv_displays_changed(const macdrv_event
*event
)
1269 HWND hwnd
= GetDesktopWindow();
1271 /* A system display change will get delivered to all GUI-attached threads,
1272 so the desktop-window-owning thread will get it and all others should
1273 ignore it. A synthesized display change event due to activation
1274 will only get delivered to the activated process. So, it needs to
1275 process it (by sending it to the desktop window). */
1276 if (event
->displays_changed
.activating
||
1277 GetWindowThreadProcessId(hwnd
, NULL
) == GetCurrentThreadId())
1279 CGDirectDisplayID mainDisplay
= CGMainDisplayID();
1280 CGDisplayModeRef mode
= CGDisplayCopyDisplayMode(mainDisplay
);
1281 size_t width
= CGDisplayModeGetWidth(mode
);
1282 size_t height
= CGDisplayModeGetHeight(mode
);
1283 int mode_bpp
= display_mode_bits_per_pixel(mode
);
1284 struct display_mode_descriptor
* desc
= create_original_display_mode_descriptor(mainDisplay
);
1285 BOOL is_original
= display_mode_matches_descriptor(mode
, desc
);
1287 free_display_mode_descriptor(desc
);
1288 CGDisplayModeRelease(mode
);
1290 macdrv_init_display_devices(TRUE
);
1292 if (is_original
&& retina_enabled
)
1298 SendMessageW(hwnd
, WM_MACDRV_UPDATE_DESKTOP_RECT
, mode_bpp
,
1299 MAKELPARAM(width
, height
));
1303 /***********************************************************************
1306 * Initialize a GPU instance and return its GUID string in guid_string and driver value in driver parameter.
1308 * Return FALSE on failure and TRUE on success.
1310 static BOOL
macdrv_init_gpu(HDEVINFO devinfo
, const struct macdrv_gpu
*gpu
, int gpu_index
, WCHAR
*guid_string
,
1313 static const BOOL present
= TRUE
;
1314 SP_DEVINFO_DATA device_data
= {sizeof(device_data
)};
1315 WCHAR instanceW
[MAX_PATH
];
1316 WCHAR nameW
[MAX_PATH
];
1317 WCHAR bufferW
[1024];
1325 sprintfW(instanceW
, gpu_instance_fmtW
, gpu
->vendor_id
, gpu
->device_id
, gpu
->subsys_id
, gpu
->revision_id
, gpu_index
);
1326 MultiByteToWideChar(CP_UTF8
, 0, gpu
->name
, -1, nameW
, ARRAY_SIZE(nameW
));
1327 if (!SetupDiOpenDeviceInfoW(devinfo
, instanceW
, NULL
, 0, &device_data
))
1329 SetupDiCreateDeviceInfoW(devinfo
, instanceW
, &GUID_DEVCLASS_DISPLAY
, nameW
, NULL
, 0, &device_data
);
1330 if (!SetupDiRegisterDeviceInfo(devinfo
, &device_data
, 0, NULL
, NULL
, NULL
))
1334 /* Write HardwareID registry property, REG_MULTI_SZ */
1335 written
= sprintfW(bufferW
, gpu_hardware_id_fmtW
, gpu
->vendor_id
, gpu
->device_id
);
1336 bufferW
[written
+ 1] = 0;
1337 if (!SetupDiSetDeviceRegistryPropertyW(devinfo
, &device_data
, SPDRP_HARDWAREID
, (const BYTE
*)bufferW
,
1338 (written
+ 2) * sizeof(WCHAR
)))
1341 /* Write DEVPKEY_Device_IsPresent property */
1342 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPKEY_Device_IsPresent
, DEVPROP_TYPE_BOOLEAN
,
1343 (const BYTE
*)&present
, sizeof(present
), 0))
1347 * This is where HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} links to */
1348 hkey
= SetupDiCreateDevRegKeyW(devinfo
, &device_data
, DICS_FLAG_GLOBAL
, 0, DIREG_DRV
, NULL
, NULL
);
1350 /* Write DriverDesc value */
1351 if (RegSetValueExW(hkey
, driver_descW
, 0, REG_SZ
, (const BYTE
*)nameW
, (lstrlenW(nameW
) + 1) * sizeof(WCHAR
)))
1353 /* Write DriverDateData value, using current time as driver date, needed by Evoland */
1354 GetSystemTimeAsFileTime(&filetime
);
1355 if (RegSetValueExW(hkey
, driver_date_dataW
, 0, REG_BINARY
, (BYTE
*)&filetime
, sizeof(filetime
)))
1359 /* Retrieve driver value for adapters */
1360 if (!SetupDiGetDeviceRegistryPropertyW(devinfo
, &device_data
, SPDRP_DRIVER
, NULL
, (BYTE
*)bufferW
, sizeof(bufferW
),
1363 lstrcpyW(driver
, nt_classW
);
1364 lstrcatW(driver
, bufferW
);
1366 /* Write GUID in VideoID in .../instance/Device Parameters, reuse the GUID if it's existent */
1367 hkey
= SetupDiCreateDevRegKeyW(devinfo
, &device_data
, DICS_FLAG_GLOBAL
, 0, DIREG_DEV
, NULL
, NULL
);
1369 size
= sizeof(bufferW
);
1370 if (RegQueryValueExW(hkey
, video_idW
, 0, NULL
, (BYTE
*)bufferW
, &size
))
1373 sprintfW(bufferW
, guid_fmtW
, guid
.Data1
, guid
.Data2
, guid
.Data3
, guid
.Data4
[0], guid
.Data4
[1], guid
.Data4
[2],
1374 guid
.Data4
[3], guid
.Data4
[4], guid
.Data4
[5], guid
.Data4
[6], guid
.Data4
[7]);
1375 if (RegSetValueExW(hkey
, video_idW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (lstrlenW(bufferW
) + 1) * sizeof(WCHAR
)))
1378 lstrcpyW(guid_string
, bufferW
);
1384 ERR("Failed to initialize GPU\n");
1388 /***********************************************************************
1389 * macdrv_init_adapter
1391 * Initialize an adapter.
1393 * Return FALSE on failure and TRUE on success.
1395 static BOOL
macdrv_init_adapter(HKEY video_hkey
, int video_index
, int gpu_index
, int adapter_index
, int monitor_count
,
1396 const struct macdrv_gpu
*gpu
, const WCHAR
*guid_string
, const WCHAR
*gpu_driver
,
1397 const struct macdrv_adapter
*adapter
)
1399 WCHAR adapter_keyW
[MAX_PATH
];
1400 WCHAR key_nameW
[MAX_PATH
];
1401 WCHAR bufferW
[1024];
1407 sprintfW(key_nameW
, device_video_fmtW
, video_index
);
1408 lstrcpyW(bufferW
, machine_prefixW
);
1409 sprintfW(adapter_keyW
, adapter_key_fmtW
, guid_string
, adapter_index
);
1410 lstrcatW(bufferW
, adapter_keyW
);
1412 /* Write value of \Device\Video? (adapter key) in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */
1413 if (RegSetValueExW(video_hkey
, key_nameW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (lstrlenW(bufferW
) + 1) * sizeof(WCHAR
)))
1416 /* Create HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} link to GPU driver */
1417 ls
= RegCreateKeyExW(HKEY_LOCAL_MACHINE
, adapter_keyW
, 0, NULL
, REG_OPTION_VOLATILE
| REG_OPTION_CREATE_LINK
,
1418 KEY_ALL_ACCESS
, NULL
, &hkey
, NULL
);
1419 if (ls
== ERROR_ALREADY_EXISTS
)
1420 RegCreateKeyExW(HKEY_LOCAL_MACHINE
, adapter_keyW
, 0, NULL
, REG_OPTION_VOLATILE
| REG_OPTION_OPEN_LINK
,
1421 KEY_ALL_ACCESS
, NULL
, &hkey
, NULL
);
1422 if (RegSetValueExW(hkey
, symbolic_link_valueW
, 0, REG_LINK
, (const BYTE
*)gpu_driver
,
1423 lstrlenW(gpu_driver
) * sizeof(WCHAR
)))
1429 * Following information is Wine specific, it doesn't really exist on Windows. It is used so that we can
1430 * implement EnumDisplayDevices etc by querying registry only. This information is most likely reported by the
1431 * device driver on Windows */
1432 RegCreateKeyExW(HKEY_CURRENT_CONFIG
, adapter_keyW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_WRITE
, NULL
, &hkey
, NULL
);
1434 /* Write GPU instance path so that we can find the GPU instance via adapters quickly. Another way is trying to match
1435 * them via the GUID in Device Parameters/VideoID, but it would require enumerating all GPU instances */
1436 sprintfW(bufferW
, gpu_instance_fmtW
, gpu
->vendor_id
, gpu
->device_id
, gpu
->subsys_id
, gpu
->revision_id
, gpu_index
);
1437 if (RegSetValueExW(hkey
, gpu_idW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (lstrlenW(bufferW
) + 1) * sizeof(WCHAR
)))
1440 /* Write all monitor instance paths under this adapter */
1441 for (i
= 0; i
< monitor_count
; i
++)
1443 sprintfW(key_nameW
, mointor_id_fmtW
, i
);
1444 sprintfW(bufferW
, monitor_instance_fmtW
, video_index
, i
);
1445 if (RegSetValueExW(hkey
, key_nameW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (lstrlenW(bufferW
) + 1) * sizeof(WCHAR
)))
1449 /* Write StateFlags */
1450 if (RegSetValueExW(hkey
, state_flagsW
, 0, REG_DWORD
, (const BYTE
*)&adapter
->state_flags
,
1451 sizeof(adapter
->state_flags
)))
1458 ERR("Failed to initialize adapter\n");
1462 /***********************************************************************
1463 * macdrv_init_monitor
1465 * Initialize an monitor.
1467 * Return FALSE on failure and TRUE on success.
1469 static BOOL
macdrv_init_monitor(HDEVINFO devinfo
, const struct macdrv_monitor
*monitor
, int monitor_index
,
1472 SP_DEVINFO_DATA device_data
= {sizeof(SP_DEVINFO_DATA
)};
1473 WCHAR nameW
[MAX_PATH
];
1474 WCHAR bufferW
[MAX_PATH
];
1479 /* Create GUID_DEVCLASS_MONITOR instance */
1480 sprintfW(bufferW
, monitor_instance_fmtW
, video_index
, monitor_index
);
1481 MultiByteToWideChar(CP_UTF8
, 0, monitor
->name
, -1, nameW
, ARRAY_SIZE(nameW
));
1482 SetupDiCreateDeviceInfoW(devinfo
, bufferW
, &GUID_DEVCLASS_MONITOR
, nameW
, NULL
, 0, &device_data
);
1483 if (!SetupDiRegisterDeviceInfo(devinfo
, &device_data
, 0, NULL
, NULL
, NULL
))
1486 /* Write HardwareID registry property */
1487 if (!SetupDiSetDeviceRegistryPropertyW(devinfo
, &device_data
, SPDRP_HARDWAREID
,
1488 (const BYTE
*)monitor_hardware_idW
, sizeof(monitor_hardware_idW
)))
1491 /* Create driver key */
1492 hkey
= SetupDiCreateDevRegKeyW(devinfo
, &device_data
, DICS_FLAG_GLOBAL
, 0, DIREG_DRV
, NULL
, NULL
);
1496 * Following properties are Wine specific, see comments in macdrv_init_adapter for details */
1498 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_STATEFLAGS
, DEVPROP_TYPE_UINT32
,
1499 (const BYTE
*)&monitor
->state_flags
, sizeof(monitor
->state_flags
), 0))
1502 rect
= rect_from_cgrect(monitor
->rc_monitor
);
1503 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_RCMONITOR
, DEVPROP_TYPE_BINARY
,
1504 (const BYTE
*)&rect
, sizeof(rect
), 0))
1507 rect
= rect_from_cgrect(monitor
->rc_work
);
1508 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_RCWORK
, DEVPROP_TYPE_BINARY
,
1509 (const BYTE
*)&rect
, sizeof(rect
), 0))
1512 sprintfW(bufferW
, adapter_name_fmtW
, video_index
+ 1);
1513 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_ADAPTERNAME
, DEVPROP_TYPE_STRING
,
1514 (const BYTE
*)bufferW
, (lstrlenW(bufferW
) + 1) * sizeof(WCHAR
), 0))
1520 ERR("Failed to initialize monitor\n");
1524 static void prepare_devices(HKEY video_hkey
)
1526 static const BOOL not_present
= FALSE
;
1527 SP_DEVINFO_DATA device_data
= {sizeof(device_data
)};
1531 /* Remove all monitors */
1532 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_MONITOR
, displayW
, NULL
, 0);
1533 while (SetupDiEnumDeviceInfo(devinfo
, i
++, &device_data
))
1535 if (!SetupDiRemoveDevice(devinfo
, &device_data
))
1536 ERR("Failed to remove monitor\n");
1538 SetupDiDestroyDeviceInfoList(devinfo
);
1540 /* Clean up old adapter keys for reinitialization */
1541 RegDeleteTreeW(video_hkey
, NULL
);
1544 * Currently SetupDiGetClassDevsW with DIGCF_PRESENT is unsupported, So we need to clean up not present devices in
1545 * case application uses SetupDiGetClassDevsW to enumerate devices. Wrong devices could exist in registry as a result
1546 * of prefix copying or having devices unplugged. But then we couldn't simply delete GPUs because we need to retain
1547 * the same GUID for the same GPU. */
1549 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY
, pciW
, NULL
, 0);
1550 while (SetupDiEnumDeviceInfo(devinfo
, i
++, &device_data
))
1552 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPKEY_Device_IsPresent
, DEVPROP_TYPE_BOOLEAN
,
1553 (const BYTE
*)¬_present
, sizeof(not_present
), 0))
1554 ERR("Failed to set GPU present property\n");
1556 SetupDiDestroyDeviceInfoList(devinfo
);
1559 static void cleanup_devices(void)
1561 SP_DEVINFO_DATA device_data
= {sizeof(device_data
)};
1567 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY
, pciW
, NULL
, 0);
1568 while (SetupDiEnumDeviceInfo(devinfo
, i
++, &device_data
))
1571 SetupDiGetDevicePropertyW(devinfo
, &device_data
, &DEVPKEY_Device_IsPresent
, &type
, (BYTE
*)&present
,
1572 sizeof(present
), NULL
, 0);
1573 if (!present
&& !SetupDiRemoveDevice(devinfo
, &device_data
))
1574 ERR("Failed to remove GPU\n");
1576 SetupDiDestroyDeviceInfoList(devinfo
);
1579 /***********************************************************************
1580 * macdrv_init_display_devices
1582 * Initialize display device registry data.
1584 void macdrv_init_display_devices(BOOL force
)
1586 static const WCHAR init_mutexW
[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t',0};
1588 struct macdrv_gpu
*gpus
= NULL
;
1589 struct macdrv_adapter
*adapters
= NULL
;
1590 struct macdrv_monitor
*monitors
= NULL
;
1591 INT gpu_count
, adapter_count
, monitor_count
;
1592 INT gpu
, adapter
, monitor
;
1593 HDEVINFO gpu_devinfo
= NULL
, monitor_devinfo
= NULL
;
1594 HKEY video_hkey
= NULL
;
1595 INT video_index
= 0;
1596 DWORD disposition
= 0;
1598 WCHAR driverW
[1024];
1600 mutex
= CreateMutexW(NULL
, FALSE
, init_mutexW
);
1601 WaitForSingleObject(mutex
, INFINITE
);
1603 if (RegCreateKeyExW(HKEY_LOCAL_MACHINE
, video_keyW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &video_hkey
,
1606 ERR("Failed to create video device key\n");
1610 /* Avoid unnecessary reinit */
1611 if (!force
&& disposition
!= REG_CREATED_NEW_KEY
)
1616 prepare_devices(video_hkey
);
1618 gpu_devinfo
= SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY
, NULL
);
1619 monitor_devinfo
= SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_MONITOR
, NULL
);
1621 /* Initialize GPUs */
1622 if (macdrv_get_gpus(&gpus
, &gpu_count
))
1624 TRACE("GPU count: %d\n", gpu_count
);
1626 for (gpu
= 0; gpu
< gpu_count
; gpu
++)
1628 if (!macdrv_init_gpu(gpu_devinfo
, &gpus
[gpu
], gpu
, guidW
, driverW
))
1631 /* Initialize adapters */
1632 if (macdrv_get_adapters(gpus
[gpu
].id
, &adapters
, &adapter_count
))
1634 TRACE("GPU: %#llx %s, adapter count: %d\n", gpus
[gpu
].id
, gpus
[gpu
].name
, adapter_count
);
1636 for (adapter
= 0; adapter
< adapter_count
; adapter
++)
1638 if (macdrv_get_monitors(adapters
[adapter
].id
, &monitors
, &monitor_count
))
1640 TRACE("adapter: %#x, monitor count: %d\n", adapters
[adapter
].id
, monitor_count
);
1642 if (!macdrv_init_adapter(video_hkey
, video_index
, gpu
, adapter
, monitor_count
, &gpus
[gpu
], guidW
, driverW
,
1643 &adapters
[adapter
]))
1646 /* Initialize monitors */
1647 for (monitor
= 0; monitor
< monitor_count
; monitor
++)
1649 TRACE("monitor: %#x %s\n", monitor
, monitors
[monitor
].name
);
1650 if (!macdrv_init_monitor(monitor_devinfo
, &monitors
[monitor
], monitor
, video_index
))
1654 macdrv_free_monitors(monitors
);
1659 macdrv_free_adapters(adapters
);
1665 SetupDiDestroyDeviceInfoList(monitor_devinfo
);
1666 SetupDiDestroyDeviceInfoList(gpu_devinfo
);
1667 RegCloseKey(video_hkey
);
1669 ReleaseMutex(mutex
);
1672 macdrv_free_gpus(gpus
);
1673 macdrv_free_adapters(adapters
);
1674 macdrv_free_monitors(monitors
);