In preparation for nulldrv display modes support.
-- v9: 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 be68a2cff32..374db92812f 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 374db92812f..627ebf30b09 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2083,7 +2083,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) { @@ -2125,6 +2152,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; }
@@ -2134,8 +2167,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;
@@ -2155,19 +2188,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 2d7b9656103..c332491efa5 100644 --- a/dlls/winemac.drv/display.c +++ b/dlls/winemac.drv/display.c @@ -881,8 +881,7 @@ LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
if (best_display_mode) { - if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL; - else if (wcsicmp(primary_adapter, devname)) + if (wcsicmp(primary_adapter, devname)) { FIXME("Changing non-primary adapter settings is currently unsupported.\n"); ret = DISP_CHANGE_SUCCESSFUL; 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 627ebf30b09..be395ba9afc 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; }
@@ -2102,6 +2119,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 c332491efa5..d68bc205591 100644 --- a/dlls/winemac.drv/display.c +++ b/dlls/winemac.drv/display.c @@ -720,7 +720,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;
@@ -733,6 +732,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) { @@ -743,58 +745,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) @@ -862,18 +828,13 @@ LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode, pthread_mutex_lock(&modes_mutex); bpp = get_default_bpp(); pthread_mutex_unlock(&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 | 91 ++++------ 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, 334 insertions(+), 379 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 be395ba9afc..d743aee570a 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++) { @@ -2184,6 +2184,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.@) */ @@ -2198,13 +2446,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 ))) @@ -2225,7 +2467,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 d68bc205591..372b3af21b7 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 { @@ -770,84 +771,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; - } - - 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(&modes_mutex); bpp = get_default_bpp(); pthread_mutex_unlock(&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 (wcsicmp(primary_adapter, devname)) + if (wcsicmp(primary_adapter, mode->dmDeviceName)) { FIXME("Changing non-primary adapter settings is currently unsupported.\n"); - ret = DISP_CHANGE_SUCCESSFUL; + continue; + } + if (is_detached_mode(mode)) + { + FIXME("Detaching adapters is currently unsupported.\n"); + continue; } - else if (macdrv_set_display_mode(&displays[0], best_display_mode)) + + 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); @@ -863,7 +853,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 { @@ -871,16 +860,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*);
Commit 9edff5b0 is causing these on macOS: ``` 009c:err:system:NtUserChangeDisplaySettings Changing L"\\.\DISPLAY2" display settings returned -2. 009c:err:explorer:initialize_display_settings Failed to initialize registry display settings for L"\\.\DISPLAY2". ```
On Wed Aug 24 09:22:07 2022 +0000, Huw Davies wrote:
Commit 9edff5b0 is causing these on macOS:
009c:err:system:NtUserChangeDisplaySettings Changing L"\\\\.\\DISPLAY2" display settings returned -2. 009c:err:explorer:initialize_display_settings Failed to initialize registry display settings for L"\\\\.\\DISPLAY2".
Oof, thanks. Is https://gitlab.winehq.org/wine/wine/-/merge_requests/576/diffs?commit_id=d82... helping by any chance?
On Wed Aug 24 09:22:07 2022 +0000, R��mi Bernon wrote:
Oof, thanks. Is https://gitlab.winehq.org/wine/wine/-/merge_requests/576/diffs?commit_id=d82... helping by any chance?
No, I'm afraid not.
On Wed Aug 24 09:27:29 2022 +0000, Huw Davies wrote:
No, I'm afraid not.
Would you mind getting logs with `WINEDEBUG=+explorer,+system,+display` before and after that commit? I'm not able to reproduce on Github macOS CI.
On Fri Aug 26 08:07:26 2022 +0000, R��mi Bernon wrote:
Would you mind getting logs with `WINEDEBUG=+explorer,+system,+display` before and after that commit? I'm not able to reproduce on Github macOS CI.
Sure (with added +pid) running `wine notepad`.
[dc57b870.log](/uploads/f3af0b042dcbbe68a48554e56979ff79/dc57b870.log)
[9edff5b0.log](/uploads/cabd4118c0b739713e63ef6dedcacdd8/9edff5b0.log)
On Fri Aug 26 08:57:20 2022 +0000, Huw Davies wrote:
Sure (with added +pid) running `wine notepad`. [dc57b870.log](/uploads/f3af0b042dcbbe68a48554e56979ff79/dc57b870.log) [9edff5b0.log](/uploads/cabd4118c0b739713e63ef6dedcacdd8/9edff5b0.log)
I see, looks like it's coming from the second monitor. Afaiu some winemac.drv doesn't really support multi monitor yet, and probably that moving more things to win32u which does ends up causing this.
On Fri Aug 26 10:05:10 2022 +0000, R��mi Bernon wrote:
I see, looks like it's coming from the second monitor. Afaiu some winemac.drv doesn't really support multi monitor yet, and probably that moving more things to win32u which does ends up causing this.
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.