diff --git a/PowerToys.sln b/PowerToys.sln index eb70eaf85f..6cb2700098 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -596,7 +596,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "projects-common", "projects src\modules\Projects\projects-common\AppUtils.h = src\modules\Projects\projects-common\AppUtils.h src\modules\Projects\projects-common\Data.h = src\modules\Projects\projects-common\Data.h src\modules\Projects\projects-common\GuidUtils.h = src\modules\Projects\projects-common\GuidUtils.h - src\modules\Projects\projects-common\MonitorEnumerator.h = src\modules\Projects\projects-common\MonitorEnumerator.h + src\modules\Projects\projects-common\MonitorUtils.h = src\modules\Projects\projects-common\MonitorUtils.h src\modules\Projects\projects-common\VirtualDesktop.h = src\modules\Projects\projects-common\VirtualDesktop.h src\modules\Projects\projects-common\WindowEnumerator.h = src\modules\Projects\projects-common\WindowEnumerator.h src\modules\Projects\projects-common\WindowFilter.h = src\modules\Projects\projects-common\WindowFilter.h diff --git a/src/common/Display/Display.vcxproj b/src/common/Display/Display.vcxproj index e69a2ada2d..87b74ba534 100644 --- a/src/common/Display/Display.vcxproj +++ b/src/common/Display/Display.vcxproj @@ -24,15 +24,18 @@ NotUsing - ..\..\..\;%(AdditionalIncludeDirectories) + ..\..\..\;..\..\common;.\;%(AdditionalIncludeDirectories) _LIB;%(PreprocessorDefinitions) + + + diff --git a/src/common/Display/DisplayUtils.cpp b/src/common/Display/DisplayUtils.cpp new file mode 100644 index 0000000000..43baef908c --- /dev/null +++ b/src/common/Display/DisplayUtils.cpp @@ -0,0 +1,147 @@ +#include "DisplayUtils.h" + +#include +#include + +#include +#include + +#include + +namespace DisplayUtils +{ + constexpr bool not_digit(wchar_t ch) + { + return '0' <= ch && ch <= '9'; + } + + std::wstring remove_non_digits(const std::wstring& input) + { + std::wstring result; + std::copy_if(input.begin(), input.end(), std::back_inserter(result), not_digit); + return result; + } + + std::pair SplitDisplayDeviceId(const std::wstring& str) noexcept + { + // format: \\?\DISPLAY#{device id}#{instance id}#{some other id} + // example: \\?\DISPLAY#GSM1388#4&125707d6&0&UID8388688#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7} + // output: { GSM1388, 4&125707d6&0&UID8388688 } + + size_t nameStartPos = str.find_first_of('#'); + size_t uidStartPos = str.find('#', nameStartPos + 1); + size_t uidEndPos = str.find('#', uidStartPos + 1); + + if (nameStartPos == std::string::npos || uidStartPos == std::string::npos || uidEndPos == std::string::npos) + { + return { str, L"" }; + } + + return { str.substr(nameStartPos + 1, uidStartPos - nameStartPos - 1), str.substr(uidStartPos + 1, uidEndPos - uidStartPos - 1) }; + } + + std::pair> GetDisplays() + { + bool success = true; + std::vector result{}; + auto allMonitors = MonitorEnumerator::Enumerate(); + + OnThreadExecutor dpiUnawareThread; + dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { + SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE); + SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED); + } }).wait(); + + for (auto& monitorData : allMonitors) + { + MONITORINFOEX monitorInfo = monitorData.second; + MONITORINFOEX dpiUnawareMonitorInfo{}; + + dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { + dpiUnawareMonitorInfo.cbSize = sizeof(dpiUnawareMonitorInfo); + if (!GetMonitorInfo(monitorData.first, &dpiUnawareMonitorInfo)) + { + return; + } + } }).wait(); + + UINT dpi = 0; + if (DPIAware::GetScreenDPIForMonitor(monitorData.first, dpi) != S_OK) + { + success = false; + break; + } + + DisplayUtils::DisplayData data{ + .monitor = monitorData.first, + .dpi = dpi, + .monitorRectDpiAware = monitorInfo.rcMonitor, + .monitorRectDpiUnaware = dpiUnawareMonitorInfo.rcMonitor, + }; + + bool foundActiveMonitor = false; + DISPLAY_DEVICE displayDevice{ .cb = sizeof(displayDevice) }; + DWORD displayDeviceIndex = 0; + while (EnumDisplayDevicesW(monitorInfo.szDevice, displayDeviceIndex, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME)) + { + /* + * if (WI_IsFlagSet(displayDevice.StateFlags, DISPLAY_DEVICE_ACTIVE) && + WI_IsFlagClear(displayDevice.StateFlags, DISPLAY_DEVICE_MIRRORING_DRIVER)) + */ + if (((displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE) == DISPLAY_DEVICE_ACTIVE) && + (displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) == 0) + { + // Find display devices associated with the display. + foundActiveMonitor = true; + break; + } + + displayDeviceIndex++; + } + + if (foundActiveMonitor) + { + auto deviceId = SplitDisplayDeviceId(displayDevice.DeviceID); + data.id = deviceId.first; + data.instanceId = deviceId.second; + try + { + std::wstring numberStr = displayDevice.DeviceName; // \\.\DISPLAY1\Monitor0 + numberStr = numberStr.substr(0, numberStr.find_last_of('\\')); // \\.\DISPLAY1 + numberStr = remove_non_digits(numberStr); + data.number = std::stoi(numberStr); + } + catch (...) + { + success = false; + break; + } + } + else + { + success = false; + + // Use the display name as a fallback value when no proper device was found. + data.id = monitorInfo.szDevice; + data.instanceId = L""; + + try + { + std::wstring numberStr = monitorInfo.szDevice; // \\.\DISPLAY1 + numberStr = remove_non_digits(numberStr); + data.number = std::stoi(numberStr); + } + catch (...) + { + success = false; + break; + } + } + + result.push_back(data); + } + + return { success, result }; + } + +} diff --git a/src/common/Display/DisplayUtils.h b/src/common/Display/DisplayUtils.h new file mode 100644 index 0000000000..0b0e86e227 --- /dev/null +++ b/src/common/Display/DisplayUtils.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +namespace DisplayUtils +{ + struct DisplayData + { + HMONITOR monitor{}; + std::wstring id; + std::wstring instanceId; + unsigned int number{}; + unsigned int dpi{}; + RECT monitorRectDpiAware{}; + RECT monitorRectDpiUnaware{}; + }; + + std::pair> GetDisplays(); +}; diff --git a/src/common/Display/MonitorEnumerator.h b/src/common/Display/MonitorEnumerator.h new file mode 100644 index 0000000000..c603bfee6d --- /dev/null +++ b/src/common/Display/MonitorEnumerator.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +class MonitorEnumerator +{ +public: + static std::vector> Enumerate() + { + MonitorEnumerator inst; + EnumDisplayMonitors(NULL, NULL, Callback, reinterpret_cast(&inst)); + return inst.m_monitors; + } + +private: + MonitorEnumerator() = default; + ~MonitorEnumerator() = default; + + static BOOL CALLBACK Callback(HMONITOR monitor, HDC /*hdc*/, LPRECT /*pRect*/, LPARAM param) + { + MonitorEnumerator* inst = reinterpret_cast(param); + MONITORINFOEX mi; + mi.cbSize = sizeof(mi); + if (GetMonitorInfo(monitor, &mi)) + { + inst->m_monitors.push_back({monitor, mi}); + } + + return TRUE; + } + + std::vector> m_monitors; +}; \ No newline at end of file diff --git a/src/modules/Projects/ProjectsSnapshotTool/MonitorUtils.cpp b/src/modules/Projects/ProjectsSnapshotTool/MonitorUtils.cpp deleted file mode 100644 index 3ae47ee592..0000000000 --- a/src/modules/Projects/ProjectsSnapshotTool/MonitorUtils.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include "pch.h" -#include "MonitorUtils.h" - -#include - -#include -#include - -#include - -namespace MonitorUtils -{ - namespace Display - { - constexpr inline bool not_digit(wchar_t ch) - { - return '0' <= ch && ch <= '9'; - } - - std::wstring remove_non_digits(const std::wstring& input) - { - std::wstring result; - std::copy_if(input.begin(), input.end(), std::back_inserter(result), not_digit); - return result; - } - - std::pair> GetDisplays() - { - bool success = true; - std::vector result{}; - - auto allMonitors = MonitorEnumerator::Enumerate(); - for (auto& monitorData : allMonitors) - { - MONITORINFOEX monitorInfo = monitorData.second; - MONITORINFOEX dpiUnawareMonitorInfo{}; - - OnThreadExecutor dpiUnawareThread; - dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { - SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE); - SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED); - - dpiUnawareMonitorInfo.cbSize = sizeof(dpiUnawareMonitorInfo); - if (!GetMonitorInfo(monitorData.first, &dpiUnawareMonitorInfo)) - { - return; - } - } }).wait(); - - float width = static_cast(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left); - float height = static_cast(monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top); - - float dpiUnawareWidth = static_cast(dpiUnawareMonitorInfo.rcMonitor.right - dpiUnawareMonitorInfo.rcMonitor.left); - float dpiUnawareHeight = static_cast(dpiUnawareMonitorInfo.rcMonitor.bottom - dpiUnawareMonitorInfo.rcMonitor.top); - - UINT dpi = 0; - if (DPIAware::GetScreenDPIForMonitor(monitorData.first, dpi) != S_OK) - { - continue; - } - - Project::Monitor monitorId{ - .monitor = monitorData.first, - .dpi = dpi, - .monitorRectDpiAware = Project::Monitor::MonitorRect { - .top = monitorInfo.rcMonitor.top, - .left = monitorInfo.rcMonitor.left, - .width = static_cast(std::roundf(width)), - .height = static_cast(std::roundf(height)), - }, - .monitorRectDpiUnaware = Project::Monitor::MonitorRect { - .top = dpiUnawareMonitorInfo.rcMonitor.top, - .left = dpiUnawareMonitorInfo.rcMonitor.left, - .width = static_cast(std::roundf(dpiUnawareWidth)), - .height = static_cast(std::roundf(dpiUnawareHeight)), - }, - }; - - bool foundActiveMonitor = false; - DISPLAY_DEVICE displayDevice{ .cb = sizeof(displayDevice) }; - DWORD displayDeviceIndex = 0; - while (EnumDisplayDevicesW(monitorInfo.szDevice, displayDeviceIndex, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME)) - { - /* - * if (WI_IsFlagSet(displayDevice.StateFlags, DISPLAY_DEVICE_ACTIVE) && - WI_IsFlagClear(displayDevice.StateFlags, DISPLAY_DEVICE_MIRRORING_DRIVER)) - */ - if (((displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE) == DISPLAY_DEVICE_ACTIVE) && - (displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) == 0) - { - // Find display devices associated with the display. - foundActiveMonitor = true; - break; - } - - displayDeviceIndex++; - } - - if (foundActiveMonitor) - { - auto deviceId = SplitDisplayDeviceId(displayDevice.DeviceID); - monitorId.id = deviceId.first; - monitorId.instanceId = deviceId.second; - try - { - std::wstring numberStr = displayDevice.DeviceName; // \\.\DISPLAY1\Monitor0 - numberStr = numberStr.substr(0, numberStr.find_last_of('\\')); // \\.\DISPLAY1 - numberStr = remove_non_digits(numberStr); - monitorId.number = std::stoi(numberStr); - } - catch (...) - { - monitorId.number = 0; - } - } - else - { - success = false; - - // Use the display name as a fallback value when no proper device was found. - monitorId.id = monitorInfo.szDevice; - monitorId.instanceId = L""; - - try - { - std::wstring numberStr = monitorInfo.szDevice; // \\.\DISPLAY1 - numberStr = remove_non_digits(numberStr); - monitorId.number = std::stoi(numberStr); - } - catch (...) - { - monitorId.number = 0; - } - } - - result.push_back(std::move(monitorId)); - } - - return { success, result }; - } - - std::pair SplitDisplayDeviceId(const std::wstring& str) noexcept - { - // format: \\?\DISPLAY#{device id}#{instance id}#{some other id} - // example: \\?\DISPLAY#GSM1388#4&125707d6&0&UID8388688#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7} - // output: { GSM1388, 4&125707d6&0&UID8388688 } - - size_t nameStartPos = str.find_first_of('#'); - size_t uidStartPos = str.find('#', nameStartPos + 1); - size_t uidEndPos = str.find('#', uidStartPos + 1); - - if (nameStartPos == std::string::npos || uidStartPos == std::string::npos || uidEndPos == std::string::npos) - { - return { str, L"" }; - } - - return { str.substr(nameStartPos + 1, uidStartPos - nameStartPos - 1), str.substr(uidStartPos + 1, uidEndPos - uidStartPos - 1) }; - } - } - - std::vector IdentifyMonitors() noexcept - { - auto displaysResult = Display::GetDisplays(); - - // retry - int retryCounter = 0; - while (!displaysResult.first && retryCounter < 100) - { - std::this_thread::sleep_for(std::chrono::milliseconds(30)); - displaysResult = Display::GetDisplays(); - retryCounter++; - } - - return displaysResult.second; - } -} \ No newline at end of file diff --git a/src/modules/Projects/ProjectsSnapshotTool/MonitorUtils.h b/src/modules/Projects/ProjectsSnapshotTool/MonitorUtils.h deleted file mode 100644 index 3942255016..0000000000 --- a/src/modules/Projects/ProjectsSnapshotTool/MonitorUtils.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -#include - -// FancyZones: MonitorUtils.h -namespace MonitorUtils -{ - namespace Display - { - std::pair> GetDisplays(); - std::pair SplitDisplayDeviceId(const std::wstring& str) noexcept; - } - - std::vector IdentifyMonitors() noexcept; -} \ No newline at end of file diff --git a/src/modules/Projects/ProjectsSnapshotTool/ProjectsSnapshotTool.vcxproj b/src/modules/Projects/ProjectsSnapshotTool/ProjectsSnapshotTool.vcxproj index 03074170de..6917ff39fc 100644 --- a/src/modules/Projects/ProjectsSnapshotTool/ProjectsSnapshotTool.vcxproj +++ b/src/modules/Projects/ProjectsSnapshotTool/ProjectsSnapshotTool.vcxproj @@ -123,14 +123,12 @@ - Create - diff --git a/src/modules/Projects/ProjectsSnapshotTool/ProjectsSnapshotTool.vcxproj.filters b/src/modules/Projects/ProjectsSnapshotTool/ProjectsSnapshotTool.vcxproj.filters index d182bcfb57..bb9fecde08 100644 --- a/src/modules/Projects/ProjectsSnapshotTool/ProjectsSnapshotTool.vcxproj.filters +++ b/src/modules/Projects/ProjectsSnapshotTool/ProjectsSnapshotTool.vcxproj.filters @@ -18,9 +18,6 @@ Header Files - - Header Files - Header Files @@ -32,9 +29,6 @@ Source Files - - Source Files - diff --git a/src/modules/Projects/ProjectsSnapshotTool/main.cpp b/src/modules/Projects/ProjectsSnapshotTool/main.cpp index 854a12973e..10bb24295c 100644 --- a/src/modules/Projects/ProjectsSnapshotTool/main.cpp +++ b/src/modules/Projects/ProjectsSnapshotTool/main.cpp @@ -6,11 +6,10 @@ #include #include #include +#include #include #include -#include - #include #include #include diff --git a/src/modules/Projects/projects-common/MonitorUtils.h b/src/modules/Projects/projects-common/MonitorUtils.h new file mode 100644 index 0000000000..7119d48b33 --- /dev/null +++ b/src/modules/Projects/projects-common/MonitorUtils.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +namespace MonitorUtils +{ + inline std::vector IdentifyMonitors() noexcept + { + auto displaysResult = DisplayUtils::GetDisplays(); + + int retryCounter = 0; + while (!displaysResult.first && retryCounter < 100) + { + std::this_thread::sleep_for(std::chrono::milliseconds(30)); + displaysResult = DisplayUtils::GetDisplays(); + retryCounter++; + } + + std::vector result{}; + for (const auto& data : displaysResult.second) + { + result.emplace_back(Project::Monitor{ + .monitor = data.monitor, + .id = data.id, + .instanceId = data.instanceId, + .number = data.number, + .dpi = data.dpi, + .monitorRectDpiAware = Project::Monitor::MonitorRect{ + .top = data.monitorRectDpiAware.top, + .left = data.monitorRectDpiAware.left, + .width = data.monitorRectDpiAware.right - data.monitorRectDpiAware.left, + .height = data.monitorRectDpiAware.bottom - data.monitorRectDpiAware.top, + }, + .monitorRectDpiUnaware = Project::Monitor::MonitorRect{ + .top = data.monitorRectDpiUnaware.top, + .left = data.monitorRectDpiUnaware.left, + .width = data.monitorRectDpiUnaware.right - data.monitorRectDpiUnaware.left, + .height = data.monitorRectDpiUnaware.bottom - data.monitorRectDpiUnaware.top, + }, + }); + } + + return result; + } +} \ No newline at end of file