> > The Connecting and Configuring Displays (CCD) Win32 APIs > provide more control over the desktop display setup. The CCD APIs > move away from using Windows Graphics Device Interface (GDI) > concepts like view name and toward Windows Display Driver Model > (WDDM) concepts like adapter, source, and target identifiers. > More info on the CCD API can be found here: > https://msdn.microsoft.com/en-us/library/windows/hardware/ff539590(v=vs.85).aspx > > This patch introduces partial functionality from the CCD API > needed by vdagent. It is needed by vdagent in order to support > multiple monitors and arbitrary resolution on Windows 10. > > Tested with both Windows 10 and Windows 7. > > Based on a patch by Sandy Stutsman <sstutsma@xxxxxxxxxx> > > Signed-off-by: Dmitry Fleytman <dfleytma@xxxxxxxxxx> > Signed-off-by: Sameeh Jubran <sameeh@xxxxxxxxxx> > --- > Makefile.am | 2 + > vdagent/desktop_layout.cpp | 150 ++---- > vdagent/desktop_layout.h | 9 +- > vdagent/display_configuration.cpp | 1071 > +++++++++++++++++++++++++++++++++++++ > vdagent/display_configuration.h | 191 +++++++ > 5 files changed, 1301 insertions(+), 122 deletions(-) > create mode 100755 vdagent/display_configuration.cpp > create mode 100755 vdagent/display_configuration.h > > diff --git a/Makefile.am b/Makefile.am > index c3fc6bf..84507e8 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -31,6 +31,8 @@ vdagent_SOURCES = \ > common/vdcommon.h \ > common/vdlog.cpp \ > common/vdlog.h \ > + vdagent/display_configuration.cpp \ > + vdagent/display_configuration.h \ > vdagent/desktop_layout.cpp \ > vdagent/desktop_layout.h \ > vdagent/display_setting.cpp \ > diff --git a/vdagent/desktop_layout.cpp b/vdagent/desktop_layout.cpp > index a7666ca..e59a1a4 100644 > --- a/vdagent/desktop_layout.cpp > +++ b/vdagent/desktop_layout.cpp > @@ -18,6 +18,7 @@ > #include <spice/qxl_windows.h> > #include <spice/qxl_dev.h> > #include "desktop_layout.h" > +#include "display_configuration.h" > #include "vdlog.h" > > #ifdef __MINGW32__ > @@ -35,15 +36,19 @@ void DisplayMode::set_res(DWORD width, DWORD height, > DWORD depth) > DesktopLayout::DesktopLayout() > : _total_width (0) > , _total_height (0) > - , _send_monitors_position(false) > + , _display_config (NULL) > { > MUTEX_INIT(_mutex); > + _display_config = DisplayConfig::create_config(); > get_displays(); > } > > DesktopLayout::~DesktopLayout() > { > clean_displays(); > + if (_display_config) { > + delete _display_config; > + } just delete _display_config, the if is redundant. > } > > void DesktopLayout::get_displays() > @@ -59,6 +64,7 @@ void DesktopLayout::get_displays() > unlock(); > return; > } > + _display_config->update_config_path(); > clean_displays(); > ZeroMemory(&dev_info, sizeof(dev_info)); > dev_info.cb = sizeof(dev_info); > @@ -82,12 +88,13 @@ void DesktopLayout::get_displays() > _displays[i] = NULL; > } > } > - attached = !!(dev_info.StateFlags & > DISPLAY_DEVICE_ATTACHED_TO_DESKTOP); > + attached = _display_config->is_attached(&dev_info); > + > EnumDisplaySettings(dev_info.DeviceName, ENUM_CURRENT_SETTINGS, > &mode); > _displays[display_id] = new DisplayMode(mode.dmPosition.x, > mode.dmPosition.y, > mode.dmPelsWidth, > mode.dmPelsHeight, > mode.dmBitsPerPel, > attached); > - update_monitor_config(dev_info.DeviceName, _displays[display_id]); > + _display_config->update_monitor_config(dev_info.DeviceName, > _displays[display_id], &mode); > } > normalize_displays_pos(); > unlock(); > @@ -121,6 +128,7 @@ void DesktopLayout::set_displays() > unlock(); > return; > } > + _display_config->update_config_path(); > ZeroMemory(&dev_info, sizeof(dev_info)); > dev_info.cb = sizeof(dev_info); > ZeroMemory(&dev_mode, sizeof(dev_mode)); > @@ -146,28 +154,32 @@ void DesktopLayout::set_displays() > break; > } > DisplayMode * mode(_displays.at(display_id)); > - if (!init_dev_mode(dev_info.DeviceName, &dev_mode, mode, normal_x, > normal_y, true)) { > + if (!init_dev_mode(dev_info.DeviceName, &dev_mode, mode)) { > vd_printf("No suitable mode found for display %S", > dev_info.DeviceName); > break; > } > vd_printf("Set display mode %lux%lu", dev_mode.dmPelsWidth, > dev_mode.dmPelsHeight); > - LONG ret = ChangeDisplaySettingsEx(dev_info.DeviceName, &dev_mode, > NULL, > - CDS_UPDATEREGISTRY | CDS_NORESET, > NULL); > - if (ret == DISP_CHANGE_SUCCESSFUL) { > + if (_display_config->update_dev_mode_position(dev_info.DeviceName, > &dev_mode, > + mode->_pos_x - > normal_x, > + mode->_pos_y - > normal_y)) { > dev_sets++; > - update_monitor_config(dev_info.DeviceName, mode); > + _display_config->update_monitor_config(dev_info.DeviceName, > mode, &dev_mode); > } > if (!is_qxl) { > display_id++; > } > } > if (dev_sets) { > - ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL); > + _display_config->update_display_settings(); > normalize_displays_pos(); > } > unlock(); > } > > +void DesktopLayout::set_position_configurable(bool flag) { > + _display_config->set_monitors_config(flag); > +} > + > // Normalize all display positions to non-negative coordinates and update > total width and height of > // the virtual desktop. Caller is responsible to lock() & unlock(). > void DesktopLayout::normalize_displays_pos() > @@ -265,125 +277,29 @@ bool DesktopLayout::get_qxl_device_id(WCHAR* > device_key, DWORD* device_id) > return key_found; > } > > -bool DesktopLayout::init_dev_mode(LPCTSTR dev_name, DEVMODE* dev_mode, > DisplayMode* mode, > - LONG normal_x, LONG normal_y, bool > set_pos) > +bool DesktopLayout::init_dev_mode(LPCTSTR dev_name, DEVMODE* dev_mode, > DisplayMode* mode) > { > - DWORD closest_diff = -1; > - DWORD best = -1; > - QXLEscapeSetCustomDisplay custom; > - HDC hdc = NULL; > - LONG ret; > - > ZeroMemory(dev_mode, sizeof(DEVMODE)); > dev_mode->dmSize = sizeof(DEVMODE); > - if (!mode || !mode->_attached) { > - //Detach monitor > - dev_mode->dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_POSITION; > - return true; > - } > - > - hdc = CreateDC(dev_name, NULL, NULL, NULL); > - if (!hdc) { > - // for some reason, windows want those 3 flags to enable monitor > - dev_mode->dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_POSITION; > - dev_mode->dmPelsWidth = mode->_width; > - dev_mode->dmPelsHeight = mode->_height; > - ret = ChangeDisplaySettingsEx(dev_name, dev_mode, NULL, > CDS_UPDATEREGISTRY, NULL); > - if (ret == DISP_CHANGE_BADMODE) { > - // custom resolution might not be set yet, use known resolution > - // FIXME: this causes client temporary resize... a > - // solution would involve passing custom resolution before > - // driver initialization, perhaps through registry > - dev_mode->dmPelsWidth = 640; > - dev_mode->dmPelsHeight = 480; > - ret = ChangeDisplaySettingsEx(dev_name, dev_mode, NULL, > CDS_UPDATEREGISTRY, NULL); > - } > - > - vd_printf("attach %ld", ret); > - hdc = CreateDC(dev_name, NULL, NULL, NULL); > - } > > - if (!hdc) { > - vd_printf("failed to create DC"); > - return false; > - } else { > - // Update custom resolution > - custom.xres = mode->_width; > - custom.yres = mode->_height; > - custom.bpp = mode->_depth; > - > - int err = ExtEscape(hdc, QXL_ESCAPE_SET_CUSTOM_DISPLAY, > - sizeof(QXLEscapeSetCustomDisplay), > (LPCSTR)&custom, 0, NULL); > - if (err <= 0) { > - vd_printf("can't set custom display, perhaps an old driver"); > - } > - DeleteDC(hdc); > + //Update monitor state > + MONITOR_STATE monitor_state = (!mode || !mode->_attached)? > MONITOR_DETACHED : MONITOR_ATTACHED; > + _display_config->set_monitor_state(dev_name, dev_mode, monitor_state); > + if (monitor_state == MONITOR_DETACHED) { > + return true; > } > > - // force refresh mode table > - DEVMODE tempDevMode; > - ZeroMemory(&tempDevMode, sizeof (tempDevMode)); > - tempDevMode.dmSize = sizeof(DEVMODE); > - EnumDisplaySettings(dev_name, 0xffffff, &tempDevMode); > - > - //Find the closest size which will fit within the monitor > - for (DWORD i = 0; EnumDisplaySettings(dev_name, i, dev_mode); i++) { > - if (dev_mode->dmPelsWidth > mode->_width || > - dev_mode->dmPelsHeight > mode->_height || > - dev_mode->dmBitsPerPel != mode->_depth) { > - continue; > - } > - DWORD wdiff = mode->_width - dev_mode->dmPelsWidth; > - DWORD hdiff = mode->_height - dev_mode->dmPelsHeight; > - DWORD diff = wdiff * wdiff + hdiff * hdiff; > - if (diff < closest_diff) { > - closest_diff = diff; > - best = i; > - } > - } > - if (best == (DWORD)-1 || !EnumDisplaySettings(dev_name, best, dev_mode)) > { > + // Update custom resolution > + dev_mode->dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_POSITION; > + dev_mode->dmPelsWidth = mode->_width; > + dev_mode->dmPelsHeight = mode->_height; > + dev_mode->dmBitsPerPel = mode->_depth; > + if (!_display_config->custom_display_escape(dev_name, dev_mode)) > return false; > - } > - dev_mode->dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; > - if (set_pos) { > - //Convert the position so that the primary is always at (0,0) > - dev_mode->dmPosition.x = mode->_pos_x - normal_x; > - dev_mode->dmPosition.y = mode->_pos_y - normal_y; > - dev_mode->dmFields |= DM_POSITION; > - } > > // update current DisplayMode (so mouse scaling works properly) > mode->_width = dev_mode->dmPelsWidth; > mode->_height = dev_mode->dmPelsHeight; > - > return true; > -} > - > -bool DesktopLayout::update_monitor_config(LPCTSTR dev_name, DisplayMode* > mode) > -{ > - QXLHead monitor_config; > - > - if (!mode || !mode->get_attached()) > - return false; > - > - //Don't configure monitors unless the client supports it > - if(!_send_monitors_position) return FALSE; > - > - HDC hdc = CreateDC(dev_name, NULL, NULL, NULL); > - > - memset(&monitor_config, 0, sizeof(monitor_config)); > - monitor_config.x = mode->_pos_x; > - monitor_config.y = mode->_pos_y; > - monitor_config.width = mode->_width; > - monitor_config.height = mode->_height; > - > - int err = ExtEscape(hdc, QXL_ESCAPE_MONITOR_CONFIG, > - sizeof(QXLHead), (LPCSTR) &monitor_config, 0, NULL); > - > - if (err < 0){ > - vd_printf("can't update monitor config, may have an older driver"); > - } > > - DeleteDC(hdc); > - return (err >= 0); > } > diff --git a/vdagent/desktop_layout.h b/vdagent/desktop_layout.h > index ece3946..fd6af76 100644 > --- a/vdagent/desktop_layout.h > +++ b/vdagent/desktop_layout.h > @@ -60,6 +60,7 @@ private: > }; > > typedef std::vector<DisplayMode*> Displays; > +class DisplayConfig; > > class DesktopLayout { > public: > @@ -73,23 +74,21 @@ public: > size_t get_display_count() { return _displays.size();} > DWORD get_total_width() { return _total_width;} > DWORD get_total_height() { return _total_height;} > - void set_position_configurable(bool flag) { _send_monitors_position = > flag; } > + void set_position_configurable(bool flag); > private: > void clean_displays(); > void normalize_displays_pos(); > DisplayMode * get_primary_display(); > - bool update_monitor_config(LPCTSTR dev_name, DisplayMode* mode); > + bool init_dev_mode(LPCTSTR dev_name, DEVMODE* dev_mode, DisplayMode* > mode); > static bool consistent_displays(); > static bool is_attached(LPCTSTR dev_name); > static bool get_qxl_device_id(WCHAR* device_key, DWORD* device_id); > - static bool init_dev_mode(LPCTSTR dev_name, DEVMODE* dev_mode, > DisplayMode* mode, > - LONG normal_x, LONG normal_y, bool set_pos); > private: > mutex_t _mutex; > Displays _displays; > DWORD _total_width; > DWORD _total_height; > - bool _send_monitors_position; > + DisplayConfig* _display_config; > }; > > #endif > diff --git a/vdagent/display_configuration.cpp > b/vdagent/display_configuration.cpp > new file mode 100755 > index 0000000..563f619 > --- /dev/null > +++ b/vdagent/display_configuration.cpp > @@ -0,0 +1,1071 @@ > +/* > +Copyright (C) 2015 Red Hat, Inc. > + > +This program is free software; you can redistribute it and/or > +modify it under the terms of the GNU General Public License as > +published by the Free Software Foundation; either version 2 of > +the License, or (at your option) any later version. > + > +This program is distributed in the hope that it will be useful, > +but WITHOUT ANY WARRANTY; without even the implied warranty of > +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +GNU General Public License for more details. > + > +You should have received a copy of the GNU General Public License > +along with this program. If not, see <http://www.gnu.org/licenses/>. > +*/ > + > +#include "display_configuration.h" > +#include <winternl.h> > + > +// > +// Definitions used by QueryDisplayConfig. > +// > + > +#define QDC_ALL_PATHS 0x00000001 > + > +// > +// Definitions used by SetDisplayConfig. > +// > + > +#define SDC_USE_SUPPLIED_DISPLAY_CONFIG 0x00000020 > +#define SDC_APPLY 0x00000080 > +#define SDC_FORCE_MODE_ENUMERATION 0x00001000 > + > +#define DISPLAYCONFIG_PATH_ACTIVE 0x00000001 > +#define DISPLAYCONFIG_PATH_MODE_IDX_INVALID 0xffffffff > + > +enum DISPLAYCONFIG_DEVICE_INFO_TYPE { > + DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1 > +}; > + > +enum DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY {}; > + > +enum DISPLAYCONFIG_ROTATION {}; > + > +enum DISPLAYCONFIG_SCANLINE_ORDERING {}; > + > +enum DISPLAYCONFIG_SCALING {}; > + > +enum DISPLAYCONFIG_PIXELFORMAT {}; > + > +enum DISPLAYCONFIG_MODE_INFO_TYPE {}; > + > +struct DISPLAYCONFIG_DEVICE_INFO_HEADER { > + DISPLAYCONFIG_DEVICE_INFO_TYPE type; > + UINT32 size; > + LUID adapterId; > + UINT32 id; > +}; > + > +struct DISPLAYCONFIG_SOURCE_DEVICE_NAME { > + DISPLAYCONFIG_DEVICE_INFO_HEADER header; > + WCHAR > viewGdiDeviceName[CCHDEVICENAME]; > +}; > + > +struct DISPLAYCONFIG_DESKTOP_IMAGE_INFO { > + POINTL PathSourceSize; > + RECTL DesktopImageRegion; > + RECTL DesktopImageClip; > +}; > + > +struct DISPLAYCONFIG_RATIONAL { > + UINT32 Numerator; > + UINT32 Denominator; > +}; > + > +struct DISPLAYCONFIG_2DREGION { > + UINT32 cx; > + UINT32 cy; > +}; > + > +struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO { > + UINT64 pixelRate; > + DISPLAYCONFIG_RATIONAL hSyncFreq; > + DISPLAYCONFIG_RATIONAL vSyncFreq; > + DISPLAYCONFIG_2DREGION activeSize; > + DISPLAYCONFIG_2DREGION totalSize; > + > + union { > + struct { > + UINT32 videoStandard : 16; > + UINT32 vSyncFreqDivider : 6; > + UINT32 reserved : 10; > + } AdditionalSignalInfo; > + UINT32 videoStandard; > + } DUMMYUNIONNAME; > + DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; > +}; > + > +struct DISPLAYCONFIG_TARGET_MODE { > + DISPLAYCONFIG_VIDEO_SIGNAL_INFO targetVideoSignalInfo; > +}; > + > +struct DISPLAYCONFIG_SOURCE_MODE { > + UINT32 width; > + UINT32 height; > + DISPLAYCONFIG_PIXELFORMAT pixelFormat; > + POINTL position; > +}; > + > +struct DISPLAYCONFIG_MODE_INFO { > + DISPLAYCONFIG_MODE_INFO_TYPE infoType; > + UINT32 id; > + LUID adapterId; > + union { > + DISPLAYCONFIG_TARGET_MODE targetMode; > + DISPLAYCONFIG_SOURCE_MODE sourceMode; > + DISPLAYCONFIG_DESKTOP_IMAGE_INFO desktopImageInfo; > + } DUMMYUNIONNAME; > +}; > + > +struct DISPLAYCONFIG_PATH_SOURCE_INFO { > + LUID adapterId; > + UINT32 id; > + union { > + UINT32 modeInfoIdx; > + struct { > + UINT32 cloneGroupId : 16; > + UINT32 sourceModeInfoIdx : 16; > + } DUMMYSTRUCTNAME; > + } DUMMYUNIONNAME; > + > + UINT32 statusFlags; > +}; > + > +struct DISPLAYCONFIG_PATH_TARGET_INFO { > + LUID adapterId; > + UINT32 id; > + union { > + UINT32 modeInfoIdx; > + struct { > + UINT32 desktopModeInfoIdx : 16; > + UINT32 targetModeInfoIdx : 16; > + } DUMMYSTRUCTNAME; > + } DUMMYUNIONNAME; > + DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology; > + DISPLAYCONFIG_ROTATION rotation; > + DISPLAYCONFIG_SCALING scaling; > + DISPLAYCONFIG_RATIONAL refreshRate; > + DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; > + BOOL targetAvailable; > + UINT32 statusFlags; > +}; > + > +struct DISPLAYCONFIG_PATH_INFO { > + DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo; > + DISPLAYCONFIG_PATH_TARGET_INFO targetInfo; > + UINT32 flags; > +}; > + > + > +enum D3DKMT_ESCAPETYPE { > + D3DKMT_ESCAPE_DRIVERPRIVATE = 0 > +}; > + > +struct D3DDDI_ESCAPEFLAGS { > + union { > + struct { > + UINT Reserved : 31; > + }; > + UINT Value; > + }; > +}; > + > +struct D3DKMT_ESCAPE { > + D3DKMT_HANDLE hAdapter; > + D3DKMT_HANDLE hDevice; > + D3DKMT_ESCAPETYPE Type; > + D3DDDI_ESCAPEFLAGS Flags; > + VOID* pPrivateDriverData; > + UINT PrivateDriverDataSize; > + D3DKMT_HANDLE hContext; > +}; > + > +typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID; > + > +struct D3DKMT_OPENADAPTERFROMHDC { > + HDC hDc; > + D3DKMT_HANDLE hAdapter; > + LUID AdapterLuid; > + D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId; > +}; > + > +struct D3DKMT_CLOSEADAPTER { > + D3DKMT_HANDLE hAdapter; > +}; > + > +struct D3DKMT_OPENADAPTERFROMDEVICENAME { > + PCWSTR pDeviceName; > + D3DKMT_HANDLE hAdapter; > + LUID AdapterLuid; > +}; > + > +struct D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME { > + WCHAR DeviceName[32]; > + D3DKMT_HANDLE hAdapter; > + LUID AdapterLuid; > + D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId; > +}; > + There are defined from Microsoft, better to move them in a separate file. The license can changed easier. > +struct QXLMonitorEscape { > + QXLMonitorEscape(DEVMODE* dev_mode) > + { > + ZeroMemory(&_head, sizeof(_head)); > + _head.x = dev_mode->dmPosition.x; > + _head.y = dev_mode->dmPosition.y; > + _head.width = dev_mode->dmPelsWidth; > + _head.height = dev_mode->dmPelsHeight; > + } > + QXLHead _head; > +}; > + > +struct QxlCustomEscapeObj : public QXLEscapeSetCustomDisplay { > + QxlCustomEscapeObj(uint32_t bitsPerPel, uint32_t width, uint32_t height) > + { > + xres = width; > + yres = height; > + bpp = bitsPerPel; > + } > + QxlCustomEscapeObj() {}; > +}; > + > +struct WDDMCustomDisplayEscape { > + WDDMCustomDisplayEscape(DEVMODE* dev_mode) > + { > + _ioctl = QXL_ESCAPE_SET_CUSTOM_DISPLAY; > + _custom.bpp = dev_mode->dmBitsPerPel; > + _custom.xres = dev_mode->dmPelsWidth; > + _custom.yres = dev_mode->dmPelsHeight; > + } > + int _ioctl; > + QXLEscapeSetCustomDisplay _custom; > +}; > + > +struct WDDMMonitorConfigEscape { > + WDDMMonitorConfigEscape(DisplayMode* mode) > + { > + _ioctl = QXL_ESCAPE_MONITOR_CONFIG; > + _head.id = _head.surface_id = 0; > + _head.x = mode->get_pos_x(); > + _head.y = mode->get_pos_y(); > + _head.width = mode->get_width(); > + _head.height = mode->get_height(); > + } > + int _ioctl; > + QXLHead _head; > +}; > + > +DisplayConfig* DisplayConfig::create_config() > +{ > + DisplayConfig* new_interface; > + //Try to open a WDDM adapter. > + new_interface = new WDDMInterface(); > + if(new_interface->type() == WDDM) { > + return new_interface; > + } > + //If that failed so clean up and assume we have an XPDM driver > + delete new_interface; > + > + new_interface = new XPDMInterface(); > + return new_interface; > +} > + > +DisplayConfig::DisplayConfig() > + : _driver_type(INVALID_DRIVER) > + , _send_monitors_config(false) > +{} > + > +bool XPDMInterface::is_attached(DISPLAY_DEVICE* dev_info) > +{ > + return !!(dev_info->StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP); > +} > + > +bool XPDMInterface::set_monitor_state(LPCTSTR device_name, DEVMODE* > dev_mode, MONITOR_STATE state) > +{ > + dev_mode->dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_POSITION; > + if (state == MONITOR_ATTACHED) { > + return true; > + } > + > + LONG status = ChangeDisplaySettingsEx(device_name, dev_mode, NULL, > CDS_UPDATEREGISTRY, NULL); > + return (status == DISP_CHANGE_SUCCESSFUL); > +} > + > +LONG XPDMInterface::update_display_settings() > +{ > + return ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL); > +} > + > +bool XPDMInterface::update_dev_mode_position(LPCTSTR device_name, > + DEVMODE* dev_mode, LONG x, LONG > y) > +{ > + //Convert the position so that the primary is always at (0,0) > + dev_mode->dmPosition.x = x; > + dev_mode->dmPosition.y = y; > + dev_mode->dmFields |= DM_POSITION; > + vd_printf("%s: setting %S at (%lu, %lu)", __FUNCTION__, device_name, > dev_mode->dmPosition.x, > + dev_mode->dmPosition.y); > + > + LONG status = ChangeDisplaySettingsEx(device_name, dev_mode, NULL, > + CDS_UPDATEREGISTRY | CDS_NORESET, > NULL); > + return (status == DISP_CHANGE_SUCCESSFUL); > +} > + > +bool XPDMInterface::custom_display_escape(LPCTSTR device_name, DEVMODE* > dev_mode) > +{ > + LONG ret; > + NTSTATUS Status (ERROR_SUCCESS); > + HDC hdc = CreateDC(device_name, NULL, NULL, NULL); > + > + if (!hdc) { > + ret = ChangeDisplaySettingsEx(device_name, dev_mode, NULL, > CDS_UPDATEREGISTRY, NULL); > + if (ret == DISP_CHANGE_BADMODE) { > + // custom resolution might not be set yet, use known resolution > + // FIXME: this causes client temporary resize... a > + // solution would involve passing custom resolution before > + // driver initialization, perhaps through registry > + dev_mode->dmPelsWidth = 640; > + dev_mode->dmPelsHeight = 480; > + ret = ChangeDisplaySettingsEx(device_name, dev_mode, NULL, > CDS_UPDATEREGISTRY, NULL); > + } > + > + vd_printf("attach %ld", ret); > + if (!(hdc = CreateDC(device_name, NULL, NULL, NULL))) { > + vd_printf("%s: failed to create DC", __FUNCTION__); > + return false; > + } > + } > + > + QxlCustomEscapeObj custom_escape(dev_mode->dmBitsPerPel, > + dev_mode->dmPelsWidth, > dev_mode->dmPelsHeight); > + > + int err = ExtEscape(hdc, QXL_ESCAPE_SET_CUSTOM_DISPLAY, > + sizeof(QXLEscapeSetCustomDisplay), (LPCSTR) &custom_escape, 0, > NULL); > + if (err <= 0) { > + vd_printf("%s: Can't set custom display, perhaps running with an > older driver?", > + __FUNCTION__); > + } > + > + if (!find_best_mode(device_name, dev_mode)) { > + Status = E_FAIL; > + } > + > + DeleteDC(hdc); > + return NT_SUCCESS(Status); > +} > + > +bool XPDMInterface::update_monitor_config(LPCTSTR device_name, DisplayMode* > mode, > + DEVMODE* dev_mode) > +{ > + if (!mode || !mode->get_attached()) { > + return false; > + } > + > + QXLMonitorEscape monitor_config(dev_mode); > + HDC hdc(CreateDC(device_name, NULL, NULL, NULL)); > + int err(0); > + > + if (!hdc || !_send_monitors_config) { > + return false; > + } > + > + err = ExtEscape(hdc, QXL_ESCAPE_MONITOR_CONFIG, sizeof(QXLHead), > + (LPCSTR) &monitor_config, 0, NULL); > + if (err < 0) { > + vd_printf("%s: %S can't update monitor config, may have old, old > driver", > + __FUNCTION__, device_name); > + } > + DeleteDC(hdc); > + return (err >= 0); > +} > + > +bool XPDMInterface::find_best_mode(LPCTSTR Device, DEVMODE* dev_mode) > +{ > + DWORD closest_diff = -1; > + DWORD best = -1; > + > + // force refresh mode table > + DEVMODE test_dev_mode; > + ZeroMemory(&test_dev_mode, sizeof(test_dev_mode)); > + test_dev_mode.dmSize = sizeof(DEVMODE); > + EnumDisplaySettings(Device, 0xffffff, &test_dev_mode); > + > + //Find the closest size which will fit within the monitor > + for (DWORD i = 0; EnumDisplaySettings(Device, i, &test_dev_mode); i++) { > + if (dev_mode->dmPelsWidth > test_dev_mode.dmPelsWidth || > + dev_mode->dmPelsHeight > test_dev_mode.dmPelsHeight || > + dev_mode->dmBitsPerPel != test_dev_mode.dmBitsPerPel) { > + continue; > + } > + DWORD wdiff = dev_mode->dmPelsWidth - test_dev_mode.dmPelsWidth; > + DWORD hdiff = dev_mode->dmPelsHeight - test_dev_mode.dmPelsHeight; > + DWORD diff = wdiff * wdiff + hdiff * hdiff; > + if (diff < closest_diff) { > + closest_diff = diff; > + best = i; > + } > + } > + vd_printf("%s: closest_diff at %lu best %lu", __FUNCTION__, > closest_diff, best); > + if (best == (DWORD) -1 || !EnumDisplaySettings(Device, best, dev_mode)) > { > + return false; > + } > + > + //Change to the best fit > + LONG status = ChangeDisplaySettingsEx(Device, dev_mode, NULL, > + CDS_UPDATEREGISTRY | CDS_NORESET, > NULL); > + return NT_SUCCESS(status); > +} > + > +WDDMInterface::WDDMInterface() > + : _pfnOpen_adapter_hdc(NULL) > + , _pfnClose_adapter(NULL) > + , _pfnEscape(NULL) > + , _pfnOpen_adapter_device_name(NULL) > + , _pfnOpen_adapter_gdi_name(NULL) > +{ > + _driver_type = INVALID_DRIVER; > + > + //Did the CCD load? > + if (_ccd.error() == ERROR_NOT_SUPPORTED) { > + return; > + } > + > + //Can we find the D3D calls we need? > + if (!init_d3d_api()) { > + return; > + } > + > + //Initialize CCD path stuff > + if (!_ccd.query_display_config()) { > + return;; > + } > + if (!_ccd.set_display_config()) { > + return; > + } > + _driver_type = WDDM; > +} > + > +WDDMInterface::~WDDMInterface() > +{ > + if (_driver_type != WDDM) { > + vd_printf("%s called with invalid driver type of %d", __FUNCTION__, > _driver_type); > + return; > + } > +} > + > +bool WDDMInterface::is_attached(DISPLAY_DEVICE* dev_info) > +{ > + return _ccd.is_attached(dev_info->DeviceName); > +} > + > +bool WDDMInterface::set_monitor_state(LPCTSTR device_name, DEVMODE* > dev_mode, MONITOR_STATE state) > +{ > + return _ccd.set_path_state(device_name, state); > +} > + > +bool WDDMInterface::custom_display_escape(LPCTSTR device_name, DEVMODE* > dev_mode) > +{ > + DISPLAYCONFIG_MODE_INFO* mode = _ccd.get_active_mode(device_name, > false); > + if (!mode) { > + return false; > + } > + > + //Don't bother if we are already set to the new resolution > + if (mode->sourceMode.width == dev_mode->dmPelsWidth && > + mode->sourceMode.height == dev_mode->dmPelsHeight) { > + return true; > + } > + > + vd_printf("%s: updating %S resolution\n", __FUNCTION__, device_name); > + > + WDDMCustomDisplayEscape wddm_escape(dev_mode); > + if (escape(device_name, &wddm_escape, sizeof(wddm_escape))) { > + return _ccd.update_mode_size(device_name, dev_mode); > + } > + > + vd_printf("%s: (%dx%d)", __FUNCTION__, mode->sourceMode.width, > mode->sourceMode.height); > + return false; > +} > + > +bool WDDMInterface::update_monitor_config(LPCTSTR device_name, DisplayMode* > display_mode, > + DEVMODE* dev_mode) > +{ > + if (!display_mode || !display_mode->get_attached()) { > + return false; > + } > + DISPLAYCONFIG_MODE_INFO* mode = _ccd.get_active_mode(device_name, > false); > + if (!mode || !_send_monitors_config) > + return false; > + > + WDDMMonitorConfigEscape wddm_escape(display_mode); > + if (escape(device_name, &wddm_escape, sizeof(wddm_escape))) { > + //Update the path position > + return _ccd.update_mode_position(device_name, dev_mode); > + } > + > + vd_printf("%s: %S failed", __FUNCTION__, device_name); > + return false; > + > +} > + > +LONG WDDMInterface::update_display_settings() > +{ > + //If we removed the primary monitor since the last call, we need to > + //reorder the other monitors, making the leftmost one the primary > + _ccd.verify_primary_position(); > + > + if (!_ccd.set_display_config()) { > + if (_ccd._error != ERROR_INVALID_PARAMETER) { > + vd_printf("%s: set_display_config failed", __FUNCTION__); > + } else { > + vd_printf("%s: Invalid parameter!", __FUNCTION__); > + _ccd.debug_print_config("After error"); > + } > + } > + return _ccd.error(); > +} > + > +void WDDMInterface::update_config_path() > +{ > + _ccd.query_display_config(); > +} > + > +bool WDDMInterface::update_dev_mode_position(LPCTSTR device_name, DEVMODE* > dev_mode, > + LONG x, LONG y) > +{ > + //Convert the position so that the primary is always at (0,0) > + dev_mode->dmPosition.x = x; > + dev_mode->dmPosition.y = y; > + _ccd.update_mode_position(device_name, dev_mode); > + return _ccd.update_mode_position(device_name, dev_mode); > +} > + > +bool WDDMInterface::init_d3d_api() > +{ > + HMODULE hModule = LoadLibrary(L"gdi32.dll"); > + > + //Look for the gdi32 functions we need to perform driver escapes > + if (!hModule) { > + vd_printf("%s something wildly wrong as we can't open gdi32.dll", > __FUNCTION__); > + _driver_type = INVALID_DRIVER; > + return false; > + } > + > + do { > + _pfnClose_adapter = (PFND3DKMT_CLOSEADAPTER) > + GetProcAddress(hModule, "D3DKMTCloseAdapter"); > + if (!_pfnClose_adapter) { > + break; > + } > + > + _pfnEscape = (PFND3DKMT_ESCAPE) GetProcAddress(hModule, > "D3DKMTEscape"); > + if (!_pfnEscape) { > + break; > + } > + > + _pfnOpen_adapter_hdc = (PFND3DKMT_OPENADAPTERFROMHDC) > + GetProcAddress(hModule, "D3DKMTOpenAdapterFromHdc"); > + if (!_pfnOpen_adapter_hdc) { > + break; > + } > + > + _pfnOpen_adapter_device_name = (PFND3DKMT_OPENADAPTERFROMDEVICENAME) > + GetProcAddress(hModule, "D3DKMTOpenAdapterFromDeviceName"); > + if (!_pfnOpen_adapter_device_name) { > + break; > + } > + > + _pfnOpen_adapter_gdi_name = > (PFND3DKMT_OPENADAPTERFROMGDIDISPLAYNAME) > + GetProcAddress(hModule, "D3DKMTOpenAdapterFromGdiDisplayName"); > + if (!_pfnOpen_adapter_gdi_name) { > + break; > + } > + > + } > + while(0); > + > + FreeLibrary(hModule); > + > + //Did we get them ? > + if (!_pfnClose_adapter || !_pfnOpen_adapter_hdc || !_pfnEscape) { > + return false; > + } > + _driver_type = WDDM; > + return true; > +} > + > +D3DKMT_HANDLE WDDMInterface::adapter_handle(LPCTSTR device_name) > +{ > + D3DKMT_HANDLE hAdapter(0); > + > + //For some reason, unknown to me, this call will occasionally fail. > + if ((hAdapter = handle_from_DC(device_name))) { > + return hAdapter; > + } > + //So try other available methods. > + if (_pfnOpen_adapter_device_name && (hAdapter = > handle_from_device_name(device_name))) { > + return hAdapter; > + } > + //One last chance to open this guy > + if (_pfnOpen_adapter_gdi_name) { > + hAdapter = handle_from_GDI_name(device_name); > + } > + > + if (!hAdapter) { > + vd_printf("%s: failed to open adapter %S", __FUNCTION__, > device_name); > + } > + > + return hAdapter; > +} > + > +D3DKMT_HANDLE WDDMInterface::handle_from_DC(LPCTSTR adapter_name) > +{ > + NTSTATUS status; > + D3DKMT_OPENADAPTERFROMHDC open_data; > + HDC hDc(CreateDC(adapter_name, NULL, NULL, NULL)); > + > + if (!hDc) { > + vd_printf("%s: %S CreateDC failed with %lu", __FUNCTION__, > adapter_name, GetLastError()); > + return 0; > + } > + > + ZeroMemory(&open_data, sizeof(D3DKMT_OPENADAPTERFROMHDC)); > + open_data.hDc = hDc; > + > + if (!NT_SUCCESS(status = _pfnOpen_adapter_hdc(&open_data))) { > + vd_printf("%s: %S open adapter from hdc failed with %lu", > __FUNCTION__, adapter_name, > + status); > + open_data.hAdapter = 0; > + } > + > + DeleteDC(hDc); > + return open_data.hAdapter; > +} > + > +D3DKMT_HANDLE WDDMInterface::handle_from_device_name(LPCTSTR adapter_name) > +{ > + D3DKMT_OPENADAPTERFROMDEVICENAME display_name_data; > + NTSTATUS status; > + > + ZeroMemory(&display_name_data, sizeof(display_name_data)); > + display_name_data.pDeviceName = adapter_name; > + > + if (NT_SUCCESS(status = > _pfnOpen_adapter_device_name(&display_name_data))) { > + return display_name_data.hAdapter; > + } > + > + vd_printf("%s %S failed with 0x%lx", __FUNCTION__, adapter_name, > status); > + return 0; > +} > + > +D3DKMT_HANDLE WDDMInterface::handle_from_GDI_name(LPCTSTR adapter_name) > +{ > + D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME gdi_display_name; > + NTSTATUS status; > + > + ZeroMemory(&gdi_display_name, sizeof(gdi_display_name)); > + memcpy((void *) gdi_display_name.DeviceName, adapter_name, > sizeof(TCHAR)* CCHDEVICENAME); > + > + if (NT_SUCCESS(status = _pfnOpen_adapter_gdi_name(&gdi_display_name))) { > + return gdi_display_name.hAdapter; > + } > + > + vd_printf("%s: %S aurrrgghh nothing works..error is 0x%lx", > __FUNCTION__, adapter_name, > + status); > + return 0; > +} > + > +void WDDMInterface::close_adapter(D3DKMT_HANDLE handle) > +{ > + D3DKMT_CLOSEADAPTER closeData; > + if (handle) { > + closeData.hAdapter = handle; > + _pfnClose_adapter(&closeData); > + } > +} > + > +bool WDDMInterface::escape(LPCTSTR device_name, void* data, UINT size_data) > +{ > + D3DKMT_ESCAPE escapeData; > + NTSTATUS status; > + D3DKMT_HANDLE hAdapter(0); > + > + if (!(hAdapter = adapter_handle(device_name))) > + return false; > + > + escapeData.hAdapter = hAdapter; > + escapeData.hDevice = 0; > + escapeData.hContext = 0; > + escapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE; > + escapeData.Flags.Value = 0; > + escapeData.pPrivateDriverData = data; > + escapeData.PrivateDriverDataSize = size_data; > + > + status = _pfnEscape(&escapeData); > + > + if (!NT_SUCCESS(status)) { > + vd_printf("%s: this should never happen. Status is 0x%lx", > __FUNCTION__, status); > + _driver_type = INVALID_DRIVER; > + } > + > + //Close the handle to this device > + close_adapter(hAdapter); > + return NT_SUCCESS(status); > +} > + > +CCD::CCD() > + :_NumPathElements(0) > + ,_NumModeElements(0) > + ,_pPathInfo(NULL) > + ,_pModeInfo(NULL) > + ,_pfnGetDeviceInfo(NULL) > + ,_pfnGetDisplayConfigBufferSizes(NULL) > + ,_pfnQueryDisplayConfig(NULL) > + ,_pfnSetDisplayConfig(NULL) > + ,_error(0) > + ,_primary_detached(false) > + ,_path_state(PATH_UPDATED) > +{ > + if (load_api()) { > + get_config_buffers(); > + } > +} > + > +CCD::~CCD() > +{ > + free_config_buffers(); > +} > + > +bool CCD::query_display_config() > +{ > + LONG query_error(ERROR_SUCCESS); > + > + //Until we get it or error != ERROR_INSUFFICIENT_BUFFER > + do { > + query_error = _pfnQueryDisplayConfig(QDC_ALL_PATHS, > &_NumPathElements, _pPathInfo, > + &_NumModeElements, _pModeInfo, NULL); > + > + // if ERROR_INSUFFICIENT_BUFFER need to retry QueryDisplayConfig > + // to get a new set of config buffers > + //(see > https://msdn.microsoft.com/en-us/library/windows/hardware/ff569215(v=vs.85).aspx > ) > + if (query_error) { > + if (query_error == ERROR_INSUFFICIENT_BUFFER) { > + free_config_buffers(); > + if (!get_config_buffers()) > + return false; > + } else { > + _error = query_error; > + vd_printf("%s failed QueryDisplayConfig with 0x%lx", > __FUNCTION__, _error); > + return false; > + } > + } > + } while(query_error); > + _path_state = PATH_CURRENT; > + return true; > +} > + > +DISPLAYCONFIG_MODE_INFO* CCD::get_active_mode(LPCTSTR device_name, bool > return_on_error) > +{ > + DISPLAYCONFIG_PATH_INFO* active_path; > + > + active_path = get_device_path(device_name, true); > + > + if (!active_path ) { > + vd_printf("%s:%S failed", __FUNCTION__, device_name); > + return NULL; > + } > + return &_pModeInfo[active_path->sourceInfo.modeInfoIdx]; > +} > + > +bool CCD::set_display_config() { > + > + debug_print_config("Before SetDisplayConfig"); > + > + if (_path_state == PATH_CURRENT) { > + vd_printf("%s: path states says nothing changed", __FUNCTION__); > + return true; > + } > + > + if (!(_error = _pfnSetDisplayConfig(_NumPathElements, _pPathInfo, > + _NumModeElements, _pModeInfo, > + SDC_APPLY | SDC_USE_SUPPLIED_DISPLAY_CONFIG | > SDC_FORCE_MODE_ENUMERATION))) { > + return true; > + } > + > + vd_printf("%s failed SetDisplayConfig with 0x%lx", __FUNCTION__, > _error); > + debug_print_config("After failed SetDisplayConfig"); > + return false; > +} > + > +DISPLAYCONFIG_PATH_INFO* CCD::get_device_path(LPCTSTR device_name, bool > bActive) > +{ > + //Search for device's active path > + for (UINT32 i = 0; i < _NumPathElements; i++) { > + DISPLAYCONFIG_PATH_INFO* path_info = &_pPathInfo[i]; > + > + //if bActive, return only paths that are currently active > + if (bActive && !is_active_path(path_info)) > + continue; > + if (!is_device_path(device_name, path_info)) > + continue; > + return path_info; > + } > + _error = ERROR_GEN_FAILURE; > + return NULL; > +} > + > +void CCD::debug_print_config(const char* prefix) > +{ > + TCHAR dev_name[CCHDEVICENAME]; > + for (UINT32 i = 0; i < _NumPathElements; i++) { > + DISPLAYCONFIG_PATH_INFO* path_info = &_pPathInfo[i]; > + if (!(path_info->flags & DISPLAYCONFIG_PATH_ACTIVE)) > + continue; > + get_device_name_config(path_info, dev_name); > + > + if (path_info->sourceInfo.modeInfoIdx == > DISPLAYCONFIG_PATH_MODE_IDX_INVALID) { > + vd_printf("%s: %S [%s] This path is active but has invalid mode > set.", __FUNCTION__, > + dev_name, prefix); > + continue; > + } > + DISPLAYCONFIG_MODE_INFO* mode = > &_pModeInfo[path_info->sourceInfo.modeInfoIdx]; > + vd_printf("%s: %S [%s] (%ld,%ld) (%ux%u).", __FUNCTION__, dev_name, > prefix, > + mode->sourceMode.position.x, mode->sourceMode.position.y, > + mode->sourceMode.width, mode->sourceMode.height); > + } > +} > + > +bool CCD::load_api() > +{ > + HMODULE hModule = LoadLibrary(L"user32.dll"); > + if(!hModule) { > + return false; > + } > + > + bool bFound_all(false); > + do { > + if (!(_pfnGetDeviceInfo = (PDISPLAYCONFIG_GETDEVICEINFO) > + GetProcAddress(hModule, "DisplayConfigGetDeviceInfo"))) { > + break; > + } > + > + if (!(_pfnGetDisplayConfigBufferSizes = > (PGETDISPLAYCONFIG_BUFFERSIZES) > + GetProcAddress(hModule, "GetDisplayConfigBufferSizes"))) { > + break; > + } > + > + if (!(_pfnQueryDisplayConfig = (PQUERYDISPLAYCONFIG) > + GetProcAddress(hModule, "QueryDisplayConfig"))) { > + break; > + } > + > + if (!(_pfnSetDisplayConfig = (PSETDISPLAYCONFIG) > + GetProcAddress(hModule, "SetDisplayConfig"))) { > + break; > + } > + bFound_all = true; > + } > + while(0); > + > + FreeLibrary(hModule); > + return bFound_all; > +} > + > +bool CCD::get_config_buffers() > +{ > + //Get Config Buffer Sizes > + free_config_buffers(); > + _error = _pfnGetDisplayConfigBufferSizes(QDC_ALL_PATHS, > &_NumPathElements, > + &_NumModeElements); > + if (_error) { > + vd_printf("%s: GetDisplayConfigBufferSizes failed with 0x%lx", > __FUNCTION__, _error); > + return false; > + } > + > + //Allocate arrays > + _pPathInfo = ((DISPLAYCONFIG_PATH_INFO*) > malloc(sizeof(DISPLAYCONFIG_PATH_INFO) * _NumPathElements)); > + _pModeInfo = ((DISPLAYCONFIG_MODE_INFO*) > malloc(sizeof(DISPLAYCONFIG_MODE_INFO) * _NumModeElements)); > + > + if (!_pPathInfo || !_pModeInfo) { > + vd_printf("%s OOM ", __FUNCTION__); > + return false; > + } > + > + ///clear the above arrays > + ZeroMemory(_pPathInfo, sizeof(DISPLAYCONFIG_PATH_INFO) * > _NumPathElements); > + ZeroMemory(_pModeInfo, sizeof(DISPLAYCONFIG_MODE_INFO) * > _NumModeElements); > + return true; > +} > + > +void CCD::free_config_buffers() > +{ > + if (_pModeInfo) { > + free(_pModeInfo); > + _pModeInfo = NULL; > + } > + if (_pPathInfo) { > + free(_pPathInfo); > + _pPathInfo = NULL; > + } > + _NumModeElements = _NumPathElements = 0; > +} > + > +bool CCD::get_device_name_config(DISPLAYCONFIG_PATH_INFO* path, LPCTSTR > dev_name) > +{ > + DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name; > + source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; > + source_name.header.size = sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME); > + source_name.header.adapterId = path->sourceInfo.adapterId; > + source_name.header.id = path->sourceInfo.id; > + > + _error = _pfnGetDeviceInfo(&source_name.header); > + if (_error) { > + vd_printf("%s DisplayConfigGetDeviceInfo failed with %lu", > __FUNCTION__, _error); > + return false; > + } > + memcpy((void *)dev_name, source_name.viewGdiDeviceName, CCHDEVICENAME* > sizeof(TCHAR)); > + return true; > +} > + > +bool CCD::is_device_path(LPCTSTR device_name, DISPLAYCONFIG_PATH_INFO* path) > +{ > + //Does this path belong to device_name? > + TCHAR dev_name[CCHDEVICENAME]; > + if (!get_device_name_config(path, dev_name)) { > + return false; > + } > + if (_tcscmp(dev_name, device_name)) { > + return false; > + } > + return true; > +} > + > +// If we have detached the primary monitor, then we need to reset the > positions of the remaining > +// monitors to insure that at least one is positioned at (0,0) > +bool CCD::verify_primary_position() > +{ > + LONG leftmost_x(0); > + if (!_primary_detached) { > + return true; > + } > + _primary_detached = false; > + > + for (UINT32 i = 0; i < _NumPathElements; i++) { > + DISPLAYCONFIG_PATH_INFO* path_info = &_pPathInfo[i]; > + if (!(path_info->flags & DISPLAYCONFIG_PATH_ACTIVE)) > + continue; > + > + DISPLAYCONFIG_MODE_INFO* mode = > &_pModeInfo[path_info->sourceInfo.modeInfoIdx]; > + if (leftmost_x > mode->sourceMode.position.x) { > + leftmost_x = mode->sourceMode.position.x; > + } > + } > + > + if (leftmost_x == 0) { > + return false; > + } > + > + for (UINT32 i = 0; i < _NumPathElements; i++) { > + DISPLAYCONFIG_PATH_INFO* path_info = &_pPathInfo[i]; > + if ((!(path_info->flags & DISPLAYCONFIG_PATH_ACTIVE))) > + continue; > + DISPLAYCONFIG_MODE_INFO* mode = > &_pModeInfo[path_info->sourceInfo.modeInfoIdx]; > + vd_printf("%s: setting mode x to %lu", __FUNCTION__, > mode->sourceMode.position.x); > + mode->sourceMode.position.x -= leftmost_x; > + } > + _path_state = PATH_UPDATED; > + return true; > +} > + > +bool CCD::update_mode_position(LPCTSTR device_name, DEVMODE* dev_mode) > +{ > + DISPLAYCONFIG_MODE_INFO* mode = get_active_mode(device_name, false); > + if (!mode) > + return false; > + > + mode->sourceMode.position.x = dev_mode->dmPosition.x; > + mode->sourceMode.position.y = dev_mode->dmPosition.y; > + vd_printf("%s: %S updated path mode to (%lu, %lu) - (%u x%u)", > __FUNCTION__, > + device_name, > + mode->sourceMode.position.x, mode->sourceMode.position.y, > + mode->sourceMode.width, mode->sourceMode.height); > + _path_state = PATH_UPDATED; > + return true; > + > +} > + > +bool CCD::update_mode_size(LPCTSTR device_name, DEVMODE* dev_mode) > +{ > + DISPLAYCONFIG_MODE_INFO* mode = get_active_mode(device_name, false); > + if (!mode) { > + return false; > + } > + > + mode->sourceMode.width = dev_mode->dmPelsWidth; > + mode->sourceMode.height = dev_mode->dmPelsHeight; > + vd_printf("%s: %S updated path mode to (%lu, %lu - (%u x %u)", > __FUNCTION__, > + device_name, > + mode->sourceMode.position.x, mode->sourceMode.position.y, > + mode->sourceMode.width, mode->sourceMode.height); > + _path_state = PATH_UPDATED; > + return true; > +} > + > +void CCD::update_detached_primary_state(LPCTSTR device_name, > DISPLAYCONFIG_PATH_INFO * path_info) > +{ > + DISPLAYCONFIG_MODE_INFO* mode(get_active_mode(device_name, false)); > + > + //will need to reset monitor positions if primary detached > + path_info->flags = path_info->flags & ~DISPLAYCONFIG_PATH_ACTIVE; > + if (!mode|| mode->sourceMode.position.x != 0 || > mode->sourceMode.position.y != 0) { > + return ; > + } > + _primary_detached = true; > +} > + > +bool CCD::set_path_state(LPCTSTR device_name, MONITOR_STATE new_state) > +{ > + DISPLAYCONFIG_PATH_INFO* path(get_device_path(device_name, false)); > + MONITOR_STATE current_path_state(MONITOR_DETACHED); > + > + if (path && (path->flags & DISPLAYCONFIG_PATH_ACTIVE)) { > + current_path_state = MONITOR_ATTACHED; > + } > + > + //If state didn't change, nothing to do > + if (current_path_state == new_state ) { > + return true; > + } > + > + if (!path) { > + return false; > + } > + > + _path_state = PATH_UPDATED; > + if (new_state == MONITOR_DETACHED) { > + update_detached_primary_state(device_name, path); > + } else { > + path->flags = path->flags | DISPLAYCONFIG_PATH_ACTIVE; > + set_display_config(); > + } > + return true; > +} > + > +bool CCD::is_attached(LPCTSTR device_name) > +{ > + DISPLAYCONFIG_PATH_INFO* path(get_device_path(device_name, false)); > + if (!path) { > + return false; > + } > + return (path->flags & DISPLAYCONFIG_PATH_ACTIVE); > +} > + > +bool CCD::is_active_path(DISPLAYCONFIG_PATH_INFO * path) > +{ > + if (!path || !(path->flags & DISPLAYCONFIG_PATH_ACTIVE) || > + (path->sourceInfo.modeInfoIdx == > DISPLAYCONFIG_PATH_MODE_IDX_INVALID)) { > + return false; > + } > + return true; > +} > diff --git a/vdagent/display_configuration.h > b/vdagent/display_configuration.h > new file mode 100755 > index 0000000..870a28d > --- /dev/null > +++ b/vdagent/display_configuration.h > @@ -0,0 +1,191 @@ > +/* > +Copyright (C) 2015 Red Hat, Inc. > + > +This program is free software; you can redistribute it and/or > +modify it under the terms of the GNU General Public License as > +published by the Free Software Foundation; either version 2 of > +the License, or (at your option) any later version. > + > +This program is distributed in the hope that it will be useful, > +but WITHOUT ANY WARRANTY; without even the implied warranty of > +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +GNU General Public License for more details. > + > +You should have received a copy of the GNU General Public License > +along with this program. If not, see <http://www.gnu.org/licenses/>. > +*/ > +#ifndef _H_DISPLAY_CONFIGURATION > +#define _H_DISPLAY_CONFIGURATION > + > +#include <spice/qxl_windows.h> > +#include <spice/qxl_dev.h> > +#include "desktop_layout.h" > +#include "vdlog.h" > + > +enum DRIVER_TYPE { > + XPDM, > + WDDM, > + INVALID_DRIVER, > +}; > + > +enum MONITOR_STATE { > + MONITOR_DETACHED, > + MONITOR_ATTACHED, > +}; > + > +enum PATH_STATE { > + PATH_UPDATED, > + PATH_CURRENT, > +}; > + > +enum DISPLAYCONFIG_TOPOLOGY_ID {}; > + > +struct DISPLAYCONFIG_DEVICE_INFO_HEADER; > +struct DISPLAYCONFIG_MODE_INFO; > +struct DISPLAYCONFIG_PATH_INFO; > + > +//Makes calls into the CCD API for getting/setting display settings on WDDM > drivers > +//Use is exclusive to wddm display config class > + > +typedef LONG(APIENTRY* > PDISPLAYCONFIG_GETDEVICEINFO)(DISPLAYCONFIG_DEVICE_INFO_HEADER*); > +typedef LONG(APIENTRY* PGETDISPLAYCONFIG_BUFFERSIZES)(UINT32, UINT32*, > UINT32*); > +typedef LONG(APIENTRY* PQUERYDISPLAYCONFIG)(UINT32, UINT32*, > DISPLAYCONFIG_PATH_INFO*, UINT32*, > + DISPLAYCONFIG_MODE_INFO*, > DISPLAYCONFIG_TOPOLOGY_ID*); > +typedef LONG(APIENTRY* PSETDISPLAYCONFIG)(UINT32, DISPLAYCONFIG_PATH_INFO*, > UINT32, > + DISPLAYCONFIG_MODE_INFO*, UINT32); > + > +class CCD { > +protected: This should be public, protected is used for inheritance. > + CCD(); > + ~CCD(); > + > + bool query_display_config(); > + bool set_display_config(); > + bool update_mode_position(LPCTSTR device_name, DEVMODE* dev_mode); > + bool update_mode_size(LPCTSTR DeviceNmae, DEVMODE* dev_mode); > + void update_detached_primary_state(LPCTSTR device_name, > DISPLAYCONFIG_PATH_INFO * path_info); > + bool set_path_state(LPCTSTR device_name, MONITOR_STATE state); > + bool is_attached(LPCTSTR device_name); > + bool is_active_path(DISPLAYCONFIG_PATH_INFO * path); > + LONG error() { return _error; } > + > +private: > + bool load_api(); > + bool get_config_buffers(); > + void free_config_buffers(); > + bool verify_primary_position(); > + bool is_device_path(LPCTSTR device_name, DISPLAYCONFIG_PATH_INFO* path); > + DISPLAYCONFIG_MODE_INFO* get_active_mode(LPCTSTR device_name, bool > return_on_error); > + DISPLAYCONFIG_PATH_INFO* get_device_path(LPCTSTR device_name, bool > bActive); > + bool get_device_name_config(DISPLAYCONFIG_PATH_INFO* path, LPCTSTR > dev_name); > + void debug_print_config(const char* prefix = NULL); > + > +private: > + //CCD API stuff > + UINT32 _NumPathElements; > + UINT32 _NumModeElements; > + DISPLAYCONFIG_PATH_INFO* _pPathInfo; > + DISPLAYCONFIG_MODE_INFO* _pModeInfo; > + > + //CCD API function pointers > + PDISPLAYCONFIG_GETDEVICEINFO _pfnGetDeviceInfo; > + PGETDISPLAYCONFIG_BUFFERSIZES _pfnGetDisplayConfigBufferSizes; > + PQUERYDISPLAYCONFIG _pfnQueryDisplayConfig; > + PSETDISPLAYCONFIG _pfnSetDisplayConfig; > + > + LONG _error; > + bool _primary_detached; > + PATH_STATE _path_state; > + friend class WDDMInterface; There should be no reason for friend. > +}; > + > +class DisplayMode; > + > +//Class provides interface to get/set display configurations > +class DisplayConfig { > +public: > + static DisplayConfig* create_config(); > + DisplayConfig();; typo (;;). > + virtual ~DisplayConfig() {}; > + > + virtual bool is_attached(DISPLAY_DEVICE* dev_info) = 0; > + virtual bool custom_display_escape(LPCTSTR device, DEVMODE* mode) = 0; > + virtual bool update_monitor_config(LPCTSTR device, DisplayMode* mode, > DEVMODE* dev_mode) = 0; > + virtual bool set_monitor_state(LPCTSTR device_name, DEVMODE* dev_mode, > MONITOR_STATE state) = 0; > + virtual LONG update_display_settings() = 0; > + virtual bool update_dev_mode_position(LPCTSTR dev_name, DEVMODE* > dev_mode, LONG x, LONG y) = 0; > + DRIVER_TYPE type() { return _driver_type; }; const and actually quite a bad design, I would remove this member entirely. > + void set_monitors_config(bool flag) { _send_monitors_config = flag; } > + virtual void update_config_path() {}; > + > +protected: > + DRIVER_TYPE _driver_type; also I would remove this field. Would be much better to have a WDDMInterface::init that returns true/false. Actually WDDMInterface is quite a bad name for an implementation. > + bool _send_monitors_config; > +}; > + > +//DisplayConfig implementation for guest with XPDM graphics drivers > +class XPDMInterface : public DisplayConfig { > +public: > + XPDMInterface() :DisplayConfig() { _driver_type = XPDM; }; > + ~XPDMInterface() {} > + bool is_attached(DISPLAY_DEVICE* dev_info);; typo (;;). > + bool custom_display_escape(LPCTSTR device_name, DEVMODE* dev_mode); > + bool update_monitor_config(LPCTSTR device_name, DisplayMode* mode, > DEVMODE* dev_mode); > + bool set_monitor_state(LPCTSTR device_name, DEVMODE* dev_mode, > MONITOR_STATE state); > + LONG update_display_settings(); > + bool update_dev_mode_position(LPCTSTR device_name, DEVMODE * dev_mode, > LONG x, LONG y); > + > +private: > + bool find_best_mode(LPCTSTR Device, DEVMODE* dev_mode); > +}; > + > +//DisplayConfig implementation for guest with WDDM graphics drivers > +typedef UINT D3DKMT_HANDLE; > + > +struct D3DKMT_ESCAPE; > +struct D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME; > +struct D3DKMT_OPENADAPTERFROMDEVICENAME; > +struct D3DKMT_CLOSEADAPTER; > +struct D3DKMT_OPENADAPTERFROMHDC; > + > +typedef NTSTATUS(APIENTRY* PFND3DKMT_ESCAPE)(CONST D3DKMT_ESCAPE*); > +typedef NTSTATUS(APIENTRY* > PFND3DKMT_OPENADAPTERFROMGDIDISPLAYNAME)(D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME*); > +typedef NTSTATUS(APIENTRY* > PFND3DKMT_OPENADAPTERFROMDEVICENAME)(D3DKMT_OPENADAPTERFROMDEVICENAME*); > +typedef NTSTATUS(APIENTRY* PFND3DKMT_CLOSEADAPTER)(D3DKMT_CLOSEADAPTER*); > +typedef NTSTATUS(APIENTRY* > PFND3DKMT_OPENADAPTERFROMHDC)(D3DKMT_OPENADAPTERFROMHDC*); > + > +class WDDMInterface : public DisplayConfig { > +public: > + WDDMInterface(); > + ~WDDMInterface(); > + bool is_attached(DISPLAY_DEVICE* dev_info); > + bool set_monitor_state(LPCTSTR device_name, DEVMODE* dev_mode, > MONITOR_STATE state); > + LONG update_display_settings(); > + bool custom_display_escape(LPCTSTR device_name, DEVMODE* dev_mode); > + bool update_monitor_config(LPCTSTR device_name, DisplayMode* mode, > DEVMODE* dev_mode); > + bool update_dev_mode_position(LPCTSTR device_name, DEVMODE * dev_mode, > LONG x, LONG y); > + void update_config_path(); > + > +private: > + bool init_d3d_api(); > + D3DKMT_HANDLE adapter_handle(LPCTSTR device_name); > + D3DKMT_HANDLE handle_from_DC(LPCTSTR adapter_name); > + D3DKMT_HANDLE handle_from_device_name(LPCTSTR adapter_name); > + D3DKMT_HANDLE handle_from_GDI_name(LPCTSTR adapter_name); > + > + void close_adapter(D3DKMT_HANDLE handle); > + bool escape(LPCTSTR device_name, void* data, UINT sizeData); > + > +private: > + //GDI Function pointers > + PFND3DKMT_OPENADAPTERFROMHDC _pfnOpen_adapter_hdc; > + PFND3DKMT_CLOSEADAPTER _pfnClose_adapter; > + PFND3DKMT_ESCAPE _pfnEscape; > + PFND3DKMT_OPENADAPTERFROMDEVICENAME _pfnOpen_adapter_device_name; > + PFND3DKMT_OPENADAPTERFROMGDIDISPLAYNAME _pfnOpen_adapter_gdi_name; > + > + //object handles the CCD API > + CCD _ccd; > +}; > + > +#endif > \ No newline at end of file > -- > 2.5.5 > Frediano _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel