From 8b59d8cd268e0b066894d7565b7b4de4e6165e8e Mon Sep 17 00:00:00 2001 From: Niels Laute Date: Mon, 16 Feb 2026 16:43:19 +0100 Subject: [PATCH] Port ps to bugreporttool --- .../BugReportTool/BugReportTool.vcxproj | 2 +- .../BugReportTool/ReportMonitorInfo.cpp | 206 ++++++++++++++---- 2 files changed, 166 insertions(+), 42 deletions(-) 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) {