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