FreeRDP
Loading...
Searching...
No Matches
SDL3/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 <SDL3/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
41typedef struct
42{
43 RECTANGLE_16 area;
44 RECTANGLE_16 workarea;
45 BOOL primary;
46} MONITOR_INFO;
47
48typedef struct
49{
50 int nmonitors;
51 RECTANGLE_16 area;
52 RECTANGLE_16 workarea;
53 MONITOR_INFO* monitors;
54} VIRTUAL_SCREEN;
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
63 int nmonitors = 0;
64 auto ids = SDL_GetDisplays(&nmonitors);
65
66 printf("listing %d monitors:\n", nmonitors);
67 for (int i = 0; i < nmonitors; i++)
68 {
69 SDL_Rect rect = {};
70 auto id = ids[i];
71 const auto brc = SDL_GetDisplayBounds(id, &rect);
72 const char* name = SDL_GetDisplayName(id);
73
74 if (!brc)
75 continue;
76 printf(" %s [%u] [%s] %dx%d\t+%d+%d\n", (i == 0) ? "*" : " ", id, name, rect.w, rect.h,
77 rect.x, rect.y);
78 }
79 SDL_free(ids);
80 SDL_Quit();
81 return 0;
82}
83
84static BOOL sdl_apply_max_size(SdlContext* sdl, UINT32* pMaxWidth, UINT32* pMaxHeight)
85{
86 WINPR_ASSERT(sdl);
87 WINPR_ASSERT(pMaxWidth);
88 WINPR_ASSERT(pMaxHeight);
89
90 auto settings = sdl->context()->settings;
91 WINPR_ASSERT(settings);
92
93 *pMaxWidth = 0;
94 *pMaxHeight = 0;
95
96 for (size_t x = 0; x < freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); x++)
97 {
98 auto monitor = static_cast<const rdpMonitor*>(
99 freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorDefArray, x));
100
101 if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
102 {
103 *pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->width);
104 *pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->height);
105 }
106 else if (freerdp_settings_get_bool(settings, FreeRDP_Workarea))
107 {
108 SDL_Rect rect = {};
109 SDL_GetDisplayUsableBounds(monitor->orig_screen, &rect);
110 *pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, rect.w);
111 *pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, rect.h);
112 }
113 else if (freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen) > 0)
114 {
115 SDL_Rect rect = {};
116 SDL_GetDisplayUsableBounds(monitor->orig_screen, &rect);
117
118 *pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, rect.w);
119 *pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, rect.h);
120
121 if (freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseWidth))
122 *pMaxWidth = (WINPR_ASSERTING_INT_CAST(uint32_t, rect.w) *
123 freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)) /
124 100;
125
126 if (freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseHeight))
127 *pMaxHeight = (WINPR_ASSERTING_INT_CAST(uint32_t, rect.h) *
128 freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)) /
129 100;
130 }
131 else if (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) &&
132 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight))
133 {
134 *pMaxWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
135 *pMaxHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
136 }
137 }
138 return TRUE;
139}
140
141static Uint32 scale(Uint32 val, float scale)
142{
143 const auto dval = static_cast<float>(val);
144 const auto sval = dval / scale;
145 return static_cast<Uint32>(sval);
146}
147
148static BOOL sdl_apply_display_properties(SdlContext* sdl)
149{
150 WINPR_ASSERT(sdl);
151
152 rdpSettings* settings = sdl->context()->settings;
153 WINPR_ASSERT(settings);
154 if (!freerdp_settings_get_bool(settings, FreeRDP_Fullscreen) &&
155 !freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
156 return TRUE;
157
158 std::vector<rdpMonitor> monitors;
159 const auto& ids = sdl->monitorIds();
160
161 for (UINT32 x = 0; x < ids.size(); x++)
162 {
163 auto id = ids[x];
164
165 float dpi = SDL_GetDisplayContentScale(id);
166 float hdpi = dpi;
167 float vdpi = dpi;
168 SDL_Rect rect = {};
169
170 if (!SDL_GetDisplayBounds(id, &rect))
171 return FALSE;
172
173 WINPR_ASSERT(rect.w > 0);
174 WINPR_ASSERT(rect.h > 0);
175
176 bool highDpi = dpi > 100;
177
178 if (highDpi)
179 {
180 // HighDPI is problematic with SDL: We can only get native resolution by creating a
181 // window. Work around this by checking the supported resolutions (and keep maximum)
182 // Also scale the DPI
183 const SDL_Rect scaleRect = rect;
184 int count = 0;
185 auto modes = SDL_GetFullscreenDisplayModes(x, &count);
186 for (int i = 0; i < count; i++)
187 {
188 auto mode = modes[i];
189 if (!mode)
190 break;
191
192 if (mode->w > rect.w)
193 {
194 rect.w = mode->w;
195 rect.h = mode->h;
196 }
197 else if (mode->w == rect.w)
198 {
199 if (mode->h > rect.h)
200 {
201 rect.w = mode->w;
202 rect.h = mode->h;
203 }
204 }
205 }
206 SDL_free(static_cast<void*>(modes));
207
208 const float dw = 1.0f * static_cast<float>(rect.w) / static_cast<float>(scaleRect.w);
209 const float dh = 1.0f * static_cast<float>(rect.h) / static_cast<float>(scaleRect.h);
210 hdpi /= dw;
211 vdpi /= dh;
212 }
213
214 const SDL_DisplayOrientation orientation = SDL_GetCurrentDisplayOrientation(id);
215 const UINT32 rdp_orientation = sdl::utils::orientaion_to_rdp(orientation);
216
217 rdpMonitor monitor = {};
218
219 /* windows uses 96 dpi as 'default' and the scale factors are in percent. */
220 const auto factor = dpi / 96.0f * 100.0f;
221 monitor.orig_screen = x;
222 monitor.x = rect.x;
223 monitor.y = rect.y;
224 monitor.width = rect.w;
225 monitor.height = rect.h;
226 monitor.is_primary = x == 0;
227 monitor.attributes.desktopScaleFactor = static_cast<UINT32>(factor);
228 monitor.attributes.deviceScaleFactor = 100;
229 monitor.attributes.orientation = rdp_orientation;
230 monitor.attributes.physicalWidth = scale(WINPR_ASSERTING_INT_CAST(uint32_t, rect.w), hdpi);
231 monitor.attributes.physicalHeight = scale(WINPR_ASSERTING_INT_CAST(uint32_t, rect.h), vdpi);
232 monitors.emplace_back(monitor);
233 }
234 return freerdp_settings_set_monitor_def_array_sorted(settings, monitors.data(),
235 monitors.size());
236}
237
238static BOOL sdl_detect_single_window(SdlContext* sdl, UINT32* pMaxWidth, UINT32* pMaxHeight)
239{
240 WINPR_ASSERT(sdl);
241 WINPR_ASSERT(pMaxWidth);
242 WINPR_ASSERT(pMaxHeight);
243
244 rdpSettings* settings = sdl->context()->settings;
245 WINPR_ASSERT(settings);
246
247 if ((!freerdp_settings_get_bool(settings, FreeRDP_UseMultimon) &&
248 !freerdp_settings_get_bool(settings, FreeRDP_SpanMonitors)) ||
249 (freerdp_settings_get_bool(settings, FreeRDP_Workarea) &&
250 !freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode)))
251 {
252 /* If no monitors were specified on the command-line then set the current monitor as active
253 */
254 if (freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds) == 0)
255 {
256 SDL_DisplayID id = 0;
257 const auto& ids = sdl->monitorIds();
258 if (!ids.empty())
259 {
260 id = ids.front();
261 }
262 sdl->setMonitorIds({ id });
263 }
264
265 // TODO: Fill monitor struct
266 if (!sdl_apply_display_properties(sdl))
267 return FALSE;
268 return sdl_apply_max_size(sdl, pMaxWidth, pMaxHeight);
269 }
270 return TRUE;
271}
272
273BOOL sdl_detect_monitors(SdlContext* sdl, UINT32* pMaxWidth, UINT32* pMaxHeight)
274{
275 WINPR_ASSERT(sdl);
276 WINPR_ASSERT(pMaxWidth);
277 WINPR_ASSERT(pMaxHeight);
278
279 rdpSettings* settings = sdl->context()->settings;
280 WINPR_ASSERT(settings);
281
282 std::vector<SDL_DisplayID> ids;
283 {
284 int numDisplays = 0;
285 auto sids = SDL_GetDisplays(&numDisplays);
286 if (sids && (numDisplays > 0))
287 ids = std::vector<SDL_DisplayID>(sids, sids + numDisplays);
288 SDL_free(sids);
289 if (numDisplays < 0)
290 return FALSE;
291 }
292
293 auto nr = freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds);
294 if (nr == 0)
295 {
296 if (freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
297 sdl->setMonitorIds(ids);
298 else
299 {
300 sdl->setMonitorIds({ ids.front() });
301 }
302 }
303 else
304 {
305 /* There were more IDs supplied than there are monitors */
306 if (nr > ids.size())
307 {
308 WLog_ERR(TAG,
309 "Found %" PRIu32 " monitor IDs, but only have %" PRIuz " monitors connected",
310 nr, ids.size());
311 return FALSE;
312 }
313
314 std::vector<SDL_DisplayID> used;
315 for (size_t x = 0; x < nr; x++)
316 {
317 auto cur = static_cast<const UINT32*>(
318 freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorIds, x));
319 WINPR_ASSERT(cur);
320
321 SDL_DisplayID id = *cur;
322
323 /* the ID is no valid monitor index */
324 if (std::find(ids.begin(), ids.end(), id) == ids.end())
325 {
326 WLog_ERR(TAG, "Supplied monitor ID[%" PRIuz "]=%" PRIu32 " is invalid", x, id);
327 return FALSE;
328 }
329
330 /* The ID is already taken */
331 if (std::find(used.begin(), used.end(), id) != used.end())
332 {
333 WLog_ERR(TAG, "Duplicate monitor ID[%" PRIuz "]=%" PRIu32 " detected", x, id);
334 return FALSE;
335 }
336 used.push_back(id);
337 }
338 sdl->setMonitorIds(used);
339 }
340
341 if (!sdl_apply_display_properties(sdl))
342 return FALSE;
343
344 auto size = static_cast<uint32_t>(sdl->monitorIds().size());
345 if (!freerdp_settings_set_uint32(settings, FreeRDP_NumMonitorIds, size))
346 return FALSE;
347
348 return sdl_detect_single_window(sdl, pMaxWidth, pMaxHeight);
349}
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_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.