| /* |
| * 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)); |
| } |
| } |