include: Add event trace flags and guid to evntrace.h.
[wine.git] / dlls / winemac.drv / display.c
blob10ecf672ebbf3446f52d8fb2dc0f04303d773cf2
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"
29 WINE_DEFAULT_DEBUG_CHANNEL(display);
32 BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW devmode, DWORD flags);
35 static CFArrayRef modes;
36 static BOOL modes_has_8bpp, modes_has_16bpp;
37 static int default_mode_bpp;
38 static CRITICAL_SECTION modes_section;
39 static CRITICAL_SECTION_DEBUG critsect_debug =
41 0, 0, &modes_section,
42 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
43 0, 0, { (DWORD_PTR)(__FILE__ ": modes_section") }
45 static CRITICAL_SECTION modes_section = { &critsect_debug, -1, 0, 0, 0, 0 };
48 static inline HMONITOR display_id_to_monitor(CGDirectDisplayID display_id)
50 return (HMONITOR)(UINT_PTR)display_id;
53 static inline CGDirectDisplayID monitor_to_display_id(HMONITOR handle)
55 return (CGDirectDisplayID)(UINT_PTR)handle;
59 static BOOL get_display_device_reg_key(char *key, unsigned len)
61 static const char display_device_guid_prop[] = "__wine_display_device_guid";
62 static const char video_path[] = "System\\CurrentControlSet\\Control\\Video\\{";
63 static const char display0[] = "}\\0000";
64 ATOM guid_atom;
66 assert(len >= sizeof(video_path) + sizeof(display0) + 40);
68 guid_atom = HandleToULong(GetPropA(GetDesktopWindow(), display_device_guid_prop));
69 if (!guid_atom) return FALSE;
71 memcpy(key, video_path, sizeof(video_path));
73 if (!GlobalGetAtomNameA(guid_atom, key + strlen(key), 40))
74 return FALSE;
76 strcat(key, display0);
78 TRACE("display device key %s\n", wine_dbgstr_a(key));
79 return TRUE;
83 static BOOL read_registry_settings(DEVMODEW *dm)
85 char wine_mac_reg_key[128];
86 HKEY hkey;
87 DWORD type, size;
88 BOOL ret = TRUE;
90 dm->dmFields = 0;
92 if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
93 return FALSE;
95 if (RegOpenKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, KEY_READ, &hkey))
96 return FALSE;
98 #define query_value(name, data) \
99 size = sizeof(DWORD); \
100 if (RegQueryValueExA(hkey, name, 0, &type, (LPBYTE)(data), &size) || \
101 type != REG_DWORD || size != sizeof(DWORD)) \
102 ret = FALSE
104 query_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
105 dm->dmFields |= DM_BITSPERPEL;
106 query_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
107 dm->dmFields |= DM_PELSWIDTH;
108 query_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
109 dm->dmFields |= DM_PELSHEIGHT;
110 query_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
111 dm->dmFields |= DM_DISPLAYFREQUENCY;
112 query_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
113 dm->dmFields |= DM_DISPLAYFLAGS;
114 query_value("DefaultSettings.XPanning", &dm->dmPosition.x);
115 query_value("DefaultSettings.YPanning", &dm->dmPosition.y);
116 query_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
117 query_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
119 #undef query_value
121 RegCloseKey(hkey);
122 return ret;
126 static BOOL write_registry_settings(const DEVMODEW *dm)
128 char wine_mac_reg_key[128];
129 HKEY hkey;
130 BOOL ret = TRUE;
132 if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
133 return FALSE;
135 if (RegCreateKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, NULL,
136 REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL))
137 return FALSE;
139 #define set_value(name, data) \
140 if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
141 ret = FALSE
143 set_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
144 set_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
145 set_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
146 set_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
147 set_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
148 set_value("DefaultSettings.XPanning", &dm->dmPosition.x);
149 set_value("DefaultSettings.YPanning", &dm->dmPosition.y);
150 set_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
151 set_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
153 #undef set_value
155 RegCloseKey(hkey);
156 return ret;
160 static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode)
162 CFStringRef pixel_encoding;
163 int bits_per_pixel = 0;
165 pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
166 if (pixel_encoding)
168 if (CFEqual(pixel_encoding, CFSTR(kIO32BitFloatPixels)))
169 bits_per_pixel = 128;
170 else if (CFEqual(pixel_encoding, CFSTR(kIO16BitFloatPixels)))
171 bits_per_pixel = 64;
172 else if (CFEqual(pixel_encoding, CFSTR(kIO64BitDirectPixels)))
173 bits_per_pixel = 64;
174 else if (CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels)))
175 bits_per_pixel = 30;
176 else if (CFEqual(pixel_encoding, CFSTR(IO32BitDirectPixels)))
177 bits_per_pixel = 32;
178 else if (CFEqual(pixel_encoding, CFSTR(IO16BitDirectPixels)))
179 bits_per_pixel = 16;
180 else if (CFEqual(pixel_encoding, CFSTR(IO8BitIndexedPixels)))
181 bits_per_pixel = 8;
182 else if (CFEqual(pixel_encoding, CFSTR(IO4BitIndexedPixels)))
183 bits_per_pixel = 4;
184 else if (CFEqual(pixel_encoding, CFSTR(IO2BitIndexedPixels)))
185 bits_per_pixel = 2;
186 else if (CFEqual(pixel_encoding, CFSTR(IO1BitIndexedPixels)))
187 bits_per_pixel = 1;
189 CFRelease(pixel_encoding);
192 return bits_per_pixel;
196 static int get_default_bpp(void)
198 int ret;
200 EnterCriticalSection(&modes_section);
202 if (!default_mode_bpp)
204 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
205 if (mode)
207 default_mode_bpp = display_mode_bits_per_pixel(mode);
208 CFRelease(mode);
211 if (!default_mode_bpp)
212 default_mode_bpp = 32;
215 ret = default_mode_bpp;
217 LeaveCriticalSection(&modes_section);
219 TRACE(" -> %d\n", ret);
220 return ret;
224 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
225 static CFDictionaryRef create_mode_dict(CGDisplayModeRef display_mode)
227 CFDictionaryRef ret;
228 SInt32 io_flags = CGDisplayModeGetIOFlags(display_mode);
229 SInt64 width = CGDisplayModeGetWidth(display_mode);
230 SInt64 height = CGDisplayModeGetHeight(display_mode);
231 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
232 CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
233 CFNumberRef cf_io_flags, cf_width, cf_height, cf_refresh;
235 io_flags &= kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeInterlacedFlag |
236 kDisplayModeStretchedFlag | kDisplayModeTelevisionFlag;
237 cf_io_flags = CFNumberCreate(NULL, kCFNumberSInt32Type, &io_flags);
238 cf_width = CFNumberCreate(NULL, kCFNumberSInt64Type, &width);
239 cf_height = CFNumberCreate(NULL, kCFNumberSInt64Type, &height);
240 cf_refresh = CFNumberCreate(NULL, kCFNumberDoubleType, &refresh_rate);
243 static const CFStringRef keys[] = {
244 CFSTR("io_flags"),
245 CFSTR("width"),
246 CFSTR("height"),
247 CFSTR("pixel_encoding"),
248 CFSTR("refresh_rate"),
250 const void* values[sizeof(keys) / sizeof(keys[0])] = {
251 cf_io_flags,
252 cf_width,
253 cf_height,
254 pixel_encoding,
255 cf_refresh,
258 ret = CFDictionaryCreate(NULL, (const void**)keys, (const void**)values, sizeof(keys) / sizeof(keys[0]),
259 &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
262 CFRelease(pixel_encoding);
263 CFRelease(cf_io_flags);
264 CFRelease(cf_width);
265 CFRelease(cf_height);
266 CFRelease(cf_refresh);
268 return ret;
270 #endif
273 /***********************************************************************
274 * copy_display_modes
276 * Wrapper around CGDisplayCopyAllDisplayModes() to include additional
277 * modes on Retina-capable systems, but filter those which would confuse
278 * Windows apps (basically duplicates at different DPIs).
280 * For example, some Retina Macs support a 1920x1200 mode, but it's not
281 * returned from CGDisplayCopyAllDisplayModes() without special options.
282 * This is especially bad if that's the user's default mode, since then
283 * no "available" mode matches the initial settings.
285 static CFArrayRef copy_display_modes(CGDirectDisplayID display)
287 CFArrayRef modes = NULL;
289 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
290 if (&kCGDisplayShowDuplicateLowResolutionModes != NULL &&
291 CGDisplayModeGetPixelWidth != NULL && CGDisplayModeGetPixelHeight != NULL)
293 CFDictionaryRef options;
294 CFMutableDictionaryRef modes_by_size;
295 CFIndex i, count;
296 CGDisplayModeRef* mode_array;
298 options = CFDictionaryCreate(NULL, (const void**)&kCGDisplayShowDuplicateLowResolutionModes,
299 (const void**)&kCFBooleanTrue, 1, &kCFTypeDictionaryKeyCallBacks,
300 &kCFTypeDictionaryValueCallBacks);
302 modes = CGDisplayCopyAllDisplayModes(display, options);
303 if (options)
304 CFRelease(options);
305 if (!modes)
306 return NULL;
308 modes_by_size = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
309 count = CFArrayGetCount(modes);
310 for (i = 0; i < count; i++)
312 BOOL better = TRUE;
313 CGDisplayModeRef new_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
314 uint32_t new_flags = CGDisplayModeGetIOFlags(new_mode);
315 CFStringRef pixel_encoding;
316 size_t width_points;
317 size_t height_points;
318 CFDictionaryRef key;
319 CGDisplayModeRef old_mode;
321 if (!(new_flags & kDisplayModeDefaultFlag) && (pixel_encoding = CGDisplayModeCopyPixelEncoding(new_mode)))
323 BOOL bpp30 = CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels));
324 CFRelease(pixel_encoding);
325 if (bpp30)
327 /* This is an odd pixel encoding. It seems it's only returned
328 when using kCGDisplayShowDuplicateLowResolutionModes. It's
329 32bpp in terms of the actual raster layout, but it's 10
330 bits per component. I think that no Windows program is
331 likely to need it and they will probably be confused by it.
332 Skip it. */
333 continue;
337 width_points = CGDisplayModeGetWidth(new_mode);
338 height_points = CGDisplayModeGetHeight(new_mode);
339 key = create_mode_dict(new_mode);
340 old_mode = (CGDisplayModeRef)CFDictionaryGetValue(modes_by_size, key);
342 if (old_mode)
344 uint32_t old_flags = CGDisplayModeGetIOFlags(old_mode);
346 /* If a given mode is the user's default, then always list it in preference to any similar
347 modes that may exist. */
348 if ((new_flags & kDisplayModeDefaultFlag) && !(old_flags & kDisplayModeDefaultFlag))
349 better = TRUE;
350 else if (!(new_flags & kDisplayModeDefaultFlag) && (old_flags & kDisplayModeDefaultFlag))
351 better = FALSE;
352 else
354 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
355 is scaled. */
356 size_t new_width_pixels = CGDisplayModeGetPixelWidth(new_mode);
357 size_t new_height_pixels = CGDisplayModeGetPixelHeight(new_mode);
358 size_t old_width_pixels = CGDisplayModeGetPixelWidth(old_mode);
359 size_t old_height_pixels = CGDisplayModeGetPixelHeight(old_mode);
360 BOOL new_size_same = (new_width_pixels == width_points && new_height_pixels == height_points);
361 BOOL old_size_same = (old_width_pixels == width_points && old_height_pixels == height_points);
363 if (new_size_same && !old_size_same)
364 better = TRUE;
365 else if (!new_size_same && old_size_same)
366 better = FALSE;
367 else
369 /* Otherwise, prefer the mode with the smaller pixel size. */
370 if (old_width_pixels < new_width_pixels || old_height_pixels < new_height_pixels)
371 better = FALSE;
376 if (better)
377 CFDictionarySetValue(modes_by_size, key, new_mode);
379 CFRelease(key);
382 CFRelease(modes);
384 count = CFDictionaryGetCount(modes_by_size);
385 mode_array = HeapAlloc(GetProcessHeap(), 0, count * sizeof(mode_array[0]));
386 CFDictionaryGetKeysAndValues(modes_by_size, NULL, (const void **)mode_array);
387 modes = CFArrayCreate(NULL, (const void **)mode_array, count, &kCFTypeArrayCallBacks);
388 HeapFree(GetProcessHeap(), 0, mode_array);
389 CFRelease(modes_by_size);
391 else
392 #endif
393 modes = CGDisplayCopyAllDisplayModes(display, NULL);
395 return modes;
399 /***********************************************************************
400 * ChangeDisplaySettingsEx (MACDRV.@)
403 LONG CDECL macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
404 HWND hwnd, DWORD flags, LPVOID lpvoid)
406 LONG ret = DISP_CHANGE_BADMODE;
407 int bpp;
408 DEVMODEW dm;
409 BOOL def_mode = TRUE;
410 struct macdrv_display *displays;
411 int num_displays;
412 CFArrayRef display_modes;
413 CFIndex count, i, safe, best;
414 CGDisplayModeRef best_display_mode;
415 uint32_t best_io_flags;
417 TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid);
419 if (devmode)
421 /* this is the minimal dmSize that XP accepts */
422 if (devmode->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
423 return DISP_CHANGE_FAILED;
425 if (devmode->dmSize >= FIELD_OFFSET(DEVMODEW, dmFields) + sizeof(devmode->dmFields))
427 if (((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel) ||
428 ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth) ||
429 ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight) ||
430 ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency))
431 def_mode = FALSE;
435 if (def_mode)
437 if (!macdrv_EnumDisplaySettingsEx(devname, ENUM_REGISTRY_SETTINGS, &dm, 0))
439 ERR("Default mode not found!\n");
440 return DISP_CHANGE_BADMODE;
443 TRACE("Return to original display mode\n");
444 devmode = &dm;
447 if ((devmode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
449 WARN("devmode doesn't specify the resolution: %04x\n", devmode->dmFields);
450 return DISP_CHANGE_BADMODE;
453 if (macdrv_get_displays(&displays, &num_displays))
454 return DISP_CHANGE_FAILED;
456 display_modes = copy_display_modes(displays[0].displayID);
457 if (!display_modes)
459 macdrv_free_displays(displays);
460 return DISP_CHANGE_FAILED;
463 bpp = get_default_bpp();
464 if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel != bpp)
465 TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, devmode->dmBitsPerPel);
467 TRACE("looking for %dx%dx%dbpp @%d Hz",
468 (devmode->dmFields & DM_PELSWIDTH ? devmode->dmPelsWidth : 0),
469 (devmode->dmFields & DM_PELSHEIGHT ? devmode->dmPelsHeight : 0),
470 bpp,
471 (devmode->dmFields & DM_DISPLAYFREQUENCY ? devmode->dmDisplayFrequency : 0));
472 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
473 TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un");
474 if (devmode->dmFields & DM_DISPLAYFLAGS)
475 TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
476 TRACE("\n");
478 safe = -1;
479 best_display_mode = NULL;
480 count = CFArrayGetCount(display_modes);
481 for (i = 0; i < count; i++)
483 CGDisplayModeRef display_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(display_modes, i);
484 uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
485 int mode_bpp = display_mode_bits_per_pixel(display_mode);
486 size_t width = CGDisplayModeGetWidth(display_mode);
487 size_t height = CGDisplayModeGetHeight(display_mode);
489 if (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag))
490 continue;
492 safe++;
494 if (bpp != mode_bpp)
495 continue;
497 if (devmode->dmFields & DM_PELSWIDTH)
499 if (devmode->dmPelsWidth != width)
500 continue;
502 if (devmode->dmFields & DM_PELSHEIGHT)
504 if (devmode->dmPelsHeight != height)
505 continue;
507 if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != 0)
509 double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
510 if (!refresh_rate)
511 refresh_rate = 60;
512 if (devmode->dmDisplayFrequency != (DWORD)refresh_rate)
513 continue;
515 if (devmode->dmFields & DM_DISPLAYFLAGS)
517 if (!(devmode->dmDisplayFlags & DM_INTERLACED) != !(io_flags & kDisplayModeInterlacedFlag))
518 continue;
520 else if (best_display_mode)
522 if (io_flags & kDisplayModeInterlacedFlag && !(best_io_flags & kDisplayModeInterlacedFlag))
523 continue;
524 else if (!(io_flags & kDisplayModeInterlacedFlag) && best_io_flags & kDisplayModeInterlacedFlag)
525 goto better;
527 if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
529 if (!(devmode->dmDisplayFixedOutput == DMDFO_STRETCH) != !(io_flags & kDisplayModeStretchedFlag))
530 continue;
532 else if (best_display_mode)
534 if (io_flags & kDisplayModeStretchedFlag && !(best_io_flags & kDisplayModeStretchedFlag))
535 continue;
536 else if (!(io_flags & kDisplayModeStretchedFlag) && best_io_flags & kDisplayModeStretchedFlag)
537 goto better;
540 if (best_display_mode)
541 continue;
543 better:
544 best_display_mode = display_mode;
545 best = safe;
546 best_io_flags = io_flags;
549 if (best_display_mode)
551 /* we have a valid mode */
552 TRACE("Requested display settings match mode %ld\n", best);
554 if ((flags & CDS_UPDATEREGISTRY) && !write_registry_settings(devmode))
556 WARN("Failed to update registry\n");
557 ret = DISP_CHANGE_NOTUPDATED;
559 else if (flags & (CDS_TEST | CDS_NORESET))
560 ret = DISP_CHANGE_SUCCESSFUL;
561 else if (macdrv_set_display_mode(&displays[0], best_display_mode))
563 int mode_bpp = display_mode_bits_per_pixel(best_display_mode);
564 size_t width = CGDisplayModeGetWidth(best_display_mode);
565 size_t height = CGDisplayModeGetHeight(best_display_mode);
567 SendMessageW(GetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
568 MAKELPARAM(width, height));
569 ret = DISP_CHANGE_SUCCESSFUL;
571 else
573 WARN("Failed to set display mode\n");
574 ret = DISP_CHANGE_FAILED;
577 else
579 /* no valid modes found */
580 ERR("No matching mode found %ux%ux%d @%u!\n", devmode->dmPelsWidth, devmode->dmPelsHeight,
581 bpp, devmode->dmDisplayFrequency);
584 CFRelease(display_modes);
585 macdrv_free_displays(displays);
587 return ret;
591 /***********************************************************************
592 * EnumDisplayMonitors (MACDRV.@)
594 BOOL CDECL macdrv_EnumDisplayMonitors(HDC hdc, LPRECT rect, MONITORENUMPROC proc, LPARAM lparam)
596 struct macdrv_display *displays;
597 int num_displays;
598 int i;
599 BOOL ret = TRUE;
601 TRACE("%p, %s, %p, %#lx\n", hdc, wine_dbgstr_rect(rect), proc, lparam);
603 if (hdc)
605 POINT origin;
606 RECT limit;
608 if (!GetDCOrgEx(hdc, &origin)) return FALSE;
609 if (GetClipBox(hdc, &limit) == ERROR) return FALSE;
611 if (rect && !IntersectRect(&limit, &limit, rect)) return TRUE;
613 if (macdrv_get_displays(&displays, &num_displays))
614 return FALSE;
616 for (i = 0; i < num_displays; i++)
618 RECT monrect = rect_from_cgrect(displays[i].frame);
619 OffsetRect(&monrect, -origin.x, -origin.y);
620 if (IntersectRect(&monrect, &monrect, &limit))
622 HMONITOR monitor = display_id_to_monitor(displays[i].displayID);
623 TRACE("monitor %d handle %p @ %s\n", i, monitor, wine_dbgstr_rect(&monrect));
624 if (!proc(monitor, hdc, &monrect, lparam))
626 ret = FALSE;
627 break;
632 else
634 if (macdrv_get_displays(&displays, &num_displays))
635 return FALSE;
637 for (i = 0; i < num_displays; i++)
639 RECT monrect = rect_from_cgrect(displays[i].frame);
640 RECT unused;
641 if (!rect || IntersectRect(&unused, &monrect, rect))
643 HMONITOR monitor = display_id_to_monitor(displays[i].displayID);
644 TRACE("monitor %d handle %p @ %s\n", i, monitor, wine_dbgstr_rect(&monrect));
645 if (!proc(monitor, 0, &monrect, lparam))
647 ret = FALSE;
648 break;
654 macdrv_free_displays(displays);
656 return ret;
660 /***********************************************************************
661 * EnumDisplaySettingsEx (MACDRV.@)
664 BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode,
665 LPDEVMODEW devmode, DWORD flags)
667 static const WCHAR dev_name[CCHDEVICENAME] =
668 { 'W','i','n','e',' ','M','a','c',' ','d','r','i','v','e','r',0 };
669 struct macdrv_display *displays = NULL;
670 int num_displays;
671 CGDisplayModeRef display_mode;
672 int display_mode_bpp;
673 BOOL synthesized = FALSE;
674 double rotation;
675 uint32_t io_flags;
677 TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname), mode, devmode, devmode->dmSize, flags);
679 memcpy(devmode->dmDeviceName, dev_name, sizeof(dev_name));
680 devmode->dmSpecVersion = DM_SPECVERSION;
681 devmode->dmDriverVersion = DM_SPECVERSION;
682 devmode->dmSize = FIELD_OFFSET(DEVMODEW, dmICMMethod);
683 devmode->dmDriverExtra = 0;
684 memset(&devmode->dmFields, 0, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields));
686 if (mode == ENUM_REGISTRY_SETTINGS)
688 TRACE("mode %d (registry) -- getting default mode\n", mode);
689 return read_registry_settings(devmode);
692 if (macdrv_get_displays(&displays, &num_displays))
693 goto failed;
695 if (mode == ENUM_CURRENT_SETTINGS)
697 TRACE("mode %d (current) -- getting current mode\n", mode);
698 display_mode = CGDisplayCopyDisplayMode(displays[0].displayID);
699 display_mode_bpp = display_mode_bits_per_pixel(display_mode);
701 else
703 DWORD count, i;
705 EnterCriticalSection(&modes_section);
707 if (mode == 0 || !modes)
709 if (modes) CFRelease(modes);
710 modes = copy_display_modes(displays[0].displayID);
711 modes_has_8bpp = modes_has_16bpp = FALSE;
713 if (modes)
715 count = CFArrayGetCount(modes);
716 for (i = 0; i < count && !(modes_has_8bpp && modes_has_16bpp); i++)
718 CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
719 int bpp = display_mode_bits_per_pixel(mode);
720 if (bpp == 8)
721 modes_has_8bpp = TRUE;
722 else if (bpp == 16)
723 modes_has_16bpp = TRUE;
728 display_mode = NULL;
729 if (modes)
731 int default_bpp = get_default_bpp();
732 DWORD seen_modes = 0;
734 count = CFArrayGetCount(modes);
735 for (i = 0; i < count; i++)
737 CGDisplayModeRef candidate = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
739 io_flags = CGDisplayModeGetIOFlags(candidate);
740 if (!(flags & EDS_RAWMODE) &&
741 (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag)))
742 continue;
744 seen_modes++;
745 if (seen_modes > mode)
747 display_mode = (CGDisplayModeRef)CFRetain(candidate);
748 display_mode_bpp = display_mode_bits_per_pixel(display_mode);
749 break;
752 /* We only synthesize modes from those having the default bpp. */
753 if (display_mode_bits_per_pixel(candidate) != default_bpp)
754 continue;
756 if (!modes_has_8bpp)
758 seen_modes++;
759 if (seen_modes > mode)
761 display_mode = (CGDisplayModeRef)CFRetain(candidate);
762 display_mode_bpp = 8;
763 synthesized = TRUE;
764 break;
768 if (!modes_has_16bpp)
770 seen_modes++;
771 if (seen_modes > mode)
773 display_mode = (CGDisplayModeRef)CFRetain(candidate);
774 display_mode_bpp = 16;
775 synthesized = TRUE;
776 break;
782 LeaveCriticalSection(&modes_section);
785 if (!display_mode)
786 goto failed;
788 /* We currently only report modes for the primary display, so it's at (0, 0). */
789 devmode->dmPosition.x = 0;
790 devmode->dmPosition.y = 0;
791 devmode->dmFields |= DM_POSITION;
793 rotation = CGDisplayRotation(displays[0].displayID);
794 devmode->dmDisplayOrientation = ((int)((rotation / 90) + 0.5)) % 4;
795 devmode->dmFields |= DM_DISPLAYORIENTATION;
797 io_flags = CGDisplayModeGetIOFlags(display_mode);
798 if (io_flags & kDisplayModeStretchedFlag)
799 devmode->dmDisplayFixedOutput = DMDFO_STRETCH;
800 else
801 devmode->dmDisplayFixedOutput = DMDFO_CENTER;
802 devmode->dmFields |= DM_DISPLAYFIXEDOUTPUT;
804 devmode->dmBitsPerPel = display_mode_bpp;
805 if (devmode->dmBitsPerPel)
806 devmode->dmFields |= DM_BITSPERPEL;
808 devmode->dmPelsWidth = CGDisplayModeGetWidth(display_mode);
809 devmode->dmPelsHeight = CGDisplayModeGetHeight(display_mode);
810 devmode->dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
812 devmode->dmDisplayFlags = 0;
813 if (io_flags & kDisplayModeInterlacedFlag)
814 devmode->dmDisplayFlags |= DM_INTERLACED;
815 devmode->dmFields |= DM_DISPLAYFLAGS;
817 devmode->dmDisplayFrequency = CGDisplayModeGetRefreshRate(display_mode);
818 if (!devmode->dmDisplayFrequency)
819 devmode->dmDisplayFrequency = 60;
820 devmode->dmFields |= DM_DISPLAYFREQUENCY;
822 CFRelease(display_mode);
823 macdrv_free_displays(displays);
825 TRACE("mode %d -- %dx%dx%dbpp @%d Hz", mode,
826 devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel,
827 devmode->dmDisplayFrequency);
828 if (devmode->dmDisplayOrientation)
829 TRACE(" rotated %u degrees", devmode->dmDisplayOrientation * 90);
830 if (devmode->dmDisplayFixedOutput == DMDFO_STRETCH)
831 TRACE(" stretched");
832 if (devmode->dmDisplayFlags & DM_INTERLACED)
833 TRACE(" interlaced");
834 if (synthesized)
835 TRACE(" (synthesized)");
836 TRACE("\n");
838 return TRUE;
840 failed:
841 TRACE("mode %d -- not present\n", mode);
842 if (displays) macdrv_free_displays(displays);
843 SetLastError(ERROR_NO_MORE_FILES);
844 return FALSE;
848 /***********************************************************************
849 * GetDeviceGammaRamp (MACDRV.@)
851 BOOL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
853 BOOL ret = FALSE;
854 DDGAMMARAMP *r = ramp;
855 struct macdrv_display *displays;
856 int num_displays;
857 uint32_t mac_entries;
858 int win_entries = sizeof(r->red) / sizeof(r->red[0]);
859 CGGammaValue *red, *green, *blue;
860 CGError err;
861 int win_entry;
863 TRACE("dev %p ramp %p\n", dev, ramp);
865 if (macdrv_get_displays(&displays, &num_displays))
867 WARN("failed to get Mac displays\n");
868 return FALSE;
871 mac_entries = CGDisplayGammaTableCapacity(displays[0].displayID);
872 red = HeapAlloc(GetProcessHeap(), 0, mac_entries * sizeof(red[0]) * 3);
873 if (!red)
874 goto done;
875 green = red + mac_entries;
876 blue = green + mac_entries;
878 err = CGGetDisplayTransferByTable(displays[0].displayID, mac_entries, red, green,
879 blue, &mac_entries);
880 if (err != kCGErrorSuccess)
882 WARN("failed to get Mac gamma table: %d\n", err);
883 goto done;
886 if (mac_entries == win_entries)
888 for (win_entry = 0; win_entry < win_entries; win_entry++)
890 r->red[win_entry] = red[win_entry] * 65535 + 0.5;
891 r->green[win_entry] = green[win_entry] * 65535 + 0.5;
892 r->blue[win_entry] = blue[win_entry] * 65535 + 0.5;
895 else
897 for (win_entry = 0; win_entry < win_entries; win_entry++)
899 double mac_pos = win_entry * (mac_entries - 1) / (double)(win_entries - 1);
900 int mac_entry = mac_pos;
901 double red_value, green_value, blue_value;
903 if (mac_entry == mac_entries - 1)
905 red_value = red[mac_entry];
906 green_value = green[mac_entry];
907 blue_value = blue[mac_entry];
909 else
911 double distance = mac_pos - mac_entry;
913 red_value = red[mac_entry] * (1 - distance) + red[mac_entry + 1] * distance;
914 green_value = green[mac_entry] * (1 - distance) + green[mac_entry + 1] * distance;
915 blue_value = blue[mac_entry] * (1 - distance) + blue[mac_entry + 1] * distance;
918 r->red[win_entry] = red_value * 65535 + 0.5;
919 r->green[win_entry] = green_value * 65535 + 0.5;
920 r->blue[win_entry] = blue_value * 65535 + 0.5;
924 ret = TRUE;
926 done:
927 HeapFree(GetProcessHeap(), 0, red);
928 macdrv_free_displays(displays);
929 return ret;
933 /***********************************************************************
934 * GetMonitorInfo (MACDRV.@)
936 BOOL CDECL macdrv_GetMonitorInfo(HMONITOR monitor, LPMONITORINFO info)
938 static const WCHAR adapter_name[] = { '\\','\\','.','\\','D','I','S','P','L','A','Y','1',0 };
939 struct macdrv_display *displays;
940 int num_displays;
941 CGDirectDisplayID display_id;
942 int i;
944 TRACE("%p, %p\n", monitor, info);
946 if (macdrv_get_displays(&displays, &num_displays))
948 ERR("couldn't get display list\n");
949 SetLastError(ERROR_GEN_FAILURE);
950 return FALSE;
953 display_id = monitor_to_display_id(monitor);
954 for (i = 0; i < num_displays; i++)
956 if (displays[i].displayID == display_id)
957 break;
960 if (i < num_displays)
962 info->rcMonitor = rect_from_cgrect(displays[i].frame);
963 info->rcWork = rect_from_cgrect(displays[i].work_frame);
965 info->dwFlags = (i == 0) ? MONITORINFOF_PRIMARY : 0;
967 if (info->cbSize >= sizeof(MONITORINFOEXW))
968 lstrcpyW(((MONITORINFOEXW*)info)->szDevice, adapter_name);
970 TRACE(" -> rcMonitor %s rcWork %s dwFlags %08x\n", wine_dbgstr_rect(&info->rcMonitor),
971 wine_dbgstr_rect(&info->rcWork), info->dwFlags);
973 else
975 ERR("invalid monitor handle\n");
976 SetLastError(ERROR_INVALID_HANDLE);
979 macdrv_free_displays(displays);
980 return (i < num_displays);
984 /***********************************************************************
985 * SetDeviceGammaRamp (MACDRV.@)
987 BOOL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
989 DDGAMMARAMP *r = ramp;
990 struct macdrv_display *displays;
991 int num_displays;
992 int win_entries = sizeof(r->red) / sizeof(r->red[0]);
993 CGGammaValue *red, *green, *blue;
994 int i;
995 CGError err = kCGErrorFailure;
997 TRACE("dev %p ramp %p\n", dev, ramp);
999 if (!allow_set_gamma)
1001 TRACE("disallowed by registry setting\n");
1002 return FALSE;
1005 if (macdrv_get_displays(&displays, &num_displays))
1007 WARN("failed to get Mac displays\n");
1008 return FALSE;
1011 red = HeapAlloc(GetProcessHeap(), 0, win_entries * sizeof(red[0]) * 3);
1012 if (!red)
1013 goto done;
1014 green = red + win_entries;
1015 blue = green + win_entries;
1017 for (i = 0; i < win_entries; i++)
1019 red[i] = r->red[i] / 65535.0;
1020 green[i] = r->green[i] / 65535.0;
1021 blue[i] = r->blue[i] / 65535.0;
1024 err = CGSetDisplayTransferByTable(displays[0].displayID, win_entries, red, green, blue);
1025 if (err != kCGErrorSuccess)
1026 WARN("failed to set display gamma table: %d\n", err);
1028 done:
1029 HeapFree(GetProcessHeap(), 0, red);
1030 macdrv_free_displays(displays);
1031 return (err == kCGErrorSuccess);
1035 /***********************************************************************
1036 * macdrv_displays_changed
1038 * Handler for DISPLAYS_CHANGED events.
1040 void macdrv_displays_changed(const macdrv_event *event)
1042 HWND hwnd = GetDesktopWindow();
1044 /* A system display change will get delivered to all GUI-attached threads,
1045 so the desktop-window-owning thread will get it and all others should
1046 ignore it. A synthesized display change event due to activation
1047 will only get delivered to the activated process. So, it needs to
1048 process it (by sending it to the desktop window). */
1049 if (event->displays_changed.activating ||
1050 GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId())
1052 CGDirectDisplayID mainDisplay = CGMainDisplayID();
1053 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(mainDisplay);
1054 size_t width = CGDisplayModeGetWidth(mode);
1055 size_t height = CGDisplayModeGetHeight(mode);
1056 int mode_bpp = display_mode_bits_per_pixel(mode);
1058 CGDisplayModeRelease(mode);
1059 SendMessageW(hwnd, WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
1060 MAKELPARAM(width, height));