/* 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 . */ #include "display_configuration.h" #include #include /* The following definitions and structures are taken * from here https://github.com/notr1ch/DWMCapture/blob/master/DWMCaptureSource.cpp */ 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; } }; 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; } uint32_t _ioctl; QXLEscapeSetCustomDisplay _custom; }; struct WDDMMonitorConfigEscape { WDDMMonitorConfigEscape(DisplayMode* mode) { _ioctl = QXL_ESCAPE_MONITOR_CONFIG; _head.id = _head.surface_id = 0; _head.x = mode ? mode->get_pos_x() : 0; _head.y = mode ? mode->get_pos_y() : 0; _head.width = mode ? mode->get_width() : 0; _head.height = mode ? mode->get_height() : 0; } uint32_t _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&) { 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("setting %S at (%lu, %lu)", 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("failed to create DC"); 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("Can't set custom display, perhaps running with an older driver?"); } 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 can't update monitor config, may have old, old driver", 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("closest_diff at %lu best %lu", 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) { LONG error(0); //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(error)) { 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) { if (_ccd.is_attached(device_name) && state == MONITOR_DETACHED) { turn_monitor_off(device_name); } 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("updating %S resolution", 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("(%dx%d)", 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 failed", device_name); return false; } bool WDDMInterface::turn_monitor_off(LPCTSTR device_name) { vd_printf("for %S", device_name); if (!_send_monitors_config) { vd_printf("do nothing as _send_monitors_config is off"); return false; } WDDMMonitorConfigEscape wddm_escape(NULL); if (escape(device_name, &wddm_escape, sizeof(wddm_escape))) { return true; } vd_printf("%S failed", device_name); return false; } LONG WDDMInterface::update_display_settings() { LONG error(0); //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(); _ccd.set_display_config(error); return 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) { dev_mode->dmPosition.x = x; dev_mode->dmPosition.y = y; return _ccd.update_mode_position(device_name, dev_mode); } bool WDDMInterface::init_d3d_api() { HMODULE hModule = GetModuleHandle(L"gdi32.dll"); //Look for the gdi32 functions we need to perform driver escapes if (!hModule) { vd_printf("something wildly wrong as we can't open gdi32.dll"); 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); //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, 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("failed to open adapter %S", 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 CreateDC failed with %lu", 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 open adapter from hdc failed with %lu", 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 failed with 0x%lx", 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)); wcsncpy(gdi_display_name.DeviceName, adapter_name, SPICE_N_ELEMENTS(gdi_display_name.DeviceName)); if (NT_SUCCESS(status = _pfnOpen_adapter_gdi_name(&gdi_display_name))) { return gdi_display_name.hAdapter; } vd_printf("%S aurrrgghh nothing works..error is 0x%lx", 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("this should never happen. Status is 0x%lx", status); } //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) ,_primary_detached(false) ,_path_state(PATH_UPDATED) { load_api(); get_config_buffers(); } CCD::~CCD() { free_config_buffers(); } bool CCD::query_display_config() { LONG query_error(ERROR_SUCCESS); if (!get_config_buffers()) return false; //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) { if (!get_config_buffers()) return false; } else { vd_printf("failed QueryDisplayConfig with 0x%lx", 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 failed", 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("path states says nothing changed"); 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("failed SetDisplayConfig with 0x%lx", 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] This path is active but has invalid mode set.", dev_name, prefix); continue; } DISPLAYCONFIG_MODE_INFO* mode = &_pModeInfo[path_info->sourceInfo.modeInfoIdx]; vd_printf("%S [%s] (%ld,%ld) (%ux%u).", dev_name, prefix, mode->sourceMode.position.x, mode->sourceMode.position.y, mode->sourceMode.width, mode->sourceMode.height); } } void CCD::load_api() { HMODULE hModule = GetModuleHandle(L"user32.dll"); if (!hModule) { throw std::exception(); } 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; } return; } while(0); throw std::exception(); } 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 == ERROR_NOT_SUPPORTED) { vd_printf("GetDisplayConfigBufferSizes failed, missing WDDM"); throw std::exception(); } if (error) { vd_printf("GetDisplayConfigBufferSizes failed with 0x%lx", 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("OOM "); 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("DisplayConfigGetDeviceInfo failed with %lu", 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("setting mode x to %lu", 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 updated path mode to (%lu, %lu) - (%ux%u)", 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 updated path mode to (%lu, %lu - (%ux%u)", 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)); }