Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
72
third_party/FreeRDP/client/SDL/SDL2/dialogs/CMakeLists.txt
vendored
Normal file
72
third_party/FreeRDP/client/SDL/SDL2/dialogs/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
set(SRCS
|
||||
sdl_button.hpp
|
||||
sdl_button.cpp
|
||||
sdl_buttons.hpp
|
||||
sdl_buttons.cpp
|
||||
sdl_dialogs.cpp
|
||||
sdl_dialogs.hpp
|
||||
sdl_widget.hpp
|
||||
sdl_widget.cpp
|
||||
sdl_input.hpp
|
||||
sdl_input.cpp
|
||||
sdl_input_widgets.cpp
|
||||
sdl_input_widgets.hpp
|
||||
sdl_select.hpp
|
||||
sdl_select.cpp
|
||||
sdl_selectlist.hpp
|
||||
sdl_selectlist.cpp
|
||||
sdl_connection_dialog.cpp
|
||||
sdl_connection_dialog.hpp
|
||||
)
|
||||
|
||||
list(APPEND LIBS sdl2_client_res winpr)
|
||||
|
||||
if(NOT WITH_SDL_LINK_SHARED)
|
||||
list(APPEND LIBS SDL2::SDL2-static)
|
||||
else()
|
||||
list(APPEND LIBS SDL2::SDL2)
|
||||
endif()
|
||||
|
||||
macro(find_sdl_component name)
|
||||
find_package(${name})
|
||||
if(NOT ${name}_FOUND)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(${name} REQUIRED ${name})
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
list(APPEND LIBS ${${name}_LIBRARIES})
|
||||
link_directories(${${name}_LIBRARY_DIRS})
|
||||
include_directories(SYSTEM ${${name}_INCLUDE_DIRS})
|
||||
else()
|
||||
list(APPEND LIBS ${${name}_STATIC_LIBRARIES})
|
||||
link_directories(${${name}_STATIC_LIBRARY_DIRS})
|
||||
include_directories(SYSTEM ${${name}_STATIC_INCLUDE_DIRS})
|
||||
endif()
|
||||
else()
|
||||
if(WITH_SDL_LINK_SHARED)
|
||||
list(APPEND LIBS ${name}::${name})
|
||||
set_target_properties(${name}::${name} PROPERTIES SYSTEM TRUE)
|
||||
else()
|
||||
list(APPEND LIBS ${name}::${name}-static)
|
||||
set_target_properties(${name}::${name}-static PROPERTIES SYSTEM TRUE)
|
||||
endif()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
find_sdl_component(SDL2_ttf)
|
||||
|
||||
option(WITH_SDL_IMAGE_DIALOGS "Build with SDL_image support (recommended)" OFF)
|
||||
if(WITH_SDL_IMAGE_DIALOGS)
|
||||
find_sdl_component(SDL2_image)
|
||||
add_compile_definitions(WITH_SDL_IMAGE_DIALOGS)
|
||||
endif()
|
||||
|
||||
add_subdirectory(res)
|
||||
|
||||
add_library(sdl2-dialogs STATIC ${SRCS})
|
||||
|
||||
target_link_libraries(sdl2-dialogs PRIVATE ${LIBS})
|
||||
|
||||
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
|
||||
# add_subdirectory(test)
|
||||
endif()
|
||||
30
third_party/FreeRDP/client/SDL/SDL2/dialogs/res/CMakeLists.txt
vendored
Normal file
30
third_party/FreeRDP/client/SDL/SDL2/dialogs/res/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP SDL Client
|
||||
#
|
||||
# Copyright 2024 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2024 Thincast Technologies GmbH
|
||||
#
|
||||
# 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.
|
||||
|
||||
set(SRCS sdl2_resource_manager.cpp sdl2_resource_manager.hpp)
|
||||
|
||||
add_library(sdl2_client_res STATIC ${SRCS})
|
||||
|
||||
if(NOT WITH_SDL_LINK_SHARED)
|
||||
target_link_libraries(sdl2_client_res ${SDL2_STATIC_LIBRARIES})
|
||||
else()
|
||||
target_link_libraries(sdl2_client_res ${SDL2_LIBRARIES})
|
||||
endif()
|
||||
|
||||
set_target_properties(sdl2_client_res PROPERTIES POSITION_INDEPENDENT_CODE ON INTERPROCEDURAL_OPTIMIZATION OFF)
|
||||
target_link_libraries(sdl2_client_res sdl-common-client-res)
|
||||
37
third_party/FreeRDP/client/SDL/SDL2/dialogs/res/sdl2_resource_manager.cpp
vendored
Normal file
37
third_party/FreeRDP/client/SDL/SDL2/dialogs/res/sdl2_resource_manager.cpp
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
*
|
||||
* 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 "sdl2_resource_manager.hpp"
|
||||
#include <iostream>
|
||||
|
||||
SDL_RWops* SDL2ResourceManager::get(const std::string& type, const std::string& id)
|
||||
{
|
||||
if (useCompiledResources())
|
||||
{
|
||||
auto d = data(type, id);
|
||||
if (!d)
|
||||
return nullptr;
|
||||
|
||||
auto s = d->size();
|
||||
if (s > INT32_MAX)
|
||||
return nullptr;
|
||||
return SDL_RWFromConstMem(d->data(), static_cast<int>(s));
|
||||
}
|
||||
|
||||
auto name = filename(type, id);
|
||||
return SDL_RWFromFile(name.c_str(), "rb");
|
||||
}
|
||||
38
third_party/FreeRDP/client/SDL/SDL2/dialogs/res/sdl2_resource_manager.hpp
vendored
Normal file
38
third_party/FreeRDP/client/SDL/SDL2/dialogs/res/sdl2_resource_manager.hpp
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
|
||||
#include <res/sdl_resource_manager.hpp>
|
||||
|
||||
class SDL2ResourceManager : public SDLResourceManager
|
||||
{
|
||||
public:
|
||||
SDL2ResourceManager() = delete;
|
||||
SDL2ResourceManager(const SDL2ResourceManager& other) = delete;
|
||||
SDL2ResourceManager(const SDL2ResourceManager&& other) = delete;
|
||||
~SDL2ResourceManager() = delete;
|
||||
SDL2ResourceManager& operator=(const SDL2ResourceManager& other) = delete;
|
||||
SDL2ResourceManager& operator=(SDL2ResourceManager&& other) = delete;
|
||||
|
||||
static SDL_RWops* get(const std::string& type, const std::string& id);
|
||||
};
|
||||
71
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_button.cpp
vendored
Normal file
71
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_button.cpp
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client Channels
|
||||
*
|
||||
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2023 Thincast Technologies GmbH
|
||||
*
|
||||
* 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 <cassert>
|
||||
#include <utility>
|
||||
|
||||
#include "sdl_button.hpp"
|
||||
|
||||
static const SDL_Color buttonbackgroundcolor = { 0x69, 0x66, 0x63, 0xff };
|
||||
static const SDL_Color buttonhighlightcolor = { 0xcd, 0xca, 0x35, 0x60 };
|
||||
static const SDL_Color buttonmouseovercolor = { 0x66, 0xff, 0x66, 0x60 };
|
||||
static const SDL_Color buttonfontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
|
||||
SdlButton::SdlButton(SDL_Renderer* renderer, std::string label, int id, SDL_Rect rect)
|
||||
: SdlWidget(renderer, rect, false), _name(std::move(label)), _id(id)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
update_text(renderer, _name, buttonfontcolor, buttonbackgroundcolor);
|
||||
}
|
||||
|
||||
SdlButton::SdlButton(SdlButton&& other) noexcept = default;
|
||||
|
||||
SdlButton::~SdlButton() = default;
|
||||
|
||||
bool SdlButton::highlight(SDL_Renderer* renderer)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
std::vector<SDL_Color> colors = { buttonbackgroundcolor, buttonhighlightcolor };
|
||||
if (!fill(renderer, colors))
|
||||
return false;
|
||||
return update_text(renderer, _name, buttonfontcolor);
|
||||
}
|
||||
|
||||
bool SdlButton::mouseover(SDL_Renderer* renderer)
|
||||
{
|
||||
std::vector<SDL_Color> colors = { buttonbackgroundcolor, buttonmouseovercolor };
|
||||
if (!fill(renderer, colors))
|
||||
return false;
|
||||
return update_text(renderer, _name, buttonfontcolor);
|
||||
}
|
||||
|
||||
bool SdlButton::update(SDL_Renderer* renderer)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
return update_text(renderer, _name, buttonfontcolor, buttonbackgroundcolor);
|
||||
}
|
||||
|
||||
int SdlButton::id() const
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
27
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_button.hpp
vendored
Normal file
27
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_button.hpp
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "sdl_widget.hpp"
|
||||
|
||||
class SdlButton : public SdlWidget
|
||||
{
|
||||
public:
|
||||
SdlButton(SDL_Renderer* renderer, std::string label, int id, SDL_Rect rect);
|
||||
SdlButton(const SdlButton& other) = delete;
|
||||
SdlButton(SdlButton&& other) noexcept;
|
||||
~SdlButton() override;
|
||||
|
||||
SdlButton& operator=(const SdlButton& other) = delete;
|
||||
SdlButton& operator=(SdlButton&& other) = delete;
|
||||
|
||||
bool highlight(SDL_Renderer* renderer);
|
||||
bool mouseover(SDL_Renderer* renderer);
|
||||
bool update(SDL_Renderer* renderer);
|
||||
|
||||
[[nodiscard]] int id() const;
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
int _id;
|
||||
};
|
||||
112
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_buttons.cpp
vendored
Normal file
112
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_buttons.cpp
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
#include <winpr/cast.h>
|
||||
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
static const Uint32 hpadding = 10;
|
||||
|
||||
SdlButtonList::~SdlButtonList() = default;
|
||||
|
||||
bool SdlButtonList::populate(SDL_Renderer* renderer, const std::vector<std::string>& labels,
|
||||
const std::vector<int>& ids, Sint32 total_width, Sint32 offsetY,
|
||||
Sint32 width, Sint32 height)
|
||||
{
|
||||
assert(renderer);
|
||||
assert(width >= 0);
|
||||
assert(height >= 0);
|
||||
assert(labels.size() == ids.size());
|
||||
|
||||
_list.clear();
|
||||
size_t button_width =
|
||||
ids.size() * (WINPR_ASSERTING_INT_CAST(uint32_t, width) + hpadding) + hpadding;
|
||||
size_t offsetX =
|
||||
WINPR_ASSERTING_INT_CAST(uint32_t, total_width) -
|
||||
std::min<size_t>(WINPR_ASSERTING_INT_CAST(uint32_t, total_width), button_width);
|
||||
for (size_t x = 0; x < ids.size(); x++)
|
||||
{
|
||||
const size_t curOffsetX = offsetX + x * (static_cast<size_t>(width) + hpadding);
|
||||
const SDL_Rect rect = { static_cast<int>(curOffsetX), offsetY, width, height };
|
||||
_list.emplace_back(renderer, labels.at(x), ids.at(x), rect);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SdlButton* SdlButtonList::get_selected(const SDL_MouseButtonEvent& button)
|
||||
{
|
||||
const Sint32 x = button.x;
|
||||
const Sint32 y = button.y;
|
||||
|
||||
return get_selected(x, y);
|
||||
}
|
||||
|
||||
SdlButton* SdlButtonList::get_selected(Sint32 x, Sint32 y)
|
||||
{
|
||||
for (auto& btn : _list)
|
||||
{
|
||||
auto r = btn.rect();
|
||||
if ((x >= r.x) && (x <= r.x + r.w) && (y >= r.y) && (y <= r.y + r.h))
|
||||
return &btn;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool SdlButtonList::set_highlight_next(bool reset)
|
||||
{
|
||||
if (reset)
|
||||
_highlighted = nullptr;
|
||||
else
|
||||
{
|
||||
auto next = _highlight_index++;
|
||||
_highlight_index %= _list.size();
|
||||
auto& element = _list.at(next);
|
||||
_highlighted = &element;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SdlButtonList::set_highlight(size_t index)
|
||||
{
|
||||
if (index >= _list.size())
|
||||
{
|
||||
_highlighted = nullptr;
|
||||
return false;
|
||||
}
|
||||
auto& element = _list.at(index);
|
||||
_highlighted = &element;
|
||||
_highlight_index = ++index % _list.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SdlButtonList::set_mouseover(Sint32 x, Sint32 y)
|
||||
{
|
||||
_mouseover = get_selected(x, y);
|
||||
return _mouseover != nullptr;
|
||||
}
|
||||
|
||||
void SdlButtonList::clear()
|
||||
{
|
||||
_list.clear();
|
||||
_mouseover = nullptr;
|
||||
_highlighted = nullptr;
|
||||
_highlight_index = 0;
|
||||
}
|
||||
|
||||
bool SdlButtonList::update(SDL_Renderer* renderer)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
for (auto& btn : _list)
|
||||
{
|
||||
if (!btn.update(renderer))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_highlighted)
|
||||
_highlighted->highlight(renderer);
|
||||
|
||||
if (_mouseover)
|
||||
_mouseover->mouseover(renderer);
|
||||
return true;
|
||||
}
|
||||
38
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_buttons.hpp
vendored
Normal file
38
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_buttons.hpp
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#include "sdl_button.hpp"
|
||||
|
||||
class SdlButtonList
|
||||
{
|
||||
public:
|
||||
SdlButtonList() = default;
|
||||
virtual ~SdlButtonList();
|
||||
|
||||
bool populate(SDL_Renderer* renderer, const std::vector<std::string>& labels,
|
||||
const std::vector<int>& ids, Sint32 total_width, Sint32 offsetY, Sint32 width,
|
||||
Sint32 height);
|
||||
|
||||
bool update(SDL_Renderer* renderer);
|
||||
SdlButton* get_selected(const SDL_MouseButtonEvent& button);
|
||||
SdlButton* get_selected(Sint32 x, Sint32 y);
|
||||
|
||||
bool set_highlight_next(bool reset = false);
|
||||
bool set_highlight(size_t index);
|
||||
bool set_mouseover(Sint32 x, Sint32 y);
|
||||
|
||||
void clear();
|
||||
|
||||
SdlButtonList(const SdlButtonList& other) = delete;
|
||||
SdlButtonList(SdlButtonList&& other) = delete;
|
||||
SdlButtonList& operator=(const SdlButtonList& other) = delete;
|
||||
SdlButtonList& operator=(SdlButtonList&& other) = delete;
|
||||
|
||||
private:
|
||||
std::vector<SdlButton> _list;
|
||||
SdlButton* _highlighted = nullptr;
|
||||
size_t _highlight_index = 0;
|
||||
SdlButton* _mouseover = nullptr;
|
||||
};
|
||||
539
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_connection_dialog.cpp
vendored
Normal file
539
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_connection_dialog.cpp
vendored
Normal file
@@ -0,0 +1,539 @@
|
||||
/**
|
||||
* 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 <cassert>
|
||||
#include <thread>
|
||||
|
||||
#include "sdl_connection_dialog.hpp"
|
||||
#include "../sdl_utils.hpp"
|
||||
#include "../sdl_freerdp.hpp"
|
||||
#include "res/sdl2_resource_manager.hpp"
|
||||
|
||||
static const SDL_Color backgroundcolor = { 0x38, 0x36, 0x35, 0xff };
|
||||
static const SDL_Color textcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
static const SDL_Color infocolor = { 0x43, 0xe0, 0x0f, 0x60 };
|
||||
static const SDL_Color warncolor = { 0xcd, 0xca, 0x35, 0x60 };
|
||||
static const SDL_Color errorcolor = { 0xf7, 0x22, 0x30, 0x60 };
|
||||
|
||||
static const Uint32 vpadding = 5;
|
||||
static const Uint32 hpadding = 5;
|
||||
|
||||
SDLConnectionDialog::SDLConnectionDialog(rdpContext* context) : _context(context)
|
||||
{
|
||||
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
|
||||
hide();
|
||||
}
|
||||
|
||||
SDLConnectionDialog::~SDLConnectionDialog()
|
||||
{
|
||||
resetTimer();
|
||||
destroyWindow();
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::visible() const
|
||||
{
|
||||
if (!_window || !_renderer)
|
||||
return false;
|
||||
|
||||
auto flags = SDL_GetWindowFlags(_window);
|
||||
return (flags & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED)) == 0;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::setTitle(const char* fmt, ...)
|
||||
{
|
||||
std::scoped_lock 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);
|
||||
if (!rc)
|
||||
return rc;
|
||||
return setTimer();
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::show()
|
||||
{
|
||||
std::scoped_lock lock(_mux);
|
||||
return show(_type_active);
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::hide()
|
||||
{
|
||||
std::scoped_lock lock(_mux);
|
||||
return show(MSG_DISCARD);
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::running() const
|
||||
{
|
||||
std::scoped_lock lock(_mux);
|
||||
return _running;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::update()
|
||||
{
|
||||
std::scoped_lock lock(_mux);
|
||||
switch (_type)
|
||||
{
|
||||
case MSG_INFO:
|
||||
case MSG_WARN:
|
||||
case MSG_ERROR:
|
||||
_type_active = _type;
|
||||
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.begin()->second.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)
|
||||
{
|
||||
std::scoped_lock lock(_mux);
|
||||
if (!renderer)
|
||||
return false;
|
||||
|
||||
if (!clearWindow(renderer))
|
||||
return false;
|
||||
|
||||
for (auto& btn : _list)
|
||||
{
|
||||
if (!btn.widget.update_text(renderer, _msg, btn.fgcolor, btn.bgcolor))
|
||||
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:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_KeyboardEvent&>(event);
|
||||
update(_renderer);
|
||||
switch (event.key.keysym.sym)
|
||||
{
|
||||
case SDLK_RETURN:
|
||||
case SDLK_RETURN2:
|
||||
case SDLK_ESCAPE:
|
||||
case SDLK_KP_ENTER:
|
||||
if (event.type == SDL_KEYUP)
|
||||
{
|
||||
freerdp_abort_connect_context(_context);
|
||||
sdl_push_quit();
|
||||
}
|
||||
break;
|
||||
case SDLK_TAB:
|
||||
_buttons.set_highlight_next();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_MOUSEMOTION:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_MouseMotionEvent&>(event);
|
||||
|
||||
_buttons.set_mouseover(event.button.x, event.button.y);
|
||||
update(_renderer);
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_MouseButtonEvent&>(event);
|
||||
update(_renderer);
|
||||
|
||||
auto button = _buttons.get_selected(event.button);
|
||||
if (button)
|
||||
{
|
||||
if (event.type == SDL_MOUSEBUTTONUP)
|
||||
{
|
||||
freerdp_abort_connect_context(_context);
|
||||
sdl_push_quit();
|
||||
}
|
||||
}
|
||||
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_MouseWheelEvent&>(event);
|
||||
update(_renderer);
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_FINGERUP:
|
||||
case SDL_FINGERDOWN:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_TouchFingerEvent&>(event);
|
||||
update(_renderer);
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||
return windowID == ev.windowID;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_WindowEvent&>(event);
|
||||
switch (ev.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
if (windowID == ev.windowID)
|
||||
{
|
||||
freerdp_abort_connect_context(_context);
|
||||
sdl_push_quit();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
update(_renderer);
|
||||
setModal();
|
||||
break;
|
||||
}
|
||||
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::createWindow()
|
||||
{
|
||||
destroyWindow();
|
||||
|
||||
const int widget_height = 50;
|
||||
const int widget_width = 600;
|
||||
const int total_height = 300;
|
||||
|
||||
auto flags = WINPR_ASSERTING_INT_CAST(
|
||||
uint32_t, SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS);
|
||||
auto rc = SDL_CreateWindowAndRenderer(widget_width, total_height, flags, &_window, &_renderer);
|
||||
if (rc != 0)
|
||||
{
|
||||
widget_log_error(rc, "SDL_CreateWindowAndRenderer");
|
||||
return false;
|
||||
}
|
||||
SDL_SetWindowTitle(_window, _title.c_str());
|
||||
setModal();
|
||||
|
||||
SDL_Color res_bgcolor;
|
||||
switch (_type_active)
|
||||
{
|
||||
case MSG_INFO:
|
||||
res_bgcolor = infocolor;
|
||||
break;
|
||||
case MSG_WARN:
|
||||
res_bgcolor = warncolor;
|
||||
break;
|
||||
case MSG_ERROR:
|
||||
res_bgcolor = errorcolor;
|
||||
break;
|
||||
case MSG_DISCARD:
|
||||
default:
|
||||
res_bgcolor = backgroundcolor;
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
std::string res_name;
|
||||
switch (_type_active)
|
||||
{
|
||||
case MSG_INFO:
|
||||
res_name = "icon_info.svg";
|
||||
break;
|
||||
case MSG_WARN:
|
||||
res_name = "icon_warning.svg";
|
||||
break;
|
||||
case MSG_ERROR:
|
||||
res_name = "icon_error.svg";
|
||||
break;
|
||||
case MSG_DISCARD:
|
||||
default:
|
||||
res_name = "";
|
||||
break;
|
||||
}
|
||||
|
||||
int height = (total_height - 3ul * vpadding) / 2ul;
|
||||
SDL_Rect iconRect{ hpadding, vpadding, widget_width / 4ul - 2ul * hpadding, height };
|
||||
widget_cfg_t icon{ textcolor,
|
||||
res_bgcolor,
|
||||
{ _renderer, iconRect,
|
||||
SDL2ResourceManager::get(SDLResourceManager::typeImages(), res_name) } };
|
||||
_list.emplace_back(std::move(icon));
|
||||
|
||||
iconRect.y += height;
|
||||
|
||||
widget_cfg_t logo{ textcolor,
|
||||
backgroundcolor,
|
||||
{ _renderer, iconRect,
|
||||
SDL2ResourceManager::get(SDLResourceManager::typeImages(),
|
||||
"FreeRDP_Icon.svg") } };
|
||||
_list.emplace_back(std::move(logo));
|
||||
|
||||
SDL_Rect rect = { widget_width / 4ul, vpadding, widget_width * 3ul / 4ul,
|
||||
total_height - 3ul * vpadding - widget_height };
|
||||
#else
|
||||
SDL_Rect rect = { hpadding, vpadding, widget_width - 2ul * hpadding,
|
||||
total_height - 2ul * vpadding };
|
||||
#endif
|
||||
|
||||
widget_cfg_t w{ textcolor, backgroundcolor, { _renderer, rect, false } };
|
||||
w.widget.set_wrap(true, widget_width);
|
||||
_list.emplace_back(std::move(w));
|
||||
rect.y += widget_height + vpadding;
|
||||
|
||||
const std::vector<int> buttonids = { 1 };
|
||||
const std::vector<std::string> buttonlabels = { "cancel" };
|
||||
_buttons.populate(_renderer, buttonlabels, buttonids, widget_width,
|
||||
total_height - widget_height - vpadding,
|
||||
static_cast<Sint32>(widget_width / 2), static_cast<Sint32>(widget_height));
|
||||
_buttons.set_highlight(0);
|
||||
|
||||
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::scoped_lock 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(WINPR_ASSERTING_INT_CAST(size_t, size));
|
||||
|
||||
va_list copy = {};
|
||||
va_copy(copy, ap);
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
|
||||
size = vsnprintf(res.data(), res.size(), fmt, copy);
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
va_end(copy);
|
||||
|
||||
} while ((size > 0) && (static_cast<size_t>(size) > res.size()));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::setTimer(Uint32 timeoutMS)
|
||||
{
|
||||
std::scoped_lock 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([[maybe_unused]] Uint32 intervalMS, void* pvthis)
|
||||
{
|
||||
auto self = static_cast<SDLConnectionDialog*>(pvthis);
|
||||
self->hide();
|
||||
self->_running = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDLConnectionDialogHider::SDLConnectionDialogHider(freerdp* instance)
|
||||
: SDLConnectionDialogHider(get(instance))
|
||||
{
|
||||
}
|
||||
|
||||
SDLConnectionDialogHider::SDLConnectionDialogHider(rdpContext* context)
|
||||
: SDLConnectionDialogHider(get(context))
|
||||
{
|
||||
}
|
||||
|
||||
SDLConnectionDialogHider::SDLConnectionDialogHider(SDLConnectionDialog* dialog) : _dialog(dialog)
|
||||
{
|
||||
if (_dialog)
|
||||
{
|
||||
_visible = _dialog->visible();
|
||||
if (_visible)
|
||||
{
|
||||
_dialog->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDLConnectionDialogHider::~SDLConnectionDialogHider()
|
||||
{
|
||||
if (_dialog && _visible)
|
||||
{
|
||||
_dialog->show();
|
||||
}
|
||||
}
|
||||
|
||||
SDLConnectionDialog* SDLConnectionDialogHider::get(freerdp* instance)
|
||||
{
|
||||
if (!instance)
|
||||
return nullptr;
|
||||
return get(instance->context);
|
||||
}
|
||||
|
||||
SDLConnectionDialog* SDLConnectionDialogHider::get(rdpContext* context)
|
||||
{
|
||||
auto sdl = get_context(context);
|
||||
if (!sdl)
|
||||
return nullptr;
|
||||
return sdl->connection_dialog.get();
|
||||
}
|
||||
131
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_connection_dialog.hpp
vendored
Normal file
131
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_connection_dialog.hpp
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#include "sdl_widget.hpp"
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
class SDLConnectionDialog
|
||||
{
|
||||
public:
|
||||
explicit SDLConnectionDialog(rdpContext* context);
|
||||
SDLConnectionDialog(const SDLConnectionDialog& other) = delete;
|
||||
SDLConnectionDialog(const SDLConnectionDialog&& other) = delete;
|
||||
virtual ~SDLConnectionDialog();
|
||||
|
||||
SDLConnectionDialog& operator=(const SDLConnectionDialog& other) = delete;
|
||||
SDLConnectionDialog& operator=(SDLConnectionDialog&& other) = delete;
|
||||
|
||||
bool visible() const;
|
||||
|
||||
bool setTitle(const char* fmt, ...);
|
||||
bool showInfo(const char* fmt, ...);
|
||||
bool showWarn(const char* fmt, ...);
|
||||
bool showError(const char* fmt, ...);
|
||||
|
||||
bool show();
|
||||
bool hide();
|
||||
|
||||
bool running() const;
|
||||
bool wait(bool ignoreRdpContextQuit = false);
|
||||
|
||||
bool handle(const SDL_Event& event);
|
||||
|
||||
private:
|
||||
enum MsgType
|
||||
{
|
||||
MSG_NONE,
|
||||
MSG_INFO,
|
||||
MSG_WARN,
|
||||
MSG_ERROR,
|
||||
MSG_DISCARD
|
||||
};
|
||||
|
||||
bool createWindow();
|
||||
void destroyWindow();
|
||||
|
||||
bool update();
|
||||
|
||||
bool setModal();
|
||||
|
||||
static bool clearWindow(SDL_Renderer* renderer);
|
||||
|
||||
bool update(SDL_Renderer* renderer);
|
||||
|
||||
bool show(MsgType type, const char* fmt, va_list ap);
|
||||
bool show(MsgType type);
|
||||
|
||||
static std::string print(const char* fmt, va_list ap);
|
||||
bool setTimer(Uint32 timeoutMS = 15000);
|
||||
void resetTimer();
|
||||
|
||||
static Uint32 timeout(Uint32 intervalMS, void* pvthis);
|
||||
|
||||
struct widget_cfg_t
|
||||
{
|
||||
SDL_Color fgcolor = {};
|
||||
SDL_Color bgcolor = {};
|
||||
SdlWidget widget;
|
||||
};
|
||||
|
||||
rdpContext* _context = nullptr;
|
||||
SDL_Window* _window = nullptr;
|
||||
SDL_Renderer* _renderer = nullptr;
|
||||
mutable std::mutex _mux;
|
||||
std::string _title;
|
||||
std::string _msg;
|
||||
MsgType _type = MSG_NONE;
|
||||
MsgType _type_active = MSG_NONE;
|
||||
SDL_TimerID _timer = -1;
|
||||
bool _running = false;
|
||||
std::vector<widget_cfg_t> _list;
|
||||
SdlButtonList _buttons;
|
||||
};
|
||||
|
||||
class SDLConnectionDialogHider
|
||||
{
|
||||
public:
|
||||
explicit SDLConnectionDialogHider(freerdp* instance);
|
||||
explicit SDLConnectionDialogHider(rdpContext* context);
|
||||
|
||||
explicit SDLConnectionDialogHider(SDLConnectionDialog* dialog);
|
||||
SDLConnectionDialogHider(const SDLConnectionDialogHider& other) = delete;
|
||||
SDLConnectionDialogHider(SDLConnectionDialogHider&& other) = delete;
|
||||
SDLConnectionDialogHider& operator=(const SDLConnectionDialogHider& other) = delete;
|
||||
SDLConnectionDialogHider& operator=(SDLConnectionDialogHider&& other) = delete;
|
||||
|
||||
~SDLConnectionDialogHider();
|
||||
|
||||
private:
|
||||
SDLConnectionDialog* get(freerdp* instance);
|
||||
static SDLConnectionDialog* get(rdpContext* context);
|
||||
|
||||
SDLConnectionDialog* _dialog = nullptr;
|
||||
bool _visible = false;
|
||||
};
|
||||
626
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_dialogs.cpp
vendored
Normal file
626
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_dialogs.cpp
vendored
Normal file
@@ -0,0 +1,626 @@
|
||||
/**
|
||||
* 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 <vector>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#include <freerdp/utils/smartcardlogon.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "../sdl_freerdp.hpp"
|
||||
#include "sdl_dialogs.hpp"
|
||||
#include "sdl_input.hpp"
|
||||
#include "sdl_input_widgets.hpp"
|
||||
#include "sdl_select.hpp"
|
||||
#include "sdl_selectlist.hpp"
|
||||
|
||||
enum
|
||||
{
|
||||
SHOW_DIALOG_ACCEPT_REJECT = 1,
|
||||
SHOW_DIALOG_TIMED_ACCEPT = 2
|
||||
};
|
||||
|
||||
static const char* type_str_for_flags(UINT32 flags)
|
||||
{
|
||||
const char* type = "RDP-Server";
|
||||
|
||||
if (flags & VERIFY_CERT_FLAG_GATEWAY)
|
||||
type = "RDP-Gateway";
|
||||
|
||||
if (flags & VERIFY_CERT_FLAG_REDIRECT)
|
||||
type = "RDP-Redirect";
|
||||
return type;
|
||||
}
|
||||
|
||||
static BOOL sdl_wait_for_result(rdpContext* context, Uint32 type, SDL_Event* result)
|
||||
{
|
||||
const SDL_Event empty = {};
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(result);
|
||||
|
||||
while (!freerdp_shall_disconnect_context(context))
|
||||
{
|
||||
*result = empty;
|
||||
const int rc = SDL_PeepEvents(result, 1, SDL_GETEVENT, type, type);
|
||||
if (rc > 0)
|
||||
return TRUE;
|
||||
Sleep(1);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int sdl_show_dialog(rdpContext* context, const char* title, const char* message,
|
||||
Sint32 flags)
|
||||
{
|
||||
SDL_Event event = {};
|
||||
|
||||
if (!sdl_push_user_event(SDL_USEREVENT_SHOW_DIALOG, title, message, flags))
|
||||
return 0;
|
||||
|
||||
if (!sdl_wait_for_result(context, SDL_USEREVENT_SHOW_RESULT, &event))
|
||||
return 0;
|
||||
|
||||
return event.user.code;
|
||||
}
|
||||
|
||||
BOOL sdl_authenticate_ex(freerdp* instance, char** username, char** password, char** domain,
|
||||
rdp_auth_reason reason)
|
||||
{
|
||||
SDL_Event event = {};
|
||||
BOOL res = FALSE;
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
|
||||
const char* target = freerdp_settings_get_server_name(instance->context->settings);
|
||||
switch (reason)
|
||||
{
|
||||
case AUTH_NLA:
|
||||
break;
|
||||
|
||||
case AUTH_TLS:
|
||||
case AUTH_RDP:
|
||||
case AUTH_SMARTCARD_PIN: /* in this case password is pin code */
|
||||
if ((*username) && (*password))
|
||||
return TRUE;
|
||||
break;
|
||||
case GW_AUTH_HTTP:
|
||||
case GW_AUTH_RDG:
|
||||
case GW_AUTH_RPC:
|
||||
target =
|
||||
freerdp_settings_get_string(instance->context->settings, FreeRDP_GatewayHostname);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
char* title = nullptr;
|
||||
size_t titlesize = 0;
|
||||
winpr_asprintf(&title, &titlesize, "Credentials required for %s", target);
|
||||
|
||||
CStringPtr scope(title, free);
|
||||
char* u = nullptr;
|
||||
char* d = nullptr;
|
||||
char* p = nullptr;
|
||||
|
||||
assert(username);
|
||||
assert(domain);
|
||||
assert(password);
|
||||
|
||||
u = *username;
|
||||
d = *domain;
|
||||
p = *password;
|
||||
|
||||
if (!sdl_push_user_event(SDL_USEREVENT_AUTH_DIALOG, title, u, d, p, reason))
|
||||
return res;
|
||||
|
||||
if (!sdl_wait_for_result(instance->context, SDL_USEREVENT_AUTH_RESULT, &event))
|
||||
return res;
|
||||
|
||||
auto arg = reinterpret_cast<SDL_UserAuthArg*>(event.padding);
|
||||
|
||||
res = arg->result > 0;
|
||||
|
||||
free(*username);
|
||||
free(*domain);
|
||||
free(*password);
|
||||
*username = arg->user;
|
||||
*domain = arg->domain;
|
||||
*password = arg->password;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
BOOL sdl_choose_smartcard(freerdp* instance, SmartcardCertInfo** cert_list, DWORD count,
|
||||
DWORD* choice, BOOL gateway)
|
||||
{
|
||||
BOOL res = FALSE;
|
||||
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(cert_list);
|
||||
WINPR_ASSERT(choice);
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
std::vector<std::string> strlist;
|
||||
std::vector<const char*> list;
|
||||
for (DWORD i = 0; i < count; i++)
|
||||
{
|
||||
const SmartcardCertInfo* cert = cert_list[i];
|
||||
char* reader = ConvertWCharToUtf8Alloc(cert->reader, nullptr);
|
||||
char* container_name = ConvertWCharToUtf8Alloc(cert->containerName, nullptr);
|
||||
|
||||
char* msg = nullptr;
|
||||
size_t len = 0;
|
||||
|
||||
winpr_asprintf(&msg, &len,
|
||||
"%s\n\tReader: %s\n\tUser: %s@%s\n\tSubject: %s\n\tIssuer: %s\n\tUPN: %s",
|
||||
container_name, reader, cert->userHint, cert->domainHint, cert->subject,
|
||||
cert->issuer, cert->upn);
|
||||
|
||||
strlist.emplace_back(msg);
|
||||
free(msg);
|
||||
free(reader);
|
||||
free(container_name);
|
||||
|
||||
auto& m = strlist.back();
|
||||
list.push_back(m.c_str());
|
||||
}
|
||||
|
||||
SDL_Event event = {};
|
||||
const char* title = "Select a logon smartcard certificate";
|
||||
if (gateway)
|
||||
title = "Select a gateway logon smartcard certificate";
|
||||
if (!sdl_push_user_event(SDL_USEREVENT_SCARD_DIALOG, title, list.data(), count))
|
||||
return res;
|
||||
|
||||
if (!sdl_wait_for_result(instance->context, SDL_USEREVENT_SCARD_RESULT, &event))
|
||||
return res;
|
||||
|
||||
res = (event.user.code >= 0);
|
||||
*choice = static_cast<DWORD>(event.user.code);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
SSIZE_T sdl_retry_dialog(freerdp* instance, const char* what, size_t current,
|
||||
[[maybe_unused]] void* userarg)
|
||||
{
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(instance->context);
|
||||
WINPR_ASSERT(what);
|
||||
|
||||
auto sdl = get_context(instance->context);
|
||||
auto settings = instance->context->settings;
|
||||
const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
|
||||
std::scoped_lock lock(sdl->critical);
|
||||
if (!sdl->connection_dialog)
|
||||
return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
|
||||
|
||||
sdl->connection_dialog->setTitle("Retry connection to %s",
|
||||
freerdp_settings_get_server_name(instance->context->settings));
|
||||
|
||||
if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
|
||||
{
|
||||
sdl->connection_dialog->showError("Unknown module %s, aborting", what);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (current == 0)
|
||||
{
|
||||
if (strcmp(what, "arm-transport") == 0)
|
||||
sdl->connection_dialog->showWarn("[%s] Starting your VM. It may take up to 5 minutes",
|
||||
what);
|
||||
}
|
||||
|
||||
const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
|
||||
|
||||
if (!enabled)
|
||||
{
|
||||
sdl->connection_dialog->showError(
|
||||
"Automatic reconnection disabled, terminating. Try to connect again later");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
|
||||
|
||||
if (current >= max)
|
||||
{
|
||||
sdl->connection_dialog->showError(
|
||||
"[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
|
||||
"tech support for help if this keeps happening.",
|
||||
what);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sdl->connection_dialog->showInfo("[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz
|
||||
"ms before next attempt",
|
||||
what, current + 1, max, delay);
|
||||
return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
|
||||
}
|
||||
|
||||
BOOL sdl_present_gateway_message(freerdp* instance, [[maybe_unused]] UINT32 type,
|
||||
BOOL isDisplayMandatory, BOOL isConsentMandatory, size_t length,
|
||||
const WCHAR* wmessage)
|
||||
{
|
||||
if (!isDisplayMandatory)
|
||||
return TRUE;
|
||||
|
||||
char* title = nullptr;
|
||||
size_t len = 0;
|
||||
winpr_asprintf(&title, &len, "[gateway]");
|
||||
|
||||
Sint32 flags = 0;
|
||||
if (isConsentMandatory)
|
||||
flags = SHOW_DIALOG_ACCEPT_REJECT;
|
||||
else if (isDisplayMandatory)
|
||||
flags = SHOW_DIALOG_TIMED_ACCEPT;
|
||||
char* message = ConvertWCharNToUtf8Alloc(wmessage, length, nullptr);
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
const int rc = sdl_show_dialog(instance->context, title, message, flags);
|
||||
free(title);
|
||||
free(message);
|
||||
return rc > 0;
|
||||
}
|
||||
|
||||
int sdl_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
|
||||
{
|
||||
int rc = -1;
|
||||
const char* str_data = freerdp_get_logon_error_info_data(data);
|
||||
const char* str_type = freerdp_get_logon_error_info_type(type);
|
||||
|
||||
if (!instance || !instance->context)
|
||||
return -1;
|
||||
|
||||
/* ignore LOGON_MSG_SESSION_CONTINUE message */
|
||||
if (type == LOGON_MSG_SESSION_CONTINUE)
|
||||
return 0;
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
|
||||
char* title = nullptr;
|
||||
size_t tlen = 0;
|
||||
winpr_asprintf(&title, &tlen, "[%s] info",
|
||||
freerdp_settings_get_server_name(instance->context->settings));
|
||||
|
||||
char* message = nullptr;
|
||||
size_t mlen = 0;
|
||||
winpr_asprintf(&message, &mlen, "Logon Error Info %s [%s]", str_data, str_type);
|
||||
|
||||
rc = sdl_show_dialog(instance->context, title, message, SHOW_DIALOG_ACCEPT_REJECT);
|
||||
free(title);
|
||||
free(message);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static DWORD sdl_show_ceritifcate_dialog(rdpContext* context, const char* title,
|
||||
const char* message)
|
||||
{
|
||||
SDLConnectionDialogHider hider(context);
|
||||
if (!sdl_push_user_event(SDL_USEREVENT_CERT_DIALOG, title, message))
|
||||
return 0;
|
||||
|
||||
SDL_Event event = {};
|
||||
if (!sdl_wait_for_result(context, SDL_USEREVENT_CERT_RESULT, &event))
|
||||
return 0;
|
||||
return static_cast<DWORD>(event.user.code);
|
||||
}
|
||||
|
||||
static char* sdl_pem_cert(const char* pem)
|
||||
{
|
||||
rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
|
||||
if (!cert)
|
||||
return nullptr;
|
||||
|
||||
char* fp = freerdp_certificate_get_fingerprint(cert);
|
||||
char* start = freerdp_certificate_get_validity(cert, TRUE);
|
||||
char* end = freerdp_certificate_get_validity(cert, FALSE);
|
||||
freerdp_certificate_free(cert);
|
||||
|
||||
char* str = nullptr;
|
||||
size_t slen = 0;
|
||||
winpr_asprintf(&str, &slen,
|
||||
"Valid from: %s\n"
|
||||
"Valid to: %s\n"
|
||||
"Thumbprint: %s\n",
|
||||
start, end, fp);
|
||||
free(fp);
|
||||
free(start);
|
||||
free(end);
|
||||
return str;
|
||||
}
|
||||
|
||||
DWORD sdl_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
|
||||
const char* common_name, const char* subject,
|
||||
const char* issuer, const char* new_fingerprint,
|
||||
const char* old_subject, const char* old_issuer,
|
||||
const char* old_fingerprint, DWORD flags)
|
||||
{
|
||||
const char* type = type_str_for_flags(flags);
|
||||
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(instance->context);
|
||||
WINPR_ASSERT(instance->context->settings);
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
/* Newer versions of FreeRDP allow exposing the whole PEM by setting
|
||||
* FreeRDP_CertificateCallbackPreferPEM to TRUE
|
||||
*/
|
||||
char* new_fp_str = nullptr;
|
||||
size_t len = 0;
|
||||
if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
|
||||
new_fp_str = sdl_pem_cert(new_fingerprint);
|
||||
else
|
||||
winpr_asprintf(&new_fp_str, &len, "Thumbprint: %s\n", new_fingerprint);
|
||||
|
||||
/* Newer versions of FreeRDP allow exposing the whole PEM by setting
|
||||
* FreeRDP_CertificateCallbackPreferPEM to TRUE
|
||||
*/
|
||||
char* old_fp_str = nullptr;
|
||||
size_t olen = 0;
|
||||
if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
|
||||
old_fp_str = sdl_pem_cert(old_fingerprint);
|
||||
else
|
||||
winpr_asprintf(&old_fp_str, &olen, "Thumbprint: %s\n", old_fingerprint);
|
||||
|
||||
const char* collission_str = "";
|
||||
if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
|
||||
{
|
||||
collission_str =
|
||||
"A matching entry with legacy SHA1 was found in local known_hosts2 store.\n"
|
||||
"If you just upgraded from a FreeRDP version before 2.0 this is expected.\n"
|
||||
"The hashing algorithm has been upgraded from SHA1 to SHA256.\n"
|
||||
"All manually accepted certificates must be reconfirmed!\n"
|
||||
"\n";
|
||||
}
|
||||
|
||||
char* title = nullptr;
|
||||
size_t tlen = 0;
|
||||
winpr_asprintf(&title, &tlen, "Certificate for %s:%" PRIu16 " (%s) has changed", host, port,
|
||||
type);
|
||||
|
||||
char* message = nullptr;
|
||||
size_t mlen = 0;
|
||||
winpr_asprintf(&message, &mlen,
|
||||
"New Certificate details:\n"
|
||||
"Common Name: %s\n"
|
||||
"Subject: %s\n"
|
||||
"Issuer: %s\n"
|
||||
"%s\n"
|
||||
"Old Certificate details:\n"
|
||||
"Subject: %s\n"
|
||||
"Issuer: %s\n"
|
||||
"%s\n"
|
||||
"%s\n"
|
||||
"The above X.509 certificate does not match the certificate used for previous "
|
||||
"connections.\n"
|
||||
"This may indicate that the certificate has been tampered with.\n"
|
||||
"Please contact the administrator of the RDP server and clarify.\n",
|
||||
common_name, subject, issuer, new_fp_str, old_subject, old_issuer, old_fp_str,
|
||||
collission_str);
|
||||
|
||||
const DWORD rc = sdl_show_ceritifcate_dialog(instance->context, title, message);
|
||||
free(title);
|
||||
free(message);
|
||||
free(new_fp_str);
|
||||
free(old_fp_str);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
DWORD sdl_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
|
||||
const char* common_name, const char* subject, const char* issuer,
|
||||
const char* fingerprint, DWORD flags)
|
||||
{
|
||||
const char* type = type_str_for_flags(flags);
|
||||
|
||||
/* Newer versions of FreeRDP allow exposing the whole PEM by setting
|
||||
* FreeRDP_CertificateCallbackPreferPEM to TRUE
|
||||
*/
|
||||
char* fp_str = nullptr;
|
||||
size_t len = 0;
|
||||
if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
|
||||
fp_str = sdl_pem_cert(fingerprint);
|
||||
else
|
||||
winpr_asprintf(&fp_str, &len, "Thumbprint: %s\n", fingerprint);
|
||||
|
||||
char* title = nullptr;
|
||||
size_t tlen = 0;
|
||||
winpr_asprintf(&title, &tlen, "New certificate for %s:%" PRIu16 " (%s)", host, port, type);
|
||||
|
||||
char* message = nullptr;
|
||||
size_t mlen = 0;
|
||||
winpr_asprintf(
|
||||
&message, &mlen,
|
||||
"Common Name: %s\n"
|
||||
"Subject: %s\n"
|
||||
"Issuer: %s\n"
|
||||
"%s\n"
|
||||
"The above X.509 certificate could not be verified, possibly because you do not have\n"
|
||||
"the CA certificate in your certificate store, or the certificate has expired.\n"
|
||||
"Please look at the OpenSSL documentation on how to add a private CA to the store.\n",
|
||||
common_name, subject, issuer, fp_str);
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
const DWORD rc = sdl_show_ceritifcate_dialog(instance->context, title, message);
|
||||
free(fp_str);
|
||||
free(title);
|
||||
free(message);
|
||||
return rc;
|
||||
}
|
||||
|
||||
BOOL sdl_cert_dialog_show(const char* title, const char* message)
|
||||
{
|
||||
int buttonid = -1;
|
||||
enum
|
||||
{
|
||||
BUTTONID_CERT_ACCEPT_PERMANENT = 23,
|
||||
BUTTONID_CERT_ACCEPT_TEMPORARY = 24,
|
||||
BUTTONID_CERT_DENY = 25
|
||||
};
|
||||
const SDL_MessageBoxButtonData buttons[] = {
|
||||
{ 0, BUTTONID_CERT_ACCEPT_PERMANENT, "permanent" },
|
||||
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_CERT_ACCEPT_TEMPORARY, "temporary" },
|
||||
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_CERT_DENY, "cancel" }
|
||||
};
|
||||
|
||||
const SDL_MessageBoxData data = { SDL_MESSAGEBOX_WARNING, nullptr, title, message,
|
||||
ARRAYSIZE(buttons), buttons, nullptr };
|
||||
const int rc = SDL_ShowMessageBox(&data, &buttonid);
|
||||
|
||||
Sint32 value = -1;
|
||||
if (rc < 0)
|
||||
value = 0;
|
||||
else
|
||||
{
|
||||
switch (buttonid)
|
||||
{
|
||||
case BUTTONID_CERT_ACCEPT_PERMANENT:
|
||||
value = 1;
|
||||
break;
|
||||
case BUTTONID_CERT_ACCEPT_TEMPORARY:
|
||||
value = 2;
|
||||
break;
|
||||
default:
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sdl_push_user_event(SDL_USEREVENT_CERT_RESULT, value);
|
||||
}
|
||||
|
||||
BOOL sdl_message_dialog_show(const char* title, const char* message, Sint32 flags)
|
||||
{
|
||||
int buttonid = -1;
|
||||
enum
|
||||
{
|
||||
BUTTONID_SHOW_ACCEPT = 24,
|
||||
BUTTONID_SHOW_DENY = 25
|
||||
};
|
||||
const SDL_MessageBoxButtonData buttons[] = {
|
||||
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_SHOW_ACCEPT, "accept" },
|
||||
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_SHOW_DENY, "cancel" }
|
||||
};
|
||||
|
||||
const int button_cnt = (flags & SHOW_DIALOG_ACCEPT_REJECT) ? 2 : 1;
|
||||
const SDL_MessageBoxData data = {
|
||||
SDL_MESSAGEBOX_WARNING, nullptr, title, message, button_cnt, buttons, nullptr
|
||||
};
|
||||
const int rc = SDL_ShowMessageBox(&data, &buttonid);
|
||||
|
||||
Sint32 value = -1;
|
||||
if (rc < 0)
|
||||
value = 0;
|
||||
else
|
||||
{
|
||||
switch (buttonid)
|
||||
{
|
||||
case BUTTONID_SHOW_ACCEPT:
|
||||
value = 1;
|
||||
break;
|
||||
default:
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sdl_push_user_event(SDL_USEREVENT_SHOW_RESULT, value);
|
||||
}
|
||||
|
||||
BOOL sdl_auth_dialog_show(const SDL_UserAuthArg* args)
|
||||
{
|
||||
const std::vector<std::string> auth = { "Username: ", "Domain: ",
|
||||
"Password: " };
|
||||
const std::vector<std::string> authPin = { "Device: ", "PIN: " };
|
||||
const std::vector<std::string> gw = { "GatewayUsername: ", "GatewayDomain: ",
|
||||
"GatewayPassword: " };
|
||||
std::vector<std::string> prompt;
|
||||
Sint32 rc = -1;
|
||||
|
||||
switch (args->result)
|
||||
{
|
||||
case AUTH_SMARTCARD_PIN:
|
||||
prompt = authPin;
|
||||
break;
|
||||
case AUTH_TLS:
|
||||
case AUTH_RDP:
|
||||
case AUTH_NLA:
|
||||
prompt = auth;
|
||||
break;
|
||||
case GW_AUTH_HTTP:
|
||||
case GW_AUTH_RDG:
|
||||
case GW_AUTH_RPC:
|
||||
prompt = gw;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<std::string> result;
|
||||
|
||||
if (!prompt.empty())
|
||||
{
|
||||
std::vector<std::string> initial{ args->user ? args->user : "Smartcard", "" };
|
||||
std::vector<Uint32> flags = { SdlInputWidget::SDL_INPUT_READONLY,
|
||||
SdlInputWidget::SDL_INPUT_MASK };
|
||||
if (args->result != AUTH_SMARTCARD_PIN)
|
||||
{
|
||||
initial = { args->user ? args->user : "", args->domain ? args->domain : "",
|
||||
args->password ? args->password : "" };
|
||||
flags = { 0, 0, SdlInputWidget::SDL_INPUT_MASK };
|
||||
}
|
||||
SdlInputWidgetList ilist(args->title, prompt, initial, flags);
|
||||
rc = ilist.run(result);
|
||||
}
|
||||
|
||||
if ((result.size() < prompt.size()))
|
||||
rc = -1;
|
||||
|
||||
char* user = nullptr;
|
||||
char* domain = nullptr;
|
||||
char* pwd = nullptr;
|
||||
if (rc > 0)
|
||||
{
|
||||
user = _strdup(result.at(0).c_str());
|
||||
if (args->result == AUTH_SMARTCARD_PIN)
|
||||
pwd = _strdup(result.at(1).c_str());
|
||||
else
|
||||
{
|
||||
domain = _strdup(result.at(1).c_str());
|
||||
pwd = _strdup(result.at(2).c_str());
|
||||
}
|
||||
}
|
||||
return sdl_push_user_event(SDL_USEREVENT_AUTH_RESULT, user, domain, pwd, rc);
|
||||
}
|
||||
|
||||
BOOL sdl_scard_dialog_show(const char* title, Sint32 count, const char** list)
|
||||
{
|
||||
const auto scount = WINPR_ASSERTING_INT_CAST(size_t, count);
|
||||
std::vector<std::string> vlist;
|
||||
vlist.reserve(scount);
|
||||
for (size_t x = 0; x < scount; x++)
|
||||
vlist.emplace_back(list[x]);
|
||||
SdlSelectList slist(title, vlist);
|
||||
Sint32 value = slist.run();
|
||||
return sdl_push_user_event(SDL_USEREVENT_SCARD_RESULT, value);
|
||||
}
|
||||
53
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_dialogs.hpp
vendored
Normal file
53
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_dialogs.hpp
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <winpr/wtypes.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#include "../sdl_types.hpp"
|
||||
#include "../sdl_utils.hpp"
|
||||
|
||||
BOOL sdl_authenticate_ex(freerdp* instance, char** username, char** password, char** domain,
|
||||
rdp_auth_reason reason);
|
||||
BOOL sdl_choose_smartcard(freerdp* instance, SmartcardCertInfo** cert_list, DWORD count,
|
||||
DWORD* choice, BOOL gateway);
|
||||
|
||||
SSIZE_T sdl_retry_dialog(freerdp* instance, const char* what, size_t current, void* userarg);
|
||||
|
||||
DWORD sdl_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
|
||||
const char* common_name, const char* subject, const char* issuer,
|
||||
const char* fingerprint, DWORD flags);
|
||||
|
||||
DWORD sdl_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
|
||||
const char* common_name, const char* subject,
|
||||
const char* issuer, const char* new_fingerprint,
|
||||
const char* old_subject, const char* old_issuer,
|
||||
const char* old_fingerprint, DWORD flags);
|
||||
|
||||
int sdl_logon_error_info(freerdp* instance, UINT32 data, UINT32 type);
|
||||
|
||||
BOOL sdl_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
|
||||
BOOL isConsentMandatory, size_t length, const WCHAR* message);
|
||||
|
||||
BOOL sdl_message_dialog_show(const char* title, const char* message, Sint32 flags);
|
||||
BOOL sdl_cert_dialog_show(const char* title, const char* message);
|
||||
BOOL sdl_scard_dialog_show(const char* title, Sint32 count, const char** list);
|
||||
BOOL sdl_auth_dialog_show(const SDL_UserAuthArg* args);
|
||||
177
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_input.cpp
vendored
Normal file
177
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_input.cpp
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* 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 "sdl_input.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_ttf.h>
|
||||
|
||||
#include "sdl_widget.hpp"
|
||||
#include "sdl_button.hpp"
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
static const SDL_Color inputbackgroundcolor = { 0x56, 0x56, 0x56, 0xff };
|
||||
static const SDL_Color inputhighlightcolor = { 0x80, 0, 0, 0x60 };
|
||||
static const SDL_Color inputmouseovercolor = { 0, 0x80, 0, 0x60 };
|
||||
static const SDL_Color inputfontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
static const SDL_Color labelbackgroundcolor = { 0x56, 0x56, 0x56, 0xff };
|
||||
static const SDL_Color labelfontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
static const Uint32 vpadding = 5;
|
||||
static const Uint32 hpadding = 10;
|
||||
|
||||
SdlInputWidget::SdlInputWidget(SDL_Renderer* renderer, std::string label, std::string initial,
|
||||
Uint32 flags, size_t offset, size_t width, size_t height)
|
||||
: _flags(flags), _text(std::move(initial)), _text_label(std::move(label)),
|
||||
_label(renderer,
|
||||
{ 0, static_cast<int>(offset * (height + vpadding)), static_cast<int>(width),
|
||||
static_cast<int>(height) },
|
||||
false),
|
||||
_input(renderer,
|
||||
{ static_cast<int>(width + hpadding), static_cast<int>(offset * (height + vpadding)),
|
||||
static_cast<int>(width), static_cast<int>(height) },
|
||||
true),
|
||||
_highlight(false), _mouseover(false)
|
||||
{
|
||||
}
|
||||
|
||||
SdlInputWidget::SdlInputWidget(SdlInputWidget&& other) noexcept
|
||||
: _flags(other._flags), _text(std::move(other._text)),
|
||||
_text_label(std::move(other._text_label)), _label(std::move(other._label)),
|
||||
_input(std::move(other._input)), _highlight(other._highlight), _mouseover(other._mouseover)
|
||||
{
|
||||
}
|
||||
|
||||
bool SdlInputWidget::fill_label(SDL_Renderer* renderer, SDL_Color color)
|
||||
{
|
||||
if (!_label.fill(renderer, color))
|
||||
return false;
|
||||
return _label.update_text(renderer, _text_label, labelfontcolor);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::update_label(SDL_Renderer* renderer)
|
||||
{
|
||||
return _label.update_text(renderer, _text_label, labelfontcolor, labelbackgroundcolor);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::set_mouseover(SDL_Renderer* renderer, bool mouseOver)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
_mouseover = mouseOver;
|
||||
return update_input(renderer);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::set_highlight(SDL_Renderer* renderer, bool highlight)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
_highlight = highlight;
|
||||
return update_input(renderer);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::update_input(SDL_Renderer* renderer)
|
||||
{
|
||||
std::vector<SDL_Color> colors = { inputbackgroundcolor };
|
||||
if (_highlight)
|
||||
colors.push_back(inputhighlightcolor);
|
||||
if (_mouseover)
|
||||
colors.push_back(inputmouseovercolor);
|
||||
|
||||
if (!_input.fill(renderer, colors))
|
||||
return false;
|
||||
return update_input(renderer, inputfontcolor);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::resize_input(size_t size)
|
||||
{
|
||||
_text.resize(size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SdlInputWidget::set_str(SDL_Renderer* renderer, const std::string& text)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
_text = text;
|
||||
if (!resize_input(_text.size()))
|
||||
return false;
|
||||
return update_input(renderer);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::remove_str(SDL_Renderer* renderer, size_t count)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
|
||||
assert(renderer);
|
||||
if (_text.empty())
|
||||
return true;
|
||||
|
||||
if (!resize_input(_text.size() - count))
|
||||
return false;
|
||||
return update_input(renderer);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::append_str(SDL_Renderer* renderer, const std::string& text)
|
||||
{
|
||||
assert(renderer);
|
||||
if (readonly())
|
||||
return true;
|
||||
|
||||
_text.append(text);
|
||||
if (!resize_input(_text.size()))
|
||||
return false;
|
||||
return update_input(renderer);
|
||||
}
|
||||
|
||||
const SDL_Rect& SdlInputWidget::input_rect() const
|
||||
{
|
||||
return _input.rect();
|
||||
}
|
||||
|
||||
std::string SdlInputWidget::value() const
|
||||
{
|
||||
return _text;
|
||||
}
|
||||
|
||||
bool SdlInputWidget::readonly() const
|
||||
{
|
||||
return (_flags & SDL_INPUT_READONLY) != 0;
|
||||
}
|
||||
|
||||
bool SdlInputWidget::update_input(SDL_Renderer* renderer, SDL_Color fgcolor)
|
||||
{
|
||||
std::string text = _text;
|
||||
if (!text.empty())
|
||||
{
|
||||
if (_flags & SDL_INPUT_MASK)
|
||||
{
|
||||
for (char& x : text)
|
||||
x = '*';
|
||||
}
|
||||
}
|
||||
|
||||
return _input.update_text(renderer, text, fgcolor);
|
||||
}
|
||||
74
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_input.hpp
vendored
Normal file
74
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_input.hpp
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <SDL.h>
|
||||
#include "sdl_widget.hpp"
|
||||
|
||||
class SdlInputWidget
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
SDL_INPUT_MASK = 1,
|
||||
SDL_INPUT_READONLY = 2
|
||||
};
|
||||
|
||||
SdlInputWidget(SDL_Renderer* renderer, std::string label, std::string initial, Uint32 flags,
|
||||
size_t offset, size_t width, size_t height);
|
||||
SdlInputWidget(SdlInputWidget&& other) noexcept;
|
||||
SdlInputWidget(const SdlInputWidget& other) = delete;
|
||||
~SdlInputWidget() = default;
|
||||
|
||||
SdlInputWidget& operator=(const SdlInputWidget& other) = delete;
|
||||
SdlInputWidget& operator=(SdlInputWidget&& other) = delete;
|
||||
|
||||
bool fill_label(SDL_Renderer* renderer, SDL_Color color);
|
||||
bool update_label(SDL_Renderer* renderer);
|
||||
|
||||
bool set_mouseover(SDL_Renderer* renderer, bool mouseOver);
|
||||
bool set_highlight(SDL_Renderer* renderer, bool highlight);
|
||||
bool update_input(SDL_Renderer* renderer);
|
||||
bool resize_input(size_t size);
|
||||
|
||||
bool set_str(SDL_Renderer* renderer, const std::string& text);
|
||||
bool remove_str(SDL_Renderer* renderer, size_t count);
|
||||
bool append_str(SDL_Renderer* renderer, const std::string& text);
|
||||
|
||||
[[nodiscard]] const SDL_Rect& input_rect() const;
|
||||
[[nodiscard]] std::string value() const;
|
||||
|
||||
[[nodiscard]] bool readonly() const;
|
||||
|
||||
protected:
|
||||
bool update_input(SDL_Renderer* renderer, SDL_Color fgcolor);
|
||||
|
||||
private:
|
||||
Uint32 _flags;
|
||||
std::string _text;
|
||||
std::string _text_label;
|
||||
SdlWidget _label;
|
||||
SdlWidget _input;
|
||||
bool _highlight;
|
||||
bool _mouseover;
|
||||
};
|
||||
299
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_input_widgets.cpp
vendored
Normal file
299
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_input_widgets.cpp
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "sdl_input_widgets.hpp"
|
||||
|
||||
static const Uint32 vpadding = 5;
|
||||
|
||||
SdlInputWidgetList::SdlInputWidgetList(const std::string& title,
|
||||
const std::vector<std::string>& labels,
|
||||
const std::vector<std::string>& initial,
|
||||
const std::vector<Uint32>& flags)
|
||||
: _window(nullptr), _renderer(nullptr)
|
||||
{
|
||||
assert(labels.size() == initial.size());
|
||||
assert(labels.size() == flags.size());
|
||||
const std::vector<int> buttonids = { INPUT_BUTTON_ACCEPT, INPUT_BUTTON_CANCEL };
|
||||
const std::vector<std::string> buttonlabels = { "accept", "cancel" };
|
||||
|
||||
const size_t widget_width = 300;
|
||||
const size_t widget_heigth = 50;
|
||||
|
||||
const size_t total_width = widget_width + widget_width;
|
||||
const size_t input_height = labels.size() * (widget_heigth + vpadding) + vpadding;
|
||||
const size_t total_height = input_height + widget_heigth;
|
||||
|
||||
assert(total_width <= INT32_MAX);
|
||||
assert(total_height <= INT32_MAX);
|
||||
Uint32 wflags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS;
|
||||
auto rc =
|
||||
SDL_CreateWindowAndRenderer(static_cast<int>(total_width), static_cast<int>(total_height),
|
||||
wflags, &_window, &_renderer);
|
||||
if (rc != 0)
|
||||
widget_log_error(rc, "SDL_CreateWindowAndRenderer");
|
||||
else
|
||||
{
|
||||
SDL_SetWindowTitle(_window, title.c_str());
|
||||
for (size_t x = 0; x < labels.size(); x++)
|
||||
_list.emplace_back(_renderer, labels.at(x), initial.at(x), flags.at(x), x, widget_width,
|
||||
widget_heigth);
|
||||
|
||||
_buttons.populate(_renderer, buttonlabels, buttonids, total_width,
|
||||
static_cast<Sint32>(input_height), static_cast<Sint32>(widget_width),
|
||||
static_cast<Sint32>(widget_heigth));
|
||||
_buttons.set_highlight(0);
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t SdlInputWidgetList::next(ssize_t current)
|
||||
{
|
||||
size_t iteration = 0;
|
||||
auto val = static_cast<size_t>(current);
|
||||
|
||||
do
|
||||
{
|
||||
if (iteration >= _list.size())
|
||||
return -1;
|
||||
|
||||
if (iteration == 0)
|
||||
{
|
||||
if (current < 0)
|
||||
val = 0;
|
||||
else
|
||||
val++;
|
||||
}
|
||||
else
|
||||
val++;
|
||||
iteration++;
|
||||
val %= _list.size();
|
||||
} while (!valid(static_cast<ssize_t>(val)));
|
||||
return static_cast<ssize_t>(val);
|
||||
}
|
||||
|
||||
bool SdlInputWidgetList::valid(ssize_t current) const
|
||||
{
|
||||
if (current < 0)
|
||||
return false;
|
||||
auto s = static_cast<size_t>(current);
|
||||
if (s >= _list.size())
|
||||
return false;
|
||||
return !_list.at(s).readonly();
|
||||
}
|
||||
|
||||
SdlInputWidget* SdlInputWidgetList::get(ssize_t index)
|
||||
{
|
||||
if (index < 0)
|
||||
return nullptr;
|
||||
auto s = static_cast<size_t>(index);
|
||||
if (s >= _list.size())
|
||||
return nullptr;
|
||||
return &_list.at(s);
|
||||
}
|
||||
|
||||
SdlInputWidgetList::~SdlInputWidgetList()
|
||||
{
|
||||
_list.clear();
|
||||
_buttons.clear();
|
||||
SDL_DestroyRenderer(_renderer);
|
||||
SDL_DestroyWindow(_window);
|
||||
}
|
||||
|
||||
bool SdlInputWidgetList::update(SDL_Renderer* renderer)
|
||||
{
|
||||
for (auto& btn : _list)
|
||||
{
|
||||
if (!btn.update_label(renderer))
|
||||
return false;
|
||||
if (!btn.update_input(renderer))
|
||||
return false;
|
||||
}
|
||||
|
||||
return _buttons.update(renderer);
|
||||
}
|
||||
|
||||
ssize_t SdlInputWidgetList::get_index(const SDL_MouseButtonEvent& button)
|
||||
{
|
||||
const Sint32 x = button.x;
|
||||
const Sint32 y = button.y;
|
||||
|
||||
assert(_list.size() <= std::numeric_limits<ssize_t>::max());
|
||||
for (size_t i = 0; i < _list.size(); i++)
|
||||
{
|
||||
auto& cur = _list.at(i);
|
||||
auto r = cur.input_rect();
|
||||
|
||||
if ((x >= r.x) && (x <= r.x + r.w) && (y >= r.y) && (y <= r.y + r.h))
|
||||
return static_cast<ssize_t>(i);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SdlInputWidgetList::run(std::vector<std::string>& result)
|
||||
{
|
||||
int res = -1;
|
||||
ssize_t LastActiveTextInput = -1;
|
||||
ssize_t CurrentActiveTextInput = next(-1);
|
||||
|
||||
if (!_window || !_renderer)
|
||||
return -2;
|
||||
|
||||
try
|
||||
{
|
||||
bool running = true;
|
||||
std::vector<SDL_Keycode> pressed;
|
||||
while (running)
|
||||
{
|
||||
if (!clear_window(_renderer))
|
||||
throw;
|
||||
|
||||
if (!update(_renderer))
|
||||
throw;
|
||||
|
||||
if (!_buttons.update(_renderer))
|
||||
throw;
|
||||
|
||||
SDL_Event event = {};
|
||||
SDL_WaitEvent(&event);
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
auto it = std::remove(pressed.begin(), pressed.end(), event.key.keysym.sym);
|
||||
pressed.erase(it, pressed.end());
|
||||
|
||||
switch (event.key.keysym.sym)
|
||||
{
|
||||
case SDLK_BACKSPACE:
|
||||
{
|
||||
auto cur = get(CurrentActiveTextInput);
|
||||
if (cur)
|
||||
{
|
||||
if (!cur->remove_str(_renderer, 1))
|
||||
throw;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDLK_TAB:
|
||||
CurrentActiveTextInput = next(CurrentActiveTextInput);
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
case SDLK_RETURN2:
|
||||
case SDLK_KP_ENTER:
|
||||
running = false;
|
||||
res = INPUT_BUTTON_ACCEPT;
|
||||
break;
|
||||
case SDLK_ESCAPE:
|
||||
running = false;
|
||||
res = INPUT_BUTTON_CANCEL;
|
||||
break;
|
||||
case SDLK_v:
|
||||
if (pressed.size() == 2)
|
||||
{
|
||||
if ((pressed.at(0) == SDLK_LCTRL) || (pressed.at(0) == SDLK_RCTRL))
|
||||
{
|
||||
auto cur = get(CurrentActiveTextInput);
|
||||
if (cur)
|
||||
{
|
||||
auto text = SDL_GetClipboardText();
|
||||
cur->set_str(_renderer, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
pressed.push_back(event.key.keysym.sym);
|
||||
break;
|
||||
case SDL_TEXTINPUT:
|
||||
{
|
||||
auto cur = get(CurrentActiveTextInput);
|
||||
if (cur)
|
||||
{
|
||||
if (!cur->append_str(_renderer, event.text.text))
|
||||
throw;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
{
|
||||
auto TextInputIndex = get_index(event.button);
|
||||
for (auto& cur : _list)
|
||||
{
|
||||
if (!cur.set_mouseover(_renderer, false))
|
||||
throw;
|
||||
}
|
||||
if (TextInputIndex >= 0)
|
||||
{
|
||||
auto& cur = _list.at(static_cast<size_t>(TextInputIndex));
|
||||
if (!cur.set_mouseover(_renderer, true))
|
||||
throw;
|
||||
}
|
||||
|
||||
_buttons.set_mouseover(event.button.x, event.button.y);
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
{
|
||||
auto val = get_index(event.button);
|
||||
if (valid(val))
|
||||
CurrentActiveTextInput = val;
|
||||
|
||||
auto button = _buttons.get_selected(event.button);
|
||||
if (button)
|
||||
{
|
||||
running = false;
|
||||
if (button->id() == INPUT_BUTTON_CANCEL)
|
||||
res = INPUT_BUTTON_CANCEL;
|
||||
else
|
||||
res = INPUT_BUTTON_ACCEPT;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
res = INPUT_BUTTON_CANCEL;
|
||||
running = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (LastActiveTextInput != CurrentActiveTextInput)
|
||||
{
|
||||
if (CurrentActiveTextInput < 0)
|
||||
SDL_StopTextInput();
|
||||
else
|
||||
SDL_StartTextInput();
|
||||
LastActiveTextInput = CurrentActiveTextInput;
|
||||
}
|
||||
|
||||
for (auto& cur : _list)
|
||||
{
|
||||
if (!cur.set_highlight(_renderer, false))
|
||||
throw;
|
||||
}
|
||||
auto cur = get(CurrentActiveTextInput);
|
||||
if (cur)
|
||||
{
|
||||
if (!cur->set_highlight(_renderer, true))
|
||||
throw;
|
||||
}
|
||||
|
||||
SDL_RenderPresent(_renderer);
|
||||
}
|
||||
|
||||
for (auto& cur : _list)
|
||||
result.push_back(cur.value());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
res = -2;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
44
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_input_widgets.hpp
vendored
Normal file
44
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_input_widgets.hpp
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
|
||||
#include "sdl_input.hpp"
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
class SdlInputWidgetList
|
||||
{
|
||||
public:
|
||||
SdlInputWidgetList(const std::string& title, const std::vector<std::string>& labels,
|
||||
const std::vector<std::string>& initial, const std::vector<Uint32>& flags);
|
||||
SdlInputWidgetList(const SdlInputWidgetList& other) = delete;
|
||||
SdlInputWidgetList(SdlInputWidgetList&& other) = delete;
|
||||
|
||||
SdlInputWidgetList& operator=(const SdlInputWidgetList& other) = delete;
|
||||
SdlInputWidgetList& operator=(SdlInputWidgetList&& other) = delete;
|
||||
|
||||
virtual ~SdlInputWidgetList();
|
||||
|
||||
int run(std::vector<std::string>& result);
|
||||
|
||||
protected:
|
||||
bool update(SDL_Renderer* renderer);
|
||||
ssize_t get_index(const SDL_MouseButtonEvent& button);
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
INPUT_BUTTON_ACCEPT = 1,
|
||||
INPUT_BUTTON_CANCEL = -2
|
||||
};
|
||||
|
||||
ssize_t next(ssize_t current);
|
||||
[[nodiscard]] bool valid(ssize_t current) const;
|
||||
SdlInputWidget* get(ssize_t index);
|
||||
|
||||
SDL_Window* _window;
|
||||
SDL_Renderer* _renderer;
|
||||
std::vector<SdlInputWidget> _list;
|
||||
SdlButtonList _buttons;
|
||||
};
|
||||
72
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_select.cpp
vendored
Normal file
72
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_select.cpp
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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 <cassert>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_ttf.h>
|
||||
|
||||
#include "sdl_select.hpp"
|
||||
#include "sdl_widget.hpp"
|
||||
#include "sdl_button.hpp"
|
||||
#include "sdl_buttons.hpp"
|
||||
#include "sdl_input_widgets.hpp"
|
||||
|
||||
static const SDL_Color labelmouseovercolor = { 0, 0x80, 0, 0x60 };
|
||||
static const SDL_Color labelbackgroundcolor = { 0x69, 0x66, 0x63, 0xff };
|
||||
static const SDL_Color labelhighlightcolor = { 0xcd, 0xca, 0x35, 0x60 };
|
||||
static const SDL_Color labelfontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
|
||||
SdlSelectWidget::SdlSelectWidget(SDL_Renderer* renderer, std::string label, SDL_Rect rect)
|
||||
: SdlWidget(renderer, rect, true), _text(std::move(label)), _mouseover(false), _highlight(false)
|
||||
{
|
||||
update_text(renderer);
|
||||
}
|
||||
|
||||
SdlSelectWidget::SdlSelectWidget(SdlSelectWidget&& other) noexcept = default;
|
||||
|
||||
SdlSelectWidget::~SdlSelectWidget() = default;
|
||||
|
||||
bool SdlSelectWidget::set_mouseover(SDL_Renderer* renderer, bool mouseOver)
|
||||
{
|
||||
_mouseover = mouseOver;
|
||||
return update_text(renderer);
|
||||
}
|
||||
|
||||
bool SdlSelectWidget::set_highlight(SDL_Renderer* renderer, bool highlight)
|
||||
{
|
||||
_highlight = highlight;
|
||||
return update_text(renderer);
|
||||
}
|
||||
|
||||
bool SdlSelectWidget::update_text(SDL_Renderer* renderer)
|
||||
{
|
||||
assert(renderer);
|
||||
std::vector<SDL_Color> colors = { labelbackgroundcolor };
|
||||
if (_highlight)
|
||||
colors.push_back(labelhighlightcolor);
|
||||
if (_mouseover)
|
||||
colors.push_back(labelmouseovercolor);
|
||||
if (!fill(renderer, colors))
|
||||
return false;
|
||||
return SdlWidget::update_text(renderer, _text, labelfontcolor);
|
||||
}
|
||||
47
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_select.hpp
vendored
Normal file
47
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_select.hpp
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL.h>
|
||||
#include "sdl_widget.hpp"
|
||||
|
||||
class SdlSelectWidget : public SdlWidget
|
||||
{
|
||||
public:
|
||||
SdlSelectWidget(SDL_Renderer* renderer, std::string label, SDL_Rect rect);
|
||||
SdlSelectWidget(SdlSelectWidget&& other) noexcept;
|
||||
~SdlSelectWidget() override;
|
||||
|
||||
bool set_mouseover(SDL_Renderer* renderer, bool mouseOver);
|
||||
bool set_highlight(SDL_Renderer* renderer, bool highlight);
|
||||
bool update_text(SDL_Renderer* renderer);
|
||||
|
||||
SdlSelectWidget(const SdlSelectWidget& other) = delete;
|
||||
SdlSelectWidget& operator=(const SdlSelectWidget& other) = delete;
|
||||
SdlSelectWidget& operator=(SdlSelectWidget&& other) = delete;
|
||||
|
||||
private:
|
||||
std::string _text;
|
||||
bool _mouseover;
|
||||
bool _highlight;
|
||||
};
|
||||
217
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_selectlist.cpp
vendored
Normal file
217
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_selectlist.cpp
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
#include <cassert>
|
||||
|
||||
#include <winpr/cast.h>
|
||||
|
||||
#include "sdl_selectlist.hpp"
|
||||
|
||||
static const Uint32 vpadding = 5;
|
||||
|
||||
SdlSelectList::SdlSelectList(const std::string& title, const std::vector<std::string>& labels)
|
||||
: _window(nullptr), _renderer(nullptr)
|
||||
{
|
||||
const size_t widget_height = 50;
|
||||
const size_t widget_width = 600;
|
||||
|
||||
const size_t total_height = labels.size() * (widget_height + vpadding) + vpadding;
|
||||
const size_t height = total_height + widget_height;
|
||||
assert(widget_width <= INT32_MAX);
|
||||
assert(height <= INT32_MAX);
|
||||
|
||||
auto flags = WINPR_ASSERTING_INT_CAST(
|
||||
uint32_t, SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS);
|
||||
auto rc = SDL_CreateWindowAndRenderer(static_cast<int>(widget_width), static_cast<int>(height),
|
||||
flags, &_window, &_renderer);
|
||||
if (rc != 0)
|
||||
widget_log_error(rc, "SDL_CreateWindowAndRenderer");
|
||||
else
|
||||
{
|
||||
SDL_SetWindowTitle(_window, title.c_str());
|
||||
|
||||
SDL_Rect rect = { 0, 0, widget_width, widget_height };
|
||||
for (auto& label : labels)
|
||||
{
|
||||
_list.emplace_back(_renderer, label, rect);
|
||||
rect.y += widget_height + vpadding;
|
||||
}
|
||||
|
||||
const std::vector<int> buttonids = { INPUT_BUTTON_ACCEPT, INPUT_BUTTON_CANCEL };
|
||||
const std::vector<std::string> buttonlabels = { "accept", "cancel" };
|
||||
_buttons.populate(_renderer, buttonlabels, buttonids, widget_width,
|
||||
static_cast<Sint32>(total_height), static_cast<Sint32>(widget_width / 2),
|
||||
static_cast<Sint32>(widget_height));
|
||||
_buttons.set_highlight(0);
|
||||
}
|
||||
}
|
||||
|
||||
SdlSelectList::~SdlSelectList()
|
||||
{
|
||||
_list.clear();
|
||||
_buttons.clear();
|
||||
SDL_DestroyRenderer(_renderer);
|
||||
SDL_DestroyWindow(_window);
|
||||
}
|
||||
|
||||
int SdlSelectList::run()
|
||||
{
|
||||
int res = -2;
|
||||
ssize_t CurrentActiveTextInput = 0;
|
||||
bool running = true;
|
||||
|
||||
if (!_window || !_renderer)
|
||||
return -2;
|
||||
try
|
||||
{
|
||||
while (running)
|
||||
{
|
||||
if (!clear_window(_renderer))
|
||||
throw;
|
||||
|
||||
if (!update_text())
|
||||
throw;
|
||||
|
||||
if (!_buttons.update(_renderer))
|
||||
throw;
|
||||
|
||||
SDL_Event event = {};
|
||||
SDL_WaitEvent(&event);
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
switch (event.key.keysym.sym)
|
||||
{
|
||||
case SDLK_UP:
|
||||
case SDLK_BACKSPACE:
|
||||
if (CurrentActiveTextInput > 0)
|
||||
CurrentActiveTextInput--;
|
||||
else
|
||||
CurrentActiveTextInput =
|
||||
WINPR_ASSERTING_INT_CAST(ssize_t, _list.size() - 1);
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
case SDLK_TAB:
|
||||
{
|
||||
if (CurrentActiveTextInput < 0)
|
||||
CurrentActiveTextInput = 0;
|
||||
else
|
||||
CurrentActiveTextInput++;
|
||||
|
||||
const auto s = _list.size();
|
||||
if (s <= 0)
|
||||
CurrentActiveTextInput = 0;
|
||||
else
|
||||
CurrentActiveTextInput =
|
||||
CurrentActiveTextInput % WINPR_ASSERTING_INT_CAST(ssize_t, s);
|
||||
}
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
case SDLK_RETURN2:
|
||||
case SDLK_KP_ENTER:
|
||||
running = false;
|
||||
res = WINPR_ASSERTING_INT_CAST(int, CurrentActiveTextInput);
|
||||
break;
|
||||
case SDLK_ESCAPE:
|
||||
running = false;
|
||||
res = INPUT_BUTTON_CANCEL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
{
|
||||
ssize_t TextInputIndex = get_index(event.button);
|
||||
reset_mouseover();
|
||||
if (TextInputIndex >= 0)
|
||||
{
|
||||
auto& cur = _list.at(WINPR_ASSERTING_INT_CAST(size_t, TextInputIndex));
|
||||
if (!cur.set_mouseover(_renderer, true))
|
||||
throw;
|
||||
}
|
||||
|
||||
_buttons.set_mouseover(event.button.x, event.button.y);
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
{
|
||||
auto button = _buttons.get_selected(event.button);
|
||||
if (button)
|
||||
{
|
||||
running = false;
|
||||
if (button->id() == INPUT_BUTTON_CANCEL)
|
||||
res = INPUT_BUTTON_CANCEL;
|
||||
else
|
||||
res = static_cast<int>(CurrentActiveTextInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentActiveTextInput = get_index(event.button);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
res = INPUT_BUTTON_CANCEL;
|
||||
running = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
reset_highlight();
|
||||
if (CurrentActiveTextInput >= 0)
|
||||
{
|
||||
auto& cur = _list.at(WINPR_ASSERTING_INT_CAST(size_t, CurrentActiveTextInput));
|
||||
if (!cur.set_highlight(_renderer, true))
|
||||
throw;
|
||||
}
|
||||
|
||||
SDL_RenderPresent(_renderer);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
ssize_t SdlSelectList::get_index(const SDL_MouseButtonEvent& button)
|
||||
{
|
||||
const Sint32 x = button.x;
|
||||
const Sint32 y = button.y;
|
||||
for (size_t i = 0; i < _list.size(); i++)
|
||||
{
|
||||
auto& cur = _list.at(i);
|
||||
auto r = cur.rect();
|
||||
|
||||
if ((x >= r.x) && (x <= r.x + r.w) && (y >= r.y) && (y <= r.y + r.h))
|
||||
return WINPR_ASSERTING_INT_CAST(ssize_t, i);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool SdlSelectList::update_text()
|
||||
{
|
||||
for (auto& cur : _list)
|
||||
{
|
||||
if (!cur.update_text(_renderer))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SdlSelectList::reset_mouseover()
|
||||
{
|
||||
for (auto& cur : _list)
|
||||
{
|
||||
cur.set_mouseover(_renderer, false);
|
||||
}
|
||||
}
|
||||
|
||||
void SdlSelectList::reset_highlight()
|
||||
{
|
||||
for (auto& cur : _list)
|
||||
{
|
||||
cur.set_highlight(_renderer, false);
|
||||
}
|
||||
}
|
||||
41
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_selectlist.hpp
vendored
Normal file
41
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_selectlist.hpp
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "sdl_select.hpp"
|
||||
#include "sdl_button.hpp"
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
class SdlSelectList
|
||||
{
|
||||
public:
|
||||
SdlSelectList(const std::string& title, const std::vector<std::string>& labels);
|
||||
virtual ~SdlSelectList();
|
||||
|
||||
int run();
|
||||
|
||||
SdlSelectList(const SdlSelectList& other) = delete;
|
||||
SdlSelectList(SdlSelectList&& other) = delete;
|
||||
SdlSelectList& operator=(const SdlSelectList& other) = delete;
|
||||
SdlSelectList& operator=(SdlSelectList&& other) = delete;
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
INPUT_BUTTON_ACCEPT = 0,
|
||||
INPUT_BUTTON_CANCEL = -2
|
||||
};
|
||||
|
||||
ssize_t get_index(const SDL_MouseButtonEvent& button);
|
||||
bool update_text();
|
||||
void reset_mouseover();
|
||||
void reset_highlight();
|
||||
|
||||
SDL_Window* _window;
|
||||
SDL_Renderer* _renderer;
|
||||
std::vector<SdlSelectWidget> _list;
|
||||
SdlButtonList _buttons;
|
||||
};
|
||||
291
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_widget.cpp
vendored
Normal file
291
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_widget.cpp
vendored
Normal file
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* 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 <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_ttf.h>
|
||||
|
||||
#include "sdl_widget.hpp"
|
||||
#include "../sdl_utils.hpp"
|
||||
|
||||
#include "res/sdl2_resource_manager.hpp"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
#include <SDL_image.h>
|
||||
#endif
|
||||
|
||||
#define TAG CLIENT_TAG("SDL.widget")
|
||||
|
||||
static const SDL_Color backgroundcolor = { 0x38, 0x36, 0x35, 0xff };
|
||||
|
||||
static const Uint32 hpadding = 10;
|
||||
|
||||
SdlWidget::SdlWidget([[maybe_unused]] SDL_Renderer* renderer, SDL_Rect rect, bool input)
|
||||
: _rect(rect), _input(input)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
auto ops = SDL2ResourceManager::get(SDLResourceManager::typeFonts(),
|
||||
"OpenSans-VariableFont_wdth,wght.ttf");
|
||||
if (!ops)
|
||||
widget_log_error(-1, "SDLResourceManager::get");
|
||||
else
|
||||
{
|
||||
_font = TTF_OpenFontRW(ops, 1, 64);
|
||||
if (!_font)
|
||||
widget_log_error(-1, "TTF_OpenFontRW");
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
SdlWidget::SdlWidget(SDL_Renderer* renderer, SDL_Rect rect, SDL_RWops* ops) : _rect(rect)
|
||||
{
|
||||
if (ops)
|
||||
{
|
||||
_image = IMG_LoadTexture_RW(renderer, ops, 1);
|
||||
if (!_image)
|
||||
widget_log_error(-1, "IMG_LoadTextureTyped_RW");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SdlWidget::SdlWidget(SdlWidget&& other) noexcept
|
||||
: _font(other._font), _image(other._image), _rect(other._rect), _input(other._input),
|
||||
_wrap(other._wrap), _text_width(other._text_width)
|
||||
{
|
||||
other._font = nullptr;
|
||||
other._image = nullptr;
|
||||
}
|
||||
|
||||
SDL_Texture* SdlWidget::render_text(SDL_Renderer* renderer, const std::string& text,
|
||||
SDL_Color fgcolor, SDL_Rect& src, SDL_Rect& dst)
|
||||
{
|
||||
auto surface = TTF_RenderUTF8_Blended(_font, text.c_str(), fgcolor);
|
||||
if (!surface)
|
||||
{
|
||||
widget_log_error(-1, "TTF_RenderText_Blended");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
SDL_FreeSurface(surface);
|
||||
if (!texture)
|
||||
{
|
||||
widget_log_error(-1, "SDL_CreateTextureFromSurface");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TTF_SizeUTF8(_font, text.c_str(), &src.w, &src.h);
|
||||
|
||||
/* Do some magic:
|
||||
* - Add padding before and after text
|
||||
* - if text is too long only show the last elements
|
||||
* - if text is too short only update used space
|
||||
*/
|
||||
dst = _rect;
|
||||
dst.x += hpadding;
|
||||
dst.w -= 2 * hpadding;
|
||||
const auto scale = static_cast<float>(dst.h) / static_cast<float>(src.h);
|
||||
const auto sws = static_cast<float>(src.w) * scale;
|
||||
const auto dws = static_cast<float>(dst.w) / scale;
|
||||
if (static_cast<float>(dst.w) > sws)
|
||||
dst.w = static_cast<int>(sws);
|
||||
if (static_cast<float>(src.w) > dws)
|
||||
{
|
||||
src.x = src.w - static_cast<int>(dws);
|
||||
src.w = static_cast<int>(dws);
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
static int scale(int w, int h)
|
||||
{
|
||||
const auto dw = static_cast<double>(w);
|
||||
const auto dh = static_cast<double>(h);
|
||||
const auto scale = dh / dw;
|
||||
const auto dr = dh * scale;
|
||||
return static_cast<int>(dr);
|
||||
}
|
||||
|
||||
SDL_Texture* SdlWidget::render_text_wrapped(SDL_Renderer* renderer, const std::string& text,
|
||||
SDL_Color fgcolor, SDL_Rect& src, SDL_Rect& dst)
|
||||
{
|
||||
Sint32 w = 0;
|
||||
Sint32 h = 0;
|
||||
TTF_SizeUTF8(_font, " ", &w, &h);
|
||||
|
||||
assert(_text_width <= UINT32_MAX);
|
||||
auto surface = TTF_RenderUTF8_Blended_Wrapped(_font, text.c_str(), fgcolor,
|
||||
static_cast<Uint32>(_text_width));
|
||||
if (!surface)
|
||||
{
|
||||
widget_log_error(-1, "TTF_RenderText_Blended");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
src.w = surface->w;
|
||||
src.h = surface->h;
|
||||
|
||||
auto texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
SDL_FreeSurface(surface);
|
||||
if (!texture)
|
||||
{
|
||||
widget_log_error(-1, "SDL_CreateTextureFromSurface");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Do some magic:
|
||||
* - Add padding before and after text
|
||||
* - if text is too long only show the last elements
|
||||
* - if text is too short only update used space
|
||||
*/
|
||||
dst = _rect;
|
||||
dst.x += hpadding;
|
||||
dst.w -= 2 * hpadding;
|
||||
auto dh = scale(src.w, src.h);
|
||||
dst.h = std::min<int>(dh, dst.h);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
SdlWidget::~SdlWidget()
|
||||
{
|
||||
TTF_CloseFont(_font);
|
||||
if (_image)
|
||||
SDL_DestroyTexture(_image);
|
||||
}
|
||||
|
||||
bool SdlWidget::error_ex(Sint32 res, const char* what, const char* file, size_t line,
|
||||
const char* fkt)
|
||||
{
|
||||
static wLog* log = nullptr;
|
||||
if (!log)
|
||||
log = WLog_Get(TAG);
|
||||
return sdl_log_error_ex(res, log, what, file, line, fkt);
|
||||
}
|
||||
|
||||
static bool draw_rect(SDL_Renderer* renderer, const SDL_Rect* rect, SDL_Color color)
|
||||
{
|
||||
const int drc = SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
|
||||
if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
|
||||
return false;
|
||||
|
||||
const int rc = SDL_RenderFillRect(renderer, rect);
|
||||
return !widget_log_error(rc, "SDL_RenderFillRect");
|
||||
}
|
||||
|
||||
bool SdlWidget::fill(SDL_Renderer* renderer, SDL_Color color)
|
||||
{
|
||||
std::vector<SDL_Color> colors = { color };
|
||||
return fill(renderer, colors);
|
||||
}
|
||||
|
||||
bool SdlWidget::fill(SDL_Renderer* renderer, const std::vector<SDL_Color>& colors)
|
||||
{
|
||||
assert(renderer);
|
||||
SDL_BlendMode mode = SDL_BLENDMODE_INVALID;
|
||||
SDL_GetRenderDrawBlendMode(renderer, &mode);
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
|
||||
for (auto color : colors)
|
||||
{
|
||||
draw_rect(renderer, &_rect, color);
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_ADD);
|
||||
}
|
||||
SDL_SetRenderDrawBlendMode(renderer, mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SdlWidget::update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor,
|
||||
SDL_Color bgcolor)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
if (!fill(renderer, bgcolor))
|
||||
return false;
|
||||
return update_text(renderer, text, fgcolor);
|
||||
}
|
||||
|
||||
bool SdlWidget::wrap() const
|
||||
{
|
||||
return _wrap;
|
||||
}
|
||||
|
||||
bool SdlWidget::set_wrap(bool wrap, size_t width)
|
||||
{
|
||||
_wrap = wrap;
|
||||
_text_width = width;
|
||||
return _wrap;
|
||||
}
|
||||
|
||||
const SDL_Rect& SdlWidget::rect() const
|
||||
{
|
||||
return _rect;
|
||||
}
|
||||
|
||||
bool SdlWidget::update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor)
|
||||
{
|
||||
|
||||
if (text.empty())
|
||||
return true;
|
||||
|
||||
SDL_Rect src{};
|
||||
SDL_Rect dst{};
|
||||
|
||||
SDL_Texture* texture = nullptr;
|
||||
if (_image)
|
||||
{
|
||||
texture = _image;
|
||||
dst = _rect;
|
||||
auto rc = SDL_QueryTexture(_image, nullptr, nullptr, &src.w, &src.h);
|
||||
if (rc < 0)
|
||||
widget_log_error(rc, "SDL_QueryTexture");
|
||||
}
|
||||
else if (_wrap)
|
||||
texture = render_text_wrapped(renderer, text, fgcolor, src, dst);
|
||||
else
|
||||
texture = render_text(renderer, text, fgcolor, src, dst);
|
||||
if (!texture)
|
||||
return false;
|
||||
|
||||
const int rc = SDL_RenderCopy(renderer, texture, &src, &dst);
|
||||
if (!_image)
|
||||
SDL_DestroyTexture(texture);
|
||||
if (rc < 0)
|
||||
return !widget_log_error(rc, "SDL_RenderCopy");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool clear_window(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");
|
||||
}
|
||||
90
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_widget.hpp
vendored
Normal file
90
third_party/FreeRDP/client/SDL/SDL2/dialogs/sdl_widget.hpp
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
#include <SDL_ttf.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#endif
|
||||
|
||||
#if !defined(HAS_NOEXCEPT)
|
||||
#if defined(__clang__)
|
||||
#if __has_feature(cxx_noexcept)
|
||||
#define HAS_NOEXCEPT
|
||||
#endif
|
||||
#elif defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ * 10 + __GNUC_MINOR__ >= 46 || \
|
||||
defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026
|
||||
#define HAS_NOEXCEPT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAS_NOEXCEPT
|
||||
#define noexcept
|
||||
#endif
|
||||
|
||||
class SdlWidget
|
||||
{
|
||||
public:
|
||||
SdlWidget(SDL_Renderer* renderer, SDL_Rect rect, bool input);
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
SdlWidget(SDL_Renderer* renderer, SDL_Rect rect, SDL_RWops* ops);
|
||||
#endif
|
||||
SdlWidget(SdlWidget&& other) noexcept;
|
||||
virtual ~SdlWidget();
|
||||
|
||||
bool fill(SDL_Renderer* renderer, SDL_Color color);
|
||||
bool fill(SDL_Renderer* renderer, const std::vector<SDL_Color>& colors);
|
||||
bool update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor);
|
||||
bool update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor,
|
||||
SDL_Color bgcolor);
|
||||
|
||||
[[nodiscard]] bool wrap() const;
|
||||
bool set_wrap(bool wrap = true, size_t width = 0);
|
||||
[[nodiscard]] const SDL_Rect& rect() const;
|
||||
|
||||
#define widget_log_error(res, what) SdlWidget::error_ex(res, what, __FILE__, __LINE__, __func__)
|
||||
static bool error_ex(Sint32 res, const char* what, const char* file, size_t line,
|
||||
const char* fkt);
|
||||
|
||||
SdlWidget(const SdlWidget& other) = delete;
|
||||
SdlWidget& operator=(const SdlWidget& other) = delete;
|
||||
SdlWidget& operator=(SdlWidget&& other) = delete;
|
||||
|
||||
private:
|
||||
SDL_Texture* render_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor,
|
||||
SDL_Rect& src, SDL_Rect& dst);
|
||||
SDL_Texture* render_text_wrapped(SDL_Renderer* renderer, const std::string& text,
|
||||
SDL_Color fgcolor, SDL_Rect& src, SDL_Rect& dst);
|
||||
|
||||
TTF_Font* _font = nullptr;
|
||||
SDL_Texture* _image = nullptr;
|
||||
SDL_Rect _rect = {};
|
||||
bool _input = false;
|
||||
bool _wrap = false;
|
||||
size_t _text_width = 0;
|
||||
};
|
||||
|
||||
bool clear_window(SDL_Renderer* renderer);
|
||||
25
third_party/FreeRDP/client/SDL/SDL2/dialogs/test/CMakeLists.txt
vendored
Normal file
25
third_party/FreeRDP/client/SDL/SDL2/dialogs/test/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
set(MODULE_NAME "TestSDL")
|
||||
set(MODULE_PREFIX "TEST_SDL")
|
||||
|
||||
set(DRIVER ${MODULE_NAME}.cpp)
|
||||
|
||||
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(TEST_SRCS TestSDLDialogs.cpp)
|
||||
|
||||
create_test_sourcelist(SRCS ${DRIVER} ${TEST_SRCS})
|
||||
|
||||
add_library(${MODULE_NAME} ${SRCS})
|
||||
|
||||
list(APPEND LIBS dialogs)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${LIBS})
|
||||
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
|
||||
|
||||
foreach(test ${TESTS})
|
||||
get_filename_component(TestName ${test} NAME_WE)
|
||||
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
|
||||
endforeach()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Client/Test")
|
||||
99
third_party/FreeRDP/client/SDL/SDL2/dialogs/test/TestSDLDialogs.cpp
vendored
Normal file
99
third_party/FreeRDP/client/SDL/SDL2/dialogs/test/TestSDLDialogs.cpp
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "../sdl_selectlist.hpp"
|
||||
#include "../sdl_input_widgets.hpp"
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
BOOL sdl_log_error_ex(Uint32 res, wLog* log, const char* what, const char* file, size_t line,
|
||||
const char* fkt)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool test_input_dialog()
|
||||
{
|
||||
const auto title = "sometitle";
|
||||
std::vector<std::string> labels;
|
||||
std::vector<std::string> initial;
|
||||
std::vector<Uint32> flags;
|
||||
for (size_t x = 0; x < 12; x++)
|
||||
{
|
||||
labels.push_back("label" + std::to_string(x));
|
||||
initial.push_back(std::to_string(x));
|
||||
|
||||
Uint32 flag = 0;
|
||||
if ((x % 2) != 0)
|
||||
flag |= SdlInputWidget::SDL_INPUT_MASK;
|
||||
if ((x % 3) == 0)
|
||||
flag |= SdlInputWidget::SDL_INPUT_READONLY;
|
||||
|
||||
flags.push_back(flag);
|
||||
}
|
||||
SdlInputWidgetList list{ title, labels, initial, flags };
|
||||
std::vector<std::string> result;
|
||||
auto rc = list.run(result);
|
||||
if (rc < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (result.size() != labels.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test_select_dialog()
|
||||
{
|
||||
const auto title = "sometitle";
|
||||
std::vector<std::string> labels;
|
||||
for (size_t x = 0; x < 12; x++)
|
||||
{
|
||||
labels.push_back("label" + std::to_string(x));
|
||||
}
|
||||
SdlSelectList list{ title, labels };
|
||||
auto rc = list.run();
|
||||
if (rc < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (static_cast<size_t>(rc) >= labels.size())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
FREERDP_API int TestSDLDialogs(int argc, char* argv[]);
|
||||
}
|
||||
|
||||
int TestSDLDialogs(int argc, char* argv[])
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
#if 0
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
try
|
||||
{
|
||||
#if 1
|
||||
if (!test_input_dialog())
|
||||
throw -1;
|
||||
#endif
|
||||
#if 1
|
||||
if (!test_select_dialog())
|
||||
throw -2;
|
||||
#endif
|
||||
}
|
||||
catch (int e)
|
||||
{
|
||||
rc = e;
|
||||
}
|
||||
SDL_Quit();
|
||||
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
Reference in New Issue
Block a user