From: R��mi Bernon rbernon@codeweavers.com
--- dlls/win32u/driver.c | 8 +- dlls/win32u/sysparams.c | 288 +++++++++++++++++++++++++- dlls/wineandroid.drv/init.c | 5 +- dlls/winemac.drv/display.c | 80 +++----- dlls/winemac.drv/macdrv.h | 3 +- dlls/winex11.drv/display.c | 391 ++---------------------------------- dlls/winex11.drv/x11drv.h | 3 +- include/wine/gdi_driver.h | 2 +- 8 files changed, 332 insertions(+), 448 deletions(-)
diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 8f23872bb20..347e57d8f1d 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -753,7 +753,7 @@ static void nulldrv_UpdateClipboard(void) { }
-static LONG nulldrv_ChangeDisplaySettingsEx( LPCWSTR name, LPDEVMODEW mode, HWND hwnd, +static LONG nulldrv_ChangeDisplaySettingsEx( const DEVMODEW *modes, 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_ChangeDisplaySettingsEx( const DEVMODEW *modes, HWND hwnd, + DWORD flags, LPVOID lparam ) { - return load_driver()->pChangeDisplaySettingsEx( name, mode, hwnd, flags, lparam ); + return load_driver()->pChangeDisplaySettingsEx( modes, hwnd, flags, lparam ); }
static BOOL loaderdrv_CurrentDisplaySettingsEx( LPCWSTR name, LPDEVMODEW mode ) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 3017d02ecaa..7fbdea7e418 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++) { @@ -2108,13 +2108,285 @@ static DEVMODEW *validate_display_settings( DEVMODEW *modes, DEVMODEW *default_m mode->dmFields |= DM_POSITION; if (devmode->dmFields & DM_POSITION) mode->dmPosition = devmode->dmPosition; else mode->dmPosition = current_mode->dmPosition; + }
- return mode; + return mode; +} + +static DEVMODEW *get_display_settings( const WCHAR *devname, const DEVMODEW *fullmode ) +{ + DEVMODEW *mode, *full, *displays; + DISPLAY_DEVICEW display_device; + 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 (devname) + { + if (!wcsicmp( devname, adapter->dev.device_name )) + memcpy( mode, fullmode, mode->dmSize + fullmode->dmDriverExtra ); + else + { + if (!(ret = user_driver->pCurrentDisplaySettingsEx( adapter->dev.device_name, mode ))) goto done; + if (!(full = find_display_mode( adapter->modes, mode ))) goto done; + mode->dmDriverExtra = full->dmDriverExtra; + memcpy( mode + 1, full + 1, full->dmDriverExtra ); + } + } + else if (!(ret = read_registry_settings( adapter->config_key, mode ))) goto done; + + lstrcpyW( mode->dmDeviceName, display_device.DeviceName ); + 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 ) +{ + 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 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 *fullmode, + HWND hwnd, DWORD flags, void *lparam ) +{ + DEVMODEW *displays; + LONG ret; + + displays = get_display_settings( devname, fullmode ); + 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->pChangeDisplaySettingsEx( displays, hwnd, flags, lparam ); + + free( displays ); + return ret; +} + /*********************************************************************** * NtUserChangeDisplaySettings (win32u.@) */ @@ -2130,13 +2402,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 ))) @@ -2161,7 +2427,7 @@ LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devm if (!(devmode = validate_display_settings( modes, &default_mode, ¤t_mode, devmode ))) 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 d48834b8817..a40efafb49c 100644 --- a/dlls/wineandroid.drv/init.c +++ b/dlls/wineandroid.drv/init.c @@ -262,10 +262,9 @@ static BOOL CDECL ANDROID_DeleteDC( PHYSDEV dev ) /*********************************************************************** * ANDROID_ChangeDisplaySettingsEx */ -LONG ANDROID_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode, - HWND hwnd, DWORD flags, LPVOID lpvoid ) +LONG ANDROID_ChangeDisplaySettingsEx( const DEVMODEW *modes, 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", modes, hwnd, flags, lpvoid ); return DISP_CHANGE_SUCCESSFUL; }
diff --git a/dlls/winemac.drv/display.c b/dlls/winemac.drv/display.c index de362c676a7..11b1e2eb9c4 100644 --- a/dlls/winemac.drv/display.c +++ b/dlls/winemac.drv/display.c @@ -807,8 +807,7 @@ better: * ChangeDisplaySettingsEx (MACDRV.@) * */ -LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode, - HWND hwnd, DWORD flags, LPVOID lpvoid) +LONG macdrv_ChangeDisplaySettingsEx(const DEVMODEW *modes, HWND hwnd, DWORD flags, LPVOID lpvoid) { WCHAR primary_adapter[CCHDEVICENAME]; LONG ret = DISP_CHANGE_BADMODE; @@ -820,35 +819,13 @@ LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode, 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", modes, 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)) return DISP_CHANGE_FAILED;
@@ -862,29 +839,41 @@ 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) - 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);
- if (best_display_mode) + for (mode = modes; 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(devmode)) + { + 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)) { @@ -902,7 +891,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,12 +898,6 @@ 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); diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 280bb529a77..13cb58fdf0e 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_ChangeDisplaySettingsEx(const DEVMODEW *modes, HWND hwnd, DWORD flags, LPVOID lpvoid) DECLSPEC_HIDDEN; extern BOOL macdrv_CurrentDisplaySettingsEx(LPCWSTR name, LPDEVMODEW devmode) DECLSPEC_HIDDEN; extern BOOL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW devmode, DWORD flags) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index 35065e7cd54..399863800fc 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -390,413 +390,52 @@ BOOL is_detached_mode(const DEVMODEW *mode) mode->dmPelsHeight == 0; }
-/* Get the full display mode with all the necessary fields set. - * Return NULL on failure. Caller should call free_full_mode() to free the returned mode. */ -static DEVMODEW *get_full_mode(ULONG_PTR id, DEVMODEW *dev_mode) +static LONG apply_display_settings(const DEVMODEW *displays, BOOL do_attach) { - DEVMODEW *modes, *full_mode, *found_mode = NULL; - UINT mode_count, mode_idx; - - if (is_detached_mode(dev_mode)) - return 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); - - if (dev_mode->dmFields & DM_BITSPERPEL && - dev_mode->dmBitsPerPel && - found_mode->dmBitsPerPel != dev_mode->dmBitsPerPel) - continue; - if (dev_mode->dmFields & DM_PELSWIDTH && found_mode->dmPelsWidth != dev_mode->dmPelsWidth) - continue; - if (dev_mode->dmFields & DM_PELSHEIGHT && 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) - continue; - if (dev_mode->dmFields & DM_DISPLAYORIENTATION && - found_mode->dmDisplayOrientation != dev_mode->dmDisplayOrientation) - continue; - - break; - } - - if (!found_mode || mode_idx == mode_count) - { - settings_handler.free_modes(modes); - return NULL; - } - - if (!(full_mode = malloc(sizeof(*found_mode) + found_mode->dmDriverExtra))) - { - settings_handler.free_modes(modes); - return NULL; - } - - memcpy(full_mode, found_mode, sizeof(*found_mode) + found_mode->dmDriverExtra); - settings_handler.free_modes(modes); - - full_mode->dmFields |= DM_POSITION; - full_mode->dmPosition = dev_mode->dmPosition; - return full_mode; -} - -static void free_full_mode(DEVMODEW *mode) -{ - if (!is_detached_mode(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) -{ - DEVMODEW *full_mode; BOOL attached_mode; - DEVMODEW *mode; LONG ret;
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) { - ULONG_PTR *id = (ULONG_PTR *)(mode + 1); + ULONG_PTR id; + + if (!settings_handler.get_id(mode->dmDeviceName, &id)) + return DISP_CHANGE_BADPARAM;
attached_mode = !is_detached_mode(mode); if ((attached_mode && !do_attach) || (!attached_mode && do_attach)) continue;
- full_mode = get_full_mode(*id, mode); - if (!full_mode) - return DISP_CHANGE_BADMODE; - TRACE("handler:%s changing %s to position:(%d,%d) resolution:%ux%u frequency:%uHz " "depth:%ubits orientation:%#x.\n", settings_handler.name, wine_dbgstr_w(mode->dmDeviceName), - full_mode->dmPosition.x, full_mode->dmPosition.y, full_mode->dmPelsWidth, - full_mode->dmPelsHeight, full_mode->dmDisplayFrequency, full_mode->dmBitsPerPel, - full_mode->dmDisplayOrientation); - - ret = settings_handler.set_current_mode(*id, full_mode); - if (attached_mode && ret == DISP_CHANGE_SUCCESSFUL) - set_display_depth(*id, full_mode->dmBitsPerPel); - free_full_mode(full_mode); - if (ret != DISP_CHANGE_SUCCESSFUL) - return ret; - } + mode->dmPosition.x, mode->dmPosition.y, mode->dmPelsWidth, + mode->dmPelsHeight, mode->dmDisplayFrequency, mode->dmBitsPerPel, + mode->dmDisplayOrientation);
- 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; + if ((ret = settings_handler.set_current_mode(id, mode))) return ret; + if (attached_mode) set_display_depth(id, mode->dmBitsPerPel); }
- return TRUE; + return DISP_CHANGE_SUCCESSFUL; }
/*********************************************************************** * ChangeDisplaySettingsEx (X11DRV.@) * */ -LONG X11DRV_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode, - HWND hwnd, DWORD flags, LPVOID lpvoid ) +LONG X11DRV_ChangeDisplaySettingsEx( const DEVMODEW *modes, HWND hwnd, DWORD flags, LPVOID lpvoid ) { - DEVMODEW *displays; LONG ret;
- ret = get_display_settings( &displays, devname, devmode ); - if (ret != DISP_CHANGE_SUCCESSFUL) - return ret; - - if (all_detached_settings( displays )) - { - WARN("Detaching all displays is not permitted.\n"); - free(displays); - return DISP_CHANGE_SUCCESSFUL; - } - - place_all_displays( displays ); - /* Detach displays first to free up CRTCs */ - ret = apply_display_settings( displays, FALSE ); + ret = apply_display_settings( modes, FALSE ); if (ret == DISP_CHANGE_SUCCESSFUL) - ret = apply_display_settings( displays, TRUE ); + ret = apply_display_settings( modes, TRUE ); if (ret == DISP_CHANGE_SUCCESSFUL) X11DRV_DisplayDevices_Update(TRUE); - free(displays); + return ret; }
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 6aa9b82e8cf..23d508120c8 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_ChangeDisplaySettingsEx( const DEVMODEW *modes, HWND hwnd, DWORD flags, LPVOID lpvoid ) DECLSPEC_HIDDEN; extern BOOL X11DRV_CurrentDisplaySettingsEx( LPCWSTR name, LPDEVMODEW devmode ) DECLSPEC_HIDDEN; extern BOOL X11DRV_EnumDisplaySettingsEx( LPCWSTR name, DWORD n, LPDEVMODEW devmode, DWORD flags ) DECLSPEC_HIDDEN; diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 8ac3be3bfb8..dad15eb0070 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 (*pChangeDisplaySettingsEx)(const DEVMODEW *,HWND,DWORD,LPVOID); BOOL (*pCurrentDisplaySettingsEx)(LPCWSTR,LPDEVMODEW); BOOL (*pEnumDisplaySettingsEx)(LPCWSTR,DWORD,LPDEVMODEW,DWORD); BOOL (*pUpdateDisplayDevices)(const struct gdi_device_manager *,BOOL,void*);