mirror of
https://github.com/FreeRDP/FreeRDP.git
synced 2024-11-23 01:44:35 +08:00
[client,sdl] create a copy for SDL3
* Copy SDL2 client to SDL3 as the new version is not API compatible * Move existing SDL2 client to SDL2 directory * Move fonts from SDL client to resources folder in top level directory
This commit is contained in:
parent
45d57a8e65
commit
8281186a6d
@ -17,6 +17,8 @@
|
||||
|
||||
# Clients
|
||||
|
||||
include(CMakeDependentOption)
|
||||
|
||||
if(WITH_CLIENT_COMMON)
|
||||
add_subdirectory(common)
|
||||
endif()
|
||||
@ -30,12 +32,24 @@ if(FREERDP_VENDOR AND WITH_CLIENT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_CLIENT_SDL)
|
||||
cmake_dependent_option(WITH_CLIENT_SDL2 "[experimental] build experimental SDL2 client" ON WITH_CLIENT_SDL OFF)
|
||||
cmake_dependent_option(WITH_CLIENT_SDL3 "[experimental] build experimental SDL3 client" OFF WITH_CLIENT_SDL OFF)
|
||||
|
||||
if (WITH_CLIENT_SDL2)
|
||||
find_package(SDL2)
|
||||
if (SDL2_FOUND)
|
||||
add_subdirectory(SDL)
|
||||
add_subdirectory(SDL2)
|
||||
else()
|
||||
message(WARNING "SDL2 requested but not found, continuing build without SDL client")
|
||||
message(WARNING "SDL2 requested but not found, continuing build without SDL2 client")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WITH_CLIENT_SDL3)
|
||||
find_package(SDL3)
|
||||
if (SDL3_FOUND)
|
||||
add_subdirectory(SDL3)
|
||||
else()
|
||||
message(WARNING "SDL3 requested but not found, continuing build without SDL3 client")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -88,21 +88,21 @@ set(SRCS
|
||||
sdl_window.cpp
|
||||
)
|
||||
|
||||
add_library(sdl_prefs STATIC
|
||||
add_library(sdl2-prefs STATIC
|
||||
sdl_prefs.hpp
|
||||
sdl_prefs.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(aad)
|
||||
set(LIBS
|
||||
list(APPEND LIBS
|
||||
winpr
|
||||
freerdp
|
||||
freerdp-client
|
||||
Threads::Threads
|
||||
sdl_client_res
|
||||
dialogs
|
||||
aad-view
|
||||
sdl_prefs
|
||||
sdl2_client_res
|
||||
sdl2-dialogs
|
||||
sdl2-aad-view
|
||||
sdl2-prefs
|
||||
)
|
||||
|
||||
if (NOT WITH_SDL_LINK_SHARED)
|
||||
@ -114,7 +114,7 @@ endif()
|
||||
AddTargetWithResourceFile(${PROJECT_NAME} "${WIN32_GUI_FLAG}" "${PROJECT_VERSION}" SRCS)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBS})
|
||||
target_link_libraries(sdl_prefs winpr)
|
||||
target_link_libraries(sdl2-prefs winpr)
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER "Client/SDL")
|
||||
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT client)
|
||||
|
@ -59,16 +59,16 @@ endif()
|
||||
|
||||
configure_file(sdl_config.hpp.in sdl_config.hpp @ONLY)
|
||||
|
||||
add_library(aad-view STATIC
|
||||
add_library(sdl2-aad-view STATIC
|
||||
${SRCS}
|
||||
)
|
||||
target_include_directories(aad-view PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_link_libraries(aad-view
|
||||
target_include_directories(sdl2-aad-view PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_link_libraries(sdl2-aad-view
|
||||
PRIVATE
|
||||
${LIBS}
|
||||
)
|
||||
target_compile_definitions(
|
||||
aad-view
|
||||
sdl2-aad-view
|
||||
PUBLIC
|
||||
${DEFINITIONS}
|
||||
)
|
@ -601,9 +601,8 @@ namespace webview
|
||||
m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
}
|
||||
g_signal_connect(G_OBJECT(m_window), "destroy",
|
||||
G_CALLBACK(+[](GtkWidget*, gpointer arg) {
|
||||
static_cast<gtk_webkit_engine*>(arg)->terminate();
|
||||
}),
|
||||
G_CALLBACK(+[](GtkWidget*, gpointer arg)
|
||||
{ static_cast<gtk_webkit_engine*>(arg)->terminate(); }),
|
||||
this);
|
||||
// Initialize webview widget
|
||||
m_webview = webkit_web_view_new();
|
||||
@ -611,12 +610,13 @@ namespace webview
|
||||
webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
|
||||
g_signal_connect(manager, "script-message-received::external",
|
||||
G_CALLBACK(+[](WebKitUserContentManager*,
|
||||
WebKitJavascriptResult* r, gpointer arg) {
|
||||
auto* w = static_cast<gtk_webkit_engine*>(arg);
|
||||
char* s = get_string_from_js_result(r);
|
||||
w->on_message(s);
|
||||
g_free(s);
|
||||
}),
|
||||
WebKitJavascriptResult* r, gpointer arg)
|
||||
{
|
||||
auto* w = static_cast<gtk_webkit_engine*>(arg);
|
||||
char* s = get_string_from_js_result(r);
|
||||
w->on_message(s);
|
||||
g_free(s);
|
||||
}),
|
||||
this);
|
||||
webkit_user_content_manager_register_script_message_handler(manager, "external");
|
||||
init("window.external={invoke:function(s){window.webkit.messageHandlers."
|
||||
@ -1039,11 +1039,13 @@ namespace webview
|
||||
if (!m_parent_window)
|
||||
{
|
||||
class_addMethod(cls, "applicationDidFinishLaunching:"_sel,
|
||||
(IMP)(+[](id self, SEL, id notification) {
|
||||
auto app = objc::msg_send<id>(notification, "object"_sel);
|
||||
auto w = get_associated_webview(self);
|
||||
w->on_application_did_finish_launching(self, app);
|
||||
}),
|
||||
(IMP)(+[](id self, SEL, id notification)
|
||||
{
|
||||
auto app =
|
||||
objc::msg_send<id>(notification, "object"_sel);
|
||||
auto w = get_associated_webview(self);
|
||||
w->on_application_did_finish_launching(self, app);
|
||||
}),
|
||||
"v@:@");
|
||||
}
|
||||
objc_registerClassPair(cls);
|
||||
@ -1054,13 +1056,15 @@ namespace webview
|
||||
auto cls = objc_allocateClassPair((Class) "NSResponder"_cls,
|
||||
"WebkitScriptMessageHandler", 0);
|
||||
class_addProtocol(cls, objc_getProtocol("WKScriptMessageHandler"));
|
||||
class_addMethod(cls, "userContentController:didReceiveScriptMessage:"_sel,
|
||||
(IMP)(+[](id self, SEL, id, id msg) {
|
||||
auto w = get_associated_webview(self);
|
||||
w->on_message(objc::msg_send<const char*>(
|
||||
objc::msg_send<id>(msg, "body"_sel), "UTF8String"_sel));
|
||||
}),
|
||||
"v@:@@");
|
||||
class_addMethod(
|
||||
cls, "userContentController:didReceiveScriptMessage:"_sel,
|
||||
(IMP)(+[](id self, SEL, id, id msg)
|
||||
{
|
||||
auto w = get_associated_webview(self);
|
||||
w->on_message(objc::msg_send<const char*>(
|
||||
objc::msg_send<id>(msg, "body"_sel), "UTF8String"_sel));
|
||||
}),
|
||||
"v@:@@");
|
||||
objc_registerClassPair(cls);
|
||||
auto instance = objc::msg_send<id>((id)cls, "new"_sel);
|
||||
objc_setAssociatedObject(instance, "webview", (id)this, OBJC_ASSOCIATION_ASSIGN);
|
||||
@ -1073,38 +1077,41 @@ namespace webview
|
||||
class_addMethod(
|
||||
cls,
|
||||
"webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:"_sel,
|
||||
(IMP)(+[](id, SEL, id, id parameters, id, id completion_handler) {
|
||||
auto allows_multiple_selection =
|
||||
objc::msg_send<BOOL>(parameters, "allowsMultipleSelection"_sel);
|
||||
auto allows_directories =
|
||||
objc::msg_send<BOOL>(parameters, "allowsDirectories"_sel);
|
||||
(IMP)(+[](id, SEL, id, id parameters, id, id completion_handler)
|
||||
{
|
||||
auto allows_multiple_selection =
|
||||
objc::msg_send<BOOL>(parameters, "allowsMultipleSelection"_sel);
|
||||
auto allows_directories =
|
||||
objc::msg_send<BOOL>(parameters, "allowsDirectories"_sel);
|
||||
|
||||
// Show a panel for selecting files.
|
||||
auto panel = objc::msg_send<id>("NSOpenPanel"_cls, "openPanel"_sel);
|
||||
objc::msg_send<void>(panel, "setCanChooseFiles:"_sel, YES);
|
||||
objc::msg_send<void>(panel, "setCanChooseDirectories:"_sel,
|
||||
allows_directories);
|
||||
objc::msg_send<void>(panel, "setAllowsMultipleSelection:"_sel,
|
||||
allows_multiple_selection);
|
||||
auto modal_response =
|
||||
objc::msg_send<NSModalResponse>(panel, "runModal"_sel);
|
||||
// Show a panel for selecting files.
|
||||
auto panel = objc::msg_send<id>("NSOpenPanel"_cls, "openPanel"_sel);
|
||||
objc::msg_send<void>(panel, "setCanChooseFiles:"_sel, YES);
|
||||
objc::msg_send<void>(panel, "setCanChooseDirectories:"_sel,
|
||||
allows_directories);
|
||||
objc::msg_send<void>(panel, "setAllowsMultipleSelection:"_sel,
|
||||
allows_multiple_selection);
|
||||
auto modal_response =
|
||||
objc::msg_send<NSModalResponse>(panel, "runModal"_sel);
|
||||
|
||||
// Get the URLs for the selected files. If the modal was canceled
|
||||
// then we pass null to the completion handler to signify
|
||||
// cancellation.
|
||||
id urls = modal_response == NSModalResponseOK
|
||||
? objc::msg_send<id>(panel, "URLs"_sel)
|
||||
: nullptr;
|
||||
// Get the URLs for the selected files. If the modal was canceled
|
||||
// then we pass null to the completion handler to signify
|
||||
// cancellation.
|
||||
id urls = modal_response == NSModalResponseOK
|
||||
? objc::msg_send<id>(panel, "URLs"_sel)
|
||||
: nullptr;
|
||||
|
||||
// Invoke the completion handler block.
|
||||
auto sig = objc::msg_send<id>("NSMethodSignature"_cls,
|
||||
"signatureWithObjCTypes:"_sel, "v@?@");
|
||||
auto invocation = objc::msg_send<id>(
|
||||
"NSInvocation"_cls, "invocationWithMethodSignature:"_sel, sig);
|
||||
objc::msg_send<void>(invocation, "setTarget:"_sel, completion_handler);
|
||||
objc::msg_send<void>(invocation, "setArgument:atIndex:"_sel, &urls, 1);
|
||||
objc::msg_send<void>(invocation, "invoke"_sel);
|
||||
}),
|
||||
// Invoke the completion handler block.
|
||||
auto sig = objc::msg_send<id>("NSMethodSignature"_cls,
|
||||
"signatureWithObjCTypes:"_sel, "v@?@");
|
||||
auto invocation = objc::msg_send<id>(
|
||||
"NSInvocation"_cls, "invocationWithMethodSignature:"_sel, sig);
|
||||
objc::msg_send<void>(invocation, "setTarget:"_sel,
|
||||
completion_handler);
|
||||
objc::msg_send<void>(invocation, "setArgument:atIndex:"_sel, &urls,
|
||||
1);
|
||||
objc::msg_send<void>(invocation, "invoke"_sel);
|
||||
}),
|
||||
"v@:@@@@");
|
||||
objc_registerClassPair(cls);
|
||||
return objc::msg_send<id>((id)cls, "new"_sel);
|
||||
@ -1115,13 +1122,14 @@ namespace webview
|
||||
objc_allocateClassPair((Class) "NSObject"_cls, "WebkitNavigationDelegate", 0);
|
||||
class_addProtocol(cls, objc_getProtocol("WKNavigationDelegate"));
|
||||
class_addMethod(cls, "webView:didFinishNavigation:"_sel,
|
||||
(IMP)(+[](id delegate, SEL sel, id webview, id navigation) {
|
||||
auto w = get_associated_webview(delegate);
|
||||
auto url = objc::msg_send<id>(webview, "URL"_sel);
|
||||
auto nstr = objc::msg_send<id>(url, "absoluteString"_sel);
|
||||
auto str = objc::msg_send<char*>(nstr, "UTF8String"_sel);
|
||||
w->m_navigateCallback(str, w->m_navigateCallbackArg);
|
||||
}),
|
||||
(IMP)(+[](id delegate, SEL sel, id webview, id navigation)
|
||||
{
|
||||
auto w = get_associated_webview(delegate);
|
||||
auto url = objc::msg_send<id>(webview, "URL"_sel);
|
||||
auto nstr = objc::msg_send<id>(url, "absoluteString"_sel);
|
||||
auto str = objc::msg_send<char*>(nstr, "UTF8String"_sel);
|
||||
w->m_navigateCallback(str, w->m_navigateCallbackArg);
|
||||
}),
|
||||
"v@:@");
|
||||
objc_registerClassPair(cls);
|
||||
auto instance = objc::msg_send<id>((id)cls, "new"_sel);
|
||||
@ -1360,7 +1368,8 @@ namespace webview
|
||||
template <typename T>
|
||||
std::array<unsigned int, 4> parse_version(const std::basic_string<T>& version) noexcept
|
||||
{
|
||||
auto parse_component = [](auto sb, auto se) -> unsigned int {
|
||||
auto parse_component = [](auto sb, auto se) -> unsigned int
|
||||
{
|
||||
try
|
||||
{
|
||||
auto n = std::stol(std::basic_string<T>(sb, se));
|
||||
@ -2256,42 +2265,44 @@ namespace webview
|
||||
wc.lpszClassName = L"webview";
|
||||
wc.hIcon = icon;
|
||||
wc.lpfnWndProc =
|
||||
(WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
|
||||
auto w = (win32_edge_engine*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
switch (msg)
|
||||
{
|
||||
case WM_SIZE:
|
||||
w->resize(hwnd);
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
DestroyWindow(hwnd);
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
w->terminate();
|
||||
break;
|
||||
case WM_GETMINMAXINFO:
|
||||
{
|
||||
auto lpmmi = (LPMINMAXINFO)lp;
|
||||
if (w == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (w->m_maxsz.x > 0 && w->m_maxsz.y > 0)
|
||||
{
|
||||
lpmmi->ptMaxSize = w->m_maxsz;
|
||||
lpmmi->ptMaxTrackSize = w->m_maxsz;
|
||||
}
|
||||
if (w->m_minsz.x > 0 && w->m_minsz.y > 0)
|
||||
{
|
||||
lpmmi->ptMinTrackSize = w->m_minsz;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return DefWindowProcW(hwnd, msg, wp, lp);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
(WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT
|
||||
{
|
||||
auto w =
|
||||
(win32_edge_engine*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
switch (msg)
|
||||
{
|
||||
case WM_SIZE:
|
||||
w->resize(hwnd);
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
DestroyWindow(hwnd);
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
w->terminate();
|
||||
break;
|
||||
case WM_GETMINMAXINFO:
|
||||
{
|
||||
auto lpmmi = (LPMINMAXINFO)lp;
|
||||
if (w == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (w->m_maxsz.x > 0 && w->m_maxsz.y > 0)
|
||||
{
|
||||
lpmmi->ptMaxSize = w->m_maxsz;
|
||||
lpmmi->ptMaxTrackSize = w->m_maxsz;
|
||||
}
|
||||
if (w->m_minsz.x > 0 && w->m_minsz.y > 0)
|
||||
{
|
||||
lpmmi->ptMinTrackSize = w->m_minsz;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return DefWindowProcW(hwnd, msg, wp, lp);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
RegisterClassExW(&wc);
|
||||
m_window = CreateWindowW(L"webview", L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, 640, 480, nullptr, nullptr, hInstance,
|
||||
@ -2469,7 +2480,9 @@ namespace webview
|
||||
PathCombineW(userDataFolder, dataPath, currentExeName);
|
||||
|
||||
m_com_handler = new webview2_com_handler(
|
||||
wnd, cb, [&](ICoreWebView2Controller* controller, ICoreWebView2* webview) {
|
||||
wnd, cb,
|
||||
[&](ICoreWebView2Controller* controller, ICoreWebView2* webview)
|
||||
{
|
||||
if (!controller || !webview)
|
||||
{
|
||||
flag.clear();
|
||||
@ -2482,10 +2495,12 @@ namespace webview
|
||||
flag.clear();
|
||||
});
|
||||
|
||||
m_com_handler->set_attempt_handler([&] {
|
||||
return m_webview2_loader.create_environment_with_options(
|
||||
nullptr, userDataFolder, nullptr, m_com_handler);
|
||||
});
|
||||
m_com_handler->set_attempt_handler(
|
||||
[&]
|
||||
{
|
||||
return m_webview2_loader.create_environment_with_options(
|
||||
nullptr, userDataFolder, nullptr, m_com_handler);
|
||||
});
|
||||
m_com_handler->try_create_environment();
|
||||
|
||||
MSG msg = {};
|
||||
@ -2602,8 +2617,8 @@ namespace webview
|
||||
// Synchronous bind
|
||||
void bind(const std::string& name, sync_binding_t fn)
|
||||
{
|
||||
auto wrapper = [this, fn](const std::string& seq, const std::string& req,
|
||||
void* /*arg*/) { resolve(seq, 0, fn(req)); };
|
||||
auto wrapper = [this, fn](const std::string& seq, const std::string& req, void* /*arg*/)
|
||||
{ resolve(seq, 0, fn(req)); };
|
||||
bind(name, wrapper, nullptr);
|
||||
}
|
||||
|
||||
@ -2651,18 +2666,20 @@ namespace webview
|
||||
|
||||
void resolve(const std::string& seq, int status, const std::string& result)
|
||||
{
|
||||
dispatch([seq, status, result, this]() {
|
||||
if (status == 0)
|
||||
{
|
||||
eval("window._rpc[" + seq + "].resolve(" + result + "); delete window._rpc[" +
|
||||
seq + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
eval("window._rpc[" + seq + "].reject(" + result + "); delete window._rpc[" +
|
||||
seq + "]");
|
||||
}
|
||||
});
|
||||
dispatch(
|
||||
[seq, status, result, this]()
|
||||
{
|
||||
if (status == 0)
|
||||
{
|
||||
eval("window._rpc[" + seq + "].resolve(" + result +
|
||||
"); delete window._rpc[" + seq + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
eval("window._rpc[" + seq + "].reject(" + result +
|
||||
"); delete window._rpc[" + seq + "]");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
@ -2755,9 +2772,8 @@ WEBVIEW_API void webview_bind(webview_t w, const char* name,
|
||||
{
|
||||
static_cast<webview::webview*>(w)->bind(
|
||||
name,
|
||||
[=](const std::string& seq, const std::string& req, void* arg) {
|
||||
fn(seq.c_str(), req.c_str(), arg);
|
||||
},
|
||||
[=](const std::string& seq, const std::string& req, void* arg)
|
||||
{ fn(seq.c_str(), req.c_str(), arg); },
|
||||
arg);
|
||||
}
|
||||
|
75
client/SDL2/dialogs/CMakeLists.txt
Normal file
75
client/SDL2/dialogs/CMakeLists.txt
Normal file
@ -0,0 +1,75 @@
|
||||
set(SRCS
|
||||
sdl_button.hpp
|
||||
sdl_button.cpp
|
||||
sdl_buttons.hpp
|
||||
sdl_buttons.cpp
|
||||
sdl_dialogs.cpp
|
||||
sdl_dialogs.hpp
|
||||
sdl_widget.hpp
|
||||
sdl_widget.cpp
|
||||
sdl_input.hpp
|
||||
sdl_input.cpp
|
||||
sdl_input_widgets.cpp
|
||||
sdl_input_widgets.hpp
|
||||
sdl_select.hpp
|
||||
sdl_select.cpp
|
||||
sdl_selectlist.hpp
|
||||
sdl_selectlist.cpp
|
||||
sdl_connection_dialog.cpp
|
||||
sdl_connection_dialog.hpp
|
||||
)
|
||||
|
||||
list(APPEND LIBS
|
||||
sdl2_client_res
|
||||
winpr
|
||||
)
|
||||
|
||||
if (NOT WITH_SDL_LINK_SHARED)
|
||||
list(APPEND LIBS ${SDL2_STATIC_LIBRARIES})
|
||||
else()
|
||||
list(APPEND LIBS ${SDL2_LIBRARIES})
|
||||
endif()
|
||||
|
||||
macro(find_sdl_component name)
|
||||
find_package(${name})
|
||||
if (NOT ${name}_FOUND)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(${name} REQUIRED ${name})
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
list(APPEND LIBS ${${name}_LIBRARIES})
|
||||
link_directories(${${name}_LIBRARY_DIRS})
|
||||
include_directories(${${name}_INCLUDE_DIRS})
|
||||
else()
|
||||
list(APPEND LIBS ${${name}_STATIC_LIBRARIES})
|
||||
link_directories(${${name}_STATIC_LIBRARY_DIRS})
|
||||
include_directories(${${name}_STATIC_INCLUDE_DIRS})
|
||||
endif()
|
||||
else()
|
||||
if (WITH_SDL_LINK_SHARED)
|
||||
list(APPEND LIBS ${name}::${name})
|
||||
else()
|
||||
list(APPEND LIBS ${name}::${name}-static)
|
||||
endif()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
find_sdl_component(SDL2_ttf)
|
||||
|
||||
option(WITH_SDL_IMAGE_DIALOGS "Build with SDL_image support (recommended)" OFF)
|
||||
if (WITH_SDL_IMAGE_DIALOGS)
|
||||
find_sdl_component(SDL2_image)
|
||||
add_definitions(-DWITH_SDL_IMAGE_DIALOGS)
|
||||
endif()
|
||||
|
||||
add_subdirectory(res)
|
||||
|
||||
add_library(sdl2-dialogs STATIC
|
||||
${SRCS}
|
||||
)
|
||||
|
||||
target_link_libraries(sdl2-dialogs PRIVATE ${LIBS})
|
||||
|
||||
if(BUILD_TESTING)
|
||||
# add_subdirectory(test)
|
||||
endif()
|
@ -1,5 +1,5 @@
|
||||
|
||||
add_executable(freerdp-res2bin
|
||||
add_executable(sdl2-res2bin
|
||||
convert_res_to_c.cpp
|
||||
)
|
||||
|
||||
@ -16,7 +16,7 @@ set(RES_SVG_FILES
|
||||
)
|
||||
|
||||
set(RES_FONT_FILES
|
||||
${CMAKE_SOURCE_DIR}/client/SDL/dialogs/font/OpenSans-VariableFont_wdth,wght.ttf
|
||||
${CMAKE_SOURCE_DIR}/resources/font/OpenSans-VariableFont_wdth,wght.ttf
|
||||
)
|
||||
|
||||
macro(convert_to_bin FILE FILE_TYPE)
|
||||
@ -33,9 +33,9 @@ macro(convert_to_bin FILE FILE_TYPE)
|
||||
add_custom_command(
|
||||
OUTPUT ${FILE_BYPRODUCTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${FILE_BIN_DIR}
|
||||
COMMAND $<TARGET_FILE:freerdp-res2bin> ${FILE} ${FILE_TYPE} ${TARGET_NAME} ${FILE_BIN_DIR}
|
||||
COMMAND $<TARGET_FILE:sdl2-res2bin> ${FILE} ${FILE_TYPE} ${TARGET_NAME} ${FILE_BIN_DIR}
|
||||
COMMENT "create image resources"
|
||||
DEPENDS freerdp-res2bin
|
||||
DEPENDS sdl2-res2bin
|
||||
DEPENDS ${FILE}
|
||||
)
|
||||
endmacro()
|
||||
@ -81,9 +81,9 @@ else()
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(sdl_client_res OBJECT
|
||||
add_library(sdl2_client_res OBJECT
|
||||
${RES_FILES}
|
||||
${SRCS}
|
||||
${FACTORY_SRCS}
|
||||
)
|
||||
set_property(TARGET sdl_client_res PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET sdl2_client_res PROPERTY POSITION_INDEPENDENT_CODE ON)
|
@ -1,4 +1,4 @@
|
||||
set(MODULE_NAME "TestSDL")
|
||||
set(MODULE_NAME "TestSDL2")
|
||||
set(MODULE_PREFIX "TEST_SDL")
|
||||
|
||||
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.cpp)
|
||||
@ -11,7 +11,7 @@ create_test_sourcelist(${MODULE_PREFIX}_SRCS
|
||||
|
||||
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp winpr sdl_prefs)
|
||||
set(${MODULE_PREFIX}_LIBS freerdp winpr sdl2-prefs)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
125
client/SDL3/CMakeLists.txt
Normal file
125
client/SDL3/CMakeLists.txt
Normal file
@ -0,0 +1,125 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP SDL Client
|
||||
#
|
||||
# Copyright 2022 Armin Novak <anovak@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.
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
endif()
|
||||
if (NOT FREERDP_DEFAULT_PROJECT_VERSION)
|
||||
set(FREERDP_DEFAULT_PROJECT_VERSION "1.0.0.0")
|
||||
endif()
|
||||
|
||||
project(sdl3-freerdp
|
||||
LANGUAGES CXX
|
||||
VERSION ${FREERDP_DEFAULT_PROJECT_VERSION}
|
||||
)
|
||||
|
||||
message("project ${PROJECT_NAME} is using version ${PROJECT_VERSION}")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS ON)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/)
|
||||
include(CommonConfigOptions)
|
||||
|
||||
include(ConfigureFreeRDP)
|
||||
|
||||
option(WITH_DEBUG_SDL_EVENTS "[dangerous, not for release builds!] Debug SDL events" ${DEFAULT_DEBUG_OPTION})
|
||||
option(WITH_DEBUG_SDL_KBD_EVENTS "[dangerous, not for release builds!] Debug SDL keyboard events" ${DEFAULT_DEBUG_OPTION})
|
||||
option(WITH_WIN_CONSOLE "Build ${PROJECT_NAME} with console support" ON)
|
||||
option(WITH_SDL_LINK_SHARED "link SDL dynamic or static" ON)
|
||||
|
||||
if(WITH_WIN_CONSOLE)
|
||||
set(WIN32_GUI_FLAG "TRUE")
|
||||
else()
|
||||
set(WIN32_GUI_FLAG "WIN32")
|
||||
endif()
|
||||
|
||||
|
||||
if (WITH_DEBUG_SDL_EVENTS)
|
||||
add_definitions(-DWITH_DEBUG_SDL_EVENTS)
|
||||
endif()
|
||||
if (WITH_DEBUG_SDL_KBD_EVENTS)
|
||||
add_definitions(-DWITH_DEBUG_SDL_KBD_EVENTS)
|
||||
endif()
|
||||
|
||||
find_package(SDL2 REQUIRED COMPONENTS)
|
||||
include_directories(${SDL2_INCLUDE_DIR})
|
||||
include_directories(${SDL2_INCLUDE_DIRS})
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_subdirectory(dialogs)
|
||||
set(SRCS
|
||||
sdl_types.hpp
|
||||
sdl_utils.cpp
|
||||
sdl_utils.hpp
|
||||
sdl_kbd.cpp
|
||||
sdl_kbd.hpp
|
||||
sdl_touch.cpp
|
||||
sdl_touch.hpp
|
||||
sdl_pointer.cpp
|
||||
sdl_pointer.hpp
|
||||
sdl_disp.cpp
|
||||
sdl_disp.hpp
|
||||
sdl_monitor.cpp
|
||||
sdl_monitor.hpp
|
||||
sdl_freerdp.hpp
|
||||
sdl_freerdp.cpp
|
||||
sdl_channels.hpp
|
||||
sdl_channels.cpp
|
||||
sdl_window.hpp
|
||||
sdl_window.cpp
|
||||
)
|
||||
|
||||
add_library(sdl3-prefs STATIC
|
||||
sdl_prefs.hpp
|
||||
sdl_prefs.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(aad)
|
||||
list(APPEND LIBS
|
||||
winpr
|
||||
freerdp
|
||||
freerdp-client
|
||||
Threads::Threads
|
||||
sdl3_client_res
|
||||
sdl3-dialogs
|
||||
sdl3-aad-view
|
||||
sdl3-prefs
|
||||
)
|
||||
|
||||
if (NOT WITH_SDL_LINK_SHARED)
|
||||
list(APPEND LIBS ${SDL2_STATIC_LIBRARIES})
|
||||
else()
|
||||
list(APPEND LIBS ${SDL2_LIBRARIES})
|
||||
endif()
|
||||
|
||||
AddTargetWithResourceFile(${PROJECT_NAME} "${WIN32_GUI_FLAG}" "${PROJECT_VERSION}" SRCS)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBS})
|
||||
target_link_libraries(sdl3-prefs winpr)
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER "Client/SDL")
|
||||
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT client)
|
||||
|
||||
add_subdirectory(man)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
75
client/SDL3/aad/CMakeLists.txt
Normal file
75
client/SDL3/aad/CMakeLists.txt
Normal file
@ -0,0 +1,75 @@
|
||||
set(WITH_WEBVIEW_DEFAULT OFF)
|
||||
if (UNIX AND NOT APPLE)
|
||||
set(WITH_WEBVIEW_DEFAULT ON)
|
||||
endif()
|
||||
|
||||
option(WITH_WEBVIEW "Build with WebView support for AAD login popup browser" ${WITH_WEBVIEW_DEFAULT})
|
||||
if (WITH_WEBVIEW)
|
||||
option(WITH_WEBVIEW_QT "Build with QtWebEngine support for AAD login broweser popup" OFF)
|
||||
|
||||
set(SRCS
|
||||
sdl_webview.hpp
|
||||
webview_impl.hpp
|
||||
sdl_webview.cpp
|
||||
)
|
||||
set(LIBS
|
||||
winpr
|
||||
)
|
||||
|
||||
if (WITH_WEBVIEW_QT)
|
||||
find_package(Qt5 COMPONENTS WebEngineWidgets REQUIRED)
|
||||
|
||||
list(APPEND SRCS
|
||||
qt/webview_impl.cpp
|
||||
)
|
||||
|
||||
list(APPEND LIBS
|
||||
Qt5::WebEngineWidgets
|
||||
)
|
||||
else()
|
||||
list(APPEND SRCS
|
||||
wrapper/webview.h
|
||||
wrapper/webview_impl.cpp
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
find_package(unofficial-webview2 CONFIG REQUIRED)
|
||||
list(APPEND LIBS
|
||||
unofficial::webview2::webview2
|
||||
)
|
||||
elseif(APPLE)
|
||||
find_library(WEBKIT Webkit REQUIRED)
|
||||
list(APPEND LIBS
|
||||
${WEBKIT}
|
||||
)
|
||||
else()
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(WEBVIEW_GTK webkit2gtk-4.0 REQUIRED)
|
||||
include_directories(${WEBVIEW_GTK_INCLUDE_DIRS})
|
||||
list(APPEND LIBS
|
||||
${WEBVIEW_GTK_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
set(SRCS
|
||||
dummy.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
configure_file(sdl_config.hpp.in sdl_config.hpp @ONLY)
|
||||
|
||||
add_library(sdl3-aad-view STATIC
|
||||
${SRCS}
|
||||
)
|
||||
target_include_directories(sdl3-aad-view PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_link_libraries(sdl3-aad-view
|
||||
PRIVATE
|
||||
${LIBS}
|
||||
)
|
||||
target_compile_definitions(
|
||||
sdl3-aad-view
|
||||
PUBLIC
|
||||
${DEFINITIONS}
|
||||
)
|
||||
|
0
client/SDL3/aad/dummy.cpp
Normal file
0
client/SDL3/aad/dummy.cpp
Normal file
105
client/SDL3/aad/qt/webview_impl.cpp
Normal file
105
client/SDL3/aad/qt/webview_impl.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Popup browser for AAD authentication
|
||||
*
|
||||
* Copyright 2023 Isaac Klein <fifthdegree@protonmail.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 <QApplication>
|
||||
#include <QWebEngineView>
|
||||
#include <QWebEngineProfile>
|
||||
#include <QWebEngineUrlScheme>
|
||||
#include <QWebEngineUrlSchemeHandler>
|
||||
#include <QWebEngineUrlRequestJob>
|
||||
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <cstdarg>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <freerdp/log.h>
|
||||
#include <freerdp/build-config.h>
|
||||
|
||||
#include "../webview_impl.hpp"
|
||||
|
||||
#define TAG CLIENT_TAG("sdl.webview")
|
||||
|
||||
class SchemeHandler : public QWebEngineUrlSchemeHandler
|
||||
{
|
||||
public:
|
||||
explicit SchemeHandler(QObject* parent = nullptr) : QWebEngineUrlSchemeHandler(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void requestStarted(QWebEngineUrlRequestJob* request) override
|
||||
{
|
||||
QUrl url = request->requestUrl();
|
||||
|
||||
int rc = -1;
|
||||
for (auto& param : url.query().split('&'))
|
||||
{
|
||||
QStringList pair = param.split('=');
|
||||
|
||||
if (pair.size() != 2 || pair[0] != QLatin1String("code"))
|
||||
continue;
|
||||
|
||||
auto qc = pair[1];
|
||||
m_code = qc.toStdString();
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
qApp->exit(rc);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string code() const
|
||||
{
|
||||
return m_code;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_code{};
|
||||
};
|
||||
|
||||
bool webview_impl_run(const std::string& title, const std::string& url, std::string& code)
|
||||
{
|
||||
int argc = 1;
|
||||
const auto vendor = QString::fromUtf8(FREERDP_VENDOR_STRING);
|
||||
const auto product = QString::fromUtf8(FREERDP_PRODUCT_STRING);
|
||||
QWebEngineUrlScheme::registerScheme(QWebEngineUrlScheme("ms-appx-web"));
|
||||
|
||||
std::string wtitle = title;
|
||||
char* argv[] = { wtitle.data() };
|
||||
QCoreApplication::setOrganizationName(vendor);
|
||||
QCoreApplication::setApplicationName(product);
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QApplication app(argc, argv);
|
||||
|
||||
SchemeHandler handler;
|
||||
QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("ms-appx-web", &handler);
|
||||
|
||||
QWebEngineView webview;
|
||||
webview.load(QUrl(QString::fromStdString(url)));
|
||||
webview.show();
|
||||
|
||||
if (app.exec() != 0)
|
||||
return false;
|
||||
|
||||
auto val = handler.code();
|
||||
if (val.empty())
|
||||
return false;
|
||||
code = val;
|
||||
|
||||
return !code.empty();
|
||||
}
|
3
client/SDL3/aad/sdl_config.hpp.in
Normal file
3
client/SDL3/aad/sdl_config.hpp.in
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#cmakedefine WITH_WEBVIEW
|
129
client/SDL3/aad/sdl_webview.cpp
Normal file
129
client/SDL3/aad/sdl_webview.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Popup browser for AAD authentication
|
||||
*
|
||||
* Copyright 2023 Isaac Klein <fifthdegree@protonmail.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 <string>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
#include <winpr/string.h>
|
||||
#include <freerdp/log.h>
|
||||
|
||||
#include "sdl_webview.hpp"
|
||||
#include "webview_impl.hpp"
|
||||
|
||||
#define TAG CLIENT_TAG("SDL.webview")
|
||||
|
||||
static BOOL sdl_webview_get_rdsaad_access_token(freerdp* instance, const char* scope,
|
||||
const char* req_cnf, char** token)
|
||||
{
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(scope);
|
||||
WINPR_ASSERT(req_cnf);
|
||||
WINPR_ASSERT(token);
|
||||
|
||||
WINPR_UNUSED(instance);
|
||||
|
||||
std::string client_id = "5177bc73-fd99-4c77-a90c-76844c9b6999";
|
||||
std::string redirect_uri =
|
||||
"ms-appx-web%3a%2f%2fMicrosoft.AAD.BrokerPlugin%2f5177bc73-fd99-4c77-a90c-76844c9b6999";
|
||||
|
||||
*token = nullptr;
|
||||
|
||||
auto url =
|
||||
"https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=" + client_id +
|
||||
"&response_type=code&scope=" + scope + "&redirect_uri=" + redirect_uri;
|
||||
|
||||
const std::string title = "FreeRDP WebView - AAD access token";
|
||||
std::string code;
|
||||
auto rc = webview_impl_run(title, url, code);
|
||||
if (!rc || code.empty())
|
||||
return FALSE;
|
||||
|
||||
auto token_request = "grant_type=authorization_code&code=" + code + "&client_id=" + client_id +
|
||||
"&scope=" + scope + "&redirect_uri=" + redirect_uri +
|
||||
"&req_cnf=" + req_cnf;
|
||||
return client_common_get_access_token(instance, token_request.c_str(), token);
|
||||
}
|
||||
|
||||
static BOOL sdl_webview_get_avd_access_token(freerdp* instance, char** token)
|
||||
{
|
||||
WINPR_ASSERT(token);
|
||||
|
||||
std::string client_id = "a85cf173-4192-42f8-81fa-777a763e6e2c";
|
||||
std::string redirect_uri =
|
||||
"ms-appx-web%3a%2f%2fMicrosoft.AAD.BrokerPlugin%2fa85cf173-4192-42f8-81fa-777a763e6e2c";
|
||||
std::string scope = "https%3A%2F%2Fwww.wvd.microsoft.com%2F.default";
|
||||
|
||||
*token = nullptr;
|
||||
|
||||
auto url =
|
||||
"https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=" + client_id +
|
||||
"&response_type=code&scope=" + scope + "&redirect_uri=" + redirect_uri;
|
||||
const std::string title = "FreeRDP WebView - AVD access token";
|
||||
std::string code;
|
||||
auto rc = webview_impl_run(title, url, code);
|
||||
if (!rc || code.empty())
|
||||
return FALSE;
|
||||
|
||||
auto token_request = "grant_type=authorization_code&code=" + code + "&client_id=" + client_id +
|
||||
"&scope=" + scope + "&redirect_uri=" + redirect_uri;
|
||||
return client_common_get_access_token(instance, token_request.c_str(), token);
|
||||
}
|
||||
|
||||
BOOL sdl_webview_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
|
||||
size_t count, ...)
|
||||
{
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(token);
|
||||
switch (tokenType)
|
||||
{
|
||||
case ACCESS_TOKEN_TYPE_AAD:
|
||||
{
|
||||
if (count < 2)
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
|
||||
", aborting",
|
||||
count);
|
||||
return FALSE;
|
||||
}
|
||||
else if (count > 2)
|
||||
WLog_WARN(TAG,
|
||||
"ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
|
||||
", ignoring",
|
||||
count);
|
||||
va_list ap;
|
||||
va_start(ap, count);
|
||||
const char* scope = va_arg(ap, const char*);
|
||||
const char* req_cnf = va_arg(ap, const char*);
|
||||
const BOOL rc = sdl_webview_get_rdsaad_access_token(instance, scope, req_cnf, token);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
case ACCESS_TOKEN_TYPE_AVD:
|
||||
if (count != 0)
|
||||
WLog_WARN(TAG,
|
||||
"ACCESS_TOKEN_TYPE_AVD expected 0 additional arguments, but got %" PRIuz
|
||||
", ignoring",
|
||||
count);
|
||||
return sdl_webview_get_avd_access_token(instance, token);
|
||||
default:
|
||||
WLog_ERR(TAG, "Unexpected value for AccessTokenType [%" PRIuz "], aborting", tokenType);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
38
client/SDL3/aad/sdl_webview.hpp
Normal file
38
client/SDL3/aad/sdl_webview.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Popup browser for AAD authentication
|
||||
*
|
||||
* Copyright 2023 Isaac Klein <fifthdegree@protonmail.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 <freerdp/freerdp.h>
|
||||
|
||||
#include <sdl_config.hpp>
|
||||
|
||||
#if defined(WITH_WEBVIEW)
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
BOOL sdl_webview_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
|
||||
size_t count, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
24
client/SDL3/aad/webview_impl.hpp
Normal file
24
client/SDL3/aad/webview_impl.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Popup browser for AAD authentication
|
||||
*
|
||||
* Copyright 2023 Isaac Klein <fifthdegree@protonmail.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>
|
||||
|
||||
bool webview_impl_run(const std::string& title, const std::string& url, std::string& code);
|
1
client/SDL3/aad/wrapper/README
Normal file
1
client/SDL3/aad/wrapper/README
Normal file
@ -0,0 +1 @@
|
||||
upstream at https://github.com/webview/webview/
|
2797
client/SDL3/aad/wrapper/webview.h
Normal file
2797
client/SDL3/aad/wrapper/webview.h
Normal file
File diff suppressed because it is too large
Load Diff
82
client/SDL3/aad/wrapper/webview_impl.cpp
Normal file
82
client/SDL3/aad/wrapper/webview_impl.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Popup browser for AAD authentication
|
||||
*
|
||||
* Copyright 2023 Isaac Klein <fifthdegree@protonmail.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 "webview.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include "../webview_impl.hpp"
|
||||
|
||||
static std::vector<std::string> split(const std::string& input, const std::string& regex)
|
||||
{
|
||||
// passing -1 as the submatch index parameter performs splitting
|
||||
std::regex re(regex);
|
||||
std::sregex_token_iterator first{ input.begin(), input.end(), re, -1 };
|
||||
std::sregex_token_iterator last;
|
||||
return { first, last };
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> urlsplit(const std::string& url)
|
||||
{
|
||||
auto pos = url.find("?");
|
||||
if (pos == std::string::npos)
|
||||
return {};
|
||||
auto surl = url.substr(pos);
|
||||
auto args = split(surl, "&");
|
||||
|
||||
std::map<std::string, std::string> argmap;
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
auto kv = split(arg, "=");
|
||||
if (kv.size() == 2)
|
||||
argmap.insert({ kv[0], kv[1] });
|
||||
}
|
||||
return argmap;
|
||||
}
|
||||
|
||||
static void fkt(const std::string& url, void* arg)
|
||||
{
|
||||
auto args = urlsplit(url);
|
||||
auto val = args.find("code");
|
||||
if (val == args.end())
|
||||
return;
|
||||
|
||||
assert(arg);
|
||||
auto rcode = static_cast<std::string*>(arg);
|
||||
*rcode = val->second;
|
||||
}
|
||||
|
||||
bool webview_impl_run(const std::string& title, const std::string& url, std::string& code)
|
||||
{
|
||||
webview::webview w(false, nullptr);
|
||||
|
||||
w.set_title(title);
|
||||
w.set_size(640, 480, WEBVIEW_HINT_NONE);
|
||||
|
||||
std::string scheme;
|
||||
w.add_scheme_handler("ms-appx-web", fkt, &scheme);
|
||||
w.add_navigate_listener(fkt, &code);
|
||||
w.navigate(url);
|
||||
w.run();
|
||||
return !code.empty();
|
||||
}
|
@ -20,7 +20,7 @@ set(SRCS
|
||||
)
|
||||
|
||||
list(APPEND LIBS
|
||||
sdl_client_res
|
||||
sdl3_client_res
|
||||
winpr
|
||||
)
|
||||
|
||||
@ -64,11 +64,11 @@ endif()
|
||||
|
||||
add_subdirectory(res)
|
||||
|
||||
add_library(dialogs STATIC
|
||||
add_library(sdl3-dialogs STATIC
|
||||
${SRCS}
|
||||
)
|
||||
|
||||
target_link_libraries(dialogs PRIVATE ${LIBS})
|
||||
target_link_libraries(sdl3-dialogs PRIVATE ${LIBS})
|
||||
|
||||
if(BUILD_TESTING)
|
||||
# add_subdirectory(test)
|
89
client/SDL3/dialogs/res/CMakeLists.txt
Normal file
89
client/SDL3/dialogs/res/CMakeLists.txt
Normal file
@ -0,0 +1,89 @@
|
||||
|
||||
add_executable(sdl3-res2bin
|
||||
convert_res_to_c.cpp
|
||||
)
|
||||
|
||||
set(SRCS
|
||||
sdl_resource_manager.cpp
|
||||
sdl_resource_manager.hpp
|
||||
)
|
||||
|
||||
set(RES_SVG_FILES
|
||||
${CMAKE_SOURCE_DIR}/resources/FreeRDP_Icon.svg
|
||||
${CMAKE_SOURCE_DIR}/resources/icon_info.svg
|
||||
${CMAKE_SOURCE_DIR}/resources/icon_warning.svg
|
||||
${CMAKE_SOURCE_DIR}/resources/icon_error.svg
|
||||
)
|
||||
|
||||
set(RES_FONT_FILES
|
||||
${CMAKE_SOURCE_DIR}/resources/font/OpenSans-VariableFont_wdth,wght.ttf
|
||||
)
|
||||
|
||||
macro(convert_to_bin FILE FILE_TYPE)
|
||||
get_filename_component(FILE_NAME ${FILE} NAME)
|
||||
string(REGEX REPLACE "[^a-zA-Z0-9]" "_" TARGET_NAME ${FILE_NAME})
|
||||
|
||||
set(FILE_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR}/bin)
|
||||
set(FILE_BYPRODUCTS ${FILE_BIN_DIR}/${TARGET_NAME}.hpp ${FILE_BIN_DIR}/${TARGET_NAME}.cpp)
|
||||
|
||||
list(APPEND FACTORY_SRCS
|
||||
${FILE_BYPRODUCTS}
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${FILE_BYPRODUCTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${FILE_BIN_DIR}
|
||||
COMMAND $<TARGET_FILE:sdl3-res2bin> ${FILE} ${FILE_TYPE} ${TARGET_NAME} ${FILE_BIN_DIR}
|
||||
COMMENT "create image resources"
|
||||
DEPENDS sdl3-res2bin
|
||||
DEPENDS ${FILE}
|
||||
)
|
||||
endmacro()
|
||||
|
||||
option(SDL_USE_COMPILED_RESOURCES "Compile in images/fonts" ON)
|
||||
|
||||
if (SDL_USE_COMPILED_RESOURCES)
|
||||
list(APPEND SRCS
|
||||
sdl_resource_file.cpp
|
||||
sdl_resource_file.hpp
|
||||
)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if (WITH_SDL_IMAGE_DIALOGS)
|
||||
foreach(FILE ${RES_SVG_FILES})
|
||||
convert_to_bin("${FILE}" "images")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
foreach(FILE ${RES_FONT_FILES})
|
||||
convert_to_bin("${FILE}" "fonts")
|
||||
endforeach()
|
||||
add_definitions(-DSDL_USE_COMPILED_RESOURCES)
|
||||
else()
|
||||
set(SDL_RESOURCE_ROOT ${CMAKE_INSTALL_FULL_DATAROOTDIR}/FreeRDP)
|
||||
if (WITH_BINARY_VERSIONING)
|
||||
string(APPEND SDL_RESOURCE_ROOT "${PROJECT_VERSION_MAJOR}")
|
||||
endif()
|
||||
|
||||
add_definitions(-DSDL_RESOURCE_ROOT="${SDL_RESOURCE_ROOT}")
|
||||
|
||||
if (WITH_SDL_IMAGE_DIALOGS)
|
||||
install(
|
||||
FILES ${RES_SVG_FILES}
|
||||
DESTINATION ${SDL_RESOURCE_ROOT}/images
|
||||
)
|
||||
endif()
|
||||
|
||||
install(
|
||||
FILES ${RES_FONT_FILES}
|
||||
DESTINATION ${SDL_RESOURCE_ROOT}/fonts
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(sdl3_client_res OBJECT
|
||||
${RES_FILES}
|
||||
${SRCS}
|
||||
${FACTORY_SRCS}
|
||||
)
|
||||
set_property(TARGET sdl3_client_res PROPERTY POSITION_INDEPENDENT_CODE ON)
|
190
client/SDL3/dialogs/res/convert_res_to_c.cpp
Normal file
190
client/SDL3/dialogs/res/convert_res_to_c.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
/**
|
||||
* 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 <string>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#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
|
||||
|
||||
static void usage(const char* prg)
|
||||
{
|
||||
std::cerr << prg << " <file> <type> <class name> <dst path>" << std::endl;
|
||||
}
|
||||
|
||||
static int write_comment_header(std::ostream& out, const fs::path& prg, const std::string& fname)
|
||||
{
|
||||
out << "/* AUTOGENERATED file, do not edit" << std::endl
|
||||
<< " *" << std::endl
|
||||
<< " * generated by '" << prg.filename() << "'" << std::endl
|
||||
<< " *" << std::endl
|
||||
<< " * contains the converted file '" << fname << "'" << std::endl
|
||||
<< " */" << std::endl
|
||||
<< std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_cpp_header(std::ostream& out, const fs::path& prg, const fs::path& file,
|
||||
const std::string& name, const std::string& type)
|
||||
{
|
||||
auto fname = file.filename().string();
|
||||
auto rc = write_comment_header(out, prg, fname);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
out << "#include <vector>" << std::endl
|
||||
<< "#include \"" << name << ".hpp\"" << std::endl
|
||||
<< std::endl
|
||||
<< "std::string " << name << "::name() {" << std::endl
|
||||
<< "return \"" << fname << "\";" << std::endl
|
||||
<< "}" << std::endl
|
||||
<< "std::string " << name << "::type() {" << std::endl
|
||||
<< "return \"" << type << "\";" << std::endl
|
||||
<< "}" << std::endl
|
||||
<< std::endl
|
||||
<< "const SDLResourceFile " << name << "::_initializer(type(), name(), init());"
|
||||
<< std::endl
|
||||
<< std::endl
|
||||
<< "std::vector<unsigned char> " << name << "::init() {" << std::endl
|
||||
<< "static const unsigned char data[] = {" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int readwrite(std::ofstream& out, std::ifstream& ifs)
|
||||
{
|
||||
size_t pos = 0;
|
||||
char c = 0;
|
||||
|
||||
std::ios backup(nullptr);
|
||||
backup.copyfmt(out);
|
||||
|
||||
while (ifs.read(&c, 1) && ifs.good())
|
||||
{
|
||||
unsigned val = c & 0xff;
|
||||
out << "0x" << std::hex << std::setfill('0') << std::setw(2) << val;
|
||||
if (ifs.peek() != EOF)
|
||||
out << ",";
|
||||
if ((pos++ % 16) == 15)
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
out.copyfmt(backup);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_cpp_trailer(std::ostream& out)
|
||||
{
|
||||
out << std::endl;
|
||||
out << "};" << std::endl;
|
||||
out << std::endl;
|
||||
out << "return std::vector<unsigned char>(data, data + sizeof(data));" << std::endl;
|
||||
out << "}" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_hpp_header(const fs::path& prg, const fs::path& file, const std::string& name,
|
||||
const std::string& fname)
|
||||
{
|
||||
std::ofstream out(file, std::ios::out);
|
||||
if (!out.is_open())
|
||||
{
|
||||
std::cerr << "Failed to open output file '" << file << "'" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
auto rc = write_comment_header(out, prg, fname);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
out << "#pragma once" << std::endl
|
||||
<< std::endl
|
||||
<< "#include <vector>" << std::endl
|
||||
<< "#include <string>" << std::endl
|
||||
<< "#include \"sdl_resource_file.hpp\"" << std::endl
|
||||
<< std::endl
|
||||
<< "class " << name << " {" << std::endl
|
||||
<< "public:" << std::endl
|
||||
<< name << "() = delete;" << std::endl
|
||||
<< std::endl
|
||||
<< "static std::string name();" << std::endl
|
||||
<< "static std::string type();" << std::endl
|
||||
<< std::endl
|
||||
<< "private:" << std::endl
|
||||
<< "static std::vector<unsigned char> init();" << std::endl
|
||||
<< "static const SDLResourceFile _initializer;" << std::endl
|
||||
<< std::endl
|
||||
<< "};" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
fs::path prg(argv[0]);
|
||||
if (argc != 5)
|
||||
{
|
||||
usage(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs::path file(argv[1]);
|
||||
std::string etype = argv[2];
|
||||
std::string cname = argv[3];
|
||||
fs::path dst(argv[4]);
|
||||
fs::path hdr(argv[4]);
|
||||
|
||||
dst /= cname + ".cpp";
|
||||
hdr /= cname + ".hpp";
|
||||
|
||||
std::ofstream out;
|
||||
out.open(dst);
|
||||
if (!out.is_open())
|
||||
{
|
||||
std::cerr << "Failed to open output file '" << dst << "'" << std::endl;
|
||||
return -2;
|
||||
}
|
||||
|
||||
std::ifstream ifs(file, std::ios::in | std::ios::binary);
|
||||
if (!ifs.is_open())
|
||||
{
|
||||
std::cerr << "Failed to open input file '" << file << "'" << std::endl;
|
||||
return -3;
|
||||
}
|
||||
|
||||
auto rc = write_cpp_header(out, prg, file, cname, etype);
|
||||
if (rc != 0)
|
||||
return -1;
|
||||
|
||||
rc = readwrite(out, ifs);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = write_cpp_trailer(out);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
return write_hpp_header(prg, hdr, cname, file.filename().string());
|
||||
}
|
25
client/SDL3/dialogs/res/sdl_resource_file.cpp
Normal file
25
client/SDL3/dialogs/res/sdl_resource_file.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 "sdl_resource_file.hpp"
|
||||
#include "sdl_resource_manager.hpp"
|
||||
|
||||
SDLResourceFile::SDLResourceFile(const std::string& type, const std::string& id,
|
||||
const std::vector<unsigned char>& data)
|
||||
{
|
||||
SDLResourceManager::insert(type, id, data);
|
||||
}
|
33
client/SDL3/dialogs/res/sdl_resource_file.hpp
Normal file
33
client/SDL3/dialogs/res/sdl_resource_file.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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 <vector>
|
||||
|
||||
class SDLResourceFile
|
||||
{
|
||||
public:
|
||||
SDLResourceFile(const std::string& type, const std::string& id,
|
||||
const std::vector<unsigned char>& data);
|
||||
virtual ~SDLResourceFile() = default;
|
||||
|
||||
private:
|
||||
SDLResourceFile(const SDLResourceFile& other) = delete;
|
||||
SDLResourceFile(const SDLResourceFile&& other) = delete;
|
||||
};
|
78
client/SDL3/dialogs/res/sdl_resource_manager.cpp
Normal file
78
client/SDL3/dialogs/res/sdl_resource_manager.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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 "sdl_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_RWops* SDLResourceManager::get(const std::string& type, const std::string& id)
|
||||
{
|
||||
std::string uuid = type + "/" + id;
|
||||
|
||||
#if defined(SDL_USE_COMPILED_RESOURCES)
|
||||
auto val = resources().find(uuid);
|
||||
if (val == resources().end())
|
||||
return nullptr;
|
||||
|
||||
const auto& v = val->second;
|
||||
return SDL_RWFromConstMem(v.data(), v.size());
|
||||
#else
|
||||
fs::path path(SDL_RESOURCE_ROOT);
|
||||
path /= type;
|
||||
path /= id;
|
||||
|
||||
if (!fs::exists(path))
|
||||
{
|
||||
std::cerr << "sdl-freerdp expects resource '" << uuid << "' at location "
|
||||
<< fs::absolute(path) << std::endl;
|
||||
std::cerr << "file not found, application will fail" << std::endl;
|
||||
}
|
||||
return SDL_RWFromFile(path.u8string().c_str(), "rb");
|
||||
#endif
|
||||
}
|
||||
|
||||
const std::string SDLResourceManager::typeFonts()
|
||||
{
|
||||
return "fonts";
|
||||
}
|
||||
|
||||
const std::string SDLResourceManager::typeImages()
|
||||
{
|
||||
return "images";
|
||||
}
|
||||
|
||||
void SDLResourceManager::insert(const std::string& type, const std::string& id,
|
||||
const std::vector<unsigned char>& data)
|
||||
{
|
||||
std::string uuid = type + "/" + id;
|
||||
resources().emplace(uuid, data);
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<unsigned char>>& SDLResourceManager::resources()
|
||||
{
|
||||
|
||||
static std::map<std::string, std::vector<unsigned char>> resources = {};
|
||||
return resources;
|
||||
}
|
46
client/SDL3/dialogs/res/sdl_resource_manager.hpp
Normal file
46
client/SDL3/dialogs/res/sdl_resource_manager.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
*
|
||||
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
|
||||
class SDLResourceManager
|
||||
{
|
||||
friend class SDLResourceFile;
|
||||
|
||||
public:
|
||||
static SDL_RWops* get(const std::string& type, const std::string& id);
|
||||
|
||||
static const std::string typeFonts();
|
||||
static const std::string typeImages();
|
||||
|
||||
protected:
|
||||
static void insert(const std::string& type, const std::string& id,
|
||||
const std::vector<unsigned char>& data);
|
||||
|
||||
private:
|
||||
SDLResourceManager() = delete;
|
||||
SDLResourceManager(const SDLResourceManager& other) = delete;
|
||||
SDLResourceManager(const SDLResourceManager&& other) = delete;
|
||||
~SDLResourceManager() = delete;
|
||||
|
||||
static std::map<std::string, std::vector<unsigned char>>& resources();
|
||||
};
|
71
client/SDL3/dialogs/sdl_button.cpp
Normal file
71
client/SDL3/dialogs/sdl_button.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client Channels
|
||||
*
|
||||
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2023 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "sdl_button.hpp"
|
||||
|
||||
static const SDL_Color buttonbackgroundcolor = { 0x69, 0x66, 0x63, 0xff };
|
||||
static const SDL_Color buttonhighlightcolor = { 0xcd, 0xca, 0x35, 0x60 };
|
||||
static const SDL_Color buttonmouseovercolor = { 0x66, 0xff, 0x66, 0x60 };
|
||||
static const SDL_Color buttonfontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
|
||||
SdlButton::SdlButton(SDL_Renderer* renderer, const std::string& label, int id, const SDL_Rect& rect)
|
||||
: SdlWidget(renderer, rect, false), _name(label), _id(id)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
update_text(renderer, _name, buttonfontcolor, buttonbackgroundcolor);
|
||||
}
|
||||
|
||||
SdlButton::SdlButton(SdlButton&& other) noexcept
|
||||
: SdlWidget(std::move(other)), _name(std::move(other._name)), _id(std::move(other._id))
|
||||
{
|
||||
}
|
||||
|
||||
bool SdlButton::highlight(SDL_Renderer* renderer)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
std::vector<SDL_Color> colors = { buttonbackgroundcolor, buttonhighlightcolor };
|
||||
if (!fill(renderer, colors))
|
||||
return false;
|
||||
return update_text(renderer, _name, buttonfontcolor);
|
||||
}
|
||||
|
||||
bool SdlButton::mouseover(SDL_Renderer* renderer)
|
||||
{
|
||||
std::vector<SDL_Color> colors = { buttonbackgroundcolor, buttonmouseovercolor };
|
||||
if (!fill(renderer, colors))
|
||||
return false;
|
||||
return update_text(renderer, _name, buttonfontcolor);
|
||||
}
|
||||
|
||||
bool SdlButton::update(SDL_Renderer* renderer)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
return update_text(renderer, _name, buttonfontcolor, buttonbackgroundcolor);
|
||||
}
|
||||
|
||||
int SdlButton::id() const
|
||||
{
|
||||
return _id;
|
||||
}
|
26
client/SDL3/dialogs/sdl_button.hpp
Normal file
26
client/SDL3/dialogs/sdl_button.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "sdl_widget.hpp"
|
||||
|
||||
class SdlButton : public SdlWidget
|
||||
{
|
||||
public:
|
||||
SdlButton(SDL_Renderer* renderer, const std::string& label, int id, const SDL_Rect& rect);
|
||||
SdlButton(SdlButton&& other) noexcept;
|
||||
~SdlButton() override = default;
|
||||
|
||||
bool highlight(SDL_Renderer* renderer);
|
||||
bool mouseover(SDL_Renderer* renderer);
|
||||
bool update(SDL_Renderer* renderer);
|
||||
|
||||
[[nodiscard]] int id() const;
|
||||
|
||||
private:
|
||||
SdlButton(const SdlButton& other) = delete;
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
int _id;
|
||||
};
|
105
client/SDL3/dialogs/sdl_buttons.cpp
Normal file
105
client/SDL3/dialogs/sdl_buttons.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
static const Uint32 hpadding = 10;
|
||||
|
||||
bool SdlButtonList::populate(SDL_Renderer* renderer, const std::vector<std::string>& labels,
|
||||
const std::vector<int>& ids, Sint32 total_width, Sint32 offsetY,
|
||||
Sint32 width, Sint32 height)
|
||||
{
|
||||
assert(renderer);
|
||||
assert(width >= 0);
|
||||
assert(height >= 0);
|
||||
assert(labels.size() == ids.size());
|
||||
|
||||
_list.clear();
|
||||
size_t button_width = ids.size() * (width + hpadding) + hpadding;
|
||||
size_t offsetX = total_width - std::min<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_Rect rect = { static_cast<int>(curOffsetX), offsetY, width, height };
|
||||
_list.emplace_back(renderer, labels[x], ids[x], rect);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SdlButton* SdlButtonList::get_selected(const SDL_MouseButtonEvent& button)
|
||||
{
|
||||
const Sint32 x = button.x;
|
||||
const Sint32 y = button.y;
|
||||
|
||||
return get_selected(x, y);
|
||||
}
|
||||
|
||||
SdlButton* SdlButtonList::get_selected(Sint32 x, Sint32 y)
|
||||
{
|
||||
for (auto& btn : _list)
|
||||
{
|
||||
auto r = btn.rect();
|
||||
if ((x >= r.x) && (x <= r.x + r.w) && (y >= r.y) && (y <= r.y + r.h))
|
||||
return &btn;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool SdlButtonList::set_highlight_next(bool reset)
|
||||
{
|
||||
if (reset)
|
||||
_highlighted = nullptr;
|
||||
else
|
||||
{
|
||||
auto next = _highlight_index++;
|
||||
_highlight_index %= _list.size();
|
||||
auto& element = _list[next];
|
||||
_highlighted = &element;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SdlButtonList::set_highlight(size_t index)
|
||||
{
|
||||
if (index >= _list.size())
|
||||
{
|
||||
_highlighted = nullptr;
|
||||
return false;
|
||||
}
|
||||
auto& element = _list[index];
|
||||
_highlighted = &element;
|
||||
_highlight_index = ++index % _list.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SdlButtonList::set_mouseover(Sint32 x, Sint32 y)
|
||||
{
|
||||
_mouseover = get_selected(x, y);
|
||||
return _mouseover != nullptr;
|
||||
}
|
||||
|
||||
void SdlButtonList::clear()
|
||||
{
|
||||
_list.clear();
|
||||
_mouseover = nullptr;
|
||||
_highlighted = nullptr;
|
||||
_highlight_index = 0;
|
||||
}
|
||||
|
||||
bool SdlButtonList::update(SDL_Renderer* renderer)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
for (auto& btn : _list)
|
||||
{
|
||||
if (!btn.update(renderer))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_highlighted)
|
||||
_highlighted->highlight(renderer);
|
||||
|
||||
if (_mouseover)
|
||||
_mouseover->mouseover(renderer);
|
||||
return true;
|
||||
}
|
37
client/SDL3/dialogs/sdl_buttons.hpp
Normal file
37
client/SDL3/dialogs/sdl_buttons.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#include "sdl_button.hpp"
|
||||
|
||||
class SdlButtonList
|
||||
{
|
||||
public:
|
||||
SdlButtonList() = default;
|
||||
virtual ~SdlButtonList() = default;
|
||||
|
||||
bool populate(SDL_Renderer* renderer, const std::vector<std::string>& labels,
|
||||
const std::vector<int>& ids, Sint32 total_width, Sint32 offsetY, Sint32 width,
|
||||
Sint32 heigth);
|
||||
|
||||
bool update(SDL_Renderer* renderer);
|
||||
SdlButton* get_selected(const SDL_MouseButtonEvent& button);
|
||||
SdlButton* get_selected(Sint32 x, Sint32 y);
|
||||
|
||||
bool set_highlight_next(bool reset = false);
|
||||
bool set_highlight(size_t index);
|
||||
bool set_mouseover(Sint32 x, Sint32 y);
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
SdlButtonList(const SdlButtonList& other) = delete;
|
||||
SdlButtonList(SdlButtonList&& other) = delete;
|
||||
|
||||
private:
|
||||
std::vector<SdlButton> _list;
|
||||
SdlButton* _highlighted = nullptr;
|
||||
size_t _highlight_index = 0;
|
||||
SdlButton* _mouseover = nullptr;
|
||||
};
|
541
client/SDL3/dialogs/sdl_connection_dialog.cpp
Normal file
541
client/SDL3/dialogs/sdl_connection_dialog.cpp
Normal file
@ -0,0 +1,541 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
|
||||
#include "sdl_connection_dialog.hpp"
|
||||
#include "../sdl_utils.hpp"
|
||||
#include "../sdl_freerdp.hpp"
|
||||
#include "res/sdl_resource_manager.hpp"
|
||||
|
||||
static const SDL_Color backgroundcolor = { 0x38, 0x36, 0x35, 0xff };
|
||||
static const SDL_Color textcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
static const SDL_Color infocolor = { 0x43, 0xe0, 0x0f, 0x60 };
|
||||
static const SDL_Color warncolor = { 0xcd, 0xca, 0x35, 0x60 };
|
||||
static const SDL_Color errorcolor = { 0xf7, 0x22, 0x30, 0x60 };
|
||||
|
||||
static const Uint32 vpadding = 5;
|
||||
static const Uint32 hpadding = 5;
|
||||
|
||||
SDLConnectionDialog::SDLConnectionDialog(rdpContext* context)
|
||||
: _context(context), _window(nullptr), _renderer(nullptr)
|
||||
{
|
||||
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
|
||||
hide();
|
||||
}
|
||||
|
||||
SDLConnectionDialog::~SDLConnectionDialog()
|
||||
{
|
||||
resetTimer();
|
||||
destroyWindow();
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::visible() const
|
||||
{
|
||||
if (!_window || !_renderer)
|
||||
return false;
|
||||
|
||||
auto flags = SDL_GetWindowFlags(_window);
|
||||
return (flags & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED)) == 0;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::setTitle(const char* fmt, ...)
|
||||
{
|
||||
std::lock_guard lock(_mux);
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
_title = print(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return show(MSG_NONE);
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::showInfo(const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
auto rc = show(MSG_INFO, fmt, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::showWarn(const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
auto rc = show(MSG_WARN, fmt, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::showError(const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
auto rc = show(MSG_ERROR, fmt, ap);
|
||||
va_end(ap);
|
||||
return setTimer();
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::show()
|
||||
{
|
||||
std::lock_guard lock(_mux);
|
||||
return show(_type_active);
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::hide()
|
||||
{
|
||||
std::lock_guard lock(_mux);
|
||||
return show(MSG_DISCARD);
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::running() const
|
||||
{
|
||||
std::lock_guard lock(_mux);
|
||||
return _running;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::update()
|
||||
{
|
||||
std::lock_guard lock(_mux);
|
||||
switch (_type)
|
||||
{
|
||||
case MSG_INFO:
|
||||
case MSG_WARN:
|
||||
case MSG_ERROR:
|
||||
_type_active = _type;
|
||||
createWindow();
|
||||
break;
|
||||
case MSG_DISCARD:
|
||||
resetTimer();
|
||||
destroyWindow();
|
||||
break;
|
||||
default:
|
||||
if (_window)
|
||||
{
|
||||
SDL_SetWindowTitle(_window, _title.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
_type = MSG_NONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::setModal()
|
||||
{
|
||||
if (_window)
|
||||
{
|
||||
auto sdl = get_context(_context);
|
||||
if (sdl->windows.empty())
|
||||
return true;
|
||||
|
||||
auto parent = sdl->windows.begin()->second.window();
|
||||
SDL_SetWindowModalFor(_window, parent);
|
||||
SDL_RaiseWindow(_window);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::clearWindow(SDL_Renderer* renderer)
|
||||
{
|
||||
assert(renderer);
|
||||
|
||||
const int drc = SDL_SetRenderDrawColor(renderer, backgroundcolor.r, backgroundcolor.g,
|
||||
backgroundcolor.b, backgroundcolor.a);
|
||||
if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
|
||||
return false;
|
||||
|
||||
const int rcls = SDL_RenderClear(renderer);
|
||||
return !widget_log_error(rcls, "SDL_RenderClear");
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::update(SDL_Renderer* renderer)
|
||||
{
|
||||
std::lock_guard lock(_mux);
|
||||
if (!renderer)
|
||||
return false;
|
||||
|
||||
if (!clearWindow(renderer))
|
||||
return false;
|
||||
|
||||
for (auto& btn : _list)
|
||||
{
|
||||
if (!btn.widget.update_text(renderer, _msg, btn.fgcolor, btn.bgcolor))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_buttons.update(renderer))
|
||||
return false;
|
||||
|
||||
SDL_RenderPresent(renderer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::wait(bool ignoreRdpContext)
|
||||
{
|
||||
while (running())
|
||||
{
|
||||
if (!ignoreRdpContext)
|
||||
{
|
||||
if (freerdp_shall_disconnect_context(_context))
|
||||
return false;
|
||||
}
|
||||
std::this_thread::yield();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::handle(const SDL_Event& event)
|
||||
{
|
||||
Uint32 windowID = 0;
|
||||
if (_window)
|
||||
{
|
||||
windowID = SDL_GetWindowID(_window);
|
||||
}
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_USEREVENT_RETRY_DIALOG:
|
||||
return update();
|
||||
case SDL_QUIT:
|
||||
resetTimer();
|
||||
destroyWindow();
|
||||
return false;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_KeyboardEvent&>(event);
|
||||
update(_renderer);
|
||||
switch (event.key.keysym.sym)
|
||||
{
|
||||
case SDLK_RETURN:
|
||||
case SDLK_RETURN2:
|
||||
case SDLK_ESCAPE:
|
||||
case SDLK_KP_ENTER:
|
||||
if (event.type == SDL_KEYUP)
|
||||
{
|
||||
freerdp_abort_event(_context);
|
||||
sdl_push_quit();
|
||||
}
|
||||
break;
|
||||
case SDLK_TAB:
|
||||
_buttons.set_highlight_next();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_MOUSEMOTION:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_MouseMotionEvent&>(event);
|
||||
|
||||
_buttons.set_mouseover(event.button.x, event.button.y);
|
||||
update(_renderer);
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_MouseButtonEvent&>(event);
|
||||
update(_renderer);
|
||||
|
||||
auto button = _buttons.get_selected(event.button);
|
||||
if (button)
|
||||
{
|
||||
if (event.type == SDL_MOUSEBUTTONUP)
|
||||
{
|
||||
freerdp_abort_event(_context);
|
||||
sdl_push_quit();
|
||||
}
|
||||
}
|
||||
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_MouseWheelEvent&>(event);
|
||||
update(_renderer);
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
return false;
|
||||
case SDL_FINGERUP:
|
||||
case SDL_FINGERDOWN:
|
||||
if (visible())
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_TouchFingerEvent&>(event);
|
||||
update(_renderer);
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||
return windowID == ev.windowID;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
auto& ev = reinterpret_cast<const SDL_WindowEvent&>(event);
|
||||
switch (ev.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
if (windowID == ev.windowID)
|
||||
{
|
||||
freerdp_abort_event(_context);
|
||||
sdl_push_quit();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
update(_renderer);
|
||||
setModal();
|
||||
break;
|
||||
}
|
||||
|
||||
return windowID == ev.windowID;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::createWindow()
|
||||
{
|
||||
destroyWindow();
|
||||
|
||||
const size_t widget_height = 50;
|
||||
const size_t widget_width = 600;
|
||||
const size_t total_height = 300;
|
||||
|
||||
_window = SDL_CreateWindow(
|
||||
_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, widget_width,
|
||||
total_height, SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS);
|
||||
if (_window == nullptr)
|
||||
{
|
||||
widget_log_error(-1, "SDL_CreateWindow");
|
||||
return false;
|
||||
}
|
||||
setModal();
|
||||
|
||||
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
|
||||
if (_renderer == nullptr)
|
||||
{
|
||||
widget_log_error(-1, "SDL_CreateRenderer");
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_Color res_bgcolor;
|
||||
switch (_type_active)
|
||||
{
|
||||
case MSG_INFO:
|
||||
res_bgcolor = infocolor;
|
||||
break;
|
||||
case MSG_WARN:
|
||||
res_bgcolor = warncolor;
|
||||
break;
|
||||
case MSG_ERROR:
|
||||
res_bgcolor = errorcolor;
|
||||
break;
|
||||
case MSG_DISCARD:
|
||||
default:
|
||||
res_bgcolor = backgroundcolor;
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(WITH_SDL_IMAGE_DIALOGS)
|
||||
std::string res_name;
|
||||
switch (_type_active)
|
||||
{
|
||||
case MSG_INFO:
|
||||
res_name = "icon_info.svg";
|
||||
break;
|
||||
case MSG_WARN:
|
||||
res_name = "icon_warning.svg";
|
||||
break;
|
||||
case MSG_ERROR:
|
||||
res_name = "icon_error.svg";
|
||||
break;
|
||||
case MSG_DISCARD:
|
||||
default:
|
||||
res_name = "";
|
||||
break;
|
||||
}
|
||||
|
||||
int height = (total_height - 3ul * vpadding) / 2ul;
|
||||
SDL_Rect iconRect{ hpadding, vpadding, widget_width / 4ul - 2ul * hpadding, height };
|
||||
widget_cfg_t icon{ textcolor,
|
||||
res_bgcolor,
|
||||
{ _renderer, iconRect,
|
||||
SDLResourceManager::get(SDLResourceManager::typeImages(), res_name) } };
|
||||
_list.emplace_back(std::move(icon));
|
||||
|
||||
iconRect.y += height;
|
||||
|
||||
widget_cfg_t logo{ textcolor,
|
||||
backgroundcolor,
|
||||
{ _renderer, iconRect,
|
||||
SDLResourceManager::get(SDLResourceManager::typeImages(),
|
||||
"FreeRDP_Icon.svg") } };
|
||||
_list.emplace_back(std::move(logo));
|
||||
|
||||
SDL_Rect rect = { widget_width / 4ul, vpadding, widget_width * 3ul / 4ul,
|
||||
total_height - 3ul * vpadding - widget_height };
|
||||
#else
|
||||
SDL_Rect rect = { hpadding, vpadding, widget_width - 2ul * hpadding,
|
||||
total_height - 2ul * vpadding };
|
||||
#endif
|
||||
|
||||
widget_cfg_t w{ textcolor, backgroundcolor, { _renderer, rect, false } };
|
||||
w.widget.set_wrap(true, widget_width);
|
||||
_list.emplace_back(std::move(w));
|
||||
rect.y += widget_height + vpadding;
|
||||
|
||||
const std::vector<int> buttonids = { 1 };
|
||||
const std::vector<std::string> buttonlabels = { "cancel" };
|
||||
_buttons.populate(_renderer, buttonlabels, buttonids, widget_width,
|
||||
total_height - widget_height - vpadding,
|
||||
static_cast<Sint32>(widget_width / 2), static_cast<Sint32>(widget_height));
|
||||
_buttons.set_highlight(0);
|
||||
|
||||
SDL_ShowWindow(_window);
|
||||
SDL_RaiseWindow(_window);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLConnectionDialog::destroyWindow()
|
||||
{
|
||||
_buttons.clear();
|
||||
_list.clear();
|
||||
SDL_DestroyRenderer(_renderer);
|
||||
SDL_DestroyWindow(_window);
|
||||
_renderer = nullptr;
|
||||
_window = nullptr;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::show(MsgType type, const char* fmt, va_list ap)
|
||||
{
|
||||
std::lock_guard lock(_mux);
|
||||
_msg = print(fmt, ap);
|
||||
return show(type);
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::show(MsgType type)
|
||||
{
|
||||
_type = type;
|
||||
return sdl_push_user_event(SDL_USEREVENT_RETRY_DIALOG);
|
||||
}
|
||||
|
||||
std::string SDLConnectionDialog::print(const char* fmt, va_list ap)
|
||||
{
|
||||
int size = -1;
|
||||
std::string res;
|
||||
|
||||
do
|
||||
{
|
||||
res.resize(128);
|
||||
if (size > 0)
|
||||
res.resize(size);
|
||||
|
||||
va_list copy;
|
||||
va_copy(copy, ap);
|
||||
size = vsnprintf(res.data(), res.size(), fmt, copy);
|
||||
va_end(copy);
|
||||
|
||||
} while ((size > 0) && (size > res.size()));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool SDLConnectionDialog::setTimer(Uint32 timeoutMS)
|
||||
{
|
||||
std::lock_guard lock(_mux);
|
||||
resetTimer();
|
||||
|
||||
_timer = SDL_AddTimer(timeoutMS, &SDLConnectionDialog::timeout, this);
|
||||
_running = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLConnectionDialog::resetTimer()
|
||||
{
|
||||
if (_running)
|
||||
SDL_RemoveTimer(_timer);
|
||||
_running = false;
|
||||
}
|
||||
|
||||
Uint32 SDLConnectionDialog::timeout(Uint32 intervalMS, void* pvthis)
|
||||
{
|
||||
auto ths = static_cast<SDLConnectionDialog*>(pvthis);
|
||||
ths->hide();
|
||||
ths->_running = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDLConnectionDialogHider::SDLConnectionDialogHider(freerdp* instance)
|
||||
: SDLConnectionDialogHider(get(instance))
|
||||
{
|
||||
}
|
||||
|
||||
SDLConnectionDialogHider::SDLConnectionDialogHider(rdpContext* context)
|
||||
: SDLConnectionDialogHider(get(context))
|
||||
{
|
||||
}
|
||||
|
||||
SDLConnectionDialogHider::SDLConnectionDialogHider(SDLConnectionDialog* dialog) : _dialog(dialog)
|
||||
{
|
||||
if (_dialog)
|
||||
{
|
||||
_visible = _dialog->visible();
|
||||
if (_visible)
|
||||
{
|
||||
_dialog->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDLConnectionDialogHider::~SDLConnectionDialogHider()
|
||||
{
|
||||
if (_dialog && _visible)
|
||||
{
|
||||
_dialog->show();
|
||||
}
|
||||
}
|
||||
|
||||
SDLConnectionDialog* SDLConnectionDialogHider::get(freerdp* instance)
|
||||
{
|
||||
if (!instance)
|
||||
return nullptr;
|
||||
return get(instance->context);
|
||||
}
|
||||
|
||||
SDLConnectionDialog* SDLConnectionDialogHider::get(rdpContext* context)
|
||||
{
|
||||
auto sdl = get_context(context);
|
||||
if (!sdl)
|
||||
return nullptr;
|
||||
return sdl->connection_dialog.get();
|
||||
}
|
129
client/SDL3/dialogs/sdl_connection_dialog.hpp
Normal file
129
client/SDL3/dialogs/sdl_connection_dialog.hpp
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#include "sdl_widget.hpp"
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
class SDLConnectionDialog
|
||||
{
|
||||
public:
|
||||
explicit SDLConnectionDialog(rdpContext* context);
|
||||
SDLConnectionDialog(const SDLConnectionDialog& other) = delete;
|
||||
SDLConnectionDialog(const SDLConnectionDialog&& other) = delete;
|
||||
virtual ~SDLConnectionDialog();
|
||||
|
||||
bool visible() const;
|
||||
|
||||
bool setTitle(const char* fmt, ...);
|
||||
bool showInfo(const char* fmt, ...);
|
||||
bool showWarn(const char* fmt, ...);
|
||||
bool showError(const char* fmt, ...);
|
||||
|
||||
bool show();
|
||||
bool hide();
|
||||
|
||||
bool running() const;
|
||||
bool wait(bool ignoreRdpContextQuit = false);
|
||||
|
||||
bool handle(const SDL_Event& event);
|
||||
|
||||
private:
|
||||
enum MsgType
|
||||
{
|
||||
MSG_NONE,
|
||||
MSG_INFO,
|
||||
MSG_WARN,
|
||||
MSG_ERROR,
|
||||
MSG_DISCARD
|
||||
};
|
||||
|
||||
private:
|
||||
bool createWindow();
|
||||
void destroyWindow();
|
||||
|
||||
bool update();
|
||||
|
||||
bool setModal();
|
||||
|
||||
bool clearWindow(SDL_Renderer* renderer);
|
||||
|
||||
bool update(SDL_Renderer* renderer);
|
||||
|
||||
bool show(MsgType type, const char* fmt, va_list ap);
|
||||
bool show(MsgType type);
|
||||
|
||||
std::string print(const char* fmt, va_list ap);
|
||||
bool setTimer(Uint32 timeoutMS = 15000);
|
||||
void resetTimer();
|
||||
|
||||
private:
|
||||
static Uint32 timeout(Uint32 intervalMS, void* _this);
|
||||
|
||||
private:
|
||||
struct widget_cfg_t
|
||||
{
|
||||
SDL_Color fgcolor = {};
|
||||
SDL_Color bgcolor = {};
|
||||
SdlWidget widget;
|
||||
};
|
||||
|
||||
private:
|
||||
rdpContext* _context = nullptr;
|
||||
SDL_Window* _window = nullptr;
|
||||
SDL_Renderer* _renderer = nullptr;
|
||||
mutable std::mutex _mux;
|
||||
std::string _title;
|
||||
std::string _msg;
|
||||
MsgType _type = MSG_NONE;
|
||||
MsgType _type_active = MSG_NONE;
|
||||
SDL_TimerID _timer = -1;
|
||||
bool _running = false;
|
||||
std::vector<widget_cfg_t> _list;
|
||||
SdlButtonList _buttons;
|
||||
};
|
||||
|
||||
class SDLConnectionDialogHider
|
||||
{
|
||||
public:
|
||||
explicit SDLConnectionDialogHider(freerdp* instance);
|
||||
explicit SDLConnectionDialogHider(rdpContext* context);
|
||||
|
||||
explicit SDLConnectionDialogHider(SDLConnectionDialog* dialog);
|
||||
|
||||
~SDLConnectionDialogHider();
|
||||
|
||||
private:
|
||||
SDLConnectionDialog* get(freerdp* instance);
|
||||
SDLConnectionDialog* get(rdpContext* context);
|
||||
|
||||
private:
|
||||
SDLConnectionDialog* _dialog = nullptr;
|
||||
bool _visible = false;
|
||||
};
|
621
client/SDL3/dialogs/sdl_dialogs.cpp
Normal file
621
client/SDL3/dialogs/sdl_dialogs.cpp
Normal file
@ -0,0 +1,621 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#include <freerdp/utils/smartcardlogon.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "../sdl_freerdp.hpp"
|
||||
#include "sdl_dialogs.hpp"
|
||||
#include "sdl_input.hpp"
|
||||
#include "sdl_input_widgets.hpp"
|
||||
#include "sdl_select.hpp"
|
||||
#include "sdl_selectlist.hpp"
|
||||
|
||||
#define TAG CLIENT_TAG("SDL.dialogs")
|
||||
|
||||
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 = { 0 };
|
||||
|
||||
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 = { 0 };
|
||||
|
||||
if (!sdl_push_user_event(SDL_USEREVENT_SHOW_DIALOG, title, message, flags))
|
||||
return 0;
|
||||
|
||||
if (!sdl_wait_for_result(context, SDL_USEREVENT_SHOW_RESULT, &event))
|
||||
return 0;
|
||||
|
||||
return event.user.code;
|
||||
}
|
||||
|
||||
BOOL sdl_authenticate_ex(freerdp* instance, char** username, char** password, char** domain,
|
||||
rdp_auth_reason reason)
|
||||
{
|
||||
SDL_Event event = { 0 };
|
||||
BOOL res = FALSE;
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
|
||||
const char* target = freerdp_settings_get_server_name(instance->context->settings);
|
||||
switch (reason)
|
||||
{
|
||||
case AUTH_NLA:
|
||||
break;
|
||||
|
||||
case AUTH_TLS:
|
||||
case AUTH_RDP:
|
||||
case AUTH_SMARTCARD_PIN: /* in this case password is pin code */
|
||||
if ((*username) && (*password))
|
||||
return TRUE;
|
||||
break;
|
||||
case GW_AUTH_HTTP:
|
||||
case GW_AUTH_RDG:
|
||||
case GW_AUTH_RPC:
|
||||
target =
|
||||
freerdp_settings_get_string(instance->context->settings, FreeRDP_GatewayHostname);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
char* title = nullptr;
|
||||
size_t titlesize = 0;
|
||||
winpr_asprintf(&title, &titlesize, "Credentials required for %s", target);
|
||||
|
||||
char* u = nullptr;
|
||||
char* d = nullptr;
|
||||
char* p = nullptr;
|
||||
|
||||
assert(username);
|
||||
assert(domain);
|
||||
assert(password);
|
||||
|
||||
u = *username;
|
||||
d = *domain;
|
||||
p = *password;
|
||||
|
||||
if (!sdl_push_user_event(SDL_USEREVENT_AUTH_DIALOG, title, u, d, p, reason))
|
||||
goto fail;
|
||||
|
||||
if (!sdl_wait_for_result(instance->context, SDL_USEREVENT_AUTH_RESULT, &event))
|
||||
goto fail;
|
||||
else
|
||||
{
|
||||
auto arg = reinterpret_cast<SDL_UserAuthArg*>(event.padding);
|
||||
|
||||
res = arg->result > 0 ? TRUE : FALSE;
|
||||
|
||||
free(*username);
|
||||
free(*domain);
|
||||
free(*password);
|
||||
*username = arg->user;
|
||||
*domain = arg->domain;
|
||||
*password = arg->password;
|
||||
}
|
||||
|
||||
fail:
|
||||
free(title);
|
||||
return res;
|
||||
}
|
||||
|
||||
BOOL sdl_choose_smartcard(freerdp* instance, SmartcardCertInfo** cert_list, DWORD count,
|
||||
DWORD* choice, BOOL gateway)
|
||||
{
|
||||
BOOL res = FALSE;
|
||||
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(cert_list);
|
||||
WINPR_ASSERT(choice);
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
std::vector<std::string> strlist;
|
||||
std::vector<const char*> list;
|
||||
for (DWORD i = 0; i < count; i++)
|
||||
{
|
||||
const SmartcardCertInfo* cert = cert_list[i];
|
||||
char* reader = ConvertWCharToUtf8Alloc(cert->reader, nullptr);
|
||||
char* container_name = ConvertWCharToUtf8Alloc(cert->containerName, nullptr);
|
||||
|
||||
char* msg = nullptr;
|
||||
size_t len = 0;
|
||||
|
||||
winpr_asprintf(&msg, &len,
|
||||
"%s\n\tReader: %s\n\tUser: %s@%s\n\tSubject: %s\n\tIssuer: %s\n\tUPN: %s",
|
||||
container_name, reader, cert->userHint, cert->domainHint, cert->subject,
|
||||
cert->issuer, cert->upn);
|
||||
|
||||
strlist.emplace_back(msg);
|
||||
free(msg);
|
||||
free(reader);
|
||||
free(container_name);
|
||||
|
||||
auto& m = strlist.back();
|
||||
list.push_back(m.c_str());
|
||||
}
|
||||
|
||||
SDL_Event event = { 0 };
|
||||
const char* title = "Select a logon smartcard certificate";
|
||||
if (gateway)
|
||||
title = "Select a gateway logon smartcard certificate";
|
||||
if (!sdl_push_user_event(SDL_USEREVENT_SCARD_DIALOG, title, list.data(), count))
|
||||
goto fail;
|
||||
|
||||
if (!sdl_wait_for_result(instance->context, SDL_USEREVENT_SCARD_RESULT, &event))
|
||||
goto fail;
|
||||
|
||||
res = (event.user.code >= 0) ? TRUE : FALSE;
|
||||
*choice = static_cast<DWORD>(event.user.code);
|
||||
|
||||
fail:
|
||||
return res;
|
||||
}
|
||||
|
||||
SSIZE_T sdl_retry_dialog(freerdp* instance, const char* what, size_t current, void* userarg)
|
||||
{
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(instance->context);
|
||||
WINPR_ASSERT(what);
|
||||
|
||||
auto sdl = get_context(instance->context);
|
||||
std::lock_guard<CriticalSection> lock(sdl->critical);
|
||||
WINPR_ASSERT(sdl->connection_dialog);
|
||||
|
||||
sdl->connection_dialog->setTitle("Retry connection to %s",
|
||||
freerdp_settings_get_server_name(instance->context->settings));
|
||||
|
||||
if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
|
||||
{
|
||||
sdl->connection_dialog->showError("Unknown module %s, aborting", what);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (current == 0)
|
||||
{
|
||||
if (strcmp(what, "arm-transport") == 0)
|
||||
sdl->connection_dialog->showWarn("[%s] Starting your VM. It may take up to 5 minutes",
|
||||
what);
|
||||
}
|
||||
|
||||
auto settings = instance->context->settings;
|
||||
const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
|
||||
|
||||
if (!enabled)
|
||||
{
|
||||
sdl->connection_dialog->showError(
|
||||
"Automatic reconnection disabled, terminating. Try to connect again later");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
|
||||
const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
|
||||
if (current >= max)
|
||||
{
|
||||
sdl->connection_dialog->showError(
|
||||
"[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
|
||||
"tech support for help if this keeps happening.",
|
||||
what);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sdl->connection_dialog->showInfo("[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz
|
||||
"ms before next attempt",
|
||||
what, current, max, delay);
|
||||
return delay;
|
||||
}
|
||||
|
||||
BOOL sdl_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
|
||||
BOOL isConsentMandatory, size_t length, const WCHAR* wmessage)
|
||||
{
|
||||
if (!isDisplayMandatory)
|
||||
return TRUE;
|
||||
|
||||
char* title = nullptr;
|
||||
size_t len = 0;
|
||||
winpr_asprintf(&title, &len, "[gateway]");
|
||||
|
||||
Sint32 flags = 0;
|
||||
if (isConsentMandatory)
|
||||
flags = SHOW_DIALOG_ACCEPT_REJECT;
|
||||
else if (isDisplayMandatory)
|
||||
flags = SHOW_DIALOG_TIMED_ACCEPT;
|
||||
char* message = ConvertWCharNToUtf8Alloc(wmessage, length, nullptr);
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
const int rc = sdl_show_dialog(instance->context, title, message, flags);
|
||||
free(title);
|
||||
free(message);
|
||||
return rc > 0 ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
int sdl_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
|
||||
{
|
||||
int rc = -1;
|
||||
const char* str_data = freerdp_get_logon_error_info_data(data);
|
||||
const char* str_type = freerdp_get_logon_error_info_type(type);
|
||||
|
||||
if (!instance || !instance->context)
|
||||
return -1;
|
||||
|
||||
/* ignore LOGON_MSG_SESSION_CONTINUE message */
|
||||
if (type == LOGON_MSG_SESSION_CONTINUE)
|
||||
return 0;
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
|
||||
char* title = nullptr;
|
||||
size_t tlen = 0;
|
||||
winpr_asprintf(&title, &tlen, "[%s] info",
|
||||
freerdp_settings_get_server_name(instance->context->settings));
|
||||
|
||||
char* message = nullptr;
|
||||
size_t mlen = 0;
|
||||
winpr_asprintf(&message, &mlen, "Logon Error Info %s [%s]", str_data, str_type);
|
||||
|
||||
rc = sdl_show_dialog(instance->context, title, message, SHOW_DIALOG_ACCEPT_REJECT);
|
||||
free(title);
|
||||
free(message);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static DWORD sdl_show_ceritifcate_dialog(rdpContext* context, const char* title,
|
||||
const char* message)
|
||||
{
|
||||
SDLConnectionDialogHider hider(context);
|
||||
if (!sdl_push_user_event(SDL_USEREVENT_CERT_DIALOG, title, message))
|
||||
return 0;
|
||||
|
||||
SDL_Event event = { 0 };
|
||||
if (!sdl_wait_for_result(context, SDL_USEREVENT_CERT_RESULT, &event))
|
||||
return 0;
|
||||
return static_cast<DWORD>(event.user.code);
|
||||
}
|
||||
|
||||
DWORD sdl_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
|
||||
const char* common_name, const char* subject,
|
||||
const char* issuer, const char* new_fingerprint,
|
||||
const char* old_subject, const char* old_issuer,
|
||||
const char* old_fingerprint, DWORD flags)
|
||||
{
|
||||
const char* type = type_str_for_flags(flags);
|
||||
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(instance->context);
|
||||
WINPR_ASSERT(instance->context->settings);
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
/* Newer versions of FreeRDP allow exposing the whole PEM by setting
|
||||
* FreeRDP_CertificateCallbackPreferPEM to TRUE
|
||||
*/
|
||||
char* new_fp_str = nullptr;
|
||||
size_t len = 0;
|
||||
if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
|
||||
{
|
||||
winpr_asprintf(&new_fp_str, &len,
|
||||
"----------- Certificate --------------\n"
|
||||
"%s\n"
|
||||
"--------------------------------------\n",
|
||||
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)
|
||||
{
|
||||
winpr_asprintf(&old_fp_str, &olen,
|
||||
"----------- Certificate --------------\n"
|
||||
"%s\n"
|
||||
"--------------------------------------\n",
|
||||
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)
|
||||
{
|
||||
winpr_asprintf(&fp_str, &len,
|
||||
"----------- Certificate --------------\n"
|
||||
"%s\n"
|
||||
"--------------------------------------\n",
|
||||
fingerprint);
|
||||
}
|
||||
else
|
||||
winpr_asprintf(&fp_str, &len, "Thumbprint: %s\n", fingerprint);
|
||||
|
||||
char* title = nullptr;
|
||||
size_t tlen = 0;
|
||||
winpr_asprintf(&title, &tlen, "New certificate for %s:%" PRIu16 " (%s)", host, port, type);
|
||||
|
||||
char* message = nullptr;
|
||||
size_t mlen = 0;
|
||||
winpr_asprintf(
|
||||
&message, &mlen,
|
||||
"Common Name: %s\n"
|
||||
"Subject: %s\n"
|
||||
"Issuer: %s\n"
|
||||
"%s\n"
|
||||
"The above X.509 certificate could not be verified, possibly because you do not have\n"
|
||||
"the CA certificate in your certificate store, or the certificate has expired.\n"
|
||||
"Please look at the OpenSSL documentation on how to add a private CA to the store.\n",
|
||||
common_name, subject, issuer, fp_str);
|
||||
|
||||
SDLConnectionDialogHider hider(instance);
|
||||
const DWORD rc = sdl_show_ceritifcate_dialog(instance->context, title, message);
|
||||
free(fp_str);
|
||||
free(title);
|
||||
free(message);
|
||||
return rc;
|
||||
}
|
||||
|
||||
BOOL sdl_cert_dialog_show(const char* title, const char* message)
|
||||
{
|
||||
int buttonid = -1;
|
||||
enum
|
||||
{
|
||||
BUTTONID_CERT_ACCEPT_PERMANENT = 23,
|
||||
BUTTONID_CERT_ACCEPT_TEMPORARY = 24,
|
||||
BUTTONID_CERT_DENY = 25
|
||||
};
|
||||
const SDL_MessageBoxButtonData buttons[] = {
|
||||
{ 0, BUTTONID_CERT_ACCEPT_PERMANENT, "permanent" },
|
||||
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_CERT_ACCEPT_TEMPORARY, "temporary" },
|
||||
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_CERT_DENY, "cancel" }
|
||||
};
|
||||
|
||||
const SDL_MessageBoxData data = { SDL_MESSAGEBOX_WARNING, nullptr, title, message,
|
||||
ARRAYSIZE(buttons), buttons, nullptr };
|
||||
const int rc = SDL_ShowMessageBox(&data, &buttonid);
|
||||
|
||||
Sint32 value = -1;
|
||||
if (rc < 0)
|
||||
value = 0;
|
||||
else
|
||||
{
|
||||
switch (buttonid)
|
||||
{
|
||||
case BUTTONID_CERT_ACCEPT_PERMANENT:
|
||||
value = 1;
|
||||
break;
|
||||
case BUTTONID_CERT_ACCEPT_TEMPORARY:
|
||||
value = 2;
|
||||
break;
|
||||
default:
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sdl_push_user_event(SDL_USEREVENT_CERT_RESULT, value);
|
||||
}
|
||||
|
||||
BOOL sdl_message_dialog_show(const char* title, const char* message, Sint32 flags)
|
||||
{
|
||||
int buttonid = -1;
|
||||
enum
|
||||
{
|
||||
BUTTONID_SHOW_ACCEPT = 24,
|
||||
BUTTONID_SHOW_DENY = 25
|
||||
};
|
||||
const SDL_MessageBoxButtonData buttons[] = {
|
||||
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, BUTTONID_SHOW_ACCEPT, "accept" },
|
||||
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, BUTTONID_SHOW_DENY, "cancel" }
|
||||
};
|
||||
|
||||
const int button_cnt = (flags & SHOW_DIALOG_ACCEPT_REJECT) ? 2 : 1;
|
||||
const SDL_MessageBoxData data = {
|
||||
SDL_MESSAGEBOX_WARNING, nullptr, title, message, button_cnt, buttons, nullptr
|
||||
};
|
||||
const int rc = SDL_ShowMessageBox(&data, &buttonid);
|
||||
|
||||
Sint32 value = -1;
|
||||
if (rc < 0)
|
||||
value = 0;
|
||||
else
|
||||
{
|
||||
switch (buttonid)
|
||||
{
|
||||
case BUTTONID_SHOW_ACCEPT:
|
||||
value = 1;
|
||||
break;
|
||||
default:
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sdl_push_user_event(SDL_USEREVENT_SHOW_RESULT, value);
|
||||
}
|
||||
|
||||
BOOL sdl_auth_dialog_show(const SDL_UserAuthArg* args)
|
||||
{
|
||||
const std::vector<std::string> auth = { "Username: ", "Domain: ",
|
||||
"Password: " };
|
||||
const std::vector<std::string> authPin = { "Device: ", "PIN: " };
|
||||
const std::vector<std::string> gw = { "GatewayUsername: ", "GatewayDomain: ",
|
||||
"GatewayPassword: " };
|
||||
std::vector<std::string> prompt;
|
||||
Sint32 rc = -1;
|
||||
|
||||
switch (args->result)
|
||||
{
|
||||
case AUTH_SMARTCARD_PIN:
|
||||
prompt = authPin;
|
||||
break;
|
||||
case AUTH_TLS:
|
||||
case AUTH_RDP:
|
||||
case AUTH_NLA:
|
||||
prompt = auth;
|
||||
break;
|
||||
case GW_AUTH_HTTP:
|
||||
case GW_AUTH_RDG:
|
||||
case GW_AUTH_RPC:
|
||||
prompt = gw;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<std::string> result;
|
||||
|
||||
if (!prompt.empty())
|
||||
{
|
||||
std::vector<std::string> initial{ args->user ? args->user : "Smartcard", "" };
|
||||
std::vector<Uint32> flags = { SdlInputWidget::SDL_INPUT_READONLY,
|
||||
SdlInputWidget::SDL_INPUT_MASK };
|
||||
if (args->result != AUTH_SMARTCARD_PIN)
|
||||
{
|
||||
initial = { args->user ? args->user : "", args->domain ? args->domain : "",
|
||||
args->password ? args->password : "" };
|
||||
flags = { 0, 0, SdlInputWidget::SDL_INPUT_MASK };
|
||||
}
|
||||
SdlInputWidgetList ilist(args->title, prompt, initial, flags);
|
||||
rc = ilist.run(result);
|
||||
}
|
||||
|
||||
if ((result.size() < prompt.size()))
|
||||
rc = -1;
|
||||
|
||||
char* user = nullptr;
|
||||
char* domain = nullptr;
|
||||
char* pwd = nullptr;
|
||||
if (rc > 0)
|
||||
{
|
||||
user = _strdup(result[0].c_str());
|
||||
if (args->result == AUTH_SMARTCARD_PIN)
|
||||
pwd = _strdup(result[1].c_str());
|
||||
else
|
||||
{
|
||||
domain = _strdup(result[1].c_str());
|
||||
pwd = _strdup(result[2].c_str());
|
||||
}
|
||||
}
|
||||
return sdl_push_user_event(SDL_USEREVENT_AUTH_RESULT, user, domain, pwd, rc);
|
||||
}
|
||||
|
||||
BOOL sdl_scard_dialog_show(const char* title, Sint32 count, const char** list)
|
||||
{
|
||||
std::vector<std::string> vlist;
|
||||
vlist.reserve(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_USEREVENT_SCARD_RESULT, value);
|
||||
}
|
53
client/SDL3/dialogs/sdl_dialogs.hpp
Normal file
53
client/SDL3/dialogs/sdl_dialogs.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <winpr/wtypes.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#include "../sdl_types.hpp"
|
||||
#include "../sdl_utils.hpp"
|
||||
|
||||
BOOL sdl_authenticate_ex(freerdp* instance, char** username, char** password, char** domain,
|
||||
rdp_auth_reason reason);
|
||||
BOOL sdl_choose_smartcard(freerdp* instance, SmartcardCertInfo** cert_list, DWORD count,
|
||||
DWORD* choice, BOOL gateway);
|
||||
|
||||
SSIZE_T sdl_retry_dialog(freerdp* instance, const char* what, size_t current, void* userarg);
|
||||
|
||||
DWORD sdl_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
|
||||
const char* common_name, const char* subject, const char* issuer,
|
||||
const char* fingerprint, DWORD flags);
|
||||
|
||||
DWORD sdl_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
|
||||
const char* common_name, const char* subject,
|
||||
const char* issuer, const char* new_fingerprint,
|
||||
const char* old_subject, const char* old_issuer,
|
||||
const char* old_fingerprint, DWORD flags);
|
||||
|
||||
int sdl_logon_error_info(freerdp* instance, UINT32 data, UINT32 type);
|
||||
|
||||
BOOL sdl_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
|
||||
BOOL isConsentMandatory, size_t length, const WCHAR* message);
|
||||
|
||||
BOOL sdl_message_dialog_show(const char* title, const char* message, Sint32 flags);
|
||||
BOOL sdl_cert_dialog_show(const char* title, const char* message);
|
||||
BOOL sdl_scard_dialog_show(const char* title, Sint32 count, const char** list);
|
||||
BOOL sdl_auth_dialog_show(const SDL_UserAuthArg* args);
|
177
client/SDL3/dialogs/sdl_input.cpp
Normal file
177
client/SDL3/dialogs/sdl_input.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* SDL Client helper dialogs
|
||||
*
|
||||
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "sdl_input.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_ttf.h>
|
||||
|
||||
#include "sdl_widget.hpp"
|
||||
#include "sdl_button.hpp"
|
||||
#include "sdl_buttons.hpp"
|
||||
|
||||
static const SDL_Color inputbackgroundcolor = { 0x56, 0x56, 0x56, 0xff };
|
||||
static const SDL_Color inputhighlightcolor = { 0x80, 0, 0, 0x60 };
|
||||
static const SDL_Color inputmouseovercolor = { 0, 0x80, 0, 0x60 };
|
||||
static const SDL_Color inputfontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
static const SDL_Color labelbackgroundcolor = { 0x56, 0x56, 0x56, 0xff };
|
||||
static const SDL_Color labelfontcolor = { 0xd1, 0xcf, 0xcd, 0xff };
|
||||
static const Uint32 vpadding = 5;
|
||||
static const Uint32 hpadding = 10;
|
||||
|
||||
SdlInputWidget::SdlInputWidget(SDL_Renderer* renderer, const std::string& label,
|
||||
const std::string& initial, Uint32 flags, size_t offset,
|
||||
size_t width, size_t height)
|
||||
: _flags(flags), _text(initial), _text_label(label),
|
||||
_label(renderer,
|
||||
{ 0, static_cast<int>(offset * (height + vpadding)), static_cast<int>(width),
|
||||
static_cast<int>(height) },
|
||||
false),
|
||||
_input(renderer,
|
||||
{ static_cast<int>(width + hpadding), static_cast<int>(offset * (height + vpadding)),
|
||||
static_cast<int>(width), static_cast<int>(height) },
|
||||
true),
|
||||
_highlight(false), _mouseover(false)
|
||||
{
|
||||
}
|
||||
|
||||
SdlInputWidget::SdlInputWidget(SdlInputWidget&& other) noexcept
|
||||
: _flags(std::move(other._flags)), _text(std::move(other._text)),
|
||||
_text_label(std::move(other._text_label)), _label(std::move(other._label)),
|
||||
_input(std::move(other._input)), _highlight(other._highlight), _mouseover(other._mouseover)
|
||||
{
|
||||
}
|
||||
|
||||
bool SdlInputWidget::fill_label(SDL_Renderer* renderer, SDL_Color color)
|
||||
{
|
||||
if (!_label.fill(renderer, color))
|
||||
return false;
|
||||
return _label.update_text(renderer, _text_label, labelfontcolor);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::update_label(SDL_Renderer* renderer)
|
||||
{
|
||||
return _label.update_text(renderer, _text_label, labelfontcolor, labelbackgroundcolor);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::set_mouseover(SDL_Renderer* renderer, bool mouseOver)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
_mouseover = mouseOver;
|
||||
return update_input(renderer);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::set_highlight(SDL_Renderer* renderer, bool highlight)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
_highlight = highlight;
|
||||
return update_input(renderer);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::update_input(SDL_Renderer* renderer)
|
||||
{
|
||||
std::vector<SDL_Color> colors = { inputbackgroundcolor };
|
||||
if (_highlight)
|
||||
colors.push_back(inputhighlightcolor);
|
||||
if (_mouseover)
|
||||
colors.push_back(inputmouseovercolor);
|
||||
|
||||
if (!_input.fill(renderer, colors))
|
||||
return false;
|
||||
return update_input(renderer, inputfontcolor);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::resize_input(size_t size)
|
||||
{
|
||||
_text.resize(size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SdlInputWidget::set_str(SDL_Renderer* renderer, const std::string& text)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
_text = text;
|
||||
if (!resize_input(_text.size()))
|
||||
return false;
|
||||
return update_input(renderer);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::remove_str(SDL_Renderer* renderer, size_t count)
|
||||
{
|
||||
if (readonly())
|
||||
return true;
|
||||
|
||||
assert(renderer);
|
||||
if (_text.empty())
|
||||
return true;
|
||||
|
||||
if (!resize_input(_text.size() - count))
|
||||
return false;
|
||||
return update_input(renderer);
|
||||
}
|
||||
|
||||
bool SdlInputWidget::append_str(SDL_Renderer* renderer, const std::string& str)
|
||||
{
|
||||
assert(renderer);
|
||||
if (readonly())
|
||||
return true;
|
||||
|
||||
_text.append(str);
|
||||
if (!resize_input(_text.size()))
|
||||
return false;
|
||||
return update_input(renderer);
|
||||
}
|
||||
|
||||
const SDL_Rect& SdlInputWidget::input_rect() const
|
||||
{
|
||||
return _input.rect();
|
||||
}
|
||||
|
||||
std::string SdlInputWidget::value() const
|
||||
{
|
||||
return _text;
|
||||
}
|
||||
|
||||
bool SdlInputWidget::readonly() const
|
||||
{
|
||||
return (_flags & SDL_INPUT_READONLY) != 0;
|
||||
}
|
||||
|
||||
bool SdlInputWidget::update_input(SDL_Renderer* renderer, SDL_Color fgcolor)
|
||||
{
|
||||
std::string text = _text;
|
||||
if (!text.empty())
|
||||
{
|
||||
if (_flags & SDL_INPUT_MASK)
|
||||
{
|
||||
for (char& x : text)
|
||||
x = '*';
|
||||
}
|
||||
}
|
||||
|
||||
return _input.update_text(renderer, text, fgcolor);
|
||||
}
|
73
client/SDL3/dialogs/sdl_input.hpp
Normal file
73
client/SDL3/dialogs/sdl_input.hpp
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 <vector>
|
||||
#include <string>
|
||||
|
||||
#include <SDL.h>
|
||||
#include "sdl_widget.hpp"
|
||||
|
||||
class SdlInputWidget
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
SDL_INPUT_MASK = 1,
|
||||
SDL_INPUT_READONLY = 2
|
||||
};
|
||||
|
||||
public:
|
||||
SdlInputWidget(SDL_Renderer* renderer, const std::string& label, const std::string& initial,
|
||||
Uint32 flags, size_t offset, size_t width, size_t height);
|
||||
SdlInputWidget(SdlInputWidget&& other) noexcept;
|
||||
|
||||
bool fill_label(SDL_Renderer* renderer, SDL_Color color);
|
||||
bool update_label(SDL_Renderer* renderer);
|
||||
|
||||
bool set_mouseover(SDL_Renderer* renderer, bool mouseOver);
|
||||
bool set_highlight(SDL_Renderer* renderer, bool hightlight);
|
||||
bool update_input(SDL_Renderer* renderer);
|
||||
bool resize_input(size_t size);
|
||||
|
||||
bool set_str(SDL_Renderer* renderer, const std::string& text);
|
||||
bool remove_str(SDL_Renderer* renderer, size_t count);
|
||||
bool append_str(SDL_Renderer* renderer, const std::string& text);
|
||||
|
||||
[[nodiscard]] const SDL_Rect& input_rect() const;
|
||||
[[nodiscard]] std::string value() const;
|
||||
|
||||
[[nodiscard]] bool readonly() const;
|
||||
|
||||
protected:
|
||||
bool update_input(SDL_Renderer* renderer, SDL_Color fgclor);
|
||||
|
||||
private:
|
||||
SdlInputWidget(const SdlInputWidget& other) = delete;
|
||||
|
||||
private:
|
||||
Uint32 _flags;
|
||||
std::string _text;
|
||||
std::string _text_label;
|
||||
SdlWidget _label;
|
||||
SdlWidget _input;
|
||||
bool _highlight;
|
||||
bool _mouseover;
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user