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 #include "wine/unicode.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(display
);
33 struct display_mode_descriptor
41 CFStringRef pixel_encoding
;
45 BOOL CDECL
macdrv_EnumDisplaySettingsEx(LPCWSTR devname
, DWORD mode
, LPDEVMODEW devmode
, DWORD flags
);
48 static const char initial_mode_key
[] = "Initial Display Mode";
49 static const WCHAR pixelencodingW
[] = {'P','i','x','e','l','E','n','c','o','d','i','n','g',0};
52 static CFArrayRef modes
;
53 static BOOL modes_has_8bpp
, modes_has_16bpp
;
54 static int default_mode_bpp
;
55 static CRITICAL_SECTION modes_section
;
56 static CRITICAL_SECTION_DEBUG critsect_debug
=
59 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
60 0, 0, { (DWORD_PTR
)(__FILE__
": modes_section") }
62 static CRITICAL_SECTION modes_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
64 static BOOL inited_original_display_mode
;
67 static inline HMONITOR
display_id_to_monitor(CGDirectDisplayID display_id
)
69 return (HMONITOR
)(UINT_PTR
)display_id
;
72 static inline CGDirectDisplayID
monitor_to_display_id(HMONITOR handle
)
74 return (CGDirectDisplayID
)(UINT_PTR
)handle
;
78 static BOOL
get_display_device_reg_key(char *key
, unsigned len
)
80 static const char display_device_guid_prop
[] = "__wine_display_device_guid";
81 static const char video_path
[] = "System\\CurrentControlSet\\Control\\Video\\{";
82 static const char display0
[] = "}\\0000";
85 assert(len
>= sizeof(video_path
) + sizeof(display0
) + 40);
87 guid_atom
= HandleToULong(GetPropA(GetDesktopWindow(), display_device_guid_prop
));
88 if (!guid_atom
) return FALSE
;
90 memcpy(key
, video_path
, sizeof(video_path
));
92 if (!GlobalGetAtomNameA(guid_atom
, key
+ strlen(key
), 40))
95 strcat(key
, display0
);
97 TRACE("display device key %s\n", wine_dbgstr_a(key
));
102 static BOOL
read_registry_settings(DEVMODEW
*dm
)
104 char wine_mac_reg_key
[128];
111 if (!get_display_device_reg_key(wine_mac_reg_key
, sizeof(wine_mac_reg_key
)))
114 if (RegOpenKeyExA(HKEY_CURRENT_CONFIG
, wine_mac_reg_key
, 0, KEY_READ
, &hkey
))
117 #define query_value(name, data) \
118 size = sizeof(DWORD); \
119 if (RegQueryValueExA(hkey, name, 0, &type, (LPBYTE)(data), &size) || \
120 type != REG_DWORD || size != sizeof(DWORD)) \
123 query_value("DefaultSettings.BitsPerPel", &dm
->dmBitsPerPel
);
124 dm
->dmFields
|= DM_BITSPERPEL
;
125 query_value("DefaultSettings.XResolution", &dm
->dmPelsWidth
);
126 dm
->dmFields
|= DM_PELSWIDTH
;
127 query_value("DefaultSettings.YResolution", &dm
->dmPelsHeight
);
128 dm
->dmFields
|= DM_PELSHEIGHT
;
129 query_value("DefaultSettings.VRefresh", &dm
->dmDisplayFrequency
);
130 dm
->dmFields
|= DM_DISPLAYFREQUENCY
;
131 query_value("DefaultSettings.Flags", &dm
->dmDisplayFlags
);
132 dm
->dmFields
|= DM_DISPLAYFLAGS
;
133 query_value("DefaultSettings.XPanning", &dm
->dmPosition
.x
);
134 query_value("DefaultSettings.YPanning", &dm
->dmPosition
.y
);
135 query_value("DefaultSettings.Orientation", &dm
->dmDisplayOrientation
);
136 query_value("DefaultSettings.FixedOutput", &dm
->dmDisplayFixedOutput
);
145 static BOOL
write_registry_settings(const DEVMODEW
*dm
)
147 char wine_mac_reg_key
[128];
151 if (!get_display_device_reg_key(wine_mac_reg_key
, sizeof(wine_mac_reg_key
)))
154 if (RegCreateKeyExA(HKEY_CURRENT_CONFIG
, wine_mac_reg_key
, 0, NULL
,
155 REG_OPTION_VOLATILE
, KEY_WRITE
, NULL
, &hkey
, NULL
))
158 #define set_value(name, data) \
159 if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
162 set_value("DefaultSettings.BitsPerPel", &dm
->dmBitsPerPel
);
163 set_value("DefaultSettings.XResolution", &dm
->dmPelsWidth
);
164 set_value("DefaultSettings.YResolution", &dm
->dmPelsHeight
);
165 set_value("DefaultSettings.VRefresh", &dm
->dmDisplayFrequency
);
166 set_value("DefaultSettings.Flags", &dm
->dmDisplayFlags
);
167 set_value("DefaultSettings.XPanning", &dm
->dmPosition
.x
);
168 set_value("DefaultSettings.YPanning", &dm
->dmPosition
.y
);
169 set_value("DefaultSettings.Orientation", &dm
->dmDisplayOrientation
);
170 set_value("DefaultSettings.FixedOutput", &dm
->dmDisplayFixedOutput
);
179 static BOOL
write_display_settings(HKEY parent_hkey
, CGDirectDisplayID displayID
)
182 char display_key_name
[19];
184 CGDisplayModeRef display_mode
;
186 CFStringRef pixel_encoding
;
190 snprintf(display_key_name
, sizeof(display_key_name
), "Display 0x%08x", CGDisplayUnitNumber(displayID
));
191 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
192 if (RegCreateKeyExA(parent_hkey
, display_key_name
, 0, NULL
,
193 REG_OPTION_VOLATILE
, KEY_WRITE
, NULL
, &display_hkey
, NULL
))
196 display_mode
= CGDisplayCopyDisplayMode(displayID
);
200 val
= CGDisplayModeGetWidth(display_mode
);
201 if (RegSetValueExA(display_hkey
, "Width", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
203 val
= CGDisplayModeGetHeight(display_mode
);
204 if (RegSetValueExA(display_hkey
, "Height", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
206 val
= CGDisplayModeGetRefreshRate(display_mode
) * 100;
207 if (RegSetValueExA(display_hkey
, "RefreshRateTimes100", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
209 val
= CGDisplayModeGetIOFlags(display_mode
);
210 if (RegSetValueExA(display_hkey
, "IOFlags", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
213 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
214 if (CGDisplayModeGetPixelWidth
!= NULL
&& CGDisplayModeGetPixelHeight
!= NULL
)
216 val
= CGDisplayModeGetPixelWidth(display_mode
);
217 if (RegSetValueExA(display_hkey
, "PixelWidth", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
219 val
= CGDisplayModeGetPixelHeight(display_mode
);
220 if (RegSetValueExA(display_hkey
, "PixelHeight", 0, REG_DWORD
, (const BYTE
*)&val
, sizeof(val
)))
225 pixel_encoding
= CGDisplayModeCopyPixelEncoding(display_mode
);
226 len
= CFStringGetLength(pixel_encoding
);
227 buf
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
228 CFStringGetCharacters(pixel_encoding
, CFRangeMake(0, len
), (UniChar
*)buf
);
230 CFRelease(pixel_encoding
);
231 if (RegSetValueExW(display_hkey
, pixelencodingW
, 0, REG_SZ
, (const BYTE
*)buf
, (len
+ 1) * sizeof(WCHAR
)))
237 HeapFree(GetProcessHeap(), 0, buf
);
238 if (display_mode
) CGDisplayModeRelease(display_mode
);
239 RegCloseKey(display_hkey
);
241 RegDeleteKeyA(parent_hkey
, display_key_name
);
246 static void init_original_display_mode(void)
248 BOOL success
= FALSE
;
249 HKEY mac_driver_hkey
, parent_hkey
;
251 struct macdrv_display
*displays
= NULL
;
254 if (inited_original_display_mode
)
257 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver */
258 if (RegCreateKeyExA(HKEY_LOCAL_MACHINE
, "Software\\Wine\\Mac Driver", 0, NULL
,
259 0, KEY_ALL_ACCESS
, NULL
, &mac_driver_hkey
, NULL
))
262 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode */
263 if (RegCreateKeyExA(mac_driver_hkey
, initial_mode_key
, 0, NULL
,
264 REG_OPTION_VOLATILE
, KEY_WRITE
, NULL
, &parent_hkey
, &disposition
))
270 /* If we didn't create a new key, then it already existed. Something already stored
271 the initial display mode since Wine was started. We don't want to overwrite it. */
272 if (disposition
!= REG_CREATED_NEW_KEY
)
275 if (macdrv_get_displays(&displays
, &num_displays
))
278 for (i
= 0; i
< num_displays
; i
++)
280 if (!write_display_settings(parent_hkey
, displays
[i
].displayID
))
288 macdrv_free_displays(displays
);
289 RegCloseKey(parent_hkey
);
290 if (!success
&& parent_hkey
)
291 RegDeleteTreeA(mac_driver_hkey
, initial_mode_key
);
292 RegCloseKey(mac_driver_hkey
);
294 inited_original_display_mode
= TRUE
;
298 static BOOL
read_dword(HKEY hkey
, const char* name
, DWORD
* val
)
300 DWORD type
, size
= sizeof(*val
);
301 if (RegQueryValueExA(hkey
, name
, 0, &type
, (BYTE
*)val
, &size
) || type
!= REG_DWORD
|| size
!= sizeof(*val
))
307 static void free_display_mode_descriptor(struct display_mode_descriptor
* desc
)
311 if (desc
->pixel_encoding
)
312 CFRelease(desc
->pixel_encoding
);
313 HeapFree(GetProcessHeap(), 0, desc
);
318 static struct display_mode_descriptor
* create_original_display_mode_descriptor(CGDirectDisplayID displayID
)
320 static const char display_key_format
[] = "Software\\Wine\\Mac Driver\\Initial Display Mode\\Display 0x%08x";
321 struct display_mode_descriptor
* ret
= NULL
;
322 struct display_mode_descriptor
* desc
;
323 char display_key
[sizeof(display_key_format
) + 10];
327 WCHAR
* pixel_encoding
= NULL
, *end
;
329 init_original_display_mode();
331 snprintf(display_key
, sizeof(display_key
), display_key_format
, CGDisplayUnitNumber(displayID
));
332 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
333 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE
, display_key
, 0, KEY_READ
, &hkey
))
336 desc
= HeapAlloc(GetProcessHeap(), 0, sizeof(*desc
));
337 desc
->pixel_encoding
= NULL
;
339 if (!read_dword(hkey
, "Width", &desc
->width
) ||
340 !read_dword(hkey
, "Height", &desc
->height
) ||
341 !read_dword(hkey
, "RefreshRateTimes100", &refresh100
) ||
342 !read_dword(hkey
, "IOFlags", &desc
->io_flags
))
345 desc
->refresh
= refresh100
/ 100.0;
349 if (!read_dword(hkey
, "PixelWidth", &desc
->pixel_width
) ||
350 !read_dword(hkey
, "PixelHeight", &desc
->pixel_height
))
352 desc
->pixel_width
= desc
->width
;
353 desc
->pixel_height
= desc
->height
;
357 if (RegQueryValueExW(hkey
, pixelencodingW
, 0, &type
, NULL
, &size
) || type
!= REG_SZ
)
359 pixel_encoding
= HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
));
360 if (RegQueryValueExW(hkey
, pixelencodingW
, 0, &type
, (BYTE
*)pixel_encoding
, &size
) || type
!= REG_SZ
)
362 if ((end
= memchrW(pixel_encoding
, 0, size
)))
363 size
= end
- pixel_encoding
;
364 desc
->pixel_encoding
= CFStringCreateWithCharacters(NULL
, (const UniChar
*)pixel_encoding
, size
);
370 free_display_mode_descriptor(desc
);
371 HeapFree(GetProcessHeap(), 0, pixel_encoding
);
377 static BOOL
display_mode_matches_descriptor(CGDisplayModeRef mode
, const struct display_mode_descriptor
* desc
)
381 CFStringRef mode_pixel_encoding
;
386 if (CGDisplayModeGetWidth(mode
) != desc
->width
||
387 CGDisplayModeGetHeight(mode
) != desc
->height
)
390 mode_io_flags
= CGDisplayModeGetIOFlags(mode
);
391 if ((desc
->io_flags
^ mode_io_flags
) & (kDisplayModeValidFlag
| kDisplayModeSafeFlag
| kDisplayModeStretchedFlag
|
392 kDisplayModeInterlacedFlag
| kDisplayModeTelevisionFlag
))
395 mode_refresh
= CGDisplayModeGetRefreshRate(mode
);
398 if (fabs(desc
->refresh
- mode_refresh
) > 0.1)
401 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
402 if (CGDisplayModeGetPixelWidth
!= NULL
&& CGDisplayModeGetPixelHeight
!= NULL
)
404 if (CGDisplayModeGetPixelWidth(mode
) != desc
->pixel_width
||
405 CGDisplayModeGetPixelHeight(mode
) != desc
->pixel_height
)
410 if (CGDisplayModeGetWidth(mode
) != desc
->pixel_width
||
411 CGDisplayModeGetHeight(mode
) != desc
->pixel_height
)
414 mode_pixel_encoding
= CGDisplayModeCopyPixelEncoding(mode
);
415 if (!CFEqual(mode_pixel_encoding
, desc
->pixel_encoding
))
417 CFRelease(mode_pixel_encoding
);
420 CFRelease(mode_pixel_encoding
);
426 static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode
)
428 CFStringRef pixel_encoding
;
429 int bits_per_pixel
= 0;
431 pixel_encoding
= CGDisplayModeCopyPixelEncoding(display_mode
);
434 if (CFEqual(pixel_encoding
, CFSTR(kIO32BitFloatPixels
)))
435 bits_per_pixel
= 128;
436 else if (CFEqual(pixel_encoding
, CFSTR(kIO16BitFloatPixels
)))
438 else if (CFEqual(pixel_encoding
, CFSTR(kIO64BitDirectPixels
)))
440 else if (CFEqual(pixel_encoding
, CFSTR(kIO30BitDirectPixels
)))
442 else if (CFEqual(pixel_encoding
, CFSTR(IO32BitDirectPixels
)))
444 else if (CFEqual(pixel_encoding
, CFSTR(IO16BitDirectPixels
)))
446 else if (CFEqual(pixel_encoding
, CFSTR(IO8BitIndexedPixels
)))
448 else if (CFEqual(pixel_encoding
, CFSTR(IO4BitIndexedPixels
)))
450 else if (CFEqual(pixel_encoding
, CFSTR(IO2BitIndexedPixels
)))
452 else if (CFEqual(pixel_encoding
, CFSTR(IO1BitIndexedPixels
)))
455 CFRelease(pixel_encoding
);
458 return bits_per_pixel
;
462 static int get_default_bpp(void)
466 EnterCriticalSection(&modes_section
);
468 if (!default_mode_bpp
)
470 CGDisplayModeRef mode
= CGDisplayCopyDisplayMode(kCGDirectMainDisplay
);
473 default_mode_bpp
= display_mode_bits_per_pixel(mode
);
477 if (!default_mode_bpp
)
478 default_mode_bpp
= 32;
481 ret
= default_mode_bpp
;
483 LeaveCriticalSection(&modes_section
);
485 TRACE(" -> %d\n", ret
);
490 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
491 static CFDictionaryRef
create_mode_dict(CGDisplayModeRef display_mode
)
494 SInt32 io_flags
= CGDisplayModeGetIOFlags(display_mode
);
495 SInt64 width
= CGDisplayModeGetWidth(display_mode
);
496 SInt64 height
= CGDisplayModeGetHeight(display_mode
);
497 double refresh_rate
= CGDisplayModeGetRefreshRate(display_mode
);
498 CFStringRef pixel_encoding
= CGDisplayModeCopyPixelEncoding(display_mode
);
499 CFNumberRef cf_io_flags
, cf_width
, cf_height
, cf_refresh
;
501 io_flags
&= kDisplayModeValidFlag
| kDisplayModeSafeFlag
| kDisplayModeInterlacedFlag
|
502 kDisplayModeStretchedFlag
| kDisplayModeTelevisionFlag
;
503 cf_io_flags
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &io_flags
);
504 cf_width
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, &width
);
505 cf_height
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, &height
);
506 cf_refresh
= CFNumberCreate(NULL
, kCFNumberDoubleType
, &refresh_rate
);
509 static const CFStringRef keys
[] = {
513 CFSTR("pixel_encoding"),
514 CFSTR("refresh_rate"),
516 const void* values
[sizeof(keys
) / sizeof(keys
[0])] = {
524 ret
= CFDictionaryCreate(NULL
, (const void**)keys
, (const void**)values
, sizeof(keys
) / sizeof(keys
[0]),
525 &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
528 CFRelease(pixel_encoding
);
529 CFRelease(cf_io_flags
);
531 CFRelease(cf_height
);
532 CFRelease(cf_refresh
);
539 /***********************************************************************
542 * Wrapper around CGDisplayCopyAllDisplayModes() to include additional
543 * modes on Retina-capable systems, but filter those which would confuse
544 * Windows apps (basically duplicates at different DPIs).
546 * For example, some Retina Macs support a 1920x1200 mode, but it's not
547 * returned from CGDisplayCopyAllDisplayModes() without special options.
548 * This is especially bad if that's the user's default mode, since then
549 * no "available" mode matches the initial settings.
551 static CFArrayRef
copy_display_modes(CGDirectDisplayID display
)
553 CFArrayRef modes
= NULL
;
555 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
556 if (&kCGDisplayShowDuplicateLowResolutionModes
!= NULL
&&
557 CGDisplayModeGetPixelWidth
!= NULL
&& CGDisplayModeGetPixelHeight
!= NULL
)
559 CFDictionaryRef options
;
560 struct display_mode_descriptor
* desc
;
561 CFMutableDictionaryRef modes_by_size
;
563 CGDisplayModeRef
* mode_array
;
565 options
= CFDictionaryCreate(NULL
, (const void**)&kCGDisplayShowDuplicateLowResolutionModes
,
566 (const void**)&kCFBooleanTrue
, 1, &kCFTypeDictionaryKeyCallBacks
,
567 &kCFTypeDictionaryValueCallBacks
);
569 modes
= CGDisplayCopyAllDisplayModes(display
, options
);
575 desc
= create_original_display_mode_descriptor(display
);
577 modes_by_size
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
578 count
= CFArrayGetCount(modes
);
579 for (i
= 0; i
< count
; i
++)
582 CGDisplayModeRef new_mode
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(modes
, i
);
583 BOOL new_is_original
= display_mode_matches_descriptor(new_mode
, desc
);
584 CFDictionaryRef key
= create_mode_dict(new_mode
);
586 /* If a given mode is the user's default, then always list it in preference to any similar
587 modes that may exist. */
592 CFStringRef pixel_encoding
= CGDisplayModeCopyPixelEncoding(new_mode
);
593 CGDisplayModeRef old_mode
;
597 BOOL bpp30
= CFEqual(pixel_encoding
, CFSTR(kIO30BitDirectPixels
));
598 CFRelease(pixel_encoding
);
601 /* This is an odd pixel encoding. It seems it's only returned
602 when using kCGDisplayShowDuplicateLowResolutionModes. It's
603 32bpp in terms of the actual raster layout, but it's 10
604 bits per component. I think that no Windows program is
605 likely to need it and they will probably be confused by it.
612 old_mode
= (CGDisplayModeRef
)CFDictionaryGetValue(modes_by_size
, key
);
615 BOOL old_is_original
= display_mode_matches_descriptor(old_mode
, desc
);
621 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
623 size_t width_points
= CGDisplayModeGetWidth(new_mode
);
624 size_t height_points
= CGDisplayModeGetHeight(new_mode
);
625 size_t new_width_pixels
= CGDisplayModeGetPixelWidth(new_mode
);
626 size_t new_height_pixels
= CGDisplayModeGetPixelHeight(new_mode
);
627 size_t old_width_pixels
= CGDisplayModeGetPixelWidth(old_mode
);
628 size_t old_height_pixels
= CGDisplayModeGetPixelHeight(old_mode
);
629 BOOL new_size_same
= (new_width_pixels
== width_points
&& new_height_pixels
== height_points
);
630 BOOL old_size_same
= (old_width_pixels
== width_points
&& old_height_pixels
== height_points
);
632 if (new_size_same
&& !old_size_same
)
634 else if (!new_size_same
&& old_size_same
)
638 /* Otherwise, prefer the mode with the smaller pixel size. */
639 if (old_width_pixels
< new_width_pixels
|| old_height_pixels
< new_height_pixels
)
647 CFDictionarySetValue(modes_by_size
, key
, new_mode
);
652 free_display_mode_descriptor(desc
);
655 count
= CFDictionaryGetCount(modes_by_size
);
656 mode_array
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(mode_array
[0]));
657 CFDictionaryGetKeysAndValues(modes_by_size
, NULL
, (const void **)mode_array
);
658 modes
= CFArrayCreate(NULL
, (const void **)mode_array
, count
, &kCFTypeArrayCallBacks
);
659 HeapFree(GetProcessHeap(), 0, mode_array
);
660 CFRelease(modes_by_size
);
664 modes
= CGDisplayCopyAllDisplayModes(display
, NULL
);
670 /***********************************************************************
671 * ChangeDisplaySettingsEx (MACDRV.@)
674 LONG CDECL
macdrv_ChangeDisplaySettingsEx(LPCWSTR devname
, LPDEVMODEW devmode
,
675 HWND hwnd
, DWORD flags
, LPVOID lpvoid
)
677 LONG ret
= DISP_CHANGE_BADMODE
;
680 BOOL def_mode
= TRUE
;
681 struct macdrv_display
*displays
;
683 CFArrayRef display_modes
;
684 CFIndex count
, i
, safe
, best
;
685 CGDisplayModeRef best_display_mode
;
686 uint32_t best_io_flags
;
688 TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname
), devmode
, hwnd
, flags
, lpvoid
);
690 init_original_display_mode();
694 /* this is the minimal dmSize that XP accepts */
695 if (devmode
->dmSize
< FIELD_OFFSET(DEVMODEW
, dmFields
))
696 return DISP_CHANGE_FAILED
;
698 if (devmode
->dmSize
>= FIELD_OFFSET(DEVMODEW
, dmFields
) + sizeof(devmode
->dmFields
))
700 if (((devmode
->dmFields
& DM_BITSPERPEL
) && devmode
->dmBitsPerPel
) ||
701 ((devmode
->dmFields
& DM_PELSWIDTH
) && devmode
->dmPelsWidth
) ||
702 ((devmode
->dmFields
& DM_PELSHEIGHT
) && devmode
->dmPelsHeight
) ||
703 ((devmode
->dmFields
& DM_DISPLAYFREQUENCY
) && devmode
->dmDisplayFrequency
))
710 if (!macdrv_EnumDisplaySettingsEx(devname
, ENUM_REGISTRY_SETTINGS
, &dm
, 0))
712 ERR("Default mode not found!\n");
713 return DISP_CHANGE_BADMODE
;
716 TRACE("Return to original display mode\n");
720 if ((devmode
->dmFields
& (DM_PELSWIDTH
| DM_PELSHEIGHT
)) != (DM_PELSWIDTH
| DM_PELSHEIGHT
))
722 WARN("devmode doesn't specify the resolution: %04x\n", devmode
->dmFields
);
723 return DISP_CHANGE_BADMODE
;
726 if (macdrv_get_displays(&displays
, &num_displays
))
727 return DISP_CHANGE_FAILED
;
729 display_modes
= copy_display_modes(displays
[0].displayID
);
732 macdrv_free_displays(displays
);
733 return DISP_CHANGE_FAILED
;
736 bpp
= get_default_bpp();
737 if ((devmode
->dmFields
& DM_BITSPERPEL
) && devmode
->dmBitsPerPel
!= bpp
)
738 TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp
, devmode
->dmBitsPerPel
);
740 TRACE("looking for %dx%dx%dbpp @%d Hz",
741 (devmode
->dmFields
& DM_PELSWIDTH
? devmode
->dmPelsWidth
: 0),
742 (devmode
->dmFields
& DM_PELSHEIGHT
? devmode
->dmPelsHeight
: 0),
744 (devmode
->dmFields
& DM_DISPLAYFREQUENCY
? devmode
->dmDisplayFrequency
: 0));
745 if (devmode
->dmFields
& DM_DISPLAYFIXEDOUTPUT
)
746 TRACE(" %sstretched", devmode
->dmDisplayFixedOutput
== DMDFO_STRETCH
? "" : "un");
747 if (devmode
->dmFields
& DM_DISPLAYFLAGS
)
748 TRACE(" %sinterlaced", devmode
->dmDisplayFlags
& DM_INTERLACED
? "" : "non-");
752 best_display_mode
= NULL
;
753 count
= CFArrayGetCount(display_modes
);
754 for (i
= 0; i
< count
; i
++)
756 CGDisplayModeRef display_mode
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(display_modes
, i
);
757 uint32_t io_flags
= CGDisplayModeGetIOFlags(display_mode
);
758 int mode_bpp
= display_mode_bits_per_pixel(display_mode
);
759 size_t width
= CGDisplayModeGetWidth(display_mode
);
760 size_t height
= CGDisplayModeGetHeight(display_mode
);
762 if (!(io_flags
& kDisplayModeValidFlag
) || !(io_flags
& kDisplayModeSafeFlag
))
770 if (devmode
->dmFields
& DM_PELSWIDTH
)
772 if (devmode
->dmPelsWidth
!= width
)
775 if (devmode
->dmFields
& DM_PELSHEIGHT
)
777 if (devmode
->dmPelsHeight
!= height
)
780 if ((devmode
->dmFields
& DM_DISPLAYFREQUENCY
) && devmode
->dmDisplayFrequency
!= 0)
782 double refresh_rate
= CGDisplayModeGetRefreshRate(display_mode
);
785 if (devmode
->dmDisplayFrequency
!= (DWORD
)refresh_rate
)
788 if (devmode
->dmFields
& DM_DISPLAYFLAGS
)
790 if (!(devmode
->dmDisplayFlags
& DM_INTERLACED
) != !(io_flags
& kDisplayModeInterlacedFlag
))
793 else if (best_display_mode
)
795 if (io_flags
& kDisplayModeInterlacedFlag
&& !(best_io_flags
& kDisplayModeInterlacedFlag
))
797 else if (!(io_flags
& kDisplayModeInterlacedFlag
) && best_io_flags
& kDisplayModeInterlacedFlag
)
800 if (devmode
->dmFields
& DM_DISPLAYFIXEDOUTPUT
)
802 if (!(devmode
->dmDisplayFixedOutput
== DMDFO_STRETCH
) != !(io_flags
& kDisplayModeStretchedFlag
))
805 else if (best_display_mode
)
807 if (io_flags
& kDisplayModeStretchedFlag
&& !(best_io_flags
& kDisplayModeStretchedFlag
))
809 else if (!(io_flags
& kDisplayModeStretchedFlag
) && best_io_flags
& kDisplayModeStretchedFlag
)
813 if (best_display_mode
)
817 best_display_mode
= display_mode
;
819 best_io_flags
= io_flags
;
822 if (best_display_mode
)
824 /* we have a valid mode */
825 TRACE("Requested display settings match mode %ld\n", best
);
827 if ((flags
& CDS_UPDATEREGISTRY
) && !write_registry_settings(devmode
))
829 WARN("Failed to update registry\n");
830 ret
= DISP_CHANGE_NOTUPDATED
;
832 else if (flags
& (CDS_TEST
| CDS_NORESET
))
833 ret
= DISP_CHANGE_SUCCESSFUL
;
834 else if (macdrv_set_display_mode(&displays
[0], best_display_mode
))
836 int mode_bpp
= display_mode_bits_per_pixel(best_display_mode
);
837 size_t width
= CGDisplayModeGetWidth(best_display_mode
);
838 size_t height
= CGDisplayModeGetHeight(best_display_mode
);
840 SendMessageW(GetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT
, mode_bpp
,
841 MAKELPARAM(width
, height
));
842 ret
= DISP_CHANGE_SUCCESSFUL
;
846 WARN("Failed to set display mode\n");
847 ret
= DISP_CHANGE_FAILED
;
852 /* no valid modes found */
853 ERR("No matching mode found %ux%ux%d @%u!\n", devmode
->dmPelsWidth
, devmode
->dmPelsHeight
,
854 bpp
, devmode
->dmDisplayFrequency
);
857 CFRelease(display_modes
);
858 macdrv_free_displays(displays
);
864 /***********************************************************************
865 * EnumDisplayMonitors (MACDRV.@)
867 BOOL CDECL
macdrv_EnumDisplayMonitors(HDC hdc
, LPRECT rect
, MONITORENUMPROC proc
, LPARAM lparam
)
869 struct macdrv_display
*displays
;
874 TRACE("%p, %s, %p, %#lx\n", hdc
, wine_dbgstr_rect(rect
), proc
, lparam
);
881 if (!GetDCOrgEx(hdc
, &origin
)) return FALSE
;
882 if (GetClipBox(hdc
, &limit
) == ERROR
) return FALSE
;
884 if (rect
&& !IntersectRect(&limit
, &limit
, rect
)) return TRUE
;
886 if (macdrv_get_displays(&displays
, &num_displays
))
889 for (i
= 0; i
< num_displays
; i
++)
891 RECT monrect
= rect_from_cgrect(displays
[i
].frame
);
892 OffsetRect(&monrect
, -origin
.x
, -origin
.y
);
893 if (IntersectRect(&monrect
, &monrect
, &limit
))
895 HMONITOR monitor
= display_id_to_monitor(displays
[i
].displayID
);
896 TRACE("monitor %d handle %p @ %s\n", i
, monitor
, wine_dbgstr_rect(&monrect
));
897 if (!proc(monitor
, hdc
, &monrect
, lparam
))
907 if (macdrv_get_displays(&displays
, &num_displays
))
910 for (i
= 0; i
< num_displays
; i
++)
912 RECT monrect
= rect_from_cgrect(displays
[i
].frame
);
914 if (!rect
|| IntersectRect(&unused
, &monrect
, rect
))
916 HMONITOR monitor
= display_id_to_monitor(displays
[i
].displayID
);
917 TRACE("monitor %d handle %p @ %s\n", i
, monitor
, wine_dbgstr_rect(&monrect
));
918 if (!proc(monitor
, 0, &monrect
, lparam
))
927 macdrv_free_displays(displays
);
933 /***********************************************************************
934 * EnumDisplaySettingsEx (MACDRV.@)
937 BOOL CDECL
macdrv_EnumDisplaySettingsEx(LPCWSTR devname
, DWORD mode
,
938 LPDEVMODEW devmode
, DWORD flags
)
940 static const WCHAR dev_name
[CCHDEVICENAME
] =
941 { 'W','i','n','e',' ','M','a','c',' ','d','r','i','v','e','r',0 };
942 struct macdrv_display
*displays
= NULL
;
944 CGDisplayModeRef display_mode
;
945 int display_mode_bpp
;
946 BOOL synthesized
= FALSE
;
950 TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname
), mode
, devmode
, devmode
->dmSize
, flags
);
952 init_original_display_mode();
954 memcpy(devmode
->dmDeviceName
, dev_name
, sizeof(dev_name
));
955 devmode
->dmSpecVersion
= DM_SPECVERSION
;
956 devmode
->dmDriverVersion
= DM_SPECVERSION
;
957 devmode
->dmSize
= FIELD_OFFSET(DEVMODEW
, dmICMMethod
);
958 devmode
->dmDriverExtra
= 0;
959 memset(&devmode
->dmFields
, 0, devmode
->dmSize
- FIELD_OFFSET(DEVMODEW
, dmFields
));
961 if (mode
== ENUM_REGISTRY_SETTINGS
)
963 TRACE("mode %d (registry) -- getting default mode\n", mode
);
964 return read_registry_settings(devmode
);
967 if (macdrv_get_displays(&displays
, &num_displays
))
970 if (mode
== ENUM_CURRENT_SETTINGS
)
972 TRACE("mode %d (current) -- getting current mode\n", mode
);
973 display_mode
= CGDisplayCopyDisplayMode(displays
[0].displayID
);
974 display_mode_bpp
= display_mode_bits_per_pixel(display_mode
);
980 EnterCriticalSection(&modes_section
);
982 if (mode
== 0 || !modes
)
984 if (modes
) CFRelease(modes
);
985 modes
= copy_display_modes(displays
[0].displayID
);
986 modes_has_8bpp
= modes_has_16bpp
= FALSE
;
990 count
= CFArrayGetCount(modes
);
991 for (i
= 0; i
< count
&& !(modes_has_8bpp
&& modes_has_16bpp
); i
++)
993 CGDisplayModeRef mode
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(modes
, i
);
994 int bpp
= display_mode_bits_per_pixel(mode
);
996 modes_has_8bpp
= TRUE
;
998 modes_has_16bpp
= TRUE
;
1003 display_mode
= NULL
;
1006 int default_bpp
= get_default_bpp();
1007 DWORD seen_modes
= 0;
1009 count
= CFArrayGetCount(modes
);
1010 for (i
= 0; i
< count
; i
++)
1012 CGDisplayModeRef candidate
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(modes
, i
);
1014 io_flags
= CGDisplayModeGetIOFlags(candidate
);
1015 if (!(flags
& EDS_RAWMODE
) &&
1016 (!(io_flags
& kDisplayModeValidFlag
) || !(io_flags
& kDisplayModeSafeFlag
)))
1020 if (seen_modes
> mode
)
1022 display_mode
= (CGDisplayModeRef
)CFRetain(candidate
);
1023 display_mode_bpp
= display_mode_bits_per_pixel(display_mode
);
1027 /* We only synthesize modes from those having the default bpp. */
1028 if (display_mode_bits_per_pixel(candidate
) != default_bpp
)
1031 if (!modes_has_8bpp
)
1034 if (seen_modes
> mode
)
1036 display_mode
= (CGDisplayModeRef
)CFRetain(candidate
);
1037 display_mode_bpp
= 8;
1043 if (!modes_has_16bpp
)
1046 if (seen_modes
> mode
)
1048 display_mode
= (CGDisplayModeRef
)CFRetain(candidate
);
1049 display_mode_bpp
= 16;
1057 LeaveCriticalSection(&modes_section
);
1063 /* We currently only report modes for the primary display, so it's at (0, 0). */
1064 devmode
->dmPosition
.x
= 0;
1065 devmode
->dmPosition
.y
= 0;
1066 devmode
->dmFields
|= DM_POSITION
;
1068 rotation
= CGDisplayRotation(displays
[0].displayID
);
1069 devmode
->dmDisplayOrientation
= ((int)((rotation
/ 90) + 0.5)) % 4;
1070 devmode
->dmFields
|= DM_DISPLAYORIENTATION
;
1072 io_flags
= CGDisplayModeGetIOFlags(display_mode
);
1073 if (io_flags
& kDisplayModeStretchedFlag
)
1074 devmode
->dmDisplayFixedOutput
= DMDFO_STRETCH
;
1076 devmode
->dmDisplayFixedOutput
= DMDFO_CENTER
;
1077 devmode
->dmFields
|= DM_DISPLAYFIXEDOUTPUT
;
1079 devmode
->dmBitsPerPel
= display_mode_bpp
;
1080 if (devmode
->dmBitsPerPel
)
1081 devmode
->dmFields
|= DM_BITSPERPEL
;
1083 devmode
->dmPelsWidth
= CGDisplayModeGetWidth(display_mode
);
1084 devmode
->dmPelsHeight
= CGDisplayModeGetHeight(display_mode
);
1085 devmode
->dmFields
|= DM_PELSWIDTH
| DM_PELSHEIGHT
;
1087 devmode
->dmDisplayFlags
= 0;
1088 if (io_flags
& kDisplayModeInterlacedFlag
)
1089 devmode
->dmDisplayFlags
|= DM_INTERLACED
;
1090 devmode
->dmFields
|= DM_DISPLAYFLAGS
;
1092 devmode
->dmDisplayFrequency
= CGDisplayModeGetRefreshRate(display_mode
);
1093 if (!devmode
->dmDisplayFrequency
)
1094 devmode
->dmDisplayFrequency
= 60;
1095 devmode
->dmFields
|= DM_DISPLAYFREQUENCY
;
1097 CFRelease(display_mode
);
1098 macdrv_free_displays(displays
);
1100 TRACE("mode %d -- %dx%dx%dbpp @%d Hz", mode
,
1101 devmode
->dmPelsWidth
, devmode
->dmPelsHeight
, devmode
->dmBitsPerPel
,
1102 devmode
->dmDisplayFrequency
);
1103 if (devmode
->dmDisplayOrientation
)
1104 TRACE(" rotated %u degrees", devmode
->dmDisplayOrientation
* 90);
1105 if (devmode
->dmDisplayFixedOutput
== DMDFO_STRETCH
)
1106 TRACE(" stretched");
1107 if (devmode
->dmDisplayFlags
& DM_INTERLACED
)
1108 TRACE(" interlaced");
1110 TRACE(" (synthesized)");
1116 TRACE("mode %d -- not present\n", mode
);
1117 if (displays
) macdrv_free_displays(displays
);
1118 SetLastError(ERROR_NO_MORE_FILES
);
1123 /***********************************************************************
1124 * GetDeviceGammaRamp (MACDRV.@)
1126 BOOL
macdrv_GetDeviceGammaRamp(PHYSDEV dev
, LPVOID ramp
)
1129 DDGAMMARAMP
*r
= ramp
;
1130 struct macdrv_display
*displays
;
1132 uint32_t mac_entries
;
1133 int win_entries
= sizeof(r
->red
) / sizeof(r
->red
[0]);
1134 CGGammaValue
*red
, *green
, *blue
;
1138 TRACE("dev %p ramp %p\n", dev
, ramp
);
1140 if (macdrv_get_displays(&displays
, &num_displays
))
1142 WARN("failed to get Mac displays\n");
1146 mac_entries
= CGDisplayGammaTableCapacity(displays
[0].displayID
);
1147 red
= HeapAlloc(GetProcessHeap(), 0, mac_entries
* sizeof(red
[0]) * 3);
1150 green
= red
+ mac_entries
;
1151 blue
= green
+ mac_entries
;
1153 err
= CGGetDisplayTransferByTable(displays
[0].displayID
, mac_entries
, red
, green
,
1154 blue
, &mac_entries
);
1155 if (err
!= kCGErrorSuccess
)
1157 WARN("failed to get Mac gamma table: %d\n", err
);
1161 if (mac_entries
== win_entries
)
1163 for (win_entry
= 0; win_entry
< win_entries
; win_entry
++)
1165 r
->red
[win_entry
] = red
[win_entry
] * 65535 + 0.5;
1166 r
->green
[win_entry
] = green
[win_entry
] * 65535 + 0.5;
1167 r
->blue
[win_entry
] = blue
[win_entry
] * 65535 + 0.5;
1172 for (win_entry
= 0; win_entry
< win_entries
; win_entry
++)
1174 double mac_pos
= win_entry
* (mac_entries
- 1) / (double)(win_entries
- 1);
1175 int mac_entry
= mac_pos
;
1176 double red_value
, green_value
, blue_value
;
1178 if (mac_entry
== mac_entries
- 1)
1180 red_value
= red
[mac_entry
];
1181 green_value
= green
[mac_entry
];
1182 blue_value
= blue
[mac_entry
];
1186 double distance
= mac_pos
- mac_entry
;
1188 red_value
= red
[mac_entry
] * (1 - distance
) + red
[mac_entry
+ 1] * distance
;
1189 green_value
= green
[mac_entry
] * (1 - distance
) + green
[mac_entry
+ 1] * distance
;
1190 blue_value
= blue
[mac_entry
] * (1 - distance
) + blue
[mac_entry
+ 1] * distance
;
1193 r
->red
[win_entry
] = red_value
* 65535 + 0.5;
1194 r
->green
[win_entry
] = green_value
* 65535 + 0.5;
1195 r
->blue
[win_entry
] = blue_value
* 65535 + 0.5;
1202 HeapFree(GetProcessHeap(), 0, red
);
1203 macdrv_free_displays(displays
);
1208 /***********************************************************************
1209 * GetMonitorInfo (MACDRV.@)
1211 BOOL CDECL
macdrv_GetMonitorInfo(HMONITOR monitor
, LPMONITORINFO info
)
1213 static const WCHAR adapter_name
[] = { '\\','\\','.','\\','D','I','S','P','L','A','Y','1',0 };
1214 struct macdrv_display
*displays
;
1216 CGDirectDisplayID display_id
;
1219 TRACE("%p, %p\n", monitor
, info
);
1221 if (macdrv_get_displays(&displays
, &num_displays
))
1223 ERR("couldn't get display list\n");
1224 SetLastError(ERROR_GEN_FAILURE
);
1228 display_id
= monitor_to_display_id(monitor
);
1229 for (i
= 0; i
< num_displays
; i
++)
1231 if (displays
[i
].displayID
== display_id
)
1235 if (i
< num_displays
)
1237 info
->rcMonitor
= rect_from_cgrect(displays
[i
].frame
);
1238 info
->rcWork
= rect_from_cgrect(displays
[i
].work_frame
);
1240 info
->dwFlags
= (i
== 0) ? MONITORINFOF_PRIMARY
: 0;
1242 if (info
->cbSize
>= sizeof(MONITORINFOEXW
))
1243 lstrcpyW(((MONITORINFOEXW
*)info
)->szDevice
, adapter_name
);
1245 TRACE(" -> rcMonitor %s rcWork %s dwFlags %08x\n", wine_dbgstr_rect(&info
->rcMonitor
),
1246 wine_dbgstr_rect(&info
->rcWork
), info
->dwFlags
);
1250 ERR("invalid monitor handle\n");
1251 SetLastError(ERROR_INVALID_HANDLE
);
1254 macdrv_free_displays(displays
);
1255 return (i
< num_displays
);
1259 /***********************************************************************
1260 * SetDeviceGammaRamp (MACDRV.@)
1262 BOOL
macdrv_SetDeviceGammaRamp(PHYSDEV dev
, LPVOID ramp
)
1264 DDGAMMARAMP
*r
= ramp
;
1265 struct macdrv_display
*displays
;
1267 int win_entries
= sizeof(r
->red
) / sizeof(r
->red
[0]);
1268 CGGammaValue
*red
, *green
, *blue
;
1270 CGError err
= kCGErrorFailure
;
1272 TRACE("dev %p ramp %p\n", dev
, ramp
);
1274 if (!allow_set_gamma
)
1276 TRACE("disallowed by registry setting\n");
1280 if (macdrv_get_displays(&displays
, &num_displays
))
1282 WARN("failed to get Mac displays\n");
1286 red
= HeapAlloc(GetProcessHeap(), 0, win_entries
* sizeof(red
[0]) * 3);
1289 green
= red
+ win_entries
;
1290 blue
= green
+ win_entries
;
1292 for (i
= 0; i
< win_entries
; i
++)
1294 red
[i
] = r
->red
[i
] / 65535.0;
1295 green
[i
] = r
->green
[i
] / 65535.0;
1296 blue
[i
] = r
->blue
[i
] / 65535.0;
1299 err
= CGSetDisplayTransferByTable(displays
[0].displayID
, win_entries
, red
, green
, blue
);
1300 if (err
!= kCGErrorSuccess
)
1301 WARN("failed to set display gamma table: %d\n", err
);
1304 HeapFree(GetProcessHeap(), 0, red
);
1305 macdrv_free_displays(displays
);
1306 return (err
== kCGErrorSuccess
);
1310 /***********************************************************************
1311 * macdrv_displays_changed
1313 * Handler for DISPLAYS_CHANGED events.
1315 void macdrv_displays_changed(const macdrv_event
*event
)
1317 HWND hwnd
= GetDesktopWindow();
1319 /* A system display change will get delivered to all GUI-attached threads,
1320 so the desktop-window-owning thread will get it and all others should
1321 ignore it. A synthesized display change event due to activation
1322 will only get delivered to the activated process. So, it needs to
1323 process it (by sending it to the desktop window). */
1324 if (event
->displays_changed
.activating
||
1325 GetWindowThreadProcessId(hwnd
, NULL
) == GetCurrentThreadId())
1327 CGDirectDisplayID mainDisplay
= CGMainDisplayID();
1328 CGDisplayModeRef mode
= CGDisplayCopyDisplayMode(mainDisplay
);
1329 size_t width
= CGDisplayModeGetWidth(mode
);
1330 size_t height
= CGDisplayModeGetHeight(mode
);
1331 int mode_bpp
= display_mode_bits_per_pixel(mode
);
1333 CGDisplayModeRelease(mode
);
1334 SendMessageW(hwnd
, WM_MACDRV_UPDATE_DESKTOP_RECT
, mode_bpp
,
1335 MAKELPARAM(width
, height
));