FreeRDP
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 
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_TIMER | 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_SetWindowModalFor(_window, parent);
152  SDL_RaiseWindow(_window);
153  }
154  return true;
155 }
156 
157 bool 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 
170 bool 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 
192 bool 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 
206 bool 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 
326 bool 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 = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS;
335  auto rc = SDL_CreateWindowAndRenderer(widget_width, total_height, flags, &_window, &_renderer);
336  if (rc != 0)
337  {
338  widget_log_error(rc, "SDL_CreateWindowAndRenderer");
339  return false;
340  }
341  SDL_SetWindowTitle(_window, _title.c_str());
342  setModal();
343 
344  SDL_Color res_bgcolor;
345  switch (_type_active)
346  {
347  case MSG_INFO:
348  res_bgcolor = infocolor;
349  break;
350  case MSG_WARN:
351  res_bgcolor = warncolor;
352  break;
353  case MSG_ERROR:
354  res_bgcolor = errorcolor;
355  break;
356  case MSG_DISCARD:
357  default:
358  res_bgcolor = backgroundcolor;
359  break;
360  }
361 
362 #if defined(WITH_SDL_IMAGE_DIALOGS)
363  std::string res_name;
364  switch (_type_active)
365  {
366  case MSG_INFO:
367  res_name = "icon_info.svg";
368  break;
369  case MSG_WARN:
370  res_name = "icon_warning.svg";
371  break;
372  case MSG_ERROR:
373  res_name = "icon_error.svg";
374  break;
375  case MSG_DISCARD:
376  default:
377  res_name = "";
378  break;
379  }
380 
381  int height = (total_height - 3ul * vpadding) / 2ul;
382  SDL_Rect iconRect{ hpadding, vpadding, widget_width / 4ul - 2ul * hpadding, height };
383  widget_cfg_t icon{ textcolor,
384  res_bgcolor,
385  { _renderer, iconRect,
386  SDL2ResourceManager::get(SDLResourceManager::typeImages(), res_name) } };
387  _list.emplace_back(std::move(icon));
388 
389  iconRect.y += height;
390 
391  widget_cfg_t logo{ textcolor,
392  backgroundcolor,
393  { _renderer, iconRect,
394  SDL2ResourceManager::get(SDLResourceManager::typeImages(),
395  "FreeRDP_Icon.svg") } };
396  _list.emplace_back(std::move(logo));
397 
398  SDL_Rect rect = { widget_width / 4ul, vpadding, widget_width * 3ul / 4ul,
399  total_height - 3ul * vpadding - widget_height };
400 #else
401  SDL_Rect rect = { hpadding, vpadding, widget_width - 2ul * hpadding,
402  total_height - 2ul * vpadding };
403 #endif
404 
405  widget_cfg_t w{ textcolor, backgroundcolor, { _renderer, rect, false } };
406  w.widget.set_wrap(true, widget_width);
407  _list.emplace_back(std::move(w));
408  rect.y += widget_height + vpadding;
409 
410  const std::vector<int> buttonids = { 1 };
411  const std::vector<std::string> buttonlabels = { "cancel" };
412  _buttons.populate(_renderer, buttonlabels, buttonids, widget_width,
413  total_height - widget_height - vpadding,
414  static_cast<Sint32>(widget_width / 2), static_cast<Sint32>(widget_height));
415  _buttons.set_highlight(0);
416 
417  SDL_ShowWindow(_window);
418  SDL_RaiseWindow(_window);
419 
420  return true;
421 }
422 
423 void SDLConnectionDialog::destroyWindow()
424 {
425  _buttons.clear();
426  _list.clear();
427  SDL_DestroyRenderer(_renderer);
428  SDL_DestroyWindow(_window);
429  _renderer = nullptr;
430  _window = nullptr;
431 }
432 
433 bool SDLConnectionDialog::show(MsgType type, const char* fmt, va_list ap)
434 {
435  std::lock_guard lock(_mux);
436  _msg = print(fmt, ap);
437  return show(type);
438 }
439 
440 bool SDLConnectionDialog::show(MsgType type)
441 {
442  _type = type;
443  return sdl_push_user_event(SDL_USEREVENT_RETRY_DIALOG);
444 }
445 
446 std::string SDLConnectionDialog::print(const char* fmt, va_list ap)
447 {
448  int size = -1;
449  std::string res;
450 
451  do
452  {
453  res.resize(128);
454  if (size > 0)
455  res.resize(size);
456 
457  va_list copy;
458  va_copy(copy, ap);
459  WINPR_PRAGMA_DIAG_PUSH
460  WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
461  size = vsnprintf(res.data(), res.size(), fmt, copy);
462  WINPR_PRAGMA_DIAG_POP
463  va_end(copy);
464 
465  } while ((size > 0) && (static_cast<size_t>(size) > res.size()));
466 
467  return res;
468 }
469 
470 bool SDLConnectionDialog::setTimer(Uint32 timeoutMS)
471 {
472  std::lock_guard lock(_mux);
473  resetTimer();
474 
475  _timer = SDL_AddTimer(timeoutMS, &SDLConnectionDialog::timeout, this);
476  _running = true;
477  return true;
478 }
479 
480 void SDLConnectionDialog::resetTimer()
481 {
482  if (_running)
483  SDL_RemoveTimer(_timer);
484  _running = false;
485 }
486 
487 Uint32 SDLConnectionDialog::timeout(Uint32 intervalMS, void* pvthis)
488 {
489  auto self = static_cast<SDLConnectionDialog*>(pvthis);
490  self->hide();
491  self->_running = false;
492  return 0;
493 }
494 
495 SDLConnectionDialogHider::SDLConnectionDialogHider(freerdp* instance)
496  : SDLConnectionDialogHider(get(instance))
497 {
498 }
499 
500 SDLConnectionDialogHider::SDLConnectionDialogHider(rdpContext* context)
501  : SDLConnectionDialogHider(get(context))
502 {
503 }
504 
505 SDLConnectionDialogHider::SDLConnectionDialogHider(SDLConnectionDialog* dialog) : _dialog(dialog)
506 {
507  if (_dialog)
508  {
509  _visible = _dialog->visible();
510  if (_visible)
511  {
512  _dialog->hide();
513  }
514  }
515 }
516 
517 SDLConnectionDialogHider::~SDLConnectionDialogHider()
518 {
519  if (_dialog && _visible)
520  {
521  _dialog->show();
522  }
523 }
524 
525 SDLConnectionDialog* SDLConnectionDialogHider::get(freerdp* instance)
526 {
527  if (!instance)
528  return nullptr;
529  return get(instance->context);
530 }
531 
532 SDLConnectionDialog* SDLConnectionDialogHider::get(rdpContext* context)
533 {
534  auto sdl = get_context(context);
535  if (!sdl)
536  return nullptr;
537  return sdl->connection_dialog.get();
538 }
static SDL_RWops * get(const std::string &type, const std::string &id)