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. --- common/vdcommon.h | 40 +++++++++++++++++++++++++++ vdagent/desktop_layout.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/common/vdcommon.h b/common/vdcommon.h index f40e68e..29437c3 100644 --- a/common/vdcommon.h +++ b/common/vdcommon.h @@ -102,6 +102,46 @@ private: PFND3DKMT_OPENADAPTERFROMHDC _pD3DKMTOpenAdapterFromHdc; }; +// CCD API +#define SDC_APPLY 0x00000080 +#define SDC_USE_SUPPLIED_DISPLAY_CONFIG 0x00000020 +#define SDC_FORCE_MODE_ENUMERATION 0x00001000 +#define QDC_ALL_PATHS 1 +struct DISPLAYCONFIG_PATH_INFO; +struct DISPLAYCONFIG_MODE_INFO; +struct DISPLAYCONFIG_TOPOLOGY_ID; +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); +class CCDLibrary { +public: + ~CCDLibrary(); + static CCDLibrary& singleton() { + static CCDLibrary instance; + return instance; + } + bool found() { + return _hUser32 && _pGetDisplayConfigBufferSizes && + _pQuieryDisplayConfig && _pSetDisplayConfig; + } + LONG get_display_config_buffer_sizes(UINT32* num_paths, UINT32* num_modes); + LONG query_display_config(UINT32* num_paths, DISPLAYCONFIG_PATH_INFO* paths, + UINT32* num_modes, DISPLAYCONFIG_MODE_INFO* modes); + LONG set_display_config(UINT32 num_paths, DISPLAYCONFIG_PATH_INFO* paths, + UINT32 num_modes, DISPLAYCONFIG_MODE_INFO* modes, + UINT32 flags); + +private: + CCDLibrary(); + + HINSTANCE _hUser32; + PFNGETDISPLAYCONFIGBUFFERSIZES _pGetDisplayConfigBufferSizes; + PFNQUERYDISPLAYCONFIG _pQuieryDisplayConfig; + PFNSETDISPLAYCONFIG _pSetDisplayConfig; +}; + #if defined __GNUC__ #define ALIGN_GCC __attribute__ ((packed)) #define ALIGN_VC diff --git a/vdagent/desktop_layout.cpp b/vdagent/desktop_layout.cpp index b5e8cfa..92493f3 100644 --- a/vdagent/desktop_layout.cpp +++ b/vdagent/desktop_layout.cpp @@ -312,6 +312,70 @@ NTSTATUS D3DKMTLibrary::close_adapter(D3DKMT_HANDLE adapter) return _pD3DKMTCloseAdapter(&func_params); } +CCDLibrary::CCDLibrary() + : _pGetDisplayConfigBufferSizes(NULL) + , _pQuieryDisplayConfig(NULL) + , _pSetDisplayConfig(NULL) +{ + _hUser32 = LoadLibrary(L"User32.dll"); + if (_hUser32) { + _pGetDisplayConfigBufferSizes = (PFNGETDISPLAYCONFIGBUFFERSIZES)GetProcAddress(_hUser32, "GetDisplayConfigBufferSizes"); + _pQuieryDisplayConfig = (PFNQUERYDISPLAYCONFIG)GetProcAddress(_hUser32, "QueryDisplayConfig"); + _pSetDisplayConfig = (PFNSETDISPLAYCONFIG)GetProcAddress(_hUser32, "SetDisplayConfig"); + } +} + + +CCDLibrary::~CCDLibrary() +{ + if (_hUser32) + FreeLibrary(_hUser32); +} + +LONG CCDLibrary::get_display_config_buffer_sizes(UINT32* num_paths, UINT32* num_modes) +{ + return _pGetDisplayConfigBufferSizes(QDC_ALL_PATHS, num_paths, num_modes); +} + +LONG CCDLibrary::query_display_config(UINT32* num_paths, DISPLAYCONFIG_PATH_INFO* paths, + UINT32* num_modes, DISPLAYCONFIG_MODE_INFO* modes) +{ + return _pQuieryDisplayConfig(QDC_ALL_PATHS, num_paths, paths, num_modes, modes, NULL); +} + +LONG CCDLibrary::set_display_config(UINT32 num_paths, DISPLAYCONFIG_PATH_INFO* paths, + UINT32 num_modes, DISPLAYCONFIG_MODE_INFO* modes, + UINT32 flags) +{ + return _pSetDisplayConfig(num_paths, paths, num_modes, modes, flags); +} + +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(); + if (!ccd.found()) return; + 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)) { + // Being conservative, struct sizes are under 100 bytes + paths = reinterpret_cast<DISPLAYCONFIG_PATH_INFO *>(new char[num_paths * 100]); + modes = reinterpret_cast<DISPLAYCONFIG_MODE_INFO *>(new char[num_modes * 100]); + status = ccd.query_display_config(&num_paths, paths, &num_modes, modes); + if (NT_SUCCESS(status)) { + ccd.set_display_config(num_paths, paths, num_modes, modes, + SDC_APPLY | + SDC_USE_SUPPLIED_DISPLAY_CONFIG | + SDC_FORCE_MODE_ENUMERATION); + } + delete[] reinterpret_cast<char *>(paths); + delete[] reinterpret_cast<char *>(modes); + } +} + bool DesktopLayout::init_dev_mode(LPCTSTR dev_name, DEVMODE* dev_mode, DisplayMode* mode, LONG normal_x, LONG normal_y, bool set_pos) { @@ -384,7 +448,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); } } -- 2.4.3 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel