In preparation for nulldrv display modes support.
-- v10: win32u: Move display placement logic out of graphics drivers. winemac.drv: Remove unnecessary display mode flags checks. win32u: Support interlaced and stretched display modes. winex11.drv: Remove unnecessary display mode flags checks. win32u: Move full display mode lookup out of graphics drivers. win32u: Sort adapter display modes after reading from the registry.
From: R��mi Bernon rbernon@codeweavers.com
--- dlls/win32u/sysparams.c | 49 ++++++++++++++++++++++++++++++++++++++ dlls/winex11.drv/display.c | 3 --- 2 files changed, 49 insertions(+), 3 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 848b14dbcb3..c37183dc73d 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -546,6 +546,53 @@ static BOOL write_registry_settings( const WCHAR *adapter_path, const DEVMODEW * return ret; }
+static int mode_compare(const void *p1, const void *p2) +{ + DWORD a_width, a_height, b_width, b_height; + const DEVMODEW *a = p1, *b = p2; + int ret; + + /* Use the width and height in landscape mode for comparison */ + if (a->dmDisplayOrientation == DMDO_DEFAULT || a->dmDisplayOrientation == DMDO_180) + { + a_width = a->dmPelsWidth; + a_height = a->dmPelsHeight; + } + else + { + a_width = a->dmPelsHeight; + a_height = a->dmPelsWidth; + } + + if (b->dmDisplayOrientation == DMDO_DEFAULT || b->dmDisplayOrientation == DMDO_180) + { + b_width = b->dmPelsWidth; + b_height = b->dmPelsHeight; + } + else + { + b_width = b->dmPelsHeight; + b_height = b->dmPelsWidth; + } + + /* Depth in descending order */ + if ((ret = b->dmBitsPerPel - a->dmBitsPerPel)) return ret; + + /* Width in ascending order */ + if ((ret = a_width - b_width)) return ret; + + /* Height in ascending order */ + if ((ret = a_height - b_height)) return ret; + + /* Frequency in descending order */ + if ((ret = b->dmDisplayFrequency - a->dmDisplayFrequency)) return ret; + + /* Orientation in ascending order */ + if ((ret = a->dmDisplayOrientation - b->dmDisplayOrientation)) return ret; + + return 0; +} + static BOOL read_display_adapter_settings( unsigned int index, struct adapter *info ) { char buffer[4096]; @@ -606,6 +653,8 @@ static BOOL read_display_adapter_settings( unsigned int index, struct adapter *i mode = NEXT_DEVMODEW(mode); } info->mode_count = i; + + qsort(info->modes, info->mode_count, sizeof(*info->modes) + info->modes->dmDriverExtra, mode_compare); }
/* DeviceID */ diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index c9f2da7e163..508401b77cf 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -403,7 +403,6 @@ static DEVMODEW *get_full_mode(ULONG_PTR id, DEVMODEW *dev_mode) if (!settings_handler.get_modes(id, EDS_ROTATEDMODE, &modes, &mode_count)) return NULL;
- qsort(modes, mode_count, sizeof(*modes) + modes[0].dmDriverExtra, mode_compare); for (mode_idx = 0; mode_idx < mode_count; ++mode_idx) { found_mode = (DEVMODEW *)((BYTE *)modes + (sizeof(*modes) + modes[0].dmDriverExtra) * mode_idx); @@ -1062,8 +1061,6 @@ BOOL X11DRV_UpdateDisplayDevices( const struct gdi_device_manager *device_manage if (!settings_handler.get_modes( adapters[adapter].id, EDS_ROTATEDMODE, &modes, &mode_count )) continue;
- qsort( modes, mode_count, sizeof(*modes) + modes[0].dmDriverExtra, mode_compare ); - for (mode = modes; mode_count; mode_count--) { TRACE( "mode: %p\n", mode );
From: R��mi Bernon rbernon@codeweavers.com
--- dlls/win32u/sysparams.c | 46 +++++++++++++++++++++++++++++++++----- dlls/winemac.drv/display.c | 3 +-- dlls/winex11.drv/display.c | 30 +------------------------ 3 files changed, 43 insertions(+), 36 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index c37183dc73d..aa644d9931d 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2091,7 +2091,34 @@ static BOOL is_detached_mode( const DEVMODEW *mode ) mode->dmPelsHeight == 0; }
-static DEVMODEW *validate_display_settings( const WCHAR *adapter_path, const WCHAR *device_name, DEVMODEW *devmode, DEVMODEW *temp_mode ) +static DEVMODEW *find_display_mode( DEVMODEW *modes, DEVMODEW *devmode ) +{ + DEVMODEW *mode; + + if (is_detached_mode( devmode )) return devmode; + + for (mode = modes; mode && mode->dmSize; mode = NEXT_DEVMODEW(mode)) + { + if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel && devmode->dmBitsPerPel != mode->dmBitsPerPel) + continue; + if ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth != mode->dmPelsWidth) + continue; + if ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight != mode->dmPelsHeight) + continue; + if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != mode->dmDisplayFrequency + && devmode->dmDisplayFrequency > 1 && mode->dmDisplayFrequency) + continue; + if ((devmode->dmFields & DM_DISPLAYORIENTATION) && devmode->dmDisplayOrientation != mode->dmDisplayOrientation) + continue; + + return mode; + } + + return NULL; +} + +static DEVMODEW *get_full_mode( const WCHAR *adapter_path, const WCHAR *device_name, DEVMODEW *modes, + DEVMODEW *devmode, DEVMODEW *temp_mode ) { if (devmode) { @@ -2133,6 +2160,12 @@ static DEVMODEW *validate_display_settings( const WCHAR *adapter_path, const WCH } }
+ if ((devmode = find_display_mode( modes, devmode )) && devmode != temp_mode) + { + devmode->dmFields |= DM_POSITION; + devmode->dmPosition = temp_mode->dmPosition; + } + return devmode; }
@@ -2142,8 +2175,8 @@ static DEVMODEW *validate_display_settings( const WCHAR *adapter_path, const WCH LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devmode, HWND hwnd, DWORD flags, void *lparam ) { + DEVMODEW *modes, temp_mode = {.dmSize = sizeof(DEVMODEW)}; WCHAR device_name[CCHDEVICENAME], adapter_path[MAX_PATH]; - DEVMODEW temp_mode = {.dmSize = sizeof(DEVMODEW)}; LONG ret = DISP_CHANGE_SUCCESSFUL; struct adapter *adapter;
@@ -2163,19 +2196,22 @@ LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devm { lstrcpyW( device_name, adapter->dev.device_name ); lstrcpyW( adapter_path, adapter->config_key ); + /* allocate an extra mode to make iteration easier */ + modes = calloc( adapter->mode_count + 1, sizeof(DEVMODEW) ); + if (modes) memcpy( modes, adapter->modes, adapter->mode_count * sizeof(DEVMODEW) ); } unlock_display_devices(); - if (!adapter) + if (!adapter || !modes) { WARN( "Invalid device name %s.\n", debugstr_us(devname) ); return DISP_CHANGE_BADPARAM; }
- if (!(devmode = validate_display_settings( adapter_path, device_name, devmode, &temp_mode ))) ret = DISP_CHANGE_BADMODE; - else if (user_driver->pChangeDisplaySettingsEx( device_name, devmode, hwnd, flags | CDS_TEST, lparam )) ret = DISP_CHANGE_BADMODE; + if (!(devmode = get_full_mode( adapter_path, device_name, modes, devmode, &temp_mode ))) ret = DISP_CHANGE_BADMODE; else if ((flags & CDS_UPDATEREGISTRY) && !write_registry_settings( adapter_path, devmode )) ret = DISP_CHANGE_NOTUPDATED; else if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL; else ret = user_driver->pChangeDisplaySettingsEx( device_name, devmode, hwnd, flags, lparam ); + free( modes );
if (ret) ERR( "Changing %s display settings returned %d.\n", debugstr_us(devname), ret ); return ret; diff --git a/dlls/winemac.drv/display.c b/dlls/winemac.drv/display.c index 7a319a56a7a..48d5e9f1ee6 100644 --- a/dlls/winemac.drv/display.c +++ b/dlls/winemac.drv/display.c @@ -885,8 +885,7 @@ LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
if (best_display_mode) { - if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL; - else if (macdrv_set_display_mode(&displays[0], best_display_mode)) + 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); diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index 508401b77cf..e0e0e6e43de 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -773,41 +773,13 @@ static BOOL all_detached_settings(const DEVMODEW *displays) LONG X11DRV_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode, HWND hwnd, DWORD flags, LPVOID lpvoid ) { - DEVMODEW *displays, *mode, *full_mode; + DEVMODEW *displays; LONG ret;
ret = get_display_settings( &displays, devname, devmode ); if (ret != DISP_CHANGE_SUCCESSFUL) return ret;
- if (flags & CDS_UPDATEREGISTRY && devname && devmode) - { - for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) - { - ULONG_PTR *id = (ULONG_PTR *)(mode + 1); - - if (!wcsicmp(mode->dmDeviceName, devname)) - { - full_mode = get_full_mode(*id, mode); - if (!full_mode) - { - free(displays); - return DISP_CHANGE_BADMODE; - } - - memcpy( &devmode->dmFields, &full_mode->dmFields, devmode->dmSize - offsetof(DEVMODEW, dmFields) ); - free_full_mode(full_mode); - break; - } - } - } - - if (flags & (CDS_TEST | CDS_NORESET)) - { - free(displays); - return DISP_CHANGE_SUCCESSFUL; - } - if (all_detached_settings( displays )) { WARN("Detaching all displays is not permitted.\n");
From: R��mi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/display.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-)
diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index e0e0e6e43de..7589c8e6516 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -407,22 +407,15 @@ static DEVMODEW *get_full_mode(ULONG_PTR id, DEVMODEW *dev_mode) { found_mode = (DEVMODEW *)((BYTE *)modes + (sizeof(*modes) + modes[0].dmDriverExtra) * mode_idx);
- if (dev_mode->dmFields & DM_BITSPERPEL && - dev_mode->dmBitsPerPel && - found_mode->dmBitsPerPel != dev_mode->dmBitsPerPel) + if (found_mode->dmBitsPerPel != dev_mode->dmBitsPerPel) continue; - if (dev_mode->dmFields & DM_PELSWIDTH && found_mode->dmPelsWidth != dev_mode->dmPelsWidth) + if (found_mode->dmPelsWidth != dev_mode->dmPelsWidth) continue; - if (dev_mode->dmFields & DM_PELSHEIGHT && found_mode->dmPelsHeight != dev_mode->dmPelsHeight) + if (found_mode->dmPelsHeight != dev_mode->dmPelsHeight) continue; - if (dev_mode->dmFields & DM_DISPLAYFREQUENCY && - dev_mode->dmDisplayFrequency && - found_mode->dmDisplayFrequency && - dev_mode->dmDisplayFrequency != 1 && - dev_mode->dmDisplayFrequency != found_mode->dmDisplayFrequency) + if (found_mode->dmDisplayFrequency != dev_mode->dmDisplayFrequency) continue; - if (dev_mode->dmFields & DM_DISPLAYORIENTATION && - found_mode->dmDisplayOrientation != dev_mode->dmDisplayOrientation) + if (found_mode->dmDisplayOrientation != dev_mode->dmDisplayOrientation) continue;
break;
From: R��mi Bernon rbernon@codeweavers.com
--- dlls/win32u/sysparams.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index aa644d9931d..07016bd7acb 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -548,6 +548,7 @@ static BOOL write_registry_settings( const WCHAR *adapter_path, const DEVMODEW *
static int mode_compare(const void *p1, const void *p2) { + BOOL a_interlaced, b_interlaced, a_stretched, b_stretched; DWORD a_width, a_height, b_width, b_height; const DEVMODEW *a = p1, *b = p2; int ret; @@ -575,6 +576,16 @@ static int mode_compare(const void *p1, const void *p2) b_height = b->dmPelsWidth; }
+ if (!(a->dmFields & DM_DISPLAYFLAGS)) a_interlaced = FALSE; + else a_interlaced = !!(a->dmDisplayFlags & DM_INTERLACED); + if (!(b->dmFields & DM_DISPLAYFLAGS)) b_interlaced = FALSE; + else b_interlaced = !!(b->dmDisplayFlags & DM_INTERLACED); + + if (!(a->dmFields & DM_DISPLAYFIXEDOUTPUT)) a_stretched = FALSE; + else a_stretched = a->dmDisplayFixedOutput == DMDFO_STRETCH; + if (!(b->dmFields & DM_DISPLAYFIXEDOUTPUT)) b_stretched = FALSE; + else b_stretched = b->dmDisplayFixedOutput == DMDFO_STRETCH; + /* Depth in descending order */ if ((ret = b->dmBitsPerPel - a->dmBitsPerPel)) return ret;
@@ -590,6 +601,12 @@ static int mode_compare(const void *p1, const void *p2) /* Orientation in ascending order */ if ((ret = a->dmDisplayOrientation - b->dmDisplayOrientation)) return ret;
+ /* Interlaced in ascending order */ + if ((ret = a_interlaced - b_interlaced)) return ret; + + /* Stretched in ascending order */ + if ((ret = a_stretched - b_stretched)) return ret; + return 0; }
@@ -2110,6 +2127,12 @@ static DEVMODEW *find_display_mode( DEVMODEW *modes, DEVMODEW *devmode ) continue; if ((devmode->dmFields & DM_DISPLAYORIENTATION) && devmode->dmDisplayOrientation != mode->dmDisplayOrientation) continue; + if ((devmode->dmFields & DM_DISPLAYFLAGS) && (mode->dmFields & DM_DISPLAYFLAGS) && + (devmode->dmDisplayFlags & DM_INTERLACED) != (mode->dmDisplayFlags & DM_INTERLACED)) + continue; + if ((devmode->dmFields & DM_DISPLAYFIXEDOUTPUT) && (mode->dmFields & DM_DISPLAYFIXEDOUTPUT) && + devmode->dmDisplayFixedOutput != mode->dmDisplayFixedOutput) + continue;
return mode; }
From: R��mi Bernon rbernon@codeweavers.com
--- dlls/winemac.drv/display.c | 75 +++++++++----------------------------- 1 file changed, 18 insertions(+), 57 deletions(-)
diff --git a/dlls/winemac.drv/display.c b/dlls/winemac.drv/display.c index 48d5e9f1ee6..e6e11401ecb 100644 --- a/dlls/winemac.drv/display.c +++ b/dlls/winemac.drv/display.c @@ -719,7 +719,6 @@ static CGDisplayModeRef find_best_display_mode(DEVMODEW *devmode, CFArrayRef dis { CFIndex count, i, best; CGDisplayModeRef best_display_mode; - uint32_t best_io_flags;
best_display_mode = NULL;
@@ -732,6 +731,9 @@ static CGDisplayModeRef find_best_display_mode(DEVMODEW *devmode, CFArrayRef dis int mode_bpp = display_mode_bits_per_pixel(display_mode); size_t width = CGDisplayModeGetWidth(display_mode); size_t height = CGDisplayModeGetHeight(display_mode); + double refresh_rate = CGDisplayModeGetRefreshRate(display_mode); + if (!refresh_rate) + refresh_rate = 60;
if (is_original && retina_enabled) { @@ -742,58 +744,22 @@ static CGDisplayModeRef find_best_display_mode(DEVMODEW *devmode, CFArrayRef dis 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 && - devmode->dmDisplayFrequency != 1) - { - 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 (devmode->dmPelsWidth != width) + continue; + if (devmode->dmPelsHeight != height) + continue; + if (devmode->dmDisplayFrequency != (DWORD)refresh_rate) + continue; + if (!(devmode->dmDisplayFlags & DM_INTERLACED) != !(io_flags & kDisplayModeInterlacedFlag)) + continue; + if (!(devmode->dmDisplayFixedOutput == DMDFO_STRETCH) != !(io_flags & kDisplayModeStretchedFlag)) + continue;
if (best_display_mode) continue;
-better: best_display_mode = display_mode; best = i; - best_io_flags = io_flags; }
if (best_display_mode) @@ -866,18 +832,13 @@ LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode, pthread_mutex_lock(&cached_modes_mutex); bpp = get_default_bpp(); pthread_mutex_unlock(&cached_modes_mutex); - if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel != bpp) + if (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("looking for %dx%dx%dbpp @%d Hz", devmode->dmPelsWidth, devmode->dmPelsHeight, + bpp, devmode->dmDisplayFrequency); + TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un"); + TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-"); TRACE("\n");
desc = create_original_display_mode_descriptor(displays[0].displayID);
From: R��mi Bernon rbernon@codeweavers.com
--- dlls/win32u/driver.c | 14 +- dlls/win32u/sysparams.c | 262 +++++++++++++++++++++++++++-- dlls/wineandroid.drv/init.c | 9 +- dlls/winemac.drv/display.c | 97 +++++------ dlls/winemac.drv/gdi.c | 2 +- dlls/winemac.drv/macdrv.h | 3 +- dlls/winex11.drv/display.c | 325 ++++-------------------------------- dlls/winex11.drv/init.c | 2 +- dlls/winex11.drv/x11drv.h | 3 +- include/wine/gdi_driver.h | 2 +- 10 files changed, 337 insertions(+), 382 deletions(-)
diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 36d08afff8a..b4ed3f8cb4e 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -753,8 +753,8 @@ static void nulldrv_UpdateClipboard(void) { }
-static LONG nulldrv_ChangeDisplaySettingsEx( LPCWSTR name, LPDEVMODEW mode, HWND hwnd, - DWORD flags, LPVOID lparam ) +static LONG nulldrv_ChangeDisplaySettings( LPDEVMODEW displays, HWND hwnd, + DWORD flags, LPVOID lparam ) { return DISP_CHANGE_FAILED; } @@ -1071,10 +1071,10 @@ static SHORT loaderdrv_VkKeyScanEx( WCHAR ch, HKL layout ) return load_driver()->pVkKeyScanEx( ch, layout ); }
-static LONG loaderdrv_ChangeDisplaySettingsEx( LPCWSTR name, LPDEVMODEW mode, HWND hwnd, - DWORD flags, LPVOID lparam ) +static LONG loaderdrv_ChangeDisplaySettings( LPDEVMODEW displays, HWND hwnd, + DWORD flags, LPVOID lparam ) { - return load_driver()->pChangeDisplaySettingsEx( name, mode, hwnd, flags, lparam ); + return load_driver()->pChangeDisplaySettings( displays, hwnd, flags, lparam ); }
static BOOL loaderdrv_EnumDisplaySettingsEx( LPCWSTR name, DWORD num, LPDEVMODEW mode, DWORD flags ) @@ -1187,7 +1187,7 @@ static const struct user_driver_funcs lazy_load_driver = nulldrv_ClipboardWindowProc, loaderdrv_UpdateClipboard, /* display modes */ - loaderdrv_ChangeDisplaySettingsEx, + loaderdrv_ChangeDisplaySettings, loaderdrv_EnumDisplaySettingsEx, loaderdrv_GetCurrentDisplaySettings, loaderdrv_UpdateDisplayDevices, @@ -1263,7 +1263,7 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(ClipCursor); SET_USER_FUNC(ClipboardWindowProc); SET_USER_FUNC(UpdateClipboard); - SET_USER_FUNC(ChangeDisplaySettingsEx); + SET_USER_FUNC(ChangeDisplaySettings); SET_USER_FUNC(EnumDisplaySettingsEx); SET_USER_FUNC(GetCurrentDisplaySettings); SET_USER_FUNC(UpdateDisplayDevices); diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 07016bd7acb..e00a3c008c2 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -660,8 +660,8 @@ static BOOL read_display_adapter_settings( unsigned int index, struct adapter *i if (query_reg_value( hkey, mode_countW, value, sizeof(buffer) ) && value->Type == REG_DWORD) info->mode_count = *(const DWORD *)value->Data;
- /* Modes */ - if ((info->modes = calloc( info->mode_count, sizeof(DEVMODEW) ))) + /* Modes, allocate an extra mode for easier iteration */ + if ((info->modes = calloc( info->mode_count + 1, sizeof(DEVMODEW) ))) { for (i = 0, mode = info->modes; i < info->mode_count; i++) { @@ -2192,6 +2192,254 @@ static DEVMODEW *get_full_mode( const WCHAR *adapter_path, const WCHAR *device_n return devmode; }
+static DEVMODEW *get_display_settings( const WCHAR *devname, const DEVMODEW *devmode ) +{ + DEVMODEW *mode, *displays; + struct adapter *adapter; + BOOL ret; + + if (!lock_display_devices()) return NULL; + + /* allocate an extra mode for easier iteration */ + if (!(displays = calloc( list_count( &adapters ) + 1, sizeof(DEVMODEW) ))) goto done; + mode = displays; + + LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry ) + { + mode->dmSize = sizeof(DEVMODEW); + if (devmode && !wcsicmp( devname, adapter->dev.device_name )) + memcpy( &mode->dmFields, &devmode->dmFields, devmode->dmSize - offsetof(DEVMODEW, dmFields) ); + else + { + if (!devname) ret = read_registry_settings( adapter->config_key, mode ); + else ret = user_driver->pGetCurrentDisplaySettings( adapter->dev.device_name, mode ); + if (!ret) goto done; + } + + lstrcpyW( mode->dmDeviceName, adapter->dev.device_name ); + mode = NEXT_DEVMODEW(mode); + } + + unlock_display_devices(); + return displays; + +done: + unlock_display_devices(); + free( displays ); + return NULL; +} + +static INT offset_length( POINT offset ) +{ + return offset.x * offset.x + offset.y * offset.y; +} + +static void set_rect_from_devmode( RECT *rect, const DEVMODEW *mode ) +{ + SetRect( rect, mode->dmPosition.x, mode->dmPosition.y, mode->dmPosition.x + mode->dmPelsWidth, + mode->dmPosition.y + mode->dmPelsHeight ); +} + +/* Check if a rect overlaps with placed display rects */ +static BOOL overlap_placed_displays( const RECT *rect, const DEVMODEW *displays ) +{ + const DEVMODEW *mode; + RECT intersect; + + for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) + { + set_rect_from_devmode( &intersect, mode ); + if ((mode->dmFields & DM_POSITION) && intersect_rect( &intersect, &intersect, rect )) return TRUE; + } + + return FALSE; +} + +/* Get the offset with minimum length to place a display next to the placed displays with no spacing and overlaps */ +static POINT get_placement_offset( const DEVMODEW *displays, const DEVMODEW *placing ) +{ + POINT points[8], left_top, offset, min_offset = {0, 0}; + INT point_idx, point_count, vertex_idx; + BOOL has_placed = FALSE, first = TRUE; + RECT desired_rect, rect; + const DEVMODEW *mode; + INT width, height; + + set_rect_from_devmode( &desired_rect, placing ); + + /* If the display to be placed is detached, no offset is needed to place it */ + if (IsRectEmpty( &desired_rect )) return min_offset; + + /* If there is no placed and attached display, place this display as it is */ + for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) + { + set_rect_from_devmode( &rect, mode ); + if ((mode->dmFields & DM_POSITION) && !IsRectEmpty( &rect )) + { + has_placed = TRUE; + break; + } + } + + if (!has_placed) return min_offset; + + /* Try to place this display with each of its four vertices at every vertex of the placed + * displays and see which combination has the minimum offset length */ + width = desired_rect.right - desired_rect.left; + height = desired_rect.bottom - desired_rect.top; + + for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) + { + set_rect_from_devmode( &rect, mode ); + if (!(mode->dmFields & DM_POSITION) || IsRectEmpty( &rect )) continue; + + /* Get four vertices of the placed display rectangle */ + points[0].x = rect.left; + points[0].y = rect.top; + points[1].x = rect.left; + points[1].y = rect.bottom; + points[2].x = rect.right; + points[2].y = rect.top; + points[3].x = rect.right; + points[3].y = rect.bottom; + point_count = 4; + + /* Intersected points when moving the display to be placed horizontally */ + if (desired_rect.bottom >= rect.top && desired_rect.top <= rect.bottom) + { + points[point_count].x = rect.left; + points[point_count++].y = desired_rect.top; + points[point_count].x = rect.right; + points[point_count++].y = desired_rect.top; + } + /* Intersected points when moving the display to be placed vertically */ + if (desired_rect.left <= rect.right && desired_rect.right >= rect.left) + { + points[point_count].x = desired_rect.left; + points[point_count++].y = rect.top; + points[point_count].x = desired_rect.left; + points[point_count++].y = rect.bottom; + } + + /* Try moving each vertex of the display rectangle to each points */ + for (point_idx = 0; point_idx < point_count; ++point_idx) + { + for (vertex_idx = 0; vertex_idx < 4; ++vertex_idx) + { + switch (vertex_idx) + { + /* Move the bottom right vertex to the point */ + case 0: + left_top.x = points[point_idx].x - width; + left_top.y = points[point_idx].y - height; + break; + /* Move the bottom left vertex to the point */ + case 1: + left_top.x = points[point_idx].x; + left_top.y = points[point_idx].y - height; + break; + /* Move the top right vertex to the point */ + case 2: + left_top.x = points[point_idx].x - width; + left_top.y = points[point_idx].y; + break; + /* Move the top left vertex to the point */ + case 3: + left_top.x = points[point_idx].x; + left_top.y = points[point_idx].y; + break; + } + + offset.x = left_top.x - desired_rect.left; + offset.y = left_top.y - desired_rect.top; + rect = desired_rect; + OffsetRect( &rect, offset.x, offset.y ); + if (!overlap_placed_displays( &rect, displays )) + { + if (first) + { + min_offset = offset; + first = FALSE; + continue; + } + + if (offset_length( offset ) < offset_length( min_offset )) min_offset = offset; + } + } + } + } + + return min_offset; +} + +static void place_all_displays( DEVMODEW *displays ) +{ + POINT min_offset, offset; + DEVMODEW *mode, *placing; + + for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) + mode->dmFields &= ~DM_POSITION; + + /* Place all displays with no extra space between them and no overlapping */ + while (1) + { + /* Place the unplaced display with the minimum offset length first */ + placing = NULL; + for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) + { + if (mode->dmFields & DM_POSITION) continue; + + offset = get_placement_offset( displays, mode ); + if (!placing || offset_length( offset ) < offset_length( min_offset )) + { + min_offset = offset; + placing = mode; + } + } + + /* If all displays are placed */ + if (!placing) break; + + placing->dmPosition.x += min_offset.x; + placing->dmPosition.y += min_offset.y; + placing->dmFields |= DM_POSITION; + } +} + +static BOOL all_detached_settings( const DEVMODEW *displays ) +{ + const DEVMODEW *mode; + + for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) + if (!is_detached_mode( mode )) return FALSE; + + return TRUE; +} + +static LONG apply_display_settings( const WCHAR *devname, const DEVMODEW *devmode, + HWND hwnd, DWORD flags, void *lparam ) +{ + DEVMODEW *displays; + LONG ret; + + displays = get_display_settings( devname, devmode ); + if (!displays) return DISP_CHANGE_FAILED; + + if (all_detached_settings( displays )) + { + WARN( "Detaching all modes is not permitted.\n" ); + free( displays ); + return DISP_CHANGE_SUCCESSFUL; + } + + place_all_displays( displays ); + + ret = user_driver->pChangeDisplaySettings( displays, hwnd, flags, lparam ); + + free( displays ); + return ret; +} + /*********************************************************************** * NtUserChangeDisplaySettings (win32u.@) */ @@ -2206,13 +2454,7 @@ LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devm TRACE( "%s %p %p %#x %p\n", debugstr_us(devname), devmode, hwnd, flags, lparam ); TRACE( "flags=%s\n", _CDS_flags(flags) );
- if ((!devname || !devname->Length) && !devmode) - { - ret = user_driver->pChangeDisplaySettingsEx( NULL, NULL, hwnd, flags, lparam ); - if (ret != DISP_CHANGE_SUCCESSFUL) - ERR( "Restoring all displays to their registry settings returned %d.\n", ret ); - return ret; - } + if ((!devname || !devname->Length) && !devmode) return apply_display_settings( NULL, NULL, hwnd, flags, lparam );
if (!lock_display_devices()) return DISP_CHANGE_FAILED; if ((adapter = find_adapter( devname ))) @@ -2233,7 +2475,7 @@ LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devm if (!(devmode = get_full_mode( adapter_path, device_name, modes, devmode, &temp_mode ))) ret = DISP_CHANGE_BADMODE; else if ((flags & CDS_UPDATEREGISTRY) && !write_registry_settings( adapter_path, devmode )) ret = DISP_CHANGE_NOTUPDATED; else if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL; - else ret = user_driver->pChangeDisplaySettingsEx( device_name, devmode, hwnd, flags, lparam ); + else ret = apply_display_settings( device_name, devmode, hwnd, flags, lparam ); free( modes );
if (ret) ERR( "Changing %s display settings returned %d.\n", debugstr_us(devname), ret ); diff --git a/dlls/wineandroid.drv/init.c b/dlls/wineandroid.drv/init.c index 738b8943175..aba642d1c5d 100644 --- a/dlls/wineandroid.drv/init.c +++ b/dlls/wineandroid.drv/init.c @@ -260,12 +260,11 @@ static BOOL CDECL ANDROID_DeleteDC( PHYSDEV dev )
/*********************************************************************** - * ANDROID_ChangeDisplaySettingsEx + * ANDROID_ChangeDisplaySettings */ -LONG ANDROID_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode, - HWND hwnd, DWORD flags, LPVOID lpvoid ) +LONG ANDROID_ChangeDisplaySettings( LPDEVMODEW displays, HWND hwnd, DWORD flags, LPVOID lpvoid ) { - FIXME( "(%s,%p,%p,0x%08x,%p)\n", debugstr_w( devname ), devmode, hwnd, flags, lpvoid ); + FIXME( "(%p,%p,0x%08x,%p)\n", displays, hwnd, flags, lpvoid ); return DISP_CHANGE_SUCCESSFUL; }
@@ -377,7 +376,7 @@ static const struct user_driver_funcs android_drv_funcs = .pMapVirtualKeyEx = ANDROID_MapVirtualKeyEx, .pVkKeyScanEx = ANDROID_VkKeyScanEx, .pSetCursor = ANDROID_SetCursor, - .pChangeDisplaySettingsEx = ANDROID_ChangeDisplaySettingsEx, + .pChangeDisplaySettings = ANDROID_ChangeDisplaySettings, .pEnumDisplaySettingsEx = ANDROID_EnumDisplaySettingsEx, .pGetCurrentDisplaySettings = ANDROID_GetCurrentDisplaySettings, .pUpdateDisplayDevices = ANDROID_UpdateDisplayDevices, diff --git a/dlls/winemac.drv/display.c b/dlls/winemac.drv/display.c index e6e11401ecb..1cebcdfce2f 100644 --- a/dlls/winemac.drv/display.c +++ b/dlls/winemac.drv/display.c @@ -34,6 +34,7 @@
WINE_DEFAULT_DEBUG_CHANNEL(display);
+#define NEXT_DEVMODEW(mode) ((DEVMODEW *)((char *)((mode) + 1) + (mode)->dmDriverExtra))
struct display_mode_descriptor { @@ -769,84 +770,73 @@ static CGDisplayModeRef find_best_display_mode(DEVMODEW *devmode, CFArrayRef dis }
/*********************************************************************** - * ChangeDisplaySettingsEx (MACDRV.@) + * ChangeDisplaySettings (MACDRV.@) * */ -LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode, - HWND hwnd, DWORD flags, LPVOID lpvoid) +LONG macdrv_ChangeDisplaySettings(LPDEVMODEW displays, HWND hwnd, DWORD flags, LPVOID lpvoid) { WCHAR primary_adapter[CCHDEVICENAME]; - LONG ret = DISP_CHANGE_BADMODE; - DEVMODEW default_mode; + LONG ret = DISP_CHANGE_SUCCESSFUL; + DEVMODEW *mode; int bpp; - struct macdrv_display *displays; + struct macdrv_display *macdrv_displays; int num_displays; CFArrayRef display_modes; struct display_mode_descriptor *desc; CGDisplayModeRef best_display_mode;
- TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid); + TRACE("%p %p 0x%08x %p\n", displays, hwnd, flags, lpvoid);
init_original_display_mode();
if (!get_primary_adapter(primary_adapter)) return DISP_CHANGE_FAILED;
- if (!devname && !devmode) - { - UNICODE_STRING str; - memset(&default_mode, 0, sizeof(default_mode)); - default_mode.dmSize = sizeof(default_mode); - RtlInitUnicodeString(&str, primary_adapter); - if (!NtUserEnumDisplaySettings(&str, ENUM_REGISTRY_SETTINGS, &default_mode, 0)) - { - ERR("Default mode not found for %s!\n", wine_dbgstr_w(primary_adapter)); - return DISP_CHANGE_BADMODE; - } - - devname = primary_adapter; - devmode = &default_mode; - } - else if (wcsicmp(primary_adapter, devname)) - { - FIXME("Changing non-primary adapter settings is currently unsupported.\n"); - return DISP_CHANGE_SUCCESSFUL; - } - - if (is_detached_mode(devmode)) - { - FIXME("Detaching adapters is currently unsupported.\n"); - return DISP_CHANGE_SUCCESSFUL; - } - - if (macdrv_get_displays(&displays, &num_displays)) + if (macdrv_get_displays(&macdrv_displays, &num_displays)) return DISP_CHANGE_FAILED;
- display_modes = copy_display_modes(displays[0].displayID, FALSE); + display_modes = copy_display_modes(macdrv_displays[0].displayID, FALSE); if (!display_modes) { - macdrv_free_displays(displays); + macdrv_free_displays(macdrv_displays); return DISP_CHANGE_FAILED; }
pthread_mutex_lock(&cached_modes_mutex); bpp = get_default_bpp(); pthread_mutex_unlock(&cached_modes_mutex); - if (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->dmPelsWidth, devmode->dmPelsHeight, - bpp, devmode->dmDisplayFrequency); - TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un"); - TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-"); - TRACE("\n");
- desc = create_original_display_mode_descriptor(displays[0].displayID); - best_display_mode = find_best_display_mode(devmode, display_modes, bpp, desc); + desc = create_original_display_mode_descriptor(macdrv_displays[0].displayID);
- if (best_display_mode) + for (mode = displays; mode->dmSize && !ret; mode = NEXT_DEVMODEW(mode)) { - if (macdrv_set_display_mode(&displays[0], best_display_mode)) + if (wcsicmp(primary_adapter, mode->dmDeviceName)) + { + FIXME("Changing non-primary adapter settings is currently unsupported.\n"); + continue; + } + if (is_detached_mode(mode)) + { + FIXME("Detaching adapters is currently unsupported.\n"); + continue; + } + + if (mode->dmBitsPerPel != bpp) + TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, mode->dmBitsPerPel); + + TRACE("looking for %dx%dx%dbpp @%d Hz", mode->dmPelsWidth, mode->dmPelsHeight, + bpp, mode->dmDisplayFrequency); + TRACE(" %sstretched", mode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un"); + TRACE(" %sinterlaced", mode->dmDisplayFlags & DM_INTERLACED ? "" : "non-"); + TRACE("\n"); + + if (!(best_display_mode = find_best_display_mode(mode, display_modes, bpp, desc))) + { + ERR("No matching mode found %ux%ux%d @%u!\n", mode->dmPelsWidth, mode->dmPelsHeight, + bpp, mode->dmDisplayFrequency); + ret = DISP_CHANGE_BADMODE; + } + else if (macdrv_set_display_mode(&macdrv_displays[0], best_display_mode)) { int mode_bpp = display_mode_bits_per_pixel(best_display_mode); size_t width = CGDisplayModeGetWidth(best_display_mode); @@ -862,7 +852,6 @@ LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
send_message(NtUserGetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp, MAKELPARAM(width, height)); - ret = DISP_CHANGE_SUCCESSFUL; } else { @@ -870,16 +859,10 @@ LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode, 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); - }
free_display_mode_descriptor(desc); CFRelease(display_modes); - macdrv_free_displays(displays); + macdrv_free_displays(macdrv_displays);
return ret; } diff --git a/dlls/winemac.drv/gdi.c b/dlls/winemac.drv/gdi.c index 2057891faa7..c3e3fa6cdb7 100644 --- a/dlls/winemac.drv/gdi.c +++ b/dlls/winemac.drv/gdi.c @@ -268,7 +268,7 @@ static const struct user_driver_funcs macdrv_funcs =
.pActivateKeyboardLayout = macdrv_ActivateKeyboardLayout, .pBeep = macdrv_Beep, - .pChangeDisplaySettingsEx = macdrv_ChangeDisplaySettingsEx, + .pChangeDisplaySettings = macdrv_ChangeDisplaySettings, .pClipCursor = macdrv_ClipCursor, .pClipboardWindowProc = macdrv_ClipboardWindowProc, .pCreateDesktopWindow = macdrv_CreateDesktopWindow, diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index b5524517a7e..1b7e6318f1a 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -123,8 +123,7 @@ static inline struct macdrv_thread_data *macdrv_thread_data(void)
extern BOOL macdrv_ActivateKeyboardLayout(HKL hkl, UINT flags) DECLSPEC_HIDDEN; extern void macdrv_Beep(void) DECLSPEC_HIDDEN; -extern LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode, - HWND hwnd, DWORD flags, LPVOID lpvoid) DECLSPEC_HIDDEN; +extern LONG macdrv_ChangeDisplaySettings(LPDEVMODEW displays, HWND hwnd, DWORD flags, LPVOID lpvoid) DECLSPEC_HIDDEN; extern BOOL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW devmode, DWORD flags) DECLSPEC_HIDDEN; extern BOOL macdrv_GetCurrentDisplaySettings(LPCWSTR name, LPDEVMODEW devmode) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index 7589c8e6516..55dfcceb6e3 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -447,283 +447,22 @@ static void free_full_mode(DEVMODEW *mode) free(mode); }
-static LONG get_display_settings(DEVMODEW **new_displays, const WCHAR *dev_name, DEVMODEW *dev_mode) -{ - DEVMODEW registry_mode, current_mode, *mode, *displays; - INT display_idx, display_count = 0; - DISPLAY_DEVICEW display_device; - LONG ret = DISP_CHANGE_FAILED; - UNICODE_STRING device_name; - - display_device.cb = sizeof(display_device); - for (display_idx = 0; !NtUserEnumDisplayDevices( NULL, display_idx, &display_device, 0 ); ++display_idx) - ++display_count; - - /* use driver extra data to store an ULONG_PTR adapter id after each mode, - * and allocate an extra mode to make iteration easier */ - if (!(displays = calloc(display_count + 1, sizeof(DEVMODEW) + sizeof(ULONG_PTR)))) goto done; - mode = displays; - - for (display_idx = 0; display_idx < display_count; ++display_idx) - { - ULONG_PTR *id = (ULONG_PTR *)(mode + 1); - - if (NtUserEnumDisplayDevices( NULL, display_idx, &display_device, 0 )) - goto done; - - if (!settings_handler.get_id(display_device.DeviceName, id)) - { - ret = DISP_CHANGE_BADPARAM; - goto done; - } - - RtlInitUnicodeString( &device_name, display_device.DeviceName ); - - if (!dev_mode) - { - memset(®istry_mode, 0, sizeof(registry_mode)); - registry_mode.dmSize = sizeof(registry_mode); - if (!NtUserEnumDisplaySettings( &device_name, ENUM_REGISTRY_SETTINGS, ®istry_mode, 0 )) - goto done; - *mode = registry_mode; - } - else if (!wcsicmp(dev_name, display_device.DeviceName)) - { - *mode = *dev_mode; - } - else - { - memset(¤t_mode, 0, sizeof(current_mode)); - current_mode.dmSize = sizeof(current_mode); - if (!NtUserEnumDisplaySettings( &device_name, ENUM_CURRENT_SETTINGS, ¤t_mode, 0 )) - goto done; - *mode = current_mode; - } - - mode->dmSize = sizeof(DEVMODEW); - lstrcpyW(mode->dmDeviceName, display_device.DeviceName); - mode->dmDriverExtra = sizeof(ULONG_PTR); - mode = NEXT_DEVMODEW(mode); - } - - *new_displays = displays; - return DISP_CHANGE_SUCCESSFUL; - -done: - free(displays); - return ret; -} - -static INT offset_length(POINT offset) -{ - return offset.x * offset.x + offset.y * offset.y; -} - -static void set_rect_from_devmode(RECT *rect, const DEVMODEW *mode) -{ - SetRect(rect, mode->dmPosition.x, mode->dmPosition.y, mode->dmPosition.x + mode->dmPelsWidth, mode->dmPosition.y + mode->dmPelsHeight); -} - -/* Check if a rect overlaps with placed display rects */ -static BOOL overlap_placed_displays(const RECT *rect, const DEVMODEW *displays) -{ - const DEVMODEW *mode; - RECT intersect; - - for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) - { - set_rect_from_devmode(&intersect, mode); - if ((mode->dmFields & DM_POSITION) && intersect_rect(&intersect, &intersect, rect)) - return TRUE; - } - return FALSE; -} - -/* Get the offset with minimum length to place a display next to the placed displays with no spacing and overlaps */ -static POINT get_placement_offset(const DEVMODEW *displays, const DEVMODEW *placing) -{ - POINT points[8], left_top, offset, min_offset = {0, 0}; - INT point_idx, point_count, vertex_idx; - BOOL has_placed = FALSE, first = TRUE; - RECT desired_rect, rect; - const DEVMODEW *mode; - INT width, height; - - set_rect_from_devmode(&desired_rect, placing); - - /* If the display to be placed is detached, no offset is needed to place it */ - if (IsRectEmpty(&desired_rect)) - return min_offset; - - /* If there is no placed and attached display, place this display as it is */ - for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) - { - set_rect_from_devmode(&rect, mode); - if ((mode->dmFields & DM_POSITION) && !IsRectEmpty(&rect)) - { - has_placed = TRUE; - break; - } - } - - if (!has_placed) - return min_offset; - - /* Try to place this display with each of its four vertices at every vertex of the placed - * displays and see which combination has the minimum offset length */ - width = desired_rect.right - desired_rect.left; - height = desired_rect.bottom - desired_rect.top; - - for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) - { - set_rect_from_devmode(&rect, mode); - if (!(mode->dmFields & DM_POSITION) || IsRectEmpty(&rect)) - continue; - - /* Get four vertices of the placed display rectangle */ - points[0].x = rect.left; - points[0].y = rect.top; - points[1].x = rect.left; - points[1].y = rect.bottom; - points[2].x = rect.right; - points[2].y = rect.top; - points[3].x = rect.right; - points[3].y = rect.bottom; - point_count = 4; - - /* Intersected points when moving the display to be placed horizontally */ - if (desired_rect.bottom >= rect.top && - desired_rect.top <= rect.bottom) - { - points[point_count].x = rect.left; - points[point_count++].y = desired_rect.top; - points[point_count].x = rect.right; - points[point_count++].y = desired_rect.top; - } - /* Intersected points when moving the display to be placed vertically */ - if (desired_rect.left <= rect.right && - desired_rect.right >= rect.left) - { - points[point_count].x = desired_rect.left; - points[point_count++].y = rect.top; - points[point_count].x = desired_rect.left; - points[point_count++].y = rect.bottom; - } - - /* Try moving each vertex of the display rectangle to each points */ - for (point_idx = 0; point_idx < point_count; ++point_idx) - { - for (vertex_idx = 0; vertex_idx < 4; ++vertex_idx) - { - switch (vertex_idx) - { - /* Move the bottom right vertex to the point */ - case 0: - left_top.x = points[point_idx].x - width; - left_top.y = points[point_idx].y - height; - break; - /* Move the bottom left vertex to the point */ - case 1: - left_top.x = points[point_idx].x; - left_top.y = points[point_idx].y - height; - break; - /* Move the top right vertex to the point */ - case 2: - left_top.x = points[point_idx].x - width; - left_top.y = points[point_idx].y; - break; - /* Move the top left vertex to the point */ - case 3: - left_top.x = points[point_idx].x; - left_top.y = points[point_idx].y; - break; - } - - offset.x = left_top.x - desired_rect.left; - offset.y = left_top.y - desired_rect.top; - rect = desired_rect; - OffsetRect(&rect, offset.x, offset.y); - if (!overlap_placed_displays(&rect, displays)) - { - if (first) - { - min_offset = offset; - first = FALSE; - continue; - } - - if (offset_length(offset) < offset_length(min_offset)) - min_offset = offset; - } - } - } - } - - return min_offset; -} - -static void place_all_displays(DEVMODEW *displays) -{ - INT left_most = INT_MAX, top_most = INT_MAX; - POINT min_offset, offset; - DEVMODEW *mode, *placing; - - for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) - mode->dmFields &= ~DM_POSITION; - - /* Place all displays with no extra space between them and no overlapping */ - while (1) - { - /* Place the unplaced display with the minimum offset length first */ - placing = NULL; - for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) - { - if (mode->dmFields & DM_POSITION) - continue; - - offset = get_placement_offset(displays, mode); - if (!placing || offset_length(offset) < offset_length(min_offset)) - { - min_offset = offset; - placing = mode; - } - } - - /* If all displays are placed */ - if (!placing) - break; - - placing->dmPosition.x += min_offset.x; - placing->dmPosition.y += min_offset.y; - placing->dmFields |= DM_POSITION; - - left_most = min(left_most, placing->dmPosition.x); - top_most = min(top_most, placing->dmPosition.y); - } - - /* Convert virtual screen coordinates to root coordinates */ - for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) - { - mode->dmPosition.x -= left_most; - mode->dmPosition.y -= top_most; - } -} - -static LONG apply_display_settings(DEVMODEW *displays, BOOL do_attach) +static LONG apply_display_settings( DEVMODEW *displays, ULONG_PTR *ids, BOOL do_attach ) { DEVMODEW *full_mode; BOOL attached_mode; + LONG count, ret; DEVMODEW *mode; - LONG ret;
- for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) + for (count = 0, mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode), count++) { - ULONG_PTR *id = (ULONG_PTR *)(mode + 1); + ULONG_PTR *id = ids + count;
attached_mode = !is_detached_mode(mode); if ((attached_mode && !do_attach) || (!attached_mode && do_attach)) continue;
+ /* FIXME: get a full mode again because X11 driver extra data isn't portable */ full_mode = get_full_mode(*id, mode); if (!full_mode) return DISP_CHANGE_BADMODE; @@ -746,49 +485,43 @@ static LONG apply_display_settings(DEVMODEW *displays, BOOL do_attach) return DISP_CHANGE_SUCCESSFUL; }
-static BOOL all_detached_settings(const DEVMODEW *displays) -{ - const DEVMODEW *mode; - - for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) - { - if (!is_detached_mode(mode)) - return FALSE; - } - - return TRUE; -} - /*********************************************************************** - * ChangeDisplaySettingsEx (X11DRV.@) + * ChangeDisplaySettings (X11DRV.@) * */ -LONG X11DRV_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode, - HWND hwnd, DWORD flags, LPVOID lpvoid ) +LONG X11DRV_ChangeDisplaySettings( LPDEVMODEW displays, HWND hwnd, DWORD flags, LPVOID lpvoid ) { - DEVMODEW *displays; - LONG ret; - - ret = get_display_settings( &displays, devname, devmode ); - if (ret != DISP_CHANGE_SUCCESSFUL) - return ret; + INT left_most = INT_MAX, top_most = INT_MAX; + LONG count, ret = DISP_CHANGE_BADPARAM; + ULONG_PTR *ids; + DEVMODEW *mode;
- if (all_detached_settings( displays )) + /* Convert virtual screen coordinates to root coordinates, and find display ids. + * We cannot safely get the ids while changing modes, as the backend state may be invalidated. + */ + for (count = 0, mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode), count++) { - WARN("Detaching all displays is not permitted.\n"); - free(displays); - return DISP_CHANGE_SUCCESSFUL; + left_most = min( left_most, mode->dmPosition.x ); + top_most = min( top_most, mode->dmPosition.y ); }
- place_all_displays( displays ); + if (!(ids = calloc( count, sizeof(*ids) ))) return DISP_CHANGE_FAILED; + for (count = 0, mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode), count++) + { + if (!settings_handler.get_id( mode->dmDeviceName, ids + count )) goto done; + mode->dmPosition.x -= left_most; + mode->dmPosition.y -= top_most; + }
/* Detach displays first to free up CRTCs */ - ret = apply_display_settings( displays, FALSE ); + ret = apply_display_settings( displays, ids, FALSE ); if (ret == DISP_CHANGE_SUCCESSFUL) - ret = apply_display_settings( displays, TRUE ); + ret = apply_display_settings( displays, ids, TRUE ); if (ret == DISP_CHANGE_SUCCESSFUL) X11DRV_DisplayDevices_Update(TRUE); - free(displays); + +done: + free( ids ); return ret; }
diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c index 82ba7ea24ea..5d688c27114 100644 --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -403,7 +403,7 @@ static const struct user_driver_funcs x11drv_funcs = .pGetCursorPos = X11DRV_GetCursorPos, .pSetCursorPos = X11DRV_SetCursorPos, .pClipCursor = X11DRV_ClipCursor, - .pChangeDisplaySettingsEx = X11DRV_ChangeDisplaySettingsEx, + .pChangeDisplaySettings = X11DRV_ChangeDisplaySettings, .pEnumDisplaySettingsEx = X11DRV_EnumDisplaySettingsEx, .pGetCurrentDisplaySettings = X11DRV_GetCurrentDisplaySettings, .pUpdateDisplayDevices = X11DRV_UpdateDisplayDevices, diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index ad4a520af13..07bdd7610d5 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -213,8 +213,7 @@ extern void X11DRV_SetCursor( HCURSOR handle ) DECLSPEC_HIDDEN; extern BOOL X11DRV_SetCursorPos( INT x, INT y ) DECLSPEC_HIDDEN; extern BOOL X11DRV_GetCursorPos( LPPOINT pos ) DECLSPEC_HIDDEN; extern BOOL X11DRV_ClipCursor( LPCRECT clip ) DECLSPEC_HIDDEN; -extern LONG X11DRV_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode, - HWND hwnd, DWORD flags, LPVOID lpvoid ) DECLSPEC_HIDDEN; +extern LONG X11DRV_ChangeDisplaySettings( LPDEVMODEW displays, HWND hwnd, DWORD flags, LPVOID lpvoid ) DECLSPEC_HIDDEN; extern BOOL X11DRV_EnumDisplaySettingsEx( LPCWSTR name, DWORD n, LPDEVMODEW devmode, DWORD flags ) DECLSPEC_HIDDEN; extern BOOL X11DRV_GetCurrentDisplaySettings( LPCWSTR name, LPDEVMODEW devmode ) DECLSPEC_HIDDEN; diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 0ba4851a2c3..e1d8998d812 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -295,7 +295,7 @@ struct user_driver_funcs LRESULT (*pClipboardWindowProc)(HWND,UINT,WPARAM,LPARAM); void (*pUpdateClipboard)(void); /* display modes */ - LONG (*pChangeDisplaySettingsEx)(LPCWSTR,LPDEVMODEW,HWND,DWORD,LPVOID); + LONG (*pChangeDisplaySettings)(LPDEVMODEW,HWND,DWORD,LPVOID); BOOL (*pEnumDisplaySettingsEx)(LPCWSTR,DWORD,LPDEVMODEW,DWORD); BOOL (*pGetCurrentDisplaySettings)(LPCWSTR,LPDEVMODEW); BOOL (*pUpdateDisplayDevices)(const struct gdi_device_manager *,BOOL,void*);
On Fri Aug 26 21:30:41 2022 +0000, R��mi Bernon wrote:
I opened https://gitlab.winehq.org/wine/wine/-/merge_requests/720 to implement a few more things in winemac to help this. It'd be nice if you could check if it indeed solves the issue with this MR rebased on top.
Hopefully this would be fixed now.
Zhiyi Zhang (@zhiyi) commented about dlls/win32u/sysparams.c:
mode = NEXT_DEVMODEW(mode); } info->mode_count = i;
qsort(info->modes, info->mode_count, sizeof(*info->modes) + info->modes->dmDriverExtra, mode_compare);
There is no dmDriverExtra so sizeof(*info->modes) is enough.
Zhiyi Zhang (@zhiyi) commented about dlls/win32u/sysparams.c:
b_height = b->dmPelsWidth; }
- if (!(a->dmFields & DM_DISPLAYFLAGS)) a_interlaced = FALSE;
- else a_interlaced = !!(a->dmDisplayFlags & DM_INTERLACED);
- if (!(b->dmFields & DM_DISPLAYFLAGS)) b_interlaced = FALSE;
- else b_interlaced = !!(b->dmDisplayFlags & DM_INTERLACED);
- if (!(a->dmFields & DM_DISPLAYFIXEDOUTPUT)) a_stretched = FALSE;
- else a_stretched = a->dmDisplayFixedOutput == DMDFO_STRETCH;
- if (!(b->dmFields & DM_DISPLAYFIXEDOUTPUT)) b_stretched = FALSE;
- else b_stretched = b->dmDisplayFixedOutput == DMDFO_STRETCH;
So a/b_stretched will either be TRUE or FALSE. But there are DMDFO_DEFAULT, DMDFO_CENTER, and DMDFO_STRETCH. That doesn't seem right.
Also, you did verify on Windows that interlaced and dmDisplayFixedOutput are in ascending order, right?
I prefer these codes to be moved right before their use so we can save a few CPU cycles if the check can return early. The same can be said for width and height. It probably doesn't matter with modern compiler optimization, but still...
On Thu Sep 1 04:09:30 2022 +0000, Zhiyi Zhang wrote:
There is no dmDriverExtra so sizeof(*info->modes) is enough.
Right now indeed but I think it's better to keep it, like in other places in this file where dmDriverExtra is still used, in case that changes someday.
Also, you did verify on Windows that interlaced and dmDisplayFixedOutput are in ascending order, right?
No I didn't, this is only to match `winemac.drv` logic and simplify the lookup there. I could perhaps find a monitor with interlaced modes but I don't think I have anything that would have the stretched thing.
In any case it's only there to keep the current behavior the same, and only `winemac.drv` uses these flags. If the behavior is wrong, it may need to be changed but I don't think it's the purpose of this MR to do that and it could be done later.
So a/b_stretched will either be TRUE or FALSE. But there are DMDFO_DEFAULT, DMDFO_CENTER, and DMDFO_STRETCH. That doesn't seem right.
In the same way, only `winemac.drv` uses these flags, and it's either DMDFO_CENTER, or DMDFO_STRETCH. If some driver needs the full set of flags at some point it'll need to be changed but right now this is only meant to move current logic, not change it.
I prefer these codes to be moved right before their use so we can save a few CPU cycles if the check can return early. The same can be said for width and height. It probably doesn't matter with modern compiler optimization, but still...
Sure.