Load server or client args from synergy-config.toml (#7467)

* Load args from .toml file

* Remove .toml files

* Handle _ arg as no key

* Remove dotenv-cpp

* Treat special last arg specially

* Improve restart warning

* Remove rogue include

* Move TOML config loader to new class

* Merge config files into one

* Add override to virtual functions

* Tidy up App decl and add missing overrides

* Add comments to organize function types

* Fixed: config.h doesn't work on Windows

* Move var position

* Improve includes in app classes

* Update ChangeLog

* Restore position of configure_file call

* Remove new entries in .in file
This commit is contained in:
Nick Bolton 2024-09-02 23:48:57 +01:00 committed by GitHub
parent 4b4bfc4bc8
commit b128863c67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 501 additions and 174 deletions

1
.gitignore vendored
View file

@ -9,6 +9,7 @@ Brewfile.lock.json
/.cache /.cache
# typical developer-created files # typical developer-created files
synergy-config.toml
.vscode .vscode
.DS_Store .DS_Store
*.code-workspace *.code-workspace

49
.vscode/launch.json vendored
View file

@ -4,6 +4,7 @@
{ {
"name": "gui unix", "name": "gui unix",
"type": "lldb", "type": "lldb",
"cwd": "${workspaceRoot}",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/build/bin/synergy", "program": "${workspaceFolder}/build/bin/synergy",
"preLaunchTask": "kill-build" "preLaunchTask": "kill-build"
@ -20,6 +21,7 @@
{ {
"name": "unittests unix", "name": "unittests unix",
"type": "lldb", "type": "lldb",
"cwd": "${workspaceRoot}",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/build/bin/unittests", "program": "${workspaceFolder}/build/bin/unittests",
"preLaunchTask": "build" "preLaunchTask": "build"
@ -27,7 +29,7 @@
{ {
"name": "unittests windows", "name": "unittests windows",
"type": "cppvsdbg", "type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin", "cwd": "${workspaceRoot}",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/build/bin/unittests", "program": "${workspaceFolder}/build/bin/unittests",
"internalConsoleOptions": "openOnSessionStart", "internalConsoleOptions": "openOnSessionStart",
@ -36,6 +38,7 @@
{ {
"name": "integtests unix", "name": "integtests unix",
"type": "lldb", "type": "lldb",
"cwd": "${workspaceRoot}",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/build/bin/integtests", "program": "${workspaceFolder}/build/bin/integtests",
"preLaunchTask": "build" "preLaunchTask": "build"
@ -43,16 +46,50 @@
{ {
"name": "integtests windows", "name": "integtests windows",
"type": "cppvsdbg", "type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin", "cwd": "${workspaceRoot}",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/build/bin/integtests", "program": "${workspaceFolder}/build/bin/integtests",
"internalConsoleOptions": "openOnSessionStart", "internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": "build" "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", "name": "daemon windows",
"type": "cppvsdbg", "type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin", "cwd": "${workspaceRoot}",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/build/bin/synergyd", "program": "${workspaceFolder}/build/bin/synergyd",
"args": ["-f"], "args": ["-f"],
@ -64,6 +101,12 @@
"type": "cppvsdbg", "type": "cppvsdbg",
"request": "attach", "request": "attach",
"processId": "${command:pickProcess}" "processId": "${command:pickProcess}"
},
{
"name": "unix attach",
"type": "lldb",
"request": "attach",
"pid": "${command:pickProcess}"
} }
] ]
} }

View file

@ -10,6 +10,7 @@ Enhancements:
- #7461 More robust `choco` install and helpful `install_deps.py` messages - #7461 More robust `choco` install and helpful `install_deps.py` messages
- #7464 Restore `--only-python` arg to only install Python deps - #7464 Restore `--only-python` arg to only install Python deps
- #7465 Also cut `+` char for `SHORT_VERSION` var used on upload - #7465 Also cut `+` char for `SHORT_VERSION` var used on upload
- #7467 Load server or client args from `synergy-config.toml`
# 1.15.1 # 1.15.1

View file

@ -10,9 +10,10 @@ macro(configure_libs)
configure_windows_libs() configure_windows_libs()
endif() endif()
config_qt() configure_qt()
configure_openssl() configure_openssl()
configure_coverage() configure_coverage()
configure_tomlplusplus()
if(BUILD_TESTS) if(BUILD_TESTS)
configure_gtest() configure_gtest()
@ -124,7 +125,9 @@ macro(configure_unix_libs)
set(TIME_WITH_SYS_TIME 1) set(TIME_WITH_SYS_TIME 1)
set(HAVE_SOCKLEN_T 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) configure_file(res/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/lib/config.h)
add_definitions(-DSYSAPI_UNIX=1 -DHAVE_CONFIG_H) add_definitions(-DSYSAPI_UNIX=1 -DHAVE_CONFIG_H)
@ -361,7 +364,7 @@ macro(configure_windows_libs)
endmacro() endmacro()
macro(config_qt) macro(configure_qt)
find_package( find_package(
Qt6 Qt6
@ -523,9 +526,21 @@ macro(configure_wintoast)
file(GLOB WINTOAST_DIR ${CMAKE_SOURCE_DIR}/subprojects/WinToast-*) file(GLOB WINTOAST_DIR ${CMAKE_SOURCE_DIR}/subprojects/WinToast-*)
if(WINTOAST_DIR) if(WINTOAST_DIR)
set(HAVE_WINTOAST true) set(HAVE_WINTOAST true)
add_definitions(-DHAVE_WINTOAST=1)
include_directories(${WINTOAST_DIR}/include) include_directories(${WINTOAST_DIR}/include)
else() else()
message(WARNING "WinToast subproject not found") message(WARNING "Subproject 'WinToast' not found")
endif() endif()
endmacro() 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()

View file

@ -10,6 +10,9 @@ else
subproject('gtest') subproject('gtest')
endif endif
# tomlplusplus: Header-only library
subproject('tomlplusplus')
if host_machine.system() == 'windows' if host_machine.system() == 'windows'
# WinToast is a niche lib which is not commonly installed, # WinToast is a niche lib which is not commonly installed,
# so depend only on the subproject. # so depend only on the subproject.

View file

@ -175,11 +175,8 @@
/* Define if libportal has xdp_portal_create_remote_desktop_session_full */ /* 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} #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} #cmakedefine HAVE_LIBPORTAL_INPUTCAPTURE ${HAVE_LIBPORTAL_INPUTCAPTURE}
/* Define if libei ei_device_start_emulating takes a sequence number */ /* Define if libei ei_device_start_emulating takes a sequence number */
#cmakedefine HAVE_LIBEI_SEQUENCE_NUMBER ${HAVE_LIBEI_SEQUENCE_NUMBER} #cmakedefine HAVE_LIBEI_SEQUENCE_NUMBER ${HAVE_LIBEI_SEQUENCE_NUMBER}
/* Define if wintoast is available */
#cmakedefine HAVE_WINTOAST ${HAVE_WINTOAST}

View file

@ -70,7 +70,10 @@ def main():
# either the shell needs to be restarted or the env vars need to be re-sourced. # either the shell needs to be restarted or the env vars need to be re-sourced.
# Restarting the shell is easier for most people. # Restarting the shell is easier for most people.
if not env.is_linux(): 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 # 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 # immediately after the script finishes. This keeps the script window open so that

View file

@ -30,6 +30,8 @@
using namespace synergy::string; using namespace synergy::string;
namespace synergy::server {
// //
// Config // Config
// //
@ -2038,3 +2040,5 @@ XConfigRead::~XConfigRead() _NOEXCEPT {
String XConfigRead::getWhat() const throw() { String XConfigRead::getWhat() const throw() {
return format("XConfigRead", "read error: %{1}", m_error.c_str()); return format("XConfigRead", "read error: %{1}", m_error.c_str());
} }
} // namespace synergy::server

View file

@ -30,12 +30,15 @@
#include <iosfwd> #include <iosfwd>
namespace synergy::server {
class Config; class Config;
class ConfigReadContext; class ConfigReadContext;
} // namespace synergy::server
class IEventQueue; class IEventQueue;
namespace std { namespace std {
template <> struct iterator_traits<Config> { template <> struct iterator_traits<synergy::server::Config> {
typedef String value_type; typedef String value_type;
typedef ptrdiff_t difference_type; typedef ptrdiff_t difference_type;
typedef bidirectional_iterator_tag iterator_category; typedef bidirectional_iterator_tag iterator_category;
@ -44,6 +47,8 @@ template <> struct iterator_traits<Config> {
}; };
}; // namespace std }; // namespace std
namespace synergy::server {
//! Server configuration //! Server configuration
/*! /*!
This class holds server configuration information. That includes This class holds server configuration information. That includes
@ -544,3 +549,5 @@ protected:
private: private:
String m_error; String m_error;
}; };
} // namespace synergy::server

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman * Copyright (C) 2002 Chris Schoeneman
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
@ -26,10 +26,7 @@
#include "common/stdexcept.h" #include "common/stdexcept.h"
#include "license/License.h" #include "license/License.h"
#include "mt/Thread.h" #include "mt/Thread.h"
#include "net/IDataSocket.h"
#include "net/IListenSocket.h"
#include "net/TCPSocket.h" #include "net/TCPSocket.h"
#include "net/XSocket.h"
#include "server/ClientListener.h" #include "server/ClientListener.h"
#include "server/ClientProxy.h" #include "server/ClientProxy.h"
#include "server/ClientProxyUnknown.h" #include "server/ClientProxyUnknown.h"
@ -38,12 +35,9 @@
#include "synergy/DropHelper.h" #include "synergy/DropHelper.h"
#include "synergy/FileChunk.h" #include "synergy/FileChunk.h"
#include "synergy/IPlatformScreen.h" #include "synergy/IPlatformScreen.h"
#include "synergy/KeyState.h"
#include "synergy/PacketStreamFilter.h" #include "synergy/PacketStreamFilter.h"
#include "synergy/Screen.h" #include "synergy/Screen.h"
#include "synergy/StreamChunker.h" #include "synergy/StreamChunker.h"
#include "synergy/XScreen.h"
#include "synergy/XSynergy.h"
#include "synergy/option_types.h" #include "synergy/option_types.h"
#include "synergy/protocol_types.h" #include "synergy/protocol_types.h"
@ -51,17 +45,16 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <fstream>
#include <sstream>
using namespace synergy::license; using namespace synergy::license;
using namespace synergy::server;
// //
// Server // Server
// //
Server::Server( Server::Server(
Config &config, PrimaryClient *primaryClient, synergy::Screen *screen, ServerConfig &config, PrimaryClient *primaryClient, synergy::Screen *screen,
IEventQueue *events, synergy::ServerArgs const &args) IEventQueue *events, synergy::ServerArgs const &args)
: m_mock(false), : m_mock(false),
m_primaryClient(primaryClient), m_primaryClient(primaryClient),
@ -265,7 +258,7 @@ Server::~Server() {
removeClient(m_primaryClient); 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 // refuse configuration if it doesn't include the primary screen
if (!config.isScreen(m_primaryClient->getName())) { if (!config.isScreen(m_primaryClient->getName())) {
return false; return false;
@ -2086,7 +2079,7 @@ void Server::closeClient(BaseClientProxy *client, const char *msg) {
forceLeaveClient(client); forceLeaveClient(client);
} }
void Server::closeClients(const Config &config) { void Server::closeClients(const ServerConfig &config) {
// collect the clients that are connected but are being dropped // collect the clients that are connected but are being dropped
// from the configuration (or who's canonical name is changing). // from the configuration (or who's canonical name is changing).
typedef std::set<BaseClientProxy *> RemovedClients; typedef std::set<BaseClientProxy *> RemovedClients;

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman * Copyright (C) 2002 Chris Schoeneman
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
@ -31,7 +31,6 @@
#include "synergy/ServerArgs.h" #include "synergy/ServerArgs.h"
#include "synergy/clipboard_types.h" #include "synergy/clipboard_types.h"
#include "synergy/key_types.h" #include "synergy/key_types.h"
#include "synergy/languages/LanguageManager.h"
#include "synergy/mouse_types.h" #include "synergy/mouse_types.h"
#include <memory> #include <memory>
@ -51,6 +50,8 @@ class ClientListener;
This class implements the top-level server algorithms for synergy. This class implements the top-level server algorithms for synergy.
*/ */
class Server : public INode { class Server : public INode {
using ServerConfig = synergy::server::Config;
public: public:
//! Lock cursor to screen data //! Lock cursor to screen data
class LockCursorToScreenInfo { class LockCursorToScreenInfo {
@ -110,8 +111,9 @@ public:
ownership of \p primaryClient. ownership of \p primaryClient.
*/ */
Server( Server(
Config &config, PrimaryClient *primaryClient, synergy::Screen *screen, ServerConfig &config, PrimaryClient *primaryClient,
IEventQueue *events, synergy::ServerArgs const &args); synergy::Screen *screen, IEventQueue *events,
synergy::ServerArgs const &args);
Server(Server const &) = delete; Server(Server const &) = delete;
Server(Server &&) = delete; Server(Server &&) = delete;
~Server(); ~Server();
@ -133,7 +135,7 @@ public:
configuration was accepted (it must include the server's name). configuration was accepted (it must include the server's name).
This will disconnect any clients no longer in the configuration. This will disconnect any clients no longer in the configuration.
*/ */
bool setConfig(const Config &); bool setConfig(const ServerConfig &);
//! Add a client //! Add a client
/*! /*!
@ -351,7 +353,7 @@ private:
void closeClient(BaseClientProxy *, const char *msg); void closeClient(BaseClientProxy *, const char *msg);
// close clients not in \p config // 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, // close all clients whether they've completed the handshake or not,
// except the primary client // except the primary client
@ -422,7 +424,7 @@ private:
SInt32 m_xDelta2, m_yDelta2; SInt32 m_xDelta2, m_yDelta2;
// current configuration // current configuration
Config *m_config; ServerConfig *m_config;
// input filter (from m_config); // input filter (from m_config);
InputFilter *m_inputFilter; InputFilter *m_inputFilter;

View file

@ -33,6 +33,7 @@
#include "ipc/IpcMessage.h" #include "ipc/IpcMessage.h"
#include "ipc/IpcServerProxy.h" #include "ipc/IpcServerProxy.h"
#include "synergy/ArgsBase.h" #include "synergy/ArgsBase.h"
#include "synergy/Config.h"
#include "synergy/XSynergy.h" #include "synergy/XSynergy.h"
#include "synergy/protocol_types.h" #include "synergy/protocol_types.h"
@ -43,8 +44,10 @@
#endif #endif
#include <charconv> #include <charconv>
#include <filesystem>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <stdexcept>
#include <stdio.h> #include <stdio.h>
#include <vector> #include <vector>
@ -56,6 +59,10 @@
#include "platform/OSXDragSimulator.h" #include "platform/OSXDragSimulator.h"
#endif #endif
const auto kConfigFilename = "synergy-config.toml";
using namespace synergy;
App *App::s_instance = nullptr; App *App::s_instance = nullptr;
// //
@ -174,8 +181,13 @@ void App::loggingFilterWarning() {
} }
void App::initApp(int argc, const char **argv) { void App::initApp(int argc, const char **argv) {
// parse command line
Config config(kConfigFilename, configSection());
if (config.load(argv[0])) {
parseArgs(config.argc(), config.argv());
} else {
parseArgs(argc, argv); parseArgs(argc, argv);
}
ARCH->setProfileDirectory(argsBase().m_profileDirectory); ARCH->setProfileDirectory(argsBase().m_profileDirectory);
ARCH->setPluginDirectory(argsBase().m_pluginDirectory); ARCH->setPluginDirectory(argsBase().m_pluginDirectory);

View file

@ -55,66 +55,43 @@ public:
App &operator=(App const &) = delete; App &operator=(App const &) = delete;
App &operator=(App &&) = 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; virtual void help() = 0;
// Parse command line arguments.
virtual void parseArgs(int argc, const char *const *argv) = 0; 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 void loadConfig() = 0;
virtual bool loadConfig(const String &pathname) = 0; virtual bool loadConfig(const String &pathname) = 0;
// A description of the daemon (used only on Windows).
virtual const char *daemonInfo() const = 0; virtual const char *daemonInfo() const = 0;
virtual std::string configSection() const = 0;
// Function pointer for function to exit immediately. virtual void version();
// TODO: this is old C code - use inheritance to normalize virtual void setByeFunc(void (*bye)(int)) { m_bye = bye; }
void (*m_bye)(int); 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() { static App &instance() {
assert(s_instance != nullptr); assert(s_instance != nullptr);
return *s_instance; return *s_instance;
} }
// If --log was specified in args, then add a file logger. void (*m_bye)(int);
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; }
private: private:
void handleIpcMessage(const Event &, void *); void handleIpcMessage(const Event &, void *);
@ -144,18 +121,24 @@ public:
virtual ~MinimalApp(); virtual ~MinimalApp();
// IApp overrides // IApp overrides
virtual int standardStartup(int argc, char **argv); virtual int standardStartup(int argc, char **argv) override;
virtual int runInner( virtual int runInner(
int argc, char **argv, ILogOutputter *outputter, StartupFunc startup); int argc, char **argv, ILogOutputter *outputter,
virtual void startNode(); StartupFunc startup) override;
virtual int mainLoop(); virtual void startNode() override;
virtual int foregroundStartup(int argc, char **argv); virtual int mainLoop() override;
virtual synergy::Screen *createScreen(); virtual int foregroundStartup(int argc, char **argv) override;
virtual void loadConfig(); virtual synergy::Screen *createScreen() override;
virtual bool loadConfig(const String &pathname); virtual void loadConfig() override;
virtual const char *daemonInfo() const; virtual bool loadConfig(const String &pathname) override;
virtual const char *daemonName() const; virtual const char *daemonInfo() const override;
virtual void parseArgs(int argc, const char *const *argv); 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: private:
Arch m_arch; Arch m_arch;

View file

@ -298,8 +298,9 @@ bool ArgParser::parseDeprecatedArgs(int argc, const char *const *argv, int &i) {
bool ArgParser::isArg( bool ArgParser::isArg(
int argi, int argc, const char *const *argv, const char *name1, int argi, int argc, const char *const *argv, const char *name1,
const char *name2, int minRequiredParameters) { const char *name2, int minRequiredParameters) {
if ((name1 != nullptr && strcmp(argv[argi], name1) == 0) || const auto match1 = (name1 != nullptr && strcmp(argv[argi], name1) == 0);
(name2 != nullptr && strcmp(argv[argi], name2) == 0)) { const auto match2 = (name2 != nullptr && strcmp(argv[argi], name2) == 0);
if (match1 || match2) {
// match. check args left. // match. check args left.
if (argi + minRequiredParameters >= argc) { if (argi + minRequiredParameters >= argc) {
LOG( LOG(

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman * Copyright (C) 2002 Chris Schoeneman
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
@ -21,16 +21,11 @@
#include "arch/Arch.h" #include "arch/Arch.h"
#include "arch/IArchTaskBarReceiver.h" #include "arch/IArchTaskBarReceiver.h"
#include "base/Event.h" #include "base/Event.h"
#include "base/EventQueue.h"
#include "base/IEventQueue.h" #include "base/IEventQueue.h"
#include "base/Log.h" #include "base/Log.h"
#include "base/String.h" #include "base/String.h"
#include "base/TMethodEventJob.h" #include "base/TMethodEventJob.h"
#include "base/TMethodJob.h"
#include "base/log_outputters.h"
#include "client/Client.h" #include "client/Client.h"
#include "common/constants.h"
#include "mt/Thread.h"
#include "net/InverseSockets/InverseSocketFactory.h" #include "net/InverseSockets/InverseSocketFactory.h"
#include "net/NetworkAddress.h" #include "net/NetworkAddress.h"
#include "net/SocketMultiplexer.h" #include "net/SocketMultiplexer.h"
@ -50,18 +45,27 @@
#if WINAPI_MSWINDOWS #if WINAPI_MSWINDOWS
#include "platform/MSWindowsScreen.h" #include "platform/MSWindowsScreen.h"
#endif #endif
#if WINAPI_XWINDOWS #if WINAPI_XWINDOWS
#include "platform/XWindowsScreen.h" #include "platform/XWindowsScreen.h"
#endif #endif
#if WINAPI_LIBEI #if WINAPI_LIBEI
#include "platform/EiScreen.h" #include "platform/EiScreen.h"
#endif #endif
#if WINAPI_CARBON #if WINAPI_CARBON
#include "platform/OSXDragSimulator.h"
#include "platform/OSXScreen.h" #include "platform/OSXScreen.h"
#endif #endif
#if defined(__APPLE__) #if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI)
#include "platform/OSXDragSimulator.h" #include "platform/wayland.h"
#endif
#if defined(MAC_OS_X_VERSION_10_7)
#include "base/TMethodJob.h"
#include "mt/Thread.h"
#endif #endif
#include <memory> #include <memory>

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman * Copyright (C) 2002 Chris Schoeneman
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
@ -39,29 +39,35 @@ public:
IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver); IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver);
virtual ~ClientApp(); virtual ~ClientApp();
// Parse client specific command line arguments. //
void parseArgs(int argc, const char *const *argv); // IApp overrides
//
// Prints help specific to client. void parseArgs(int argc, const char *const *argv) override;
void help(); void help() override;
const char *daemonName() const override;
// Returns arguments that are common and for client. const char *daemonInfo() const override;
synergy::ClientArgs &args() const { void loadConfig() override {}
return (synergy::ClientArgs &)argsBase(); bool loadConfig(const String &pathname) override { return false; }
} int foregroundStartup(int argc, char **argv) override;
int standardStartup(int argc, char **argv) override;
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);
int runInner( int runInner(
int argc, char **argv, ILogOutputter *outputter, StartupFunc startup); int argc, char **argv, ILogOutputter *outputter,
synergy::Screen *createScreen(); 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();
void updateStatus(const String &msg); void updateStatus(const String &msg);
void resetRestartTimeout(); void resetRestartTimeout();
@ -81,13 +87,18 @@ public:
void closeClient(Client *client); void closeClient(Client *client);
bool startClient(); bool startClient();
void stopClient(); void stopClient();
int mainLoop(); Client *getClientPtr() { return m_client; }
void startNode();
synergy::ClientArgs &args() const {
return (synergy::ClientArgs &)argsBase();
}
//
// Static functions
//
static ClientApp &instance() { return (ClientApp &)App::instance(); } static ClientApp &instance() { return (ClientApp &)App::instance(); }
Client *getClientPtr() { return m_client; }
private: private:
ISocketFactory *getSocketFactory() const; ISocketFactory *getSocketFactory() const;

110
src/lib/synergy/Config.cpp Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "Config.h"
#include "base/Log.h"
#include <cstring>
#include <filesystem>
#include <string>
#include <vector>
#if HAVE_TOMLPLUSPLUS
#include <toml++/toml.hpp>
#endif
namespace synergy {
Config::Config(const std::string &filename, const std::string &section)
: m_filename(filename),
m_section(section) {}
const char *const *Config::argv() const { return m_argv.data(); }
int Config::argc() const { return static_cast<int>(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 &section = 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

53
src/lib/synergy/Config.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdexcept>
#include <string>
#include <vector>
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 &section);
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<std::string> m_args;
std::vector<const char *> m_argv;
};
} // namespace synergy

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman * Copyright (C) 2002 Chris Schoeneman
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
@ -19,22 +19,17 @@
#include "synergy/ServerApp.h" #include "synergy/ServerApp.h"
#include "arch/Arch.h" #include "arch/Arch.h"
#include "base/EventQueue.h"
#include "base/FunctionEventJob.h"
#include "base/IEventQueue.h" #include "base/IEventQueue.h"
#include "base/Log.h" #include "base/Log.h"
#include "base/Path.h" #include "base/Path.h"
#include "base/TMethodEventJob.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/InverseSockets/InverseSocketFactory.h"
#include "net/SocketMultiplexer.h" #include "net/SocketMultiplexer.h"
#include "net/TCPSocketFactory.h" #include "net/TCPSocketFactory.h"
#include "net/XSocket.h" #include "net/XSocket.h"
#include "platform/wayland.h"
#include "server/ClientListener.h" #include "server/ClientListener.h"
#include "server/ClientProxy.h" #include "server/ClientProxy.h"
#include "server/Config.h"
#include "server/PrimaryClient.h" #include "server/PrimaryClient.h"
#include "server/Server.h" #include "server/Server.h"
#include "synergy/App.h" #include "synergy/App.h"
@ -51,18 +46,27 @@
#if WINAPI_MSWINDOWS #if WINAPI_MSWINDOWS
#include "platform/MSWindowsScreen.h" #include "platform/MSWindowsScreen.h"
#endif #endif
#if WINAPI_XWINDOWS #if WINAPI_XWINDOWS
#include "platform/XWindowsScreen.h" #include "platform/XWindowsScreen.h"
#endif #endif
#if WINAPI_LIBEI #if WINAPI_LIBEI
#include "platform/EiScreen.h" #include "platform/EiScreen.h"
#endif #endif
#if WINAPI_CARBON #if WINAPI_CARBON
#include "platform/OSXDragSimulator.h"
#include "platform/OSXScreen.h" #include "platform/OSXScreen.h"
#endif #endif
#if defined(__APPLE__) #if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI)
#include "platform/OSXDragSimulator.h" #include "platform/wayland.h"
#endif
#if defined(MAC_OS_X_VERSION_10_7)
#include "base/TMethodJob.h"
#include "mt/Thread.h"
#endif #endif
#include <fstream> #include <fstream>
@ -70,6 +74,8 @@
#include <sstream> #include <sstream>
#include <stdio.h> #include <stdio.h>
using namespace synergy::server;
// //
// ServerApp // ServerApp
// //
@ -88,6 +94,7 @@ ServerApp::ServerApp(
ServerApp::~ServerApp() {} ServerApp::~ServerApp() {}
void ServerApp::parseArgs(int argc, const char *const *argv) { void ServerApp::parseArgs(int argc, const char *const *argv) {
ArgParser argParser(this); ArgParser argParser(this);
bool result = argParser.parseServerArgs(args(), argc, argv); bool result = argParser.parseServerArgs(args(), argc, argv);
@ -184,16 +191,18 @@ void ServerApp::reloadConfig(const Event &, void *) {
void ServerApp::loadConfig() { void ServerApp::loadConfig() {
bool loaded = false; bool loaded = false;
std::string path;
// load the config file, if specified // load the config file, if specified
if (!args().m_configFile.empty()) { 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 // load the default configuration if no explicit file given
else { else {
// get the user's home directory // get the user's home directory
String path = ARCH->getUserDirectory(); path = ARCH->getUserDirectory();
if (!path.empty()) { if (!path.empty()) {
// complete path // complete path
path = ARCH->concatPath(path, USR_CONFIG_NAME); path = ARCH->concatPath(path, USR_CONFIG_NAME);
@ -218,7 +227,9 @@ void ServerApp::loadConfig() {
} }
if (!loaded) { 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); m_bye(kExitConfig);
} }
} }
@ -607,7 +618,8 @@ ClientListener *ServerApp::openClientListener(const NetworkAddress &address) {
return listen; return listen;
} }
Server *ServerApp::openServer(Config &config, PrimaryClient *primaryClient) { Server *
ServerApp::openServer(ServerConfig &config, PrimaryClient *primaryClient) {
Server *server = Server *server =
new Server(config, primaryClient, m_serverScreen, m_events, args()); new Server(config, primaryClient, m_serverScreen, m_events, args());
try { try {

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman * Copyright (C) 2002 Chris Schoeneman
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
@ -25,9 +25,6 @@
#include "net/NetworkAddress.h" #include "net/NetworkAddress.h"
#include "server/Config.h" #include "server/Config.h"
#include "synergy/App.h" #include "synergy/App.h"
#include "synergy/ArgsBase.h"
#include <map>
enum EServerState { enum EServerState {
kUninitialized, kUninitialized,
@ -53,31 +50,43 @@ class ServerArgs;
} }
class ServerApp : public App { class ServerApp : public App {
using ServerConfig = synergy::server::Config;
public: public:
ServerApp( ServerApp(
IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver); IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver);
virtual ~ServerApp(); virtual ~ServerApp();
// Parse server specific command line arguments. //
void parseArgs(int argc, const char *const *argv); // IApp overrides
//
// Prints help specific to server. void parseArgs(int argc, const char *const *argv) override;
void help(); 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 { // App overrides
return (synergy::ServerArgs &)argsBase(); //
}
const char *daemonName() const; std::string configSection() const override { return "server"; }
const char *daemonInfo() const;
// TODO: Document these functions. //
static void reloadSignalHandler(Arch::ESignal, void *); // Regular functions
//
void reloadConfig(const Event &, void *); void reloadConfig(const Event &, void *);
void loadConfig();
bool loadConfig(const String &pathname);
void forceReconnect(const Event &, void *); void forceReconnect(const Event &, void *);
void resetServer(const Event &, void *); void resetServer(const Event &, void *);
void handleClientConnected(const Event &, void *vlistener); void handleClientConnected(const Event &, void *vlistener);
@ -94,25 +103,31 @@ public:
bool initServer(); bool initServer();
void retryHandler(const Event &, void *); void retryHandler(const Event &, void *);
synergy::Screen *openServerScreen(); synergy::Screen *openServerScreen();
synergy::Screen *createScreen();
PrimaryClient *openPrimaryClient(const String &name, synergy::Screen *screen); PrimaryClient *openPrimaryClient(const String &name, synergy::Screen *screen);
void handleScreenError(const Event &, void *); void handleScreenError(const Event &, void *);
void handleSuspend(const Event &, void *); void handleSuspend(const Event &, void *);
void handleResume(const Event &, void *); void handleResume(const Event &, void *);
ClientListener *openClientListener(const NetworkAddress &address); ClientListener *openClientListener(const NetworkAddress &address);
Server *openServer(Config &config, PrimaryClient *primaryClient); Server *openServer(ServerConfig &config, PrimaryClient *primaryClient);
void handleNoClients(const Event &, void *); void handleNoClients(const Event &, void *);
bool startServer(); bool startServer();
int mainLoop(); Server *getServerPtr() { return m_server; }
int runInner(
int argc, char **argv, ILogOutputter *outputter, StartupFunc startup);
int standardStartup(int argc, char **argv);
int foregroundStartup(int argc, char **argv);
void startNode();
synergy::ServerArgs &args() const {
return (synergy::ServerArgs &)argsBase();
}
//
// Static functions
//
static void reloadSignalHandler(Arch::ESignal, void *);
static ServerApp &instance() { return (ServerApp &)App::instance(); } 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; Server *m_server;
EServerState m_serverState; EServerState m_serverState;
@ -121,11 +136,6 @@ public:
ClientListener *m_listener; ClientListener *m_listener;
EventQueueTimer *m_timer; EventQueueTimer *m_timer;
NetworkAddress *m_synergyAddress; NetworkAddress *m_synergyAddress;
private:
void handleScreenSwitched(const Event &, void *data);
ISocketFactory *getSocketFactory() const;
NetworkAddress getAddress(const NetworkAddress &address) const;
}; };
// configuration file name // configuration file name

View file

@ -27,6 +27,7 @@ namespace synergy {
class ServerArgs : public ArgsBase { class ServerArgs : public ArgsBase {
using License = synergy::license::License; using License = synergy::license::License;
using Config = synergy::server::Config;
public: public:
ServerArgs(); ServerArgs();

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "synergy/Config.h"
#include <filesystem>
#include <fstream>
#include <gtest/gtest.h>
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);
}

View file

@ -41,4 +41,5 @@ public:
MOCK_METHOD(int, mainLoop, (), (override)); MOCK_METHOD(int, mainLoop, (), (override));
MOCK_METHOD(int, foregroundStartup, (int, char **), (override)); MOCK_METHOD(int, foregroundStartup, (int, char **), (override));
MOCK_METHOD(synergy::Screen *, createScreen, (), (override)); MOCK_METHOD(synergy::Screen *, createScreen, (), (override));
MOCK_METHOD(std::string, configSection, (), (const, override));
}; };

View file

@ -6,6 +6,8 @@
/libportal /libportal
/gi-docgen /gi-docgen
/munit /munit
/dotenv-cpp
/tomlplusplus-*
# Added by dependencies. # Added by dependencies.
/gi-docgen.wrap /gi-docgen.wrap

View file

@ -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