Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
87
third_party/FreeRDP/client/SDL/SDL3/dialogs/CMakeLists.txt
vendored
Normal file
87
third_party/FreeRDP/client/SDL/SDL3/dialogs/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
# 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
|
||||
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_widget_list.hpp
|
||||
sdl_widget_list.cpp
|
||||
sdl_input_widget.hpp
|
||||
sdl_input_widget.cpp
|
||||
sdl_input_widget_pair.hpp
|
||||
sdl_input_widget_pair.cpp
|
||||
sdl_input_widget_pair_list.hpp
|
||||
sdl_input_widget_pair_list.cpp
|
||||
sdl_select.hpp
|
||||
sdl_select.cpp
|
||||
sdl_select_list.hpp
|
||||
sdl_select_list.cpp
|
||||
sdl_selectable_widget.cpp
|
||||
sdl_selectable_widget.hpp
|
||||
sdl_connection_dialog.cpp
|
||||
sdl_connection_dialog.hpp
|
||||
sdl_connection_dialog_wrapper.cpp
|
||||
sdl_connection_dialog_wrapper.hpp
|
||||
sdl_blend_mode_guard.cpp
|
||||
sdl_blend_mode_guard.hpp
|
||||
)
|
||||
|
||||
list(APPEND LIBS sdl3_client_res winpr)
|
||||
|
||||
if(NOT WITH_SDL_LINK_SHARED)
|
||||
list(APPEND LIBS ${SDL3_STATIC_LIBRARIES})
|
||||
else()
|
||||
list(APPEND LIBS ${SDL3_LIBRARIES})
|
||||
endif()
|
||||
|
||||
macro(find_sdl_component name)
|
||||
find_package(${name} REQUIRED)
|
||||
if(WITH_SDL_LINK_SHARED)
|
||||
list(APPEND LIBS ${name}::${name})
|
||||
set_target_properties(${name}::${name}-shared PROPERTIES SYSTEM TRUE)
|
||||
else()
|
||||
list(APPEND LIBS ${name}::${name}-static)
|
||||
set_target_properties(${name}::${name}-static PROPERTIES SYSTEM TRUE)
|
||||
endif()
|
||||
|
||||
endmacro()
|
||||
|
||||
find_sdl_component(SDL3_ttf)
|
||||
|
||||
option(WITH_SDL_IMAGE_DIALOGS "Build with SDL_image support (recommended)" OFF)
|
||||
if(WITH_SDL_IMAGE_DIALOGS)
|
||||
find_sdl_component(SDL3_image)
|
||||
add_compile_definitions(WITH_SDL_IMAGE_DIALOGS)
|
||||
endif()
|
||||
|
||||
add_subdirectory(res)
|
||||
|
||||
add_library(sdl3-dialogs STATIC ${SRCS} sdl_connection_dialog_hider.hpp sdl_connection_dialog_hider.cpp)
|
||||
|
||||
set_property(TARGET sdl3-dialogs PROPERTY FOLDER "Client/SDL")
|
||||
target_link_libraries(sdl3-dialogs PRIVATE ${LIBS})
|
||||
|
||||
option(SDL_DIALOG_TEST "Build dialog test binaries" OFF)
|
||||
if(SDL_DIALOG_TEST)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
93
third_party/FreeRDP/client/SDL/SDL3/dialogs/font/OFL.txt
vendored
Normal file
93
third_party/FreeRDP/client/SDL/SDL3/dialogs/font/OFL.txt
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
BIN
third_party/FreeRDP/client/SDL/SDL3/dialogs/font/OpenSans-Italic-VariableFont_wdth,wght.ttf
vendored
Normal file
BIN
third_party/FreeRDP/client/SDL/SDL3/dialogs/font/OpenSans-Italic-VariableFont_wdth,wght.ttf
vendored
Normal file
Binary file not shown.
BIN
third_party/FreeRDP/client/SDL/SDL3/dialogs/font/OpenSans-VariableFont_wdth,wght.ttf
vendored
Normal file
BIN
third_party/FreeRDP/client/SDL/SDL3/dialogs/font/OpenSans-VariableFont_wdth,wght.ttf
vendored
Normal file
Binary file not shown.
100
third_party/FreeRDP/client/SDL/SDL3/dialogs/font/README.txt
vendored
Normal file
100
third_party/FreeRDP/client/SDL/SDL3/dialogs/font/README.txt
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
Open Sans Variable Font
|
||||
=======================
|
||||
|
||||
This download contains Open Sans as both variable fonts and static fonts.
|
||||
|
||||
Open Sans is a variable font with these axes:
|
||||
wdth
|
||||
wght
|
||||
|
||||
This means all the styles are contained in these files:
|
||||
OpenSans-VariableFont_wdth,wght.ttf
|
||||
OpenSans-Italic-VariableFont_wdth,wght.ttf
|
||||
|
||||
If your app fully supports variable fonts, you can now pick intermediate styles
|
||||
that aren’t available as static fonts. Not all apps support variable fonts, and
|
||||
in those cases you can use the static font files for Open Sans:
|
||||
static/OpenSans_Condensed-Light.ttf
|
||||
static/OpenSans_Condensed-Regular.ttf
|
||||
static/OpenSans_Condensed-Medium.ttf
|
||||
static/OpenSans_Condensed-SemiBold.ttf
|
||||
static/OpenSans_Condensed-Bold.ttf
|
||||
static/OpenSans_Condensed-ExtraBold.ttf
|
||||
static/OpenSans_SemiCondensed-Light.ttf
|
||||
static/OpenSans_SemiCondensed-Regular.ttf
|
||||
static/OpenSans_SemiCondensed-Medium.ttf
|
||||
static/OpenSans_SemiCondensed-SemiBold.ttf
|
||||
static/OpenSans_SemiCondensed-Bold.ttf
|
||||
static/OpenSans_SemiCondensed-ExtraBold.ttf
|
||||
static/OpenSans-Light.ttf
|
||||
static/OpenSans-Regular.ttf
|
||||
static/OpenSans-Medium.ttf
|
||||
static/OpenSans-SemiBold.ttf
|
||||
static/OpenSans-Bold.ttf
|
||||
static/OpenSans-ExtraBold.ttf
|
||||
static/OpenSans_Condensed-LightItalic.ttf
|
||||
static/OpenSans_Condensed-Italic.ttf
|
||||
static/OpenSans_Condensed-MediumItalic.ttf
|
||||
static/OpenSans_Condensed-SemiBoldItalic.ttf
|
||||
static/OpenSans_Condensed-BoldItalic.ttf
|
||||
static/OpenSans_Condensed-ExtraBoldItalic.ttf
|
||||
static/OpenSans_SemiCondensed-LightItalic.ttf
|
||||
static/OpenSans_SemiCondensed-Italic.ttf
|
||||
static/OpenSans_SemiCondensed-MediumItalic.ttf
|
||||
static/OpenSans_SemiCondensed-SemiBoldItalic.ttf
|
||||
static/OpenSans_SemiCondensed-BoldItalic.ttf
|
||||
static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf
|
||||
static/OpenSans-LightItalic.ttf
|
||||
static/OpenSans-Italic.ttf
|
||||
static/OpenSans-MediumItalic.ttf
|
||||
static/OpenSans-SemiBoldItalic.ttf
|
||||
static/OpenSans-BoldItalic.ttf
|
||||
static/OpenSans-ExtraBoldItalic.ttf
|
||||
|
||||
Get started
|
||||
-----------
|
||||
|
||||
1. Install the font files you want to use
|
||||
|
||||
2. Use your app's font picker to view the font family and all the
|
||||
available styles
|
||||
|
||||
Learn more about variable fonts
|
||||
-------------------------------
|
||||
|
||||
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
|
||||
https://variablefonts.typenetwork.com
|
||||
https://medium.com/variable-fonts
|
||||
|
||||
In desktop apps
|
||||
|
||||
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
|
||||
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
|
||||
|
||||
Online
|
||||
|
||||
https://developers.google.com/fonts/docs/getting_started
|
||||
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
|
||||
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
|
||||
|
||||
Installing fonts
|
||||
|
||||
MacOS: https://support.apple.com/en-us/HT201749
|
||||
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
|
||||
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
|
||||
|
||||
Android Apps
|
||||
|
||||
https://developers.google.com/fonts/docs/android
|
||||
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
|
||||
|
||||
License
|
||||
-------
|
||||
Please read the full license text (OFL.txt) to understand the permissions,
|
||||
restrictions and requirements for usage, redistribution, and modification.
|
||||
|
||||
You can use them in your products & projects – print or digital,
|
||||
commercial or otherwise.
|
||||
|
||||
This isn't legal advice, please consider consulting a lawyer and see the full
|
||||
license for all details.
|
||||
31
third_party/FreeRDP/client/SDL/SDL3/dialogs/res/CMakeLists.txt
vendored
Normal file
31
third_party/FreeRDP/client/SDL/SDL3/dialogs/res/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# 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 sdl3_resource_manager.cpp sdl3_resource_manager.hpp)
|
||||
|
||||
add_library(sdl3_client_res STATIC ${SRCS})
|
||||
set_property(TARGET sdl3_client_res PROPERTY FOLDER "Client/SDL")
|
||||
|
||||
if(NOT WITH_SDL_LINK_SHARED)
|
||||
target_link_libraries(sdl3_client_res ${SDL3_STATIC_LIBRARIES})
|
||||
else()
|
||||
target_link_libraries(sdl3_client_res ${SDL3_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(sdl3_client_res sdl-common-client-res)
|
||||
|
||||
set_target_properties(sdl3_client_res PROPERTIES POSITION_INDEPENDENT_CODE ON INTERPROCEDURAL_OPTIMIZATION OFF)
|
||||
43
third_party/FreeRDP/client/SDL/SDL3/dialogs/res/sdl3_resource_manager.cpp
vendored
Normal file
43
third_party/FreeRDP/client/SDL/SDL3/dialogs/res/sdl3_resource_manager.cpp
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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 "sdl3_resource_manager.hpp"
|
||||
#include <iostream>
|
||||
#if __has_include(<filesystem>)
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
#elif __has_include(<experimental/filesystem>)
|
||||
#include <experimental/filesystem>
|
||||
namespace fs = std::experimental::filesystem;
|
||||
#else
|
||||
#error Could not find system header "<filesystem>" or "<experimental/filesystem>"
|
||||
#endif
|
||||
|
||||
SDL_IOStream* SDL3ResourceManager::get(const std::string& type, const std::string& id)
|
||||
{
|
||||
if (useCompiledResources())
|
||||
{
|
||||
auto d = data(type, id);
|
||||
if (!d)
|
||||
return nullptr;
|
||||
|
||||
return SDL_IOFromConstMem(d->data(), d->size());
|
||||
}
|
||||
|
||||
auto name = filename(type, id);
|
||||
return SDL_IOFromFile(name.c_str(), "rb");
|
||||
}
|
||||
38
third_party/FreeRDP/client/SDL/SDL3/dialogs/res/sdl3_resource_manager.hpp
vendored
Normal file
38
third_party/FreeRDP/client/SDL/SDL3/dialogs/res/sdl3_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 <SDL3/SDL.h>
|
||||
|
||||
#include <res/sdl_resource_manager.hpp>
|
||||
|
||||
class SDL3ResourceManager : public SDLResourceManager
|
||||
{
|
||||
public:
|
||||
SDL3ResourceManager() = delete;
|
||||
SDL3ResourceManager(const SDL3ResourceManager& other) = delete;
|
||||
SDL3ResourceManager(const SDL3ResourceManager&& other) = delete;
|
||||
~SDL3ResourceManager() = delete;
|
||||
SDL3ResourceManager& operator=(const SDL3ResourceManager& other) = delete;
|
||||
SDL3ResourceManager& operator=(SDL3ResourceManager&& other) = delete;
|
||||
|
||||
[[nodiscard]] static SDL_IOStream* get(const std::string& type, const std::string& id);
|
||||
};
|
||||
45
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_blend_mode_guard.cpp
vendored
Normal file
45
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_blend_mode_guard.cpp
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "sdl_blend_mode_guard.hpp"
|
||||
|
||||
SdlBlendModeGuard::SdlBlendModeGuard(const std::shared_ptr<SDL_Renderer>& renderer,
|
||||
SDL_BlendMode mode)
|
||||
: _renderer(renderer)
|
||||
{
|
||||
const auto rcb = SDL_GetRenderDrawBlendMode(_renderer.get(), &_restore_mode);
|
||||
if (!rcb)
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"[%s] SDL_GetRenderDrawBlendMode() failed with %s", __func__, SDL_GetError());
|
||||
else
|
||||
{
|
||||
const auto rbm = SDL_SetRenderDrawBlendMode(_renderer.get(), mode);
|
||||
if (!rbm)
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"[%s] SDL_SetRenderDrawBlendMode() failed with %s", __func__,
|
||||
SDL_GetError());
|
||||
else
|
||||
_current_mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
bool SdlBlendModeGuard::update(SDL_BlendMode mode)
|
||||
{
|
||||
if (_current_mode != mode)
|
||||
{
|
||||
if (!SDL_SetRenderDrawBlendMode(_renderer.get(), mode))
|
||||
{
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"[%s] SDL_SetRenderDrawBlendMode() failed with %s", __func__,
|
||||
SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
_current_mode = mode;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SdlBlendModeGuard::~SdlBlendModeGuard()
|
||||
{
|
||||
const auto rbm = SDL_SetRenderDrawBlendMode(_renderer.get(), _restore_mode);
|
||||
if (!rbm)
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"[%s] SDL_SetRenderDrawBlendMode() failed with %s", __func__, SDL_GetError());
|
||||
}
|
||||
46
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_blend_mode_guard.hpp
vendored
Normal file
46
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_blend_mode_guard.hpp
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2025 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2025 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
class SdlBlendModeGuard
|
||||
{
|
||||
public:
|
||||
explicit SdlBlendModeGuard(const std::shared_ptr<SDL_Renderer>& renderer,
|
||||
SDL_BlendMode mode = SDL_BLENDMODE_NONE);
|
||||
~SdlBlendModeGuard();
|
||||
|
||||
SdlBlendModeGuard(SdlBlendModeGuard&& other) noexcept;
|
||||
SdlBlendModeGuard(const SdlBlendModeGuard& other) = delete;
|
||||
|
||||
SdlBlendModeGuard& operator=(const SdlBlendModeGuard& other) = delete;
|
||||
SdlBlendModeGuard& operator=(SdlBlendModeGuard&& other) = delete;
|
||||
|
||||
[[nodiscard]] bool update(SDL_BlendMode mode);
|
||||
|
||||
private:
|
||||
SDL_BlendMode _restore_mode = SDL_BLENDMODE_INVALID;
|
||||
SDL_BlendMode _current_mode = SDL_BLENDMODE_INVALID;
|
||||
std::shared_ptr<SDL_Renderer> _renderer;
|
||||
};
|
||||
45
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_button.cpp
vendored
Normal file
45
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_button.cpp
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
SdlButton::SdlButton(std::shared_ptr<SDL_Renderer>& renderer, const std::string& label, int id,
|
||||
const SDL_FRect& rect)
|
||||
: SdlSelectableWidget(renderer, rect), _id(id)
|
||||
{
|
||||
_backgroundcolor = { 0x69, 0x66, 0x63, 0xff };
|
||||
_highlightcolor = { 0xcd, 0xca, 0x35, 0x60 };
|
||||
_mouseovercolor = { 0x66, 0xff, 0x66, 0x60 };
|
||||
_fontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
std::ignore = update_text(label);
|
||||
std::ignore = update();
|
||||
}
|
||||
|
||||
SdlButton::SdlButton(SdlButton&& other) noexcept = default;
|
||||
|
||||
SdlButton::~SdlButton() = default;
|
||||
|
||||
int SdlButton::id() const
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
24
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_button.hpp
vendored
Normal file
24
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_button.hpp
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "sdl_selectable_widget.hpp"
|
||||
|
||||
class SdlButton : public SdlSelectableWidget
|
||||
{
|
||||
public:
|
||||
SdlButton(std::shared_ptr<SDL_Renderer>& renderer, const std::string& label, int id,
|
||||
const SDL_FRect& rect);
|
||||
SdlButton(SdlButton&& other) noexcept;
|
||||
SdlButton(const SdlButton& other) = delete;
|
||||
~SdlButton() override;
|
||||
|
||||
SdlButton& operator=(const SdlButton& other) = delete;
|
||||
SdlButton& operator=(SdlButton&& other) = delete;
|
||||
|
||||
[[nodiscard]] int id() const;
|
||||
|
||||
private:
|
||||
int _id;
|
||||
};
|
||||
108
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_buttons.cpp
vendored
Normal file
108
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_buttons.cpp
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
static const Uint32 hpadding = 10;
|
||||
|
||||
SdlButtonList::~SdlButtonList() = default;
|
||||
|
||||
bool SdlButtonList::populate(std::shared_ptr<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() * (static_cast<size_t>(width) + hpadding) + hpadding;
|
||||
size_t offsetX = static_cast<size_t>(total_width) -
|
||||
std::min<size_t>(static_cast<size_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_FRect rect = { static_cast<float>(curOffsetX), static_cast<float>(offsetY),
|
||||
static_cast<float>(width), static_cast<float>(height) };
|
||||
auto button = std::make_shared<SdlButton>(renderer, labels.at(x), ids.at(x), rect);
|
||||
_list.emplace_back(button);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<SdlButton> SdlButtonList::get_selected(const SDL_MouseButtonEvent& button)
|
||||
{
|
||||
const auto x = button.x;
|
||||
const auto y = button.y;
|
||||
|
||||
return get_selected(x, y);
|
||||
}
|
||||
|
||||
std::shared_ptr<SdlButton> SdlButtonList::get_selected(float x, float 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(float x, float y)
|
||||
{
|
||||
_mouseover = get_selected(x, y);
|
||||
return _mouseover != nullptr;
|
||||
}
|
||||
|
||||
void SdlButtonList::clear()
|
||||
{
|
||||
_list.clear();
|
||||
_mouseover = nullptr;
|
||||
_highlighted = nullptr;
|
||||
_highlight_index = 0;
|
||||
}
|
||||
|
||||
bool SdlButtonList::update()
|
||||
{
|
||||
for (auto& btn : _list)
|
||||
{
|
||||
if (!btn->highlight(btn == _highlighted))
|
||||
return false;
|
||||
if (!btn->mouseover(btn == _mouseover))
|
||||
return false;
|
||||
|
||||
if (!btn->update())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
39
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_buttons.hpp
vendored
Normal file
39
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_buttons.hpp
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "sdl_button.hpp"
|
||||
|
||||
class SdlButtonList
|
||||
{
|
||||
public:
|
||||
SdlButtonList() = default;
|
||||
SdlButtonList(const SdlButtonList& other) = delete;
|
||||
SdlButtonList(SdlButtonList&& other) = delete;
|
||||
virtual ~SdlButtonList();
|
||||
|
||||
SdlButtonList& operator=(const SdlButtonList& other) = delete;
|
||||
SdlButtonList& operator=(SdlButtonList&& other) = delete;
|
||||
|
||||
[[nodiscard]] bool populate(std::shared_ptr<SDL_Renderer>& renderer,
|
||||
const std::vector<std::string>& labels, const std::vector<int>& ids,
|
||||
Sint32 total_width, Sint32 offsetY, Sint32 width, Sint32 height);
|
||||
|
||||
[[nodiscard]] bool update();
|
||||
[[nodiscard]] std::shared_ptr<SdlButton> get_selected(const SDL_MouseButtonEvent& button);
|
||||
[[nodiscard]] std::shared_ptr<SdlButton> get_selected(float x, float y);
|
||||
|
||||
bool set_highlight_next(bool reset = false);
|
||||
bool set_highlight(size_t index);
|
||||
bool set_mouseover(float x, float y);
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<SdlButton>> _list;
|
||||
std::shared_ptr<SdlButton> _highlighted = nullptr;
|
||||
size_t _highlight_index = 0;
|
||||
std::shared_ptr<SdlButton> _mouseover = nullptr;
|
||||
};
|
||||
479
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog.cpp
vendored
Normal file
479
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog.cpp
vendored
Normal file
@@ -0,0 +1,479 @@
|
||||
/**
|
||||
* 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_context.hpp"
|
||||
#include "res/sdl3_resource_manager.hpp"
|
||||
|
||||
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)
|
||||
{
|
||||
std::ignore = hide();
|
||||
}
|
||||
|
||||
SDLConnectionDialog::~SDLConnectionDialog()
|
||||
{
|
||||
resetTimer();
|
||||
destroyWindow();
|
||||
}
|
||||
|
||||
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(SdlConnectionDialogWrapper::MSG_NONE);
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::showInfo(const char* fmt, ...)
|
||||
{
|
||||
va_list ap = {};
|
||||
va_start(ap, fmt);
|
||||
auto rc = show(SdlConnectionDialogWrapper::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(SdlConnectionDialogWrapper::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(SdlConnectionDialogWrapper::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(SdlConnectionDialogWrapper::MSG_DISCARD);
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::running() const
|
||||
{
|
||||
std::scoped_lock lock(_mux);
|
||||
return _running;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::updateMsg(SdlConnectionDialogWrapper::MsgType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SdlConnectionDialogWrapper::MSG_INFO:
|
||||
case SdlConnectionDialogWrapper::MSG_WARN:
|
||||
case SdlConnectionDialogWrapper::MSG_ERROR:
|
||||
_type_active = type;
|
||||
if (!createWindow())
|
||||
return false;
|
||||
break;
|
||||
case SdlConnectionDialogWrapper::MSG_DISCARD:
|
||||
resetTimer();
|
||||
destroyWindow();
|
||||
break;
|
||||
default:
|
||||
if (_window)
|
||||
{
|
||||
SDL_SetWindowTitle(_window.get(), _title.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::setModal()
|
||||
{
|
||||
if (_window)
|
||||
{
|
||||
auto sdl = get_context(_context);
|
||||
auto parent = sdl->getFirstWindow();
|
||||
if (!parent)
|
||||
return true;
|
||||
|
||||
if (!SDL_SetWindowParent(_window.get(), parent->window()))
|
||||
return false;
|
||||
if (!SDL_SetWindowModal(_window.get(), true))
|
||||
return false;
|
||||
if (!SDL_RaiseWindow(_window.get()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::updateInternal()
|
||||
{
|
||||
std::scoped_lock lock(_mux);
|
||||
for (auto& btn : _list)
|
||||
{
|
||||
if (!btn.widget.update_text(_msg))
|
||||
return false;
|
||||
}
|
||||
|
||||
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.get());
|
||||
}
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_USER_RETRY_DIALOG:
|
||||
{
|
||||
std::scoped_lock lock(_mux);
|
||||
auto type = static_cast<SdlConnectionDialogWrapper::MsgType>(event.user.code);
|
||||
return updateMsg(type);
|
||||
}
|
||||
case SDL_EVENT_QUIT:
|
||||
resetTimer();
|
||||
destroyWindow();
|
||||
return false;
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_KeyboardEvent&>(event);
|
||||
if (!update())
|
||||
return false;
|
||||
switch (event.key.key)
|
||||
{
|
||||
case SDLK_RETURN:
|
||||
case SDLK_RETURN2:
|
||||
case SDLK_ESCAPE:
|
||||
case SDLK_KP_ENTER:
|
||||
if (event.type == SDL_EVENT_KEY_UP)
|
||||
{
|
||||
freerdp_abort_connect_context(_context);
|
||||
std::ignore = sdl_push_quit();
|
||||
}
|
||||
break;
|
||||
case SDLK_TAB:
|
||||
if (!_buttons.set_highlight_next())
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_MouseMotionEvent&>(event);
|
||||
|
||||
_buttons.set_mouseover(event.button.x, event.button.y);
|
||||
if (!update())
|
||||
return false;
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_MouseButtonEvent&>(event);
|
||||
if (!update())
|
||||
return false;
|
||||
|
||||
auto button = _buttons.get_selected(event.button);
|
||||
if (button)
|
||||
{
|
||||
if (event.type == SDL_EVENT_MOUSE_BUTTON_UP)
|
||||
{
|
||||
freerdp_abort_connect_context(_context);
|
||||
std::ignore = sdl_push_quit();
|
||||
}
|
||||
}
|
||||
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_EVENT_MOUSE_WHEEL:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_MouseWheelEvent&>(event);
|
||||
if (!update())
|
||||
return false;
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_EVENT_FINGER_UP:
|
||||
case SDL_EVENT_FINGER_DOWN:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_TouchFingerEvent&>(event);
|
||||
if (!update())
|
||||
return false;
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
if ((event.type >= SDL_EVENT_WINDOW_FIRST) && (event.type <= SDL_EVENT_WINDOW_LAST))
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_WindowEvent&>(event);
|
||||
switch (ev.type)
|
||||
{
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
if (windowID == ev.windowID)
|
||||
{
|
||||
freerdp_abort_connect_context(_context);
|
||||
std::ignore = sdl_push_quit();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!update())
|
||||
return false;
|
||||
if (!setModal())
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::visible() const
|
||||
{
|
||||
std::scoped_lock lock(_mux);
|
||||
return SdlWidgetList::visible();
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::createWindow()
|
||||
{
|
||||
destroyWindow();
|
||||
|
||||
const size_t widget_height = 50;
|
||||
const size_t widget_width = 600;
|
||||
const size_t total_height = 300;
|
||||
|
||||
if (!reset(_title, widget_width, total_height))
|
||||
return false;
|
||||
|
||||
if (!setModal())
|
||||
return false;
|
||||
|
||||
SDL_Color res_bgcolor;
|
||||
switch (_type_active)
|
||||
{
|
||||
case SdlConnectionDialogWrapper::MSG_INFO:
|
||||
res_bgcolor = infocolor;
|
||||
break;
|
||||
case SdlConnectionDialogWrapper::MSG_WARN:
|
||||
res_bgcolor = warncolor;
|
||||
break;
|
||||
case SdlConnectionDialogWrapper::MSG_ERROR:
|
||||
res_bgcolor = errorcolor;
|
||||
break;
|
||||
case SdlConnectionDialogWrapper::MSG_DISCARD:
|
||||
default:
|
||||
res_bgcolor = _backgroundcolor;
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
std::string res_name;
|
||||
switch (_type_active)
|
||||
{
|
||||
case SdlConnectionDialogWrapper::MSG_INFO:
|
||||
res_name = "icon_info.svg";
|
||||
break;
|
||||
case SdlConnectionDialogWrapper::MSG_WARN:
|
||||
res_name = "icon_warning.svg";
|
||||
break;
|
||||
case SdlConnectionDialogWrapper::MSG_ERROR:
|
||||
res_name = "icon_error.svg";
|
||||
break;
|
||||
case SdlConnectionDialogWrapper::MSG_DISCARD:
|
||||
default:
|
||||
res_name = "";
|
||||
break;
|
||||
}
|
||||
|
||||
const auto height = (total_height - 3.0f * vpadding) / 2.0f;
|
||||
SDL_FRect iconRect{ hpadding, vpadding, widget_width / 4.0f - 2.0f * hpadding, height };
|
||||
widget_cfg_t icon{ textcolor,
|
||||
res_bgcolor,
|
||||
{ _renderer, iconRect,
|
||||
SDL3ResourceManager::get(SDLResourceManager::typeImages(), res_name) } };
|
||||
_list.emplace_back(std::move(icon));
|
||||
|
||||
iconRect.y += height;
|
||||
|
||||
widget_cfg_t logo{ textcolor,
|
||||
_backgroundcolor,
|
||||
{ _renderer, iconRect,
|
||||
SDL3ResourceManager::get(SDLResourceManager::typeImages(),
|
||||
"FreeRDP_Icon.svg") } };
|
||||
_list.emplace_back(std::move(logo));
|
||||
|
||||
SDL_FRect rect = { widget_width / 4.0f, vpadding, widget_width * 3.0f / 4.0f,
|
||||
total_height - 3ul * vpadding - widget_height };
|
||||
#else
|
||||
SDL_FRect rect = { hpadding, vpadding, widget_width - 2ul * hpadding,
|
||||
total_height - 2ul * vpadding };
|
||||
#endif
|
||||
|
||||
widget_cfg_t w{ textcolor, _backgroundcolor, { _renderer, rect } };
|
||||
if (!w.widget.set_wrap(true, widget_width))
|
||||
return false;
|
||||
_list.emplace_back(std::move(w));
|
||||
rect.y += widget_height + vpadding;
|
||||
|
||||
const std::vector<int> buttonids = { 1 };
|
||||
const std::vector<std::string> buttonlabels = { "cancel" };
|
||||
if (!_buttons.populate(_renderer, buttonlabels, buttonids, widget_width,
|
||||
total_height - widget_height - vpadding,
|
||||
static_cast<Sint32>(widget_width / 2),
|
||||
static_cast<Sint32>(widget_height)))
|
||||
return false;
|
||||
if (!_buttons.set_highlight(0))
|
||||
return false;
|
||||
|
||||
if (!SDL_ShowWindow(_window.get()))
|
||||
return false;
|
||||
if (!SDL_RaiseWindow(_window.get()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLConnectionDialog::destroyWindow()
|
||||
{
|
||||
_buttons.clear();
|
||||
_list.clear();
|
||||
_renderer = nullptr;
|
||||
_window = nullptr;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::show(SdlConnectionDialogWrapper::MsgType type, const char* fmt,
|
||||
va_list ap)
|
||||
{
|
||||
std::scoped_lock lock(_mux);
|
||||
_msg = print(fmt, ap);
|
||||
return show(type);
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::show(SdlConnectionDialogWrapper::MsgType type)
|
||||
{
|
||||
if (SDL_IsMainThread())
|
||||
return updateMsg(type);
|
||||
else
|
||||
return sdl_push_user_event(SDL_EVENT_USER_RETRY_DIALOG, type);
|
||||
}
|
||||
|
||||
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(uint32_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(void* pvthis, [[maybe_unused]] SDL_TimerID timerID,
|
||||
[[maybe_unused]] Uint32 intervalMS)
|
||||
{
|
||||
auto self = static_cast<SDLConnectionDialog*>(pvthis);
|
||||
std::ignore = self->hide();
|
||||
self->_running = false;
|
||||
return 0;
|
||||
}
|
||||
95
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog.hpp
vendored
Normal file
95
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog.hpp
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 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 <SDL3/SDL.h>
|
||||
|
||||
#include "sdl_buttons.hpp"
|
||||
#include "sdl_connection_dialog_wrapper.hpp"
|
||||
#include "sdl_widget.hpp"
|
||||
#include "sdl_widget_list.hpp"
|
||||
|
||||
class SDLConnectionDialog : public SdlWidgetList
|
||||
{
|
||||
public:
|
||||
explicit SDLConnectionDialog(rdpContext* context);
|
||||
SDLConnectionDialog(const SDLConnectionDialog& other) = delete;
|
||||
SDLConnectionDialog(const SDLConnectionDialog&& other) = delete;
|
||||
~SDLConnectionDialog() override;
|
||||
|
||||
SDLConnectionDialog& operator=(const SDLConnectionDialog& other) = delete;
|
||||
SDLConnectionDialog& operator=(SDLConnectionDialog&& other) = delete;
|
||||
|
||||
[[nodiscard]] bool setTitle(const char* fmt, ...);
|
||||
[[nodiscard]] bool showInfo(const char* fmt, ...);
|
||||
[[nodiscard]] bool showWarn(const char* fmt, ...);
|
||||
[[nodiscard]] bool showError(const char* fmt, ...);
|
||||
|
||||
[[nodiscard]] bool show();
|
||||
[[nodiscard]] bool hide();
|
||||
|
||||
[[nodiscard]] bool running() const;
|
||||
[[nodiscard]] bool wait(bool ignoreRdpContextQuit = false);
|
||||
|
||||
[[nodiscard]] bool handle(const SDL_Event& event);
|
||||
|
||||
[[nodiscard]] bool visible() const override;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] bool updateInternal() override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool createWindow();
|
||||
void destroyWindow();
|
||||
|
||||
[[nodiscard]] bool updateMsg(SdlConnectionDialogWrapper::MsgType type);
|
||||
|
||||
[[nodiscard]] bool setModal();
|
||||
|
||||
[[nodiscard]] bool show(SdlConnectionDialogWrapper::MsgType type, const char* fmt, va_list ap);
|
||||
[[nodiscard]] bool show(SdlConnectionDialogWrapper::MsgType type);
|
||||
|
||||
[[nodiscard]] static std::string print(const char* fmt, va_list ap);
|
||||
[[nodiscard]] bool setTimer(Uint32 timeoutMS = 15000);
|
||||
void resetTimer();
|
||||
|
||||
[[nodiscard]] static Uint32 timeout(void* pvthis, SDL_TimerID timerID, Uint32 intervalMS);
|
||||
|
||||
struct widget_cfg_t
|
||||
{
|
||||
SDL_Color fgcolor = {};
|
||||
SDL_Color bgcolor = {};
|
||||
SdlWidget widget;
|
||||
};
|
||||
|
||||
rdpContext* _context = nullptr;
|
||||
mutable std::mutex _mux;
|
||||
std::string _title;
|
||||
std::string _msg;
|
||||
SdlConnectionDialogWrapper::MsgType _type_active = SdlConnectionDialogWrapper::MSG_NONE;
|
||||
SDL_TimerID _timer = 0;
|
||||
bool _running = false;
|
||||
std::vector<widget_cfg_t> _list;
|
||||
};
|
||||
13
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog_hider.cpp
vendored
Normal file
13
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog_hider.cpp
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "sdl_connection_dialog_hider.hpp"
|
||||
#include "../sdl_context.hpp"
|
||||
|
||||
SDLConnectionDialogHider::SDLConnectionDialogHider(SdlContext* sdl)
|
||||
: _sdl(sdl), _visible(_sdl->getDialog().isVisible())
|
||||
{
|
||||
_sdl->getDialog().show(false);
|
||||
}
|
||||
|
||||
SDLConnectionDialogHider::~SDLConnectionDialogHider()
|
||||
{
|
||||
_sdl->getDialog().show(_visible);
|
||||
}
|
||||
39
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog_hider.hpp
vendored
Normal file
39
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog_hider.hpp
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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 "../sdl_types.hpp"
|
||||
|
||||
class SDLConnectionDialogHider
|
||||
{
|
||||
public:
|
||||
explicit SDLConnectionDialogHider(SdlContext* sdl);
|
||||
|
||||
SDLConnectionDialogHider(const SDLConnectionDialogHider& other) = delete;
|
||||
SDLConnectionDialogHider(SDLConnectionDialogHider&& other) = delete;
|
||||
SDLConnectionDialogHider& operator=(const SDLConnectionDialogHider& other) = delete;
|
||||
SDLConnectionDialogHider& operator=(SDLConnectionDialogHider&& other) = delete;
|
||||
|
||||
~SDLConnectionDialogHider();
|
||||
|
||||
private:
|
||||
SdlContext* _sdl = nullptr;
|
||||
bool _visible = false;
|
||||
};
|
||||
287
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog_wrapper.cpp
vendored
Normal file
287
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog_wrapper.cpp
vendored
Normal file
@@ -0,0 +1,287 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2025 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2025 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 <sstream>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/settings.h>
|
||||
#include <freerdp/log.h>
|
||||
|
||||
#include "../sdl_utils.hpp"
|
||||
#include "sdl_connection_dialog.hpp"
|
||||
#include "sdl_connection_dialog_wrapper.hpp"
|
||||
|
||||
SdlConnectionDialogWrapper::SdlConnectionDialogWrapper(wLog* log) : _log(log)
|
||||
{
|
||||
}
|
||||
|
||||
SdlConnectionDialogWrapper::~SdlConnectionDialogWrapper() = default;
|
||||
|
||||
void SdlConnectionDialogWrapper::create(rdpContext* context)
|
||||
{
|
||||
const auto enabled =
|
||||
freerdp_settings_get_bool(context->settings, FreeRDP_UseCommonStdioCallbacks);
|
||||
_connection_dialog.reset();
|
||||
if (!enabled)
|
||||
_connection_dialog = std::make_unique<SDLConnectionDialog>(context);
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::destroy()
|
||||
{
|
||||
_connection_dialog.reset();
|
||||
}
|
||||
|
||||
bool SdlConnectionDialogWrapper::isRunning() const
|
||||
{
|
||||
std::unique_lock lock(_mux);
|
||||
if (!_connection_dialog)
|
||||
return false;
|
||||
return _connection_dialog->running();
|
||||
}
|
||||
|
||||
bool SdlConnectionDialogWrapper::isVisible() const
|
||||
{
|
||||
std::unique_lock lock(_mux);
|
||||
if (!_connection_dialog)
|
||||
return false;
|
||||
return _connection_dialog->visible();
|
||||
}
|
||||
|
||||
bool SdlConnectionDialogWrapper::handleEvent(const SDL_Event& event)
|
||||
{
|
||||
std::unique_lock lock(_mux);
|
||||
if (!_connection_dialog)
|
||||
return false;
|
||||
return _connection_dialog->handle(event);
|
||||
}
|
||||
|
||||
WINPR_ATTR_FORMAT_ARG(1, 0)
|
||||
static std::string format(WINPR_FORMAT_ARG const char* fmt, va_list ap)
|
||||
{
|
||||
va_list ap1 = {};
|
||||
va_copy(ap1, ap);
|
||||
const int size = vsnprintf(nullptr, 0, fmt, ap1);
|
||||
va_end(ap1);
|
||||
|
||||
if (size < 0)
|
||||
return "";
|
||||
|
||||
std::string msg;
|
||||
msg.resize(static_cast<size_t>(size) + 1);
|
||||
|
||||
va_list ap2 = {};
|
||||
va_copy(ap2, ap);
|
||||
std::ignore = vsnprintf(msg.data(), msg.size(), fmt, ap2);
|
||||
va_end(ap2);
|
||||
return msg;
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::setTitle(const char* fmt, ...)
|
||||
{
|
||||
va_list ap = {};
|
||||
va_start(ap, fmt);
|
||||
setTitle(format(fmt, ap));
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::setTitle(const std::string& title)
|
||||
{
|
||||
push(EventArg{ title });
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::showInfo(const char* fmt, ...)
|
||||
{
|
||||
va_list ap = {};
|
||||
va_start(ap, fmt);
|
||||
showInfo(format(fmt, ap));
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::showInfo(const std::string& info)
|
||||
{
|
||||
show(MSG_INFO, info);
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::showWarn(const char* fmt, ...)
|
||||
{
|
||||
va_list ap = {};
|
||||
va_start(ap, fmt);
|
||||
showWarn(format(fmt, ap));
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::showWarn(const std::string& info)
|
||||
{
|
||||
show(MSG_WARN, info);
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::showError(const char* fmt, ...)
|
||||
{
|
||||
va_list ap = {};
|
||||
va_start(ap, fmt);
|
||||
showError(format(fmt, ap));
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::showError(const std::string& error)
|
||||
{
|
||||
show(MSG_ERROR, error);
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::show(SdlConnectionDialogWrapper::MsgType type,
|
||||
const std::string& msg)
|
||||
{
|
||||
push({ type, msg, true });
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::show(bool visible)
|
||||
{
|
||||
push(EventArg{ visible });
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::handleShow()
|
||||
{
|
||||
std::unique_lock lock(_mux);
|
||||
while (!_queue.empty())
|
||||
{
|
||||
auto arg = _queue.front();
|
||||
_queue.pop();
|
||||
|
||||
if (arg.hasTitle() && _connection_dialog)
|
||||
{
|
||||
std::ignore = _connection_dialog->setTitle(arg.title().c_str());
|
||||
}
|
||||
|
||||
if (arg.hasType() && arg.hasMessage())
|
||||
{
|
||||
switch (arg.type())
|
||||
{
|
||||
case SdlConnectionDialogWrapper::MSG_INFO:
|
||||
if (_connection_dialog)
|
||||
std::ignore = _connection_dialog->showInfo(arg.message().c_str());
|
||||
else
|
||||
WLog_Print(_log, WLOG_INFO, "%s", arg.message().c_str());
|
||||
break;
|
||||
case SdlConnectionDialogWrapper::MSG_WARN:
|
||||
if (_connection_dialog)
|
||||
std::ignore = _connection_dialog->showWarn(arg.message().c_str());
|
||||
else
|
||||
WLog_Print(_log, WLOG_WARN, "%s", arg.message().c_str());
|
||||
break;
|
||||
case SdlConnectionDialogWrapper::MSG_ERROR:
|
||||
if (_connection_dialog)
|
||||
std::ignore = _connection_dialog->showError(arg.message().c_str());
|
||||
else
|
||||
WLog_Print(_log, WLOG_ERROR, "%s", arg.message().c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg.hasVisibility() && _connection_dialog)
|
||||
{
|
||||
if (arg.visible())
|
||||
std::ignore = _connection_dialog->show();
|
||||
else
|
||||
std::ignore = _connection_dialog->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SdlConnectionDialogWrapper::push(EventArg&& arg)
|
||||
{
|
||||
{
|
||||
std::unique_lock lock(_mux);
|
||||
_queue.push(std::move(arg));
|
||||
}
|
||||
|
||||
auto rc = SDL_RunOnMainThread(
|
||||
[](void* user)
|
||||
{
|
||||
auto dlg = static_cast<SdlConnectionDialogWrapper*>(user);
|
||||
dlg->handleShow();
|
||||
},
|
||||
this, false);
|
||||
if (!rc)
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "[%s] SDL_RunOnMainThread failed with %s",
|
||||
__func__, SDL_GetError());
|
||||
}
|
||||
|
||||
SdlConnectionDialogWrapper::EventArg::EventArg(bool visible) : _visible(visible), _mask(8)
|
||||
{
|
||||
}
|
||||
|
||||
SdlConnectionDialogWrapper::EventArg::EventArg(const std::string& title) : _title(title), _mask(1)
|
||||
{
|
||||
}
|
||||
|
||||
SdlConnectionDialogWrapper::EventArg::EventArg(MsgType type, const std::string& msg, bool visible)
|
||||
: _message(msg), _type(type), _visible(visible), _mask(14)
|
||||
{
|
||||
}
|
||||
|
||||
bool SdlConnectionDialogWrapper::EventArg::hasTitle() const
|
||||
{
|
||||
return _mask & 0x01;
|
||||
}
|
||||
|
||||
const std::string& SdlConnectionDialogWrapper::EventArg::title() const
|
||||
{
|
||||
return _title;
|
||||
}
|
||||
|
||||
bool SdlConnectionDialogWrapper::EventArg::hasMessage() const
|
||||
{
|
||||
return _mask & 0x02;
|
||||
}
|
||||
|
||||
const std::string& SdlConnectionDialogWrapper::EventArg::message() const
|
||||
{
|
||||
return _message;
|
||||
}
|
||||
|
||||
bool SdlConnectionDialogWrapper::EventArg::hasType() const
|
||||
{
|
||||
return _mask & 0x04;
|
||||
}
|
||||
|
||||
SdlConnectionDialogWrapper::MsgType SdlConnectionDialogWrapper::EventArg::type() const
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
bool SdlConnectionDialogWrapper::EventArg::hasVisibility() const
|
||||
{
|
||||
return _mask & 0x08;
|
||||
}
|
||||
|
||||
bool SdlConnectionDialogWrapper::EventArg::visible() const
|
||||
{
|
||||
return _visible;
|
||||
}
|
||||
|
||||
std::string SdlConnectionDialogWrapper::EventArg::str() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "{ title:" << _title << ", message:" << _message << ", type:" << _type
|
||||
<< ", visible:" << _visible << ", mask:" << _mask << "}";
|
||||
return ss.str();
|
||||
}
|
||||
122
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog_wrapper.hpp
vendored
Normal file
122
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_connection_dialog_wrapper.hpp
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2025 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2025 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/platform.h>
|
||||
#include <freerdp/types.h>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
class SDLConnectionDialog;
|
||||
|
||||
class SdlConnectionDialogWrapper
|
||||
{
|
||||
public:
|
||||
enum MsgType
|
||||
{
|
||||
MSG_NONE,
|
||||
MSG_INFO,
|
||||
MSG_WARN,
|
||||
MSG_ERROR,
|
||||
MSG_DISCARD
|
||||
};
|
||||
|
||||
explicit SdlConnectionDialogWrapper(wLog* log);
|
||||
~SdlConnectionDialogWrapper();
|
||||
|
||||
SdlConnectionDialogWrapper(const SdlConnectionDialogWrapper& other) = delete;
|
||||
SdlConnectionDialogWrapper(SdlConnectionDialogWrapper&& other) = delete;
|
||||
|
||||
SdlConnectionDialogWrapper& operator=(const SdlConnectionDialogWrapper& other) = delete;
|
||||
SdlConnectionDialogWrapper& operator=(SdlConnectionDialogWrapper&& other) = delete;
|
||||
|
||||
void create(rdpContext* context);
|
||||
void destroy();
|
||||
|
||||
[[nodiscard]] bool isRunning() const;
|
||||
[[nodiscard]] bool isVisible() const;
|
||||
|
||||
[[nodiscard]] bool handleEvent(const SDL_Event& event);
|
||||
|
||||
WINPR_ATTR_FORMAT_ARG(2, 3)
|
||||
void setTitle(WINPR_FORMAT_ARG const char* fmt, ...);
|
||||
void setTitle(const std::string& title);
|
||||
|
||||
WINPR_ATTR_FORMAT_ARG(2, 3)
|
||||
void showInfo(WINPR_FORMAT_ARG const char* fmt, ...);
|
||||
void showInfo(const std::string& info);
|
||||
|
||||
WINPR_ATTR_FORMAT_ARG(2, 3)
|
||||
void showWarn(WINPR_FORMAT_ARG const char* fmt, ...);
|
||||
void showWarn(const std::string& info);
|
||||
|
||||
WINPR_ATTR_FORMAT_ARG(2, 3)
|
||||
void showError(WINPR_FORMAT_ARG const char* fmt, ...);
|
||||
void showError(const std::string& error);
|
||||
|
||||
void show(SdlConnectionDialogWrapper::MsgType type, const std::string& msg);
|
||||
|
||||
void show(bool visible = true);
|
||||
|
||||
void handleShow();
|
||||
|
||||
private:
|
||||
class EventArg
|
||||
{
|
||||
public:
|
||||
explicit EventArg(bool visible);
|
||||
explicit EventArg(const std::string& title);
|
||||
EventArg(SdlConnectionDialogWrapper::MsgType type, const std::string& msg, bool visible);
|
||||
|
||||
[[nodiscard]] bool hasTitle() const;
|
||||
[[nodiscard]] const std::string& title() const;
|
||||
|
||||
[[nodiscard]] bool hasMessage() const;
|
||||
[[nodiscard]] const std::string& message() const;
|
||||
|
||||
[[nodiscard]] bool hasType() const;
|
||||
[[nodiscard]] SdlConnectionDialogWrapper::MsgType type() const;
|
||||
|
||||
[[nodiscard]] bool hasVisibility() const;
|
||||
[[nodiscard]] bool visible() const;
|
||||
|
||||
[[nodiscard]] std::string str() const;
|
||||
|
||||
private:
|
||||
std::string _title;
|
||||
std::string _message;
|
||||
SdlConnectionDialogWrapper::MsgType _type = MSG_NONE;
|
||||
bool _visible = false;
|
||||
uint32_t _mask = 0;
|
||||
};
|
||||
void push(EventArg&& arg);
|
||||
|
||||
mutable std::mutex _mux;
|
||||
std::unique_ptr<SDLConnectionDialog> _connection_dialog;
|
||||
std::queue<EventArg> _queue;
|
||||
wLog* _log = nullptr;
|
||||
};
|
||||
648
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_dialogs.cpp
vendored
Normal file
648
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_dialogs.cpp
vendored
Normal file
@@ -0,0 +1,648 @@
|
||||
/**
|
||||
* 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 <SDL3/SDL.h>
|
||||
|
||||
#include "../sdl_context.hpp"
|
||||
#include "sdl_dialogs.hpp"
|
||||
#include "sdl_input_widget_pair.hpp"
|
||||
#include "sdl_input_widget_pair_list.hpp"
|
||||
#include "sdl_select.hpp"
|
||||
#include "sdl_select_list.hpp"
|
||||
#include "sdl_connection_dialog.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_EVENT_USER_SHOW_DIALOG, title, message, flags))
|
||||
return 0;
|
||||
|
||||
if (!sdl_wait_for_result(context, SDL_EVENT_USER_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;
|
||||
|
||||
const char* target = freerdp_settings_get_server_name(instance->context->settings);
|
||||
switch (reason)
|
||||
{
|
||||
case AUTH_RDSTLS:
|
||||
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 guard(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_EVENT_USER_AUTH_DIALOG, title, u, d, p, reason))
|
||||
return res;
|
||||
|
||||
if (!sdl_wait_for_result(instance->context, SDL_EVENT_USER_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);
|
||||
|
||||
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_EVENT_USER_SCARD_DIALOG, title, list.data(), count))
|
||||
return res;
|
||||
|
||||
if (!sdl_wait_for_result(instance->context, SDL_EVENT_USER_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 BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
|
||||
const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
|
||||
|
||||
sdl->getDialog().setTitle("Retry connection to %s",
|
||||
freerdp_settings_get_server_name(instance->context->settings));
|
||||
|
||||
if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
|
||||
{
|
||||
sdl->getDialog().showError("Unknown module %s, aborting", what);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (current == 0)
|
||||
{
|
||||
if (strcmp(what, "arm-transport") == 0)
|
||||
sdl->getDialog().showWarn("[%s] Starting your VM. It may take up to 5 minutes", what);
|
||||
}
|
||||
|
||||
if (!enabled)
|
||||
{
|
||||
sdl->getDialog().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->getDialog().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->getDialog().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);
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
if (!sdl_push_user_event(SDL_EVENT_USER_CERT_DIALOG, title, message))
|
||||
return 0;
|
||||
|
||||
SDL_Event event = {};
|
||||
if (!sdl_wait_for_result(context, SDL_EVENT_USER_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);
|
||||
|
||||
/* 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);
|
||||
|
||||
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_EVENT_USER_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_EVENT_USER_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_RDSTLS:
|
||||
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 = { SdlInputWidgetPair::SDL_INPUT_READONLY,
|
||||
SdlInputWidgetPair::SDL_INPUT_MASK };
|
||||
if (args->result != AUTH_SMARTCARD_PIN)
|
||||
{
|
||||
if (args->result == AUTH_RDSTLS)
|
||||
{
|
||||
initial = { args->user ? args->user : "", args->password ? args->password : "" };
|
||||
flags = { 0, SdlInputWidgetPair::SDL_INPUT_MASK };
|
||||
}
|
||||
else
|
||||
{
|
||||
initial = { args->user ? args->user : "", args->domain ? args->domain : "",
|
||||
args->password ? args->password : "" };
|
||||
flags = { 0, 0, SdlInputWidgetPair::SDL_INPUT_MASK };
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t selected = -1;
|
||||
switch (args->result)
|
||||
{
|
||||
case AUTH_SMARTCARD_PIN:
|
||||
case AUTH_RDSTLS:
|
||||
break;
|
||||
default:
|
||||
if (args->user)
|
||||
{
|
||||
selected++;
|
||||
if (args->domain)
|
||||
selected++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
SdlInputWidgetPairList ilist(args->title, prompt, initial, flags, selected);
|
||||
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_EVENT_USER_AUTH_RESULT, user, domain, pwd, rc);
|
||||
}
|
||||
|
||||
BOOL sdl_scard_dialog_show(const char* title, Sint32 count, const char** list)
|
||||
{
|
||||
std::vector<std::string> vlist;
|
||||
vlist.reserve(WINPR_ASSERTING_INT_CAST(size_t, count));
|
||||
for (Sint32 x = 0; x < count; x++)
|
||||
vlist.emplace_back(list[x]);
|
||||
SdlSelectList slist(title, vlist);
|
||||
Sint32 value = slist.run();
|
||||
return sdl_push_user_event(SDL_EVENT_USER_SCARD_RESULT, value);
|
||||
}
|
||||
|
||||
void sdl_dialogs_uninit()
|
||||
{
|
||||
TTF_Quit();
|
||||
}
|
||||
|
||||
void sdl_dialogs_init()
|
||||
{
|
||||
TTF_Init();
|
||||
}
|
||||
59
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_dialogs.hpp
vendored
Normal file
59
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_dialogs.hpp
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
[[nodiscard]] BOOL sdl_authenticate_ex(freerdp* instance, char** username, char** password,
|
||||
char** domain, rdp_auth_reason reason);
|
||||
[[nodiscard]] BOOL sdl_choose_smartcard(freerdp* instance, SmartcardCertInfo** cert_list,
|
||||
DWORD count, DWORD* choice, BOOL gateway);
|
||||
|
||||
[[nodiscard]] SSIZE_T sdl_retry_dialog(freerdp* instance, const char* what, size_t current,
|
||||
void* userarg);
|
||||
|
||||
[[nodiscard]] 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);
|
||||
|
||||
[[nodiscard]] 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);
|
||||
|
||||
[[nodiscard]] int sdl_logon_error_info(freerdp* instance, UINT32 data, UINT32 type);
|
||||
|
||||
[[nodiscard]] BOOL sdl_present_gateway_message(freerdp* instance, UINT32 type,
|
||||
BOOL isDisplayMandatory, BOOL isConsentMandatory,
|
||||
size_t length, const WCHAR* message);
|
||||
|
||||
[[nodiscard]] BOOL sdl_message_dialog_show(const char* title, const char* message, Sint32 flags);
|
||||
[[nodiscard]] BOOL sdl_cert_dialog_show(const char* title, const char* message);
|
||||
[[nodiscard]] BOOL sdl_scard_dialog_show(const char* title, Sint32 count, const char** list);
|
||||
[[nodiscard]] BOOL sdl_auth_dialog_show(const SDL_UserAuthArg* args);
|
||||
|
||||
void sdl_dialogs_init();
|
||||
void sdl_dialogs_uninit();
|
||||
52
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget.cpp
vendored
Normal file
52
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget.cpp
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2025 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2025 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 "sdl_input_widget.hpp"
|
||||
|
||||
SdlInputWidget::SdlInputWidget(std::shared_ptr<SDL_Renderer>& renderer, const SDL_FRect& rect)
|
||||
: SdlSelectableWidget(renderer, rect)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
SdlInputWidget::~SdlInputWidget() = default;
|
||||
|
||||
std::string SdlInputWidget::text() const
|
||||
{
|
||||
return _text;
|
||||
}
|
||||
|
||||
void SdlInputWidget::init()
|
||||
{
|
||||
_backgroundcolor = { 0x56, 0x56, 0x56, 0xff };
|
||||
_fontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
_highlightcolor = { 0x80, 0, 0, 0x60 };
|
||||
_mouseovercolor = { 0, 0x80, 0, 0x60 };
|
||||
}
|
||||
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
SdlInputWidget::SdlInputWidget(std::shared_ptr<SDL_Renderer>& renderer, const SDL_FRect& rect,
|
||||
SDL_IOStream* ops)
|
||||
: SdlSelectableWidget(renderer, rect, ops)
|
||||
{
|
||||
init();
|
||||
}
|
||||
#endif
|
||||
|
||||
SdlInputWidget::SdlInputWidget(SdlInputWidget&& other) noexcept = default;
|
||||
43
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget.hpp
vendored
Normal file
43
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget.hpp
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2025 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2025 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sdl_selectable_widget.hpp"
|
||||
|
||||
class SdlInputWidget : public SdlSelectableWidget
|
||||
{
|
||||
public:
|
||||
SdlInputWidget(std::shared_ptr<SDL_Renderer>& renderer, const SDL_FRect& rect);
|
||||
SdlInputWidget(std::shared_ptr<SDL_Renderer>& renderer, const SDL_FRect& rect,
|
||||
SDL_IOStream* ops);
|
||||
|
||||
SdlInputWidget(SdlInputWidget&& other) noexcept;
|
||||
SdlInputWidget(const SdlInputWidget& other) = delete;
|
||||
|
||||
SdlInputWidget& operator=(const SdlInputWidget& other) = delete;
|
||||
SdlInputWidget& operator=(SdlInputWidget&& other) noexcept = delete;
|
||||
|
||||
~SdlInputWidget() override;
|
||||
|
||||
[[nodiscard]] std::string text() const;
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
129
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget_pair.cpp
vendored
Normal file
129
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget_pair.cpp
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* 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_widget_pair.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include "sdl_widget.hpp"
|
||||
#include "sdl_button.hpp"
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
SdlInputWidgetPair::SdlInputWidgetPair(std::shared_ptr<SDL_Renderer>& renderer,
|
||||
const std::string& label, const std::string& initial,
|
||||
Uint32 flags, size_t offset, size_t width, size_t height)
|
||||
: _flags(flags), _label(renderer, { 0, static_cast<float>(offset * (height + _vpadding)),
|
||||
static_cast<float>(width), static_cast<float>(height) }),
|
||||
_input(renderer, { static_cast<float>(width + _hpadding),
|
||||
static_cast<float>(offset * (height + _vpadding)),
|
||||
static_cast<float>(width), static_cast<float>(height) })
|
||||
{
|
||||
std::ignore = _label.update_text(label);
|
||||
std::ignore = update_input_text(initial);
|
||||
}
|
||||
|
||||
SdlInputWidgetPair::SdlInputWidgetPair(SdlInputWidgetPair&& other) noexcept = default;
|
||||
|
||||
bool SdlInputWidgetPair::set_mouseover(bool mouseOver)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
return _input.mouseover(mouseOver);
|
||||
}
|
||||
|
||||
bool SdlInputWidgetPair::set_highlight(bool highlight)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
return _input.highlight(highlight);
|
||||
}
|
||||
|
||||
bool SdlInputWidgetPair::set_str(const std::string& text)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
return update_input_text(text);
|
||||
}
|
||||
|
||||
bool SdlInputWidgetPair::remove_str(size_t count)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
|
||||
auto text = _text;
|
||||
if (text.empty())
|
||||
return true;
|
||||
|
||||
auto newsize = text.size() - std::min<size_t>(text.size(), count);
|
||||
return update_input_text(text.substr(0, newsize));
|
||||
}
|
||||
|
||||
bool SdlInputWidgetPair::append_str(const std::string& text)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
|
||||
auto itext = _text;
|
||||
itext.append(text);
|
||||
return update_input_text(itext);
|
||||
}
|
||||
|
||||
const SDL_FRect& SdlInputWidgetPair::input_rect() const
|
||||
{
|
||||
return _input.rect();
|
||||
}
|
||||
|
||||
std::string SdlInputWidgetPair::value() const
|
||||
{
|
||||
return _text;
|
||||
}
|
||||
|
||||
bool SdlInputWidgetPair::readonly() const
|
||||
{
|
||||
return (_flags & SDL_INPUT_READONLY) != 0;
|
||||
}
|
||||
|
||||
bool SdlInputWidgetPair::update()
|
||||
{
|
||||
// TODO: Draw the pair area
|
||||
if (!_label.update())
|
||||
return false;
|
||||
if (!_input.update())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SdlInputWidgetPair::update_input_text(const std::string& txt)
|
||||
{
|
||||
_text = txt;
|
||||
auto text = txt;
|
||||
|
||||
if (_flags & SDL_INPUT_MASK)
|
||||
{
|
||||
std::fill(text.begin(), text.end(), '*');
|
||||
}
|
||||
|
||||
return _input.update_text(text);
|
||||
}
|
||||
73
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget_pair.hpp
vendored
Normal file
73
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget_pair.hpp
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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 <string>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include "sdl_widget.hpp"
|
||||
#include "sdl_input_widget.hpp"
|
||||
|
||||
class SdlInputWidgetPair
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
SDL_INPUT_MASK = 1,
|
||||
SDL_INPUT_READONLY = 2
|
||||
};
|
||||
|
||||
SdlInputWidgetPair(std::shared_ptr<SDL_Renderer>& renderer, const std::string& label,
|
||||
const std::string& initial, Uint32 flags, size_t offset, size_t width,
|
||||
size_t height);
|
||||
SdlInputWidgetPair(SdlInputWidgetPair&& other) noexcept;
|
||||
SdlInputWidgetPair(const SdlInputWidgetPair& other) = delete;
|
||||
~SdlInputWidgetPair() = default;
|
||||
|
||||
SdlInputWidgetPair& operator=(const SdlInputWidgetPair& other) = delete;
|
||||
SdlInputWidgetPair& operator=(SdlInputWidgetPair&& other) = delete;
|
||||
|
||||
bool set_mouseover(bool mouseOver);
|
||||
bool set_highlight(bool highlight);
|
||||
|
||||
[[nodiscard]] bool set_str(const std::string& text);
|
||||
[[nodiscard]] bool remove_str(size_t count);
|
||||
[[nodiscard]] bool append_str(const std::string& text);
|
||||
|
||||
[[nodiscard]] const SDL_FRect& input_rect() const;
|
||||
[[nodiscard]] std::string value() const;
|
||||
|
||||
[[nodiscard]] bool readonly() const;
|
||||
|
||||
[[nodiscard]] bool update();
|
||||
|
||||
protected:
|
||||
[[nodiscard]] bool update_input_text(const std::string& txt);
|
||||
|
||||
Uint32 _vpadding = 5;
|
||||
Uint32 _hpadding = 10;
|
||||
|
||||
private:
|
||||
Uint32 _flags{};
|
||||
SdlWidget _label;
|
||||
SdlInputWidget _input;
|
||||
std::string _text;
|
||||
};
|
||||
313
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget_pair_list.cpp
vendored
Normal file
313
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget_pair_list.cpp
vendored
Normal file
@@ -0,0 +1,313 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2025 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2025 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 <algorithm>
|
||||
|
||||
#include <winpr/cast.h>
|
||||
|
||||
#include "sdl_widget_list.hpp"
|
||||
#include "sdl_input_widget_pair_list.hpp"
|
||||
|
||||
static const Uint32 vpadding = 5;
|
||||
|
||||
SdlInputWidgetPairList::SdlInputWidgetPairList(const std::string& title,
|
||||
const std::vector<std::string>& labels,
|
||||
const std::vector<std::string>& initial,
|
||||
const std::vector<Uint32>& flags, ssize_t selected)
|
||||
{
|
||||
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);
|
||||
|
||||
if (reset(title, total_width, total_height))
|
||||
{
|
||||
for (size_t x = 0; x < labels.size(); x++)
|
||||
{
|
||||
auto widget =
|
||||
std::make_shared<SdlInputWidgetPair>(_renderer, labels.at(x), initial.at(x),
|
||||
flags.at(x), x, widget_width, widget_heigth);
|
||||
m_list.emplace_back(widget);
|
||||
}
|
||||
|
||||
std::ignore = _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);
|
||||
m_currentActiveTextInput = selected;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t SdlInputWidgetPairList::next(ssize_t current)
|
||||
{
|
||||
size_t iteration = 0;
|
||||
auto val = static_cast<size_t>(current);
|
||||
|
||||
do
|
||||
{
|
||||
if (iteration >= m_list.size())
|
||||
return -1;
|
||||
|
||||
if (iteration == 0)
|
||||
{
|
||||
if (current < 0)
|
||||
val = 0;
|
||||
else
|
||||
val++;
|
||||
}
|
||||
else
|
||||
val++;
|
||||
iteration++;
|
||||
val %= m_list.size();
|
||||
} while (!valid(static_cast<ssize_t>(val)));
|
||||
return static_cast<ssize_t>(val);
|
||||
}
|
||||
|
||||
bool SdlInputWidgetPairList::valid(ssize_t current) const
|
||||
{
|
||||
if (current < 0)
|
||||
return false;
|
||||
auto s = static_cast<size_t>(current);
|
||||
if (s >= m_list.size())
|
||||
return false;
|
||||
return !m_list.at(s)->readonly();
|
||||
}
|
||||
|
||||
std::shared_ptr<SdlInputWidgetPair> SdlInputWidgetPairList::get(ssize_t index)
|
||||
{
|
||||
if (index < 0)
|
||||
return nullptr;
|
||||
auto s = static_cast<size_t>(index);
|
||||
if (s >= m_list.size())
|
||||
return nullptr;
|
||||
return m_list.at(s);
|
||||
}
|
||||
|
||||
SdlInputWidgetPairList::~SdlInputWidgetPairList()
|
||||
{
|
||||
m_list.clear();
|
||||
_buttons.clear();
|
||||
}
|
||||
|
||||
bool SdlInputWidgetPairList::updateInternal()
|
||||
{
|
||||
for (auto& btn : m_list)
|
||||
{
|
||||
if (!btn->update())
|
||||
return false;
|
||||
if (!btn->update())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t SdlInputWidgetPairList::get_index(const SDL_MouseButtonEvent& button)
|
||||
{
|
||||
const auto x = button.x;
|
||||
const auto y = button.y;
|
||||
for (size_t i = 0; i < m_list.size(); i++)
|
||||
{
|
||||
auto& cur = m_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 WINPR_ASSERTING_INT_CAST(ssize_t, i);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SdlInputWidgetPairList::run(std::vector<std::string>& result)
|
||||
{
|
||||
int res = -1;
|
||||
ssize_t LastActiveTextInput = -1;
|
||||
m_currentActiveTextInput = next(m_currentActiveTextInput);
|
||||
|
||||
if (!_window || !_renderer)
|
||||
return -2;
|
||||
|
||||
if (!SDL_StartTextInput(_window.get()))
|
||||
return -3;
|
||||
|
||||
try
|
||||
{
|
||||
bool running = true;
|
||||
while (running)
|
||||
{
|
||||
if (!update())
|
||||
throw;
|
||||
|
||||
SDL_Event event = {};
|
||||
if (!SDL_WaitEvent(&event))
|
||||
throw;
|
||||
do
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_KEY_UP:
|
||||
{
|
||||
switch (event.key.key)
|
||||
{
|
||||
case SDLK_BACKSPACE:
|
||||
{
|
||||
auto cur = get(m_currentActiveTextInput);
|
||||
if (cur)
|
||||
{
|
||||
if ((event.key.mod & SDL_KMOD_CTRL) != 0)
|
||||
{
|
||||
if (!cur->set_str(""))
|
||||
throw;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!cur->remove_str(1))
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDLK_TAB:
|
||||
m_currentActiveTextInput = next(m_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 ((event.key.mod & SDL_KMOD_CTRL) != 0)
|
||||
{
|
||||
auto cur = get(m_currentActiveTextInput);
|
||||
if (cur)
|
||||
{
|
||||
auto text = SDL_GetClipboardText();
|
||||
if (!cur->set_str(text))
|
||||
throw;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_TEXT_INPUT:
|
||||
{
|
||||
auto cur = get(m_currentActiveTextInput);
|
||||
if (cur)
|
||||
{
|
||||
if (!cur->append_str(event.text.text))
|
||||
throw;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
{
|
||||
auto TextInputIndex = get_index(event.button);
|
||||
for (auto& cur : m_list)
|
||||
{
|
||||
cur->set_mouseover(false);
|
||||
}
|
||||
if (TextInputIndex >= 0)
|
||||
{
|
||||
auto& cur = m_list.at(static_cast<size_t>(TextInputIndex));
|
||||
cur->set_mouseover(true);
|
||||
}
|
||||
|
||||
_buttons.set_mouseover(event.button.x, event.button.y);
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
{
|
||||
auto val = get_index(event.button);
|
||||
if (valid(val))
|
||||
m_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_EVENT_QUIT:
|
||||
res = INPUT_BUTTON_CANCEL;
|
||||
running = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (SDL_PollEvent(&event));
|
||||
|
||||
if (LastActiveTextInput != m_currentActiveTextInput)
|
||||
{
|
||||
LastActiveTextInput = m_currentActiveTextInput;
|
||||
}
|
||||
|
||||
for (auto& cur : m_list)
|
||||
{
|
||||
if (!cur->set_highlight(false))
|
||||
throw;
|
||||
}
|
||||
auto cur = get(m_currentActiveTextInput);
|
||||
if (cur)
|
||||
{
|
||||
if (!cur->set_highlight(true))
|
||||
throw;
|
||||
}
|
||||
|
||||
auto rc = SDL_RenderPresent(_renderer.get());
|
||||
if (!rc)
|
||||
{
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "[%s] SDL_RenderPresent failed with %s",
|
||||
__func__, SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& cur : m_list)
|
||||
result.push_back(cur->value());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
res = -2;
|
||||
}
|
||||
if (!SDL_StopTextInput(_window.get()))
|
||||
return -4;
|
||||
|
||||
return res;
|
||||
}
|
||||
63
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget_pair_list.hpp
vendored
Normal file
63
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_input_widget_pair_list.hpp
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2025 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2025 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "sdl_widget_list.hpp"
|
||||
#include "sdl_input_widget_pair.hpp"
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
class SdlInputWidgetPairList : public SdlWidgetList
|
||||
{
|
||||
public:
|
||||
SdlInputWidgetPairList(const std::string& title, const std::vector<std::string>& labels,
|
||||
const std::vector<std::string>& initial,
|
||||
const std::vector<Uint32>& flags, ssize_t selected = -1);
|
||||
SdlInputWidgetPairList(const SdlInputWidgetPairList& other) = delete;
|
||||
SdlInputWidgetPairList(SdlInputWidgetPairList&& other) = delete;
|
||||
|
||||
~SdlInputWidgetPairList() override;
|
||||
|
||||
SdlInputWidgetPairList& operator=(const SdlInputWidgetPairList& other) = delete;
|
||||
SdlInputWidgetPairList& operator=(SdlInputWidgetPairList&& other) = delete;
|
||||
|
||||
[[nodiscard]] int run(std::vector<std::string>& result);
|
||||
|
||||
protected:
|
||||
[[nodiscard]] bool updateInternal() override;
|
||||
[[nodiscard]] ssize_t get_index(const SDL_MouseButtonEvent& button);
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
INPUT_BUTTON_ACCEPT = 1,
|
||||
INPUT_BUTTON_CANCEL = -2
|
||||
};
|
||||
|
||||
[[nodiscard]] ssize_t next(ssize_t current);
|
||||
[[nodiscard]] bool valid(ssize_t current) const;
|
||||
[[nodiscard]] std::shared_ptr<SdlInputWidgetPair> get(ssize_t index);
|
||||
|
||||
std::vector<std::shared_ptr<SdlInputWidgetPair>> m_list;
|
||||
ssize_t m_currentActiveTextInput = -1;
|
||||
};
|
||||
45
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_select.cpp
vendored
Normal file
45
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_select.cpp
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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 <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include "sdl_select.hpp"
|
||||
#include "sdl_widget.hpp"
|
||||
#include "sdl_button.hpp"
|
||||
#include "sdl_buttons.hpp"
|
||||
#include "sdl_input_widget_pair_list.hpp"
|
||||
|
||||
SdlSelectWidget::SdlSelectWidget(std::shared_ptr<SDL_Renderer>& renderer, const std::string& label,
|
||||
const SDL_FRect& rect)
|
||||
: SdlSelectableWidget(renderer, rect)
|
||||
{
|
||||
_backgroundcolor = { 0x69, 0x66, 0x63, 0xff };
|
||||
_fontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
std::ignore = update_text(label);
|
||||
}
|
||||
|
||||
SdlSelectWidget::~SdlSelectWidget() = default;
|
||||
|
||||
SdlSelectWidget::SdlSelectWidget(SdlSelectWidget&& other) noexcept = default;
|
||||
39
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_select.hpp
vendored
Normal file
39
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_select.hpp
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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 <SDL3/SDL.h>
|
||||
#include "sdl_selectable_widget.hpp"
|
||||
|
||||
class SdlSelectWidget : public SdlSelectableWidget
|
||||
{
|
||||
public:
|
||||
SdlSelectWidget(std::shared_ptr<SDL_Renderer>& renderer, const std::string& label,
|
||||
const SDL_FRect& rect);
|
||||
SdlSelectWidget(SdlSelectWidget&& other) noexcept;
|
||||
SdlSelectWidget(const SdlSelectWidget& other) = delete;
|
||||
~SdlSelectWidget() override;
|
||||
|
||||
SdlSelectWidget& operator=(const SdlSelectWidget& other) = delete;
|
||||
SdlSelectWidget& operator=(SdlSelectWidget&& other) = delete;
|
||||
};
|
||||
200
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_select_list.cpp
vendored
Normal file
200
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_select_list.cpp
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
#include <cassert>
|
||||
#include <winpr/cast.h>
|
||||
#include "sdl_select_list.hpp"
|
||||
#include "../sdl_utils.hpp"
|
||||
|
||||
static const Uint32 vpadding = 5;
|
||||
|
||||
SdlSelectList::SdlSelectList(const std::string& title, const std::vector<std::string>& labels)
|
||||
{
|
||||
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;
|
||||
if (reset(title, widget_width, height))
|
||||
{
|
||||
SDL_FRect 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" };
|
||||
std::ignore = _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() = default;
|
||||
|
||||
int SdlSelectList::run()
|
||||
{
|
||||
int res = -2;
|
||||
ssize_t CurrentActiveTextInput = 0;
|
||||
bool running = true;
|
||||
|
||||
if (!_window || !_renderer)
|
||||
return -2;
|
||||
try
|
||||
{
|
||||
while (running)
|
||||
{
|
||||
if (!update())
|
||||
throw;
|
||||
|
||||
SDL_Event event = {};
|
||||
if (!SDL_WaitEvent(&event))
|
||||
throw;
|
||||
do
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
switch (event.key.key)
|
||||
{
|
||||
case SDLK_UP:
|
||||
case SDLK_BACKSPACE:
|
||||
if (CurrentActiveTextInput > 0)
|
||||
CurrentActiveTextInput--;
|
||||
else if (_list.empty())
|
||||
CurrentActiveTextInput = 0;
|
||||
else
|
||||
{
|
||||
auto s = _list.size();
|
||||
CurrentActiveTextInput =
|
||||
WINPR_ASSERTING_INT_CAST(ssize_t, s) - 1;
|
||||
}
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
case SDLK_TAB:
|
||||
if ((CurrentActiveTextInput < 0) || _list.empty())
|
||||
CurrentActiveTextInput = 0;
|
||||
else
|
||||
{
|
||||
auto s = _list.size();
|
||||
CurrentActiveTextInput++;
|
||||
if (s > 0)
|
||||
{
|
||||
CurrentActiveTextInput =
|
||||
CurrentActiveTextInput %
|
||||
WINPR_ASSERTING_INT_CAST(ssize_t, s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
case SDLK_RETURN2:
|
||||
case SDLK_KP_ENTER:
|
||||
running = false;
|
||||
res = static_cast<int>(CurrentActiveTextInput);
|
||||
break;
|
||||
case SDLK_ESCAPE:
|
||||
running = false;
|
||||
res = INPUT_BUTTON_CANCEL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
{
|
||||
auto TextInputIndex = get_index(event.button);
|
||||
reset_mouseover();
|
||||
if (TextInputIndex >= 0)
|
||||
{
|
||||
auto& cur = _list.at(WINPR_ASSERTING_INT_CAST(size_t, TextInputIndex));
|
||||
if (!cur.mouseover(true))
|
||||
throw;
|
||||
}
|
||||
|
||||
_buttons.set_mouseover(event.button.x, event.button.y);
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
{
|
||||
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_EVENT_QUIT:
|
||||
res = INPUT_BUTTON_CANCEL;
|
||||
running = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (SDL_PollEvent(&event));
|
||||
reset_highlight();
|
||||
if (CurrentActiveTextInput >= 0)
|
||||
{
|
||||
auto& cur = _list.at(WINPR_ASSERTING_INT_CAST(size_t, CurrentActiveTextInput));
|
||||
if (!cur.highlight(true))
|
||||
throw;
|
||||
}
|
||||
|
||||
if (!update())
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool SdlSelectList::updateInternal()
|
||||
{
|
||||
for (auto& cur : _list)
|
||||
{
|
||||
if (!cur.update())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t SdlSelectList::get_index(const SDL_MouseButtonEvent& button)
|
||||
{
|
||||
const auto x = button.x;
|
||||
const auto 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;
|
||||
}
|
||||
|
||||
void SdlSelectList::reset_mouseover()
|
||||
{
|
||||
for (auto& cur : _list)
|
||||
{
|
||||
cur.mouseover(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SdlSelectList::reset_highlight()
|
||||
{
|
||||
for (auto& cur : _list)
|
||||
{
|
||||
cur.highlight(false);
|
||||
}
|
||||
}
|
||||
41
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_select_list.hpp
vendored
Normal file
41
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_select_list.hpp
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "sdl_widget_list.hpp"
|
||||
#include "sdl_select.hpp"
|
||||
#include "sdl_button.hpp"
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
class SdlSelectList : public SdlWidgetList
|
||||
{
|
||||
public:
|
||||
SdlSelectList(const std::string& title, const std::vector<std::string>& labels);
|
||||
SdlSelectList(const SdlSelectList& other) = delete;
|
||||
SdlSelectList(SdlSelectList&& other) = delete;
|
||||
~SdlSelectList() override;
|
||||
|
||||
SdlSelectList& operator=(const SdlSelectList& other) = delete;
|
||||
SdlSelectList& operator=(SdlSelectList&& other) = delete;
|
||||
|
||||
[[nodiscard]] int run();
|
||||
|
||||
protected:
|
||||
[[nodiscard]] bool updateInternal() override;
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
INPUT_BUTTON_ACCEPT = 0,
|
||||
INPUT_BUTTON_CANCEL = -2
|
||||
};
|
||||
|
||||
ssize_t get_index(const SDL_MouseButtonEvent& button);
|
||||
void reset_mouseover();
|
||||
void reset_highlight();
|
||||
|
||||
std::vector<SdlSelectWidget> _list;
|
||||
};
|
||||
68
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_selectable_widget.cpp
vendored
Normal file
68
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_selectable_widget.cpp
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2025 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2025 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 "sdl_selectable_widget.hpp"
|
||||
#include "sdl_blend_mode_guard.hpp"
|
||||
|
||||
SdlSelectableWidget::SdlSelectableWidget(std::shared_ptr<SDL_Renderer>& renderer,
|
||||
const SDL_FRect& rect)
|
||||
: SdlWidget(renderer, rect)
|
||||
{
|
||||
}
|
||||
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
SdlSelectableWidget::SdlSelectableWidget(std::shared_ptr<SDL_Renderer>& renderer,
|
||||
const SDL_FRect& rect, SDL_IOStream* ops)
|
||||
: SdlWidget(renderer, rect, ops)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
SdlSelectableWidget::SdlSelectableWidget(SdlSelectableWidget&& other) noexcept = default;
|
||||
|
||||
SdlSelectableWidget::~SdlSelectableWidget() = default;
|
||||
|
||||
bool SdlSelectableWidget::highlight(bool enable)
|
||||
{
|
||||
_highlight = enable;
|
||||
return update();
|
||||
}
|
||||
|
||||
bool SdlSelectableWidget::mouseover(bool enable)
|
||||
{
|
||||
_mouseover = enable;
|
||||
return update();
|
||||
}
|
||||
|
||||
bool SdlSelectableWidget::updateInternal()
|
||||
{
|
||||
SdlBlendModeGuard guard(_renderer, SDL_BLENDMODE_NONE);
|
||||
std::vector<SDL_Color> colors;
|
||||
colors.push_back(_backgroundcolor);
|
||||
if (_highlight)
|
||||
colors.push_back(_highlightcolor);
|
||||
|
||||
if (_mouseover)
|
||||
colors.push_back(_mouseovercolor);
|
||||
|
||||
if (!fill(colors))
|
||||
return false;
|
||||
|
||||
return SdlWidget::updateInternal();
|
||||
}
|
||||
50
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_selectable_widget.hpp
vendored
Normal file
50
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_selectable_widget.hpp
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2025 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2025 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sdl_widget.hpp"
|
||||
|
||||
class SdlSelectableWidget : public SdlWidget
|
||||
{
|
||||
public:
|
||||
SdlSelectableWidget(std::shared_ptr<SDL_Renderer>& renderer, const SDL_FRect& rect);
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
SdlSelectableWidget(std::shared_ptr<SDL_Renderer>& renderer, const SDL_FRect& rect,
|
||||
SDL_IOStream* ops);
|
||||
#endif
|
||||
SdlSelectableWidget(SdlSelectableWidget&& other) noexcept;
|
||||
SdlSelectableWidget(const SdlSelectableWidget& other) = delete;
|
||||
~SdlSelectableWidget() override;
|
||||
|
||||
SdlSelectableWidget& operator=(const SdlSelectableWidget& other) = delete;
|
||||
SdlSelectableWidget& operator=(SdlSelectableWidget&& other) = delete;
|
||||
|
||||
bool highlight(bool enable);
|
||||
bool mouseover(bool enable);
|
||||
|
||||
protected:
|
||||
[[nodiscard]] bool updateInternal() override;
|
||||
SDL_Color _highlightcolor = { 0xcd, 0xca, 0x35, 0x60 };
|
||||
SDL_Color _mouseovercolor = { 0x66, 0xff, 0x66, 0x60 };
|
||||
|
||||
private:
|
||||
bool _mouseover = false;
|
||||
bool _highlight = false;
|
||||
};
|
||||
332
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_widget.cpp
vendored
Normal file
332
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_widget.cpp
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
/**
|
||||
* 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 <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include "sdl_widget.hpp"
|
||||
#include "sdl_blend_mode_guard.hpp"
|
||||
#include "../sdl_utils.hpp"
|
||||
|
||||
#include "res/sdl3_resource_manager.hpp"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
#endif
|
||||
|
||||
#define TAG CLIENT_TAG("SDL.widget")
|
||||
|
||||
static const Uint32 hpadding = 10;
|
||||
|
||||
SdlWidget::SdlWidget(std::shared_ptr<SDL_Renderer>& renderer, const SDL_FRect& rect)
|
||||
: _renderer(renderer),
|
||||
_engine(TTF_CreateRendererTextEngine(renderer.get()), TTF_DestroyRendererTextEngine),
|
||||
_rect(rect)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
auto ops = SDL3ResourceManager::get(SDLResourceManager::typeFonts(),
|
||||
"OpenSans-VariableFont_wdth,wght.ttf");
|
||||
if (!ops)
|
||||
widget_log_error(false, "SDLResourceManager::get");
|
||||
else
|
||||
{
|
||||
_font = std::shared_ptr<TTF_Font>(TTF_OpenFontIO(ops, true, 64), TTF_CloseFont);
|
||||
if (!_font)
|
||||
widget_log_error(false, "TTF_OpenFontRW");
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
SdlWidget::SdlWidget(std::shared_ptr<SDL_Renderer>& renderer, const SDL_FRect& rect,
|
||||
SDL_IOStream* ops)
|
||||
: _renderer(renderer),
|
||||
_engine(TTF_CreateRendererTextEngine(renderer.get()), TTF_DestroySurfaceTextEngine),
|
||||
_rect(rect)
|
||||
{
|
||||
if (ops)
|
||||
{
|
||||
_image = std::shared_ptr<SDL_Texture>(IMG_LoadTexture_IO(renderer.get(), ops, true),
|
||||
SDL_DestroyTexture);
|
||||
if (!_image)
|
||||
widget_log_error(false, "IMG_LoadTexture_IO");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SdlWidget::SdlWidget(SdlWidget&& other) noexcept
|
||||
: _renderer(std::move(other._renderer)), _backgroundcolor(other._backgroundcolor),
|
||||
_fontcolor(other._fontcolor), _text(std::move(other._text)), _font(std::move(other._font)),
|
||||
_image(std::move(other._image)), _engine(std::move(other._engine)), _rect(other._rect),
|
||||
_wrap(other._wrap), _text_width(other._text_width)
|
||||
{
|
||||
other._font = nullptr;
|
||||
other._image = nullptr;
|
||||
other._engine = nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SDL_Texture> SdlWidget::render_text(const std::string& text, SDL_Color fgcolor,
|
||||
SDL_FRect& src, SDL_FRect& dst) const
|
||||
{
|
||||
auto surface = std::shared_ptr<SDL_Surface>(
|
||||
TTF_RenderText_Blended(_font.get(), text.c_str(), 0, fgcolor), SDL_DestroySurface);
|
||||
if (!surface)
|
||||
{
|
||||
widget_log_error(false, "TTF_RenderText_Blended");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto texture = std::shared_ptr<SDL_Texture>(
|
||||
SDL_CreateTextureFromSurface(_renderer.get(), surface.get()), SDL_DestroyTexture);
|
||||
if (!texture)
|
||||
{
|
||||
widget_log_error(false, "SDL_CreateTextureFromSurface");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!_engine)
|
||||
{
|
||||
widget_log_error(false, "TTF_CreateRendererTextEngine");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<TTF_Text, decltype(&TTF_DestroyText)> txt(
|
||||
TTF_CreateText(_engine.get(), _font.get(), text.c_str(), text.size()), TTF_DestroyText);
|
||||
|
||||
if (!txt)
|
||||
{
|
||||
widget_log_error(false, "TTF_CreateText");
|
||||
return nullptr;
|
||||
}
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
if (!TTF_GetTextSize(txt.get(), &w, &h))
|
||||
{
|
||||
widget_log_error(false, "TTF_GetTextSize");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
src.w = static_cast<float>(w);
|
||||
src.h = static_cast<float>(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 float scale = dst.h / src.h;
|
||||
const float sws = (src.w) * scale;
|
||||
const float dws = (dst.w) / scale;
|
||||
dst.w = std::min(dst.w, sws);
|
||||
if (src.w > dws)
|
||||
{
|
||||
src.x = src.w - dws;
|
||||
src.w = dws;
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
static float scale(float dw, float dh)
|
||||
{
|
||||
const auto scale = dh / dw;
|
||||
const auto dr = dh * scale;
|
||||
return dr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SDL_Texture> SdlWidget::render_text_wrapped(const std::string& text,
|
||||
SDL_Color fgcolor, SDL_FRect& src,
|
||||
SDL_FRect& dst) const
|
||||
{
|
||||
assert(_text_width < INT32_MAX);
|
||||
|
||||
auto surface = std::shared_ptr<SDL_Surface>(
|
||||
TTF_RenderText_Blended_Wrapped(_font.get(), text.c_str(), 0, fgcolor,
|
||||
static_cast<int>(_text_width)),
|
||||
SDL_DestroySurface);
|
||||
if (!surface)
|
||||
{
|
||||
widget_log_error(false, "TTF_RenderText_Blended");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
src.w = static_cast<float>(surface->w);
|
||||
src.h = static_cast<float>(surface->h);
|
||||
|
||||
auto texture = std::shared_ptr<SDL_Texture>(
|
||||
SDL_CreateTextureFromSurface(_renderer.get(), surface.get()), SDL_DestroyTexture);
|
||||
if (!texture)
|
||||
{
|
||||
widget_log_error(false, "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<float>(dh, dst.h);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
SdlWidget::~SdlWidget() = default;
|
||||
|
||||
bool SdlWidget::error_ex(bool success, const char* what, const char* file, size_t line,
|
||||
const char* fkt)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
// Flip SDL3 convention to existing code convention to minimize code changes
|
||||
return false;
|
||||
}
|
||||
static wLog* log = nullptr;
|
||||
if (!log)
|
||||
log = WLog_Get(TAG);
|
||||
// Use -1 as it indicates error similar to SDL2 conventions
|
||||
// sdl_log_error_ex treats any value other than 0 as SDL error
|
||||
return sdl_log_error_ex(-1, log, what, file, line, fkt);
|
||||
}
|
||||
|
||||
bool SdlWidget::updateInternal()
|
||||
{
|
||||
return update_text(_text);
|
||||
}
|
||||
|
||||
bool SdlWidget::draw_rect(const SDL_FRect& rect, SDL_Color color) const
|
||||
{
|
||||
const auto drc = SDL_SetRenderDrawColor(_renderer.get(), color.r, color.g, color.b, color.a);
|
||||
if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
|
||||
return false;
|
||||
|
||||
const auto rc = SDL_RenderFillRect(_renderer.get(), &rect);
|
||||
return !widget_log_error(rc, "SDL_RenderFillRect");
|
||||
}
|
||||
|
||||
bool SdlWidget::fill(SDL_Color color) const
|
||||
{
|
||||
std::vector<SDL_Color> colors = { color };
|
||||
return fill(colors);
|
||||
}
|
||||
|
||||
bool SdlWidget::fill(const std::vector<SDL_Color>& colors) const
|
||||
{
|
||||
SdlBlendModeGuard guard(_renderer, SDL_BLENDMODE_NONE);
|
||||
|
||||
for (auto color : colors)
|
||||
{
|
||||
if (!draw_rect(_rect, color))
|
||||
return false;
|
||||
|
||||
if (!guard.update(SDL_BLENDMODE_ADD))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SdlWidget::wrap() const
|
||||
{
|
||||
return _wrap;
|
||||
}
|
||||
|
||||
bool SdlWidget::set_wrap(bool wrap, size_t width)
|
||||
{
|
||||
_wrap = wrap;
|
||||
_text_width = width;
|
||||
return _wrap;
|
||||
}
|
||||
|
||||
const SDL_FRect& SdlWidget::rect() const
|
||||
{
|
||||
return _rect;
|
||||
}
|
||||
|
||||
bool SdlWidget::clear() const
|
||||
{
|
||||
if (!_renderer)
|
||||
return false;
|
||||
|
||||
SdlBlendModeGuard guard(_renderer, SDL_BLENDMODE_NONE);
|
||||
|
||||
const auto drc = SDL_SetRenderDrawColor(_renderer.get(), _backgroundcolor.r, _backgroundcolor.g,
|
||||
_backgroundcolor.b, _backgroundcolor.a);
|
||||
if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
|
||||
return false;
|
||||
|
||||
const auto rcls = SDL_RenderRect(_renderer.get(), &_rect);
|
||||
return !widget_log_error(rcls, "SDL_RenderRect");
|
||||
}
|
||||
|
||||
bool SdlWidget::update()
|
||||
{
|
||||
if (!clear())
|
||||
return false;
|
||||
// TODO: Draw widget specifics
|
||||
return updateInternal();
|
||||
}
|
||||
|
||||
bool SdlWidget::update_text(const std::string& text)
|
||||
{
|
||||
_text = text;
|
||||
if (_text.empty())
|
||||
return true;
|
||||
|
||||
SDL_FRect src{};
|
||||
SDL_FRect dst{};
|
||||
|
||||
std::shared_ptr<SDL_Texture> texture;
|
||||
if (_image)
|
||||
{
|
||||
texture = _image;
|
||||
dst = _rect;
|
||||
auto propId = SDL_GetTextureProperties(_image.get());
|
||||
auto w = SDL_GetNumberProperty(propId, SDL_PROP_TEXTURE_WIDTH_NUMBER, -1);
|
||||
auto h = SDL_GetNumberProperty(propId, SDL_PROP_TEXTURE_HEIGHT_NUMBER, -1);
|
||||
if (w < 0 || h < 0)
|
||||
{
|
||||
if (!widget_log_error(false, "SDL_GetTextureProperties"))
|
||||
return false;
|
||||
}
|
||||
src.w = static_cast<float>(w);
|
||||
src.h = static_cast<float>(h);
|
||||
}
|
||||
else if (_wrap)
|
||||
texture = render_text_wrapped(_text, _fontcolor, src, dst);
|
||||
else
|
||||
texture = render_text(_text, _fontcolor, src, dst);
|
||||
if (!texture)
|
||||
return false;
|
||||
|
||||
const auto rc = SDL_RenderTexture(_renderer.get(), texture.get(), &src, &dst);
|
||||
return !widget_log_error(rc, "SDL_RenderCopy");
|
||||
}
|
||||
100
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_widget.hpp
vendored
Normal file
100
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_widget.hpp
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* 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 <memory>
|
||||
#include <vector>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/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(std::shared_ptr<SDL_Renderer>& renderer, const SDL_FRect& rect);
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
SdlWidget(std::shared_ptr<SDL_Renderer>& renderer, const SDL_FRect& rect, SDL_IOStream* ops);
|
||||
#endif
|
||||
SdlWidget(const SdlWidget& other) = delete;
|
||||
SdlWidget(SdlWidget&& other) noexcept;
|
||||
virtual ~SdlWidget();
|
||||
|
||||
SdlWidget& operator=(const SdlWidget& other) = delete;
|
||||
SdlWidget& operator=(SdlWidget&& other) = delete;
|
||||
|
||||
[[nodiscard]] bool fill(SDL_Color color) const;
|
||||
[[nodiscard]] bool fill(const std::vector<SDL_Color>& colors) const;
|
||||
[[nodiscard]] bool update_text(const std::string& text);
|
||||
|
||||
[[nodiscard]] bool wrap() const;
|
||||
[[nodiscard]] bool set_wrap(bool wrap = true, size_t width = 0);
|
||||
[[nodiscard]] const SDL_FRect& rect() const;
|
||||
|
||||
[[nodiscard]] bool update();
|
||||
|
||||
#define widget_log_error(res, what) SdlWidget::error_ex(res, what, __FILE__, __LINE__, __func__)
|
||||
static bool error_ex(bool success, const char* what, const char* file, size_t line,
|
||||
const char* fkt);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<SDL_Renderer> _renderer;
|
||||
SDL_Color _backgroundcolor = { 0x56, 0x56, 0x56, 0xff };
|
||||
SDL_Color _fontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
mutable std::string _text;
|
||||
|
||||
virtual bool clear() const;
|
||||
virtual bool updateInternal();
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool draw_rect(const SDL_FRect& rect, SDL_Color color) const;
|
||||
[[nodiscard]] std::shared_ptr<SDL_Texture>
|
||||
render_text(const std::string& text, SDL_Color fgcolor, SDL_FRect& src, SDL_FRect& dst) const;
|
||||
[[nodiscard]] std::shared_ptr<SDL_Texture> render_text_wrapped(const std::string& text,
|
||||
SDL_Color fgcolor,
|
||||
SDL_FRect& src,
|
||||
SDL_FRect& dst) const;
|
||||
|
||||
std::shared_ptr<TTF_Font> _font = nullptr;
|
||||
std::shared_ptr<SDL_Texture> _image = nullptr;
|
||||
std::shared_ptr<TTF_TextEngine> _engine = nullptr;
|
||||
SDL_FRect _rect = {};
|
||||
bool _wrap = false;
|
||||
size_t _text_width = 0;
|
||||
};
|
||||
63
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_widget_list.cpp
vendored
Normal file
63
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_widget_list.cpp
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "sdl_widget_list.hpp"
|
||||
#include "sdl_blend_mode_guard.hpp"
|
||||
|
||||
SdlWidgetList::~SdlWidgetList() = default;
|
||||
|
||||
bool SdlWidgetList::reset(const std::string& title, size_t width, size_t height)
|
||||
{
|
||||
auto w = WINPR_ASSERTING_INT_CAST(int, width);
|
||||
auto h = WINPR_ASSERTING_INT_CAST(int, height);
|
||||
SDL_Renderer* renderer = nullptr;
|
||||
SDL_Window* window = nullptr;
|
||||
auto rc = SDL_CreateWindowAndRenderer(
|
||||
title.c_str(), w, h, SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS, &window, &renderer);
|
||||
_renderer = std::shared_ptr<SDL_Renderer>(renderer, SDL_DestroyRenderer);
|
||||
_window = std::shared_ptr<SDL_Window>(window, SDL_DestroyWindow);
|
||||
if (!rc)
|
||||
widget_log_error(rc, "SDL_CreateWindowAndRenderer");
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool SdlWidgetList::visible() const
|
||||
{
|
||||
if (!_window || !_renderer)
|
||||
return false;
|
||||
|
||||
auto flags = SDL_GetWindowFlags(_window.get());
|
||||
return (flags & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED)) == 0;
|
||||
}
|
||||
|
||||
bool SdlWidgetList::clearWindow()
|
||||
{
|
||||
if (!_renderer)
|
||||
return false;
|
||||
|
||||
SdlBlendModeGuard guard(_renderer, SDL_BLENDMODE_NONE);
|
||||
const auto drc = SDL_SetRenderDrawColor(_renderer.get(), _backgroundcolor.r, _backgroundcolor.g,
|
||||
_backgroundcolor.b, _backgroundcolor.a);
|
||||
if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
|
||||
return false;
|
||||
|
||||
const auto rcls = SDL_RenderClear(_renderer.get());
|
||||
return !widget_log_error(rcls, "SDL_RenderClear");
|
||||
}
|
||||
|
||||
bool SdlWidgetList::update()
|
||||
{
|
||||
if (!visible())
|
||||
return true;
|
||||
|
||||
if (!clearWindow())
|
||||
return false;
|
||||
if (!updateInternal())
|
||||
return false;
|
||||
if (!_buttons.update())
|
||||
return false;
|
||||
auto rc = SDL_RenderPresent(_renderer.get());
|
||||
if (!rc)
|
||||
{
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "[%s] SDL_RenderPresent failed with %s", __func__,
|
||||
SDL_GetError());
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
34
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_widget_list.hpp
vendored
Normal file
34
third_party/FreeRDP/client/SDL/SDL3/dialogs/sdl_widget_list.hpp
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
class SdlWidgetList
|
||||
{
|
||||
public:
|
||||
SdlWidgetList() = default;
|
||||
|
||||
SdlWidgetList(const SdlWidgetList& other) = delete;
|
||||
SdlWidgetList(SdlWidgetList&& other) = delete;
|
||||
SdlWidgetList& operator=(const SdlWidgetList& other) = delete;
|
||||
SdlWidgetList& operator=(SdlWidgetList&& other) = delete;
|
||||
virtual ~SdlWidgetList();
|
||||
|
||||
[[nodiscard]] virtual bool reset(const std::string& title, size_t width, size_t height);
|
||||
|
||||
[[nodiscard]] virtual bool visible() const;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] bool update();
|
||||
[[nodiscard]] virtual bool clearWindow();
|
||||
[[nodiscard]] virtual bool updateInternal() = 0;
|
||||
|
||||
std::shared_ptr<SDL_Window> _window;
|
||||
std::shared_ptr<SDL_Renderer> _renderer;
|
||||
SdlButtonList _buttons;
|
||||
SDL_Color _backgroundcolor{ 0x38, 0x36, 0x35, 0xff };
|
||||
};
|
||||
5
third_party/FreeRDP/client/SDL/SDL3/dialogs/test/CMakeLists.txt
vendored
Normal file
5
third_party/FreeRDP/client/SDL/SDL3/dialogs/test/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
set(NAMES "TestSDLDialogInput;TestSDLDialogSelectList")
|
||||
foreach(NAME ${NAMES})
|
||||
add_executable(${NAME} ${NAME}.cpp)
|
||||
target_link_libraries(${NAME} PRIVATE sdl3-dialogs freerdp-client freerdp)
|
||||
endforeach()
|
||||
59
third_party/FreeRDP/client/SDL/SDL3/dialogs/test/TestSDLDialogInput.cpp
vendored
Normal file
59
third_party/FreeRDP/client/SDL/SDL3/dialogs/test/TestSDLDialogInput.cpp
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "../sdl_dialogs.hpp"
|
||||
#include "../sdl_input_widget_pair_list.hpp"
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
typedef int (*fkt_t)(void);
|
||||
|
||||
BOOL sdl_log_error_ex(Sint32 res, wLog* log, const char* what, const char* file, size_t line,
|
||||
const char* fkt)
|
||||
{
|
||||
WINPR_UNUSED(file);
|
||||
|
||||
WLog_Print(log, WLOG_ERROR, "[%s:%" PRIuz "][%s]: %s", fkt, line, what, "xxx");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int auth_dialogs(void)
|
||||
{
|
||||
const std::string title = "sometitle";
|
||||
std::vector<std::string> result;
|
||||
|
||||
std::vector<std::string> initial{ "Smartcard", "abc", "def" };
|
||||
std::vector<Uint32> flags = { SdlInputWidgetPair::SDL_INPUT_READONLY,
|
||||
SdlInputWidgetPair::SDL_INPUT_MASK, 0 };
|
||||
|
||||
const std::vector<std::string>& labels{ "foo", "bar", "gaga" };
|
||||
|
||||
SdlInputWidgetPairList ilist(title.c_str(), labels, initial, flags);
|
||||
auto rc = ilist.run(result);
|
||||
|
||||
if ((result.size() < labels.size()))
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int runTest(fkt_t fkt)
|
||||
{
|
||||
int rc;
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
sdl_dialogs_init();
|
||||
try
|
||||
{
|
||||
rc = fkt();
|
||||
}
|
||||
catch (int e)
|
||||
{
|
||||
rc = e;
|
||||
}
|
||||
sdl_dialogs_uninit();
|
||||
SDL_Quit();
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
|
||||
{
|
||||
return runTest(auth_dialogs);
|
||||
}
|
||||
47
third_party/FreeRDP/client/SDL/SDL3/dialogs/test/TestSDLDialogSelectList.cpp
vendored
Normal file
47
third_party/FreeRDP/client/SDL/SDL3/dialogs/test/TestSDLDialogSelectList.cpp
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "../sdl_dialogs.hpp"
|
||||
#include "../sdl_select_list.hpp"
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
typedef int (*fkt_t)(void);
|
||||
|
||||
BOOL sdl_log_error_ex(Sint32 res, wLog* log, const char* what, const char* file, size_t line,
|
||||
const char* fkt)
|
||||
{
|
||||
WINPR_UNUSED(file);
|
||||
|
||||
WLog_Print(log, WLOG_ERROR, "[%s:%" PRIuz "][%s]: %s", fkt, line, what, "xxx");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int select_dialogs(void)
|
||||
{
|
||||
const std::vector<std::string> labels{ "foo", "bar", "gaga", "blabla" };
|
||||
SdlSelectList list{ "title", labels };
|
||||
return list.run();
|
||||
}
|
||||
|
||||
static int runTest(fkt_t fkt)
|
||||
{
|
||||
int rc;
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
sdl_dialogs_init();
|
||||
try
|
||||
{
|
||||
rc = fkt();
|
||||
}
|
||||
catch (int e)
|
||||
{
|
||||
rc = e;
|
||||
}
|
||||
sdl_dialogs_uninit();
|
||||
SDL_Quit();
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
|
||||
{
|
||||
return runTest(select_dialogs);
|
||||
}
|
||||
Reference in New Issue
Block a user