diff --git a/.gitignore b/.gitignore index c0a47f406..a8fb3d04d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ Brewfile.lock.json /.cache # typical developer-created files +synergy-config.toml .vscode .DS_Store *.code-workspace diff --git a/.vscode/launch.json b/.vscode/launch.json index b9425187d..238ac908a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,7 @@ { "name": "gui unix", "type": "lldb", + "cwd": "${workspaceRoot}", "request": "launch", "program": "${workspaceFolder}/build/bin/synergy", "preLaunchTask": "kill-build" @@ -20,6 +21,7 @@ { "name": "unittests unix", "type": "lldb", + "cwd": "${workspaceRoot}", "request": "launch", "program": "${workspaceFolder}/build/bin/unittests", "preLaunchTask": "build" @@ -27,7 +29,7 @@ { "name": "unittests windows", "type": "cppvsdbg", - "cwd": "${workspaceRoot}/build/bin", + "cwd": "${workspaceRoot}", "request": "launch", "program": "${workspaceFolder}/build/bin/unittests", "internalConsoleOptions": "openOnSessionStart", @@ -36,6 +38,7 @@ { "name": "integtests unix", "type": "lldb", + "cwd": "${workspaceRoot}", "request": "launch", "program": "${workspaceFolder}/build/bin/integtests", "preLaunchTask": "build" @@ -43,16 +46,50 @@ { "name": "integtests windows", "type": "cppvsdbg", - "cwd": "${workspaceRoot}/build/bin", + "cwd": "${workspaceRoot}", "request": "launch", "program": "${workspaceFolder}/build/bin/integtests", "internalConsoleOptions": "openOnSessionStart", "preLaunchTask": "build" }, + { + "name": "server unix", + "type": "lldb", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin/synergys", + "preLaunchTask": "build" + }, + { + "name": "server windows", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin/synergys", + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build" + }, + { + "name": "client unix", + "type": "lldb", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin/synergyc", + "preLaunchTask": "build" + }, + { + "name": "client windows", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin/synergyc", + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build" + }, { "name": "daemon windows", "type": "cppvsdbg", - "cwd": "${workspaceRoot}/build/bin", + "cwd": "${workspaceRoot}", "request": "launch", "program": "${workspaceFolder}/build/bin/synergyd", "args": ["-f"], @@ -64,6 +101,12 @@ "type": "cppvsdbg", "request": "attach", "processId": "${command:pickProcess}" + }, + { + "name": "unix attach", + "type": "lldb", + "request": "attach", + "pid": "${command:pickProcess}" } ] } diff --git a/ChangeLog b/ChangeLog index 345b67edb..2c2ea7beb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,7 @@ Enhancements: - #7461 More robust `choco` install and helpful `install_deps.py` messages - #7464 Restore `--only-python` arg to only install Python deps - #7465 Also cut `+` char for `SHORT_VERSION` var used on upload +- #7467 Load server or client args from `synergy-config.toml` # 1.15.1 diff --git a/cmake/Libraries.cmake b/cmake/Libraries.cmake index c3955ec92..4c0a0b5a3 100644 --- a/cmake/Libraries.cmake +++ b/cmake/Libraries.cmake @@ -10,9 +10,10 @@ macro(configure_libs) configure_windows_libs() endif() - config_qt() + configure_qt() configure_openssl() configure_coverage() + configure_tomlplusplus() if(BUILD_TESTS) configure_gtest() @@ -124,7 +125,9 @@ macro(configure_unix_libs) set(TIME_WITH_SYS_TIME 1) set(HAVE_SOCKLEN_T 1) - # For config.h, save the results based on a template (config.h.in). + # Unix only: For config.h, save the results based on a template (config.h.in). + # Note that this won't work on Windows because filenames are not case sensitive, + # and we have header files named "Config.h" (upper case 'C'). configure_file(res/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/lib/config.h) add_definitions(-DSYSAPI_UNIX=1 -DHAVE_CONFIG_H) @@ -361,7 +364,7 @@ macro(configure_windows_libs) endmacro() -macro(config_qt) +macro(configure_qt) find_package( Qt6 @@ -523,9 +526,21 @@ macro(configure_wintoast) file(GLOB WINTOAST_DIR ${CMAKE_SOURCE_DIR}/subprojects/WinToast-*) if(WINTOAST_DIR) set(HAVE_WINTOAST true) + add_definitions(-DHAVE_WINTOAST=1) include_directories(${WINTOAST_DIR}/include) else() - message(WARNING "WinToast subproject not found") + message(WARNING "Subproject 'WinToast' not found") endif() endmacro() + +macro(configure_tomlplusplus) + file(GLOB TOMLPLUSPLUS_DIR ${CMAKE_SOURCE_DIR}/subprojects/tomlplusplus-*) + if(TOMLPLUSPLUS_DIR) + set(HAVE_TOMLPLUSPLUS true) + add_definitions(-DHAVE_TOMLPLUSPLUS=1) + include_directories(${TOMLPLUSPLUS_DIR}/include) + else() + message(WARNING "Subproject 'tomlplusplus' not found") + endif() +endmacro() diff --git a/meson.build b/meson.build index b6f12a908..32baeeb5b 100644 --- a/meson.build +++ b/meson.build @@ -10,6 +10,9 @@ else subproject('gtest') endif +# tomlplusplus: Header-only library +subproject('tomlplusplus') + if host_machine.system() == 'windows' # WinToast is a niche lib which is not commonly installed, # so depend only on the subproject. diff --git a/res/config.h.in b/res/config.h.in index d5af31e46..8a040406a 100644 --- a/res/config.h.in +++ b/res/config.h.in @@ -175,11 +175,8 @@ /* Define if libportal has xdp_portal_create_remote_desktop_session_full */ #cmakedefine HAVE_LIBPORTAL_CREATE_REMOTE_DESKTOP_SESSION_FULL ${HAVE_LIBPORTAL_CREATE_REMOTE_DESKTOP_SESSION_FULL} -/* Define if libportal has inputcapture support */ +/* Define if libportal has input capture support */ #cmakedefine HAVE_LIBPORTAL_INPUTCAPTURE ${HAVE_LIBPORTAL_INPUTCAPTURE} /* Define if libei ei_device_start_emulating takes a sequence number */ #cmakedefine HAVE_LIBEI_SEQUENCE_NUMBER ${HAVE_LIBEI_SEQUENCE_NUMBER} - -/* Define if wintoast is available */ -#cmakedefine HAVE_WINTOAST ${HAVE_WINTOAST} diff --git a/scripts/install_deps.py b/scripts/install_deps.py index ac243ae35..64673b2b5 100755 --- a/scripts/install_deps.py +++ b/scripts/install_deps.py @@ -70,7 +70,10 @@ def main(): # either the shell needs to be restarted or the env vars need to be re-sourced. # Restarting the shell is easier for most people. if not env.is_linux(): - print(f"{colors.WARNING_TEXT} Please restart your shells for new env vars") + print( + f"{colors.WARNING_TEXT} You may need to restart your terminal " + "or IDE to use new env vars" + ) # Useful on Windows, when elevated, Python is opened in a new window and closes # immediately after the script finishes. This keeps the script window open so that diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp index 37dbac3ce..7bc78f7fc 100644 --- a/src/lib/server/Config.cpp +++ b/src/lib/server/Config.cpp @@ -30,6 +30,8 @@ using namespace synergy::string; +namespace synergy::server { + // // Config // @@ -2038,3 +2040,5 @@ XConfigRead::~XConfigRead() _NOEXCEPT { String XConfigRead::getWhat() const throw() { return format("XConfigRead", "read error: %{1}", m_error.c_str()); } + +} // namespace synergy::server diff --git a/src/lib/server/Config.h b/src/lib/server/Config.h index f3149f590..e36698d83 100644 --- a/src/lib/server/Config.h +++ b/src/lib/server/Config.h @@ -30,12 +30,15 @@ #include +namespace synergy::server { class Config; class ConfigReadContext; +} // namespace synergy::server + class IEventQueue; namespace std { -template <> struct iterator_traits { +template <> struct iterator_traits { typedef String value_type; typedef ptrdiff_t difference_type; typedef bidirectional_iterator_tag iterator_category; @@ -44,6 +47,8 @@ template <> struct iterator_traits { }; }; // namespace std +namespace synergy::server { + //! Server configuration /*! This class holds server configuration information. That includes @@ -544,3 +549,5 @@ protected: private: String m_error; }; + +} // namespace synergy::server diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 096b16c79..6c3006a40 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or @@ -26,10 +26,7 @@ #include "common/stdexcept.h" #include "license/License.h" #include "mt/Thread.h" -#include "net/IDataSocket.h" -#include "net/IListenSocket.h" #include "net/TCPSocket.h" -#include "net/XSocket.h" #include "server/ClientListener.h" #include "server/ClientProxy.h" #include "server/ClientProxyUnknown.h" @@ -38,12 +35,9 @@ #include "synergy/DropHelper.h" #include "synergy/FileChunk.h" #include "synergy/IPlatformScreen.h" -#include "synergy/KeyState.h" #include "synergy/PacketStreamFilter.h" #include "synergy/Screen.h" #include "synergy/StreamChunker.h" -#include "synergy/XScreen.h" -#include "synergy/XSynergy.h" #include "synergy/option_types.h" #include "synergy/protocol_types.h" @@ -51,17 +45,16 @@ #include #include #include -#include -#include using namespace synergy::license; +using namespace synergy::server; // // Server // Server::Server( - Config &config, PrimaryClient *primaryClient, synergy::Screen *screen, + ServerConfig &config, PrimaryClient *primaryClient, synergy::Screen *screen, IEventQueue *events, synergy::ServerArgs const &args) : m_mock(false), m_primaryClient(primaryClient), @@ -265,7 +258,7 @@ Server::~Server() { removeClient(m_primaryClient); } -bool Server::setConfig(const Config &config) { +bool Server::setConfig(const ServerConfig &config) { // refuse configuration if it doesn't include the primary screen if (!config.isScreen(m_primaryClient->getName())) { return false; @@ -2086,7 +2079,7 @@ void Server::closeClient(BaseClientProxy *client, const char *msg) { forceLeaveClient(client); } -void Server::closeClients(const Config &config) { +void Server::closeClients(const ServerConfig &config) { // collect the clients that are connected but are being dropped // from the configuration (or who's canonical name is changing). typedef std::set RemovedClients; diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index d0beafcc2..89b23dcf1 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or @@ -31,7 +31,6 @@ #include "synergy/ServerArgs.h" #include "synergy/clipboard_types.h" #include "synergy/key_types.h" -#include "synergy/languages/LanguageManager.h" #include "synergy/mouse_types.h" #include @@ -51,6 +50,8 @@ class ClientListener; This class implements the top-level server algorithms for synergy. */ class Server : public INode { + using ServerConfig = synergy::server::Config; + public: //! Lock cursor to screen data class LockCursorToScreenInfo { @@ -110,8 +111,9 @@ public: ownership of \p primaryClient. */ Server( - Config &config, PrimaryClient *primaryClient, synergy::Screen *screen, - IEventQueue *events, synergy::ServerArgs const &args); + ServerConfig &config, PrimaryClient *primaryClient, + synergy::Screen *screen, IEventQueue *events, + synergy::ServerArgs const &args); Server(Server const &) = delete; Server(Server &&) = delete; ~Server(); @@ -133,7 +135,7 @@ public: configuration was accepted (it must include the server's name). This will disconnect any clients no longer in the configuration. */ - bool setConfig(const Config &); + bool setConfig(const ServerConfig &); //! Add a client /*! @@ -351,7 +353,7 @@ private: void closeClient(BaseClientProxy *, const char *msg); // close clients not in \p config - void closeClients(const Config &config); + void closeClients(const ServerConfig &config); // close all clients whether they've completed the handshake or not, // except the primary client @@ -422,7 +424,7 @@ private: SInt32 m_xDelta2, m_yDelta2; // current configuration - Config *m_config; + ServerConfig *m_config; // input filter (from m_config); InputFilter *m_inputFilter; diff --git a/src/lib/synergy/App.cpp b/src/lib/synergy/App.cpp index e30bd6d72..354e77167 100644 --- a/src/lib/synergy/App.cpp +++ b/src/lib/synergy/App.cpp @@ -33,6 +33,7 @@ #include "ipc/IpcMessage.h" #include "ipc/IpcServerProxy.h" #include "synergy/ArgsBase.h" +#include "synergy/Config.h" #include "synergy/XSynergy.h" #include "synergy/protocol_types.h" @@ -43,8 +44,10 @@ #endif #include +#include #include #include +#include #include #include @@ -56,6 +59,10 @@ #include "platform/OSXDragSimulator.h" #endif +const auto kConfigFilename = "synergy-config.toml"; + +using namespace synergy; + App *App::s_instance = nullptr; // @@ -174,8 +181,13 @@ void App::loggingFilterWarning() { } void App::initApp(int argc, const char **argv) { - // parse command line - parseArgs(argc, argv); + + Config config(kConfigFilename, configSection()); + if (config.load(argv[0])) { + parseArgs(config.argc(), config.argv()); + } else { + parseArgs(argc, argv); + } ARCH->setProfileDirectory(argsBase().m_profileDirectory); ARCH->setPluginDirectory(argsBase().m_pluginDirectory); diff --git a/src/lib/synergy/App.h b/src/lib/synergy/App.h index 2ad87afde..9693bed43 100644 --- a/src/lib/synergy/App.h +++ b/src/lib/synergy/App.h @@ -55,66 +55,43 @@ public: App &operator=(App const &) = delete; App &operator=(App &&) = delete; - // Returns args that are common between server and client. - synergy::ArgsBase &argsBase() const { return *m_args; } - - // Prints the current compiled version. - virtual void version(); - - // Prints help specific to client or server. virtual void help() = 0; - - // Parse command line arguments. virtual void parseArgs(int argc, const char *const *argv) = 0; - - int run(int argc, char **argv); - - int daemonMainLoop(int, const char **); - virtual void loadConfig() = 0; virtual bool loadConfig(const String &pathname) = 0; - - // A description of the daemon (used only on Windows). virtual const char *daemonInfo() const = 0; + virtual std::string configSection() const = 0; - // Function pointer for function to exit immediately. - // TODO: this is old C code - use inheritance to normalize - void (*m_bye)(int); + virtual void version(); + virtual void setByeFunc(void (*bye)(int)) { m_bye = bye; } + virtual void bye(int error) { m_bye(error); } + virtual IEventQueue *getEvents() const { return m_events; } + + ARCH_APP_UTIL &appUtil() { return m_appUtil; } + synergy::ArgsBase &argsBase() const { return *m_args; } + int run(int argc, char **argv); + int daemonMainLoop(int, const char **); + void setupFileLogging(); + void loggingFilterWarning(); + void initApp(int argc, const char **argv); + void initApp(int argc, char **argv) { initApp(argc, (const char **)argv); } + void setEvents(EventQueue &events) { m_events = &events; } + void setSocketMultiplexer(SocketMultiplexer *sm) { m_socketMultiplexer = sm; } + + virtual IArchTaskBarReceiver *taskBarReceiver() const { + return m_taskBarReceiver; + } + + SocketMultiplexer *getSocketMultiplexer() const { + return m_socketMultiplexer; + } static App &instance() { assert(s_instance != nullptr); return *s_instance; } - // If --log was specified in args, then add a file logger. - void setupFileLogging(); - - // If messages will be hidden (to improve performance), warn user. - void loggingFilterWarning(); - - // Parses args, sets up file logging, and loads the config. - void initApp(int argc, const char **argv); - - // HACK: accept non-const, but make it const anyway - void initApp(int argc, char **argv) { initApp(argc, (const char **)argv); } - - ARCH_APP_UTIL &appUtil() { return m_appUtil; } - - virtual IArchTaskBarReceiver *taskBarReceiver() const { - return m_taskBarReceiver; - } - - virtual void setByeFunc(void (*bye)(int)) { m_bye = bye; } - virtual void bye(int error) { m_bye(error); } - - virtual IEventQueue *getEvents() const { return m_events; } - - void setSocketMultiplexer(SocketMultiplexer *sm) { m_socketMultiplexer = sm; } - SocketMultiplexer *getSocketMultiplexer() const { - return m_socketMultiplexer; - } - - void setEvents(EventQueue &events) { m_events = &events; } + void (*m_bye)(int); private: void handleIpcMessage(const Event &, void *); @@ -144,18 +121,24 @@ public: virtual ~MinimalApp(); // IApp overrides - virtual int standardStartup(int argc, char **argv); + virtual int standardStartup(int argc, char **argv) override; virtual int runInner( - int argc, char **argv, ILogOutputter *outputter, StartupFunc startup); - virtual void startNode(); - virtual int mainLoop(); - virtual int foregroundStartup(int argc, char **argv); - virtual synergy::Screen *createScreen(); - virtual void loadConfig(); - virtual bool loadConfig(const String &pathname); - virtual const char *daemonInfo() const; - virtual const char *daemonName() const; - virtual void parseArgs(int argc, const char *const *argv); + int argc, char **argv, ILogOutputter *outputter, + StartupFunc startup) override; + virtual void startNode() override; + virtual int mainLoop() override; + virtual int foregroundStartup(int argc, char **argv) override; + virtual synergy::Screen *createScreen() override; + virtual void loadConfig() override; + virtual bool loadConfig(const String &pathname) override; + virtual const char *daemonInfo() const override; + virtual const char *daemonName() const override; + virtual void parseArgs(int argc, const char *const *argv) override; + + // + // App overrides + // + std::string configSection() const override { return ""; } private: Arch m_arch; diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 675af370b..641dc55a3 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -298,8 +298,9 @@ bool ArgParser::parseDeprecatedArgs(int argc, const char *const *argv, int &i) { bool ArgParser::isArg( int argi, int argc, const char *const *argv, const char *name1, const char *name2, int minRequiredParameters) { - if ((name1 != nullptr && strcmp(argv[argi], name1) == 0) || - (name2 != nullptr && strcmp(argv[argi], name2) == 0)) { + const auto match1 = (name1 != nullptr && strcmp(argv[argi], name1) == 0); + const auto match2 = (name2 != nullptr && strcmp(argv[argi], name2) == 0); + if (match1 || match2) { // match. check args left. if (argi + minRequiredParameters >= argc) { LOG( diff --git a/src/lib/synergy/ClientApp.cpp b/src/lib/synergy/ClientApp.cpp index d96ae2ad2..5072a122a 100644 --- a/src/lib/synergy/ClientApp.cpp +++ b/src/lib/synergy/ClientApp.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or @@ -21,16 +21,11 @@ #include "arch/Arch.h" #include "arch/IArchTaskBarReceiver.h" #include "base/Event.h" -#include "base/EventQueue.h" #include "base/IEventQueue.h" #include "base/Log.h" #include "base/String.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" -#include "base/log_outputters.h" #include "client/Client.h" -#include "common/constants.h" -#include "mt/Thread.h" #include "net/InverseSockets/InverseSocketFactory.h" #include "net/NetworkAddress.h" #include "net/SocketMultiplexer.h" @@ -50,18 +45,27 @@ #if WINAPI_MSWINDOWS #include "platform/MSWindowsScreen.h" #endif + #if WINAPI_XWINDOWS #include "platform/XWindowsScreen.h" #endif + #if WINAPI_LIBEI #include "platform/EiScreen.h" #endif + #if WINAPI_CARBON +#include "platform/OSXDragSimulator.h" #include "platform/OSXScreen.h" #endif -#if defined(__APPLE__) -#include "platform/OSXDragSimulator.h" +#if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI) +#include "platform/wayland.h" +#endif + +#if defined(MAC_OS_X_VERSION_10_7) +#include "base/TMethodJob.h" +#include "mt/Thread.h" #endif #include diff --git a/src/lib/synergy/ClientApp.h b/src/lib/synergy/ClientApp.h index 8a9293717..60688fae8 100644 --- a/src/lib/synergy/ClientApp.h +++ b/src/lib/synergy/ClientApp.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or @@ -39,29 +39,35 @@ public: IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver); virtual ~ClientApp(); - // Parse client specific command line arguments. - void parseArgs(int argc, const char *const *argv); + // + // IApp overrides + // - // Prints help specific to client. - void help(); - - // Returns arguments that are common and for client. - synergy::ClientArgs &args() const { - return (synergy::ClientArgs &)argsBase(); - } - - const char *daemonName() const; - const char *daemonInfo() const; - - // TODO: move to server only (not supported on client) - void loadConfig() {} - bool loadConfig(const String &pathname) { return false; } - - int foregroundStartup(int argc, char **argv); - int standardStartup(int argc, char **argv); + void parseArgs(int argc, const char *const *argv) override; + void help() override; + const char *daemonName() const override; + const char *daemonInfo() const override; + void loadConfig() override {} + bool loadConfig(const String &pathname) override { return false; } + int foregroundStartup(int argc, char **argv) override; + int standardStartup(int argc, char **argv) override; int runInner( - int argc, char **argv, ILogOutputter *outputter, StartupFunc startup); - synergy::Screen *createScreen(); + int argc, char **argv, ILogOutputter *outputter, + StartupFunc startup) override; + synergy::Screen *createScreen() override; + int mainLoop() override; + void startNode() override; + + // + // App overrides + // + + std::string configSection() const override { return "client"; } + + // + // Regular functions + // + void updateStatus(); void updateStatus(const String &msg); void resetRestartTimeout(); @@ -81,13 +87,18 @@ public: void closeClient(Client *client); bool startClient(); void stopClient(); - int mainLoop(); - void startNode(); + Client *getClientPtr() { return m_client; } + + synergy::ClientArgs &args() const { + return (synergy::ClientArgs &)argsBase(); + } + + // + // Static functions + // static ClientApp &instance() { return (ClientApp &)App::instance(); } - Client *getClientPtr() { return m_client; } - private: ISocketFactory *getSocketFactory() const; diff --git a/src/lib/synergy/Config.cpp b/src/lib/synergy/Config.cpp new file mode 100644 index 000000000..b5f2d1543 --- /dev/null +++ b/src/lib/synergy/Config.cpp @@ -0,0 +1,110 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "Config.h" + +#include "base/Log.h" + +#include +#include +#include +#include + +#if HAVE_TOMLPLUSPLUS +#include +#endif + +namespace synergy { + +Config::Config(const std::string &filename, const std::string §ion) + : m_filename(filename), + m_section(section) {} + +const char *const *Config::argv() const { return m_argv.data(); } + +int Config::argc() const { return static_cast(m_argv.size()); } + +bool Config::load(const std::string &firstArg) { + +#if HAVE_TOMLPLUSPLUS + m_args.push_back(firstArg); + + if (m_filename.empty() || !std::filesystem::exists(m_filename)) { + LOG((CLOG_DEBUG "no config file at: %s", m_filename.c_str())); + return false; + } + + toml::table configTable; + try { + LOG((CLOG_INFO "loading config file: %s", m_filename.c_str())); + configTable = toml::parse_file(m_filename); + + } catch (const toml::parse_error &err) { + LOG((CLOG_ERR "toml parse error: %s", err.what())); + throw ParseError(); + } + + if (!configTable.contains(m_section)) { + LOG((CLOG_WARN "no %s section found in config file", m_section.c_str())); + return false; + } + + const auto §ion = configTable[m_section]; + const auto args = section["args"]; + if (!args.is_table()) { + LOG((CLOG_WARN "no args table found in config file")); + return false; + } + + std::string specialLastArg = ""; + const auto &table = *(args.as_table()); + for (const auto &pair : table) { + const auto &key = pair.first; + if (key.str() == "_last") { + specialLastArg = pair.second.as_string()->get(); + continue; + } + + m_args.push_back("--" + std::string(key.str())); + + if (pair.second.is_string()) { + const auto value = pair.second.as_string()->get(); + m_args.push_back(value); + } + } + + if (!specialLastArg.empty()) { + m_args.push_back(specialLastArg); + } + + if (m_args.empty()) { + LOG((CLOG_WARN "no args loaded from config file")); + return false; + } + + for (const auto &arg : m_args) { + m_argv.push_back(arg.c_str()); + } + + return true; +#else + LOG((CLOG_WARN "toml++ not available, config file not loaded")); + return false; +#endif // HAVE_TOMLPLUSPLUS +} + +} // namespace synergy diff --git a/src/lib/synergy/Config.h b/src/lib/synergy/Config.h new file mode 100644 index 000000000..d7542684e --- /dev/null +++ b/src/lib/synergy/Config.h @@ -0,0 +1,53 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +namespace synergy { + +//! App configuration +/*! +Parses a configuration file describing start args and potentially other +configuration options in future. The configuration file is in TOML format. + +Initially this class was created to as a developer convenience; it is a +convenient place to specify args without needing to fiddle with IDE configs. +*/ +class Config { + class ParseError : public std::runtime_error { + public: + explicit ParseError() : std::runtime_error("failed to parse config file") {} + }; + +public: + explicit Config(const std::string &filename, const std::string §ion); + bool load(const std::string &firstArg); + const char *const *argv() const; + int argc() const; + +private: + std::string m_filename; + std::string m_section; + std::vector m_args; + std::vector m_argv; +}; + +} // namespace synergy diff --git a/src/lib/synergy/ServerApp.cpp b/src/lib/synergy/ServerApp.cpp index 35d7500b8..7fee752d7 100644 --- a/src/lib/synergy/ServerApp.cpp +++ b/src/lib/synergy/ServerApp.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or @@ -19,22 +19,17 @@ #include "synergy/ServerApp.h" #include "arch/Arch.h" -#include "base/EventQueue.h" -#include "base/FunctionEventJob.h" #include "base/IEventQueue.h" #include "base/Log.h" #include "base/Path.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" -#include "base/log_outputters.h" -#include "common/constants.h" #include "net/InverseSockets/InverseSocketFactory.h" #include "net/SocketMultiplexer.h" #include "net/TCPSocketFactory.h" #include "net/XSocket.h" -#include "platform/wayland.h" #include "server/ClientListener.h" #include "server/ClientProxy.h" +#include "server/Config.h" #include "server/PrimaryClient.h" #include "server/Server.h" #include "synergy/App.h" @@ -51,18 +46,27 @@ #if WINAPI_MSWINDOWS #include "platform/MSWindowsScreen.h" #endif + #if WINAPI_XWINDOWS #include "platform/XWindowsScreen.h" #endif + #if WINAPI_LIBEI #include "platform/EiScreen.h" #endif + #if WINAPI_CARBON +#include "platform/OSXDragSimulator.h" #include "platform/OSXScreen.h" #endif -#if defined(__APPLE__) -#include "platform/OSXDragSimulator.h" +#if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI) +#include "platform/wayland.h" +#endif + +#if defined(MAC_OS_X_VERSION_10_7) +#include "base/TMethodJob.h" +#include "mt/Thread.h" #endif #include @@ -70,6 +74,8 @@ #include #include +using namespace synergy::server; + // // ServerApp // @@ -88,6 +94,7 @@ ServerApp::ServerApp( ServerApp::~ServerApp() {} void ServerApp::parseArgs(int argc, const char *const *argv) { + ArgParser argParser(this); bool result = argParser.parseServerArgs(args(), argc, argv); @@ -184,16 +191,18 @@ void ServerApp::reloadConfig(const Event &, void *) { void ServerApp::loadConfig() { bool loaded = false; + std::string path; // load the config file, if specified if (!args().m_configFile.empty()) { - loaded = loadConfig(args().m_configFile); + path = args().m_configFile; + loaded = loadConfig(path); } // load the default configuration if no explicit file given else { // get the user's home directory - String path = ARCH->getUserDirectory(); + path = ARCH->getUserDirectory(); if (!path.empty()) { // complete path path = ARCH->concatPath(path, USR_CONFIG_NAME); @@ -218,7 +227,9 @@ void ServerApp::loadConfig() { } if (!loaded) { - LOG((CLOG_CRIT "%s: no configuration available", args().m_pname)); + LOG( + (CLOG_CRIT "%s: failed to load config: %s", args().m_pname, + path.c_str())); m_bye(kExitConfig); } } @@ -607,7 +618,8 @@ ClientListener *ServerApp::openClientListener(const NetworkAddress &address) { return listen; } -Server *ServerApp::openServer(Config &config, PrimaryClient *primaryClient) { +Server * +ServerApp::openServer(ServerConfig &config, PrimaryClient *primaryClient) { Server *server = new Server(config, primaryClient, m_serverScreen, m_events, args()); try { diff --git a/src/lib/synergy/ServerApp.h b/src/lib/synergy/ServerApp.h index 3fcd750df..4d7828e19 100644 --- a/src/lib/synergy/ServerApp.h +++ b/src/lib/synergy/ServerApp.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or @@ -25,9 +25,6 @@ #include "net/NetworkAddress.h" #include "server/Config.h" #include "synergy/App.h" -#include "synergy/ArgsBase.h" - -#include enum EServerState { kUninitialized, @@ -53,31 +50,43 @@ class ServerArgs; } class ServerApp : public App { + using ServerConfig = synergy::server::Config; + public: ServerApp( IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver); virtual ~ServerApp(); - // Parse server specific command line arguments. - void parseArgs(int argc, const char *const *argv); + // + // IApp overrides + // - // Prints help specific to server. - void help(); + void parseArgs(int argc, const char *const *argv) override; + void help() override; + const char *daemonName() const override; + const char *daemonInfo() const override; + void loadConfig() override; + bool loadConfig(const String &pathname) override; + synergy::Screen *createScreen() override; + int mainLoop() override; + int runInner( + int argc, char **argv, ILogOutputter *outputter, + StartupFunc startup) override; + int standardStartup(int argc, char **argv) override; + int foregroundStartup(int argc, char **argv) override; + void startNode() override; - // Returns arguments that are common and for server. - synergy::ServerArgs &args() const { - return (synergy::ServerArgs &)argsBase(); - } + // + // App overrides + // - const char *daemonName() const; - const char *daemonInfo() const; + std::string configSection() const override { return "server"; } - // TODO: Document these functions. - static void reloadSignalHandler(Arch::ESignal, void *); + // + // Regular functions + // void reloadConfig(const Event &, void *); - void loadConfig(); - bool loadConfig(const String &pathname); void forceReconnect(const Event &, void *); void resetServer(const Event &, void *); void handleClientConnected(const Event &, void *vlistener); @@ -94,25 +103,31 @@ public: bool initServer(); void retryHandler(const Event &, void *); synergy::Screen *openServerScreen(); - synergy::Screen *createScreen(); PrimaryClient *openPrimaryClient(const String &name, synergy::Screen *screen); void handleScreenError(const Event &, void *); void handleSuspend(const Event &, void *); void handleResume(const Event &, void *); ClientListener *openClientListener(const NetworkAddress &address); - Server *openServer(Config &config, PrimaryClient *primaryClient); + Server *openServer(ServerConfig &config, PrimaryClient *primaryClient); void handleNoClients(const Event &, void *); bool startServer(); - int mainLoop(); - int runInner( - int argc, char **argv, ILogOutputter *outputter, StartupFunc startup); - int standardStartup(int argc, char **argv); - int foregroundStartup(int argc, char **argv); - void startNode(); + Server *getServerPtr() { return m_server; } + synergy::ServerArgs &args() const { + return (synergy::ServerArgs &)argsBase(); + } + + // + // Static functions + // + + static void reloadSignalHandler(Arch::ESignal, void *); static ServerApp &instance() { return (ServerApp &)App::instance(); } - Server *getServerPtr() { return m_server; } +private: + void handleScreenSwitched(const Event &, void *data); + ISocketFactory *getSocketFactory() const; + NetworkAddress getAddress(const NetworkAddress &address) const; Server *m_server; EServerState m_serverState; @@ -121,11 +136,6 @@ public: ClientListener *m_listener; EventQueueTimer *m_timer; NetworkAddress *m_synergyAddress; - -private: - void handleScreenSwitched(const Event &, void *data); - ISocketFactory *getSocketFactory() const; - NetworkAddress getAddress(const NetworkAddress &address) const; }; // configuration file name diff --git a/src/lib/synergy/ServerArgs.h b/src/lib/synergy/ServerArgs.h index 5438ea34c..3b280971a 100644 --- a/src/lib/synergy/ServerArgs.h +++ b/src/lib/synergy/ServerArgs.h @@ -27,6 +27,7 @@ namespace synergy { class ServerArgs : public ArgsBase { using License = synergy::license::License; + using Config = synergy::server::Config; public: ServerArgs(); diff --git a/src/test/integtests/synergy/ConfigTests.cpp b/src/test/integtests/synergy/ConfigTests.cpp new file mode 100644 index 000000000..52fbbd59e --- /dev/null +++ b/src/test/integtests/synergy/ConfigTests.cpp @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "synergy/Config.h" + +#include +#include +#include + +using namespace synergy; + +const auto kTestFilename = "tmp/test/test.toml"; + +TEST(ConfigTests, LoadConfigFile) { + std::ofstream testFile(kTestFilename); + testFile << "[test.args]\n" + R"(test-arg = "test opt")"; + testFile.close(); + + try { + Config config(kTestFilename, "test"); + + ASSERT_TRUE(config.load("test")); + ASSERT_EQ(config.argc(), 3); + ASSERT_STREQ(config.argv()[0], "test"); + ASSERT_STREQ(config.argv()[1], "--test-arg"); + ASSERT_STREQ(config.argv()[2], "test opt"); + + } catch (const std::exception &e) { + FAIL() << e.what(); + } + + std::filesystem::remove(kTestFilename); +} diff --git a/src/test/mock/synergy/MockApp.h b/src/test/mock/synergy/MockApp.h index c134ab673..1b09de362 100644 --- a/src/test/mock/synergy/MockApp.h +++ b/src/test/mock/synergy/MockApp.h @@ -41,4 +41,5 @@ public: MOCK_METHOD(int, mainLoop, (), (override)); MOCK_METHOD(int, foregroundStartup, (int, char **), (override)); MOCK_METHOD(synergy::Screen *, createScreen, (), (override)); + MOCK_METHOD(std::string, configSection, (), (const, override)); }; diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 62fb4fc35..74e30399f 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -6,6 +6,8 @@ /libportal /gi-docgen /munit +/dotenv-cpp +/tomlplusplus-* # Added by dependencies. /gi-docgen.wrap diff --git a/subprojects/tomlplusplus.wrap b/subprojects/tomlplusplus.wrap new file mode 100644 index 000000000..97ff296fe --- /dev/null +++ b/subprojects/tomlplusplus.wrap @@ -0,0 +1,10 @@ +[wrap-file] +directory = tomlplusplus-3.4.0 +source_url = https://github.com/marzer/tomlplusplus/archive/v3.4.0.tar.gz +source_filename = tomlplusplus-3.4.0.tar.gz +source_hash = 8517f65938a4faae9ccf8ebb36631a38c1cadfb5efa85d9a72e15b9e97d25155 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/tomlplusplus_3.4.0-1/tomlplusplus-3.4.0.tar.gz +wrapdb_version = 3.4.0-1 + +[provide] +dependency_names = tomlplusplus