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