FreeRDP
Loading...
Searching...
No Matches
SDL3/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 <SDL3/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_context.hpp"
41
42typedef struct
43{
44 RECTANGLE_16 area;
45 RECTANGLE_16 workarea;
46 BOOL primary;
47} MONITOR_INFO;
48
49typedef struct
50{
51 int nmonitors;
52 RECTANGLE_16 area;
53 RECTANGLE_16 workarea;
54 MONITOR_INFO* monitors;
55} VIRTUAL_SCREEN;
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
64 int nmonitors = 0;
65 auto ids = SDL_GetDisplays(&nmonitors);
66
67 printf("listing %d monitors:\n", nmonitors);
68 for (int i = 0; i < nmonitors; i++)
69 {
70 SDL_Rect rect = {};
71 auto id = ids[i];
72 const auto brc = SDL_GetDisplayBounds(id, &rect);
73 const char* name = SDL_GetDisplayName(id);
74
75 if (!brc)
76 continue;
77 printf(" %s [%u] [%s] %dx%d\t+%d+%d\n", (i == 0) ? "*" : " ", id, name, rect.w, rect.h,
78 rect.x, rect.y);
79 }
80 SDL_free(ids);
81 SDL_Quit();
82 return 0;
83}
84
85[[nodiscard]] static BOOL sdl_apply_mon_max_size(SdlContext* sdl, UINT32* pMaxWidth,
86 UINT32* pMaxHeight)
87{
88 int32_t left = 0;
89 int32_t right = 0;
90 int32_t top = 0;
91 int32_t bottom = 0;
92
93 WINPR_ASSERT(pMaxWidth);
94 WINPR_ASSERT(pMaxHeight);
95
96 auto settings = sdl->context()->settings;
97 WINPR_ASSERT(settings);
98
99 for (size_t x = 0; x < freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); x++)
100 {
101 auto monitor = static_cast<const rdpMonitor*>(
102 freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorDefArray, x));
103
104 left = std::min(monitor->x, left);
105 top = std::min(monitor->y, top);
106 right = std::max(monitor->x + monitor->width, right);
107 bottom = std::max(monitor->y + monitor->height, bottom);
108 }
109
110 const int32_t w = right - left;
111 const int32_t h = bottom - top;
112
113 *pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, w);
114 *pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, h);
115 return TRUE;
116}
117
118[[nodiscard]] static BOOL sdl_apply_max_size(SdlContext* sdl, UINT32* pMaxWidth, UINT32* pMaxHeight)
119{
120 WINPR_ASSERT(sdl);
121 WINPR_ASSERT(pMaxWidth);
122 WINPR_ASSERT(pMaxHeight);
123
124 auto settings = sdl->context()->settings;
125 WINPR_ASSERT(settings);
126
127 *pMaxWidth = 0;
128 *pMaxHeight = 0;
129
130 for (size_t x = 0; x < freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); x++)
131 {
132 auto monitor = static_cast<const rdpMonitor*>(
133 freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorDefArray, x));
134
135 if (freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode))
136 {
137 /* RAIL requires full display coverage for 1:1 coordinates. Work area insets
138 * are reported via SPI_SET_WORK_AREA instead of clipping session size. */
139 SDL_Rect rect = {};
140 if (SDL_GetDisplayBounds(monitor->orig_screen, &rect))
141 {
142 *pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, rect.w);
143 *pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, rect.h);
144 }
145 }
146 else if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
147 {
148 *pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->width);
149 *pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->height);
150 }
151 else if (freerdp_settings_get_bool(settings, FreeRDP_Workarea))
152 {
153 SDL_Rect rect = {};
154 SDL_GetDisplayUsableBounds(monitor->orig_screen, &rect);
155 *pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, rect.w);
156 *pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, rect.h);
157 }
158 else if (freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen) > 0)
159 {
160 SDL_Rect rect = {};
161 SDL_GetDisplayUsableBounds(monitor->orig_screen, &rect);
162
163 *pMaxWidth = WINPR_ASSERTING_INT_CAST(uint32_t, rect.w);
164 *pMaxHeight = WINPR_ASSERTING_INT_CAST(uint32_t, rect.h);
165
166 if (freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseWidth))
167 *pMaxWidth = (WINPR_ASSERTING_INT_CAST(uint32_t, rect.w) *
168 freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)) /
169 100;
170
171 if (freerdp_settings_get_bool(settings, FreeRDP_PercentScreenUseHeight))
172 *pMaxHeight = (WINPR_ASSERTING_INT_CAST(uint32_t, rect.h) *
173 freerdp_settings_get_uint32(settings, FreeRDP_PercentScreen)) /
174 100;
175 }
176 else if (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) &&
177 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight))
178 {
179 *pMaxWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
180 *pMaxHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
181 }
182 }
183 return TRUE;
184}
185
186[[nodiscard]] static BOOL sdl_apply_display_properties(SdlContext* sdl)
187{
188 WINPR_ASSERT(sdl);
189
190 rdpSettings* settings = sdl->context()->settings;
191 WINPR_ASSERT(settings);
192
193 std::vector<rdpMonitor> monitors;
194 if (!freerdp_settings_get_bool(settings, FreeRDP_Fullscreen) &&
195 !freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
196 {
197 if (freerdp_settings_get_bool(settings, FreeRDP_Workarea))
198 {
199 if (sdl->monitorIds().empty())
200 return FALSE;
201 const auto id = sdl->monitorIds().front();
202 auto monitor = sdl->getDisplay(id);
203 monitor.is_primary = true;
204 monitor.x = 0;
205 monitor.y = 0;
206 monitors.emplace_back(monitor);
207 return freerdp_settings_set_monitor_def_array_sorted(settings, monitors.data(),
208 monitors.size());
209 }
210 return TRUE;
211 }
212 for (const auto& id : sdl->monitorIds())
213 {
214 const auto monitor = sdl->getDisplay(id);
215 monitors.emplace_back(monitor);
216 }
217 // With an explicit /monitors: list the first entry is the primary (like xfreerdp),
218 // not whatever the local desktop considers primary.
219 if (!monitors.empty() && (freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds) > 0))
220 {
221 for (auto& monitor : monitors)
222 monitor.is_primary = FALSE;
223 monitors.at(0).is_primary = TRUE;
224 }
225 // /monitors: may select a subset that excludes the SDL primary. The
226 // library rejects arrays without a primary, so promote the first entry
227 // when none is marked.
228 if (!monitors.empty() && std::none_of(monitors.cbegin(), monitors.cend(),
229 [](const rdpMonitor& m) { return m.is_primary; }))
230 monitors.at(0).is_primary = true;
231 return freerdp_settings_set_monitor_def_array_sorted(settings, monitors.data(),
232 monitors.size());
233}
234
235[[nodiscard]] static BOOL sdl_detect_single_window(SdlContext* sdl, UINT32* pMaxWidth,
236 UINT32* pMaxHeight)
237{
238 WINPR_ASSERT(sdl);
239 WINPR_ASSERT(pMaxWidth);
240 WINPR_ASSERT(pMaxHeight);
241
242 rdpSettings* settings = sdl->context()->settings;
243 WINPR_ASSERT(settings);
244
245 if ((!freerdp_settings_get_bool(settings, FreeRDP_UseMultimon) &&
246 !freerdp_settings_get_bool(settings, FreeRDP_SpanMonitors)) ||
247 (freerdp_settings_get_bool(settings, FreeRDP_Workarea) &&
248 !freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode)))
249 {
250 /* If no monitors were specified on the command-line then set the current monitor as active
251 */
252 if (freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds) == 0)
253 {
254 SDL_DisplayID id = 0;
255 const auto& ids = sdl->monitorIds();
256 if (!ids.empty())
257 {
258 id = ids.front();
259 }
260 sdl->setMonitorIds({ id });
261 }
262
263 if (!sdl_apply_display_properties(sdl))
264 return FALSE;
265 return sdl_apply_max_size(sdl, pMaxWidth, pMaxHeight);
266 }
267 return sdl_apply_mon_max_size(sdl, pMaxWidth, pMaxHeight);
268}
269
270BOOL sdl_detect_monitors(SdlContext* sdl, UINT32* pMaxWidth, UINT32* pMaxHeight)
271{
272 WINPR_ASSERT(sdl);
273 WINPR_ASSERT(pMaxWidth);
274 WINPR_ASSERT(pMaxHeight);
275
276 rdpSettings* settings = sdl->context()->settings;
277 WINPR_ASSERT(settings);
278
279 const auto& ids = sdl->getDisplayIds();
280 auto nr = freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds);
281 /* Announce all monitors in RemoteApp mode so windows can maximize on secondary displays. */
282 if ((nr == 0) && freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode) &&
283 !freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
284 {
285 if (!freerdp_settings_set_bool(settings, FreeRDP_UseMultimon, TRUE))
286 return FALSE;
287 }
288 if (nr == 0)
289 {
290 if (freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
291 sdl->setMonitorIds(ids);
292 else
293 {
294 sdl->setMonitorIds({ ids.front() });
295 }
296 }
297 else
298 {
299 /* There were more IDs supplied than there are monitors */
300 if (nr > ids.size())
301 {
302 WLog_ERR(TAG,
303 "Found %" PRIu32 " monitor IDs, but only have %" PRIuz " monitors connected",
304 nr, ids.size());
305 return FALSE;
306 }
307
308 std::vector<SDL_DisplayID> used;
309 for (size_t x = 0; x < nr; x++)
310 {
311 auto cur = static_cast<const UINT32*>(
312 freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorIds, x));
313 WINPR_ASSERT(cur);
314
315 SDL_DisplayID id = *cur;
316
317 /* the ID is no valid monitor index */
318 if (std::find(ids.begin(), ids.end(), id) == ids.end())
319 {
320 WLog_ERR(TAG, "Supplied monitor ID[%" PRIuz "]=%" PRIu32 " is invalid", x, id);
321 return FALSE;
322 }
323
324 /* The ID is already taken */
325 if (std::find(used.begin(), used.end(), id) != used.end())
326 {
327 WLog_ERR(TAG, "Duplicate monitor ID[%" PRIuz "]=%" PRIu32 " detected", x, id);
328 return FALSE;
329 }
330 used.push_back(id);
331 }
332 sdl->setMonitorIds(used);
333 }
334
335 if (!sdl_apply_display_properties(sdl))
336 return FALSE;
337
338 auto size = static_cast<uint32_t>(sdl->monitorIds().size());
339 if (!freerdp_settings_set_uint32(settings, FreeRDP_NumMonitorIds, size))
340 return FALSE;
341
342 return sdl_detect_single_window(sdl, pMaxWidth, pMaxHeight);
343}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
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 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.