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 <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Ken Thomases 2015-10-26 23:39:06 -05:00 committed by Alexandre Julliard
parent 02c6676864
commit dbb0bacf67
1 changed files with 278 additions and 4 deletions

View File

@ -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;