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
32 #define WIN32_NO_STATUS
35 WINE_DEFAULT_DEBUG_CHANNEL(display
);
38 struct display_mode_descriptor
46 CFStringRef pixel_encoding
;
50 BOOL
macdrv_EnumDisplaySettingsEx(LPCWSTR devname
, DWORD mode
, LPDEVMODEW devmode
, DWORD flags
);
52 static const WCHAR initial_mode_keyW
[] = {'I','n','i','t','i','a','l',' ','D','i','s','p','l','a','y',
54 static const WCHAR pixelencodingW
[] = {'P','i','x','e','l','E','n','c','o','d','i','n','g',0};
56 static CFArrayRef modes
;
57 static BOOL modes_has_8bpp
, modes_has_16bpp
;
58 static int default_mode_bpp
;
59 static pthread_mutex_t modes_mutex
= PTHREAD_MUTEX_INITIALIZER
;
61 static BOOL inited_original_display_mode
;
63 static HANDLE
get_display_device_init_mutex(void)
65 static const WCHAR init_mutexW
[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t'};
66 UNICODE_STRING name
= { sizeof(init_mutexW
), sizeof(init_mutexW
), (WCHAR
*)init_mutexW
};
67 OBJECT_ATTRIBUTES attr
;
70 InitializeObjectAttributes(&attr
, &name
, OBJ_OPENIF
, NULL
, NULL
);
71 NtCreateMutant(&mutex
, MUTEX_ALL_ACCESS
, &attr
, FALSE
);
72 if (mutex
) NtWaitForSingleObject(mutex
, FALSE
, NULL
);
76 static void release_display_device_init_mutex(HANDLE mutex
)
78 NtReleaseMutant(mutex
, NULL
);
82 static HKEY
get_display_device_reg_key(const WCHAR
*device_name
)
84 static const WCHAR display
[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y'};
85 static const WCHAR video_key
[] = {
86 '\\','R','e','g','i','s','t','r','y',
87 '\\','M','a','c','h','i','n','e',
88 '\\','H','A','R','D','W','A','R','E',
89 '\\','D','E','V','I','C','E','M','A','P',
90 '\\','V','I','D','E','O'};
91 static const WCHAR current_config_key
[] = {
92 '\\','R','e','g','i','s','t','r','y',
93 '\\','M','a','c','h','i','n','e',
94 '\\','S','y','s','t','e','m',
95 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
96 '\\','H','a','r','d','w','a','r','e',' ','P','r','o','f','i','l','e','s',
97 '\\','C','u','r','r','e','n','t'};
98 WCHAR value_name
[MAX_PATH
], buffer
[4096], *end_ptr
;
99 KEY_VALUE_PARTIAL_INFORMATION
*value
= (void *)buffer
;
100 DWORD adapter_index
, size
;
101 char adapter_name
[100];
104 /* Device name has to be \\.\DISPLAY%d */
105 if (wcsnicmp(device_name
, display
, ARRAY_SIZE(display
)))
108 /* Parse \\.\DISPLAY* */
109 adapter_index
= wcstol(device_name
+ ARRAY_SIZE(display
), &end_ptr
, 10) - 1;
113 /* Open \Device\Video* in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */
114 if (!(hkey
= reg_open_key(NULL
, video_key
, sizeof(video_key
)))) return FALSE
;
115 sprintf(adapter_name
, "\\Device\\Video%d", adapter_index
);
116 asciiz_to_unicode(value_name
, adapter_name
);
117 size
= query_reg_value(hkey
, value_name
, value
, sizeof(buffer
));
119 if (!size
|| value
->Type
!= REG_SZ
) return FALSE
;
121 /* Replace \Registry\Machine\ prefix with HKEY_CURRENT_CONFIG */
122 memmove(buffer
+ ARRAYSIZE(current_config_key
), (const WCHAR
*)value
->Data
+ 17,
123 size
- 17 * sizeof(WCHAR
));
124 memcpy(buffer
, current_config_key
, sizeof(current_config_key
));
125 TRACE("display device %s registry settings key %s.\n", wine_dbgstr_w(device_name
),
126 wine_dbgstr_w(buffer
));
127 return reg_open_key(NULL
, buffer
, lstrlenW(buffer
) * sizeof(WCHAR
));
131 static BOOL
query_display_setting(HKEY hkey
, const char *name
, DWORD
*ret
)
135 KEY_VALUE_PARTIAL_INFORMATION
*value
= (void *)buffer
;
137 asciiz_to_unicode(nameW
, name
);
138 if (query_reg_value(hkey
, nameW
, value
, sizeof(buffer
)) != sizeof(DWORD
) ||
139 value
->Type
!= REG_DWORD
)
142 *ret
= *(DWORD
*)value
->Data
;
147 static BOOL
read_registry_settings(const WCHAR
*device_name
, DEVMODEW
*dm
)
155 mutex
= get_display_device_init_mutex();
156 if (!(hkey
= get_display_device_reg_key(device_name
)))
158 release_display_device_init_mutex(mutex
);
162 ret
&= query_display_setting(hkey
, "DefaultSettings.BitsPerPel", &dm
->dmBitsPerPel
);
163 dm
->dmFields
|= DM_BITSPERPEL
;
164 ret
&= query_display_setting(hkey
, "DefaultSettings.XResolution", &dm
->dmPelsWidth
);
165 dm
->dmFields
|= DM_PELSWIDTH
;
166 ret
&= query_display_setting(hkey
, "DefaultSettings.YResolution", &dm
->dmPelsHeight
);
167 dm
->dmFields
|= DM_PELSHEIGHT
;
168 ret
&= query_display_setting(hkey
, "DefaultSettings.VRefresh", &dm
->dmDisplayFrequency
);
169 dm
->dmFields
|= DM_DISPLAYFREQUENCY
;
170 ret
&= query_display_setting(hkey
, "DefaultSettings.Flags", &dm
->dmDisplayFlags
);
171 dm
->dmFields
|= DM_DISPLAYFLAGS
;
172 ret
&= query_display_setting(hkey
, "DefaultSettings.XPanning", (DWORD
*)&dm
->dmPosition
.x
);
173 ret
&= query_display_setting(hkey
, "DefaultSettings.YPanning", (DWORD
*)&dm
->dmPosition
.y
);
174 dm
->dmFields
|= DM_POSITION
;
175 ret
&= query_display_setting(hkey
, "DefaultSettings.Orientation", &dm
->dmDisplayOrientation
);
176 dm
->dmFields
|= DM_DISPLAYORIENTATION
;
177 ret
&= query_display_setting(hkey
, "DefaultSettings.FixedOutput", &dm
->dmDisplayFixedOutput
);
180 release_display_device_init_mutex(mutex
);
185 static BOOL
set_setting_value(HKEY hkey
, const char *name
, DWORD val
)
188 UNICODE_STRING str
= { asciiz_to_unicode(nameW
, name
) - sizeof(WCHAR
), sizeof(nameW
), nameW
};
189 return !NtSetValueKey(hkey
, &str
, 0, REG_DWORD
, &val
, sizeof(val
));
193 static BOOL
write_registry_settings(const WCHAR
*device_name
, const DEVMODEW
*dm
)
199 mutex
= get_display_device_init_mutex();
200 if (!(hkey
= get_display_device_reg_key(device_name
)))
202 release_display_device_init_mutex(mutex
);
206 ret
&= set_setting_value(hkey
, "DefaultSettings.BitsPerPel", dm
->dmBitsPerPel
);
207 ret
&= set_setting_value(hkey
, "DefaultSettings.XResolution", dm
->dmPelsWidth
);
208 ret
&= set_setting_value(hkey
, "DefaultSettings.YResolution", dm
->dmPelsHeight
);
209 ret
&= set_setting_value(hkey
, "DefaultSettings.VRefresh", dm
->dmDisplayFrequency
);
210 ret
&= set_setting_value(hkey
, "DefaultSettings.Flags", dm
->dmDisplayFlags
);
211 ret
&= set_setting_value(hkey
, "DefaultSettings.XPanning", dm
->dmPosition
.x
);
212 ret
&= set_setting_value(hkey
, "DefaultSettings.YPanning", dm
->dmPosition
.y
);
213 ret
&= set_setting_value(hkey
, "DefaultSettings.Orientation", dm
->dmDisplayOrientation
);
214 ret
&= set_setting_value(hkey
, "DefaultSettings.FixedOutput", dm
->dmDisplayFixedOutput
);
217 release_display_device_init_mutex(mutex
);
222 static BOOL
write_display_settings(HKEY parent_hkey
, CGDirectDisplayID displayID
)
225 char display_key_name
[19];
227 CGDisplayModeRef display_mode
;
230 CFStringRef pixel_encoding
;
234 snprintf(display_key_name
, sizeof(display_key_name
), "Display 0x%08x", CGDisplayUnitNumber(displayID
));
235 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
236 if (!(display_hkey
= reg_create_ascii_key(parent_hkey
, display_key_name
, REG_OPTION_VOLATILE
, NULL
)))
239 display_mode
= CGDisplayCopyDisplayMode(displayID
);
243 val
= CGDisplayModeGetWidth(display_mode
);
244 if (!set_setting_value(display_hkey
, "Width", val
))
246 val
= CGDisplayModeGetHeight(display_mode
);
247 if (!set_setting_value(display_hkey
, "Height", val
))
249 val
= CGDisplayModeGetRefreshRate(display_mode
) * 100;
250 if (!set_setting_value(display_hkey
, "RefreshRateTimes100", val
))
252 val
= CGDisplayModeGetIOFlags(display_mode
);
253 if (!set_setting_value(display_hkey
, "IOFlags", val
))
256 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
257 if (&CGDisplayModeGetPixelWidth
!= NULL
&& &CGDisplayModeGetPixelHeight
!= NULL
)
259 val
= CGDisplayModeGetPixelWidth(display_mode
);
260 if (!set_setting_value(display_hkey
, "PixelWidth", val
))
262 val
= CGDisplayModeGetPixelHeight(display_mode
);
263 if (!set_setting_value(display_hkey
, "PixelHeight", val
))
268 pixel_encoding
= CGDisplayModeCopyPixelEncoding(display_mode
);
269 len
= CFStringGetLength(pixel_encoding
);
270 buf
= malloc((len
+ 1) * sizeof(WCHAR
));
271 CFStringGetCharacters(pixel_encoding
, CFRangeMake(0, len
), (UniChar
*)buf
);
273 CFRelease(pixel_encoding
);
274 RtlInitUnicodeString(&str
, pixelencodingW
);
275 if (NtSetValueKey(display_hkey
, &str
, 0, REG_SZ
, (const BYTE
*)buf
, (len
+ 1) * sizeof(WCHAR
)))
282 if (display_mode
) CGDisplayModeRelease(display_mode
);
283 NtClose(display_hkey
);
287 reg_delete_tree(parent_hkey
, nameW
, asciiz_to_unicode(nameW
, display_key_name
) - sizeof(WCHAR
));
293 static void init_original_display_mode(void)
295 BOOL success
= FALSE
;
296 HKEY mac_driver_hkey
, parent_hkey
;
298 struct macdrv_display
*displays
= NULL
;
301 if (inited_original_display_mode
)
304 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver */
305 mac_driver_hkey
= reg_create_ascii_key(NULL
, "\\Registry\\Machine\\Software\\Wine\\Mac Driver",
307 if (!mac_driver_hkey
)
310 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode */
311 if (!(parent_hkey
= reg_create_key(mac_driver_hkey
, initial_mode_keyW
, sizeof(initial_mode_keyW
),
312 REG_OPTION_VOLATILE
, &disposition
)))
318 /* If we didn't create a new key, then it already existed. Something already stored
319 the initial display mode since Wine was started. We don't want to overwrite it. */
320 if (disposition
!= REG_CREATED_NEW_KEY
)
323 if (macdrv_get_displays(&displays
, &num_displays
))
326 for (i
= 0; i
< num_displays
; i
++)
328 if (!write_display_settings(parent_hkey
, displays
[i
].displayID
))
336 macdrv_free_displays(displays
);
337 NtClose(parent_hkey
);
338 if (!success
&& parent_hkey
)
339 reg_delete_tree(mac_driver_hkey
, initial_mode_keyW
, sizeof(initial_mode_keyW
));
340 NtClose(mac_driver_hkey
);
342 inited_original_display_mode
= TRUE
;
346 static BOOL
read_dword(HKEY hkey
, const char* name
, DWORD
* val
)
348 char buffer
[offsetof(KEY_VALUE_PARTIAL_INFORMATION
, Data
[sizeof(*val
)])];
349 KEY_VALUE_PARTIAL_INFORMATION
*value
= (void *)buffer
;
351 asciiz_to_unicode(nameW
, name
);
352 if (query_reg_value(hkey
, nameW
, value
, sizeof(buffer
)) != sizeof(*val
) || value
->Type
!= REG_DWORD
)
354 *val
= *(DWORD
*)value
->Data
;
359 static void free_display_mode_descriptor(struct display_mode_descriptor
* desc
)
363 if (desc
->pixel_encoding
)
364 CFRelease(desc
->pixel_encoding
);
370 static struct display_mode_descriptor
* create_original_display_mode_descriptor(CGDirectDisplayID displayID
)
372 static const char display_key_format
[] =
373 "\\Registry\\Machine\\Software\\Wine\\Mac Driver\\Initial Display Mode\\Display 0x%08x";
374 struct display_mode_descriptor
* ret
= NULL
;
375 struct display_mode_descriptor
* desc
;
376 char display_key
[sizeof(display_key_format
) + 10];
377 WCHAR nameW
[ARRAYSIZE(display_key
)];
379 KEY_VALUE_PARTIAL_INFORMATION
*value
= (void *)buffer
;
383 init_original_display_mode();
385 snprintf(display_key
, sizeof(display_key
), display_key_format
, CGDisplayUnitNumber(displayID
));
386 /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */
387 if (!(hkey
= reg_open_key(NULL
, nameW
, asciiz_to_unicode(nameW
, display_key
))))
390 desc
= malloc(sizeof(*desc
));
391 desc
->pixel_encoding
= NULL
;
393 if (!read_dword(hkey
, "Width", &desc
->width
) ||
394 !read_dword(hkey
, "Height", &desc
->height
) ||
395 !read_dword(hkey
, "RefreshRateTimes100", &refresh100
) ||
396 !read_dword(hkey
, "IOFlags", &desc
->io_flags
))
399 desc
->refresh
= refresh100
/ 100.0;
403 if (!read_dword(hkey
, "PixelWidth", &desc
->pixel_width
) ||
404 !read_dword(hkey
, "PixelHeight", &desc
->pixel_height
))
406 desc
->pixel_width
= desc
->width
;
407 desc
->pixel_height
= desc
->height
;
410 if (!query_reg_value(hkey
, pixelencodingW
, value
, sizeof(buffer
)) || value
->Type
!= REG_SZ
)
412 desc
->pixel_encoding
= CFStringCreateWithCharacters(NULL
, (const UniChar
*)value
->Data
,
413 lstrlenW((const WCHAR
*)value
->Data
));
419 free_display_mode_descriptor(desc
);
425 static BOOL
display_mode_matches_descriptor(CGDisplayModeRef mode
, const struct display_mode_descriptor
* desc
)
429 CFStringRef mode_pixel_encoding
;
434 if (CGDisplayModeGetWidth(mode
) != desc
->width
||
435 CGDisplayModeGetHeight(mode
) != desc
->height
)
438 mode_io_flags
= CGDisplayModeGetIOFlags(mode
);
439 if ((desc
->io_flags
^ mode_io_flags
) & (kDisplayModeValidFlag
| kDisplayModeSafeFlag
| kDisplayModeStretchedFlag
|
440 kDisplayModeInterlacedFlag
| kDisplayModeTelevisionFlag
))
443 mode_refresh
= CGDisplayModeGetRefreshRate(mode
);
446 if (fabs(desc
->refresh
- mode_refresh
) > 0.1)
449 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
450 if (&CGDisplayModeGetPixelWidth
!= NULL
&& &CGDisplayModeGetPixelHeight
!= NULL
)
452 if (CGDisplayModeGetPixelWidth(mode
) != desc
->pixel_width
||
453 CGDisplayModeGetPixelHeight(mode
) != desc
->pixel_height
)
458 if (CGDisplayModeGetWidth(mode
) != desc
->pixel_width
||
459 CGDisplayModeGetHeight(mode
) != desc
->pixel_height
)
462 mode_pixel_encoding
= CGDisplayModeCopyPixelEncoding(mode
);
463 if (!CFEqual(mode_pixel_encoding
, desc
->pixel_encoding
))
465 CFRelease(mode_pixel_encoding
);
468 CFRelease(mode_pixel_encoding
);
474 static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode
)
476 CFStringRef pixel_encoding
;
477 int bits_per_pixel
= 0;
479 pixel_encoding
= CGDisplayModeCopyPixelEncoding(display_mode
);
482 if (CFEqual(pixel_encoding
, CFSTR(kIO32BitFloatPixels
)))
483 bits_per_pixel
= 128;
484 else if (CFEqual(pixel_encoding
, CFSTR(kIO16BitFloatPixels
)))
486 else if (CFEqual(pixel_encoding
, CFSTR(kIO64BitDirectPixels
)))
488 else if (CFEqual(pixel_encoding
, CFSTR(kIO30BitDirectPixels
)))
490 else if (CFEqual(pixel_encoding
, CFSTR(IO32BitDirectPixels
)))
492 else if (CFEqual(pixel_encoding
, CFSTR(IO16BitDirectPixels
)))
494 else if (CFEqual(pixel_encoding
, CFSTR(IO8BitIndexedPixels
)))
496 else if (CFEqual(pixel_encoding
, CFSTR(IO4BitIndexedPixels
)))
498 else if (CFEqual(pixel_encoding
, CFSTR(IO2BitIndexedPixels
)))
500 else if (CFEqual(pixel_encoding
, CFSTR(IO1BitIndexedPixels
)))
503 CFRelease(pixel_encoding
);
506 return bits_per_pixel
;
510 static int get_default_bpp(void)
514 if (!default_mode_bpp
)
516 CGDisplayModeRef mode
= CGDisplayCopyDisplayMode(kCGDirectMainDisplay
);
519 default_mode_bpp
= display_mode_bits_per_pixel(mode
);
523 if (!default_mode_bpp
)
524 default_mode_bpp
= 32;
527 ret
= default_mode_bpp
;
529 TRACE(" -> %d\n", ret
);
534 static BOOL
display_mode_is_supported(CGDisplayModeRef display_mode
)
536 uint32_t io_flags
= CGDisplayModeGetIOFlags(display_mode
);
537 return (io_flags
& kDisplayModeValidFlag
) && (io_flags
& kDisplayModeSafeFlag
);
541 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
542 static CFDictionaryRef
create_mode_dict(CGDisplayModeRef display_mode
, BOOL is_original
)
545 SInt32 io_flags
= CGDisplayModeGetIOFlags(display_mode
);
546 SInt64 width
= CGDisplayModeGetWidth(display_mode
);
547 SInt64 height
= CGDisplayModeGetHeight(display_mode
);
548 double refresh_rate
= CGDisplayModeGetRefreshRate(display_mode
);
549 CFStringRef pixel_encoding
= CGDisplayModeCopyPixelEncoding(display_mode
);
550 CFNumberRef cf_io_flags
, cf_width
, cf_height
, cf_refresh
;
552 if (retina_enabled
&& is_original
)
558 io_flags
&= kDisplayModeValidFlag
| kDisplayModeSafeFlag
| kDisplayModeInterlacedFlag
|
559 kDisplayModeStretchedFlag
| kDisplayModeTelevisionFlag
;
560 cf_io_flags
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &io_flags
);
561 cf_width
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, &width
);
562 cf_height
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, &height
);
563 cf_refresh
= CFNumberCreate(NULL
, kCFNumberDoubleType
, &refresh_rate
);
566 static const CFStringRef keys
[] = {
570 CFSTR("pixel_encoding"),
571 CFSTR("refresh_rate"),
573 const void* values
[ARRAY_SIZE(keys
)] = {
581 ret
= CFDictionaryCreate(NULL
, (const void**)keys
, (const void**)values
, ARRAY_SIZE(keys
),
582 &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
585 CFRelease(pixel_encoding
);
586 CFRelease(cf_io_flags
);
588 CFRelease(cf_height
);
589 CFRelease(cf_refresh
);
595 static BOOL
mode_is_preferred(CGDisplayModeRef new_mode
, CGDisplayModeRef old_mode
,
596 struct display_mode_descriptor
*original_mode_desc
,
597 BOOL include_unsupported
)
599 BOOL new_is_supported
;
600 CFStringRef pixel_encoding
;
601 size_t width_points
, height_points
;
602 size_t old_width_pixels
, old_height_pixels
, new_width_pixels
, new_height_pixels
;
603 BOOL old_size_same
, new_size_same
;
605 /* If a given mode is the user's default, then always list it in preference to any similar
606 modes that may exist. */
607 if (display_mode_matches_descriptor(new_mode
, original_mode_desc
))
610 /* Skip unsupported modes unless told to do otherwise. */
611 new_is_supported
= display_mode_is_supported(new_mode
);
612 if (!new_is_supported
&& !include_unsupported
)
615 pixel_encoding
= CGDisplayModeCopyPixelEncoding(new_mode
);
618 BOOL bpp30
= CFEqual(pixel_encoding
, CFSTR(kIO30BitDirectPixels
));
619 CFRelease(pixel_encoding
);
622 /* This is an odd pixel encoding. It seems it's only returned
623 when using kCGDisplayShowDuplicateLowResolutionModes. It's
624 32bpp in terms of the actual raster layout, but it's 10
625 bits per component. I think that no Windows program is
626 likely to need it and they will probably be confused by it.
635 /* Prefer the original mode over any similar mode. */
636 if (display_mode_matches_descriptor(old_mode
, original_mode_desc
))
639 /* Prefer supported modes over similar unsupported ones. */
640 if (!new_is_supported
&& display_mode_is_supported(old_mode
))
643 /* Otherwise, prefer a mode whose pixel size equals its point size over one which
645 width_points
= CGDisplayModeGetWidth(new_mode
);
646 height_points
= CGDisplayModeGetHeight(new_mode
);
647 new_width_pixels
= CGDisplayModeGetPixelWidth(new_mode
);
648 new_height_pixels
= CGDisplayModeGetPixelHeight(new_mode
);
649 old_width_pixels
= CGDisplayModeGetPixelWidth(old_mode
);
650 old_height_pixels
= CGDisplayModeGetPixelHeight(old_mode
);
651 new_size_same
= (new_width_pixels
== width_points
&& new_height_pixels
== height_points
);
652 old_size_same
= (old_width_pixels
== width_points
&& old_height_pixels
== height_points
);
654 if (new_size_same
&& !old_size_same
)
657 if (!new_size_same
&& old_size_same
)
660 /* Otherwise, prefer the mode with the smaller pixel size. */
661 return new_width_pixels
< old_width_pixels
&& new_height_pixels
< old_height_pixels
;
666 static CFComparisonResult
mode_compare(const void *p1
, const void *p2
, void *context
)
668 CGDisplayModeRef a
= (CGDisplayModeRef
)p1
, b
= (CGDisplayModeRef
)p2
;
670 double a_refresh_rate
, b_refresh_rate
;
672 /* Sort by bpp descending, */
673 a_val
= display_mode_bits_per_pixel(a
);
674 b_val
= display_mode_bits_per_pixel(b
);
676 return kCFCompareGreaterThan
;
677 else if (a_val
> b_val
)
678 return kCFCompareLessThan
;
680 /* then width ascending, */
681 a_val
= CGDisplayModeGetWidth(a
);
682 b_val
= CGDisplayModeGetWidth(b
);
684 return kCFCompareLessThan
;
685 else if (a_val
> b_val
)
686 return kCFCompareGreaterThan
;
688 /* then height ascending, */
689 a_val
= CGDisplayModeGetHeight(a
);
690 b_val
= CGDisplayModeGetHeight(b
);
692 return kCFCompareLessThan
;
693 else if (a_val
> b_val
)
694 return kCFCompareGreaterThan
;
696 /* then refresh rate descending. */
697 a_refresh_rate
= CGDisplayModeGetRefreshRate(a
);
698 b_refresh_rate
= CGDisplayModeGetRefreshRate(b
);
699 if (a_refresh_rate
< b_refresh_rate
)
700 return kCFCompareGreaterThan
;
701 else if (a_refresh_rate
> b_refresh_rate
)
702 return kCFCompareLessThan
;
704 return kCFCompareEqualTo
;
708 /***********************************************************************
711 * Wrapper around CGDisplayCopyAllDisplayModes() to include additional
712 * modes on Retina-capable systems, but filter those which would confuse
713 * Windows apps (basically duplicates at different DPIs).
715 * For example, some Retina Macs support a 1920x1200 mode, but it's not
716 * returned from CGDisplayCopyAllDisplayModes() without special options.
717 * This is especially bad if that's the user's default mode, since then
718 * no "available" mode matches the initial settings.
720 * If include_unsupported is FALSE, display modes with IO flags that
721 * indicate that they are invalid or unsafe are filtered.
723 static CFArrayRef
copy_display_modes(CGDirectDisplayID display
, BOOL include_unsupported
)
725 CFArrayRef modes
= NULL
;
727 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
728 if (&CGDisplayModeGetPixelWidth
!= NULL
&& &CGDisplayModeGetPixelHeight
!= NULL
)
730 CFDictionaryRef options
;
731 struct display_mode_descriptor
* desc
;
732 CFMutableDictionaryRef modes_by_size
;
734 CGDisplayModeRef
* mode_array
;
736 options
= CFDictionaryCreate(NULL
, (const void**)&kCGDisplayShowDuplicateLowResolutionModes
,
737 (const void**)&kCFBooleanTrue
, 1, &kCFTypeDictionaryKeyCallBacks
,
738 &kCFTypeDictionaryValueCallBacks
);
740 modes
= CGDisplayCopyAllDisplayModes(display
, options
);
746 desc
= create_original_display_mode_descriptor(display
);
748 modes_by_size
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
749 count
= CFArrayGetCount(modes
);
750 for (i
= 0; i
< count
; i
++)
752 CGDisplayModeRef new_mode
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(modes
, i
);
753 BOOL new_is_original
= display_mode_matches_descriptor(new_mode
, desc
);
754 CFDictionaryRef key
= create_mode_dict(new_mode
, new_is_original
);
755 CGDisplayModeRef old_mode
= (CGDisplayModeRef
)CFDictionaryGetValue(modes_by_size
, key
);
757 if (mode_is_preferred(new_mode
, old_mode
, desc
, include_unsupported
))
758 CFDictionarySetValue(modes_by_size
, key
, new_mode
);
763 free_display_mode_descriptor(desc
);
766 count
= CFDictionaryGetCount(modes_by_size
);
767 mode_array
= malloc(count
* sizeof(mode_array
[0]));
768 CFDictionaryGetKeysAndValues(modes_by_size
, NULL
, (const void **)mode_array
);
769 modes
= CFArrayCreate(NULL
, (const void **)mode_array
, count
, &kCFTypeArrayCallBacks
);
771 CFRelease(modes_by_size
);
775 modes
= CGDisplayCopyAllDisplayModes(display
, NULL
);
779 CFIndex count
= CFArrayGetCount(modes
);
780 CFMutableArrayRef sorted_modes
= CFArrayCreateMutableCopy(NULL
, count
, modes
);
782 CFArraySortValues(sorted_modes
, CFRangeMake(0, count
), mode_compare
, NULL
);
790 void check_retina_status(void)
794 struct display_mode_descriptor
* desc
= create_original_display_mode_descriptor(kCGDirectMainDisplay
);
795 CGDisplayModeRef mode
= CGDisplayCopyDisplayMode(kCGDirectMainDisplay
);
796 BOOL new_value
= display_mode_matches_descriptor(mode
, desc
);
798 CGDisplayModeRelease(mode
);
799 free_display_mode_descriptor(desc
);
801 if (new_value
!= retina_on
)
802 macdrv_set_cocoa_retina_mode(new_value
);
806 static BOOL
get_primary_adapter(WCHAR
*name
)
812 for (i
= 0; !NtUserEnumDisplayDevices(NULL
, i
, &dd
, 0); ++i
)
814 if (dd
.StateFlags
& DISPLAY_DEVICE_PRIMARY_DEVICE
)
816 lstrcpyW(name
, dd
.DeviceName
);
824 static BOOL
is_detached_mode(const DEVMODEW
*mode
)
826 return mode
->dmFields
& DM_POSITION
&&
827 mode
->dmFields
& DM_PELSWIDTH
&&
828 mode
->dmFields
& DM_PELSHEIGHT
&&
829 mode
->dmPelsWidth
== 0 &&
830 mode
->dmPelsHeight
== 0;
833 /***********************************************************************
834 * ChangeDisplaySettingsEx (MACDRV.@)
837 LONG
macdrv_ChangeDisplaySettingsEx(LPCWSTR devname
, LPDEVMODEW devmode
,
838 HWND hwnd
, DWORD flags
, LPVOID lpvoid
)
840 WCHAR primary_adapter
[CCHDEVICENAME
];
841 LONG ret
= DISP_CHANGE_BADMODE
;
842 DEVMODEW default_mode
;
844 struct macdrv_display
*displays
;
846 CFArrayRef display_modes
;
847 struct display_mode_descriptor
* desc
;
848 CFIndex count
, i
, best
;
849 CGDisplayModeRef best_display_mode
;
850 uint32_t best_io_flags
;
851 BOOL best_is_original
;
853 TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname
), devmode
, hwnd
, flags
, lpvoid
);
855 init_original_display_mode();
857 if (!get_primary_adapter(primary_adapter
))
858 return DISP_CHANGE_FAILED
;
860 if (!devname
&& !devmode
)
863 memset(&default_mode
, 0, sizeof(default_mode
));
864 default_mode
.dmSize
= sizeof(default_mode
);
865 RtlInitUnicodeString(&str
, primary_adapter
);
866 if (!NtUserEnumDisplaySettings(&str
, ENUM_REGISTRY_SETTINGS
, &default_mode
, 0))
868 ERR("Default mode not found for %s!\n", wine_dbgstr_w(primary_adapter
));
869 return DISP_CHANGE_BADMODE
;
872 devname
= primary_adapter
;
873 devmode
= &default_mode
;
876 if (is_detached_mode(devmode
))
878 FIXME("Detaching adapters is currently unsupported.\n");
879 return DISP_CHANGE_SUCCESSFUL
;
882 if (macdrv_get_displays(&displays
, &num_displays
))
883 return DISP_CHANGE_FAILED
;
885 display_modes
= copy_display_modes(displays
[0].displayID
, FALSE
);
888 macdrv_free_displays(displays
);
889 return DISP_CHANGE_FAILED
;
892 pthread_mutex_lock(&modes_mutex
);
893 bpp
= get_default_bpp();
894 pthread_mutex_unlock(&modes_mutex
);
895 if ((devmode
->dmFields
& DM_BITSPERPEL
) && devmode
->dmBitsPerPel
!= bpp
)
896 TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp
, devmode
->dmBitsPerPel
);
898 TRACE("looking for %dx%dx%dbpp @%d Hz",
899 (devmode
->dmFields
& DM_PELSWIDTH
? devmode
->dmPelsWidth
: 0),
900 (devmode
->dmFields
& DM_PELSHEIGHT
? devmode
->dmPelsHeight
: 0),
902 (devmode
->dmFields
& DM_DISPLAYFREQUENCY
? devmode
->dmDisplayFrequency
: 0));
903 if (devmode
->dmFields
& DM_DISPLAYFIXEDOUTPUT
)
904 TRACE(" %sstretched", devmode
->dmDisplayFixedOutput
== DMDFO_STRETCH
? "" : "un");
905 if (devmode
->dmFields
& DM_DISPLAYFLAGS
)
906 TRACE(" %sinterlaced", devmode
->dmDisplayFlags
& DM_INTERLACED
? "" : "non-");
909 desc
= create_original_display_mode_descriptor(displays
[0].displayID
);
911 best_display_mode
= NULL
;
912 count
= CFArrayGetCount(display_modes
);
913 for (i
= 0; i
< count
; i
++)
915 CGDisplayModeRef display_mode
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(display_modes
, i
);
916 BOOL is_original
= display_mode_matches_descriptor(display_mode
, desc
);
917 uint32_t io_flags
= CGDisplayModeGetIOFlags(display_mode
);
918 int mode_bpp
= display_mode_bits_per_pixel(display_mode
);
919 size_t width
= CGDisplayModeGetWidth(display_mode
);
920 size_t height
= CGDisplayModeGetHeight(display_mode
);
922 if (is_original
&& retina_enabled
)
931 if (devmode
->dmFields
& DM_PELSWIDTH
)
933 if (devmode
->dmPelsWidth
!= width
)
936 if (devmode
->dmFields
& DM_PELSHEIGHT
)
938 if (devmode
->dmPelsHeight
!= height
)
941 if ((devmode
->dmFields
& DM_DISPLAYFREQUENCY
) &&
942 devmode
->dmDisplayFrequency
!= 0 &&
943 devmode
->dmDisplayFrequency
!= 1)
945 double refresh_rate
= CGDisplayModeGetRefreshRate(display_mode
);
948 if (devmode
->dmDisplayFrequency
!= (DWORD
)refresh_rate
)
951 if (devmode
->dmFields
& DM_DISPLAYFLAGS
)
953 if (!(devmode
->dmDisplayFlags
& DM_INTERLACED
) != !(io_flags
& kDisplayModeInterlacedFlag
))
956 else if (best_display_mode
)
958 if (io_flags
& kDisplayModeInterlacedFlag
&& !(best_io_flags
& kDisplayModeInterlacedFlag
))
960 else if (!(io_flags
& kDisplayModeInterlacedFlag
) && best_io_flags
& kDisplayModeInterlacedFlag
)
963 if (devmode
->dmFields
& DM_DISPLAYFIXEDOUTPUT
)
965 if (!(devmode
->dmDisplayFixedOutput
== DMDFO_STRETCH
) != !(io_flags
& kDisplayModeStretchedFlag
))
968 else if (best_display_mode
)
970 if (io_flags
& kDisplayModeStretchedFlag
&& !(best_io_flags
& kDisplayModeStretchedFlag
))
972 else if (!(io_flags
& kDisplayModeStretchedFlag
) && best_io_flags
& kDisplayModeStretchedFlag
)
976 if (best_display_mode
)
980 best_display_mode
= display_mode
;
982 best_io_flags
= io_flags
;
983 best_is_original
= is_original
;
986 if (best_display_mode
)
988 /* we have a valid mode */
989 TRACE("Requested display settings match mode %ld\n", best
);
991 if ((flags
& CDS_UPDATEREGISTRY
) && !write_registry_settings(devname
, devmode
))
993 WARN("Failed to update registry\n");
994 ret
= DISP_CHANGE_NOTUPDATED
;
996 else if (flags
& (CDS_TEST
| CDS_NORESET
))
997 ret
= DISP_CHANGE_SUCCESSFUL
;
998 else if (wcsicmp(primary_adapter
, devname
))
1000 FIXME("Changing non-primary adapter settings is currently unsupported.\n");
1001 ret
= DISP_CHANGE_SUCCESSFUL
;
1003 else if (macdrv_set_display_mode(&displays
[0], best_display_mode
))
1005 int mode_bpp
= display_mode_bits_per_pixel(best_display_mode
);
1006 size_t width
= CGDisplayModeGetWidth(best_display_mode
);
1007 size_t height
= CGDisplayModeGetHeight(best_display_mode
);
1009 macdrv_init_display_devices(TRUE
);
1011 if (best_is_original
&& retina_enabled
)
1017 send_message(NtUserGetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT
, mode_bpp
,
1018 MAKELPARAM(width
, height
));
1019 ret
= DISP_CHANGE_SUCCESSFUL
;
1023 WARN("Failed to set display mode\n");
1024 ret
= DISP_CHANGE_FAILED
;
1029 /* no valid modes found */
1030 ERR("No matching mode found %ux%ux%d @%u!\n", devmode
->dmPelsWidth
, devmode
->dmPelsHeight
,
1031 bpp
, devmode
->dmDisplayFrequency
);
1034 free_display_mode_descriptor(desc
);
1035 CFRelease(display_modes
);
1036 macdrv_free_displays(displays
);
1041 /***********************************************************************
1042 * EnumDisplaySettingsEx (MACDRV.@)
1045 BOOL
macdrv_EnumDisplaySettingsEx(LPCWSTR devname
, DWORD mode
, DEVMODEW
*devmode
, DWORD flags
)
1047 static const WCHAR dev_name
[CCHDEVICENAME
] =
1048 { 'W','i','n','e',' ','M','a','c',' ','d','r','i','v','e','r',0 };
1049 struct macdrv_display
*displays
= NULL
;
1051 CGDisplayModeRef display_mode
;
1052 int display_mode_bpp
;
1053 BOOL synthesized
= FALSE
;
1057 TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname
), mode
, devmode
, devmode
->dmSize
, flags
);
1059 init_original_display_mode();
1061 memcpy(devmode
->dmDeviceName
, dev_name
, sizeof(dev_name
));
1062 devmode
->dmSpecVersion
= DM_SPECVERSION
;
1063 devmode
->dmDriverVersion
= DM_SPECVERSION
;
1064 devmode
->dmSize
= FIELD_OFFSET(DEVMODEW
, dmICMMethod
);
1065 devmode
->dmDriverExtra
= 0;
1066 memset(&devmode
->dmFields
, 0, devmode
->dmSize
- FIELD_OFFSET(DEVMODEW
, dmFields
));
1068 if (mode
== ENUM_REGISTRY_SETTINGS
)
1070 TRACE("mode %d (registry) -- getting default mode\n", mode
);
1071 return read_registry_settings(devname
, devmode
);
1074 if (macdrv_get_displays(&displays
, &num_displays
))
1077 if (mode
== ENUM_CURRENT_SETTINGS
)
1079 TRACE("mode %d (current) -- getting current mode\n", mode
);
1080 display_mode
= CGDisplayCopyDisplayMode(displays
[0].displayID
);
1081 display_mode_bpp
= display_mode_bits_per_pixel(display_mode
);
1087 pthread_mutex_lock(&modes_mutex
);
1089 if (mode
== 0 || !modes
)
1091 if (modes
) CFRelease(modes
);
1092 modes
= copy_display_modes(displays
[0].displayID
, (flags
& EDS_RAWMODE
) != 0);
1093 modes_has_8bpp
= modes_has_16bpp
= FALSE
;
1097 count
= CFArrayGetCount(modes
);
1098 for (i
= 0; i
< count
&& !(modes_has_8bpp
&& modes_has_16bpp
); i
++)
1100 CGDisplayModeRef mode
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(modes
, i
);
1101 int bpp
= display_mode_bits_per_pixel(mode
);
1103 modes_has_8bpp
= TRUE
;
1105 modes_has_16bpp
= TRUE
;
1110 display_mode
= NULL
;
1114 DWORD seen_modes
= 0;
1116 count
= CFArrayGetCount(modes
);
1117 for (i
= 0; i
< count
; i
++)
1119 CGDisplayModeRef candidate
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(modes
, i
);
1122 if (seen_modes
> mode
)
1124 display_mode
= (CGDisplayModeRef
)CFRetain(candidate
);
1125 display_mode_bpp
= display_mode_bits_per_pixel(display_mode
);
1130 default_bpp
= get_default_bpp();
1132 /* If all the real modes are exhausted, synthesize lower bpp modes. */
1133 if (!display_mode
&& (!modes_has_16bpp
|| !modes_has_8bpp
))
1135 /* We want to synthesize higher depths first. */
1136 int synth_bpps
[] = { modes_has_16bpp
? 0 : 16, modes_has_8bpp
? 0 : 8 };
1137 size_t synth_bpp_idx
;
1138 for (synth_bpp_idx
= 0; synth_bpp_idx
< 2; synth_bpp_idx
++)
1140 int synth_bpp
= synth_bpps
[synth_bpp_idx
];
1144 for (i
= 0; i
< count
; i
++)
1146 CGDisplayModeRef candidate
= (CGDisplayModeRef
)CFArrayGetValueAtIndex(modes
, i
);
1147 /* We only synthesize modes from those having the default bpp. */
1148 if (display_mode_bits_per_pixel(candidate
) != default_bpp
)
1152 if (seen_modes
> mode
)
1154 display_mode
= (CGDisplayModeRef
)CFRetain(candidate
);
1155 display_mode_bpp
= synth_bpp
;
1167 pthread_mutex_unlock(&modes_mutex
);
1173 /* We currently only report modes for the primary display, so it's at (0, 0). */
1174 devmode
->dmPosition
.x
= 0;
1175 devmode
->dmPosition
.y
= 0;
1176 devmode
->dmFields
|= DM_POSITION
;
1178 rotation
= CGDisplayRotation(displays
[0].displayID
);
1179 devmode
->dmDisplayOrientation
= ((int)((rotation
/ 90) + 0.5)) % 4;
1180 devmode
->dmFields
|= DM_DISPLAYORIENTATION
;
1182 io_flags
= CGDisplayModeGetIOFlags(display_mode
);
1183 if (io_flags
& kDisplayModeStretchedFlag
)
1184 devmode
->dmDisplayFixedOutput
= DMDFO_STRETCH
;
1186 devmode
->dmDisplayFixedOutput
= DMDFO_CENTER
;
1187 devmode
->dmFields
|= DM_DISPLAYFIXEDOUTPUT
;
1189 devmode
->dmBitsPerPel
= display_mode_bpp
;
1190 if (devmode
->dmBitsPerPel
)
1191 devmode
->dmFields
|= DM_BITSPERPEL
;
1193 devmode
->dmPelsWidth
= CGDisplayModeGetWidth(display_mode
);
1194 devmode
->dmPelsHeight
= CGDisplayModeGetHeight(display_mode
);
1197 struct display_mode_descriptor
* desc
= create_original_display_mode_descriptor(displays
[0].displayID
);
1198 if (display_mode_matches_descriptor(display_mode
, desc
))
1200 devmode
->dmPelsWidth
*= 2;
1201 devmode
->dmPelsHeight
*= 2;
1203 free_display_mode_descriptor(desc
);
1205 devmode
->dmFields
|= DM_PELSWIDTH
| DM_PELSHEIGHT
;
1207 devmode
->dmDisplayFlags
= 0;
1208 if (io_flags
& kDisplayModeInterlacedFlag
)
1209 devmode
->dmDisplayFlags
|= DM_INTERLACED
;
1210 devmode
->dmFields
|= DM_DISPLAYFLAGS
;
1212 devmode
->dmDisplayFrequency
= CGDisplayModeGetRefreshRate(display_mode
);
1213 if (!devmode
->dmDisplayFrequency
)
1214 devmode
->dmDisplayFrequency
= 60;
1215 devmode
->dmFields
|= DM_DISPLAYFREQUENCY
;
1217 CFRelease(display_mode
);
1218 macdrv_free_displays(displays
);
1220 TRACE("mode %d -- %dx%dx%dbpp @%d Hz", mode
,
1221 devmode
->dmPelsWidth
, devmode
->dmPelsHeight
, devmode
->dmBitsPerPel
,
1222 devmode
->dmDisplayFrequency
);
1223 if (devmode
->dmDisplayOrientation
)
1224 TRACE(" rotated %u degrees", devmode
->dmDisplayOrientation
* 90);
1225 if (devmode
->dmDisplayFixedOutput
== DMDFO_STRETCH
)
1226 TRACE(" stretched");
1227 if (devmode
->dmDisplayFlags
& DM_INTERLACED
)
1228 TRACE(" interlaced");
1230 TRACE(" (synthesized)");
1236 TRACE("mode %d -- not present\n", mode
);
1237 if (displays
) macdrv_free_displays(displays
);
1238 SetLastError(ERROR_NO_MORE_FILES
);
1243 /***********************************************************************
1244 * GetDeviceGammaRamp (MACDRV.@)
1246 BOOL
macdrv_GetDeviceGammaRamp(PHYSDEV dev
, LPVOID ramp
)
1249 DDGAMMARAMP
*r
= ramp
;
1250 struct macdrv_display
*displays
;
1252 uint32_t mac_entries
;
1253 int win_entries
= ARRAY_SIZE(r
->red
);
1254 CGGammaValue
*red
, *green
, *blue
;
1258 TRACE("dev %p ramp %p\n", dev
, ramp
);
1260 if (macdrv_get_displays(&displays
, &num_displays
))
1262 WARN("failed to get Mac displays\n");
1266 mac_entries
= CGDisplayGammaTableCapacity(displays
[0].displayID
);
1267 red
= malloc(mac_entries
* sizeof(red
[0]) * 3);
1270 green
= red
+ mac_entries
;
1271 blue
= green
+ mac_entries
;
1273 err
= CGGetDisplayTransferByTable(displays
[0].displayID
, mac_entries
, red
, green
,
1274 blue
, &mac_entries
);
1275 if (err
!= kCGErrorSuccess
)
1277 WARN("failed to get Mac gamma table: %d\n", err
);
1281 if (mac_entries
== win_entries
)
1283 for (win_entry
= 0; win_entry
< win_entries
; win_entry
++)
1285 r
->red
[win_entry
] = red
[win_entry
] * 65535 + 0.5;
1286 r
->green
[win_entry
] = green
[win_entry
] * 65535 + 0.5;
1287 r
->blue
[win_entry
] = blue
[win_entry
] * 65535 + 0.5;
1292 for (win_entry
= 0; win_entry
< win_entries
; win_entry
++)
1294 double mac_pos
= win_entry
* (mac_entries
- 1) / (double)(win_entries
- 1);
1295 int mac_entry
= mac_pos
;
1296 double red_value
, green_value
, blue_value
;
1298 if (mac_entry
== mac_entries
- 1)
1300 red_value
= red
[mac_entry
];
1301 green_value
= green
[mac_entry
];
1302 blue_value
= blue
[mac_entry
];
1306 double distance
= mac_pos
- mac_entry
;
1308 red_value
= red
[mac_entry
] * (1 - distance
) + red
[mac_entry
+ 1] * distance
;
1309 green_value
= green
[mac_entry
] * (1 - distance
) + green
[mac_entry
+ 1] * distance
;
1310 blue_value
= blue
[mac_entry
] * (1 - distance
) + blue
[mac_entry
+ 1] * distance
;
1313 r
->red
[win_entry
] = red_value
* 65535 + 0.5;
1314 r
->green
[win_entry
] = green_value
* 65535 + 0.5;
1315 r
->blue
[win_entry
] = blue_value
* 65535 + 0.5;
1323 macdrv_free_displays(displays
);
1327 /***********************************************************************
1328 * SetDeviceGammaRamp (MACDRV.@)
1330 BOOL
macdrv_SetDeviceGammaRamp(PHYSDEV dev
, LPVOID ramp
)
1332 DDGAMMARAMP
*r
= ramp
;
1333 struct macdrv_display
*displays
;
1335 int win_entries
= ARRAY_SIZE(r
->red
);
1336 CGGammaValue
*red
, *green
, *blue
;
1338 CGError err
= kCGErrorFailure
;
1340 TRACE("dev %p ramp %p\n", dev
, ramp
);
1342 if (!allow_set_gamma
)
1344 TRACE("disallowed by registry setting\n");
1348 if (macdrv_get_displays(&displays
, &num_displays
))
1350 WARN("failed to get Mac displays\n");
1354 red
= malloc(win_entries
* sizeof(red
[0]) * 3);
1357 green
= red
+ win_entries
;
1358 blue
= green
+ win_entries
;
1360 for (i
= 0; i
< win_entries
; i
++)
1362 red
[i
] = r
->red
[i
] / 65535.0;
1363 green
[i
] = r
->green
[i
] / 65535.0;
1364 blue
[i
] = r
->blue
[i
] / 65535.0;
1367 err
= CGSetDisplayTransferByTable(displays
[0].displayID
, win_entries
, red
, green
, blue
);
1368 if (err
!= kCGErrorSuccess
)
1369 WARN("failed to set display gamma table: %d\n", err
);
1373 macdrv_free_displays(displays
);
1374 return (err
== kCGErrorSuccess
);
1377 /***********************************************************************
1378 * init_registry_display_settings
1380 * Initialize registry display settings when new display devices are added.
1382 static void init_registry_display_settings(void)
1384 DEVMODEW dm
= {.dmSize
= sizeof(dm
)};
1385 DISPLAY_DEVICEW dd
= {sizeof(dd
)};
1390 while (!NtUserEnumDisplayDevices(NULL
, i
++, &dd
, 0))
1392 RtlInitUnicodeString(&str
, dd
.DeviceName
);
1394 /* Skip if the device already has registry display settings */
1395 if (NtUserEnumDisplaySettings(&str
, ENUM_REGISTRY_SETTINGS
, &dm
, 0))
1398 if (!NtUserEnumDisplaySettings(&str
, ENUM_CURRENT_SETTINGS
, &dm
, 0))
1400 ERR("Failed to query current display settings for %s.\n", wine_dbgstr_w(dd
.DeviceName
));
1404 TRACE("Device %s current display mode %ux%u %ubits %uHz at %d,%d.\n",
1405 wine_dbgstr_w(dd
.DeviceName
), dm
.dmPelsWidth
, dm
.dmPelsHeight
, dm
.dmBitsPerPel
,
1406 dm
.dmDisplayFrequency
, dm
.dmPosition
.x
, dm
.dmPosition
.y
);
1408 ret
= NtUserChangeDisplaySettings(&str
, &dm
, NULL
,
1409 CDS_GLOBAL
| CDS_NORESET
| CDS_UPDATEREGISTRY
, NULL
);
1410 if (ret
!= DISP_CHANGE_SUCCESSFUL
)
1411 ERR("Failed to save registry display settings for %s, returned %d.\n",
1412 wine_dbgstr_w(dd
.DeviceName
), ret
);
1416 /***********************************************************************
1417 * macdrv_displays_changed
1419 * Handler for DISPLAYS_CHANGED events.
1421 void macdrv_displays_changed(const macdrv_event
*event
)
1423 HWND hwnd
= NtUserGetDesktopWindow();
1425 /* A system display change will get delivered to all GUI-attached threads,
1426 so the desktop-window-owning thread will get it and all others should
1427 ignore it. A synthesized display change event due to activation
1428 will only get delivered to the activated process. So, it needs to
1429 process it (by sending it to the desktop window). */
1430 if (event
->displays_changed
.activating
||
1431 NtUserGetWindowThread(hwnd
, NULL
) == GetCurrentThreadId())
1433 CGDirectDisplayID mainDisplay
= CGMainDisplayID();
1434 CGDisplayModeRef mode
= CGDisplayCopyDisplayMode(mainDisplay
);
1435 size_t width
= CGDisplayModeGetWidth(mode
);
1436 size_t height
= CGDisplayModeGetHeight(mode
);
1437 int mode_bpp
= display_mode_bits_per_pixel(mode
);
1438 struct display_mode_descriptor
* desc
= create_original_display_mode_descriptor(mainDisplay
);
1439 BOOL is_original
= display_mode_matches_descriptor(mode
, desc
);
1441 free_display_mode_descriptor(desc
);
1442 CGDisplayModeRelease(mode
);
1444 macdrv_init_display_devices(TRUE
);
1445 init_registry_display_settings();
1447 if (is_original
&& retina_enabled
)
1453 send_message(hwnd
, WM_MACDRV_UPDATE_DESKTOP_RECT
, mode_bpp
,
1454 MAKELPARAM(width
, height
));
1458 static BOOL force_display_devices_refresh
;
1460 void macdrv_UpdateDisplayDevices( const struct gdi_device_manager
*device_manager
,
1461 BOOL force
, void *param
)
1463 struct macdrv_adapter
*adapters
, *adapter
;
1464 struct macdrv_monitor
*monitors
, *monitor
;
1465 struct macdrv_gpu
*gpus
, *gpu
;
1466 INT gpu_count
, adapter_count
, monitor_count
;
1469 if (!force
&& !force_display_devices_refresh
) return;
1470 force_display_devices_refresh
= FALSE
;
1472 /* Initialize GPUs */
1473 if (macdrv_get_gpus(&gpus
, &gpu_count
))
1475 ERR("could not get GPUs\n");
1478 TRACE("GPU count: %d\n", gpu_count
);
1480 for (gpu
= gpus
; gpu
< gpus
+ gpu_count
; gpu
++)
1482 struct gdi_gpu gdi_gpu
=
1485 .vendor_id
= gpu
->vendor_id
,
1486 .device_id
= gpu
->device_id
,
1487 .subsys_id
= gpu
->subsys_id
,
1488 .revision_id
= gpu
->revision_id
,
1490 RtlUTF8ToUnicodeN(gdi_gpu
.name
, sizeof(gdi_gpu
.name
), &len
, gpu
->name
, strlen(gpu
->name
));
1491 device_manager
->add_gpu(&gdi_gpu
, param
);
1493 /* Initialize adapters */
1494 if (macdrv_get_adapters(gpu
->id
, &adapters
, &adapter_count
)) break;
1495 TRACE("GPU: %llx %s, adapter count: %d\n", gpu
->id
, debugstr_a(gpu
->name
), adapter_count
);
1497 for (adapter
= adapters
; adapter
< adapters
+ adapter_count
; adapter
++)
1499 struct gdi_adapter gdi_adapter
=
1502 .state_flags
= adapter
->state_flags
,
1504 device_manager
->add_adapter( &gdi_adapter
, param
);
1506 if (macdrv_get_monitors(adapter
->id
, &monitors
, &monitor_count
)) break;
1507 TRACE("adapter: %#x, monitor count: %d\n", adapter
->id
, monitor_count
);
1509 /* Initialize monitors */
1510 for (monitor
= monitors
; monitor
< monitors
+ monitor_count
; monitor
++)
1512 struct gdi_monitor gdi_monitor
=
1514 .rc_monitor
= rect_from_cgrect(monitor
->rc_monitor
),
1515 .rc_work
= rect_from_cgrect(monitor
->rc_work
),
1516 .state_flags
= monitor
->state_flags
,
1518 RtlUTF8ToUnicodeN(gdi_monitor
.name
, sizeof(gdi_monitor
.name
), &len
,
1519 monitor
->name
, strlen(monitor
->name
));
1520 TRACE("monitor: %s\n", debugstr_a(monitor
->name
));
1521 device_manager
->add_monitor( &gdi_monitor
, param
);
1524 macdrv_free_monitors(monitors
);
1527 macdrv_free_adapters(adapters
);
1530 macdrv_free_gpus(gpus
);
1533 /***********************************************************************
1534 * macdrv_init_display_devices
1536 * Initialize display device registry data.
1538 void macdrv_init_display_devices(BOOL force
)
1540 UINT32 num_path
, num_mode
;
1542 if (force
) force_display_devices_refresh
= TRUE
;
1543 /* trigger refresh in win32u */
1544 NtUserGetDisplayConfigBufferSizes( QDC_ONLY_ACTIVE_PATHS
, &num_path
, &num_mode
);