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