FreeRDP
Loading...
Searching...
No Matches
sdl_connection_dialog_wrapper.cpp
1
21#include <sstream>
22
23#include <freerdp/freerdp.h>
24#include <freerdp/settings.h>
25#include <freerdp/log.h>
26
27#include "../sdl_utils.hpp"
28#include "sdl_connection_dialog.hpp"
29#include "sdl_connection_dialog_wrapper.hpp"
30
34
35SdlConnectionDialogWrapper::~SdlConnectionDialogWrapper() = default;
36
37void SdlConnectionDialogWrapper::create(rdpContext* context)
38{
39 const auto enabled =
40 freerdp_settings_get_bool(context->settings, FreeRDP_UseCommonStdioCallbacks);
41 _connection_dialog.reset();
42#if defined(__EMSCRIPTEN__)
43 /* Every SDL window of an Emscripten build draws on the page's one canvas, so a dialog
44 * window would take over the session's canvas. SDL 3.2 also throws from creating a
45 * window while another one is open (FS.mkdir of its drop directory; SDL 3.4 catches
46 * it), which unwinds the main thread while it holds _mux. The messages go to the log. */
47 WINPR_UNUSED(enabled);
48#else
49 if (!enabled)
50 _connection_dialog = std::make_unique<SDLConnectionDialog>(context);
51#endif
52}
53
54void SdlConnectionDialogWrapper::destroy()
55{
56 _connection_dialog.reset();
57}
58
59bool SdlConnectionDialogWrapper::isRunning() const
60{
61 std::unique_lock lock(_mux);
62 /* Messages are queued here and handled on the main thread, so the dialog is still busy
63 * while the queue is not empty. Without this the event loop can leave before an error
64 * pushed by the RDP thread was ever shown. */
65 if (!_queue.empty())
66 return true;
67 if (!_connection_dialog)
68 return false;
69 return _connection_dialog->running();
70}
71
72bool SdlConnectionDialogWrapper::isVisible() const
73{
74 std::unique_lock lock(_mux);
75 if (!_connection_dialog)
76 return false;
77 return _connection_dialog->visible();
78}
79
80bool SdlConnectionDialogWrapper::handleEvent(const SDL_Event& event)
81{
82 std::unique_lock lock(_mux);
83 if (!_connection_dialog)
84 return false;
85 return _connection_dialog->handle(event);
86}
87
88WINPR_ATTR_FORMAT_ARG(1, 0)
89static std::string format(WINPR_FORMAT_ARG const char* fmt, va_list ap)
90{
91 va_list ap1 = {};
92 va_copy(ap1, ap);
93 const int size = vsnprintf(nullptr, 0, fmt, ap1);
94 va_end(ap1);
95
96 if (size < 0)
97 return "";
98
99 std::string msg;
100 msg.resize(static_cast<size_t>(size) + 1);
101
102 va_list ap2 = {};
103 va_copy(ap2, ap);
104 std::ignore = vsnprintf(msg.data(), msg.size(), fmt, ap2);
105 va_end(ap2);
106 return msg;
107}
108
109void SdlConnectionDialogWrapper::setTitle(const char* fmt, ...)
110{
111 va_list ap = {};
112 va_start(ap, fmt);
113 setTitle(format(fmt, ap));
114 va_end(ap);
115}
116
117void SdlConnectionDialogWrapper::setTitle(const std::string& title)
118{
119 push(EventArg{ title });
120}
121
122void SdlConnectionDialogWrapper::showInfo(const char* fmt, ...)
123{
124 va_list ap = {};
125 va_start(ap, fmt);
126 showInfo(format(fmt, ap));
127 va_end(ap);
128}
129
130void SdlConnectionDialogWrapper::showInfo(const std::string& info)
131{
132 show(MSG_INFO, info);
133}
134
135void SdlConnectionDialogWrapper::showWarn(const char* fmt, ...)
136{
137 va_list ap = {};
138 va_start(ap, fmt);
139 showWarn(format(fmt, ap));
140 va_end(ap);
141}
142
143void SdlConnectionDialogWrapper::showWarn(const std::string& info)
144{
145 show(MSG_WARN, info);
146}
147
148void SdlConnectionDialogWrapper::showError(const char* fmt, ...)
149{
150 va_list ap = {};
151 va_start(ap, fmt);
152 showError(format(fmt, ap));
153 va_end(ap);
154}
155
156void SdlConnectionDialogWrapper::showError(const std::string& error)
157{
158 show(MSG_ERROR, error);
159}
160
161void SdlConnectionDialogWrapper::show(SdlConnectionDialogWrapper::MsgType type,
162 const std::string& msg)
163{
164 push({ type, msg, true });
165}
166
167void SdlConnectionDialogWrapper::show(bool visible)
168{
169 push(EventArg{ visible });
170}
171
172void SdlConnectionDialogWrapper::handleShow()
173{
174 std::unique_lock lock(_mux);
175 while (!_queue.empty())
176 {
177 /* take the message out of the queue: pop() destroys the queued one */
178 auto arg = std::move(_queue.front());
179 _queue.pop();
180
181 if (arg.hasTitle() && _connection_dialog)
182 {
183 std::ignore = _connection_dialog->setTitle(arg.title().c_str());
184 }
185
186 if (arg.hasType() && arg.hasMessage())
187 {
188 switch (arg.type())
189 {
190 case SdlConnectionDialogWrapper::MSG_INFO:
191 if (_connection_dialog)
192 std::ignore = _connection_dialog->showInfo(arg.message().c_str());
193 else
194 WLog_Print(_log, WLOG_INFO, "%s", arg.message().c_str());
195 break;
196 case SdlConnectionDialogWrapper::MSG_WARN:
197 if (_connection_dialog)
198 std::ignore = _connection_dialog->showWarn(arg.message().c_str());
199 else
200 WLog_Print(_log, WLOG_WARN, "%s", arg.message().c_str());
201 break;
202 case SdlConnectionDialogWrapper::MSG_ERROR:
203 if (_connection_dialog)
204 std::ignore = _connection_dialog->showError(arg.message().c_str());
205 else
206 WLog_Print(_log, WLOG_ERROR, "%s", arg.message().c_str());
207 break;
208 default:
209 break;
210 }
211 }
212
213 if (arg.hasVisibility() && _connection_dialog)
214 {
215 if (arg.visible())
216 std::ignore = _connection_dialog->show();
217 else
218 std::ignore = _connection_dialog->hide();
219 }
220 }
221}
222
223void SdlConnectionDialogWrapper::push(EventArg&& arg)
224{
225 {
226 std::unique_lock lock(_mux);
227 _queue.push(std::move(arg));
228 }
229
230 auto rc = SDL_RunOnMainThread(
231 [](void* user)
232 {
233 auto dlg = static_cast<SdlConnectionDialogWrapper*>(user);
234 dlg->handleShow();
235 },
236 this, false);
237 if (!rc)
238 SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "[%s] SDL_RunOnMainThread failed with %s",
239 __func__, SDL_GetError());
240}
241
242SdlConnectionDialogWrapper::EventArg::EventArg(bool visible) : _visible(visible), _mask(8)
243{
244}
245
246SdlConnectionDialogWrapper::EventArg::EventArg(const std::string& title) : _title(title), _mask(1)
247{
248}
249
250SdlConnectionDialogWrapper::EventArg::EventArg(MsgType type, const std::string& msg, bool visible)
251 : _message(msg), _type(type), _visible(visible), _mask(14)
252{
253}
254
255bool SdlConnectionDialogWrapper::EventArg::hasTitle() const
256{
257 return _mask & 0x01;
258}
259
260const std::string& SdlConnectionDialogWrapper::EventArg::title() const
261{
262 return _title;
263}
264
265bool SdlConnectionDialogWrapper::EventArg::hasMessage() const
266{
267 return _mask & 0x02;
268}
269
270const std::string& SdlConnectionDialogWrapper::EventArg::message() const
271{
272 return _message;
273}
274
275bool SdlConnectionDialogWrapper::EventArg::hasType() const
276{
277 return _mask & 0x04;
278}
279
280SdlConnectionDialogWrapper::MsgType SdlConnectionDialogWrapper::EventArg::type() const
281{
282 return _type;
283}
284
285bool SdlConnectionDialogWrapper::EventArg::hasVisibility() const
286{
287 return _mask & 0x08;
288}
289
290bool SdlConnectionDialogWrapper::EventArg::visible() const
291{
292 return _visible;
293}
294
295std::string SdlConnectionDialogWrapper::EventArg::str() const
296{
297 std::stringstream ss;
298 ss << "{ title:" << _title << ", message:" << _message << ", type:" << _type
299 << ", visible:" << _visible << ", mask:" << _mask << "}";
300 return ss.str();
301}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.