In preparation for nulldrv display modes support.
-- v5: win32u: Move display placement logic out of graphics drivers. win32u: Move full display mode lookup out of graphics drivers. win32u: Use current mode position if desired mode doesn't specify it. win32u: Always copy devmode in validate_display_settings. win32u: Read registry or current mode when validation needs it.
From: R��mi Bernon rbernon@codeweavers.com
--- dlls/win32u/sysparams.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index ef2462e6540..c678f6adcf9 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2037,7 +2037,7 @@ static BOOL is_detached_mode( const DEVMODEW *mode ) mode->dmPelsHeight == 0; }
-static DEVMODEW *validate_display_settings( DEVMODEW *default_mode, DEVMODEW *current_mode, DEVMODEW *devmode ) +static DEVMODEW *validate_display_settings( const WCHAR *adapter_path, const WCHAR *device_name, DEVMODEW *devmode, DEVMODEW *temp_mode ) { if (devmode) { @@ -2054,9 +2054,9 @@ static DEVMODEW *validate_display_settings( DEVMODEW *default_mode, DEVMODEW *cu
if (!devmode) { - if (!default_mode->dmSize) return NULL; + if (!read_registry_settings( adapter_path, temp_mode )) return NULL; TRACE( "Return to original display mode\n" ); - devmode = default_mode; + devmode = temp_mode; }
if ((devmode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT)) @@ -2067,9 +2067,10 @@ static DEVMODEW *validate_display_settings( DEVMODEW *default_mode, DEVMODEW *cu
if (!is_detached_mode( devmode ) && (!devmode->dmPelsWidth || !devmode->dmPelsHeight)) { - if (!current_mode->dmSize) return NULL; - if (!devmode->dmPelsWidth) devmode->dmPelsWidth = current_mode->dmPelsWidth; - if (!devmode->dmPelsHeight) devmode->dmPelsHeight = current_mode->dmPelsHeight; + DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}; + if (!user_driver->pGetCurrentDisplaySettings( device_name, ¤t_mode )) return NULL; + if (!devmode->dmPelsWidth) devmode->dmPelsWidth = current_mode.dmPelsWidth; + if (!devmode->dmPelsHeight) devmode->dmPelsHeight = current_mode.dmPelsHeight; }
return devmode; @@ -2081,8 +2082,8 @@ static DEVMODEW *validate_display_settings( DEVMODEW *default_mode, DEVMODEW *cu LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devmode, HWND hwnd, DWORD flags, void *lparam ) { - DEVMODEW default_mode = {.dmSize = sizeof(DEVMODEW)}, current_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;
@@ -2110,10 +2111,7 @@ LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devm return DISP_CHANGE_BADPARAM; }
- if (!read_registry_settings( adapter_path, &default_mode )) default_mode.dmSize = 0; - if (!NtUserEnumDisplaySettings( devname, ENUM_CURRENT_SETTINGS, ¤t_mode, 0 )) current_mode.dmSize = 0; - - if (!(devmode = validate_display_settings( &default_mode, ¤t_mode, devmode ))) ret = DISP_CHANGE_BADMODE; + 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; 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;
From: R��mi Bernon rbernon@codeweavers.com
So that updating dmPelsWidth / dmPelsHeight doesn't modify user devmode. --- dlls/win32u/sysparams.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index c678f6adcf9..079cdc313c3 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2052,12 +2052,13 @@ static DEVMODEW *validate_display_settings( const WCHAR *adapter_path, const WCH devmode = NULL; }
- if (!devmode) + if (devmode) memcpy( temp_mode, devmode, devmode->dmSize ); + else { if (!read_registry_settings( adapter_path, temp_mode )) return NULL; TRACE( "Return to original display mode\n" ); - devmode = temp_mode; } + devmode = temp_mode;
if ((devmode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT)) {
From: R��mi Bernon rbernon@codeweavers.com
--- dlls/win32u/sysparams.c | 4 +++- dlls/winex11.drv/display.c | 10 ---------- 2 files changed, 3 insertions(+), 11 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 079cdc313c3..0dbcedc38d4 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2066,12 +2066,14 @@ static DEVMODEW *validate_display_settings( const WCHAR *adapter_path, const WCH return NULL; }
- if (!is_detached_mode( devmode ) && (!devmode->dmPelsWidth || !devmode->dmPelsHeight)) + if (!is_detached_mode( devmode ) && (!devmode->dmPelsWidth || !devmode->dmPelsHeight || !(devmode->dmFields & DM_POSITION))) { DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}; if (!user_driver->pGetCurrentDisplaySettings( device_name, ¤t_mode )) return NULL; if (!devmode->dmPelsWidth) devmode->dmPelsWidth = current_mode.dmPelsWidth; if (!devmode->dmPelsHeight) devmode->dmPelsHeight = current_mode.dmPelsHeight; + if (!(devmode->dmFields & DM_POSITION)) devmode->dmPosition = current_mode.dmPosition; + devmode->dmFields |= DM_POSITION; }
return devmode; diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index 77f71b73b77..34fd0136fc3 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -498,16 +498,6 @@ static LONG get_display_settings(DEVMODEW **new_displays, const WCHAR *dev_name, else if (!wcsicmp(dev_name, display_device.DeviceName)) { *mode = *dev_mode; - if (!(dev_mode->dmFields & DM_POSITION)) - { - 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->dmFields |= DM_POSITION; - mode->dmPosition = current_mode.dmPosition; - } } else {
From: R��mi Bernon rbernon@codeweavers.com
--- dlls/win32u/sysparams.c | 48 ++++++++++++++++++++++++++++++++++---- dlls/winemac.drv/display.c | 3 +-- dlls/winex11.drv/display.c | 30 +----------------------- 3 files changed, 45 insertions(+), 36 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 0dbcedc38d4..7429c17c94e 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2037,7 +2037,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 && devmode->dmDisplayFrequency != 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) { @@ -2076,6 +2103,12 @@ static DEVMODEW *validate_display_settings( const WCHAR *adapter_path, const WCH devmode->dmFields |= DM_POSITION; }
+ if ((devmode = find_display_mode( modes, devmode )) && devmode != temp_mode) + { + devmode->dmFields |= DM_POSITION; + devmode->dmPosition = temp_mode->dmPosition; + } + return devmode; }
@@ -2085,10 +2118,11 @@ 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; + SIZE_T size;
TRACE( "%s %p %p %#x %p\n", debugstr_us(devname), devmode, hwnd, flags, lparam ); TRACE( "flags=%s\n", _CDS_flags(flags) ); @@ -2106,19 +2140,23 @@ LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devm { lstrcpyW( device_name, adapter->dev.device_name ); lstrcpyW( adapter_path, adapter->config_key ); + size = sizeof(DEVMODEW) + adapter->modes[0].dmDriverExtra; + /* allocate an extra mode to make iteration easier */ + modes = calloc( adapter->mode_count + 1, size ); + if (modes) memcpy( modes, adapter->modes, adapter->mode_count * size ); } 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 8baa4b6df83..52c4b8846f1 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 34fd0136fc3..5304a2c0ecb 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -774,41 +774,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/win32u/driver.c | 14 +- dlls/win32u/sysparams.c | 275 +++++++++++++++++++++++++++-- dlls/wineandroid.drv/init.c | 9 +- dlls/winemac.drv/display.c | 101 +++++------ dlls/winemac.drv/gdi.c | 2 +- dlls/winemac.drv/macdrv.h | 3 +- dlls/winex11.drv/display.c | 342 ++++-------------------------------- dlls/winex11.drv/init.c | 2 +- dlls/winex11.drv/x11drv.h | 3 +- include/wine/gdi_driver.h | 2 +- 10 files changed, 357 insertions(+), 396 deletions(-)
diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index e8f410b7f4c..32c7ed0a591 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 7429c17c94e..e942df4e731 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -602,8 +602,8 @@ static BOOL read_display_adapter_settings( unsigned int index, struct adapter *i if (query_reg_value( hkey, driver_extraW, value, sizeof(buffer) ) && value->Type == REG_DWORD) driver_extra = *(const DWORD *)value->Data;
- /* Modes */ - if ((info->modes = calloc( info->mode_count, sizeof(DEVMODEW) + driver_extra ))) + /* Modes, allocate an extra mode for easier iteration */ + if ((info->modes = calloc( info->mode_count + 1, sizeof(DEVMODEW) + driver_extra ))) { for (i = 0, mode = info->modes; i < info->mode_count; i++) { @@ -2112,6 +2112,267 @@ 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, *full, *displays; + struct adapter *adapter; + UINT size = 0; + BOOL ret; + + if (!lock_display_devices()) return NULL; + + LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry ) + { + if (!adapter->mode_count) continue; + size += sizeof(DEVMODEW) + adapter->modes[0].dmDriverExtra; + } + + /* allocate an extra mode for easier iteration */ + if (!(displays = calloc( 1, size + sizeof(DEVMODEW) ))) goto done; + mode = displays; + + LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry ) + { + if (!adapter->mode_count) continue; + + 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; + } + + if (!(full = find_display_mode( adapter->modes, mode ))) goto done; + mode->dmDriverExtra = full->dmDriverExtra; + memcpy( mode + 1, full + 1, full->dmDriverExtra ); + + 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.@) */ @@ -2127,13 +2388,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 ))) @@ -2155,7 +2410,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 4ddc66e73e8..7d05475dd27 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 52c4b8846f1..3e2788f8803 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 { @@ -804,89 +805,78 @@ better: }
/*********************************************************************** - * 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->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel != bpp) - TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, devmode->dmBitsPerPel); - - TRACE("looking for %dx%dx%dbpp @%d Hz", - (devmode->dmFields & DM_PELSWIDTH ? devmode->dmPelsWidth : 0), - (devmode->dmFields & DM_PELSHEIGHT ? devmode->dmPelsHeight : 0), - bpp, - (devmode->dmFields & DM_DISPLAYFREQUENCY ? devmode->dmDisplayFrequency : 0)); - if (devmode->dmFields & DM_DISPLAYFIXEDOUTPUT) - TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un"); - if (devmode->dmFields & DM_DISPLAYFLAGS) - TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-"); - TRACE("\n");
- 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; + } + + if ((mode->dmFields & DM_BITSPERPEL) && 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->dmFields & DM_PELSWIDTH ? mode->dmPelsWidth : 0), + (mode->dmFields & DM_PELSHEIGHT ? mode->dmPelsHeight : 0), + bpp, + (mode->dmFields & DM_DISPLAYFREQUENCY ? mode->dmDisplayFrequency : 0)); + if (mode->dmFields & DM_DISPLAYFIXEDOUTPUT) + TRACE(" %sstretched", mode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un"); + if (mode->dmFields & DM_DISPLAYFLAGS) + 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(&displays[0], best_display_mode)) + 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); @@ -902,7 +892,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 { @@ -910,16 +899,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 2e44206dc92..e0492046ccd 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 5304a2c0ecb..2556ac15b4e 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -408,22 +408,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; @@ -455,283 +448,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; @@ -754,49 +486,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 31a94d1632a..7300bd91aec 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*);
This should now pass the tests, as shown here https://testbot.winehq.org/JobDetails.pl?Key=120730. The issue was that the `winex11` driver extra data layout isn't portable and so reading it from the registry doesn't work. I've kept the `winex11` `get_full_mode` helper call for now.
For XRandR I believe RROutput and RRMode are XID and should be valid across process (though arguably if the X11 display isn't the same, it may not work), but for XF86VM there's possibly some private data in XF86VidModeModeInfo which I'm not sure can be.
Maybe the idea of putting the driver data in the registry is just wrong.
Zhiyi Zhang (@zhiyi) commented about dlls/win32u/sysparams.c:
return NULL; }
- if (!is_detached_mode( devmode ) && (!devmode->dmPelsWidth || !devmode->dmPelsHeight))
- if (!is_detached_mode( devmode ) && (!devmode->dmPelsWidth || !devmode->dmPelsHeight || !(devmode->dmFields & DM_POSITION))) { DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}; if (!user_driver->pGetCurrentDisplaySettings( device_name, ¤t_mode )) return NULL; if (!devmode->dmPelsWidth) devmode->dmPelsWidth = current_mode.dmPelsWidth; if (!devmode->dmPelsHeight) devmode->dmPelsHeight = current_mode.dmPelsHeight;
if (!(devmode->dmFields & DM_POSITION)) devmode->dmPosition = current_mode.dmPosition;
devmode->dmFields |= DM_POSITION;
This line should be inside of the if branch.
Maybe the idea of putting the driver data in the registry is just wrong.
Yeah, it doesn't seem necessary to have driver private data in the registry.
On Tue Aug 9 09:43:15 2022 +0000, Zhiyi Zhang wrote:
Maybe the idea of putting the driver data in the registry is just wrong.
Yeah, it doesn't seem necessary to have driver private data in the registry.
Well it would avoid having to enumerate available modes each time display modes are changed, which I think could be a good thing.