diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 32de53622..1836db0bd 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -32,32 +32,9 @@ if(FREERDP_VENDOR AND WITH_CLIENT) endif() endif() - cmake_dependent_option(WITH_CLIENT_SDL_VERSIONED "append sdl version to client binaries" OFF WITH_CLIENT_SDL OFF) - 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 AND WITH_CLIENT_SDL3) - message("Building both, SDL2 and SDL3 clients, forcing WITH_CLIENT_SDL_VERSIONED=ON") - set(WITH_CLIENT_SDL_VERSIONED ON) - endif() - - if (WITH_CLIENT_SDL2) - find_package(SDL2) - if (SDL2_FOUND) - add_subdirectory(SDL2) - else() - 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() + if (WITH_CLIENT_SDL) + add_subdirectory(SDL) + endif() if(WITH_X11) add_subdirectory(X11) diff --git a/client/SDL3/CMakeLists.txt b/client/SDL/CMakeLists.txt similarity index 55% rename from client/SDL3/CMakeLists.txt rename to client/SDL/CMakeLists.txt index e641d5b38..2c5e4631f 100644 --- a/client/SDL3/CMakeLists.txt +++ b/client/SDL/CMakeLists.txt @@ -1,7 +1,8 @@ # FreeRDP: A Remote Desktop Protocol Implementation # FreeRDP SDL Client # -# Copyright 2022 Armin Novak +# Copyright 2024 Armin Novak +# Copyright 2024 Thincast Technologies GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +15,6 @@ # 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) @@ -24,7 +24,7 @@ if (NOT FREERDP_DEFAULT_PROJECT_VERSION) set(FREERDP_DEFAULT_PROJECT_VERSION "1.0.0.0") endif() -project(sdl3-freerdp +project(sdl-freerdp LANGUAGES CXX VERSION ${FREERDP_DEFAULT_PROJECT_VERSION} ) @@ -59,73 +59,35 @@ if (WITH_DEBUG_SDL_KBD_EVENTS) add_definitions(-DWITH_DEBUG_SDL_KBD_EVENTS) endif() -find_package(SDL3 REQUIRED COMPONENTS) +include(CMakeDependentOption) -find_package(Threads REQUIRED) +cmake_dependent_option(WITH_CLIENT_SDL_VERSIONED "append sdl version to client binaries" OFF WITH_CLIENT_SDL OFF) +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) -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 - sdl_clip.hpp - sdl_clip.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 ${SDL3_STATIC_LIBRARIES}) -else() - list(APPEND LIBS ${SDL3_LIBRARIES}) +if (WITH_CLIENT_SDL2 AND WITH_CLIENT_SDL3) + message("Building both, SDL2 and SDL3 clients, forcing WITH_CLIENT_SDL_VERSIONED=ON") + set(WITH_CLIENT_SDL_VERSIONED ON) endif() -AddTargetWithResourceFile(${PROJECT_NAME} "${WIN32_GUI_FLAG}" "${PROJECT_VERSION}" SRCS) +add_subdirectory(common) +include_directories(common) -target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBS}) -target_link_libraries(sdl3-prefs winpr) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER "Client/SDL") -if (NOT WITH_CLIENT_SDL_VERSIONED) - set_target_properties(${PROJECT_NAME} - PROPERTIES - OUTPUT_NAME "sdl-freerdp" - ) +if (WITH_CLIENT_SDL2) + find_package(SDL2) + if (SDL2_FOUND) + add_subdirectory(SDL2) + else() + message(WARNING "SDL2 requested but not found, continuing build without SDL2 client") + endif() endif() -install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT client) -add_subdirectory(man) - -if(BUILD_TESTING) - add_subdirectory(test) +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() + diff --git a/client/SDL/SDL2/CMakeLists.txt b/client/SDL/SDL2/CMakeLists.txt new file mode 100644 index 000000000..8291b4100 --- /dev/null +++ b/client/SDL/SDL2/CMakeLists.txt @@ -0,0 +1,80 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP SDL Client +# +# Copyright 2022 Armin Novak +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(MODULE_NAME "sdl2-freerdp") + +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 +) + +list(APPEND LIBS + winpr + freerdp + freerdp-client + Threads::Threads + sdl2_client_res + sdl2-dialogs + sdl-common-aad-view + sdl-common-prefs + ) + +if (NOT WITH_SDL_LINK_SHARED) + list(APPEND LIBS ${SDL2_STATIC_LIBRARIES}) +else() + list(APPEND LIBS ${SDL2_LIBRARIES}) +endif() + +AddTargetWithResourceFile(${MODULE_NAME} "${WIN32_GUI_FLAG}" "${PROJECT_VERSION}" SRCS) + +target_link_libraries(${MODULE_NAME} PRIVATE ${LIBS}) + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/SDL") + +if (NOT WITH_CLIENT_SDL_VERSIONED) + set_target_properties(${MODULE_NAME} + PROPERTIES + OUTPUT_NAME ${PROJECT_NAME} + ) +endif() + +install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT client) + +add_subdirectory(man) diff --git a/client/SDL2/dialogs/CMakeLists.txt b/client/SDL/SDL2/dialogs/CMakeLists.txt similarity index 100% rename from client/SDL2/dialogs/CMakeLists.txt rename to client/SDL/SDL2/dialogs/CMakeLists.txt diff --git a/client/SDL/SDL2/dialogs/res/CMakeLists.txt b/client/SDL/SDL2/dialogs/res/CMakeLists.txt new file mode 100644 index 000000000..b22da6218 --- /dev/null +++ b/client/SDL/SDL2/dialogs/res/CMakeLists.txt @@ -0,0 +1,35 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP SDL Client +# +# Copyright 2024 Armin Novak +# Copyright 2024 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(SRCS + sdl2_resource_manager.cpp + sdl2_resource_manager.hpp +) + +add_library(sdl2_client_res STATIC + ${SRCS} +) + +if (NOT WITH_SDL_LINK_SHARED) + target_link_libraries(sdl2_client_res ${SDL2_STATIC_LIBRARIES}) +else() + target_link_libraries(sdl2_client_res ${SDL2_LIBRARIES}) +endif() + +set_property(TARGET sdl2_client_res PROPERTY POSITION_INDEPENDENT_CODE ON) +target_link_libraries(sdl2_client_res sdl-common-client-res) diff --git a/client/SDL3/aad/sdl_webview.hpp b/client/SDL/SDL2/dialogs/res/sdl2_resource_manager.cpp similarity index 55% rename from client/SDL3/aad/sdl_webview.hpp rename to client/SDL/SDL2/dialogs/res/sdl2_resource_manager.cpp index 49461d69a..fe8e99719 100644 --- a/client/SDL3/aad/sdl_webview.hpp +++ b/client/SDL/SDL2/dialogs/res/sdl2_resource_manager.cpp @@ -1,14 +1,13 @@ /** * FreeRDP: A Remote Desktop Protocol Implementation - * Popup browser for AAD authentication * - * Copyright 2023 Isaac Klein + * Copyright 2023 Armin Novak * * 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 + * 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, @@ -16,23 +15,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "sdl2_resource_manager.hpp" +#include -#pragma once - -#include - -#include - -#if defined(WITH_WEBVIEW) -#ifdef __cplusplus -extern "C" +SDL_RWops* SDL2ResourceManager::get(const std::string& type, const std::string& id) { -#endif + if (useCompiledResources()) + { + auto d = data(type, id); + if (!d) + return nullptr; - BOOL sdl_webview_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token, - size_t count, ...); + return SDL_RWFromConstMem(d->data(), d->size()); + } -#ifdef __cplusplus + auto name = filename(type, id); + return SDL_RWFromFile(name.c_str(), "rb"); } -#endif -#endif diff --git a/client/SDL3/dialogs/res/sdl_resource_file.cpp b/client/SDL/SDL2/dialogs/res/sdl2_resource_manager.hpp similarity index 71% rename from client/SDL3/dialogs/res/sdl_resource_file.cpp rename to client/SDL/SDL2/dialogs/res/sdl2_resource_manager.hpp index c48612d92..d04300bef 100644 --- a/client/SDL3/dialogs/res/sdl_resource_file.cpp +++ b/client/SDL/SDL2/dialogs/res/sdl2_resource_manager.hpp @@ -15,11 +15,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "sdl_resource_file.hpp" -#include "sdl_resource_manager.hpp" +#pragma once -SDLResourceFile::SDLResourceFile(const std::string& type, const std::string& id, - const std::vector& data) +#include +#include +#include +#include + +#include + +class SDL2ResourceManager : public SDLResourceManager { - SDLResourceManager::insert(type, id, data); -} + public: + static SDL_RWops* get(const std::string& type, const std::string& id); +}; diff --git a/client/SDL2/dialogs/sdl_button.cpp b/client/SDL/SDL2/dialogs/sdl_button.cpp similarity index 100% rename from client/SDL2/dialogs/sdl_button.cpp rename to client/SDL/SDL2/dialogs/sdl_button.cpp diff --git a/client/SDL2/dialogs/sdl_button.hpp b/client/SDL/SDL2/dialogs/sdl_button.hpp similarity index 100% rename from client/SDL2/dialogs/sdl_button.hpp rename to client/SDL/SDL2/dialogs/sdl_button.hpp diff --git a/client/SDL2/dialogs/sdl_buttons.cpp b/client/SDL/SDL2/dialogs/sdl_buttons.cpp similarity index 100% rename from client/SDL2/dialogs/sdl_buttons.cpp rename to client/SDL/SDL2/dialogs/sdl_buttons.cpp diff --git a/client/SDL2/dialogs/sdl_buttons.hpp b/client/SDL/SDL2/dialogs/sdl_buttons.hpp similarity index 100% rename from client/SDL2/dialogs/sdl_buttons.hpp rename to client/SDL/SDL2/dialogs/sdl_buttons.hpp diff --git a/client/SDL2/dialogs/sdl_connection_dialog.cpp b/client/SDL/SDL2/dialogs/sdl_connection_dialog.cpp similarity index 97% rename from client/SDL2/dialogs/sdl_connection_dialog.cpp rename to client/SDL/SDL2/dialogs/sdl_connection_dialog.cpp index fa638591d..3da64e82c 100644 --- a/client/SDL2/dialogs/sdl_connection_dialog.cpp +++ b/client/SDL/SDL2/dialogs/sdl_connection_dialog.cpp @@ -22,7 +22,7 @@ #include "sdl_connection_dialog.hpp" #include "../sdl_utils.hpp" #include "../sdl_freerdp.hpp" -#include "res/sdl_resource_manager.hpp" +#include "res/sdl2_resource_manager.hpp" static const SDL_Color backgroundcolor = { 0x38, 0x36, 0x35, 0xff }; static const SDL_Color textcolor = { 0xd1, 0xcf, 0xcd, 0xff }; @@ -389,7 +389,7 @@ bool SDLConnectionDialog::createWindow() widget_cfg_t icon{ textcolor, res_bgcolor, { _renderer, iconRect, - SDLResourceManager::get(SDLResourceManager::typeImages(), res_name) } }; + SDL2ResourceManager::get(SDLResourceManager::typeImages(), res_name) } }; _list.emplace_back(std::move(icon)); iconRect.y += height; @@ -397,8 +397,8 @@ bool SDLConnectionDialog::createWindow() widget_cfg_t logo{ textcolor, backgroundcolor, { _renderer, iconRect, - SDLResourceManager::get(SDLResourceManager::typeImages(), - "FreeRDP_Icon.svg") } }; + SDL2ResourceManager::get(SDLResourceManager::typeImages(), + "FreeRDP_Icon.svg") } }; _list.emplace_back(std::move(logo)); SDL_Rect rect = { widget_width / 4ul, vpadding, widget_width * 3ul / 4ul, diff --git a/client/SDL2/dialogs/sdl_connection_dialog.hpp b/client/SDL/SDL2/dialogs/sdl_connection_dialog.hpp similarity index 100% rename from client/SDL2/dialogs/sdl_connection_dialog.hpp rename to client/SDL/SDL2/dialogs/sdl_connection_dialog.hpp diff --git a/client/SDL2/dialogs/sdl_dialogs.cpp b/client/SDL/SDL2/dialogs/sdl_dialogs.cpp similarity index 100% rename from client/SDL2/dialogs/sdl_dialogs.cpp rename to client/SDL/SDL2/dialogs/sdl_dialogs.cpp diff --git a/client/SDL2/dialogs/sdl_dialogs.hpp b/client/SDL/SDL2/dialogs/sdl_dialogs.hpp similarity index 100% rename from client/SDL2/dialogs/sdl_dialogs.hpp rename to client/SDL/SDL2/dialogs/sdl_dialogs.hpp diff --git a/client/SDL2/dialogs/sdl_input.cpp b/client/SDL/SDL2/dialogs/sdl_input.cpp similarity index 100% rename from client/SDL2/dialogs/sdl_input.cpp rename to client/SDL/SDL2/dialogs/sdl_input.cpp diff --git a/client/SDL2/dialogs/sdl_input.hpp b/client/SDL/SDL2/dialogs/sdl_input.hpp similarity index 100% rename from client/SDL2/dialogs/sdl_input.hpp rename to client/SDL/SDL2/dialogs/sdl_input.hpp diff --git a/client/SDL2/dialogs/sdl_input_widgets.cpp b/client/SDL/SDL2/dialogs/sdl_input_widgets.cpp similarity index 100% rename from client/SDL2/dialogs/sdl_input_widgets.cpp rename to client/SDL/SDL2/dialogs/sdl_input_widgets.cpp diff --git a/client/SDL2/dialogs/sdl_input_widgets.hpp b/client/SDL/SDL2/dialogs/sdl_input_widgets.hpp similarity index 100% rename from client/SDL2/dialogs/sdl_input_widgets.hpp rename to client/SDL/SDL2/dialogs/sdl_input_widgets.hpp diff --git a/client/SDL2/dialogs/sdl_select.cpp b/client/SDL/SDL2/dialogs/sdl_select.cpp similarity index 100% rename from client/SDL2/dialogs/sdl_select.cpp rename to client/SDL/SDL2/dialogs/sdl_select.cpp diff --git a/client/SDL2/dialogs/sdl_select.hpp b/client/SDL/SDL2/dialogs/sdl_select.hpp similarity index 100% rename from client/SDL2/dialogs/sdl_select.hpp rename to client/SDL/SDL2/dialogs/sdl_select.hpp diff --git a/client/SDL2/dialogs/sdl_selectlist.cpp b/client/SDL/SDL2/dialogs/sdl_selectlist.cpp similarity index 100% rename from client/SDL2/dialogs/sdl_selectlist.cpp rename to client/SDL/SDL2/dialogs/sdl_selectlist.cpp diff --git a/client/SDL2/dialogs/sdl_selectlist.hpp b/client/SDL/SDL2/dialogs/sdl_selectlist.hpp similarity index 100% rename from client/SDL2/dialogs/sdl_selectlist.hpp rename to client/SDL/SDL2/dialogs/sdl_selectlist.hpp diff --git a/client/SDL2/dialogs/sdl_widget.cpp b/client/SDL/SDL2/dialogs/sdl_widget.cpp similarity index 97% rename from client/SDL2/dialogs/sdl_widget.cpp rename to client/SDL/SDL2/dialogs/sdl_widget.cpp index 6e11b5a9a..55a562e74 100644 --- a/client/SDL2/dialogs/sdl_widget.cpp +++ b/client/SDL/SDL2/dialogs/sdl_widget.cpp @@ -27,7 +27,7 @@ #include "sdl_widget.hpp" #include "../sdl_utils.hpp" -#include "res/sdl_resource_manager.hpp" +#include "res/sdl2_resource_manager.hpp" #include @@ -46,8 +46,8 @@ SdlWidget::SdlWidget(SDL_Renderer* renderer, const SDL_Rect& rect, bool input) { assert(renderer); - auto ops = SDLResourceManager::get(SDLResourceManager::typeFonts(), - "OpenSans-VariableFont_wdth,wght.ttf"); + auto ops = SDL2ResourceManager::get(SDLResourceManager::typeFonts(), + "OpenSans-VariableFont_wdth,wght.ttf"); if (!ops) widget_log_error(-1, "SDLResourceManager::get"); else diff --git a/client/SDL2/dialogs/sdl_widget.hpp b/client/SDL/SDL2/dialogs/sdl_widget.hpp similarity index 100% rename from client/SDL2/dialogs/sdl_widget.hpp rename to client/SDL/SDL2/dialogs/sdl_widget.hpp diff --git a/client/SDL2/dialogs/test/CMakeLists.txt b/client/SDL/SDL2/dialogs/test/CMakeLists.txt similarity index 100% rename from client/SDL2/dialogs/test/CMakeLists.txt rename to client/SDL/SDL2/dialogs/test/CMakeLists.txt diff --git a/client/SDL2/dialogs/test/TestSDLDialogs.cpp b/client/SDL/SDL2/dialogs/test/TestSDLDialogs.cpp similarity index 100% rename from client/SDL2/dialogs/test/TestSDLDialogs.cpp rename to client/SDL/SDL2/dialogs/test/TestSDLDialogs.cpp diff --git a/client/SDL2/man/CMakeLists.txt b/client/SDL/SDL2/man/CMakeLists.txt similarity index 64% rename from client/SDL2/man/CMakeLists.txt rename to client/SDL/SDL2/man/CMakeLists.txt index fce3439e6..a642f5591 100644 --- a/client/SDL2/man/CMakeLists.txt +++ b/client/SDL/SDL2/man/CMakeLists.txt @@ -5,4 +5,4 @@ set(DEPS sdl2-freerdp-envvar.1.xml ) -generate_and_install_freerdp_man_from_xml(${PROJECT_NAME} "1" "${DEPS}") +generate_and_install_freerdp_man_from_xml(${MODULE_NAME} "1" "${DEPS}") diff --git a/client/SDL2/man/sdl2-freerdp-config.1.xml.in b/client/SDL/SDL2/man/sdl2-freerdp-config.1.xml.in similarity index 96% rename from client/SDL2/man/sdl2-freerdp-config.1.xml.in rename to client/SDL/SDL2/man/sdl2-freerdp-config.1.xml.in index 3bace734f..7db5ff8bf 100644 --- a/client/SDL2/man/sdl2-freerdp-config.1.xml.in +++ b/client/SDL/SDL2/man/sdl2-freerdp-config.1.xml.in @@ -8,7 +8,7 @@ The configuration file is stored per user. The XDG_CONFIG_HOME environment variable can be used to override the base directory. This defaults to ~/.config - The location relative to XDG_CONFIG_HOME is $XDG_CONFIG_HOME/@VENDOR@/@PRODUCT@/@PROJECT_NAME@.json + The location relative to XDG_CONFIG_HOME is $XDG_CONFIG_HOME/@VENDOR@/@PRODUCT@/@MODULE_NAME@.json The configuration is stored in JSON format diff --git a/client/SDL2/man/sdl2-freerdp-envvar.1.xml.in b/client/SDL/SDL2/man/sdl2-freerdp-envvar.1.xml.in similarity index 100% rename from client/SDL2/man/sdl2-freerdp-envvar.1.xml.in rename to client/SDL/SDL2/man/sdl2-freerdp-envvar.1.xml.in diff --git a/client/SDL2/man/sdl2-freerdp-examples.1.xml.in b/client/SDL/SDL2/man/sdl2-freerdp-examples.1.xml.in similarity index 100% rename from client/SDL2/man/sdl2-freerdp-examples.1.xml.in rename to client/SDL/SDL2/man/sdl2-freerdp-examples.1.xml.in diff --git a/client/SDL2/man/sdl2-freerdp.1.xml.in b/client/SDL/SDL2/man/sdl2-freerdp.1.xml.in similarity index 100% rename from client/SDL2/man/sdl2-freerdp.1.xml.in rename to client/SDL/SDL2/man/sdl2-freerdp.1.xml.in diff --git a/client/SDL2/sdl_channels.cpp b/client/SDL/SDL2/sdl_channels.cpp similarity index 100% rename from client/SDL2/sdl_channels.cpp rename to client/SDL/SDL2/sdl_channels.cpp diff --git a/client/SDL2/sdl_channels.hpp b/client/SDL/SDL2/sdl_channels.hpp similarity index 100% rename from client/SDL2/sdl_channels.hpp rename to client/SDL/SDL2/sdl_channels.hpp diff --git a/client/SDL2/sdl_disp.cpp b/client/SDL/SDL2/sdl_disp.cpp similarity index 100% rename from client/SDL2/sdl_disp.cpp rename to client/SDL/SDL2/sdl_disp.cpp diff --git a/client/SDL2/sdl_disp.hpp b/client/SDL/SDL2/sdl_disp.hpp similarity index 100% rename from client/SDL2/sdl_disp.hpp rename to client/SDL/SDL2/sdl_disp.hpp diff --git a/client/SDL2/sdl_freerdp.cpp b/client/SDL/SDL2/sdl_freerdp.cpp similarity index 99% rename from client/SDL2/sdl_freerdp.cpp rename to client/SDL/SDL2/sdl_freerdp.cpp index d4cdf27f1..76aa62b63 100644 --- a/client/SDL2/sdl_freerdp.cpp +++ b/client/SDL/SDL2/sdl_freerdp.cpp @@ -60,7 +60,7 @@ #include "sdl_prefs.hpp" #include "dialogs/sdl_dialogs.hpp" -#include "aad/sdl_webview.hpp" +#include #define SDL_TAG CLIENT_TAG("SDL") diff --git a/client/SDL2/sdl_freerdp.hpp b/client/SDL/SDL2/sdl_freerdp.hpp similarity index 100% rename from client/SDL2/sdl_freerdp.hpp rename to client/SDL/SDL2/sdl_freerdp.hpp diff --git a/client/SDL2/sdl_kbd.cpp b/client/SDL/SDL2/sdl_kbd.cpp similarity index 100% rename from client/SDL2/sdl_kbd.cpp rename to client/SDL/SDL2/sdl_kbd.cpp diff --git a/client/SDL2/sdl_kbd.hpp b/client/SDL/SDL2/sdl_kbd.hpp similarity index 100% rename from client/SDL2/sdl_kbd.hpp rename to client/SDL/SDL2/sdl_kbd.hpp diff --git a/client/SDL2/sdl_monitor.cpp b/client/SDL/SDL2/sdl_monitor.cpp similarity index 100% rename from client/SDL2/sdl_monitor.cpp rename to client/SDL/SDL2/sdl_monitor.cpp diff --git a/client/SDL2/sdl_monitor.hpp b/client/SDL/SDL2/sdl_monitor.hpp similarity index 100% rename from client/SDL2/sdl_monitor.hpp rename to client/SDL/SDL2/sdl_monitor.hpp diff --git a/client/SDL2/sdl_pointer.cpp b/client/SDL/SDL2/sdl_pointer.cpp similarity index 100% rename from client/SDL2/sdl_pointer.cpp rename to client/SDL/SDL2/sdl_pointer.cpp diff --git a/client/SDL2/sdl_pointer.hpp b/client/SDL/SDL2/sdl_pointer.hpp similarity index 100% rename from client/SDL2/sdl_pointer.hpp rename to client/SDL/SDL2/sdl_pointer.hpp diff --git a/client/SDL2/sdl_touch.cpp b/client/SDL/SDL2/sdl_touch.cpp similarity index 100% rename from client/SDL2/sdl_touch.cpp rename to client/SDL/SDL2/sdl_touch.cpp diff --git a/client/SDL2/sdl_touch.hpp b/client/SDL/SDL2/sdl_touch.hpp similarity index 100% rename from client/SDL2/sdl_touch.hpp rename to client/SDL/SDL2/sdl_touch.hpp diff --git a/client/SDL2/sdl_types.hpp b/client/SDL/SDL2/sdl_types.hpp similarity index 100% rename from client/SDL2/sdl_types.hpp rename to client/SDL/SDL2/sdl_types.hpp diff --git a/client/SDL2/sdl_utils.cpp b/client/SDL/SDL2/sdl_utils.cpp similarity index 100% rename from client/SDL2/sdl_utils.cpp rename to client/SDL/SDL2/sdl_utils.cpp diff --git a/client/SDL2/sdl_utils.hpp b/client/SDL/SDL2/sdl_utils.hpp similarity index 100% rename from client/SDL2/sdl_utils.hpp rename to client/SDL/SDL2/sdl_utils.hpp diff --git a/client/SDL2/sdl_window.cpp b/client/SDL/SDL2/sdl_window.cpp similarity index 100% rename from client/SDL2/sdl_window.cpp rename to client/SDL/SDL2/sdl_window.cpp diff --git a/client/SDL2/sdl_window.hpp b/client/SDL/SDL2/sdl_window.hpp similarity index 100% rename from client/SDL2/sdl_window.hpp rename to client/SDL/SDL2/sdl_window.hpp diff --git a/client/SDL/SDL3/CMakeLists.txt b/client/SDL/SDL3/CMakeLists.txt new file mode 100644 index 000000000..136f04f1a --- /dev/null +++ b/client/SDL/SDL3/CMakeLists.txt @@ -0,0 +1,80 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP SDL Client +# +# Copyright 2022 Armin Novak +# Copyright 2024 Armin Novak +# Copyright 2024 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(MODULE_NAME "sdl3-freerdp") + +find_package(SDL3 REQUIRED COMPONENTS) + +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 + sdl_clip.hpp + sdl_clip.cpp +) + +list(APPEND LIBS + winpr + freerdp + freerdp-client + Threads::Threads + sdl3_client_res + sdl3-dialogs + sdl-common-aad-view + sdl-common-prefs + ) + +if (NOT WITH_SDL_LINK_SHARED) + list(APPEND LIBS ${SDL3_STATIC_LIBRARIES}) +else() + list(APPEND LIBS ${SDL3_LIBRARIES}) +endif() + +AddTargetWithResourceFile(${MODULE_NAME} "${WIN32_GUI_FLAG}" "${PROJECT_VERSION}" SRCS) + +target_link_libraries(${MODULE_NAME} PRIVATE ${LIBS}) + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/SDL") +if (NOT WITH_CLIENT_SDL_VERSIONED) + set_target_properties(${MODULE_NAME} + PROPERTIES + OUTPUT_NAME ${PROJECT_NAME} + ) +endif() +install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT client) + +add_subdirectory(man) diff --git a/client/SDL3/dialogs/CMakeLists.txt b/client/SDL/SDL3/dialogs/CMakeLists.txt similarity index 62% rename from client/SDL3/dialogs/CMakeLists.txt rename to client/SDL/SDL3/dialogs/CMakeLists.txt index ee6295a01..f221612e0 100644 --- a/client/SDL3/dialogs/CMakeLists.txt +++ b/client/SDL/SDL3/dialogs/CMakeLists.txt @@ -1,3 +1,20 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP SDL Client +# +# Copyright 2024 Armin Novak +# Copyright 2024 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. set(SRCS sdl_button.hpp sdl_button.cpp diff --git a/client/SDL3/dialogs/font/OFL.txt b/client/SDL/SDL3/dialogs/font/OFL.txt similarity index 100% rename from client/SDL3/dialogs/font/OFL.txt rename to client/SDL/SDL3/dialogs/font/OFL.txt diff --git a/client/SDL3/dialogs/font/OpenSans-Italic-VariableFont_wdth,wght.ttf b/client/SDL/SDL3/dialogs/font/OpenSans-Italic-VariableFont_wdth,wght.ttf similarity index 100% rename from client/SDL3/dialogs/font/OpenSans-Italic-VariableFont_wdth,wght.ttf rename to client/SDL/SDL3/dialogs/font/OpenSans-Italic-VariableFont_wdth,wght.ttf diff --git a/client/SDL3/dialogs/font/OpenSans-VariableFont_wdth,wght.ttf b/client/SDL/SDL3/dialogs/font/OpenSans-VariableFont_wdth,wght.ttf similarity index 100% rename from client/SDL3/dialogs/font/OpenSans-VariableFont_wdth,wght.ttf rename to client/SDL/SDL3/dialogs/font/OpenSans-VariableFont_wdth,wght.ttf diff --git a/client/SDL3/dialogs/font/README.txt b/client/SDL/SDL3/dialogs/font/README.txt similarity index 100% rename from client/SDL3/dialogs/font/README.txt rename to client/SDL/SDL3/dialogs/font/README.txt diff --git a/client/SDL/SDL3/dialogs/res/CMakeLists.txt b/client/SDL/SDL3/dialogs/res/CMakeLists.txt new file mode 100644 index 000000000..8ff2c5196 --- /dev/null +++ b/client/SDL/SDL3/dialogs/res/CMakeLists.txt @@ -0,0 +1,34 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP SDL Client +# +# Copyright 2024 Armin Novak +# Copyright 2024 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SRCS + sdl3_resource_manager.cpp + sdl3_resource_manager.hpp +) + +add_library(sdl3_client_res STATIC + ${SRCS} +) +if (NOT WITH_SDL_LINK_SHARED) + target_link_libraries(sdl3_client_res ${SDL3_STATIC_LIBRARIES}) +else() + target_link_libraries(sdl3_client_res ${SDL3_LIBRARIES}) +endif() + +target_link_libraries(sdl3_client_res sdl-common-client-res) + +set_property(TARGET sdl3_client_res PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/SDL/SDL3/dialogs/res/sdl3_resource_manager.cpp b/client/SDL/SDL3/dialogs/res/sdl3_resource_manager.cpp new file mode 100644 index 000000000..0f1bd46b6 --- /dev/null +++ b/client/SDL/SDL3/dialogs/res/sdl3_resource_manager.cpp @@ -0,0 +1,43 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2023 Armin Novak + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "sdl3_resource_manager.hpp" +#include +#if __has_include() +#include +namespace fs = std::filesystem; +#elif __has_include() +#include +namespace fs = std::experimental::filesystem; +#else +#error Could not find system header "" or "" +#endif + +SDL_IOStream* SDL3ResourceManager::get(const std::string& type, const std::string& id) +{ + if (useCompiledResources()) + { + auto d = data(type, id); + if (!d) + return nullptr; + + return SDL_IOFromConstMem(d->data(), d->size()); + } + + auto name = filename(type, id); + return SDL_IOFromFile(name.c_str(), "rb"); +} diff --git a/client/SDL3/dialogs/res/sdl_resource_file.hpp b/client/SDL/SDL3/dialogs/res/sdl3_resource_manager.hpp similarity index 70% rename from client/SDL3/dialogs/res/sdl_resource_file.hpp rename to client/SDL/SDL3/dialogs/res/sdl3_resource_manager.hpp index 5846921d1..471dbf62e 100644 --- a/client/SDL3/dialogs/res/sdl_resource_file.hpp +++ b/client/SDL/SDL3/dialogs/res/sdl3_resource_manager.hpp @@ -18,16 +18,14 @@ #pragma once #include +#include #include +#include -class SDLResourceFile +#include + +class SDL3ResourceManager : public SDLResourceManager { public: - SDLResourceFile(const std::string& type, const std::string& id, - const std::vector& data); - virtual ~SDLResourceFile() = default; - - private: - SDLResourceFile(const SDLResourceFile& other) = delete; - SDLResourceFile(const SDLResourceFile&& other) = delete; + static SDL_IOStream* get(const std::string& type, const std::string& id); }; diff --git a/client/SDL3/dialogs/sdl_button.cpp b/client/SDL/SDL3/dialogs/sdl_button.cpp similarity index 100% rename from client/SDL3/dialogs/sdl_button.cpp rename to client/SDL/SDL3/dialogs/sdl_button.cpp diff --git a/client/SDL3/dialogs/sdl_button.hpp b/client/SDL/SDL3/dialogs/sdl_button.hpp similarity index 100% rename from client/SDL3/dialogs/sdl_button.hpp rename to client/SDL/SDL3/dialogs/sdl_button.hpp diff --git a/client/SDL3/dialogs/sdl_buttons.cpp b/client/SDL/SDL3/dialogs/sdl_buttons.cpp similarity index 100% rename from client/SDL3/dialogs/sdl_buttons.cpp rename to client/SDL/SDL3/dialogs/sdl_buttons.cpp diff --git a/client/SDL3/dialogs/sdl_buttons.hpp b/client/SDL/SDL3/dialogs/sdl_buttons.hpp similarity index 100% rename from client/SDL3/dialogs/sdl_buttons.hpp rename to client/SDL/SDL3/dialogs/sdl_buttons.hpp diff --git a/client/SDL3/dialogs/sdl_connection_dialog.cpp b/client/SDL/SDL3/dialogs/sdl_connection_dialog.cpp similarity index 97% rename from client/SDL3/dialogs/sdl_connection_dialog.cpp rename to client/SDL/SDL3/dialogs/sdl_connection_dialog.cpp index 88d738322..85ff5a7b4 100644 --- a/client/SDL3/dialogs/sdl_connection_dialog.cpp +++ b/client/SDL/SDL3/dialogs/sdl_connection_dialog.cpp @@ -22,7 +22,7 @@ #include "sdl_connection_dialog.hpp" #include "../sdl_utils.hpp" #include "../sdl_freerdp.hpp" -#include "res/sdl_resource_manager.hpp" +#include "res/sdl3_resource_manager.hpp" static const SDL_Color backgroundcolor = { 0x38, 0x36, 0x35, 0xff }; static const SDL_Color textcolor = { 0xd1, 0xcf, 0xcd, 0xff }; @@ -386,7 +386,7 @@ bool SDLConnectionDialog::createWindow() widget_cfg_t icon{ textcolor, res_bgcolor, { _renderer, iconRect, - SDLResourceManager::get(SDLResourceManager::typeImages(), res_name) } }; + SDL3ResourceManager::get(SDLResourceManager::typeImages(), res_name) } }; _list.emplace_back(std::move(icon)); iconRect.y += height; @@ -394,8 +394,8 @@ bool SDLConnectionDialog::createWindow() widget_cfg_t logo{ textcolor, backgroundcolor, { _renderer, iconRect, - SDLResourceManager::get(SDLResourceManager::typeImages(), - "FreeRDP_Icon.svg") } }; + SDL3ResourceManager::get(SDLResourceManager::typeImages(), + "FreeRDP_Icon.svg") } }; _list.emplace_back(std::move(logo)); SDL_FRect rect = { widget_width / 4ul, vpadding, widget_width * 3ul / 4ul, diff --git a/client/SDL3/dialogs/sdl_connection_dialog.hpp b/client/SDL/SDL3/dialogs/sdl_connection_dialog.hpp similarity index 100% rename from client/SDL3/dialogs/sdl_connection_dialog.hpp rename to client/SDL/SDL3/dialogs/sdl_connection_dialog.hpp diff --git a/client/SDL3/dialogs/sdl_dialogs.cpp b/client/SDL/SDL3/dialogs/sdl_dialogs.cpp similarity index 100% rename from client/SDL3/dialogs/sdl_dialogs.cpp rename to client/SDL/SDL3/dialogs/sdl_dialogs.cpp diff --git a/client/SDL3/dialogs/sdl_dialogs.hpp b/client/SDL/SDL3/dialogs/sdl_dialogs.hpp similarity index 100% rename from client/SDL3/dialogs/sdl_dialogs.hpp rename to client/SDL/SDL3/dialogs/sdl_dialogs.hpp diff --git a/client/SDL3/dialogs/sdl_input.cpp b/client/SDL/SDL3/dialogs/sdl_input.cpp similarity index 100% rename from client/SDL3/dialogs/sdl_input.cpp rename to client/SDL/SDL3/dialogs/sdl_input.cpp diff --git a/client/SDL3/dialogs/sdl_input.hpp b/client/SDL/SDL3/dialogs/sdl_input.hpp similarity index 100% rename from client/SDL3/dialogs/sdl_input.hpp rename to client/SDL/SDL3/dialogs/sdl_input.hpp diff --git a/client/SDL3/dialogs/sdl_input_widgets.cpp b/client/SDL/SDL3/dialogs/sdl_input_widgets.cpp similarity index 100% rename from client/SDL3/dialogs/sdl_input_widgets.cpp rename to client/SDL/SDL3/dialogs/sdl_input_widgets.cpp diff --git a/client/SDL3/dialogs/sdl_input_widgets.hpp b/client/SDL/SDL3/dialogs/sdl_input_widgets.hpp similarity index 100% rename from client/SDL3/dialogs/sdl_input_widgets.hpp rename to client/SDL/SDL3/dialogs/sdl_input_widgets.hpp diff --git a/client/SDL3/dialogs/sdl_select.cpp b/client/SDL/SDL3/dialogs/sdl_select.cpp similarity index 100% rename from client/SDL3/dialogs/sdl_select.cpp rename to client/SDL/SDL3/dialogs/sdl_select.cpp diff --git a/client/SDL3/dialogs/sdl_select.hpp b/client/SDL/SDL3/dialogs/sdl_select.hpp similarity index 100% rename from client/SDL3/dialogs/sdl_select.hpp rename to client/SDL/SDL3/dialogs/sdl_select.hpp diff --git a/client/SDL3/dialogs/sdl_selectlist.cpp b/client/SDL/SDL3/dialogs/sdl_selectlist.cpp similarity index 100% rename from client/SDL3/dialogs/sdl_selectlist.cpp rename to client/SDL/SDL3/dialogs/sdl_selectlist.cpp diff --git a/client/SDL3/dialogs/sdl_selectlist.hpp b/client/SDL/SDL3/dialogs/sdl_selectlist.hpp similarity index 100% rename from client/SDL3/dialogs/sdl_selectlist.hpp rename to client/SDL/SDL3/dialogs/sdl_selectlist.hpp diff --git a/client/SDL3/dialogs/sdl_widget.cpp b/client/SDL/SDL3/dialogs/sdl_widget.cpp similarity index 97% rename from client/SDL3/dialogs/sdl_widget.cpp rename to client/SDL/SDL3/dialogs/sdl_widget.cpp index b7ffc5196..ee3c1f7d2 100644 --- a/client/SDL3/dialogs/sdl_widget.cpp +++ b/client/SDL/SDL3/dialogs/sdl_widget.cpp @@ -27,7 +27,7 @@ #include "sdl_widget.hpp" #include "../sdl_utils.hpp" -#include "res/sdl_resource_manager.hpp" +#include "res/sdl3_resource_manager.hpp" #include @@ -46,8 +46,8 @@ SdlWidget::SdlWidget(SDL_Renderer* renderer, const SDL_FRect& rect, bool input) { assert(renderer); - auto ops = SDLResourceManager::get(SDLResourceManager::typeFonts(), - "OpenSans-VariableFont_wdth,wght.ttf"); + auto ops = SDL3ResourceManager::get(SDLResourceManager::typeFonts(), + "OpenSans-VariableFont_wdth,wght.ttf"); if (!ops) widget_log_error(-1, "SDLResourceManager::get"); else diff --git a/client/SDL3/dialogs/sdl_widget.hpp b/client/SDL/SDL3/dialogs/sdl_widget.hpp similarity index 100% rename from client/SDL3/dialogs/sdl_widget.hpp rename to client/SDL/SDL3/dialogs/sdl_widget.hpp diff --git a/client/SDL3/dialogs/test/CMakeLists.txt b/client/SDL/SDL3/dialogs/test/CMakeLists.txt similarity index 100% rename from client/SDL3/dialogs/test/CMakeLists.txt rename to client/SDL/SDL3/dialogs/test/CMakeLists.txt diff --git a/client/SDL3/dialogs/test/TestSDLDialogs.cpp b/client/SDL/SDL3/dialogs/test/TestSDLDialogs.cpp similarity index 100% rename from client/SDL3/dialogs/test/TestSDLDialogs.cpp rename to client/SDL/SDL3/dialogs/test/TestSDLDialogs.cpp diff --git a/client/SDL3/man/CMakeLists.txt b/client/SDL/SDL3/man/CMakeLists.txt similarity index 64% rename from client/SDL3/man/CMakeLists.txt rename to client/SDL/SDL3/man/CMakeLists.txt index 2abdc94ee..0c228b25e 100644 --- a/client/SDL3/man/CMakeLists.txt +++ b/client/SDL/SDL3/man/CMakeLists.txt @@ -5,4 +5,4 @@ set(DEPS sdl3-freerdp-envvar.1.xml ) -generate_and_install_freerdp_man_from_xml(${PROJECT_NAME} "1" "${DEPS}") +generate_and_install_freerdp_man_from_xml(${MODULE_NAME} "1" "${DEPS}") diff --git a/client/SDL3/man/sdl3-freerdp-config.1.xml.in b/client/SDL/SDL3/man/sdl3-freerdp-config.1.xml.in similarity index 96% rename from client/SDL3/man/sdl3-freerdp-config.1.xml.in rename to client/SDL/SDL3/man/sdl3-freerdp-config.1.xml.in index 3bace734f..7db5ff8bf 100644 --- a/client/SDL3/man/sdl3-freerdp-config.1.xml.in +++ b/client/SDL/SDL3/man/sdl3-freerdp-config.1.xml.in @@ -8,7 +8,7 @@ The configuration file is stored per user. The XDG_CONFIG_HOME environment variable can be used to override the base directory. This defaults to ~/.config - The location relative to XDG_CONFIG_HOME is $XDG_CONFIG_HOME/@VENDOR@/@PRODUCT@/@PROJECT_NAME@.json + The location relative to XDG_CONFIG_HOME is $XDG_CONFIG_HOME/@VENDOR@/@PRODUCT@/@MODULE_NAME@.json The configuration is stored in JSON format diff --git a/client/SDL3/man/sdl3-freerdp-envvar.1.xml.in b/client/SDL/SDL3/man/sdl3-freerdp-envvar.1.xml.in similarity index 100% rename from client/SDL3/man/sdl3-freerdp-envvar.1.xml.in rename to client/SDL/SDL3/man/sdl3-freerdp-envvar.1.xml.in diff --git a/client/SDL3/man/sdl3-freerdp-examples.1.xml.in b/client/SDL/SDL3/man/sdl3-freerdp-examples.1.xml.in similarity index 100% rename from client/SDL3/man/sdl3-freerdp-examples.1.xml.in rename to client/SDL/SDL3/man/sdl3-freerdp-examples.1.xml.in diff --git a/client/SDL3/man/sdl3-freerdp.1.xml.in b/client/SDL/SDL3/man/sdl3-freerdp.1.xml.in similarity index 100% rename from client/SDL3/man/sdl3-freerdp.1.xml.in rename to client/SDL/SDL3/man/sdl3-freerdp.1.xml.in diff --git a/client/SDL3/sdl_channels.cpp b/client/SDL/SDL3/sdl_channels.cpp similarity index 100% rename from client/SDL3/sdl_channels.cpp rename to client/SDL/SDL3/sdl_channels.cpp diff --git a/client/SDL3/sdl_channels.hpp b/client/SDL/SDL3/sdl_channels.hpp similarity index 100% rename from client/SDL3/sdl_channels.hpp rename to client/SDL/SDL3/sdl_channels.hpp diff --git a/client/SDL3/sdl_clip.cpp b/client/SDL/SDL3/sdl_clip.cpp similarity index 100% rename from client/SDL3/sdl_clip.cpp rename to client/SDL/SDL3/sdl_clip.cpp diff --git a/client/SDL3/sdl_clip.hpp b/client/SDL/SDL3/sdl_clip.hpp similarity index 100% rename from client/SDL3/sdl_clip.hpp rename to client/SDL/SDL3/sdl_clip.hpp diff --git a/client/SDL3/sdl_disp.cpp b/client/SDL/SDL3/sdl_disp.cpp similarity index 100% rename from client/SDL3/sdl_disp.cpp rename to client/SDL/SDL3/sdl_disp.cpp diff --git a/client/SDL3/sdl_disp.hpp b/client/SDL/SDL3/sdl_disp.hpp similarity index 100% rename from client/SDL3/sdl_disp.hpp rename to client/SDL/SDL3/sdl_disp.hpp diff --git a/client/SDL3/sdl_freerdp.cpp b/client/SDL/SDL3/sdl_freerdp.cpp similarity index 99% rename from client/SDL3/sdl_freerdp.cpp rename to client/SDL/SDL3/sdl_freerdp.cpp index 9aa06a99f..f0dabbaef 100644 --- a/client/SDL3/sdl_freerdp.cpp +++ b/client/SDL/SDL3/sdl_freerdp.cpp @@ -60,7 +60,7 @@ #include "sdl_prefs.hpp" #include "dialogs/sdl_dialogs.hpp" -#include "aad/sdl_webview.hpp" +#include #define SDL_TAG CLIENT_TAG("SDL") diff --git a/client/SDL3/sdl_freerdp.hpp b/client/SDL/SDL3/sdl_freerdp.hpp similarity index 100% rename from client/SDL3/sdl_freerdp.hpp rename to client/SDL/SDL3/sdl_freerdp.hpp diff --git a/client/SDL3/sdl_kbd.cpp b/client/SDL/SDL3/sdl_kbd.cpp similarity index 100% rename from client/SDL3/sdl_kbd.cpp rename to client/SDL/SDL3/sdl_kbd.cpp diff --git a/client/SDL3/sdl_kbd.hpp b/client/SDL/SDL3/sdl_kbd.hpp similarity index 100% rename from client/SDL3/sdl_kbd.hpp rename to client/SDL/SDL3/sdl_kbd.hpp diff --git a/client/SDL3/sdl_monitor.cpp b/client/SDL/SDL3/sdl_monitor.cpp similarity index 100% rename from client/SDL3/sdl_monitor.cpp rename to client/SDL/SDL3/sdl_monitor.cpp diff --git a/client/SDL3/sdl_monitor.hpp b/client/SDL/SDL3/sdl_monitor.hpp similarity index 100% rename from client/SDL3/sdl_monitor.hpp rename to client/SDL/SDL3/sdl_monitor.hpp diff --git a/client/SDL3/sdl_pointer.cpp b/client/SDL/SDL3/sdl_pointer.cpp similarity index 100% rename from client/SDL3/sdl_pointer.cpp rename to client/SDL/SDL3/sdl_pointer.cpp diff --git a/client/SDL3/sdl_pointer.hpp b/client/SDL/SDL3/sdl_pointer.hpp similarity index 100% rename from client/SDL3/sdl_pointer.hpp rename to client/SDL/SDL3/sdl_pointer.hpp diff --git a/client/SDL3/sdl_touch.cpp b/client/SDL/SDL3/sdl_touch.cpp similarity index 100% rename from client/SDL3/sdl_touch.cpp rename to client/SDL/SDL3/sdl_touch.cpp diff --git a/client/SDL3/sdl_touch.hpp b/client/SDL/SDL3/sdl_touch.hpp similarity index 100% rename from client/SDL3/sdl_touch.hpp rename to client/SDL/SDL3/sdl_touch.hpp diff --git a/client/SDL3/sdl_types.hpp b/client/SDL/SDL3/sdl_types.hpp similarity index 100% rename from client/SDL3/sdl_types.hpp rename to client/SDL/SDL3/sdl_types.hpp diff --git a/client/SDL3/sdl_utils.cpp b/client/SDL/SDL3/sdl_utils.cpp similarity index 100% rename from client/SDL3/sdl_utils.cpp rename to client/SDL/SDL3/sdl_utils.cpp diff --git a/client/SDL3/sdl_utils.hpp b/client/SDL/SDL3/sdl_utils.hpp similarity index 100% rename from client/SDL3/sdl_utils.hpp rename to client/SDL/SDL3/sdl_utils.hpp diff --git a/client/SDL3/sdl_window.cpp b/client/SDL/SDL3/sdl_window.cpp similarity index 100% rename from client/SDL3/sdl_window.cpp rename to client/SDL/SDL3/sdl_window.cpp diff --git a/client/SDL3/sdl_window.hpp b/client/SDL/SDL3/sdl_window.hpp similarity index 100% rename from client/SDL3/sdl_window.hpp rename to client/SDL/SDL3/sdl_window.hpp diff --git a/client/SDL/common/CMakeLists.txt b/client/SDL/common/CMakeLists.txt new file mode 100644 index 000000000..9102aaa1a --- /dev/null +++ b/client/SDL/common/CMakeLists.txt @@ -0,0 +1,30 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP SDL Client +# +# Copyright 2024 Armin Novak +# Copyright 2024 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_subdirectory(aad) +add_subdirectory(res) + +add_library(sdl-common-prefs STATIC + sdl_prefs.hpp + sdl_prefs.cpp +) +target_link_libraries(sdl-common-prefs winpr) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/client/SDL2/aad/CMakeLists.txt b/client/SDL/common/aad/CMakeLists.txt similarity index 59% rename from client/SDL2/aad/CMakeLists.txt rename to client/SDL/common/aad/CMakeLists.txt index 1dcc978a1..4ddc60f13 100644 --- a/client/SDL2/aad/CMakeLists.txt +++ b/client/SDL/common/aad/CMakeLists.txt @@ -1,3 +1,20 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP SDL Client +# +# Copyright 2024 Armin Novak +# Copyright 2024 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. set(WITH_WEBVIEW_DEFAULT OFF) if (UNIX AND NOT APPLE) set(WITH_WEBVIEW_DEFAULT ON) @@ -58,17 +75,18 @@ else() endif() configure_file(sdl_config.hpp.in sdl_config.hpp @ONLY) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) -add_library(sdl2-aad-view STATIC +add_library(sdl-common-aad-view STATIC ${SRCS} ) -target_include_directories(sdl2-aad-view PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) -target_link_libraries(sdl2-aad-view +target_include_directories(sdl-common-aad-view PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(sdl-common-aad-view PRIVATE ${LIBS} ) target_compile_definitions( - sdl2-aad-view + sdl-common-aad-view PUBLIC ${DEFINITIONS} ) diff --git a/client/SDL2/aad/dummy.cpp b/client/SDL/common/aad/dummy.cpp similarity index 100% rename from client/SDL2/aad/dummy.cpp rename to client/SDL/common/aad/dummy.cpp diff --git a/client/SDL2/aad/qt/webview_impl.cpp b/client/SDL/common/aad/qt/webview_impl.cpp similarity index 100% rename from client/SDL2/aad/qt/webview_impl.cpp rename to client/SDL/common/aad/qt/webview_impl.cpp diff --git a/client/SDL2/aad/sdl_config.hpp.in b/client/SDL/common/aad/sdl_config.hpp.in similarity index 100% rename from client/SDL2/aad/sdl_config.hpp.in rename to client/SDL/common/aad/sdl_config.hpp.in diff --git a/client/SDL2/aad/sdl_webview.cpp b/client/SDL/common/aad/sdl_webview.cpp similarity index 100% rename from client/SDL2/aad/sdl_webview.cpp rename to client/SDL/common/aad/sdl_webview.cpp diff --git a/client/SDL2/aad/sdl_webview.hpp b/client/SDL/common/aad/sdl_webview.hpp similarity index 100% rename from client/SDL2/aad/sdl_webview.hpp rename to client/SDL/common/aad/sdl_webview.hpp diff --git a/client/SDL2/aad/webview_impl.hpp b/client/SDL/common/aad/webview_impl.hpp similarity index 100% rename from client/SDL2/aad/webview_impl.hpp rename to client/SDL/common/aad/webview_impl.hpp diff --git a/client/SDL2/aad/wrapper/README b/client/SDL/common/aad/wrapper/README similarity index 100% rename from client/SDL2/aad/wrapper/README rename to client/SDL/common/aad/wrapper/README diff --git a/client/SDL2/aad/wrapper/webview.h b/client/SDL/common/aad/wrapper/webview.h similarity index 100% rename from client/SDL2/aad/wrapper/webview.h rename to client/SDL/common/aad/wrapper/webview.h diff --git a/client/SDL2/aad/wrapper/webview_impl.cpp b/client/SDL/common/aad/wrapper/webview_impl.cpp similarity index 100% rename from client/SDL2/aad/wrapper/webview_impl.cpp rename to client/SDL/common/aad/wrapper/webview_impl.cpp diff --git a/client/SDL/common/res/CMakeLists.txt b/client/SDL/common/res/CMakeLists.txt new file mode 100644 index 000000000..ec7e9c787 --- /dev/null +++ b/client/SDL/common/res/CMakeLists.txt @@ -0,0 +1,105 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP SDL Client +# +# Copyright 2024 Armin Novak +# Copyright 2024 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +add_executable(sdl-common-res2bin + convert_res_to_c.cpp +) + +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 $ ${FILE} ${FILE_TYPE} ${TARGET_NAME} ${FILE_BIN_DIR} + COMMENT "create image resources" + DEPENDS sdl-common-res2bin + DEPENDS ${FILE} + ) +endmacro() + +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 +) + +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(sdl-common-client-res OBJECT + ${RES_FILES} + ${SRCS} + ${FACTORY_SRCS} +) +set_property(TARGET sdl-common-client-res PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/SDL2/dialogs/res/convert_res_to_c.cpp b/client/SDL/common/res/convert_res_to_c.cpp similarity index 100% rename from client/SDL2/dialogs/res/convert_res_to_c.cpp rename to client/SDL/common/res/convert_res_to_c.cpp diff --git a/client/SDL2/dialogs/res/sdl_resource_file.cpp b/client/SDL/common/res/sdl_resource_file.cpp similarity index 100% rename from client/SDL2/dialogs/res/sdl_resource_file.cpp rename to client/SDL/common/res/sdl_resource_file.cpp diff --git a/client/SDL2/dialogs/res/sdl_resource_file.hpp b/client/SDL/common/res/sdl_resource_file.hpp similarity index 100% rename from client/SDL2/dialogs/res/sdl_resource_file.hpp rename to client/SDL/common/res/sdl_resource_file.hpp diff --git a/client/SDL3/dialogs/res/sdl_resource_manager.cpp b/client/SDL/common/res/sdl_resource_manager.cpp similarity index 78% rename from client/SDL3/dialogs/res/sdl_resource_manager.cpp rename to client/SDL/common/res/sdl_resource_manager.cpp index 4bc54e6a2..f036cf6c3 100644 --- a/client/SDL3/dialogs/res/sdl_resource_manager.cpp +++ b/client/SDL/common/res/sdl_resource_manager.cpp @@ -27,32 +27,6 @@ namespace fs = std::experimental::filesystem; #error Could not find system header "" or "" #endif -SDL_IOStream* 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_IOFromConstMem(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_IOFromFile(path.u8string().c_str(), "rb"); -#endif -} - const std::string SDLResourceManager::typeFonts() { return "fonts"; @@ -70,6 +44,51 @@ void SDLResourceManager::insert(const std::string& type, const std::string& id, resources().emplace(uuid, data); } +bool SDLResourceManager::useCompiledResources() +{ +#if defined(SDL_USE_COMPILED_RESOURCES) + return true; +#else + return false; +#endif +} + +const std::vector* SDLResourceManager::data(const std::string& type, + const std::string& id) +{ +#if defined(SDL_USE_COMPILED_RESOURCES) + std::string uuid = type + "/" + id; + auto val = resources().find(uuid); + if (val == resources().end()) + return nullptr; + + return &val->second; +#else + return nullptr; +#endif +} + +std::string SDLResourceManager::filename(const std::string& type, const std::string& id) +{ +#if defined(SDL_RESOURCE_ROOT) + std::string uuid = type + "/" + id; + 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 ""; + } + return path.u8string(); +#else + return ""; +#endif +} + std::map>& SDLResourceManager::resources() { diff --git a/client/SDL2/dialogs/res/sdl_resource_manager.hpp b/client/SDL/common/res/sdl_resource_manager.hpp similarity index 86% rename from client/SDL2/dialogs/res/sdl_resource_manager.hpp rename to client/SDL/common/res/sdl_resource_manager.hpp index b4f463c21..612d08aac 100644 --- a/client/SDL2/dialogs/res/sdl_resource_manager.hpp +++ b/client/SDL/common/res/sdl_resource_manager.hpp @@ -20,15 +20,12 @@ #include #include #include -#include 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(); @@ -36,6 +33,11 @@ class SDLResourceManager static void insert(const std::string& type, const std::string& id, const std::vector& data); + static const std::vector* data(const std::string& type, const std::string& id); + static std::string filename(const std::string& type, const std::string& id); + + static bool useCompiledResources(); + private: SDLResourceManager() = delete; SDLResourceManager(const SDLResourceManager& other) = delete; diff --git a/client/SDL2/sdl_prefs.cpp b/client/SDL/common/sdl_prefs.cpp similarity index 100% rename from client/SDL2/sdl_prefs.cpp rename to client/SDL/common/sdl_prefs.cpp diff --git a/client/SDL2/sdl_prefs.hpp b/client/SDL/common/sdl_prefs.hpp similarity index 100% rename from client/SDL2/sdl_prefs.hpp rename to client/SDL/common/sdl_prefs.hpp diff --git a/client/SDL2/test/CMakeLists.txt b/client/SDL/common/test/CMakeLists.txt similarity index 94% rename from client/SDL2/test/CMakeLists.txt rename to client/SDL/common/test/CMakeLists.txt index 9c7037542..020271d61 100644 --- a/client/SDL2/test/CMakeLists.txt +++ b/client/SDL/common/test/CMakeLists.txt @@ -11,7 +11,7 @@ create_test_sourcelist(${MODULE_PREFIX}_SRCS add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) -set(${MODULE_PREFIX}_LIBS freerdp winpr sdl2-prefs) +set(${MODULE_PREFIX}_LIBS freerdp winpr sdl-common-prefs) target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) diff --git a/client/SDL2/test/TestSDLPrefs.cpp b/client/SDL/common/test/TestSDLPrefs.cpp similarity index 100% rename from client/SDL2/test/TestSDLPrefs.cpp rename to client/SDL/common/test/TestSDLPrefs.cpp diff --git a/client/SDL2/test/sdl-freerdp.json b/client/SDL/common/test/sdl-freerdp.json similarity index 100% rename from client/SDL2/test/sdl-freerdp.json rename to client/SDL/common/test/sdl-freerdp.json diff --git a/client/SDL2/CMakeLists.txt b/client/SDL2/CMakeLists.txt deleted file mode 100644 index 994386fce..000000000 --- a/client/SDL2/CMakeLists.txt +++ /dev/null @@ -1,133 +0,0 @@ -# FreeRDP: A Remote Desktop Protocol Implementation -# FreeRDP SDL Client -# -# Copyright 2022 Armin Novak -# -# 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(sdl2-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(sdl2-prefs STATIC - sdl_prefs.hpp - sdl_prefs.cpp -) - -add_subdirectory(aad) -list(APPEND LIBS - winpr - freerdp - freerdp-client - Threads::Threads - sdl2_client_res - sdl2-dialogs - sdl2-aad-view - sdl2-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(sdl2-prefs winpr) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER "Client/SDL") - -if (NOT WITH_CLIENT_SDL_VERSIONED) - set_target_properties(${PROJECT_NAME} - PROPERTIES - OUTPUT_NAME "sdl-freerdp" - ) -endif() - -install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT client) - -add_subdirectory(man) - -if(BUILD_TESTING) - add_subdirectory(test) -endif() diff --git a/client/SDL2/dialogs/res/CMakeLists.txt b/client/SDL2/dialogs/res/CMakeLists.txt deleted file mode 100644 index 33ca0e1cd..000000000 --- a/client/SDL2/dialogs/res/CMakeLists.txt +++ /dev/null @@ -1,89 +0,0 @@ - -add_executable(sdl2-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 $ ${FILE} ${FILE_TYPE} ${TARGET_NAME} ${FILE_BIN_DIR} - COMMENT "create image resources" - DEPENDS sdl2-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(sdl2_client_res OBJECT - ${RES_FILES} - ${SRCS} - ${FACTORY_SRCS} -) -set_property(TARGET sdl2_client_res PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/SDL2/dialogs/res/sdl_resource_manager.cpp b/client/SDL2/dialogs/res/sdl_resource_manager.cpp deleted file mode 100644 index 0f0682a8f..000000000 --- a/client/SDL2/dialogs/res/sdl_resource_manager.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * - * Copyright 2023 Armin Novak - * - * 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 -#if __has_include() -#include -namespace fs = std::filesystem; -#elif __has_include() -#include -namespace fs = std::experimental::filesystem; -#else -#error Could not find system header "" or "" -#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& data) -{ - std::string uuid = type + "/" + id; - resources().emplace(uuid, data); -} - -std::map>& SDLResourceManager::resources() -{ - - static std::map> resources = {}; - return resources; -} diff --git a/client/SDL3/aad/CMakeLists.txt b/client/SDL3/aad/CMakeLists.txt deleted file mode 100644 index 09ce3d39a..000000000 --- a/client/SDL3/aad/CMakeLists.txt +++ /dev/null @@ -1,75 +0,0 @@ -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} -) - diff --git a/client/SDL3/aad/dummy.cpp b/client/SDL3/aad/dummy.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/client/SDL3/aad/qt/webview_impl.cpp b/client/SDL3/aad/qt/webview_impl.cpp deleted file mode 100644 index e70cc46ab..000000000 --- a/client/SDL3/aad/qt/webview_impl.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * Popup browser for AAD authentication - * - * Copyright 2023 Isaac Klein - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#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(); -} diff --git a/client/SDL3/aad/sdl_config.hpp.in b/client/SDL3/aad/sdl_config.hpp.in deleted file mode 100644 index 34d075180..000000000 --- a/client/SDL3/aad/sdl_config.hpp.in +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#cmakedefine WITH_WEBVIEW diff --git a/client/SDL3/aad/sdl_webview.cpp b/client/SDL3/aad/sdl_webview.cpp deleted file mode 100644 index b4df75b8f..000000000 --- a/client/SDL3/aad/sdl_webview.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * Popup browser for AAD authentication - * - * Copyright 2023 Isaac Klein - * - * 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 -#include -#include -#include -#include - -#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; - } -} diff --git a/client/SDL3/aad/webview_impl.hpp b/client/SDL3/aad/webview_impl.hpp deleted file mode 100644 index 25bca3c0a..000000000 --- a/client/SDL3/aad/webview_impl.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * Popup browser for AAD authentication - * - * Copyright 2023 Isaac Klein - * - * 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 - -bool webview_impl_run(const std::string& title, const std::string& url, std::string& code); diff --git a/client/SDL3/aad/wrapper/README b/client/SDL3/aad/wrapper/README deleted file mode 100644 index da906bac6..000000000 --- a/client/SDL3/aad/wrapper/README +++ /dev/null @@ -1 +0,0 @@ -upstream at https://github.com/webview/webview/ diff --git a/client/SDL3/aad/wrapper/webview.h b/client/SDL3/aad/wrapper/webview.h deleted file mode 100644 index 91cabf61e..000000000 --- a/client/SDL3/aad/wrapper/webview.h +++ /dev/null @@ -1,2797 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2017 Serge Zaitsev - * Copyright (c) 2022 Steffen André Langnes - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef WEBVIEW_H -#define WEBVIEW_H - -#ifndef WEBVIEW_API -#define WEBVIEW_API extern -#endif - -#ifndef WEBVIEW_VERSION_MAJOR -// The current library major version. -#define WEBVIEW_VERSION_MAJOR 0 -#endif - -#ifndef WEBVIEW_VERSION_MINOR -// The current library minor version. -#define WEBVIEW_VERSION_MINOR 10 -#endif - -#ifndef WEBVIEW_VERSION_PATCH -// The current library patch version. -#define WEBVIEW_VERSION_PATCH 0 -#endif - -#ifndef WEBVIEW_VERSION_PRE_RELEASE -// SemVer 2.0.0 pre-release labels prefixed with "-". -#define WEBVIEW_VERSION_PRE_RELEASE "" -#endif - -#ifndef WEBVIEW_VERSION_BUILD_METADATA -// SemVer 2.0.0 build metadata prefixed with "+". -#define WEBVIEW_VERSION_BUILD_METADATA "" -#endif - -// Utility macro for stringifying a macro argument. -#define WEBVIEW_STRINGIFY(x) #x - -// Utility macro for stringifying the result of a macro argument expansion. -#define WEBVIEW_EXPAND_AND_STRINGIFY(x) WEBVIEW_STRINGIFY(x) - -// SemVer 2.0.0 version number in MAJOR.MINOR.PATCH format. -#define WEBVIEW_VERSION_NUMBER \ - WEBVIEW_EXPAND_AND_STRINGIFY(WEBVIEW_VERSION_MAJOR) \ - "." WEBVIEW_EXPAND_AND_STRINGIFY(WEBVIEW_VERSION_MINOR) "." WEBVIEW_EXPAND_AND_STRINGIFY( \ - WEBVIEW_VERSION_PATCH) - -// Holds the elements of a MAJOR.MINOR.PATCH version number. -typedef struct -{ - // Major version. - unsigned int major; - // Minor version. - unsigned int minor; - // Patch version. - unsigned int patch; -} webview_version_t; - -// Holds the library's version information. -typedef struct -{ - // The elements of the version number. - webview_version_t version; - // SemVer 2.0.0 version number in MAJOR.MINOR.PATCH format. - char version_number[32]; - // SemVer 2.0.0 pre-release labels prefixed with "-" if specified, otherwise - // an empty string. - char pre_release[48]; - // SemVer 2.0.0 build metadata prefixed with "+", otherwise an empty string. - char build_metadata[48]; -} webview_version_info_t; - -#ifdef __cplusplus -extern "C" -{ -#endif - - typedef void* webview_t; - - // Creates a new webview instance. If debug is non-zero - developer tools will - // be enabled (if the platform supports them). Window parameter can be a - // pointer to the native window handle. If it's non-null - then child WebView - // is embedded into the given parent window. Otherwise a new window is created. - // Depending on the platform, a GtkWindow, NSWindow or HWND pointer can be - // passed here. Returns null on failure. Creation can fail for various reasons - // such as when required runtime dependencies are missing or when window creation - // fails. - WEBVIEW_API webview_t webview_create(int debug, void* window); - - // Destroys a webview and closes the native window. - WEBVIEW_API void webview_destroy(webview_t w); - - // Runs the main loop until it's terminated. After this function exits - you - // must destroy the webview. - WEBVIEW_API void webview_run(webview_t w); - - // Stops the main loop. It is safe to call this function from another other - // background thread. - WEBVIEW_API void webview_terminate(webview_t w); - - // Posts a function to be executed on the main thread. You normally do not need - // to call this function, unless you want to tweak the native window. - WEBVIEW_API void webview_dispatch(webview_t w, void (*fn)(webview_t w, void* arg), void* arg); - - // Returns a native window handle pointer. When using GTK backend the pointer - // is GtkWindow pointer, when using Cocoa backend the pointer is NSWindow - // pointer, when using Win32 backend the pointer is HWND pointer. - WEBVIEW_API void* webview_get_window(webview_t w); - - // Updates the title of the native window. Must be called from the UI thread. - WEBVIEW_API void webview_set_title(webview_t w, const char* title); - -// Window size hints -#define WEBVIEW_HINT_NONE 0 // Width and height are default size -#define WEBVIEW_HINT_MIN 1 // Width and height are minimum bounds -#define WEBVIEW_HINT_MAX 2 // Width and height are maximum bounds -#define WEBVIEW_HINT_FIXED 3 // Window size can not be changed by a user - // Updates native window size. See WEBVIEW_HINT constants. - WEBVIEW_API void webview_set_size(webview_t w, int width, int height, int hints); - - // Navigates webview to the given URL. URL may be a properly encoded data URI. - // Examples: - // webview_navigate(w, "https://github.com/webview/webview"); - // webview_navigate(w, "data:text/html,%3Ch1%3EHello%3C%2Fh1%3E"); - // webview_navigate(w, "data:text/html;base64,PGgxPkhlbGxvPC9oMT4="); - WEBVIEW_API void webview_navigate(webview_t w, const char* url); - - // Set webview HTML directly. - // Example: webview_set_html(w, "

