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