libwine: Remove __wine_main_arg* from the public header.
[wine/zf.git] / dlls / winemac.drv / display.c
blob4c610c45737465d4fb5078bdb7c9693ec661ce7c
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 /* 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[] = {
76 'P','C','I','\\',
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','\\',
81 '%','0','8','X',0};
82 static const WCHAR gpu_hardware_id_fmtW[] = {
83 'P','C','I','\\',
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','\\',
97 '%','s','\\',
98 '%','0','4','x',0};
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";
140 ATOM guid_atom;
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))
150 return FALSE;
152 strcat(key, display0);
154 TRACE("display device key %s\n", wine_dbgstr_a(key));
155 return TRUE;
159 static BOOL read_registry_settings(DEVMODEW *dm)
161 char wine_mac_reg_key[128];
162 HKEY hkey;
163 DWORD type, size;
164 BOOL ret = TRUE;
166 dm->dmFields = 0;
168 if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
169 return FALSE;
171 if (RegOpenKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, KEY_READ, &hkey))
172 return FALSE;
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)) \
178 ret = FALSE
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);
195 #undef query_value
197 RegCloseKey(hkey);
198 return ret;
202 static BOOL write_registry_settings(const DEVMODEW *dm)
204 char wine_mac_reg_key[128];
205 HKEY hkey;
206 BOOL ret = TRUE;
208 if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
209 return FALSE;
211 if (RegCreateKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, NULL,
212 REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL))
213 return FALSE;
215 #define set_value(name, data) \
216 if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
217 ret = FALSE
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);
229 #undef set_value
231 RegCloseKey(hkey);
232 return ret;
236 static BOOL write_display_settings(HKEY parent_hkey, CGDirectDisplayID displayID)
238 BOOL ret = FALSE;
239 char display_key_name[19];
240 HKEY display_hkey;
241 CGDisplayModeRef display_mode;
242 DWORD val;
243 CFStringRef pixel_encoding;
244 size_t len;
245 WCHAR* buf = NULL;
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))
251 return FALSE;
253 display_mode = CGDisplayCopyDisplayMode(displayID);
254 if (!display_mode)
255 goto fail;
257 val = CGDisplayModeGetWidth(display_mode);
258 if (RegSetValueExA(display_hkey, "Width", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
259 goto fail;
260 val = CGDisplayModeGetHeight(display_mode);
261 if (RegSetValueExA(display_hkey, "Height", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
262 goto fail;
263 val = CGDisplayModeGetRefreshRate(display_mode) * 100;
264 if (RegSetValueExA(display_hkey, "RefreshRateTimes100", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
265 goto fail;
266 val = CGDisplayModeGetIOFlags(display_mode);
267 if (RegSetValueExA(display_hkey, "IOFlags", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
268 goto fail;
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)))
275 goto fail;
276 val = CGDisplayModeGetPixelHeight(display_mode);
277 if (RegSetValueExA(display_hkey, "PixelHeight", 0, REG_DWORD, (const BYTE*)&val, sizeof(val)))
278 goto fail;
280 #endif
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);
286 buf[len] = 0;
287 CFRelease(pixel_encoding);
288 if (RegSetValueExW(display_hkey, pixelencodingW, 0, REG_SZ, (const BYTE*)buf, (len + 1) * sizeof(WCHAR)))
289 goto fail;
291 ret = TRUE;
293 fail:
294 HeapFree(GetProcessHeap(), 0, buf);
295 if (display_mode) CGDisplayModeRelease(display_mode);
296 RegCloseKey(display_hkey);
297 if (!ret)
298 RegDeleteKeyA(parent_hkey, display_key_name);
299 return ret;
303 static void init_original_display_mode(void)
305 BOOL success = FALSE;
306 HKEY mac_driver_hkey, parent_hkey;
307 DWORD disposition;
308 struct macdrv_display *displays = NULL;
309 int num_displays, i;
311 if (inited_original_display_mode)
312 return;
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))
317 return;
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))
323 parent_hkey = NULL;
324 goto fail;
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)
330 goto done;
332 if (macdrv_get_displays(&displays, &num_displays))
333 goto fail;
335 for (i = 0; i < num_displays; i++)
337 if (!write_display_settings(parent_hkey, displays[i].displayID))
338 goto fail;
341 done:
342 success = TRUE;
344 fail:
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);
350 if (success)
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))
359 return FALSE;
360 return TRUE;
364 static void free_display_mode_descriptor(struct display_mode_descriptor* desc)
366 if (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];
381 HKEY hkey;
382 DWORD type, size;
383 DWORD refresh100;
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))
391 return NULL;
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))
400 goto done;
401 if (refresh100)
402 desc->refresh = refresh100 / 100.0;
403 else
404 desc->refresh = 60;
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;
413 size = 0;
414 if (RegQueryValueExW(hkey, pixelencodingW, 0, &type, NULL, &size) || type != REG_SZ)
415 goto done;
416 pixel_encoding = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
417 if (RegQueryValueExW(hkey, pixelencodingW, 0, &type, (BYTE*)pixel_encoding, &size) || type != REG_SZ)
418 goto done;
419 if ((end = memchrW(pixel_encoding, 0, size)))
420 size = end - pixel_encoding;
421 desc->pixel_encoding = CFStringCreateWithCharacters(NULL, (const UniChar*)pixel_encoding, size);
423 ret = desc;
425 done:
426 if (!ret)
427 free_display_mode_descriptor(desc);
428 HeapFree(GetProcessHeap(), 0, pixel_encoding);
429 RegCloseKey(hkey);
430 return ret;
434 static BOOL display_mode_matches_descriptor(CGDisplayModeRef mode, const struct display_mode_descriptor* desc)
436 DWORD mode_io_flags;
437 double mode_refresh;
438 CFStringRef mode_pixel_encoding;
440 if (!desc)
441 return FALSE;
443 if (CGDisplayModeGetWidth(mode) != desc->width ||
444 CGDisplayModeGetHeight(mode) != desc->height)
445 return FALSE;
447 mode_io_flags = CGDisplayModeGetIOFlags(mode);
448 if ((desc->io_flags ^ mode_io_flags) & (kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeStretchedFlag |
449 kDisplayModeInterlacedFlag | kDisplayModeTelevisionFlag))
450 return FALSE;
452 mode_refresh = CGDisplayModeGetRefreshRate(mode);
453 if (!mode_refresh)
454 mode_refresh = 60;
455 if (fabs(desc->refresh - mode_refresh) > 0.1)
456 return FALSE;
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)
463 return FALSE;
465 else
466 #endif
467 if (CGDisplayModeGetWidth(mode) != desc->pixel_width ||
468 CGDisplayModeGetHeight(mode) != desc->pixel_height)
469 return FALSE;
471 mode_pixel_encoding = CGDisplayModeCopyPixelEncoding(mode);
472 if (!CFEqual(mode_pixel_encoding, desc->pixel_encoding))
474 CFRelease(mode_pixel_encoding);
475 return FALSE;
477 CFRelease(mode_pixel_encoding);
479 return TRUE;
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);
489 if (pixel_encoding)
491 if (CFEqual(pixel_encoding, CFSTR(kIO32BitFloatPixels)))
492 bits_per_pixel = 128;
493 else if (CFEqual(pixel_encoding, CFSTR(kIO16BitFloatPixels)))
494 bits_per_pixel = 64;
495 else if (CFEqual(pixel_encoding, CFSTR(kIO64BitDirectPixels)))
496 bits_per_pixel = 64;
497 else if (CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels)))
498 bits_per_pixel = 30;
499 else if (CFEqual(pixel_encoding, CFSTR(IO32BitDirectPixels)))
500 bits_per_pixel = 32;
501 else if (CFEqual(pixel_encoding, CFSTR(IO16BitDirectPixels)))
502 bits_per_pixel = 16;
503 else if (CFEqual(pixel_encoding, CFSTR(IO8BitIndexedPixels)))
504 bits_per_pixel = 8;
505 else if (CFEqual(pixel_encoding, CFSTR(IO4BitIndexedPixels)))
506 bits_per_pixel = 4;
507 else if (CFEqual(pixel_encoding, CFSTR(IO2BitIndexedPixels)))
508 bits_per_pixel = 2;
509 else if (CFEqual(pixel_encoding, CFSTR(IO1BitIndexedPixels)))
510 bits_per_pixel = 1;
512 CFRelease(pixel_encoding);
515 return bits_per_pixel;
519 static int get_default_bpp(void)
521 int ret;
523 EnterCriticalSection(&modes_section);
525 if (!default_mode_bpp)
527 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
528 if (mode)
530 default_mode_bpp = display_mode_bits_per_pixel(mode);
531 CFRelease(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);
543 return 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)
550 CFDictionaryRef ret;
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)
560 width *= 2;
561 height *= 2;
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[] = {
573 CFSTR("io_flags"),
574 CFSTR("width"),
575 CFSTR("height"),
576 CFSTR("pixel_encoding"),
577 CFSTR("refresh_rate"),
579 const void* values[ARRAY_SIZE(keys)] = {
580 cf_io_flags,
581 cf_width,
582 cf_height,
583 pixel_encoding,
584 cf_refresh,
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);
593 CFRelease(cf_width);
594 CFRelease(cf_height);
595 CFRelease(cf_refresh);
597 return ret;
599 #endif
602 /***********************************************************************
603 * copy_display_modes
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;
624 CFIndex i, count;
625 CGDisplayModeRef* mode_array;
627 options = CFDictionaryCreate(NULL, (const void**)&kCGDisplayShowDuplicateLowResolutionModes,
628 (const void**)&kCFBooleanTrue, 1, &kCFTypeDictionaryKeyCallBacks,
629 &kCFTypeDictionaryValueCallBacks);
631 modes = CGDisplayCopyAllDisplayModes(display, options);
632 if (options)
633 CFRelease(options);
634 if (!modes)
635 return NULL;
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++)
643 BOOL better = TRUE;
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. */
650 if (new_is_original)
651 better = TRUE;
652 else
654 CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(new_mode);
655 CGDisplayModeRef old_mode;
657 if (pixel_encoding)
659 BOOL bpp30 = CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels));
660 CFRelease(pixel_encoding);
661 if (bpp30)
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.
668 Skip it. */
669 CFRelease(key);
670 continue;
674 old_mode = (CGDisplayModeRef)CFDictionaryGetValue(modes_by_size, key);
675 if (old_mode)
677 BOOL old_is_original = display_mode_matches_descriptor(old_mode, desc);
679 if (old_is_original)
680 better = FALSE;
681 else
683 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
684 is scaled. */
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)
695 better = TRUE;
696 else if (!new_size_same && old_size_same)
697 better = FALSE;
698 else
700 /* Otherwise, prefer the mode with the smaller pixel size. */
701 if (old_width_pixels < new_width_pixels || old_height_pixels < new_height_pixels)
702 better = FALSE;
708 if (better)
709 CFDictionarySetValue(modes_by_size, key, new_mode);
711 CFRelease(key);
714 free_display_mode_descriptor(desc);
715 CFRelease(modes);
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);
724 else
725 #endif
726 modes = CGDisplayCopyAllDisplayModes(display, NULL);
728 return modes;
732 void check_retina_status(void)
734 if (retina_enabled)
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;
757 int bpp;
758 struct macdrv_display *displays;
759 int num_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);
775 if (!display_modes)
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),
788 bpp,
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-");
794 TRACE("\n");
796 desc = create_original_display_mode_descriptor(displays[0].displayID);
798 safe = -1;
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)
812 width *= 2;
813 height *= 2;
816 if (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag))
817 continue;
819 safe++;
821 if (bpp != mode_bpp)
822 continue;
824 if (devmode->dmFields & DM_PELSWIDTH)
826 if (devmode->dmPelsWidth != width)
827 continue;
829 if (devmode->dmFields & DM_PELSHEIGHT)
831 if (devmode->dmPelsHeight != height)
832 continue;
834 if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != 0)
836 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
837 if (!refresh_rate)
838 refresh_rate = 60;
839 if (devmode->dmDisplayFrequency != (DWORD)refresh_rate)
840 continue;
842 if (devmode->dmFields & DM_DISPLAYFLAGS)
844 if (!(devmode->dmDisplayFlags & DM_INTERLACED) != !(io_flags & kDisplayModeInterlacedFlag))
845 continue;
847 else if (best_display_mode)
849 if (io_flags & kDisplayModeInterlacedFlag && !(best_io_flags & kDisplayModeInterlacedFlag))
850 continue;
851 else if (!(io_flags & kDisplayModeInterlacedFlag) && best_io_flags & kDisplayModeInterlacedFlag)
852 goto better;
854 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
856 if (!(devmode->dmDisplayFixedOutput == DMDFO_STRETCH) != !(io_flags & kDisplayModeStretchedFlag))
857 continue;
859 else if (best_display_mode)
861 if (io_flags & kDisplayModeStretchedFlag && !(best_io_flags & kDisplayModeStretchedFlag))
862 continue;
863 else if (!(io_flags & kDisplayModeStretchedFlag) && best_io_flags & kDisplayModeStretchedFlag)
864 goto better;
867 if (best_display_mode)
868 continue;
870 better:
871 best_display_mode = display_mode;
872 best = safe;
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)
897 width *= 2;
898 height *= 2;
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);
907 else
909 WARN("Failed to set display mode\n");
910 ret = DISP_CHANGE_FAILED;
913 else
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);
924 return ret;
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;
937 int num_displays;
938 CGDisplayModeRef display_mode;
939 int display_mode_bpp;
940 BOOL synthesized = FALSE;
941 double rotation;
942 uint32_t io_flags;
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))
962 goto failed;
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);
970 else
972 DWORD count, i;
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;
982 if (modes)
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);
989 if (bpp == 8)
990 modes_has_8bpp = TRUE;
991 else if (bpp == 16)
992 modes_has_16bpp = TRUE;
997 display_mode = NULL;
998 if (modes)
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)))
1011 continue;
1013 seen_modes++;
1014 if (seen_modes > mode)
1016 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1017 display_mode_bpp = display_mode_bits_per_pixel(display_mode);
1018 break;
1021 /* We only synthesize modes from those having the default bpp. */
1022 if (display_mode_bits_per_pixel(candidate) != default_bpp)
1023 continue;
1025 if (!modes_has_8bpp)
1027 seen_modes++;
1028 if (seen_modes > mode)
1030 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1031 display_mode_bpp = 8;
1032 synthesized = TRUE;
1033 break;
1037 if (!modes_has_16bpp)
1039 seen_modes++;
1040 if (seen_modes > mode)
1042 display_mode = (CGDisplayModeRef)CFRetain(candidate);
1043 display_mode_bpp = 16;
1044 synthesized = TRUE;
1045 break;
1051 LeaveCriticalSection(&modes_section);
1054 if (!display_mode)
1055 goto failed;
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;
1069 else
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);
1079 if (retina_enabled)
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");
1113 if (synthesized)
1114 TRACE(" (synthesized)");
1115 TRACE("\n");
1117 return TRUE;
1119 failed:
1120 TRACE("mode %d -- not present\n", mode);
1121 if (displays) macdrv_free_displays(displays);
1122 SetLastError(ERROR_NO_MORE_FILES);
1123 return FALSE;
1127 /***********************************************************************
1128 * GetDeviceGammaRamp (MACDRV.@)
1130 BOOL CDECL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1132 BOOL ret = FALSE;
1133 DDGAMMARAMP *r = ramp;
1134 struct macdrv_display *displays;
1135 int num_displays;
1136 uint32_t mac_entries;
1137 int win_entries = ARRAY_SIZE(r->red);
1138 CGGammaValue *red, *green, *blue;
1139 CGError err;
1140 int win_entry;
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");
1147 return FALSE;
1150 mac_entries = CGDisplayGammaTableCapacity(displays[0].displayID);
1151 red = HeapAlloc(GetProcessHeap(), 0, mac_entries * sizeof(red[0]) * 3);
1152 if (!red)
1153 goto done;
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);
1162 goto done;
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;
1174 else
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];
1188 else
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;
1203 ret = TRUE;
1205 done:
1206 HeapFree(GetProcessHeap(), 0, red);
1207 macdrv_free_displays(displays);
1208 return ret;
1211 /***********************************************************************
1212 * SetDeviceGammaRamp (MACDRV.@)
1214 BOOL CDECL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
1216 DDGAMMARAMP *r = ramp;
1217 struct macdrv_display *displays;
1218 int num_displays;
1219 int win_entries = ARRAY_SIZE(r->red);
1220 CGGammaValue *red, *green, *blue;
1221 int i;
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");
1229 return FALSE;
1232 if (macdrv_get_displays(&displays, &num_displays))
1234 WARN("failed to get Mac displays\n");
1235 return FALSE;
1238 red = HeapAlloc(GetProcessHeap(), 0, win_entries * sizeof(red[0]) * 3);
1239 if (!red)
1240 goto done;
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);
1255 done:
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)
1294 width *= 2;
1295 height *= 2;
1298 SendMessageW(hwnd, WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
1299 MAKELPARAM(width, height));
1303 /***********************************************************************
1304 * macdrv_init_gpu
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,
1311 WCHAR *driver)
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];
1318 FILETIME filetime;
1319 HKEY hkey = NULL;
1320 GUID guid;
1321 INT written;
1322 DWORD size;
1323 BOOL ret = FALSE;
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))
1331 goto done;
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)))
1339 goto done;
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))
1344 goto done;
1346 /* Open driver key.
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)))
1352 goto done;
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)))
1356 goto done;
1357 RegCloseKey(hkey);
1359 /* Retrieve driver value for adapters */
1360 if (!SetupDiGetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, sizeof(bufferW),
1361 NULL))
1362 goto done;
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))
1372 UuidCreate(&guid);
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)))
1376 goto done;
1378 lstrcpyW(guid_string, bufferW);
1380 ret = TRUE;
1381 done:
1382 RegCloseKey(hkey);
1383 if (!ret)
1384 ERR("Failed to initialize GPU\n");
1385 return ret;
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];
1402 HKEY hkey = NULL;
1403 BOOL ret = FALSE;
1404 LSTATUS ls;
1405 INT i;
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)))
1414 goto done;
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)))
1424 goto done;
1425 RegCloseKey(hkey);
1426 hkey = NULL;
1428 /* FIXME:
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)))
1438 goto done;
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)))
1446 goto done;
1449 /* Write StateFlags */
1450 if (RegSetValueExW(hkey, state_flagsW, 0, REG_DWORD, (const BYTE *)&adapter->state_flags,
1451 sizeof(adapter->state_flags)))
1452 goto done;
1454 ret = TRUE;
1455 done:
1456 RegCloseKey(hkey);
1457 if (!ret)
1458 ERR("Failed to initialize adapter\n");
1459 return ret;
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,
1470 int video_index)
1472 SP_DEVINFO_DATA device_data = {sizeof(SP_DEVINFO_DATA)};
1473 WCHAR nameW[MAX_PATH];
1474 WCHAR bufferW[MAX_PATH];
1475 HKEY hkey;
1476 RECT rect;
1477 BOOL ret = FALSE;
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))
1484 goto done;
1486 /* Write HardwareID registry property */
1487 if (!SetupDiSetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_HARDWAREID,
1488 (const BYTE *)monitor_hardware_idW, sizeof(monitor_hardware_idW)))
1489 goto done;
1491 /* Create driver key */
1492 hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
1493 RegCloseKey(hkey);
1495 /* FIXME:
1496 * Following properties are Wine specific, see comments in macdrv_init_adapter for details */
1497 /* StateFlags */
1498 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_STATEFLAGS, DEVPROP_TYPE_UINT32,
1499 (const BYTE *)&monitor->state_flags, sizeof(monitor->state_flags), 0))
1500 goto done;
1501 /* RcMonitor */
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))
1505 goto done;
1506 /* RcWork */
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))
1510 goto done;
1511 /* Adapter name */
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))
1515 goto done;
1517 ret = TRUE;
1518 done:
1519 if (!ret)
1520 ERR("Failed to initialize monitor\n");
1521 return ret;
1524 static void prepare_devices(HKEY video_hkey)
1526 static const BOOL not_present = FALSE;
1527 SP_DEVINFO_DATA device_data = {sizeof(device_data)};
1528 HDEVINFO devinfo;
1529 DWORD i = 0;
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);
1543 /* FIXME:
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. */
1548 i = 0;
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 *)&not_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)};
1562 HDEVINFO devinfo;
1563 DWORD type;
1564 DWORD i = 0;
1565 BOOL present;
1567 devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, pciW, NULL, 0);
1568 while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
1570 present = FALSE;
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};
1587 HANDLE mutex;
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;
1597 WCHAR guidW[40];
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,
1604 &disposition))
1606 ERR("Failed to create video device key\n");
1607 goto done;
1610 /* Avoid unnecessary reinit */
1611 if (!force && disposition != REG_CREATED_NEW_KEY)
1612 goto done;
1614 TRACE("\n");
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))
1623 goto done;
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))
1629 goto done;
1631 /* Initialize adapters */
1632 if (macdrv_get_adapters(gpus[gpu].id, &adapters, &adapter_count))
1633 goto done;
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))
1639 goto done;
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]))
1644 goto done;
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))
1651 goto done;
1654 macdrv_free_monitors(monitors);
1655 monitors = NULL;
1656 video_index++;
1659 macdrv_free_adapters(adapters);
1660 adapters = NULL;
1663 done:
1664 cleanup_devices();
1665 SetupDiDestroyDeviceInfoList(monitor_devinfo);
1666 SetupDiDestroyDeviceInfoList(gpu_devinfo);
1667 RegCloseKey(video_hkey);
1669 ReleaseMutex(mutex);
1670 CloseHandle(mutex);
1672 macdrv_free_gpus(gpus);
1673 macdrv_free_adapters(adapters);
1674 macdrv_free_monitors(monitors);