From 954705e3a06e16b6eacc1f219ceab87e8de5e039 Mon Sep 17 00:00:00 2001 From: vldmr11080 <57061786+vldmr11080@users.noreply.github.com> Date: Tue, 13 Oct 2020 17:22:25 +0200 Subject: [PATCH] [FancyZones] Screen enumeration improvement (#6908) * Improvements in enumeration of available screens (work areas) * Minor code style improvement * Address PR comments * Store map of display device name to device index * Address PR comments * Update comment * Break when suitable device is found --- src/modules/fancyzones/lib/FancyZones.cpp | 62 +++++++++++-------- src/modules/fancyzones/lib/ZoneWindow.cpp | 22 ++++--- src/modules/fancyzones/lib/ZoneWindow.h | 4 +- src/modules/fancyzones/lib/util.cpp | 24 +++++++ src/modules/fancyzones/lib/util.h | 30 +-------- .../fancyzones/tests/UnitTests/Util.Spec.cpp | 16 ++--- .../tests/UnitTests/ZoneWindow.Spec.cpp | 4 +- 7 files changed, 85 insertions(+), 77 deletions(-) diff --git a/src/modules/fancyzones/lib/FancyZones.cpp b/src/modules/fancyzones/lib/FancyZones.cpp index 1f221f1cda..6699e2810d 100644 --- a/src/modules/fancyzones/lib/FancyZones.cpp +++ b/src/modules/fancyzones/lib/FancyZones.cpp @@ -173,7 +173,7 @@ public: LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; void OnDisplayChange(DisplayChangeType changeType) noexcept; - void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept; + void AddZoneWindow(HMONITOR monitor, const std::wstring& deviceId) noexcept; protected: static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; @@ -915,7 +915,7 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept } } -void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept +void FancyZones::AddZoneWindow(HMONITOR monitor, const std::wstring& deviceId) noexcept { std::unique_lock writeLock(m_lock); @@ -971,38 +971,45 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, void FancyZones::UpdateZoneWindows() noexcept { - auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL { - MONITORINFOEX mi; - mi.cbSize = sizeof(mi); - if (GetMonitorInfo(monitor, &mi)) - { - DISPLAY_DEVICE displayDevice = { sizeof(displayDevice) }; - PCWSTR deviceId = nullptr; + // Mapping between display device name and device index (operating system identifies each display device with an index value). + std::unordered_map displayDeviceIdxMap; + struct capture + { + FancyZones* fancyZones; + std::unordered_map* displayDeviceIdx; + }; - bool validMonitor = true; - if (EnumDisplayDevices(mi.szDevice, 0, &displayDevice, 1)) + auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL { + capture* params = reinterpret_cast(data); + MONITORINFOEX mi{ { .cbSize = sizeof(mi)} }; + if (GetMonitorInfoW(monitor, &mi)) + { + auto& displayDeviceIdxMap = *(params->displayDeviceIdx); + FancyZones* fancyZones = params->fancyZones; + + DISPLAY_DEVICE displayDevice{ .cb = sizeof(displayDevice) }; + std::wstring deviceId; + while (EnumDisplayDevicesW(mi.szDevice, displayDeviceIdxMap[mi.szDevice], &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME)) { - if (WI_IsFlagSet(displayDevice.StateFlags, DISPLAY_DEVICE_MIRRORING_DRIVER)) - { - validMonitor = FALSE; - } - else if (displayDevice.DeviceID[0] != L'\0') + ++displayDeviceIdxMap[mi.szDevice]; + // Only take active monitors (presented as being "on" by the respective GDI view) and monitors that don't + // represent a pseudo device used to mirror application drawing. + if (WI_IsFlagSet(displayDevice.StateFlags, DISPLAY_DEVICE_ACTIVE) && + WI_IsFlagClear(displayDevice.StateFlags, DISPLAY_DEVICE_MIRRORING_DRIVER)) { deviceId = displayDevice.DeviceID; + fancyZones->AddZoneWindow(monitor, deviceId); + break; } } - if (validMonitor) + if (deviceId.empty()) { - if (!deviceId) - { - deviceId = GetSystemMetrics(SM_REMOTESESSION) ? - L"\\\\?\\DISPLAY#REMOTEDISPLAY#" : - L"\\\\?\\DISPLAY#LOCALDISPLAY#"; - } + deviceId = GetSystemMetrics(SM_REMOTESESSION) ? + L"\\\\?\\DISPLAY#REMOTEDISPLAY#" : + L"\\\\?\\DISPLAY#LOCALDISPLAY#"; - auto strongThis = reinterpret_cast(data); - strongThis->AddZoneWindow(monitor, deviceId); + fancyZones->AddZoneWindow(monitor, deviceId); } } return TRUE; @@ -1010,11 +1017,12 @@ void FancyZones::UpdateZoneWindows() noexcept if (m_settings->GetSettings()->spanZonesAcrossMonitors) { - AddZoneWindow(nullptr, NULL); + AddZoneWindow(nullptr, {}); } else { - EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast(this)); + capture capture{ this, &displayDeviceIdxMap }; + EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast(&capture)); } } diff --git a/src/modules/fancyzones/lib/ZoneWindow.cpp b/src/modules/fancyzones/lib/ZoneWindow.cpp index 141fe800cc..a5b3566cfa 100644 --- a/src/modules/fancyzones/lib/ZoneWindow.cpp +++ b/src/modules/fancyzones/lib/ZoneWindow.cpp @@ -27,24 +27,26 @@ using namespace FancyZonesUtils; namespace ZoneWindowUtils { - std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId) + std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& deviceId, const std::wstring& virtualDesktopId) { - wchar_t uniqueId[256]{}; // Parsed deviceId + resolution + virtualDesktopId - MONITORINFOEXW mi; mi.cbSize = sizeof(mi); - if (virtualDesktopId && GetMonitorInfo(monitor, &mi)) + if (!virtualDesktopId.empty() && GetMonitorInfo(monitor, &mi)) { - wchar_t parsedId[256]{}; - ParseDeviceId(deviceId, parsedId, 256); - Rect const monitorRect(mi.rcMonitor); - StringCchPrintf(uniqueId, ARRAYSIZE(uniqueId), L"%s_%d_%d_%s", parsedId, monitorRect.width(), monitorRect.height(), virtualDesktopId); + // Unique identifier format: ___ + return ParseDeviceId(deviceId) + + L'_' + + std::to_wstring(monitorRect.width()) + + L'_' + + std::to_wstring(monitorRect.height()) + + L'_' + + virtualDesktopId; } - return std::wstring{ uniqueId }; + return {}; } - std::wstring GenerateUniqueIdAllMonitorsArea(PCWSTR virtualDesktopId) + std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId) { std::wstring result{ ZonedWindowProperties::MultiMonitorDeviceID }; diff --git a/src/modules/fancyzones/lib/ZoneWindow.h b/src/modules/fancyzones/lib/ZoneWindow.h index b4be8c5dfb..aeeb318f22 100644 --- a/src/modules/fancyzones/lib/ZoneWindow.h +++ b/src/modules/fancyzones/lib/ZoneWindow.h @@ -4,8 +4,8 @@ namespace ZoneWindowUtils { - std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId); - std::wstring GenerateUniqueIdAllMonitorsArea(PCWSTR virtualDesktopId); + std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& devideId, const std::wstring& virtualDesktopId); + std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId); } /** diff --git a/src/modules/fancyzones/lib/util.cpp b/src/modules/fancyzones/lib/util.cpp index 82bfa46c29..5a35d4a993 100644 --- a/src/modules/fancyzones/lib/util.cpp +++ b/src/modules/fancyzones/lib/util.cpp @@ -40,6 +40,30 @@ namespace namespace FancyZonesUtils { + std::wstring ParseDeviceId(const std::wstring& deviceId) + { + // We're interested in the unique part between the first and last #'s + // Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7} + // Example output: DELA026#5&10a58c63&0&UID16777488 + static const std::wstring defaultDeviceId = L"FallbackDevice"; + if (deviceId.empty()) + { + return defaultDeviceId; + } + + size_t start = deviceId.find(L'#'); + size_t end = deviceId.rfind(L'#'); + if (start != std::wstring::npos && end != std::wstring::npos && start != end) + { + size_t size = end - (start + 1); + return deviceId.substr(start + 1, size); + } + else + { + return defaultDeviceId; + } + } + typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*); UINT GetDpiForMonitor(HMONITOR monitor) noexcept { diff --git a/src/modules/fancyzones/lib/util.h b/src/modules/fancyzones/lib/util.h index 35be976741..8f2cffed1e 100644 --- a/src/modules/fancyzones/lib/util.h +++ b/src/modules/fancyzones/lib/util.h @@ -103,34 +103,6 @@ namespace FancyZonesUtils return fallbackColor; } } - - inline void ParseDeviceId(PCWSTR deviceId, PWSTR parsedId, size_t size) - { - // We're interested in the unique part between the first and last #'s - // Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7} - // Example output: DELA026#5&10a58c63&0&UID16777488 - const std::wstring defaultDeviceId = L"FallbackDevice"; - if (!deviceId) - { - StringCchCopy(parsedId, size, defaultDeviceId.c_str()); - return; - } - wchar_t buffer[256]; - StringCchCopy(buffer, 256, deviceId); - - PWSTR pszStart = wcschr(buffer, L'#'); - PWSTR pszEnd = wcsrchr(buffer, L'#'); - if (pszStart && pszEnd && (pszStart != pszEnd)) - { - pszStart++; // skip past the first # - *pszEnd = '\0'; - StringCchCopy(parsedId, size, pszStart); - } - else - { - StringCchCopy(parsedId, size, defaultDeviceId.c_str()); - } - } inline BYTE OpacitySettingToAlpha(int opacity) { @@ -185,6 +157,8 @@ namespace FancyZonesUtils return result; } + std::wstring ParseDeviceId(const std::wstring& deviceId); + UINT GetDpiForMonitor(HMONITOR monitor) noexcept; void OrderMonitors(std::vector>& monitorInfo); void SizeWindowToRect(HWND window, RECT rect) noexcept; diff --git a/src/modules/fancyzones/tests/UnitTests/Util.Spec.cpp b/src/modules/fancyzones/tests/UnitTests/Util.Spec.cpp index d9dabe7b94..c06f344136 100644 --- a/src/modules/fancyzones/tests/UnitTests/Util.Spec.cpp +++ b/src/modules/fancyzones/tests/UnitTests/Util.Spec.cpp @@ -45,10 +45,10 @@ namespace FancyZonesUnitTests // We're interested in the unique part between the first and last #'s // Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7} // Example output: DELA026#5&10a58c63&0&UID16777488 - PCWSTR input = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}"; - wchar_t output[256]{}; - ParseDeviceId(input, output, ARRAYSIZE(output)); - Assert::AreEqual(0, wcscmp(output, L"DELA026#5&10a58c63&0&UID16777488")); + const std::wstring input = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}"; + const std::wstring actual = ParseDeviceId(input); + const std::wstring expected = L"DELA026#5&10a58c63&0&UID16777488"; + Assert::AreEqual(expected, actual); } TEST_METHOD(TestParseInvalidDeviceId) @@ -56,10 +56,10 @@ namespace FancyZonesUnitTests // We're interested in the unique part between the first and last #'s // Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7} // Example output: DELA026#5&10a58c63&0&UID16777488 - PCWSTR input = L"AnInvalidDeviceId"; - wchar_t output[256]{}; - ParseDeviceId(input, output, ARRAYSIZE(output)); - Assert::AreEqual(0, wcscmp(output, L"FallbackDevice")); + const std::wstring input = L"AnInvalidDeviceId"; + const std::wstring actual = ParseDeviceId(input); + const std::wstring expected = L"FallbackDevice"; + Assert::AreEqual(expected, actual); } TEST_METHOD(TestMonitorOrdering01) diff --git a/src/modules/fancyzones/tests/UnitTests/ZoneWindow.Spec.cpp b/src/modules/fancyzones/tests/UnitTests/ZoneWindow.Spec.cpp index c9dac75cea..c02685b6e1 100644 --- a/src/modules/fancyzones/tests/UnitTests/ZoneWindow.Spec.cpp +++ b/src/modules/fancyzones/tests/UnitTests/ZoneWindow.Spec.cpp @@ -160,7 +160,7 @@ namespace FancyZonesUnitTests TEST_METHOD(CreateZoneWindowNoDeviceId) { // Generate unique id without device id - std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, nullptr, m_virtualDesktopId.c_str()); + std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, {}, m_virtualDesktopId); auto zoneWindow = MakeZoneWindow(winrt::make_self().get(), m_hInst, m_monitor, uniqueId, {}, false); const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom); @@ -178,7 +178,7 @@ namespace FancyZonesUnitTests TEST_METHOD(CreateZoneWindowNoDesktopId) { // Generate unique id without virtual desktop id - std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, m_deviceId.c_str(), nullptr); + std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, m_deviceId, {}); auto zoneWindow = MakeZoneWindow(winrt::make_self().get(), m_hInst, m_monitor, uniqueId, {}, false); const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);