When a new custom display mode is added, the current WDDM driver notifies a disconnection and reconnection of the virtual monitor to force Windows to update the display modes. This produces an ugly effect, keeping the screen black for up to some seconds and usually not repainting it afterwards. This patch uses the CCD API to update the display modes, and produces just a quick flash followed by a whole screen repaint. For best results, it should be used with a driver that does not update the display modes by itself, but it is still compatible with the current implementation. --- vdagent/desktop_layout.cpp | 27 +++++++++- vdagent/wddm.cpp | 13 +++++ vdagent/wddm.h | 124 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 1 deletion(-) diff --git a/vdagent/desktop_layout.cpp b/vdagent/desktop_layout.cpp index 64fa649..e3b4cef 100644 --- a/vdagent/desktop_layout.cpp +++ b/vdagent/desktop_layout.cpp @@ -266,6 +266,29 @@ bool DesktopLayout::get_qxl_device_id(WCHAR* device_key, DWORD* device_id) return key_found; } +static void update_display_modes() +{ + // The trick here is to call SetDisplayConfig with the current config and + // the SDC_FORCE_MODE_ENUMERATION flag + CCDLibrary &ccd = CCDLibrary::singleton(); + UINT32 num_paths = 0, num_modes = 0; + DISPLAYCONFIG_PATH_INFO * paths; + DISPLAYCONFIG_MODE_INFO * modes; + LONG status = ccd.get_display_config_buffer_sizes(&num_paths, &num_modes); + if (NT_SUCCESS(status)) { + std::vector<DISPLAYCONFIG_PATH_INFO> paths(num_paths); + std::vector<DISPLAYCONFIG_MODE_INFO> modes(num_modes); + status = ccd.query_display_config(&num_paths, &paths.front(), + &num_modes, &modes.front()); + if (NT_SUCCESS(status)) { + ccd.set_display_config(num_paths, &paths.front(), num_modes, &modes.front(), + SDC_APPLY | + SDC_USE_SUPPLIED_DISPLAY_CONFIG | + SDC_FORCE_MODE_ENUMERATION); + } + } +} + bool DesktopLayout::init_dev_mode(LPCTSTR dev_name, DEVMODE* dev_mode, DisplayMode* mode, LONG normal_x, LONG normal_y, bool set_pos) { @@ -325,7 +348,9 @@ bool DesktopLayout::init_dev_mode(LPCTSTR dev_name, DEVMODE* dev_mode, DisplayMo vd_printf("escape xres = %d, yres = %d, bpp = %d", custom.xres, custom.yres, custom.bpp); NTSTATUS status = d3dkmt.escape_driver_private(adapter, &custom, sizeof(QXLEscapeSetCustomDisplay)); - if (!NT_SUCCESS(status)) { + if (NT_SUCCESS(status)) { + update_display_modes(); + } else { vd_printf("escape failed with error 0x%08X", status); } } diff --git a/vdagent/wddm.cpp b/vdagent/wddm.cpp index ae586c5..6a4b79f 100644 --- a/vdagent/wddm.cpp +++ b/vdagent/wddm.cpp @@ -61,3 +61,16 @@ NTSTATUS D3DKMTLibrary::close_adapter(D3DKMT_HANDLE adapter) func_params.hAdapter = adapter; return _pD3DKMTCloseAdapter(&func_params); } + +CCDLibrary::CCDLibrary() + : _pGetDisplayConfigBufferSizes(NULL) + , _pQueryDisplayConfig(NULL) + , _pSetDisplayConfig(NULL) +{ + HINSTANCE _hUser32 = GetModuleHandle(L"user32.dll"); + if (_hUser32) { + _pGetDisplayConfigBufferSizes = (PFNGETDISPLAYCONFIGBUFFERSIZES)GetProcAddress(_hUser32, "GetDisplayConfigBufferSizes"); + _pQueryDisplayConfig = (PFNQUERYDISPLAYCONFIG)GetProcAddress(_hUser32, "QueryDisplayConfig"); + _pSetDisplayConfig = (PFNSETDISPLAYCONFIG)GetProcAddress(_hUser32, "SetDisplayConfig"); + } +} diff --git a/vdagent/wddm.h b/vdagent/wddm.h index 408c0b6..d3b31be 100644 --- a/vdagent/wddm.h +++ b/vdagent/wddm.h @@ -18,6 +18,7 @@ #ifndef _H_WDDM #define _H_WDDM +#define WINVER 0x0601 #include <windows.h> // WDDM data types and constants @@ -79,4 +80,127 @@ private: PFND3DKMT_OPENADAPTERFROMHDC _pD3DKMTOpenAdapterFromHdc; }; +// CCD API +#ifndef SDC_FORCE_MODE_ENUMERATION +#define SDC_APPLY 0x00000080 +#define SDC_USE_SUPPLIED_DISPLAY_CONFIG 0x00000020 +#define SDC_FORCE_MODE_ENUMERATION 0x00001000 +#define QDC_ALL_PATHS 1 + +struct DISPLAYCONFIG_TOPOLOGY_ID; + +typedef struct DISPLAYCONFIG_PATH_SOURCE_INFO { + LUID adapterId; + UINT32 id; + UINT32 modeInfoIdx; + UINT32 statusFlags; +} DISPLAYCONFIG_PATH_SOURCE_INFO; + +enum DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY {}; +enum DISPLAYCONFIG_ROTATION {}; +enum DISPLAYCONFIG_SCALING {}; +enum DISPLAYCONFIG_SCANLINE_ORDERING {}; + +typedef struct DISPLAYCONFIG_RATIONAL { + UINT32 Numerator; + UINT32 Denominator; +} DISPLAYCONFIG_RATIONAL; + +typedef struct DISPLAYCONFIG_PATH_TARGET_INFO { + LUID adapterId; + UINT32 id; + UINT32 modeInfoIdx; + DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology; + DISPLAYCONFIG_ROTATION rotation; + DISPLAYCONFIG_SCALING scaling; + DISPLAYCONFIG_RATIONAL refreshRate; + DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; + WINBOOL targetAvailable; + UINT32 statusFlags; +} DISPLAYCONFIG_PATH_TARGET_INFO; + +typedef struct DISPLAYCONFIG_PATH_INFO { + DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo; + DISPLAYCONFIG_PATH_TARGET_INFO targetInfo; + UINT32 flags; +} DISPLAYCONFIG_PATH_INFO; + +enum DISPLAYCONFIG_MODE_INFO_TYPE {}; +enum DISPLAYCONFIG_PIXELFORMAT {}; + +typedef struct DISPLAYCONFIG_SOURCE_MODE { + UINT32 width; + UINT32 height; + DISPLAYCONFIG_PIXELFORMAT pixelFormat; + POINTL position; +} DISPLAYCONFIG_SOURCE_MODE; + +typedef struct DISPLAYCONFIG_2DREGION { + UINT32 cx; + UINT32 cy; +} DISPLAYCONFIG_2DREGION; + +typedef struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO { + UINT64 pixelRate; + DISPLAYCONFIG_RATIONAL hSyncFreq; + DISPLAYCONFIG_RATIONAL vSyncFreq; + DISPLAYCONFIG_2DREGION activeSize; + DISPLAYCONFIG_2DREGION totalSize; + UINT32 videoStandard; + DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; +} DISPLAYCONFIG_VIDEO_SIGNAL_INFO; + +typedef struct DISPLAYCONFIG_TARGET_MODE { + DISPLAYCONFIG_VIDEO_SIGNAL_INFO targetVideoSignalInfo; +} DISPLAYCONFIG_TARGET_MODE; + +typedef struct DISPLAYCONFIG_MODE_INFO { + DISPLAYCONFIG_MODE_INFO_TYPE infoType; + UINT32 id; + LUID adapterId; + union { + DISPLAYCONFIG_TARGET_MODE targetMode; + DISPLAYCONFIG_SOURCE_MODE sourceMode; + }; +} DISPLAYCONFIG_MODE_INFO; +#endif + +class CCDLibrary { +public: + static CCDLibrary& singleton() { + static CCDLibrary instance; + return instance; + } + LONG get_display_config_buffer_sizes(UINT32* num_paths, UINT32* num_modes) { + return _pGetDisplayConfigBufferSizes ? + _pGetDisplayConfigBufferSizes(QDC_ALL_PATHS, num_paths, num_modes) : -1; + } + LONG query_display_config(UINT32* num_paths, DISPLAYCONFIG_PATH_INFO* paths, + UINT32* num_modes, DISPLAYCONFIG_MODE_INFO* modes) { + return _pQueryDisplayConfig ? + _pQueryDisplayConfig(QDC_ALL_PATHS, num_paths, paths, num_modes, modes, NULL) : -1; + } + LONG set_display_config(UINT32 num_paths, DISPLAYCONFIG_PATH_INFO* paths, + UINT32 num_modes, DISPLAYCONFIG_MODE_INFO* modes, + UINT32 flags) { + return _pSetDisplayConfig ? + _pSetDisplayConfig(num_paths, paths, num_modes, modes, flags) : -1; + } + +private: + typedef LONG (*PFNGETDISPLAYCONFIGBUFFERSIZES)(UINT32, UINT32*, UINT32*); + typedef LONG (*PFNQUERYDISPLAYCONFIG)(UINT32, UINT32*, DISPLAYCONFIG_PATH_INFO*, UINT32*, + DISPLAYCONFIG_MODE_INFO*, DISPLAYCONFIG_TOPOLOGY_ID*); + typedef LONG (*PFNSETDISPLAYCONFIG)(UINT32, DISPLAYCONFIG_PATH_INFO*, UINT32, + DISPLAYCONFIG_MODE_INFO*, UINT32); + + CCDLibrary(); + CCDLibrary(const CCDLibrary &) {} + CCDLibrary &operator=(const CCDLibrary &) { return *this; } + + PFNGETDISPLAYCONFIGBUFFERSIZES _pGetDisplayConfigBufferSizes; + PFNQUERYDISPLAYCONFIG _pQueryDisplayConfig; + PFNSETDISPLAYCONFIG _pSetDisplayConfig; +}; + #endif -- 2.4.3 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel