[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:
parent
dde19380e9
commit
954705e3a0
|
@ -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))
|
{
|
||||||
{
|
FancyZones* fancyZones;
|
||||||
DISPLAY_DEVICE displayDevice = { sizeof(displayDevice) };
|
std::unordered_map<std::wstring, DWORD>* displayDeviceIdx;
|
||||||
PCWSTR deviceId = nullptr;
|
};
|
||||||
|
|
||||||
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))
|
||||||
|
{
|
||||||
|
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))
|
++displayDeviceIdxMap[mi.szDevice];
|
||||||
{
|
// Only take active monitors (presented as being "on" by the respective GDI view) and monitors that don't
|
||||||
validMonitor = FALSE;
|
// represent a pseudo device used to mirror application drawing.
|
||||||
}
|
if (WI_IsFlagSet(displayDevice.StateFlags, DISPLAY_DEVICE_ACTIVE) &&
|
||||||
else if (displayDevice.DeviceID[0] != L'\0')
|
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) ?
|
||||||
{
|
L"\\\\?\\DISPLAY#REMOTEDISPLAY#" :
|
||||||
deviceId = GetSystemMetrics(SM_REMOTESESSION) ?
|
L"\\\\?\\DISPLAY#LOCALDISPLAY#";
|
||||||
L"\\\\?\\DISPLAY#REMOTEDISPLAY#" :
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue