From 6f305dd881e16f77f9eb183684d04b0b8746b769 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Wed, 22 Jul 2020 15:25:08 +0800 Subject: [PATCH] winex11.drv: Support setting virtual desktop display mode using the new display settings handler interface. Signed-off-by: Zhiyi Zhang Signed-off-by: Alexandre Julliard --- dlls/winex11.drv/desktop.c | 11 ++++ dlls/winex11.drv/settings.c | 123 +++++++++++++++++++++++++++++++++++- dlls/winex11.drv/x11drv.h | 6 ++ 3 files changed, 139 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/desktop.c b/dlls/winex11.drv/desktop.c index 72a315e35be..ea269d8f2cc 100644 --- a/dlls/winex11.drv/desktop.c +++ b/dlls/winex11.drv/desktop.c @@ -251,6 +251,16 @@ static BOOL X11DRV_desktop_get_current_mode( ULONG_PTR id, DEVMODEW *mode ) return TRUE; } +static LONG X11DRV_desktop_set_current_mode( ULONG_PTR id, DEVMODEW *mode ) +{ + if (mode->dmFields & DM_BITSPERPEL && mode->dmBitsPerPel != screen_bpp) + WARN("Cannot change screen color depth from %dbits to %dbits!\n", screen_bpp, mode->dmBitsPerPel); + + desktop_width = mode->dmPelsWidth; + desktop_height = mode->dmPelsHeight; + return DISP_CHANGE_SUCCESSFUL; +} + static void query_desktop_work_area( RECT *rc_work ) { static const WCHAR trayW[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d',0}; @@ -378,6 +388,7 @@ void X11DRV_init_desktop( Window win, unsigned int width, unsigned int height ) settings_handler.get_modes = X11DRV_desktop_get_modes; settings_handler.free_modes = X11DRV_desktop_free_modes; settings_handler.get_current_mode = X11DRV_desktop_get_current_mode; + settings_handler.set_current_mode = X11DRV_desktop_set_current_mode; X11DRV_Settings_SetHandler( &settings_handler ); } diff --git a/dlls/winex11.drv/settings.c b/dlls/winex11.drv/settings.c index 8fc8cbd44dd..28c405ae0e0 100644 --- a/dlls/winex11.drv/settings.c +++ b/dlls/winex11.drv/settings.c @@ -30,6 +30,7 @@ #include "winreg.h" #include "wingdi.h" #include "wine/debug.h" +#include "wine/heap.h" #include "wine/unicode.h" WINE_DEFAULT_DEBUG_CHANNEL(x11settings); @@ -466,6 +467,53 @@ 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 free the returned mode. */ +static DEVMODEW *get_full_mode(ULONG_PTR id, const DEVMODEW *dev_mode) +{ + DEVMODEW *modes, *full_mode, *found_mode = NULL; + UINT mode_count, mode_idx; + + if (!handler.get_modes(id, 0, &modes, &mode_count)) + return NULL; + + 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 && 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; + + break; + } + + if (!found_mode || mode_idx == mode_count) + { + handler.free_modes(modes); + return NULL; + } + + if (!(full_mode = heap_alloc(sizeof(*found_mode) + found_mode->dmDriverExtra))) + { + handler.free_modes(modes); + return NULL; + } + + memcpy(full_mode, found_mode, sizeof(*found_mode) + found_mode->dmDriverExtra); + handler.free_modes(modes); + return full_mode; +} + /*********************************************************************** * ChangeDisplaySettingsEx (X11DRV.@) * @@ -475,9 +523,82 @@ LONG CDECL X11DRV_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode, { WCHAR primary_adapter[CCHDEVICENAME]; char bpp_buffer[16], freq_buffer[18]; - DEVMODEW default_mode; + DEVMODEW default_mode, *full_mode; + ULONG_PTR id; + LONG ret; DWORD i; + /* Use the new interface if it is available */ + if (!handler.name) + goto old_interface; + + if (!get_primary_adapter(primary_adapter)) + return DISP_CHANGE_FAILED; + + if (!devname && !devmode) + { + default_mode.dmSize = sizeof(default_mode); + if (!EnumDisplaySettingsExW(primary_adapter, 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 (!handler.get_id(devname, &id)) + { + ERR("Failed to get %s device id.\n", wine_dbgstr_w(devname)); + return DISP_CHANGE_BADPARAM; + } + + if (is_detached_mode(devmode)) + { + FIXME("Detaching adapters is currently unsupported.\n"); + return DISP_CHANGE_SUCCESSFUL; + } + + if (!(full_mode = get_full_mode(id, devmode))) + { + ERR("Failed to find a valid mode.\n"); + return DISP_CHANGE_BADMODE; + } + + if (flags & CDS_UPDATEREGISTRY && !write_registry_settings(devname, full_mode)) + { + ERR("Failed to write %s display settings to registry.\n", wine_dbgstr_w(devname)); + heap_free(full_mode); + return DISP_CHANGE_NOTUPDATED; + } + + if (lstrcmpiW(primary_adapter, devname)) + { + FIXME("Changing non-primary adapter %s settings is currently unsupported.\n", + wine_dbgstr_w(devname)); + heap_free(full_mode); + return DISP_CHANGE_SUCCESSFUL; + } + + if (flags & (CDS_TEST | CDS_NORESET)) + { + heap_free(full_mode); + return DISP_CHANGE_SUCCESSFUL; + } + + TRACE("handler:%s device:%s position:(%d,%d) resolution:%ux%u frequency:%uHz depth:%ubits " + "orientation:%#x.\n", handler.name, wine_dbgstr_w(devname), full_mode->u1.s2.dmPosition.x, + full_mode->u1.s2.dmPosition.y, full_mode->dmPelsWidth, full_mode->dmPelsHeight, + full_mode->dmDisplayFrequency, full_mode->dmBitsPerPel, full_mode->u1.s2.dmDisplayOrientation); + + ret = handler.set_current_mode(id, full_mode); + if (ret == DISP_CHANGE_SUCCESSFUL) + X11DRV_DisplayDevices_Update(TRUE); + heap_free(full_mode); + return ret; + +old_interface: if (!get_primary_adapter(primary_adapter)) return DISP_CHANGE_FAILED; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index b19e0e3e80a..1c6b666227f 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -697,6 +697,12 @@ struct x11drv_settings_handler * * Return FALSE on failure with parameters unchanged and error code set. Return TRUE on success */ BOOL (*get_current_mode)(ULONG_PTR id, DEVMODEW *mode); + + /* set_current_mode() will be called to change the display mode of the display device of id. + * mode must be a valid mode from get_modes() with optional fields, such as dmPosition set. + * + * Return DISP_CHANGE_*, same as ChangeDisplaySettingsExW() return values */ + LONG (*set_current_mode)(ULONG_PTR id, DEVMODEW *mode); }; extern void X11DRV_Settings_SetHandler(const struct x11drv_settings_handler *handler) DECLSPEC_HIDDEN;