win32u: Move display placement logic out of graphics drivers.

This commit is contained in:
Rémi Bernon 2022-08-16 09:13:24 +02:00 committed by Alexandre Julliard
parent 8b737af658
commit 12d0792f74
10 changed files with 337 additions and 382 deletions

View file

@ -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);

View file

@ -660,8 +660,8 @@ static BOOL read_display_adapter_settings( unsigned int index, struct adapter *i
if (query_reg_value( hkey, mode_countW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
info->mode_count = *(const DWORD *)value->Data;
/* Modes */
if ((info->modes = calloc( info->mode_count, sizeof(DEVMODEW) )))
/* Modes, allocate an extra mode for easier iteration */
if ((info->modes = calloc( info->mode_count + 1, sizeof(DEVMODEW) )))
{
for (i = 0, mode = info->modes; i < info->mode_count; i++)
{
@ -2192,6 +2192,254 @@ static DEVMODEW *get_full_mode( const WCHAR *adapter_path, const WCHAR *device_n
return devmode;
}
static DEVMODEW *get_display_settings( const WCHAR *devname, const DEVMODEW *devmode )
{
DEVMODEW *mode, *displays;
struct adapter *adapter;
BOOL ret;
if (!lock_display_devices()) return NULL;
/* allocate an extra mode for easier iteration */
if (!(displays = calloc( list_count( &adapters ) + 1, sizeof(DEVMODEW) ))) goto done;
mode = displays;
LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry )
{
mode->dmSize = sizeof(DEVMODEW);
if (devmode && !wcsicmp( devname, adapter->dev.device_name ))
memcpy( &mode->dmFields, &devmode->dmFields, devmode->dmSize - offsetof(DEVMODEW, dmFields) );
else
{
if (!devname) ret = read_registry_settings( adapter->config_key, mode );
else ret = user_driver->pGetCurrentDisplaySettings( adapter->dev.device_name, mode );
if (!ret) goto done;
}
lstrcpyW( mode->dmDeviceName, adapter->dev.device_name );
mode = NEXT_DEVMODEW(mode);
}
unlock_display_devices();
return displays;
done:
unlock_display_devices();
free( displays );
return NULL;
}
static INT offset_length( POINT offset )
{
return offset.x * offset.x + offset.y * offset.y;
}
static void set_rect_from_devmode( RECT *rect, const DEVMODEW *mode )
{
SetRect( rect, mode->dmPosition.x, mode->dmPosition.y, mode->dmPosition.x + mode->dmPelsWidth,
mode->dmPosition.y + mode->dmPelsHeight );
}
/* Check if a rect overlaps with placed display rects */
static BOOL overlap_placed_displays( const RECT *rect, const DEVMODEW *displays )
{
const DEVMODEW *mode;
RECT intersect;
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
{
set_rect_from_devmode( &intersect, mode );
if ((mode->dmFields & DM_POSITION) && intersect_rect( &intersect, &intersect, rect )) return TRUE;
}
return FALSE;
}
/* Get the offset with minimum length to place a display next to the placed displays with no spacing and overlaps */
static POINT get_placement_offset( const DEVMODEW *displays, const DEVMODEW *placing )
{
POINT points[8], left_top, offset, min_offset = {0, 0};
INT point_idx, point_count, vertex_idx;
BOOL has_placed = FALSE, first = TRUE;
RECT desired_rect, rect;
const DEVMODEW *mode;
INT width, height;
set_rect_from_devmode( &desired_rect, placing );
/* If the display to be placed is detached, no offset is needed to place it */
if (IsRectEmpty( &desired_rect )) return min_offset;
/* If there is no placed and attached display, place this display as it is */
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
{
set_rect_from_devmode( &rect, mode );
if ((mode->dmFields & DM_POSITION) && !IsRectEmpty( &rect ))
{
has_placed = TRUE;
break;
}
}
if (!has_placed) return min_offset;
/* Try to place this display with each of its four vertices at every vertex of the placed
* displays and see which combination has the minimum offset length */
width = desired_rect.right - desired_rect.left;
height = desired_rect.bottom - desired_rect.top;
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
{
set_rect_from_devmode( &rect, mode );
if (!(mode->dmFields & DM_POSITION) || IsRectEmpty( &rect )) continue;
/* Get four vertices of the placed display rectangle */
points[0].x = rect.left;
points[0].y = rect.top;
points[1].x = rect.left;
points[1].y = rect.bottom;
points[2].x = rect.right;
points[2].y = rect.top;
points[3].x = rect.right;
points[3].y = rect.bottom;
point_count = 4;
/* Intersected points when moving the display to be placed horizontally */
if (desired_rect.bottom >= rect.top && desired_rect.top <= rect.bottom)
{
points[point_count].x = rect.left;
points[point_count++].y = desired_rect.top;
points[point_count].x = rect.right;
points[point_count++].y = desired_rect.top;
}
/* Intersected points when moving the display to be placed vertically */
if (desired_rect.left <= rect.right && desired_rect.right >= rect.left)
{
points[point_count].x = desired_rect.left;
points[point_count++].y = rect.top;
points[point_count].x = desired_rect.left;
points[point_count++].y = rect.bottom;
}
/* Try moving each vertex of the display rectangle to each points */
for (point_idx = 0; point_idx < point_count; ++point_idx)
{
for (vertex_idx = 0; vertex_idx < 4; ++vertex_idx)
{
switch (vertex_idx)
{
/* Move the bottom right vertex to the point */
case 0:
left_top.x = points[point_idx].x - width;
left_top.y = points[point_idx].y - height;
break;
/* Move the bottom left vertex to the point */
case 1:
left_top.x = points[point_idx].x;
left_top.y = points[point_idx].y - height;
break;
/* Move the top right vertex to the point */
case 2:
left_top.x = points[point_idx].x - width;
left_top.y = points[point_idx].y;
break;
/* Move the top left vertex to the point */
case 3:
left_top.x = points[point_idx].x;
left_top.y = points[point_idx].y;
break;
}
offset.x = left_top.x - desired_rect.left;
offset.y = left_top.y - desired_rect.top;
rect = desired_rect;
OffsetRect( &rect, offset.x, offset.y );
if (!overlap_placed_displays( &rect, displays ))
{
if (first)
{
min_offset = offset;
first = FALSE;
continue;
}
if (offset_length( offset ) < offset_length( min_offset )) min_offset = offset;
}
}
}
}
return min_offset;
}
static void place_all_displays( DEVMODEW *displays )
{
POINT min_offset, offset;
DEVMODEW *mode, *placing;
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
mode->dmFields &= ~DM_POSITION;
/* Place all displays with no extra space between them and no overlapping */
while (1)
{
/* Place the unplaced display with the minimum offset length first */
placing = NULL;
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
{
if (mode->dmFields & DM_POSITION) continue;
offset = get_placement_offset( displays, mode );
if (!placing || offset_length( offset ) < offset_length( min_offset ))
{
min_offset = offset;
placing = mode;
}
}
/* If all displays are placed */
if (!placing) break;
placing->dmPosition.x += min_offset.x;
placing->dmPosition.y += min_offset.y;
placing->dmFields |= DM_POSITION;
}
}
static BOOL all_detached_settings( const DEVMODEW *displays )
{
const DEVMODEW *mode;
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
if (!is_detached_mode( mode )) return FALSE;
return TRUE;
}
static LONG apply_display_settings( const WCHAR *devname, const DEVMODEW *devmode,
HWND hwnd, DWORD flags, void *lparam )
{
DEVMODEW *displays;
LONG ret;
displays = get_display_settings( devname, devmode );
if (!displays) return DISP_CHANGE_FAILED;
if (all_detached_settings( displays ))
{
WARN( "Detaching all modes is not permitted.\n" );
free( displays );
return DISP_CHANGE_SUCCESSFUL;
}
place_all_displays( displays );
ret = user_driver->pChangeDisplaySettings( displays, hwnd, flags, lparam );
free( displays );
return ret;
}
/***********************************************************************
* NtUserChangeDisplaySettings (win32u.@)
*/
@ -2206,13 +2454,7 @@ LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devm
TRACE( "%s %p %p %#x %p\n", debugstr_us(devname), devmode, hwnd, flags, lparam );
TRACE( "flags=%s\n", _CDS_flags(flags) );
if ((!devname || !devname->Length) && !devmode)
{
ret = user_driver->pChangeDisplaySettingsEx( NULL, NULL, hwnd, flags, lparam );
if (ret != DISP_CHANGE_SUCCESSFUL)
ERR( "Restoring all displays to their registry settings returned %d.\n", ret );
return ret;
}
if ((!devname || !devname->Length) && !devmode) return apply_display_settings( NULL, NULL, hwnd, flags, lparam );
if (!lock_display_devices()) return DISP_CHANGE_FAILED;
if ((adapter = find_adapter( devname )))
@ -2233,7 +2475,7 @@ LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devm
if (!(devmode = get_full_mode( adapter_path, device_name, modes, devmode, &temp_mode ))) ret = DISP_CHANGE_BADMODE;
else if ((flags & CDS_UPDATEREGISTRY) && !write_registry_settings( adapter_path, devmode )) ret = DISP_CHANGE_NOTUPDATED;
else if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL;
else ret = user_driver->pChangeDisplaySettingsEx( device_name, devmode, hwnd, flags, lparam );
else ret = apply_display_settings( device_name, devmode, hwnd, flags, lparam );
free( modes );
if (ret) ERR( "Changing %s display settings returned %d.\n", debugstr_us(devname), ret );

View file

@ -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,

View file

@ -34,6 +34,7 @@
WINE_DEFAULT_DEBUG_CHANNEL(display);
#define NEXT_DEVMODEW(mode) ((DEVMODEW *)((char *)((mode) + 1) + (mode)->dmDriverExtra))
struct display_mode_descriptor
{
@ -769,84 +770,73 @@ static CGDisplayModeRef find_best_display_mode(DEVMODEW *devmode, CFArrayRef dis
}
/***********************************************************************
* ChangeDisplaySettingsEx (MACDRV.@)
* ChangeDisplaySettings (MACDRV.@)
*
*/
LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
HWND hwnd, DWORD flags, LPVOID lpvoid)
LONG macdrv_ChangeDisplaySettings(LPDEVMODEW displays, HWND hwnd, DWORD flags, LPVOID lpvoid)
{
WCHAR primary_adapter[CCHDEVICENAME];
LONG ret = DISP_CHANGE_BADMODE;
DEVMODEW default_mode;
LONG ret = DISP_CHANGE_SUCCESSFUL;
DEVMODEW *mode;
int bpp;
struct macdrv_display *displays;
struct macdrv_display *macdrv_displays;
int num_displays;
CFArrayRef display_modes;
struct display_mode_descriptor *desc;
CGDisplayModeRef best_display_mode;
TRACE("%s %p %p 0x%08x %p\n", debugstr_w(devname), devmode, hwnd, flags, lpvoid);
TRACE("%p %p 0x%08x %p\n", displays, hwnd, flags, lpvoid);
init_original_display_mode();
if (!get_primary_adapter(primary_adapter))
return DISP_CHANGE_FAILED;
if (!devname && !devmode)
{
UNICODE_STRING str;
memset(&default_mode, 0, sizeof(default_mode));
default_mode.dmSize = sizeof(default_mode);
RtlInitUnicodeString(&str, primary_adapter);
if (!NtUserEnumDisplaySettings(&str, ENUM_REGISTRY_SETTINGS, &default_mode, 0))
{
ERR("Default mode not found for %s!\n", wine_dbgstr_w(primary_adapter));
return DISP_CHANGE_BADMODE;
}
devname = primary_adapter;
devmode = &default_mode;
}
else if (wcsicmp(primary_adapter, devname))
{
FIXME("Changing non-primary adapter settings is currently unsupported.\n");
return DISP_CHANGE_SUCCESSFUL;
}
if (is_detached_mode(devmode))
{
FIXME("Detaching adapters is currently unsupported.\n");
return DISP_CHANGE_SUCCESSFUL;
}
if (macdrv_get_displays(&displays, &num_displays))
if (macdrv_get_displays(&macdrv_displays, &num_displays))
return DISP_CHANGE_FAILED;
display_modes = copy_display_modes(displays[0].displayID, FALSE);
display_modes = copy_display_modes(macdrv_displays[0].displayID, FALSE);
if (!display_modes)
{
macdrv_free_displays(displays);
macdrv_free_displays(macdrv_displays);
return DISP_CHANGE_FAILED;
}
pthread_mutex_lock(&cached_modes_mutex);
bpp = get_default_bpp();
pthread_mutex_unlock(&cached_modes_mutex);
if (devmode->dmBitsPerPel != bpp)
TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, devmode->dmBitsPerPel);
TRACE("looking for %dx%dx%dbpp @%d Hz", devmode->dmPelsWidth, devmode->dmPelsHeight,
bpp, devmode->dmDisplayFrequency);
TRACE(" %sstretched", devmode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un");
TRACE(" %sinterlaced", devmode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
TRACE("\n");
desc = create_original_display_mode_descriptor(macdrv_displays[0].displayID);
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 = displays; mode->dmSize && !ret; mode = NEXT_DEVMODEW(mode))
{
if (macdrv_set_display_mode(&displays[0], best_display_mode))
if (wcsicmp(primary_adapter, mode->dmDeviceName))
{
FIXME("Changing non-primary adapter settings is currently unsupported.\n");
continue;
}
if (is_detached_mode(mode))
{
FIXME("Detaching adapters is currently unsupported.\n");
continue;
}
if (mode->dmBitsPerPel != bpp)
TRACE("using default %d bpp instead of caller's request %d bpp\n", bpp, mode->dmBitsPerPel);
TRACE("looking for %dx%dx%dbpp @%d Hz", mode->dmPelsWidth, mode->dmPelsHeight,
bpp, mode->dmDisplayFrequency);
TRACE(" %sstretched", mode->dmDisplayFixedOutput == DMDFO_STRETCH ? "" : "un");
TRACE(" %sinterlaced", mode->dmDisplayFlags & DM_INTERLACED ? "" : "non-");
TRACE("\n");
if (!(best_display_mode = find_best_display_mode(mode, display_modes, bpp, desc)))
{
ERR("No matching mode found %ux%ux%d @%u!\n", mode->dmPelsWidth, mode->dmPelsHeight,
bpp, mode->dmDisplayFrequency);
ret = DISP_CHANGE_BADMODE;
}
else if (macdrv_set_display_mode(&macdrv_displays[0], best_display_mode))
{
int mode_bpp = display_mode_bits_per_pixel(best_display_mode);
size_t width = CGDisplayModeGetWidth(best_display_mode);
@ -862,7 +852,6 @@ LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
send_message(NtUserGetDesktopWindow(), WM_MACDRV_UPDATE_DESKTOP_RECT, mode_bpp,
MAKELPARAM(width, height));
ret = DISP_CHANGE_SUCCESSFUL;
}
else
{
@ -870,16 +859,10 @@ LONG macdrv_ChangeDisplaySettingsEx(LPCWSTR devname, LPDEVMODEW devmode,
ret = DISP_CHANGE_FAILED;
}
}
else
{
/* no valid modes found */
ERR("No matching mode found %ux%ux%d @%u!\n", devmode->dmPelsWidth, devmode->dmPelsHeight,
bpp, devmode->dmDisplayFrequency);
}
free_display_mode_descriptor(desc);
CFRelease(display_modes);
macdrv_free_displays(displays);
macdrv_free_displays(macdrv_displays);
return ret;
}

View file

@ -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,

View file

@ -123,8 +123,7 @@ static inline RECT rect_from_cgrect(CGRect cgrect)
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;

View file

@ -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(&registry_mode, 0, sizeof(registry_mode));
registry_mode.dmSize = sizeof(registry_mode);
if (!NtUserEnumDisplaySettings( &device_name, ENUM_REGISTRY_SETTINGS, &registry_mode, 0 ))
goto done;
*mode = registry_mode;
}
else if (!wcsicmp(dev_name, display_device.DeviceName))
{
*mode = *dev_mode;
}
else
{
memset(&current_mode, 0, sizeof(current_mode));
current_mode.dmSize = sizeof(current_mode);
if (!NtUserEnumDisplaySettings( &device_name, ENUM_CURRENT_SETTINGS, &current_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;
INT left_most = INT_MAX, top_most = INT_MAX;
LONG count, ret = DISP_CHANGE_BADPARAM;
ULONG_PTR *ids;
DEVMODEW *mode;
ret = get_display_settings( &displays, devname, devmode );
if (ret != DISP_CHANGE_SUCCESSFUL)
return ret;
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;
}

View file

@ -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,

View file

@ -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;

View file

@ -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*);