The Direct3D 9 API operates on either the Windows XP display driver model (XPDM) or the Windows Vista display driver model (WDDM), depending on the operating system installed. This patch implements the WDDM interface while using the CCD API to do so. Moreover it introduces multiple monitors support and arbitrary resolution for Windows 10 while preserving backward compatiblity with previous versions of Windows. Based on a patch by Sandy Stutsman <sstutsma@xxxxxxxxxx> Signed-off-by: Dmitry Fleytman <dfleytma@xxxxxxxxxx> Signed-off-by: Sameeh Jubran <sameeh@xxxxxxxxxx> --- vdagent/desktop_layout.cpp | 148 +++-------- vdagent/desktop_layout.h | 9 +- vdagent/display_configuration.cpp | 538 ++++++++++++++++++++++++++++++++++++++ vdagent/display_configuration.h | 83 ++++++ 4 files changed, 656 insertions(+), 122 deletions(-) diff --git a/vdagent/desktop_layout.cpp b/vdagent/desktop_layout.cpp index a7666ca..9c3e873 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,17 @@ 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(); + delete _display_config; } void DesktopLayout::get_displays() @@ -59,6 +62,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 +86,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 +126,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 +152,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 +275,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); + //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; } - if (!hdc) { - vd_printf("failed to create DC"); + // 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; - } 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); - } - - // 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)) { - 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 index d042c14..2737518 100755 --- a/vdagent/display_configuration.cpp +++ b/vdagent/display_configuration.cpp @@ -152,6 +152,544 @@ struct DISPLAYCONFIG_PATH_INFO { UINT32 flags; }; + +enum D3DKMT_ESCAPETYPE { + D3DKMT_ESCAPE_DRIVERPRIVATE = 0 +}; + +struct D3DDDI_ESCAPEFLAGS { + union { + struct { + UINT Reserved : 31; + }; + UINT Value; + }; +}; + +struct D3DKMT_ESCAPE { + D3D_HANDLE hAdapter; + D3D_HANDLE hDevice; + D3DKMT_ESCAPETYPE Type; + D3DDDI_ESCAPEFLAGS Flags; + VOID* pPrivateDriverData; + UINT PrivateDriverDataSize; + D3D_HANDLE hContext; +}; + +struct D3DKMT_OPENADAPTERFROMHDC { + HDC hDc; + D3D_HANDLE hAdapter; + LUID AdapterLuid; + UINT VidPnSourceId; +}; + +struct D3DKMT_CLOSEADAPTER { + D3D_HANDLE hAdapter; +}; + +struct D3DKMT_OPENADAPTERFROMDEVICENAME { + const WCHAR *pDeviceName; + D3D_HANDLE hAdapter; + LUID AdapterLuid; +}; + +struct D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME { + WCHAR DeviceName[32]; + D3D_HANDLE hAdapter; + LUID AdapterLuid; + UINT VidPnSourceId; +}; + +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. + If that failed, assume we have an XPDM driver */ + try { + new_interface = new WDDMInterface(); + } catch (std::exception& e) { + new_interface = new XPDMInterface(); + } + return new_interface; +} + +DisplayConfig::DisplayConfig() + : _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) +{ + //Did the CCD load? + if (_ccd.error() == ERROR_NOT_SUPPORTED) { + throw std::exception(); + } + + //Can we find the D3D calls we need? + if (!init_d3d_api()) { + throw std::exception(); + } + + //Initialize CCD path stuff + if (!_ccd.query_display_config()) { + throw std::exception(); + } + + if (!_ccd.set_display_config()) { + throw std::exception(); + } +} + +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__); + 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; + } + return true; +} + +D3D_HANDLE WDDMInterface::adapter_handle(LPCTSTR device_name) +{ + D3D_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; +} + +D3D_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; +} + +D3D_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; +} + +D3D_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(D3D_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; + D3D_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); + } + + //Close the handle to this device + close_adapter(hAdapter); + return NT_SUCCESS(status); +} + CCD::CCD() :_NumPathElements(0) ,_NumModeElements(0) diff --git a/vdagent/display_configuration.h b/vdagent/display_configuration.h index 4a1a7d4..b3d0198 100755 --- a/vdagent/display_configuration.h +++ b/vdagent/display_configuration.h @@ -91,4 +91,87 @@ private: PATH_STATE _path_state; }; +class DisplayMode; + +//Class provides interface to get/set display configurations +class DisplayConfig { +public: + static DisplayConfig* create_config(); + DisplayConfig(); + 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; + void set_monitors_config(bool flag) { _send_monitors_config = flag; } + virtual void update_config_path() {}; + +protected: + bool _send_monitors_config; +}; + +//DisplayConfig implementation for guest with XPDM graphics drivers +class XPDMInterface : public DisplayConfig { +public: + XPDMInterface() :DisplayConfig() {}; + bool is_attached(DISPLAY_DEVICE* dev_info); + 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 D3D_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(); + 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(); + D3D_HANDLE adapter_handle(LPCTSTR device_name); + D3D_HANDLE handle_from_DC(LPCTSTR adapter_name); + D3D_HANDLE handle_from_device_name(LPCTSTR adapter_name); + D3D_HANDLE handle_from_GDI_name(LPCTSTR adapter_name); + + void close_adapter(D3D_HANDLE handle); + bool escape(LPCTSTR device_name, void* data, UINT sizeData); + + //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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel