[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
This commit is contained in:
vldmr11080 2020-10-13 17:22:25 +02:00 committed by GitHub
parent dde19380e9
commit 954705e3a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 77 deletions

View file

@ -173,7 +173,7 @@ public:
LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
void OnDisplayChange(DisplayChangeType changeType) noexcept; void OnDisplayChange(DisplayChangeType changeType) noexcept;
void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept; void AddZoneWindow(HMONITOR monitor, const std::wstring& deviceId) noexcept;
protected: protected:
static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; 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); 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 void FancyZones::UpdateZoneWindows() noexcept
{ {
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL { // Mapping between display device name and device index (operating system identifies each display device with an index value).
MONITORINFOEX mi; std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;
mi.cbSize = sizeof(mi); struct capture
if (GetMonitorInfo(monitor, &mi))
{ {
DISPLAY_DEVICE displayDevice = { sizeof(displayDevice) }; FancyZones* fancyZones;
PCWSTR deviceId = nullptr; std::unordered_map<std::wstring, DWORD>* displayDeviceIdx;
};
bool validMonitor = true; auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
if (EnumDisplayDevices(mi.szDevice, 0, &displayDevice, 1)) capture* params = reinterpret_cast<capture*>(data);
MONITORINFOEX mi{ { .cbSize = sizeof(mi)} };
if (GetMonitorInfoW(monitor, &mi))
{ {
if (WI_IsFlagSet(displayDevice.StateFlags, DISPLAY_DEVICE_MIRRORING_DRIVER)) 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))
{ {
validMonitor = FALSE; ++displayDeviceIdxMap[mi.szDevice];
} // Only take active monitors (presented as being "on" by the respective GDI view) and monitors that don't
else if (displayDevice.DeviceID[0] != L'\0') // 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; deviceId = displayDevice.DeviceID;
fancyZones->AddZoneWindow(monitor, deviceId);
break;
} }
} }
if (validMonitor) if (deviceId.empty())
{
if (!deviceId)
{ {
deviceId = GetSystemMetrics(SM_REMOTESESSION) ? deviceId = GetSystemMetrics(SM_REMOTESESSION) ?
L"\\\\?\\DISPLAY#REMOTEDISPLAY#" : L"\\\\?\\DISPLAY#REMOTEDISPLAY#" :
L"\\\\?\\DISPLAY#LOCALDISPLAY#"; L"\\\\?\\DISPLAY#LOCALDISPLAY#";
}
auto strongThis = reinterpret_cast<FancyZones*>(data); fancyZones->AddZoneWindow(monitor, deviceId);
strongThis->AddZoneWindow(monitor, deviceId);
} }
} }
return TRUE; return TRUE;
@ -1010,11 +1017,12 @@ void FancyZones::UpdateZoneWindows() noexcept
if (m_settings->GetSettings()->spanZonesAcrossMonitors) if (m_settings->GetSettings()->spanZonesAcrossMonitors)
{ {
AddZoneWindow(nullptr, NULL); AddZoneWindow(nullptr, {});
} }
else else
{ {
EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast<LPARAM>(this)); capture capture{ this, &displayDeviceIdxMap };
EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast<LPARAM>(&capture));
} }
} }

View file

@ -27,24 +27,26 @@ using namespace FancyZonesUtils;
namespace ZoneWindowUtils 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; MONITORINFOEXW mi;
mi.cbSize = sizeof(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); Rect const monitorRect(mi.rcMonitor);
StringCchPrintf(uniqueId, ARRAYSIZE(uniqueId), L"%s_%d_%d_%s", parsedId, monitorRect.width(), monitorRect.height(), virtualDesktopId); // Unique identifier format: <parsed-device-id>_<width>_<height>_<virtual-desktop-id>
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 }; std::wstring result{ ZonedWindowProperties::MultiMonitorDeviceID };

View file

@ -4,8 +4,8 @@
namespace ZoneWindowUtils namespace ZoneWindowUtils
{ {
std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId); std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& devideId, const std::wstring& virtualDesktopId);
std::wstring GenerateUniqueIdAllMonitorsArea(PCWSTR virtualDesktopId); std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId);
} }
/** /**

View file

@ -40,6 +40,30 @@ namespace
namespace FancyZonesUtils 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*); typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
UINT GetDpiForMonitor(HMONITOR monitor) noexcept UINT GetDpiForMonitor(HMONITOR monitor) noexcept
{ {

View file

@ -104,34 +104,6 @@ namespace FancyZonesUtils
} }
} }
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) inline BYTE OpacitySettingToAlpha(int opacity)
{ {
return static_cast<BYTE>(opacity * 2.55); return static_cast<BYTE>(opacity * 2.55);
@ -185,6 +157,8 @@ namespace FancyZonesUtils
return result; return result;
} }
std::wstring ParseDeviceId(const std::wstring& deviceId);
UINT GetDpiForMonitor(HMONITOR monitor) noexcept; UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo); void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
void SizeWindowToRect(HWND window, RECT rect) noexcept; void SizeWindowToRect(HWND window, RECT rect) noexcept;

View file

@ -45,10 +45,10 @@ namespace FancyZonesUnitTests
// We're interested in the unique part between the first and last #'s // 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 input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
// Example output: DELA026#5&10a58c63&0&UID16777488 // Example output: DELA026#5&10a58c63&0&UID16777488
PCWSTR input = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}"; const std::wstring input = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}";
wchar_t output[256]{}; const std::wstring actual = ParseDeviceId(input);
ParseDeviceId(input, output, ARRAYSIZE(output)); const std::wstring expected = L"DELA026#5&10a58c63&0&UID16777488";
Assert::AreEqual(0, wcscmp(output, L"DELA026#5&10a58c63&0&UID16777488")); Assert::AreEqual(expected, actual);
} }
TEST_METHOD(TestParseInvalidDeviceId) TEST_METHOD(TestParseInvalidDeviceId)
@ -56,10 +56,10 @@ namespace FancyZonesUnitTests
// We're interested in the unique part between the first and last #'s // 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 input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
// Example output: DELA026#5&10a58c63&0&UID16777488 // Example output: DELA026#5&10a58c63&0&UID16777488
PCWSTR input = L"AnInvalidDeviceId"; const std::wstring input = L"AnInvalidDeviceId";
wchar_t output[256]{}; const std::wstring actual = ParseDeviceId(input);
ParseDeviceId(input, output, ARRAYSIZE(output)); const std::wstring expected = L"FallbackDevice";
Assert::AreEqual(0, wcscmp(output, L"FallbackDevice")); Assert::AreEqual(expected, actual);
} }
TEST_METHOD(TestMonitorOrdering01) TEST_METHOD(TestMonitorOrdering01)

View file

@ -160,7 +160,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowNoDeviceId) TEST_METHOD(CreateZoneWindowNoDeviceId)
{ {
// Generate unique id without device id // 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<MockZoneWindowHost>().get(), m_hInst, m_monitor, uniqueId, {}, false); auto zoneWindow = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().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); 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) TEST_METHOD(CreateZoneWindowNoDesktopId)
{ {
// Generate unique id without virtual desktop id // 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<MockZoneWindowHost>().get(), m_hInst, m_monitor, uniqueId, {}, false); auto zoneWindow = MakeZoneWindow(winrt::make_self<MockZoneWindowHost>().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); const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);