FreeRDP
Loading...
Searching...
No Matches
SDL3/dialogs/sdl_connection_dialog.cpp
1
19#include <cassert>
20#include <thread>
21
22#include "sdl_connection_dialog.hpp"
23#include "../sdl_utils.hpp"
24#include "../sdl_context.hpp"
25#include "res/sdl3_resource_manager.hpp"
26
27static const SDL_Color textcolor = { 0xd1, 0xcf, 0xcd, 0xff };
28static const SDL_Color infocolor = { 0x43, 0xe0, 0x0f, 0x60 };
29static const SDL_Color warncolor = { 0xcd, 0xca, 0x35, 0x60 };
30static const SDL_Color errorcolor = { 0xf7, 0x22, 0x30, 0x60 };
31
32static const Uint32 vpadding = 5;
33static const Uint32 hpadding = 5;
34
35SDLConnectionDialog::SDLConnectionDialog(rdpContext* context) : _context(context)
36{
37 /* Nothing to hide yet: there is no window and no timer. Off the main thread hide()
38 * would only queue a discard event that can arrive after the first message was shown
39 * and tear it down again. */
40}
41
42SDLConnectionDialog::~SDLConnectionDialog()
43{
44 resetTimer();
45 destroyWindow();
46}
47
48bool SDLConnectionDialog::setTitle(const char* fmt, ...)
49{
50 std::scoped_lock lock(_mux);
51 va_list ap = {};
52 va_start(ap, fmt);
53 _title = print(fmt, ap);
54 va_end(ap);
55
56 return show(SdlConnectionDialogWrapper::MSG_NONE);
57}
58
59bool SDLConnectionDialog::showInfo(const char* fmt, ...)
60{
61 va_list ap = {};
62 va_start(ap, fmt);
63 auto rc = show(SdlConnectionDialogWrapper::MSG_INFO, fmt, ap);
64 va_end(ap);
65 return rc;
66}
67
68bool SDLConnectionDialog::showWarn(const char* fmt, ...)
69{
70 va_list ap = {};
71 va_start(ap, fmt);
72 auto rc = show(SdlConnectionDialogWrapper::MSG_WARN, fmt, ap);
73 va_end(ap);
74 return rc;
75}
76
77bool SDLConnectionDialog::showError(const char* fmt, ...)
78{
79 va_list ap = {};
80 va_start(ap, fmt);
81 auto rc = show(SdlConnectionDialogWrapper::MSG_ERROR, fmt, ap);
82 va_end(ap);
83 if (!rc)
84 return rc;
85 return setTimer();
86}
87
88bool SDLConnectionDialog::show()
89{
90 std::scoped_lock lock(_mux);
91 return show(_type_active);
92}
93
94bool SDLConnectionDialog::hide()
95{
96 std::scoped_lock lock(_mux);
97 return show(SdlConnectionDialogWrapper::MSG_DISCARD);
98}
99
100bool SDLConnectionDialog::running() const
101{
102 std::scoped_lock lock(_mux);
103 return _running;
104}
105
106bool SDLConnectionDialog::updateMsg(SdlConnectionDialogWrapper::MsgType type)
107{
108 switch (type)
109 {
110 case SdlConnectionDialogWrapper::MSG_INFO:
111 case SdlConnectionDialogWrapper::MSG_WARN:
112 case SdlConnectionDialogWrapper::MSG_ERROR:
113 _type_active = type;
114 if (!createWindow())
115 return false;
116 break;
117 case SdlConnectionDialogWrapper::MSG_DISCARD:
118 resetTimer();
119 destroyWindow();
120 break;
121 default:
122 if (_window)
123 {
124 SDL_SetWindowTitle(_window.get(), _title.c_str());
125 }
126 break;
127 }
128 return true;
129}
130
131bool SDLConnectionDialog::setModal()
132{
133 if (_window)
134 {
135 auto sdl = get_context(_context);
136 auto parent = sdl->getFirstWindow();
137 if (!parent)
138 return true;
139
140 if (!SDL_SetWindowParent(_window.get(), parent->window()))
141 return false;
142 if (!SDL_SetWindowModal(_window.get(), true))
143 return false;
144 if (!SDL_RaiseWindow(_window.get()))
145 return false;
146 }
147 return true;
148}
149
150bool SDLConnectionDialog::updateInternal()
151{
152 std::scoped_lock lock(_mux);
153 for (auto& btn : _list)
154 {
155 if (!btn.widget.update_text(_msg))
156 return false;
157 }
158
159 return true;
160}
161
162bool SDLConnectionDialog::wait(bool ignoreRdpContext)
163{
164 while (running())
165 {
166 if (!ignoreRdpContext)
167 {
168 if (freerdp_shall_disconnect_context(_context))
169 return false;
170 }
171 std::this_thread::yield();
172 }
173 return true;
174}
175
176bool SDLConnectionDialog::handle(const SDL_Event& event)
177{
178 Uint32 windowID = 0;
179 if (_window)
180 {
181 windowID = SDL_GetWindowID(_window.get());
182 }
183
184 switch (event.type)
185 {
186 case SDL_EVENT_USER_RETRY_DIALOG:
187 {
188 std::scoped_lock lock(_mux);
189 auto type = static_cast<SdlConnectionDialogWrapper::MsgType>(event.user.code);
190 return updateMsg(type);
191 }
192 case SDL_EVENT_QUIT:
193 resetTimer();
194 destroyWindow();
195 return false;
196 case SDL_EVENT_KEY_DOWN:
197 case SDL_EVENT_KEY_UP:
198 if (visible())
199 {
200 auto& ev = reinterpret_cast<const SDL_KeyboardEvent&>(event);
201 if (!update())
202 return false;
203 switch (event.key.key)
204 {
205 case SDLK_RETURN:
206 case SDLK_RETURN2:
207 case SDLK_ESCAPE:
208 case SDLK_KP_ENTER:
209 if (event.type == SDL_EVENT_KEY_UP)
210 {
211 freerdp_abort_connect_context(_context);
212 std::ignore = sdl_push_quit();
213 }
214 break;
215 case SDLK_TAB:
216 if (!_buttons.set_highlight_next())
217 return false;
218 break;
219 default:
220 break;
221 }
222
223 return windowID == ev.windowID;
224 }
225 return false;
226 case SDL_EVENT_MOUSE_MOTION:
227 if (visible())
228 {
229 auto& ev = reinterpret_cast<const SDL_MouseMotionEvent&>(event);
230
231 _buttons.set_mouseover(event.button.x, event.button.y);
232 if (!update())
233 return false;
234 return windowID == ev.windowID;
235 }
236 return false;
237 case SDL_EVENT_MOUSE_BUTTON_DOWN:
238 case SDL_EVENT_MOUSE_BUTTON_UP:
239 if (visible())
240 {
241 auto& ev = reinterpret_cast<const SDL_MouseButtonEvent&>(event);
242 if (!update())
243 return false;
244
245 auto button = _buttons.get_selected(event.button);
246 if (button)
247 {
248 if (event.type == SDL_EVENT_MOUSE_BUTTON_UP)
249 {
250 freerdp_abort_connect_context(_context);
251 std::ignore = sdl_push_quit();
252 }
253 }
254
255 return windowID == ev.windowID;
256 }
257 return false;
258 case SDL_EVENT_MOUSE_WHEEL:
259 if (visible())
260 {
261 auto& ev = reinterpret_cast<const SDL_MouseWheelEvent&>(event);
262 if (!update())
263 return false;
264 return windowID == ev.windowID;
265 }
266 return false;
267 case SDL_EVENT_FINGER_UP:
268 case SDL_EVENT_FINGER_DOWN:
269 if (visible())
270 {
271 auto& ev = reinterpret_cast<const SDL_TouchFingerEvent&>(event);
272 if (!update())
273 return false;
274 return windowID == ev.windowID;
275 }
276 return false;
277 default:
278 if ((event.type >= SDL_EVENT_WINDOW_FIRST) && (event.type <= SDL_EVENT_WINDOW_LAST))
279 {
280 auto& ev = reinterpret_cast<const SDL_WindowEvent&>(event);
281 switch (ev.type)
282 {
283 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
284 if (windowID == ev.windowID)
285 {
286 freerdp_abort_connect_context(_context);
287 std::ignore = sdl_push_quit();
288 }
289 break;
290 default:
291 if (!update())
292 return false;
293 if (!setModal())
294 return false;
295 break;
296 }
297
298 return windowID == ev.windowID;
299 }
300 return false;
301 }
302}
303
304bool SDLConnectionDialog::visible() const
305{
306 std::scoped_lock lock(_mux);
307 return SdlWidgetList::visible();
308}
309
310bool SDLConnectionDialog::createWindow()
311{
312 destroyWindow();
313
314 const size_t widget_height = 50;
315 const size_t widget_width = 600;
316 const size_t total_height = 300;
317
318 if (!reset(_title, widget_width, total_height))
319 return false;
320
321 if (!setModal())
322 return false;
323
324 SDL_Color res_bgcolor;
325 switch (_type_active)
326 {
327 case SdlConnectionDialogWrapper::MSG_INFO:
328 res_bgcolor = infocolor;
329 break;
330 case SdlConnectionDialogWrapper::MSG_WARN:
331 res_bgcolor = warncolor;
332 break;
333 case SdlConnectionDialogWrapper::MSG_ERROR:
334 res_bgcolor = errorcolor;
335 break;
336 case SdlConnectionDialogWrapper::MSG_DISCARD:
337 default:
338 res_bgcolor = _backgroundcolor;
339 break;
340 }
341
342#if defined(WITH_SDL_IMAGE_DIALOGS)
343 std::string res_name;
344 switch (_type_active)
345 {
346 case SdlConnectionDialogWrapper::MSG_INFO:
347 res_name = "icon_info.svg";
348 break;
349 case SdlConnectionDialogWrapper::MSG_WARN:
350 res_name = "icon_warning.svg";
351 break;
352 case SdlConnectionDialogWrapper::MSG_ERROR:
353 res_name = "icon_error.svg";
354 break;
355 case SdlConnectionDialogWrapper::MSG_DISCARD:
356 default:
357 res_name = "";
358 break;
359 }
360
361 const auto height = (total_height - 3.0f * vpadding) / 2.0f;
362 SDL_FRect iconRect{ hpadding, vpadding, widget_width / 4.0f - 2.0f * hpadding, height };
363 widget_cfg_t icon{ textcolor,
364 res_bgcolor,
365 { _renderer, iconRect,
366 SDL3ResourceManager::get(SDLResourceManager::typeImages(), res_name) } };
367 _list.emplace_back(std::move(icon));
368
369 iconRect.y += height;
370
371 widget_cfg_t logo{ textcolor,
372 _backgroundcolor,
373 { _renderer, iconRect,
374 SDL3ResourceManager::get(SDLResourceManager::typeImages(),
375 "FreeRDP_Icon.svg") } };
376 _list.emplace_back(std::move(logo));
377
378 SDL_FRect rect = { widget_width / 4.0f, vpadding, widget_width * 3.0f / 4.0f,
379 total_height - 3ul * vpadding - widget_height };
380#else
381 SDL_FRect rect = { hpadding, vpadding, widget_width - 2ul * hpadding,
382 total_height - 2ul * vpadding };
383#endif
384
385 widget_cfg_t w{ textcolor, _backgroundcolor, { _renderer, rect } };
386 if (!w.widget.set_wrap(true, widget_width))
387 return false;
388 _list.emplace_back(std::move(w));
389 rect.y += widget_height + vpadding;
390
391 const std::vector<int> buttonids = { 1 };
392 const std::vector<std::string> buttonlabels = { "cancel" };
393 if (!_buttons.populate(_renderer, buttonlabels, buttonids, widget_width,
394 total_height - widget_height - vpadding,
395 static_cast<Sint32>(widget_width / 2),
396 static_cast<Sint32>(widget_height)))
397 return false;
398 if (!_buttons.set_highlight(0))
399 return false;
400
401 if (!SDL_ShowWindow(_window.get()))
402 return false;
403 if (!SDL_RaiseWindow(_window.get()))
404 return false;
405
406 return true;
407}
408
409void SDLConnectionDialog::destroyWindow()
410{
411 _buttons.clear();
412 _list.clear();
413 _renderer = nullptr;
414 _window = nullptr;
415}
416
417bool SDLConnectionDialog::show(SdlConnectionDialogWrapper::MsgType type, const char* fmt,
418 va_list ap)
419{
420 std::scoped_lock lock(_mux);
421 _msg = print(fmt, ap);
422 return show(type);
423}
424
425bool SDLConnectionDialog::show(SdlConnectionDialogWrapper::MsgType type)
426{
427 if (SDL_IsMainThread())
428 return updateMsg(type);
429 else
430 return sdl_push_user_event(SDL_EVENT_USER_RETRY_DIALOG, type);
431}
432
433std::string SDLConnectionDialog::print(const char* fmt, va_list ap)
434{
435 int size = -1;
436 std::string res;
437
438 do
439 {
440 res.resize(128);
441 if (size > 0)
442 res.resize(WINPR_ASSERTING_INT_CAST(uint32_t, size));
443
444 va_list copy = {};
445 va_copy(copy, ap);
446 WINPR_PRAGMA_DIAG_PUSH
447 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
448 size = vsnprintf(res.data(), res.size(), fmt, copy);
449 WINPR_PRAGMA_DIAG_POP
450 va_end(copy);
451
452 } while ((size > 0) && (static_cast<size_t>(size) > res.size()));
453
454 return res;
455}
456
457bool SDLConnectionDialog::setTimer(Uint32 timeoutMS)
458{
459 std::scoped_lock lock(_mux);
460 resetTimer();
461
462 _timer = SDL_AddTimer(timeoutMS, &SDLConnectionDialog::timeout, this);
463 _running = true;
464 return true;
465}
466
467void SDLConnectionDialog::resetTimer()
468{
469 if (_running)
470 SDL_RemoveTimer(_timer);
471 _running = false;
472}
473
474Uint32 SDLConnectionDialog::timeout(void* pvthis, [[maybe_unused]] SDL_TimerID timerID,
475 [[maybe_unused]] Uint32 intervalMS)
476{
477 auto self = static_cast<SDLConnectionDialog*>(pvthis);
478 std::ignore = self->hide();
479 self->_running = false;
480 return 0;
481}
static SDL_IOStream * get(const std::string &type, const std::string &id)