mirror of
git://source.winehq.org/git/wine.git
synced 2024-08-31 22:44:18 +00:00
win32u: Move display placement logic out of graphics drivers.
This commit is contained in:
parent
8b737af658
commit
12d0792f74
|
@ -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);
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -447,283 +447,22 @@ static void free_full_mode(DEVMODEW *mode)
|
|||
free(mode);
|
||||
}
|
||||
|
||||
static LONG get_display_settings(DEVMODEW **new_displays, const WCHAR *dev_name, DEVMODEW *dev_mode)
|
||||
{
|
||||
DEVMODEW registry_mode, current_mode, *mode, *displays;
|
||||
INT display_idx, display_count = 0;
|
||||
DISPLAY_DEVICEW display_device;
|
||||
LONG ret = DISP_CHANGE_FAILED;
|
||||
UNICODE_STRING device_name;
|
||||
|
||||
display_device.cb = sizeof(display_device);
|
||||
for (display_idx = 0; !NtUserEnumDisplayDevices( NULL, display_idx, &display_device, 0 ); ++display_idx)
|
||||
++display_count;
|
||||
|
||||
/* use driver extra data to store an ULONG_PTR adapter id after each mode,
|
||||
* and allocate an extra mode to make iteration easier */
|
||||
if (!(displays = calloc(display_count + 1, sizeof(DEVMODEW) + sizeof(ULONG_PTR)))) goto done;
|
||||
mode = displays;
|
||||
|
||||
for (display_idx = 0; display_idx < display_count; ++display_idx)
|
||||
{
|
||||
ULONG_PTR *id = (ULONG_PTR *)(mode + 1);
|
||||
|
||||
if (NtUserEnumDisplayDevices( NULL, display_idx, &display_device, 0 ))
|
||||
goto done;
|
||||
|
||||
if (!settings_handler.get_id(display_device.DeviceName, id))
|
||||
{
|
||||
ret = DISP_CHANGE_BADPARAM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
RtlInitUnicodeString( &device_name, display_device.DeviceName );
|
||||
|
||||
if (!dev_mode)
|
||||
{
|
||||
memset(®istry_mode, 0, sizeof(registry_mode));
|
||||
registry_mode.dmSize = sizeof(registry_mode);
|
||||
if (!NtUserEnumDisplaySettings( &device_name, ENUM_REGISTRY_SETTINGS, ®istry_mode, 0 ))
|
||||
goto done;
|
||||
*mode = registry_mode;
|
||||
}
|
||||
else if (!wcsicmp(dev_name, display_device.DeviceName))
|
||||
{
|
||||
*mode = *dev_mode;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(¤t_mode, 0, sizeof(current_mode));
|
||||
current_mode.dmSize = sizeof(current_mode);
|
||||
if (!NtUserEnumDisplaySettings( &device_name, ENUM_CURRENT_SETTINGS, ¤t_mode, 0 ))
|
||||
goto done;
|
||||
*mode = current_mode;
|
||||
}
|
||||
|
||||
mode->dmSize = sizeof(DEVMODEW);
|
||||
lstrcpyW(mode->dmDeviceName, display_device.DeviceName);
|
||||
mode->dmDriverExtra = sizeof(ULONG_PTR);
|
||||
mode = NEXT_DEVMODEW(mode);
|
||||
}
|
||||
|
||||
*new_displays = displays;
|
||||
return DISP_CHANGE_SUCCESSFUL;
|
||||
|
||||
done:
|
||||
free(displays);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static INT offset_length(POINT offset)
|
||||
{
|
||||
return offset.x * offset.x + offset.y * offset.y;
|
||||
}
|
||||
|
||||
static void set_rect_from_devmode(RECT *rect, const DEVMODEW *mode)
|
||||
{
|
||||
SetRect(rect, mode->dmPosition.x, mode->dmPosition.y, mode->dmPosition.x + mode->dmPelsWidth, mode->dmPosition.y + mode->dmPelsHeight);
|
||||
}
|
||||
|
||||
/* Check if a rect overlaps with placed display rects */
|
||||
static BOOL overlap_placed_displays(const RECT *rect, const DEVMODEW *displays)
|
||||
{
|
||||
const DEVMODEW *mode;
|
||||
RECT intersect;
|
||||
|
||||
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
||||
{
|
||||
set_rect_from_devmode(&intersect, mode);
|
||||
if ((mode->dmFields & DM_POSITION) && intersect_rect(&intersect, &intersect, rect))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Get the offset with minimum length to place a display next to the placed displays with no spacing and overlaps */
|
||||
static POINT get_placement_offset(const DEVMODEW *displays, const DEVMODEW *placing)
|
||||
{
|
||||
POINT points[8], left_top, offset, min_offset = {0, 0};
|
||||
INT point_idx, point_count, vertex_idx;
|
||||
BOOL has_placed = FALSE, first = TRUE;
|
||||
RECT desired_rect, rect;
|
||||
const DEVMODEW *mode;
|
||||
INT width, height;
|
||||
|
||||
set_rect_from_devmode(&desired_rect, placing);
|
||||
|
||||
/* If the display to be placed is detached, no offset is needed to place it */
|
||||
if (IsRectEmpty(&desired_rect))
|
||||
return min_offset;
|
||||
|
||||
/* If there is no placed and attached display, place this display as it is */
|
||||
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
||||
{
|
||||
set_rect_from_devmode(&rect, mode);
|
||||
if ((mode->dmFields & DM_POSITION) && !IsRectEmpty(&rect))
|
||||
{
|
||||
has_placed = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_placed)
|
||||
return min_offset;
|
||||
|
||||
/* Try to place this display with each of its four vertices at every vertex of the placed
|
||||
* displays and see which combination has the minimum offset length */
|
||||
width = desired_rect.right - desired_rect.left;
|
||||
height = desired_rect.bottom - desired_rect.top;
|
||||
|
||||
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
||||
{
|
||||
set_rect_from_devmode(&rect, mode);
|
||||
if (!(mode->dmFields & DM_POSITION) || IsRectEmpty(&rect))
|
||||
continue;
|
||||
|
||||
/* Get four vertices of the placed display rectangle */
|
||||
points[0].x = rect.left;
|
||||
points[0].y = rect.top;
|
||||
points[1].x = rect.left;
|
||||
points[1].y = rect.bottom;
|
||||
points[2].x = rect.right;
|
||||
points[2].y = rect.top;
|
||||
points[3].x = rect.right;
|
||||
points[3].y = rect.bottom;
|
||||
point_count = 4;
|
||||
|
||||
/* Intersected points when moving the display to be placed horizontally */
|
||||
if (desired_rect.bottom >= rect.top &&
|
||||
desired_rect.top <= rect.bottom)
|
||||
{
|
||||
points[point_count].x = rect.left;
|
||||
points[point_count++].y = desired_rect.top;
|
||||
points[point_count].x = rect.right;
|
||||
points[point_count++].y = desired_rect.top;
|
||||
}
|
||||
/* Intersected points when moving the display to be placed vertically */
|
||||
if (desired_rect.left <= rect.right &&
|
||||
desired_rect.right >= rect.left)
|
||||
{
|
||||
points[point_count].x = desired_rect.left;
|
||||
points[point_count++].y = rect.top;
|
||||
points[point_count].x = desired_rect.left;
|
||||
points[point_count++].y = rect.bottom;
|
||||
}
|
||||
|
||||
/* Try moving each vertex of the display rectangle to each points */
|
||||
for (point_idx = 0; point_idx < point_count; ++point_idx)
|
||||
{
|
||||
for (vertex_idx = 0; vertex_idx < 4; ++vertex_idx)
|
||||
{
|
||||
switch (vertex_idx)
|
||||
{
|
||||
/* Move the bottom right vertex to the point */
|
||||
case 0:
|
||||
left_top.x = points[point_idx].x - width;
|
||||
left_top.y = points[point_idx].y - height;
|
||||
break;
|
||||
/* Move the bottom left vertex to the point */
|
||||
case 1:
|
||||
left_top.x = points[point_idx].x;
|
||||
left_top.y = points[point_idx].y - height;
|
||||
break;
|
||||
/* Move the top right vertex to the point */
|
||||
case 2:
|
||||
left_top.x = points[point_idx].x - width;
|
||||
left_top.y = points[point_idx].y;
|
||||
break;
|
||||
/* Move the top left vertex to the point */
|
||||
case 3:
|
||||
left_top.x = points[point_idx].x;
|
||||
left_top.y = points[point_idx].y;
|
||||
break;
|
||||
}
|
||||
|
||||
offset.x = left_top.x - desired_rect.left;
|
||||
offset.y = left_top.y - desired_rect.top;
|
||||
rect = desired_rect;
|
||||
OffsetRect(&rect, offset.x, offset.y);
|
||||
if (!overlap_placed_displays(&rect, displays))
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
min_offset = offset;
|
||||
first = FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (offset_length(offset) < offset_length(min_offset))
|
||||
min_offset = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return min_offset;
|
||||
}
|
||||
|
||||
static void place_all_displays(DEVMODEW *displays)
|
||||
{
|
||||
INT left_most = INT_MAX, top_most = INT_MAX;
|
||||
POINT min_offset, offset;
|
||||
DEVMODEW *mode, *placing;
|
||||
|
||||
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
||||
mode->dmFields &= ~DM_POSITION;
|
||||
|
||||
/* Place all displays with no extra space between them and no overlapping */
|
||||
while (1)
|
||||
{
|
||||
/* Place the unplaced display with the minimum offset length first */
|
||||
placing = NULL;
|
||||
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
||||
{
|
||||
if (mode->dmFields & DM_POSITION)
|
||||
continue;
|
||||
|
||||
offset = get_placement_offset(displays, mode);
|
||||
if (!placing || offset_length(offset) < offset_length(min_offset))
|
||||
{
|
||||
min_offset = offset;
|
||||
placing = mode;
|
||||
}
|
||||
}
|
||||
|
||||
/* If all displays are placed */
|
||||
if (!placing)
|
||||
break;
|
||||
|
||||
placing->dmPosition.x += min_offset.x;
|
||||
placing->dmPosition.y += min_offset.y;
|
||||
placing->dmFields |= DM_POSITION;
|
||||
|
||||
left_most = min(left_most, placing->dmPosition.x);
|
||||
top_most = min(top_most, placing->dmPosition.y);
|
||||
}
|
||||
|
||||
/* Convert virtual screen coordinates to root coordinates */
|
||||
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
||||
{
|
||||
mode->dmPosition.x -= left_most;
|
||||
mode->dmPosition.y -= top_most;
|
||||
}
|
||||
}
|
||||
|
||||
static LONG apply_display_settings(DEVMODEW *displays, BOOL do_attach)
|
||||
static LONG apply_display_settings( DEVMODEW *displays, ULONG_PTR *ids, BOOL do_attach )
|
||||
{
|
||||
DEVMODEW *full_mode;
|
||||
BOOL attached_mode;
|
||||
LONG count, ret;
|
||||
DEVMODEW *mode;
|
||||
LONG ret;
|
||||
|
||||
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
||||
for (count = 0, mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode), count++)
|
||||
{
|
||||
ULONG_PTR *id = (ULONG_PTR *)(mode + 1);
|
||||
ULONG_PTR *id = ids + count;
|
||||
|
||||
attached_mode = !is_detached_mode(mode);
|
||||
if ((attached_mode && !do_attach) || (!attached_mode && do_attach))
|
||||
continue;
|
||||
|
||||
/* FIXME: get a full mode again because X11 driver extra data isn't portable */
|
||||
full_mode = get_full_mode(*id, mode);
|
||||
if (!full_mode)
|
||||
return DISP_CHANGE_BADMODE;
|
||||
|
@ -746,49 +485,43 @@ static LONG apply_display_settings(DEVMODEW *displays, BOOL do_attach)
|
|||
return DISP_CHANGE_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static BOOL all_detached_settings(const DEVMODEW *displays)
|
||||
{
|
||||
const DEVMODEW *mode;
|
||||
|
||||
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
||||
{
|
||||
if (!is_detached_mode(mode))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* ChangeDisplaySettingsEx (X11DRV.@)
|
||||
* ChangeDisplaySettings (X11DRV.@)
|
||||
*
|
||||
*/
|
||||
LONG X11DRV_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode,
|
||||
HWND hwnd, DWORD flags, LPVOID lpvoid )
|
||||
LONG X11DRV_ChangeDisplaySettings( LPDEVMODEW displays, HWND hwnd, DWORD flags, LPVOID lpvoid )
|
||||
{
|
||||
DEVMODEW *displays;
|
||||
LONG ret;
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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*);
|
||||
|
|
Loading…
Reference in a new issue