[Spice-commits] 2 commits - Makefile.am vdagent/desktop_layout.cpp vdagent/desktop_layout.h vdagent/display_configuration.cpp vdagent/display_configuration.h
Frediano Ziglio
fziglio at kemper.freedesktop.org
Tue Aug 16 10:32:35 UTC 2016
Makefile.am | 2
vdagent/desktop_layout.cpp | 152 +-------
vdagent/desktop_layout.h | 9
vdagent/display_configuration.cpp | 684 ++++++++++++++++++++++++++++++++++++++
vdagent/display_configuration.h | 128 +++++++
5 files changed, 849 insertions(+), 126 deletions(-)
New commits:
commit d81d8cdbde1eca557d65dfe88b2f923e1cca32ed
Author: Sameeh Jubran <sameeh at daynix.com>
Date: Tue Aug 16 12:02:25 2016 +0300
Encapsulating XPDM implementation
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 encapsulates the current XPDM interface implementation into
XPDMInterface class which inherits DisplayConfig class. This patch
makes it easier to introduce WDDM interface to Vdagent in future
patches.
Based on a patch by Sandy Stutsman <sstutsma at redhat.com>
Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
Signed-off-by: Sameeh Jubran <sameeh at daynix.com>
Acked-by: Frediano Ziglio <fziglio at redhat.com>
diff --git a/vdagent/desktop_layout.cpp b/vdagent/desktop_layout.cpp
index c0632cc..65f06c1 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,128 +275,28 @@ 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;
- dev_mode->dmPelsWidth = 0;
- dev_mode->dmPelsHeight = 0;
- ret = ChangeDisplaySettingsEx(dev_name, dev_mode, NULL, CDS_UPDATEREGISTRY, NULL);
- return (ret == DISP_CHANGE_SUCCESSFUL);
- }
-
- 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
index 8e4ad16..01fdbb0 100644
--- a/vdagent/display_configuration.cpp
+++ b/vdagent/display_configuration.cpp
@@ -153,6 +153,181 @@ struct DISPLAYCONFIG_PATH_INFO {
UINT32 flags;
};
+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() {};
+};
+
+DisplayConfig* DisplayConfig::create_config()
+{
+ DisplayConfig* new_interface;
+ 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)
+{
+ if (state == MONITOR_DETACHED) {
+ dev_mode->dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_POSITION;
+ dev_mode->dmPelsWidth = 0;
+ dev_mode->dmPelsHeight = 0;
+
+ LONG status = ChangeDisplaySettingsEx(device_name, dev_mode, NULL, CDS_UPDATEREGISTRY, NULL);
+ return (status == DISP_CHANGE_SUCCESSFUL);
+ }
+ return true;
+}
+
+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)
+{
+ 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);
+}
+
CCD::CCD()
:_numPathElements(0)
,_numModeElements(0)
diff --git a/vdagent/display_configuration.h b/vdagent/display_configuration.h
index 64ce74a..388ac9d 100644
--- a/vdagent/display_configuration.h
+++ b/vdagent/display_configuration.h
@@ -89,4 +89,40 @@ 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);
+};
+
#endif
\ No newline at end of file
commit 588a7134df69de9ef9418c2bd8fae2ba96119f16
Author: Sameeh Jubran <sameeh at daynix.com>
Date: Tue Aug 16 12:02:24 2016 +0300
Introducing Windows CCD API
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.
Based on a patch by Sandy Stutsman <sstutsma at redhat.com>
Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
Signed-off-by: Sameeh Jubran <sameeh at daynix.com>
Acked-by: Frediano Ziglio <fziglio at redhat.com>
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/display_configuration.cpp b/vdagent/display_configuration.cpp
new file mode 100644
index 0000000..8e4ad16
--- /dev/null
+++ b/vdagent/display_configuration.cpp
@@ -0,0 +1,509 @@
+/*
+Copyright (C) 2015-2016 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>
+
+/* The following definitions and structures are taken
+from the wine project repository and can be found
+under: "wine/include/wingdi.h" */
+
+#define QDC_ALL_PATHS 0x00000001
+
+#define SDC_USE_SUPPLIED_DISPLAY_CONFIG 0x00000020
+#define SDC_APPLY 0x00000080
+#define SDC_SAVE_TO_DATABASE 0x00000200
+#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;
+};
+
+CCD::CCD()
+ :_numPathElements(0)
+ ,_numModeElements(0)
+ ,_pPathInfo(NULL)
+ ,_pModeInfo(NULL)
+ ,_pfnGetDeviceInfo(NULL)
+ ,_pfnGetDisplayConfigBufferSizes(NULL)
+ ,_pfnQueryDisplayConfig(NULL)
+ ,_pfnSetDisplayConfig(NULL)
+ ,_primary_detached(false)
+ ,_path_state(PATH_UPDATED)
+{
+ if (load_api()) {
+ get_config_buffers();
+ }
+ else {
+ throw std::exception();
+ }
+}
+
+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 {
+ vd_printf("%s failed QueryDisplayConfig with 0x%lx", __FUNCTION__, query_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(LONG & error) {
+
+ 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 | SDC_SAVE_TO_DATABASE))) {
+ 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;
+ }
+ 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 = GetModuleHandle(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);
+
+ return bFound_all;
+}
+
+bool CCD::get_config_buffers()
+{
+ //Get Config Buffer Sizes
+ free_config_buffers();
+ LONG error(ERROR_SUCCESS);
+ error = _pfnGetDisplayConfigBufferSizes(QDC_ALL_PATHS, &_numPathElements,
+ &_numModeElements);
+ if (error) {
+ vd_printf("%s: GetDisplayConfigBufferSizes failed with 0x%lx", __FUNCTION__, error);
+ return false;
+ }
+
+ //Allocate arrays
+ _pPathInfo = new(std::nothrow) DISPLAYCONFIG_PATH_INFO[_numPathElements];
+ _pModeInfo = new(std::nothrow) DISPLAYCONFIG_MODE_INFO[_numModeElements];
+
+ if (!_pPathInfo || !_pModeInfo) {
+ vd_printf("%s OOM ", __FUNCTION__);
+ free_config_buffers();
+ 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()
+{
+ delete[] _pModeInfo;
+ _pModeInfo = NULL;
+ delete[] _pPathInfo;
+ _pPathInfo = NULL;
+ _numModeElements = _numPathElements = 0;
+}
+
+bool CCD::get_device_name_config(DISPLAYCONFIG_PATH_INFO* path, LPTSTR dev_name)
+{
+ LONG error(ERROR_SUCCESS);
+
+ 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)
+// Windows specify that there must be such a monitor which is considered the primary one
+void CCD::verify_primary_position()
+{
+ LONG leftmost_x(LONG_MAX);
+ LONG leftmost_y(LONG_MAX);
+ if (!_primary_detached) {
+ return;
+ }
+ _primary_detached = false;
+
+ for (UINT32 i = 0; i < _numPathElements; i++) {
+ DISPLAYCONFIG_PATH_INFO* path_info = &_pPathInfo[i];
+ if (!is_active_path(path_info))
+ continue;
+
+ const POINTL& position(_pModeInfo[path_info->sourceInfo.modeInfoIdx].sourceMode.position);
+ // we already have a primary monitor so we have nothing to do
+ if (position.x == 0 && position.y == 0)
+ return;
+ if (leftmost_x > position.x) {
+ leftmost_x = position.x;
+ leftmost_y = position.y;
+ }
+ // in case there are more monitors on the left most, choose the top one
+ if (leftmost_x == position.x && leftmost_y > position.y)
+ leftmost_y = position.y;
+ }
+
+ // update all active monitors adjusting the choosen monitor to (0,0)
+ for (UINT32 i = 0; i < _numPathElements; i++) {
+ DISPLAYCONFIG_PATH_INFO* path_info = &_pPathInfo[i];
+ if (!is_active_path(path_info))
+ continue;
+ POINTL& position(_pModeInfo[path_info->sourceInfo.modeInfoIdx].sourceMode.position);
+ vd_printf("%s: setting mode x to %lu", __FUNCTION__, position.x);
+ position.x -= leftmost_x;
+ position.y -= leftmost_y;
+ }
+ _path_state = PATH_UPDATED;
+}
+
+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);
+ LONG error(0);
+
+ if (is_active_path(path)) {
+ 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(error);
+ }
+ return true;
+}
+
+bool CCD::is_attached(LPCTSTR device_name)
+{
+ return is_active_path(get_device_path(device_name, false));
+}
+
+bool CCD::is_active_path(DISPLAYCONFIG_PATH_INFO * path)
+{
+ return (path && (path->flags & DISPLAYCONFIG_PATH_ACTIVE) &&
+ (path->sourceInfo.modeInfoIdx != DISPLAYCONFIG_PATH_MODE_IDX_INVALID));
+}
diff --git a/vdagent/display_configuration.h b/vdagent/display_configuration.h
new file mode 100644
index 0000000..64ce74a
--- /dev/null
+++ b/vdagent/display_configuration.h
@@ -0,0 +1,92 @@
+/*
+Copyright (C) 2015-2016 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 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 {
+public:
+ CCD();
+ ~CCD();
+
+ bool query_display_config();
+ bool set_display_config(LONG & error);
+ 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);
+ DISPLAYCONFIG_MODE_INFO* get_active_mode(LPCTSTR device_name, bool return_on_error);
+ DISPLAYCONFIG_PATH_INFO* get_device_path(LPCTSTR device_name, bool bActive);
+ void verify_primary_position();
+ void debug_print_config(const char* prefix = NULL);
+
+private:
+ bool load_api();
+ bool get_config_buffers();
+ void free_config_buffers();
+ bool is_device_path(LPCTSTR device_name, DISPLAYCONFIG_PATH_INFO* path);
+ bool get_device_name_config(DISPLAYCONFIG_PATH_INFO* path, LPTSTR dev_name);
+
+ //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;
+
+ bool _primary_detached;
+ PATH_STATE _path_state;
+};
+
+#endif
\ No newline at end of file
More information about the Spice-commits
mailing list