aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/barrier
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@ubuntu.com>2018-04-25 18:07:30 -0400
committerLibravatarUnit 193 <unit193@ubuntu.com>2018-04-25 18:07:30 -0400
commit9b1b081cfdb1c0fb6457278775e0823f8bc10f62 (patch)
treece8840148d8445055ba9e4f12263b2208f234c16 /src/lib/barrier
Import Upstream version 2.0.0+dfsgupstream/2.0.0+dfsg
Diffstat (limited to 'src/lib/barrier')
-rw-r--r--src/lib/barrier/App.cpp329
-rw-r--r--src/lib/barrier/App.h200
-rw-r--r--src/lib/barrier/AppUtil.cpp52
-rw-r--r--src/lib/barrier/AppUtil.h40
-rw-r--r--src/lib/barrier/ArgParser.cpp519
-rw-r--r--src/lib/barrier/ArgParser.h63
-rw-r--r--src/lib/barrier/ArgsBase.cpp53
-rw-r--r--src/lib/barrier/ArgsBase.h54
-rw-r--r--src/lib/barrier/CMakeLists.txt40
-rw-r--r--src/lib/barrier/Chunk.cpp30
-rw-r--r--src/lib/barrier/Chunk.h30
-rw-r--r--src/lib/barrier/ClientApp.cpp560
-rw-r--r--src/lib/barrier/ClientApp.h83
-rw-r--r--src/lib/barrier/ClientArgs.cpp23
-rw-r--r--src/lib/barrier/ClientArgs.h30
-rw-r--r--src/lib/barrier/ClientTaskBarReceiver.cpp141
-rw-r--r--src/lib/barrier/ClientTaskBarReceiver.h95
-rw-r--r--src/lib/barrier/Clipboard.cpp118
-rw-r--r--src/lib/barrier/Clipboard.h71
-rw-r--r--src/lib/barrier/ClipboardChunk.cpp154
-rw-r--r--src/lib/barrier/ClipboardChunk.h60
-rw-r--r--src/lib/barrier/DragInformation.cpp158
-rw-r--r--src/lib/barrier/DragInformation.h53
-rw-r--r--src/lib/barrier/DropHelper.cpp53
-rw-r--r--src/lib/barrier/DropHelper.h27
-rw-r--r--src/lib/barrier/FileChunk.cpp156
-rw-r--r--src/lib/barrier/FileChunk.h46
-rw-r--r--src/lib/barrier/IApp.h47
-rw-r--r--src/lib/barrier/IAppUtil.h31
-rw-r--r--src/lib/barrier/IClient.h176
-rw-r--r--src/lib/barrier/IClipboard.cpp168
-rw-r--r--src/lib/barrier/IClipboard.h169
-rw-r--r--src/lib/barrier/IKeyState.cpp161
-rw-r--r--src/lib/barrier/IKeyState.h174
-rw-r--r--src/lib/barrier/INode.h25
-rw-r--r--src/lib/barrier/IPlatformScreen.cpp24
-rw-r--r--src/lib/barrier/IPlatformScreen.h227
-rw-r--r--src/lib/barrier/IPrimaryScreen.cpp91
-rw-r--r--src/lib/barrier/IPrimaryScreen.h165
-rw-r--r--src/lib/barrier/IScreen.h71
-rw-r--r--src/lib/barrier/IScreenSaver.h75
-rw-r--r--src/lib/barrier/ISecondaryScreen.h61
-rw-r--r--src/lib/barrier/KeyMap.cpp1344
-rw-r--r--src/lib/barrier/KeyMap.h512
-rw-r--r--src/lib/barrier/KeyState.cpp936
-rw-r--r--src/lib/barrier/KeyState.h232
-rw-r--r--src/lib/barrier/PacketStreamFilter.cpp198
-rw-r--r--src/lib/barrier/PacketStreamFilter.h59
-rw-r--r--src/lib/barrier/PlatformScreen.cpp123
-rw-r--r--src/lib/barrier/PlatformScreen.h127
-rw-r--r--src/lib/barrier/PortableTaskBarReceiver.cpp121
-rw-r--r--src/lib/barrier/PortableTaskBarReceiver.h96
-rw-r--r--src/lib/barrier/ProtocolUtil.cpp544
-rw-r--r--src/lib/barrier/ProtocolUtil.h96
-rw-r--r--src/lib/barrier/Screen.cpp559
-rw-r--r--src/lib/barrier/Screen.h345
-rw-r--r--src/lib/barrier/ServerApp.cpp859
-rw-r--r--src/lib/barrier/ServerApp.h127
-rw-r--r--src/lib/barrier/ServerArgs.cpp25
-rw-r--r--src/lib/barrier/ServerArgs.h32
-rw-r--r--src/lib/barrier/ServerTaskBarReceiver.cpp138
-rw-r--r--src/lib/barrier/ServerTaskBarReceiver.h98
-rw-r--r--src/lib/barrier/StreamChunker.cpp166
-rw-r--r--src/lib/barrier/StreamChunker.h45
-rw-r--r--src/lib/barrier/ToolApp.cpp205
-rw-r--r--src/lib/barrier/ToolApp.h37
-rw-r--r--src/lib/barrier/ToolArgs.cpp29
-rw-r--r--src/lib/barrier/ToolArgs.h34
-rw-r--r--src/lib/barrier/XBarrier.cpp133
-rw-r--r--src/lib/barrier/XBarrier.h135
-rw-r--r--src/lib/barrier/XScreen.cpp68
-rw-r--r--src/lib/barrier/XScreen.h68
-rw-r--r--src/lib/barrier/clipboard_types.h42
-rw-r--r--src/lib/barrier/key_types.cpp208
-rw-r--r--src/lib/barrier/key_types.h314
-rw-r--r--src/lib/barrier/mouse_types.h41
-rw-r--r--src/lib/barrier/option_types.h99
-rw-r--r--src/lib/barrier/protocol_types.cpp53
-rw-r--r--src/lib/barrier/protocol_types.h337
-rw-r--r--src/lib/barrier/unix/AppUtilUnix.cpp46
-rw-r--r--src/lib/barrier/unix/AppUtilUnix.h34
-rw-r--r--src/lib/barrier/win32/AppUtilWindows.cpp185
-rw-r--r--src/lib/barrier/win32/AppUtilWindows.h62
-rw-r--r--src/lib/barrier/win32/DaemonApp.cpp361
-rw-r--r--src/lib/barrier/win32/DaemonApp.h58
85 files changed, 14234 insertions, 0 deletions
diff --git a/src/lib/barrier/App.cpp b/src/lib/barrier/App.cpp
new file mode 100644
index 0000000..1f4eda3
--- /dev/null
+++ b/src/lib/barrier/App.cpp
@@ -0,0 +1,329 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/App.h"
+
+#include "base/Log.h"
+#include "common/Version.h"
+#include "barrier/protocol_types.h"
+#include "arch/Arch.h"
+#include "base/XBase.h"
+#include "arch/XArch.h"
+#include "base/log_outputters.h"
+#include "barrier/XBarrier.h"
+#include "barrier/ArgsBase.h"
+#include "ipc/IpcServerProxy.h"
+#include "base/TMethodEventJob.h"
+#include "ipc/IpcMessage.h"
+#include "ipc/Ipc.h"
+#include "base/EventQueue.h"
+
+#if SYSAPI_WIN32
+#include "arch/win32/ArchMiscWindows.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodJob.h"
+#endif
+
+#include <iostream>
+#include <stdio.h>
+
+#if WINAPI_CARBON
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+#if defined(__APPLE__)
+#include "platform/OSXDragSimulator.h"
+#endif
+
+App* App::s_instance = nullptr;
+
+//
+// App
+//
+
+App::App(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver, ArgsBase* args) :
+ m_bye(&exit),
+ m_taskBarReceiver(NULL),
+ m_suspended(false),
+ m_events(events),
+ m_args(args),
+ m_fileLog(nullptr),
+ m_createTaskBarReceiver(createTaskBarReceiver),
+ m_appUtil(events),
+ m_ipcClient(nullptr),
+ m_socketMultiplexer(nullptr)
+{
+ assert(s_instance == nullptr);
+ s_instance = this;
+}
+
+App::~App()
+{
+ s_instance = nullptr;
+ delete m_args;
+}
+
+void
+App::version()
+{
+ char buffer[500];
+ sprintf(
+ buffer,
+ "%s %s, protocol version %d.%d\n%s",
+ argsBase().m_pname,
+ kVersion,
+ kProtocolMajorVersion,
+ kProtocolMinorVersion,
+ kCopyright
+ );
+
+ std::cout << buffer << std::endl;
+}
+
+int
+App::run(int argc, char** argv)
+{
+#if MAC_OS_X_VERSION_10_7
+ // dock hide only supported on lion :(
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ GetCurrentProcess(&psn);
+#pragma GCC diagnostic pop
+
+ TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
+#endif
+
+ // install application in to arch
+ appUtil().adoptApp(this);
+
+ // HACK: fail by default (saves us setting result in each catch)
+ int result = kExitFailed;
+
+ try {
+ result = appUtil().run(argc, argv);
+ }
+ catch (XExitApp& e) {
+ // instead of showing a nasty error, just exit with the error code.
+ // not sure if i like this behaviour, but it's probably better than
+ // using the exit(int) function!
+ result = e.getCode();
+ }
+ catch (std::exception& e) {
+ LOG((CLOG_CRIT "An error occurred: %s\n", e.what()));
+ }
+ catch (...) {
+ LOG((CLOG_CRIT "An unknown error occurred.\n"));
+ }
+
+ appUtil().beforeAppExit();
+
+ return result;
+}
+
+int
+App::daemonMainLoop(int, const char**)
+{
+#if SYSAPI_WIN32
+ SystemLogger sysLogger(daemonName(), false);
+#else
+ SystemLogger sysLogger(daemonName(), true);
+#endif
+ return mainLoop();
+}
+
+void
+App::setupFileLogging()
+{
+ if (argsBase().m_logFile != NULL) {
+ m_fileLog = new FileLogOutputter(argsBase().m_logFile);
+ CLOG->insert(m_fileLog);
+ LOG((CLOG_DEBUG1 "logging to file (%s) enabled", argsBase().m_logFile));
+ }
+}
+
+void
+App::loggingFilterWarning()
+{
+ if (CLOG->getFilter() > CLOG->getConsoleMaxLevel()) {
+ if (argsBase().m_logFile == NULL) {
+ LOG((CLOG_WARN "log messages above %s are NOT sent to console (use file logging)",
+ CLOG->getFilterName(CLOG->getConsoleMaxLevel())));
+ }
+ }
+}
+
+void
+App::initApp(int argc, const char** argv)
+{
+ // parse command line
+ parseArgs(argc, argv);
+
+ ARCH->setProfileDirectory(argsBase().m_profileDirectory);
+ ARCH->setPluginDirectory(argsBase().m_pluginDirectory);
+
+ // set log filter
+ if (!CLOG->setFilter(argsBase().m_logFilter)) {
+ LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
+ argsBase().m_pname, argsBase().m_logFilter, argsBase().m_pname));
+ m_bye(kExitArgs);
+ }
+ loggingFilterWarning();
+
+ if (argsBase().m_enableDragDrop) {
+ LOG((CLOG_INFO "drag and drop enabled"));
+ }
+
+ // setup file logging after parsing args
+ setupFileLogging();
+
+ // load configuration
+ loadConfig();
+
+ if (!argsBase().m_disableTray) {
+
+ // create a log buffer so we can show the latest message
+ // as a tray icon tooltip
+ BufferedLogOutputter* logBuffer = new BufferedLogOutputter(1000);
+ CLOG->insert(logBuffer, true);
+
+ // make the task bar receiver. the user can control this app
+ // through the task bar.
+ m_taskBarReceiver = m_createTaskBarReceiver(logBuffer, m_events);
+ }
+}
+
+void
+App::initIpcClient()
+{
+ m_ipcClient = new IpcClient(m_events, m_socketMultiplexer);
+ m_ipcClient->connect();
+
+ m_events->adoptHandler(
+ m_events->forIpcClient().messageReceived(), m_ipcClient,
+ new TMethodEventJob<App>(this, &App::handleIpcMessage));
+}
+
+void
+App::cleanupIpcClient()
+{
+ m_ipcClient->disconnect();
+ m_events->removeHandler(m_events->forIpcClient().messageReceived(), m_ipcClient);
+ delete m_ipcClient;
+}
+
+void
+App::handleIpcMessage(const Event& e, void*)
+{
+ IpcMessage* m = static_cast<IpcMessage*>(e.getDataObject());
+ if (m->type() == kIpcShutdown) {
+ LOG((CLOG_INFO "got ipc shutdown message"));
+ m_events->addEvent(Event(Event::kQuit));
+ }
+}
+
+void
+App::runEventsLoop(void*)
+{
+ m_events->loop();
+
+#if defined(MAC_OS_X_VERSION_10_7)
+
+ stopCocoaLoop();
+
+#endif
+}
+
+//
+// MinimalApp
+//
+
+MinimalApp::MinimalApp() :
+ App(NULL, NULL, new ArgsBase())
+{
+ m_arch.init();
+ setEvents(m_events);
+}
+
+MinimalApp::~MinimalApp()
+{
+}
+
+int
+MinimalApp::standardStartup(int argc, char** argv)
+{
+ return 0;
+}
+
+int
+MinimalApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
+{
+ return 0;
+}
+
+void
+MinimalApp::startNode()
+{
+}
+
+int
+MinimalApp::mainLoop()
+{
+ return 0;
+}
+
+int
+MinimalApp::foregroundStartup(int argc, char** argv)
+{
+ return 0;
+}
+
+barrier::Screen*
+MinimalApp::createScreen()
+{
+ return NULL;
+}
+
+void
+MinimalApp::loadConfig()
+{
+}
+
+bool
+MinimalApp::loadConfig(const String& pathname)
+{
+ return false;
+}
+
+const char*
+MinimalApp::daemonInfo() const
+{
+ return "";
+}
+
+const char*
+MinimalApp::daemonName() const
+{
+ return "";
+}
+
+void
+MinimalApp::parseArgs(int argc, const char* const* argv)
+{
+}
diff --git a/src/lib/barrier/App.h b/src/lib/barrier/App.h
new file mode 100644
index 0000000..b7c77a0
--- /dev/null
+++ b/src/lib/barrier/App.h
@@ -0,0 +1,200 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "ipc/IpcClient.h"
+#include "barrier/IApp.h"
+#include "base/String.h"
+#include "base/Log.h"
+#include "base/EventQueue.h"
+#include "common/common.h"
+
+#if SYSAPI_WIN32
+#include "barrier/win32/AppUtilWindows.h"
+#elif SYSAPI_UNIX
+#include "barrier/unix/AppUtilUnix.h"
+#endif
+
+class IArchTaskBarReceiver;
+class BufferedLogOutputter;
+class ILogOutputter;
+class FileLogOutputter;
+namespace barrier { class Screen; }
+class IEventQueue;
+class SocketMultiplexer;
+
+typedef IArchTaskBarReceiver* (*CreateTaskBarReceiverFunc)(const BufferedLogOutputter*, IEventQueue* events);
+
+class App : public IApp {
+public:
+ App(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver, ArgsBase* args);
+ virtual ~App();
+
+ // Returns args that are common between server and client.
+ 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;
+
+ // Function pointer for function to exit immediately.
+ // TODO: this is old C code - use inheritance to normalize
+ void (*m_bye)(int);
+
+ 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; }
+
+private:
+ void handleIpcMessage(const Event&, void*);
+
+protected:
+ void initIpcClient();
+ void cleanupIpcClient();
+ void runEventsLoop(void*);
+
+ IArchTaskBarReceiver* m_taskBarReceiver;
+ bool m_suspended;
+ IEventQueue* m_events;
+
+private:
+ ArgsBase* m_args;
+ static App* s_instance;
+ FileLogOutputter* m_fileLog;
+ CreateTaskBarReceiverFunc m_createTaskBarReceiver;
+ ARCH_APP_UTIL m_appUtil;
+ IpcClient* m_ipcClient;
+ SocketMultiplexer* m_socketMultiplexer;
+};
+
+class MinimalApp : public App {
+public:
+ MinimalApp();
+ virtual ~MinimalApp();
+
+ // IApp overrides
+ virtual int standardStartup(int argc, char** argv);
+ 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 barrier::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);
+
+private:
+ Arch m_arch;
+ Log m_log;
+ EventQueue m_events;
+};
+
+#if WINAPI_MSWINDOWS
+#define DAEMON_RUNNING(running_) ArchMiscWindows::daemonRunning(running_)
+#else
+#define DAEMON_RUNNING(running_)
+#endif
+
+#define HELP_COMMON_INFO_1 \
+ " -d, --debug <level> filter out log messages with priority below level.\n" \
+ " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" \
+ " DEBUG, DEBUG1, DEBUG2.\n" \
+ " -n, --name <screen-name> use screen-name instead the hostname to identify\n" \
+ " this screen in the configuration.\n" \
+ " -1, --no-restart do not try to restart on failure.\n" \
+ " --restart restart the server automatically if it fails. (*)\n" \
+ " -l --log <file> write log messages to file.\n" \
+ " --no-tray disable the system tray icon.\n" \
+ " --enable-drag-drop enable file drag & drop.\n" \
+ " --enable-crypto enable the crypto (ssl) plugin.\n"
+
+#define HELP_COMMON_INFO_2 \
+ " -h, --help display this help and exit.\n" \
+ " --version display version information and exit.\n"
+
+#define HELP_COMMON_ARGS \
+ " [--name <screen-name>]" \
+ " [--restart|--no-restart]" \
+ " [--debug <level>]"
+
+// system args (windows/unix)
+#if SYSAPI_UNIX
+
+// unix daemon mode args
+# define HELP_SYS_ARGS \
+ " [--daemon|--no-daemon]"
+# define HELP_SYS_INFO \
+ " -f, --no-daemon run in the foreground.\n" \
+ " --daemon run as a daemon. (*)\n"
+
+#elif SYSAPI_WIN32
+
+// windows args
+# define HELP_SYS_ARGS \
+ " [--service <action>] [--relaunch] [--exit-pause]"
+# define HELP_SYS_INFO \
+ " --service <action> manage the windows service, valid options are:\n" \
+ " install/uninstall/start/stop\n" \
+ " --relaunch persistently relaunches process in current user \n" \
+ " session (useful for vista and upward).\n" \
+ " --exit-pause wait for key press on exit, can be useful for\n" \
+ " reading error messages that occur on exit.\n"
+#endif
diff --git a/src/lib/barrier/AppUtil.cpp b/src/lib/barrier/AppUtil.cpp
new file mode 100644
index 0000000..3298d7b
--- /dev/null
+++ b/src/lib/barrier/AppUtil.cpp
@@ -0,0 +1,52 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/AppUtil.h"
+
+AppUtil* AppUtil::s_instance = nullptr;
+
+AppUtil::AppUtil() :
+m_app(nullptr)
+{
+ s_instance = this;
+}
+
+AppUtil::~AppUtil()
+{
+}
+
+void
+AppUtil::adoptApp(IApp* app)
+{
+ app->setByeFunc(&exitAppStatic);
+ m_app = app;
+}
+
+IApp&
+AppUtil::app() const
+{
+ assert(m_app != nullptr);
+ return *m_app;
+}
+
+AppUtil&
+AppUtil::instance()
+{
+ assert(s_instance != nullptr);
+ return *s_instance;
+}
diff --git a/src/lib/barrier/AppUtil.h b/src/lib/barrier/AppUtil.h
new file mode 100644
index 0000000..6f5f073
--- /dev/null
+++ b/src/lib/barrier/AppUtil.h
@@ -0,0 +1,40 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/IAppUtil.h"
+#include "barrier/XBarrier.h"
+
+class AppUtil : public IAppUtil {
+public:
+ AppUtil();
+ virtual ~AppUtil();
+
+ virtual void adoptApp(IApp* app);
+ IApp& app() const;
+ virtual void exitApp(int code) { throw XExitApp(code); }
+
+ static AppUtil& instance();
+ static void exitAppStatic(int code) { instance().exitApp(code); }
+ virtual void beforeAppExit() {}
+
+private:
+ IApp* m_app;
+ static AppUtil* s_instance;
+};
diff --git a/src/lib/barrier/ArgParser.cpp b/src/lib/barrier/ArgParser.cpp
new file mode 100644
index 0000000..20e849c
--- /dev/null
+++ b/src/lib/barrier/ArgParser.cpp
@@ -0,0 +1,519 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "barrier/ArgParser.h"
+
+#include "barrier/StreamChunker.h"
+#include "barrier/App.h"
+#include "barrier/ServerArgs.h"
+#include "barrier/ClientArgs.h"
+#include "barrier/ToolArgs.h"
+#include "barrier/ArgsBase.h"
+#include "base/Log.h"
+#include "base/String.h"
+
+#ifdef WINAPI_MSWINDOWS
+#include <VersionHelpers.h>
+#endif
+
+ArgsBase* ArgParser::m_argsBase = NULL;
+
+ArgParser::ArgParser(App* app) :
+ m_app(app)
+{
+}
+
+bool
+ArgParser::parseServerArgs(ServerArgs& args, int argc, const char* const* argv)
+{
+ setArgsBase(args);
+ updateCommonArgs(argv);
+
+ for (int i = 1; i < argc; ++i) {
+ if (parsePlatformArg(args, argc, argv, i)) {
+ continue;
+ }
+ else if (parseGenericArgs(argc, argv, i)) {
+ continue;
+ }
+ else if (parseDeprecatedArgs(argc, argv, i)) {
+ continue;
+ }
+ else if (isArg(i, argc, argv, "-a", "--address", 1)) {
+ // save listen address
+ args.m_barrierAddress = argv[++i];
+ }
+ else if (isArg(i, argc, argv, "-c", "--config", 1)) {
+ // save configuration file path
+ args.m_configFile = argv[++i];
+ }
+ else {
+ LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, args.m_pname, argv[i], args.m_pname));
+ return false;
+ }
+ }
+
+ if (checkUnexpectedArgs()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+ArgParser::parseClientArgs(ClientArgs& args, int argc, const char* const* argv)
+{
+ setArgsBase(args);
+ updateCommonArgs(argv);
+
+ int i;
+ for (i = 1; i < argc; ++i) {
+ if (parsePlatformArg(args, argc, argv, i)) {
+ continue;
+ }
+ else if (parseGenericArgs(argc, argv, i)) {
+ continue;
+ }
+ else if (parseDeprecatedArgs(argc, argv, i)) {
+ continue;
+ }
+ else if (isArg(i, argc, argv, NULL, "--camp")) {
+ // ignore -- included for backwards compatibility
+ }
+ else if (isArg(i, argc, argv, NULL, "--no-camp")) {
+ // ignore -- included for backwards compatibility
+ }
+ else if (isArg(i, argc, argv, NULL, "--yscroll", 1)) {
+ // define scroll
+ args.m_yscroll = atoi(argv[++i]);
+ }
+ else {
+ if (i + 1 == argc) {
+ args.m_barrierAddress = argv[i];
+ return true;
+ }
+
+ LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, args.m_pname, argv[i], args.m_pname));
+ return false;
+ }
+ }
+
+ if (args.m_shouldExit)
+ return true;
+
+ // exactly one non-option argument (server-address)
+ if (i == argc) {
+ LOG((CLOG_PRINT "%s: a server address or name is required" BYE,
+ args.m_pname, args.m_pname));
+ return false;
+ }
+
+ if (checkUnexpectedArgs()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+ArgParser::parsePlatformArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i)
+{
+#if WINAPI_MSWINDOWS
+ if (isArg(i, argc, argv, NULL, "--service")) {
+ LOG((CLOG_WARN "obsolete argument --service, use barrierd instead."));
+ argsBase.m_shouldExit = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--exit-pause")) {
+ argsBase.m_pauseOnExit = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--stop-on-desk-switch")) {
+ argsBase.m_stopOnDeskSwitch = true;
+ }
+ else {
+ // option not supported here
+ return false;
+ }
+
+ return true;
+#elif WINAPI_XWINDOWS
+ if (isArg(i, argc, argv, "-display", "--display", 1)) {
+ // use alternative display
+ argsBase.m_display = argv[++i];
+ }
+
+ else if (isArg(i, argc, argv, NULL, "--no-xinitthreads")) {
+ argsBase.m_disableXInitThreads = true;
+ }
+
+ else {
+ // option not supported here
+ return false;
+ }
+
+ return true;
+#elif WINAPI_CARBON
+ // no options for carbon
+ return false;
+#endif
+}
+
+bool
+ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv)
+{
+ for (int i = 1; i < argc; ++i) {
+ if (isArg(i, argc, argv, NULL, "--get-active-desktop", 0)) {
+ args.m_printActiveDesktopName = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--login-auth", 0)) {
+ args.m_loginAuthenticate = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--get-installed-dir", 0)) {
+ args.m_getInstalledDir = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--get-profile-dir", 0)) {
+ args.m_getProfileDir = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--get-arch", 0)) {
+ args.m_getArch = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--notify-activation", 0)) {
+ args.m_notifyActivation = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--notify-update", 0)) {
+ args.m_notifyUpdate = true;
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ return false;
+}
+
+bool
+ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i)
+{
+ if (isArg(i, argc, argv, "-d", "--debug", 1)) {
+ // change logging level
+ argsBase().m_logFilter = argv[++i];
+ }
+ else if (isArg(i, argc, argv, "-l", "--log", 1)) {
+ argsBase().m_logFile = argv[++i];
+ }
+ else if (isArg(i, argc, argv, "-f", "--no-daemon")) {
+ // not a daemon
+ argsBase().m_daemon = false;
+ }
+ else if (isArg(i, argc, argv, NULL, "--daemon")) {
+ // daemonize
+ argsBase().m_daemon = true;
+ }
+ else if (isArg(i, argc, argv, "-n", "--name", 1)) {
+ // save screen name
+ argsBase().m_name = argv[++i];
+ }
+ else if (isArg(i, argc, argv, "-1", "--no-restart")) {
+ // don't try to restart
+ argsBase().m_restartable = false;
+ }
+ else if (isArg(i, argc, argv, NULL, "--restart")) {
+ // try to restart
+ argsBase().m_restartable = true;
+ }
+ else if (isArg(i, argc, argv, "-z", NULL)) {
+ argsBase().m_backend = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--no-hooks")) {
+ argsBase().m_noHooks = true;
+ }
+ else if (isArg(i, argc, argv, "-h", "--help")) {
+ if (m_app) {
+ m_app->help();
+ }
+ argsBase().m_shouldExit = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--version")) {
+ if (m_app) {
+ m_app->version();
+ }
+ argsBase().m_shouldExit = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--no-tray")) {
+ argsBase().m_disableTray = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--ipc")) {
+ argsBase().m_enableIpc = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--server")) {
+ // HACK: stop error happening when using portable (barrierp)
+ }
+ else if (isArg(i, argc, argv, NULL, "--client")) {
+ // HACK: stop error happening when using portable (barrierp)
+ }
+ else if (isArg(i, argc, argv, NULL, "--enable-drag-drop")) {
+ bool useDragDrop = true;
+
+#ifdef WINAPI_XWINDOWS
+
+ useDragDrop = false;
+ LOG((CLOG_INFO "ignoring --enable-drag-drop, not supported on linux."));
+
+#endif
+
+#ifdef WINAPI_MSWINDOWS
+
+ if (!IsWindowsVistaOrGreater()) {
+ useDragDrop = false;
+ LOG((CLOG_INFO "ignoring --enable-drag-drop, not supported below vista."));
+ }
+#endif
+
+ if (useDragDrop) {
+ argsBase().m_enableDragDrop = true;
+ }
+ }
+ else if (isArg(i, argc, argv, NULL, "--enable-crypto")) {
+ argsBase().m_enableCrypto = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--profile-dir", 1)) {
+ argsBase().m_profileDirectory = argv[++i];
+ }
+ else if (isArg(i, argc, argv, NULL, "--plugin-dir", 1)) {
+ argsBase().m_pluginDirectory = argv[++i];
+ }
+ else {
+ // option not supported here
+ return false;
+ }
+
+ return true;
+}
+
+bool
+ArgParser::parseDeprecatedArgs(int argc, const char* const* argv, int& i)
+{
+ if (isArg(i, argc, argv, NULL, "--crypto-pass")) {
+ LOG((CLOG_NOTE "--crypto-pass is deprecated"));
+ i++;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--res-w")) {
+ LOG((CLOG_NOTE "--res-w is deprecated"));
+ i++;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--res-h")) {
+ LOG((CLOG_NOTE "--res-h is deprecated"));
+ i++;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--prm-wc")) {
+ LOG((CLOG_NOTE "--prm-wc is deprecated"));
+ i++;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--prm-hc")) {
+ LOG((CLOG_NOTE "--prm-hc is deprecated"));
+ i++;
+ return true;
+ }
+
+ return false;
+}
+
+bool
+ArgParser::isArg(
+ int argi, int argc, const char* const* argv,
+ const char* name1, const char* name2,
+ int minRequiredParameters)
+{
+ if ((name1 != NULL && strcmp(argv[argi], name1) == 0) ||
+ (name2 != NULL && strcmp(argv[argi], name2) == 0)) {
+ // match. check args left.
+ if (argi + minRequiredParameters >= argc) {
+ LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
+ argsBase().m_pname, argv[argi], argsBase().m_pname));
+ argsBase().m_shouldExit = true;
+ return false;
+ }
+ return true;
+ }
+
+ // no match
+ return false;
+}
+
+void
+ArgParser::splitCommandString(String& command, std::vector<String>& argv)
+{
+ if (command.empty()) {
+ return ;
+ }
+
+ size_t leftDoubleQuote = 0;
+ size_t rightDoubleQuote = 0;
+ searchDoubleQuotes(command, leftDoubleQuote, rightDoubleQuote);
+
+ size_t startPos = 0;
+ size_t space = command.find(" ", startPos);
+
+ while (space != String::npos) {
+ bool ignoreThisSpace = false;
+
+ // check if the space is between two double quotes
+ if (space > leftDoubleQuote && space < rightDoubleQuote) {
+ ignoreThisSpace = true;
+ }
+ else if (space > rightDoubleQuote){
+ searchDoubleQuotes(command, leftDoubleQuote, rightDoubleQuote, rightDoubleQuote + 1);
+ }
+
+ if (!ignoreThisSpace) {
+ String subString = command.substr(startPos, space - startPos);
+
+ removeDoubleQuotes(subString);
+ argv.push_back(subString);
+ }
+
+ // find next space
+ if (ignoreThisSpace) {
+ space = command.find(" ", rightDoubleQuote + 1);
+ }
+ else {
+ startPos = space + 1;
+ space = command.find(" ", startPos);
+ }
+ }
+
+ String subString = command.substr(startPos, command.size());
+ removeDoubleQuotes(subString);
+ argv.push_back(subString);
+}
+
+bool
+ArgParser::searchDoubleQuotes(String& command, size_t& left, size_t& right, size_t startPos)
+{
+ bool result = false;
+ left = String::npos;
+ right = String::npos;
+
+ left = command.find("\"", startPos);
+ if (left != String::npos) {
+ right = command.find("\"", left + 1);
+ if (right != String::npos) {
+ result = true;
+ }
+ }
+
+ if (!result) {
+ left = 0;
+ right = 0;
+ }
+
+ return result;
+}
+
+void
+ArgParser::removeDoubleQuotes(String& arg)
+{
+ // if string is surrounded by double quotes, remove them
+ if (arg[0] == '\"' &&
+ arg[arg.size() - 1] == '\"') {
+ arg = arg.substr(1, arg.size() - 2);
+ }
+}
+
+const char**
+ArgParser::getArgv(std::vector<String>& argsArray)
+{
+ size_t argc = argsArray.size();
+
+ // caller is responsible for deleting the outer array only
+ // we use the c string pointers from argsArray and assign
+ // them to the inner array. So caller only need to use
+ // delete[] to delete the outer array
+ const char** argv = new const char*[argc];
+
+ for (size_t i = 0; i < argc; i++) {
+ argv[i] = argsArray[i].c_str();
+ }
+
+ return argv;
+}
+
+String
+ArgParser::assembleCommand(std::vector<String>& argsArray, String ignoreArg, int parametersRequired)
+{
+ String result;
+
+ for (std::vector<String>::iterator it = argsArray.begin(); it != argsArray.end(); ++it) {
+ if (it->compare(ignoreArg) == 0) {
+ it = it + parametersRequired;
+ continue;
+ }
+
+ // if there is a space in this arg, use double quotes surround it
+ if ((*it).find(" ") != String::npos) {
+ (*it).insert(0, "\"");
+ (*it).push_back('\"');
+ }
+
+ result.append(*it);
+ // add space to saperate args
+ result.append(" ");
+ }
+
+ if (!result.empty()) {
+ // remove the tail space
+ result = result.substr(0, result.size() - 1);
+ }
+
+ return result;
+}
+
+void
+ArgParser::updateCommonArgs(const char* const* argv)
+{
+ argsBase().m_name = ARCH->getHostName();
+ argsBase().m_pname = ARCH->getBasename(argv[0]);
+}
+
+bool
+ArgParser::checkUnexpectedArgs()
+{
+#if SYSAPI_WIN32
+ // suggest that user installs as a windows service. when launched as
+ // service, process should automatically detect that it should run in
+ // daemon mode.
+ if (argsBase().m_daemon) {
+ LOG((CLOG_ERR
+ "the --daemon argument is not supported on windows. "
+ "instead, install %s as a service (--service install)",
+ argsBase().m_pname));
+ return true;
+ }
+#endif
+
+ return false;
+}
diff --git a/src/lib/barrier/ArgParser.h b/src/lib/barrier/ArgParser.h
new file mode 100644
index 0000000..5fc2649
--- /dev/null
+++ b/src/lib/barrier/ArgParser.h
@@ -0,0 +1,63 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "base/String.h"
+#include "common/stdvector.h"
+
+class ServerArgs;
+class ClientArgs;
+class ToolArgs;
+class ArgsBase;
+class App;
+
+class ArgParser {
+
+public:
+ ArgParser(App* app);
+
+ bool parseServerArgs(ServerArgs& args, int argc, const char* const* argv);
+ bool parseClientArgs(ClientArgs& args, int argc, const char* const* argv);
+ bool parsePlatformArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i);
+ bool parseToolArgs(ToolArgs& args, int argc, const char* const* argv);
+ bool parseGenericArgs(int argc, const char* const* argv, int& i);
+ bool parseDeprecatedArgs(int argc, const char* const* argv, int& i);
+ void setArgsBase(ArgsBase& argsBase) { m_argsBase = &argsBase; }
+
+ static bool isArg(int argi, int argc, const char* const* argv,
+ const char* name1, const char* name2,
+ int minRequiredParameters = 0);
+ static void splitCommandString(String& command, std::vector<String>& argv);
+ static bool searchDoubleQuotes(String& command, size_t& left,
+ size_t& right, size_t startPos = 0);
+ static void removeDoubleQuotes(String& arg);
+ static const char** getArgv(std::vector<String>& argsArray);
+ static String assembleCommand(std::vector<String>& argsArray,
+ String ignoreArg = "", int parametersRequired = 0);
+
+private:
+ void updateCommonArgs(const char* const* argv);
+ bool checkUnexpectedArgs();
+
+ static ArgsBase& argsBase() { return *m_argsBase; }
+
+private:
+ App* m_app;
+
+ static ArgsBase* m_argsBase;
+};
diff --git a/src/lib/barrier/ArgsBase.cpp b/src/lib/barrier/ArgsBase.cpp
new file mode 100644
index 0000000..eedb312
--- /dev/null
+++ b/src/lib/barrier/ArgsBase.cpp
@@ -0,0 +1,53 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "barrier/ArgsBase.h"
+
+ArgsBase::ArgsBase() :
+#if SYSAPI_WIN32
+m_daemon(false), // daemon mode not supported on windows (use --service)
+m_debugServiceWait(false),
+m_pauseOnExit(false),
+m_stopOnDeskSwitch(false),
+#else
+m_daemon(true), // backward compatibility for unix (daemon by default)
+#endif
+#if WINAPI_XWINDOWS
+m_disableXInitThreads(false),
+#endif
+m_backend(false),
+m_restartable(true),
+m_noHooks(false),
+m_pname(NULL),
+m_logFilter(NULL),
+m_logFile(NULL),
+m_display(NULL),
+m_disableTray(false),
+m_enableIpc(false),
+m_enableDragDrop(false),
+m_shouldExit(false),
+m_barrierAddress(),
+m_enableCrypto(false),
+m_profileDirectory(""),
+m_pluginDirectory("")
+{
+}
+
+ArgsBase::~ArgsBase()
+{
+}
diff --git a/src/lib/barrier/ArgsBase.h b/src/lib/barrier/ArgsBase.h
new file mode 100644
index 0000000..1f49984
--- /dev/null
+++ b/src/lib/barrier/ArgsBase.h
@@ -0,0 +1,54 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "base/String.h"
+
+class ArgsBase {
+public:
+ ArgsBase();
+ virtual ~ArgsBase();
+
+public:
+ bool m_daemon;
+ bool m_backend;
+ bool m_restartable;
+ bool m_noHooks;
+ const char* m_pname;
+ const char* m_logFilter;
+ const char* m_logFile;
+ const char* m_display;
+ String m_name;
+ bool m_disableTray;
+ bool m_enableIpc;
+ bool m_enableDragDrop;
+#if SYSAPI_WIN32
+ bool m_debugServiceWait;
+ bool m_pauseOnExit;
+ bool m_stopOnDeskSwitch;
+#endif
+#if WINAPI_XWINDOWS
+ bool m_disableXInitThreads;
+#endif
+ bool m_shouldExit;
+ String m_barrierAddress;
+ bool m_enableCrypto;
+ String m_profileDirectory;
+ String m_pluginDirectory;
+};
diff --git a/src/lib/barrier/CMakeLists.txt b/src/lib/barrier/CMakeLists.txt
new file mode 100644
index 0000000..6978aef
--- /dev/null
+++ b/src/lib/barrier/CMakeLists.txt
@@ -0,0 +1,40 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+# arch
+if (WIN32)
+ file(GLOB arch_headers "win32/*.h")
+ file(GLOB arch_sources "win32/*.cpp")
+elseif (UNIX)
+ file(GLOB arch_headers "unix/*.h")
+ file(GLOB arch_sources "unix/*.cpp")
+endif()
+
+list(APPEND sources ${arch_sources})
+list(APPEND headers ${arch_headers})
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(synlib STATIC ${sources})
+
+if (UNIX)
+ target_link_libraries(synlib arch client ipc net base platform mt server)
+endif()
diff --git a/src/lib/barrier/Chunk.cpp b/src/lib/barrier/Chunk.cpp
new file mode 100644
index 0000000..f11bff5
--- /dev/null
+++ b/src/lib/barrier/Chunk.cpp
@@ -0,0 +1,30 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-2016 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 "barrier/Chunk.h"
+#include "base/String.h"
+
+Chunk::Chunk(size_t size): m_dataSize(0)
+{
+ m_chunk = new char[size];
+ memset(m_chunk, 0, size);
+}
+
+Chunk::~Chunk()
+{
+ delete[] m_chunk;
+}
diff --git a/src/lib/barrier/Chunk.h b/src/lib/barrier/Chunk.h
new file mode 100644
index 0000000..42b85bf
--- /dev/null
+++ b/src/lib/barrier/Chunk.h
@@ -0,0 +1,30 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-2016 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 "common/basic_types.h"
+
+class Chunk {
+public:
+ Chunk(size_t size);
+ ~Chunk();
+
+public:
+ size_t m_dataSize;
+ char* m_chunk;
+};
diff --git a/src/lib/barrier/ClientApp.cpp b/src/lib/barrier/ClientApp.cpp
new file mode 100644
index 0000000..87686f2
--- /dev/null
+++ b/src/lib/barrier/ClientApp.cpp
@@ -0,0 +1,560 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/ClientApp.h"
+
+#include "client/Client.h"
+#include "barrier/ArgParser.h"
+#include "barrier/protocol_types.h"
+#include "barrier/Screen.h"
+#include "barrier/XScreen.h"
+#include "barrier/ClientArgs.h"
+#include "net/NetworkAddress.h"
+#include "net/TCPSocketFactory.h"
+#include "net/SocketMultiplexer.h"
+#include "net/XSocket.h"
+#include "mt/Thread.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "arch/Arch.h"
+#include "base/String.h"
+#include "base/Event.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+#include "base/log_outputters.h"
+#include "base/EventQueue.h"
+#include "base/TMethodJob.h"
+#include "base/Log.h"
+#include "common/Version.h"
+
+#if SYSAPI_WIN32
+#include "arch/win32/ArchMiscWindows.h"
+#endif
+
+#if WINAPI_MSWINDOWS
+#include "platform/MSWindowsScreen.h"
+#elif WINAPI_XWINDOWS
+#include "platform/XWindowsScreen.h"
+#elif WINAPI_CARBON
+#include "platform/OSXScreen.h"
+#endif
+
+#if defined(__APPLE__)
+#include "platform/OSXDragSimulator.h"
+#endif
+
+#include <iostream>
+#include <stdio.h>
+#include <sstream>
+
+#define RETRY_TIME 1.0
+
+ClientApp::ClientApp(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver) :
+ App(events, createTaskBarReceiver, new ClientArgs()),
+ m_client(NULL),
+ m_clientScreen(NULL),
+ m_serverAddress(NULL)
+{
+}
+
+ClientApp::~ClientApp()
+{
+}
+
+void
+ClientApp::parseArgs(int argc, const char* const* argv)
+{
+ ArgParser argParser(this);
+ bool result = argParser.parseClientArgs(args(), argc, argv);
+
+ if (!result || args().m_shouldExit) {
+ m_bye(kExitArgs);
+ }
+ else {
+ // save server address
+ if (!args().m_barrierAddress.empty()) {
+ try {
+ *m_serverAddress = NetworkAddress(args().m_barrierAddress, kDefaultPort);
+ m_serverAddress->resolve();
+ }
+ catch (XSocketAddress& e) {
+ // allow an address that we can't look up if we're restartable.
+ // we'll try to resolve the address each time we connect to the
+ // server. a bad port will never get better. patch by Brent
+ // Priddy.
+ if (!args().m_restartable || e.getError() == XSocketAddress::kBadPort) {
+ LOG((CLOG_PRINT "%s: %s" BYE,
+ args().m_pname, e.what(), args().m_pname));
+ m_bye(kExitFailed);
+ }
+ }
+ }
+ }
+}
+
+void
+ClientApp::help()
+{
+#if WINAPI_XWINDOWS
+# define WINAPI_ARG \
+ " [--display <display>] [--no-xinitthreads]"
+# define WINAPI_INFO \
+ " --display <display> connect to the X server at <display>\n" \
+ " --no-xinitthreads do not call XInitThreads()\n"
+#else
+# define WINAPI_ARG ""
+# define WINAPI_INFO ""
+#endif
+
+ std::ostringstream buffer;
+ buffer << "Start the barrier client and connect to a remote server component." << std::endl
+ << std::endl
+ << "Usage: " << args().m_pname << " [--yscroll <delta>]" << WINAPI_ARG << HELP_SYS_ARGS
+ << HELP_COMMON_ARGS << " <server-address>" << std::endl
+ << std::endl
+ << "Options:" << std::endl
+ << HELP_COMMON_INFO_1 << WINAPI_INFO << HELP_SYS_INFO
+ << " --yscroll <delta> defines the vertical scrolling delta, which is" << std::endl
+ << " 120 by default." << std::endl
+ << HELP_COMMON_INFO_2
+ << std::endl
+ << "Default options are marked with a *" << std::endl
+ << std::endl
+ << "The server address is of the form: [<hostname>][:<port>]. The hostname" << std::endl
+ << "must be the address or hostname of the server. The port overrides the" << std::endl
+ << "default port, " << kDefaultPort << "." << std::endl;
+
+ LOG((CLOG_PRINT "%s", buffer.str().c_str()));
+}
+
+const char*
+ClientApp::daemonName() const
+{
+#if SYSAPI_WIN32
+ return "Barrier Client";
+#elif SYSAPI_UNIX
+ return "barrierc";
+#endif
+}
+
+const char*
+ClientApp::daemonInfo() const
+{
+#if SYSAPI_WIN32
+ return "Allows another computer to share it's keyboard and mouse with this computer.";
+#elif SYSAPI_UNIX
+ return "";
+#endif
+}
+
+barrier::Screen*
+ClientApp::createScreen()
+{
+#if WINAPI_MSWINDOWS
+ return new barrier::Screen(new MSWindowsScreen(
+ false, args().m_noHooks, args().m_stopOnDeskSwitch, m_events), m_events);
+#elif WINAPI_XWINDOWS
+ return new barrier::Screen(new XWindowsScreen(
+ args().m_display, false, args().m_disableXInitThreads,
+ args().m_yscroll, m_events), m_events);
+#elif WINAPI_CARBON
+ return new barrier::Screen(new OSXScreen(m_events, false), m_events);
+#endif
+}
+
+void
+ClientApp::updateStatus()
+{
+ updateStatus("");
+}
+
+
+void
+ClientApp::updateStatus(const String& msg)
+{
+ if (m_taskBarReceiver)
+ {
+ m_taskBarReceiver->updateStatus(m_client, msg);
+ }
+}
+
+
+void
+ClientApp::resetRestartTimeout()
+{
+ // retry time can nolonger be changed
+ //s_retryTime = 0.0;
+}
+
+
+double
+ClientApp::nextRestartTimeout()
+{
+ // retry at a constant rate (Issue 52)
+ return RETRY_TIME;
+
+ /*
+ // choose next restart timeout. we start with rapid retries
+ // then slow down.
+ if (s_retryTime < 1.0) {
+ s_retryTime = 1.0;
+ }
+ else if (s_retryTime < 3.0) {
+ s_retryTime = 3.0;
+ }
+ else {
+ s_retryTime = 5.0;
+ }
+ return s_retryTime;
+ */
+}
+
+
+void
+ClientApp::handleScreenError(const Event&, void*)
+{
+ LOG((CLOG_CRIT "error on screen"));
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+
+barrier::Screen*
+ClientApp::openClientScreen()
+{
+ barrier::Screen* screen = createScreen();
+ screen->setEnableDragDrop(argsBase().m_enableDragDrop);
+ m_events->adoptHandler(m_events->forIScreen().error(),
+ screen->getEventTarget(),
+ new TMethodEventJob<ClientApp>(
+ this, &ClientApp::handleScreenError));
+ return screen;
+}
+
+
+void
+ClientApp::closeClientScreen(barrier::Screen* screen)
+{
+ if (screen != NULL) {
+ m_events->removeHandler(m_events->forIScreen().error(),
+ screen->getEventTarget());
+ delete screen;
+ }
+}
+
+
+void
+ClientApp::handleClientRestart(const Event&, void* vtimer)
+{
+ // discard old timer
+ EventQueueTimer* timer = static_cast<EventQueueTimer*>(vtimer);
+ m_events->deleteTimer(timer);
+ m_events->removeHandler(Event::kTimer, timer);
+
+ // reconnect
+ startClient();
+}
+
+
+void
+ClientApp::scheduleClientRestart(double retryTime)
+{
+ // install a timer and handler to retry later
+ LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
+ EventQueueTimer* timer = m_events->newOneShotTimer(retryTime, NULL);
+ m_events->adoptHandler(Event::kTimer, timer,
+ new TMethodEventJob<ClientApp>(this, &ClientApp::handleClientRestart, timer));
+}
+
+
+void
+ClientApp::handleClientConnected(const Event&, void*)
+{
+ LOG((CLOG_NOTE "connected to server"));
+ resetRestartTimeout();
+ updateStatus();
+}
+
+
+void
+ClientApp::handleClientFailed(const Event& e, void*)
+{
+ Client::FailInfo* info =
+ static_cast<Client::FailInfo*>(e.getData());
+
+ updateStatus(String("Failed to connect to server: ") + info->m_what);
+ if (!args().m_restartable || !info->m_retry) {
+ LOG((CLOG_ERR "failed to connect to server: %s", info->m_what.c_str()));
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ else {
+ LOG((CLOG_WARN "failed to connect to server: %s", info->m_what.c_str()));
+ if (!m_suspended) {
+ scheduleClientRestart(nextRestartTimeout());
+ }
+ }
+ delete info;
+}
+
+
+void
+ClientApp::handleClientDisconnected(const Event&, void*)
+{
+ LOG((CLOG_NOTE "disconnected from server"));
+ if (!args().m_restartable) {
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ else if (!m_suspended) {
+ scheduleClientRestart(nextRestartTimeout());
+ }
+ updateStatus();
+}
+
+Client*
+ClientApp::openClient(const String& name, const NetworkAddress& address,
+ barrier::Screen* screen)
+{
+ Client* client = new Client(
+ m_events,
+ name,
+ address,
+ new TCPSocketFactory(m_events, getSocketMultiplexer()),
+ screen,
+ args());
+
+ try {
+ m_events->adoptHandler(
+ m_events->forClient().connected(),
+ client->getEventTarget(),
+ new TMethodEventJob<ClientApp>(this, &ClientApp::handleClientConnected));
+
+ m_events->adoptHandler(
+ m_events->forClient().connectionFailed(),
+ client->getEventTarget(),
+ new TMethodEventJob<ClientApp>(this, &ClientApp::handleClientFailed));
+
+ m_events->adoptHandler(
+ m_events->forClient().disconnected(),
+ client->getEventTarget(),
+ new TMethodEventJob<ClientApp>(this, &ClientApp::handleClientDisconnected));
+
+ } catch (std::bad_alloc &ba) {
+ delete client;
+ throw ba;
+ }
+
+ return client;
+}
+
+
+void
+ClientApp::closeClient(Client* client)
+{
+ if (client == NULL) {
+ return;
+ }
+
+ m_events->removeHandler(m_events->forClient().connected(), client);
+ m_events->removeHandler(m_events->forClient().connectionFailed(), client);
+ m_events->removeHandler(m_events->forClient().disconnected(), client);
+ delete client;
+}
+
+int
+ClientApp::foregroundStartup(int argc, char** argv)
+{
+ initApp(argc, argv);
+
+ // never daemonize
+ return mainLoop();
+}
+
+bool
+ClientApp::startClient()
+{
+ double retryTime;
+ barrier::Screen* clientScreen = NULL;
+ try {
+ if (m_clientScreen == NULL) {
+ clientScreen = openClientScreen();
+ m_client = openClient(args().m_name,
+ *m_serverAddress, clientScreen);
+ m_clientScreen = clientScreen;
+ LOG((CLOG_NOTE "started client"));
+ }
+
+ m_client->connect();
+
+ updateStatus();
+ return true;
+ }
+ catch (XScreenUnavailable& e) {
+ LOG((CLOG_WARN "secondary screen unavailable: %s", e.what()));
+ closeClientScreen(clientScreen);
+ updateStatus(String("secondary screen unavailable: ") + e.what());
+ retryTime = e.getRetryTime();
+ }
+ catch (XScreenOpenFailure& e) {
+ LOG((CLOG_CRIT "failed to start client: %s", e.what()));
+ closeClientScreen(clientScreen);
+ return false;
+ }
+ catch (XBase& e) {
+ LOG((CLOG_CRIT "failed to start client: %s", e.what()));
+ closeClientScreen(clientScreen);
+ return false;
+ }
+
+ if (args().m_restartable) {
+ scheduleClientRestart(retryTime);
+ return true;
+ }
+ else {
+ // don't try again
+ return false;
+ }
+}
+
+
+void
+ClientApp::stopClient()
+{
+ closeClient(m_client);
+ closeClientScreen(m_clientScreen);
+ m_client = NULL;
+ m_clientScreen = NULL;
+}
+
+
+int
+ClientApp::mainLoop()
+{
+ // create socket multiplexer. this must happen after daemonization
+ // on unix because threads evaporate across a fork().
+ SocketMultiplexer multiplexer;
+ setSocketMultiplexer(&multiplexer);
+
+ // start client, etc
+ appUtil().startNode();
+
+ // init ipc client after node start, since create a new screen wipes out
+ // the event queue (the screen ctors call adoptBuffer).
+ if (argsBase().m_enableIpc) {
+ initIpcClient();
+ }
+
+ // run event loop. if startClient() failed we're supposed to retry
+ // later. the timer installed by startClient() will take care of
+ // that.
+ DAEMON_RUNNING(true);
+
+#if defined(MAC_OS_X_VERSION_10_7)
+
+ Thread thread(
+ new TMethodJob<ClientApp>(
+ this, &ClientApp::runEventsLoop,
+ NULL));
+
+ // wait until carbon loop is ready
+ OSXScreen* screen = dynamic_cast<OSXScreen*>(
+ m_clientScreen->getPlatformScreen());
+ screen->waitForCarbonLoop();
+
+ runCocoaApp();
+#else
+ m_events->loop();
+#endif
+
+ DAEMON_RUNNING(false);
+
+ // close down
+ LOG((CLOG_DEBUG1 "stopping client"));
+ stopClient();
+ updateStatus();
+ LOG((CLOG_NOTE "stopped client"));
+
+ if (argsBase().m_enableIpc) {
+ cleanupIpcClient();
+ }
+
+ return kExitSuccess;
+}
+
+static
+int
+daemonMainLoopStatic(int argc, const char** argv)
+{
+ return ClientApp::instance().daemonMainLoop(argc, argv);
+}
+
+int
+ClientApp::standardStartup(int argc, char** argv)
+{
+ initApp(argc, argv);
+
+ // daemonize if requested
+ if (args().m_daemon) {
+ return ARCH->daemonize(daemonName(), &daemonMainLoopStatic);
+ }
+ else {
+ return mainLoop();
+ }
+}
+
+int
+ClientApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
+{
+ // general initialization
+ m_serverAddress = new NetworkAddress;
+ args().m_pname = ARCH->getBasename(argv[0]);
+
+ // install caller's output filter
+ if (outputter != NULL) {
+ CLOG->insert(outputter);
+ }
+
+ int result;
+ try
+ {
+ // run
+ result = startup(argc, argv);
+ }
+ catch (...)
+ {
+ if (m_taskBarReceiver)
+ {
+ // done with task bar receiver
+ delete m_taskBarReceiver;
+ }
+
+ delete m_serverAddress;
+
+ throw;
+ }
+
+ return result;
+}
+
+void
+ClientApp::startNode()
+{
+ // start the client. if this return false then we've failed and
+ // we shouldn't retry.
+ LOG((CLOG_DEBUG1 "starting client"));
+ if (!startClient()) {
+ m_bye(kExitFailed);
+ }
+}
diff --git a/src/lib/barrier/ClientApp.h b/src/lib/barrier/ClientApp.h
new file mode 100644
index 0000000..777f3d3
--- /dev/null
+++ b/src/lib/barrier/ClientApp.h
@@ -0,0 +1,83 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/App.h"
+
+namespace barrier { class Screen; }
+class Event;
+class Client;
+class NetworkAddress;
+class Thread;
+class ClientArgs;
+
+class ClientApp : public App {
+public:
+ ClientApp(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver);
+ virtual ~ClientApp();
+
+ // Parse client specific command line arguments.
+ void parseArgs(int argc, const char* const* argv);
+
+ // Prints help specific to client.
+ void help();
+
+ // Returns arguments that are common and for client.
+ ClientArgs& args() const { return (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);
+ int runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup);
+ barrier::Screen* createScreen();
+ void updateStatus();
+ void updateStatus(const String& msg);
+ void resetRestartTimeout();
+ double nextRestartTimeout();
+ void handleScreenError(const Event&, void*);
+ barrier::Screen* openClientScreen();
+ void closeClientScreen(barrier::Screen* screen);
+ void handleClientRestart(const Event&, void* vtimer);
+ void scheduleClientRestart(double retryTime);
+ void handleClientConnected(const Event&, void*);
+ void handleClientFailed(const Event& e, void*);
+ void handleClientDisconnected(const Event&, void*);
+ Client* openClient(const String& name, const NetworkAddress& address,
+ barrier::Screen* screen);
+ void closeClient(Client* client);
+ bool startClient();
+ void stopClient();
+ int mainLoop();
+ void startNode();
+
+ static ClientApp& instance() { return (ClientApp&)App::instance(); }
+
+ Client* getClientPtr() { return m_client; }
+
+private:
+ Client* m_client;
+ barrier::Screen*m_clientScreen;
+ NetworkAddress* m_serverAddress;
+};
diff --git a/src/lib/barrier/ClientArgs.cpp b/src/lib/barrier/ClientArgs.cpp
new file mode 100644
index 0000000..5c9ed88
--- /dev/null
+++ b/src/lib/barrier/ClientArgs.cpp
@@ -0,0 +1,23 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "barrier/ClientArgs.h"
+
+ClientArgs::ClientArgs() :
+ m_yscroll(0)
+{
+}
diff --git a/src/lib/barrier/ClientArgs.h b/src/lib/barrier/ClientArgs.h
new file mode 100644
index 0000000..70285fa
--- /dev/null
+++ b/src/lib/barrier/ClientArgs.h
@@ -0,0 +1,30 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "barrier/ArgsBase.h"
+
+class NetworkAddress;
+
+class ClientArgs : public ArgsBase {
+public:
+ ClientArgs();
+
+public:
+ int m_yscroll;
+};
diff --git a/src/lib/barrier/ClientTaskBarReceiver.cpp b/src/lib/barrier/ClientTaskBarReceiver.cpp
new file mode 100644
index 0000000..2ea6566
--- /dev/null
+++ b/src/lib/barrier/ClientTaskBarReceiver.cpp
@@ -0,0 +1,141 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 Chris Schoeneman
+ *
+ * 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 "barrier/ClientTaskBarReceiver.h"
+#include "client/Client.h"
+#include "mt/Lock.h"
+#include "base/String.h"
+#include "base/IEventQueue.h"
+#include "arch/Arch.h"
+#include "common/Version.h"
+
+//
+// ClientTaskBarReceiver
+//
+
+ClientTaskBarReceiver::ClientTaskBarReceiver(IEventQueue* events) :
+ m_state(kNotRunning),
+ m_events(events)
+{
+ // do nothing
+}
+
+ClientTaskBarReceiver::~ClientTaskBarReceiver()
+{
+ // do nothing
+}
+
+void
+ClientTaskBarReceiver::updateStatus(Client* client, const String& errorMsg)
+{
+ {
+ // update our status
+ m_errorMessage = errorMsg;
+ if (client == NULL) {
+ if (m_errorMessage.empty()) {
+ m_state = kNotRunning;
+ }
+ else {
+ m_state = kNotWorking;
+ }
+ }
+ else {
+ m_server = client->getServerAddress().getHostname();
+
+ if (client->isConnected()) {
+ m_state = kConnected;
+ }
+ else if (client->isConnecting()) {
+ m_state = kConnecting;
+ }
+ else {
+ m_state = kNotConnected;
+ }
+ }
+
+ // let subclasses have a go
+ onStatusChanged(client);
+ }
+
+ // tell task bar
+ ARCH->updateReceiver(this);
+}
+
+ClientTaskBarReceiver::EState
+ClientTaskBarReceiver::getStatus() const
+{
+ return m_state;
+}
+
+const String&
+ClientTaskBarReceiver::getErrorMessage() const
+{
+ return m_errorMessage;
+}
+
+void
+ClientTaskBarReceiver::quit()
+{
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+void
+ClientTaskBarReceiver::onStatusChanged(Client*)
+{
+ // do nothing
+}
+
+void
+ClientTaskBarReceiver::lock() const
+{
+ // do nothing
+}
+
+void
+ClientTaskBarReceiver::unlock() const
+{
+ // do nothing
+}
+
+std::string
+ClientTaskBarReceiver::getToolTip() const
+{
+ switch (m_state) {
+ case kNotRunning:
+ return barrier::string::sprintf("%s: Not running", kAppVersion);
+
+ case kNotWorking:
+ return barrier::string::sprintf("%s: %s",
+ kAppVersion, m_errorMessage.c_str());
+
+ case kNotConnected:
+ return barrier::string::sprintf("%s: Not connected: %s",
+ kAppVersion, m_errorMessage.c_str());
+
+ case kConnecting:
+ return barrier::string::sprintf("%s: Connecting to %s...",
+ kAppVersion, m_server.c_str());
+
+ case kConnected:
+ return barrier::string::sprintf("%s: Connected to %s",
+ kAppVersion, m_server.c_str());
+
+ default:
+ return "";
+ }
+}
diff --git a/src/lib/barrier/ClientTaskBarReceiver.h b/src/lib/barrier/ClientTaskBarReceiver.h
new file mode 100644
index 0000000..da15154
--- /dev/null
+++ b/src/lib/barrier/ClientTaskBarReceiver.h
@@ -0,0 +1,95 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 Chris Schoeneman
+ *
+ * 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/>.
+ */
+
+#ifndef CCLIENTTASKBARRECEIVER_H
+#define CCLIENTTASKBARRECEIVER_H
+
+#include "base/String.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "base/log_outputters.h"
+#include "client/Client.h"
+
+class IEventQueue;
+
+//! Implementation of IArchTaskBarReceiver for the barrier server
+class ClientTaskBarReceiver : public IArchTaskBarReceiver {
+public:
+ ClientTaskBarReceiver(IEventQueue* events);
+ virtual ~ClientTaskBarReceiver();
+
+ //! @name manipulators
+ //@{
+
+ //! Update status
+ /*!
+ Determine the status and query required information from the client.
+ */
+ void updateStatus(Client*, const String& errorMsg);
+
+ void updateStatus(INode* n, const String& errorMsg) { updateStatus((Client*)n, errorMsg); }
+
+ //@}
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus() = 0;
+ virtual void runMenu(int x, int y) = 0;
+ virtual void primaryAction() = 0;
+ virtual void lock() const;
+ virtual void unlock() const;
+ virtual const Icon getIcon() const = 0;
+ virtual std::string getToolTip() const;
+ virtual void cleanup() {}
+
+protected:
+ enum EState {
+ kNotRunning,
+ kNotWorking,
+ kNotConnected,
+ kConnecting,
+ kConnected,
+ kMaxState
+ };
+
+ //! Get status
+ EState getStatus() const;
+
+ //! Get error message
+ const String& getErrorMessage() const;
+
+ //! Quit app
+ /*!
+ Causes the application to quit gracefully
+ */
+ void quit();
+
+ //! Status change notification
+ /*!
+ Called when status changes. The default implementation does nothing.
+ */
+ virtual void onStatusChanged(Client* client);
+
+private:
+ EState m_state;
+ String m_errorMessage;
+ String m_server;
+ IEventQueue* m_events;
+};
+
+IArchTaskBarReceiver* createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events);
+
+#endif
diff --git a/src/lib/barrier/Clipboard.cpp b/src/lib/barrier/Clipboard.cpp
new file mode 100644
index 0000000..a6a166d
--- /dev/null
+++ b/src/lib/barrier/Clipboard.cpp
@@ -0,0 +1,118 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/Clipboard.h"
+
+//
+// Clipboard
+//
+
+Clipboard::Clipboard() :
+ m_open(false),
+ m_owner(false)
+{
+ open(0);
+ empty();
+ close();
+}
+
+Clipboard::~Clipboard()
+{
+ // do nothing
+}
+
+bool
+Clipboard::empty()
+{
+ assert(m_open);
+
+ // clear all data
+ for (SInt32 index = 0; index < kNumFormats; ++index) {
+ m_data[index] = "";
+ m_added[index] = false;
+ }
+
+ // save time
+ m_timeOwned = m_time;
+
+ // we're the owner now
+ m_owner = true;
+
+ return true;
+}
+
+void
+Clipboard::add(EFormat format, const String& data)
+{
+ assert(m_open);
+ assert(m_owner);
+
+ m_data[format] = data;
+ m_added[format] = true;
+}
+
+bool
+Clipboard::open(Time time) const
+{
+ assert(!m_open);
+
+ m_open = true;
+ m_time = time;
+
+ return true;
+}
+
+void
+Clipboard::close() const
+{
+ assert(m_open);
+
+ m_open = false;
+}
+
+Clipboard::Time
+Clipboard::getTime() const
+{
+ return m_timeOwned;
+}
+
+bool
+Clipboard::has(EFormat format) const
+{
+ assert(m_open);
+ return m_added[format];
+}
+
+String
+Clipboard::get(EFormat format) const
+{
+ assert(m_open);
+ return m_data[format];
+}
+
+void
+Clipboard::unmarshall(const String& data, Time time)
+{
+ IClipboard::unmarshall(this, data, time);
+}
+
+String
+Clipboard::marshall() const
+{
+ return IClipboard::marshall(this);
+}
diff --git a/src/lib/barrier/Clipboard.h b/src/lib/barrier/Clipboard.h
new file mode 100644
index 0000000..23bea75
--- /dev/null
+++ b/src/lib/barrier/Clipboard.h
@@ -0,0 +1,71 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/IClipboard.h"
+
+//! Memory buffer clipboard
+/*!
+This class implements a clipboard that stores data in memory.
+*/
+class Clipboard : public IClipboard {
+public:
+ Clipboard();
+ virtual ~Clipboard();
+
+ //! @name manipulators
+ //@{
+
+ //! Unmarshall clipboard data
+ /*!
+ Extract marshalled clipboard data and store it in this clipboard.
+ Sets the clipboard time to \c time.
+ */
+ void unmarshall(const String& data, Time time);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Marshall clipboard data
+ /*!
+ Merge this clipboard's data into a single buffer that can be later
+ unmarshalled to restore the clipboard and return the buffer.
+ */
+ String marshall() const;
+
+ //@}
+
+ // IClipboard overrides
+ virtual bool empty();
+ virtual void add(EFormat, const String& data);
+ virtual bool open(Time) const;
+ virtual void close() const;
+ virtual Time getTime() const;
+ virtual bool has(EFormat) const;
+ virtual String get(EFormat) const;
+
+private:
+ mutable bool m_open;
+ mutable Time m_time;
+ bool m_owner;
+ Time m_timeOwned;
+ bool m_added[kNumFormats];
+ String m_data[kNumFormats];
+};
diff --git a/src/lib/barrier/ClipboardChunk.cpp b/src/lib/barrier/ClipboardChunk.cpp
new file mode 100644
index 0000000..bc71471
--- /dev/null
+++ b/src/lib/barrier/ClipboardChunk.cpp
@@ -0,0 +1,154 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-2016 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 "barrier/ClipboardChunk.h"
+
+#include "barrier/ProtocolUtil.h"
+#include "barrier/protocol_types.h"
+#include "io/IStream.h"
+#include "base/Log.h"
+#include <cstring>
+
+size_t ClipboardChunk::s_expectedSize = 0;
+
+ClipboardChunk::ClipboardChunk(size_t size) :
+ Chunk(size)
+{
+ m_dataSize = size - CLIPBOARD_CHUNK_META_SIZE;
+}
+
+ClipboardChunk*
+ClipboardChunk::start(
+ ClipboardID id,
+ UInt32 sequence,
+ const String& size)
+{
+ size_t sizeLength = size.size();
+ ClipboardChunk* start = new ClipboardChunk(sizeLength + CLIPBOARD_CHUNK_META_SIZE);
+ char* chunk = start->m_chunk;
+
+ chunk[0] = id;
+ std::memcpy (&chunk[1], &sequence, 4);
+ chunk[5] = kDataStart;
+ memcpy(&chunk[6], size.c_str(), sizeLength);
+ chunk[sizeLength + CLIPBOARD_CHUNK_META_SIZE - 1] = '\0';
+
+ return start;
+}
+
+ClipboardChunk*
+ClipboardChunk::data(
+ ClipboardID id,
+ UInt32 sequence,
+ const String& data)
+{
+ size_t dataSize = data.size();
+ ClipboardChunk* chunk = new ClipboardChunk(dataSize + CLIPBOARD_CHUNK_META_SIZE);
+ char* chunkData = chunk->m_chunk;
+
+ chunkData[0] = id;
+ std::memcpy (&chunkData[1], &sequence, 4);
+ chunkData[5] = kDataChunk;
+ memcpy(&chunkData[6], data.c_str(), dataSize);
+ chunkData[dataSize + CLIPBOARD_CHUNK_META_SIZE - 1] = '\0';
+
+ return chunk;
+}
+
+ClipboardChunk*
+ClipboardChunk::end(ClipboardID id, UInt32 sequence)
+{
+ ClipboardChunk* end = new ClipboardChunk(CLIPBOARD_CHUNK_META_SIZE);
+ char* chunk = end->m_chunk;
+
+ chunk[0] = id;
+ std::memcpy (&chunk[1], &sequence, 4);
+ chunk[5] = kDataEnd;
+ chunk[CLIPBOARD_CHUNK_META_SIZE - 1] = '\0';
+
+ return end;
+}
+
+int
+ClipboardChunk::assemble(barrier::IStream* stream,
+ String& dataCached,
+ ClipboardID& id,
+ UInt32& sequence)
+{
+ UInt8 mark;
+ String data;
+
+ if (!ProtocolUtil::readf(stream, kMsgDClipboard + 4, &id, &sequence, &mark, &data)) {
+ return kError;
+ }
+
+ if (mark == kDataStart) {
+ s_expectedSize = barrier::string::stringToSizeType(data);
+ LOG((CLOG_DEBUG "start receiving clipboard data"));
+ dataCached.clear();
+ return kStart;
+ }
+ else if (mark == kDataChunk) {
+ dataCached.append(data);
+ return kNotFinish;
+ }
+ else if (mark == kDataEnd) {
+ // validate
+ if (id >= kClipboardEnd) {
+ return kError;
+ }
+ else if (s_expectedSize != dataCached.size()) {
+ LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", s_expectedSize, dataCached.size()));
+ return kError;
+ }
+ return kFinish;
+ }
+
+ LOG((CLOG_ERR "clipboard transmission failed: unknown error"));
+ return kError;
+}
+
+void
+ClipboardChunk::send(barrier::IStream* stream, void* data)
+{
+ ClipboardChunk* clipboardData = static_cast<ClipboardChunk*>(data);
+
+ LOG((CLOG_DEBUG1 "sending clipboard chunk"));
+
+ char* chunk = clipboardData->m_chunk;
+ ClipboardID id = chunk[0];
+ UInt32 sequence;
+ std::memcpy (&sequence, &chunk[1], 4);
+ UInt8 mark = chunk[5];
+ String dataChunk(&chunk[6], clipboardData->m_dataSize);
+
+ switch (mark) {
+ case kDataStart:
+ LOG((CLOG_DEBUG2 "sending clipboard chunk start: size=%s", dataChunk.c_str()));
+ break;
+
+ case kDataChunk:
+ LOG((CLOG_DEBUG2 "sending clipboard chunk data: size=%i", dataChunk.size()));
+ break;
+
+ case kDataEnd:
+ LOG((CLOG_DEBUG2 "sending clipboard finished"));
+ break;
+ }
+
+ ProtocolUtil::writef(stream, kMsgDClipboard, id, sequence, mark, &dataChunk);
+}
diff --git a/src/lib/barrier/ClipboardChunk.h b/src/lib/barrier/ClipboardChunk.h
new file mode 100644
index 0000000..6402aca
--- /dev/null
+++ b/src/lib/barrier/ClipboardChunk.h
@@ -0,0 +1,60 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-2016 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 "barrier/Chunk.h"
+#include "barrier/clipboard_types.h"
+#include "base/String.h"
+#include "common/basic_types.h"
+
+#define CLIPBOARD_CHUNK_META_SIZE 7
+
+namespace barrier {
+class IStream;
+};
+
+class ClipboardChunk : public Chunk {
+public:
+ ClipboardChunk(size_t size);
+
+ static ClipboardChunk*
+ start(
+ ClipboardID id,
+ UInt32 sequence,
+ const String& size);
+ static ClipboardChunk*
+ data(
+ ClipboardID id,
+ UInt32 sequence,
+ const String& data);
+ static ClipboardChunk*
+ end(ClipboardID id, UInt32 sequence);
+
+ static int assemble(
+ barrier::IStream* stream,
+ String& dataCached,
+ ClipboardID& id,
+ UInt32& sequence);
+
+ static void send(barrier::IStream* stream, void* data);
+
+ static size_t getExpectedSize() { return s_expectedSize; }
+
+private:
+ static size_t s_expectedSize;
+};
diff --git a/src/lib/barrier/DragInformation.cpp b/src/lib/barrier/DragInformation.cpp
new file mode 100644
index 0000000..db28f3d
--- /dev/null
+++ b/src/lib/barrier/DragInformation.cpp
@@ -0,0 +1,158 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-2016 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 "barrier/DragInformation.h"
+#include "base/Log.h"
+
+#include <fstream>
+#include <sstream>
+#include <stdexcept>
+
+using namespace std;
+
+DragInformation::DragInformation() :
+ m_filename(),
+ m_filesize(0)
+{
+}
+
+void
+DragInformation::parseDragInfo(DragFileList& dragFileList, UInt32 fileNum, String data)
+{
+ size_t startPos = 0;
+ size_t findResult1 = 0;
+ size_t findResult2 = 0;
+ dragFileList.clear();
+ String slash("\\");
+ if (data.find("/", startPos) != string::npos) {
+ slash = "/";
+ }
+
+ UInt32 index = 0;
+ while (index < fileNum) {
+ findResult1 = data.find(',', startPos);
+ findResult2 = data.find_last_of(slash, findResult1);
+
+ if (findResult1 == startPos) {
+ //TODO: file number does not match, something goes wrong
+ break;
+ }
+
+ // set filename
+ if (findResult1 - findResult2 > 1) {
+ String filename = data.substr(findResult2 + 1,
+ findResult1 - findResult2 - 1);
+ DragInformation di;
+ di.setFilename(filename);
+ dragFileList.push_back(di);
+ }
+ startPos = findResult1 + 1;
+
+ //set filesize
+ findResult2 = data.find(',', startPos);
+ if (findResult2 - findResult1 > 1) {
+ String filesize = data.substr(findResult1 + 1,
+ findResult2 - findResult1 - 1);
+ size_t size = stringToNum(filesize);
+ dragFileList.at(index).setFilesize(size);
+ }
+ startPos = findResult1 + 1;
+
+ ++index;
+ }
+
+ LOG((CLOG_DEBUG "drag info received, total drag file number: %i",
+ dragFileList.size()));
+
+ for (size_t i = 0; i < dragFileList.size(); ++i) {
+ LOG((CLOG_DEBUG "dragging file %i name: %s",
+ i + 1,
+ dragFileList.at(i).getFilename().c_str()));
+ }
+}
+
+String
+DragInformation::getDragFileExtension(String filename)
+{
+ size_t findResult = string::npos;
+ findResult = filename.find_last_of(".", filename.size());
+ if (findResult != string::npos) {
+ return filename.substr(findResult + 1, filename.size() - findResult - 1);
+ }
+ else {
+ return "";
+ }
+}
+
+int
+DragInformation::setupDragInfo(DragFileList& fileList, String& output)
+{
+ int size = static_cast<int>(fileList.size());
+ for (int i = 0; i < size; ++i) {
+ output.append(fileList.at(i).getFilename());
+ output.append(",");
+ String filesize = getFileSize(fileList.at(i).getFilename());
+ output.append(filesize);
+ output.append(",");
+ }
+ return size;
+}
+
+bool
+DragInformation::isFileValid(String filename)
+{
+ bool result = false;
+ std::fstream file(filename.c_str(), ios::in|ios::binary);
+
+ if (file.is_open()) {
+ result = true;
+ }
+
+ file. close();
+
+ return result;
+}
+
+size_t
+DragInformation::stringToNum(String& str)
+{
+ istringstream iss(str.c_str());
+ size_t size;
+ iss >> size;
+ return size;
+}
+
+String
+DragInformation::getFileSize(String& filename)
+{
+ std::fstream file(filename.c_str(), ios::in|ios::binary);
+
+ if (!file.is_open()) {
+ throw std::runtime_error("failed to get file size");
+ }
+
+ // check file size
+ file.seekg (0, std::ios::end);
+ size_t size = (size_t)file.tellg();
+
+ stringstream ss;
+ ss << size;
+
+ file. close();
+
+ return ss.str();
+}
diff --git a/src/lib/barrier/DragInformation.h b/src/lib/barrier/DragInformation.h
new file mode 100644
index 0000000..b985bd1
--- /dev/null
+++ b/src/lib/barrier/DragInformation.h
@@ -0,0 +1,53 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-2016 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 "common/stdvector.h"
+#include "base/String.h"
+#include "base/EventTypes.h"
+
+class DragInformation;
+typedef std::vector<DragInformation> DragFileList;
+
+class DragInformation {
+public:
+ DragInformation();
+ ~DragInformation() { }
+
+ String& getFilename() { return m_filename; }
+ void setFilename(String& name) { m_filename = name; }
+ size_t getFilesize() { return m_filesize; }
+ void setFilesize(size_t size) { m_filesize = size; }
+
+ static void parseDragInfo(DragFileList& dragFileList, UInt32 fileNum, String data);
+ static String getDragFileExtension(String filename);
+ // helper function to setup drag info
+ // example: filename1,filesize1,filename2,filesize2,
+ // return file count
+ static int setupDragInfo(DragFileList& fileList, String& output);
+
+ static bool isFileValid(String filename);
+
+private:
+ static size_t stringToNum(String& str);
+ static String getFileSize(String& filename);
+
+private:
+ String m_filename;
+ size_t m_filesize;
+};
diff --git a/src/lib/barrier/DropHelper.cpp b/src/lib/barrier/DropHelper.cpp
new file mode 100644
index 0000000..ee5e5ee
--- /dev/null
+++ b/src/lib/barrier/DropHelper.cpp
@@ -0,0 +1,53 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "barrier/DropHelper.h"
+
+#include "base/Log.h"
+
+#include <fstream>
+
+void
+DropHelper::writeToDir(const String& destination, DragFileList& fileList, String& data)
+{
+ LOG((CLOG_DEBUG "dropping file, files=%i target=%s", fileList.size(), destination.c_str()));
+
+ if (!destination.empty() && fileList.size() > 0) {
+ std::fstream file;
+ String dropTarget = destination;
+#ifdef SYSAPI_WIN32
+ dropTarget.append("\\");
+#else
+ dropTarget.append("/");
+#endif
+ dropTarget.append(fileList.at(0).getFilename());
+ file.open(dropTarget.c_str(), std::ios::out | std::ios::binary);
+ if (!file.is_open()) {
+ LOG((CLOG_ERR "drop file failed: can not open %s", dropTarget.c_str()));
+ }
+
+ file.write(data.c_str(), data.size());
+ file.close();
+
+ LOG((CLOG_DEBUG "%s is saved to %s", fileList.at(0).getFilename().c_str(), destination.c_str()));
+
+ fileList.clear();
+ }
+ else {
+ LOG((CLOG_ERR "drop file failed: drop target is empty"));
+ }
+}
diff --git a/src/lib/barrier/DropHelper.h b/src/lib/barrier/DropHelper.h
new file mode 100644
index 0000000..67facbb
--- /dev/null
+++ b/src/lib/barrier/DropHelper.h
@@ -0,0 +1,27 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "barrier/DragInformation.h"
+#include "base/String.h"
+
+class DropHelper {
+public:
+ static void writeToDir(const String& destination,
+ DragFileList& fileList, String& data);
+};
diff --git a/src/lib/barrier/FileChunk.cpp b/src/lib/barrier/FileChunk.cpp
new file mode 100644
index 0000000..3a98568
--- /dev/null
+++ b/src/lib/barrier/FileChunk.cpp
@@ -0,0 +1,156 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-2016 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 "barrier/FileChunk.h"
+
+#include "barrier/ProtocolUtil.h"
+#include "barrier/protocol_types.h"
+#include "io/IStream.h"
+#include "base/Stopwatch.h"
+#include "base/Log.h"
+
+static const UInt16 kIntervalThreshold = 1;
+
+FileChunk::FileChunk(size_t size) :
+ Chunk(size)
+{
+ m_dataSize = size - FILE_CHUNK_META_SIZE;
+}
+
+FileChunk*
+FileChunk::start(const String& size)
+{
+ size_t sizeLength = size.size();
+ FileChunk* start = new FileChunk(sizeLength + FILE_CHUNK_META_SIZE);
+ char* chunk = start->m_chunk;
+ chunk[0] = kDataStart;
+ memcpy(&chunk[1], size.c_str(), sizeLength);
+ chunk[sizeLength + 1] = '\0';
+
+ return start;
+}
+
+FileChunk*
+FileChunk::data(UInt8* data, size_t dataSize)
+{
+ FileChunk* chunk = new FileChunk(dataSize + FILE_CHUNK_META_SIZE);
+ char* chunkData = chunk->m_chunk;
+ chunkData[0] = kDataChunk;
+ memcpy(&chunkData[1], data, dataSize);
+ chunkData[dataSize + 1] = '\0';
+
+ return chunk;
+}
+
+FileChunk*
+FileChunk::end()
+{
+ FileChunk* end = new FileChunk(FILE_CHUNK_META_SIZE);
+ char* chunk = end->m_chunk;
+ chunk[0] = kDataEnd;
+ chunk[1] = '\0';
+
+ return end;
+}
+
+int
+FileChunk::assemble(barrier::IStream* stream, String& dataReceived, size_t& expectedSize)
+{
+ // parse
+ UInt8 mark = 0;
+ String content;
+ static size_t receivedDataSize;
+ static double elapsedTime;
+ static Stopwatch stopwatch;
+
+ if (!ProtocolUtil::readf(stream, kMsgDFileTransfer + 4, &mark, &content)) {
+ return kError;
+ }
+
+ switch (mark) {
+ case kDataStart:
+ dataReceived.clear();
+ expectedSize = barrier::string::stringToSizeType(content);
+ receivedDataSize = 0;
+ elapsedTime = 0;
+ stopwatch.reset();
+
+ if (CLOG->getFilter() >= kDEBUG2) {
+ LOG((CLOG_DEBUG2 "recv file size=%s", content.c_str()));
+ stopwatch.start();
+ }
+ return kStart;
+
+ case kDataChunk:
+ dataReceived.append(content);
+ if (CLOG->getFilter() >= kDEBUG2) {
+ LOG((CLOG_DEBUG2 "recv file chunck size=%i", content.size()));
+ double interval = stopwatch.getTime();
+ receivedDataSize += content.size();
+ LOG((CLOG_DEBUG2 "recv file interval=%f s", interval));
+ if (interval >= kIntervalThreshold) {
+ double averageSpeed = receivedDataSize / interval / 1000;
+ LOG((CLOG_DEBUG2 "recv file average speed=%f kb/s", averageSpeed));
+
+ receivedDataSize = 0;
+ elapsedTime += interval;
+ stopwatch.reset();
+ }
+ }
+ return kNotFinish;
+
+ case kDataEnd:
+ if (expectedSize != dataReceived.size()) {
+ LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataReceived.size()));
+ return kError;
+ }
+
+ if (CLOG->getFilter() >= kDEBUG2) {
+ LOG((CLOG_DEBUG2 "file transfer finished"));
+ elapsedTime += stopwatch.getTime();
+ double averageSpeed = expectedSize / elapsedTime / 1000;
+ LOG((CLOG_DEBUG2 "file transfer finished: total time consumed=%f s", elapsedTime));
+ LOG((CLOG_DEBUG2 "file transfer finished: total data received=%i kb", expectedSize / 1000));
+ LOG((CLOG_DEBUG2 "file transfer finished: total average speed=%f kb/s", averageSpeed));
+ }
+ return kFinish;
+ }
+
+ return kError;
+}
+
+void
+FileChunk::send(barrier::IStream* stream, UInt8 mark, char* data, size_t dataSize)
+{
+ String chunk(data, dataSize);
+
+ switch (mark) {
+ case kDataStart:
+ LOG((CLOG_DEBUG2 "sending file chunk start: size=%s", data));
+ break;
+
+ case kDataChunk:
+ LOG((CLOG_DEBUG2 "sending file chunk: size=%i", chunk.size()));
+ break;
+
+ case kDataEnd:
+ LOG((CLOG_DEBUG2 "sending file finished"));
+ break;
+ }
+
+ ProtocolUtil::writef(stream, kMsgDFileTransfer, mark, &chunk);
+}
diff --git a/src/lib/barrier/FileChunk.h b/src/lib/barrier/FileChunk.h
new file mode 100644
index 0000000..bdc2f64
--- /dev/null
+++ b/src/lib/barrier/FileChunk.h
@@ -0,0 +1,46 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-2016 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 "barrier/Chunk.h"
+#include "base/String.h"
+#include "common/basic_types.h"
+
+#define FILE_CHUNK_META_SIZE 2
+
+namespace barrier {
+class IStream;
+};
+
+class FileChunk : public Chunk {
+public:
+ FileChunk(size_t size);
+
+ static FileChunk* start(const String& size);
+ static FileChunk* data(UInt8* data, size_t dataSize);
+ static FileChunk* end();
+ static int assemble(
+ barrier::IStream* stream,
+ String& dataCached,
+ size_t& expectedSize);
+ static void send(
+ barrier::IStream* stream,
+ UInt8 mark,
+ char* data,
+ size_t dataSize);
+};
diff --git a/src/lib/barrier/IApp.h b/src/lib/barrier/IApp.h
new file mode 100644
index 0000000..3a8cd56
--- /dev/null
+++ b/src/lib/barrier/IApp.h
@@ -0,0 +1,47 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "common/IInterface.h"
+
+typedef int (*StartupFunc)(int, char**);
+
+class ILogOutputter;
+class ArgsBase;
+class IArchTaskBarReceiver;
+namespace barrier { class Screen; }
+class IEventQueue;
+
+class IApp : public IInterface
+{
+public:
+ virtual void setByeFunc(void(*bye)(int)) = 0;
+ virtual ArgsBase& argsBase() const = 0;
+ virtual int standardStartup(int argc, char** argv) = 0;
+ virtual int runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) = 0;
+ virtual void startNode() = 0;
+ virtual IArchTaskBarReceiver* taskBarReceiver() const = 0;
+ virtual void bye(int error) = 0;
+ virtual int mainLoop() = 0;
+ virtual void initApp(int argc, const char** argv) = 0;
+ virtual const char* daemonName() const = 0;
+ virtual int foregroundStartup(int argc, char** argv) = 0;
+ virtual barrier::Screen* createScreen() = 0;
+ virtual IEventQueue* getEvents() const = 0;
+};
diff --git a/src/lib/barrier/IAppUtil.h b/src/lib/barrier/IAppUtil.h
new file mode 100644
index 0000000..39df65d
--- /dev/null
+++ b/src/lib/barrier/IAppUtil.h
@@ -0,0 +1,31 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "common/IInterface.h"
+#include "barrier/IApp.h"
+
+class IAppUtil : public IInterface {
+public:
+ virtual void adoptApp(IApp* app) = 0;
+ virtual IApp& app() const = 0;
+ virtual int run(int argc, char** argv) = 0;
+ virtual void beforeAppExit() = 0;
+ virtual void startNode() = 0;
+};
diff --git a/src/lib/barrier/IClient.h b/src/lib/barrier/IClient.h
new file mode 100644
index 0000000..d9b2194
--- /dev/null
+++ b/src/lib/barrier/IClient.h
@@ -0,0 +1,176 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/clipboard_types.h"
+#include "barrier/IScreen.h"
+#include "barrier/key_types.h"
+#include "barrier/mouse_types.h"
+#include "barrier/option_types.h"
+#include "base/String.h"
+
+//! Client interface
+/*!
+This interface defines the methods necessary for the server to
+communicate with a client.
+*/
+class IClient : public IScreen {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Enter screen
+ /*!
+ Enter the screen. The cursor should be warped to \p xAbs,yAbs.
+ \p mask is the expected toggle button state and the client should
+ update its state to match. \p forScreensaver is true iff the
+ screen is being entered because the screen saver is starting.
+ Subsequent clipboard events should report \p seqNum.
+ */
+ virtual void enter(SInt32 xAbs, SInt32 yAbs,
+ UInt32 seqNum, KeyModifierMask mask,
+ bool forScreensaver) = 0;
+
+ //! Leave screen
+ /*!
+ Leave the screen. Return false iff the user may not leave the
+ client's screen (because, for example, a button is down).
+ */
+ virtual bool leave() = 0;
+
+ //! Set clipboard
+ /*!
+ Update the client's clipboard. This implies that the client's
+ clipboard is now up to date. If the client's clipboard was
+ already known to be up to date then this may do nothing. \c data
+ has marshalled clipboard data.
+ */
+ virtual void setClipboard(ClipboardID, const IClipboard*) = 0;
+
+ //! Grab clipboard
+ /*!
+ Grab (i.e. take ownership of) the client's clipboard. Since this
+ is called when another client takes ownership of the clipboard it
+ implies that the client's clipboard is out of date.
+ */
+ virtual void grabClipboard(ClipboardID) = 0;
+
+ //! Mark clipboard dirty
+ /*!
+ Mark the client's clipboard as dirty (out of date) or clean (up to
+ date).
+ */
+ virtual void setClipboardDirty(ClipboardID, bool dirty) = 0;
+
+ //! Notify of key press
+ /*!
+ Synthesize key events to generate a press of key \c id. If possible
+ match the given modifier mask. The KeyButton identifies the physical
+ key on the server that generated this key down. The client must
+ ensure that a key up or key repeat that uses the same KeyButton will
+ synthesize an up or repeat for the same client key synthesized by
+ keyDown().
+ */
+ virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0;
+
+ //! Notify of key repeat
+ /*!
+ Synthesize key events to generate a press and release of key \c id
+ \c count times. If possible match the given modifier mask.
+ */
+ virtual void keyRepeat(KeyID id, KeyModifierMask,
+ SInt32 count, KeyButton) = 0;
+
+ //! Notify of key release
+ /*!
+ Synthesize key events to generate a release of key \c id. If possible
+ match the given modifier mask.
+ */
+ virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0;
+
+ //! Notify of mouse press
+ /*!
+ Synthesize mouse events to generate a press of mouse button \c id.
+ */
+ virtual void mouseDown(ButtonID id) = 0;
+
+ //! Notify of mouse release
+ /*!
+ Synthesize mouse events to generate a release of mouse button \c id.
+ */
+ virtual void mouseUp(ButtonID id) = 0;
+
+ //! Notify of mouse motion
+ /*!
+ Synthesize mouse events to generate mouse motion to the absolute
+ screen position \c xAbs,yAbs.
+ */
+ virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0;
+
+ //! Notify of mouse motion
+ /*!
+ Synthesize mouse events to generate mouse motion by the relative
+ amount \c xRel,yRel.
+ */
+ virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0;
+
+ //! Notify of mouse wheel motion
+ /*!
+ Synthesize mouse events to generate mouse wheel motion of \c xDelta
+ and \c yDelta. Deltas are positive for motion away from the user or
+ to the right and negative for motion towards the user or to the left.
+ Each wheel click should generate a delta of +/-120.
+ */
+ virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta) = 0;
+
+ //! Notify of screen saver change
+ virtual void screensaver(bool activate) = 0;
+
+ //! Notify of options changes
+ /*!
+ Reset all options to their default values.
+ */
+ virtual void resetOptions() = 0;
+
+ //! Notify of options changes
+ /*!
+ Set options to given values. Ignore unknown options and don't
+ modify our options that aren't given in \c options.
+ */
+ virtual void setOptions(const OptionsList& options) = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get client name
+ /*!
+ Return the client's name.
+ */
+ virtual String getName() const = 0;
+
+ //@}
+
+ // IScreen overrides
+ virtual void* getEventTarget() const = 0;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const = 0;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
+};
diff --git a/src/lib/barrier/IClipboard.cpp b/src/lib/barrier/IClipboard.cpp
new file mode 100644
index 0000000..19b4b56
--- /dev/null
+++ b/src/lib/barrier/IClipboard.cpp
@@ -0,0 +1,168 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 Chris Schoeneman
+ *
+ * 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 "barrier/IClipboard.h"
+#include "common/stdvector.h"
+
+//
+// IClipboard
+//
+
+void
+IClipboard::unmarshall(IClipboard* clipboard, const String& data, Time time)
+{
+ assert(clipboard != NULL);
+
+ const char* index = data.data();
+
+ if (clipboard->open(time)) {
+ // clear existing data
+ clipboard->empty();
+
+ // read the number of formats
+ const UInt32 numFormats = readUInt32(index);
+ index += 4;
+
+ // read each format
+ for (UInt32 i = 0; i < numFormats; ++i) {
+ // get the format id
+ IClipboard::EFormat format =
+ static_cast<IClipboard::EFormat>(readUInt32(index));
+ index += 4;
+
+ // get the size of the format data
+ UInt32 size = readUInt32(index);
+ index += 4;
+
+ // save the data if it's a known format. if either the client
+ // or server supports more clipboard formats than the other
+ // then one of them will get a format >= kNumFormats here.
+ if (format <IClipboard::kNumFormats) {
+ clipboard->add(format, String(index, size));
+ }
+ index += size;
+ }
+
+ // done
+ clipboard->close();
+ }
+}
+
+String
+IClipboard::marshall(const IClipboard* clipboard)
+{
+ // return data format:
+ // 4 bytes => number of formats included
+ // 4 bytes => format enum
+ // 4 bytes => clipboard data size n
+ // n bytes => clipboard data
+ // back to the second 4 bytes if there is another format
+
+ assert(clipboard != NULL);
+
+ String data;
+
+ std::vector<String> formatData;
+ formatData.resize(IClipboard::kNumFormats);
+ // FIXME -- use current time
+ if (clipboard->open(0)) {
+
+ // compute size of marshalled data
+ UInt32 size = 4;
+ UInt32 numFormats = 0;
+ for (UInt32 format = 0; format != IClipboard::kNumFormats; ++format) {
+ if (clipboard->has(static_cast<IClipboard::EFormat>(format))) {
+ ++numFormats;
+ formatData[format] =
+ clipboard->get(static_cast<IClipboard::EFormat>(format));
+ size += 4 + 4 + (UInt32)formatData[format].size();
+ }
+ }
+
+ // allocate space
+ data.reserve(size);
+
+ // marshall the data
+ writeUInt32(&data, numFormats);
+ for (UInt32 format = 0; format != IClipboard::kNumFormats; ++format) {
+ if (clipboard->has(static_cast<IClipboard::EFormat>(format))) {
+ writeUInt32(&data, format);
+ writeUInt32(&data, (UInt32)formatData[format].size());
+ data += formatData[format];
+ }
+ }
+ clipboard->close();
+ }
+
+ return data;
+}
+
+bool
+IClipboard::copy(IClipboard* dst, const IClipboard* src)
+{
+ assert(dst != NULL);
+ assert(src != NULL);
+
+ return copy(dst, src, src->getTime());
+}
+
+bool
+IClipboard::copy(IClipboard* dst, const IClipboard* src, Time time)
+{
+ assert(dst != NULL);
+ assert(src != NULL);
+
+ bool success = false;
+ if (src->open(time)) {
+ if (dst->open(time)) {
+ if (dst->empty()) {
+ for (SInt32 format = 0;
+ format != IClipboard::kNumFormats; ++format) {
+ IClipboard::EFormat eFormat = (IClipboard::EFormat)format;
+ if (src->has(eFormat)) {
+ dst->add(eFormat, src->get(eFormat));
+ }
+ }
+ success = true;
+ }
+ dst->close();
+ }
+ src->close();
+ }
+
+ return success;
+}
+
+UInt32
+IClipboard::readUInt32(const char* buf)
+{
+ const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf);
+ return (static_cast<UInt32>(ubuf[0]) << 24) |
+ (static_cast<UInt32>(ubuf[1]) << 16) |
+ (static_cast<UInt32>(ubuf[2]) << 8) |
+ static_cast<UInt32>(ubuf[3]);
+}
+
+void
+IClipboard::writeUInt32(String* buf, UInt32 v)
+{
+ *buf += static_cast<UInt8>((v >> 24) & 0xff);
+ *buf += static_cast<UInt8>((v >> 16) & 0xff);
+ *buf += static_cast<UInt8>((v >> 8) & 0xff);
+ *buf += static_cast<UInt8>( v & 0xff);
+}
diff --git a/src/lib/barrier/IClipboard.h b/src/lib/barrier/IClipboard.h
new file mode 100644
index 0000000..e11b264
--- /dev/null
+++ b/src/lib/barrier/IClipboard.h
@@ -0,0 +1,169 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "base/String.h"
+#include "base/EventTypes.h"
+#include "common/IInterface.h"
+
+//! Clipboard interface
+/*!
+This interface defines the methods common to all clipboards.
+*/
+class IClipboard : public IInterface {
+public:
+ //! Timestamp type
+ /*!
+ Timestamp type. Timestamps are in milliseconds from some
+ arbitrary starting time. Timestamps will wrap around to 0
+ after about 49 3/4 days.
+ */
+ typedef UInt32 Time;
+
+ //! Clipboard formats
+ /*!
+ The list of known clipboard formats. kNumFormats must be last and
+ formats must be sequential starting from zero. Clipboard data set
+ via add() and retrieved via get() must be in one of these formats.
+ Platform dependent clipboard subclasses can and should present any
+ suitable formats derivable from these formats.
+
+ \c kText is a text format encoded in UTF-8. Newlines are LF (not
+ CR or LF/CR).
+
+ \c kBitmap is an image format. The data is a BMP file without the
+ 14 byte header (i.e. starting at the INFOHEADER) and with the image
+ data immediately following the 40 byte INFOHEADER.
+
+ \c kHTML is a text format encoded in UTF-8 and containing a valid
+ HTML fragment (but not necessarily a complete HTML document).
+ Newlines are LF.
+ */
+ enum EFormat {
+ kText, //!< Text format, UTF-8, newline is LF
+ kHTML, //!< HTML format, HTML fragment, UTF-8, newline is LF
+ kBitmap, //!< Bitmap format, BMP 24/32bpp, BI_RGB
+ kNumFormats //!< The number of clipboard formats
+ };
+
+ //! @name manipulators
+ //@{
+
+ //! Empty clipboard
+ /*!
+ Take ownership of the clipboard and clear all data from it.
+ This must be called between a successful open() and close().
+ Return false if the clipboard ownership could not be taken;
+ the clipboard should not be emptied in this case.
+ */
+ virtual bool empty() = 0;
+
+ //! Add data
+ /*!
+ Add data in the given format to the clipboard. May only be
+ called after a successful empty().
+ */
+ virtual void add(EFormat, const String& data) = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Open clipboard
+ /*!
+ Open the clipboard. Return true iff the clipboard could be
+ opened. If open() returns true then the client must call
+ close() at some later time; if it returns false then close()
+ must not be called. \c time should be the current time or
+ a time in the past when the open should effectively have taken
+ place.
+ */
+ virtual bool open(Time time) const = 0;
+
+ //! Close clipboard
+ /*!
+ Close the clipboard. close() must match a preceding successful
+ open(). This signals that the clipboard has been filled with
+ all the necessary data or all data has been read. It does not
+ mean the clipboard ownership should be released (if it was
+ taken).
+ */
+ virtual void close() const = 0;
+
+ //! Get time
+ /*!
+ Return the timestamp passed to the last successful open().
+ */
+ virtual Time getTime() const = 0;
+
+ //! Check for data
+ /*!
+ Return true iff the clipboard contains data in the given
+ format. Must be called between a successful open() and close().
+ */
+ virtual bool has(EFormat) const = 0;
+
+ //! Get data
+ /*!
+ Return the data in the given format. Returns the empty string
+ if there is no data in that format. Must be called between
+ a successful open() and close().
+ */
+ virtual String get(EFormat) const = 0;
+
+ //! Marshall clipboard data
+ /*!
+ Merge \p clipboard's data into a single buffer that can be later
+ unmarshalled to restore the clipboard and return the buffer.
+ */
+ static String marshall(const IClipboard* clipboard);
+
+ //! Unmarshall clipboard data
+ /*!
+ Extract marshalled clipboard data and store it in \p clipboard.
+ Sets the clipboard time to \c time.
+ */
+ static void unmarshall(IClipboard* clipboard,
+ const String& data, Time time);
+
+ //! Copy clipboard
+ /*!
+ Transfers all the data in one clipboard to another. The
+ clipboards can be of any concrete clipboard type (and
+ they don't have to be the same type). This also sets
+ the destination clipboard's timestamp to source clipboard's
+ timestamp. Returns true iff the copy succeeded.
+ */
+ static bool copy(IClipboard* dst, const IClipboard* src);
+
+ //! Copy clipboard
+ /*!
+ Transfers all the data in one clipboard to another. The
+ clipboards can be of any concrete clipboard type (and they
+ don't have to be the same type). This also sets the
+ timestamp to \c time. Returns true iff the copy succeeded.
+ */
+ static bool copy(IClipboard* dst, const IClipboard* src, Time);
+
+ //@}
+
+private:
+ static UInt32 readUInt32(const char*);
+ static void writeUInt32(String*, UInt32);
+};
diff --git a/src/lib/barrier/IKeyState.cpp b/src/lib/barrier/IKeyState.cpp
new file mode 100644
index 0000000..5d1114c
--- /dev/null
+++ b/src/lib/barrier/IKeyState.cpp
@@ -0,0 +1,161 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 Chris Schoeneman
+ *
+ * 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 "barrier/IKeyState.h"
+#include "base/EventQueue.h"
+
+#include <cstring>
+#include <cstdlib>
+
+//
+// IKeyState
+//
+
+IKeyState::IKeyState(IEventQueue* events)
+{
+}
+
+//
+// IKeyState::KeyInfo
+//
+
+IKeyState::KeyInfo*
+IKeyState::KeyInfo::alloc(KeyID id,
+ KeyModifierMask mask, KeyButton button, SInt32 count)
+{
+ KeyInfo* info = (KeyInfo*)malloc(sizeof(KeyInfo));
+ info->m_key = id;
+ info->m_mask = mask;
+ info->m_button = button;
+ info->m_count = count;
+ info->m_screens = NULL;
+ info->m_screensBuffer[0] = '\0';
+ return info;
+}
+
+IKeyState::KeyInfo*
+IKeyState::KeyInfo::alloc(KeyID id,
+ KeyModifierMask mask, KeyButton button, SInt32 count,
+ const std::set<String>& destinations)
+{
+ String screens = join(destinations);
+
+ // build structure
+ KeyInfo* info = (KeyInfo*)malloc(sizeof(KeyInfo) + screens.size());
+ info->m_key = id;
+ info->m_mask = mask;
+ info->m_button = button;
+ info->m_count = count;
+ info->m_screens = info->m_screensBuffer;
+ strcpy(info->m_screensBuffer, screens.c_str());
+ return info;
+}
+
+IKeyState::KeyInfo*
+IKeyState::KeyInfo::alloc(const KeyInfo& x)
+{
+ KeyInfo* info = (KeyInfo*)malloc(sizeof(KeyInfo) +
+ strlen(x.m_screensBuffer));
+ info->m_key = x.m_key;
+ info->m_mask = x.m_mask;
+ info->m_button = x.m_button;
+ info->m_count = x.m_count;
+ info->m_screens = x.m_screens ? info->m_screensBuffer : NULL;
+ strcpy(info->m_screensBuffer, x.m_screensBuffer);
+ return info;
+}
+
+bool
+IKeyState::KeyInfo::isDefault(const char* screens)
+{
+ return (screens == NULL || screens[0] == '\0');
+}
+
+bool
+IKeyState::KeyInfo::contains(const char* screens, const String& name)
+{
+ // special cases
+ if (isDefault(screens)) {
+ return false;
+ }
+ if (screens[0] == '*') {
+ return true;
+ }
+
+ // search
+ String match;
+ match.reserve(name.size() + 2);
+ match += ":";
+ match += name;
+ match += ":";
+ return (strstr(screens, match.c_str()) != NULL);
+}
+
+bool
+IKeyState::KeyInfo::equal(const KeyInfo* a, const KeyInfo* b)
+{
+ return (a->m_key == b->m_key &&
+ a->m_mask == b->m_mask &&
+ a->m_button == b->m_button &&
+ a->m_count == b->m_count &&
+ strcmp(a->m_screensBuffer, b->m_screensBuffer) == 0);
+}
+
+String
+IKeyState::KeyInfo::join(const std::set<String>& destinations)
+{
+ // collect destinations into a string. names are surrounded by ':'
+ // which makes searching easy. the string is empty if there are no
+ // destinations and "*" means all destinations.
+ String screens;
+ for (std::set<String>::const_iterator i = destinations.begin();
+ i != destinations.end(); ++i) {
+ if (*i == "*") {
+ screens = "*";
+ break;
+ }
+ else {
+ if (screens.empty()) {
+ screens = ":";
+ }
+ screens += *i;
+ screens += ":";
+ }
+ }
+ return screens;
+}
+
+void
+IKeyState::KeyInfo::split(const char* screens, std::set<String>& dst)
+{
+ dst.clear();
+ if (isDefault(screens)) {
+ return;
+ }
+ if (screens[0] == '*') {
+ dst.insert("*");
+ return;
+ }
+
+ const char* i = screens + 1;
+ while (*i != '\0') {
+ const char* j = strchr(i, ':');
+ dst.insert(String(i, j - i));
+ i = j + 1;
+ }
+}
diff --git a/src/lib/barrier/IKeyState.h b/src/lib/barrier/IKeyState.h
new file mode 100644
index 0000000..b9d4706
--- /dev/null
+++ b/src/lib/barrier/IKeyState.h
@@ -0,0 +1,174 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 Chris Schoeneman
+ *
+ * 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 "barrier/key_types.h"
+#include "base/Event.h"
+#include "base/String.h"
+#include "base/IEventQueue.h"
+#include "base/EventTypes.h"
+#include "common/stdset.h"
+#include "common/IInterface.h"
+
+//! Key state interface
+/*!
+This interface provides access to set and query the keyboard state and
+to synthesize key events.
+*/
+class IKeyState : public IInterface {
+public:
+ IKeyState(IEventQueue* events);
+
+ enum {
+ kNumButtons = 0x200
+ };
+
+ //! Key event data
+ class KeyInfo {
+ public:
+ static KeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count);
+ static KeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count,
+ const std::set<String>& destinations);
+ static KeyInfo* alloc(const KeyInfo&);
+
+ static bool isDefault(const char* screens);
+ static bool contains(const char* screens, const String& name);
+ static bool equal(const KeyInfo*, const KeyInfo*);
+ static String join(const std::set<String>& destinations);
+ static void split(const char* screens, std::set<String>&);
+
+ public:
+ KeyID m_key;
+ KeyModifierMask m_mask;
+ KeyButton m_button;
+ SInt32 m_count;
+ char* m_screens;
+ char m_screensBuffer[1];
+ };
+
+ typedef std::set<KeyButton> KeyButtonSet;
+
+ //! @name manipulators
+ //@{
+
+ //! Update the keyboard map
+ /*!
+ Causes the key state to get updated to reflect the current keyboard
+ mapping.
+ */
+ virtual void updateKeyMap() = 0;
+
+ //! Update the key state
+ /*!
+ Causes the key state to get updated to reflect the physical keyboard
+ state.
+ */
+ virtual void updateKeyState() = 0;
+
+ //! Set half-duplex mask
+ /*!
+ Sets which modifier toggle keys are half-duplex. A half-duplex
+ toggle key doesn't report a key release when toggled on and
+ doesn't report a key press when toggled off.
+ */
+ virtual void setHalfDuplexMask(KeyModifierMask) = 0;
+
+ //! Fake a key press
+ /*!
+ Synthesizes a key press event and updates the key state.
+ */
+ virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button) = 0;
+
+ //! Fake a key repeat
+ /*!
+ Synthesizes a key repeat event and updates the key state.
+ */
+ virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button) = 0;
+
+ //! Fake a key release
+ /*!
+ Synthesizes a key release event and updates the key state.
+ */
+ virtual bool fakeKeyUp(KeyButton button) = 0;
+
+ //! Fake key releases for all fake pressed keys
+ /*!
+ Synthesizes a key release event for every key that is synthetically
+ pressed and updates the key state.
+ */
+ virtual void fakeAllKeysUp() = 0;
+
+ //! Fake ctrl+alt+del
+ /*!
+ Synthesize a press of ctrl+alt+del. Return true if processing is
+ complete and false if normal key processing should continue.
+ */
+ virtual bool fakeCtrlAltDel() = 0;
+
+ //! Fake a media key
+ /*!
+ Synthesizes a media key down and up. Only Mac would implement this by
+ use cocoa appkit framework.
+ */
+ virtual bool fakeMediaKey(KeyID id) = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Test if key is pressed
+ /*!
+ Returns true iff the given key is down. Half-duplex toggles
+ always return false.
+ */
+ virtual bool isKeyDown(KeyButton) const = 0;
+
+ //! Get the active modifiers
+ /*!
+ Returns the modifiers that are currently active according to our
+ shadowed state.
+ */
+ virtual KeyModifierMask
+ getActiveModifiers() const = 0;
+
+ //! Get the active modifiers from OS
+ /*!
+ Returns the modifiers that are currently active according to the
+ operating system.
+ */
+ virtual KeyModifierMask
+ pollActiveModifiers() const = 0;
+
+ //! Get the active keyboard layout from OS
+ /*!
+ Returns the active keyboard layout according to the operating system.
+ */
+ virtual SInt32 pollActiveGroup() const = 0;
+
+ //! Get the keys currently pressed from OS
+ /*!
+ Adds any keys that are currently pressed according to the operating
+ system to \p pressedKeys.
+ */
+ virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0;
+
+ //@}
+};
diff --git a/src/lib/barrier/INode.h b/src/lib/barrier/INode.h
new file mode 100644
index 0000000..2e78f7c
--- /dev/null
+++ b/src/lib/barrier/INode.h
@@ -0,0 +1,25 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "common/IInterface.h"
+
+class INode : IInterface {
+
+};
diff --git a/src/lib/barrier/IPlatformScreen.cpp b/src/lib/barrier/IPlatformScreen.cpp
new file mode 100644
index 0000000..d1d9f78
--- /dev/null
+++ b/src/lib/barrier/IPlatformScreen.cpp
@@ -0,0 +1,24 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2016 Symless.
+ *
+ * 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 COPYING 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 "barrier/IPlatformScreen.h"
+
+bool
+IPlatformScreen::fakeMediaKey(KeyID id)
+{
+ return false;
+}
diff --git a/src/lib/barrier/IPlatformScreen.h b/src/lib/barrier/IPlatformScreen.h
new file mode 100644
index 0000000..440e218
--- /dev/null
+++ b/src/lib/barrier/IPlatformScreen.h
@@ -0,0 +1,227 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/DragInformation.h"
+#include "barrier/clipboard_types.h"
+#include "barrier/IScreen.h"
+#include "barrier/IPrimaryScreen.h"
+#include "barrier/ISecondaryScreen.h"
+#include "barrier/IKeyState.h"
+#include "barrier/option_types.h"
+
+class IClipboard;
+
+//! Screen interface
+/*!
+This interface defines the methods common to all platform dependent
+screen implementations that are used by both primary and secondary
+screens.
+*/
+class IPlatformScreen : public IScreen,
+ public IPrimaryScreen, public ISecondaryScreen,
+ public IKeyState {
+public:
+ //! @name manipulators
+ //@{
+
+ IPlatformScreen(IEventQueue* events) : IKeyState(events) { }
+
+ //! Enable screen
+ /*!
+ Enable the screen, preparing it to report system and user events.
+ For a secondary screen it also means preparing to synthesize events
+ and hiding the cursor.
+ */
+ virtual void enable() = 0;
+
+ //! Disable screen
+ /*!
+ Undoes the operations in enable() and events should no longer
+ be reported.
+ */
+ virtual void disable() = 0;
+
+ //! Enter screen
+ /*!
+ Called when the user navigates to this screen.
+ */
+ virtual void enter() = 0;
+
+ //! Leave screen
+ /*!
+ Called when the user navigates off the screen. Returns true on
+ success, false on failure. A typical reason for failure is being
+ unable to install the keyboard and mouse snoopers on a primary
+ screen. Secondary screens should not fail.
+ */
+ virtual bool leave() = 0;
+
+ //! Set clipboard
+ /*!
+ Set the contents of the system clipboard indicated by \c id.
+ */
+ virtual bool setClipboard(ClipboardID id, const IClipboard*) = 0;
+
+ //! Check clipboard owner
+ /*!
+ Check ownership of all clipboards and post grab events for any that
+ have changed. This is used as a backup in case the system doesn't
+ reliably report clipboard ownership changes.
+ */
+ virtual void checkClipboards() = 0;
+
+ //! Open screen saver
+ /*!
+ Open the screen saver. If \c notify is true then this object must
+ send events when the screen saver activates or deactivates until
+ \c closeScreensaver() is called. If \c notify is false then the
+ screen saver is disabled and restored on \c closeScreensaver().
+ */
+ virtual void openScreensaver(bool notify) = 0;
+
+ //! Close screen saver
+ /*!
+ // Close the screen saver. Stop reporting screen saver activation
+ and deactivation and, if the screen saver was disabled by
+ openScreensaver(), enable the screen saver.
+ */
+ virtual void closeScreensaver() = 0;
+
+ //! Activate/deactivate screen saver
+ /*!
+ Forcibly activate the screen saver if \c activate is true otherwise
+ forcibly deactivate it.
+ */
+ virtual void screensaver(bool activate) = 0;
+
+ //! Notify of options changes
+ /*!
+ Reset all options to their default values.
+ */
+ virtual void resetOptions() = 0;
+
+ //! Notify of options changes
+ /*!
+ Set options to given values. Ignore unknown options and don't
+ modify options that aren't given in \c options.
+ */
+ virtual void setOptions(const OptionsList& options) = 0;
+
+ //! Set clipboard sequence number
+ /*!
+ Sets the sequence number to use in subsequent clipboard events.
+ */
+ virtual void setSequenceNumber(UInt32) = 0;
+
+ //! Change dragging status
+ virtual void setDraggingStarted(bool started) = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Test if is primary screen
+ /*!
+ Return true iff this screen is a primary screen.
+ */
+ virtual bool isPrimary() const = 0;
+
+ //@}
+
+ // IScreen overrides
+ virtual void* getEventTarget() const = 0;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const = 0;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
+
+ // IPrimaryScreen overrides
+ virtual void reconfigure(UInt32 activeSides) = 0;
+ virtual void warpCursor(SInt32 x, SInt32 y) = 0;
+ virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask) = 0;
+ virtual void unregisterHotKey(UInt32 id) = 0;
+ virtual void fakeInputBegin() = 0;
+ virtual void fakeInputEnd() = 0;
+ virtual SInt32 getJumpZoneSize() const = 0;
+ virtual bool isAnyMouseButtonDown(UInt32& buttonID) const = 0;
+ virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
+
+ // ISecondaryScreen overrides
+ virtual void fakeMouseButton(ButtonID id, bool press) = 0;
+ virtual void fakeMouseMove(SInt32 x, SInt32 y) = 0;
+ virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0;
+ virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0;
+
+ // IKeyState overrides
+ virtual void updateKeyMap() = 0;
+ virtual void updateKeyState() = 0;
+ virtual void setHalfDuplexMask(KeyModifierMask) = 0;
+ virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button) = 0;
+ virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button) = 0;
+ virtual bool fakeKeyUp(KeyButton button) = 0;
+ virtual void fakeAllKeysUp() = 0;
+ virtual bool fakeCtrlAltDel() = 0;
+ virtual bool fakeMediaKey(KeyID id);
+ virtual bool isKeyDown(KeyButton) const = 0;
+ virtual KeyModifierMask
+ getActiveModifiers() const = 0;
+ virtual KeyModifierMask
+ pollActiveModifiers() const = 0;
+ virtual SInt32 pollActiveGroup() const = 0;
+ virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0;
+
+ virtual String& getDraggingFilename() = 0;
+ virtual void clearDraggingFilename() = 0;
+ virtual bool isDraggingStarted() = 0;
+ virtual bool isFakeDraggingStarted() = 0;
+
+ virtual void fakeDraggingFiles(DragFileList fileList) = 0;
+ virtual const String&
+ getDropTarget() const = 0;
+
+protected:
+ //! Handle system event
+ /*!
+ A platform screen is expected to install a handler for system
+ events in its c'tor like so:
+ \code
+ m_events->adoptHandler(Event::kSystem,
+ m_events->getSystemTarget(),
+ new TMethodEventJob<CXXXPlatformScreen>(this,
+ &CXXXPlatformScreen::handleSystemEvent));
+ \endcode
+ It should remove the handler in its d'tor. Override the
+ \c handleSystemEvent() method to process system events.
+ It should post the events \c IScreen as appropriate.
+
+ A primary screen has further responsibilities. It should post
+ the events in \c IPrimaryScreen as appropriate. It should also
+ call \c onKey() on its \c KeyState whenever a key is pressed
+ or released (but not for key repeats). And it should call
+ \c updateKeyMap() on its \c KeyState if necessary when the keyboard
+ mapping changes.
+
+ The target of all events should be the value returned by
+ \c getEventTarget().
+ */
+ virtual void handleSystemEvent(const Event& event, void*) = 0;
+};
diff --git a/src/lib/barrier/IPrimaryScreen.cpp b/src/lib/barrier/IPrimaryScreen.cpp
new file mode 100644
index 0000000..4954e4f
--- /dev/null
+++ b/src/lib/barrier/IPrimaryScreen.cpp
@@ -0,0 +1,91 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 Chris Schoeneman
+ *
+ * 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 "barrier/IPrimaryScreen.h"
+#include "base/EventQueue.h"
+
+#include <cstdlib>
+
+//
+// IPrimaryScreen::ButtonInfo
+//
+
+IPrimaryScreen::ButtonInfo*
+IPrimaryScreen::ButtonInfo::alloc(ButtonID id, KeyModifierMask mask)
+{
+ ButtonInfo* info = (ButtonInfo*)malloc(sizeof(ButtonInfo));
+ info->m_button = id;
+ info->m_mask = mask;
+ return info;
+}
+
+IPrimaryScreen::ButtonInfo*
+IPrimaryScreen::ButtonInfo::alloc(const ButtonInfo& x)
+{
+ ButtonInfo* info = (ButtonInfo*)malloc(sizeof(ButtonInfo));
+ info->m_button = x.m_button;
+ info->m_mask = x.m_mask;
+ return info;
+}
+
+bool
+IPrimaryScreen::ButtonInfo::equal(const ButtonInfo* a, const ButtonInfo* b)
+{
+ return (a->m_button == b->m_button && a->m_mask == b->m_mask);
+}
+
+
+//
+// IPrimaryScreen::MotionInfo
+//
+
+IPrimaryScreen::MotionInfo*
+IPrimaryScreen::MotionInfo::alloc(SInt32 x, SInt32 y)
+{
+ MotionInfo* info = (MotionInfo*)malloc(sizeof(MotionInfo));
+ info->m_x = x;
+ info->m_y = y;
+ return info;
+}
+
+
+//
+// IPrimaryScreen::WheelInfo
+//
+
+IPrimaryScreen::WheelInfo*
+IPrimaryScreen::WheelInfo::alloc(SInt32 xDelta, SInt32 yDelta)
+{
+ WheelInfo* info = (WheelInfo*)malloc(sizeof(WheelInfo));
+ info->m_xDelta = xDelta;
+ info->m_yDelta = yDelta;
+ return info;
+}
+
+
+//
+// IPrimaryScreen::HotKeyInfo
+//
+
+IPrimaryScreen::HotKeyInfo*
+IPrimaryScreen::HotKeyInfo::alloc(UInt32 id)
+{
+ HotKeyInfo* info = (HotKeyInfo*)malloc(sizeof(HotKeyInfo));
+ info->m_id = id;
+ return info;
+}
diff --git a/src/lib/barrier/IPrimaryScreen.h b/src/lib/barrier/IPrimaryScreen.h
new file mode 100644
index 0000000..7f3fa9c
--- /dev/null
+++ b/src/lib/barrier/IPrimaryScreen.h
@@ -0,0 +1,165 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 Chris Schoeneman
+ *
+ * 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 "barrier/key_types.h"
+#include "barrier/mouse_types.h"
+#include "base/Event.h"
+#include "base/EventTypes.h"
+#include "common/IInterface.h"
+
+//! Primary screen interface
+/*!
+This interface defines the methods common to all platform dependent
+primary screen implementations.
+*/
+class IPrimaryScreen : public IInterface {
+public:
+ //! Button event data
+ class ButtonInfo {
+ public:
+ static ButtonInfo* alloc(ButtonID, KeyModifierMask);
+ static ButtonInfo* alloc(const ButtonInfo&);
+
+ static bool equal(const ButtonInfo*, const ButtonInfo*);
+
+ public:
+ ButtonID m_button;
+ KeyModifierMask m_mask;
+ };
+ //! Motion event data
+ class MotionInfo {
+ public:
+ static MotionInfo* alloc(SInt32 x, SInt32 y);
+
+ public:
+ SInt32 m_x;
+ SInt32 m_y;
+ };
+ //! Wheel motion event data
+ class WheelInfo {
+ public:
+ static WheelInfo* alloc(SInt32 xDelta, SInt32 yDelta);
+
+ public:
+ SInt32 m_xDelta;
+ SInt32 m_yDelta;
+ };
+ //! Hot key event data
+ class HotKeyInfo {
+ public:
+ static HotKeyInfo* alloc(UInt32 id);
+
+ public:
+ UInt32 m_id;
+ };
+
+ //! @name manipulators
+ //@{
+
+ //! Update configuration
+ /*!
+ This is called when the configuration has changed. \c activeSides
+ is a bitmask of EDirectionMask indicating which sides of the
+ primary screen are linked to clients. Override to handle the
+ possible change in jump zones.
+ */
+ virtual void reconfigure(UInt32 activeSides) = 0;
+
+ //! Warp cursor
+ /*!
+ Warp the cursor to the absolute coordinates \c x,y. Also
+ discard input events up to and including the warp before
+ returning.
+ */
+ virtual void warpCursor(SInt32 x, SInt32 y) = 0;
+
+ //! Register a system hotkey
+ /*!
+ Registers a system-wide hotkey. The screen should arrange for an event
+ to be delivered to itself when the hot key is pressed or released. When
+ that happens the screen should post a \c getHotKeyDownEvent() or
+ \c getHotKeyUpEvent(), respectively. The hot key is key \p key with
+ exactly the modifiers \p mask. Returns 0 on failure otherwise an id
+ that can be used to unregister the hotkey.
+
+ A hot key is a set of modifiers and a key, which may itself be a modifier.
+ The hot key is pressed when the hot key's modifiers and only those
+ modifiers are logically down (active) and the key is pressed. The hot
+ key is released when the key is released, regardless of the modifiers.
+
+ The hot key event should be generated no matter what window or application
+ has the focus. No other window or application should receive the key
+ press or release events (they can and should see the modifier key events).
+ When the key is a modifier, it's acceptable to allow the user to press
+ the modifiers in any order or to require the user to press the given key
+ last.
+ */
+ virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask) = 0;
+
+ //! Unregister a system hotkey
+ /*!
+ Unregisters a previously registered hot key.
+ */
+ virtual void unregisterHotKey(UInt32 id) = 0;
+
+ //! Prepare to synthesize input on primary screen
+ /*!
+ Prepares the primary screen to receive synthesized input. We do not
+ want to receive this synthesized input as user input so this method
+ ensures that we ignore it. Calls to \c fakeInputBegin() may not be
+ nested.
+ */
+ virtual void fakeInputBegin() = 0;
+
+ //! Done synthesizing input on primary screen
+ /*!
+ Undoes whatever \c fakeInputBegin() did.
+ */
+ virtual void fakeInputEnd() = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get jump zone size
+ /*!
+ Return the jump zone size, the size of the regions on the edges of
+ the screen that cause the cursor to jump to another screen.
+ */
+ virtual SInt32 getJumpZoneSize() const = 0;
+
+ //! Test if mouse is pressed
+ /*!
+ Return true if any mouse button is currently pressed. Ideally,
+ "current" means up to the last processed event but it can mean
+ the current physical mouse button state.
+ */
+ virtual bool isAnyMouseButtonDown(UInt32& buttonID) const = 0;
+
+ //! Get cursor center position
+ /*!
+ Return the cursor center position which is where we park the
+ cursor to compute cursor motion deltas and should be far from
+ the edges of the screen, typically the center.
+ */
+ virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
+
+ //@}
+};
diff --git a/src/lib/barrier/IScreen.h b/src/lib/barrier/IScreen.h
new file mode 100644
index 0000000..47d6578
--- /dev/null
+++ b/src/lib/barrier/IScreen.h
@@ -0,0 +1,71 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 Chris Schoeneman
+ *
+ * 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 "barrier/clipboard_types.h"
+#include "base/Event.h"
+#include "base/EventTypes.h"
+#include "common/IInterface.h"
+
+class IClipboard;
+
+//! Screen interface
+/*!
+This interface defines the methods common to all screens.
+*/
+class IScreen : public IInterface {
+public:
+ struct ClipboardInfo {
+ public:
+ ClipboardID m_id;
+ UInt32 m_sequenceNumber;
+ };
+
+ //! @name accessors
+ //@{
+
+ //! Get event target
+ /*!
+ Returns the target used for events created by this object.
+ */
+ virtual void* getEventTarget() const = 0;
+
+ //! Get clipboard
+ /*!
+ Save the contents of the clipboard indicated by \c id and return
+ true iff successful.
+ */
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
+
+ //! Get screen shape
+ /*!
+ Return the position of the upper-left corner of the screen in \c x and
+ \c y and the size of the screen in \c width and \c height.
+ */
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const = 0;
+
+ //! Get cursor position
+ /*!
+ Return the current position of the cursor in \c x and \c y.
+ */
+ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
+
+ //@}
+};
diff --git a/src/lib/barrier/IScreenSaver.h b/src/lib/barrier/IScreenSaver.h
new file mode 100644
index 0000000..fc21ac5
--- /dev/null
+++ b/src/lib/barrier/IScreenSaver.h
@@ -0,0 +1,75 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "base/Event.h"
+#include "common/IInterface.h"
+
+//! Screen saver interface
+/*!
+This interface defines the methods common to all screen savers.
+*/
+class IScreenSaver : public IInterface {
+public:
+ // note -- the c'tor/d'tor must *not* enable/disable the screen saver
+
+ //! @name manipulators
+ //@{
+
+ //! Enable screen saver
+ /*!
+ Enable the screen saver, restoring the screen saver settings to
+ what they were when disable() was previously called. If disable()
+ wasn't previously called then it should keep the current settings
+ or use reasonable defaults.
+ */
+ virtual void enable() = 0;
+
+ //! Disable screen saver
+ /*!
+ Disable the screen saver, saving the old settings for the next
+ call to enable().
+ */
+ virtual void disable() = 0;
+
+ //! Activate screen saver
+ /*!
+ Activate (i.e. show) the screen saver.
+ */
+ virtual void activate() = 0;
+
+ //! Deactivate screen saver
+ /*!
+ Deactivate (i.e. hide) the screen saver, reseting the screen saver
+ timer.
+ */
+ virtual void deactivate() = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Test if screen saver on
+ /*!
+ Returns true iff the screen saver is currently active (showing).
+ */
+ virtual bool isActive() const = 0;
+
+ //@}
+};
diff --git a/src/lib/barrier/ISecondaryScreen.h b/src/lib/barrier/ISecondaryScreen.h
new file mode 100644
index 0000000..527ca2e
--- /dev/null
+++ b/src/lib/barrier/ISecondaryScreen.h
@@ -0,0 +1,61 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 Chris Schoeneman
+ *
+ * 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 "barrier/mouse_types.h"
+#include "base/Event.h"
+#include "base/EventTypes.h"
+#include "common/IInterface.h"
+
+//! Secondary screen interface
+/*!
+This interface defines the methods common to all platform dependent
+secondary screen implementations.
+*/
+class ISecondaryScreen : public IInterface {
+public:
+ //! @name accessors
+ //@{
+
+ //! Fake mouse press/release
+ /*!
+ Synthesize a press or release of mouse button \c id.
+ */
+ virtual void fakeMouseButton(ButtonID id, bool press) = 0;
+
+ //! Fake mouse move
+ /*!
+ Synthesize a mouse move to the absolute coordinates \c x,y.
+ */
+ virtual void fakeMouseMove(SInt32 x, SInt32 y) = 0;
+
+ //! Fake mouse move
+ /*!
+ Synthesize a mouse move to the relative coordinates \c dx,dy.
+ */
+ virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0;
+
+ //! Fake mouse wheel
+ /*!
+ Synthesize a mouse wheel event of amount \c xDelta and \c yDelta.
+ */
+ virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0;
+
+ //@}
+};
diff --git a/src/lib/barrier/KeyMap.cpp b/src/lib/barrier/KeyMap.cpp
new file mode 100644
index 0000000..fd68204
--- /dev/null
+++ b/src/lib/barrier/KeyMap.cpp
@@ -0,0 +1,1344 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2005 Chris Schoeneman
+ *
+ * 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 "barrier/KeyMap.h"
+#include "barrier/key_types.h"
+#include "base/Log.h"
+
+#include <assert.h>
+#include <cctype>
+#include <cstdlib>
+
+namespace barrier {
+
+KeyMap::NameToKeyMap* KeyMap::s_nameToKeyMap = NULL;
+KeyMap::NameToModifierMap* KeyMap::s_nameToModifierMap = NULL;
+KeyMap::KeyToNameMap* KeyMap::s_keyToNameMap = NULL;
+KeyMap::ModifierToNameMap* KeyMap::s_modifierToNameMap = NULL;
+
+KeyMap::KeyMap() :
+ m_numGroups(0),
+ m_composeAcrossGroups(false)
+{
+ m_modifierKeyItem.m_id = kKeyNone;
+ m_modifierKeyItem.m_group = 0;
+ m_modifierKeyItem.m_button = 0;
+ m_modifierKeyItem.m_required = 0;
+ m_modifierKeyItem.m_sensitive = 0;
+ m_modifierKeyItem.m_generates = 0;
+ m_modifierKeyItem.m_dead = false;
+ m_modifierKeyItem.m_lock = false;
+ m_modifierKeyItem.m_client = 0;
+}
+
+KeyMap::~KeyMap()
+{
+ // do nothing
+}
+
+void
+KeyMap::swap(KeyMap& x)
+{
+ m_keyIDMap.swap(x.m_keyIDMap);
+ m_modifierKeys.swap(x.m_modifierKeys);
+ m_halfDuplex.swap(x.m_halfDuplex);
+ m_halfDuplexMods.swap(x.m_halfDuplexMods);
+ SInt32 tmp1 = m_numGroups;
+ m_numGroups = x.m_numGroups;
+ x.m_numGroups = tmp1;
+ bool tmp2 = m_composeAcrossGroups;
+ m_composeAcrossGroups = x.m_composeAcrossGroups;
+ x.m_composeAcrossGroups = tmp2;
+}
+
+void
+KeyMap::addKeyEntry(const KeyItem& item)
+{
+ // ignore kKeyNone
+ if (item.m_id == kKeyNone) {
+ return;
+ }
+
+ // resize number of groups for key
+ SInt32 numGroups = item.m_group + 1;
+ if (getNumGroups() > numGroups) {
+ numGroups = getNumGroups();
+ }
+ KeyGroupTable& groupTable = m_keyIDMap[item.m_id];
+ if (groupTable.size() < static_cast<size_t>(numGroups)) {
+ groupTable.resize(numGroups);
+ }
+
+ // make a list from the item
+ KeyItemList items;
+ items.push_back(item);
+
+ // set group and dead key flag on the item
+ KeyItem& newItem = items.back();
+ newItem.m_dead = isDeadKey(item.m_id);
+
+ // mask the required bits with the sensitive bits
+ newItem.m_required &= newItem.m_sensitive;
+
+ // see if we already have this item; just return if so
+ KeyEntryList& entries = groupTable[item.m_group];
+ for (size_t i = 0, n = entries.size(); i < n; ++i) {
+ if (entries[i].size() == 1 && newItem == entries[i][0]) {
+ return;
+ }
+ }
+
+ // add item list
+ entries.push_back(items);
+ LOG((CLOG_DEBUG5 "add key: %04x %d %03x %04x (%04x %04x %04x)%s", newItem.m_id, newItem.m_group, newItem.m_button, newItem.m_client, newItem.m_required, newItem.m_sensitive, newItem.m_generates, newItem.m_dead ? " dead" : ""));
+}
+
+void
+KeyMap::addKeyAliasEntry(KeyID targetID, SInt32 group,
+ KeyModifierMask targetRequired,
+ KeyModifierMask targetSensitive,
+ KeyID sourceID,
+ KeyModifierMask sourceRequired,
+ KeyModifierMask sourceSensitive)
+{
+ // if we can already generate the target as desired then we're done.
+ if (findCompatibleKey(targetID, group, targetRequired,
+ targetSensitive) != NULL) {
+ return;
+ }
+
+ // find a compatible source, preferably in the same group
+ for (SInt32 gd = 0, n = getNumGroups(); gd < n; ++gd) {
+ SInt32 eg = getEffectiveGroup(group, gd);
+ const KeyItemList* sourceEntry =
+ findCompatibleKey(sourceID, eg,
+ sourceRequired, sourceSensitive);
+ if (sourceEntry != NULL && sourceEntry->size() == 1) {
+ KeyMap::KeyItem targetItem = sourceEntry->back();
+ targetItem.m_id = targetID;
+ targetItem.m_group = eg;
+ addKeyEntry(targetItem);
+ break;
+ }
+ }
+}
+
+bool
+KeyMap::addKeyCombinationEntry(KeyID id, SInt32 group,
+ const KeyID* keys, UInt32 numKeys)
+{
+ // disallow kKeyNone
+ if (id == kKeyNone) {
+ return false;
+ }
+
+ SInt32 numGroups = group + 1;
+ if (getNumGroups() > numGroups) {
+ numGroups = getNumGroups();
+ }
+ KeyGroupTable& groupTable = m_keyIDMap[id];
+ if (groupTable.size() < static_cast<size_t>(numGroups)) {
+ groupTable.resize(numGroups);
+ }
+ if (!groupTable[group].empty()) {
+ // key is already in the table
+ return false;
+ }
+
+ // convert to buttons
+ KeyItemList items;
+ for (UInt32 i = 0; i < numKeys; ++i) {
+ KeyIDMap::const_iterator gtIndex = m_keyIDMap.find(keys[i]);
+ if (gtIndex == m_keyIDMap.end()) {
+ return false;
+ }
+ const KeyGroupTable& groupTable = gtIndex->second;
+
+ // if we allow group switching during composition then search all
+ // groups for keys, otherwise search just the given group.
+ SInt32 n = 1;
+ if (m_composeAcrossGroups) {
+ n = (SInt32)groupTable.size();
+ }
+
+ bool found = false;
+ for (SInt32 gd = 0; gd < n && !found; ++gd) {
+ SInt32 eg = (group + gd) % getNumGroups();
+ const KeyEntryList& entries = groupTable[eg];
+ for (size_t j = 0; j < entries.size(); ++j) {
+ if (entries[j].size() == 1) {
+ found = true;
+ items.push_back(entries[j][0]);
+ break;
+ }
+ }
+ }
+ if (!found) {
+ // required key is not in keyboard group
+ return false;
+ }
+ }
+
+ // add key
+ groupTable[group].push_back(items);
+ return true;
+}
+
+void
+KeyMap::allowGroupSwitchDuringCompose()
+{
+ m_composeAcrossGroups = true;
+}
+
+void
+KeyMap::addHalfDuplexButton(KeyButton button)
+{
+ m_halfDuplex.insert(button);
+}
+
+void
+KeyMap::clearHalfDuplexModifiers()
+{
+ m_halfDuplexMods.clear();
+}
+
+void
+KeyMap::addHalfDuplexModifier(KeyID key)
+{
+ m_halfDuplexMods.insert(key);
+}
+
+void
+KeyMap::finish()
+{
+ m_numGroups = findNumGroups();
+
+ // make sure every key has the same number of groups
+ for (KeyIDMap::iterator i = m_keyIDMap.begin();
+ i != m_keyIDMap.end(); ++i) {
+ i->second.resize(m_numGroups);
+ }
+
+ // compute keys that generate each modifier
+ setModifierKeys();
+}
+
+void
+KeyMap::foreachKey(ForeachKeyCallback cb, void* userData)
+{
+ for (KeyIDMap::iterator i = m_keyIDMap.begin();
+ i != m_keyIDMap.end(); ++i) {
+ KeyGroupTable& groupTable = i->second;
+ for (size_t group = 0; group < groupTable.size(); ++group) {
+ KeyEntryList& entryList = groupTable[group];
+ for (size_t j = 0; j < entryList.size(); ++j) {
+ KeyItemList& itemList = entryList[j];
+ for (size_t k = 0; k < itemList.size(); ++k) {
+ (*cb)(i->first, static_cast<SInt32>(group),
+ itemList[k], userData);
+ }
+ }
+ }
+ }
+}
+
+const KeyMap::KeyItem*
+KeyMap::mapKey(Keystrokes& keys, KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const
+{
+ LOG((CLOG_DEBUG1 "mapKey %04x (%d) with mask %04x, start state: %04x", id, id, desiredMask, currentState));
+
+ // handle group change
+ if (id == kKeyNextGroup) {
+ keys.push_back(Keystroke(1, false, false));
+ return NULL;
+ }
+ else if (id == kKeyPrevGroup) {
+ keys.push_back(Keystroke(-1, false, false));
+ return NULL;
+ }
+
+ const KeyItem* item;
+ switch (id) {
+ case kKeyShift_L:
+ case kKeyShift_R:
+ case kKeyControl_L:
+ case kKeyControl_R:
+ case kKeyAlt_L:
+ case kKeyAlt_R:
+ case kKeyMeta_L:
+ case kKeyMeta_R:
+ case kKeySuper_L:
+ case kKeySuper_R:
+ case kKeyAltGr:
+ case kKeyCapsLock:
+ case kKeyNumLock:
+ case kKeyScrollLock:
+ item = mapModifierKey(keys, id, group, activeModifiers,
+ currentState, desiredMask, isAutoRepeat);
+ break;
+
+ case kKeySetModifiers:
+ if (!keysForModifierState(0, group, activeModifiers, currentState,
+ desiredMask, desiredMask, 0, keys)) {
+ LOG((CLOG_DEBUG1 "unable to set modifiers %04x", desiredMask));
+ return NULL;
+ }
+ return &m_modifierKeyItem;
+
+ case kKeyClearModifiers:
+ if (!keysForModifierState(0, group, activeModifiers, currentState,
+ currentState & ~desiredMask,
+ desiredMask, 0, keys)) {
+ LOG((CLOG_DEBUG1 "unable to clear modifiers %04x", desiredMask));
+ return NULL;
+ }
+ return &m_modifierKeyItem;
+
+ default:
+ if (isCommand(desiredMask)) {
+ item = mapCommandKey(keys, id, group, activeModifiers,
+ currentState, desiredMask, isAutoRepeat);
+ }
+ else {
+ item = mapCharacterKey(keys, id, group, activeModifiers,
+ currentState, desiredMask, isAutoRepeat);
+ }
+ break;
+ }
+
+ if (item != NULL) {
+ LOG((CLOG_DEBUG1 "mapped to %03x, new state %04x", item->m_button, currentState));
+ }
+ return item;
+}
+
+SInt32
+KeyMap::getNumGroups() const
+{
+ return m_numGroups;
+}
+
+SInt32
+KeyMap::getEffectiveGroup(SInt32 group, SInt32 offset) const
+{
+ return (group + offset + getNumGroups()) % getNumGroups();
+}
+
+const KeyMap::KeyItemList*
+KeyMap::findCompatibleKey(KeyID id, SInt32 group,
+ KeyModifierMask required, KeyModifierMask sensitive) const
+{
+ assert(group >= 0 && group < getNumGroups());
+
+ KeyIDMap::const_iterator i = m_keyIDMap.find(id);
+ if (i == m_keyIDMap.end()) {
+ return NULL;
+ }
+
+ const KeyEntryList& entries = i->second[group];
+ for (size_t j = 0; j < entries.size(); ++j) {
+ if ((entries[j].back().m_sensitive & sensitive) == 0 ||
+ (entries[j].back().m_required & sensitive) ==
+ (required & sensitive)) {
+ return &entries[j];
+ }
+ }
+
+ return NULL;
+}
+
+bool
+KeyMap::isHalfDuplex(KeyID key, KeyButton button) const
+{
+ return (m_halfDuplex.count(button) + m_halfDuplexMods.count(key) > 0);
+}
+
+bool
+KeyMap::isCommand(KeyModifierMask mask) const
+{
+ return ((mask & getCommandModifiers()) != 0);
+}
+
+KeyModifierMask
+KeyMap::getCommandModifiers() const
+{
+ // we currently treat ctrl, alt, meta and super as command modifiers.
+ // some platforms may have a more limited set (OS X only needs Alt)
+ // but this works anyway.
+ return KeyModifierControl |
+ KeyModifierAlt |
+ KeyModifierAltGr |
+ KeyModifierMeta |
+ KeyModifierSuper;
+}
+
+void
+KeyMap::collectButtons(const ModifierToKeys& mods, ButtonToKeyMap& keys)
+{
+ keys.clear();
+ for (ModifierToKeys::const_iterator i = mods.begin();
+ i != mods.end(); ++i) {
+ keys.insert(std::make_pair(i->second.m_button, &i->second));
+ }
+}
+
+void
+KeyMap::initModifierKey(KeyItem& item)
+{
+ item.m_generates = 0;
+ item.m_lock = false;
+ switch (item.m_id) {
+ case kKeyShift_L:
+ case kKeyShift_R:
+ item.m_generates = KeyModifierShift;
+ break;
+
+ case kKeyControl_L:
+ case kKeyControl_R:
+ item.m_generates = KeyModifierControl;
+ break;
+
+ case kKeyAlt_L:
+ case kKeyAlt_R:
+ item.m_generates = KeyModifierAlt;
+ break;
+
+ case kKeyMeta_L:
+ case kKeyMeta_R:
+ item.m_generates = KeyModifierMeta;
+ break;
+
+ case kKeySuper_L:
+ case kKeySuper_R:
+ item.m_generates = KeyModifierSuper;
+ break;
+
+ case kKeyAltGr:
+ item.m_generates = KeyModifierAltGr;
+ break;
+
+ case kKeyCapsLock:
+ item.m_generates = KeyModifierCapsLock;
+ item.m_lock = true;
+ break;
+
+ case kKeyNumLock:
+ item.m_generates = KeyModifierNumLock;
+ item.m_lock = true;
+ break;
+
+ case kKeyScrollLock:
+ item.m_generates = KeyModifierScrollLock;
+ item.m_lock = true;
+ break;
+
+ default:
+ // not a modifier
+ break;
+ }
+}
+
+SInt32
+KeyMap::findNumGroups() const
+{
+ size_t max = 0;
+ for (KeyIDMap::const_iterator i = m_keyIDMap.begin();
+ i != m_keyIDMap.end(); ++i) {
+ if (i->second.size() > max) {
+ max = i->second.size();
+ }
+ }
+ return static_cast<SInt32>(max);
+}
+
+void
+KeyMap::setModifierKeys()
+{
+ m_modifierKeys.clear();
+ m_modifierKeys.resize(kKeyModifierNumBits * getNumGroups());
+ for (KeyIDMap::const_iterator i = m_keyIDMap.begin();
+ i != m_keyIDMap.end(); ++i) {
+ const KeyGroupTable& groupTable = i->second;
+ for (size_t g = 0; g < groupTable.size(); ++g) {
+ const KeyEntryList& entries = groupTable[g];
+ for (size_t j = 0; j < entries.size(); ++j) {
+ // skip multi-key sequences
+ if (entries[j].size() != 1) {
+ continue;
+ }
+
+ // skip keys that don't generate a modifier
+ const KeyItem& item = entries[j].back();
+ if (item.m_generates == 0) {
+ continue;
+ }
+
+ // add key to each indicated modifier in this group
+ for (SInt32 b = 0; b < kKeyModifierNumBits; ++b) {
+ // skip if item doesn't generate bit b
+ if (((1u << b) & item.m_generates) != 0) {
+ SInt32 mIndex = (SInt32)g * kKeyModifierNumBits + b;
+ m_modifierKeys[mIndex].push_back(&item);
+ }
+ }
+ }
+ }
+ }
+}
+
+const KeyMap::KeyItem*
+KeyMap::mapCommandKey(Keystrokes& keys, KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const
+{
+ static const KeyModifierMask s_overrideModifiers = 0xffffu;
+
+ // find KeySym in table
+ KeyIDMap::const_iterator i = m_keyIDMap.find(id);
+ if (i == m_keyIDMap.end()) {
+ // unknown key
+ LOG((CLOG_DEBUG1 "key %04x is not on keyboard", id));
+ return NULL;
+ }
+ const KeyGroupTable& keyGroupTable = i->second;
+
+ // find the first key that generates this KeyID
+ const KeyItem* keyItem = NULL;
+ SInt32 numGroups = getNumGroups();
+ for (SInt32 groupOffset = 0; groupOffset < numGroups; ++groupOffset) {
+ SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset);
+ const KeyEntryList& entryList = keyGroupTable[effectiveGroup];
+ for (size_t i = 0; i < entryList.size(); ++i) {
+ if (entryList[i].size() != 1) {
+ // ignore multikey entries
+ continue;
+ }
+
+ // match based on shift and make sure all required modifiers,
+ // except shift, are already in the desired mask; we're
+ // after the right button not the right character.
+ // we'll use desiredMask as-is, overriding the key's required
+ // modifiers, when synthesizing this button.
+ const KeyItem& item = entryList[i].back();
+ KeyModifierMask desiredShiftMask = KeyModifierShift & desiredMask;
+ KeyModifierMask requiredIgnoreShiftMask = item.m_required & ~KeyModifierShift;
+ if ((item.m_required & desiredShiftMask) == (item.m_sensitive & desiredShiftMask) &&
+ ((requiredIgnoreShiftMask & desiredMask) == requiredIgnoreShiftMask)) {
+ LOG((CLOG_INFO "found key in group %d", effectiveGroup));
+ keyItem = &item;
+ break;
+ }
+ }
+ if (keyItem != NULL) {
+ break;
+ }
+ }
+ if (keyItem == NULL) {
+ // no mapping for this keysym
+ LOG((CLOG_DEBUG1 "no mapping for key %04x", id));
+ return NULL;
+ }
+
+ // make working copy of modifiers
+ ModifierToKeys newModifiers = activeModifiers;
+ KeyModifierMask newState = currentState;
+ SInt32 newGroup = group;
+
+ // don't try to change CapsLock
+ desiredMask = (desiredMask & ~KeyModifierCapsLock) |
+ (currentState & KeyModifierCapsLock);
+
+ // add the key
+ if (!keysForKeyItem(*keyItem, newGroup, newModifiers,
+ newState, desiredMask,
+ s_overrideModifiers, isAutoRepeat, keys)) {
+ LOG((CLOG_DEBUG1 "can't map key"));
+ keys.clear();
+ return NULL;
+ }
+
+ // add keystrokes to restore modifier keys
+ if (!keysToRestoreModifiers(*keyItem, group, newModifiers, newState,
+ activeModifiers, keys)) {
+ LOG((CLOG_DEBUG1 "failed to restore modifiers"));
+ keys.clear();
+ return NULL;
+ }
+
+ // add keystrokes to restore group
+ if (newGroup != group) {
+ keys.push_back(Keystroke(group, true, true));
+ }
+
+ // save new modifiers
+ activeModifiers = newModifiers;
+ currentState = newState;
+
+ return keyItem;
+}
+
+const KeyMap::KeyItem*
+KeyMap::mapCharacterKey(Keystrokes& keys, KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const
+{
+ // find KeySym in table
+ KeyIDMap::const_iterator i = m_keyIDMap.find(id);
+ if (i == m_keyIDMap.end()) {
+ // unknown key
+ LOG((CLOG_DEBUG1 "key %04x is not on keyboard", id));
+ return NULL;
+ }
+ const KeyGroupTable& keyGroupTable = i->second;
+
+ // find best key in any group, starting with the active group
+ SInt32 keyIndex = -1;
+ SInt32 numGroups = getNumGroups();
+ SInt32 groupOffset;
+ LOG((CLOG_DEBUG1 "find best: %04x %04x", currentState, desiredMask));
+ for (groupOffset = 0; groupOffset < numGroups; ++groupOffset) {
+ SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset);
+ keyIndex = findBestKey(keyGroupTable[effectiveGroup],
+ currentState, desiredMask);
+ if (keyIndex != -1) {
+ LOG((CLOG_DEBUG1 "found key in group %d", effectiveGroup));
+ break;
+ }
+ }
+ if (keyIndex == -1) {
+ // no mapping for this keysym
+ LOG((CLOG_DEBUG1 "no mapping for key %04x", id));
+ return NULL;
+ }
+
+ // get keys to press for key
+ SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset);
+ const KeyItemList& itemList = keyGroupTable[effectiveGroup][keyIndex];
+ if (itemList.empty()) {
+ return NULL;
+ }
+ const KeyItem& keyItem = itemList.back();
+
+ // make working copy of modifiers
+ ModifierToKeys newModifiers = activeModifiers;
+ KeyModifierMask newState = currentState;
+ SInt32 newGroup = group;
+
+ // add each key
+ for (size_t j = 0; j < itemList.size(); ++j) {
+ if (!keysForKeyItem(itemList[j], newGroup, newModifiers,
+ newState, desiredMask,
+ 0, isAutoRepeat, keys)) {
+ LOG((CLOG_DEBUG1 "can't map key"));
+ keys.clear();
+ return NULL;
+ }
+ }
+
+ // add keystrokes to restore modifier keys
+ if (!keysToRestoreModifiers(keyItem, group, newModifiers, newState,
+ activeModifiers, keys)) {
+ LOG((CLOG_DEBUG1 "failed to restore modifiers"));
+ keys.clear();
+ return NULL;
+ }
+
+ // add keystrokes to restore group
+ if (newGroup != group) {
+ keys.push_back(Keystroke(group, true, true));
+ }
+
+ // save new modifiers
+ activeModifiers = newModifiers;
+ currentState = newState;
+
+ return &keyItem;
+}
+
+const KeyMap::KeyItem*
+KeyMap::mapModifierKey(Keystrokes& keys, KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const
+{
+ return mapCharacterKey(keys, id, group, activeModifiers,
+ currentState, desiredMask, isAutoRepeat);
+}
+
+SInt32
+KeyMap::findBestKey(const KeyEntryList& entryList,
+ KeyModifierMask /*currentState*/,
+ KeyModifierMask desiredState) const
+{
+ // check for an item that can accommodate the desiredState exactly
+ for (SInt32 i = 0; i < (SInt32)entryList.size(); ++i) {
+ const KeyItem& item = entryList[i].back();
+ if ((item.m_required & desiredState) == item.m_required &&
+ (item.m_required & desiredState) == (item.m_sensitive & desiredState)) {
+ LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i + 1, entryList.size()));
+ return i;
+ }
+ }
+
+ // choose the item that requires the fewest modifier changes
+ SInt32 bestCount = 32;
+ SInt32 bestIndex = -1;
+ for (SInt32 i = 0; i < (SInt32)entryList.size(); ++i) {
+ const KeyItem& item = entryList[i].back();
+ KeyModifierMask change =
+ ((item.m_required ^ desiredState) & item.m_sensitive);
+ SInt32 n = getNumModifiers(change);
+ if (n < bestCount) {
+ bestCount = n;
+ bestIndex = i;
+ }
+ }
+ if (bestIndex != -1) {
+ LOG((CLOG_DEBUG1 "best key index %d of %d (%d modifiers)",
+ bestIndex + 1, entryList.size(), bestCount));
+ }
+
+ return bestIndex;
+}
+
+
+const KeyMap::KeyItem*
+KeyMap::keyForModifier(KeyButton button, SInt32 group,
+ SInt32 modifierBit) const
+{
+ assert(modifierBit >= 0 && modifierBit < kKeyModifierNumBits);
+ assert(group >= 0 && group < getNumGroups());
+
+ // find a key that generates the given modifier in the given group
+ // but doesn't use the given button, presumably because we're trying
+ // to generate a KeyID that's only bound the the given button.
+ // this is important when a shift button is modified by shift; we
+ // must use the other shift button to do the shifting.
+ const ModifierKeyItemList& items =
+ m_modifierKeys[group * kKeyModifierNumBits + modifierBit];
+ for (ModifierKeyItemList::const_iterator i = items.begin();
+ i != items.end(); ++i) {
+ if ((*i)->m_button != button) {
+ return (*i);
+ }
+ }
+ return NULL;
+}
+
+bool
+KeyMap::keysForKeyItem(const KeyItem& keyItem, SInt32& group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState, KeyModifierMask desiredState,
+ KeyModifierMask overrideModifiers,
+ bool isAutoRepeat,
+ Keystrokes& keystrokes) const
+{
+ static const KeyModifierMask s_notRequiredMask =
+ KeyModifierAltGr | KeyModifierNumLock | KeyModifierScrollLock;
+
+ // add keystrokes to adjust the group
+ if (group != keyItem.m_group) {
+ group = keyItem.m_group;
+ keystrokes.push_back(Keystroke(group, true, false));
+ }
+
+ EKeystroke type;
+ if (keyItem.m_dead) {
+ // adjust modifiers for dead key
+ if (!keysForModifierState(keyItem.m_button, group,
+ activeModifiers, currentState,
+ keyItem.m_required, keyItem.m_sensitive,
+ 0, keystrokes)) {
+ LOG((CLOG_DEBUG1 "unable to match modifier state for dead key %d", keyItem.m_button));
+ return false;
+ }
+
+ // press and release the dead key
+ type = kKeystrokeClick;
+ }
+ else {
+ // if this a command key then we don't have to match some of the
+ // key's required modifiers.
+ KeyModifierMask sensitive = keyItem.m_sensitive & ~overrideModifiers;
+
+ // XXX -- must handle pressing a modifier. in particular, if we want
+ // to synthesize a KeyID on level 1 of a KeyButton that has Shift_L
+ // mapped to level 0 then we must release that button if it's down
+ // (in order to satisfy a shift modifier) then press a different
+ // button (any other button) mapped to the shift modifier and then
+ // the Shift_L button.
+ // match key's required state
+ LOG((CLOG_DEBUG1 "state: %04x,%04x,%04x", currentState, keyItem.m_required, sensitive));
+ if (!keysForModifierState(keyItem.m_button, group,
+ activeModifiers, currentState,
+ keyItem.m_required, sensitive,
+ 0, keystrokes)) {
+ LOG((CLOG_DEBUG1 "unable to match modifier state (%04x,%04x) for key %d", keyItem.m_required, keyItem.m_sensitive, keyItem.m_button));
+ return false;
+ }
+
+ // match desiredState as closely as possible. we must not
+ // change any modifiers in keyItem.m_sensitive. and if the key
+ // is a modifier, we don't want to change that modifier.
+ LOG((CLOG_DEBUG1 "desired state: %04x %04x,%04x,%04x", desiredState, currentState, keyItem.m_required, keyItem.m_sensitive));
+ if (!keysForModifierState(keyItem.m_button, group,
+ activeModifiers, currentState,
+ desiredState,
+ ~(sensitive | keyItem.m_generates),
+ s_notRequiredMask, keystrokes)) {
+ LOG((CLOG_DEBUG1 "unable to match desired modifier state (%04x,%04x) for key %d", desiredState, ~keyItem.m_sensitive & 0xffffu, keyItem.m_button));
+ return false;
+ }
+
+ // repeat or press of key
+ type = isAutoRepeat ? kKeystrokeRepeat : kKeystrokePress;
+ }
+ addKeystrokes(type, keyItem, activeModifiers, currentState, keystrokes);
+
+ return true;
+}
+
+bool
+KeyMap::keysToRestoreModifiers(const KeyItem& keyItem, SInt32,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ const ModifierToKeys& desiredModifiers,
+ Keystrokes& keystrokes) const
+{
+ // XXX -- we're not considering modified modifiers here
+
+ ModifierToKeys oldModifiers = activeModifiers;
+
+ // get the pressed modifier buttons before and after
+ ButtonToKeyMap oldKeys, newKeys;
+ collectButtons(oldModifiers, oldKeys);
+ collectButtons(desiredModifiers, newKeys);
+
+ // release unwanted keys
+ for (ModifierToKeys::const_iterator i = oldModifiers.begin();
+ i != oldModifiers.end(); ++i) {
+ KeyButton button = i->second.m_button;
+ if (button != keyItem.m_button && newKeys.count(button) == 0) {
+ EKeystroke type = kKeystrokeRelease;
+ if (i->second.m_lock) {
+ type = kKeystrokeUnmodify;
+ }
+ addKeystrokes(type, i->second,
+ activeModifiers, currentState, keystrokes);
+ }
+ }
+
+ // press wanted keys
+ for (ModifierToKeys::const_iterator i = desiredModifiers.begin();
+ i != desiredModifiers.end(); ++i) {
+ KeyButton button = i->second.m_button;
+ if (button != keyItem.m_button && oldKeys.count(button) == 0) {
+ EKeystroke type = kKeystrokePress;
+ if (i->second.m_lock) {
+ type = kKeystrokeModify;
+ }
+ addKeystrokes(type, i->second,
+ activeModifiers, currentState, keystrokes);
+ }
+ }
+
+ return true;
+}
+
+bool
+KeyMap::keysForModifierState(KeyButton button, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask requiredState, KeyModifierMask sensitiveMask,
+ KeyModifierMask notRequiredMask,
+ Keystrokes& keystrokes) const
+{
+ // compute which modifiers need changing
+ KeyModifierMask flipMask = ((currentState ^ requiredState) & sensitiveMask);
+ // if a modifier is not required then don't even try to match it. if
+ // we don't mask out notRequiredMask then we'll try to match those
+ // modifiers but succeed if we can't. however, this is known not
+ // to work if the key itself is a modifier (the numlock toggle can
+ // interfere) so we don't try to match at all.
+ flipMask &= ~notRequiredMask;
+ LOG((CLOG_DEBUG1 "flip: %04x (%04x vs %04x in %04x - %04x)", flipMask, currentState, requiredState, sensitiveMask & 0xffffu, notRequiredMask & 0xffffu));
+ if (flipMask == 0) {
+ return true;
+ }
+
+ // fix modifiers. this is complicated by the fact that a modifier may
+ // be sensitive to other modifiers! (who thought that up?)
+ //
+ // we'll assume that modifiers with higher bits are affected by modifiers
+ // with lower bits. there's not much basis for that assumption except
+ // that we're pretty sure shift isn't changed by other modifiers.
+ for (SInt32 bit = kKeyModifierNumBits; bit-- > 0; ) {
+ KeyModifierMask mask = (1u << bit);
+ if ((flipMask & mask) == 0) {
+ // modifier is already correct
+ continue;
+ }
+
+ // do we want the modifier active or inactive?
+ bool active = ((requiredState & mask) != 0);
+
+ // get the KeyItem for the modifier in the group
+ const KeyItem* keyItem = keyForModifier(button, group, bit);
+ if (keyItem == NULL) {
+ if ((mask & notRequiredMask) == 0) {
+ LOG((CLOG_DEBUG1 "no key for modifier %04x", mask));
+ return false;
+ }
+ else {
+ continue;
+ }
+ }
+
+ // if this modifier is sensitive to modifiers then adjust those
+ // modifiers. also check if our assumption was correct. note
+ // that we only need to adjust the modifiers on key down.
+ KeyModifierMask sensitive = keyItem->m_sensitive;
+ if ((sensitive & mask) != 0) {
+ // modifier is sensitive to itself. that makes no sense
+ // so ignore it.
+ LOG((CLOG_DEBUG1 "modifier %04x modified by itself", mask));
+ sensitive &= ~mask;
+ }
+ if (sensitive != 0) {
+ if (sensitive > mask) {
+ // our assumption is incorrect
+ LOG((CLOG_DEBUG1 "modifier %04x modified by %04x", mask, sensitive));
+ return false;
+ }
+ if (active && !keysForModifierState(button, group,
+ activeModifiers, currentState,
+ keyItem->m_required, sensitive,
+ notRequiredMask, keystrokes)) {
+ return false;
+ }
+ else if (!active) {
+ // release the modifier
+ // XXX -- this doesn't work! if Alt and Meta are mapped
+ // to one key and we want to release Meta we can't do
+ // that without also releasing Alt.
+ // need to think about support for modified modifiers.
+ }
+ }
+
+ // current state should match required state
+ if ((currentState & sensitive) != (keyItem->m_required & sensitive)) {
+ LOG((CLOG_DEBUG1 "unable to match modifier state for modifier %04x (%04x vs %04x in %04x)", mask, currentState, keyItem->m_required, sensitive));
+ return false;
+ }
+
+ // add keystrokes
+ EKeystroke type = active ? kKeystrokeModify : kKeystrokeUnmodify;
+ addKeystrokes(type, *keyItem, activeModifiers, currentState,
+ keystrokes);
+ }
+
+ return true;
+}
+
+void
+KeyMap::addKeystrokes(EKeystroke type, const KeyItem& keyItem,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ Keystrokes& keystrokes) const
+{
+ KeyButton button = keyItem.m_button;
+ UInt32 data = keyItem.m_client;
+ switch (type) {
+ case kKeystrokePress:
+ keystrokes.push_back(Keystroke(button, true, false, data));
+ if (keyItem.m_generates != 0) {
+ if (!keyItem.m_lock || (currentState & keyItem.m_generates) == 0) {
+ // add modifier key and activate modifier
+ activeModifiers.insert(std::make_pair(
+ keyItem.m_generates, keyItem));
+ currentState |= keyItem.m_generates;
+ }
+ else {
+ // deactivate locking modifier
+ activeModifiers.erase(keyItem.m_generates);
+ currentState &= ~keyItem.m_generates;
+ }
+ }
+ break;
+
+ case kKeystrokeRelease:
+ keystrokes.push_back(Keystroke(button, false, false, data));
+ if (keyItem.m_generates != 0 && !keyItem.m_lock) {
+ // remove key from active modifiers
+ std::pair<ModifierToKeys::iterator,
+ ModifierToKeys::iterator> range =
+ activeModifiers.equal_range(keyItem.m_generates);
+ for (ModifierToKeys::iterator i = range.first;
+ i != range.second; ++i) {
+ if (i->second.m_button == button) {
+ activeModifiers.erase(i);
+ break;
+ }
+ }
+
+ // if no more keys for this modifier then deactivate modifier
+ if (activeModifiers.count(keyItem.m_generates) == 0) {
+ currentState &= ~keyItem.m_generates;
+ }
+ }
+ break;
+
+ case kKeystrokeRepeat:
+ keystrokes.push_back(Keystroke(button, false, true, data));
+ keystrokes.push_back(Keystroke(button, true, true, data));
+ // no modifier changes on key repeat
+ break;
+
+ case kKeystrokeClick:
+ keystrokes.push_back(Keystroke(button, true, false, data));
+ keystrokes.push_back(Keystroke(button, false, false, data));
+ // no modifier changes on key click
+ break;
+
+ case kKeystrokeModify:
+ case kKeystrokeUnmodify:
+ if (keyItem.m_lock) {
+ // we assume there's just one button for this modifier
+ if (m_halfDuplex.count(button) > 0) {
+ if (type == kKeystrokeModify) {
+ // turn half-duplex toggle on (press)
+ keystrokes.push_back(Keystroke(button, true, false, data));
+ }
+ else {
+ // turn half-duplex toggle off (release)
+ keystrokes.push_back(Keystroke(button, false, false, data));
+ }
+ }
+ else {
+ // toggle (click)
+ keystrokes.push_back(Keystroke(button, true, false, data));
+ keystrokes.push_back(Keystroke(button, false, false, data));
+ }
+ }
+ else if (type == kKeystrokeModify) {
+ // press modifier
+ keystrokes.push_back(Keystroke(button, true, false, data));
+ }
+ else {
+ // release all the keys that generate the modifier that are
+ // currently down
+ std::pair<ModifierToKeys::const_iterator,
+ ModifierToKeys::const_iterator> range =
+ activeModifiers.equal_range(keyItem.m_generates);
+ for (ModifierToKeys::const_iterator i = range.first;
+ i != range.second; ++i) {
+ keystrokes.push_back(Keystroke(i->second.m_button,
+ false, false, i->second.m_client));
+ }
+ }
+
+ if (type == kKeystrokeModify) {
+ activeModifiers.insert(std::make_pair(
+ keyItem.m_generates, keyItem));
+ currentState |= keyItem.m_generates;
+ }
+ else {
+ activeModifiers.erase(keyItem.m_generates);
+ currentState &= ~keyItem.m_generates;
+ }
+ break;
+ }
+}
+
+SInt32
+KeyMap::getNumModifiers(KeyModifierMask state)
+{
+ SInt32 n = 0;
+ for (; state != 0; state >>= 1) {
+ if ((state & 1) != 0) {
+ ++n;
+ }
+ }
+ return n;
+}
+
+bool
+KeyMap::isDeadKey(KeyID key)
+{
+ return (key == kKeyCompose || (key >= 0x0300 && key <= 0x036f));
+}
+
+KeyID
+KeyMap::getDeadKey(KeyID key)
+{
+ if (isDeadKey(key)) {
+ // already dead
+ return key;
+ }
+
+ switch (key) {
+ case '`':
+ return kKeyDeadGrave;
+
+ case 0xb4u:
+ return kKeyDeadAcute;
+
+ case '^':
+ case 0x2c6:
+ return kKeyDeadCircumflex;
+
+ case '~':
+ case 0x2dcu:
+ return kKeyDeadTilde;
+
+ case 0xafu:
+ return kKeyDeadMacron;
+
+ case 0x2d8u:
+ return kKeyDeadBreve;
+
+ case 0x2d9u:
+ return kKeyDeadAbovedot;
+
+ case 0xa8u:
+ return kKeyDeadDiaeresis;
+
+ case 0xb0u:
+ case 0x2dau:
+ return kKeyDeadAbovering;
+
+ case '\"':
+ case 0x2ddu:
+ return kKeyDeadDoubleacute;
+
+ case 0x2c7u:
+ return kKeyDeadCaron;
+
+ case 0xb8u:
+ return kKeyDeadCedilla;
+
+ case 0x2dbu:
+ return kKeyDeadOgonek;
+
+ default:
+ // unknown
+ return kKeyNone;
+ }
+}
+
+String
+KeyMap::formatKey(KeyID key, KeyModifierMask mask)
+{
+ // initialize tables
+ initKeyNameMaps();
+
+ String x;
+ for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) {
+ KeyModifierMask mod = (1u << i);
+ if ((mask & mod) != 0 && s_modifierToNameMap->count(mod) > 0) {
+ x += s_modifierToNameMap->find(mod)->second;
+ x += "+";
+ }
+ }
+ if (key != kKeyNone) {
+ if (s_keyToNameMap->count(key) > 0) {
+ x += s_keyToNameMap->find(key)->second;
+ }
+ // XXX -- we're assuming ASCII here
+ else if (key >= 33 && key < 127) {
+ x += (char)key;
+ }
+ else {
+ x += barrier::string::sprintf("\\u%04x", key);
+ }
+ }
+ else if (!x.empty()) {
+ // remove trailing '+'
+ x.erase(x.size() - 1);
+ }
+ return x;
+}
+
+bool
+KeyMap::parseKey(const String& x, KeyID& key)
+{
+ // initialize tables
+ initKeyNameMaps();
+
+ // parse the key
+ key = kKeyNone;
+ if (s_nameToKeyMap->count(x) > 0) {
+ key = s_nameToKeyMap->find(x)->second;
+ }
+ // XXX -- we're assuming ASCII encoding here
+ else if (x.size() == 1) {
+ if (!isgraph(x[0])) {
+ // unknown key
+ return false;
+ }
+ key = (KeyID)x[0];
+ }
+ else if (x.size() == 6 && x[0] == '\\' && x[1] == 'u') {
+ // escaped unicode (\uXXXX where XXXX is a hex number)
+ char* end;
+ key = (KeyID)strtol(x.c_str() + 2, &end, 16);
+ if (*end != '\0') {
+ return false;
+ }
+ }
+ else if (!x.empty()) {
+ // unknown key
+ return false;
+ }
+
+ return true;
+}
+
+bool
+KeyMap::parseModifiers(String& x, KeyModifierMask& mask)
+{
+ // initialize tables
+ initKeyNameMaps();
+
+ mask = 0;
+ String::size_type tb = x.find_first_not_of(" \t", 0);
+ while (tb != String::npos) {
+ // get next component
+ String::size_type te = x.find_first_of(" \t+)", tb);
+ if (te == String::npos) {
+ te = x.size();
+ }
+ String c = x.substr(tb, te - tb);
+ if (c.empty()) {
+ // missing component
+ return false;
+ }
+
+ if (s_nameToModifierMap->count(c) > 0) {
+ KeyModifierMask mod = s_nameToModifierMap->find(c)->second;
+ if ((mask & mod) != 0) {
+ // modifier appears twice
+ return false;
+ }
+ mask |= mod;
+ }
+ else {
+ // unknown string
+ x.erase(0, tb);
+ String::size_type tb = x.find_first_not_of(" \t");
+ String::size_type te = x.find_last_not_of(" \t");
+ if (tb == String::npos) {
+ x = "";
+ }
+ else {
+ x = x.substr(tb, te - tb + 1);
+ }
+ return true;
+ }
+
+ // check for '+' or end of string
+ tb = x.find_first_not_of(" \t", te);
+ if (tb != String::npos) {
+ if (x[tb] != '+') {
+ // expected '+'
+ return false;
+ }
+ tb = x.find_first_not_of(" \t", tb + 1);
+ }
+ }
+
+ // parsed the whole thing
+ x = "";
+ return true;
+}
+
+void
+KeyMap::initKeyNameMaps()
+{
+ // initialize tables
+ if (s_nameToKeyMap == NULL) {
+ s_nameToKeyMap = new NameToKeyMap;
+ s_keyToNameMap = new KeyToNameMap;
+ for (const KeyNameMapEntry* i = kKeyNameMap; i->m_name != NULL; ++i) {
+ (*s_nameToKeyMap)[i->m_name] = i->m_id;
+ (*s_keyToNameMap)[i->m_id] = i->m_name;
+ }
+ }
+ if (s_nameToModifierMap == NULL) {
+ s_nameToModifierMap = new NameToModifierMap;
+ s_modifierToNameMap = new ModifierToNameMap;
+ for (const KeyModifierNameMapEntry* i = kModifierNameMap;
+ i->m_name != NULL; ++i) {
+ (*s_nameToModifierMap)[i->m_name] = i->m_mask;
+ (*s_modifierToNameMap)[i->m_mask] = i->m_name;
+ }
+ }
+}
+
+
+//
+// KeyMap::KeyItem
+//
+
+bool
+KeyMap::KeyItem::operator==(const KeyItem& x) const
+{
+ return (m_id == x.m_id &&
+ m_group == x.m_group &&
+ m_button == x.m_button &&
+ m_required == x.m_required &&
+ m_sensitive == x.m_sensitive &&
+ m_generates == x.m_generates &&
+ m_dead == x.m_dead &&
+ m_lock == x.m_lock &&
+ m_client == x.m_client);
+}
+
+
+//
+// KeyMap::Keystroke
+//
+
+KeyMap::Keystroke::Keystroke(KeyButton button,
+ bool press, bool repeat, UInt32 data) :
+ m_type(kButton)
+{
+ m_data.m_button.m_button = button;
+ m_data.m_button.m_press = press;
+ m_data.m_button.m_repeat = repeat;
+ m_data.m_button.m_client = data;
+}
+
+KeyMap::Keystroke::Keystroke(SInt32 group, bool absolute, bool restore) :
+ m_type(kGroup)
+{
+ m_data.m_group.m_group = group;
+ m_data.m_group.m_absolute = absolute;
+ m_data.m_group.m_restore = restore;
+}
+
+}
diff --git a/src/lib/barrier/KeyMap.h b/src/lib/barrier/KeyMap.h
new file mode 100644
index 0000000..b6eb865
--- /dev/null
+++ b/src/lib/barrier/KeyMap.h
@@ -0,0 +1,512 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2005 Chris Schoeneman
+ *
+ * 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 "barrier/key_types.h"
+#include "base/String.h"
+#include "common/stdmap.h"
+#include "common/stdset.h"
+#include "common/stdvector.h"
+
+#include <gtest/gtest_prod.h>
+
+namespace barrier {
+
+//! Key map
+/*!
+This class provides a keyboard mapping.
+*/
+class KeyMap {
+public:
+ KeyMap();
+ virtual ~KeyMap();
+
+ //! KeyID synthesis info
+ /*!
+ This structure contains the information necessary to synthesize a
+ keystroke that generates a KeyID (stored elsewhere). \c m_sensitive
+ lists the modifiers that the key is affected by and must therefore
+ be in the correct state, which is listed in \c m_required. If the
+ key is mapped to a modifier, that modifier is in \c m_generates and
+ is not in \c m_sensitive.
+ */
+ struct KeyItem {
+ public:
+ KeyID m_id; //!< KeyID
+ SInt32 m_group; //!< Group for key
+ KeyButton m_button; //!< Button to generate KeyID
+ KeyModifierMask m_required; //!< Modifiers required for KeyID
+ KeyModifierMask m_sensitive; //!< Modifiers key is sensitive to
+ KeyModifierMask m_generates; //!< Modifiers key is mapped to
+ bool m_dead; //!< \c true if this is a dead KeyID
+ bool m_lock; //!< \c true if this locks a modifier
+ UInt32 m_client; //!< Client data
+
+ public:
+ bool operator==(const KeyItem&) const;
+ };
+
+ //! The KeyButtons needed to synthesize a KeyID
+ /*!
+ An ordered list of \c KeyItems produces a particular KeyID. If
+ the KeyID can be synthesized directly then there is one entry in
+ the list. If dead keys are required then they're listed first.
+ A list is the minimal set of keystrokes necessary to synthesize
+ the KeyID, so it doesn't include no-ops. A list does not include
+ any modifier keys unless the KeyID is a modifier, in which case
+ it has exactly one KeyItem for the modifier itself.
+ */
+ typedef std::vector<KeyItem> KeyItemList;
+
+ //! A keystroke
+ class Keystroke {
+ public:
+ enum EType {
+ kButton, //!< Synthesize button
+ kGroup //!< Set new group
+ };
+
+ Keystroke(KeyButton, bool press, bool repeat, UInt32 clientData);
+ Keystroke(SInt32 group, bool absolute, bool restore);
+
+ public:
+ struct Button {
+ public:
+ KeyButton m_button; //!< Button to synthesize
+ bool m_press; //!< \c true iff press
+ bool m_repeat; //!< \c true iff for an autorepeat
+ UInt32 m_client; //!< Client data
+ };
+ struct Group {
+ public:
+ SInt32 m_group; //!< Group/offset to change to/by
+ bool m_absolute; //!< \c true iff change to, else by
+ bool m_restore; //!< \c true iff for restoring state
+ };
+ union Data {
+ public:
+ Button m_button;
+ Group m_group;
+ };
+
+ EType m_type;
+ Data m_data;
+ };
+
+ //! A sequence of keystrokes
+ typedef std::vector<Keystroke> Keystrokes;
+
+ //! A mapping of a modifier to keys for that modifier
+ typedef std::multimap<KeyModifierMask, KeyItem> ModifierToKeys;
+
+ //! A set of buttons
+ typedef std::map<KeyButton, const KeyItem*> ButtonToKeyMap;
+
+ //! Callback type for \c foreachKey
+ typedef void (*ForeachKeyCallback)(KeyID, SInt32 group,
+ KeyItem&, void* userData);
+
+ //! @name manipulators
+ //@{
+
+ //! Swap with another \c KeyMap
+ virtual void swap(KeyMap&);
+
+ //! Add a key entry
+ /*!
+ Adds \p item to the entries for the item's id and group. The
+ \c m_dead member is set automatically.
+ */
+ void addKeyEntry(const KeyItem& item);
+
+ //! Add an alias key entry
+ /*!
+ If \p targetID with the modifiers given by \p targetRequired and
+ \p targetSensitive is not available in group \p group then find an
+ entry for \p sourceID with modifiers given by \p sourceRequired and
+ \p sourceSensitive in any group with exactly one item and, if found,
+ add a new item just like it except using id \p targetID. This
+ effectively makes the \p sourceID an alias for \p targetID (i.e. we
+ can generate \p targetID using \p sourceID).
+ */
+ void addKeyAliasEntry(KeyID targetID, SInt32 group,
+ KeyModifierMask targetRequired,
+ KeyModifierMask targetSensitive,
+ KeyID sourceID,
+ KeyModifierMask sourceRequired,
+ KeyModifierMask sourceSensitive);
+
+ //! Add a key sequence entry
+ /*!
+ Adds the sequence of keys \p keys (\p numKeys elements long) to
+ synthesize key \p id in group \p group. This looks up in the
+ map each key in \p keys. If all are found then each key is
+ converted to the button for that key and the buttons are added
+ as the entry for \p id. If \p id is already in the map or at
+ least one key in \p keys is not in the map then nothing is added
+ and this returns \c false, otherwise it returns \c true.
+ */
+ bool addKeyCombinationEntry(KeyID id, SInt32 group,
+ const KeyID* keys, UInt32 numKeys);
+
+ //! Enable composition across groups
+ /*!
+ If called then the keyboard map will allow switching between groups
+ during key composition. Not all systems allow that.
+ */
+ void allowGroupSwitchDuringCompose();
+
+ //! Add a half-duplex button
+ /*!
+ Records that button \p button is a half-duplex key. This is called
+ when translating the system's keyboard map. It's independent of the
+ half-duplex modifier calls.
+ */
+ void addHalfDuplexButton(KeyButton button);
+
+ //! Remove all half-duplex modifiers
+ /*!
+ Removes all half-duplex modifiers. This is called to set user
+ configurable half-duplex settings.
+ */
+ void clearHalfDuplexModifiers();
+
+ //! Add a half-duplex modifier
+ /*!
+ Records that modifier key \p key is half-duplex. This is called to
+ set user configurable half-duplex settings.
+ */
+ virtual void addHalfDuplexModifier(KeyID key);
+
+ //! Finish adding entries
+ /*!
+ Called after adding entries, this does some internal housekeeping.
+ */
+ virtual void finish();
+
+ //! Iterate over all added keys items
+ /*!
+ Calls \p cb for every key item.
+ */
+ virtual void foreachKey(ForeachKeyCallback cb, void* userData);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Map key press/repeat to keystrokes.
+ /*!
+ Converts press/repeat of key \p id in group \p group with current
+ modifiers as given in \p currentState and the desired modifiers in
+ \p desiredMask into the keystrokes necessary to synthesize that key
+ event in \p keys. It returns the \c KeyItem of the key being
+ pressed/repeated, or NULL if the key cannot be mapped.
+ */
+ virtual const KeyItem* mapKey(Keystrokes& keys, KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const;
+
+ //! Get number of groups
+ /*!
+ Returns the number of keyboard groups (independent layouts) in the map.
+ */
+ SInt32 getNumGroups() const;
+
+ //! Compute a group number
+ /*!
+ Returns the number of the group \p offset groups after group \p group.
+ */
+ SInt32 getEffectiveGroup(SInt32 group, SInt32 offset) const;
+
+ //! Find key entry compatible with modifiers
+ /*!
+ Returns the \c KeyItemList for the first entry for \p id in group
+ \p group that is compatible with the given modifiers, or NULL
+ if there isn't one. A button list is compatible with a modifiers
+ if it is either insensitive to all modifiers in \p sensitive or
+ it requires the modifiers to be in the state indicated by \p required
+ for every modifier indicated by \p sensitive.
+ */
+ const KeyItemList* findCompatibleKey(KeyID id, SInt32 group,
+ KeyModifierMask required,
+ KeyModifierMask sensitive) const;
+
+ //! Test if modifier is half-duplex
+ /*!
+ Returns \c true iff modifier key \p key or button \p button is
+ half-duplex.
+ */
+ virtual bool isHalfDuplex(KeyID key, KeyButton button) const;
+
+ //! Test if modifiers indicate a command
+ /*!
+ Returns \c true iff the modifiers in \p mask contain any command
+ modifiers. A command modifier is used for keyboard shortcuts and
+ hotkeys, Rather than trying to synthesize a character, a command
+ is trying to synthesize a particular set of buttons. So it's not
+ important to match the shift or AltGr state to achieve a character
+ but it is important to match the modifier state exactly.
+ */
+ bool isCommand(KeyModifierMask mask) const;
+
+ // Get the modifiers that indicate a command
+ /*!
+ Returns the modifiers that when combined with other keys indicate
+ a command (e.g. shortcut or hotkey).
+ */
+ KeyModifierMask getCommandModifiers() const;
+
+ //! Get buttons from modifier map
+ /*!
+ Put all the keys in \p modifiers into \p keys.
+ */
+ static void collectButtons(const ModifierToKeys& modifiers,
+ ButtonToKeyMap& keys);
+
+ //! Set modifier key state
+ /*!
+ Sets the modifier key state (\c m_generates and \c m_lock) in \p item
+ based on the \c m_id in \p item.
+ */
+ static void initModifierKey(KeyItem& item);
+
+ //! Test for a dead key
+ /*!
+ Returns \c true if \p key is a dead key.
+ */
+ static bool isDeadKey(KeyID key);
+
+ //! Get corresponding dead key
+ /*!
+ Returns the dead key corresponding to \p key if one exists, otherwise
+ return \c kKeyNone. This returns \p key if it's already a dead key.
+ */
+ static KeyID getDeadKey(KeyID key);
+
+ //! Get string for a key and modifier mask
+ /*!
+ Converts a key and modifier mask into a string representing the
+ combination.
+ */
+ static String formatKey(KeyID key, KeyModifierMask);
+
+ //! Parse a string into a key
+ /*!
+ Converts a string into a key. Returns \c true on success and \c false
+ if the string cannot be parsed.
+ */
+ static bool parseKey(const String&, KeyID&);
+
+ //! Parse a string into a modifier mask
+ /*!
+ Converts a string into a modifier mask. Returns \c true on success
+ and \c false if the string cannot be parsed. The modifiers plus any
+ remaining leading and trailing whitespace is stripped from the input
+ string.
+ */
+ static bool parseModifiers(String&, KeyModifierMask&);
+
+ //@}
+
+private:
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_requiredDown_matchExactFirstItem);
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_requiredAndExtraSensitiveDown_matchExactFirstItem);
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_requiredAndExtraSensitiveDown_matchExactSecondItem);
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_extraSensitiveDown_matchExactSecondItem);
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_noRequiredDown_matchOneRequiredChangeItem);
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_onlyOneRequiredDown_matchTwoRequiredChangesItem);
+ FRIEND_TEST(KeyMapTests, findBestKey_noRequiredDown_cannotMatch);
+
+private:
+ //! Ways to synthesize a key
+ enum EKeystroke {
+ kKeystrokePress, //!< Synthesize a press
+ kKeystrokeRelease, //!< Synthesize a release
+ kKeystrokeRepeat, //!< Synthesize an autorepeat
+ kKeystrokeClick, //!< Synthesize a press and release
+ kKeystrokeModify, //!< Synthesize pressing a modifier
+ kKeystrokeUnmodify //!< Synthesize releasing a modifier
+ };
+
+ // A list of ways to synthesize a KeyID
+ typedef std::vector<KeyItemList> KeyEntryList;
+
+ // computes the number of groups
+ SInt32 findNumGroups() const;
+
+ // computes the map of modifiers to the keys that generate the modifiers
+ void setModifierKeys();
+
+ // maps a command key. a command key is a keyboard shortcut and we're
+ // trying to synthesize a button press with an exact sets of modifiers,
+ // not trying to synthesize a character. so we just need to find the
+ // right button and synthesize the requested modifiers without regard
+ // to what character they would synthesize. we disallow multikey
+ // entries since they don't make sense as hotkeys.
+ const KeyItem* mapCommandKey(Keystrokes& keys,
+ KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const;
+
+ // maps a character key. a character key is trying to synthesize a
+ // particular KeyID and isn't entirely concerned with the modifiers
+ // used to do it.
+ const KeyItem* mapCharacterKey(Keystrokes& keys,
+ KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const;
+
+ // maps a modifier key
+ const KeyItem* mapModifierKey(Keystrokes& keys,
+ KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const;
+
+ // returns the index into \p entryList of the KeyItemList requiring
+ // the fewest modifier changes between \p currentState and
+ // \p desiredState.
+ SInt32 findBestKey(const KeyEntryList& entryList,
+ KeyModifierMask currentState,
+ KeyModifierMask desiredState) const;
+
+ // gets the \c KeyItem used to synthesize the modifier who's bit is
+ // given by \p modifierBit in group \p group and does not synthesize
+ // the key \p button.
+ const KeyItem* keyForModifier(KeyButton button, SInt32 group,
+ SInt32 modifierBit) const;
+
+ // fills \p keystrokes with the keys to synthesize the key in
+ // \p keyItem taking the modifiers into account. returns \c true
+ // iff successful and sets \p currentState to the
+ // resulting modifier state.
+ bool keysForKeyItem(const KeyItem& keyItem,
+ SInt32& group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredState,
+ KeyModifierMask overrideModifiers,
+ bool isAutoRepeat,
+ Keystrokes& keystrokes) const;
+
+ // fills \p keystrokes with the keys to synthesize the modifiers
+ // in \p desiredModifiers from the active modifiers listed in
+ // \p activeModifiers not including the key in \p keyItem.
+ // returns \c true iff successful.
+ bool keysToRestoreModifiers(const KeyItem& keyItem,
+ SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ const ModifierToKeys& desiredModifiers,
+ Keystrokes& keystrokes) const;
+
+ // fills \p keystrokes and \p undo with the keys to change the
+ // current modifier state in \p currentState to match the state in
+ // \p requiredState for each modifier indicated in \p sensitiveMask.
+ // returns \c true iff successful and sets \p currentState to the
+ // resulting modifier state.
+ bool keysForModifierState(KeyButton button, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask requiredState,
+ KeyModifierMask sensitiveMask,
+ KeyModifierMask notRequiredMask,
+ Keystrokes& keystrokes) const;
+
+ // Adds keystrokes to synthesize key \p keyItem in mode \p type to
+ // \p keystrokes and to undo the synthesis to \p undo.
+ void addKeystrokes(EKeystroke type,
+ const KeyItem& keyItem,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ Keystrokes& keystrokes) const;
+
+ // Returns the number of modifiers indicated in \p state.
+ static SInt32 getNumModifiers(KeyModifierMask state);
+
+ // Initialize key name/id maps
+ static void initKeyNameMaps();
+
+ // not implemented
+ KeyMap(const KeyMap&);
+ KeyMap& operator=(const KeyMap&);
+
+private:
+ // Ways to synthesize a KeyID over multiple keyboard groups
+ typedef std::vector<KeyEntryList> KeyGroupTable;
+
+ // Table of KeyID to ways to synthesize that KeyID
+ typedef std::map<KeyID, KeyGroupTable> KeyIDMap;
+
+ // List of KeyItems that generate a particular modifier
+ typedef std::vector<const KeyItem*> ModifierKeyItemList;
+
+ // Map a modifier to the KeyItems that synthesize that modifier
+ typedef std::vector<ModifierKeyItemList> ModifierToKeyTable;
+
+ // A set of keys
+ typedef std::set<KeyID> KeySet;
+
+ // A set of buttons
+ typedef std::set<KeyButton> KeyButtonSet;
+
+ // Key maps for parsing/formatting
+ typedef std::map<String, KeyID,
+ barrier::string::CaselessCmp> NameToKeyMap;
+ typedef std::map<String, KeyModifierMask,
+ barrier::string::CaselessCmp> NameToModifierMap;
+ typedef std::map<KeyID, String> KeyToNameMap;
+ typedef std::map<KeyModifierMask, String> ModifierToNameMap;
+
+ // KeyID info
+ KeyIDMap m_keyIDMap;
+ SInt32 m_numGroups;
+ ModifierToKeyTable m_modifierKeys;
+
+ // composition info
+ bool m_composeAcrossGroups;
+
+ // half-duplex info
+ KeyButtonSet m_halfDuplex; // half-duplex set by barrier
+ KeySet m_halfDuplexMods; // half-duplex set by user
+
+ // dummy KeyItem for changing modifiers
+ KeyItem m_modifierKeyItem;
+
+ // parsing/formatting tables
+ static NameToKeyMap* s_nameToKeyMap;
+ static NameToModifierMap* s_nameToModifierMap;
+ static KeyToNameMap* s_keyToNameMap;
+ static ModifierToNameMap* s_modifierToNameMap;
+};
+
+}
diff --git a/src/lib/barrier/KeyState.cpp b/src/lib/barrier/KeyState.cpp
new file mode 100644
index 0000000..fc5579d
--- /dev/null
+++ b/src/lib/barrier/KeyState.cpp
@@ -0,0 +1,936 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 Chris Schoeneman
+ *
+ * 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 "barrier/KeyState.h"
+#include "base/Log.h"
+
+#include <cstring>
+#include <algorithm>
+#include <iterator>
+#include <list>
+
+static const KeyButton kButtonMask = (KeyButton)(IKeyState::kNumButtons - 1);
+
+static const KeyID s_decomposeTable[] = {
+ // spacing version of dead keys
+ 0x0060, 0x0300, 0x0020, 0, // grave, dead_grave, space
+ 0x00b4, 0x0301, 0x0020, 0, // acute, dead_acute, space
+ 0x005e, 0x0302, 0x0020, 0, // asciicircum, dead_circumflex, space
+ 0x007e, 0x0303, 0x0020, 0, // asciitilde, dead_tilde, space
+ 0x00a8, 0x0308, 0x0020, 0, // diaeresis, dead_diaeresis, space
+ 0x00b0, 0x030a, 0x0020, 0, // degree, dead_abovering, space
+ 0x00b8, 0x0327, 0x0020, 0, // cedilla, dead_cedilla, space
+ 0x02db, 0x0328, 0x0020, 0, // ogonek, dead_ogonek, space
+ 0x02c7, 0x030c, 0x0020, 0, // caron, dead_caron, space
+ 0x02d9, 0x0307, 0x0020, 0, // abovedot, dead_abovedot, space
+ 0x02dd, 0x030b, 0x0020, 0, // doubleacute, dead_doubleacute, space
+ 0x02d8, 0x0306, 0x0020, 0, // breve, dead_breve, space
+ 0x00af, 0x0304, 0x0020, 0, // macron, dead_macron, space
+
+ // Latin-1 (ISO 8859-1)
+ 0x00c0, 0x0300, 0x0041, 0, // Agrave, dead_grave, A
+ 0x00c1, 0x0301, 0x0041, 0, // Aacute, dead_acute, A
+ 0x00c2, 0x0302, 0x0041, 0, // Acircumflex, dead_circumflex, A
+ 0x00c3, 0x0303, 0x0041, 0, // Atilde, dead_tilde, A
+ 0x00c4, 0x0308, 0x0041, 0, // Adiaeresis, dead_diaeresis, A
+ 0x00c5, 0x030a, 0x0041, 0, // Aring, dead_abovering, A
+ 0x00c7, 0x0327, 0x0043, 0, // Ccedilla, dead_cedilla, C
+ 0x00c8, 0x0300, 0x0045, 0, // Egrave, dead_grave, E
+ 0x00c9, 0x0301, 0x0045, 0, // Eacute, dead_acute, E
+ 0x00ca, 0x0302, 0x0045, 0, // Ecircumflex, dead_circumflex, E
+ 0x00cb, 0x0308, 0x0045, 0, // Ediaeresis, dead_diaeresis, E
+ 0x00cc, 0x0300, 0x0049, 0, // Igrave, dead_grave, I
+ 0x00cd, 0x0301, 0x0049, 0, // Iacute, dead_acute, I
+ 0x00ce, 0x0302, 0x0049, 0, // Icircumflex, dead_circumflex, I
+ 0x00cf, 0x0308, 0x0049, 0, // Idiaeresis, dead_diaeresis, I
+ 0x00d1, 0x0303, 0x004e, 0, // Ntilde, dead_tilde, N
+ 0x00d2, 0x0300, 0x004f, 0, // Ograve, dead_grave, O
+ 0x00d3, 0x0301, 0x004f, 0, // Oacute, dead_acute, O
+ 0x00d4, 0x0302, 0x004f, 0, // Ocircumflex, dead_circumflex, O
+ 0x00d5, 0x0303, 0x004f, 0, // Otilde, dead_tilde, O
+ 0x00d6, 0x0308, 0x004f, 0, // Odiaeresis, dead_diaeresis, O
+ 0x00d9, 0x0300, 0x0055, 0, // Ugrave, dead_grave, U
+ 0x00da, 0x0301, 0x0055, 0, // Uacute, dead_acute, U
+ 0x00db, 0x0302, 0x0055, 0, // Ucircumflex, dead_circumflex, U
+ 0x00dc, 0x0308, 0x0055, 0, // Udiaeresis, dead_diaeresis, U
+ 0x00dd, 0x0301, 0x0059, 0, // Yacute, dead_acute, Y
+ 0x00e0, 0x0300, 0x0061, 0, // agrave, dead_grave, a
+ 0x00e1, 0x0301, 0x0061, 0, // aacute, dead_acute, a
+ 0x00e2, 0x0302, 0x0061, 0, // acircumflex, dead_circumflex, a
+ 0x00e3, 0x0303, 0x0061, 0, // atilde, dead_tilde, a
+ 0x00e4, 0x0308, 0x0061, 0, // adiaeresis, dead_diaeresis, a
+ 0x00e5, 0x030a, 0x0061, 0, // aring, dead_abovering, a
+ 0x00e7, 0x0327, 0x0063, 0, // ccedilla, dead_cedilla, c
+ 0x00e8, 0x0300, 0x0065, 0, // egrave, dead_grave, e
+ 0x00e9, 0x0301, 0x0065, 0, // eacute, dead_acute, e
+ 0x00ea, 0x0302, 0x0065, 0, // ecircumflex, dead_circumflex, e
+ 0x00eb, 0x0308, 0x0065, 0, // ediaeresis, dead_diaeresis, e
+ 0x00ec, 0x0300, 0x0069, 0, // igrave, dead_grave, i
+ 0x00ed, 0x0301, 0x0069, 0, // iacute, dead_acute, i
+ 0x00ee, 0x0302, 0x0069, 0, // icircumflex, dead_circumflex, i
+ 0x00ef, 0x0308, 0x0069, 0, // idiaeresis, dead_diaeresis, i
+ 0x00f1, 0x0303, 0x006e, 0, // ntilde, dead_tilde, n
+ 0x00f2, 0x0300, 0x006f, 0, // ograve, dead_grave, o
+ 0x00f3, 0x0301, 0x006f, 0, // oacute, dead_acute, o
+ 0x00f4, 0x0302, 0x006f, 0, // ocircumflex, dead_circumflex, o
+ 0x00f5, 0x0303, 0x006f, 0, // otilde, dead_tilde, o
+ 0x00f6, 0x0308, 0x006f, 0, // odiaeresis, dead_diaeresis, o
+ 0x00f9, 0x0300, 0x0075, 0, // ugrave, dead_grave, u
+ 0x00fa, 0x0301, 0x0075, 0, // uacute, dead_acute, u
+ 0x00fb, 0x0302, 0x0075, 0, // ucircumflex, dead_circumflex, u
+ 0x00fc, 0x0308, 0x0075, 0, // udiaeresis, dead_diaeresis, u
+ 0x00fd, 0x0301, 0x0079, 0, // yacute, dead_acute, y
+ 0x00ff, 0x0308, 0x0079, 0, // ydiaeresis, dead_diaeresis, y
+
+ // Latin-2 (ISO 8859-2)
+ 0x0104, 0x0328, 0x0041, 0, // Aogonek, dead_ogonek, A
+ 0x013d, 0x030c, 0x004c, 0, // Lcaron, dead_caron, L
+ 0x015a, 0x0301, 0x0053, 0, // Sacute, dead_acute, S
+ 0x0160, 0x030c, 0x0053, 0, // Scaron, dead_caron, S
+ 0x015e, 0x0327, 0x0053, 0, // Scedilla, dead_cedilla, S
+ 0x0164, 0x030c, 0x0054, 0, // Tcaron, dead_caron, T
+ 0x0179, 0x0301, 0x005a, 0, // Zacute, dead_acute, Z
+ 0x017d, 0x030c, 0x005a, 0, // Zcaron, dead_caron, Z
+ 0x017b, 0x0307, 0x005a, 0, // Zabovedot, dead_abovedot, Z
+ 0x0105, 0x0328, 0x0061, 0, // aogonek, dead_ogonek, a
+ 0x013e, 0x030c, 0x006c, 0, // lcaron, dead_caron, l
+ 0x015b, 0x0301, 0x0073, 0, // sacute, dead_acute, s
+ 0x0161, 0x030c, 0x0073, 0, // scaron, dead_caron, s
+ 0x015f, 0x0327, 0x0073, 0, // scedilla, dead_cedilla, s
+ 0x0165, 0x030c, 0x0074, 0, // tcaron, dead_caron, t
+ 0x017a, 0x0301, 0x007a, 0, // zacute, dead_acute, z
+ 0x017e, 0x030c, 0x007a, 0, // zcaron, dead_caron, z
+ 0x017c, 0x0307, 0x007a, 0, // zabovedot, dead_abovedot, z
+ 0x0154, 0x0301, 0x0052, 0, // Racute, dead_acute, R
+ 0x0102, 0x0306, 0x0041, 0, // Abreve, dead_breve, A
+ 0x0139, 0x0301, 0x004c, 0, // Lacute, dead_acute, L
+ 0x0106, 0x0301, 0x0043, 0, // Cacute, dead_acute, C
+ 0x010c, 0x030c, 0x0043, 0, // Ccaron, dead_caron, C
+ 0x0118, 0x0328, 0x0045, 0, // Eogonek, dead_ogonek, E
+ 0x011a, 0x030c, 0x0045, 0, // Ecaron, dead_caron, E
+ 0x010e, 0x030c, 0x0044, 0, // Dcaron, dead_caron, D
+ 0x0143, 0x0301, 0x004e, 0, // Nacute, dead_acute, N
+ 0x0147, 0x030c, 0x004e, 0, // Ncaron, dead_caron, N
+ 0x0150, 0x030b, 0x004f, 0, // Odoubleacute, dead_doubleacute, O
+ 0x0158, 0x030c, 0x0052, 0, // Rcaron, dead_caron, R
+ 0x016e, 0x030a, 0x0055, 0, // Uring, dead_abovering, U
+ 0x0170, 0x030b, 0x0055, 0, // Udoubleacute, dead_doubleacute, U
+ 0x0162, 0x0327, 0x0054, 0, // Tcedilla, dead_cedilla, T
+ 0x0155, 0x0301, 0x0072, 0, // racute, dead_acute, r
+ 0x0103, 0x0306, 0x0061, 0, // abreve, dead_breve, a
+ 0x013a, 0x0301, 0x006c, 0, // lacute, dead_acute, l
+ 0x0107, 0x0301, 0x0063, 0, // cacute, dead_acute, c
+ 0x010d, 0x030c, 0x0063, 0, // ccaron, dead_caron, c
+ 0x0119, 0x0328, 0x0065, 0, // eogonek, dead_ogonek, e
+ 0x011b, 0x030c, 0x0065, 0, // ecaron, dead_caron, e
+ 0x010f, 0x030c, 0x0064, 0, // dcaron, dead_caron, d
+ 0x0144, 0x0301, 0x006e, 0, // nacute, dead_acute, n
+ 0x0148, 0x030c, 0x006e, 0, // ncaron, dead_caron, n
+ 0x0151, 0x030b, 0x006f, 0, // odoubleacute, dead_doubleacute, o
+ 0x0159, 0x030c, 0x0072, 0, // rcaron, dead_caron, r
+ 0x016f, 0x030a, 0x0075, 0, // uring, dead_abovering, u
+ 0x0171, 0x030b, 0x0075, 0, // udoubleacute, dead_doubleacute, u
+ 0x0163, 0x0327, 0x0074, 0, // tcedilla, dead_cedilla, t
+
+ // Latin-3 (ISO 8859-3)
+ 0x0124, 0x0302, 0x0048, 0, // Hcircumflex, dead_circumflex, H
+ 0x0130, 0x0307, 0x0049, 0, // Iabovedot, dead_abovedot, I
+ 0x011e, 0x0306, 0x0047, 0, // Gbreve, dead_breve, G
+ 0x0134, 0x0302, 0x004a, 0, // Jcircumflex, dead_circumflex, J
+ 0x0125, 0x0302, 0x0068, 0, // hcircumflex, dead_circumflex, h
+ 0x011f, 0x0306, 0x0067, 0, // gbreve, dead_breve, g
+ 0x0135, 0x0302, 0x006a, 0, // jcircumflex, dead_circumflex, j
+ 0x010a, 0x0307, 0x0043, 0, // Cabovedot, dead_abovedot, C
+ 0x0108, 0x0302, 0x0043, 0, // Ccircumflex, dead_circumflex, C
+ 0x0120, 0x0307, 0x0047, 0, // Gabovedot, dead_abovedot, G
+ 0x011c, 0x0302, 0x0047, 0, // Gcircumflex, dead_circumflex, G
+ 0x016c, 0x0306, 0x0055, 0, // Ubreve, dead_breve, U
+ 0x015c, 0x0302, 0x0053, 0, // Scircumflex, dead_circumflex, S
+ 0x010b, 0x0307, 0x0063, 0, // cabovedot, dead_abovedot, c
+ 0x0109, 0x0302, 0x0063, 0, // ccircumflex, dead_circumflex, c
+ 0x0121, 0x0307, 0x0067, 0, // gabovedot, dead_abovedot, g
+ 0x011d, 0x0302, 0x0067, 0, // gcircumflex, dead_circumflex, g
+ 0x016d, 0x0306, 0x0075, 0, // ubreve, dead_breve, u
+ 0x015d, 0x0302, 0x0073, 0, // scircumflex, dead_circumflex, s
+
+ // Latin-4 (ISO 8859-4)
+ 0x0156, 0x0327, 0x0052, 0, // Rcedilla, dead_cedilla, R
+ 0x0128, 0x0303, 0x0049, 0, // Itilde, dead_tilde, I
+ 0x013b, 0x0327, 0x004c, 0, // Lcedilla, dead_cedilla, L
+ 0x0112, 0x0304, 0x0045, 0, // Emacron, dead_macron, E
+ 0x0122, 0x0327, 0x0047, 0, // Gcedilla, dead_cedilla, G
+ 0x0157, 0x0327, 0x0072, 0, // rcedilla, dead_cedilla, r
+ 0x0129, 0x0303, 0x0069, 0, // itilde, dead_tilde, i
+ 0x013c, 0x0327, 0x006c, 0, // lcedilla, dead_cedilla, l
+ 0x0113, 0x0304, 0x0065, 0, // emacron, dead_macron, e
+ 0x0123, 0x0327, 0x0067, 0, // gcedilla, dead_cedilla, g
+ 0x0100, 0x0304, 0x0041, 0, // Amacron, dead_macron, A
+ 0x012e, 0x0328, 0x0049, 0, // Iogonek, dead_ogonek, I
+ 0x0116, 0x0307, 0x0045, 0, // Eabovedot, dead_abovedot, E
+ 0x012a, 0x0304, 0x0049, 0, // Imacron, dead_macron, I
+ 0x0145, 0x0327, 0x004e, 0, // Ncedilla, dead_cedilla, N
+ 0x014c, 0x0304, 0x004f, 0, // Omacron, dead_macron, O
+ 0x0136, 0x0327, 0x004b, 0, // Kcedilla, dead_cedilla, K
+ 0x0172, 0x0328, 0x0055, 0, // Uogonek, dead_ogonek, U
+ 0x0168, 0x0303, 0x0055, 0, // Utilde, dead_tilde, U
+ 0x016a, 0x0304, 0x0055, 0, // Umacron, dead_macron, U
+ 0x0101, 0x0304, 0x0061, 0, // amacron, dead_macron, a
+ 0x012f, 0x0328, 0x0069, 0, // iogonek, dead_ogonek, i
+ 0x0117, 0x0307, 0x0065, 0, // eabovedot, dead_abovedot, e
+ 0x012b, 0x0304, 0x0069, 0, // imacron, dead_macron, i
+ 0x0146, 0x0327, 0x006e, 0, // ncedilla, dead_cedilla, n
+ 0x014d, 0x0304, 0x006f, 0, // omacron, dead_macron, o
+ 0x0137, 0x0327, 0x006b, 0, // kcedilla, dead_cedilla, k
+ 0x0173, 0x0328, 0x0075, 0, // uogonek, dead_ogonek, u
+ 0x0169, 0x0303, 0x0075, 0, // utilde, dead_tilde, u
+ 0x016b, 0x0304, 0x0075, 0, // umacron, dead_macron, u
+
+ // Latin-8 (ISO 8859-14)
+ 0x1e02, 0x0307, 0x0042, 0, // Babovedot, dead_abovedot, B
+ 0x1e03, 0x0307, 0x0062, 0, // babovedot, dead_abovedot, b
+ 0x1e0a, 0x0307, 0x0044, 0, // Dabovedot, dead_abovedot, D
+ 0x1e80, 0x0300, 0x0057, 0, // Wgrave, dead_grave, W
+ 0x1e82, 0x0301, 0x0057, 0, // Wacute, dead_acute, W
+ 0x1e0b, 0x0307, 0x0064, 0, // dabovedot, dead_abovedot, d
+ 0x1ef2, 0x0300, 0x0059, 0, // Ygrave, dead_grave, Y
+ 0x1e1e, 0x0307, 0x0046, 0, // Fabovedot, dead_abovedot, F
+ 0x1e1f, 0x0307, 0x0066, 0, // fabovedot, dead_abovedot, f
+ 0x1e40, 0x0307, 0x004d, 0, // Mabovedot, dead_abovedot, M
+ 0x1e41, 0x0307, 0x006d, 0, // mabovedot, dead_abovedot, m
+ 0x1e56, 0x0307, 0x0050, 0, // Pabovedot, dead_abovedot, P
+ 0x1e81, 0x0300, 0x0077, 0, // wgrave, dead_grave, w
+ 0x1e57, 0x0307, 0x0070, 0, // pabovedot, dead_abovedot, p
+ 0x1e83, 0x0301, 0x0077, 0, // wacute, dead_acute, w
+ 0x1e60, 0x0307, 0x0053, 0, // Sabovedot, dead_abovedot, S
+ 0x1ef3, 0x0300, 0x0079, 0, // ygrave, dead_grave, y
+ 0x1e84, 0x0308, 0x0057, 0, // Wdiaeresis, dead_diaeresis, W
+ 0x1e85, 0x0308, 0x0077, 0, // wdiaeresis, dead_diaeresis, w
+ 0x1e61, 0x0307, 0x0073, 0, // sabovedot, dead_abovedot, s
+ 0x0174, 0x0302, 0x0057, 0, // Wcircumflex, dead_circumflex, W
+ 0x1e6a, 0x0307, 0x0054, 0, // Tabovedot, dead_abovedot, T
+ 0x0176, 0x0302, 0x0059, 0, // Ycircumflex, dead_circumflex, Y
+ 0x0175, 0x0302, 0x0077, 0, // wcircumflex, dead_circumflex, w
+ 0x1e6b, 0x0307, 0x0074, 0, // tabovedot, dead_abovedot, t
+ 0x0177, 0x0302, 0x0079, 0, // ycircumflex, dead_circumflex, y
+
+ // Latin-9 (ISO 8859-15)
+ 0x0178, 0x0308, 0x0059, 0, // Ydiaeresis, dead_diaeresis, Y
+
+ // Compose key sequences
+ 0x00c6, kKeyCompose, 0x0041, 0x0045, 0, // AE, A, E
+ 0x00c1, kKeyCompose, 0x0041, 0x0027, 0, // Aacute, A, apostrophe
+ 0x00c2, kKeyCompose, 0x0041, 0x0053, 0, // Acircumflex, A, asciicircum
+ 0x00c3, kKeyCompose, 0x0041, 0x0022, 0, // Adiaeresis, A, quotedbl
+ 0x00c0, kKeyCompose, 0x0041, 0x0060, 0, // Agrave, A, grave
+ 0x00c5, kKeyCompose, 0x0041, 0x002a, 0, // Aring, A, asterisk
+ 0x00c3, kKeyCompose, 0x0041, 0x007e, 0, // Atilde, A, asciitilde
+ 0x00c7, kKeyCompose, 0x0043, 0x002c, 0, // Ccedilla, C, comma
+ 0x00d0, kKeyCompose, 0x0044, 0x002d, 0, // ETH, D, minus
+ 0x00c9, kKeyCompose, 0x0045, 0x0027, 0, // Eacute, E, apostrophe
+ 0x00ca, kKeyCompose, 0x0045, 0x0053, 0, // Ecircumflex, E, asciicircum
+ 0x00cb, kKeyCompose, 0x0045, 0x0022, 0, // Ediaeresis, E, quotedbl
+ 0x00c8, kKeyCompose, 0x0045, 0x0060, 0, // Egrave, E, grave
+ 0x00cd, kKeyCompose, 0x0049, 0x0027, 0, // Iacute, I, apostrophe
+ 0x00ce, kKeyCompose, 0x0049, 0x0053, 0, // Icircumflex, I, asciicircum
+ 0x00cf, kKeyCompose, 0x0049, 0x0022, 0, // Idiaeresis, I, quotedbl
+ 0x00cc, kKeyCompose, 0x0049, 0x0060, 0, // Igrave, I, grave
+ 0x00d1, kKeyCompose, 0x004e, 0x007e, 0, // Ntilde, N, asciitilde
+ 0x00d3, kKeyCompose, 0x004f, 0x0027, 0, // Oacute, O, apostrophe
+ 0x00d4, kKeyCompose, 0x004f, 0x0053, 0, // Ocircumflex, O, asciicircum
+ 0x00d6, kKeyCompose, 0x004f, 0x0022, 0, // Odiaeresis, O, quotedbl
+ 0x00d2, kKeyCompose, 0x004f, 0x0060, 0, // Ograve, O, grave
+ 0x00d8, kKeyCompose, 0x004f, 0x002f, 0, // Ooblique, O, slash
+ 0x00d5, kKeyCompose, 0x004f, 0x007e, 0, // Otilde, O, asciitilde
+ 0x00de, kKeyCompose, 0x0054, 0x0048, 0, // THORN, T, H
+ 0x00da, kKeyCompose, 0x0055, 0x0027, 0, // Uacute, U, apostrophe
+ 0x00db, kKeyCompose, 0x0055, 0x0053, 0, // Ucircumflex, U, asciicircum
+ 0x00dc, kKeyCompose, 0x0055, 0x0022, 0, // Udiaeresis, U, quotedbl
+ 0x00d9, kKeyCompose, 0x0055, 0x0060, 0, // Ugrave, U, grave
+ 0x00dd, kKeyCompose, 0x0059, 0x0027, 0, // Yacute, Y, apostrophe
+ 0x00e1, kKeyCompose, 0x0061, 0x0027, 0, // aacute, a, apostrophe
+ 0x00e2, kKeyCompose, 0x0061, 0x0053, 0, // acircumflex, a, asciicircum
+ 0x00b4, kKeyCompose, 0x0027, 0x0027, 0, // acute, apostrophe, apostrophe
+ 0x00e4, kKeyCompose, 0x0061, 0x0022, 0, // adiaeresis, a, quotedbl
+ 0x00e6, kKeyCompose, 0x0061, 0x0065, 0, // ae, a, e
+ 0x00e0, kKeyCompose, 0x0061, 0x0060, 0, // agrave, a, grave
+ 0x00e5, kKeyCompose, 0x0061, 0x002a, 0, // aring, a, asterisk
+ 0x0040, kKeyCompose, 0x0041, 0x0054, 0, // at, A, T
+ 0x00e3, kKeyCompose, 0x0061, 0x007e, 0, // atilde, a, asciitilde
+ 0x005c, kKeyCompose, 0x002f, 0x002f, 0, // backslash, slash, slash
+ 0x007c, kKeyCompose, 0x004c, 0x0056, 0, // bar, L, V
+ 0x007b, kKeyCompose, 0x0028, 0x002d, 0, // braceleft, parenleft, minus
+ 0x007d, kKeyCompose, 0x0029, 0x002d, 0, // braceright, parenright, minus
+ 0x005b, kKeyCompose, 0x0028, 0x0028, 0, // bracketleft, parenleft, parenleft
+ 0x005d, kKeyCompose, 0x0029, 0x0029, 0, // bracketright, parenright, parenright
+ 0x00a6, kKeyCompose, 0x0042, 0x0056, 0, // brokenbar, B, V
+ 0x00e7, kKeyCompose, 0x0063, 0x002c, 0, // ccedilla, c, comma
+ 0x00b8, kKeyCompose, 0x002c, 0x002c, 0, // cedilla, comma, comma
+ 0x00a2, kKeyCompose, 0x0063, 0x002f, 0, // cent, c, slash
+ 0x00a9, kKeyCompose, 0x0028, 0x0063, 0, // copyright, parenleft, c
+ 0x00a4, kKeyCompose, 0x006f, 0x0078, 0, // currency, o, x
+ 0x00b0, kKeyCompose, 0x0030, 0x0053, 0, // degree, 0, asciicircum
+ 0x00a8, kKeyCompose, 0x0022, 0x0022, 0, // diaeresis, quotedbl, quotedbl
+ 0x00f7, kKeyCompose, 0x003a, 0x002d, 0, // division, colon, minus
+ 0x00e9, kKeyCompose, 0x0065, 0x0027, 0, // eacute, e, apostrophe
+ 0x00ea, kKeyCompose, 0x0065, 0x0053, 0, // ecircumflex, e, asciicircum
+ 0x00eb, kKeyCompose, 0x0065, 0x0022, 0, // ediaeresis, e, quotedbl
+ 0x00e8, kKeyCompose, 0x0065, 0x0060, 0, // egrave, e, grave
+ 0x00f0, kKeyCompose, 0x0064, 0x002d, 0, // eth, d, minus
+ 0x00a1, kKeyCompose, 0x0021, 0x0021, 0, // exclamdown, exclam, exclam
+ 0x00ab, kKeyCompose, 0x003c, 0x003c, 0, // guillemotleft, less, less
+ 0x00bb, kKeyCompose, 0x003e, 0x003e, 0, // guillemotright, greater, greater
+ 0x0023, kKeyCompose, 0x002b, 0x002b, 0, // numbersign, plus, plus
+ 0x00ad, kKeyCompose, 0x002d, 0x002d, 0, // hyphen, minus, minus
+ 0x00ed, kKeyCompose, 0x0069, 0x0027, 0, // iacute, i, apostrophe
+ 0x00ee, kKeyCompose, 0x0069, 0x0053, 0, // icircumflex, i, asciicircum
+ 0x00ef, kKeyCompose, 0x0069, 0x0022, 0, // idiaeresis, i, quotedbl
+ 0x00ec, kKeyCompose, 0x0069, 0x0060, 0, // igrave, i, grave
+ 0x00af, kKeyCompose, 0x002d, 0x0053, 0, // macron, minus, asciicircum
+ 0x00ba, kKeyCompose, 0x006f, 0x005f, 0, // masculine, o, underscore
+ 0x00b5, kKeyCompose, 0x0075, 0x002f, 0, // mu, u, slash
+ 0x00d7, kKeyCompose, 0x0078, 0x0078, 0, // multiply, x, x
+ 0x00a0, kKeyCompose, 0x0020, 0x0020, 0, // nobreakspace, space, space
+ 0x00ac, kKeyCompose, 0x002c, 0x002d, 0, // notsign, comma, minus
+ 0x00f1, kKeyCompose, 0x006e, 0x007e, 0, // ntilde, n, asciitilde
+ 0x00f3, kKeyCompose, 0x006f, 0x0027, 0, // oacute, o, apostrophe
+ 0x00f4, kKeyCompose, 0x006f, 0x0053, 0, // ocircumflex, o, asciicircum
+ 0x00f6, kKeyCompose, 0x006f, 0x0022, 0, // odiaeresis, o, quotedbl
+ 0x00f2, kKeyCompose, 0x006f, 0x0060, 0, // ograve, o, grave
+ 0x00bd, kKeyCompose, 0x0031, 0x0032, 0, // onehalf, 1, 2
+ 0x00bc, kKeyCompose, 0x0031, 0x0034, 0, // onequarter, 1, 4
+ 0x00b9, kKeyCompose, 0x0031, 0x0053, 0, // onesuperior, 1, asciicircum
+ 0x00aa, kKeyCompose, 0x0061, 0x005f, 0, // ordfeminine, a, underscore
+ 0x00f8, kKeyCompose, 0x006f, 0x002f, 0, // oslash, o, slash
+ 0x00f5, kKeyCompose, 0x006f, 0x007e, 0, // otilde, o, asciitilde
+ 0x00b6, kKeyCompose, 0x0070, 0x0021, 0, // paragraph, p, exclam
+ 0x00b7, kKeyCompose, 0x002e, 0x002e, 0, // periodcentered, period, period
+ 0x00b1, kKeyCompose, 0x002b, 0x002d, 0, // plusminus, plus, minus
+ 0x00bf, kKeyCompose, 0x003f, 0x003f, 0, // questiondown, question, question
+ 0x00ae, kKeyCompose, 0x0028, 0x0072, 0, // registered, parenleft, r
+ 0x00a7, kKeyCompose, 0x0073, 0x006f, 0, // section, s, o
+ 0x00df, kKeyCompose, 0x0073, 0x0073, 0, // ssharp, s, s
+ 0x00a3, kKeyCompose, 0x004c, 0x002d, 0, // sterling, L, minus
+ 0x00fe, kKeyCompose, 0x0074, 0x0068, 0, // thorn, t, h
+ 0x00be, kKeyCompose, 0x0033, 0x0034, 0, // threequarters, 3, 4
+ 0x00b3, kKeyCompose, 0x0033, 0x0053, 0, // threesuperior, 3, asciicircum
+ 0x00b2, kKeyCompose, 0x0032, 0x0053, 0, // twosuperior, 2, asciicircum
+ 0x00fa, kKeyCompose, 0x0075, 0x0027, 0, // uacute, u, apostrophe
+ 0x00fb, kKeyCompose, 0x0075, 0x0053, 0, // ucircumflex, u, asciicircum
+ 0x00fc, kKeyCompose, 0x0075, 0x0022, 0, // udiaeresis, u, quotedbl
+ 0x00f9, kKeyCompose, 0x0075, 0x0060, 0, // ugrave, u, grave
+ 0x00fd, kKeyCompose, 0x0079, 0x0027, 0, // yacute, y, apostrophe
+ 0x00ff, kKeyCompose, 0x0079, 0x0022, 0, // ydiaeresis, y, quotedbl
+ 0x00a5, kKeyCompose, 0x0079, 0x003d, 0, // yen, y, equal
+
+ // end of table
+ 0
+};
+
+static const KeyID s_numpadTable[] = {
+ kKeyKP_Space, 0x0020,
+ kKeyKP_Tab, kKeyTab,
+ kKeyKP_Enter, kKeyReturn,
+ kKeyKP_F1, kKeyF1,
+ kKeyKP_F2, kKeyF2,
+ kKeyKP_F3, kKeyF3,
+ kKeyKP_F4, kKeyF4,
+ kKeyKP_Home, kKeyHome,
+ kKeyKP_Left, kKeyLeft,
+ kKeyKP_Up, kKeyUp,
+ kKeyKP_Right, kKeyRight,
+ kKeyKP_Down, kKeyDown,
+ kKeyKP_PageUp, kKeyPageUp,
+ kKeyKP_PageDown, kKeyPageDown,
+ kKeyKP_End, kKeyEnd,
+ kKeyKP_Begin, kKeyBegin,
+ kKeyKP_Insert, kKeyInsert,
+ kKeyKP_Delete, kKeyDelete,
+ kKeyKP_Equal, 0x003d,
+ kKeyKP_Multiply, 0x002a,
+ kKeyKP_Add, 0x002b,
+ kKeyKP_Separator, 0x002c,
+ kKeyKP_Subtract, 0x002d,
+ kKeyKP_Decimal, 0x002e,
+ kKeyKP_Divide, 0x002f,
+ kKeyKP_0, 0x0030,
+ kKeyKP_1, 0x0031,
+ kKeyKP_2, 0x0032,
+ kKeyKP_3, 0x0033,
+ kKeyKP_4, 0x0034,
+ kKeyKP_5, 0x0035,
+ kKeyKP_6, 0x0036,
+ kKeyKP_7, 0x0037,
+ kKeyKP_8, 0x0038,
+ kKeyKP_9, 0x0039
+};
+
+//
+// KeyState
+//
+
+KeyState::KeyState(IEventQueue* events) :
+ IKeyState(events),
+ m_keyMapPtr(new barrier::KeyMap()),
+ m_keyMap(*m_keyMapPtr),
+ m_mask(0),
+ m_events(events)
+{
+ init();
+}
+
+KeyState::KeyState(IEventQueue* events, barrier::KeyMap& keyMap) :
+ IKeyState(events),
+ m_keyMapPtr(0),
+ m_keyMap(keyMap),
+ m_mask(0),
+ m_events(events)
+{
+ init();
+}
+
+KeyState::~KeyState()
+{
+ if (m_keyMapPtr)
+ delete m_keyMapPtr;
+}
+
+void
+KeyState::init()
+{
+ memset(&m_keys, 0, sizeof(m_keys));
+ memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys));
+ memset(&m_keyClientData, 0, sizeof(m_keyClientData));
+ memset(&m_serverKeys, 0, sizeof(m_serverKeys));
+}
+
+void
+KeyState::onKey(KeyButton button, bool down, KeyModifierMask newState)
+{
+ // update modifier state
+ m_mask = newState;
+ LOG((CLOG_DEBUG1 "new mask: 0x%04x", m_mask));
+
+ // ignore bogus buttons
+ button &= kButtonMask;
+ if (button == 0) {
+ return;
+ }
+
+ // update key state
+ if (down) {
+ m_keys[button] = 1;
+ m_syntheticKeys[button] = 1;
+ }
+ else {
+ m_keys[button] = 0;
+ m_syntheticKeys[button] = 0;
+ }
+}
+
+void
+KeyState::sendKeyEvent(
+ void* target, bool press, bool isAutoRepeat,
+ KeyID key, KeyModifierMask mask,
+ SInt32 count, KeyButton button)
+{
+ if (m_keyMap.isHalfDuplex(key, button)) {
+ if (isAutoRepeat) {
+ // ignore auto-repeat on half-duplex keys
+ }
+ else {
+ m_events->addEvent(Event(m_events->forIKeyState().keyDown(), target,
+ KeyInfo::alloc(key, mask, button, 1)));
+ m_events->addEvent(Event(m_events->forIKeyState().keyUp(), target,
+ KeyInfo::alloc(key, mask, button, 1)));
+ }
+ }
+ else {
+ if (isAutoRepeat) {
+ m_events->addEvent(Event(m_events->forIKeyState().keyRepeat(), target,
+ KeyInfo::alloc(key, mask, button, count)));
+ }
+ else if (press) {
+ m_events->addEvent(Event(m_events->forIKeyState().keyDown(), target,
+ KeyInfo::alloc(key, mask, button, 1)));
+ }
+ else {
+ m_events->addEvent(Event(m_events->forIKeyState().keyUp(), target,
+ KeyInfo::alloc(key, mask, button, 1)));
+ }
+ }
+}
+
+void
+KeyState::updateKeyMap()
+{
+ // get the current keyboard map
+ barrier::KeyMap keyMap;
+ getKeyMap(keyMap);
+ m_keyMap.swap(keyMap);
+ m_keyMap.finish();
+
+ // add special keys
+ addCombinationEntries();
+ addKeypadEntries();
+ addAliasEntries();
+}
+
+void
+KeyState::updateKeyState()
+{
+ // reset our state
+ memset(&m_keys, 0, sizeof(m_keys));
+ memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys));
+ memset(&m_keyClientData, 0, sizeof(m_keyClientData));
+ memset(&m_serverKeys, 0, sizeof(m_serverKeys));
+ m_activeModifiers.clear();
+
+ // get the current keyboard state
+ KeyButtonSet keysDown;
+ pollPressedKeys(keysDown);
+ for (KeyButtonSet::const_iterator i = keysDown.begin();
+ i != keysDown.end(); ++i) {
+ m_keys[*i] = 1;
+ }
+
+ // get the current modifier state
+ m_mask = pollActiveModifiers();
+
+ // set active modifiers
+ AddActiveModifierContext addModifierContext(pollActiveGroup(), m_mask,
+ m_activeModifiers);
+ m_keyMap.foreachKey(&KeyState::addActiveModifierCB, &addModifierContext);
+
+ LOG((CLOG_DEBUG1 "modifiers on update: 0x%04x", m_mask));
+}
+
+void
+KeyState::addActiveModifierCB(KeyID, SInt32 group,
+ barrier::KeyMap::KeyItem& keyItem, void* vcontext)
+{
+ AddActiveModifierContext* context =
+ static_cast<AddActiveModifierContext*>(vcontext);
+ if (group == context->m_activeGroup &&
+ (keyItem.m_generates & context->m_mask) != 0) {
+ context->m_activeModifiers.insert(std::make_pair(
+ keyItem.m_generates, keyItem));
+ }
+}
+
+void
+KeyState::setHalfDuplexMask(KeyModifierMask mask)
+{
+ m_keyMap.clearHalfDuplexModifiers();
+ if ((mask & KeyModifierCapsLock) != 0) {
+ m_keyMap.addHalfDuplexModifier(kKeyCapsLock);
+ }
+ if ((mask & KeyModifierNumLock) != 0) {
+ m_keyMap.addHalfDuplexModifier(kKeyNumLock);
+ }
+ if ((mask & KeyModifierScrollLock) != 0) {
+ m_keyMap.addHalfDuplexModifier(kKeyScrollLock);
+ }
+}
+
+void
+KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID)
+{
+ // if this server key is already down then this is probably a
+ // mis-reported autorepeat.
+ serverID &= kButtonMask;
+ if (m_serverKeys[serverID] != 0) {
+ fakeKeyRepeat(id, mask, 1, serverID);
+ return;
+ }
+
+ // ignore certain keys
+ if (isIgnoredKey(id, mask)) {
+ LOG((CLOG_DEBUG1 "ignored key %04x %04x", id, mask));
+ return;
+ }
+
+ // get keys for key press
+ Keystrokes keys;
+ ModifierToKeys oldActiveModifiers = m_activeModifiers;
+ const barrier::KeyMap::KeyItem* keyItem =
+ m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
+ getActiveModifiersRValue(), mask, false);
+ if (keyItem == NULL) {
+ // a media key won't be mapped on mac, so we need to fake it in a
+ // special way
+ if (id == kKeyAudioDown || id == kKeyAudioUp ||
+ id == kKeyAudioMute || id == kKeyAudioPlay ||
+ id == kKeyAudioPrev || id == kKeyAudioNext ||
+ id == kKeyBrightnessDown || id == kKeyBrightnessUp
+ ) {
+ LOG((CLOG_DEBUG1 "emulating media key"));
+ fakeMediaKey(id);
+ }
+
+ return;
+ }
+
+ KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask);
+ updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers);
+ if (localID != 0) {
+ // note keys down
+ ++m_keys[localID];
+ ++m_syntheticKeys[localID];
+ m_keyClientData[localID] = keyItem->m_client;
+ m_serverKeys[serverID] = localID;
+ }
+
+ // generate key events
+ fakeKeys(keys, 1);
+}
+
+bool
+KeyState::fakeKeyRepeat(
+ KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton serverID)
+{
+ serverID &= kButtonMask;
+
+ // if we haven't seen this button go down then ignore it
+ KeyButton oldLocalID = m_serverKeys[serverID];
+ if (oldLocalID == 0) {
+ return false;
+ }
+
+ // get keys for key repeat
+ Keystrokes keys;
+ ModifierToKeys oldActiveModifiers = m_activeModifiers;
+ const barrier::KeyMap::KeyItem* keyItem =
+ m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
+ getActiveModifiersRValue(), mask, true);
+ if (keyItem == NULL) {
+ return false;
+ }
+ KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask);
+ if (localID == 0) {
+ return false;
+ }
+
+ // if the KeyButton for the auto-repeat is not the same as for the
+ // initial press then mark the initial key as released and the new
+ // key as pressed. this can happen when we auto-repeat after a
+ // dead key. for example, a dead accent followed by 'a' will
+ // generate an 'a with accent' followed by a repeating 'a'. the
+ // KeyButtons for the two KeyIDs might be different.
+ if (localID != oldLocalID) {
+ // replace key up with previous KeyButton but leave key down
+ // alone so it uses the new KeyButton.
+ for (Keystrokes::iterator index = keys.begin();
+ index != keys.end(); ++index) {
+ if (index->m_type == Keystroke::kButton &&
+ index->m_data.m_button.m_button == localID) {
+ index->m_data.m_button.m_button = oldLocalID;
+ break;
+ }
+ }
+
+ // note that old key is now up
+ --m_keys[oldLocalID];
+ --m_syntheticKeys[oldLocalID];
+
+ // note keys down
+ updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers);
+ ++m_keys[localID];
+ ++m_syntheticKeys[localID];
+ m_keyClientData[localID] = keyItem->m_client;
+ m_serverKeys[serverID] = localID;
+ }
+
+ // generate key events
+ fakeKeys(keys, count);
+ return true;
+}
+
+bool
+KeyState::fakeKeyUp(KeyButton serverID)
+{
+ // if we haven't seen this button go down then ignore it
+ KeyButton localID = m_serverKeys[serverID & kButtonMask];
+ if (localID == 0) {
+ return false;
+ }
+
+ // get the sequence of keys to simulate key release
+ Keystrokes keys;
+ keys.push_back(Keystroke(localID, false, false, m_keyClientData[localID]));
+
+ // note keys down
+ --m_keys[localID];
+ --m_syntheticKeys[localID];
+ m_serverKeys[serverID] = 0;
+
+ // check if this is a modifier
+ ModifierToKeys::iterator i = m_activeModifiers.begin();
+ while (i != m_activeModifiers.end()) {
+ if (i->second.m_button == localID && !i->second.m_lock) {
+ // modifier is no longer down
+ KeyModifierMask mask = i->first;
+
+ ModifierToKeys::iterator tmp = i;
+ ++i;
+ m_activeModifiers.erase(tmp);
+
+ if (m_activeModifiers.count(mask) == 0) {
+ // no key for modifier is down so deactivate modifier
+ m_mask &= ~mask;
+ LOG((CLOG_DEBUG1 "new state %04x", m_mask));
+ }
+ }
+ else {
+ ++i;
+ }
+ }
+
+ // generate key events
+ fakeKeys(keys, 1);
+ return true;
+}
+
+void
+KeyState::fakeAllKeysUp()
+{
+ Keystrokes keys;
+ for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
+ if (m_syntheticKeys[i] > 0) {
+ keys.push_back(Keystroke(i, false, false, m_keyClientData[i]));
+ m_keys[i] = 0;
+ m_syntheticKeys[i] = 0;
+ }
+ }
+ fakeKeys(keys, 1);
+ memset(&m_serverKeys, 0, sizeof(m_serverKeys));
+ m_activeModifiers.clear();
+ m_mask = pollActiveModifiers();
+}
+
+bool
+KeyState::fakeMediaKey(KeyID id)
+{
+ return false;
+}
+
+bool
+KeyState::isKeyDown(KeyButton button) const
+{
+ return (m_keys[button & kButtonMask] > 0);
+}
+
+KeyModifierMask
+KeyState::getActiveModifiers() const
+{
+ return m_mask;
+}
+
+KeyModifierMask&
+KeyState::getActiveModifiersRValue()
+{
+ return m_mask;
+}
+
+SInt32
+KeyState::getEffectiveGroup(SInt32 group, SInt32 offset) const
+{
+ return m_keyMap.getEffectiveGroup(group, offset);
+}
+
+bool
+KeyState::isIgnoredKey(KeyID key, KeyModifierMask) const
+{
+ switch (key) {
+ case kKeyCapsLock:
+ case kKeyNumLock:
+ case kKeyScrollLock:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+KeyButton
+KeyState::getButton(KeyID id, SInt32 group) const
+{
+ const barrier::KeyMap::KeyItemList* items =
+ m_keyMap.findCompatibleKey(id, group, 0, 0);
+ if (items == NULL) {
+ return 0;
+ }
+ else {
+ return items->back().m_button;
+ }
+}
+
+void
+KeyState::addAliasEntries()
+{
+ for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
+ // if we can't shift any kKeyTab key in a particular group but we can
+ // shift kKeyLeftTab then add a shifted kKeyTab entry that matches a
+ // shifted kKeyLeftTab entry.
+ m_keyMap.addKeyAliasEntry(kKeyTab, g,
+ KeyModifierShift, KeyModifierShift,
+ kKeyLeftTab,
+ KeyModifierShift, KeyModifierShift);
+
+ // if we have no kKeyLeftTab but we do have a kKeyTab that can be
+ // shifted then add kKeyLeftTab that matches a kKeyTab.
+ m_keyMap.addKeyAliasEntry(kKeyLeftTab, g,
+ KeyModifierShift, KeyModifierShift,
+ kKeyTab,
+ 0, KeyModifierShift);
+
+ // map non-breaking space to space
+ m_keyMap.addKeyAliasEntry(0x20, g, 0, 0, 0xa0, 0, 0);
+ }
+}
+
+void
+KeyState::addKeypadEntries()
+{
+ // map every numpad key to its equivalent non-numpad key if it's not
+ // on the keyboard.
+ for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
+ for (size_t i = 0; i < sizeof(s_numpadTable) /
+ sizeof(s_numpadTable[0]); i += 2) {
+ m_keyMap.addKeyCombinationEntry(s_numpadTable[i], g,
+ s_numpadTable + i + 1, 1);
+ }
+ }
+}
+
+void
+KeyState::addCombinationEntries()
+{
+ for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
+ // add dead and compose key composition sequences
+ for (const KeyID* i = s_decomposeTable; *i != 0; ++i) {
+ // count the decomposed keys for this key
+ UInt32 numKeys = 0;
+ for (const KeyID* j = i; *++j != 0; ) {
+ ++numKeys;
+ }
+
+ // add an entry for this key
+ m_keyMap.addKeyCombinationEntry(*i, g, i + 1, numKeys);
+
+ // next key
+ i += numKeys + 1;
+ }
+ }
+}
+
+void
+KeyState::fakeKeys(const Keystrokes& keys, UInt32 count)
+{
+ // do nothing if no keys or no repeats
+ if (count == 0 || keys.empty()) {
+ return;
+ }
+
+ // generate key events
+ LOG((CLOG_DEBUG1 "keystrokes:"));
+ for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) {
+ if (k->m_type == Keystroke::kButton && k->m_data.m_button.m_repeat) {
+ // repeat from here up to but not including the next key
+ // with m_repeat == false count times.
+ Keystrokes::const_iterator start = k;
+ while (count-- > 0) {
+ // send repeating events
+ for (k = start; k != keys.end() &&
+ k->m_type == Keystroke::kButton &&
+ k->m_data.m_button.m_repeat; ++k) {
+ fakeKey(*k);
+ }
+ }
+
+ // note -- k is now on the first non-repeat key after the
+ // repeat keys, exactly where we'd like to continue from.
+ }
+ else {
+ // send event
+ fakeKey(*k);
+
+ // next key
+ ++k;
+ }
+ }
+}
+
+void
+KeyState::updateModifierKeyState(KeyButton button,
+ const ModifierToKeys& oldModifiers,
+ const ModifierToKeys& newModifiers)
+{
+ // get the pressed modifier buttons before and after
+ barrier::KeyMap::ButtonToKeyMap oldKeys, newKeys;
+ for (ModifierToKeys::const_iterator i = oldModifiers.begin();
+ i != oldModifiers.end(); ++i) {
+ oldKeys.insert(std::make_pair(i->second.m_button, &i->second));
+ }
+ for (ModifierToKeys::const_iterator i = newModifiers.begin();
+ i != newModifiers.end(); ++i) {
+ newKeys.insert(std::make_pair(i->second.m_button, &i->second));
+ }
+
+ // get the modifier buttons that were pressed or released
+ barrier::KeyMap::ButtonToKeyMap pressed, released;
+ std::set_difference(oldKeys.begin(), oldKeys.end(),
+ newKeys.begin(), newKeys.end(),
+ std::inserter(released, released.end()),
+ ButtonToKeyLess());
+ std::set_difference(newKeys.begin(), newKeys.end(),
+ oldKeys.begin(), oldKeys.end(),
+ std::inserter(pressed, pressed.end()),
+ ButtonToKeyLess());
+
+ // update state
+ for (barrier::KeyMap::ButtonToKeyMap::const_iterator i = released.begin();
+ i != released.end(); ++i) {
+ if (i->first != button) {
+ m_keys[i->first] = 0;
+ m_syntheticKeys[i->first] = 0;
+ }
+ }
+ for (barrier::KeyMap::ButtonToKeyMap::const_iterator i = pressed.begin();
+ i != pressed.end(); ++i) {
+ if (i->first != button) {
+ m_keys[i->first] = 1;
+ m_syntheticKeys[i->first] = 1;
+ m_keyClientData[i->first] = i->second->m_client;
+ }
+ }
+}
+
+//
+// KeyState::AddActiveModifierContext
+//
+
+KeyState::AddActiveModifierContext::AddActiveModifierContext(
+ SInt32 group, KeyModifierMask mask,
+ ModifierToKeys& activeModifiers) :
+ m_activeGroup(group),
+ m_mask(mask),
+ m_activeModifiers(activeModifiers)
+{
+ // do nothing
+}
diff --git a/src/lib/barrier/KeyState.h b/src/lib/barrier/KeyState.h
new file mode 100644
index 0000000..737d515
--- /dev/null
+++ b/src/lib/barrier/KeyState.h
@@ -0,0 +1,232 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 Chris Schoeneman
+ *
+ * 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 "barrier/IKeyState.h"
+#include "barrier/KeyMap.h"
+
+//! Core key state
+/*!
+This class provides key state services. Subclasses must implement a few
+platform specific methods.
+*/
+class KeyState : public IKeyState {
+public:
+ KeyState(IEventQueue* events);
+ KeyState(IEventQueue* events, barrier::KeyMap& keyMap);
+ virtual ~KeyState();
+
+ //! @name manipulators
+ //@{
+
+ //! Handle key event
+ /*!
+ Sets the state of \p button to down or up and updates the current
+ modifier state to \p newState. This method should be called by
+ primary screens only in response to local events. For auto-repeat
+ set \p down to \c true. Overrides must forward to the superclass.
+ */
+ virtual void onKey(KeyButton button, bool down,
+ KeyModifierMask newState);
+
+ //! Post a key event
+ /*!
+ Posts a key event. This may adjust the event or post additional
+ events in some circumstances. If this is overridden it must forward
+ to the superclass.
+ */
+ virtual void sendKeyEvent(void* target,
+ bool press, bool isAutoRepeat,
+ KeyID key, KeyModifierMask mask,
+ SInt32 count, KeyButton button);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //@}
+
+ // IKeyState overrides
+ virtual void updateKeyMap();
+ virtual void updateKeyState();
+ virtual void setHalfDuplexMask(KeyModifierMask);
+ virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button);
+ virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button);
+ virtual bool fakeKeyUp(KeyButton button);
+ virtual void fakeAllKeysUp();
+ virtual bool fakeCtrlAltDel() = 0;
+ virtual bool fakeMediaKey(KeyID id);
+
+ virtual bool isKeyDown(KeyButton) const;
+ virtual KeyModifierMask
+ getActiveModifiers() const;
+ virtual KeyModifierMask
+ pollActiveModifiers() const = 0;
+ virtual SInt32 pollActiveGroup() const = 0;
+ virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0;
+
+ SInt32 getKeyState(KeyButton keyButton) { return m_keys[keyButton]; }
+
+protected:
+ typedef barrier::KeyMap::Keystroke Keystroke;
+
+ //! @name protected manipulators
+ //@{
+
+ //! Get the keyboard map
+ /*!
+ Fills \p keyMap with the current keyboard map.
+ */
+ virtual void getKeyMap(barrier::KeyMap& keyMap) = 0;
+
+ //! Fake a key event
+ /*!
+ Synthesize an event for \p keystroke.
+ */
+ virtual void fakeKey(const Keystroke& keystroke) = 0;
+
+ //! Get the active modifiers
+ /*!
+ Returns the modifiers that are currently active according to our
+ shadowed state. The state may be modified.
+ */
+ virtual KeyModifierMask&
+ getActiveModifiersRValue();
+
+ //@}
+ //! @name protected accessors
+ //@{
+
+ //! Compute a group number
+ /*!
+ Returns the number of the group \p offset groups after group \p group.
+ */
+ SInt32 getEffectiveGroup(SInt32 group, SInt32 offset) const;
+
+ //! Check if key is ignored
+ /*!
+ Returns \c true if and only if the key should always be ignored.
+ The default returns \c true only for the toggle keys.
+ */
+ virtual bool isIgnoredKey(KeyID key, KeyModifierMask mask) const;
+
+ //! Get button for a KeyID
+ /*!
+ Return the button mapped to key \p id in group \p group if any,
+ otherwise returns 0.
+ */
+ KeyButton getButton(KeyID id, SInt32 group) const;
+
+ //@}
+
+private:
+ typedef barrier::KeyMap::Keystrokes Keystrokes;
+ typedef barrier::KeyMap::ModifierToKeys ModifierToKeys;
+public:
+ struct AddActiveModifierContext {
+ public:
+ AddActiveModifierContext(SInt32 group, KeyModifierMask mask,
+ ModifierToKeys& activeModifiers);
+
+ public:
+ SInt32 m_activeGroup;
+ KeyModifierMask m_mask;
+ ModifierToKeys& m_activeModifiers;
+
+ private:
+ // not implemented
+ AddActiveModifierContext(const AddActiveModifierContext&);
+ AddActiveModifierContext& operator=(const AddActiveModifierContext&);
+ };
+private:
+
+ class ButtonToKeyLess {
+ public:
+ bool operator()(const barrier::KeyMap::ButtonToKeyMap::value_type& a,
+ const barrier::KeyMap::ButtonToKeyMap::value_type b) const
+ {
+ return (a.first < b.first);
+ }
+ };
+
+ // not implemented
+ KeyState(const KeyState&);
+ KeyState& operator=(const KeyState&);
+
+ // called by all ctors.
+ void init();
+
+ // adds alias key sequences. these are sequences that are equivalent
+ // to other sequences.
+ void addAliasEntries();
+
+ // adds non-keypad key sequences for keypad KeyIDs
+ void addKeypadEntries();
+
+ // adds key sequences for combination KeyIDs (those built using
+ // dead keys)
+ void addCombinationEntries();
+
+ // synthesize key events. synthesize auto-repeat events count times.
+ void fakeKeys(const Keystrokes&, UInt32 count);
+
+ // update key state to match changes to modifiers
+ void updateModifierKeyState(KeyButton button,
+ const ModifierToKeys& oldModifiers,
+ const ModifierToKeys& newModifiers);
+
+ // active modifiers collection callback
+ static void addActiveModifierCB(KeyID id, SInt32 group,
+ barrier::KeyMap::KeyItem& keyItem, void* vcontext);
+
+private:
+ // must be declared before m_keyMap. used when this class owns the key map.
+ barrier::KeyMap* m_keyMapPtr;
+
+ // the keyboard map
+ barrier::KeyMap& m_keyMap;
+
+ // current modifier state
+ KeyModifierMask m_mask;
+
+ // the active modifiers and the buttons activating them
+ ModifierToKeys m_activeModifiers;
+
+ // current keyboard state (> 0 if pressed, 0 otherwise). this is
+ // initialized to the keyboard state according to the system then
+ // it tracks synthesized events.
+ SInt32 m_keys[kNumButtons];
+
+ // synthetic keyboard state (> 0 if pressed, 0 otherwise). this
+ // tracks the synthesized keyboard state. if m_keys[n] > 0 but
+ // m_syntheticKeys[n] == 0 then the key was pressed locally and
+ // not synthesized yet.
+ SInt32 m_syntheticKeys[kNumButtons];
+
+ // client data for each pressed key
+ UInt32 m_keyClientData[kNumButtons];
+
+ // server keyboard state. an entry is 0 if not the key isn't pressed
+ // otherwise it's the local KeyButton synthesized for the server key.
+ KeyButton m_serverKeys[kNumButtons];
+
+ IEventQueue* m_events;
+};
diff --git a/src/lib/barrier/PacketStreamFilter.cpp b/src/lib/barrier/PacketStreamFilter.cpp
new file mode 100644
index 0000000..16f0fe7
--- /dev/null
+++ b/src/lib/barrier/PacketStreamFilter.cpp
@@ -0,0 +1,198 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 Chris Schoeneman
+ *
+ * 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 "barrier/PacketStreamFilter.h"
+#include "base/IEventQueue.h"
+#include "mt/Lock.h"
+#include "base/TMethodEventJob.h"
+
+#include <cstring>
+#include <memory>
+
+//
+// PacketStreamFilter
+//
+
+PacketStreamFilter::PacketStreamFilter(IEventQueue* events, barrier::IStream* stream, bool adoptStream) :
+ StreamFilter(events, stream, adoptStream),
+ m_size(0),
+ m_inputShutdown(false),
+ m_events(events)
+{
+ // do nothing
+}
+
+PacketStreamFilter::~PacketStreamFilter()
+{
+ // do nothing
+}
+
+void
+PacketStreamFilter::close()
+{
+ Lock lock(&m_mutex);
+ m_size = 0;
+ m_buffer.pop(m_buffer.getSize());
+ StreamFilter::close();
+}
+
+UInt32
+PacketStreamFilter::read(void* buffer, UInt32 n)
+{
+ if (n == 0) {
+ return 0;
+ }
+
+ Lock lock(&m_mutex);
+
+ // if not enough data yet then give up
+ if (!isReadyNoLock()) {
+ return 0;
+ }
+
+ // read no more than what's left in the buffered packet
+ if (n > m_size) {
+ n = m_size;
+ }
+
+ // read it
+ if (buffer != NULL) {
+ memcpy(buffer, m_buffer.peek(n), n);
+ }
+ m_buffer.pop(n);
+ m_size -= n;
+
+ // get next packet's size if we've finished with this packet and
+ // there's enough data to do so.
+ readPacketSize();
+
+ if (m_inputShutdown && m_size == 0) {
+ m_events->addEvent(Event(m_events->forIStream().inputShutdown(),
+ getEventTarget(), NULL));
+ }
+
+ return n;
+}
+
+void
+PacketStreamFilter::write(const void* buffer, UInt32 count)
+{
+ // write the length of the payload
+ UInt8 length[4];
+ length[0] = (UInt8)((count >> 24) & 0xff);
+ length[1] = (UInt8)((count >> 16) & 0xff);
+ length[2] = (UInt8)((count >> 8) & 0xff);
+ length[3] = (UInt8)( count & 0xff);
+ getStream()->write(length, sizeof(length));
+
+ // write the payload
+ getStream()->write(buffer, count);
+}
+
+void
+PacketStreamFilter::shutdownInput()
+{
+ Lock lock(&m_mutex);
+ m_size = 0;
+ m_buffer.pop(m_buffer.getSize());
+ StreamFilter::shutdownInput();
+}
+
+bool
+PacketStreamFilter::isReady() const
+{
+ Lock lock(&m_mutex);
+ return isReadyNoLock();
+}
+
+UInt32
+PacketStreamFilter::getSize() const
+{
+ Lock lock(&m_mutex);
+ return isReadyNoLock() ? m_size : 0;
+}
+
+bool
+PacketStreamFilter::isReadyNoLock() const
+{
+ return (m_size != 0 && m_buffer.getSize() >= m_size);
+}
+
+void
+PacketStreamFilter::readPacketSize()
+{
+ // note -- m_mutex must be locked on entry
+
+ if (m_size == 0 && m_buffer.getSize() >= 4) {
+ UInt8 buffer[4];
+ memcpy(buffer, m_buffer.peek(sizeof(buffer)), sizeof(buffer));
+ m_buffer.pop(sizeof(buffer));
+ m_size = ((UInt32)buffer[0] << 24) |
+ ((UInt32)buffer[1] << 16) |
+ ((UInt32)buffer[2] << 8) |
+ (UInt32)buffer[3];
+ }
+}
+
+bool
+PacketStreamFilter::readMore()
+{
+ // note if we have whole packet
+ bool wasReady = isReadyNoLock();
+
+ // read more data
+ char buffer[4096];
+ UInt32 n = getStream()->read(buffer, sizeof(buffer));
+ while (n > 0) {
+ m_buffer.write(buffer, n);
+ n = getStream()->read(buffer, sizeof(buffer));
+ }
+
+ // if we don't yet have the next packet size then get it,
+ // if possible.
+ readPacketSize();
+
+ // note if we now have a whole packet
+ bool isReady = isReadyNoLock();
+
+ // if we weren't ready before but now we are then send a
+ // input ready event apparently from the filtered stream.
+ return (wasReady != isReady);
+}
+
+void
+PacketStreamFilter::filterEvent(const Event& event)
+{
+ if (event.getType() == m_events->forIStream().inputReady()) {
+ Lock lock(&m_mutex);
+ if (!readMore()) {
+ return;
+ }
+ }
+ else if (event.getType() == m_events->forIStream().inputShutdown()) {
+ // discard this if we have buffered data
+ Lock lock(&m_mutex);
+ m_inputShutdown = true;
+ if (m_size != 0) {
+ return;
+ }
+ }
+
+ // pass event
+ StreamFilter::filterEvent(event);
+}
diff --git a/src/lib/barrier/PacketStreamFilter.h b/src/lib/barrier/PacketStreamFilter.h
new file mode 100644
index 0000000..bcbd604
--- /dev/null
+++ b/src/lib/barrier/PacketStreamFilter.h
@@ -0,0 +1,59 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 Chris Schoeneman
+ *
+ * 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 "io/StreamFilter.h"
+#include "io/StreamBuffer.h"
+#include "mt/Mutex.h"
+
+class IEventQueue;
+
+//! Packetizing stream filter
+/*!
+Filters a stream to read and write packets.
+*/
+class PacketStreamFilter : public StreamFilter {
+public:
+ PacketStreamFilter(IEventQueue* events, barrier::IStream* stream, bool adoptStream = true);
+ ~PacketStreamFilter();
+
+ // IStream overrides
+ virtual void close();
+ virtual UInt32 read(void* buffer, UInt32 n);
+ virtual void write(const void* buffer, UInt32 n);
+ virtual void shutdownInput();
+ virtual bool isReady() const;
+ virtual UInt32 getSize() const;
+
+protected:
+ // StreamFilter overrides
+ virtual void filterEvent(const Event&);
+
+private:
+ bool isReadyNoLock() const;
+ void readPacketSize();
+ bool readMore();
+
+private:
+ Mutex m_mutex;
+ UInt32 m_size;
+ StreamBuffer m_buffer;
+ bool m_inputShutdown;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/barrier/PlatformScreen.cpp b/src/lib/barrier/PlatformScreen.cpp
new file mode 100644
index 0000000..b0fdc75
--- /dev/null
+++ b/src/lib/barrier/PlatformScreen.cpp
@@ -0,0 +1,123 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 Chris Schoeneman
+ *
+ * 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 "barrier/PlatformScreen.h"
+#include "barrier/App.h"
+#include "barrier/ArgsBase.h"
+
+PlatformScreen::PlatformScreen(IEventQueue* events) :
+ IPlatformScreen(events),
+ m_draggingStarted(false),
+ m_fakeDraggingStarted(false)
+{
+}
+
+PlatformScreen::~PlatformScreen()
+{
+ // do nothing
+}
+
+void
+PlatformScreen::updateKeyMap()
+{
+ getKeyState()->updateKeyMap();
+}
+
+void
+PlatformScreen::updateKeyState()
+{
+ getKeyState()->updateKeyState();
+ updateButtons();
+}
+
+void
+PlatformScreen::setHalfDuplexMask(KeyModifierMask mask)
+{
+ getKeyState()->setHalfDuplexMask(mask);
+}
+
+void
+PlatformScreen::fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button)
+{
+ getKeyState()->fakeKeyDown(id, mask, button);
+}
+
+bool
+PlatformScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button)
+{
+ return getKeyState()->fakeKeyRepeat(id, mask, count, button);
+}
+
+bool
+PlatformScreen::fakeKeyUp(KeyButton button)
+{
+ return getKeyState()->fakeKeyUp(button);
+}
+
+void
+PlatformScreen::fakeAllKeysUp()
+{
+ getKeyState()->fakeAllKeysUp();
+}
+
+bool
+PlatformScreen::fakeCtrlAltDel()
+{
+ return getKeyState()->fakeCtrlAltDel();
+}
+
+bool
+PlatformScreen::isKeyDown(KeyButton button) const
+{
+ return getKeyState()->isKeyDown(button);
+}
+
+KeyModifierMask
+PlatformScreen::getActiveModifiers() const
+{
+ return getKeyState()->getActiveModifiers();
+}
+
+KeyModifierMask
+PlatformScreen::pollActiveModifiers() const
+{
+ return getKeyState()->pollActiveModifiers();
+}
+
+SInt32
+PlatformScreen::pollActiveGroup() const
+{
+ return getKeyState()->pollActiveGroup();
+}
+
+void
+PlatformScreen::pollPressedKeys(KeyButtonSet& pressedKeys) const
+{
+ getKeyState()->pollPressedKeys(pressedKeys);
+}
+
+bool
+PlatformScreen::isDraggingStarted()
+{
+ if (App::instance().argsBase().m_enableDragDrop) {
+ return m_draggingStarted;
+ }
+ return false;
+}
diff --git a/src/lib/barrier/PlatformScreen.h b/src/lib/barrier/PlatformScreen.h
new file mode 100644
index 0000000..82cbfaa
--- /dev/null
+++ b/src/lib/barrier/PlatformScreen.h
@@ -0,0 +1,127 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 Chris Schoeneman
+ *
+ * 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 "barrier/IPlatformScreen.h"
+#include "barrier/DragInformation.h"
+#include "common/stdexcept.h"
+
+//! Base screen implementation
+/*!
+This screen implementation is the superclass of all other screen
+implementations. It implements a handful of methods and requires
+subclasses to implement the rest.
+*/
+class PlatformScreen : public IPlatformScreen {
+public:
+ PlatformScreen(IEventQueue* events);
+ virtual ~PlatformScreen();
+
+ // IScreen overrides
+ virtual void* getEventTarget() const = 0;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const = 0;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
+
+ // IPrimaryScreen overrides
+ virtual void reconfigure(UInt32 activeSides) = 0;
+ virtual void warpCursor(SInt32 x, SInt32 y) = 0;
+ virtual UInt32 registerHotKey(KeyID key,
+ KeyModifierMask mask) = 0;
+ virtual void unregisterHotKey(UInt32 id) = 0;
+ virtual void fakeInputBegin() = 0;
+ virtual void fakeInputEnd() = 0;
+ virtual SInt32 getJumpZoneSize() const = 0;
+ virtual bool isAnyMouseButtonDown(UInt32& buttonID) const = 0;
+ virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
+
+ // ISecondaryScreen overrides
+ virtual void fakeMouseButton(ButtonID id, bool press) = 0;
+ virtual void fakeMouseMove(SInt32 x, SInt32 y) = 0;
+ virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0;
+ virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0;
+
+ // IKeyState overrides
+ virtual void updateKeyMap();
+ virtual void updateKeyState();
+ virtual void setHalfDuplexMask(KeyModifierMask);
+ virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button);
+ virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button);
+ virtual bool fakeKeyUp(KeyButton button);
+ virtual void fakeAllKeysUp();
+ virtual bool fakeCtrlAltDel();
+ virtual bool isKeyDown(KeyButton) const;
+ virtual KeyModifierMask
+ getActiveModifiers() const;
+ virtual KeyModifierMask
+ pollActiveModifiers() const;
+ virtual SInt32 pollActiveGroup() const;
+ virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const;
+
+ virtual void setDraggingStarted(bool started) { m_draggingStarted = started; }
+ virtual bool isDraggingStarted();
+ virtual bool isFakeDraggingStarted() { return m_fakeDraggingStarted; }
+ virtual String& getDraggingFilename() { return m_draggingFilename; }
+ virtual void clearDraggingFilename() { }
+
+ // IPlatformScreen overrides
+ virtual void enable() = 0;
+ virtual void disable() = 0;
+ virtual void enter() = 0;
+ virtual bool leave() = 0;
+ virtual bool setClipboard(ClipboardID, const IClipboard*) = 0;
+ virtual void checkClipboards() = 0;
+ virtual void openScreensaver(bool notify) = 0;
+ virtual void closeScreensaver() = 0;
+ virtual void screensaver(bool activate) = 0;
+ virtual void resetOptions() = 0;
+ virtual void setOptions(const OptionsList& options) = 0;
+ virtual void setSequenceNumber(UInt32) = 0;
+ virtual bool isPrimary() const = 0;
+
+ virtual void fakeDraggingFiles(DragFileList fileList) { throw std::runtime_error("fakeDraggingFiles not implemented"); }
+ virtual const String&
+ getDropTarget() const { throw std::runtime_error("getDropTarget not implemented"); }
+
+protected:
+ //! Update mouse buttons
+ /*!
+ Subclasses must implement this method to update their internal mouse
+ button mapping and, if desired, state tracking.
+ */
+ virtual void updateButtons() = 0;
+
+ //! Get the key state
+ /*!
+ Subclasses must implement this method to return the platform specific
+ key state object that each subclass must have.
+ */
+ virtual IKeyState* getKeyState() const = 0;
+
+ // IPlatformScreen overrides
+ virtual void handleSystemEvent(const Event& event, void*) = 0;
+
+protected:
+ String m_draggingFilename;
+ bool m_draggingStarted;
+ bool m_fakeDraggingStarted;
+};
diff --git a/src/lib/barrier/PortableTaskBarReceiver.cpp b/src/lib/barrier/PortableTaskBarReceiver.cpp
new file mode 100644
index 0000000..384cacd
--- /dev/null
+++ b/src/lib/barrier/PortableTaskBarReceiver.cpp
@@ -0,0 +1,121 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 Chris Schoeneman
+ *
+ * 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 "barrier/PortableTaskBarReceiver.h"
+#include "mt/Lock.h"
+#include "base/String.h"
+#include "base/IEventQueue.h"
+#include "arch/Arch.h"
+#include "common/Version.h"
+
+//
+// PortableTaskBarReceiver
+//
+
+PortableTaskBarReceiver::PortableTaskBarReceiver(IEventQueue* events) :
+ m_state(kNotRunning),
+ m_events(events)
+{
+ // do nothing
+}
+
+PortableTaskBarReceiver::~PortableTaskBarReceiver()
+{
+ // do nothing
+}
+
+void
+PortableTaskBarReceiver::updateStatus(INode* node, const String& errorMsg)
+{
+ {
+ // update our status
+ m_errorMessage = errorMsg;
+ if (node == NULL) {
+ if (m_errorMessage.empty()) {
+ m_state = kNotRunning;
+ }
+ else {
+ m_state = kNotWorking;
+ }
+ }
+ else {
+ m_state = kNotConnected;
+ }
+
+ // let subclasses have a go
+ onStatusChanged(node);
+ }
+
+ // tell task bar
+ ARCH->updateReceiver(this);
+}
+
+PortableTaskBarReceiver::EState
+PortableTaskBarReceiver::getStatus() const
+{
+ return m_state;
+}
+
+const String&
+PortableTaskBarReceiver::getErrorMessage() const
+{
+ return m_errorMessage;
+}
+
+void
+PortableTaskBarReceiver::quit()
+{
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+void
+PortableTaskBarReceiver::onStatusChanged(INode*)
+{
+ // do nothing
+}
+
+void
+PortableTaskBarReceiver::lock() const
+{
+ // do nothing
+}
+
+void
+PortableTaskBarReceiver::unlock() const
+{
+ // do nothing
+}
+
+std::string
+PortableTaskBarReceiver::getToolTip() const
+{
+ switch (m_state) {
+ case kNotRunning:
+ return barrier::string::sprintf("%s: Not running", kAppVersion);
+
+ case kNotWorking:
+ return barrier::string::sprintf("%s: %s",
+ kAppVersion, m_errorMessage.c_str());
+
+ case kNotConnected:
+ return barrier::string::sprintf("%s: Unknown", kAppVersion);
+
+ default:
+ return "";
+ }
+}
diff --git a/src/lib/barrier/PortableTaskBarReceiver.h b/src/lib/barrier/PortableTaskBarReceiver.h
new file mode 100644
index 0000000..d335e44
--- /dev/null
+++ b/src/lib/barrier/PortableTaskBarReceiver.h
@@ -0,0 +1,96 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 Chris Schoeneman
+ *
+ * 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 "barrier/INode.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "base/log_outputters.h"
+#include "base/EventTypes.h"
+#include "base/Event.h"
+#include "base/String.h"
+#include "common/stdvector.h"
+
+class IEventQueue;
+
+//! Implementation of IArchTaskBarReceiver for the barrier server
+class PortableTaskBarReceiver : public IArchTaskBarReceiver {
+public:
+ PortableTaskBarReceiver(IEventQueue* events);
+ virtual ~PortableTaskBarReceiver();
+
+ //! @name manipulators
+ //@{
+
+ //! Update status
+ /*!
+ Determine the status and query required information from the server.
+ */
+ void updateStatus(INode*, const String& errorMsg);
+
+ //@}
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus() = 0;
+ virtual void runMenu(int x, int y) = 0;
+ virtual void primaryAction() = 0;
+ virtual void lock() const;
+ virtual void unlock() const;
+ virtual const Icon getIcon() const = 0;
+ virtual std::string getToolTip() const;
+
+protected:
+ typedef std::vector<String> Clients;
+ enum EState {
+ kNotRunning,
+ kNotWorking,
+ kNotConnected,
+ kConnected,
+ kMaxState
+ };
+
+ //! Get status
+ EState getStatus() const;
+
+ //! Get error message
+ const String& getErrorMessage() const;
+
+ //! Quit app
+ /*!
+ Causes the application to quit gracefully
+ */
+ void quit();
+
+ //! Status change notification
+ /*!
+ Called when status changes. The default implementation does
+ nothing.
+ */
+ virtual void onStatusChanged(INode* node);
+
+private:
+ EState m_state;
+ String m_errorMessage;
+
+ String m_server;
+ Clients m_clients;
+
+ IEventQueue* m_events;
+};
+
+IArchTaskBarReceiver* createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events);
diff --git a/src/lib/barrier/ProtocolUtil.cpp b/src/lib/barrier/ProtocolUtil.cpp
new file mode 100644
index 0000000..42fe69c
--- /dev/null
+++ b/src/lib/barrier/ProtocolUtil.cpp
@@ -0,0 +1,544 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/ProtocolUtil.h"
+#include "io/IStream.h"
+#include "base/Log.h"
+#include "common/stdvector.h"
+
+#include <cctype>
+#include <cstring>
+
+//
+// ProtocolUtil
+//
+
+void
+ProtocolUtil::writef(barrier::IStream* stream, const char* fmt, ...)
+{
+ assert(stream != NULL);
+ assert(fmt != NULL);
+ LOG((CLOG_DEBUG2 "writef(%s)", fmt));
+
+ va_list args;
+ va_start(args, fmt);
+ UInt32 size = getLength(fmt, args);
+ va_end(args);
+ va_start(args, fmt);
+ vwritef(stream, fmt, size, args);
+ va_end(args);
+}
+
+bool
+ProtocolUtil::readf(barrier::IStream* stream, const char* fmt, ...)
+{
+ assert(stream != NULL);
+ assert(fmt != NULL);
+ LOG((CLOG_DEBUG2 "readf(%s)", fmt));
+
+ bool result;
+ va_list args;
+ va_start(args, fmt);
+ try {
+ vreadf(stream, fmt, args);
+ result = true;
+ }
+ catch (XIO&) {
+ result = false;
+ }
+ va_end(args);
+ return result;
+}
+
+void
+ProtocolUtil::vwritef(barrier::IStream* stream,
+ const char* fmt, UInt32 size, va_list args)
+{
+ assert(stream != NULL);
+ assert(fmt != NULL);
+
+ // done if nothing to write
+ if (size == 0) {
+ return;
+ }
+
+ // fill buffer
+ UInt8* buffer = new UInt8[size];
+ writef(buffer, fmt, args);
+
+ try {
+ // write buffer
+ stream->write(buffer, size);
+ LOG((CLOG_DEBUG2 "wrote %d bytes", size));
+
+ delete[] buffer;
+ }
+ catch (XBase&) {
+ delete[] buffer;
+ throw;
+ }
+}
+
+void
+ProtocolUtil::vreadf(barrier::IStream* stream, const char* fmt, va_list args)
+{
+ assert(stream != NULL);
+ assert(fmt != NULL);
+
+ // begin scanning
+ while (*fmt) {
+ if (*fmt == '%') {
+ // format specifier. determine argument size.
+ ++fmt;
+ UInt32 len = eatLength(&fmt);
+ switch (*fmt) {
+ case 'i': {
+ // check for valid length
+ assert(len == 1 || len == 2 || len == 4);
+
+ // read the data
+ UInt8 buffer[4];
+ read(stream, buffer, len);
+
+ // convert it
+ void* v = va_arg(args, void*);
+ switch (len) {
+ case 1:
+ // 1 byte integer
+ *static_cast<UInt8*>(v) = buffer[0];
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *static_cast<UInt8*>(v), *static_cast<UInt8*>(v)));
+ break;
+
+ case 2:
+ // 2 byte integer
+ *static_cast<UInt16*>(v) =
+ static_cast<UInt16>(
+ (static_cast<UInt16>(buffer[0]) << 8) |
+ static_cast<UInt16>(buffer[1]));
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *static_cast<UInt16*>(v), *static_cast<UInt16*>(v)));
+ break;
+
+ case 4:
+ // 4 byte integer
+ *static_cast<UInt32*>(v) =
+ (static_cast<UInt32>(buffer[0]) << 24) |
+ (static_cast<UInt32>(buffer[1]) << 16) |
+ (static_cast<UInt32>(buffer[2]) << 8) |
+ static_cast<UInt32>(buffer[3]);
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *static_cast<UInt32*>(v), *static_cast<UInt32*>(v)));
+ break;
+ }
+ break;
+ }
+
+ case 'I': {
+ // check for valid length
+ assert(len == 1 || len == 2 || len == 4);
+
+ // read the vector length
+ UInt8 buffer[4];
+ read(stream, buffer, 4);
+ UInt32 n = (static_cast<UInt32>(buffer[0]) << 24) |
+ (static_cast<UInt32>(buffer[1]) << 16) |
+ (static_cast<UInt32>(buffer[2]) << 8) |
+ static_cast<UInt32>(buffer[3]);
+
+ // convert it
+ void* v = va_arg(args, void*);
+ switch (len) {
+ case 1:
+ // 1 byte integer
+ for (UInt32 i = 0; i < n; ++i) {
+ read(stream, buffer, 1);
+ static_cast<std::vector<UInt8>*>(v)->push_back(
+ buffer[0]);
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, static_cast<std::vector<UInt8>*>(v)->back(), static_cast<std::vector<UInt8>*>(v)->back()));
+ }
+ break;
+
+ case 2:
+ // 2 byte integer
+ for (UInt32 i = 0; i < n; ++i) {
+ read(stream, buffer, 2);
+ static_cast<std::vector<UInt16>*>(v)->push_back(
+ static_cast<UInt16>(
+ (static_cast<UInt16>(buffer[0]) << 8) |
+ static_cast<UInt16>(buffer[1])));
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, static_cast<std::vector<UInt16>*>(v)->back(), static_cast<std::vector<UInt16>*>(v)->back()));
+ }
+ break;
+
+ case 4:
+ // 4 byte integer
+ for (UInt32 i = 0; i < n; ++i) {
+ read(stream, buffer, 4);
+ static_cast<std::vector<UInt32>*>(v)->push_back(
+ (static_cast<UInt32>(buffer[0]) << 24) |
+ (static_cast<UInt32>(buffer[1]) << 16) |
+ (static_cast<UInt32>(buffer[2]) << 8) |
+ static_cast<UInt32>(buffer[3]));
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, static_cast<std::vector<UInt32>*>(v)->back(), static_cast<std::vector<UInt32>*>(v)->back()));
+ }
+ break;
+ }
+ break;
+ }
+
+ case 's': {
+ assert(len == 0);
+
+ // read the string length
+ UInt8 buffer[128];
+ read(stream, buffer, 4);
+ UInt32 len = (static_cast<UInt32>(buffer[0]) << 24) |
+ (static_cast<UInt32>(buffer[1]) << 16) |
+ (static_cast<UInt32>(buffer[2]) << 8) |
+ static_cast<UInt32>(buffer[3]);
+
+ // use a fixed size buffer if its big enough
+ const bool useFixed = (len <= sizeof(buffer));
+
+ // allocate a buffer to read the data
+ UInt8* sBuffer = buffer;
+ if (!useFixed) {
+ sBuffer = new UInt8[len];
+ }
+
+ // read the data
+ try {
+ read(stream, sBuffer, len);
+ }
+ catch (...) {
+ if (!useFixed) {
+ delete[] sBuffer;
+ }
+ throw;
+ }
+
+ LOG((CLOG_DEBUG2 "readf: read %d byte string", len));
+
+ // save the data
+ String* dst = va_arg(args, String*);
+ dst->assign((const char*)sBuffer, len);
+
+ // release the buffer
+ if (!useFixed) {
+ delete[] sBuffer;
+ }
+ break;
+ }
+
+ case '%':
+ assert(len == 0);
+ break;
+
+ default:
+ assert(0 && "invalid format specifier");
+ }
+
+ // next format character
+ ++fmt;
+ }
+ else {
+ // read next character
+ char buffer[1];
+ read(stream, buffer, 1);
+
+ // verify match
+ if (buffer[0] != *fmt) {
+ LOG((CLOG_DEBUG2 "readf: format mismatch: %c vs %c", *fmt, buffer[0]));
+ throw XIOReadMismatch();
+ }
+
+ // next format character
+ ++fmt;
+ }
+ }
+}
+
+UInt32
+ProtocolUtil::getLength(const char* fmt, va_list args)
+{
+ UInt32 n = 0;
+ while (*fmt) {
+ if (*fmt == '%') {
+ // format specifier. determine argument size.
+ ++fmt;
+ UInt32 len = eatLength(&fmt);
+ switch (*fmt) {
+ case 'i':
+ assert(len == 1 || len == 2 || len == 4);
+ (void)va_arg(args, UInt32);
+ break;
+
+ case 'I':
+ assert(len == 1 || len == 2 || len == 4);
+ switch (len) {
+ case 1:
+ len = (UInt32)(va_arg(args, std::vector<UInt8>*))->size() + 4;
+ break;
+
+ case 2:
+ len = 2 * (UInt32)(va_arg(args, std::vector<UInt16>*))->size() + 4;
+ break;
+
+ case 4:
+ len = 4 * (UInt32)(va_arg(args, std::vector<UInt32>*))->size() + 4;
+ break;
+ }
+ break;
+
+ case 's':
+ assert(len == 0);
+ len = (UInt32)(va_arg(args, String*))->size() + 4;
+ (void)va_arg(args, UInt8*);
+ break;
+
+ case 'S':
+ assert(len == 0);
+ len = va_arg(args, UInt32) + 4;
+ (void)va_arg(args, UInt8*);
+ break;
+
+ case '%':
+ assert(len == 0);
+ len = 1;
+ break;
+
+ default:
+ assert(0 && "invalid format specifier");
+ }
+
+ // accumulate size
+ n += len;
+ ++fmt;
+ }
+ else {
+ // regular character
+ ++n;
+ ++fmt;
+ }
+ }
+ return n;
+}
+
+void
+ProtocolUtil::writef(void* buffer, const char* fmt, va_list args)
+{
+ UInt8* dst = static_cast<UInt8*>(buffer);
+
+ while (*fmt) {
+ if (*fmt == '%') {
+ // format specifier. determine argument size.
+ ++fmt;
+ UInt32 len = eatLength(&fmt);
+ switch (*fmt) {
+ case 'i': {
+ const UInt32 v = va_arg(args, UInt32);
+ switch (len) {
+ case 1:
+ // 1 byte integer
+ *dst++ = static_cast<UInt8>(v & 0xff);
+ break;
+
+ case 2:
+ // 2 byte integer
+ *dst++ = static_cast<UInt8>((v >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( v & 0xff);
+ break;
+
+ case 4:
+ // 4 byte integer
+ *dst++ = static_cast<UInt8>((v >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((v >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((v >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( v & 0xff);
+ break;
+
+ default:
+ assert(0 && "invalid integer format length");
+ return;
+ }
+ break;
+ }
+
+ case 'I': {
+ switch (len) {
+ case 1: {
+ // 1 byte integers
+ const std::vector<UInt8>* list =
+ va_arg(args, const std::vector<UInt8>*);
+ const UInt32 n = (UInt32)list->size();
+ *dst++ = static_cast<UInt8>((n >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( n & 0xff);
+ for (UInt32 i = 0; i < n; ++i) {
+ *dst++ = (*list)[i];
+ }
+ break;
+ }
+
+ case 2: {
+ // 2 byte integers
+ const std::vector<UInt16>* list =
+ va_arg(args, const std::vector<UInt16>*);
+ const UInt32 n = (UInt32)list->size();
+ *dst++ = static_cast<UInt8>((n >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( n & 0xff);
+ for (UInt32 i = 0; i < n; ++i) {
+ const UInt16 v = (*list)[i];
+ *dst++ = static_cast<UInt8>((v >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( v & 0xff);
+ }
+ break;
+ }
+
+ case 4: {
+ // 4 byte integers
+ const std::vector<UInt32>* list =
+ va_arg(args, const std::vector<UInt32>*);
+ const UInt32 n = (UInt32)list->size();
+ *dst++ = static_cast<UInt8>((n >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( n & 0xff);
+ for (UInt32 i = 0; i < n; ++i) {
+ const UInt32 v = (*list)[i];
+ *dst++ = static_cast<UInt8>((v >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((v >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((v >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( v & 0xff);
+ }
+ break;
+ }
+
+ default:
+ assert(0 && "invalid integer vector format length");
+ return;
+ }
+ break;
+ }
+
+ case 's': {
+ assert(len == 0);
+ const String* src = va_arg(args, String*);
+ const UInt32 len = (src != NULL) ? (UInt32)src->size() : 0;
+ *dst++ = static_cast<UInt8>((len >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((len >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((len >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( len & 0xff);
+ if (len != 0) {
+ memcpy(dst, src->data(), len);
+ dst += len;
+ }
+ break;
+ }
+
+ case 'S': {
+ assert(len == 0);
+ const UInt32 len = va_arg(args, UInt32);
+ const UInt8* src = va_arg(args, UInt8*);
+ *dst++ = static_cast<UInt8>((len >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((len >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((len >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( len & 0xff);
+ memcpy(dst, src, len);
+ dst += len;
+ break;
+ }
+
+ case '%':
+ assert(len == 0);
+ *dst++ = '%';
+ break;
+
+ default:
+ assert(0 && "invalid format specifier");
+ }
+
+ // next format character
+ ++fmt;
+ }
+ else {
+ // copy regular character
+ *dst++ = *fmt++;
+ }
+ }
+}
+
+UInt32
+ProtocolUtil::eatLength(const char** pfmt)
+{
+ const char* fmt = *pfmt;
+ UInt32 n = 0;
+ for (;;) {
+ UInt32 d;
+ switch (*fmt) {
+ case '0': d = 0; break;
+ case '1': d = 1; break;
+ case '2': d = 2; break;
+ case '3': d = 3; break;
+ case '4': d = 4; break;
+ case '5': d = 5; break;
+ case '6': d = 6; break;
+ case '7': d = 7; break;
+ case '8': d = 8; break;
+ case '9': d = 9; break;
+ default: *pfmt = fmt; return n;
+ }
+ n = 10 * n + d;
+ ++fmt;
+ }
+}
+
+void
+ProtocolUtil::read(barrier::IStream* stream, void* vbuffer, UInt32 count)
+{
+ assert(stream != NULL);
+ assert(vbuffer != NULL);
+
+ UInt8* buffer = static_cast<UInt8*>(vbuffer);
+ while (count > 0) {
+ // read more
+ UInt32 n = stream->read(buffer, count);
+
+ // bail if stream has hungup
+ if (n == 0) {
+ LOG((CLOG_DEBUG2 "unexpected disconnect in readf(), %d bytes left", count));
+ throw XIOEndOfStream();
+ }
+
+ // prepare for next read
+ buffer += n;
+ count -= n;
+ }
+}
+
+
+//
+// XIOReadMismatch
+//
+
+String
+XIOReadMismatch::getWhat() const throw()
+{
+ return format("XIOReadMismatch", "ProtocolUtil::readf() mismatch");
+}
diff --git a/src/lib/barrier/ProtocolUtil.h b/src/lib/barrier/ProtocolUtil.h
new file mode 100644
index 0000000..78bb5ca
--- /dev/null
+++ b/src/lib/barrier/ProtocolUtil.h
@@ -0,0 +1,96 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "io/XIO.h"
+#include "base/EventTypes.h"
+
+#include <stdarg.h>
+
+namespace barrier { class IStream; }
+
+//! Barrier protocol utilities
+/*!
+This class provides various functions for implementing the barrier
+protocol.
+*/
+class ProtocolUtil {
+public:
+ //! Write formatted data
+ /*!
+ Write formatted binary data to a stream. \c fmt consists of
+ regular characters and format specifiers. Format specifiers
+ begin with \%. All characters not part of a format specifier
+ are regular and are transmitted unchanged.
+
+ Format specifiers are:
+ - \%\% -- literal `\%'
+ - \%1i -- converts integer argument to 1 byte integer
+ - \%2i -- converts integer argument to 2 byte integer in NBO
+ - \%4i -- converts integer argument to 4 byte integer in NBO
+ - \%1I -- converts std::vector<UInt8>* to 1 byte integers
+ - \%2I -- converts std::vector<UInt16>* to 2 byte integers in NBO
+ - \%4I -- converts std::vector<UInt32>* to 4 byte integers in NBO
+ - \%s -- converts String* to stream of bytes
+ - \%S -- converts integer N and const UInt8* to stream of N bytes
+ */
+ static void writef(barrier::IStream*,
+ const char* fmt, ...);
+
+ //! Read formatted data
+ /*!
+ Read formatted binary data from a buffer. This performs the
+ reverse operation of writef(). Returns true if the entire
+ format was successfully parsed, false otherwise.
+
+ Format specifiers are:
+ - \%\% -- read (and discard) a literal `\%'
+ - \%1i -- reads a 1 byte integer; argument is a SInt32* or UInt32*
+ - \%2i -- reads an NBO 2 byte integer; arg is SInt32* or UInt32*
+ - \%4i -- reads an NBO 4 byte integer; arg is SInt32* or UInt32*
+ - \%1I -- reads 1 byte integers; arg is std::vector<UInt8>*
+ - \%2I -- reads NBO 2 byte integers; arg is std::vector<UInt16>*
+ - \%4I -- reads NBO 4 byte integers; arg is std::vector<UInt32>*
+ - \%s -- reads bytes; argument must be a String*, \b not a char*
+ */
+ static bool readf(barrier::IStream*,
+ const char* fmt, ...);
+
+private:
+ static void vwritef(barrier::IStream*,
+ const char* fmt, UInt32 size, va_list);
+ static void vreadf(barrier::IStream*,
+ const char* fmt, va_list);
+
+ static UInt32 getLength(const char* fmt, va_list);
+ static void writef(void*, const char* fmt, va_list);
+ static UInt32 eatLength(const char** fmt);
+ static void read(barrier::IStream*, void*, UInt32);
+};
+
+//! Mismatched read exception
+/*!
+Thrown by ProtocolUtil::readf() when the data being read does not
+match the format.
+*/
+class XIOReadMismatch : public XIO {
+public:
+ // XBase overrides
+ virtual String getWhat() const throw();
+};
diff --git a/src/lib/barrier/Screen.cpp b/src/lib/barrier/Screen.cpp
new file mode 100644
index 0000000..32442f6
--- /dev/null
+++ b/src/lib/barrier/Screen.cpp
@@ -0,0 +1,559 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 Chris Schoeneman
+ *
+ * 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 "barrier/Screen.h"
+#include "barrier/IPlatformScreen.h"
+#include "barrier/protocol_types.h"
+#include "base/Log.h"
+#include "base/IEventQueue.h"
+#include "server/ClientProxy.h"
+#include "base/TMethodEventJob.h"
+
+namespace barrier {
+
+//
+// Screen
+//
+
+Screen::Screen(IPlatformScreen* platformScreen, IEventQueue* events) :
+ m_screen(platformScreen),
+ m_isPrimary(platformScreen->isPrimary()),
+ m_enabled(false),
+ m_entered(m_isPrimary),
+ m_screenSaverSync(true),
+ m_fakeInput(false),
+ m_events(events),
+ m_mock(false),
+ m_enableDragDrop(false)
+{
+ assert(m_screen != NULL);
+
+ // reset options
+ resetOptions();
+
+ LOG((CLOG_DEBUG "opened display"));
+}
+
+Screen::~Screen()
+{
+ if (m_mock) {
+ return;
+ }
+
+ if (m_enabled) {
+ disable();
+ }
+ assert(!m_enabled);
+ assert(m_entered == m_isPrimary);
+ delete m_screen;
+ LOG((CLOG_DEBUG "closed display"));
+}
+
+void
+Screen::enable()
+{
+ assert(!m_enabled);
+
+ m_screen->updateKeyMap();
+ m_screen->updateKeyState();
+ m_screen->enable();
+ if (m_isPrimary) {
+ enablePrimary();
+ }
+ else {
+ enableSecondary();
+ }
+
+ // note activation
+ m_enabled = true;
+}
+
+void
+Screen::disable()
+{
+ assert(m_enabled);
+
+ if (!m_isPrimary && m_entered) {
+ leave();
+ }
+ else if (m_isPrimary && !m_entered) {
+ enter(0);
+ }
+ m_screen->disable();
+ if (m_isPrimary) {
+ disablePrimary();
+ }
+ else {
+ disableSecondary();
+ }
+
+ // note deactivation
+ m_enabled = false;
+}
+
+void
+Screen::enter(KeyModifierMask toggleMask)
+{
+ assert(m_entered == false);
+ LOG((CLOG_INFO "entering screen"));
+
+ // now on screen
+ m_entered = true;
+
+ m_screen->enter();
+ if (m_isPrimary) {
+ enterPrimary();
+ }
+ else {
+ enterSecondary(toggleMask);
+ }
+}
+
+bool
+Screen::leave()
+{
+ assert(m_entered == true);
+ LOG((CLOG_INFO "leaving screen"));
+
+ if (!m_screen->leave()) {
+ return false;
+ }
+ if (m_isPrimary) {
+ leavePrimary();
+ }
+ else {
+ leaveSecondary();
+ }
+
+ // make sure our idea of clipboard ownership is correct
+ m_screen->checkClipboards();
+
+ // now not on screen
+ m_entered = false;
+
+ return true;
+}
+
+void
+Screen::reconfigure(UInt32 activeSides)
+{
+ assert(m_isPrimary);
+ m_screen->reconfigure(activeSides);
+}
+
+void
+Screen::warpCursor(SInt32 x, SInt32 y)
+{
+ assert(m_isPrimary);
+ m_screen->warpCursor(x, y);
+}
+
+void
+Screen::setClipboard(ClipboardID id, const IClipboard* clipboard)
+{
+ m_screen->setClipboard(id, clipboard);
+}
+
+void
+Screen::grabClipboard(ClipboardID id)
+{
+ m_screen->setClipboard(id, NULL);
+}
+
+void
+Screen::screensaver(bool activate)
+{
+ if (!m_isPrimary) {
+ // activate/deactivation screen saver iff synchronization enabled
+ if (m_screenSaverSync) {
+ m_screen->screensaver(activate);
+ }
+ }
+}
+
+void
+Screen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
+{
+ // check for ctrl+alt+del emulation
+ if (id == kKeyDelete &&
+ (mask & (KeyModifierControl | KeyModifierAlt)) ==
+ (KeyModifierControl | KeyModifierAlt)) {
+ LOG((CLOG_DEBUG "emulating ctrl+alt+del press"));
+ if (m_screen->fakeCtrlAltDel()) {
+ return;
+ }
+ }
+ m_screen->fakeKeyDown(id, mask, button);
+}
+
+void
+Screen::keyRepeat(KeyID id,
+ KeyModifierMask mask, SInt32 count, KeyButton button)
+{
+ assert(!m_isPrimary);
+ m_screen->fakeKeyRepeat(id, mask, count, button);
+}
+
+void
+Screen::keyUp(KeyID, KeyModifierMask, KeyButton button)
+{
+ m_screen->fakeKeyUp(button);
+}
+
+void
+Screen::mouseDown(ButtonID button)
+{
+ m_screen->fakeMouseButton(button, true);
+}
+
+void
+Screen::mouseUp(ButtonID button)
+{
+ m_screen->fakeMouseButton(button, false);
+}
+
+void
+Screen::mouseMove(SInt32 x, SInt32 y)
+{
+ assert(!m_isPrimary);
+ m_screen->fakeMouseMove(x, y);
+}
+
+void
+Screen::mouseRelativeMove(SInt32 dx, SInt32 dy)
+{
+ assert(!m_isPrimary);
+ m_screen->fakeMouseRelativeMove(dx, dy);
+}
+
+void
+Screen::mouseWheel(SInt32 xDelta, SInt32 yDelta)
+{
+ assert(!m_isPrimary);
+ m_screen->fakeMouseWheel(xDelta, yDelta);
+}
+
+void
+Screen::resetOptions()
+{
+ // reset options
+ m_halfDuplex = 0;
+
+ // if screen saver synchronization was off then turn it on since
+ // that's the default option state.
+ if (!m_screenSaverSync) {
+ m_screenSaverSync = true;
+ if (!m_isPrimary) {
+ m_screen->openScreensaver(false);
+ }
+ }
+
+ // let screen handle its own options
+ m_screen->resetOptions();
+}
+
+void
+Screen::setOptions(const OptionsList& options)
+{
+ // update options
+ bool oldScreenSaverSync = m_screenSaverSync;
+ for (UInt32 i = 0, n = (UInt32)options.size(); i < n; i += 2) {
+ if (options[i] == kOptionScreenSaverSync) {
+ m_screenSaverSync = (options[i + 1] != 0);
+ LOG((CLOG_DEBUG1 "screen saver synchronization %s", m_screenSaverSync ? "on" : "off"));
+ }
+ else if (options[i] == kOptionHalfDuplexCapsLock) {
+ if (options[i + 1] != 0) {
+ m_halfDuplex |= KeyModifierCapsLock;
+ }
+ else {
+ m_halfDuplex &= ~KeyModifierCapsLock;
+ }
+ LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", ((m_halfDuplex & KeyModifierCapsLock) != 0) ? "on" : "off"));
+ }
+ else if (options[i] == kOptionHalfDuplexNumLock) {
+ if (options[i + 1] != 0) {
+ m_halfDuplex |= KeyModifierNumLock;
+ }
+ else {
+ m_halfDuplex &= ~KeyModifierNumLock;
+ }
+ LOG((CLOG_DEBUG1 "half-duplex num-lock %s", ((m_halfDuplex & KeyModifierNumLock) != 0) ? "on" : "off"));
+ }
+ else if (options[i] == kOptionHalfDuplexScrollLock) {
+ if (options[i + 1] != 0) {
+ m_halfDuplex |= KeyModifierScrollLock;
+ }
+ else {
+ m_halfDuplex &= ~KeyModifierScrollLock;
+ }
+ LOG((CLOG_DEBUG1 "half-duplex scroll-lock %s", ((m_halfDuplex & KeyModifierScrollLock) != 0) ? "on" : "off"));
+ }
+ }
+
+ // update half-duplex options
+ m_screen->setHalfDuplexMask(m_halfDuplex);
+
+ // update screen saver synchronization
+ if (!m_isPrimary && oldScreenSaverSync != m_screenSaverSync) {
+ if (m_screenSaverSync) {
+ m_screen->openScreensaver(false);
+ }
+ else {
+ m_screen->closeScreensaver();
+ }
+ }
+
+ // let screen handle its own options
+ m_screen->setOptions(options);
+}
+
+void
+Screen::setSequenceNumber(UInt32 seqNum)
+{
+ m_screen->setSequenceNumber(seqNum);
+}
+
+UInt32
+Screen::registerHotKey(KeyID key, KeyModifierMask mask)
+{
+ return m_screen->registerHotKey(key, mask);
+}
+
+void
+Screen::unregisterHotKey(UInt32 id)
+{
+ m_screen->unregisterHotKey(id);
+}
+
+void
+Screen::fakeInputBegin()
+{
+ assert(!m_fakeInput);
+
+ m_fakeInput = true;
+ m_screen->fakeInputBegin();
+}
+
+void
+Screen::fakeInputEnd()
+{
+ assert(m_fakeInput);
+
+ m_fakeInput = false;
+ m_screen->fakeInputEnd();
+}
+
+bool
+Screen::isOnScreen() const
+{
+ return m_entered;
+}
+
+bool
+Screen::isLockedToScreen() const
+{
+ // check for pressed mouse buttons
+ // HACK: commented out as it breaks new drag drop feature
+ UInt32 buttonID = 0;
+
+ if (m_screen->isAnyMouseButtonDown(buttonID)) {
+ if (buttonID != kButtonLeft) {
+ LOG((CLOG_DEBUG "locked by mouse buttonID: %d", buttonID));
+ }
+
+ if (m_enableDragDrop) {
+ return (buttonID == kButtonLeft) ? false : true;
+ }
+ else {
+ return true;
+ }
+ }
+
+ // not locked
+ return false;
+}
+
+SInt32
+Screen::getJumpZoneSize() const
+{
+ if (!m_isPrimary) {
+ return 0;
+ }
+ else {
+ return m_screen->getJumpZoneSize();
+ }
+}
+
+void
+Screen::getCursorCenter(SInt32& x, SInt32& y) const
+{
+ m_screen->getCursorCenter(x, y);
+}
+
+KeyModifierMask
+Screen::getActiveModifiers() const
+{
+ return m_screen->getActiveModifiers();
+}
+
+KeyModifierMask
+Screen::pollActiveModifiers() const
+{
+ return m_screen->pollActiveModifiers();
+}
+
+bool
+Screen::isDraggingStarted() const
+{
+ return m_screen->isDraggingStarted();
+}
+
+bool
+Screen::isFakeDraggingStarted() const
+{
+ return m_screen->isFakeDraggingStarted();
+}
+
+void
+Screen::setDraggingStarted(bool started)
+{
+ m_screen->setDraggingStarted(started);
+}
+
+void
+Screen::startDraggingFiles(DragFileList& fileList)
+{
+ m_screen->fakeDraggingFiles(fileList);
+}
+
+void
+Screen::setEnableDragDrop(bool enabled)
+{
+ m_enableDragDrop = enabled;
+}
+
+String&
+Screen::getDraggingFilename() const
+{
+ return m_screen->getDraggingFilename();
+}
+
+void
+Screen::clearDraggingFilename()
+{
+ m_screen->clearDraggingFilename();
+}
+
+const String&
+Screen::getDropTarget() const
+{
+ return m_screen->getDropTarget();
+}
+
+void*
+Screen::getEventTarget() const
+{
+ return m_screen;
+}
+
+bool
+Screen::getClipboard(ClipboardID id, IClipboard* clipboard) const
+{
+ return m_screen->getClipboard(id, clipboard);
+}
+
+void
+Screen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
+{
+ m_screen->getShape(x, y, w, h);
+}
+
+void
+Screen::getCursorPos(SInt32& x, SInt32& y) const
+{
+ m_screen->getCursorPos(x, y);
+}
+
+void
+Screen::enablePrimary()
+{
+ // get notified of screen saver activation/deactivation
+ m_screen->openScreensaver(true);
+
+ // claim screen changed size
+ m_events->addEvent(Event(m_events->forIScreen().shapeChanged(), getEventTarget()));
+}
+
+void
+Screen::enableSecondary()
+{
+ // assume primary has all clipboards
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ grabClipboard(id);
+ }
+
+ // disable the screen saver if synchronization is enabled
+ if (m_screenSaverSync) {
+ m_screen->openScreensaver(false);
+ }
+}
+
+void
+Screen::disablePrimary()
+{
+ // done with screen saver
+ m_screen->closeScreensaver();
+}
+
+void
+Screen::disableSecondary()
+{
+ // done with screen saver
+ m_screen->closeScreensaver();
+}
+
+void
+Screen::enterPrimary()
+{
+ // do nothing
+}
+
+void
+Screen::enterSecondary(KeyModifierMask)
+{
+ // do nothing
+}
+
+void
+Screen::leavePrimary()
+{
+ // we don't track keys while on the primary screen so update our
+ // idea of them now. this is particularly to update the state of
+ // the toggle modifiers.
+ m_screen->updateKeyState();
+}
+
+void
+Screen::leaveSecondary()
+{
+ // release any keys we think are still down
+ m_screen->fakeAllKeysUp();
+}
+
+}
diff --git a/src/lib/barrier/Screen.h b/src/lib/barrier/Screen.h
new file mode 100644
index 0000000..b16feff
--- /dev/null
+++ b/src/lib/barrier/Screen.h
@@ -0,0 +1,345 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/DragInformation.h"
+#include "barrier/clipboard_types.h"
+#include "barrier/IScreen.h"
+#include "barrier/key_types.h"
+#include "barrier/mouse_types.h"
+#include "barrier/option_types.h"
+#include "base/String.h"
+
+class IClipboard;
+class IPlatformScreen;
+class IEventQueue;
+
+namespace barrier {
+
+//! Platform independent screen
+/*!
+This is a platform independent screen. It can work as either a
+primary or secondary screen.
+*/
+class Screen : public IScreen {
+public:
+ Screen(IPlatformScreen* platformScreen, IEventQueue* events);
+ virtual ~Screen();
+
+#ifdef TEST_ENV
+ Screen() : m_mock(true) { }
+#endif
+
+ //! @name manipulators
+ //@{
+
+ //! Activate screen
+ /*!
+ Activate the screen, preparing it to report system and user events.
+ For a secondary screen it also means disabling the screen saver if
+ synchronizing it and preparing to synthesize events.
+ */
+ virtual void enable();
+
+ //! Deactivate screen
+ /*!
+ Undoes the operations in activate() and events are no longer
+ reported. It also releases keys that are logically pressed.
+ */
+ virtual void disable();
+
+ //! Enter screen
+ /*!
+ Called when the user navigates to this screen. \p toggleMask has the
+ toggle keys that should be turned on on the secondary screen.
+ */
+ void enter(KeyModifierMask toggleMask);
+
+ //! Leave screen
+ /*!
+ Called when the user navigates off this screen.
+ */
+ bool leave();
+
+ //! Update configuration
+ /*!
+ This is called when the configuration has changed. \c activeSides
+ is a bitmask of EDirectionMask indicating which sides of the
+ primary screen are linked to clients.
+ */
+ void reconfigure(UInt32 activeSides);
+
+ //! Warp cursor
+ /*!
+ Warps the cursor to the absolute coordinates \c x,y. Also
+ discards input events up to and including the warp before
+ returning.
+ */
+ void warpCursor(SInt32 x, SInt32 y);
+
+ //! Set clipboard
+ /*!
+ Sets the system's clipboard contents. This is usually called
+ soon after an enter().
+ */
+ void setClipboard(ClipboardID, const IClipboard*);
+
+ //! Grab clipboard
+ /*!
+ Grabs (i.e. take ownership of) the system clipboard.
+ */
+ void grabClipboard(ClipboardID);
+
+ //! Activate/deactivate screen saver
+ /*!
+ Forcibly activates the screen saver if \c activate is true otherwise
+ forcibly deactivates it.
+ */
+ void screensaver(bool activate);
+
+ //! Notify of key press
+ /*!
+ Synthesize key events to generate a press of key \c id. If possible
+ match the given modifier mask. The KeyButton identifies the physical
+ key on the server that generated this key down. The client must
+ ensure that a key up or key repeat that uses the same KeyButton will
+ synthesize an up or repeat for the same client key synthesized by
+ keyDown().
+ */
+ void keyDown(KeyID id, KeyModifierMask, KeyButton);
+
+ //! Notify of key repeat
+ /*!
+ Synthesize key events to generate a press and release of key \c id
+ \c count times. If possible match the given modifier mask.
+ */
+ void keyRepeat(KeyID id, KeyModifierMask,
+ SInt32 count, KeyButton);
+
+ //! Notify of key release
+ /*!
+ Synthesize key events to generate a release of key \c id. If possible
+ match the given modifier mask.
+ */
+ void keyUp(KeyID id, KeyModifierMask, KeyButton);
+
+ //! Notify of mouse press
+ /*!
+ Synthesize mouse events to generate a press of mouse button \c id.
+ */
+ void mouseDown(ButtonID id);
+
+ //! Notify of mouse release
+ /*!
+ Synthesize mouse events to generate a release of mouse button \c id.
+ */
+ void mouseUp(ButtonID id);
+
+ //! Notify of mouse motion
+ /*!
+ Synthesize mouse events to generate mouse motion to the absolute
+ screen position \c xAbs,yAbs.
+ */
+ void mouseMove(SInt32 xAbs, SInt32 yAbs);
+
+ //! Notify of mouse motion
+ /*!
+ Synthesize mouse events to generate mouse motion by the relative
+ amount \c xRel,yRel.
+ */
+ void mouseRelativeMove(SInt32 xRel, SInt32 yRel);
+
+ //! Notify of mouse wheel motion
+ /*!
+ Synthesize mouse events to generate mouse wheel motion of \c xDelta
+ and \c yDelta. Deltas are positive for motion away from the user or
+ to the right and negative for motion towards the user or to the left.
+ Each wheel click should generate a delta of +/-120.
+ */
+ void mouseWheel(SInt32 xDelta, SInt32 yDelta);
+
+ //! Notify of options changes
+ /*!
+ Resets all options to their default values.
+ */
+ virtual void resetOptions();
+
+ //! Notify of options changes
+ /*!
+ Set options to given values. Ignores unknown options and doesn't
+ modify options that aren't given in \c options.
+ */
+ virtual void setOptions(const OptionsList& options);
+
+ //! Set clipboard sequence number
+ /*!
+ Sets the sequence number to use in subsequent clipboard events.
+ */
+ void setSequenceNumber(UInt32);
+
+ //! Register a system hotkey
+ /*!
+ Registers a system-wide hotkey for key \p key with modifiers \p mask.
+ Returns an id used to unregister the hotkey.
+ */
+ UInt32 registerHotKey(KeyID key, KeyModifierMask mask);
+
+ //! Unregister a system hotkey
+ /*!
+ Unregisters a previously registered hot key.
+ */
+ void unregisterHotKey(UInt32 id);
+
+ //! Prepare to synthesize input on primary screen
+ /*!
+ Prepares the primary screen to receive synthesized input. We do not
+ want to receive this synthesized input as user input so this method
+ ensures that we ignore it. Calls to \c fakeInputBegin() may not be
+ nested.
+ */
+ void fakeInputBegin();
+
+ //! Done synthesizing input on primary screen
+ /*!
+ Undoes whatever \c fakeInputBegin() did.
+ */
+ void fakeInputEnd();
+
+ //! Change dragging status
+ void setDraggingStarted(bool started);
+
+ //! Fake a files dragging operation
+ void startDraggingFiles(DragFileList& fileList);
+
+ void setEnableDragDrop(bool enabled);
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Test if cursor on screen
+ /*!
+ Returns true iff the cursor is on the screen.
+ */
+ bool isOnScreen() const;
+
+ //! Get screen lock state
+ /*!
+ Returns true if there's any reason that the user should not be
+ allowed to leave the screen (usually because a button or key is
+ pressed). If this method returns true it logs a message as to
+ why at the CLOG_DEBUG level.
+ */
+ bool isLockedToScreen() const;
+
+ //! Get jump zone size
+ /*!
+ Return the jump zone size, the size of the regions on the edges of
+ the screen that cause the cursor to jump to another screen.
+ */
+ SInt32 getJumpZoneSize() const;
+
+ //! Get cursor center position
+ /*!
+ Return the cursor center position which is where we park the
+ cursor to compute cursor motion deltas and should be far from
+ the edges of the screen, typically the center.
+ */
+ void getCursorCenter(SInt32& x, SInt32& y) const;
+
+ //! Get the active modifiers
+ /*!
+ Returns the modifiers that are currently active according to our
+ shadowed state.
+ */
+ KeyModifierMask getActiveModifiers() const;
+
+ //! Get the active modifiers from OS
+ /*!
+ Returns the modifiers that are currently active according to the
+ operating system.
+ */
+ KeyModifierMask pollActiveModifiers() const;
+
+ //! Test if file is dragged on primary screen
+ bool isDraggingStarted() const;
+
+ //! Test if file is dragged on secondary screen
+ bool isFakeDraggingStarted() const;
+
+ //! Get the filename of the file being dragged
+ String& getDraggingFilename() const;
+
+ //! Clear the filename of the file that was dragged
+ void clearDraggingFilename();
+
+ //! Get the drop target directory
+ const String& getDropTarget() const;
+
+ //@}
+
+ // IScreen overrides
+ virtual void* getEventTarget() const;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const;
+
+ IPlatformScreen* getPlatformScreen() { return m_screen; }
+
+protected:
+ void enablePrimary();
+ void enableSecondary();
+ void disablePrimary();
+ void disableSecondary();
+
+ void enterPrimary();
+ void enterSecondary(KeyModifierMask toggleMask);
+ void leavePrimary();
+ void leaveSecondary();
+
+private:
+ // our platform dependent screen
+ IPlatformScreen* m_screen;
+
+ // true if screen is being used as a primary screen, false otherwise
+ bool m_isPrimary;
+
+ // true if screen is enabled
+ bool m_enabled;
+
+ // true if the cursor is on this screen
+ bool m_entered;
+
+ // true if screen saver should be synchronized to server
+ bool m_screenSaverSync;
+
+ // note toggle keys that toggles on up/down (false) or on
+ // transition (true)
+ KeyModifierMask m_halfDuplex;
+
+ // true if we're faking input on a primary screen
+ bool m_fakeInput;
+
+ IEventQueue* m_events;
+
+ bool m_mock;
+ bool m_enableDragDrop;
+};
+
+}
diff --git a/src/lib/barrier/ServerApp.cpp b/src/lib/barrier/ServerApp.cpp
new file mode 100644
index 0000000..112f290
--- /dev/null
+++ b/src/lib/barrier/ServerApp.cpp
@@ -0,0 +1,859 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/ServerApp.h"
+
+#include "server/Server.h"
+#include "server/ClientListener.h"
+#include "server/ClientProxy.h"
+#include "server/PrimaryClient.h"
+#include "barrier/ArgParser.h"
+#include "barrier/Screen.h"
+#include "barrier/XScreen.h"
+#include "barrier/ServerTaskBarReceiver.h"
+#include "barrier/ServerArgs.h"
+#include "net/SocketMultiplexer.h"
+#include "net/TCPSocketFactory.h"
+#include "net/XSocket.h"
+#include "arch/Arch.h"
+#include "base/EventQueue.h"
+#include "base/log_outputters.h"
+#include "base/FunctionEventJob.h"
+#include "base/TMethodJob.h"
+#include "base/IEventQueue.h"
+#include "base/Log.h"
+#include "base/TMethodEventJob.h"
+#include "common/Version.h"
+
+#if SYSAPI_WIN32
+#include "arch/win32/ArchMiscWindows.h"
+#endif
+
+#if WINAPI_MSWINDOWS
+#include "platform/MSWindowsScreen.h"
+#elif WINAPI_XWINDOWS
+#include "platform/XWindowsScreen.h"
+#elif WINAPI_CARBON
+#include "platform/OSXScreen.h"
+#endif
+
+#if defined(__APPLE__)
+#include "platform/OSXDragSimulator.h"
+#endif
+
+#include <iostream>
+#include <stdio.h>
+#include <fstream>
+#include <sstream>
+
+//
+// ServerApp
+//
+
+ServerApp::ServerApp(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver) :
+ App(events, createTaskBarReceiver, new ServerArgs()),
+ m_server(NULL),
+ m_serverState(kUninitialized),
+ m_serverScreen(NULL),
+ m_primaryClient(NULL),
+ m_listener(NULL),
+ m_timer(NULL),
+ m_barrierAddress(NULL)
+{
+}
+
+ServerApp::~ServerApp()
+{
+}
+
+void
+ServerApp::parseArgs(int argc, const char* const* argv)
+{
+ ArgParser argParser(this);
+ bool result = argParser.parseServerArgs(args(), argc, argv);
+
+ if (!result || args().m_shouldExit) {
+ m_bye(kExitArgs);
+ }
+ else {
+ if (!args().m_barrierAddress.empty()) {
+ try {
+ *m_barrierAddress = NetworkAddress(args().m_barrierAddress,
+ kDefaultPort);
+ m_barrierAddress->resolve();
+ }
+ catch (XSocketAddress& e) {
+ LOG((CLOG_PRINT "%s: %s" BYE,
+ args().m_pname, e.what(), args().m_pname));
+ m_bye(kExitArgs);
+ }
+ }
+ }
+}
+
+void
+ServerApp::help()
+{
+ // window api args (windows/x-windows/carbon)
+#if WINAPI_XWINDOWS
+# define WINAPI_ARGS \
+ " [--display <display>] [--no-xinitthreads]"
+# define WINAPI_INFO \
+ " --display <display> connect to the X server at <display>\n" \
+ " --no-xinitthreads do not call XInitThreads()\n"
+#else
+# define WINAPI_ARGS ""
+# define WINAPI_INFO ""
+#endif
+
+ std::ostringstream buffer;
+ buffer << "Start the barrier server component." << std::endl
+ << std::endl
+ << "Usage: " << args().m_pname
+ << " [--address <address>]"
+ << " [--config <pathname>]"
+ << WINAPI_ARGS << HELP_SYS_ARGS << HELP_COMMON_ARGS << std::endl
+ << std::endl
+ << "Options:" << std::endl
+ << " -a, --address <address> listen for clients on the given address." << std::endl
+ << " -c, --config <pathname> use the named configuration file instead." << std::endl
+ << HELP_COMMON_INFO_1 << WINAPI_INFO << HELP_SYS_INFO << HELP_COMMON_INFO_2 << std::endl
+ << "Default options are marked with a *" << std::endl
+ << std::endl
+ << "The argument for --address is of the form: [<hostname>][:<port>]. The" << std::endl
+ << "hostname must be the address or hostname of an interface on the system." << std::endl
+ << "The default is to listen on all interfaces. The port overrides the" << std::endl
+ << "default port, " << kDefaultPort << "." << std::endl
+ << std::endl
+ << "If no configuration file pathname is provided then the first of the" << std::endl
+ << "following to load successfully sets the configuration:" << std::endl
+ << " $HOME/" << USR_CONFIG_NAME << std::endl
+ << " " << ARCH->concatPath(ARCH->getSystemDirectory(), SYS_CONFIG_NAME) << std::endl;
+
+ LOG((CLOG_PRINT "%s", buffer.str().c_str()));
+}
+
+void
+ServerApp::reloadSignalHandler(Arch::ESignal, void*)
+{
+ IEventQueue* events = App::instance().getEvents();
+ events->addEvent(Event(events->forServerApp().reloadConfig(),
+ events->getSystemTarget()));
+}
+
+void
+ServerApp::reloadConfig(const Event&, void*)
+{
+ LOG((CLOG_DEBUG "reload configuration"));
+ if (loadConfig(args().m_configFile)) {
+ if (m_server != NULL) {
+ m_server->setConfig(*args().m_config);
+ }
+ LOG((CLOG_NOTE "reloaded configuration"));
+ }
+}
+
+void
+ServerApp::loadConfig()
+{
+ bool loaded = false;
+
+ // load the config file, if specified
+ if (!args().m_configFile.empty()) {
+ loaded = loadConfig(args().m_configFile);
+ }
+
+ // load the default configuration if no explicit file given
+ else {
+ // get the user's home directory
+ String path = ARCH->getUserDirectory();
+ if (!path.empty()) {
+ // complete path
+ path = ARCH->concatPath(path, USR_CONFIG_NAME);
+
+ // now try loading the user's configuration
+ if (loadConfig(path)) {
+ loaded = true;
+ args().m_configFile = path;
+ }
+ }
+ if (!loaded) {
+ // try the system-wide config file
+ path = ARCH->getSystemDirectory();
+ if (!path.empty()) {
+ path = ARCH->concatPath(path, SYS_CONFIG_NAME);
+ if (loadConfig(path)) {
+ loaded = true;
+ args().m_configFile = path;
+ }
+ }
+ }
+ }
+
+ if (!loaded) {
+ LOG((CLOG_PRINT "%s: no configuration available", args().m_pname));
+ m_bye(kExitConfig);
+ }
+}
+
+bool
+ServerApp::loadConfig(const String& pathname)
+{
+ try {
+ // load configuration
+ LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str()));
+ std::ifstream configStream(pathname.c_str());
+ if (!configStream.is_open()) {
+ // report failure to open configuration as a debug message
+ // since we try several paths and we expect some to be
+ // missing.
+ LOG((CLOG_DEBUG "cannot open configuration \"%s\"",
+ pathname.c_str()));
+ return false;
+ }
+ configStream >> *args().m_config;
+ LOG((CLOG_DEBUG "configuration read successfully"));
+ return true;
+ }
+ catch (XConfigRead& e) {
+ // report error in configuration file
+ LOG((CLOG_ERR "cannot read configuration \"%s\": %s",
+ pathname.c_str(), e.what()));
+ }
+ return false;
+}
+
+void
+ServerApp::forceReconnect(const Event&, void*)
+{
+ if (m_server != NULL) {
+ m_server->disconnect();
+ }
+}
+
+void
+ServerApp::handleClientConnected(const Event&, void* vlistener)
+{
+ ClientListener* listener = static_cast<ClientListener*>(vlistener);
+ ClientProxy* client = listener->getNextClient();
+ if (client != NULL) {
+ m_server->adoptClient(client);
+ updateStatus();
+ }
+}
+
+void
+ServerApp::handleClientsDisconnected(const Event&, void*)
+{
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+void
+ServerApp::closeServer(Server* server)
+{
+ if (server == NULL) {
+ return;
+ }
+
+ // tell all clients to disconnect
+ server->disconnect();
+
+ // wait for clients to disconnect for up to timeout seconds
+ double timeout = 3.0;
+ EventQueueTimer* timer = m_events->newOneShotTimer(timeout, NULL);
+ m_events->adoptHandler(Event::kTimer, timer,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::handleClientsDisconnected));
+ m_events->adoptHandler(m_events->forServer().disconnected(), server,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::handleClientsDisconnected));
+
+ m_events->loop();
+
+ m_events->removeHandler(Event::kTimer, timer);
+ m_events->deleteTimer(timer);
+ m_events->removeHandler(m_events->forServer().disconnected(), server);
+
+ // done with server
+ delete server;
+}
+
+void
+ServerApp::stopRetryTimer()
+{
+ if (m_timer != NULL) {
+ m_events->deleteTimer(m_timer);
+ m_events->removeHandler(Event::kTimer, NULL);
+ m_timer = NULL;
+ }
+}
+
+void
+ServerApp::updateStatus()
+{
+ updateStatus("");
+}
+
+void ServerApp::updateStatus(const String& msg)
+{
+ if (m_taskBarReceiver)
+ {
+ m_taskBarReceiver->updateStatus(m_server, msg);
+ }
+}
+
+void
+ServerApp::closeClientListener(ClientListener* listen)
+{
+ if (listen != NULL) {
+ m_events->removeHandler(m_events->forClientListener().connected(), listen);
+ delete listen;
+ }
+}
+
+void
+ServerApp::stopServer()
+{
+ if (m_serverState == kStarted) {
+ closeServer(m_server);
+ closeClientListener(m_listener);
+ m_server = NULL;
+ m_listener = NULL;
+ m_serverState = kInitialized;
+ }
+ else if (m_serverState == kStarting) {
+ stopRetryTimer();
+ m_serverState = kInitialized;
+ }
+ assert(m_server == NULL);
+ assert(m_listener == NULL);
+}
+
+void
+ServerApp::closePrimaryClient(PrimaryClient* primaryClient)
+{
+ delete primaryClient;
+}
+
+void
+ServerApp::closeServerScreen(barrier::Screen* screen)
+{
+ if (screen != NULL) {
+ m_events->removeHandler(m_events->forIScreen().error(),
+ screen->getEventTarget());
+ m_events->removeHandler(m_events->forIScreen().suspend(),
+ screen->getEventTarget());
+ m_events->removeHandler(m_events->forIScreen().resume(),
+ screen->getEventTarget());
+ delete screen;
+ }
+}
+
+void ServerApp::cleanupServer()
+{
+ stopServer();
+ if (m_serverState == kInitialized) {
+ closePrimaryClient(m_primaryClient);
+ closeServerScreen(m_serverScreen);
+ m_primaryClient = NULL;
+ m_serverScreen = NULL;
+ m_serverState = kUninitialized;
+ }
+ else if (m_serverState == kInitializing ||
+ m_serverState == kInitializingToStart) {
+ stopRetryTimer();
+ m_serverState = kUninitialized;
+ }
+ assert(m_primaryClient == NULL);
+ assert(m_serverScreen == NULL);
+ assert(m_serverState == kUninitialized);
+}
+
+void
+ServerApp::retryHandler(const Event&, void*)
+{
+ // discard old timer
+ assert(m_timer != NULL);
+ stopRetryTimer();
+
+ // try initializing/starting the server again
+ switch (m_serverState) {
+ case kUninitialized:
+ case kInitialized:
+ case kStarted:
+ assert(0 && "bad internal server state");
+ break;
+
+ case kInitializing:
+ LOG((CLOG_DEBUG1 "retry server initialization"));
+ m_serverState = kUninitialized;
+ if (!initServer()) {
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ break;
+
+ case kInitializingToStart:
+ LOG((CLOG_DEBUG1 "retry server initialization"));
+ m_serverState = kUninitialized;
+ if (!initServer()) {
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ else if (m_serverState == kInitialized) {
+ LOG((CLOG_DEBUG1 "starting server"));
+ if (!startServer()) {
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ }
+ break;
+
+ case kStarting:
+ LOG((CLOG_DEBUG1 "retry starting server"));
+ m_serverState = kInitialized;
+ if (!startServer()) {
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ break;
+ }
+}
+
+bool ServerApp::initServer()
+{
+ // skip if already initialized or initializing
+ if (m_serverState != kUninitialized) {
+ return true;
+ }
+
+ double retryTime;
+ barrier::Screen* serverScreen = NULL;
+ PrimaryClient* primaryClient = NULL;
+ try {
+ String name = args().m_config->getCanonicalName(args().m_name);
+ serverScreen = openServerScreen();
+ primaryClient = openPrimaryClient(name, serverScreen);
+ m_serverScreen = serverScreen;
+ m_primaryClient = primaryClient;
+ m_serverState = kInitialized;
+ updateStatus();
+ return true;
+ }
+ catch (XScreenUnavailable& e) {
+ LOG((CLOG_WARN "primary screen unavailable: %s", e.what()));
+ closePrimaryClient(primaryClient);
+ closeServerScreen(serverScreen);
+ updateStatus(String("primary screen unavailable: ") + e.what());
+ retryTime = e.getRetryTime();
+ }
+ catch (XScreenOpenFailure& e) {
+ LOG((CLOG_CRIT "failed to start server: %s", e.what()));
+ closePrimaryClient(primaryClient);
+ closeServerScreen(serverScreen);
+ return false;
+ }
+ catch (XBase& e) {
+ LOG((CLOG_CRIT "failed to start server: %s", e.what()));
+ closePrimaryClient(primaryClient);
+ closeServerScreen(serverScreen);
+ return false;
+ }
+
+ if (args().m_restartable) {
+ // install a timer and handler to retry later
+ assert(m_timer == NULL);
+ LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
+ m_timer = m_events->newOneShotTimer(retryTime, NULL);
+ m_events->adoptHandler(Event::kTimer, m_timer,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::retryHandler));
+ m_serverState = kInitializing;
+ return true;
+ }
+ else {
+ // don't try again
+ return false;
+ }
+}
+
+barrier::Screen*
+ServerApp::openServerScreen()
+{
+ barrier::Screen* screen = createScreen();
+ screen->setEnableDragDrop(argsBase().m_enableDragDrop);
+ m_events->adoptHandler(m_events->forIScreen().error(),
+ screen->getEventTarget(),
+ new TMethodEventJob<ServerApp>(
+ this, &ServerApp::handleScreenError));
+ m_events->adoptHandler(m_events->forIScreen().suspend(),
+ screen->getEventTarget(),
+ new TMethodEventJob<ServerApp>(
+ this, &ServerApp::handleSuspend));
+ m_events->adoptHandler(m_events->forIScreen().resume(),
+ screen->getEventTarget(),
+ new TMethodEventJob<ServerApp>(
+ this, &ServerApp::handleResume));
+ return screen;
+}
+
+bool
+ServerApp::startServer()
+{
+ // skip if already started or starting
+ if (m_serverState == kStarting || m_serverState == kStarted) {
+ return true;
+ }
+
+ // initialize if necessary
+ if (m_serverState != kInitialized) {
+ if (!initServer()) {
+ // hard initialization failure
+ return false;
+ }
+ if (m_serverState == kInitializing) {
+ // not ready to start
+ m_serverState = kInitializingToStart;
+ return true;
+ }
+ assert(m_serverState == kInitialized);
+ }
+
+ double retryTime;
+ ClientListener* listener = NULL;
+ try {
+ listener = openClientListener(args().m_config->getBarrierAddress());
+ m_server = openServer(*args().m_config, m_primaryClient);
+ listener->setServer(m_server);
+ m_server->setListener(listener);
+ m_listener = listener;
+ updateStatus();
+ LOG((CLOG_NOTE "started server, waiting for clients"));
+ m_serverState = kStarted;
+ return true;
+ }
+ catch (XSocketAddressInUse& e) {
+ LOG((CLOG_WARN "cannot listen for clients: %s", e.what()));
+ closeClientListener(listener);
+ updateStatus(String("cannot listen for clients: ") + e.what());
+ retryTime = 10.0;
+ }
+ catch (XBase& e) {
+ LOG((CLOG_CRIT "failed to start server: %s", e.what()));
+ closeClientListener(listener);
+ return false;
+ }
+
+ if (args().m_restartable) {
+ // install a timer and handler to retry later
+ assert(m_timer == NULL);
+ LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
+ m_timer = m_events->newOneShotTimer(retryTime, NULL);
+ m_events->adoptHandler(Event::kTimer, m_timer,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::retryHandler));
+ m_serverState = kStarting;
+ return true;
+ }
+ else {
+ // don't try again
+ return false;
+ }
+}
+
+barrier::Screen*
+ServerApp::createScreen()
+{
+#if WINAPI_MSWINDOWS
+ return new barrier::Screen(new MSWindowsScreen(
+ true, args().m_noHooks, args().m_stopOnDeskSwitch, m_events), m_events);
+#elif WINAPI_XWINDOWS
+ return new barrier::Screen(new XWindowsScreen(
+ args().m_display, true, args().m_disableXInitThreads, 0, m_events), m_events);
+#elif WINAPI_CARBON
+ return new barrier::Screen(new OSXScreen(m_events, true), m_events);
+#endif
+}
+
+PrimaryClient*
+ServerApp::openPrimaryClient(const String& name, barrier::Screen* screen)
+{
+ LOG((CLOG_DEBUG1 "creating primary screen"));
+ return new PrimaryClient(name, screen);
+
+}
+
+void
+ServerApp::handleScreenError(const Event&, void*)
+{
+ LOG((CLOG_CRIT "error on screen"));
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+void
+ServerApp::handleSuspend(const Event&, void*)
+{
+ if (!m_suspended) {
+ LOG((CLOG_INFO "suspend"));
+ stopServer();
+ m_suspended = true;
+ }
+}
+
+void
+ServerApp::handleResume(const Event&, void*)
+{
+ if (m_suspended) {
+ LOG((CLOG_INFO "resume"));
+ startServer();
+ m_suspended = false;
+ }
+}
+
+ClientListener*
+ServerApp::openClientListener(const NetworkAddress& address)
+{
+ ClientListener* listen = new ClientListener(
+ address,
+ new TCPSocketFactory(m_events, getSocketMultiplexer()),
+ m_events,
+ args().m_enableCrypto);
+
+ m_events->adoptHandler(
+ m_events->forClientListener().connected(), listen,
+ new TMethodEventJob<ServerApp>(
+ this, &ServerApp::handleClientConnected, listen));
+
+ return listen;
+}
+
+Server*
+ServerApp::openServer(Config& config, PrimaryClient* primaryClient)
+{
+ Server* server = new Server(config, primaryClient, m_serverScreen, m_events, args());
+ try {
+ m_events->adoptHandler(
+ m_events->forServer().disconnected(), server,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::handleNoClients));
+
+ m_events->adoptHandler(
+ m_events->forServer().screenSwitched(), server,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::handleScreenSwitched));
+
+ } catch (std::bad_alloc &ba) {
+ delete server;
+ throw ba;
+ }
+
+ return server;
+}
+
+void
+ServerApp::handleNoClients(const Event&, void*)
+{
+ updateStatus();
+}
+
+void
+ServerApp::handleScreenSwitched(const Event& e, void*)
+{
+}
+
+int
+ServerApp::mainLoop()
+{
+ // create socket multiplexer. this must happen after daemonization
+ // on unix because threads evaporate across a fork().
+ SocketMultiplexer multiplexer;
+ setSocketMultiplexer(&multiplexer);
+
+ // if configuration has no screens then add this system
+ // as the default
+ if (args().m_config->begin() == args().m_config->end()) {
+ args().m_config->addScreen(args().m_name);
+ }
+
+ // set the contact address, if provided, in the config.
+ // otherwise, if the config doesn't have an address, use
+ // the default.
+ if (m_barrierAddress->isValid()) {
+ args().m_config->setBarrierAddress(*m_barrierAddress);
+ }
+ else if (!args().m_config->getBarrierAddress().isValid()) {
+ args().m_config->setBarrierAddress(NetworkAddress(kDefaultPort));
+ }
+
+ // canonicalize the primary screen name
+ String primaryName = args().m_config->getCanonicalName(args().m_name);
+ if (primaryName.empty()) {
+ LOG((CLOG_CRIT "unknown screen name `%s'", args().m_name.c_str()));
+ return kExitFailed;
+ }
+
+ // start server, etc
+ appUtil().startNode();
+
+ // init ipc client after node start, since create a new screen wipes out
+ // the event queue (the screen ctors call adoptBuffer).
+ if (argsBase().m_enableIpc) {
+ initIpcClient();
+ }
+
+ // handle hangup signal by reloading the server's configuration
+ ARCH->setSignalHandler(Arch::kHANGUP, &reloadSignalHandler, NULL);
+ m_events->adoptHandler(m_events->forServerApp().reloadConfig(),
+ m_events->getSystemTarget(),
+ new TMethodEventJob<ServerApp>(this, &ServerApp::reloadConfig));
+
+ // handle force reconnect event by disconnecting clients. they'll
+ // reconnect automatically.
+ m_events->adoptHandler(m_events->forServerApp().forceReconnect(),
+ m_events->getSystemTarget(),
+ new TMethodEventJob<ServerApp>(this, &ServerApp::forceReconnect));
+
+ // to work around the sticky meta keys problem, we'll give users
+ // the option to reset the state of barriers
+ m_events->adoptHandler(m_events->forServerApp().resetServer(),
+ m_events->getSystemTarget(),
+ new TMethodEventJob<ServerApp>(this, &ServerApp::resetServer));
+
+ // run event loop. if startServer() failed we're supposed to retry
+ // later. the timer installed by startServer() will take care of
+ // that.
+ DAEMON_RUNNING(true);
+
+#if defined(MAC_OS_X_VERSION_10_7)
+
+ Thread thread(
+ new TMethodJob<ServerApp>(
+ this, &ServerApp::runEventsLoop,
+ NULL));
+
+ // wait until carbon loop is ready
+ OSXScreen* screen = dynamic_cast<OSXScreen*>(
+ m_serverScreen->getPlatformScreen());
+ screen->waitForCarbonLoop();
+
+ runCocoaApp();
+#else
+ m_events->loop();
+#endif
+
+ DAEMON_RUNNING(false);
+
+ // close down
+ LOG((CLOG_DEBUG1 "stopping server"));
+ m_events->removeHandler(m_events->forServerApp().forceReconnect(),
+ m_events->getSystemTarget());
+ m_events->removeHandler(m_events->forServerApp().reloadConfig(),
+ m_events->getSystemTarget());
+ cleanupServer();
+ updateStatus();
+ LOG((CLOG_NOTE "stopped server"));
+
+ if (argsBase().m_enableIpc) {
+ cleanupIpcClient();
+ }
+
+ return kExitSuccess;
+}
+
+void ServerApp::resetServer(const Event&, void*)
+{
+ LOG((CLOG_DEBUG1 "resetting server"));
+ stopServer();
+ cleanupServer();
+ startServer();
+}
+
+int
+ServerApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
+{
+ // general initialization
+ m_barrierAddress = new NetworkAddress;
+ args().m_config = new Config(m_events);
+ args().m_pname = ARCH->getBasename(argv[0]);
+
+ // install caller's output filter
+ if (outputter != NULL) {
+ CLOG->insert(outputter);
+ }
+
+ // run
+ int result = startup(argc, argv);
+
+ if (m_taskBarReceiver)
+ {
+ // done with task bar receiver
+ delete m_taskBarReceiver;
+ }
+
+ delete args().m_config;
+ delete m_barrierAddress;
+ return result;
+}
+
+int daemonMainLoopStatic(int argc, const char** argv) {
+ return ServerApp::instance().daemonMainLoop(argc, argv);
+}
+
+int
+ServerApp::standardStartup(int argc, char** argv)
+{
+ initApp(argc, argv);
+
+ // daemonize if requested
+ if (args().m_daemon) {
+ return ARCH->daemonize(daemonName(), daemonMainLoopStatic);
+ }
+ else {
+ return mainLoop();
+ }
+}
+
+int
+ServerApp::foregroundStartup(int argc, char** argv)
+{
+ initApp(argc, argv);
+
+ // never daemonize
+ return mainLoop();
+}
+
+const char*
+ServerApp::daemonName() const
+{
+#if SYSAPI_WIN32
+ return "Barrier Server";
+#elif SYSAPI_UNIX
+ return "barriers";
+#endif
+}
+
+const char*
+ServerApp::daemonInfo() const
+{
+#if SYSAPI_WIN32
+ return "Shares this computers mouse and keyboard with other computers.";
+#elif SYSAPI_UNIX
+ return "";
+#endif
+}
+
+void
+ServerApp::startNode()
+{
+ // start the server. if this return false then we've failed and
+ // we shouldn't retry.
+ LOG((CLOG_DEBUG1 "starting server"));
+ if (!startServer()) {
+ m_bye(kExitFailed);
+ }
+}
diff --git a/src/lib/barrier/ServerApp.h b/src/lib/barrier/ServerApp.h
new file mode 100644
index 0000000..528aa24
--- /dev/null
+++ b/src/lib/barrier/ServerApp.h
@@ -0,0 +1,127 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/ArgsBase.h"
+#include "barrier/App.h"
+#include "base/String.h"
+#include "server/Config.h"
+#include "net/NetworkAddress.h"
+#include "arch/Arch.h"
+#include "arch/IArchMultithread.h"
+#include "barrier/ArgsBase.h"
+#include "base/EventTypes.h"
+
+#include <map>
+
+enum EServerState {
+ kUninitialized,
+ kInitializing,
+ kInitializingToStart,
+ kInitialized,
+ kStarting,
+ kStarted
+};
+
+class Server;
+namespace barrier { class Screen; }
+class ClientListener;
+class EventQueueTimer;
+class ILogOutputter;
+class IEventQueue;
+class ServerArgs;
+
+class ServerApp : public App {
+public:
+ ServerApp(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver);
+ virtual ~ServerApp();
+
+ // Parse server specific command line arguments.
+ void parseArgs(int argc, const char* const* argv);
+
+ // Prints help specific to server.
+ void help();
+
+ // Returns arguments that are common and for server.
+ ServerArgs& args() const { return (ServerArgs&)argsBase(); }
+
+ const char* daemonName() const;
+ const char* daemonInfo() const;
+
+ // TODO: Document these functions.
+ static void reloadSignalHandler(Arch::ESignal, void*);
+
+ 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);
+ void handleClientsDisconnected(const Event&, void*);
+ void closeServer(Server* server);
+ void stopRetryTimer();
+ void updateStatus();
+ void updateStatus(const String& msg);
+ void closeClientListener(ClientListener* listen);
+ void stopServer();
+ void closePrimaryClient(PrimaryClient* primaryClient);
+ void closeServerScreen(barrier::Screen* screen);
+ void cleanupServer();
+ bool initServer();
+ void retryHandler(const Event&, void*);
+ barrier::Screen* openServerScreen();
+ barrier::Screen* createScreen();
+ PrimaryClient* openPrimaryClient(const String& name, barrier::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);
+ 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();
+
+ static ServerApp& instance() { return (ServerApp&)App::instance(); }
+
+ Server* getServerPtr() { return m_server; }
+
+ Server* m_server;
+ EServerState m_serverState;
+ barrier::Screen* m_serverScreen;
+ PrimaryClient* m_primaryClient;
+ ClientListener* m_listener;
+ EventQueueTimer* m_timer;
+ NetworkAddress* m_barrierAddress;
+
+private:
+ void handleScreenSwitched(const Event&, void* data);
+};
+
+// configuration file name
+#if SYSAPI_WIN32
+#define USR_CONFIG_NAME "barrier.sgc"
+#define SYS_CONFIG_NAME "barrier.sgc"
+#elif SYSAPI_UNIX
+#define USR_CONFIG_NAME ".barrier.conf"
+#define SYS_CONFIG_NAME "barrier.conf"
+#endif
diff --git a/src/lib/barrier/ServerArgs.cpp b/src/lib/barrier/ServerArgs.cpp
new file mode 100644
index 0000000..49832f2
--- /dev/null
+++ b/src/lib/barrier/ServerArgs.cpp
@@ -0,0 +1,25 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "barrier/ServerArgs.h"
+
+ServerArgs::ServerArgs() :
+ m_configFile(),
+ m_config(NULL)
+{
+}
+
diff --git a/src/lib/barrier/ServerArgs.h b/src/lib/barrier/ServerArgs.h
new file mode 100644
index 0000000..9c6e568
--- /dev/null
+++ b/src/lib/barrier/ServerArgs.h
@@ -0,0 +1,32 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "barrier/ArgsBase.h"
+
+class NetworkAddress;
+class Config;
+
+class ServerArgs : public ArgsBase {
+public:
+ ServerArgs();
+
+public:
+ String m_configFile;
+ Config* m_config;
+};
diff --git a/src/lib/barrier/ServerTaskBarReceiver.cpp b/src/lib/barrier/ServerTaskBarReceiver.cpp
new file mode 100644
index 0000000..b427cd1
--- /dev/null
+++ b/src/lib/barrier/ServerTaskBarReceiver.cpp
@@ -0,0 +1,138 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 Chris Schoeneman
+ *
+ * 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 "barrier/ServerTaskBarReceiver.h"
+#include "server/Server.h"
+#include "mt/Lock.h"
+#include "base/String.h"
+#include "base/IEventQueue.h"
+#include "arch/Arch.h"
+#include "common/Version.h"
+
+//
+// ServerTaskBarReceiver
+//
+
+ServerTaskBarReceiver::ServerTaskBarReceiver(IEventQueue* events) :
+ m_state(kNotRunning),
+ m_events(events)
+{
+ // do nothing
+}
+
+ServerTaskBarReceiver::~ServerTaskBarReceiver()
+{
+ // do nothing
+}
+
+void
+ServerTaskBarReceiver::updateStatus(Server* server, const String& errorMsg)
+{
+ {
+ // update our status
+ m_errorMessage = errorMsg;
+ if (server == NULL) {
+ if (m_errorMessage.empty()) {
+ m_state = kNotRunning;
+ }
+ else {
+ m_state = kNotWorking;
+ }
+ }
+ else {
+ m_clients.clear();
+ server->getClients(m_clients);
+ if (m_clients.size() <= 1) {
+ m_state = kNotConnected;
+ }
+ else {
+ m_state = kConnected;
+ }
+ }
+
+ // let subclasses have a go
+ onStatusChanged(server);
+ }
+
+ // tell task bar
+ ARCH->updateReceiver(this);
+}
+
+ServerTaskBarReceiver::EState
+ServerTaskBarReceiver::getStatus() const
+{
+ return m_state;
+}
+
+const String&
+ServerTaskBarReceiver::getErrorMessage() const
+{
+ return m_errorMessage;
+}
+
+const ServerTaskBarReceiver::Clients&
+ServerTaskBarReceiver::getClients() const
+{
+ return m_clients;
+}
+
+void
+ServerTaskBarReceiver::quit()
+{
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+void
+ServerTaskBarReceiver::onStatusChanged(Server*)
+{
+ // do nothing
+}
+
+void
+ServerTaskBarReceiver::lock() const
+{
+ // do nothing
+}
+
+void
+ServerTaskBarReceiver::unlock() const
+{
+ // do nothing
+}
+
+std::string
+ServerTaskBarReceiver::getToolTip() const
+{
+ switch (m_state) {
+ case kNotRunning:
+ return barrier::string::sprintf("%s: Not running", kAppVersion);
+
+ case kNotWorking:
+ return barrier::string::sprintf("%s: %s",
+ kAppVersion, m_errorMessage.c_str());
+
+ case kNotConnected:
+ return barrier::string::sprintf("%s: Waiting for clients", kAppVersion);
+
+ case kConnected:
+ return barrier::string::sprintf("%s: Connected", kAppVersion);
+
+ default:
+ return "";
+ }
+}
diff --git a/src/lib/barrier/ServerTaskBarReceiver.h b/src/lib/barrier/ServerTaskBarReceiver.h
new file mode 100644
index 0000000..3cef9c0
--- /dev/null
+++ b/src/lib/barrier/ServerTaskBarReceiver.h
@@ -0,0 +1,98 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 Chris Schoeneman
+ *
+ * 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 "server/Server.h"
+#include "barrier/ServerApp.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "base/EventTypes.h"
+#include "base/String.h"
+#include "base/Event.h"
+#include "common/stdvector.h"
+
+class IEventQueue;
+
+//! Implementation of IArchTaskBarReceiver for the barrier server
+class ServerTaskBarReceiver : public IArchTaskBarReceiver {
+public:
+ ServerTaskBarReceiver(IEventQueue* events);
+ virtual ~ServerTaskBarReceiver();
+
+ //! @name manipulators
+ //@{
+
+ //! Update status
+ /*!
+ Determine the status and query required information from the server.
+ */
+ void updateStatus(Server*, const String& errorMsg);
+
+ void updateStatus(INode* n, const String& errorMsg) { updateStatus((Server*)n, errorMsg); }
+
+ //@}
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus() = 0;
+ virtual void runMenu(int x, int y) = 0;
+ virtual void primaryAction() = 0;
+ virtual void lock() const;
+ virtual void unlock() const;
+ virtual const Icon getIcon() const = 0;
+ virtual std::string getToolTip() const;
+
+protected:
+ typedef std::vector<String> Clients;
+ enum EState {
+ kNotRunning,
+ kNotWorking,
+ kNotConnected,
+ kConnected,
+ kMaxState
+ };
+
+ //! Get status
+ EState getStatus() const;
+
+ //! Get error message
+ const String& getErrorMessage() const;
+
+ //! Get connected clients
+ const Clients& getClients() const;
+
+ //! Quit app
+ /*!
+ Causes the application to quit gracefully
+ */
+ void quit();
+
+ //! Status change notification
+ /*!
+ Called when status changes. The default implementation does
+ nothing.
+ */
+ virtual void onStatusChanged(Server* server);
+
+private:
+ EState m_state;
+ String m_errorMessage;
+ Clients m_clients;
+ IEventQueue* m_events;
+};
+
+IArchTaskBarReceiver* createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events);
diff --git a/src/lib/barrier/StreamChunker.cpp b/src/lib/barrier/StreamChunker.cpp
new file mode 100644
index 0000000..8b8971c
--- /dev/null
+++ b/src/lib/barrier/StreamChunker.cpp
@@ -0,0 +1,166 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-2016 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 "barrier/StreamChunker.h"
+
+#include "mt/Lock.h"
+#include "mt/Mutex.h"
+#include "barrier/FileChunk.h"
+#include "barrier/ClipboardChunk.h"
+#include "barrier/protocol_types.h"
+#include "base/EventTypes.h"
+#include "base/Event.h"
+#include "base/IEventQueue.h"
+#include "base/EventTypes.h"
+#include "base/Log.h"
+#include "base/Stopwatch.h"
+#include "base/String.h"
+#include "common/stdexcept.h"
+
+#include <fstream>
+
+using namespace std;
+
+static const size_t g_chunkSize = 32 * 1024; //32kb
+
+bool StreamChunker::s_isChunkingFile = false;
+bool StreamChunker::s_interruptFile = false;
+Mutex* StreamChunker::s_interruptMutex = NULL;
+
+void
+StreamChunker::sendFile(
+ char* filename,
+ IEventQueue* events,
+ void* eventTarget)
+{
+ s_isChunkingFile = true;
+
+ std::fstream file(static_cast<char*>(filename), std::ios::in | std::ios::binary);
+
+ if (!file.is_open()) {
+ throw runtime_error("failed to open file");
+ }
+
+ // check file size
+ file.seekg (0, std::ios::end);
+ size_t size = (size_t)file.tellg();
+
+ // send first message (file size)
+ String fileSize = barrier::string::sizeTypeToString(size);
+ FileChunk* sizeMessage = FileChunk::start(fileSize);
+
+ events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, sizeMessage));
+
+ // send chunk messages with a fixed chunk size
+ size_t sentLength = 0;
+ size_t chunkSize = g_chunkSize;
+ file.seekg (0, std::ios::beg);
+
+ while (true) {
+ if (s_interruptFile) {
+ s_interruptFile = false;
+ LOG((CLOG_DEBUG "file transmission interrupted"));
+ break;
+ }
+
+ events->addEvent(Event(events->forFile().keepAlive(), eventTarget));
+
+ // make sure we don't read too much from the mock data.
+ if (sentLength + chunkSize > size) {
+ chunkSize = size - sentLength;
+ }
+
+ char* chunkData = new char[chunkSize];
+ file.read(chunkData, chunkSize);
+ UInt8* data = reinterpret_cast<UInt8*>(chunkData);
+ FileChunk* fileChunk = FileChunk::data(data, chunkSize);
+ delete[] chunkData;
+
+ events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, fileChunk));
+
+ sentLength += chunkSize;
+ file.seekg (sentLength, std::ios::beg);
+
+ if (sentLength == size) {
+ break;
+ }
+ }
+
+ // send last message
+ FileChunk* end = FileChunk::end();
+
+ events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, end));
+
+ file.close();
+
+ s_isChunkingFile = false;
+}
+
+void
+StreamChunker::sendClipboard(
+ String& data,
+ size_t size,
+ ClipboardID id,
+ UInt32 sequence,
+ IEventQueue* events,
+ void* eventTarget)
+{
+ // send first message (data size)
+ String dataSize = barrier::string::sizeTypeToString(size);
+ ClipboardChunk* sizeMessage = ClipboardChunk::start(id, sequence, dataSize);
+
+ events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, sizeMessage));
+
+ // send clipboard chunk with a fixed size
+ size_t sentLength = 0;
+ size_t chunkSize = g_chunkSize;
+
+ while (true) {
+ events->addEvent(Event(events->forFile().keepAlive(), eventTarget));
+
+ // make sure we don't read too much from the mock data.
+ if (sentLength + chunkSize > size) {
+ chunkSize = size - sentLength;
+ }
+
+ String chunk(data.substr(sentLength, chunkSize).c_str(), chunkSize);
+ ClipboardChunk* dataChunk = ClipboardChunk::data(id, sequence, chunk);
+
+ events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, dataChunk));
+
+ sentLength += chunkSize;
+ if (sentLength == size) {
+ break;
+ }
+ }
+
+ // send last message
+ ClipboardChunk* end = ClipboardChunk::end(id, sequence);
+
+ events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, end));
+
+ LOG((CLOG_DEBUG "sent clipboard size=%d", sentLength));
+}
+
+void
+StreamChunker::interruptFile()
+{
+ if (s_isChunkingFile) {
+ s_interruptFile = true;
+ LOG((CLOG_INFO "previous dragged file has become invalid"));
+ }
+}
diff --git a/src/lib/barrier/StreamChunker.h b/src/lib/barrier/StreamChunker.h
new file mode 100644
index 0000000..ab57c7e
--- /dev/null
+++ b/src/lib/barrier/StreamChunker.h
@@ -0,0 +1,45 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-2016 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 "barrier/clipboard_types.h"
+#include "base/String.h"
+
+class IEventQueue;
+class Mutex;
+
+class StreamChunker {
+public:
+ static void sendFile(
+ char* filename,
+ IEventQueue* events,
+ void* eventTarget);
+ static void sendClipboard(
+ String& data,
+ size_t size,
+ ClipboardID id,
+ UInt32 sequence,
+ IEventQueue* events,
+ void* eventTarget);
+ static void interruptFile();
+
+private:
+ static bool s_isChunkingFile;
+ static bool s_interruptFile;
+ static Mutex* s_interruptMutex;
+};
diff --git a/src/lib/barrier/ToolApp.cpp b/src/lib/barrier/ToolApp.cpp
new file mode 100644
index 0000000..ae85e6d
--- /dev/null
+++ b/src/lib/barrier/ToolApp.cpp
@@ -0,0 +1,205 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "barrier/ToolApp.h"
+
+#include "barrier/ArgParser.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+#include "base/String.h"
+
+#include <iostream>
+#include <sstream>
+
+#if SYSAPI_WIN32
+#include "platform/MSWindowsSession.h"
+#endif
+
+#define JSON_URL "https://symless.com/account/json/"
+
+enum {
+ kErrorOk,
+ kErrorArgs,
+ kErrorException,
+ kErrorUnknown
+};
+
+UInt32
+ToolApp::run(int argc, char** argv)
+{
+ if (argc <= 1) {
+ std::cerr << "no args" << std::endl;
+ return kErrorArgs;
+ }
+
+ try {
+ ArgParser argParser(this);
+ bool result = argParser.parseToolArgs(m_args, argc, argv);
+
+ if (!result) {
+ m_bye(kExitArgs);
+ }
+
+ if (m_args.m_printActiveDesktopName) {
+#if SYSAPI_WIN32
+ MSWindowsSession session;
+ String name = session.getActiveDesktopName();
+ if (name.empty()) {
+ LOG((CLOG_CRIT "failed to get active desktop name"));
+ return kExitFailed;
+ }
+ else {
+ String output = barrier::string::sprintf("activeDesktop:%s", name.c_str());
+ LOG((CLOG_INFO "%s", output.c_str()));
+ }
+#endif
+ }
+ else if (m_args.m_loginAuthenticate) {
+ loginAuth();
+ }
+ else if (m_args.m_getInstalledDir) {
+ std::cout << ARCH->getInstalledDirectory() << std::endl;
+ }
+ else if (m_args.m_getProfileDir) {
+ std::cout << ARCH->getProfileDirectory() << std::endl;
+ }
+ else if (m_args.m_getArch) {
+ std::cout << ARCH->getPlatformName() << std::endl;
+ }
+ else if (m_args.m_notifyUpdate) {
+ notifyUpdate();
+ }
+ else if (m_args.m_notifyActivation) {
+ notifyActivation();
+ }
+ else {
+ throw XBarrier("Nothing to do");
+ }
+ }
+ catch (std::exception& e) {
+ LOG((CLOG_CRIT "An error occurred: %s\n", e.what()));
+ return kExitFailed;
+ }
+ catch (...) {
+ LOG((CLOG_CRIT "An unknown error occurred.\n"));
+ return kExitFailed;
+ }
+
+#if WINAPI_XWINDOWS
+ // HACK: avoid sigsegv on linux
+ m_bye(kErrorOk);
+#endif
+
+ return kErrorOk;
+}
+
+void
+ToolApp::help()
+{
+}
+
+void
+ToolApp::loginAuth()
+{
+ String credentials;
+ std::cin >> credentials;
+
+ std::vector<String> parts = barrier::string::splitString(credentials, ':');
+ size_t count = parts.size();
+
+ if (count == 2 ) {
+ String email = parts[0];
+ String password = parts[1];
+
+ std::stringstream ss;
+ ss << JSON_URL << "auth/";
+ ss << "?email=" << ARCH->internet().urlEncode(email);
+ ss << "&password=" << password;
+
+ std::cout << ARCH->internet().get(ss.str()) << std::endl;
+ }
+ else {
+ throw XBarrier("Invalid credentials.");
+ }
+}
+
+void
+ToolApp::notifyUpdate()
+{
+ String data;
+ std::cin >> data;
+
+ std::vector<String> parts = barrier::string::splitString(data, ':');
+ size_t count = parts.size();
+
+ if (count == 3) {
+ std::stringstream ss;
+ ss << JSON_URL << "notify/update";
+ ss << "?from=" << parts[0];
+ ss << "&to=" << parts[1];
+
+ std::cout << ARCH->internet().get(ss.str()) << std::endl;
+ }
+ else {
+ throw XBarrier("Invalid update data.");
+ }
+}
+
+void
+ToolApp::notifyActivation()
+{
+ String info;
+ std::cin >> info;
+
+ std::vector<String> parts = barrier::string::splitString(info, ':');
+ size_t count = parts.size();
+
+ if (count == 3 || count == 4) {
+ String action = parts[0];
+ String identity = parts[1];
+ String macHash = parts[2];
+ String os;
+
+ if (count == 4) {
+ os = parts[3];
+ }
+ else {
+ os = ARCH->getOSName();
+ }
+
+ std::stringstream ss;
+ ss << JSON_URL << "notify/";
+ ss << "?action=" << action;
+ ss << "&identity=" << ARCH->internet().urlEncode(identity);
+ ss << "&mac=" << ARCH->internet().urlEncode(macHash);
+ ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName());
+ ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName());
+
+ try {
+ std::cout << ARCH->internet().get(ss.str()) << std::endl;
+ }
+ catch (std::exception& e) {
+ LOG((CLOG_NOTE "An error occurred during notification: %s\n", e.what()));
+ }
+ catch (...) {
+ LOG((CLOG_NOTE "An unknown error occurred during notification.\n"));
+ }
+ }
+ else {
+ LOG((CLOG_NOTE "notification failed"));
+ }
+}
diff --git a/src/lib/barrier/ToolApp.h b/src/lib/barrier/ToolApp.h
new file mode 100644
index 0000000..5cb9a7c
--- /dev/null
+++ b/src/lib/barrier/ToolApp.h
@@ -0,0 +1,37 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "barrier/App.h"
+#include "barrier/ToolArgs.h"
+#include "common/basic_types.h"
+
+class ToolApp : public MinimalApp
+{
+public:
+ UInt32 run(int argc, char** argv);
+ void help();
+
+private:
+ void loginAuth();
+ void notifyActivation();
+ void notifyUpdate();
+
+private:
+ ToolArgs m_args;
+};
diff --git a/src/lib/barrier/ToolArgs.cpp b/src/lib/barrier/ToolArgs.cpp
new file mode 100644
index 0000000..634a784
--- /dev/null
+++ b/src/lib/barrier/ToolArgs.cpp
@@ -0,0 +1,29 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "barrier/ToolArgs.h"
+
+ToolArgs::ToolArgs() :
+ m_printActiveDesktopName(false),
+ m_loginAuthenticate(false),
+ m_getInstalledDir(false),
+ m_getProfileDir(false),
+ m_getArch(false),
+ m_notifyActivation(false),
+ m_notifyUpdate(false)
+{
+}
diff --git a/src/lib/barrier/ToolArgs.h b/src/lib/barrier/ToolArgs.h
new file mode 100644
index 0000000..36b4be3
--- /dev/null
+++ b/src/lib/barrier/ToolArgs.h
@@ -0,0 +1,34 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 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 "base/String.h"
+
+class ToolArgs {
+public:
+ ToolArgs();
+
+public:
+ bool m_printActiveDesktopName;
+ bool m_loginAuthenticate;
+ bool m_getInstalledDir;
+ bool m_getProfileDir;
+ bool m_getArch;
+ bool m_notifyActivation;
+ bool m_notifyUpdate;
+};
diff --git a/src/lib/barrier/XBarrier.cpp b/src/lib/barrier/XBarrier.cpp
new file mode 100644
index 0000000..49a015e
--- /dev/null
+++ b/src/lib/barrier/XBarrier.cpp
@@ -0,0 +1,133 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/XBarrier.h"
+#include "base/String.h"
+
+//
+// XBadClient
+//
+
+String
+XBadClient::getWhat() const throw()
+{
+ return "XBadClient";
+}
+
+
+//
+// XIncompatibleClient
+//
+
+XIncompatibleClient::XIncompatibleClient(int major, int minor) :
+ m_major(major),
+ m_minor(minor)
+{
+ // do nothing
+}
+
+int
+XIncompatibleClient::getMajor() const throw()
+{
+ return m_major;
+}
+
+int
+XIncompatibleClient::getMinor() const throw()
+{
+ return m_minor;
+}
+
+String
+XIncompatibleClient::getWhat() const throw()
+{
+ return format("XIncompatibleClient", "incompatible client %{1}.%{2}",
+ barrier::string::sprintf("%d", m_major).c_str(),
+ barrier::string::sprintf("%d", m_minor).c_str());
+}
+
+
+//
+// XDuplicateClient
+//
+
+XDuplicateClient::XDuplicateClient(const String& name) :
+ m_name(name)
+{
+ // do nothing
+}
+
+const String&
+XDuplicateClient::getName() const throw()
+{
+ return m_name;
+}
+
+String
+XDuplicateClient::getWhat() const throw()
+{
+ return format("XDuplicateClient", "duplicate client %{1}", m_name.c_str());
+}
+
+
+//
+// XUnknownClient
+//
+
+XUnknownClient::XUnknownClient(const String& name) :
+ m_name(name)
+{
+ // do nothing
+}
+
+const String&
+XUnknownClient::getName() const throw()
+{
+ return m_name;
+}
+
+String
+XUnknownClient::getWhat() const throw()
+{
+ return format("XUnknownClient", "unknown client %{1}", m_name.c_str());
+}
+
+
+//
+// XExitApp
+//
+
+XExitApp::XExitApp(int code) :
+ m_code(code)
+{
+ // do nothing
+}
+
+int
+XExitApp::getCode() const throw()
+{
+ return m_code;
+}
+
+String
+XExitApp::getWhat() const throw()
+{
+ return format(
+ "XExitApp", "exiting with code %{1}",
+ barrier::string::sprintf("%d", m_code).c_str());
+}
diff --git a/src/lib/barrier/XBarrier.h b/src/lib/barrier/XBarrier.h
new file mode 100644
index 0000000..fdf5213
--- /dev/null
+++ b/src/lib/barrier/XBarrier.h
@@ -0,0 +1,135 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "base/XBase.h"
+
+//! Generic barrier exception
+XBASE_SUBCLASS(XBarrier, XBase);
+
+//! Subscription error
+/*!
+Thrown when there is a problem with the subscription.
+*/
+XBASE_SUBCLASS(XSubscription, XBarrier);
+
+//! Client error exception
+/*!
+Thrown when the client fails to follow the protocol.
+*/
+XBASE_SUBCLASS_WHAT(XBadClient, XBarrier);
+
+//! Incompatible client exception
+/*!
+Thrown when a client attempting to connect has an incompatible version.
+*/
+class XIncompatibleClient : public XBarrier {
+public:
+ XIncompatibleClient(int major, int minor);
+
+ //! @name accessors
+ //@{
+
+ //! Get client's major version number
+ int getMajor() const throw();
+ //! Get client's minor version number
+ int getMinor() const throw();
+
+ //@}
+
+protected:
+ virtual String getWhat() const throw();
+
+private:
+ int m_major;
+ int m_minor;
+};
+
+//! Client already connected exception
+/*!
+Thrown when a client attempting to connect is using the same name as
+a client that is already connected.
+*/
+class XDuplicateClient : public XBarrier {
+public:
+ XDuplicateClient(const String& name);
+ virtual ~XDuplicateClient() _NOEXCEPT { }
+
+ //! @name accessors
+ //@{
+
+ //! Get client's name
+ virtual const String&
+ getName() const throw();
+
+ //@}
+
+protected:
+ virtual String getWhat() const throw();
+
+private:
+ String m_name;
+};
+
+//! Client not in map exception
+/*!
+Thrown when a client attempting to connect is using a name that is
+unknown to the server.
+*/
+class XUnknownClient : public XBarrier {
+public:
+ XUnknownClient(const String& name);
+ virtual ~XUnknownClient() _NOEXCEPT { }
+
+ //! @name accessors
+ //@{
+
+ //! Get the client's name
+ virtual const String&
+ getName() const throw();
+
+ //@}
+
+protected:
+ virtual String getWhat() const throw();
+
+private:
+ String m_name;
+};
+
+//! Generic exit eception
+/*!
+Thrown when we want to abort, with the opportunity to clean up. This is a
+little bit of a hack, but it's a better way of exiting, than just calling
+exit(int).
+*/
+class XExitApp : public XBarrier {
+public:
+ XExitApp(int code);
+ virtual ~XExitApp() _NOEXCEPT { }
+
+ //! Get the exit code
+ int getCode() const throw();
+
+protected:
+ virtual String getWhat() const throw();
+
+private:
+ int m_code;
+};
diff --git a/src/lib/barrier/XScreen.cpp b/src/lib/barrier/XScreen.cpp
new file mode 100644
index 0000000..a202240
--- /dev/null
+++ b/src/lib/barrier/XScreen.cpp
@@ -0,0 +1,68 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/XScreen.h"
+
+//
+// XScreenOpenFailure
+//
+
+String
+XScreenOpenFailure::getWhat() const throw()
+{
+ return format("XScreenOpenFailure", "unable to open screen");
+}
+
+
+//
+// XScreenXInputFailure
+//
+
+String
+XScreenXInputFailure::getWhat() const throw()
+{
+ return "";
+}
+
+
+//
+// XScreenUnavailable
+//
+
+XScreenUnavailable::XScreenUnavailable(double timeUntilRetry) :
+ m_timeUntilRetry(timeUntilRetry)
+{
+ // do nothing
+}
+
+XScreenUnavailable::~XScreenUnavailable() _NOEXCEPT
+{
+ // do nothing
+}
+
+double
+XScreenUnavailable::getRetryTime() const
+{
+ return m_timeUntilRetry;
+}
+
+String
+XScreenUnavailable::getWhat() const throw()
+{
+ return format("XScreenUnavailable", "unable to open screen");
+}
diff --git a/src/lib/barrier/XScreen.h b/src/lib/barrier/XScreen.h
new file mode 100644
index 0000000..0cb511e
--- /dev/null
+++ b/src/lib/barrier/XScreen.h
@@ -0,0 +1,68 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "base/XBase.h"
+
+//! Generic screen exception
+XBASE_SUBCLASS(XScreen, XBase);
+
+//! Cannot open screen exception
+/*!
+Thrown when a screen cannot be opened or initialized.
+*/
+XBASE_SUBCLASS_WHAT(XScreenOpenFailure, XScreen);
+
+//! XInput exception
+/*!
+Thrown when an XInput error occurs
+*/
+XBASE_SUBCLASS_WHAT(XScreenXInputFailure, XScreen);
+
+//! Screen unavailable exception
+/*!
+Thrown when a screen cannot be opened or initialized but retrying later
+may be successful.
+*/
+class XScreenUnavailable : public XScreenOpenFailure {
+public:
+ /*!
+ \c timeUntilRetry is the suggested time the caller should wait until
+ trying to open the screen again.
+ */
+ XScreenUnavailable(double timeUntilRetry);
+ virtual ~XScreenUnavailable() _NOEXCEPT;
+
+ //! @name manipulators
+ //@{
+
+ //! Get retry time
+ /*!
+ Returns the suggested time to wait until retrying to open the screen.
+ */
+ double getRetryTime() const;
+
+ //@}
+
+protected:
+ virtual String getWhat() const throw();
+
+private:
+ double m_timeUntilRetry;
+};
diff --git a/src/lib/barrier/clipboard_types.h b/src/lib/barrier/clipboard_types.h
new file mode 100644
index 0000000..54f2732
--- /dev/null
+++ b/src/lib/barrier/clipboard_types.h
@@ -0,0 +1,42 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "common/basic_types.h"
+
+//! Clipboard ID
+/*!
+Type to hold a clipboard identifier.
+*/
+typedef UInt8 ClipboardID;
+
+//! @name Clipboard identifiers
+//@{
+// clipboard identifiers. kClipboardClipboard is what is normally
+// considered the clipboard (e.g. the cut/copy/paste menu items
+// affect it). kClipboardSelection is the selection on those
+// platforms that can treat the selection as a clipboard (e.g. X
+// windows). clipboard identifiers must be sequential starting
+// at zero.
+static const ClipboardID kClipboardClipboard = 0;
+static const ClipboardID kClipboardSelection = 1;
+
+// the number of clipboards (i.e. one greater than the last clipboard id)
+static const ClipboardID kClipboardEnd = 2;
+//@}
diff --git a/src/lib/barrier/key_types.cpp b/src/lib/barrier/key_types.cpp
new file mode 100644
index 0000000..902670d
--- /dev/null
+++ b/src/lib/barrier/key_types.cpp
@@ -0,0 +1,208 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/key_types.h"
+
+const KeyNameMapEntry kKeyNameMap[] = {
+ { "AltGr", kKeyAltGr },
+ { "Alt_L", kKeyAlt_L },
+ { "Alt_R", kKeyAlt_R },
+ { "AppMail", kKeyAppMail },
+ { "AppMedia", kKeyAppMedia },
+ { "AppUser1", kKeyAppUser1 },
+ { "AppUser2", kKeyAppUser2 },
+ { "AudioDown", kKeyAudioDown },
+ { "AudioMute", kKeyAudioMute },
+ { "AudioNext", kKeyAudioNext },
+ { "AudioPlay", kKeyAudioPlay },
+ { "AudioPrev", kKeyAudioPrev },
+ { "AudioStop", kKeyAudioStop },
+ { "AudioUp", kKeyAudioUp },
+ { "BackSpace", kKeyBackSpace },
+ { "Begin", kKeyBegin },
+ { "Break", kKeyBreak },
+ { "Cancel", kKeyCancel },
+ { "CapsLock", kKeyCapsLock },
+ { "Clear", kKeyClear },
+ { "Control_L", kKeyControl_L },
+ { "Control_R", kKeyControl_R },
+ { "Delete", kKeyDelete },
+ { "Down", kKeyDown },
+ { "Eject", kKeyEject },
+ { "End", kKeyEnd },
+ { "Escape", kKeyEscape },
+ { "Execute", kKeyExecute },
+ { "F1", kKeyF1 },
+ { "F2", kKeyF2 },
+ { "F3", kKeyF3 },
+ { "F4", kKeyF4 },
+ { "F5", kKeyF5 },
+ { "F6", kKeyF6 },
+ { "F7", kKeyF7 },
+ { "F8", kKeyF8 },
+ { "F9", kKeyF9 },
+ { "F10", kKeyF10 },
+ { "F11", kKeyF11 },
+ { "F12", kKeyF12 },
+ { "F13", kKeyF13 },
+ { "F14", kKeyF14 },
+ { "F15", kKeyF15 },
+ { "F16", kKeyF16 },
+ { "F17", kKeyF17 },
+ { "F18", kKeyF18 },
+ { "F19", kKeyF19 },
+ { "F20", kKeyF20 },
+ { "F21", kKeyF21 },
+ { "F22", kKeyF22 },
+ { "F23", kKeyF23 },
+ { "F24", kKeyF24 },
+ { "F25", kKeyF25 },
+ { "F26", kKeyF26 },
+ { "F27", kKeyF27 },
+ { "F28", kKeyF28 },
+ { "F29", kKeyF29 },
+ { "F30", kKeyF30 },
+ { "F31", kKeyF31 },
+ { "F32", kKeyF32 },
+ { "F33", kKeyF33 },
+ { "F34", kKeyF34 },
+ { "F35", kKeyF35 },
+ { "Find", kKeyFind },
+ { "Help", kKeyHelp },
+ { "Henkan", kKeyHenkan },
+ { "Home", kKeyHome },
+ { "Hyper_L", kKeyHyper_L },
+ { "Hyper_R", kKeyHyper_R },
+ { "Insert", kKeyInsert },
+ { "KP_0", kKeyKP_0 },
+ { "KP_1", kKeyKP_1 },
+ { "KP_2", kKeyKP_2 },
+ { "KP_3", kKeyKP_3 },
+ { "KP_4", kKeyKP_4 },
+ { "KP_5", kKeyKP_5 },
+ { "KP_6", kKeyKP_6 },
+ { "KP_7", kKeyKP_7 },
+ { "KP_8", kKeyKP_8 },
+ { "KP_9", kKeyKP_9 },
+ { "KP_Add", kKeyKP_Add },
+ { "KP_Begin", kKeyKP_Begin },
+ { "KP_Decimal", kKeyKP_Decimal },
+ { "KP_Delete", kKeyKP_Delete },
+ { "KP_Divide", kKeyKP_Divide },
+ { "KP_Down", kKeyKP_Down },
+ { "KP_End", kKeyKP_End },
+ { "KP_Enter", kKeyKP_Enter },
+ { "KP_Equal", kKeyKP_Equal },
+ { "KP_F1", kKeyKP_F1 },
+ { "KP_F2", kKeyKP_F2 },
+ { "KP_F3", kKeyKP_F3 },
+ { "KP_F4", kKeyKP_F4 },
+ { "KP_Home", kKeyKP_Home },
+ { "KP_Insert", kKeyKP_Insert },
+ { "KP_Left", kKeyKP_Left },
+ { "KP_Multiply", kKeyKP_Multiply },
+ { "KP_PageDown", kKeyKP_PageDown },
+ { "KP_PageUp", kKeyKP_PageUp },
+ { "KP_Right", kKeyKP_Right },
+ { "KP_Separator", kKeyKP_Separator },
+ { "KP_Space", kKeyKP_Space },
+ { "KP_Subtract", kKeyKP_Subtract },
+ { "KP_Tab", kKeyKP_Tab },
+ { "KP_Up", kKeyKP_Up },
+ { "Left", kKeyLeft },
+ { "LeftTab", kKeyLeftTab },
+ { "Linefeed", kKeyLinefeed },
+ { "Menu", kKeyMenu },
+ { "Meta_L", kKeyMeta_L },
+ { "Meta_R", kKeyMeta_R },
+ { "NumLock", kKeyNumLock },
+ { "PageDown", kKeyPageDown },
+ { "PageUp", kKeyPageUp },
+ { "Pause", kKeyPause },
+ { "Print", kKeyPrint },
+ { "Redo", kKeyRedo },
+ { "Return", kKeyReturn },
+ { "Right", kKeyRight },
+ { "ScrollLock", kKeyScrollLock },
+ { "Select", kKeySelect },
+ { "ShiftLock", kKeyShiftLock },
+ { "Shift_L", kKeyShift_L },
+ { "Shift_R", kKeyShift_R },
+ { "Sleep", kKeySleep },
+ { "Super_L", kKeySuper_L },
+ { "Super_R", kKeySuper_R },
+ { "SysReq", kKeySysReq },
+ { "Tab", kKeyTab },
+ { "Undo", kKeyUndo },
+ { "Up", kKeyUp },
+ { "WWWBack", kKeyWWWBack },
+ { "WWWFavorites", kKeyWWWFavorites },
+ { "WWWForward", kKeyWWWForward },
+ { "WWWHome", kKeyWWWHome },
+ { "WWWRefresh", kKeyWWWRefresh },
+ { "WWWSearch", kKeyWWWSearch },
+ { "WWWStop", kKeyWWWStop },
+ { "Zenkaku", kKeyZenkaku },
+ { "Space", 0x0020 },
+ { "Exclaim", 0x0021 },
+ { "DoubleQuote", 0x0022 },
+ { "Number", 0x0023 },
+ { "Dollar", 0x0024 },
+ { "Percent", 0x0025 },
+ { "Ampersand", 0x0026 },
+ { "Apostrophe", 0x0027 },
+ { "ParenthesisL", 0x0028 },
+ { "ParenthesisR", 0x0029 },
+ { "Asterisk", 0x002a },
+ { "Plus", 0x002b },
+ { "Comma", 0x002c },
+ { "Minus", 0x002d },
+ { "Period", 0x002e },
+ { "Slash", 0x002f },
+ { "Colon", 0x003a },
+ { "Semicolon", 0x003b },
+ { "Less", 0x003c },
+ { "Equal", 0x003d },
+ { "Greater", 0x003e },
+ { "Question", 0x003f },
+ { "At", 0x0040 },
+ { "BracketL", 0x005b },
+ { "Backslash", 0x005c },
+ { "BracketR", 0x005d },
+ { "Circumflex", 0x005e },
+ { "Underscore", 0x005f },
+ { "Grave", 0x0060 },
+ { "BraceL", 0x007b },
+ { "Bar", 0x007c },
+ { "BraceR", 0x007d },
+ { "Tilde", 0x007e },
+ { NULL, 0 },
+};
+
+const KeyModifierNameMapEntry kModifierNameMap[] = {
+ { "Alt", KeyModifierAlt },
+ { "AltGr", KeyModifierAltGr },
+// { "CapsLock", KeyModifierCapsLock },
+ { "Control", KeyModifierControl },
+ { "Meta", KeyModifierMeta },
+// { "NumLock", KeyModifierNumLock },
+// { "ScrollLock", KeyModifierScrollLock },
+ { "Shift", KeyModifierShift },
+ { "Super", KeyModifierSuper },
+ { NULL, 0 },
+};
diff --git a/src/lib/barrier/key_types.h b/src/lib/barrier/key_types.h
new file mode 100644
index 0000000..7a8ea53
--- /dev/null
+++ b/src/lib/barrier/key_types.h
@@ -0,0 +1,314 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "common/basic_types.h"
+
+//! Key ID
+/*!
+Type to hold a key symbol identifier. The encoding is UTF-32, using
+U+E000 through U+EFFF for the various control keys (e.g. arrow
+keys, function keys, modifier keys, etc).
+*/
+typedef UInt32 KeyID;
+
+//! Key Code
+/*!
+Type to hold a physical key identifier. That is, it identifies a
+physical key on the keyboard. KeyButton 0 is reserved to be an
+invalid key; platforms that use 0 as a physical key identifier
+will have to remap that value to some arbitrary unused id.
+*/
+typedef UInt16 KeyButton;
+
+//! Modifier key mask
+/*!
+Type to hold a bitmask of key modifiers (e.g. shift keys).
+*/
+typedef UInt32 KeyModifierMask;
+
+//! Modifier key ID
+/*!
+Type to hold the id of a key modifier (e.g. a shift key).
+*/
+typedef UInt32 KeyModifierID;
+
+//! @name Modifier key masks
+//@{
+static const KeyModifierMask KeyModifierShift = 0x0001;
+static const KeyModifierMask KeyModifierControl = 0x0002;
+static const KeyModifierMask KeyModifierAlt = 0x0004;
+static const KeyModifierMask KeyModifierMeta = 0x0008;
+static const KeyModifierMask KeyModifierSuper = 0x0010;
+static const KeyModifierMask KeyModifierAltGr = 0x0020;
+static const KeyModifierMask KeyModifierLevel5Lock = 0x0040;
+static const KeyModifierMask KeyModifierCapsLock = 0x1000;
+static const KeyModifierMask KeyModifierNumLock = 0x2000;
+static const KeyModifierMask KeyModifierScrollLock = 0x4000;
+//@}
+
+//! @name Modifier key bits
+//@{
+static const UInt32 kKeyModifierBitNone = 16;
+static const UInt32 kKeyModifierBitShift = 0;
+static const UInt32 kKeyModifierBitControl = 1;
+static const UInt32 kKeyModifierBitAlt = 2;
+static const UInt32 kKeyModifierBitMeta = 3;
+static const UInt32 kKeyModifierBitSuper = 4;
+static const UInt32 kKeyModifierBitAltGr = 5;
+static const UInt32 kKeyModifierBitLevel5Lock = 6;
+static const UInt32 kKeyModifierBitCapsLock = 12;
+static const UInt32 kKeyModifierBitNumLock = 13;
+static const UInt32 kKeyModifierBitScrollLock = 14;
+static const SInt32 kKeyModifierNumBits = 16;
+//@}
+
+//! @name Modifier key identifiers
+//@{
+static const KeyModifierID kKeyModifierIDNull = 0;
+static const KeyModifierID kKeyModifierIDShift = 1;
+static const KeyModifierID kKeyModifierIDControl = 2;
+static const KeyModifierID kKeyModifierIDAlt = 3;
+static const KeyModifierID kKeyModifierIDMeta = 4;
+static const KeyModifierID kKeyModifierIDSuper = 5;
+static const KeyModifierID kKeyModifierIDAltGr = 6;
+static const KeyModifierID kKeyModifierIDLast = 7;
+//@}
+
+//! @name Key identifiers
+//@{
+// all identifiers except kKeyNone and those in 0xE000 to 0xE0FF
+// inclusive are equal to the corresponding X11 keysym - 0x1000.
+
+// no key
+static const KeyID kKeyNone = 0x0000;
+
+// TTY functions
+static const KeyID kKeyBackSpace = 0xEF08; /* back space, back char */
+static const KeyID kKeyTab = 0xEF09;
+static const KeyID kKeyLinefeed = 0xEF0A; /* Linefeed, LF */
+static const KeyID kKeyClear = 0xEF0B;
+static const KeyID kKeyReturn = 0xEF0D; /* Return, enter */
+static const KeyID kKeyPause = 0xEF13; /* Pause, hold */
+static const KeyID kKeyScrollLock = 0xEF14;
+static const KeyID kKeySysReq = 0xEF15;
+static const KeyID kKeyEscape = 0xEF1B;
+static const KeyID kKeyHenkan = 0xEF23; /* Start/Stop Conversion */
+static const KeyID kKeyKana = 0xEF26; /* Kana */
+static const KeyID kKeyHiraganaKatakana = 0xEF27; /* Hiragana/Katakana toggle */
+static const KeyID kKeyZenkaku = 0xEF2A; /* Zenkaku/Hankaku */
+static const KeyID kKeyKanzi = 0xEF2A; /* Kanzi */
+static const KeyID kKeyHangul = 0xEF31; /* Hangul */
+static const KeyID kKeyHanja = 0xEF34; /* Hanja */
+static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */
+
+// cursor control
+static const KeyID kKeyHome = 0xEF50;
+static const KeyID kKeyLeft = 0xEF51; /* Move left, left arrow */
+static const KeyID kKeyUp = 0xEF52; /* Move up, up arrow */
+static const KeyID kKeyRight = 0xEF53; /* Move right, right arrow */
+static const KeyID kKeyDown = 0xEF54; /* Move down, down arrow */
+static const KeyID kKeyPageUp = 0xEF55;
+static const KeyID kKeyPageDown = 0xEF56;
+static const KeyID kKeyEnd = 0xEF57; /* EOL */
+static const KeyID kKeyBegin = 0xEF58; /* BOL */
+
+// misc functions
+static const KeyID kKeySelect = 0xEF60; /* Select, mark */
+static const KeyID kKeyPrint = 0xEF61;
+static const KeyID kKeyExecute = 0xEF62; /* Execute, run, do */
+static const KeyID kKeyInsert = 0xEF63; /* Insert, insert here */
+static const KeyID kKeyUndo = 0xEF65; /* Undo, oops */
+static const KeyID kKeyRedo = 0xEF66; /* redo, again */
+static const KeyID kKeyMenu = 0xEF67;
+static const KeyID kKeyFind = 0xEF68; /* Find, search */
+static const KeyID kKeyCancel = 0xEF69; /* Cancel, stop, abort, exit */
+static const KeyID kKeyHelp = 0xEF6A; /* Help */
+static const KeyID kKeyBreak = 0xEF6B;
+static const KeyID kKeyAltGr = 0xEF7E; /* Character set switch */
+static const KeyID kKeyNumLock = 0xEF7F;
+
+// keypad
+static const KeyID kKeyKP_Space = 0xEF80; /* space */
+static const KeyID kKeyKP_Tab = 0xEF89;
+static const KeyID kKeyKP_Enter = 0xEF8D; /* enter */
+static const KeyID kKeyKP_F1 = 0xEF91; /* PF1, KP_A, ... */
+static const KeyID kKeyKP_F2 = 0xEF92;
+static const KeyID kKeyKP_F3 = 0xEF93;
+static const KeyID kKeyKP_F4 = 0xEF94;
+static const KeyID kKeyKP_Home = 0xEF95;
+static const KeyID kKeyKP_Left = 0xEF96;
+static const KeyID kKeyKP_Up = 0xEF97;
+static const KeyID kKeyKP_Right = 0xEF98;
+static const KeyID kKeyKP_Down = 0xEF99;
+static const KeyID kKeyKP_PageUp = 0xEF9A;
+static const KeyID kKeyKP_PageDown = 0xEF9B;
+static const KeyID kKeyKP_End = 0xEF9C;
+static const KeyID kKeyKP_Begin = 0xEF9D;
+static const KeyID kKeyKP_Insert = 0xEF9E;
+static const KeyID kKeyKP_Delete = 0xEF9F;
+static const KeyID kKeyKP_Equal = 0xEFBD; /* equals */
+static const KeyID kKeyKP_Multiply = 0xEFAA;
+static const KeyID kKeyKP_Add = 0xEFAB;
+static const KeyID kKeyKP_Separator= 0xEFAC; /* separator, often comma */
+static const KeyID kKeyKP_Subtract = 0xEFAD;
+static const KeyID kKeyKP_Decimal = 0xEFAE;
+static const KeyID kKeyKP_Divide = 0xEFAF;
+static const KeyID kKeyKP_0 = 0xEFB0;
+static const KeyID kKeyKP_1 = 0xEFB1;
+static const KeyID kKeyKP_2 = 0xEFB2;
+static const KeyID kKeyKP_3 = 0xEFB3;
+static const KeyID kKeyKP_4 = 0xEFB4;
+static const KeyID kKeyKP_5 = 0xEFB5;
+static const KeyID kKeyKP_6 = 0xEFB6;
+static const KeyID kKeyKP_7 = 0xEFB7;
+static const KeyID kKeyKP_8 = 0xEFB8;
+static const KeyID kKeyKP_9 = 0xEFB9;
+
+// function keys
+static const KeyID kKeyF1 = 0xEFBE;
+static const KeyID kKeyF2 = 0xEFBF;
+static const KeyID kKeyF3 = 0xEFC0;
+static const KeyID kKeyF4 = 0xEFC1;
+static const KeyID kKeyF5 = 0xEFC2;
+static const KeyID kKeyF6 = 0xEFC3;
+static const KeyID kKeyF7 = 0xEFC4;
+static const KeyID kKeyF8 = 0xEFC5;
+static const KeyID kKeyF9 = 0xEFC6;
+static const KeyID kKeyF10 = 0xEFC7;
+static const KeyID kKeyF11 = 0xEFC8;
+static const KeyID kKeyF12 = 0xEFC9;
+static const KeyID kKeyF13 = 0xEFCA;
+static const KeyID kKeyF14 = 0xEFCB;
+static const KeyID kKeyF15 = 0xEFCC;
+static const KeyID kKeyF16 = 0xEFCD;
+static const KeyID kKeyF17 = 0xEFCE;
+static const KeyID kKeyF18 = 0xEFCF;
+static const KeyID kKeyF19 = 0xEFD0;
+static const KeyID kKeyF20 = 0xEFD1;
+static const KeyID kKeyF21 = 0xEFD2;
+static const KeyID kKeyF22 = 0xEFD3;
+static const KeyID kKeyF23 = 0xEFD4;
+static const KeyID kKeyF24 = 0xEFD5;
+static const KeyID kKeyF25 = 0xEFD6;
+static const KeyID kKeyF26 = 0xEFD7;
+static const KeyID kKeyF27 = 0xEFD8;
+static const KeyID kKeyF28 = 0xEFD9;
+static const KeyID kKeyF29 = 0xEFDA;
+static const KeyID kKeyF30 = 0xEFDB;
+static const KeyID kKeyF31 = 0xEFDC;
+static const KeyID kKeyF32 = 0xEFDD;
+static const KeyID kKeyF33 = 0xEFDE;
+static const KeyID kKeyF34 = 0xEFDF;
+static const KeyID kKeyF35 = 0xEFE0;
+
+// modifiers
+static const KeyID kKeyShift_L = 0xEFE1; /* Left shift */
+static const KeyID kKeyShift_R = 0xEFE2; /* Right shift */
+static const KeyID kKeyControl_L = 0xEFE3; /* Left control */
+static const KeyID kKeyControl_R = 0xEFE4; /* Right control */
+static const KeyID kKeyCapsLock = 0xEFE5; /* Caps lock */
+static const KeyID kKeyShiftLock = 0xEFE6; /* Shift lock */
+static const KeyID kKeyMeta_L = 0xEFE7; /* Left meta */
+static const KeyID kKeyMeta_R = 0xEFE8; /* Right meta */
+static const KeyID kKeyAlt_L = 0xEFE9; /* Left alt */
+static const KeyID kKeyAlt_R = 0xEFEA; /* Right alt */
+static const KeyID kKeySuper_L = 0xEFEB; /* Left super */
+static const KeyID kKeySuper_R = 0xEFEC; /* Right super */
+static const KeyID kKeyHyper_L = 0xEFED; /* Left hyper */
+static const KeyID kKeyHyper_R = 0xEFEE; /* Right hyper */
+
+// multi-key character composition
+static const KeyID kKeyCompose = 0xEF20;
+static const KeyID kKeyDeadGrave = 0x0300;
+static const KeyID kKeyDeadAcute = 0x0301;
+static const KeyID kKeyDeadCircumflex = 0x0302;
+static const KeyID kKeyDeadTilde = 0x0303;
+static const KeyID kKeyDeadMacron = 0x0304;
+static const KeyID kKeyDeadBreve = 0x0306;
+static const KeyID kKeyDeadAbovedot = 0x0307;
+static const KeyID kKeyDeadDiaeresis = 0x0308;
+static const KeyID kKeyDeadAbovering = 0x030a;
+static const KeyID kKeyDeadDoubleacute = 0x030b;
+static const KeyID kKeyDeadCaron = 0x030c;
+static const KeyID kKeyDeadCedilla = 0x0327;
+static const KeyID kKeyDeadOgonek = 0x0328;
+
+// more function and modifier keys
+static const KeyID kKeyLeftTab = 0xEE20;
+
+// update modifiers
+static const KeyID kKeySetModifiers = 0xEE06;
+static const KeyID kKeyClearModifiers = 0xEE07;
+
+// group change
+static const KeyID kKeyNextGroup = 0xEE08;
+static const KeyID kKeyPrevGroup = 0xEE0A;
+
+// extended keys
+static const KeyID kKeyEject = 0xE001;
+static const KeyID kKeySleep = 0xE05F;
+static const KeyID kKeyWWWBack = 0xE0A6;
+static const KeyID kKeyWWWForward = 0xE0A7;
+static const KeyID kKeyWWWRefresh = 0xE0A8;
+static const KeyID kKeyWWWStop = 0xE0A9;
+static const KeyID kKeyWWWSearch = 0xE0AA;
+static const KeyID kKeyWWWFavorites = 0xE0AB;
+static const KeyID kKeyWWWHome = 0xE0AC;
+static const KeyID kKeyAudioMute = 0xE0AD;
+static const KeyID kKeyAudioDown = 0xE0AE;
+static const KeyID kKeyAudioUp = 0xE0AF;
+static const KeyID kKeyAudioNext = 0xE0B0;
+static const KeyID kKeyAudioPrev = 0xE0B1;
+static const KeyID kKeyAudioStop = 0xE0B2;
+static const KeyID kKeyAudioPlay = 0xE0B3;
+static const KeyID kKeyAppMail = 0xE0B4;
+static const KeyID kKeyAppMedia = 0xE0B5;
+static const KeyID kKeyAppUser1 = 0xE0B6;
+static const KeyID kKeyAppUser2 = 0xE0B7;
+static const KeyID kKeyBrightnessDown = 0xE0B8;
+static const KeyID kKeyBrightnessUp = 0xE0B9;
+static const KeyID kKeyMissionControl = 0xE0C0;
+static const KeyID kKeyLaunchpad = 0xE0C1;
+
+//@}
+
+struct KeyNameMapEntry {
+ const char* m_name;
+ KeyID m_id;
+};
+struct KeyModifierNameMapEntry {
+ const char* m_name;
+ KeyModifierMask m_mask;
+};
+
+//! Key name to KeyID table
+/*!
+A table of key names to the corresponding KeyID. Only the keys listed
+above plus non-alphanumeric ASCII characters are in the table. The end
+of the table is the first pair with a NULL m_name.
+*/
+extern const struct KeyNameMapEntry kKeyNameMap[];
+
+//! Modifier key name to KeyModifierMask table
+/*!
+A table of modifier key names to the corresponding KeyModifierMask.
+The end of the table is the first pair with a NULL m_name.
+*/
+extern const struct KeyModifierNameMapEntry kModifierNameMap[];
diff --git a/src/lib/barrier/mouse_types.h b/src/lib/barrier/mouse_types.h
new file mode 100644
index 0000000..cf860c0
--- /dev/null
+++ b/src/lib/barrier/mouse_types.h
@@ -0,0 +1,41 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "base/EventTypes.h"
+
+//! Mouse button ID
+/*!
+Type to hold a mouse button identifier.
+*/
+typedef UInt8 ButtonID;
+
+//! @name Mouse button identifiers
+//@{
+static const ButtonID kButtonNone = 0;
+static const ButtonID kButtonLeft = 1;
+static const ButtonID kButtonMiddle = 2;
+static const ButtonID kButtonRight = 3;
+static const ButtonID kButtonExtra0 = 4;
+
+static const ButtonID kMacButtonRight = 2;
+static const ButtonID kMacButtonMiddle = 3;
+//@}
+
+static const UInt8 NumButtonIDs = 5;
diff --git a/src/lib/barrier/option_types.h b/src/lib/barrier/option_types.h
new file mode 100644
index 0000000..6323e37
--- /dev/null
+++ b/src/lib/barrier/option_types.h
@@ -0,0 +1,99 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "base/EventTypes.h"
+#include "common/stdvector.h"
+
+//! Option ID
+/*!
+Type to hold an option identifier.
+*/
+typedef UInt32 OptionID;
+
+//! Option Value
+/*!
+Type to hold an option value.
+*/
+typedef SInt32 OptionValue;
+
+// for now, options are just pairs of integers
+typedef std::vector<UInt32> OptionsList;
+
+// macro for packing 4 character strings into 4 byte integers
+#define OPTION_CODE(_s) \
+ (static_cast<UInt32>(static_cast<unsigned char>(_s[0]) << 24) | \
+ static_cast<UInt32>(static_cast<unsigned char>(_s[1]) << 16) | \
+ static_cast<UInt32>(static_cast<unsigned char>(_s[2]) << 8) | \
+ static_cast<UInt32>(static_cast<unsigned char>(_s[3]) ))
+
+//! @name Option identifiers
+//@{
+static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL");
+static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL");
+static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL");
+static const OptionID kOptionModifierMapForShift = OPTION_CODE("MMFS");
+static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC");
+static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA");
+static const OptionID kOptionModifierMapForAltGr = OPTION_CODE("MMFG");
+static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM");
+static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR");
+static const OptionID kOptionHeartbeat = OPTION_CODE("HART");
+static const OptionID kOptionScreenSwitchCorners = OPTION_CODE("SSCM");
+static const OptionID kOptionScreenSwitchCornerSize = OPTION_CODE("SSCS");
+static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT");
+static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT");
+static const OptionID kOptionScreenSwitchNeedsShift = OPTION_CODE("SSNS");
+static const OptionID kOptionScreenSwitchNeedsControl = OPTION_CODE("SSNC");
+static const OptionID kOptionScreenSwitchNeedsAlt = OPTION_CODE("SSNA");
+static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR");
+static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU");
+static const OptionID kOptionScreenPreserveFocus = OPTION_CODE("SFOC");
+static const OptionID kOptionRelativeMouseMoves = OPTION_CODE("MDLT");
+static const OptionID kOptionWin32KeepForeground = OPTION_CODE("_KFW");
+static const OptionID kOptionClipboardSharing = OPTION_CODE("CLPS");
+//@}
+
+//! @name Screen switch corner enumeration
+//@{
+enum EScreenSwitchCorners {
+ kNoCorner,
+ kTopLeft,
+ kTopRight,
+ kBottomLeft,
+ kBottomRight,
+ kFirstCorner = kTopLeft,
+ kLastCorner = kBottomRight
+};
+//@}
+
+//! @name Screen switch corner masks
+//@{
+enum EScreenSwitchCornerMasks {
+ kNoCornerMask = 0,
+ kTopLeftMask = 1 << (kTopLeft - kFirstCorner),
+ kTopRightMask = 1 << (kTopRight - kFirstCorner),
+ kBottomLeftMask = 1 << (kBottomLeft - kFirstCorner),
+ kBottomRightMask = 1 << (kBottomRight - kFirstCorner),
+ kAllCornersMask = kTopLeftMask | kTopRightMask |
+ kBottomLeftMask | kBottomRightMask
+};
+//@}
+
+#undef OPTION_CODE
diff --git a/src/lib/barrier/protocol_types.cpp b/src/lib/barrier/protocol_types.cpp
new file mode 100644
index 0000000..07acf61
--- /dev/null
+++ b/src/lib/barrier/protocol_types.cpp
@@ -0,0 +1,53 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/protocol_types.h"
+
+const char* kMsgHello = "Barrier%2i%2i";
+const char* kMsgHelloBack = "Barrier%2i%2i%s";
+const char* kMsgCNoop = "CNOP";
+const char* kMsgCClose = "CBYE";
+const char* kMsgCEnter = "CINN%2i%2i%4i%2i";
+const char* kMsgCLeave = "COUT";
+const char* kMsgCClipboard = "CCLP%1i%4i";
+const char* kMsgCScreenSaver = "CSEC%1i";
+const char* kMsgCResetOptions = "CROP";
+const char* kMsgCInfoAck = "CIAK";
+const char* kMsgCKeepAlive = "CALV";
+const char* kMsgDKeyDown = "DKDN%2i%2i%2i";
+const char* kMsgDKeyDown1_0 = "DKDN%2i%2i";
+const char* kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i";
+const char* kMsgDKeyRepeat1_0 = "DKRP%2i%2i%2i";
+const char* kMsgDKeyUp = "DKUP%2i%2i%2i";
+const char* kMsgDKeyUp1_0 = "DKUP%2i%2i";
+const char* kMsgDMouseDown = "DMDN%1i";
+const char* kMsgDMouseUp = "DMUP%1i";
+const char* kMsgDMouseMove = "DMMV%2i%2i";
+const char* kMsgDMouseRelMove = "DMRM%2i%2i";
+const char* kMsgDMouseWheel = "DMWM%2i%2i";
+const char* kMsgDMouseWheel1_0 = "DMWM%2i";
+const char* kMsgDClipboard = "DCLP%1i%4i%1i%s";
+const char* kMsgDInfo = "DINF%2i%2i%2i%2i%2i%2i%2i";
+const char* kMsgDSetOptions = "DSOP%4I";
+const char* kMsgDFileTransfer = "DFTR%1i%s";
+const char* kMsgDDragInfo = "DDRG%2i%s";
+const char* kMsgQInfo = "QINF";
+const char* kMsgEIncompatible = "EICV%2i%2i";
+const char* kMsgEBusy = "EBSY";
+const char* kMsgEUnknown = "EUNK";
+const char* kMsgEBad = "EBAD";
diff --git a/src/lib/barrier/protocol_types.h b/src/lib/barrier/protocol_types.h
new file mode 100644
index 0000000..bc5e037
--- /dev/null
+++ b/src/lib/barrier/protocol_types.h
@@ -0,0 +1,337 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "base/EventTypes.h"
+
+// protocol version number
+// 1.0: initial protocol
+// 1.1: adds KeyCode to key press, release, and repeat
+// 1.2: adds mouse relative motion
+// 1.3: adds keep alive and deprecates heartbeats,
+// adds horizontal mouse scrolling
+// 1.4: adds crypto support
+// 1.5: adds file transfer and removes home brew crypto
+// 1.6: adds clipboard streaming
+// NOTE: with new version, barrier minor version should increment
+static const SInt16 kProtocolMajorVersion = 1;
+static const SInt16 kProtocolMinorVersion = 6;
+
+// default contact port number
+static const UInt16 kDefaultPort = 24800;
+
+// maximum total length for greeting returned by client
+static const UInt32 kMaxHelloLength = 1024;
+
+// time between kMsgCKeepAlive (in seconds). a non-positive value disables
+// keep alives. this is the default rate that can be overridden using an
+// option.
+static const double kKeepAliveRate = 3.0;
+
+// number of skipped kMsgCKeepAlive messages that indicates a problem
+static const double kKeepAlivesUntilDeath = 3.0;
+
+// obsolete heartbeat stuff
+static const double kHeartRate = -1.0;
+static const double kHeartBeatsUntilDeath = 3.0;
+
+// direction constants
+enum EDirection {
+ kNoDirection,
+ kLeft,
+ kRight,
+ kTop,
+ kBottom,
+ kFirstDirection = kLeft,
+ kLastDirection = kBottom,
+ kNumDirections = kLastDirection - kFirstDirection + 1
+};
+enum EDirectionMask {
+ kNoDirMask = 0,
+ kLeftMask = 1 << kLeft,
+ kRightMask = 1 << kRight,
+ kTopMask = 1 << kTop,
+ kBottomMask = 1 << kBottom
+};
+
+// Data transfer constants
+enum EDataTransfer {
+ kDataStart = 1,
+ kDataChunk = 2,
+ kDataEnd = 3
+};
+
+// Data received constants
+enum EDataReceived {
+ kStart,
+ kNotFinish,
+ kFinish,
+ kError
+};
+
+//
+// message codes (trailing NUL is not part of code). in comments, $n
+// refers to the n'th argument (counting from one). message codes are
+// always 4 bytes optionally followed by message specific parameters
+// except those for the greeting handshake.
+//
+
+//
+// positions and sizes are signed 16 bit integers.
+//
+
+//
+// greeting handshake messages
+//
+
+// say hello to client; primary -> secondary
+// $1 = protocol major version number supported by server. $2 =
+// protocol minor version number supported by server.
+extern const char* kMsgHello;
+
+// respond to hello from server; secondary -> primary
+// $1 = protocol major version number supported by client. $2 =
+// protocol minor version number supported by client. $3 = client
+// name.
+extern const char* kMsgHelloBack;
+
+
+//
+// command codes
+//
+
+// no operation; secondary -> primary
+extern const char* kMsgCNoop;
+
+// close connection; primary -> secondary
+extern const char* kMsgCClose;
+
+// enter screen: primary -> secondary
+// entering screen at screen position $1 = x, $2 = y. x,y are
+// absolute screen coordinates. $3 = sequence number, which is
+// used to order messages between screens. the secondary screen
+// must return this number with some messages. $4 = modifier key
+// mask. this will have bits set for each toggle modifier key
+// that is activated on entry to the screen. the secondary screen
+// should adjust its toggle modifiers to reflect that state.
+extern const char* kMsgCEnter;
+
+// leave screen: primary -> secondary
+// leaving screen. the secondary screen should send clipboard
+// data in response to this message for those clipboards that
+// it has grabbed (i.e. has sent a kMsgCClipboard for and has
+// not received a kMsgCClipboard for with a greater sequence
+// number) and that were grabbed or have changed since the
+// last leave.
+extern const char* kMsgCLeave;
+
+// grab clipboard: primary <-> secondary
+// sent by screen when some other app on that screen grabs a
+// clipboard. $1 = the clipboard identifier, $2 = sequence number.
+// secondary screens must use the sequence number passed in the
+// most recent kMsgCEnter. the primary always sends 0.
+extern const char* kMsgCClipboard;
+
+// screensaver change: primary -> secondary
+// screensaver on primary has started ($1 == 1) or closed ($1 == 0)
+extern const char* kMsgCScreenSaver;
+
+// reset options: primary -> secondary
+// client should reset all of its options to their defaults.
+extern const char* kMsgCResetOptions;
+
+// resolution change acknowledgment: primary -> secondary
+// sent by primary in response to a secondary screen's kMsgDInfo.
+// this is sent for every kMsgDInfo, whether or not the primary
+// had sent a kMsgQInfo.
+extern const char* kMsgCInfoAck;
+
+// keep connection alive: primary <-> secondary
+// sent by the server periodically to verify that connections are still
+// up and running. clients must reply in kind on receipt. if the server
+// gets an error sending the message or does not receive a reply within
+// a reasonable time then the server disconnects the client. if the
+// client doesn't receive these (or any message) periodically then it
+// should disconnect from the server. the appropriate interval is
+// defined by an option.
+extern const char* kMsgCKeepAlive;
+
+//
+// data codes
+//
+
+// key pressed: primary -> secondary
+// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton
+// the KeyButton identifies the physical key on the primary used to
+// generate this key. the secondary should note the KeyButton along
+// with the physical key it uses to generate the key press. on
+// release, the secondary can then use the primary's KeyButton to
+// find its corresponding physical key and release it. this is
+// necessary because the KeyID on release may not be the KeyID of
+// the press. this can happen with combining (dead) keys or if
+// the keyboard layouts are not identical and the user releases
+// a modifier key before releasing the modified key.
+extern const char* kMsgDKeyDown;
+
+// key pressed 1.0: same as above but without KeyButton
+extern const char* kMsgDKeyDown1_0;
+
+// key auto-repeat: primary -> secondary
+// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats, $4 = KeyButton
+extern const char* kMsgDKeyRepeat;
+
+// key auto-repeat 1.0: same as above but without KeyButton
+extern const char* kMsgDKeyRepeat1_0;
+
+// key released: primary -> secondary
+// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton
+extern const char* kMsgDKeyUp;
+
+// key released 1.0: same as above but without KeyButton
+extern const char* kMsgDKeyUp1_0;
+
+// mouse button pressed: primary -> secondary
+// $1 = ButtonID
+extern const char* kMsgDMouseDown;
+
+// mouse button released: primary -> secondary
+// $1 = ButtonID
+extern const char* kMsgDMouseUp;
+
+// mouse moved: primary -> secondary
+// $1 = x, $2 = y. x,y are absolute screen coordinates.
+extern const char* kMsgDMouseMove;
+
+// relative mouse move: primary -> secondary
+// $1 = dx, $2 = dy. dx,dy are motion deltas.
+extern const char* kMsgDMouseRelMove;
+
+// mouse scroll: primary -> secondary
+// $1 = xDelta, $2 = yDelta. the delta should be +120 for one tick forward
+// (away from the user) or right and -120 for one tick backward (toward
+// the user) or left.
+extern const char* kMsgDMouseWheel;
+
+// mouse vertical scroll: primary -> secondary
+// like as kMsgDMouseWheel except only sends $1 = yDelta.
+extern const char* kMsgDMouseWheel1_0;
+
+// clipboard data: primary <-> secondary
+// $2 = sequence number, $3 = mark $4 = clipboard data. the sequence number
+// is 0 when sent by the primary. secondary screens should use the
+// sequence number from the most recent kMsgCEnter. $1 = clipboard
+// identifier.
+extern const char* kMsgDClipboard;
+
+// client data: secondary -> primary
+// $1 = coordinate of leftmost pixel on secondary screen,
+// $2 = coordinate of topmost pixel on secondary screen,
+// $3 = width of secondary screen in pixels,
+// $4 = height of secondary screen in pixels,
+// $5 = size of warp zone, (obsolete)
+// $6, $7 = the x,y position of the mouse on the secondary screen.
+//
+// the secondary screen must send this message in response to the
+// kMsgQInfo message. it must also send this message when the
+// screen's resolution changes. in this case, the secondary screen
+// should ignore any kMsgDMouseMove messages until it receives a
+// kMsgCInfoAck in order to prevent attempts to move the mouse off
+// the new screen area.
+extern const char* kMsgDInfo;
+
+// set options: primary -> secondary
+// client should set the given option/value pairs. $1 = option/value
+// pairs.
+extern const char* kMsgDSetOptions;
+
+// file data: primary <-> secondary
+// transfer file data. A mark is used in the first byte.
+// 0 means the content followed is the file size.
+// 1 means the content followed is the chunk data.
+// 2 means the file transfer is finished.
+extern const char* kMsgDFileTransfer;
+
+// drag infomation: primary <-> secondary
+// transfer drag infomation. The first 2 bytes are used for storing
+// the number of dragging objects. Then the following string consists
+// of each object's directory.
+extern const char* kMsgDDragInfo;
+
+//
+// query codes
+//
+
+// query screen info: primary -> secondary
+// client should reply with a kMsgDInfo.
+extern const char* kMsgQInfo;
+
+
+//
+// error codes
+//
+
+// incompatible versions: primary -> secondary
+// $1 = major version of primary, $2 = minor version of primary.
+extern const char* kMsgEIncompatible;
+
+// name provided when connecting is already in use: primary -> secondary
+extern const char* kMsgEBusy;
+
+// unknown client: primary -> secondary
+// name provided when connecting is not in primary's screen
+// configuration map.
+extern const char* kMsgEUnknown;
+
+// protocol violation: primary -> secondary
+// primary should disconnect after sending this message.
+extern const char* kMsgEBad;
+
+
+//
+// structures
+//
+
+//! Screen information
+/*!
+This class contains information about a screen.
+*/
+class ClientInfo {
+public:
+ //! Screen position
+ /*!
+ The position of the upper-left corner of the screen. This is
+ typically 0,0.
+ */
+ SInt32 m_x, m_y;
+
+ //! Screen size
+ /*!
+ The size of the screen in pixels.
+ */
+ SInt32 m_w, m_h;
+
+ //! Obsolete (jump zone size)
+ SInt32 obsolete1;
+
+ //! Mouse position
+ /*!
+ The current location of the mouse cursor.
+ */
+ SInt32 m_mx, m_my;
+};
diff --git a/src/lib/barrier/unix/AppUtilUnix.cpp b/src/lib/barrier/unix/AppUtilUnix.cpp
new file mode 100644
index 0000000..a1548d8
--- /dev/null
+++ b/src/lib/barrier/unix/AppUtilUnix.cpp
@@ -0,0 +1,46 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/unix/AppUtilUnix.h"
+#include "barrier/ArgsBase.h"
+
+AppUtilUnix::AppUtilUnix(IEventQueue* events)
+{
+}
+
+AppUtilUnix::~AppUtilUnix()
+{
+}
+
+int
+standardStartupStatic(int argc, char** argv)
+{
+ return AppUtil::instance().app().standardStartup(argc, argv);
+}
+
+int
+AppUtilUnix::run(int argc, char** argv)
+{
+ return app().runInner(argc, argv, NULL, &standardStartupStatic);
+}
+
+void
+AppUtilUnix::startNode()
+{
+ app().startNode();
+}
diff --git a/src/lib/barrier/unix/AppUtilUnix.h b/src/lib/barrier/unix/AppUtilUnix.h
new file mode 100644
index 0000000..fefcfea
--- /dev/null
+++ b/src/lib/barrier/unix/AppUtilUnix.h
@@ -0,0 +1,34 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/AppUtil.h"
+
+#define ARCH_APP_UTIL AppUtilUnix
+
+class IEventQueue;
+
+class AppUtilUnix : public AppUtil {
+public:
+ AppUtilUnix(IEventQueue* events);
+ virtual ~AppUtilUnix();
+
+ int run(int argc, char** argv);
+ void startNode();
+};
diff --git a/src/lib/barrier/win32/AppUtilWindows.cpp b/src/lib/barrier/win32/AppUtilWindows.cpp
new file mode 100644
index 0000000..560b029
--- /dev/null
+++ b/src/lib/barrier/win32/AppUtilWindows.cpp
@@ -0,0 +1,185 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/win32/AppUtilWindows.h"
+#include "barrier/Screen.h"
+#include "barrier/ArgsBase.h"
+#include "barrier/App.h"
+#include "barrier/XBarrier.h"
+#include "platform/MSWindowsScreen.h"
+#include "arch/win32/XArchWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "base/Log.h"
+#include "base/log_outputters.h"
+#include "base/IEventQueue.h"
+#include "base/Event.h"
+#include "base/EventQueue.h"
+#include "common/Version.h"
+
+#include <sstream>
+#include <iostream>
+#include <conio.h>
+#include <VersionHelpers.h>
+
+AppUtilWindows::AppUtilWindows(IEventQueue* events) :
+ m_events(events),
+ m_exitMode(kExitModeNormal)
+{
+ if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)consoleHandler, TRUE) == FALSE)
+ {
+ throw XArch(new XArchEvalWindows());
+ }
+}
+
+AppUtilWindows::~AppUtilWindows()
+{
+}
+
+BOOL WINAPI AppUtilWindows::consoleHandler(DWORD)
+{
+ LOG((CLOG_INFO "got shutdown signal"));
+ IEventQueue* events = AppUtil::instance().app().getEvents();
+ events->addEvent(Event(Event::kQuit));
+ return TRUE;
+}
+
+static
+int
+mainLoopStatic()
+{
+ return AppUtil::instance().app().mainLoop();
+}
+
+int
+AppUtilWindows::daemonNTMainLoop(int argc, const char** argv)
+{
+ app().initApp(argc, argv);
+ debugServiceWait();
+
+ // NB: what the hell does this do?!
+ app().argsBase().m_backend = false;
+
+ return ArchMiscWindows::runDaemon(mainLoopStatic);
+}
+
+void
+AppUtilWindows::exitApp(int code)
+{
+ switch (m_exitMode) {
+
+ case kExitModeDaemon:
+ ArchMiscWindows::daemonFailed(code);
+ break;
+
+ default:
+ throw XExitApp(code);
+ }
+}
+
+int daemonNTMainLoopStatic(int argc, const char** argv)
+{
+ return AppUtilWindows::instance().daemonNTMainLoop(argc, argv);
+}
+
+int
+AppUtilWindows::daemonNTStartup(int, char**)
+{
+ SystemLogger sysLogger(app().daemonName(), false);
+ m_exitMode = kExitModeDaemon;
+ return ARCH->daemonize(app().daemonName(), daemonNTMainLoopStatic);
+}
+
+static
+int
+daemonNTStartupStatic(int argc, char** argv)
+{
+ return AppUtilWindows::instance().daemonNTStartup(argc, argv);
+}
+
+static
+int
+foregroundStartupStatic(int argc, char** argv)
+{
+ return AppUtil::instance().app().foregroundStartup(argc, argv);
+}
+
+void
+AppUtilWindows::beforeAppExit()
+{
+ // this can be handy for debugging, since the application is launched in
+ // a new console window, and will normally close on exit (making it so
+ // that we can't see error messages).
+ if (app().argsBase().m_pauseOnExit) {
+ std::cout << std::endl << "press any key to exit..." << std::endl;
+ int c = _getch();
+ }
+}
+
+int
+AppUtilWindows::run(int argc, char** argv)
+{
+ if (!IsWindowsXPSP3OrGreater()) {
+ throw std::runtime_error("Barrier only supports Windows XP SP3 and above.");
+ }
+
+ // record window instance for tray icon, etc
+ ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
+
+ MSWindowsScreen::init(ArchMiscWindows::instanceWin32());
+ Thread::getCurrentThread().setPriority(-14);
+
+ StartupFunc startup;
+ if (ArchMiscWindows::wasLaunchedAsService()) {
+ startup = &daemonNTStartupStatic;
+ } else {
+ startup = &foregroundStartupStatic;
+ app().argsBase().m_daemon = false;
+ }
+
+ return app().runInner(argc, argv, NULL, startup);
+}
+
+AppUtilWindows&
+AppUtilWindows::instance()
+{
+ return (AppUtilWindows&)AppUtil::instance();
+}
+
+void
+AppUtilWindows::debugServiceWait()
+{
+ if (app().argsBase().m_debugServiceWait)
+ {
+ while(true)
+ {
+ // this code is only executed when the process is launched via the
+ // windows service controller (and --debug-service-wait arg is
+ // used). to debug, set a breakpoint on this line so that
+ // execution is delayed until the debugger is attached.
+ ARCH->sleep(1);
+ LOG((CLOG_INFO "waiting for debugger to attach"));
+ }
+ }
+}
+
+void
+AppUtilWindows::startNode()
+{
+ app().startNode();
+}
diff --git a/src/lib/barrier/win32/AppUtilWindows.h b/src/lib/barrier/win32/AppUtilWindows.h
new file mode 100644
index 0000000..c5da228
--- /dev/null
+++ b/src/lib/barrier/win32/AppUtilWindows.h
@@ -0,0 +1,62 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * 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 "barrier/AppUtil.h"
+#include "base/String.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include "Windows.h"
+
+#define ARCH_APP_UTIL AppUtilWindows
+
+class IEventQueue;
+
+enum AppExitMode {
+ kExitModeNormal,
+ kExitModeDaemon
+};
+
+class AppUtilWindows : public AppUtil {
+public:
+ AppUtilWindows(IEventQueue* events);
+ virtual ~AppUtilWindows();
+
+ int daemonNTStartup(int, char**);
+
+ int daemonNTMainLoop(int argc, const char** argv);
+
+ void debugServiceWait();
+
+ int run(int argc, char** argv);
+
+ void exitApp(int code);
+
+ void beforeAppExit();
+
+ static AppUtilWindows& instance();
+
+ void startNode();
+
+private:
+ AppExitMode m_exitMode;
+ IEventQueue* m_events;
+
+ static BOOL WINAPI consoleHandler(DWORD Event);
+};
diff --git a/src/lib/barrier/win32/DaemonApp.cpp b/src/lib/barrier/win32/DaemonApp.cpp
new file mode 100644
index 0000000..62fecf8
--- /dev/null
+++ b/src/lib/barrier/win32/DaemonApp.cpp
@@ -0,0 +1,361 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "barrier/win32/DaemonApp.h"
+
+#include "barrier/App.h"
+#include "barrier/ArgParser.h"
+#include "barrier/ServerArgs.h"
+#include "barrier/ClientArgs.h"
+#include "ipc/IpcClientProxy.h"
+#include "ipc/IpcMessage.h"
+#include "ipc/IpcLogOutputter.h"
+#include "net/SocketMultiplexer.h"
+#include "arch/XArch.h"
+#include "base/Log.h"
+#include "base/TMethodJob.h"
+#include "base/TMethodEventJob.h"
+#include "base/EventQueue.h"
+#include "base/log_outputters.h"
+#include "base/Log.h"
+
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/win32/XArchWindows.h"
+#include "barrier/Screen.h"
+#include "platform/MSWindowsScreen.h"
+#include "platform/MSWindowsDebugOutputter.h"
+#include "platform/MSWindowsWatchdog.h"
+#include "platform/MSWindowsEventQueueBuffer.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#include <string>
+#include <iostream>
+#include <sstream>
+
+using namespace std;
+
+DaemonApp* DaemonApp::s_instance = NULL;
+
+int
+mainLoopStatic()
+{
+ DaemonApp::s_instance->mainLoop(true);
+ return kExitSuccess;
+}
+
+int
+mainLoopStatic(int, const char**)
+{
+ return ArchMiscWindows::runDaemon(mainLoopStatic);
+}
+
+DaemonApp::DaemonApp() :
+ m_ipcServer(nullptr),
+ m_ipcLogOutputter(nullptr),
+ m_watchdog(nullptr),
+ m_events(nullptr),
+ m_fileLogOutputter(nullptr)
+{
+ s_instance = this;
+}
+
+DaemonApp::~DaemonApp()
+{
+}
+
+int
+DaemonApp::run(int argc, char** argv)
+{
+ // win32 instance needed for threading, etc.
+ ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
+
+ Arch arch;
+ arch.init();
+
+ Log log;
+ EventQueue events;
+ m_events = &events;
+
+ bool uninstall = false;
+ try
+ {
+ // sends debug messages to visual studio console window.
+ log.insert(new MSWindowsDebugOutputter());
+
+ // default log level to system setting.
+ string logLevel = arch.setting("LogLevel");
+ if (logLevel != "")
+ log.setFilter(logLevel.c_str());
+
+ bool foreground = false;
+
+ for (int i = 1; i < argc; ++i) {
+ string arg(argv[i]);
+
+ if (arg == "/f" || arg == "-f") {
+ foreground = true;
+ }
+ else if (arg == "/install") {
+ uninstall = true;
+ arch.installDaemon();
+ return kExitSuccess;
+ }
+ else if (arg == "/uninstall") {
+ arch.uninstallDaemon();
+ return kExitSuccess;
+ }
+ else {
+ stringstream ss;
+ ss << "Unrecognized argument: " << arg;
+ foregroundError(ss.str().c_str());
+ return kExitArgs;
+ }
+ }
+
+ if (foreground) {
+ // run process in foreground instead of daemonizing.
+ // useful for debugging.
+ mainLoop(false);
+ }
+ else {
+ arch.daemonize("Barrier", mainLoopStatic);
+ }
+
+ return kExitSuccess;
+ }
+ catch (XArch& e) {
+ String message = e.what();
+ if (uninstall && (message.find("The service has not been started") != String::npos)) {
+ // TODO: if we're keeping this use error code instead (what is it?!).
+ // HACK: this message happens intermittently, not sure where from but
+ // it's quite misleading for the user. they thing something has gone
+ // horribly wrong, but it's just the service manager reporting a false
+ // positive (the service has actually shut down in most cases).
+ }
+ else {
+ foregroundError(message.c_str());
+ }
+ return kExitFailed;
+ }
+ catch (std::exception& e) {
+ foregroundError(e.what());
+ return kExitFailed;
+ }
+ catch (...) {
+ foregroundError("Unrecognized error.");
+ return kExitFailed;
+ }
+}
+
+void
+DaemonApp::mainLoop(bool daemonized)
+{
+ try
+ {
+ DAEMON_RUNNING(true);
+
+ if (daemonized) {
+ m_fileLogOutputter = new FileLogOutputter(logFilename().c_str());
+ CLOG->insert(m_fileLogOutputter);
+ }
+
+ // create socket multiplexer. this must happen after daemonization
+ // on unix because threads evaporate across a fork().
+ SocketMultiplexer multiplexer;
+
+ // uses event queue, must be created here.
+ m_ipcServer = new IpcServer(m_events, &multiplexer);
+
+ // send logging to gui via ipc, log system adopts outputter.
+ m_ipcLogOutputter = new IpcLogOutputter(*m_ipcServer, kIpcClientGui, true);
+ CLOG->insert(m_ipcLogOutputter);
+
+ m_watchdog = new MSWindowsWatchdog(daemonized, false, *m_ipcServer, *m_ipcLogOutputter);
+ m_watchdog->setFileLogOutputter(m_fileLogOutputter);
+
+ m_events->adoptHandler(
+ m_events->forIpcServer().messageReceived(), m_ipcServer,
+ new TMethodEventJob<DaemonApp>(this, &DaemonApp::handleIpcMessage));
+
+ m_ipcServer->listen();
+
+ // install the platform event queue to handle service stop events.
+ m_events->adoptBuffer(new MSWindowsEventQueueBuffer(m_events));
+
+ String command = ARCH->setting("Command");
+ bool elevate = ARCH->setting("Elevate") == "1";
+ if (command != "") {
+ LOG((CLOG_INFO "using last known command: %s", command.c_str()));
+ m_watchdog->setCommand(command, elevate);
+ }
+
+ m_watchdog->startAsync();
+
+ m_events->loop();
+
+ m_watchdog->stop();
+ delete m_watchdog;
+
+ m_events->removeHandler(
+ m_events->forIpcServer().messageReceived(), m_ipcServer);
+
+ CLOG->remove(m_ipcLogOutputter);
+ delete m_ipcLogOutputter;
+ delete m_ipcServer;
+
+ DAEMON_RUNNING(false);
+ }
+ catch (std::exception& e) {
+ LOG((CLOG_CRIT "An error occurred: %s", e.what()));
+ }
+ catch (...) {
+ LOG((CLOG_CRIT "An unknown error occurred.\n"));
+ }
+}
+
+void
+DaemonApp::foregroundError(const char* message)
+{
+ MessageBox(NULL, message, "Barrier Service", MB_OK | MB_ICONERROR);
+}
+
+std::string
+DaemonApp::logFilename()
+{
+ string logFilename;
+ logFilename = ARCH->setting("LogFilename");
+ if (logFilename.empty()) {
+ logFilename = ARCH->getLogDirectory();
+ logFilename.append("/");
+ logFilename.append(LOG_FILENAME);
+ }
+
+ return logFilename;
+}
+
+void
+DaemonApp::handleIpcMessage(const Event& e, void*)
+{
+ IpcMessage* m = static_cast<IpcMessage*>(e.getDataObject());
+ switch (m->type()) {
+ case kIpcCommand: {
+ IpcCommandMessage* cm = static_cast<IpcCommandMessage*>(m);
+ String command = cm->command();
+
+ // if empty quotes, clear.
+ if (command == "\"\"") {
+ command.clear();
+ }
+
+ if (!command.empty()) {
+ LOG((CLOG_DEBUG "new command, elevate=%d command=%s", cm->elevate(), command.c_str()));
+
+ std::vector<String> argsArray;
+ ArgParser::splitCommandString(command, argsArray);
+ ArgParser argParser(NULL);
+ const char** argv = argParser.getArgv(argsArray);
+ ServerArgs serverArgs;
+ ClientArgs clientArgs;
+ int argc = static_cast<int>(argsArray.size());
+ bool server = argsArray[0].find("barriers") != String::npos ? true : false;
+ ArgsBase* argBase = NULL;
+
+ if (server) {
+ argParser.parseServerArgs(serverArgs, argc, argv);
+ argBase = &serverArgs;
+ }
+ else {
+ argParser.parseClientArgs(clientArgs, argc, argv);
+ argBase = &clientArgs;
+ }
+
+ delete[] argv;
+
+ String logLevel(argBase->m_logFilter);
+ if (!logLevel.empty()) {
+ try {
+ // change log level based on that in the command string
+ // and change to that log level now.
+ ARCH->setting("LogLevel", logLevel);
+ CLOG->setFilter(logLevel.c_str());
+ }
+ catch (XArch& e) {
+ LOG((CLOG_ERR "failed to save LogLevel setting, %s", e.what()));
+ }
+ }
+
+ // eg. no log-to-file while running in foreground
+ if (m_fileLogOutputter != nullptr) {
+ String logFilename;
+ if (argBase->m_logFile != NULL) {
+ logFilename = String(argBase->m_logFile);
+ ARCH->setting("LogFilename", logFilename);
+ m_watchdog->setFileLogOutputter(m_fileLogOutputter);
+ command = ArgParser::assembleCommand(argsArray, "--log", 1);
+ LOG((CLOG_DEBUG "removed log file argument and filename %s from command ", logFilename.c_str()));
+ LOG((CLOG_DEBUG "new command, elevate=%d command=%s", cm->elevate(), command.c_str()));
+ } else {
+ m_watchdog->setFileLogOutputter(NULL);
+ }
+ m_fileLogOutputter->setLogFilename(logFilename.c_str());
+ }
+ }
+ else {
+ LOG((CLOG_DEBUG "empty command, elevate=%d", cm->elevate()));
+ }
+
+ try {
+ // store command in system settings. this is used when the daemon
+ // next starts.
+ ARCH->setting("Command", command);
+
+ // TODO: it would be nice to store bools/ints...
+ ARCH->setting("Elevate", String(cm->elevate() ? "1" : "0"));
+ }
+ catch (XArch& e) {
+ LOG((CLOG_ERR "failed to save settings, %s", e.what()));
+ }
+
+ // tell the relauncher about the new command. this causes the
+ // relauncher to stop the existing command and start the new
+ // command.
+ m_watchdog->setCommand(command, cm->elevate());
+
+ break;
+ }
+
+ case kIpcHello:
+ IpcHelloMessage* hm = static_cast<IpcHelloMessage*>(m);
+ String type;
+ switch (hm->clientType()) {
+ case kIpcClientGui: type = "gui"; break;
+ case kIpcClientNode: type = "node"; break;
+ default: type = "unknown"; break;
+ }
+
+ LOG((CLOG_DEBUG "ipc hello, type=%s", type.c_str()));
+
+ const char * serverstatus = m_watchdog->isProcessActive() ? "active" : "not active";
+ LOG((CLOG_INFO "server status: %s", serverstatus));
+
+ m_ipcLogOutputter->notifyBuffer();
+ break;
+ }
+}
diff --git a/src/lib/barrier/win32/DaemonApp.h b/src/lib/barrier/win32/DaemonApp.h
new file mode 100644
index 0000000..2a8484b
--- /dev/null
+++ b/src/lib/barrier/win32/DaemonApp.h
@@ -0,0 +1,58 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "arch/Arch.h"
+#include "ipc/IpcServer.h"
+
+#include <string>
+
+class Event;
+class IpcLogOutputter;
+class FileLogOutputter;
+
+class MSWindowsWatchdog;
+
+class DaemonApp {
+
+public:
+ DaemonApp();
+ virtual ~DaemonApp();
+ int run(int argc, char** argv);
+ void mainLoop(bool daemonized);
+
+private:
+ void daemonize();
+ void foregroundError(const char* message);
+ std::string logFilename();
+ void handleIpcMessage(const Event&, void*);
+
+public:
+ static DaemonApp* s_instance;
+
+ MSWindowsWatchdog* m_watchdog;
+
+private:
+ IpcServer* m_ipcServer;
+ IpcLogOutputter* m_ipcLogOutputter;
+ IEventQueue* m_events;
+ FileLogOutputter* m_fileLogOutputter;
+};
+
+#define LOG_FILENAME "barrierd.log"