diff --git a/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj b/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj
index be9a9bb279..0e38b0eaea 100644
--- a/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj
+++ b/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj
@@ -34,7 +34,7 @@
Console
- Version.lib;Wevtapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ Version.lib;Wevtapi.lib;Shcore.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
diff --git a/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp b/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp
index 888f26e48e..83f63cac8a 100644
--- a/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp
+++ b/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp
@@ -1,76 +1,200 @@
-#pragma once
#include "ReportMonitorInfo.h"
#include
+#include
#include
-#include "../../../src/common/utils/winapi_error.h"
+#include
+#include
+
using namespace std;
namespace
{
- int BuildMonitorInfoReport(std::wostream& os)
+ struct MonitorData
{
- struct capture
+ LONG left{};
+ LONG top{};
+ LONG right{};
+ LONG bottom{};
+ LONG width{};
+ LONG height{};
+ UINT dpi{};
+ int scalingPercent{};
+ bool primary{};
+ wstring deviceName;
+ wstring deviceId;
+ wstring deviceString;
+ bool active{};
+ bool mirroring{};
+ };
+
+ struct GapData
+ {
+ size_t monitorA{};
+ size_t monitorB{};
+ LONG gapPx{};
+ LONG verticalOverlapPx{};
+ };
+
+ UINT GetMonitorDpi(HMONITOR hMonitor)
+ {
+ UINT dpiX = 0, dpiY = 0;
+ if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)) && dpiX > 0)
{
- std::wostream* os = nullptr;
+ return dpiX;
+ }
+ return 96;
+ }
+
+ vector CollectMonitorData()
+ {
+ struct EnumState
+ {
+ vector* monitors = nullptr;
};
- auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM prm) -> BOOL {
- std::wostream& os = *(reinterpret_cast(prm))->os;
- MONITORINFOEX mi;
+ vector monitors;
+ EnumState state;
+ state.monitors = &monitors;
+
+ auto callback = [](HMONITOR hMonitor, HDC, RECT*, LPARAM param) -> BOOL {
+ auto& monitors = *(reinterpret_cast(param))->monitors;
+ MONITORINFOEXW mi{};
mi.cbSize = sizeof(mi);
- if (GetMonitorInfoW(monitor, &mi))
+ if (!GetMonitorInfoW(hMonitor, &mi))
{
- os << "GetMonitorInfo OK\n";
- DISPLAY_DEVICE displayDevice = { sizeof(displayDevice) };
+ return TRUE;
+ }
- DWORD i = 0;
- while (EnumDisplayDevicesW(mi.szDevice, i++, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
- {
- const bool active = displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE;
- const bool mirroring = displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER;
- os << "EnumDisplayDevices OK:\n"
- << "\tMirroring = " << mirroring << '\n'
- << "\tActive = " << active << '\n'
- << "\tDeviceID = " << displayDevice.DeviceID << '\n'
- << "\tDeviceKey = " << displayDevice.DeviceKey << '\n'
- << "\tDeviceName = " << displayDevice.DeviceName << '\n'
- << "\tDeviceString = " << displayDevice.DeviceString << '\n';
- }
- }
- else
+ MonitorData data;
+ data.left = mi.rcMonitor.left;
+ data.top = mi.rcMonitor.top;
+ data.right = mi.rcMonitor.right;
+ data.bottom = mi.rcMonitor.bottom;
+ data.width = mi.rcMonitor.right - mi.rcMonitor.left;
+ data.height = mi.rcMonitor.bottom - mi.rcMonitor.top;
+ data.primary = (mi.dwFlags & MONITORINFOF_PRIMARY) != 0;
+ data.deviceName = mi.szDevice;
+ data.dpi = GetMonitorDpi(hMonitor);
+ data.scalingPercent = static_cast(round((data.dpi / 96.0) * 100.0));
+
+ DISPLAY_DEVICE displayDevice = { sizeof(displayDevice) };
+ if (EnumDisplayDevicesW(mi.szDevice, 0, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
{
- auto message = get_last_error_message(GetLastError());
- os << "GetMonitorInfo FAILED: " << (message.has_value() ? message.value() : L"") << '\n';
+ data.active = (displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE) != 0;
+ data.mirroring = (displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) != 0;
+ data.deviceId = displayDevice.DeviceID;
+ data.deviceString = displayDevice.DeviceString;
}
+
+ monitors.push_back(std::move(data));
return TRUE;
};
- capture c;
- c.os = &os;
- if (EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast(& c)))
+ EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast(&state));
+ return monitors;
+ }
+
+ vector AnalyzeGaps(const vector& monitors)
+ {
+ vector gaps;
+ for (size_t i = 0; i < monitors.size(); i++)
{
- os << "EnumDisplayMonitors OK\n";
+ for (size_t j = i + 1; j < monitors.size(); j++)
+ {
+ const auto& m1 = monitors[i];
+ const auto& m2 = monitors[j];
+
+ LONG hGap = min(abs(m1.right - m2.left), abs(m2.right - m1.left));
+ LONG vOverlapStart = max(m1.top, m2.top);
+ LONG vOverlapEnd = min(m1.bottom, m2.bottom);
+ LONG vOverlap = vOverlapEnd - vOverlapStart;
+
+ if (hGap > 50 && vOverlap > 0)
+ {
+ gaps.push_back({ i, j, hGap, vOverlap });
+ }
+ }
}
- else
+ return gaps;
+ }
+
+ // Escape a wide string for JSON output
+ wstring JsonEscape(const wstring& input)
+ {
+ wstring result;
+ result.reserve(input.size());
+ for (auto ch : input)
{
- auto message = get_last_error_message(GetLastError());
- os << "EnumDisplayMonitors FAILED: " << (message.has_value() ? message.value() : L"") << '\n';
+ switch (ch)
+ {
+ case L'\\': result += L"\\\\"; break;
+ case L'"': result += L"\\\""; break;
+ case L'\n': result += L"\\n"; break;
+ case L'\r': result += L"\\r"; break;
+ case L'\t': result += L"\\t"; break;
+ default: result += ch; break;
+ }
}
- return 0;
+ return result;
+ }
+
+ void WriteJsonReport(const filesystem::path& outputPath, const vector& monitors, const vector& gaps)
+ {
+ wofstream os(outputPath);
+ os << L"{\n";
+ os << L" \"monitor_count\": " << monitors.size() << L",\n";
+ os << L" \"monitors\": [\n";
+
+ for (size_t i = 0; i < monitors.size(); i++)
+ {
+ const auto& m = monitors[i];
+ os << L" {\n";
+ os << L" \"left\": " << m.left << L",\n";
+ os << L" \"top\": " << m.top << L",\n";
+ os << L" \"right\": " << m.right << L",\n";
+ os << L" \"bottom\": " << m.bottom << L",\n";
+ os << L" \"width\": " << m.width << L",\n";
+ os << L" \"height\": " << m.height << L",\n";
+ os << L" \"dpi\": " << m.dpi << L",\n";
+ os << L" \"scaling_percent\": " << m.scalingPercent << L",\n";
+ os << L" \"primary\": " << (m.primary ? L"true" : L"false") << L",\n";
+ os << L" \"device_name\": \"" << JsonEscape(m.deviceName) << L"\",\n";
+ os << L" \"device_id\": \"" << JsonEscape(m.deviceId) << L"\",\n";
+ os << L" \"device_string\": \"" << JsonEscape(m.deviceString) << L"\",\n";
+ os << L" \"active\": " << (m.active ? L"true" : L"false") << L",\n";
+ os << L" \"mirroring\": " << (m.mirroring ? L"true" : L"false") << L"\n";
+ os << L" }" << (i + 1 < monitors.size() ? L"," : L"") << L"\n";
+ }
+
+ os << L" ],\n";
+ os << L" \"coordinate_gaps\": [\n";
+
+ for (size_t i = 0; i < gaps.size(); i++)
+ {
+ const auto& g = gaps[i];
+ os << L" {\n";
+ os << L" \"monitor_a\": " << g.monitorA << L",\n";
+ os << L" \"monitor_b\": " << g.monitorB << L",\n";
+ os << L" \"gap_px\": " << g.gapPx << L",\n";
+ os << L" \"vertical_overlap_px\": " << g.verticalOverlapPx << L"\n";
+ os << L" }" << (i + 1 < gaps.size() ? L"," : L"") << L"\n";
+ }
+
+ os << L" ]\n";
+ os << L"}\n";
}
}
void ReportMonitorInfo(const filesystem::path& tmpDir)
{
- auto monitorReportPath = tmpDir;
- monitorReportPath.append("monitor-report-info.txt");
+ auto reportPath = tmpDir / L"monitor-report-info.json";
try
{
- wofstream monitorReport(monitorReportPath);
- monitorReport << "GetSystemMetrics = " << GetSystemMetrics(SM_CMONITORS) << '\n';
- BuildMonitorInfoReport(monitorReport);
+ auto monitors = CollectMonitorData();
+ auto gaps = AnalyzeGaps(monitors);
+ WriteJsonReport(reportPath, monitors, gaps);
}
catch (std::exception& ex)
{