FreeRDP
Loading...
Searching...
No Matches
SDL2/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_freerdp.hpp"
25#include "res/sdl2_resource_manager.hpp"
26
27static const SDL_Color backgroundcolor = { 0x38, 0x36, 0x35, 0xff };
28static const SDL_Color textcolor = { 0xd1, 0xcf, 0xcd, 0xff };
29static const SDL_Color infocolor = { 0x43, 0xe0, 0x0f, 0x60 };
30static const SDL_Color warncolor = { 0xcd, 0xca, 0x35, 0x60 };
31static const SDL_Color errorcolor = { 0xf7, 0x22, 0x30, 0x60 };
32
33static const Uint32 vpadding = 5;
34static const Uint32 hpadding = 5;
35
36SDLConnectionDialog::SDLConnectionDialog(rdpContext* context) : _context(context)
37{
38 SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
39 hide();
40}
41
42SDLConnectionDialog::~SDLConnectionDialog()
43{
44 resetTimer();
45 destroyWindow();
46 SDL_Quit();
47}
48
49bool SDLConnectionDialog::visible() const
50{
51 if (!_window || !_renderer)
52 return false;
53
54 auto flags = SDL_GetWindowFlags(_window);
55 return (flags & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED)) == 0;
56}
57
58bool SDLConnectionDialog::setTitle(const char* fmt, ...)
59{
60 std::lock_guard lock(_mux);
61 va_list ap = {};
62 va_start(ap, fmt);
63 _title = print(fmt, ap);
64 va_end(ap);
65
66 return show(MSG_NONE);
67}
68
69bool SDLConnectionDialog::showInfo(const char* fmt, ...)
70{
71 va_list ap = {};
72 va_start(ap, fmt);
73 auto rc = show(MSG_INFO, fmt, ap);
74 va_end(ap);
75 return rc;
76}
77
78bool SDLConnectionDialog::showWarn(const char* fmt, ...)
79{
80 va_list ap = {};
81 va_start(ap, fmt);
82 auto rc = show(MSG_WARN, fmt, ap);
83 va_end(ap);
84 return rc;
85}
86
87bool SDLConnectionDialog::showError(const char* fmt, ...)
88{
89 va_list ap = {};
90 va_start(ap, fmt);
91 auto rc = show(MSG_ERROR, fmt, ap);
92 va_end(ap);
93 if (!rc)
94 return rc;
95 return setTimer();
96}
97
98bool SDLConnectionDialog::show()
99{
100 std::lock_guard lock(_mux);
101 return show(_type_active);
102}
103
104bool SDLConnectionDialog::hide()
105{
106 std::lock_guard lock(_mux);
107 return show(MSG_DISCARD);
108}
109
110bool SDLConnectionDialog::running() const
111{
112 std::lock_guard lock(_mux);
113 return _running;
114}
115
116bool SDLConnectionDialog::update()
117{
118 std::lock_guard lock(_mux);
119 switch (_type)
120 {
121 case MSG_INFO:
122 case MSG_WARN:
123 case MSG_ERROR:
124 _type_active = _type;
125 createWindow();
126 break;
127 case MSG_DISCARD:
128 resetTimer();
129 destroyWindow();
130 break;
131 default:
132 if (_window)
133 {
134 SDL_SetWindowTitle(_window, _title.c_str());
135 }
136 break;
137 }
138 _type = MSG_NONE;
139 return true;
140}
141
142bool SDLConnectionDialog::setModal()
143{
144 if (_window)
145 {
146 auto sdl = get_context(_context);
147 if (sdl->windows.empty())
148 return true;
149
150 auto parent = sdl->windows.begin()->second.window();
151 SDL_SetWindowModalFor(_window, parent);
152 SDL_RaiseWindow(_window);
153 }
154 return true;
155}
156
157bool SDLConnectionDialog::clearWindow(SDL_Renderer* renderer)
158{
159 assert(renderer);
160
161 const int drc = SDL_SetRenderDrawColor(renderer, backgroundcolor.r, backgroundcolor.g,
162 backgroundcolor.b, backgroundcolor.a);
163 if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
164 return false;
165
166 const int rcls = SDL_RenderClear(renderer);
167 return !widget_log_error(rcls, "SDL_RenderClear");
168}
169
170bool SDLConnectionDialog::update(SDL_Renderer* renderer)
171{
172 std::lock_guard lock(_mux);
173 if (!renderer)
174 return false;
175
176 if (!clearWindow(renderer))
177 return false;
178
179 for (auto& btn : _list)
180 {
181 if (!btn.widget.update_text(renderer, _msg, btn.fgcolor, btn.bgcolor))
182 return false;
183 }
184
185 if (!_buttons.update(renderer))
186 return false;
187
188 SDL_RenderPresent(renderer);
189 return true;
190}
191
192bool SDLConnectionDialog::wait(bool ignoreRdpContext)
193{
194 while (running())
195 {
196 if (!ignoreRdpContext)
197 {
198 if (freerdp_shall_disconnect_context(_context))
199 return false;
200 }
201 std::this_thread::yield();
202 }
203 return true;
204}
205
206bool SDLConnectionDialog::handle(const SDL_Event& event)
207{
208 Uint32 windowID = 0;
209 if (_window)
210 {
211 windowID = SDL_GetWindowID(_window);
212 }
213
214 switch (event.type)
215 {
216 case SDL_USEREVENT_RETRY_DIALOG:
217 return update();
218 case SDL_QUIT:
219 resetTimer();
220 destroyWindow();
221 return false;
222 case SDL_KEYDOWN:
223 case SDL_KEYUP:
224 if (visible())
225 {
226 auto& ev = reinterpret_cast<const SDL_KeyboardEvent&>(event);
227 update(_renderer);
228 switch (event.key.keysym.sym)
229 {
230 case SDLK_RETURN:
231 case SDLK_RETURN2:
232 case SDLK_ESCAPE:
233 case SDLK_KP_ENTER:
234 if (event.type == SDL_KEYUP)
235 {
236 freerdp_abort_event(_context);
237 sdl_push_quit();
238 }
239 break;
240 case SDLK_TAB:
241 _buttons.set_highlight_next();
242 break;
243 default:
244 break;
245 }
246
247 return windowID == ev.windowID;
248 }
249 return false;
250 case SDL_MOUSEMOTION:
251 if (visible())
252 {
253 auto& ev = reinterpret_cast<const SDL_MouseMotionEvent&>(event);
254
255 _buttons.set_mouseover(event.button.x, event.button.y);
256 update(_renderer);
257 return windowID == ev.windowID;
258 }
259 return false;
260 case SDL_MOUSEBUTTONDOWN:
261 case SDL_MOUSEBUTTONUP:
262 if (visible())
263 {
264 auto& ev = reinterpret_cast<const SDL_MouseButtonEvent&>(event);
265 update(_renderer);
266
267 auto button = _buttons.get_selected(event.button);
268 if (button)
269 {
270 if (event.type == SDL_MOUSEBUTTONUP)
271 {
272 freerdp_abort_event(_context);
273 sdl_push_quit();
274 }
275 }
276
277 return windowID == ev.windowID;
278 }
279 return false;
280 case SDL_MOUSEWHEEL:
281 if (visible())
282 {
283 auto& ev = reinterpret_cast<const SDL_MouseWheelEvent&>(event);
284 update(_renderer);
285 return windowID == ev.windowID;
286 }
287 return false;
288 case SDL_FINGERUP:
289 case SDL_FINGERDOWN:
290 if (visible())
291 {
292 auto& ev = reinterpret_cast<const SDL_TouchFingerEvent&>(event);
293 update(_renderer);
294#if SDL_VERSION_ATLEAST(2, 0, 18)
295 return windowID == ev.windowID;
296#else
297 return false;
298#endif
299 }
300 return false;
301 case SDL_WINDOWEVENT:
302 {
303 auto& ev = reinterpret_cast<const SDL_WindowEvent&>(event);
304 switch (ev.event)
305 {
306 case SDL_WINDOWEVENT_CLOSE:
307 if (windowID == ev.windowID)
308 {
309 freerdp_abort_event(_context);
310 sdl_push_quit();
311 }
312 break;
313 default:
314 update(_renderer);
315 setModal();
316 break;
317 }
318
319 return windowID == ev.windowID;
320 }
321 default:
322 return false;
323 }
324}
325
326bool SDLConnectionDialog::createWindow()
327{
328 destroyWindow();
329
330 const int widget_height = 50;
331 const int widget_width = 600;
332 const int total_height = 300;
333
334 auto flags = WINPR_ASSERTING_INT_CAST(
335 uint32_t, SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS);
336 auto rc = SDL_CreateWindowAndRenderer(widget_width, total_height, flags, &_window, &_renderer);
337 if (rc != 0)
338 {
339 widget_log_error(rc, "SDL_CreateWindowAndRenderer");
340 return false;
341 }
342 SDL_SetWindowTitle(_window, _title.c_str());
343 setModal();
344
345 SDL_Color res_bgcolor;
346 switch (_type_active)
347 {
348 case MSG_INFO:
349 res_bgcolor = infocolor;
350 break;
351 case MSG_WARN:
352 res_bgcolor = warncolor;
353 break;
354 case MSG_ERROR:
355 res_bgcolor = errorcolor;
356 break;
357 case MSG_DISCARD:
358 default:
359 res_bgcolor = backgroundcolor;
360 break;
361 }
362
363#if defined(WITH_SDL_IMAGE_DIALOGS)
364 std::string res_name;
365 switch (_type_active)
366 {
367 case MSG_INFO:
368 res_name = "icon_info.svg";
369 break;
370 case MSG_WARN:
371 res_name = "icon_warning.svg";
372 break;
373 case MSG_ERROR:
374 res_name = "icon_error.svg";
375 break;
376 case MSG_DISCARD:
377 default:
378 res_name = "";
379 break;
380 }
381
382 int height = (total_height - 3ul * vpadding) / 2ul;
383 SDL_Rect iconRect{ hpadding, vpadding, widget_width / 4ul - 2ul * hpadding, height };
384 widget_cfg_t icon{ textcolor,
385 res_bgcolor,
386 { _renderer, iconRect,
387 SDL2ResourceManager::get(SDLResourceManager::typeImages(), res_name) } };
388 _list.emplace_back(std::move(icon));
389
390 iconRect.y += height;
391
392 widget_cfg_t logo{ textcolor,
393 backgroundcolor,
394 { _renderer, iconRect,
395 SDL2ResourceManager::get(SDLResourceManager::typeImages(),
396 "FreeRDP_Icon.svg") } };
397 _list.emplace_back(std::move(logo));
398
399 SDL_Rect rect = { widget_width / 4ul, vpadding, widget_width * 3ul / 4ul,
400 total_height - 3ul * vpadding - widget_height };
401#else
402 SDL_Rect rect = { hpadding, vpadding, widget_width - 2ul * hpadding,
403 total_height - 2ul * vpadding };
404#endif
405
406 widget_cfg_t w{ textcolor, backgroundcolor, { _renderer, rect, false } };
407 w.widget.set_wrap(true, widget_width);
408 _list.emplace_back(std::move(w));
409 rect.y += widget_height + vpadding;
410
411 const std::vector<int> buttonids = { 1 };
412 const std::vector<std::string> buttonlabels = { "cancel" };
413 _buttons.populate(_renderer, buttonlabels, buttonids, widget_width,
414 total_height - widget_height - vpadding,
415 static_cast<Sint32>(widget_width / 2), static_cast<Sint32>(widget_height));
416 _buttons.set_highlight(0);
417
418 SDL_ShowWindow(_window);
419 SDL_RaiseWindow(_window);
420
421 return true;
422}
423
424void SDLConnectionDialog::destroyWindow()
425{
426 _buttons.clear();
427 _list.clear();
428 SDL_DestroyRenderer(_renderer);
429 SDL_DestroyWindow(_window);
430 _renderer = nullptr;
431 _window = nullptr;
432}
433
434bool SDLConnectionDialog::show(MsgType type, const char* fmt, va_list ap)
435{
436 std::lock_guard lock(_mux);
437 _msg = print(fmt, ap);
438 return show(type);
439}
440
441bool SDLConnectionDialog::show(MsgType type)
442{
443 _type = type;
444 return sdl_push_user_event(SDL_USEREVENT_RETRY_DIALOG);
445}
446
447std::string SDLConnectionDialog::print(const char* fmt, va_list ap)
448{
449 int size = -1;
450 std::string res;
451
452 do
453 {
454 res.resize(128);
455 if (size > 0)
456 res.resize(WINPR_ASSERTING_INT_CAST(size_t, size));
457
458 va_list copy;
459 va_copy(copy, ap);
460 WINPR_PRAGMA_DIAG_PUSH
461 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
462 size = vsnprintf(res.data(), res.size(), fmt, copy);
463 WINPR_PRAGMA_DIAG_POP
464 va_end(copy);
465
466 } while ((size > 0) && (static_cast<size_t>(size) > res.size()));
467
468 return res;
469}
470
471bool SDLConnectionDialog::setTimer(Uint32 timeoutMS)
472{
473 std::lock_guard lock(_mux);
474 resetTimer();
475
476 _timer = SDL_AddTimer(timeoutMS, &SDLConnectionDialog::timeout, this);
477 _running = true;
478 return true;
479}
480
481void SDLConnectionDialog::resetTimer()
482{
483 if (_running)
484 SDL_RemoveTimer(_timer);
485 _running = false;
486}
487
488Uint32 SDLConnectionDialog::timeout([[maybe_unused]] Uint32 intervalMS, void* pvthis)
489{
490 auto self = static_cast<SDLConnectionDialog*>(pvthis);
491 self->hide();
492 self->_running = false;
493 return 0;
494}
495
496SDLConnectionDialogHider::SDLConnectionDialogHider(freerdp* instance)
497 : SDLConnectionDialogHider(get(instance))
498{
499}
500
501SDLConnectionDialogHider::SDLConnectionDialogHider(rdpContext* context)
502 : SDLConnectionDialogHider(get(context))
503{
504}
505
506SDLConnectionDialogHider::SDLConnectionDialogHider(SDLConnectionDialog* dialog) : _dialog(dialog)
507{
508 if (_dialog)
509 {
510 _visible = _dialog->visible();
511 if (_visible)
512 {
513 _dialog->hide();
514 }
515 }
516}
517
518SDLConnectionDialogHider::~SDLConnectionDialogHider()
519{
520 if (_dialog && _visible)
521 {
522 _dialog->show();
523 }
524}
525
526SDLConnectionDialog* SDLConnectionDialogHider::get(freerdp* instance)
527{
528 if (!instance)
529 return nullptr;
530 return get(instance->context);
531}
532
533SDLConnectionDialog* SDLConnectionDialogHider::get(rdpContext* context)
534{
535 auto sdl = get_context(context);
536 if (!sdl)
537 return nullptr;
538 return sdl->connection_dialog.get();
539}
static SDL_RWops * get(const std::string &type, const std::string &id)