FreeRDP
Loading...
Searching...
No Matches
SDL2/sdl_monitor.cpp
1
22#include <freerdp/config.h>
23
24#include <cmath>
25#include <cstdio>
26#include <cstdlib>
27#include <cstring>
28#include <algorithm>
29
30#include <SDL.h>
31
32#include <winpr/assert.h>
33#include <winpr/crt.h>
34
35#include <freerdp/log.h>
36
37#define TAG CLIENT_TAG("sdl")
38
39#include "sdl_monitor.hpp"
40#include "sdl_freerdp.hpp"
41
42using MONITOR_INFO = struct
43{
44 RECTANGLE_16 area;
45 RECTANGLE_16 workarea;
46 BOOL primary;
47};
48
49using VIRTUAL_SCREEN = struct
50{
51 int nmonitors;
52 RECTANGLE_16 area;
53 RECTANGLE_16 workarea;
54 MONITOR_INFO* monitors;
55};
56
57/* See MSDN Section on Multiple Display Monitors: http://msdn.microsoft.com/en-us/library/dd145071
58 */
59
60int sdl_list_monitors([[maybe_unused]] SdlContext* sdl)
61{
62 SDL_Init(SDL_INIT_VIDEO);
63 const int nmonitors = SDL_GetNumVideoDisplays();
64
65 printf("listing %d monitors:\n", nmonitors);
66 for (int i = 0; i < nmonitors; i++)
67 {
68 SDL_Rect rect = {};
69 const int brc = SDL_GetDisplayBounds(i, &rect);
70 const char* name = SDL_GetDisplayName(i);
71
72 if (brc != 0)
73 continue;
74 printf(" %s [%d] [%s] %dx%d\t+%d+%d\n", (i == 0) ? "*" : " ", i, name, rect.w, rect.h,
75 rect.x, rect.y);
76 }
77
78 SDL_Quit();
79 return 0;
80}
81
82static BOOL sdl_apply_max_size(SdlContext* sdl, UINT32* pMaxWidth, UINT32* pMaxHeight)
83{
84 WINPR_ASSERT(sdl);
85 WINPR_ASSERT(pMaxWidth);
86 WINPR_ASSERT(pMaxHeight);
87
88 auto settings = sdl->context()->settings;
89 WINPR_ASSERT(settings);
90
91 *pMaxWidth = 0;
92 *pMaxHeight = 0;
93
94 for (size_t x = 0; x < freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); x++)
95 {
96 auto monitor = static_cast<const rdpMonitor*>(
97 freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorDefArray, x));
98
99 if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
100 {
101 *pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->width);
102 *pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->height);
103 }
104 else if (freerdp_settings_get_bool(settings, FreeRDP_Workarea))
105 {
106 SDL_Rect rect = {};
107 SDL_GetDisplayUsableBounds(WINPR_ASSERTING_INT_CAST(int, monitor->orig_screen), &rect);
108 *pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, rect.w);
109 *pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, rect.h);
110 }
111 else if (freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen) > 0)
112 {
113 SDL_Rect rect = {};
114 SDL_GetDisplayUsableBounds(WINPR_ASSERTING_INT_CAST(int, monitor->orig_screen), &rect);
115
116 *pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, rect.w);
117 *pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, rect.h);
118
119 if (freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseWidth))
120 *pMaxWidth = (WINPR_ASSERTING_INT_CAST(uint32_t, rect.w) *
121 freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)) /
122 100;
123
124 if (freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseHeight))
125 *pMaxHeight = (WINPR_ASSERTING_INT_CAST(uint32_t, rect.h) *
126 freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)) /
127 100;
128 }
129 else if (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) &&
130 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight))
131 {
132 *pMaxWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
133 *pMaxHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
134 }
135 }
136 return TRUE;
137}
138
139#if SDL_VERSION_ATLEAST(2, 0, 10)
140static UINT32 sdl_orientaion_to_rdp(SDL_DisplayOrientation orientation)
141{
142 switch (orientation)
143 {
144 case SDL_ORIENTATION_LANDSCAPE:
145 return ORIENTATION_LANDSCAPE;
146 case SDL_ORIENTATION_LANDSCAPE_FLIPPED:
147 return ORIENTATION_LANDSCAPE_FLIPPED;
148 case SDL_ORIENTATION_PORTRAIT_FLIPPED:
149 return ORIENTATION_PORTRAIT_FLIPPED;
150 case SDL_ORIENTATION_PORTRAIT:
151 default:
152 return ORIENTATION_PORTRAIT;
153 }
154}
155#endif
156
157/* a length in pixels at the given dots per inch, in millimetres (the unit of the physical size) */
158static Uint32 to_mm(Uint32 pixels, float dpi)
159{
160 const auto inches = static_cast<float>(pixels) / dpi;
161 return static_cast<Uint32>(std::lround(inches * 25.4f));
162}
163
164static BOOL sdl_apply_display_properties(SdlContext* sdl)
165{
166 WINPR_ASSERT(sdl);
167
168 rdpSettings* settings = sdl->context()->settings;
169 WINPR_ASSERT(settings);
170
171 if (!freerdp_settings_get_bool(settings, FreeRDP_Fullscreen) &&
172 !freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
173 return TRUE;
174
175 const UINT32 numIds = freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds);
176 std::vector<rdpMonitor> monitors;
177
178 for (UINT32 x = 0; x < numIds; x++)
179 {
180 auto id = static_cast<const int*>(
181 freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorIds, x));
182 WINPR_ASSERT(id);
183
184 float ddpi = 1.0f;
185 float hdpi = 1.0f;
186 float vdpi = 1.0f;
187 SDL_Rect rect = {};
188
189 if (SDL_GetDisplayBounds(*id, &rect) < 0)
190 return FALSE;
191
192 if (SDL_GetDisplayDPI(*id, &ddpi, &hdpi, &vdpi) < 0)
193 return FALSE;
194
195 WINPR_ASSERT(rect.w > 0);
196 WINPR_ASSERT(rect.h > 0);
197 WINPR_ASSERT(ddpi > 0);
198 WINPR_ASSERT(hdpi > 0);
199 WINPR_ASSERT(vdpi > 0);
200
201 bool highDpi = hdpi > 100;
202
203 if (highDpi)
204 {
205 // HighDPI is problematic with SDL: We can only get native resolution by creating a
206 // window. Work around this by checking the supported resolutions (and keep maximum)
207 // Also scale the DPI
208 const SDL_Rect scaleRect = rect;
209 for (int i = 0; i < SDL_GetNumDisplayModes(*id); i++)
210 {
211 SDL_DisplayMode mode = {};
212 SDL_GetDisplayMode(WINPR_ASSERTING_INT_CAST(int, x), i, &mode);
213
214 if (mode.w > rect.w)
215 {
216 rect.w = mode.w;
217 rect.h = mode.h;
218 }
219 else if (mode.w == rect.w)
220 {
221 if (mode.h > rect.h)
222 {
223 rect.w = mode.w;
224 rect.h = mode.h;
225 }
226 }
227 }
228
229 const float dw = 1.0f * static_cast<float>(rect.w) / static_cast<float>(scaleRect.w);
230 const float dh = 1.0f * static_cast<float>(rect.h) / static_cast<float>(scaleRect.h);
231 hdpi /= dw;
232 vdpi /= dh;
233 }
234
235#if SDL_VERSION_ATLEAST(2, 0, 10)
236 const SDL_DisplayOrientation orientation = SDL_GetDisplayOrientation(*id);
237 const UINT32 rdp_orientation = sdl_orientaion_to_rdp(orientation);
238#else
239 const UINT32 rdp_orientation = ORIENTATION_LANDSCAPE;
240#endif
241
242 rdpMonitor monitor = {};
243
244 /* windows uses 96 dpi as 'default' and the scale factors are in percent. */
245 const auto factor = ddpi / 96.0f * 100.0f;
246 monitor.orig_screen = x;
247 monitor.x = rect.x;
248 monitor.y = rect.y;
249 monitor.width = rect.w;
250 monitor.height = rect.h;
251 monitor.is_primary = x == 0;
252 monitor.attributes.desktopScaleFactor = static_cast<UINT32>(factor);
253 monitor.attributes.deviceScaleFactor = 100;
254 monitor.attributes.orientation = rdp_orientation;
255 monitor.attributes.physicalWidth = to_mm(WINPR_ASSERTING_INT_CAST(uint32_t, rect.w), hdpi);
256 monitor.attributes.physicalHeight = to_mm(WINPR_ASSERTING_INT_CAST(uint32_t, rect.h), vdpi);
257 monitors.emplace_back(monitor);
258 }
259 return freerdp_settings_set_monitor_def_array_sorted(settings, monitors.data(),
260 monitors.size());
261}
262
263static BOOL sdl_detect_single_window(SdlContext* sdl, UINT32* pMaxWidth, UINT32* pMaxHeight)
264{
265 WINPR_ASSERT(sdl);
266 WINPR_ASSERT(pMaxWidth);
267 WINPR_ASSERT(pMaxHeight);
268
269 rdpSettings* settings = sdl->context()->settings;
270 WINPR_ASSERT(settings);
271
272 if ((!freerdp_settings_get_bool(settings, FreeRDP_UseMultimon) &&
273 !freerdp_settings_get_bool(settings, FreeRDP_SpanMonitors)) ||
274 (freerdp_settings_get_bool(settings, FreeRDP_Workarea) &&
275 !freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode)))
276 {
277 /* If no monitors were specified on the command-line then set the current monitor as active
278 */
279 if (freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds) == 0)
280 {
281 const size_t id = (!sdl->windows.empty())
282 ? WINPR_ASSERTING_INT_CAST(
283 uint32_t, sdl->windows.begin()->second.displayIndex())
284 : 0;
285 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_MonitorIds, &id, 1))
286 return FALSE;
287 }
288 else
289 {
290
291 /* Always sets number of monitors from command-line to just 1.
292 * If the monitor is invalid then we will default back to current monitor
293 * later as a fallback. So, there is no need to validate command-line entry here.
294 */
295 if (!freerdp_settings_set_uint32(settings, FreeRDP_NumMonitorIds, 1))
296 return FALSE;
297 }
298
299 // TODO: Fill monitor struct
300 if (!sdl_apply_display_properties(sdl))
301 return FALSE;
302 return sdl_apply_max_size(sdl, pMaxWidth, pMaxHeight);
303 }
304 return TRUE;
305}
306
307BOOL sdl_detect_monitors(SdlContext* sdl, UINT32* pMaxWidth, UINT32* pMaxHeight)
308{
309 WINPR_ASSERT(sdl);
310 WINPR_ASSERT(pMaxWidth);
311 WINPR_ASSERT(pMaxHeight);
312
313 rdpSettings* settings = sdl->context()->settings;
314 WINPR_ASSERT(settings);
315
316 const int numDisplays = SDL_GetNumVideoDisplays();
317 auto nr = freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds);
318 if (numDisplays < 0)
319 return FALSE;
320
321 if (nr == 0)
322 {
323 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_MonitorIds, nullptr,
324 static_cast<size_t>(numDisplays)))
325 return FALSE;
326 for (size_t x = 0; x < static_cast<size_t>(numDisplays); x++)
327 {
328 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_MonitorIds, x, &x))
329 return FALSE;
330 }
331 }
332 else
333 {
334
335 /* There were more IDs supplied than there are monitors */
336 if (nr > static_cast<UINT32>(numDisplays))
337 {
338 WLog_ERR(TAG, "Found %" PRIu32 " monitor IDs, but only have %d monitors connected", nr,
339 numDisplays);
340 return FALSE;
341 }
342
343 std::vector<UINT32> used;
344 for (size_t x = 0; x < nr; x++)
345 {
346 auto cur = static_cast<const UINT32*>(
347 freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorIds, x));
348 WINPR_ASSERT(cur);
349
350 auto id = *cur;
351
352 /* the ID is no valid monitor index */
353 if (id >= nr)
354 {
355 WLog_ERR(TAG,
356 "Supplied monitor ID[%" PRIuz "]=%" PRIu32 " is invalid, only [0-%" PRIu32
357 "] are allowed",
358 x, id, nr - 1);
359 return FALSE;
360 }
361
362 /* The ID is already taken */
363 if (std::find(used.begin(), used.end(), id) != used.end())
364 {
365 WLog_ERR(TAG, "Duplicate monitor ID[%" PRIuz "]=%" PRIu32 " detected", x, id);
366 return FALSE;
367 }
368 used.push_back(*cur);
369 }
370 }
371
372 if (!sdl_apply_display_properties(sdl))
373 return FALSE;
374
375 return sdl_detect_single_window(sdl, pMaxWidth, pMaxHeight);
376}
377
378INT64 sdl_monitor_id_for_index(SdlContext* sdl, UINT32 index)
379{
380 WINPR_ASSERT(sdl);
381 auto settings = sdl->context()->settings;
382
383 auto nr = freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds);
384 if (nr == 0)
385 return index;
386
387 if (nr <= index)
388 return -1;
389
390 auto cur = static_cast<const UINT32*>(
391 freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorIds, index));
392 WINPR_ASSERT(cur);
393 return *cur;
394}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_pointer_len(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id, const void *data, size_t len)
Set a pointer to value data.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_monitor_def_array_sorted(rdpSettings *settings, const rdpMonitor *monitors, size_t count)
Sort monitor array according to:
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.