From dbb0bacf6731304f074f492438982949449a5266 Mon Sep 17 00:00:00 2001 From: Ken Thomases Date: Mon, 26 Oct 2015 23:39:06 -0500 Subject: [PATCH] winemac: Fix how the user's default display mode is determined. The code had been checking the kDisplayModeDefaultFlag in the mode's IOFlags, but that doesn't do what I thought. That indicates which mode the driver considers to be the default for the hardware. It turns out there's no way to query the user's default mode. So, at the first opportunity during a given Wine session, the Mac driver queries the current display mode and assumes that's the user's default mode. It records that in a volatile registry key for use by subsequent processes during that same session. This doesn't use the existing registry key under HKEY_CURRENT_CONFIG\System\CurrentControlSet\Control\Video that records the mode when CDS_UPDATEREGISTRY is used with ChangeDisplaySettingsEx() -- which explorer.exe does during start-up -- because a) that doesn't support the distinction between pixel size and point size for Retina modes, and b) in theory, apps could overwrite that. Signed-off-by: Ken Thomases Signed-off-by: Alexandre Julliard --- dlls/winemac.drv/display.c | 282 ++++++++++++++++++++++++++++++++++++- 1 file changed, 278 insertions(+), 4 deletions(-) diff --git a/dlls/winemac.drv/display.c b/dlls/winemac.drv/display.c index d561e689ac..e1357d8942 100644 --- a/dlls/winemac.drv/display.c +++ b/dlls/winemac.drv/display.c @@ -25,13 +25,30 @@ #include "winuser.h" #include "winreg.h" #include "ddrawi.h" +#include "wine/unicode.h" WINE_DEFAULT_DEBUG_CHANNEL(display); +struct display_mode_descriptor +{ + DWORD width; + DWORD height; + DWORD pixel_width; + DWORD pixel_height; + DWORD io_flags; + double refresh; + CFStringRef pixel_encoding; +}; + + BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW devmode, DWORD flags); +static const char initial_mode_key[] = "Initial Display Mode"; +static const WCHAR pixelencodingW[] = {'P','i','x','e','l','E','n','c','o','d','i','n','g',0}; + + static CFArrayRef modes; static BOOL modes_has_8bpp, modes_has_16bpp; static int default_mode_bpp; @@ -44,6 +61,8 @@ static CRITICAL_SECTION_DEBUG critsect_debug = }; static CRITICAL_SECTION modes_section = { &critsect_debug, -1, 0, 0, 0, 0 }; +static BOOL inited_original_display_mode; + static inline HMONITOR display_id_to_monitor(CGDirectDisplayID display_id) { @@ -157,6 +176,253 @@ static BOOL write_registry_settings(const DEVMODEW *dm) } +static BOOL write_display_settings(HKEY parent_hkey, CGDirectDisplayID displayID) +{ + BOOL ret = FALSE; + char display_key_name[19]; + HKEY display_hkey; + CGDisplayModeRef display_mode; + DWORD val; + CFStringRef pixel_encoding; + size_t len; + WCHAR* buf = NULL; + + snprintf(display_key_name, sizeof(display_key_name), "Display 0x%08x", displayID); + /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */ + if (RegCreateKeyExA(parent_hkey, display_key_name, 0, NULL, + REG_OPTION_VOLATILE, KEY_WRITE, NULL, &display_hkey, NULL)) + return FALSE; + + display_mode = CGDisplayCopyDisplayMode(displayID); + if (!display_mode) + goto fail; + + val = CGDisplayModeGetWidth(display_mode); + if (RegSetValueExA(display_hkey, "Width", 0, REG_DWORD, (const BYTE*)&val, sizeof(val))) + goto fail; + val = CGDisplayModeGetHeight(display_mode); + if (RegSetValueExA(display_hkey, "Height", 0, REG_DWORD, (const BYTE*)&val, sizeof(val))) + goto fail; + val = CGDisplayModeGetRefreshRate(display_mode) * 100; + if (RegSetValueExA(display_hkey, "RefreshRateTimes100", 0, REG_DWORD, (const BYTE*)&val, sizeof(val))) + goto fail; + val = CGDisplayModeGetIOFlags(display_mode); + if (RegSetValueExA(display_hkey, "IOFlags", 0, REG_DWORD, (const BYTE*)&val, sizeof(val))) + goto fail; + +#if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + if (CGDisplayModeGetPixelWidth != NULL && CGDisplayModeGetPixelHeight != NULL) + { + val = CGDisplayModeGetPixelWidth(display_mode); + if (RegSetValueExA(display_hkey, "PixelWidth", 0, REG_DWORD, (const BYTE*)&val, sizeof(val))) + goto fail; + val = CGDisplayModeGetPixelHeight(display_mode); + if (RegSetValueExA(display_hkey, "PixelHeight", 0, REG_DWORD, (const BYTE*)&val, sizeof(val))) + goto fail; + } +#endif + + pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode); + len = CFStringGetLength(pixel_encoding); + buf = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR)); + CFStringGetCharacters(pixel_encoding, CFRangeMake(0, len), (UniChar*)buf); + buf[len] = 0; + CFRelease(pixel_encoding); + if (RegSetValueExW(display_hkey, pixelencodingW, 0, REG_SZ, (const BYTE*)buf, (len + 1) * sizeof(WCHAR))) + goto fail; + + ret = TRUE; + +fail: + HeapFree(GetProcessHeap(), 0, buf); + if (display_mode) CGDisplayModeRelease(display_mode); + RegCloseKey(display_hkey); + if (!ret) + RegDeleteKeyA(parent_hkey, display_key_name); + return ret; +} + + +static void init_original_display_mode(void) +{ + BOOL success = FALSE; + HKEY mac_driver_hkey, parent_hkey; + DWORD disposition; + struct macdrv_display *displays = NULL; + int num_displays, i; + + if (inited_original_display_mode) + return; + + /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver */ + if (RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Mac Driver", 0, NULL, + 0, KEY_ALL_ACCESS, NULL, &mac_driver_hkey, NULL)) + return; + + /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode */ + if (RegCreateKeyExA(mac_driver_hkey, initial_mode_key, 0, NULL, + REG_OPTION_VOLATILE, KEY_WRITE, NULL, &parent_hkey, &disposition)) + { + parent_hkey = NULL; + goto fail; + } + + /* If we didn't create a new key, then it already existed. Something already stored + the initial display mode since Wine was started. We don't want to overwrite it. */ + if (disposition != REG_CREATED_NEW_KEY) + goto done; + + if (macdrv_get_displays(&displays, &num_displays)) + goto fail; + + for (i = 0; i < num_displays; i++) + { + if (!write_display_settings(parent_hkey, displays[i].displayID)) + goto fail; + } + +done: + success = TRUE; + +fail: + macdrv_free_displays(displays); + RegCloseKey(parent_hkey); + if (!success && parent_hkey) + RegDeleteTreeA(mac_driver_hkey, initial_mode_key); + RegCloseKey(mac_driver_hkey); + if (success) + inited_original_display_mode = TRUE; +} + + +static BOOL read_dword(HKEY hkey, const char* name, DWORD* val) +{ + DWORD type, size = sizeof(*val); + if (RegQueryValueExA(hkey, name, 0, &type, (BYTE*)val, &size) || type != REG_DWORD || size != sizeof(*val)) + return FALSE; + return TRUE; +} + + +static void free_display_mode_descriptor(struct display_mode_descriptor* desc) +{ + if (desc) + { + if (desc->pixel_encoding) + CFRelease(desc->pixel_encoding); + HeapFree(GetProcessHeap(), 0, desc); + } +} + + +static struct display_mode_descriptor* create_original_display_mode_descriptor(CGDirectDisplayID displayID) +{ + static const char display_key_format[] = "Software\\Wine\\Mac Driver\\Initial Display Mode\\Display 0x%08x"; + struct display_mode_descriptor* ret = NULL; + struct display_mode_descriptor* desc; + char display_key[sizeof(display_key_format) + 10]; + HKEY hkey; + DWORD type, size; + DWORD refresh100; + WCHAR* pixel_encoding = NULL, *end; + + init_original_display_mode(); + + snprintf(display_key, sizeof(display_key), display_key_format, displayID); + /* @@ Wine registry key: HKLM\Software\Wine\Mac Driver\Initial Display Mode\Display 0xnnnnnnnn */ + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, display_key, 0, KEY_READ, &hkey)) + return NULL; + + desc = HeapAlloc(GetProcessHeap(), 0, sizeof(*desc)); + desc->pixel_encoding = NULL; + + if (!read_dword(hkey, "Width", &desc->width) || + !read_dword(hkey, "Height", &desc->height) || + !read_dword(hkey, "RefreshRateTimes100", &refresh100) || + !read_dword(hkey, "IOFlags", &desc->io_flags)) + goto done; + if (refresh100) + desc->refresh = refresh100 / 100.0; + else + desc->refresh = 60; + + if (!read_dword(hkey, "PixelWidth", &desc->pixel_width) || + !read_dword(hkey, "PixelHeight", &desc->pixel_height)) + { + desc->pixel_width = desc->width; + desc->pixel_height = desc->height; + } + + size = 0; + if (RegQueryValueExW(hkey, pixelencodingW, 0, &type, NULL, &size) || type != REG_SZ) + goto done; + pixel_encoding = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); + if (RegQueryValueExW(hkey, pixelencodingW, 0, &type, (BYTE*)pixel_encoding, &size) || type != REG_SZ) + goto done; + if ((end = memchrW(pixel_encoding, 0, size))) + size = end - pixel_encoding; + desc->pixel_encoding = CFStringCreateWithCharacters(NULL, (const UniChar*)pixel_encoding, size); + + ret = desc; + +done: + if (!ret) + free_display_mode_descriptor(desc); + HeapFree(GetProcessHeap(), 0, pixel_encoding); + RegCloseKey(hkey); + return ret; +} + + +static BOOL display_mode_matches_descriptor(CGDisplayModeRef mode, const struct display_mode_descriptor* desc) +{ + DWORD mode_io_flags; + double mode_refresh; + CFStringRef mode_pixel_encoding; + + if (!desc) + return FALSE; + + if (CGDisplayModeGetWidth(mode) != desc->width || + CGDisplayModeGetHeight(mode) != desc->height) + return FALSE; + + mode_io_flags = CGDisplayModeGetIOFlags(mode); + if ((desc->io_flags ^ mode_io_flags) & (kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeStretchedFlag | + kDisplayModeInterlacedFlag | kDisplayModeTelevisionFlag)) + return FALSE; + + mode_refresh = CGDisplayModeGetRefreshRate(mode); + if (!mode_refresh) + mode_refresh = 60; + if (fabs(desc->refresh - mode_refresh) > 0.1) + return FALSE; + +#if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + if (CGDisplayModeGetPixelWidth != NULL && CGDisplayModeGetPixelHeight != NULL) + { + if (CGDisplayModeGetPixelWidth(mode) != desc->pixel_width || + CGDisplayModeGetPixelHeight(mode) != desc->pixel_height) + return FALSE; + } + else +#endif + if (CGDisplayModeGetWidth(mode) != desc->pixel_width || + CGDisplayModeGetHeight(mode) != desc->pixel_height) + return FALSE; + + mode_pixel_encoding = CGDisplayModeCopyPixelEncoding(mode); + if (!CFEqual(mode_pixel_encoding, desc->pixel_encoding)) + { + CFRelease(mode_pixel_encoding); + return FALSE; + } + CFRelease(mode_pixel_encoding); + + return TRUE; +} + + static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode) { CFStringRef pixel_encoding; @@ -291,6 +557,7 @@ static CFArrayRef copy_display_modes(CGDirectDisplayID display) CGDisplayModeGetPixelWidth != NULL && CGDisplayModeGetPixelHeight != NULL) { CFDictionaryRef options; + struct display_mode_descriptor* desc; CFMutableDictionaryRef modes_by_size; CFIndex i, count; CGDisplayModeRef* mode_array; @@ -305,18 +572,20 @@ static CFArrayRef copy_display_modes(CGDirectDisplayID display) if (!modes) return NULL; + desc = create_original_display_mode_descriptor(display); + modes_by_size = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); count = CFArrayGetCount(modes); for (i = 0; i < count; i++) { BOOL better = TRUE; CGDisplayModeRef new_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i); - uint32_t new_flags = CGDisplayModeGetIOFlags(new_mode); + BOOL new_is_original = display_mode_matches_descriptor(new_mode, desc); CFDictionaryRef key = create_mode_dict(new_mode); /* If a given mode is the user's default, then always list it in preference to any similar modes that may exist. */ - if (new_flags & kDisplayModeDefaultFlag) + if (new_is_original) better = TRUE; else { @@ -343,9 +612,9 @@ static CFArrayRef copy_display_modes(CGDirectDisplayID display) old_mode = (CGDisplayModeRef)CFDictionaryGetValue(modes_by_size, key); if (old_mode) { - uint32_t old_flags = CGDisplayModeGetIOFlags(old_mode); + BOOL old_is_original = display_mode_matches_descriptor(old_mode, desc); - if (old_flags & kDisplayModeDefaultFlag) + if (old_is_original) better = FALSE; else { @@ -380,6 +649,7 @@ static CFArrayRef copy_display_modes(CGDirectDisplayID display) CFRelease(key); } + free_display_mode_descriptor(desc); CFRelease(modes); count = CFDictionaryGetCount(modes_by_size); @@ -417,6 +687,8 @@ LONG CDECL macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode, TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid); + init_original_display_mode(); + if (devmode) { /* this is the minimal dmSize that XP accepts */ @@ -677,6 +949,8 @@ BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname), mode, devmode, devmode->dmSize, flags); + init_original_display_mode(); + memcpy(devmode->dmDeviceName, dev_name, sizeof(dev_name)); devmode->dmSpecVersion = DM_SPECVERSION; devmode->dmDriverVersion = DM_SPECVERSION;