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