2023-12-15 15:55:27 +01:00
|
|
|
/**
|
|
|
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
|
|
|
* SDL Client helper dialogs
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <thread>
|
|
|
|
|
|
|
|
|
|
#include "sdl_connection_dialog.hpp"
|
|
|
|
|
#include "../sdl_utils.hpp"
|
|
|
|
|
#include "../sdl_freerdp.hpp"
|
|
|
|
|
|
|
|
|
|
static const SDL_Color backgroundcolor = { 0x38, 0x36, 0x35, 0xff };
|
|
|
|
|
static const SDL_Color textcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
|
|
|
|
|
|
|
|
|
static const Uint32 hpadding = 10;
|
|
|
|
|
static const Uint32 vpadding = 5;
|
|
|
|
|
|
|
|
|
|
SDLConnectionDialog::SDLConnectionDialog(rdpContext* context)
|
|
|
|
|
: _context(context), _window(nullptr), _renderer(nullptr)
|
|
|
|
|
{
|
|
|
|
|
hide();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDLConnectionDialog::~SDLConnectionDialog()
|
|
|
|
|
{
|
|
|
|
|
resetTimer();
|
|
|
|
|
destroyWindow();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::visible() const
|
|
|
|
|
{
|
|
|
|
|
return _window && _renderer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::setTitle(const char* fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(_mux);
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
_title = print(fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
return show(MSG_NONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::showInfo(const char* fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
auto rc = show(MSG_INFO, fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::showWarn(const char* fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
auto rc = show(MSG_WARN, fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::showError(const char* fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
auto rc = show(MSG_ERROR, fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
return setTimer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::hide()
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(_mux);
|
|
|
|
|
return show(MSG_DISCARD);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::running() const
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(_mux);
|
|
|
|
|
return _running;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::update()
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(_mux);
|
|
|
|
|
switch (_type)
|
|
|
|
|
{
|
|
|
|
|
case MSG_INFO:
|
|
|
|
|
case MSG_WARN:
|
|
|
|
|
case MSG_ERROR:
|
|
|
|
|
createWindow();
|
|
|
|
|
break;
|
|
|
|
|
case MSG_DISCARD:
|
|
|
|
|
resetTimer();
|
|
|
|
|
destroyWindow();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
if (_window)
|
|
|
|
|
{
|
|
|
|
|
SDL_SetWindowTitle(_window, _title.c_str());
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
_type = MSG_NONE;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::setModal()
|
|
|
|
|
{
|
|
|
|
|
if (_window)
|
|
|
|
|
{
|
|
|
|
|
auto sdl = get_context(_context);
|
|
|
|
|
if (sdl->windows.empty())
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
auto parent = sdl->windows.front().window;
|
|
|
|
|
SDL_SetWindowModalFor(_window, parent);
|
|
|
|
|
SDL_RaiseWindow(_window);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::clearWindow(SDL_Renderer* renderer)
|
|
|
|
|
{
|
|
|
|
|
assert(renderer);
|
|
|
|
|
|
|
|
|
|
const int drc = SDL_SetRenderDrawColor(renderer, backgroundcolor.r, backgroundcolor.g,
|
|
|
|
|
backgroundcolor.b, backgroundcolor.a);
|
|
|
|
|
if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const int rcls = SDL_RenderClear(renderer);
|
|
|
|
|
return !widget_log_error(rcls, "SDL_RenderClear");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::update(SDL_Renderer* renderer)
|
|
|
|
|
{
|
|
|
|
|
if (!renderer)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!clearWindow(renderer))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (auto& btn : _list)
|
|
|
|
|
{
|
|
|
|
|
if (!btn.update_text(renderer, _msg, textcolor))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!_buttons.update(renderer))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
SDL_RenderPresent(renderer);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::wait(bool ignoreRdpContext)
|
|
|
|
|
{
|
|
|
|
|
while (running())
|
|
|
|
|
{
|
|
|
|
|
if (!ignoreRdpContext)
|
|
|
|
|
{
|
|
|
|
|
if (freerdp_shall_disconnect_context(_context))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
std::this_thread::yield();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::handle(const SDL_Event& event)
|
|
|
|
|
{
|
|
|
|
|
Uint32 windowID = 0;
|
|
|
|
|
if (_window)
|
|
|
|
|
{
|
|
|
|
|
windowID = SDL_GetWindowID(_window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (event.type)
|
|
|
|
|
{
|
|
|
|
|
case SDL_USEREVENT_RETRY_DIALOG:
|
|
|
|
|
return update();
|
|
|
|
|
case SDL_QUIT:
|
|
|
|
|
resetTimer();
|
|
|
|
|
destroyWindow();
|
|
|
|
|
return false;
|
|
|
|
|
case SDL_KEYDOWN:
|
|
|
|
|
case SDL_KEYUP:
|
|
|
|
|
{
|
|
|
|
|
auto ev = reinterpret_cast<const SDL_KeyboardEvent&>(event);
|
|
|
|
|
update(_renderer);
|
|
|
|
|
return windowID == ev.windowID;
|
|
|
|
|
}
|
|
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
|
|
|
{
|
|
|
|
|
auto ev = reinterpret_cast<const SDL_MouseButtonEvent&>(event);
|
|
|
|
|
update(_renderer);
|
|
|
|
|
return windowID == ev.windowID;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SDL_MOUSEWHEEL:
|
|
|
|
|
{
|
|
|
|
|
auto ev = reinterpret_cast<const SDL_MouseWheelEvent&>(event);
|
|
|
|
|
update(_renderer);
|
|
|
|
|
return windowID == ev.windowID;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SDL_FINGERUP:
|
|
|
|
|
case SDL_FINGERDOWN:
|
|
|
|
|
{
|
|
|
|
|
auto ev = reinterpret_cast<const SDL_TouchFingerEvent&>(event);
|
|
|
|
|
update(_renderer);
|
2023-12-15 19:59:06 +01:00
|
|
|
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
2023-12-15 15:55:27 +01:00
|
|
|
return windowID == ev.windowID;
|
2023-12-15 19:59:06 +01:00
|
|
|
#else
|
|
|
|
|
return false;
|
|
|
|
|
#endif
|
2023-12-15 15:55:27 +01:00
|
|
|
}
|
|
|
|
|
case SDL_WINDOWEVENT:
|
|
|
|
|
{
|
|
|
|
|
auto ev = reinterpret_cast<const SDL_WindowEvent&>(event);
|
|
|
|
|
switch (ev.event)
|
|
|
|
|
{
|
|
|
|
|
case SDL_WINDOWEVENT_CLOSE:
|
|
|
|
|
if (windowID == ev.windowID)
|
|
|
|
|
{
|
|
|
|
|
resetTimer();
|
|
|
|
|
destroyWindow();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
update(_renderer);
|
|
|
|
|
setModal();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return windowID == ev.windowID;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::createWindow()
|
|
|
|
|
{
|
|
|
|
|
destroyWindow();
|
|
|
|
|
|
|
|
|
|
const size_t widget_height = 50;
|
|
|
|
|
const size_t widget_width = 600;
|
|
|
|
|
const size_t total_height = 400;
|
|
|
|
|
|
|
|
|
|
_window = SDL_CreateWindow(_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
|
|
|
|
widget_width, total_height, 0);
|
|
|
|
|
if (_window == nullptr)
|
|
|
|
|
{
|
|
|
|
|
widget_log_error(-1, "SDL_CreateWindow");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
setModal();
|
|
|
|
|
|
|
|
|
|
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
|
|
|
|
|
if (_renderer == nullptr)
|
|
|
|
|
{
|
|
|
|
|
widget_log_error(-1, "SDL_CreateRenderer");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDL_Rect rect = { 0, 0, widget_width, widget_height };
|
|
|
|
|
_list.push_back(SdlWidget(_renderer, rect, false));
|
|
|
|
|
rect.y += widget_height + vpadding;
|
|
|
|
|
|
|
|
|
|
const std::vector<int> buttonids = { 1 };
|
|
|
|
|
const std::vector<std::string> buttonlabels = { "cancel" };
|
|
|
|
|
_buttons.populate(_renderer, buttonlabels, buttonids, static_cast<Sint32>(total_height),
|
|
|
|
|
static_cast<Sint32>(widget_width / 2), static_cast<Sint32>(widget_height));
|
|
|
|
|
|
|
|
|
|
SDL_ShowWindow(_window);
|
|
|
|
|
SDL_RaiseWindow(_window);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SDLConnectionDialog::destroyWindow()
|
|
|
|
|
{
|
|
|
|
|
_buttons.clear();
|
|
|
|
|
_list.clear();
|
|
|
|
|
SDL_DestroyRenderer(_renderer);
|
|
|
|
|
SDL_DestroyWindow(_window);
|
|
|
|
|
_renderer = nullptr;
|
|
|
|
|
_window = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::show(MsgType type, const char* fmt, va_list ap)
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(_mux);
|
|
|
|
|
_msg = print(fmt, ap);
|
|
|
|
|
return show(type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::show(MsgType type)
|
|
|
|
|
{
|
|
|
|
|
_type = type;
|
|
|
|
|
return sdl_push_user_event(SDL_USEREVENT_RETRY_DIALOG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string SDLConnectionDialog::print(const char* fmt, va_list ap)
|
|
|
|
|
{
|
|
|
|
|
int size = -1;
|
|
|
|
|
std::string res;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
res.resize(128);
|
|
|
|
|
if (size > 0)
|
|
|
|
|
res.resize(size);
|
|
|
|
|
|
|
|
|
|
va_list copy;
|
|
|
|
|
va_copy(copy, ap);
|
|
|
|
|
size = vsnprintf(res.data(), res.size(), fmt, copy);
|
|
|
|
|
va_end(copy);
|
|
|
|
|
|
|
|
|
|
} while ((size > 0) && (size > res.size()));
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SDLConnectionDialog::setTimer(Uint32 timeoutMS)
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(_mux);
|
|
|
|
|
resetTimer();
|
|
|
|
|
|
|
|
|
|
_timer = SDL_AddTimer(timeoutMS, &SDLConnectionDialog::timeout, this);
|
|
|
|
|
_running = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SDLConnectionDialog::resetTimer()
|
|
|
|
|
{
|
|
|
|
|
if (_running)
|
|
|
|
|
SDL_RemoveTimer(_timer);
|
|
|
|
|
_running = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Uint32 SDLConnectionDialog::timeout(Uint32 intervalMS, void* pvthis)
|
|
|
|
|
{
|
|
|
|
|
auto ths = static_cast<SDLConnectionDialog*>(pvthis);
|
|
|
|
|
ths->hide();
|
|
|
|
|
ths->_running = false;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|