OpenCPN Partial API docs
Loading...
Searching...
No Matches
displays.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2010 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
24#include <iostream>
25#include "config.h"
26#include "displays.h"
27#include "model/logger.h"
28
29// Platform-specific includes
30#if __linux__
31#ifdef OCPN_HAVE_X11
32#include <X11/Xlib.h>
33#include <X11/extensions/Xrandr.h>
34#include <gdk/gdk.h>
35#endif
36#elif _WIN32
37#include <Windows.h>
38#include <ShellScalingApi.h>
39#include <locale>
40#include <codecvt>
41#elif __APPLE__
42#include <CoreGraphics/CoreGraphics.h>
43#endif
44
45std::vector<OCPN_MonitorInfo> g_monitor_info;
46
47static size_t g_num_monitors = 0;
48
49#if _WIN32
50
51BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor,
52 LPRECT lprcMonitor, LPARAM dwData) {
53 MONITORINFOEX monitorInfo;
54 monitorInfo.cbSize = sizeof(MONITORINFOEX);
55 if (GetMonitorInfo(hMonitor, &monitorInfo)) {
56 UINT rawdpiX, rawdpiY;
57 UINT effectivedpiX, effectivedpiY;
58 /* In newer SDKs (Windows 8.1+) it is much simpler to get DPI for each
59 * monitor as GetDpiForMonitor actually is exposed */
60 HRESULT hr = GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &effectivedpiX,
61 &effectivedpiY);
62 if (!SUCCEEDED(hr)) {
63 WARNING_LOG << "GetDpiForMonitor MDT_EFFECTIVE_DPI failed, falling back "
64 "to 96 DPI";
65 effectivedpiX = 96;
66 effectivedpiY = 96;
67 }
68 hr = GetDpiForMonitor(hMonitor, MDT_RAW_DPI, &rawdpiX, &rawdpiY);
69 if (!SUCCEEDED(hr) || rawdpiX == 0 || rawdpiY == 0) {
70 WARNING_LOG << "GetDpiForMonitor MDT_RAW_DPI failed, falling back to "
71 << effectivedpiX
72 << " DPI"; // This happens in virtualized environments
73 rawdpiX = effectivedpiX;
74 rawdpiY = effectivedpiY;
75 }
76 DEBUG_LOG << "Raw DPI " << rawdpiX << "x" << rawdpiY;
77 DEBUG_LOG << "Effective DPI " << effectivedpiX << "x" << effectivedpiY;
78 DEVICE_SCALE_FACTOR scaleFactor;
79 hr = GetScaleFactorForMonitor(hMonitor, &scaleFactor);
80 if (!SUCCEEDED(hr) || scaleFactor == DEVICE_SCALE_FACTOR_INVALID) {
81 WARNING_LOG << "GetScaleFactorForMonitor failed, falling back to 100";
82 scaleFactor = SCALE_100_PERCENT;
83 }
84
85 auto width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
86 auto height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;
87 auto mmx = width * 25.4 / rawdpiX;
88 auto mmy = height * 25.4 / rawdpiY;
89 std::wstring ws(monitorInfo.szDevice);
90 DEBUG_LOG << "Display " << hMonitor << ":";
91 DEBUG_LOG << " Monitor Name: " << std::string(ws.begin(), ws.end());
92 DEBUG_LOG << " Resolution: " << width << "x" << height;
93 DEBUG_LOG << " Physical Size: " << mmx << " mm x " << mmy << " mm";
94 DEBUG_LOG << " Scale factor:" << scaleFactor;
95 g_monitor_info.push_back(
96 {std::string(ws.begin(), ws.end()), static_cast<size_t>(mmx),
97 static_cast<size_t>(mmy), static_cast<size_t>(width),
98 static_cast<size_t>(height), static_cast<size_t>(width),
99 static_cast<size_t>(height), static_cast<size_t>(scaleFactor)});
100 } else {
101 DEBUG_LOG << "GetMonitorInfo failed";
102 }
103 return TRUE;
104}
105#endif
106
107// Function to enumerate monitors and retrieve screen size
109 g_monitor_info.clear();
110#if __linux__
111#ifdef OCPN_HAVE_X11
112 Display* display = XOpenDisplay(nullptr);
113 if (!display) {
114 std::cerr << "Error opening X display." << std::endl;
115 return;
116 }
117
118 int screen = DefaultScreen(display);
119 Window root = RootWindow(display, screen);
120
121 XRRScreenResources* resources = XRRGetScreenResources(display, root);
122 if (!resources) {
123 ERROR_LOG << "Error getting screen resources.";
124 XCloseDisplay(display);
125 return;
126 }
127
128 GdkDisplay* gdk_display = gdk_display_get_default();
129 int gdk_num_monitors = gdk_display_get_n_monitors(gdk_display);
130 DEBUG_LOG << "GDK Monitors: " << gdk_num_monitors;
131 size_t scale;
132 for (int i = 0; i < resources->noutput; ++i) {
133 XRROutputInfo* outputInfo =
134 XRRGetOutputInfo(display, resources, resources->outputs[i]);
135 XRRCrtcInfo* crtcInfo =
136 XRRGetCrtcInfo(display, resources, resources->crtcs[i]);
137 scale = 100;
138 if (i < gdk_num_monitors) {
139 GdkMonitor* monitor = gdk_display_get_monitor(gdk_display, i);
140 scale = gdk_monitor_get_scale_factor(monitor) * 100;
141 }
142 if (outputInfo && crtcInfo) {
143 // Physical size can be unknown and reported as zero (For example over
144 // VNC, assume a "standard" DPI of 96 in that case to guess it)
145 size_t mm_width = outputInfo->mm_width > 0
146 ? outputInfo->mm_width
147 : crtcInfo->width * 25.4 / 96.0;
148 size_t mm_height = outputInfo->mm_height > 0
149 ? outputInfo->mm_height
150 : crtcInfo->height * 25.4 / 96.0;
151 DEBUG_LOG << "Monitor " << i + 1 << ":";
152 DEBUG_LOG << " Name: " << outputInfo->name;
153 DEBUG_LOG << " Connection: "
154 << (outputInfo->connection == RR_Connected
155 ? "Connected"
156 : "Disconnected/Unknown");
157 DEBUG_LOG << " Physical Size (mm): " << mm_width << " x " << mm_height;
158 DEBUG_LOG << " Resolution: " << crtcInfo->width << " x "
159 << crtcInfo->height;
160 DEBUG_LOG << " Scale: " << scale;
161 if (outputInfo->connection == RR_Connected && crtcInfo->width > 0 &&
162 crtcInfo->height > 0) {
163 g_monitor_info.push_back({outputInfo->name, mm_width, mm_height,
164 crtcInfo->width, crtcInfo->height,
165 crtcInfo->width, crtcInfo->height, scale});
166 }
167 }
168 XRRFreeOutputInfo(outputInfo);
169 XRRFreeCrtcInfo(crtcInfo);
170 }
171
172 XRRFreeScreenResources(resources);
173 XCloseDisplay(display);
174#endif
175#elif _WIN32
176 EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, 0);
177 // Get the names of the monitors
178 std::vector<DISPLAYCONFIG_PATH_INFO> paths;
179 std::vector<DISPLAYCONFIG_MODE_INFO> modes;
180 UINT32 flags = QDC_ONLY_ACTIVE_PATHS;
181 LONG isError = ERROR_INSUFFICIENT_BUFFER;
182
183 UINT32 pathCount, modeCount;
184 isError = GetDisplayConfigBufferSizes(flags, &pathCount, &modeCount);
185
186 if (!isError) {
187 // Allocate the path and mode arrays
188 paths.resize(pathCount);
189 modes.resize(modeCount);
190
191 // Get all active paths and their modes
192 isError = QueryDisplayConfig(flags, &pathCount, paths.data(), &modeCount,
193 modes.data(), nullptr);
194
195 // The function may have returned fewer paths/modes than estimated
196 paths.resize(pathCount);
197 modes.resize(modeCount);
198
199 if (!isError) {
200 // For each active path
201 for (int i = 0; i < paths.size(); i++) {
202 // Find the target (monitor) friendly name
203 DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {};
204 targetName.header.adapterId = paths[i].targetInfo.adapterId;
205 targetName.header.id = paths[i].targetInfo.id;
206 targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
207 targetName.header.size = sizeof(targetName);
208 isError = DisplayConfigGetDeviceInfo(&targetName.header);
209
210 if (!isError) {
211 if (targetName.flags.friendlyNameFromEdid) {
212 std::wstring ws(targetName.monitorFriendlyDeviceName);
213 std::wstring ws1(targetName.monitorDevicePath);
214 DEBUG_LOG << "Monitor found: " << std::string(ws.begin(), ws.end())
215 << " - " << std::string(ws1.begin(), ws1.end());
216 if (i < g_monitor_info.size()) {
217 g_monitor_info[i].name = std::string(ws.begin(), ws.end());
218 }
219 }
220 }
221 }
222 }
223 }
224#elif __APPLE__
225 CGDirectDisplayID displayArray[32];
226 uint32_t displayCount;
227 CGGetOnlineDisplayList(32, displayArray, &displayCount);
228
229 for (uint32_t i = 0; i < displayCount; ++i) {
230 CGDirectDisplayID displayID = displayArray[i];
231 CGSize displayPhysicalSize = CGDisplayScreenSize(displayID);
232 int width = CGDisplayModeGetWidth(CGDisplayCopyDisplayMode(displayID));
233 int height = CGDisplayModeGetHeight(CGDisplayCopyDisplayMode(displayID));
234 int pixel_width =
235 CGDisplayModeGetPixelWidth(CGDisplayCopyDisplayMode(displayID));
236 int pixel_height =
237 CGDisplayModeGetPixelHeight(CGDisplayCopyDisplayMode(displayID));
238 DEBUG_LOG << "Display " << i + 1 << ":";
239 DEBUG_LOG << " Physical Size: " << displayPhysicalSize.width << "x"
240 << displayPhysicalSize.height << " mm";
241 DEBUG_LOG << " Resolution: " << width << "x" << height << " pixels";
242 DEBUG_LOG << " Pixel resolution: " << pixel_width << "x" << pixel_height
243 << " pixels";
244 g_monitor_info.push_back(
245 {std::to_string(i + 1), static_cast<size_t>(displayPhysicalSize.width),
246 static_cast<size_t>(displayPhysicalSize.height),
247 static_cast<size_t>(width), static_cast<size_t>(height),
248 static_cast<size_t>(pixel_width), static_cast<size_t>(pixel_height),
249 100});
250 }
251#endif
252 if (g_monitor_info.size() == 0) {
253 // This should never happen, but just in case...
254 // If we didn't find any monitors at all, add some dummy default that makes
255 // at least some sense (15.6 inch full HD) We might also use wxDisplaySize
256 // and wxDisplaySizeMM here, but what the heck would they report?
257 g_monitor_info.push_back(
258 {"Dummy monitor", 340, 190, 1920, 1080, 1920, 1080, 100});
259 }
260 g_num_monitors = g_monitor_info.size();
261 DEBUG_LOG << "Number of monitors: " << g_num_monitors;
262 DEBUG_LOG << "Monitor info:";
263 for (const auto& monitor : g_monitor_info) {
264 DEBUG_LOG << "Monitor: " << monitor.name << " " << monitor.width_mm << "x"
265 << monitor.height_mm << "mm " << monitor.width << "x"
266 << monitor.height << "DIP " << monitor.width_px << "x"
267 << monitor.height_px << "px " << monitor.scale << "%";
268 }
269}
std::vector< OCPN_MonitorInfo > g_monitor_info
Information about the monitors connected to the system.
Definition displays.cpp:45
void EnumerateMonitors()
Enumerate the monitors connected to the system.
Definition displays.cpp:108
Display utilities.
Enhanced logging interface on top of wx/log.h.