winebus.sys: Add missing keyboard free_device callback.
[wine.git] / dlls / winemac.drv / display.c
blob6706c88341ca4d4dce398a06d2737be1f0b93f57
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 #include "config.h"
24 #include "macdrv.h"
25 #include "winuser.h"
26 #include "winreg.h"
27 #include "ddrawi.h"
28 #include "rpc.h"
29 #include "initguid.h"
30 #include "devguid.h"
31 #include "devpkey.h"
32 #include "setupapi.h"
33 #define WIN32_NO_STATUS
34 #include "winternl.h"
35 #include "wine/unicode.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(display);
40 struct display_mode_descriptor
42 DWORD width;
43 DWORD height;
44 DWORD pixel_width;
45 DWORD pixel_height;
46 DWORD io_flags;
47 double refresh;
48 CFStringRef pixel_encoding;
52 BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW devmode, DWORD flags);
54 DEFINE_DEVPROPKEY(DEVPROPKEY_GPU_LUID, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2);
55 DEFINE_DEVPROPKEY(DEVPROPKEY_MONITOR_GPU_LUID, 0xca085853, 0x16ce, 0x48aa, 0xb1, 0x14, 0xde, 0x9c, 0x72, 0x33, 0x42, 0x23, 1);
56 DEFINE_DEVPROPKEY(DEVPROPKEY_MONITOR_OUTPUT_ID, 0xca085853, 0x16ce, 0x48aa, 0xb1, 0x14, 0xde, 0x9c, 0x72, 0x33, 0x42, 0x23, 2);
58 /* Wine specific monitor properties */
59 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_STATEFLAGS, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 2);
60 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_RCMONITOR, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 3);
61 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_RCWORK, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 4);
62 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_ADAPTERNAME, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 5);
64 static const char initial_mode_key[] = "Initial Display Mode";
65 static const WCHAR pixelencodingW[] = {'P','i','x','e','l','E','n','c','o','d','i','n','g',0};
66 static const WCHAR driver_date_dataW[] = {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0};
67 static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0};
68 static const WCHAR displayW[] = {'D','I','S','P','L','A','Y',0};
69 static const WCHAR pciW[] = {'P','C','I',0};
70 static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',0};
71 static const WCHAR symbolic_link_valueW[]= {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
72 static const WCHAR gpu_idW[] = {'G','P','U','I','D',0};
73 static const WCHAR monitor_id_fmtW[] = {'M','o','n','i','t','o','r','I','D','%','d',0};
74 static const WCHAR adapter_prefixW[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y'};
75 static const WCHAR adapter_name_fmtW[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y','%','d',0};
76 static const WCHAR state_flagsW[] = {'S','t','a','t','e','F','l','a','g','s',0};
77 static const WCHAR guid_fmtW[] = {
78 '{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-','%','0','2','x','%','0','2','x','-',
79 '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0};
80 static const WCHAR gpu_instance_fmtW[] = {
81 'P','C','I','\\',
82 'V','E','N','_','%','0','4','X','&',
83 'D','E','V','_','%','0','4','X','&',
84 'S','U','B','S','Y','S','_','%','0','8','X','&',
85 'R','E','V','_','%','0','2','X','\\',
86 '%','0','8','X',0};
87 static const WCHAR gpu_hardware_id_fmtW[] = {
88 'P','C','I','\\',
89 'V','E','N','_','%','0','4','X','&',
90 'D','E','V','_','%','0','4','X','&',
91 'S','U','B','S','Y','S','_','0','0','0','0','0','0','0','0','&',
92 'R','E','V','_','0','0',0};
93 static const WCHAR video_keyW[] = {
94 'H','A','R','D','W','A','R','E','\\',
95 'D','E','V','I','C','E','M','A','P','\\',
96 'V','I','D','E','O',0};
97 static const WCHAR adapter_key_fmtW[] = {
98 'S','y','s','t','e','m','\\',
99 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
100 'C','o','n','t','r','o','l','\\',
101 'V','i','d','e','o','\\',
102 '%','s','\\',
103 '%','0','4','x',0};
104 static const WCHAR device_video_fmtW[] = {
105 '\\','D','e','v','i','c','e','\\',
106 'V','i','d','e','o','%','d',0};
107 static const WCHAR machine_prefixW[] = {
108 '\\','R','e','g','i','s','t','r','y','\\',
109 'M','a','c','h','i','n','e','\\',0};
110 static const WCHAR nt_classW[] = {
111 '\\','R','e','g','i','s','t','r','y','\\',
112 'M','a','c','h','i','n','e','\\',
113 'S','y','s','t','e','m','\\',
114 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
115 'C','o','n','t','r','o','l','\\',
116 'C','l','a','s','s','\\',0};
117 static const WCHAR monitor_instance_fmtW[] = {
118 'D','I','S','P','L','A','Y','\\',
119 'D','e','f','a','u','l','t','_','M','o','n','i','t','o','r','\\',
120 '%','0','4','X','&','%','0','4','X',0};
121 static const WCHAR monitor_hardware_idW[] = {
122 'M','O','N','I','T','O','R','\\',
123 'D','e','f','a','u','l','t','_','M','o','n','i','t','o','r',0,0};
126 static CFArrayRef modes;
127 static BOOL modes_has_8bpp, modes_has_16bpp;
128 static int default_mode_bpp;
129 static CRITICAL_SECTION modes_section;
130 static CRITICAL_SECTION_DEBUG critsect_debug =
132 0, 0, &modes_section,
133 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
134 0, 0, { (DWORD_PTR)(__FILE__ ": modes_section") }
136 static CRITICAL_SECTION modes_section = { &critsect_debug, -1, 0, 0, 0, 0 };
138 static BOOL inited_original_display_mode;
140 static HANDLE get_display_device_init_mutex(void)
142 static const WCHAR init_mutexW[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t',0};
143 HANDLE mutex = CreateMutexW(NULL, FALSE, init_mutexW);
145 WaitForSingleObject(mutex, INFINITE);
146 return mutex;
149 static void release_display_device_init_mutex(HANDLE mutex)
151 ReleaseMutex(mutex);
152 CloseHandle(mutex);
155 static BOOL get_display_device_reg_key(const WCHAR *device_name, WCHAR *key, unsigned len)
157 WCHAR value_name[MAX_PATH], buffer[MAX_PATH], *end_ptr;
158 DWORD adapter_index, size;
160 /* Device name has to be \\.\DISPLAY%d */
161 if (strncmpiW(device_name, adapter_prefixW, ARRAY_SIZE(adapter_prefixW)))
162 return FALSE;
164 /* Parse \\.\DISPLAY* */
165 adapter_index = strtolW(device_name + ARRAY_SIZE(adapter_prefixW), &end_ptr, 10) - 1;
166 if (*end_ptr)
167 return FALSE;
169 /* Open \Device\Video* in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */
170 sprintfW(value_name, device_video_fmtW, adapter_index);
171 size = sizeof(buffer);
172 if (RegGetValueW(HKEY_LOCAL_MACHINE, video_keyW, value_name, RRF_RT_REG_SZ, NULL, buffer, &size))
173 return FALSE;
175 if (len < lstrlenW(buffer + 18) + 1)
176 return FALSE;
178 /* Skip \Registry\Machine\ prefix */
179 lstrcpyW(key, buffer + 18);
180 TRACE("display device %s registry settings key %s.\n", wine_dbgstr_w(device_name), wine_dbgstr_w(key));
181 return TRUE;
185 static BOOL read_registry_settings(const WCHAR *device_name, DEVMODEW *dm)
187 WCHAR wine_mac_reg_key[MAX_PATH];
188 HANDLE mutex;
189 HKEY hkey;
190 DWORD type, size;
191 BOOL ret = TRUE;
193 dm->dmFields = 0;
195 mutex = get_display_device_init_mutex();
196 if (!get_display_device_reg_key(device_name, wine_mac_reg_key, ARRAY_SIZE(wine_mac_reg_key)))
198 release_display_device_init_mutex(mutex);
199 return FALSE;
202 if (RegOpenKeyExW(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, KEY_READ, &hkey))
204 release_display_device_init_mutex(mutex);
205 return FALSE;
208 #define query_value(name, data) \
209 size = sizeof(DWORD); \
210 if (RegQueryValueExA(hkey, name, 0, &type, (LPBYTE)(data), &size) || \
211 type != REG_DWORD || size != sizeof(DWORD)) \
212 ret = FALSE
214 query_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
215 dm->dmFields |= DM_BITSPERPEL;
216 query_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
217 dm->dmFields |= DM_PELSWIDTH;
218 query_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
219 dm->dmFields |= DM_PELSHEIGHT;
220 query_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
221 dm->dmFields |= DM_DISPLAYFREQUENCY;
222 query_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
223 dm->dmFields |= DM_DISPLAYFLAGS;
224 query_value("DefaultSettings.XPanning", &dm->dmPosition.x);
225 query_value("DefaultSettings.YPanning", &dm->dmPosition.y);
226 dm->dmFields |= DM_POSITION;
227 query_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
228 dm->dmFields |= DM_DISPLAYORIENTATION;
229 query_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
231 #undef query_value
233 RegCloseKey(hkey);
234 release_display_device_init_mutex(mutex);
235 return ret;
239 static BOOL write_registry_settings(const WCHAR *device_name, const DEVMODEW *dm)
241 WCHAR wine_mac_reg_key[MAX_PATH];
242 HANDLE mutex;
243 HKEY hkey;
244 BOOL ret = TRUE;
246 mutex = get_display_device_init_mutex();
247 if (!get_display_device_reg_key(device_name, wine_mac_reg_key, ARRAY_SIZE(wine_mac_reg_key)))
249 release_display_device_init_mutex(mutex);
250 return FALSE;
253 if (RegCreateKeyExW(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, NULL,
254 REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL))
256 release_display_device_init_mutex(mutex);
257 return FALSE;
260 #define set_value(name, data) \
261 if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
262 ret = FALSE
264 set_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
265 set_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
266 set_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
267 set_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
268 set_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
269 set_value("DefaultSettings.XPanning", &dm->dmPosition.x);
270 set_value("DefaultSettings.YPanning", &dm->dmPosition.y);
271 set_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
272 set_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
274 #undef set_value
276 RegCloseKey(hkey);
277 release_display_device_init_mutex(mutex);
278 return ret;
282 static BOOL write_display_settings(HKEY parent_hkey, CGDirectDisplayID displayID)
284 BOOL ret = FALSE;
285 char display_key_name[19];
286 HKEY display_hkey;
287 CGDisplayModeRef display_mode;
288 DWORD val;
289 CFStringRef pixel_encoding;
290 size_t len;
291 WCHAR* buf = NULL;
293 snprintf(display_key_name, sizeof(display_key_name), "Display 0x%08x", CGDisplayUnitNumber(displayID));
294 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
295 if (RegCreateKeyExA(parent_hkey, display_key_name, 0, NULL,
296 REG_OPTION_VOLATILE, KEY_WRITE, NULL, &display_hkey, NULL))
297 return FALSE;
299 display_mode = CGDisplayCopyDisplayMode(displayID);
300 if (!display_mode)
301 goto fail;
303 val = CGDisplayModeGetWidth(display_mode);
304 if (RegSetValueExA(display_hkey, "Width", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
305 goto fail;
306 val = CGDisplayModeGetHeight(display_mode);
307 if (RegSetValueExA(display_hkey, "Height", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
308 goto fail;
309 val = CGDisplayModeGetRefreshRate(display_mode) * 100;
310 if (RegSetValueExA(display_hkey, "RefreshRateTimes100", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
311 goto fail;
312 val = CGDisplayModeGetIOFlags(display_mode);
313 if (RegSetValueExA(display_hkey, "IOFlags", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
314 goto fail;
316 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
317 if (&CGDisplayModeGetPixelWidth != NULL && &CGDisplayModeGetPixelHeight != NULL)
319 val = CGDisplayModeGetPixelWidth(display_mode);
320 if (RegSetValueExA(display_hkey, "PixelWidth", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
321 goto fail;
322 val = CGDisplayModeGetPixelHeight(display_mode);
323 if (RegSetValueExA(display_hkey, "PixelHeight", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
324 goto fail;
326 #endif
328 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
329 len = CFStringGetLength(pixel_encoding);
330 buf = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
331 CFStringGetCharacters(pixel_encoding, CFRangeMake(0, len), (UniChar*)buf);
332 buf[len] = 0;
333 CFRelease(pixel_encoding);
334 if (RegSetValueExW(display_hkey, pixelencodingW, 0, REG_SZ, (const BYTE*)buf, (len + 1) * sizeof(WCHAR)))
335 goto fail;
337 ret = TRUE;
339 fail:
340 HeapFree(GetProcessHeap(), 0, buf);
341 if (display_mode) CGDisplayModeRelease(display_mode);
342 RegCloseKey(display_hkey);
343 if (!ret)
344 RegDeleteKeyA(parent_hkey, display_key_name);
345 return ret;
349 static void init_original_display_mode(void)
351 BOOL success = FALSE;
352 HKEY mac_driver_hkey, parent_hkey;
353 DWORD disposition;
354 struct macdrv_display *displays = NULL;
355 int num_displays, i;
357 if (inited_original_display_mode)
358 return;
360 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver */
361 if (RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Mac Driver", 0, NULL,
362 0, KEY_ALL_ACCESS, NULL, &mac_driver_hkey, NULL))
363 return;
365 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode */
366 if (RegCreateKeyExA(mac_driver_hkey, initial_mode_key, 0, NULL,
367 REG_OPTION_VOLATILE, KEY_WRITE, NULL, &parent_hkey, &disposition))
369 parent_hkey = NULL;
370 goto fail;
373 /* If we didn't create a new key, then it already existed. Something already stored
374 the initial display mode since Wine was started. We don't want to overwrite it. */
375 if (disposition != REG_CREATED_NEW_KEY)
376 goto done;
378 if (macdrv_get_displays(&displays, &num_displays))
379 goto fail;
381 for (i = 0; i < num_displays; i++)
383 if (!write_display_settings(parent_hkey, displays[i].displayID))
384 goto fail;
387 done:
388 success = TRUE;
390 fail:
391 macdrv_free_displays(displays);
392 RegCloseKey(parent_hkey);
393 if (!success && parent_hkey)
394 RegDeleteTreeA(mac_driver_hkey, initial_mode_key);
395 RegCloseKey(mac_driver_hkey);
396 if (success)
397 inited_original_display_mode = TRUE;
401 static BOOL read_dword(HKEY hkey, const char* name, DWORD* val)
403 DWORD type, size = sizeof(*val);
404 if (RegQueryValueExA(hkey, name, 0, &type, (BYTE*)val, &size) || type != REG_DWORD || size != sizeof(*val))
405 return FALSE;
406 return TRUE;
410 static void free_display_mode_descriptor(struct display_mode_descriptor* desc)
412 if (desc)
414 if (desc->pixel_encoding)
415 CFRelease(desc->pixel_encoding);
416 HeapFree(GetProcessHeap(), 0, desc);
421 static struct display_mode_descriptor* create_original_display_mode_descriptor(CGDirectDisplayID displayID)
423 static const char display_key_format[] = "Software\\Wine\\Mac Driver\\Initial Display Mode\\Display 0x%08x";
424 struct display_mode_descriptor* ret = NULL;
425 struct display_mode_descriptor* desc;
426 char display_key[sizeof(display_key_format) + 10];
427 HKEY hkey;
428 DWORD type, size;
429 DWORD refresh100;
430 WCHAR* pixel_encoding = NULL, *end;
432 init_original_display_mode();
434 snprintf(display_key, sizeof(display_key), display_key_format, CGDisplayUnitNumber(displayID));
435 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
436 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, display_key, 0, KEY_READ, &hkey))
437 return NULL;
439 desc = HeapAlloc(GetProcessHeap(), 0, sizeof(*desc));
440 desc->pixel_encoding = NULL;
442 if (!read_dword(hkey, "Width", &desc->width) ||
443 !read_dword(hkey, "Height", &desc->height) ||
444 !read_dword(hkey, "RefreshRateTimes100", &refresh100) ||
445 !read_dword(hkey, "IOFlags", &desc->io_flags))
446 goto done;
447 if (refresh100)
448 desc->refresh = refresh100 / 100.0;
449 else
450 desc->refresh = 60;
452 if (!read_dword(hkey, "PixelWidth", &desc->pixel_width) ||
453 !read_dword(hkey, "PixelHeight", &desc->pixel_height))
455 desc->pixel_width = desc->width;
456 desc->pixel_height = desc->height;
459 size = 0;
460 if (RegQueryValueExW(hkey, pixelencodingW, 0, &type, NULL, &size) || type != REG_SZ)
461 goto done;
462 pixel_encoding = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
463 if (RegQueryValueExW(hkey, pixelencodingW, 0, &type, (BYTE*)pixel_encoding, &size) || type != REG_SZ)
464 goto done;
465 if ((end = memchrW(pixel_encoding, 0, size)))
466 size = end - pixel_encoding;
467 desc->pixel_encoding = CFStringCreateWithCharacters(NULL, (const UniChar*)pixel_encoding, size);
469 ret = desc;
471 done:
472 if (!ret)
473 free_display_mode_descriptor(desc);
474 HeapFree(GetProcessHeap(), 0, pixel_encoding);
475 RegCloseKey(hkey);
476 return ret;
480 static BOOL display_mode_matches_descriptor(CGDisplayModeRef mode, const struct display_mode_descriptor* desc)
482 DWORD mode_io_flags;
483 double mode_refresh;
484 CFStringRef mode_pixel_encoding;
486 if (!desc)
487 return FALSE;
489 if (CGDisplayModeGetWidth(mode) != desc->width ||
490 CGDisplayModeGetHeight(mode) != desc->height)
491 return FALSE;
493 mode_io_flags = CGDisplayModeGetIOFlags(mode);
494 if ((desc->io_flags ^ mode_io_flags) & (kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeStretchedFlag |
495 kDisplayModeInterlacedFlag | kDisplayModeTelevisionFlag))
496 return FALSE;
498 mode_refresh = CGDisplayModeGetRefreshRate(mode);
499 if (!mode_refresh)
500 mode_refresh = 60;
501 if (fabs(desc->refresh - mode_refresh) > 0.1)
502 return FALSE;
504 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
505 if (&CGDisplayModeGetPixelWidth != NULL && &CGDisplayModeGetPixelHeight != NULL)
507 if (CGDisplayModeGetPixelWidth(mode) != desc->pixel_width ||
508 CGDisplayModeGetPixelHeight(mode) != desc->pixel_height)
509 return FALSE;
511 else
512 #endif
513 if (CGDisplayModeGetWidth(mode) != desc->pixel_width ||
514 CGDisplayModeGetHeight(mode) != desc->pixel_height)
515 return FALSE;
517 mode_pixel_encoding = CGDisplayModeCopyPixelEncoding(mode);
518 if (!CFEqual(mode_pixel_encoding, desc->pixel_encoding))
520 CFRelease(mode_pixel_encoding);
521 return FALSE;
523 CFRelease(mode_pixel_encoding);
525 return TRUE;
529 static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode)
531 CFStringRef pixel_encoding;
532 int bits_per_pixel = 0;
534 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
535 if (pixel_encoding)
537 if (CFEqual(pixel_encoding, CFSTR(kIO32BitFloatPixels)))
538 bits_per_pixel = 128;
539 else if (CFEqual(pixel_encoding, CFSTR(kIO16BitFloatPixels)))
540 bits_per_pixel = 64;
541 else if (CFEqual(pixel_encoding, CFSTR(kIO64BitDirectPixels)))
542 bits_per_pixel = 64;
543 else if (CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels)))
544 bits_per_pixel = 30;
545 else if (CFEqual(pixel_encoding, CFSTR(IO32BitDirectPixels)))
546 bits_per_pixel = 32;
547 else if (CFEqual(pixel_encoding, CFSTR(IO16BitDirectPixels)))
548 bits_per_pixel = 16;
549 else if (CFEqual(pixel_encoding, CFSTR(IO8BitIndexedPixels)))
550 bits_per_pixel = 8;
551 else if (CFEqual(pixel_encoding, CFSTR(IO4BitIndexedPixels)))
552 bits_per_pixel = 4;
553 else if (CFEqual(pixel_encoding, CFSTR(IO2BitIndexedPixels)))
554 bits_per_pixel = 2;
555 else if (CFEqual(pixel_encoding, CFSTR(IO1BitIndexedPixels)))
556 bits_per_pixel = 1;
558 CFRelease(pixel_encoding);
561 return bits_per_pixel;
565 static int get_default_bpp(void)
567 int ret;
569 EnterCriticalSection(&modes_section);
571 if (!default_mode_bpp)
573 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
574 if (mode)
576 default_mode_bpp = display_mode_bits_per_pixel(mode);
577 CFRelease(mode);
580 if (!default_mode_bpp)
581 default_mode_bpp = 32;
584 ret = default_mode_bpp;
586 LeaveCriticalSection(&modes_section);
588 TRACE(" -> %d\n", ret);
589 return ret;
593 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
594 static CFDictionaryRef create_mode_dict(CGDisplayModeRef display_mode, BOOL is_original)
596 CFDictionaryRef ret;
597 SInt32 io_flags = CGDisplayModeGetIOFlags(display_mode);
598 SInt64 width = CGDisplayModeGetWidth(display_mode);
599 SInt64 height = CGDisplayModeGetHeight(display_mode);
600 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
601 CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
602 CFNumberRef cf_io_flags, cf_width, cf_height, cf_refresh;
604 if (retina_enabled && is_original)
606 width *= 2;
607 height *= 2;
610 io_flags &= kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeInterlacedFlag |
611 kDisplayModeStretchedFlag | kDisplayModeTelevisionFlag;
612 cf_io_flags = CFNumberCreate(NULL, kCFNumberSInt32Type, &io_flags);
613 cf_width = CFNumberCreate(NULL, kCFNumberSInt64Type, &width);
614 cf_height = CFNumberCreate(NULL, kCFNumberSInt64Type, &height);
615 cf_refresh = CFNumberCreate(NULL, kCFNumberDoubleType, &refresh_rate);
618 static const CFStringRef keys[] = {
619 CFSTR("io_flags"),
620 CFSTR("width"),
621 CFSTR("height"),
622 CFSTR("pixel_encoding"),
623 CFSTR("refresh_rate"),
625 const void* values[ARRAY_SIZE(keys)] = {
626 cf_io_flags,
627 cf_width,
628 cf_height,
629 pixel_encoding,
630 cf_refresh,
633 ret = CFDictionaryCreate(NULL, (const void**)keys, (const void**)values, ARRAY_SIZE(keys),
634 &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
637 CFRelease(pixel_encoding);
638 CFRelease(cf_io_flags);
639 CFRelease(cf_width);
640 CFRelease(cf_height);
641 CFRelease(cf_refresh);
643 return ret;
645 #endif
648 /***********************************************************************
649 * copy_display_modes
651 * Wrapper around CGDisplayCopyAllDisplayModes() to include additional
652 * modes on Retina-capable systems, but filter those which would confuse
653 * Windows apps (basically duplicates at different DPIs).
655 * For example, some Retina Macs support a 1920x1200 mode, but it's not
656 * returned from CGDisplayCopyAllDisplayModes() without special options.
657 * This is especially bad if that's the user's default mode, since then
658 * no "available" mode matches the initial settings.
660 static CFArrayRef copy_display_modes(CGDirectDisplayID display)
662 CFArrayRef modes = NULL;
664 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
665 if (&CGDisplayModeGetPixelWidth != NULL && &CGDisplayModeGetPixelHeight != NULL)
667 CFDictionaryRef options;
668 struct display_mode_descriptor* desc;
669 CFMutableDictionaryRef modes_by_size;
670 CFIndex i, count;
671 CGDisplayModeRef* mode_array;
673 options = CFDictionaryCreate(NULL, (const void**)&kCGDisplayShowDuplicateLowResolutionModes,
674 (const void**)&kCFBooleanTrue, 1, &kCFTypeDictionaryKeyCallBacks,
675 &kCFTypeDictionaryValueCallBacks);
677 modes = CGDisplayCopyAllDisplayModes(display, options);
678 if (options)
679 CFRelease(options);
680 if (!modes)
681 return NULL;
683 desc = create_original_display_mode_descriptor(display);
685 modes_by_size = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
686 count = CFArrayGetCount(modes);
687 for (i = 0; i < count; i++)
689 BOOL better = TRUE;
690 CGDisplayModeRef new_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
691 BOOL new_is_original = display_mode_matches_descriptor(new_mode, desc);
692 CFDictionaryRef key = create_mode_dict(new_mode, new_is_original);
694 /* If a given mode is the user's default, then always list it in preference to any similar
695 modes that may exist. */
696 if (new_is_original)
697 better = TRUE;
698 else
700 CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(new_mode);
701 CGDisplayModeRef old_mode;
703 if (pixel_encoding)
705 BOOL bpp30 = CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels));
706 CFRelease(pixel_encoding);
707 if (bpp30)
709 /* This is an odd pixel encoding. It seems it's only returned
710 when using kCGDisplayShowDuplicateLowResolutionModes. It's
711 32bpp in terms of the actual raster layout, but it's 10
712 bits per component. I think that no Windows program is
713 likely to need it and they will probably be confused by it.
714 Skip it. */
715 CFRelease(key);
716 continue;
720 old_mode = (CGDisplayModeRef)CFDictionaryGetValue(modes_by_size, key);
721 if (old_mode)
723 BOOL old_is_original = display_mode_matches_descriptor(old_mode, desc);
725 if (old_is_original)
726 better = FALSE;
727 else
729 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
730 is scaled. */
731 size_t width_points = CGDisplayModeGetWidth(new_mode);
732 size_t height_points = CGDisplayModeGetHeight(new_mode);
733 size_t new_width_pixels = CGDisplayModeGetPixelWidth(new_mode);
734 size_t new_height_pixels = CGDisplayModeGetPixelHeight(new_mode);
735 size_t old_width_pixels = CGDisplayModeGetPixelWidth(old_mode);
736 size_t old_height_pixels = CGDisplayModeGetPixelHeight(old_mode);
737 BOOL new_size_same = (new_width_pixels == width_points && new_height_pixels == height_points);
738 BOOL old_size_same = (old_width_pixels == width_points && old_height_pixels == height_points);
740 if (new_size_same && !old_size_same)
741 better = TRUE;
742 else if (!new_size_same && old_size_same)
743 better = FALSE;
744 else
746 /* Otherwise, prefer the mode with the smaller pixel size. */
747 if (old_width_pixels < new_width_pixels || old_height_pixels < new_height_pixels)
748 better = FALSE;
754 if (better)
755 CFDictionarySetValue(modes_by_size, key, new_mode);
757 CFRelease(key);
760 free_display_mode_descriptor(desc);
761 CFRelease(modes);
763 count = CFDictionaryGetCount(modes_by_size);
764 mode_array = HeapAlloc(GetProcessHeap(), 0, count * sizeof(mode_array[0]));
765 CFDictionaryGetKeysAndValues(modes_by_size, NULL, (const void **)mode_array);
766 modes = CFArrayCreate(NULL, (const void **)mode_array, count, &kCFTypeArrayCallBacks);
767 HeapFree(GetProcessHeap(), 0, mode_array);
768 CFRelease(modes_by_size);
770 else
771 #endif
772 modes = CGDisplayCopyAllDisplayModes(display, NULL);
774 return modes;
778 void check_retina_status(void)
780 if (retina_enabled)
782 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(kCGDirectMainDisplay);
783 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
784 BOOL new_value = display_mode_matches_descriptor(mode, desc);
786 CGDisplayModeRelease(mode);
787 free_display_mode_descriptor(desc);
789 if (new_value != retina_on)
790 macdrv_set_cocoa_retina_mode(new_value);
794 static BOOL get_primary_adapter(WCHAR *name)
796 DISPLAY_DEVICEW dd;
797 DWORD i;
799 dd.cb = sizeof(dd);
800 for (i = 0; EnumDisplayDevicesW(NULL, i, &dd, 0); ++i)
802 if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
804 lstrcpyW(name, dd.DeviceName);
805 return TRUE;
809 return FALSE;
812 static BOOL is_detached_mode(const DEVMODEW *mode)
814 return mode->dmFields & DM_POSITION &&
815 mode->dmFields & DM_PELSWIDTH &&
816 mode->dmFields & DM_PELSHEIGHT &&
817 mode->dmPelsWidth == 0 &&
818 mode->dmPelsHeight == 0;
821 /***********************************************************************
822 * ChangeDisplaySettingsEx (MACDRV.@)
825 LONG CDECL macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
826 HWND hwnd, DWORD flags, LPVOID lpvoid)
828 WCHAR primary_adapter[CCHDEVICENAME];
829 LONG ret = DISP_CHANGE_BADMODE;
830 DEVMODEW default_mode;
831 int bpp;
832 struct macdrv_display *displays;
833 int num_displays;
834 CFArrayRef display_modes;
835 struct display_mode_descriptor* desc;
836 CFIndex count, i, safe, best;
837 CGDisplayModeRef best_display_mode;
838 uint32_t best_io_flags;
839 BOOL best_is_original;
841 TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid);
843 init_original_display_mode();
845 if (!get_primary_adapter(primary_adapter))
846 return DISP_CHANGE_FAILED;
848 if (!devname && !devmode)
850 memset(&default_mode, 0, sizeof(default_mode));
851 default_mode.dmSize = sizeof(default_mode);
852 if (!EnumDisplaySettingsExW(primary_adapter, ENUM_REGISTRY_SETTINGS, &default_mode, 0))
854 ERR("Default mode not found for %s!\n", wine_dbgstr_w(primary_adapter));
855 return DISP_CHANGE_BADMODE;
858 devname = primary_adapter;
859 devmode = &default_mode;
862 if (is_detached_mode(devmode))
864 FIXME("Detaching adapters is currently unsupported.\n");
865 return DISP_CHANGE_SUCCESSFUL;
868 if (macdrv_get_displays(&displays, &num_displays))
869 return DISP_CHANGE_FAILED;
871 display_modes = copy_display_modes(displays[0].displayID);
872 if (!display_modes)
874 macdrv_free_displays(displays);
875 return DISP_CHANGE_FAILED;
878 bpp = get_default_bpp();
879 if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel != bpp)
880 TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, devmode->dmBitsPerPel);
882 TRACE("looking for %dx%dx%dbpp @%d Hz",
883 (devmode->dmFields & DM_PELSWIDTH ? devmode->dmPelsWidth : 0),
884 (devmode->dmFields & DM_PELSHEIGHT ? devmode->dmPelsHeight : 0),
885 bpp,
886 (devmode->dmFields & DM_DISPLAYFREQUENCY ? devmode->dmDisplayFrequency : 0));
887 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
888 TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un");
889 if (devmode->dmFields & DM_DISPLAYFLAGS)
890 TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
891 TRACE("\n");
893 desc = create_original_display_mode_descriptor(displays[0].displayID);
895 safe = -1;
896 best_display_mode = NULL;
897 count = CFArrayGetCount(display_modes);
898 for (i = 0; i < count; i++)
900 CGDisplayModeRef display_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(display_modes, i);
901 BOOL is_original = display_mode_matches_descriptor(display_mode, desc);
902 uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
903 int mode_bpp = display_mode_bits_per_pixel(display_mode);
904 size_t width = CGDisplayModeGetWidth(display_mode);
905 size_t height = CGDisplayModeGetHeight(display_mode);
907 if (is_original && retina_enabled)
909 width *= 2;
910 height *= 2;
913 if (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag))
914 continue;
916 safe++;
918 if (bpp != mode_bpp)
919 continue;
921 if (devmode->dmFields & DM_PELSWIDTH)
923 if (devmode->dmPelsWidth != width)
924 continue;
926 if (devmode->dmFields & DM_PELSHEIGHT)
928 if (devmode->dmPelsHeight != height)
929 continue;
931 if ((devmode->dmFields & DM_DISPLAYFREQUENCY) &&
932 devmode->dmDisplayFrequency != 0 &&
933 devmode->dmDisplayFrequency != 1)
935 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
936 if (!refresh_rate)
937 refresh_rate = 60;
938 if (devmode->dmDisplayFrequency != (DWORD)refresh_rate)
939 continue;
941 if (devmode->dmFields & DM_DISPLAYFLAGS)
943 if (!(devmode->dmDisplayFlags & DM_INTERLACED) != !(io_flags & kDisplayModeInterlacedFlag))
944 continue;
946 else if (best_display_mode)
948 if (io_flags & kDisplayModeInterlacedFlag && !(best_io_flags & kDisplayModeInterlacedFlag))
949 continue;
950 else if (!(io_flags & kDisplayModeInterlacedFlag) && best_io_flags & kDisplayModeInterlacedFlag)
951 goto better;
953 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
955 if (!(devmode->dmDisplayFixedOutput == DMDFO_STRETCH) != !(io_flags & kDisplayModeStretchedFlag))
956 continue;
958 else if (best_display_mode)
960 if (io_flags & kDisplayModeStretchedFlag && !(best_io_flags & kDisplayModeStretchedFlag))
961 continue;
962 else if (!(io_flags & kDisplayModeStretchedFlag) && best_io_flags & kDisplayModeStretchedFlag)
963 goto better;
966 if (best_display_mode)
967 continue;
969 better:
970 best_display_mode = display_mode;
971 best = safe;
972 best_io_flags = io_flags;
973 best_is_original = is_original;
976 if (best_display_mode)
978 /* we have a valid mode */
979 TRACE("Requested display settings match mode %ld\n", best);
981 if ((flags & CDS_UPDATEREGISTRY) && !write_registry_settings(devname, devmode))
983 WARN("Failed to update registry\n");
984 ret = DISP_CHANGE_NOTUPDATED;
986 else if (flags & (CDS_TEST | CDS_NORESET))
987 ret = DISP_CHANGE_SUCCESSFUL;
988 else if (lstrcmpiW(primary_adapter, devname))
990 FIXME("Changing non-primary adapter settings is currently unsupported.\n");
991 ret = DISP_CHANGE_SUCCESSFUL;
993 else if (macdrv_set_display_mode(&displays[0], best_display_mode))
995 int mode_bpp = display_mode_bits_per_pixel(best_display_mode);
996 size_t width = CGDisplayModeGetWidth(best_display_mode);
997 size_t height = CGDisplayModeGetHeight(best_display_mode);
999 macdrv_init_display_devices(TRUE);
1001 if (best_is_original && retina_enabled)
1003 width *= 2;
1004 height *= 2;
1007 SendMessageW(GetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
1008 MAKELPARAM(width, height));
1009 ret = DISP_CHANGE_SUCCESSFUL;
1011 else
1013 WARN("Failed to set display mode\n");
1014 ret = DISP_CHANGE_FAILED;
1017 else
1019 /* no valid modes found */
1020 ERR("No matching mode found %ux%ux%d @%u!\n", devmode->dmPelsWidth, devmode->dmPelsHeight,
1021 bpp, devmode->dmDisplayFrequency);
1024 free_display_mode_descriptor(desc);
1025 CFRelease(display_modes);
1026 macdrv_free_displays(displays);
1028 return ret;
1031 /***********************************************************************
1032 * EnumDisplaySettingsEx (MACDRV.@)
1035 BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode,
1036 LPDEVMODEW devmode, DWORD flags)
1038 static const WCHAR dev_name[CCHDEVICENAME] =
1039 { 'W','i','n','e',' ','M','a','c',' ','d','r','i','v','e','r',0 };
1040 struct macdrv_display *displays = NULL;
1041 int num_displays;
1042 CGDisplayModeRef display_mode;
1043 int display_mode_bpp;
1044 BOOL synthesized = FALSE;
1045 double rotation;
1046 uint32_t io_flags;
1048 TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname), mode, devmode, devmode->dmSize, flags);
1050 init_original_display_mode();
1052 memcpy(devmode->dmDeviceName, dev_name, sizeof(dev_name));
1053 devmode->dmSpecVersion = DM_SPECVERSION;
1054 devmode->dmDriverVersion = DM_SPECVERSION;
1055 devmode->dmSize = FIELD_OFFSET(DEVMODEW, dmICMMethod);
1056 devmode->dmDriverExtra = 0;
1057 memset(&devmode->dmFields, 0, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields));
1059 if (mode == ENUM_REGISTRY_SETTINGS)
1061 TRACE("mode %d (registry) -- getting default mode\n", mode);
1062 return read_registry_settings(devname, devmode);
1065 if (macdrv_get_displays(&displays, &num_displays))
1066 goto failed;
1068 if (mode == ENUM_CURRENT_SETTINGS)
1070 TRACE("mode %d (current) -- getting current mode\n", mode);
1071 display_mode = CGDisplayCopyDisplayMode(displays[0].displayID);
1072 display_mode_bpp = display_mode_bits_per_pixel(display_mode);
1074 else
1076 DWORD count, i;
1078 EnterCriticalSection(&modes_section);
1080 if (mode == 0 || !modes)
1082 if (modes) CFRelease(modes);
1083 modes = copy_display_modes(displays[0].displayID);
1084 modes_has_8bpp = modes_has_16bpp = FALSE;
1086 if (modes)
1088 count = CFArrayGetCount(modes);
1089 for (i = 0; i < count && !(modes_has_8bpp && modes_has_16bpp); i++)
1091 CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
1092 int bpp = display_mode_bits_per_pixel(mode);
1093 if (bpp == 8)
1094 modes_has_8bpp = TRUE;
1095 else if (bpp == 16)
1096 modes_has_16bpp = TRUE;
1101 display_mode = NULL;
1102 if (modes)
1104 int default_bpp = get_default_bpp();
1105 DWORD seen_modes = 0;
1107 count = CFArrayGetCount(modes);
1108 for (i = 0; i < count; i++)
1110 CGDisplayModeRef candidate = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
1112 io_flags = CGDisplayModeGetIOFlags(candidate);
1113 if (!(flags & EDS_RAWMODE) &&
1114 (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag)))
1115 continue;
1117 seen_modes++;
1118 if (seen_modes > mode)
1120 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1121 display_mode_bpp = display_mode_bits_per_pixel(display_mode);
1122 break;
1125 /* We only synthesize modes from those having the default bpp. */
1126 if (display_mode_bits_per_pixel(candidate) != default_bpp)
1127 continue;
1129 if (!modes_has_8bpp)
1131 seen_modes++;
1132 if (seen_modes > mode)
1134 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1135 display_mode_bpp = 8;
1136 synthesized = TRUE;
1137 break;
1141 if (!modes_has_16bpp)
1143 seen_modes++;
1144 if (seen_modes > mode)
1146 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1147 display_mode_bpp = 16;
1148 synthesized = TRUE;
1149 break;
1155 LeaveCriticalSection(&modes_section);
1158 if (!display_mode)
1159 goto failed;
1161 /* We currently only report modes for the primary display, so it's at (0, 0). */
1162 devmode->dmPosition.x = 0;
1163 devmode->dmPosition.y = 0;
1164 devmode->dmFields |= DM_POSITION;
1166 rotation = CGDisplayRotation(displays[0].displayID);
1167 devmode->dmDisplayOrientation = ((int)((rotation / 90) + 0.5)) % 4;
1168 devmode->dmFields |= DM_DISPLAYORIENTATION;
1170 io_flags = CGDisplayModeGetIOFlags(display_mode);
1171 if (io_flags & kDisplayModeStretchedFlag)
1172 devmode->dmDisplayFixedOutput = DMDFO_STRETCH;
1173 else
1174 devmode->dmDisplayFixedOutput = DMDFO_CENTER;
1175 devmode->dmFields |= DM_DISPLAYFIXEDOUTPUT;
1177 devmode->dmBitsPerPel = display_mode_bpp;
1178 if (devmode->dmBitsPerPel)
1179 devmode->dmFields |= DM_BITSPERPEL;
1181 devmode->dmPelsWidth = CGDisplayModeGetWidth(display_mode);
1182 devmode->dmPelsHeight = CGDisplayModeGetHeight(display_mode);
1183 if (retina_enabled)
1185 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(displays[0].displayID);
1186 if (display_mode_matches_descriptor(display_mode, desc))
1188 devmode->dmPelsWidth *= 2;
1189 devmode->dmPelsHeight *= 2;
1191 free_display_mode_descriptor(desc);
1193 devmode->dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
1195 devmode->dmDisplayFlags = 0;
1196 if (io_flags & kDisplayModeInterlacedFlag)
1197 devmode->dmDisplayFlags |= DM_INTERLACED;
1198 devmode->dmFields |= DM_DISPLAYFLAGS;
1200 devmode->dmDisplayFrequency = CGDisplayModeGetRefreshRate(display_mode);
1201 if (!devmode->dmDisplayFrequency)
1202 devmode->dmDisplayFrequency = 60;
1203 devmode->dmFields |= DM_DISPLAYFREQUENCY;
1205 CFRelease(display_mode);
1206 macdrv_free_displays(displays);
1208 TRACE("mode %d -- %dx%dx%dbpp @%d Hz", mode,
1209 devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel,
1210 devmode->dmDisplayFrequency);
1211 if (devmode->dmDisplayOrientation)
1212 TRACE(" rotated %u degrees", devmode->dmDisplayOrientation * 90);
1213 if (devmode->dmDisplayFixedOutput == DMDFO_STRETCH)
1214 TRACE(" stretched");
1215 if (devmode->dmDisplayFlags & DM_INTERLACED)
1216 TRACE(" interlaced");
1217 if (synthesized)
1218 TRACE(" (synthesized)");
1219 TRACE("\n");
1221 return TRUE;
1223 failed:
1224 TRACE("mode %d -- not present\n", mode);
1225 if (displays) macdrv_free_displays(displays);
1226 SetLastError(ERROR_NO_MORE_FILES);
1227 return FALSE;
1231 /***********************************************************************
1232 * GetDeviceGammaRamp (MACDRV.@)
1234 BOOL CDECL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1236 BOOL ret = FALSE;
1237 DDGAMMARAMP *r = ramp;
1238 struct macdrv_display *displays;
1239 int num_displays;
1240 uint32_t mac_entries;
1241 int win_entries = ARRAY_SIZE(r->red);
1242 CGGammaValue *red, *green, *blue;
1243 CGError err;
1244 int win_entry;
1246 TRACE("dev %p ramp %p\n", dev, ramp);
1248 if (macdrv_get_displays(&displays, &num_displays))
1250 WARN("failed to get Mac displays\n");
1251 return FALSE;
1254 mac_entries = CGDisplayGammaTableCapacity(displays[0].displayID);
1255 red = HeapAlloc(GetProcessHeap(), 0, mac_entries * sizeof(red[0]) * 3);
1256 if (!red)
1257 goto done;
1258 green = red + mac_entries;
1259 blue = green + mac_entries;
1261 err = CGGetDisplayTransferByTable(displays[0].displayID, mac_entries, red, green,
1262 blue, &mac_entries);
1263 if (err != kCGErrorSuccess)
1265 WARN("failed to get Mac gamma table: %d\n", err);
1266 goto done;
1269 if (mac_entries == win_entries)
1271 for (win_entry = 0; win_entry < win_entries; win_entry++)
1273 r->red[win_entry] = red[win_entry] * 65535 + 0.5;
1274 r->green[win_entry] = green[win_entry] * 65535 + 0.5;
1275 r->blue[win_entry] = blue[win_entry] * 65535 + 0.5;
1278 else
1280 for (win_entry = 0; win_entry < win_entries; win_entry++)
1282 double mac_pos = win_entry * (mac_entries - 1) / (double)(win_entries - 1);
1283 int mac_entry = mac_pos;
1284 double red_value, green_value, blue_value;
1286 if (mac_entry == mac_entries - 1)
1288 red_value = red[mac_entry];
1289 green_value = green[mac_entry];
1290 blue_value = blue[mac_entry];
1292 else
1294 double distance = mac_pos - mac_entry;
1296 red_value = red[mac_entry] * (1 - distance) + red[mac_entry + 1] * distance;
1297 green_value = green[mac_entry] * (1 - distance) + green[mac_entry + 1] * distance;
1298 blue_value = blue[mac_entry] * (1 - distance) + blue[mac_entry + 1] * distance;
1301 r->red[win_entry] = red_value * 65535 + 0.5;
1302 r->green[win_entry] = green_value * 65535 + 0.5;
1303 r->blue[win_entry] = blue_value * 65535 + 0.5;
1307 ret = TRUE;
1309 done:
1310 HeapFree(GetProcessHeap(), 0, red);
1311 macdrv_free_displays(displays);
1312 return ret;
1315 /***********************************************************************
1316 * SetDeviceGammaRamp (MACDRV.@)
1318 BOOL CDECL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1320 DDGAMMARAMP *r = ramp;
1321 struct macdrv_display *displays;
1322 int num_displays;
1323 int win_entries = ARRAY_SIZE(r->red);
1324 CGGammaValue *red, *green, *blue;
1325 int i;
1326 CGError err = kCGErrorFailure;
1328 TRACE("dev %p ramp %p\n", dev, ramp);
1330 if (!allow_set_gamma)
1332 TRACE("disallowed by registry setting\n");
1333 return FALSE;
1336 if (macdrv_get_displays(&displays, &num_displays))
1338 WARN("failed to get Mac displays\n");
1339 return FALSE;
1342 red = HeapAlloc(GetProcessHeap(), 0, win_entries * sizeof(red[0]) * 3);
1343 if (!red)
1344 goto done;
1345 green = red + win_entries;
1346 blue = green + win_entries;
1348 for (i = 0; i < win_entries; i++)
1350 red[i] = r->red[i] / 65535.0;
1351 green[i] = r->green[i] / 65535.0;
1352 blue[i] = r->blue[i] / 65535.0;
1355 err = CGSetDisplayTransferByTable(displays[0].displayID, win_entries, red, green, blue);
1356 if (err != kCGErrorSuccess)
1357 WARN("failed to set display gamma table: %d\n", err);
1359 done:
1360 HeapFree(GetProcessHeap(), 0, red);
1361 macdrv_free_displays(displays);
1362 return (err == kCGErrorSuccess);
1365 /***********************************************************************
1366 * init_registry_display_settings
1368 * Initialize registry display settings when new display devices are added.
1370 static void init_registry_display_settings(void)
1372 DEVMODEW dm = {.dmSize = sizeof(dm)};
1373 DISPLAY_DEVICEW dd = {sizeof(dd)};
1374 DWORD i = 0;
1375 LONG ret;
1377 while (EnumDisplayDevicesW(NULL, i++, &dd, 0))
1379 /* Skip if the device already has registry display settings */
1380 if (EnumDisplaySettingsExW(dd.DeviceName, ENUM_REGISTRY_SETTINGS, &dm, 0))
1381 continue;
1383 if (!EnumDisplaySettingsExW(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm, 0))
1385 ERR("Failed to query current display settings for %s.\n", wine_dbgstr_w(dd.DeviceName));
1386 continue;
1389 TRACE("Device %s current display mode %ux%u %ubits %uHz at %d,%d.\n",
1390 wine_dbgstr_w(dd.DeviceName), dm.dmPelsWidth, dm.dmPelsHeight, dm.dmBitsPerPel,
1391 dm.dmDisplayFrequency, dm.dmPosition.x, dm.dmPosition.y);
1393 ret = ChangeDisplaySettingsExW(dd.DeviceName, &dm, NULL,
1394 CDS_GLOBAL | CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
1395 if (ret != DISP_CHANGE_SUCCESSFUL)
1396 ERR("Failed to save registry display settings for %s, returned %d.\n",
1397 wine_dbgstr_w(dd.DeviceName), ret);
1401 /***********************************************************************
1402 * macdrv_displays_changed
1404 * Handler for DISPLAYS_CHANGED events.
1406 void macdrv_displays_changed(const macdrv_event *event)
1408 HWND hwnd = GetDesktopWindow();
1410 /* A system display change will get delivered to all GUI-attached threads,
1411 so the desktop-window-owning thread will get it and all others should
1412 ignore it. A synthesized display change event due to activation
1413 will only get delivered to the activated process. So, it needs to
1414 process it (by sending it to the desktop window). */
1415 if (event->displays_changed.activating ||
1416 GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId())
1418 CGDirectDisplayID mainDisplay = CGMainDisplayID();
1419 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(mainDisplay);
1420 size_t width = CGDisplayModeGetWidth(mode);
1421 size_t height = CGDisplayModeGetHeight(mode);
1422 int mode_bpp = display_mode_bits_per_pixel(mode);
1423 struct display_mode_descriptor* desc = create_original_display_mode_descriptor(mainDisplay);
1424 BOOL is_original = display_mode_matches_descriptor(mode, desc);
1426 free_display_mode_descriptor(desc);
1427 CGDisplayModeRelease(mode);
1429 macdrv_init_display_devices(TRUE);
1430 init_registry_display_settings();
1432 if (is_original && retina_enabled)
1434 width *= 2;
1435 height *= 2;
1438 SendMessageW(hwnd, WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
1439 MAKELPARAM(width, height));
1443 /***********************************************************************
1444 * macdrv_init_gpu
1446 * Initialize a GPU instance.
1447 * Return its GUID string in guid_string, driver value in driver parameter and LUID in gpu_luid.
1449 * Return FALSE on failure and TRUE on success.
1451 static BOOL macdrv_init_gpu(HDEVINFO devinfo, const struct macdrv_gpu *gpu, int gpu_index, WCHAR *guid_string,
1452 WCHAR *driver, LUID *gpu_luid)
1454 static const BOOL present = TRUE;
1455 SP_DEVINFO_DATA device_data = {sizeof(device_data)};
1456 WCHAR instanceW[MAX_PATH];
1457 DEVPROPTYPE property_type;
1458 WCHAR nameW[MAX_PATH];
1459 WCHAR bufferW[1024];
1460 FILETIME filetime;
1461 HKEY hkey = NULL;
1462 GUID guid;
1463 LUID luid;
1464 INT written;
1465 DWORD size;
1466 BOOL ret = FALSE;
1468 sprintfW(instanceW, gpu_instance_fmtW, gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index);
1469 MultiByteToWideChar(CP_UTF8, 0, gpu->name, -1, nameW, ARRAY_SIZE(nameW));
1470 if (!SetupDiOpenDeviceInfoW(devinfo, instanceW, NULL, 0, &device_data))
1472 SetupDiCreateDeviceInfoW(devinfo, instanceW, &GUID_DEVCLASS_DISPLAY, nameW, NULL, 0, &device_data);
1473 if (!SetupDiRegisterDeviceInfo(devinfo, &device_data, 0, NULL, NULL, NULL))
1474 goto done;
1477 /* Write HardwareID registry property, REG_MULTI_SZ */
1478 written = sprintfW(bufferW, gpu_hardware_id_fmtW, gpu->vendor_id, gpu->device_id);
1479 bufferW[written + 1] = 0;
1480 if (!SetupDiSetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_HARDWAREID, (const BYTE *)bufferW,
1481 (written + 2) * sizeof(WCHAR)))
1482 goto done;
1484 /* Write DEVPKEY_Device_IsPresent property */
1485 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, DEVPROP_TYPE_BOOLEAN,
1486 (const BYTE *)&present, sizeof(present), 0))
1487 goto done;
1489 /* Write DEVPROPKEY_GPU_LUID property */
1490 if (!SetupDiGetDevicePropertyW(devinfo, &device_data, &DEVPROPKEY_GPU_LUID, &property_type,
1491 (BYTE *)&luid, sizeof(luid), NULL, 0))
1493 if (!AllocateLocallyUniqueId(&luid))
1494 goto done;
1496 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPROPKEY_GPU_LUID,
1497 DEVPROP_TYPE_UINT64, (const BYTE *)&luid, sizeof(luid), 0))
1498 goto done;
1500 *gpu_luid = luid;
1501 TRACE("GPU id:0x%s name:%s LUID:%08x:%08x.\n", wine_dbgstr_longlong(gpu->id), gpu->name,
1502 luid.HighPart, luid.LowPart);
1504 /* Open driver key.
1505 * This is where HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} links to */
1506 hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
1508 /* Write DriverDesc value */
1509 if (RegSetValueExW(hkey, driver_descW, 0, REG_SZ, (const BYTE *)nameW, (lstrlenW(nameW) + 1) * sizeof(WCHAR)))
1510 goto done;
1511 /* Write DriverDateData value, using current time as driver date, needed by Evoland */
1512 GetSystemTimeAsFileTime(&filetime);
1513 if (RegSetValueExW(hkey, driver_date_dataW, 0, REG_BINARY, (BYTE *)&filetime, sizeof(filetime)))
1514 goto done;
1515 RegCloseKey(hkey);
1517 /* Retrieve driver value for adapters */
1518 if (!SetupDiGetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, sizeof(bufferW),
1519 NULL))
1520 goto done;
1521 lstrcpyW(driver, nt_classW);
1522 lstrcatW(driver, bufferW);
1524 /* Write GUID in VideoID in .../instance/Device Parameters, reuse the GUID if it's existent */
1525 hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, NULL, NULL);
1527 size = sizeof(bufferW);
1528 if (RegQueryValueExW(hkey, video_idW, 0, NULL, (BYTE *)bufferW, &size))
1530 UuidCreate(&guid);
1531 sprintfW(bufferW, guid_fmtW, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
1532 guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
1533 if (RegSetValueExW(hkey, video_idW, 0, REG_SZ, (const BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR)))
1534 goto done;
1536 lstrcpyW(guid_string, bufferW);
1538 ret = TRUE;
1539 done:
1540 RegCloseKey(hkey);
1541 if (!ret)
1542 ERR("Failed to initialize GPU\n");
1543 return ret;
1546 /***********************************************************************
1547 * macdrv_init_adapter
1549 * Initialize an adapter.
1551 * Return FALSE on failure and TRUE on success.
1553 static BOOL macdrv_init_adapter(HKEY video_hkey, int video_index, int gpu_index, int adapter_index, int monitor_count,
1554 const struct macdrv_gpu *gpu, const WCHAR *guid_string, const WCHAR *gpu_driver,
1555 const struct macdrv_adapter *adapter)
1557 WCHAR adapter_keyW[MAX_PATH];
1558 WCHAR key_nameW[MAX_PATH];
1559 WCHAR bufferW[1024];
1560 HKEY hkey = NULL;
1561 BOOL ret = FALSE;
1562 LSTATUS ls;
1563 INT i;
1565 sprintfW(key_nameW, device_video_fmtW, video_index);
1566 lstrcpyW(bufferW, machine_prefixW);
1567 sprintfW(adapter_keyW, adapter_key_fmtW, guid_string, adapter_index);
1568 lstrcatW(bufferW, adapter_keyW);
1570 /* Write value of \Device\Video? (adapter key) in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */
1571 if (RegSetValueExW(video_hkey, key_nameW, 0, REG_SZ, (const BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR)))
1572 goto done;
1574 /* Create HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} link to GPU driver */
1575 ls = RegCreateKeyExW(HKEY_LOCAL_MACHINE, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
1576 KEY_ALL_ACCESS, NULL, &hkey, NULL);
1577 if (ls == ERROR_ALREADY_EXISTS)
1578 RegCreateKeyExW(HKEY_LOCAL_MACHINE, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK,
1579 KEY_ALL_ACCESS, NULL, &hkey, NULL);
1580 if (RegSetValueExW(hkey, symbolic_link_valueW, 0, REG_LINK, (const BYTE *)gpu_driver,
1581 lstrlenW(gpu_driver) * sizeof(WCHAR)))
1582 goto done;
1583 RegCloseKey(hkey);
1584 hkey = NULL;
1586 /* FIXME:
1587 * Following information is Wine specific, it doesn't really exist on Windows. It is used so that we can
1588 * implement EnumDisplayDevices etc by querying registry only. This information is most likely reported by the
1589 * device driver on Windows */
1590 RegCreateKeyExW(HKEY_CURRENT_CONFIG, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL);
1592 /* Write GPU instance path so that we can find the GPU instance via adapters quickly. Another way is trying to match
1593 * them via the GUID in Device Parameters/VideoID, but it would require enumerating all GPU instances */
1594 sprintfW(bufferW, gpu_instance_fmtW, gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index);
1595 if (RegSetValueExW(hkey, gpu_idW, 0, REG_SZ, (const BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR)))
1596 goto done;
1598 /* Write all monitor instance paths under this adapter */
1599 for (i = 0; i < monitor_count; i++)
1601 sprintfW(key_nameW, monitor_id_fmtW, i);
1602 sprintfW(bufferW, monitor_instance_fmtW, video_index, i);
1603 if (RegSetValueExW(hkey, key_nameW, 0, REG_SZ, (const BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR)))
1604 goto done;
1607 /* Write StateFlags */
1608 if (RegSetValueExW(hkey, state_flagsW, 0, REG_DWORD, (const BYTE *)&adapter->state_flags,
1609 sizeof(adapter->state_flags)))
1610 goto done;
1612 ret = TRUE;
1613 done:
1614 RegCloseKey(hkey);
1615 if (!ret)
1616 ERR("Failed to initialize adapter\n");
1617 return ret;
1620 /***********************************************************************
1621 * macdrv_init_monitor
1623 * Initialize an monitor.
1625 * Return FALSE on failure and TRUE on success.
1627 static BOOL macdrv_init_monitor(HDEVINFO devinfo, const struct macdrv_monitor *monitor, int monitor_index,
1628 int video_index, const LUID *gpu_luid, UINT output_id)
1630 SP_DEVINFO_DATA device_data = {sizeof(SP_DEVINFO_DATA)};
1631 WCHAR nameW[MAX_PATH];
1632 WCHAR bufferW[MAX_PATH];
1633 HKEY hkey;
1634 RECT rect;
1635 BOOL ret = FALSE;
1637 /* Create GUID_DEVCLASS_MONITOR instance */
1638 sprintfW(bufferW, monitor_instance_fmtW, video_index, monitor_index);
1639 MultiByteToWideChar(CP_UTF8, 0, monitor->name, -1, nameW, ARRAY_SIZE(nameW));
1640 SetupDiCreateDeviceInfoW(devinfo, bufferW, &GUID_DEVCLASS_MONITOR, nameW, NULL, 0, &device_data);
1641 if (!SetupDiRegisterDeviceInfo(devinfo, &device_data, 0, NULL, NULL, NULL))
1642 goto done;
1644 /* Write HardwareID registry property */
1645 if (!SetupDiSetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_HARDWAREID,
1646 (const BYTE *)monitor_hardware_idW, sizeof(monitor_hardware_idW)))
1647 goto done;
1649 /* Write DEVPROPKEY_MONITOR_GPU_LUID */
1650 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPROPKEY_MONITOR_GPU_LUID,
1651 DEVPROP_TYPE_INT64, (const BYTE *)gpu_luid, sizeof(*gpu_luid), 0))
1652 goto done;
1654 /* Write DEVPROPKEY_MONITOR_OUTPUT_ID */
1655 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPROPKEY_MONITOR_OUTPUT_ID,
1656 DEVPROP_TYPE_UINT32, (const BYTE *)&output_id, sizeof(output_id), 0))
1657 goto done;
1659 /* Create driver key */
1660 hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
1661 RegCloseKey(hkey);
1663 /* FIXME:
1664 * Following properties are Wine specific, see comments in macdrv_init_adapter for details */
1665 /* StateFlags */
1666 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_STATEFLAGS, DEVPROP_TYPE_UINT32,
1667 (const BYTE *)&monitor->state_flags, sizeof(monitor->state_flags), 0))
1668 goto done;
1669 /* RcMonitor */
1670 rect = rect_from_cgrect(monitor->rc_monitor);
1671 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_RCMONITOR, DEVPROP_TYPE_BINARY,
1672 (const BYTE *)&rect, sizeof(rect), 0))
1673 goto done;
1674 /* RcWork */
1675 rect = rect_from_cgrect(monitor->rc_work);
1676 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_RCWORK, DEVPROP_TYPE_BINARY,
1677 (const BYTE *)&rect, sizeof(rect), 0))
1678 goto done;
1679 /* Adapter name */
1680 sprintfW(bufferW, adapter_name_fmtW, video_index + 1);
1681 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_ADAPTERNAME, DEVPROP_TYPE_STRING,
1682 (const BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR), 0))
1683 goto done;
1685 ret = TRUE;
1686 done:
1687 if (!ret)
1688 ERR("Failed to initialize monitor\n");
1689 return ret;
1692 static void prepare_devices(HKEY video_hkey)
1694 static const BOOL not_present = FALSE;
1695 SP_DEVINFO_DATA device_data = {sizeof(device_data)};
1696 HDEVINFO devinfo;
1697 DWORD i = 0;
1699 /* Remove all monitors */
1700 devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_MONITOR, displayW, NULL, 0);
1701 while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
1703 if (!SetupDiRemoveDevice(devinfo, &device_data))
1704 ERR("Failed to remove monitor\n");
1706 SetupDiDestroyDeviceInfoList(devinfo);
1708 /* Clean up old adapter keys for reinitialization */
1709 RegDeleteTreeW(video_hkey, NULL);
1711 /* FIXME:
1712 * Currently SetupDiGetClassDevsW with DIGCF_PRESENT is unsupported, So we need to clean up not present devices in
1713 * case application uses SetupDiGetClassDevsW to enumerate devices. Wrong devices could exist in registry as a result
1714 * of prefix copying or having devices unplugged. But then we couldn't simply delete GPUs because we need to retain
1715 * the same GUID for the same GPU. */
1716 i = 0;
1717 devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, pciW, NULL, 0);
1718 while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
1720 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, DEVPROP_TYPE_BOOLEAN,
1721 (const BYTE *)&not_present, sizeof(not_present), 0))
1722 ERR("Failed to set GPU present property\n");
1724 SetupDiDestroyDeviceInfoList(devinfo);
1727 static void cleanup_devices(void)
1729 SP_DEVINFO_DATA device_data = {sizeof(device_data)};
1730 HDEVINFO devinfo;
1731 DWORD type;
1732 DWORD i = 0;
1733 BOOL present;
1735 devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, pciW, NULL, 0);
1736 while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
1738 present = FALSE;
1739 SetupDiGetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, &type, (BYTE *)&present,
1740 sizeof(present), NULL, 0);
1741 if (!present && !SetupDiRemoveDevice(devinfo, &device_data))
1742 ERR("Failed to remove GPU\n");
1744 SetupDiDestroyDeviceInfoList(devinfo);
1747 /***********************************************************************
1748 * macdrv_init_display_devices
1750 * Initialize display device registry data.
1752 void macdrv_init_display_devices(BOOL force)
1754 HANDLE mutex;
1755 struct macdrv_gpu *gpus = NULL;
1756 struct macdrv_adapter *adapters = NULL;
1757 struct macdrv_monitor *monitors = NULL;
1758 INT gpu_count, adapter_count, monitor_count;
1759 INT gpu, adapter, monitor;
1760 HDEVINFO gpu_devinfo = NULL, monitor_devinfo = NULL;
1761 HKEY video_hkey = NULL;
1762 INT video_index = 0;
1763 DWORD disposition = 0;
1764 WCHAR guidW[40];
1765 WCHAR driverW[1024];
1766 LUID gpu_luid;
1767 UINT output_id = 0;
1769 mutex = get_display_device_init_mutex();
1771 if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, video_keyW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &video_hkey,
1772 &disposition))
1774 ERR("Failed to create video device key\n");
1775 goto done;
1778 /* Avoid unnecessary reinit */
1779 if (!force && disposition != REG_CREATED_NEW_KEY)
1780 goto done;
1782 TRACE("\n");
1784 prepare_devices(video_hkey);
1786 gpu_devinfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY, NULL);
1787 monitor_devinfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_MONITOR, NULL);
1789 /* Initialize GPUs */
1790 if (macdrv_get_gpus(&gpus, &gpu_count))
1791 goto done;
1792 TRACE("GPU count: %d\n", gpu_count);
1793 if (!gpu_count)
1794 ERR("No GPUs detected\n");
1796 for (gpu = 0; gpu < gpu_count; gpu++)
1798 if (!macdrv_init_gpu(gpu_devinfo, &gpus[gpu], gpu, guidW, driverW, &gpu_luid))
1799 goto done;
1801 /* Initialize adapters */
1802 if (macdrv_get_adapters(gpus[gpu].id, &adapters, &adapter_count))
1803 goto done;
1804 TRACE("GPU: %#llx %s, adapter count: %d\n", gpus[gpu].id, gpus[gpu].name, adapter_count);
1806 for (adapter = 0; adapter < adapter_count; adapter++)
1808 if (macdrv_get_monitors(adapters[adapter].id, &monitors, &monitor_count))
1809 goto done;
1810 TRACE("adapter: %#x, monitor count: %d\n", adapters[adapter].id, monitor_count);
1812 if (!macdrv_init_adapter(video_hkey, video_index, gpu, adapter, monitor_count, &gpus[gpu], guidW, driverW,
1813 &adapters[adapter]))
1814 goto done;
1816 /* Initialize monitors */
1817 for (monitor = 0; monitor < monitor_count; monitor++)
1819 TRACE("monitor: %#x %s\n", monitor, monitors[monitor].name);
1820 if (!macdrv_init_monitor(monitor_devinfo, &monitors[monitor], monitor, video_index, &gpu_luid, output_id++))
1821 goto done;
1824 macdrv_free_monitors(monitors);
1825 monitors = NULL;
1826 video_index++;
1829 macdrv_free_adapters(adapters);
1830 adapters = NULL;
1833 done:
1834 cleanup_devices();
1835 SetupDiDestroyDeviceInfoList(monitor_devinfo);
1836 SetupDiDestroyDeviceInfoList(gpu_devinfo);
1837 RegCloseKey(video_hkey);
1839 release_display_device_init_mutex(mutex);
1841 macdrv_free_gpus(gpus);
1842 macdrv_free_adapters(adapters);
1843 macdrv_free_monitors(monitors);