mirror of
https://github.com/reactos/wine.git
synced 2024-11-24 20:30:01 +00:00
1063 lines
36 KiB
C
1063 lines
36 KiB
C
/*
|
|
* MACDRV display settings
|
|
*
|
|
* Copyright 2003 Alexander James Pasadyn
|
|
* Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "macdrv.h"
|
|
#include "winuser.h"
|
|
#include "winreg.h"
|
|
#include "ddrawi.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(display);
|
|
|
|
|
|
BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW devmode, DWORD flags);
|
|
|
|
|
|
static CFArrayRef modes;
|
|
static BOOL modes_has_8bpp, modes_has_16bpp;
|
|
static int default_mode_bpp;
|
|
static CRITICAL_SECTION modes_section;
|
|
static CRITICAL_SECTION_DEBUG critsect_debug =
|
|
{
|
|
0, 0, &modes_section,
|
|
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
|
|
0, 0, { (DWORD_PTR)(__FILE__ ": modes_section") }
|
|
};
|
|
static CRITICAL_SECTION modes_section = { &critsect_debug, -1, 0, 0, 0, 0 };
|
|
|
|
|
|
static inline HMONITOR display_id_to_monitor(CGDirectDisplayID display_id)
|
|
{
|
|
return (HMONITOR)(UINT_PTR)display_id;
|
|
}
|
|
|
|
static inline CGDirectDisplayID monitor_to_display_id(HMONITOR handle)
|
|
{
|
|
return (CGDirectDisplayID)(UINT_PTR)handle;
|
|
}
|
|
|
|
|
|
static BOOL get_display_device_reg_key(char *key, unsigned len)
|
|
{
|
|
static const char display_device_guid_prop[] = "__wine_display_device_guid";
|
|
static const char video_path[] = "System\\CurrentControlSet\\Control\\Video\\{";
|
|
static const char display0[] = "}\\0000";
|
|
ATOM guid_atom;
|
|
|
|
assert(len >= sizeof(video_path) + sizeof(display0) + 40);
|
|
|
|
guid_atom = HandleToULong(GetPropA(GetDesktopWindow(), display_device_guid_prop));
|
|
if (!guid_atom) return FALSE;
|
|
|
|
memcpy(key, video_path, sizeof(video_path));
|
|
|
|
if (!GlobalGetAtomNameA(guid_atom, key + strlen(key), 40))
|
|
return FALSE;
|
|
|
|
strcat(key, display0);
|
|
|
|
TRACE("display device key %s\n", wine_dbgstr_a(key));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static BOOL read_registry_settings(DEVMODEW *dm)
|
|
{
|
|
char wine_mac_reg_key[128];
|
|
HKEY hkey;
|
|
DWORD type, size;
|
|
BOOL ret = TRUE;
|
|
|
|
dm->dmFields = 0;
|
|
|
|
if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
|
|
return FALSE;
|
|
|
|
if (RegOpenKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, KEY_READ, &hkey))
|
|
return FALSE;
|
|
|
|
#define query_value(name, data) \
|
|
size = sizeof(DWORD); \
|
|
if (RegQueryValueExA(hkey, name, 0, &type, (LPBYTE)(data), &size) || \
|
|
type != REG_DWORD || size != sizeof(DWORD)) \
|
|
ret = FALSE
|
|
|
|
query_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
|
|
dm->dmFields |= DM_BITSPERPEL;
|
|
query_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
|
|
dm->dmFields |= DM_PELSWIDTH;
|
|
query_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
|
|
dm->dmFields |= DM_PELSHEIGHT;
|
|
query_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
|
|
dm->dmFields |= DM_DISPLAYFREQUENCY;
|
|
query_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
|
|
dm->dmFields |= DM_DISPLAYFLAGS;
|
|
query_value("DefaultSettings.XPanning", &dm->dmPosition.x);
|
|
query_value("DefaultSettings.YPanning", &dm->dmPosition.y);
|
|
query_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
|
|
query_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
|
|
|
|
#undef query_value
|
|
|
|
RegCloseKey(hkey);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static BOOL write_registry_settings(const DEVMODEW *dm)
|
|
{
|
|
char wine_mac_reg_key[128];
|
|
HKEY hkey;
|
|
BOOL ret = TRUE;
|
|
|
|
if (!get_display_device_reg_key(wine_mac_reg_key, sizeof(wine_mac_reg_key)))
|
|
return FALSE;
|
|
|
|
if (RegCreateKeyExA(HKEY_CURRENT_CONFIG, wine_mac_reg_key, 0, NULL,
|
|
REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL))
|
|
return FALSE;
|
|
|
|
#define set_value(name, data) \
|
|
if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
|
|
ret = FALSE
|
|
|
|
set_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
|
|
set_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
|
|
set_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
|
|
set_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
|
|
set_value("DefaultSettings.Flags", &dm->dmDisplayFlags);
|
|
set_value("DefaultSettings.XPanning", &dm->dmPosition.x);
|
|
set_value("DefaultSettings.YPanning", &dm->dmPosition.y);
|
|
set_value("DefaultSettings.Orientation", &dm->dmDisplayOrientation);
|
|
set_value("DefaultSettings.FixedOutput", &dm->dmDisplayFixedOutput);
|
|
|
|
#undef set_value
|
|
|
|
RegCloseKey(hkey);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int display_mode_bits_per_pixel(CGDisplayModeRef display_mode)
|
|
{
|
|
CFStringRef pixel_encoding;
|
|
int bits_per_pixel = 0;
|
|
|
|
pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
|
|
if (pixel_encoding)
|
|
{
|
|
if (CFEqual(pixel_encoding, CFSTR(kIO32BitFloatPixels)))
|
|
bits_per_pixel = 128;
|
|
else if (CFEqual(pixel_encoding, CFSTR(kIO16BitFloatPixels)))
|
|
bits_per_pixel = 64;
|
|
else if (CFEqual(pixel_encoding, CFSTR(kIO64BitDirectPixels)))
|
|
bits_per_pixel = 64;
|
|
else if (CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels)))
|
|
bits_per_pixel = 30;
|
|
else if (CFEqual(pixel_encoding, CFSTR(IO32BitDirectPixels)))
|
|
bits_per_pixel = 32;
|
|
else if (CFEqual(pixel_encoding, CFSTR(IO16BitDirectPixels)))
|
|
bits_per_pixel = 16;
|
|
else if (CFEqual(pixel_encoding, CFSTR(IO8BitIndexedPixels)))
|
|
bits_per_pixel = 8;
|
|
else if (CFEqual(pixel_encoding, CFSTR(IO4BitIndexedPixels)))
|
|
bits_per_pixel = 4;
|
|
else if (CFEqual(pixel_encoding, CFSTR(IO2BitIndexedPixels)))
|
|
bits_per_pixel = 2;
|
|
else if (CFEqual(pixel_encoding, CFSTR(IO1BitIndexedPixels)))
|
|
bits_per_pixel = 1;
|
|
|
|
CFRelease(pixel_encoding);
|
|
}
|
|
|
|
return bits_per_pixel;
|
|
}
|
|
|
|
|
|
static int get_default_bpp(void)
|
|
{
|
|
int ret;
|
|
|
|
EnterCriticalSection(&modes_section);
|
|
|
|
if (!default_mode_bpp)
|
|
{
|
|
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
|
|
if (mode)
|
|
{
|
|
default_mode_bpp = display_mode_bits_per_pixel(mode);
|
|
CFRelease(mode);
|
|
}
|
|
|
|
if (!default_mode_bpp)
|
|
default_mode_bpp = 32;
|
|
}
|
|
|
|
ret = default_mode_bpp;
|
|
|
|
LeaveCriticalSection(&modes_section);
|
|
|
|
TRACE(" -> %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
|
|
static CFDictionaryRef create_mode_dict(CGDisplayModeRef display_mode)
|
|
{
|
|
CFDictionaryRef ret;
|
|
SInt32 io_flags = CGDisplayModeGetIOFlags(display_mode);
|
|
SInt64 width = CGDisplayModeGetWidth(display_mode);
|
|
SInt64 height = CGDisplayModeGetHeight(display_mode);
|
|
double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
|
|
CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(display_mode);
|
|
CFNumberRef cf_io_flags, cf_width, cf_height, cf_refresh;
|
|
|
|
io_flags &= kDisplayModeValidFlag | kDisplayModeSafeFlag | kDisplayModeInterlacedFlag |
|
|
kDisplayModeStretchedFlag | kDisplayModeTelevisionFlag;
|
|
cf_io_flags = CFNumberCreate(NULL, kCFNumberSInt32Type, &io_flags);
|
|
cf_width = CFNumberCreate(NULL, kCFNumberSInt64Type, &width);
|
|
cf_height = CFNumberCreate(NULL, kCFNumberSInt64Type, &height);
|
|
cf_refresh = CFNumberCreate(NULL, kCFNumberDoubleType, &refresh_rate);
|
|
|
|
{
|
|
static const CFStringRef keys[] = {
|
|
CFSTR("io_flags"),
|
|
CFSTR("width"),
|
|
CFSTR("height"),
|
|
CFSTR("pixel_encoding"),
|
|
CFSTR("refresh_rate"),
|
|
};
|
|
const void* values[sizeof(keys) / sizeof(keys[0])] = {
|
|
cf_io_flags,
|
|
cf_width,
|
|
cf_height,
|
|
pixel_encoding,
|
|
cf_refresh,
|
|
};
|
|
|
|
ret = CFDictionaryCreate(NULL, (const void**)keys, (const void**)values, sizeof(keys) / sizeof(keys[0]),
|
|
&kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
|
}
|
|
|
|
CFRelease(pixel_encoding);
|
|
CFRelease(cf_io_flags);
|
|
CFRelease(cf_width);
|
|
CFRelease(cf_height);
|
|
CFRelease(cf_refresh);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
/***********************************************************************
|
|
* copy_display_modes
|
|
*
|
|
* Wrapper around CGDisplayCopyAllDisplayModes() to include additional
|
|
* modes on Retina-capable systems, but filter those which would confuse
|
|
* Windows apps (basically duplicates at different DPIs).
|
|
*
|
|
* For example, some Retina Macs support a 1920x1200 mode, but it's not
|
|
* returned from CGDisplayCopyAllDisplayModes() without special options.
|
|
* This is especially bad if that's the user's default mode, since then
|
|
* no "available" mode matches the initial settings.
|
|
*/
|
|
static CFArrayRef copy_display_modes(CGDirectDisplayID display)
|
|
{
|
|
CFArrayRef modes = NULL;
|
|
|
|
#if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
|
|
if (&kCGDisplayShowDuplicateLowResolutionModes != NULL &&
|
|
CGDisplayModeGetPixelWidth != NULL && CGDisplayModeGetPixelHeight != NULL)
|
|
{
|
|
CFDictionaryRef options;
|
|
CFMutableDictionaryRef modes_by_size;
|
|
CFIndex i, count;
|
|
CGDisplayModeRef* mode_array;
|
|
|
|
options = CFDictionaryCreate(NULL, (const void**)&kCGDisplayShowDuplicateLowResolutionModes,
|
|
(const void**)&kCFBooleanTrue, 1, &kCFTypeDictionaryKeyCallBacks,
|
|
&kCFTypeDictionaryValueCallBacks);
|
|
|
|
modes = CGDisplayCopyAllDisplayModes(display, options);
|
|
if (options)
|
|
CFRelease(options);
|
|
if (!modes)
|
|
return NULL;
|
|
|
|
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);
|
|
CFStringRef pixel_encoding;
|
|
size_t width_points;
|
|
size_t height_points;
|
|
CFDictionaryRef key;
|
|
CGDisplayModeRef old_mode;
|
|
|
|
if (!(new_flags & kDisplayModeDefaultFlag) && (pixel_encoding = CGDisplayModeCopyPixelEncoding(new_mode)))
|
|
{
|
|
BOOL bpp30 = CFEqual(pixel_encoding, CFSTR(kIO30BitDirectPixels));
|
|
CFRelease(pixel_encoding);
|
|
if (bpp30)
|
|
{
|
|
/* This is an odd pixel encoding. It seems it's only returned
|
|
when using kCGDisplayShowDuplicateLowResolutionModes. It's
|
|
32bpp in terms of the actual raster layout, but it's 10
|
|
bits per component. I think that no Windows program is
|
|
likely to need it and they will probably be confused by it.
|
|
Skip it. */
|
|
continue;
|
|
}
|
|
}
|
|
|
|
width_points = CGDisplayModeGetWidth(new_mode);
|
|
height_points = CGDisplayModeGetHeight(new_mode);
|
|
key = create_mode_dict(new_mode);
|
|
old_mode = (CGDisplayModeRef)CFDictionaryGetValue(modes_by_size, key);
|
|
|
|
if (old_mode)
|
|
{
|
|
uint32_t old_flags = CGDisplayModeGetIOFlags(old_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) && !(old_flags & kDisplayModeDefaultFlag))
|
|
better = TRUE;
|
|
else if (!(new_flags & kDisplayModeDefaultFlag) && (old_flags & kDisplayModeDefaultFlag))
|
|
better = FALSE;
|
|
else
|
|
{
|
|
/* Otherwise, prefer a mode whose pixel size equals its point size over one which
|
|
is scaled. */
|
|
size_t new_width_pixels = CGDisplayModeGetPixelWidth(new_mode);
|
|
size_t new_height_pixels = CGDisplayModeGetPixelHeight(new_mode);
|
|
size_t old_width_pixels = CGDisplayModeGetPixelWidth(old_mode);
|
|
size_t old_height_pixels = CGDisplayModeGetPixelHeight(old_mode);
|
|
BOOL new_size_same = (new_width_pixels == width_points && new_height_pixels == height_points);
|
|
BOOL old_size_same = (old_width_pixels == width_points && old_height_pixels == height_points);
|
|
|
|
if (new_size_same && !old_size_same)
|
|
better = TRUE;
|
|
else if (!new_size_same && old_size_same)
|
|
better = FALSE;
|
|
else
|
|
{
|
|
/* Otherwise, prefer the mode with the smaller pixel size. */
|
|
if (old_width_pixels < new_width_pixels || old_height_pixels < new_height_pixels)
|
|
better = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (better)
|
|
CFDictionarySetValue(modes_by_size, key, new_mode);
|
|
|
|
CFRelease(key);
|
|
}
|
|
|
|
CFRelease(modes);
|
|
|
|
count = CFDictionaryGetCount(modes_by_size);
|
|
mode_array = HeapAlloc(GetProcessHeap(), 0, count * sizeof(mode_array[0]));
|
|
CFDictionaryGetKeysAndValues(modes_by_size, NULL, (const void **)mode_array);
|
|
modes = CFArrayCreate(NULL, (const void **)mode_array, count, &kCFTypeArrayCallBacks);
|
|
HeapFree(GetProcessHeap(), 0, mode_array);
|
|
CFRelease(modes_by_size);
|
|
}
|
|
else
|
|
#endif
|
|
modes = CGDisplayCopyAllDisplayModes(display, NULL);
|
|
|
|
return modes;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ChangeDisplaySettingsEx (MACDRV.@)
|
|
*
|
|
*/
|
|
LONG CDECL macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
|
|
HWND hwnd, DWORD flags, LPVOID lpvoid)
|
|
{
|
|
LONG ret = DISP_CHANGE_BADMODE;
|
|
int bpp;
|
|
DEVMODEW dm;
|
|
BOOL def_mode = TRUE;
|
|
struct macdrv_display *displays;
|
|
int num_displays;
|
|
CFArrayRef display_modes;
|
|
CFIndex count, i, safe, best;
|
|
CGDisplayModeRef best_display_mode;
|
|
uint32_t best_io_flags;
|
|
|
|
TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid);
|
|
|
|
if (devmode)
|
|
{
|
|
/* this is the minimal dmSize that XP accepts */
|
|
if (devmode->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
|
|
return DISP_CHANGE_FAILED;
|
|
|
|
if (devmode->dmSize >= FIELD_OFFSET(DEVMODEW, dmFields) + sizeof(devmode->dmFields))
|
|
{
|
|
if (((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel) ||
|
|
((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth) ||
|
|
((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight) ||
|
|
((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency))
|
|
def_mode = FALSE;
|
|
}
|
|
}
|
|
|
|
if (def_mode)
|
|
{
|
|
if (!macdrv_EnumDisplaySettingsEx(devname, ENUM_REGISTRY_SETTINGS, &dm, 0))
|
|
{
|
|
ERR("Default mode not found!\n");
|
|
return DISP_CHANGE_BADMODE;
|
|
}
|
|
|
|
TRACE("Return to original display mode\n");
|
|
devmode = &dm;
|
|
}
|
|
|
|
if ((devmode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
|
|
{
|
|
WARN("devmode doesn't specify the resolution: %04x\n", devmode->dmFields);
|
|
return DISP_CHANGE_BADMODE;
|
|
}
|
|
|
|
if (macdrv_get_displays(&displays, &num_displays))
|
|
return DISP_CHANGE_FAILED;
|
|
|
|
display_modes = copy_display_modes(displays[0].displayID);
|
|
if (!display_modes)
|
|
{
|
|
macdrv_free_displays(displays);
|
|
return DISP_CHANGE_FAILED;
|
|
}
|
|
|
|
bpp = get_default_bpp();
|
|
if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel != bpp)
|
|
TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, devmode->dmBitsPerPel);
|
|
|
|
TRACE("looking for %dx%dx%dbpp @%d Hz",
|
|
(devmode->dmFields & DM_PELSWIDTH ? devmode->dmPelsWidth : 0),
|
|
(devmode->dmFields & DM_PELSHEIGHT ? devmode->dmPelsHeight : 0),
|
|
bpp,
|
|
(devmode->dmFields & DM_DISPLAYFREQUENCY ? devmode->dmDisplayFrequency : 0));
|
|
if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
|
|
TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un");
|
|
if (devmode->dmFields & DM_DISPLAYFLAGS)
|
|
TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
|
|
TRACE("\n");
|
|
|
|
safe = -1;
|
|
best_display_mode = NULL;
|
|
count = CFArrayGetCount(display_modes);
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
CGDisplayModeRef display_mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(display_modes, i);
|
|
uint32_t io_flags = CGDisplayModeGetIOFlags(display_mode);
|
|
int mode_bpp = display_mode_bits_per_pixel(display_mode);
|
|
size_t width = CGDisplayModeGetWidth(display_mode);
|
|
size_t height = CGDisplayModeGetHeight(display_mode);
|
|
|
|
if (!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag))
|
|
continue;
|
|
|
|
safe++;
|
|
|
|
if (bpp != mode_bpp)
|
|
continue;
|
|
|
|
if (devmode->dmFields & DM_PELSWIDTH)
|
|
{
|
|
if (devmode->dmPelsWidth != width)
|
|
continue;
|
|
}
|
|
if (devmode->dmFields & DM_PELSHEIGHT)
|
|
{
|
|
if (devmode->dmPelsHeight != height)
|
|
continue;
|
|
}
|
|
if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != 0)
|
|
{
|
|
double refresh_rate = CGDisplayModeGetRefreshRate(display_mode);
|
|
if (!refresh_rate)
|
|
refresh_rate = 60;
|
|
if (devmode->dmDisplayFrequency != (DWORD)refresh_rate)
|
|
continue;
|
|
}
|
|
if (devmode->dmFields & DM_DISPLAYFLAGS)
|
|
{
|
|
if (!(devmode->dmDisplayFlags & DM_INTERLACED) != !(io_flags & kDisplayModeInterlacedFlag))
|
|
continue;
|
|
}
|
|
else if (best_display_mode)
|
|
{
|
|
if (io_flags & kDisplayModeInterlacedFlag && !(best_io_flags & kDisplayModeInterlacedFlag))
|
|
continue;
|
|
else if (!(io_flags & kDisplayModeInterlacedFlag) && best_io_flags & kDisplayModeInterlacedFlag)
|
|
goto better;
|
|
}
|
|
if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT)
|
|
{
|
|
if (!(devmode->dmDisplayFixedOutput == DMDFO_STRETCH) != !(io_flags & kDisplayModeStretchedFlag))
|
|
continue;
|
|
}
|
|
else if (best_display_mode)
|
|
{
|
|
if (io_flags & kDisplayModeStretchedFlag && !(best_io_flags & kDisplayModeStretchedFlag))
|
|
continue;
|
|
else if (!(io_flags & kDisplayModeStretchedFlag) && best_io_flags & kDisplayModeStretchedFlag)
|
|
goto better;
|
|
}
|
|
|
|
if (best_display_mode)
|
|
continue;
|
|
|
|
better:
|
|
best_display_mode = display_mode;
|
|
best = safe;
|
|
best_io_flags = io_flags;
|
|
}
|
|
|
|
if (best_display_mode)
|
|
{
|
|
/* we have a valid mode */
|
|
TRACE("Requested display settings match mode %ld\n", best);
|
|
|
|
if ((flags & CDS_UPDATEREGISTRY) && !write_registry_settings(devmode))
|
|
{
|
|
WARN("Failed to update registry\n");
|
|
ret = DISP_CHANGE_NOTUPDATED;
|
|
}
|
|
else if (flags & (CDS_TEST | CDS_NORESET))
|
|
ret = DISP_CHANGE_SUCCESSFUL;
|
|
else if (macdrv_set_display_mode(&displays[0], best_display_mode))
|
|
{
|
|
int mode_bpp = display_mode_bits_per_pixel(best_display_mode);
|
|
size_t width = CGDisplayModeGetWidth(best_display_mode);
|
|
size_t height = CGDisplayModeGetHeight(best_display_mode);
|
|
|
|
SendMessageW(GetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
|
|
MAKELPARAM(width, height));
|
|
ret = DISP_CHANGE_SUCCESSFUL;
|
|
}
|
|
else
|
|
{
|
|
WARN("Failed to set display mode\n");
|
|
ret = DISP_CHANGE_FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* no valid modes found */
|
|
ERR("No matching mode found %ux%ux%d @%u!\n", devmode->dmPelsWidth, devmode->dmPelsHeight,
|
|
bpp, devmode->dmDisplayFrequency);
|
|
}
|
|
|
|
CFRelease(display_modes);
|
|
macdrv_free_displays(displays);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* EnumDisplayMonitors (MACDRV.@)
|
|
*/
|
|
BOOL CDECL macdrv_EnumDisplayMonitors(HDC hdc, LPRECT rect, MONITORENUMPROC proc, LPARAM lparam)
|
|
{
|
|
struct macdrv_display *displays;
|
|
int num_displays;
|
|
int i;
|
|
BOOL ret = TRUE;
|
|
|
|
TRACE("%p, %s, %p, %#lx\n", hdc, wine_dbgstr_rect(rect), proc, lparam);
|
|
|
|
if (hdc)
|
|
{
|
|
POINT origin;
|
|
RECT limit;
|
|
|
|
if (!GetDCOrgEx(hdc, &origin)) return FALSE;
|
|
if (GetClipBox(hdc, &limit) == ERROR) return FALSE;
|
|
|
|
if (rect && !IntersectRect(&limit, &limit, rect)) return TRUE;
|
|
|
|
if (macdrv_get_displays(&displays, &num_displays))
|
|
return FALSE;
|
|
|
|
for (i = 0; i < num_displays; i++)
|
|
{
|
|
RECT monrect = rect_from_cgrect(displays[i].frame);
|
|
OffsetRect(&monrect, -origin.x, -origin.y);
|
|
if (IntersectRect(&monrect, &monrect, &limit))
|
|
{
|
|
HMONITOR monitor = display_id_to_monitor(displays[i].displayID);
|
|
TRACE("monitor %d handle %p @ %s\n", i, monitor, wine_dbgstr_rect(&monrect));
|
|
if (!proc(monitor, hdc, &monrect, lparam))
|
|
{
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (macdrv_get_displays(&displays, &num_displays))
|
|
return FALSE;
|
|
|
|
for (i = 0; i < num_displays; i++)
|
|
{
|
|
RECT monrect = rect_from_cgrect(displays[i].frame);
|
|
RECT unused;
|
|
if (!rect || IntersectRect(&unused, &monrect, rect))
|
|
{
|
|
HMONITOR monitor = display_id_to_monitor(displays[i].displayID);
|
|
TRACE("monitor %d handle %p @ %s\n", i, monitor, wine_dbgstr_rect(&monrect));
|
|
if (!proc(monitor, 0, &monrect, lparam))
|
|
{
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
macdrv_free_displays(displays);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* EnumDisplaySettingsEx (MACDRV.@)
|
|
*
|
|
*/
|
|
BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode,
|
|
LPDEVMODEW devmode, DWORD flags)
|
|
{
|
|
static const WCHAR dev_name[CCHDEVICENAME] =
|
|
{ 'W','i','n','e',' ','M','a','c',' ','d','r','i','v','e','r',0 };
|
|
struct macdrv_display *displays = NULL;
|
|
int num_displays;
|
|
CGDisplayModeRef display_mode;
|
|
int display_mode_bpp;
|
|
BOOL synthesized = FALSE;
|
|
double rotation;
|
|
uint32_t io_flags;
|
|
|
|
TRACE("%s, %u, %p + %hu, %08x\n", debugstr_w(devname), mode, devmode, devmode->dmSize, flags);
|
|
|
|
memcpy(devmode->dmDeviceName, dev_name, sizeof(dev_name));
|
|
devmode->dmSpecVersion = DM_SPECVERSION;
|
|
devmode->dmDriverVersion = DM_SPECVERSION;
|
|
devmode->dmSize = FIELD_OFFSET(DEVMODEW, dmICMMethod);
|
|
devmode->dmDriverExtra = 0;
|
|
memset(&devmode->dmFields, 0, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields));
|
|
|
|
if (mode == ENUM_REGISTRY_SETTINGS)
|
|
{
|
|
TRACE("mode %d (registry) -- getting default mode\n", mode);
|
|
return read_registry_settings(devmode);
|
|
}
|
|
|
|
if (macdrv_get_displays(&displays, &num_displays))
|
|
goto failed;
|
|
|
|
if (mode == ENUM_CURRENT_SETTINGS)
|
|
{
|
|
TRACE("mode %d (current) -- getting current mode\n", mode);
|
|
display_mode = CGDisplayCopyDisplayMode(displays[0].displayID);
|
|
display_mode_bpp = display_mode_bits_per_pixel(display_mode);
|
|
}
|
|
else
|
|
{
|
|
DWORD count, i;
|
|
|
|
EnterCriticalSection(&modes_section);
|
|
|
|
if (mode == 0 || !modes)
|
|
{
|
|
if (modes) CFRelease(modes);
|
|
modes = copy_display_modes(displays[0].displayID);
|
|
modes_has_8bpp = modes_has_16bpp = FALSE;
|
|
|
|
if (modes)
|
|
{
|
|
count = CFArrayGetCount(modes);
|
|
for (i = 0; i < count && !(modes_has_8bpp && modes_has_16bpp); i++)
|
|
{
|
|
CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
|
|
int bpp = display_mode_bits_per_pixel(mode);
|
|
if (bpp == 8)
|
|
modes_has_8bpp = TRUE;
|
|
else if (bpp == 16)
|
|
modes_has_16bpp = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
display_mode = NULL;
|
|
if (modes)
|
|
{
|
|
int default_bpp = get_default_bpp();
|
|
DWORD seen_modes = 0;
|
|
|
|
count = CFArrayGetCount(modes);
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
CGDisplayModeRef candidate = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
|
|
|
|
io_flags = CGDisplayModeGetIOFlags(candidate);
|
|
if (!(flags & EDS_RAWMODE) &&
|
|
(!(io_flags & kDisplayModeValidFlag) || !(io_flags & kDisplayModeSafeFlag)))
|
|
continue;
|
|
|
|
seen_modes++;
|
|
if (seen_modes > mode)
|
|
{
|
|
display_mode = (CGDisplayModeRef)CFRetain(candidate);
|
|
display_mode_bpp = display_mode_bits_per_pixel(display_mode);
|
|
break;
|
|
}
|
|
|
|
/* We only synthesize modes from those having the default bpp. */
|
|
if (display_mode_bits_per_pixel(candidate) != default_bpp)
|
|
continue;
|
|
|
|
if (!modes_has_8bpp)
|
|
{
|
|
seen_modes++;
|
|
if (seen_modes > mode)
|
|
{
|
|
display_mode = (CGDisplayModeRef)CFRetain(candidate);
|
|
display_mode_bpp = 8;
|
|
synthesized = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!modes_has_16bpp)
|
|
{
|
|
seen_modes++;
|
|
if (seen_modes > mode)
|
|
{
|
|
display_mode = (CGDisplayModeRef)CFRetain(candidate);
|
|
display_mode_bpp = 16;
|
|
synthesized = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&modes_section);
|
|
}
|
|
|
|
if (!display_mode)
|
|
goto failed;
|
|
|
|
/* We currently only report modes for the primary display, so it's at (0, 0). */
|
|
devmode->dmPosition.x = 0;
|
|
devmode->dmPosition.y = 0;
|
|
devmode->dmFields |= DM_POSITION;
|
|
|
|
rotation = CGDisplayRotation(displays[0].displayID);
|
|
devmode->dmDisplayOrientation = ((int)((rotation / 90) + 0.5)) % 4;
|
|
devmode->dmFields |= DM_DISPLAYORIENTATION;
|
|
|
|
io_flags = CGDisplayModeGetIOFlags(display_mode);
|
|
if (io_flags & kDisplayModeStretchedFlag)
|
|
devmode->dmDisplayFixedOutput = DMDFO_STRETCH;
|
|
else
|
|
devmode->dmDisplayFixedOutput = DMDFO_CENTER;
|
|
devmode->dmFields |= DM_DISPLAYFIXEDOUTPUT;
|
|
|
|
devmode->dmBitsPerPel = display_mode_bpp;
|
|
if (devmode->dmBitsPerPel)
|
|
devmode->dmFields |= DM_BITSPERPEL;
|
|
|
|
devmode->dmPelsWidth = CGDisplayModeGetWidth(display_mode);
|
|
devmode->dmPelsHeight = CGDisplayModeGetHeight(display_mode);
|
|
devmode->dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
|
|
devmode->dmDisplayFlags = 0;
|
|
if (io_flags & kDisplayModeInterlacedFlag)
|
|
devmode->dmDisplayFlags |= DM_INTERLACED;
|
|
devmode->dmFields |= DM_DISPLAYFLAGS;
|
|
|
|
devmode->dmDisplayFrequency = CGDisplayModeGetRefreshRate(display_mode);
|
|
if (!devmode->dmDisplayFrequency)
|
|
devmode->dmDisplayFrequency = 60;
|
|
devmode->dmFields |= DM_DISPLAYFREQUENCY;
|
|
|
|
CFRelease(display_mode);
|
|
macdrv_free_displays(displays);
|
|
|
|
TRACE("mode %d -- %dx%dx%dbpp @%d Hz", mode,
|
|
devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel,
|
|
devmode->dmDisplayFrequency);
|
|
if (devmode->dmDisplayOrientation)
|
|
TRACE(" rotated %u degrees", devmode->dmDisplayOrientation * 90);
|
|
if (devmode->dmDisplayFixedOutput == DMDFO_STRETCH)
|
|
TRACE(" stretched");
|
|
if (devmode->dmDisplayFlags & DM_INTERLACED)
|
|
TRACE(" interlaced");
|
|
if (synthesized)
|
|
TRACE(" (synthesized)");
|
|
TRACE("\n");
|
|
|
|
return TRUE;
|
|
|
|
failed:
|
|
TRACE("mode %d -- not present\n", mode);
|
|
if (displays) macdrv_free_displays(displays);
|
|
SetLastError(ERROR_NO_MORE_FILES);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetDeviceGammaRamp (MACDRV.@)
|
|
*/
|
|
BOOL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
|
|
{
|
|
BOOL ret = FALSE;
|
|
DDGAMMARAMP *r = ramp;
|
|
struct macdrv_display *displays;
|
|
int num_displays;
|
|
uint32_t mac_entries;
|
|
int win_entries = sizeof(r->red) / sizeof(r->red[0]);
|
|
CGGammaValue *red, *green, *blue;
|
|
CGError err;
|
|
int win_entry;
|
|
|
|
TRACE("dev %p ramp %p\n", dev, ramp);
|
|
|
|
if (macdrv_get_displays(&displays, &num_displays))
|
|
{
|
|
WARN("failed to get Mac displays\n");
|
|
return FALSE;
|
|
}
|
|
|
|
mac_entries = CGDisplayGammaTableCapacity(displays[0].displayID);
|
|
red = HeapAlloc(GetProcessHeap(), 0, mac_entries * sizeof(red[0]) * 3);
|
|
if (!red)
|
|
goto done;
|
|
green = red + mac_entries;
|
|
blue = green + mac_entries;
|
|
|
|
err = CGGetDisplayTransferByTable(displays[0].displayID, mac_entries, red, green,
|
|
blue, &mac_entries);
|
|
if (err != kCGErrorSuccess)
|
|
{
|
|
WARN("failed to get Mac gamma table: %d\n", err);
|
|
goto done;
|
|
}
|
|
|
|
if (mac_entries == win_entries)
|
|
{
|
|
for (win_entry = 0; win_entry < win_entries; win_entry++)
|
|
{
|
|
r->red[win_entry] = red[win_entry] * 65535 + 0.5;
|
|
r->green[win_entry] = green[win_entry] * 65535 + 0.5;
|
|
r->blue[win_entry] = blue[win_entry] * 65535 + 0.5;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (win_entry = 0; win_entry < win_entries; win_entry++)
|
|
{
|
|
double mac_pos = win_entry * (mac_entries - 1) / (double)(win_entries - 1);
|
|
int mac_entry = mac_pos;
|
|
double red_value, green_value, blue_value;
|
|
|
|
if (mac_entry == mac_entries - 1)
|
|
{
|
|
red_value = red[mac_entry];
|
|
green_value = green[mac_entry];
|
|
blue_value = blue[mac_entry];
|
|
}
|
|
else
|
|
{
|
|
double distance = mac_pos - mac_entry;
|
|
|
|
red_value = red[mac_entry] * (1 - distance) + red[mac_entry + 1] * distance;
|
|
green_value = green[mac_entry] * (1 - distance) + green[mac_entry + 1] * distance;
|
|
blue_value = blue[mac_entry] * (1 - distance) + blue[mac_entry + 1] * distance;
|
|
}
|
|
|
|
r->red[win_entry] = red_value * 65535 + 0.5;
|
|
r->green[win_entry] = green_value * 65535 + 0.5;
|
|
r->blue[win_entry] = blue_value * 65535 + 0.5;
|
|
}
|
|
}
|
|
|
|
ret = TRUE;
|
|
|
|
done:
|
|
HeapFree(GetProcessHeap(), 0, red);
|
|
macdrv_free_displays(displays);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetMonitorInfo (MACDRV.@)
|
|
*/
|
|
BOOL CDECL macdrv_GetMonitorInfo(HMONITOR monitor, LPMONITORINFO info)
|
|
{
|
|
static const WCHAR adapter_name[] = { '\\','\\','.','\\','D','I','S','P','L','A','Y','1',0 };
|
|
struct macdrv_display *displays;
|
|
int num_displays;
|
|
CGDirectDisplayID display_id;
|
|
int i;
|
|
|
|
TRACE("%p, %p\n", monitor, info);
|
|
|
|
if (macdrv_get_displays(&displays, &num_displays))
|
|
{
|
|
ERR("couldn't get display list\n");
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
return FALSE;
|
|
}
|
|
|
|
display_id = monitor_to_display_id(monitor);
|
|
for (i = 0; i < num_displays; i++)
|
|
{
|
|
if (displays[i].displayID == display_id)
|
|
break;
|
|
}
|
|
|
|
if (i < num_displays)
|
|
{
|
|
info->rcMonitor = rect_from_cgrect(displays[i].frame);
|
|
info->rcWork = rect_from_cgrect(displays[i].work_frame);
|
|
|
|
info->dwFlags = (i == 0) ? MONITORINFOF_PRIMARY : 0;
|
|
|
|
if (info->cbSize >= sizeof(MONITORINFOEXW))
|
|
lstrcpyW(((MONITORINFOEXW*)info)->szDevice, adapter_name);
|
|
|
|
TRACE(" -> rcMonitor %s rcWork %s dwFlags %08x\n", wine_dbgstr_rect(&info->rcMonitor),
|
|
wine_dbgstr_rect(&info->rcWork), info->dwFlags);
|
|
}
|
|
else
|
|
{
|
|
ERR("invalid monitor handle\n");
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
macdrv_free_displays(displays);
|
|
return (i < num_displays);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetDeviceGammaRamp (MACDRV.@)
|
|
*/
|
|
BOOL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp)
|
|
{
|
|
DDGAMMARAMP *r = ramp;
|
|
struct macdrv_display *displays;
|
|
int num_displays;
|
|
int win_entries = sizeof(r->red) / sizeof(r->red[0]);
|
|
CGGammaValue *red, *green, *blue;
|
|
int i;
|
|
CGError err = kCGErrorFailure;
|
|
|
|
TRACE("dev %p ramp %p\n", dev, ramp);
|
|
|
|
if (!allow_set_gamma)
|
|
{
|
|
TRACE("disallowed by registry setting\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (macdrv_get_displays(&displays, &num_displays))
|
|
{
|
|
WARN("failed to get Mac displays\n");
|
|
return FALSE;
|
|
}
|
|
|
|
red = HeapAlloc(GetProcessHeap(), 0, win_entries * sizeof(red[0]) * 3);
|
|
if (!red)
|
|
goto done;
|
|
green = red + win_entries;
|
|
blue = green + win_entries;
|
|
|
|
for (i = 0; i < win_entries; i++)
|
|
{
|
|
red[i] = r->red[i] / 65535.0;
|
|
green[i] = r->green[i] / 65535.0;
|
|
blue[i] = r->blue[i] / 65535.0;
|
|
}
|
|
|
|
err = CGSetDisplayTransferByTable(displays[0].displayID, win_entries, red, green, blue);
|
|
if (err != kCGErrorSuccess)
|
|
WARN("failed to set display gamma table: %d\n", err);
|
|
|
|
done:
|
|
HeapFree(GetProcessHeap(), 0, red);
|
|
macdrv_free_displays(displays);
|
|
return (err == kCGErrorSuccess);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* macdrv_displays_changed
|
|
*
|
|
* Handler for DISPLAYS_CHANGED events.
|
|
*/
|
|
void macdrv_displays_changed(const macdrv_event *event)
|
|
{
|
|
HWND hwnd = GetDesktopWindow();
|
|
|
|
/* A system display change will get delivered to all GUI-attached threads,
|
|
so the desktop-window-owning thread will get it and all others should
|
|
ignore it. A synthesized display change event due to activation
|
|
will only get delivered to the activated process. So, it needs to
|
|
process it (by sending it to the desktop window). */
|
|
if (event->displays_changed.activating ||
|
|
GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId())
|
|
{
|
|
CGDirectDisplayID mainDisplay = CGMainDisplayID();
|
|
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(mainDisplay);
|
|
size_t width = CGDisplayModeGetWidth(mode);
|
|
size_t height = CGDisplayModeGetHeight(mode);
|
|
int mode_bpp = display_mode_bits_per_pixel(mode);
|
|
|
|
CGDisplayModeRelease(mode);
|
|
SendMessageW(hwnd, WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
|
|
MAKELPARAM(width, height));
|
|
}
|
|
}
|