moved display utils

This commit is contained in:
seraphima
2024-06-23 23:05:44 +02:00
parent ed7d3ec973
commit 77c59faa8a
11 changed files with 255 additions and 205 deletions

View File

@@ -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

View File

@@ -24,15 +24,18 @@
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<AdditionalIncludeDirectories>..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\..\;..\..\common;.\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="DisplayUtils.h" />
<ClInclude Include="MonitorEnumerator.h" />
<ClInclude Include="monitors.h" />
<ClInclude Include="dpi_aware.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="DisplayUtils.cpp" />
<ClCompile Include="monitors.cpp" />
<ClCompile Include="dpi_aware.cpp" />
</ItemGroup>

View File

@@ -0,0 +1,147 @@
#include "DisplayUtils.h"
#include <algorithm>
#include <iterator>
#include <dpi_aware.h>
#include <MonitorEnumerator.h>
#include <utils/OnThreadExecutor.h>
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<std::wstring, std::wstring> 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<bool, std::vector<DisplayUtils::DisplayData>> GetDisplays()
{
bool success = true;
std::vector<DisplayUtils::DisplayData> 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 };
}
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <Windows.h>
#include <string>
#include <vector>
namespace DisplayUtils
{
struct DisplayData
{
HMONITOR monitor{};
std::wstring id;
std::wstring instanceId;
unsigned int number{};
unsigned int dpi{};
RECT monitorRectDpiAware{};
RECT monitorRectDpiUnaware{};
};
std::pair<bool, std::vector<DisplayData>> GetDisplays();
};

View File

@@ -0,0 +1,35 @@
#pragma once
#include <functional>
#include <vector>
#include <Windows.h>
class MonitorEnumerator
{
public:
static std::vector<std::pair<HMONITOR, MONITORINFOEX>> Enumerate()
{
MonitorEnumerator inst;
EnumDisplayMonitors(NULL, NULL, Callback, reinterpret_cast<LPARAM>(&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<MonitorEnumerator*>(param);
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(monitor, &mi))
{
inst->m_monitors.push_back({monitor, mi});
}
return TRUE;
}
std::vector<std::pair<HMONITOR, MONITORINFOEX>> m_monitors;
};

View File

@@ -1,176 +0,0 @@
#include "pch.h"
#include "MonitorUtils.h"
#include <ShellScalingApi.h>
#include <projects-common/MonitorEnumerator.h>
#include <OnThreadExecutor.h>
#include <common/Display/dpi_aware.h>
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<bool, std::vector<Project::Monitor>> GetDisplays()
{
bool success = true;
std::vector<Project::Monitor> 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<float>(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left);
float height = static_cast<float>(monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top);
float dpiUnawareWidth = static_cast<float>(dpiUnawareMonitorInfo.rcMonitor.right - dpiUnawareMonitorInfo.rcMonitor.left);
float dpiUnawareHeight = static_cast<float>(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<int>(std::roundf(width)),
.height = static_cast<int>(std::roundf(height)),
},
.monitorRectDpiUnaware = Project::Monitor::MonitorRect {
.top = dpiUnawareMonitorInfo.rcMonitor.top,
.left = dpiUnawareMonitorInfo.rcMonitor.left,
.width = static_cast<int>(std::roundf(dpiUnawareWidth)),
.height = static_cast<int>(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<std::wstring, std::wstring> 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<Project::Monitor> 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;
}
}

View File

@@ -1,17 +0,0 @@
#pragma once
#include <vector>
#include <projects-common/Data.h>
// FancyZones: MonitorUtils.h
namespace MonitorUtils
{
namespace Display
{
std::pair<bool, std::vector<Project::Monitor>> GetDisplays();
std::pair<std::wstring, std::wstring> SplitDisplayDeviceId(const std::wstring& str) noexcept;
}
std::vector<Project::Monitor> IdentifyMonitors() noexcept;
}

View File

@@ -123,14 +123,12 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="MonitorUtils.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="MonitorUtils.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
</ItemGroup>

View File

@@ -18,9 +18,6 @@
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="MonitorUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -32,9 +29,6 @@
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="MonitorUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -6,11 +6,10 @@
#include <projects-common/AppUtils.h>
#include <projects-common/Data.h>
#include <projects-common/GuidUtils.h>
#include <projects-common/MonitorUtils.h>
#include <projects-common/WindowEnumerator.h>
#include <projects-common/WindowFilter.h>
#include <MonitorUtils.h>
#include <common/utils/gpo.h>
#include <common/utils/logger_helper.h>
#include <common/utils/UnhandledExceptionHandler.h>

View File

@@ -0,0 +1,46 @@
#pragma once
#include <projects-common/Data.h>
#include <common/Display/DisplayUtils.h>
namespace MonitorUtils
{
inline std::vector<Project::Monitor> 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<Project::Monitor> 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;
}
}