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
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
=
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";
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))
76 strcat(key
, display0
);
78 TRACE("display device key %s\n", wine_dbgstr_a(key
));
83 static BOOL
read_registry_settings(DEVMODEW
*dm
)
85 char wine_mac_reg_key
[128];
92 if (!get_display_device_reg_key(wine_mac_reg_key
, sizeof(wine_mac_reg_key
)))
95 if (RegOpenKeyExA(HKEY_CURRENT_CONFIG
, wine_mac_reg_key
, 0, KEY_READ
, &hkey
))
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)) \
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
);
126 static BOOL
write_registry_settings(const DEVMODEW
*dm
)
128 char wine_mac_reg_key
[128];
132 if (!get_display_device_reg_key(wine_mac_reg_key
, sizeof(wine_mac_reg_key
)))
135 if (RegCreateKeyExA(HKEY_CURRENT_CONFIG
, wine_mac_reg_key
, 0, NULL
,
136 REG_OPTION_VOLATILE
, KEY_WRITE
, NULL
, &hkey
, NULL
))
139 #define set_value(name, data) \
140 if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
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
);
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
);
168 if (CFEqual(pixel_encoding
, CFSTR(kIO32BitFloatPixels
)))
169 bits_per_pixel
= 128;
170 else if (CFEqual(pixel_encoding
, CFSTR(kIO16BitFloatPixels
)))
172 else if (CFEqual(pixel_encoding
, CFSTR(kIO64BitDirectPixels
)))
174 else if (CFEqual(pixel_encoding
, CFSTR(kIO30BitDirectPixels
)))
176 else if (CFEqual(pixel_encoding
, CFSTR(IO32BitDirectPixels
)))
178 else if (CFEqual(pixel_encoding
, CFSTR(IO16BitDirectPixels
)))
180 else if (CFEqual(pixel_encoding
, CFSTR(IO8BitIndexedPixels
)))
182 else if (CFEqual(pixel_encoding
, CFSTR(IO4BitIndexedPixels
)))
184 else if (CFEqual(pixel_encoding
, CFSTR(IO2BitIndexedPixels
)))
186 else if (CFEqual(pixel_encoding
, CFSTR(IO1BitIndexedPixels
)))
189 CFRelease(pixel_encoding
);
192 return bits_per_pixel
;
196 static int get_default_bpp(void)
200 EnterCriticalSection(&modes_section
);
202 if (!default_mode_bpp
)
204 CGDisplayModeRef mode
= CGDisplayCopyDisplayMode(kCGDirectMainDisplay
);
207 default_mode_bpp
= display_mode_bits_per_pixel(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
);
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
)
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
[] = {
247 CFSTR("pixel_encoding"),
248 CFSTR("refresh_rate"),
250 const void* values
[sizeof(keys
) / sizeof(keys
[0])] = {
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
);
265 CFRelease(cf_height
);
266 CFRelease(cf_refresh
);
273 /***********************************************************************
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
;
296 CGDisplayModeRef
* mode_array
;
298 options
= CFDictionaryCreate(NULL
, (const void**)&kCGDisplayShowDuplicateLowResolutionModes
,
299 (const void**)&kCFBooleanTrue
, 1, &kCFTypeDictionaryKeyCallBacks
,
300 &kCFTypeDictionaryValueCallBacks
);
302 modes
= CGDisplayCopyAllDisplayModes(display
, options
);
308 modes_by_size
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
309 count
= CFArrayGetCount(modes
);
310 for (i
= 0; i
< count
; i
++)
313 CGDisplayModeRef new_mode
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(modes
, i
);
314 uint32_t new_flags
= CGDisplayModeGetIOFlags(new_mode
);
315 CFStringRef pixel_encoding
;
317 size_t height_points
;
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
);
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.
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
);
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
))
350 else if (!(new_flags
& kDisplayModeDefaultFlag
) && (old_flags
& kDisplayModeDefaultFlag
))
354 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
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
)
365 else if (!new_size_same
&& old_size_same
)
369 /* Otherwise, prefer the mode with the smaller pixel size. */
370 if (old_width_pixels
< new_width_pixels
|| old_height_pixels
< new_height_pixels
)
377 CFDictionarySetValue(modes_by_size
, key
, new_mode
);
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
);
393 modes
= CGDisplayCopyAllDisplayModes(display
, NULL
);
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
;
409 BOOL def_mode
= TRUE
;
410 struct macdrv_display
*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
);
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
))
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");
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
);
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),
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-");
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
))
497 if (devmode
->dmFields
& DM_PELSWIDTH
)
499 if (devmode
->dmPelsWidth
!= width
)
502 if (devmode
->dmFields
& DM_PELSHEIGHT
)
504 if (devmode
->dmPelsHeight
!= height
)
507 if ((devmode
->dmFields
& DM_DISPLAYFREQUENCY
) && devmode
->dmDisplayFrequency
!= 0)
509 double refresh_rate
= CGDisplayModeGetRefreshRate(display_mode
);
512 if (devmode
->dmDisplayFrequency
!= (DWORD
)refresh_rate
)
515 if (devmode
->dmFields
& DM_DISPLAYFLAGS
)
517 if (!(devmode
->dmDisplayFlags
& DM_INTERLACED
) != !(io_flags
& kDisplayModeInterlacedFlag
))
520 else if (best_display_mode
)
522 if (io_flags
& kDisplayModeInterlacedFlag
&& !(best_io_flags
& kDisplayModeInterlacedFlag
))
524 else if (!(io_flags
& kDisplayModeInterlacedFlag
) && best_io_flags
& kDisplayModeInterlacedFlag
)
527 if (devmode
->dmFields
& DM_DISPLAYFIXEDOUTPUT
)
529 if (!(devmode
->dmDisplayFixedOutput
== DMDFO_STRETCH
) != !(io_flags
& kDisplayModeStretchedFlag
))
532 else if (best_display_mode
)
534 if (io_flags
& kDisplayModeStretchedFlag
&& !(best_io_flags
& kDisplayModeStretchedFlag
))
536 else if (!(io_flags
& kDisplayModeStretchedFlag
) && best_io_flags
& kDisplayModeStretchedFlag
)
540 if (best_display_mode
)
544 best_display_mode
= display_mode
;
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
;
573 WARN("Failed to set display mode\n");
574 ret
= DISP_CHANGE_FAILED
;
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
);
591 /***********************************************************************
592 * EnumDisplayMonitors (MACDRV.@)
594 BOOL CDECL
macdrv_EnumDisplayMonitors(HDC hdc
, LPRECT rect
, MONITORENUMPROC proc
, LPARAM lparam
)
596 struct macdrv_display
*displays
;
601 TRACE("%p, %s, %p, %#lx\n", hdc
, wine_dbgstr_rect(rect
), proc
, lparam
);
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
))
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
))
634 if (macdrv_get_displays(&displays
, &num_displays
))
637 for (i
= 0; i
< num_displays
; i
++)
639 RECT monrect
= rect_from_cgrect(displays
[i
].frame
);
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
))
654 macdrv_free_displays(displays
);
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
;
671 CGDisplayModeRef display_mode
;
672 int display_mode_bpp
;
673 BOOL synthesized
= FALSE
;
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
))
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
);
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
;
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
);
721 modes_has_8bpp
= TRUE
;
723 modes_has_16bpp
= TRUE
;
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
)))
745 if (seen_modes
> mode
)
747 display_mode
= (CGDisplayModeRef
)CFRetain(candidate
);
748 display_mode_bpp
= display_mode_bits_per_pixel(display_mode
);
752 /* We only synthesize modes from those having the default bpp. */
753 if (display_mode_bits_per_pixel(candidate
) != default_bpp
)
759 if (seen_modes
> mode
)
761 display_mode
= (CGDisplayModeRef
)CFRetain(candidate
);
762 display_mode_bpp
= 8;
768 if (!modes_has_16bpp
)
771 if (seen_modes
> mode
)
773 display_mode
= (CGDisplayModeRef
)CFRetain(candidate
);
774 display_mode_bpp
= 16;
782 LeaveCriticalSection(&modes_section
);
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
;
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
)
832 if (devmode
->dmDisplayFlags
& DM_INTERLACED
)
833 TRACE(" interlaced");
835 TRACE(" (synthesized)");
841 TRACE("mode %d -- not present\n", mode
);
842 if (displays
) macdrv_free_displays(displays
);
843 SetLastError(ERROR_NO_MORE_FILES
);
848 /***********************************************************************
849 * GetDeviceGammaRamp (MACDRV.@)
851 BOOL
macdrv_GetDeviceGammaRamp(PHYSDEV dev
, LPVOID ramp
)
854 DDGAMMARAMP
*r
= ramp
;
855 struct macdrv_display
*displays
;
857 uint32_t mac_entries
;
858 int win_entries
= sizeof(r
->red
) / sizeof(r
->red
[0]);
859 CGGammaValue
*red
, *green
, *blue
;
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");
871 mac_entries
= CGDisplayGammaTableCapacity(displays
[0].displayID
);
872 red
= HeapAlloc(GetProcessHeap(), 0, mac_entries
* sizeof(red
[0]) * 3);
875 green
= red
+ mac_entries
;
876 blue
= green
+ mac_entries
;
878 err
= CGGetDisplayTransferByTable(displays
[0].displayID
, mac_entries
, red
, green
,
880 if (err
!= kCGErrorSuccess
)
882 WARN("failed to get Mac gamma table: %d\n", err
);
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;
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
];
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;
927 HeapFree(GetProcessHeap(), 0, red
);
928 macdrv_free_displays(displays
);
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
;
941 CGDirectDisplayID display_id
;
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
);
953 display_id
= monitor_to_display_id(monitor
);
954 for (i
= 0; i
< num_displays
; i
++)
956 if (displays
[i
].displayID
== display_id
)
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
);
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
;
992 int win_entries
= sizeof(r
->red
) / sizeof(r
->red
[0]);
993 CGGammaValue
*red
, *green
, *blue
;
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");
1005 if (macdrv_get_displays(&displays
, &num_displays
))
1007 WARN("failed to get Mac displays\n");
1011 red
= HeapAlloc(GetProcessHeap(), 0, win_entries
* sizeof(red
[0]) * 3);
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
);
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
));