Hello

"); - WEBVIEW_API void webview_set_html(webview_t w, const char* html); - - // Injects JavaScript code at the initialization of the new page. Every time - // the webview will open a the new page - this initialization code will be - // executed. It is guaranteed that code is executed before window.onload. - WEBVIEW_API void webview_init(webview_t w, const char* js); - - // Evaluates arbitrary JavaScript code. Evaluation happens asynchronously, also - // the result of the expression is ignored. Use RPC bindings if you want to - // receive notifications about the results of the evaluation. - WEBVIEW_API void webview_eval(webview_t w, const char* js); - - // Binds a native C callback so that it will appear under the given name as a - // global JavaScript function. Internally it uses webview_init(). Callback - // receives a request string and a user-provided argument pointer. Request - // string is a JSON array of all the arguments passed to the JavaScript - // function. - WEBVIEW_API void webview_bind(webview_t w, const char* name, - void (*fn)(const char* seq, const char* req, void* arg), - void* arg); - - // Removes a native C callback that was previously set by webview_bind. - WEBVIEW_API void webview_unbind(webview_t w, const char* name); - - // Allows to return a value from the native binding. Original request pointer - // must be provided to help internal RPC engine match requests with responses. - // If status is zero - result is expected to be a valid JSON result value. - // If status is not zero - result is an error JSON object. - WEBVIEW_API void webview_return(webview_t w, const char* seq, int status, const char* result); - - // Get the library's version information. - // @since 0.10 - WEBVIEW_API const webview_version_info_t* webview_version(); - -#ifdef __cplusplus -} - -#ifndef WEBVIEW_HEADER - -#if !defined(WEBVIEW_GTK) && !defined(WEBVIEW_COCOA) && !defined(WEBVIEW_EDGE) -#if defined(__APPLE__) -#define WEBVIEW_COCOA -#elif defined(__unix__) -#define WEBVIEW_GTK -#elif defined(_WIN32) -#define WEBVIEW_EDGE -#else -#error "please, specify webview backend" -#endif -#endif - -#ifndef WEBVIEW_DEPRECATED -#if __cplusplus >= 201402L -#define WEBVIEW_DEPRECATED(reason) [[deprecated(reason)]] -#elif defined(_MSC_VER) -#define WEBVIEW_DEPRECATED(reason) __declspec(deprecated(reason)) -#else -#define WEBVIEW_DEPRECATED(reason) __attribute__((deprecated(reason))) -#endif -#endif - -#ifndef WEBVIEW_DEPRECATED_PRIVATE -#define WEBVIEW_DEPRECATED_PRIVATE WEBVIEW_DEPRECATED("Private API should not be used") -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace webview -{ - - using dispatch_fn_t = std::function; - - namespace detail - { - - // The library's version information. - constexpr const webview_version_info_t library_version_info{ - { WEBVIEW_VERSION_MAJOR, WEBVIEW_VERSION_MINOR, WEBVIEW_VERSION_PATCH }, - WEBVIEW_VERSION_NUMBER, - WEBVIEW_VERSION_PRE_RELEASE, - WEBVIEW_VERSION_BUILD_METADATA - }; - - inline int json_parse_c(const char* s, size_t sz, const char* key, size_t keysz, - const char** value, size_t* valuesz) - { - enum - { - JSON_STATE_VALUE, - JSON_STATE_LITERAL, - JSON_STATE_STRING, - JSON_STATE_ESCAPE, - JSON_STATE_UTF8 - } state = JSON_STATE_VALUE; - const char* k = nullptr; - int index = 1; - int depth = 0; - int utf8_bytes = 0; - - *value = nullptr; - *valuesz = 0; - - if (key == nullptr) - { - index = static_cast(keysz); - if (index < 0) - { - return -1; - } - keysz = 0; - } - - for (; sz > 0; s++, sz--) - { - enum - { - JSON_ACTION_NONE, - JSON_ACTION_START, - JSON_ACTION_END, - JSON_ACTION_START_STRUCT, - JSON_ACTION_END_STRUCT - } action = JSON_ACTION_NONE; - auto c = static_cast(*s); - switch (state) - { - case JSON_STATE_VALUE: - if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || c == ':') - { - continue; - } - else if (c == '"') - { - action = JSON_ACTION_START; - state = JSON_STATE_STRING; - } - else if (c == '{' || c == '[') - { - action = JSON_ACTION_START_STRUCT; - } - else if (c == '}' || c == ']') - { - action = JSON_ACTION_END_STRUCT; - } - else if (c == 't' || c == 'f' || c == 'n' || c == '-' || - (c >= '0' && c <= '9')) - { - action = JSON_ACTION_START; - state = JSON_STATE_LITERAL; - } - else - { - return -1; - } - break; - case JSON_STATE_LITERAL: - if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || - c == ']' || c == '}' || c == ':') - { - state = JSON_STATE_VALUE; - s--; - sz++; - action = JSON_ACTION_END; - } - else if (c < 32 || c > 126) - { - return -1; - } // fallthrough - case JSON_STATE_STRING: - if (c < 32 || (c > 126 && c < 192)) - { - return -1; - } - else if (c == '"') - { - action = JSON_ACTION_END; - state = JSON_STATE_VALUE; - } - else if (c == '\\') - { - state = JSON_STATE_ESCAPE; - } - else if (c >= 192 && c < 224) - { - utf8_bytes = 1; - state = JSON_STATE_UTF8; - } - else if (c >= 224 && c < 240) - { - utf8_bytes = 2; - state = JSON_STATE_UTF8; - } - else if (c >= 240 && c < 247) - { - utf8_bytes = 3; - state = JSON_STATE_UTF8; - } - else if (c >= 128 && c < 192) - { - return -1; - } - break; - case JSON_STATE_ESCAPE: - if (c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' || c == 'n' || - c == 'r' || c == 't' || c == 'u') - { - state = JSON_STATE_STRING; - } - else - { - return -1; - } - break; - case JSON_STATE_UTF8: - if (c < 128 || c > 191) - { - return -1; - } - utf8_bytes--; - if (utf8_bytes == 0) - { - state = JSON_STATE_STRING; - } - break; - default: - return -1; - } - - if (action == JSON_ACTION_END_STRUCT) - { - depth--; - } - - if (depth == 1) - { - if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT) - { - if (index == 0) - { - *value = s; - } - else if (keysz > 0 && index == 1) - { - k = s; - } - else - { - index--; - } - } - else if (action == JSON_ACTION_END || action == JSON_ACTION_END_STRUCT) - { - if (*value != nullptr && index == 0) - { - *valuesz = (size_t)(s + 1 - *value); - return 0; - } - else if (keysz > 0 && k != nullptr) - { - if (keysz == (size_t)(s - k - 1) && memcmp(key, k + 1, keysz) == 0) - { - index = 0; - } - else - { - index = 2; - } - k = nullptr; - } - } - } - - if (action == JSON_ACTION_START_STRUCT) - { - depth++; - } - } - return -1; - } - - inline std::string json_escape(const std::string& s) - { - // TODO: implement - return '"' + s + '"'; - } - - inline int json_unescape(const char* s, size_t n, char* out) - { - int r = 0; - if (*s++ != '"') - { - return -1; - } - while (n > 2) - { - char c = *s; - if (c == '\\') - { - s++; - n--; - switch (*s) - { - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case '\\': - c = '\\'; - break; - case '/': - c = '/'; - break; - case '\"': - c = '\"'; - break; - default: // TODO: support unicode decoding - return -1; - } - } - if (out != nullptr) - { - *out++ = c; - } - s++; - n--; - r++; - } - if (*s != '"') - { - return -1; - } - if (out != nullptr) - { - *out = '\0'; - } - return r; - } - - inline std::string json_parse(const std::string& s, const std::string& key, const int index) - { - const char* value = nullptr; - size_t value_sz = 0; - if (key.empty()) - { - json_parse_c(s.c_str(), s.length(), nullptr, index, &value, &value_sz); - } - else - { - json_parse_c(s.c_str(), s.length(), key.c_str(), key.length(), &value, &value_sz); - } - if (value != nullptr) - { - if (value[0] != '"') - { - return { value, value_sz }; - } - int n = json_unescape(value, value_sz, nullptr); - if (n > 0) - { - char* decoded = new char[n + 1]; - json_unescape(value, value_sz, decoded); - std::string result(decoded, n); - delete[] decoded; - return result; - } - } - return ""; - } - - } // namespace detail - - WEBVIEW_DEPRECATED_PRIVATE - inline int json_parse_c(const char* s, size_t sz, const char* key, size_t keysz, - const char** value, size_t* valuesz) - { - return detail::json_parse_c(s, sz, key, keysz, value, valuesz); - } - - WEBVIEW_DEPRECATED_PRIVATE - inline std::string json_escape(const std::string& s) - { - return detail::json_escape(s); - } - - WEBVIEW_DEPRECATED_PRIVATE - inline int json_unescape(const char* s, size_t n, char* out) - { - return detail::json_unescape(s, n, out); - } - - WEBVIEW_DEPRECATED_PRIVATE - inline std::string json_parse(const std::string& s, const std::string& key, const int index) - { - return detail::json_parse(s, key, index); - } - -} // namespace webview - -#if defined(WEBVIEW_GTK) -// -// ==================================================================== -// -// This implementation uses webkit2gtk backend. It requires gtk+3.0 and -// webkit2gtk-4.0 libraries. Proper compiler flags can be retrieved via: -// -// pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.0 -// -// ==================================================================== -// -#include -#include -#include - -namespace webview -{ - namespace detail - { - - class gtk_webkit_engine - { - public: - gtk_webkit_engine(bool debug, void* window) : m_window(static_cast(window)) - { - if (gtk_init_check(nullptr, nullptr) == FALSE) - { - return; - } - m_window = static_cast(window); - if (m_window == nullptr) - { - m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - } - g_signal_connect(G_OBJECT(m_window), "destroy", - G_CALLBACK(+[](GtkWidget*, gpointer arg) - { static_cast(arg)->terminate(); }), - this); - // Initialize webview widget - m_webview = webkit_web_view_new(); - WebKitUserContentManager* manager = - 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(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." - "external.postMessage(s);}}"); - - gtk_container_add(GTK_CONTAINER(m_window), GTK_WIDGET(m_webview)); - gtk_widget_grab_focus(GTK_WIDGET(m_webview)); - - WebKitSettings* settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_webview)); - webkit_settings_set_javascript_can_access_clipboard(settings, true); - if (debug) - { - webkit_settings_set_enable_write_console_messages_to_stdout(settings, true); - webkit_settings_set_enable_developer_extras(settings, true); - } - - gtk_widget_show_all(m_window); - } - virtual ~gtk_webkit_engine() = default; - void* window() - { - return (void*)m_window; - } - void run() - { - gtk_main(); - } - void terminate() - { - gtk_main_quit(); - } - void dispatch(std::function f) - { - g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)([](void* f) -> int { - (*static_cast(f))(); - return G_SOURCE_REMOVE; - }), - new std::function(f), - [](void* f) { delete static_cast(f); }); - } - - void set_title(const std::string& title) - { - gtk_window_set_title(GTK_WINDOW(m_window), title.c_str()); - } - - void set_size(int width, int height, int hints) - { - gtk_window_set_resizable(GTK_WINDOW(m_window), hints != WEBVIEW_HINT_FIXED); - if (hints == WEBVIEW_HINT_NONE) - { - gtk_window_resize(GTK_WINDOW(m_window), width, height); - } - else if (hints == WEBVIEW_HINT_FIXED) - { - gtk_widget_set_size_request(m_window, width, height); - } - else - { - GdkGeometry g; - g.min_width = g.max_width = width; - g.min_height = g.max_height = height; - GdkWindowHints h = - (hints == WEBVIEW_HINT_MIN ? GDK_HINT_MIN_SIZE : GDK_HINT_MAX_SIZE); - // This defines either MIN_SIZE, or MAX_SIZE, but not both: - gtk_window_set_geometry_hints(GTK_WINDOW(m_window), nullptr, &g, h); - } - } - - void navigate(const std::string& url) - { - webkit_web_view_load_uri(WEBKIT_WEB_VIEW(m_webview), url.c_str()); - } - - void add_navigate_listener(std::function callback, - void* arg) - { - g_signal_connect(WEBKIT_WEB_VIEW(m_webview), "load-changed", - G_CALLBACK(on_load_changed), this); - navigateCallbackArg = arg; - navigateCallback = std::move(callback); - } - - void add_scheme_handler(const std::string& scheme, - std::function callback, - void* arg) - { - auto view = WEBKIT_WEB_VIEW(m_webview); - auto context = webkit_web_view_get_context(view); - - scheme_handlers.insert({ scheme, { .arg = arg, .fkt = callback } }); - webkit_web_context_register_uri_scheme(context, scheme.c_str(), scheme_handler, - static_cast(this), nullptr); - } - - void set_html(const std::string& html) - { - webkit_web_view_load_html(WEBKIT_WEB_VIEW(m_webview), html.c_str(), nullptr); - } - - void init(const std::string& js) - { - WebKitUserContentManager* manager = - webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview)); - webkit_user_content_manager_add_script( - manager, webkit_user_script_new( - js.c_str(), WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, - WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START, nullptr, nullptr)); - } - - void eval(const std::string& js) - { - webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(m_webview), js.c_str(), nullptr, - nullptr, nullptr); - } - - private: - virtual void on_message(const std::string& msg) = 0; - - struct handler_t - { - void* arg; - std::function fkt; - }; - - std::map scheme_handlers; - - void scheme_handler_call(const std::string& scheme, const std::string& url) - { - auto handler = scheme_handlers.find(scheme); - if (handler != scheme_handlers.end()) - { - const auto& arg = handler->second; - arg.fkt(url, arg.arg); - } - } - - static void scheme_handler(WebKitURISchemeRequest* request, gpointer user_data) - { - auto _this = static_cast(user_data); - - auto scheme = webkit_uri_scheme_request_get_scheme(request); - auto uri = webkit_uri_scheme_request_get_uri(request); - _this->scheme_handler_call(scheme, uri); - } - - static char* get_string_from_js_result(WebKitJavascriptResult* r) - { - char* s = nullptr; -#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22 - JSCValue* value = webkit_javascript_result_get_js_value(r); - s = jsc_value_to_string(value); -#else - JSGlobalContextRef ctx = webkit_javascript_result_get_global_context(r); - JSValueRef value = webkit_javascript_result_get_value(r); - JSStringRef js = JSValueToStringCopy(ctx, value, nullptr); - size_t n = JSStringGetMaximumUTF8CStringSize(js); - s = g_new(char, n); - JSStringGetUTF8CString(js, s, n); - JSStringRelease(js); -#endif - return s; - } - - GtkWidget* m_window; - GtkWidget* m_webview; - - void* navigateCallbackArg = nullptr; - std::function navigateCallback = nullptr; - - static void on_load_changed(WebKitWebView* web_view, WebKitLoadEvent load_event, - gpointer arg) - { - if (load_event == WEBKIT_LOAD_FINISHED) - { - auto inst = static_cast(arg); - inst->navigateCallback(webkit_web_view_get_uri(web_view), - inst->navigateCallbackArg); - } - } - }; - - } // namespace detail - - using browser_engine = detail::gtk_webkit_engine; - -} // namespace webview - -#elif defined(WEBVIEW_COCOA) - -// -// ==================================================================== -// -// This implementation uses Cocoa WKWebView backend on macOS. It is -// written using ObjC runtime and uses WKWebView class as a browser runtime. -// You should pass "-framework Webkit" flag to the compiler. -// -// ==================================================================== -// - -#include -#include -#include - -namespace webview -{ - namespace detail - { - namespace objc - { - - // A convenient template function for unconditionally casting the specified - // C-like function into a function that can be called with the given return - // type and arguments. Caller takes full responsibility for ensuring that - // the function call is valid. It is assumed that the function will not - // throw exceptions. - template - Result invoke(Callable callable, Args... args) noexcept - { - return reinterpret_cast(callable)(args...); - } - - // Calls objc_msgSend. - template Result msg_send(Args... args) noexcept - { - return invoke(objc_msgSend, args...); - } - - } // namespace objc - - enum NSBackingStoreType : NSUInteger - { - NSBackingStoreBuffered = 2 - }; - - enum NSWindowStyleMask : NSUInteger - { - NSWindowStyleMaskTitled = 1, - NSWindowStyleMaskClosable = 2, - NSWindowStyleMaskMiniaturizable = 4, - NSWindowStyleMaskResizable = 8 - }; - - enum NSApplicationActivationPolicy : NSInteger - { - NSApplicationActivationPolicyRegular = 0 - }; - - enum WKUserScriptInjectionTime : NSInteger - { - WKUserScriptInjectionTimeAtDocumentStart = 0 - }; - - enum NSModalResponse : NSInteger - { - NSModalResponseOK = 1 - }; - - // Convenient conversion of string literals. - inline id operator"" _cls(const char* s, std::size_t) - { - return (id)objc_getClass(s); - } - inline SEL operator"" _sel(const char* s, std::size_t) - { - return sel_registerName(s); - } - inline id operator"" _str(const char* s, std::size_t) - { - return objc::msg_send("NSString"_cls, "stringWithUTF8String:"_sel, s); - } - - class cocoa_wkwebview_engine - { - public: - cocoa_wkwebview_engine(bool debug, void* window) - : m_debug{ debug }, m_parent_window{ window } - { - auto app = get_shared_application(); - auto delegate = create_app_delegate(); - objc_setAssociatedObject(delegate, "webview", (id)this, OBJC_ASSOCIATION_ASSIGN); - objc::msg_send(app, "setDelegate:"_sel, delegate); - - // See comments related to application lifecycle in create_app_delegate(). - if (window) - { - on_application_did_finish_launching(delegate, app); - } - else - { - // Start the main run loop so that the app delegate gets the - // NSApplicationDidFinishLaunchingNotification notification after the run - // loop has started in order to perform further initialization. - // We need to return from this constructor so this run loop is only - // temporary. - objc::msg_send(app, "run"_sel); - } - } - virtual ~cocoa_wkwebview_engine() = default; - void* window() - { - return (void*)m_window; - } - void terminate() - { - auto app = get_shared_application(); - objc::msg_send(app, "terminate:"_sel, nullptr); - } - void run() - { - auto app = get_shared_application(); - objc::msg_send(app, "run"_sel); - } - void dispatch(std::function f) - { - dispatch_async_f(dispatch_get_main_queue(), new dispatch_fn_t(f), - (dispatch_function_t)([](void* arg) { - auto f = static_cast(arg); - (*f)(); - delete f; - })); - } - void set_title(const std::string& title) - { - objc::msg_send( - m_window, "setTitle:"_sel, - objc::msg_send("NSString"_cls, "stringWithUTF8String:"_sel, title.c_str())); - } - void set_size(int width, int height, int hints) - { - auto style = static_cast(NSWindowStyleMaskTitled | - NSWindowStyleMaskClosable | - NSWindowStyleMaskMiniaturizable); - if (hints != WEBVIEW_HINT_FIXED) - { - style = static_cast(style | NSWindowStyleMaskResizable); - } - objc::msg_send(m_window, "setStyleMask:"_sel, style); - - if (hints == WEBVIEW_HINT_MIN) - { - objc::msg_send(m_window, "setContentMinSize:"_sel, - CGSizeMake(width, height)); - } - else if (hints == WEBVIEW_HINT_MAX) - { - objc::msg_send(m_window, "setContentMaxSize:"_sel, - CGSizeMake(width, height)); - } - else - { - objc::msg_send(m_window, "setFrame:display:animate:"_sel, - CGRectMake(0, 0, width, height), YES, NO); - } - objc::msg_send(m_window, "center"_sel); - } - void navigate(const std::string& url) - { - auto nsurl = objc::msg_send( - "NSURL"_cls, "URLWithString:"_sel, - objc::msg_send("NSString"_cls, "stringWithUTF8String:"_sel, url.c_str())); - - objc::msg_send( - m_webview, "loadRequest:"_sel, - objc::msg_send("NSURLRequest"_cls, "requestWithURL:"_sel, nsurl)); - } - - void add_navigate_listener(std::function callback, - void* arg) - { - m_navigateCallback = callback; - m_navigateCallbackArg = arg; - } - - void set_html(const std::string& html) - { - objc::msg_send( - m_webview, "loadHTMLString:baseURL:"_sel, - objc::msg_send("NSString"_cls, "stringWithUTF8String:"_sel, html.c_str()), - nullptr); - } - void init(const std::string& js) - { - // Equivalent Obj-C: - // [m_manager addUserScript:[[WKUserScript alloc] initWithSource:[NSString - // stringWithUTF8String:js.c_str()] - // injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]] - objc::msg_send( - m_manager, "addUserScript:"_sel, - objc::msg_send( - objc::msg_send("WKUserScript"_cls, "alloc"_sel), - "initWithSource:injectionTime:forMainFrameOnly:"_sel, - objc::msg_send("NSString"_cls, "stringWithUTF8String:"_sel, js.c_str()), - WKUserScriptInjectionTimeAtDocumentStart, YES)); - } - void eval(const std::string& js) - { - objc::msg_send( - m_webview, "evaluateJavaScript:completionHandler:"_sel, - objc::msg_send("NSString"_cls, "stringWithUTF8String:"_sel, js.c_str()), - nullptr); - } - - private: - virtual void on_message(const std::string& msg) = 0; - id create_app_delegate() - { - // Note: Avoid registering the class name "AppDelegate" as it is the - // default name in projects created with Xcode, and using the same name - // causes objc_registerClassPair to crash. - auto cls = - objc_allocateClassPair((Class) "NSResponder"_cls, "WebviewAppDelegate", 0); - class_addProtocol(cls, objc_getProtocol("NSTouchBarProvider")); - class_addMethod(cls, "applicationShouldTerminateAfterLastWindowClosed:"_sel, - (IMP)(+[](id, SEL, id) -> BOOL { return 1; }), "c@:@"); - // If the library was not initialized with an existing window then the user - // is likely managing the application lifecycle and we would not get the - // "applicationDidFinishLaunching:" message and therefore do not need to - // add this method. - if (!m_parent_window) - { - class_addMethod(cls, "applicationDidFinishLaunching:"_sel, - (IMP)(+[](id self, SEL, id notification) - { - auto app = - objc::msg_send(notification, "object"_sel); - auto w = get_associated_webview(self); - w->on_application_did_finish_launching(self, app); - }), - "v@:@"); - } - objc_registerClassPair(cls); - return objc::msg_send((id)cls, "new"_sel); - } - id create_script_message_handler() - { - 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( - objc::msg_send(msg, "body"_sel), "UTF8String"_sel)); - }), - "v@:@@"); - objc_registerClassPair(cls); - auto instance = objc::msg_send((id)cls, "new"_sel); - objc_setAssociatedObject(instance, "webview", (id)this, OBJC_ASSOCIATION_ASSIGN); - return instance; - } - static id create_webkit_ui_delegate() - { - auto cls = objc_allocateClassPair((Class) "NSObject"_cls, "WebkitUIDelegate", 0); - class_addProtocol(cls, objc_getProtocol("WKUIDelegate")); - 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(parameters, "allowsMultipleSelection"_sel); - auto allows_directories = - objc::msg_send(parameters, "allowsDirectories"_sel); - - // Show a panel for selecting files. - auto panel = objc::msg_send("NSOpenPanel"_cls, "openPanel"_sel); - objc::msg_send(panel, "setCanChooseFiles:"_sel, YES); - objc::msg_send(panel, "setCanChooseDirectories:"_sel, - allows_directories); - objc::msg_send(panel, "setAllowsMultipleSelection:"_sel, - allows_multiple_selection); - auto modal_response = - objc::msg_send(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(panel, "URLs"_sel) - : nullptr; - - // Invoke the completion handler block. - auto sig = objc::msg_send("NSMethodSignature"_cls, - "signatureWithObjCTypes:"_sel, "v@?@"); - auto invocation = objc::msg_send( - "NSInvocation"_cls, "invocationWithMethodSignature:"_sel, sig); - objc::msg_send(invocation, "setTarget:"_sel, - completion_handler); - objc::msg_send(invocation, "setArgument:atIndex:"_sel, &urls, - 1); - objc::msg_send(invocation, "invoke"_sel); - }), - "v@:@@@@"); - objc_registerClassPair(cls); - return objc::msg_send((id)cls, "new"_sel); - } - id create_webkit_navigation_delegate() - { - auto cls = - 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(webview, "URL"_sel); - auto nstr = objc::msg_send(url, "absoluteString"_sel); - auto str = objc::msg_send(nstr, "UTF8String"_sel); - w->m_navigateCallback(str, w->m_navigateCallbackArg); - }), - "v@:@"); - objc_registerClassPair(cls); - auto instance = objc::msg_send((id)cls, "new"_sel); - objc_setAssociatedObject(instance, "webview", (id)this, OBJC_ASSOCIATION_ASSIGN); - return instance; - } - static id get_shared_application() - { - return objc::msg_send("NSApplication"_cls, "sharedApplication"_sel); - } - static cocoa_wkwebview_engine* get_associated_webview(id object) - { - auto w = (cocoa_wkwebview_engine*)objc_getAssociatedObject(object, "webview"); - assert(w); - return w; - } - static id get_main_bundle() noexcept - { - return objc::msg_send("NSBundle"_cls, "mainBundle"_sel); - } - static bool is_app_bundled() noexcept - { - auto bundle = get_main_bundle(); - if (!bundle) - { - return false; - } - auto bundle_path = objc::msg_send(bundle, "bundlePath"_sel); - auto bundled = objc::msg_send(bundle_path, "hasSuffix:"_sel, ".app"_str); - return !!bundled; - } - void on_application_did_finish_launching(id /*delegate*/, id app) - { - // See comments related to application lifecycle in create_app_delegate(). - if (!m_parent_window) - { - // Stop the main run loop so that we can return - // from the constructor. - objc::msg_send(app, "stop:"_sel, nullptr); - } - - // Activate the app if it is not bundled. - // Bundled apps launched from Finder are activated automatically but - // otherwise not. Activating the app even when it has been launched from - // Finder does not seem to be harmful but calling this function is rarely - // needed as proper activation is normally taken care of for us. - // Bundled apps have a default activation policy of - // NSApplicationActivationPolicyRegular while non-bundled apps have a - // default activation policy of NSApplicationActivationPolicyProhibited. - if (!is_app_bundled()) - { - // "setActivationPolicy:" must be invoked before - // "activateIgnoringOtherApps:" for activation to work. - objc::msg_send(app, "setActivationPolicy:"_sel, - NSApplicationActivationPolicyRegular); - // Activate the app regardless of other active apps. - // This can be obtrusive so we only do it when necessary. - objc::msg_send(app, "activateIgnoringOtherApps:"_sel, YES); - } - - // Main window - if (!m_parent_window) - { - m_window = objc::msg_send("NSWindow"_cls, "alloc"_sel); - auto style = NSWindowStyleMaskTitled; - m_window = objc::msg_send( - m_window, "initWithContentRect:styleMask:backing:defer:"_sel, - CGRectMake(0, 0, 0, 0), style, NSBackingStoreBuffered, NO); - } - else - { - m_window = (id)m_parent_window; - } - - // Webview - auto config = objc::msg_send("WKWebViewConfiguration"_cls, "new"_sel); - m_manager = objc::msg_send(config, "userContentController"_sel); - m_webview = objc::msg_send("WKWebView"_cls, "alloc"_sel); - - if (m_debug) - { - // Equivalent Obj-C: - // [[config preferences] setValue:@YES forKey:@"developerExtrasEnabled"]; - objc::msg_send( - objc::msg_send(config, "preferences"_sel), "setValue:forKey:"_sel, - objc::msg_send("NSNumber"_cls, "numberWithBool:"_sel, YES), - "developerExtrasEnabled"_str); - } - - // Equivalent Obj-C: - // [[config preferences] setValue:@YES forKey:@"fullScreenEnabled"]; - objc::msg_send(objc::msg_send(config, "preferences"_sel), - "setValue:forKey:"_sel, - objc::msg_send("NSNumber"_cls, "numberWithBool:"_sel, YES), - "fullScreenEnabled"_str); - - // Equivalent Obj-C: - // [[config preferences] setValue:@YES forKey:@"javaScriptCanAccessClipboard"]; - objc::msg_send(objc::msg_send(config, "preferences"_sel), - "setValue:forKey:"_sel, - objc::msg_send("NSNumber"_cls, "numberWithBool:"_sel, YES), - "javaScriptCanAccessClipboard"_str); - - // Equivalent Obj-C: - // [[config preferences] setValue:@YES forKey:@"DOMPasteAllowed"]; - objc::msg_send(objc::msg_send(config, "preferences"_sel), - "setValue:forKey:"_sel, - objc::msg_send("NSNumber"_cls, "numberWithBool:"_sel, YES), - "DOMPasteAllowed"_str); - - auto ui_delegate = create_webkit_ui_delegate(); - objc::msg_send(m_webview, "initWithFrame:configuration:"_sel, - CGRectMake(0, 0, 0, 0), config); - objc::msg_send(m_webview, "setUIDelegate:"_sel, ui_delegate); - - auto navigation_delegate = create_webkit_navigation_delegate(); - objc::msg_send(m_webview, "setNavigationDelegate:"_sel, navigation_delegate); - auto script_message_handler = create_script_message_handler(); - objc::msg_send(m_manager, "addScriptMessageHandler:name:"_sel, - script_message_handler, "external"_str); - - init(R""( - window.external = { - invoke: function(s) { - window.webkit.messageHandlers.external.postMessage(s); - }, - }; - )""); - objc::msg_send(m_window, "setContentView:"_sel, m_webview); - objc::msg_send(m_window, "makeKeyAndOrderFront:"_sel, nullptr); - } - bool m_debug; - void* m_parent_window; - id m_window; - id m_webview; - id m_manager; - void* m_navigateCallbackArg = nullptr; - std::function m_navigateCallback = 0; - }; - - } // namespace detail - - using browser_engine = detail::cocoa_wkwebview_engine; - -} // namespace webview - -#elif defined(WEBVIEW_EDGE) - -// -// ==================================================================== -// -// This implementation uses Win32 API to create a native window. It -// uses Edge/Chromium webview2 backend as a browser engine. -// -// ==================================================================== -// - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include - -#include "WebView2.h" - -#ifdef _MSC_VER -#pragma comment(lib, "advapi32.lib") -#pragma comment(lib, "ole32.lib") -#pragma comment(lib, "shell32.lib") -#pragma comment(lib, "shlwapi.lib") -#pragma comment(lib, "user32.lib") -#pragma comment(lib, "version.lib") -#endif - -namespace webview -{ - namespace detail - { - - using msg_cb_t = std::function; - - // Converts a narrow (UTF-8-encoded) string into a wide (UTF-16-encoded) string. - inline std::wstring widen_string(const std::string& input) - { - if (input.empty()) - { - return std::wstring(); - } - UINT cp = CP_UTF8; - DWORD flags = MB_ERR_INVALID_CHARS; - auto input_c = input.c_str(); - auto input_length = static_cast(input.size()); - auto required_length = - MultiByteToWideChar(cp, flags, input_c, input_length, nullptr, 0); - if (required_length > 0) - { - std::wstring output(static_cast(required_length), L'\0'); - if (MultiByteToWideChar(cp, flags, input_c, input_length, &output[0], - required_length) > 0) - { - return output; - } - } - // Failed to convert string from UTF-8 to UTF-16 - return std::wstring(); - } - - // Converts a wide (UTF-16-encoded) string into a narrow (UTF-8-encoded) string. - inline std::string narrow_string(const std::wstring& input) - { - if (input.empty()) - { - return std::string(); - } - UINT cp = CP_UTF8; - DWORD flags = WC_ERR_INVALID_CHARS; - auto input_c = input.c_str(); - auto input_length = static_cast(input.size()); - auto required_length = - WideCharToMultiByte(cp, flags, input_c, input_length, nullptr, 0, nullptr, nullptr); - if (required_length > 0) - { - std::string output(static_cast(required_length), '\0'); - if (WideCharToMultiByte(cp, flags, input_c, input_length, &output[0], - required_length, nullptr, nullptr) > 0) - { - return output; - } - } - // Failed to convert string from UTF-16 to UTF-8 - return std::string(); - } - - // Parses a version string with 1-4 integral components, e.g. "1.2.3.4". - // Missing or invalid components default to 0, and excess components are ignored. - template - std::array parse_version(const std::basic_string& version) noexcept - { - auto parse_component = [](auto sb, auto se) -> unsigned int - { - try - { - auto n = std::stol(std::basic_string(sb, se)); - return n < 0 ? 0 : n; - } - catch (std::exception&) - { - return 0; - } - }; - auto end = version.end(); - auto sb = version.begin(); // subrange begin - auto se = sb; // subrange end - unsigned int ci = 0; // component index - std::array components{}; - while (sb != end && se != end && ci < components.size()) - { - if (*se == static_cast('.')) - { - components[ci++] = parse_component(sb, se); - sb = ++se; - continue; - } - ++se; - } - if (sb < se && ci < components.size()) - { - components[ci] = parse_component(sb, se); - } - return components; - } - - template - auto parse_version(const T (&version)[Length]) noexcept - { - return parse_version(std::basic_string(version, Length)); - } - - std::wstring get_file_version_string(const std::wstring& file_path) noexcept - { - DWORD dummy_handle; // Unused - DWORD info_buffer_length = GetFileVersionInfoSizeW(file_path.c_str(), &dummy_handle); - if (info_buffer_length == 0) - { - return std::wstring(); - } - std::vector info_buffer; - info_buffer.reserve(info_buffer_length); - if (!GetFileVersionInfoW(file_path.c_str(), 0, info_buffer_length, info_buffer.data())) - { - return std::wstring(); - } - auto sub_block = L"\\StringFileInfo\\040904B0\\ProductVersion"; - LPWSTR version = nullptr; - unsigned int version_length = 0; - if (!VerQueryValueW(info_buffer.data(), sub_block, reinterpret_cast(&version), - &version_length)) - { - return std::wstring(); - } - if (!version || version_length == 0) - { - return std::wstring(); - } - return std::wstring(version, version_length); - } - - // A wrapper around COM library initialization. Calls CoInitializeEx in the - // constructor and CoUninitialize in the destructor. - class com_init_wrapper - { - public: - com_init_wrapper(DWORD dwCoInit) - { - // We can safely continue as long as COM was either successfully - // initialized or already initialized. - // RPC_E_CHANGED_MODE means that CoInitializeEx was already called with - // a different concurrency model. - switch (CoInitializeEx(nullptr, dwCoInit)) - { - case S_OK: - case S_FALSE: - m_initialized = true; - break; - } - } - - ~com_init_wrapper() - { - if (m_initialized) - { - CoUninitialize(); - m_initialized = false; - } - } - - com_init_wrapper(const com_init_wrapper& other) = delete; - com_init_wrapper& operator=(const com_init_wrapper& other) = delete; - com_init_wrapper(com_init_wrapper&& other) = delete; - com_init_wrapper& operator=(com_init_wrapper&& other) = delete; - - bool is_initialized() const - { - return m_initialized; - } - - private: - bool m_initialized = false; - }; - - // Holds a symbol name and associated type for code clarity. - template class library_symbol - { - public: - using type = T; - - constexpr explicit library_symbol(const char* name) : m_name(name) - { - } - constexpr const char* get_name() const - { - return m_name; - } - - private: - const char* m_name; - }; - - // Loads a native shared library and allows one to get addresses for those - // symbols. - class native_library - { - public: - explicit native_library(const wchar_t* name) : m_handle(LoadLibraryW(name)) - { - } - - ~native_library() - { - if (m_handle) - { - FreeLibrary(m_handle); - m_handle = nullptr; - } - } - - native_library(const native_library& other) = delete; - native_library& operator=(const native_library& other) = delete; - native_library(native_library&& other) = default; - native_library& operator=(native_library&& other) = default; - - // Returns true if the library is currently loaded; otherwise false. - operator bool() const - { - return is_loaded(); - } - - // Get the address for the specified symbol or nullptr if not found. - template typename Symbol::type get(const Symbol& symbol) const - { - if (is_loaded()) - { - return reinterpret_cast( - GetProcAddress(m_handle, symbol.get_name())); - } - return nullptr; - } - - // Returns true if the library is currently loaded; otherwise false. - bool is_loaded() const - { - return !!m_handle; - } - - void detach() - { - m_handle = nullptr; - } - - private: - HMODULE m_handle = nullptr; - }; - - struct user32_symbols - { - using DPI_AWARENESS_CONTEXT = HANDLE; - using SetProcessDpiAwarenessContext_t = BOOL(WINAPI*)(DPI_AWARENESS_CONTEXT); - using SetProcessDPIAware_t = BOOL(WINAPI*)(); - - static constexpr auto SetProcessDpiAwarenessContext = - library_symbol("SetProcessDpiAwarenessContext"); - static constexpr auto SetProcessDPIAware = - library_symbol("SetProcessDPIAware"); - }; - - struct shcore_symbols - { - typedef enum - { - PROCESS_PER_MONITOR_DPI_AWARE = 2 - } PROCESS_DPI_AWARENESS; - using SetProcessDpiAwareness_t = HRESULT(WINAPI*)(PROCESS_DPI_AWARENESS); - - static constexpr auto SetProcessDpiAwareness = - library_symbol("SetProcessDpiAwareness"); - }; - - class reg_key - { - public: - explicit reg_key(HKEY root_key, const wchar_t* sub_key, DWORD options, - REGSAM sam_desired) - { - HKEY handle; - auto status = RegOpenKeyExW(root_key, sub_key, options, sam_desired, &handle); - if (status == ERROR_SUCCESS) - { - m_handle = handle; - } - } - - explicit reg_key(HKEY root_key, const std::wstring& sub_key, DWORD options, - REGSAM sam_desired) - : reg_key(root_key, sub_key.c_str(), options, sam_desired) - { - } - - virtual ~reg_key() - { - if (m_handle) - { - RegCloseKey(m_handle); - m_handle = nullptr; - } - } - - reg_key(const reg_key& other) = delete; - reg_key& operator=(const reg_key& other) = delete; - reg_key(reg_key&& other) = delete; - reg_key& operator=(reg_key&& other) = delete; - - bool is_open() const - { - return !!m_handle; - } - bool get_handle() const - { - return m_handle; - } - - std::wstring query_string(const wchar_t* name) const - { - DWORD buf_length = 0; - // Get the size of the data in bytes. - auto status = - RegQueryValueExW(m_handle, name, nullptr, nullptr, nullptr, &buf_length); - if (status != ERROR_SUCCESS && status != ERROR_MORE_DATA) - { - return std::wstring(); - } - // Read the data. - std::wstring result(buf_length / sizeof(wchar_t), 0); - auto buf = reinterpret_cast(&result[0]); - status = RegQueryValueExW(m_handle, name, nullptr, nullptr, buf, &buf_length); - if (status != ERROR_SUCCESS) - { - return std::wstring(); - } - // Remove trailing null-characters. - for (std::size_t length = result.size(); length > 0; --length) - { - if (result[length - 1] != 0) - { - result.resize(length); - break; - } - } - return result; - } - - private: - HKEY m_handle = nullptr; - }; - - inline bool enable_dpi_awareness() - { - auto user32 = native_library(L"user32.dll"); - if (auto fn = user32.get(user32_symbols::SetProcessDpiAwarenessContext)) - { - if (fn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) - { - return true; - } - return GetLastError() == ERROR_ACCESS_DENIED; - } - if (auto shcore = native_library(L"shcore.dll")) - { - if (auto fn = shcore.get(shcore_symbols::SetProcessDpiAwareness)) - { - auto result = fn(shcore_symbols::PROCESS_PER_MONITOR_DPI_AWARE); - return result == S_OK || result == E_ACCESSDENIED; - } - } - if (auto fn = user32.get(user32_symbols::SetProcessDPIAware)) - { - return !!fn(); - } - return true; - } - -// Enable built-in WebView2Loader implementation by default. -#ifndef WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL -#define WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL 1 -#endif - -// Link WebView2Loader.dll explicitly by default only if the built-in -// implementation is enabled. -#ifndef WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK -#define WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL -#endif - -// Explicit linking of WebView2Loader.dll should be used along with -// the built-in implementation. -#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 && WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK != 1 -#undef WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK -#error Please set WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK=1. -#endif - -#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 - // Gets the last component of a Windows native file path. - // For example, if the path is "C:\a\b" then the result is "b". - template - std::basic_string get_last_native_path_component(const std::basic_string& path) - { - if (auto pos = path.find_last_of(static_cast('\\')); - pos != std::basic_string::npos) - { - return path.substr(pos + 1); - } - return std::basic_string(); - } -#endif /* WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL */ - - template struct cast_info_t - { - using type = T; - IID iid; - }; - - namespace mswebview2 - { - static constexpr IID IID_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler{ - 0x6C4819F3, 0xC9B7, 0x4260, 0x81, 0x27, 0xC9, 0xF5, 0xBD, 0xE7, 0xF6, 0x8C - }; - static constexpr IID IID_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler{ - 0x4E8A3389, 0xC9D8, 0x4BD2, 0xB6, 0xB5, 0x12, 0x4F, 0xEE, 0x6C, 0xC1, 0x4D - }; - static constexpr IID IID_ICoreWebView2PermissionRequestedEventHandler{ - 0x15E1C6A3, 0xC72A, 0x4DF3, 0x91, 0xD7, 0xD0, 0x97, 0xFB, 0xEC, 0x6B, 0xFD - }; - static constexpr IID IID_ICoreWebView2WebMessageReceivedEventHandler{ - 0x57213F19, 0x00E6, 0x49FA, 0x8E, 0x07, 0x89, 0x8E, 0xA0, 0x1E, 0xCB, 0xD2 - }; - -#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 - enum class webview2_runtime_type - { - installed = 0, - embedded = 1 - }; - - namespace webview2_symbols - { - using CreateWebViewEnvironmentWithOptionsInternal_t = HRESULT(STDMETHODCALLTYPE*)( - bool, webview2_runtime_type, PCWSTR, IUnknown*, - ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler*); - using DllCanUnloadNow_t = HRESULT(STDMETHODCALLTYPE*)(); - - static constexpr auto CreateWebViewEnvironmentWithOptionsInternal = - library_symbol( - "CreateWebViewEnvironmentWithOptionsInternal"); - static constexpr auto DllCanUnloadNow = - library_symbol("DllCanUnloadNow"); - } // namespace webview2_symbols -#endif /* WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL */ - -#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1 - namespace webview2_symbols - { - using CreateCoreWebView2EnvironmentWithOptions_t = HRESULT(STDMETHODCALLTYPE*)( - PCWSTR, PCWSTR, ICoreWebView2EnvironmentOptions*, - ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler*); - using GetAvailableCoreWebView2BrowserVersionString_t = - HRESULT(STDMETHODCALLTYPE*)(PCWSTR, LPWSTR*); - - static constexpr auto CreateCoreWebView2EnvironmentWithOptions = - library_symbol( - "CreateCoreWebView2EnvironmentWithOptions"); - static constexpr auto GetAvailableCoreWebView2BrowserVersionString = - library_symbol( - "GetAvailableCoreWebView2BrowserVersionString"); - } // namespace webview2_symbols -#endif /* WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK */ - - class loader - { - public: - HRESULT create_environment_with_options( - PCWSTR browser_dir, PCWSTR user_data_dir, - ICoreWebView2EnvironmentOptions* env_options, - ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* created_handler) - const - { -#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1 - if (m_lib.is_loaded()) - { - if (auto fn = m_lib.get( - webview2_symbols::CreateCoreWebView2EnvironmentWithOptions)) - { - return fn(browser_dir, user_data_dir, env_options, created_handler); - } - } -#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 - return create_environment_with_options_impl(browser_dir, user_data_dir, - env_options, created_handler); -#else - return S_FALSE; -#endif -#else - return ::CreateCoreWebView2EnvironmentWithOptions(browser_dir, user_data_dir, - env_options, created_handler); -#endif /* WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK */ - } - - HRESULT - get_available_browser_version_string(PCWSTR browser_dir, LPWSTR* version) const - { -#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1 - if (m_lib.is_loaded()) - { - if (auto fn = m_lib.get( - webview2_symbols::GetAvailableCoreWebView2BrowserVersionString)) - { - return fn(browser_dir, version); - } - } -#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 - return get_available_browser_version_string_impl(browser_dir, version); -#else - return S_FALSE; -#endif -#else - return ::GetAvailableCoreWebView2BrowserVersionString(browser_dir, version); -#endif /* WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK */ - } - - private: -#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 - struct client_info_t - { - bool found = false; - std::wstring dll_path; - std::wstring version; - webview2_runtime_type runtime_type; - }; - - HRESULT create_environment_with_options_impl( - PCWSTR browser_dir, PCWSTR user_data_dir, - ICoreWebView2EnvironmentOptions* env_options, - ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* created_handler) - const - { - auto found_client = find_available_client(browser_dir); - if (!found_client.found) - { - return -1; - } - auto client_dll = native_library(found_client.dll_path.c_str()); - if (auto fn = client_dll.get( - webview2_symbols::CreateWebViewEnvironmentWithOptionsInternal)) - { - return fn(true, found_client.runtime_type, user_data_dir, env_options, - created_handler); - } - if (auto fn = client_dll.get(webview2_symbols::DllCanUnloadNow)) - { - if (!fn()) - { - client_dll.detach(); - } - } - return ERROR_SUCCESS; - } - - HRESULT - get_available_browser_version_string_impl(PCWSTR browser_dir, LPWSTR* version) const - { - if (!version) - { - return -1; - } - auto found_client = find_available_client(browser_dir); - if (!found_client.found) - { - return -1; - } - auto info_length_bytes = - found_client.version.size() * sizeof(found_client.version[0]); - auto info = static_cast(CoTaskMemAlloc(info_length_bytes)); - if (!info) - { - return -1; - } - CopyMemory(info, found_client.version.c_str(), info_length_bytes); - *version = info; - return 0; - } - - client_info_t find_available_client(PCWSTR browser_dir) const - { - if (browser_dir) - { - return find_embedded_client(api_version, browser_dir); - } - auto found_client = - find_installed_client(api_version, true, default_release_channel_guid); - if (!found_client.found) - { - found_client = - find_installed_client(api_version, false, default_release_channel_guid); - } - return found_client; - } - - std::wstring make_client_dll_path(const std::wstring& dir) const - { - auto dll_path = dir; - if (!dll_path.empty()) - { - auto last_char = dir[dir.size() - 1]; - if (last_char != L'\\' && last_char != L'/') - { - dll_path += L'\\'; - } - } - dll_path += L"EBWebView\\"; -#if defined(_M_X64) || defined(__x86_64__) - dll_path += L"x64"; -#elif defined(_M_IX86) || defined(__i386__) - dll_path += L"x86"; -#elif defined(_M_ARM64) || defined(__aarch64__) - dll_path += L"arm64"; -#else -#error WebView2 integration for this platform is not yet supported. -#endif - dll_path += L"\\EmbeddedBrowserWebView.dll"; - return dll_path; - } - - client_info_t find_installed_client(unsigned int min_api_version, bool system, - const std::wstring& release_channel) const - { - std::wstring sub_key = client_state_reg_sub_key; - sub_key += release_channel; - auto root_key = system ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - reg_key key(root_key, sub_key, 0, KEY_READ | KEY_WOW64_32KEY); - if (!key.is_open()) - { - return {}; - } - auto ebwebview_value = key.query_string(L"EBWebView"); - - auto client_version_string = get_last_native_path_component(ebwebview_value); - auto client_version = parse_version(client_version_string); - if (client_version[2] < min_api_version) - { - // Our API version is greater than the runtime API version. - return {}; - } - - auto client_dll_path = make_client_dll_path(ebwebview_value); - return { true, client_dll_path, client_version_string, - webview2_runtime_type::installed }; - } - - client_info_t find_embedded_client(unsigned int min_api_version, - const std::wstring& dir) const - { - auto client_dll_path = make_client_dll_path(dir); - - auto client_version_string = get_file_version_string(client_dll_path); - auto client_version = parse_version(client_version_string); - if (client_version[2] < min_api_version) - { - // Our API version is greater than the runtime API version. - return {}; - } - - return { true, client_dll_path, client_version_string, - webview2_runtime_type::embedded }; - } - - // The minimum WebView2 API version we need regardless of the SDK release - // actually used. The number comes from the SDK release version, - // e.g. 1.0.1150.38. To be safe the SDK should have a number that is greater - // than or equal to this number. The Edge browser webview client must - // have a number greater than or equal to this number. - static constexpr unsigned int api_version = 1150; - - static constexpr auto client_state_reg_sub_key = - L"SOFTWARE\\Microsoft\\EdgeUpdate\\ClientState\\"; - - // GUID for the stable release channel. - static constexpr auto stable_release_guid = - L"{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"; - - static constexpr auto default_release_channel_guid = stable_release_guid; -#endif /* WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL */ - -#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1 - native_library m_lib{ L"WebView2Loader.dll" }; -#endif - }; - - namespace cast_info - { - static constexpr auto controller_completed = - cast_info_t{ - IID_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler - }; - - static constexpr auto environment_completed = - cast_info_t{ - IID_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler - }; - - static constexpr auto message_received = - cast_info_t{ - IID_ICoreWebView2WebMessageReceivedEventHandler - }; - - static constexpr auto permission_requested = - cast_info_t{ - IID_ICoreWebView2PermissionRequestedEventHandler - }; - } // namespace cast_info - } // namespace mswebview2 - - class webview2_com_handler - : public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, - public ICoreWebView2CreateCoreWebView2ControllerCompletedHandler, - public ICoreWebView2WebMessageReceivedEventHandler, - public ICoreWebView2PermissionRequestedEventHandler, - public ICoreWebView2NavigationCompletedEventHandler - { - using webview2_com_handler_cb_t = - std::function; - - public: - webview2_com_handler(HWND hwnd, msg_cb_t msgCb, webview2_com_handler_cb_t cb) - : m_window(hwnd), m_msgCb(msgCb), m_cb(cb) - { - } - - virtual ~webview2_com_handler() = default; - webview2_com_handler(const webview2_com_handler& other) = delete; - webview2_com_handler& operator=(const webview2_com_handler& other) = delete; - webview2_com_handler(webview2_com_handler&& other) = delete; - webview2_com_handler& operator=(webview2_com_handler&& other) = delete; - - ULONG STDMETHODCALLTYPE AddRef() - { - return ++m_ref_count; - } - ULONG STDMETHODCALLTYPE Release() - { - if (m_ref_count > 1) - { - return --m_ref_count; - } - delete this; - return 0; - } - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppv) - { - using namespace mswebview2::cast_info; - - if (!ppv) - { - return E_POINTER; - } - - // All of the COM interfaces we implement should be added here regardless - // of whether they are required. - // This is just to be on the safe side in case the WebView2 Runtime ever - // requests a pointer to an interface we implement. - // The WebView2 Runtime must at the very least be able to get a pointer to - // ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler when we use - // our custom WebView2 loader implementation, and observations have shown - // that it is the only interface requested in this case. None have been - // observed to be requested when using the official WebView2 loader. - - if (cast_if_equal_iid(riid, controller_completed, ppv) || - cast_if_equal_iid(riid, environment_completed, ppv) || - cast_if_equal_iid(riid, message_received, ppv) || - cast_if_equal_iid(riid, permission_requested, ppv)) - { - return S_OK; - } - - return E_NOINTERFACE; - } - HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, ICoreWebView2Environment* env) - { - if (SUCCEEDED(res)) - { - res = env->CreateCoreWebView2Controller(m_window, this); - if (SUCCEEDED(res)) - { - return S_OK; - } - } - try_create_environment(); - return S_OK; - } - HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, ICoreWebView2Controller* controller) - { - if (FAILED(res)) - { - // See try_create_environment() regarding - // HRESULT_FROM_WIN32(ERROR_INVALID_STATE). - // The result is E_ABORT if the parent window has been destroyed already. - switch (res) - { - case HRESULT_FROM_WIN32(ERROR_INVALID_STATE): - case E_ABORT: - return S_OK; - } - try_create_environment(); - return S_OK; - } - - ICoreWebView2* webview; - ::EventRegistrationToken token; - controller->get_CoreWebView2(&webview); - webview->add_WebMessageReceived(this, &token); - webview->add_PermissionRequested(this, &token); - webview->add_NavigationCompleted(this, &token); - - m_cb(controller, webview); - return S_OK; - } - HRESULT STDMETHODCALLTYPE Invoke(ICoreWebView2* sender, - ICoreWebView2WebMessageReceivedEventArgs* args) - { - LPWSTR message; - args->TryGetWebMessageAsString(&message); - m_msgCb(narrow_string(message)); - sender->PostWebMessageAsString(message); - - CoTaskMemFree(message); - return S_OK; - } - HRESULT STDMETHODCALLTYPE Invoke(ICoreWebView2* sender, - ICoreWebView2PermissionRequestedEventArgs* args) - { - COREWEBVIEW2_PERMISSION_KIND kind; - args->get_PermissionKind(&kind); - if (kind == COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ) - { - args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW); - } - return S_OK; - } - HRESULT STDMETHODCALLTYPE Invoke(ICoreWebView2* sender, - ICoreWebView2NavigationCompletedEventArgs* args) - { - PWSTR uri = nullptr; - auto hr = sender->get_Source(&uri); - if (SUCCEEDED(hr)) - { - auto curi = std::wstring_convert >().to_bytes(uri); - if (navigateCallback) - navigateCallback(curi, navigateCallbackArg); - } - CoTaskMemFree(uri); - return hr; - } - - // Checks whether the specified IID equals the IID of the specified type and - // if so casts the "this" pointer to T and returns it. Returns nullptr on - // mismatching IIDs. - // If ppv is specified then the pointer will also be assigned to *ppv. - template - T* cast_if_equal_iid(REFIID riid, const cast_info_t& info, - LPVOID* ppv = nullptr) noexcept - { - T* ptr = nullptr; - if (IsEqualIID(riid, info.iid)) - { - ptr = static_cast(this); - ptr->AddRef(); - } - if (ppv) - { - *ppv = ptr; - } - return ptr; - } - - // Set the function that will perform the initiating logic for creating - // the WebView2 environment. - void set_attempt_handler(std::function attempt_handler) noexcept - { - m_attempt_handler = attempt_handler; - } - - // Retry creating a WebView2 environment. - // The initiating logic for creating the environment is defined by the - // caller of set_attempt_handler(). - void try_create_environment() noexcept - { - // WebView creation fails with HRESULT_FROM_WIN32(ERROR_INVALID_STATE) if - // a running instance using the same user data folder exists, and the - // Environment objects have different EnvironmentOptions. - // Source: - // https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environment?view=webview2-1.0.1150.38 - if (m_attempts < m_max_attempts) - { - ++m_attempts; - auto res = m_attempt_handler(); - if (SUCCEEDED(res)) - { - return; - } - // Not entirely sure if this error code only applies to - // CreateCoreWebView2Controller so we check here as well. - if (res == HRESULT_FROM_WIN32(ERROR_INVALID_STATE)) - { - return; - } - try_create_environment(); - return; - } - // Give up. - m_cb(nullptr, nullptr); - } - - void STDMETHODCALLTYPE add_navigate_listener( - std::function callback, void* arg) - { - navigateCallback = std::move(callback); - navigateCallbackArg = arg; - } - - private: - HWND m_window; - msg_cb_t m_msgCb; - webview2_com_handler_cb_t m_cb; - std::atomic m_ref_count{ 1 }; - std::function m_attempt_handler; - unsigned int m_max_attempts = 5; - unsigned int m_attempts = 0; - void* navigateCallbackArg = nullptr; - std::function navigateCallback = 0; - }; - - class win32_edge_engine - { - public: - win32_edge_engine(bool debug, void* window) - { - if (!is_webview2_available()) - { - return; - } - if (!m_com_init.is_initialized()) - { - return; - } - enable_dpi_awareness(); - if (window == nullptr) - { - HINSTANCE hInstance = GetModuleHandle(nullptr); - HICON icon = (HICON)LoadImage(hInstance, IDI_APPLICATION, IMAGE_ICON, - GetSystemMetrics(SM_CXICON), - GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR); - - WNDCLASSEXW wc; - ZeroMemory(&wc, sizeof(WNDCLASSEX)); - wc.cbSize = sizeof(WNDCLASSEX); - wc.hInstance = hInstance; - 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; - }); - RegisterClassExW(&wc); - m_window = CreateWindowW(L"webview", L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, - CW_USEDEFAULT, 640, 480, nullptr, nullptr, hInstance, - nullptr); - if (m_window == nullptr) - { - return; - } - SetWindowLongPtr(m_window, GWLP_USERDATA, (LONG_PTR)this); - } - else - { - m_window = *(static_cast(window)); - } - - ShowWindow(m_window, SW_SHOW); - UpdateWindow(m_window); - SetFocus(m_window); - - auto cb = std::bind(&win32_edge_engine::on_message, this, std::placeholders::_1); - - embed(m_window, debug, cb); - resize(m_window); - m_controller->MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC); - } - - virtual ~win32_edge_engine() - { - if (m_com_handler) - { - m_com_handler->Release(); - m_com_handler = nullptr; - } - if (m_webview) - { - m_webview->Release(); - m_webview = nullptr; - } - if (m_controller) - { - m_controller->Release(); - m_controller = nullptr; - } - } - - win32_edge_engine(const win32_edge_engine& other) = delete; - win32_edge_engine& operator=(const win32_edge_engine& other) = delete; - win32_edge_engine(win32_edge_engine&& other) = delete; - win32_edge_engine& operator=(win32_edge_engine&& other) = delete; - - void run() - { - MSG msg; - BOOL res; - while ((res = GetMessage(&msg, nullptr, 0, 0)) != -1) - { - if (msg.hwnd) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - continue; - } - if (msg.message == WM_APP) - { - auto f = (dispatch_fn_t*)(msg.lParam); - (*f)(); - delete f; - } - else if (msg.message == WM_QUIT) - { - return; - } - } - } - void* window() - { - return (void*)m_window; - } - void terminate() - { - PostQuitMessage(0); - } - void dispatch(dispatch_fn_t f) - { - PostThreadMessage(m_main_thread, WM_APP, 0, (LPARAM) new dispatch_fn_t(f)); - } - - void set_title(const std::string& title) - { - SetWindowTextW(m_window, widen_string(title).c_str()); - } - - void set_size(int width, int height, int hints) - { - auto style = GetWindowLong(m_window, GWL_STYLE); - if (hints == WEBVIEW_HINT_FIXED) - { - style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); - } - else - { - style |= (WS_THICKFRAME | WS_MAXIMIZEBOX); - } - SetWindowLong(m_window, GWL_STYLE, style); - - if (hints == WEBVIEW_HINT_MAX) - { - m_maxsz.x = width; - m_maxsz.y = height; - } - else if (hints == WEBVIEW_HINT_MIN) - { - m_minsz.x = width; - m_minsz.y = height; - } - else - { - RECT r; - r.left = r.top = 0; - r.right = width; - r.bottom = height; - AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, 0); - SetWindowPos(m_window, nullptr, r.left, r.top, r.right - r.left, - r.bottom - r.top, - SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_FRAMECHANGED); - resize(m_window); - } - } - - void navigate(const std::string& url) - { - auto wurl = widen_string(url); - m_webview->Navigate(wurl.c_str()); - } - - void init(const std::string& js) - { - auto wjs = widen_string(js); - m_webview->AddScriptToExecuteOnDocumentCreated(wjs.c_str(), nullptr); - } - - void eval(const std::string& js) - { - auto wjs = widen_string(js); - m_webview->ExecuteScript(wjs.c_str(), nullptr); - } - - void add_navigate_listener(std::function callback, - void* arg) - { - m_com_handler->add_navigate_listener(callback, arg); - } - - void set_html(const std::string& html) - { - m_webview->NavigateToString(widen_string(html).c_str()); - } - - private: - bool embed(HWND wnd, bool debug, msg_cb_t cb) - { - std::atomic_flag flag = ATOMIC_FLAG_INIT; - flag.test_and_set(); - - wchar_t currentExePath[MAX_PATH]; - GetModuleFileNameW(nullptr, currentExePath, MAX_PATH); - wchar_t* currentExeName = PathFindFileNameW(currentExePath); - - wchar_t dataPath[MAX_PATH]; - if (!SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_APPDATA, nullptr, 0, dataPath))) - { - return false; - } - wchar_t userDataFolder[MAX_PATH]; - PathCombineW(userDataFolder, dataPath, currentExeName); - - m_com_handler = new webview2_com_handler( - wnd, cb, - [&](ICoreWebView2Controller* controller, ICoreWebView2* webview) - { - if (!controller || !webview) - { - flag.clear(); - return; - } - controller->AddRef(); - webview->AddRef(); - m_controller = controller; - m_webview = 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->try_create_environment(); - - MSG msg = {}; - while (flag.test_and_set() && GetMessage(&msg, nullptr, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - if (!m_controller || !m_webview) - { - return false; - } - ICoreWebView2Settings* settings = nullptr; - auto res = m_webview->get_Settings(&settings); - if (res != S_OK) - { - return false; - } - res = settings->put_AreDevToolsEnabled(debug ? TRUE : FALSE); - if (res != S_OK) - { - return false; - } - init("window.external={invoke:s=>window.chrome.webview.postMessage(s)}"); - return true; - } - - void resize(HWND wnd) - { - if (m_controller == nullptr) - { - return; - } - RECT bounds; - GetClientRect(wnd, &bounds); - m_controller->put_Bounds(bounds); - } - - bool is_webview2_available() const noexcept - { - LPWSTR version_info = nullptr; - auto res = - m_webview2_loader.get_available_browser_version_string(nullptr, &version_info); - // The result will be equal to HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) - // if the WebView2 runtime is not installed. - auto ok = SUCCEEDED(res) && version_info; - if (version_info) - { - CoTaskMemFree(version_info); - } - return ok; - } - - virtual void on_message(const std::string& msg) = 0; - - // The app is expected to call CoInitializeEx before - // CreateCoreWebView2EnvironmentWithOptions. - // Source: - // https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl#createcorewebview2environmentwithoptions - com_init_wrapper m_com_init{ COINIT_APARTMENTTHREADED }; - HWND m_window = nullptr; - POINT m_minsz = POINT{ 0, 0 }; - POINT m_maxsz = POINT{ 0, 0 }; - DWORD m_main_thread = GetCurrentThreadId(); - ICoreWebView2* m_webview = nullptr; - ICoreWebView2Controller* m_controller = nullptr; - webview2_com_handler* m_com_handler = nullptr; - mswebview2::loader m_webview2_loader; - }; - - } // namespace detail - - using browser_engine = detail::win32_edge_engine; - -} // namespace webview - -#endif /* WEBVIEW_GTK, WEBVIEW_COCOA, WEBVIEW_EDGE */ - -namespace webview -{ - - class webview : public browser_engine - { - public: - webview(bool debug = false, void* wnd = nullptr) : browser_engine(debug, wnd) - { - } - - void navigate(const std::string& url) - { - if (url.empty()) - { - browser_engine::navigate("about:blank"); - return; - } - browser_engine::navigate(url); - } - - using binding_t = std::function; - class binding_ctx_t - { - public: - binding_ctx_t(binding_t callback, void* arg) : callback(callback), arg(arg) - { - } - // This function is called upon execution of the bound JS function - binding_t callback; - // This user-supplied argument is passed to the callback - void* arg; - }; - - using sync_binding_t = std::function; - - // 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)); }; - bind(name, wrapper, nullptr); - } - - // Asynchronous bind - void bind(const std::string& name, binding_t fn, void* arg) - { - if (bindings.count(name) > 0) - { - return; - } - bindings.emplace(name, binding_ctx_t(fn, arg)); - auto js = "(function() { var name = '" + name + "';" + R""( - var RPC = window._rpc = (window._rpc || {nextSeq: 1}); - window[name] = function() { - var seq = RPC.nextSeq++; - var promise = new Promise(function(resolve, reject) { - RPC[seq] = { - resolve: resolve, - reject: reject, - }; - }); - window.external.invoke(JSON.stringify({ - id: seq, - method: name, - params: Array.prototype.slice.call(arguments), - })); - return promise; - } - })())""; - init(js); - eval(js); - } - - void unbind(const std::string& name) - { - auto found = bindings.find(name); - if (found != bindings.end()) - { - auto js = "delete window['" + name + "'];"; - init(js); - eval(js); - bindings.erase(found); - } - } - - 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 + "]"); - } - }); - } - - private: - void on_message(const std::string& msg) override - { - auto seq = detail::json_parse(msg, "id", 0); - auto name = detail::json_parse(msg, "method", 0); - auto args = detail::json_parse(msg, "params", 0); - auto found = bindings.find(name); - if (found == bindings.end()) - { - return; - } - const auto& context = found->second; - context.callback(seq, args, context.arg); - } - - std::map bindings; - }; -} // namespace webview - -WEBVIEW_API webview_t webview_create(int debug, void* wnd) -{ - auto w = new webview::webview(debug, wnd); - if (!w->window()) - { - delete w; - return nullptr; - } - return w; -} - -WEBVIEW_API void webview_destroy(webview_t w) -{ - delete static_cast(w); -} - -WEBVIEW_API void webview_run(webview_t w) -{ - static_cast(w)->run(); -} - -WEBVIEW_API void webview_terminate(webview_t w) -{ - static_cast(w)->terminate(); -} - -WEBVIEW_API void webview_dispatch(webview_t w, void (*fn)(webview_t, void*), void* arg) -{ - static_cast(w)->dispatch([=]() { fn(w, arg); }); -} - -WEBVIEW_API void* webview_get_window(webview_t w) -{ - return static_cast(w)->window(); -} - -WEBVIEW_API void webview_set_title(webview_t w, const char* title) -{ - static_cast(w)->set_title(title); -} - -WEBVIEW_API void webview_set_size(webview_t w, int width, int height, int hints) -{ - static_cast(w)->set_size(width, height, hints); -} - -WEBVIEW_API void webview_navigate(webview_t w, const char* url) -{ - static_cast(w)->navigate(url); -} - -WEBVIEW_API void webview_set_html(webview_t w, const char* html) -{ - static_cast(w)->set_html(html); -} - -WEBVIEW_API void webview_init(webview_t w, const char* js) -{ - static_cast(w)->init(js); -} - -WEBVIEW_API void webview_eval(webview_t w, const char* js) -{ - static_cast(w)->eval(js); -} - -WEBVIEW_API void webview_bind(webview_t w, const char* name, - void (*fn)(const char* seq, const char* req, void* arg), void* arg) -{ - static_cast(w)->bind( - name, - [=](const std::string& seq, const std::string& req, void* arg) - { fn(seq.c_str(), req.c_str(), arg); }, - arg); -} - -WEBVIEW_API void webview_unbind(webview_t w, const char* name) -{ - static_cast(w)->unbind(name); -} - -WEBVIEW_API void webview_return(webview_t w, const char* seq, int status, const char* result) -{ - static_cast(w)->resolve(seq, status, result); -} - -WEBVIEW_API const webview_version_info_t* webview_version() -{ - return &webview::detail::library_version_info; -} - -#endif /* WEBVIEW_HEADER */ -#endif /* __cplusplus */ -#endif /* WEBVIEW_H */ diff --git a/client/SDL3/aad/wrapper/webview_impl.cpp b/client/SDL3/aad/wrapper/webview_impl.cpp deleted file mode 100644 index 5f4d3d597..000000000 --- a/client/SDL3/aad/wrapper/webview_impl.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * Popup browser for AAD authentication - * - * Copyright 2023 Isaac Klein - * - * 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 -#include -#include -#include -#include -#include -#include "../webview_impl.hpp" - -static std::vector 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 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 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(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(); -} diff --git a/client/SDL3/dialogs/res/CMakeLists.txt b/client/SDL3/dialogs/res/CMakeLists.txt deleted file mode 100644 index 87101c3a8..000000000 --- a/client/SDL3/dialogs/res/CMakeLists.txt +++ /dev/null @@ -1,95 +0,0 @@ - -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 $ ${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} -) -if (NOT WITH_SDL_LINK_SHARED) - target_link_libraries(sdl3_client_res ${SDL3_STATIC_LIBRARIES}) -else() - target_link_libraries(sdl3_client_res ${SDL3_LIBRARIES}) -endif() - -set_property(TARGET sdl3_client_res PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/SDL3/dialogs/res/convert_res_to_c.cpp b/client/SDL3/dialogs/res/convert_res_to_c.cpp deleted file mode 100644 index 8ef828adf..000000000 --- a/client/SDL3/dialogs/res/convert_res_to_c.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * - * Copyright 2023 Armin Novak - * - * 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 -#include -#include -#include -#include -#if __has_include() -#include -namespace fs = std::filesystem; -#elif __has_include() -#include -namespace fs = std::experimental::filesystem; -#else -#error Could not find system header "" or "" -#endif - -static void usage(const char* prg) -{ - std::cerr << prg << " " << 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 " << 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 " << 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(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 " << std::endl - << "#include " << 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 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()); -} diff --git a/client/SDL3/dialogs/res/sdl_resource_manager.hpp b/client/SDL3/dialogs/res/sdl_resource_manager.hpp deleted file mode 100644 index caf3d5628..000000000 --- a/client/SDL3/dialogs/res/sdl_resource_manager.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * - * Copyright 2023 Armin Novak - * - * 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 -#include -#include -#include - -class SDLResourceManager -{ - friend class SDLResourceFile; - - public: - static SDL_IOStream* 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& data); - - private: - SDLResourceManager() = delete; - SDLResourceManager(const SDLResourceManager& other) = delete; - SDLResourceManager(const SDLResourceManager&& other) = delete; - ~SDLResourceManager() = delete; - - static std::map>& resources(); -}; diff --git a/client/SDL3/sdl_prefs.cpp b/client/SDL3/sdl_prefs.cpp deleted file mode 100644 index f73393906..000000000 --- a/client/SDL3/sdl_prefs.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * SDL Prefs - * - * Copyright 2022 Armin Novak - * - * 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 -#if __has_include() -#include -namespace fs = std::filesystem; -#elif __has_include() -#include -namespace fs = std::experimental::filesystem; -#else -#error Could not find system header "" or "" -#endif - -#include "sdl_prefs.hpp" - -#include -#include -#include -#include - -SdlPref::WINPR_JSONPtr SdlPref::get() -{ - auto config = get_pref_file(); - - std::ifstream ifs(config); - std::string content((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); - return { WINPR_JSON_ParseWithLength(content.c_str(), content.size()), WINPR_JSON_Delete }; -} - -WINPR_JSON* SdlPref::get_item(const std::string& key) -{ - if (!_config) - return nullptr; - return WINPR_JSON_GetObjectItem(_config.get(), key.c_str()); -} - -std::string SdlPref::item_to_str(WINPR_JSON* item, const std::string& fallback) -{ - if (!item || !WINPR_JSON_IsString(item)) - return fallback; - auto str = WINPR_JSON_GetStringValue(item); - if (!str) - return {}; - return str; -} - -std::string SdlPref::get_string(const std::string& key, const std::string& fallback) -{ - auto item = get_item(key); - return item_to_str(item, fallback); -} - -bool SdlPref::get_bool(const std::string& key, bool fallback) -{ - auto item = get_item(key); - if (!item || !WINPR_JSON_IsBool(item)) - return fallback; - return WINPR_JSON_IsTrue(item); -} - -int64_t SdlPref::get_int(const std::string& key, int64_t fallback) -{ - auto item = get_item(key); - if (!item || !WINPR_JSON_IsNumber(item)) - return fallback; - auto val = WINPR_JSON_GetNumberValue(item); - return static_cast(val); -} - -std::vector SdlPref::get_array(const std::string& key, - const std::vector& fallback) -{ - auto item = get_item(key); - if (!item || !WINPR_JSON_IsArray(item)) - return fallback; - - std::vector values; - for (int x = 0; x < WINPR_JSON_GetArraySize(item); x++) - { - auto cur = WINPR_JSON_GetArrayItem(item, x); - values.push_back(item_to_str(cur)); - } - - return values; -} - -SdlPref::SdlPref(const std::string& file) : _name(file), _config(get()) -{ -} - -std::string SdlPref::get_pref_dir() -{ - using CStringPtr = std::unique_ptr; - CStringPtr path(GetKnownPath(KNOWN_PATH_XDG_CONFIG_HOME), free); - if (!path) - return {}; - - fs::path config{ path.get() }; - config /= FREERDP_VENDOR; - config /= FREERDP_PRODUCT; - return config.string(); -} - -std::string SdlPref::get_default_file() -{ - fs::path config{ SdlPref::get_pref_dir() }; - config /= "sdl-freerdp.json"; - return config.string(); -} - -std::shared_ptr SdlPref::instance(const std::string& name) -{ - static std::shared_ptr _instance; - if (!_instance || (_instance->get_pref_file() != name)) - _instance.reset(new SdlPref(name)); - return _instance; -} - -std::string SdlPref::get_pref_file() -{ - return _name; -} diff --git a/client/SDL3/sdl_prefs.hpp b/client/SDL3/sdl_prefs.hpp deleted file mode 100644 index 225d5a1f7..000000000 --- a/client/SDL3/sdl_prefs.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * SDL Prefs - * - * Copyright 2022 Armin Novak - * - * 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 -#include -#include -#include - -class SdlPref -{ - public: - static std::shared_ptr instance(const std::string& name = SdlPref::get_default_file()); - - std::string get_pref_file(); - - std::string get_string(const std::string& key, const std::string& fallback = ""); - int64_t get_int(const std::string& key, int64_t fallback = 0); - bool get_bool(const std::string& key, bool fallback = false); - std::vector get_array(const std::string& key, - const std::vector& fallback = {}); - - private: - using WINPR_JSONPtr = std::unique_ptr; - - std::string _name; - WINPR_JSONPtr _config; - - SdlPref(const std::string& file); - - WINPR_JSON* get_item(const std::string& key); - WINPR_JSONPtr get(); - - static std::string get_pref_dir(); - static std::string get_default_file(); - static std::string item_to_str(WINPR_JSON* item, const std::string& fallback = ""); -}; diff --git a/client/SDL3/test/CMakeLists.txt b/client/SDL3/test/CMakeLists.txt deleted file mode 100644 index 34836b196..000000000 --- a/client/SDL3/test/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -set(MODULE_NAME "TestSDL3") -set(MODULE_PREFIX "TEST_SDL") - -set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.cpp) - -set(${MODULE_PREFIX}_TESTS TestSDLPrefs.cpp) - -create_test_sourcelist(${MODULE_PREFIX}_SRCS - ${${MODULE_PREFIX}_DRIVER} - ${${MODULE_PREFIX}_TESTS}) - -add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) - -set(${MODULE_PREFIX}_LIBS freerdp winpr sdl3-prefs) - -target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) - -set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") - -set(TEST_SRC_AREA "${CMAKE_CURRENT_SOURCE_DIR}") -set(TEST_BIN_AREA "${CMAKE_CURRENT_BINARY_DIR}") - -if (WIN32) - string(REPLACE "\\" "\\\\" TEST_SRC_AREA "${TEST_SRC_AREA}") - string(REPLACE "\\" "\\\\" TEST_BIN_AREA "${TEST_BIN_AREA}") -endif() - -add_compile_definitions(TEST_SRC_AREA="${TEST_SRC_AREA}") -add_compile_definitions(TEST_BIN_AREA="${TEST_BIN_AREA}") - -foreach(test ${${MODULE_PREFIX}_TESTS}) - get_filename_component(TestName ${test} NAME_WE) - add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) -endforeach() - -set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Client/Test") diff --git a/client/SDL3/test/TestSDLPrefs.cpp b/client/SDL3/test/TestSDLPrefs.cpp deleted file mode 100644 index db4a75302..000000000 --- a/client/SDL3/test/TestSDLPrefs.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "../sdl_prefs.hpp" - -#include -#include - -#include -#include - -#if __has_include() -#include -namespace fs = std::filesystem; -#elif __has_include() -#include -namespace fs = std::experimental::filesystem; -#else -#error Could not find system header "" or "" -#endif - -static std::shared_ptr instance() -{ -#if !defined(TEST_SRC_AREA) -#error "Please define TEST_SRC_AREA to the source directory of this test case" -#endif - fs::path src(TEST_SRC_AREA); - src /= "sdl-freerdp.json"; - if (!fs::exists(src)) - { - std::cerr << "[ERROR] test configuration file '" << src << "' does not exist" << std::endl; - return nullptr; - } - - return SdlPref::instance(src.string()); -} - -int TestSDLPrefs(int argc, char* argv[]) -{ - WINPR_UNUSED(argc); - WINPR_UNUSED(argv); - -#if defined(WITH_WINPR_JSON) - printf("implementation: json\n"); -#else - printf("implementation: fallback\n"); -#endif - -#if defined(WITH_WINPR_JSON) - printf("config: %s\n", instance()->get_pref_file().c_str()); -#endif - - auto string_value = instance()->get_string("string_key", "cba"); -#if defined(WITH_WINPR_JSON) - if (string_value != "abc") - return -1; -#else - if (string_value != "cba") - return -1; -#endif - - auto string_value_nonexistent = instance()->get_string("string_key_nonexistent", "cba"); - if (string_value_nonexistent != "cba") - return -1; - - auto int_value = instance()->get_int("int_key", 321); -#if defined(WITH_WINPR_JSON) - if (int_value != 123) - return -1; -#else - if (int_value != 321) - return -1; -#endif - - auto int_value_nonexistent = instance()->get_int("int_key_nonexistent", 321); - if (int_value_nonexistent != 321) - return -1; - - auto bool_value = instance()->get_bool("bool_key", false); -#if defined(WITH_WINPR_JSON) - if (!bool_value) - return -1; -#else - if (bool_value) - return -1; -#endif - - auto bool_value_nonexistent = instance()->get_bool("bool_key_nonexistent", false); - if (bool_value_nonexistent) - return -1; - - auto array_value = instance()->get_array("array_key", { "c", "b", "a" }); - if (array_value.size() != 3) - return -1; -#if defined(WITH_WINPR_JSON) - if (array_value[0] != "a") - return -1; - if (array_value[1] != "b") - return -1; - if (array_value[2] != "c") - return -1; -#else - if (array_value[0] != "c") - return -1; - if (array_value[1] != "b") - return -1; - if (array_value[2] != "a") - return -1; -#endif - - auto array_value_nonexistent = - instance()->get_array("array_key_nonexistent", { "c", "b", "a" }); - if (array_value_nonexistent.size() != 3) - return -1; - - if (array_value_nonexistent[0] != "c") - return -1; - if (array_value_nonexistent[1] != "b") - return -1; - if (array_value_nonexistent[2] != "a") - return -1; - - return 0; -} diff --git a/client/SDL3/test/sdl-freerdp.json b/client/SDL3/test/sdl-freerdp.json deleted file mode 100644 index 17facb32e..000000000 --- a/client/SDL3/test/sdl-freerdp.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "string_key": "abc", - "int_key": 123, - "bool_key": true, - "array_key": [ - "a", - "b", - "c" - ] -} \ No newline at end of file