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
28 WINE_DEFAULT_DEBUG_CHANNEL(display);
31 BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW devmode, DWORD flags);
34 static CFArrayRef modes;
35 static int default_mode_bpp;
36 static CRITICAL_SECTION modes_section;
37 static CRITICAL_SECTION_DEBUG critsect_debug =
40 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
41 0, 0, { (DWORD_PTR)(__FILE__ ": modes_section") }
43 static CRITICAL_SECTION modes_section = { &critsect_debug, -1, 0, 0, 0, 0 };
46 static inline HMONITOR display_id_to_monitor(CGDirectDisplayID display_id)
48 return (HMONITOR)(UINT_PTR)display_id;
51 static inline CGDirectDisplayID monitor_to_display_id(HMONITOR handle)
53 return (CGDirectDisplayID)(UINT_PTR)handle;
57 static BOOL get_display_device_reg_key(char *key, unsigned len)
59 static const char display_device_guid_prop[] = "__wine_display_device_guid";
60 static const char video_path[] = "System\\CurrentControlSet\\Control\\Video\\{";
61 static const char display0[] = "}\\0000";
64 assert(len >= sizeof(video_path) + sizeof(display0) + 40);
66 guid_atom = HandleToULong(GetPropA(GetDesktopWindow(), display_device_guid_prop));
67 if (!guid_atom) return FALSE;
69 memcpy(key, video_path, sizeof(video_path));
71 if (!GlobalGetAtomNameA(guid_atom, key + strlen(key), 40))
74 strcat(key, display0);
76 TRACE("display device key %s\n", wine_dbgstr_a(key));
81 static BOOL read_registry_settings(DEVMODEW *dm)
83 char wine_mac_reg_key[128];
90 if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
93 if (RegOpenKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, KEY_READ, &hkey))
96 #define query_value(name, data) \
97 size = sizeof(DWORD); \
98 if (RegQueryValueExA(hkey, name, 0, &type, (LPBYTE)(data), &size) || \
99 type != REG_DWORD || size != sizeof(DWORD)) \
102 query_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
103 dm->dmFields |= DM_BITSPERPEL;
104 query_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
105 dm->dmFields |= DM_PELSWIDTH;
106 query_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
107 dm->dmFields |= DM_PELSHEIGHT;
108 query_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
109 dm->dmFields |= DM_DISPLAYFREQUENCY;
110 query_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
111 dm->dmFields |= DM_DISPLAYFLAGS;
112 query_value("DefaultSettings.XPanning", &dm->dmPosition.x);
113 query_value("DefaultSettings.YPanning", &dm->dmPosition.y);
114 query_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
115 query_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
124 static BOOL write_registry_settings(const DEVMODEW *dm)
126 char wine_mac_reg_key[128];
130 if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
133 if (RegCreateKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, NULL,
134 REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL))
137 #define set_value(name, data) \
138 if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
141 set_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
142 set_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
143 set_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
144 set_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
145 set_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
146 set_value("DefaultSettings.XPanning", &dm->dmPosition.x);
147 set_value("DefaultSettings.YPanning", &dm->dmPosition.y);
148 set_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
149 set_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
158 static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode)
160 CFStringRef pixel_encoding;
161 int bits_per_pixel = 0;
163 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
166 if (CFEqual(pixel_encoding, CFSTR(kIO32BitFloatPixels)))
167 bits_per_pixel = 128;
168 else if (CFEqual(pixel_encoding, CFSTR(kIO16BitFloatPixels)))
170 else if (CFEqual(pixel_encoding, CFSTR(kIO64BitDirectPixels)))
172 else if (CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels)))
174 else if (CFEqual(pixel_encoding, CFSTR(IO32BitDirectPixels)))
176 else if (CFEqual(pixel_encoding, CFSTR(IO16BitDirectPixels)))
178 else if (CFEqual(pixel_encoding, CFSTR(IO8BitIndexedPixels)))
180 else if (CFEqual(pixel_encoding, CFSTR(IO4BitIndexedPixels)))
182 else if (CFEqual(pixel_encoding, CFSTR(IO2BitIndexedPixels)))
184 else if (CFEqual(pixel_encoding, CFSTR(IO1BitIndexedPixels)))
187 CFRelease(pixel_encoding);
190 return bits_per_pixel;
194 static int get_default_bpp(void)
198 EnterCriticalSection(&modes_section);
200 if (!default_mode_bpp)
202 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
205 default_mode_bpp = display_mode_bits_per_pixel(mode);
209 if (!default_mode_bpp)
210 default_mode_bpp = 32;
213 ret = default_mode_bpp;
215 LeaveCriticalSection(&modes_section);
217 TRACE(" -> %d\n", ret);
222 /***********************************************************************
223 * ChangeDisplaySettingsEx (MACDRV.@)
226 LONG CDECL macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
227 HWND hwnd, DWORD flags, LPVOID lpvoid)
229 LONG ret = DISP_CHANGE_BADMODE;
232 BOOL def_mode = TRUE;
233 struct macdrv_display *displays;
235 CFArrayRef display_modes;
236 CFIndex count, i, safe;
238 TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid);
242 /* this is the minimal dmSize that XP accepts */
243 if (devmode->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
244 return DISP_CHANGE_FAILED;
246 if (devmode->dmSize >= FIELD_OFFSET(DEVMODEW, dmFields) + sizeof(devmode->dmFields))
248 if (((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel) ||
249 ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth) ||
250 ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight) ||
251 ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency))
258 if (!macdrv_EnumDisplaySettingsEx(devname, ENUM_REGISTRY_SETTINGS, &dm, 0))
260 ERR("Default mode not found!\n");
261 return DISP_CHANGE_BADMODE;
264 TRACE("Return to original display mode\n");
268 if ((devmode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
270 WARN("devmode doesn't specify the resolution: %04x\n", devmode->dmFields);
271 return DISP_CHANGE_BADMODE;
274 if (macdrv_get_displays(&displays, &num_displays))
275 return DISP_CHANGE_FAILED;
277 display_modes = CGDisplayCopyAllDisplayModes(displays[0].displayID, NULL);
280 macdrv_free_displays(displays);
281 return DISP_CHANGE_FAILED;
284 bpp = get_default_bpp();
285 if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel != bpp)
286 TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, devmode->dmBitsPerPel);
288 TRACE("looking for %dx%dx%dbpp @%d Hz",
289 (devmode->dmFields & DM_PELSWIDTH ? devmode->dmPelsWidth : 0),
290 (devmode->dmFields & DM_PELSHEIGHT ? devmode->dmPelsHeight : 0),
292 (devmode->dmFields & DM_DISPLAYFREQUENCY ? devmode->dmDisplayFrequency : 0));
293 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
294 TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un");
295 if (devmode->dmFields & DM_DISPLAYFLAGS)
296 TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
300 count = CFArrayGetCount(display_modes);
301 for (i = 0; i < count; i++)
303 CGDisplayModeRef display_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(display_modes, i);
304 uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
305 int mode_bpp = display_mode_bits_per_pixel(display_mode);
306 size_t width = CGDisplayModeGetWidth(display_mode);
307 size_t height = CGDisplayModeGetHeight(display_mode);
309 if (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag))
317 if (devmode->dmFields & DM_PELSWIDTH)
319 if (devmode->dmPelsWidth != width)
322 if (devmode->dmFields & DM_PELSHEIGHT)
324 if (devmode->dmPelsHeight != height)
327 if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != 0)
329 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
332 if (devmode->dmDisplayFrequency != (DWORD)refresh_rate)
335 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
337 if (!(devmode->dmDisplayFixedOutput == DMDFO_STRETCH) != !(io_flags & kDisplayModeStretchedFlag))
340 if (devmode->dmFields & DM_DISPLAYFLAGS)
342 if (!(devmode->dmDisplayFlags & DM_INTERLACED) != !(io_flags & kDisplayModeInterlacedFlag))
346 /* we have a valid mode */
347 TRACE("Requested display settings match mode %ld\n", safe);
349 if ((flags & CDS_UPDATEREGISTRY) && !write_registry_settings(devmode))
351 WARN("Failed to update registry\n");
352 ret = DISP_CHANGE_NOTUPDATED;
356 if (flags & (CDS_TEST | CDS_NORESET))
357 ret = DISP_CHANGE_SUCCESSFUL;
360 if (macdrv_set_display_mode(&displays[0], display_mode))
362 SendMessageW(GetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
363 MAKELPARAM(width, height));
364 ret = DISP_CHANGE_SUCCESSFUL;
368 WARN("Failed to set display mode\n");
369 ret = DISP_CHANGE_FAILED;
376 CFRelease(display_modes);
377 macdrv_free_displays(displays);
381 /* no valid modes found */
382 ERR("No matching mode found %ux%ux%d @%u!\n", devmode->dmPelsWidth, devmode->dmPelsHeight,
383 bpp, devmode->dmDisplayFrequency);
390 /***********************************************************************
391 * EnumDisplayMonitors (MACDRV.@)
393 BOOL CDECL macdrv_EnumDisplayMonitors(HDC hdc, LPRECT rect, MONITORENUMPROC proc, LPARAM lparam)
395 struct macdrv_display *displays;
400 TRACE("%p, %s, %p, %#lx\n", hdc, wine_dbgstr_rect(rect), proc, lparam);
407 if (!GetDCOrgEx(hdc, &origin)) return FALSE;
408 if (GetClipBox(hdc, &limit) == ERROR) return FALSE;
410 if (rect && !IntersectRect(&limit, &limit, rect)) return TRUE;
412 if (macdrv_get_displays(&displays, &num_displays))
415 for (i = 0; i < num_displays; i++)
417 RECT monrect = rect_from_cgrect(displays[i].frame);
418 OffsetRect(&monrect, -origin.x, -origin.y);
419 if (IntersectRect(&monrect, &monrect, &limit))
421 HMONITOR monitor = display_id_to_monitor(displays[i].displayID);
422 TRACE("monitor %d handle %p @ %s\n", i, monitor, wine_dbgstr_rect(&monrect));
423 if (!proc(monitor, hdc, &monrect, lparam))
433 if (macdrv_get_displays(&displays, &num_displays))
436 for (i = 0; i < num_displays; i++)
438 RECT monrect = rect_from_cgrect(displays[i].frame);
440 if (!rect || IntersectRect(&unused, &monrect, rect))
442 HMONITOR monitor = display_id_to_monitor(displays[i].displayID);
443 TRACE("monitor %d handle %p @ %s\n", i, monitor, wine_dbgstr_rect(&monrect));
444 if (!proc(monitor, 0, &monrect, lparam))
453 macdrv_free_displays(displays);
459 /***********************************************************************
460 * EnumDisplaySettingsEx (MACDRV.@)
463 BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode,
464 LPDEVMODEW devmode, DWORD flags)
466 static const WCHAR dev_name[CCHDEVICENAME] =
467 { 'W','i','n','e',' ','M','a','c',' ','d','r','i','v','e','r',0 };
468 struct macdrv_display *displays = NULL;
470 CGDisplayModeRef display_mode;
474 TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname), mode, devmode, devmode->dmSize, flags);
476 memcpy(devmode->dmDeviceName, dev_name, sizeof(dev_name));
477 devmode->dmSpecVersion = DM_SPECVERSION;
478 devmode->dmDriverVersion = DM_SPECVERSION;
479 devmode->dmSize = FIELD_OFFSET(DEVMODEW, dmICMMethod);
480 devmode->dmDriverExtra = 0;
481 memset(&devmode->dmFields, 0, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields));
483 if (mode == ENUM_REGISTRY_SETTINGS)
485 TRACE("mode %d (registry) -- getting default mode\n", mode);
486 return read_registry_settings(devmode);
489 if (macdrv_get_displays(&displays, &num_displays))
492 if (mode == ENUM_CURRENT_SETTINGS)
494 TRACE("mode %d (current) -- getting current mode\n", mode);
495 display_mode = CGDisplayCopyDisplayMode(displays[0].displayID);
499 EnterCriticalSection(&modes_section);
501 if (mode == 0 || !modes)
503 if (modes) CFRelease(modes);
504 modes = CGDisplayCopyAllDisplayModes(displays[0].displayID, NULL);
510 if (flags & EDS_RAWMODE)
512 if (mode < CFArrayGetCount(modes))
513 display_mode = (CGDisplayModeRef)CFRetain(CFArrayGetValueAtIndex(modes, mode));
517 DWORD count, i, safe_modes = 0;
518 count = CFArrayGetCount(modes);
519 for (i = 0; i < count; i++)
521 CGDisplayModeRef candidate = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
522 io_flags = CGDisplayModeGetIOFlags(candidate);
523 if ((io_flags & kDisplayModeValidFlag) &&
524 (io_flags & kDisplayModeSafeFlag))
527 if (safe_modes > mode)
529 display_mode = (CGDisplayModeRef)CFRetain(candidate);
537 LeaveCriticalSection(&modes_section);
543 /* We currently only report modes for the primary display, so it's at (0, 0). */
544 devmode->dmPosition.x = 0;
545 devmode->dmPosition.y = 0;
546 devmode->dmFields |= DM_POSITION;
548 rotation = CGDisplayRotation(displays[0].displayID);
549 devmode->dmDisplayOrientation = ((int)((rotation / 90) + 0.5)) % 4;
550 devmode->dmFields |= DM_DISPLAYORIENTATION;
552 io_flags = CGDisplayModeGetIOFlags(display_mode);
553 if (io_flags & kDisplayModeStretchedFlag)
554 devmode->dmDisplayFixedOutput = DMDFO_STRETCH;
556 devmode->dmDisplayFixedOutput = DMDFO_CENTER;
557 devmode->dmFields |= DM_DISPLAYFIXEDOUTPUT;
559 devmode->dmBitsPerPel = display_mode_bits_per_pixel(display_mode);
560 if (devmode->dmBitsPerPel)
561 devmode->dmFields |= DM_BITSPERPEL;
563 devmode->dmPelsWidth = CGDisplayModeGetWidth(display_mode);
564 devmode->dmPelsHeight = CGDisplayModeGetHeight(display_mode);
565 devmode->dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
567 devmode->dmDisplayFlags = 0;
568 if (io_flags & kDisplayModeInterlacedFlag)
569 devmode->dmDisplayFlags |= DM_INTERLACED;
570 devmode->dmFields |= DM_DISPLAYFLAGS;
572 devmode->dmDisplayFrequency = CGDisplayModeGetRefreshRate(display_mode);
573 if (!devmode->dmDisplayFrequency)
574 devmode->dmDisplayFrequency = 60;
575 devmode->dmFields |= DM_DISPLAYFREQUENCY;
577 CFRelease(display_mode);
578 macdrv_free_displays(displays);
580 TRACE("mode %d -- %dx%dx%dbpp @%d Hz", mode,
581 devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel,
582 devmode->dmDisplayFrequency);
583 if (devmode->dmDisplayOrientation)
584 TRACE(" rotated %u degrees", devmode->dmDisplayOrientation * 90);
585 if (devmode->dmDisplayFixedOutput == DMDFO_STRETCH)
587 if (devmode->dmDisplayFlags & DM_INTERLACED)
588 TRACE(" interlaced");
594 TRACE("mode %d -- not present\n", mode);
595 if (displays) macdrv_free_displays(displays);
596 SetLastError(ERROR_NO_MORE_FILES);
601 /***********************************************************************
602 * GetMonitorInfo (MACDRV.@)
604 BOOL CDECL macdrv_GetMonitorInfo(HMONITOR monitor, LPMONITORINFO info)
606 static const WCHAR adapter_name[] = { '\\','\\','.','\\','D','I','S','P','L','A','Y','1',0 };
607 struct macdrv_display *displays;
609 CGDirectDisplayID display_id;
612 TRACE("%p, %p\n", monitor, info);
614 if (macdrv_get_displays(&displays, &num_displays))
616 ERR("couldn't get display list\n");
617 SetLastError(ERROR_GEN_FAILURE);
621 display_id = monitor_to_display_id(monitor);
622 for (i = 0; i < num_displays; i++)
624 if (displays[i].displayID == display_id)
628 if (i < num_displays)
630 info->rcMonitor = rect_from_cgrect(displays[i].frame);
631 info->rcWork = rect_from_cgrect(displays[i].work_frame);
633 info->dwFlags = (i == 0) ? MONITORINFOF_PRIMARY : 0;
635 if (info->cbSize >= sizeof(MONITORINFOEXW))
636 lstrcpyW(((MONITORINFOEXW*)info)->szDevice, adapter_name);
638 TRACE(" -> rcMonitor %s rcWork %s dwFlags %08x\n", wine_dbgstr_rect(&info->rcMonitor),
639 wine_dbgstr_rect(&info->rcWork), info->dwFlags);
643 ERR("invalid monitor handle\n");
644 SetLastError(ERROR_INVALID_HANDLE);
647 macdrv_free_displays(displays);
648 return (i < num_displays);
652 /***********************************************************************
653 * macdrv_displays_changed
655 * Handler for DISPLAYS_CHANGED events.
657 void macdrv_displays_changed(const macdrv_event *event)
659 HWND hwnd = GetDesktopWindow();
661 /* A system display change will get delivered to all GUI-attached threads,
662 so the desktop-window-owning thread will get it and all others should
663 ignore it. A synthesized display change event due to activation
664 will only get delivered to the activated process. So, it needs to
665 process it (by sending it to the desktop window). */
666 if (event->displays_changed.activating ||
667 GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId())
669 CGDirectDisplayID mainDisplay = CGMainDisplayID();
670 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(mainDisplay);
671 size_t width = CGDisplayModeGetWidth(mode);
672 size_t height = CGDisplayModeGetHeight(mode);
673 int mode_bpp = display_mode_bits_per_pixel(mode);
675 CGDisplayModeRelease(mode);
676 SendMessageW(hwnd, WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
677 MAKELPARAM(width, height));