aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/arch
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/arch')
-rw-r--r--src/lib/arch/Arch.cpp60
-rw-r--r--src/lib/arch/Arch.h144
-rw-r--r--src/lib/arch/ArchConsoleStd.cpp33
-rw-r--r--src/lib/arch/ArchConsoleStd.h34
-rw-r--r--src/lib/arch/ArchDaemonNone.cpp85
-rw-r--r--src/lib/arch/ArchDaemonNone.h50
-rw-r--r--src/lib/arch/CMakeLists.txt47
-rw-r--r--src/lib/arch/IArchConsole.h66
-rw-r--r--src/lib/arch/IArchDaemon.h128
-rw-r--r--src/lib/arch/IArchFile.h105
-rw-r--r--src/lib/arch/IArchLog.h63
-rw-r--r--src/lib/arch/IArchMultithread.h273
-rw-r--r--src/lib/arch/IArchNetwork.h283
-rw-r--r--src/lib/arch/IArchSleep.h44
-rw-r--r--src/lib/arch/IArchString.cpp190
-rw-r--r--src/lib/arch/IArchString.h72
-rw-r--r--src/lib/arch/IArchSystem.h66
-rw-r--r--src/lib/arch/IArchTaskBar.h63
-rw-r--r--src/lib/arch/IArchTaskBarReceiver.h98
-rw-r--r--src/lib/arch/IArchTime.h41
-rw-r--r--src/lib/arch/XArch.h161
-rw-r--r--src/lib/arch/multibyte.h56
-rw-r--r--src/lib/arch/unix/ArchConsoleUnix.cpp23
-rw-r--r--src/lib/arch/unix/ArchConsoleUnix.h29
-rw-r--r--src/lib/arch/unix/ArchDaemonUnix.cpp132
-rw-r--r--src/lib/arch/unix/ArchDaemonUnix.h36
-rw-r--r--src/lib/arch/unix/ArchFileUnix.cpp163
-rw-r--r--src/lib/arch/unix/ArchFileUnix.h47
-rw-r--r--src/lib/arch/unix/ArchInternetUnix.cpp126
-rw-r--r--src/lib/arch/unix/ArchInternetUnix.h28
-rw-r--r--src/lib/arch/unix/ArchLogUnix.cpp84
-rw-r--r--src/lib/arch/unix/ArchLogUnix.h36
-rw-r--r--src/lib/arch/unix/ArchMultithreadPosix.cpp812
-rw-r--r--src/lib/arch/unix/ArchMultithreadPosix.h115
-rw-r--r--src/lib/arch/unix/ArchNetworkBSD.cpp1005
-rw-r--r--src/lib/arch/unix/ArchNetworkBSD.h105
-rw-r--r--src/lib/arch/unix/ArchSleepUnix.cpp94
-rw-r--r--src/lib/arch/unix/ArchSleepUnix.h33
-rw-r--r--src/lib/arch/unix/ArchStringUnix.cpp42
-rw-r--r--src/lib/arch/unix/ArchStringUnix.h34
-rw-r--r--src/lib/arch/unix/ArchSystemUnix.cpp80
-rw-r--r--src/lib/arch/unix/ArchSystemUnix.h38
-rw-r--r--src/lib/arch/unix/ArchTaskBarXWindows.cpp51
-rw-r--r--src/lib/arch/unix/ArchTaskBarXWindows.h35
-rw-r--r--src/lib/arch/unix/ArchTimeUnix.cpp52
-rw-r--r--src/lib/arch/unix/ArchTimeUnix.h33
-rw-r--r--src/lib/arch/unix/XArchUnix.cpp32
-rw-r--r--src/lib/arch/unix/XArchUnix.h33
-rw-r--r--src/lib/arch/vsnprintf.h67
-rw-r--r--src/lib/arch/win32/ArchConsoleWindows.cpp23
-rw-r--r--src/lib/arch/win32/ArchConsoleWindows.h29
-rw-r--r--src/lib/arch/win32/ArchDaemonWindows.cpp704
-rw-r--r--src/lib/arch/win32/ArchDaemonWindows.h151
-rw-r--r--src/lib/arch/win32/ArchFileWindows.cpp203
-rw-r--r--src/lib/arch/win32/ArchFileWindows.h47
-rw-r--r--src/lib/arch/win32/ArchInternetWindows.cpp224
-rw-r--r--src/lib/arch/win32/ArchInternetWindows.h28
-rw-r--r--src/lib/arch/win32/ArchLogWindows.cpp95
-rw-r--r--src/lib/arch/win32/ArchLogWindows.h42
-rw-r--r--src/lib/arch/win32/ArchMiscWindows.cpp524
-rw-r--r--src/lib/arch/win32/ArchMiscWindows.h202
-rw-r--r--src/lib/arch/win32/ArchMultithreadWindows.cpp705
-rw-r--r--src/lib/arch/win32/ArchMultithreadWindows.h116
-rw-r--r--src/lib/arch/win32/ArchNetworkWinsock.cpp985
-rw-r--r--src/lib/arch/win32/ArchNetworkWinsock.h111
-rw-r--r--src/lib/arch/win32/ArchSleepWindows.cpp61
-rw-r--r--src/lib/arch/win32/ArchSleepWindows.h33
-rw-r--r--src/lib/arch/win32/ArchStringWindows.cpp46
-rw-r--r--src/lib/arch/win32/ArchStringWindows.h34
-rw-r--r--src/lib/arch/win32/ArchSystemWindows.cpp166
-rw-r--r--src/lib/arch/win32/ArchSystemWindows.h39
-rw-r--r--src/lib/arch/win32/ArchTaskBarWindows.cpp514
-rw-r--r--src/lib/arch/win32/ArchTaskBarWindows.h114
-rw-r--r--src/lib/arch/win32/ArchTimeWindows.cpp89
-rw-r--r--src/lib/arch/win32/ArchTimeWindows.h33
-rw-r--r--src/lib/arch/win32/XArchWindows.cpp120
-rw-r--r--src/lib/arch/win32/XArchWindows.h49
77 files changed, 11014 insertions, 0 deletions
diff --git a/src/lib/arch/Arch.cpp b/src/lib/arch/Arch.cpp
new file mode 100644
index 0000000..0a3b3e5
--- /dev/null
+++ b/src/lib/arch/Arch.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "arch/Arch.h"
+
+//
+// Arch
+//
+
+Arch* Arch::s_instance = NULL;
+
+Arch::Arch()
+{
+ assert(s_instance == NULL);
+ s_instance = this;
+}
+
+Arch::Arch(Arch* arch)
+{
+ s_instance = arch;
+}
+
+Arch::~Arch()
+{
+#if SYSAPI_WIN32
+ ArchMiscWindows::cleanup();
+#endif
+}
+
+void
+Arch::init()
+{
+ ARCH_NETWORK::init();
+#if SYSAPI_WIN32
+ ARCH_TASKBAR::init();
+ ArchMiscWindows::init();
+#endif
+}
+
+Arch*
+Arch::getInstance()
+{
+ assert(s_instance != NULL);
+ return s_instance;
+}
diff --git a/src/lib/arch/Arch.h b/src/lib/arch/Arch.h
new file mode 100644
index 0000000..42a73c2
--- /dev/null
+++ b/src/lib/arch/Arch.h
@@ -0,0 +1,144 @@
+/*
+ * 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/>.
+ */
+
+// TODO: consider whether or not to use either encapsulation (as below)
+// or inheritance (as it is now) for the ARCH stuff.
+//
+// case for encapsulation:
+// pros:
+// - compiler errors for missing pv implementations are not absolutely bonkers.
+// - function names don't have to be so verbose.
+// - easier to understand and debug.
+// - ctors in IArch implementations can call other implementations.
+// cons:
+// - slightly more code for calls to ARCH.
+// - you'll have to modify each ARCH call.
+//
+// also, we may want to consider making each encapsulated
+// class lazy-loaded so that apps like the daemon don't load
+// stuff when they don't need it.
+
+#pragma once
+
+#include "common/common.h"
+
+#if SYSAPI_WIN32
+# include "arch/win32/ArchConsoleWindows.h"
+# include "arch/win32/ArchDaemonWindows.h"
+# include "arch/win32/ArchFileWindows.h"
+# include "arch/win32/ArchLogWindows.h"
+# include "arch/win32/ArchMiscWindows.h"
+# include "arch/win32/ArchMultithreadWindows.h"
+# include "arch/win32/ArchNetworkWinsock.h"
+# include "arch/win32/ArchSleepWindows.h"
+# include "arch/win32/ArchStringWindows.h"
+# include "arch/win32/ArchSystemWindows.h"
+# include "arch/win32/ArchTaskBarWindows.h"
+# include "arch/win32/ArchTimeWindows.h"
+# include "arch/win32/ArchInternetWindows.h"
+#elif SYSAPI_UNIX
+# include "arch/unix/ArchConsoleUnix.h"
+# include "arch/unix/ArchDaemonUnix.h"
+# include "arch/unix/ArchFileUnix.h"
+# include "arch/unix/ArchLogUnix.h"
+# if HAVE_PTHREAD
+# include "arch/unix/ArchMultithreadPosix.h"
+# endif
+# include "arch/unix/ArchNetworkBSD.h"
+# include "arch/unix/ArchSleepUnix.h"
+# include "arch/unix/ArchStringUnix.h"
+# include "arch/unix/ArchSystemUnix.h"
+# include "arch/unix/ArchTaskBarXWindows.h"
+# include "arch/unix/ArchTimeUnix.h"
+# include "arch/unix/ArchInternetUnix.h"
+#endif
+
+/*!
+\def ARCH
+This macro evaluates to the singleton Arch object.
+*/
+#define ARCH (Arch::getInstance())
+
+//! Delegating implementation of architecture dependent interfaces
+/*!
+This class is a centralized interface to all architecture dependent
+interface implementations (except miscellaneous functions). It
+instantiates an implementation of each interface and delegates calls
+to each method to those implementations. Clients should use the
+\c ARCH macro to access this object. Clients must also instantiate
+exactly one of these objects before attempting to call any method,
+typically at the beginning of \c main().
+*/
+class Arch : public ARCH_CONSOLE,
+ public ARCH_DAEMON,
+ public ARCH_FILE,
+ public ARCH_LOG,
+ public ARCH_MULTITHREAD,
+ public ARCH_NETWORK,
+ public ARCH_SLEEP,
+ public ARCH_STRING,
+ public ARCH_SYSTEM,
+ public ARCH_TASKBAR,
+ public ARCH_TIME {
+public:
+ Arch();
+ Arch(Arch* arch);
+ virtual ~Arch();
+
+ //! Call init on other arch classes.
+ /*!
+ Some arch classes depend on others to exist first. When init is called
+ these clases will have ARCH available for use.
+ */
+ virtual void init();
+
+ //
+ // accessors
+ //
+
+ //! Return the singleton instance
+ /*!
+ The client must have instantiated exactly once Arch object before
+ calling this function.
+ */
+ static Arch* getInstance();
+
+ static void setInstance(Arch* s) { s_instance = s; }
+
+ ARCH_INTERNET& internet() const { return (ARCH_INTERNET&)m_internet; }
+
+private:
+ static Arch* s_instance;
+ ARCH_INTERNET m_internet;
+};
+
+//! Convenience object to lock/unlock an arch mutex
+class ArchMutexLock {
+public:
+ ArchMutexLock(ArchMutex mutex) : m_mutex(mutex)
+ {
+ ARCH->lockMutex(m_mutex);
+ }
+ ~ArchMutexLock()
+ {
+ ARCH->unlockMutex(m_mutex);
+ }
+
+private:
+ ArchMutex m_mutex;
+};
diff --git a/src/lib/arch/ArchConsoleStd.cpp b/src/lib/arch/ArchConsoleStd.cpp
new file mode 100644
index 0000000..f7f7691
--- /dev/null
+++ b/src/lib/arch/ArchConsoleStd.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "arch/ArchConsoleStd.h"
+#include "base/Log.h"
+
+#include <iostream>
+
+void
+ArchConsoleStd::writeConsole(ELevel level, const char* str)
+{
+ if ((level >= kFATAL) && (level <= kWARNING))
+ std::cerr << str << std::endl;
+ else
+ std::cout << str << std::endl;
+
+ std::cout.flush();
+} \ No newline at end of file
diff --git a/src/lib/arch/ArchConsoleStd.h b/src/lib/arch/ArchConsoleStd.h
new file mode 100644
index 0000000..8560fad
--- /dev/null
+++ b/src/lib/arch/ArchConsoleStd.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 "arch/IArchConsole.h"
+
+//! Cross platform implementation of IArchConsole
+class ArchConsoleStd : public IArchConsole {
+public:
+ ArchConsoleStd() { }
+ virtual ~ArchConsoleStd() { }
+
+ // IArchConsole overrides
+ virtual void openConsole(const char* title) { }
+ virtual void closeConsole() { }
+ virtual void showConsole(bool) { }
+ virtual void writeConsole(ELevel level, const char*);
+};
diff --git a/src/lib/arch/ArchDaemonNone.cpp b/src/lib/arch/ArchDaemonNone.cpp
new file mode 100644
index 0000000..1222549
--- /dev/null
+++ b/src/lib/arch/ArchDaemonNone.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 "arch/ArchDaemonNone.h"
+
+//
+// ArchDaemonNone
+//
+
+ArchDaemonNone::ArchDaemonNone()
+{
+ // do nothing
+}
+
+ArchDaemonNone::~ArchDaemonNone()
+{
+ // do nothing
+}
+
+void
+ArchDaemonNone::installDaemon(const char*,
+ const char*,
+ const char*,
+ const char*,
+ const char*)
+{
+ // do nothing
+}
+
+void
+ArchDaemonNone::uninstallDaemon(const char*)
+{
+ // do nothing
+}
+
+int
+ArchDaemonNone::daemonize(const char* name, DaemonFunc func)
+{
+ // simply forward the call to func. obviously, this doesn't
+ // do any daemonizing.
+ return func(1, &name);
+}
+
+bool
+ArchDaemonNone::canInstallDaemon(const char*)
+{
+ return false;
+}
+
+bool
+ArchDaemonNone::isDaemonInstalled(const char*)
+{
+ return false;
+}
+
+void
+ArchDaemonNone::installDaemon()
+{
+}
+
+void
+ArchDaemonNone::uninstallDaemon()
+{
+}
+
+std::string
+ArchDaemonNone::commandLine() const
+{
+ return "";
+}
diff --git a/src/lib/arch/ArchDaemonNone.h b/src/lib/arch/ArchDaemonNone.h
new file mode 100644
index 0000000..fd59758
--- /dev/null
+++ b/src/lib/arch/ArchDaemonNone.h
@@ -0,0 +1,50 @@
+/*
+ * 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 "arch/IArchDaemon.h"
+
+#define ARCH_DAEMON ArchDaemonNone
+
+//! Dummy implementation of IArchDaemon
+/*!
+This class implements IArchDaemon for a platform that does not have
+daemons. The install and uninstall functions do nothing, the query
+functions return false, and \c daemonize() simply calls the passed
+function and returns its result.
+*/
+class ArchDaemonNone : public IArchDaemon {
+public:
+ ArchDaemonNone();
+ virtual ~ArchDaemonNone();
+
+ // IArchDaemon overrides
+ virtual void installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies);
+ virtual void uninstallDaemon(const char* name);
+ virtual int daemonize(const char* name, DaemonFunc func);
+ virtual bool canInstallDaemon(const char* name);
+ virtual bool isDaemonInstalled(const char* name);
+ virtual void installDaemon();
+ virtual void uninstallDaemon();
+ virtual std::string commandLine() const;
+};
diff --git a/src/lib/arch/CMakeLists.txt b/src/lib/arch/CMakeLists.txt
new file mode 100644
index 0000000..113cdd9
--- /dev/null
+++ b/src/lib/arch/CMakeLists.txt
@@ -0,0 +1,47 @@
+# 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")
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+# 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(arch STATIC ${sources})
+
+if (UNIX)
+ target_link_libraries(arch ${libs})
+ if (NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+ target_link_libraries(arch dl)
+ endif()
+endif()
diff --git a/src/lib/arch/IArchConsole.h b/src/lib/arch/IArchConsole.h
new file mode 100644
index 0000000..d115c50
--- /dev/null
+++ b/src/lib/arch/IArchConsole.h
@@ -0,0 +1,66 @@
+/*
+ * 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 "base/ELevel.h"
+
+//! Interface for architecture dependent console output
+/*!
+This interface defines the console operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchConsole : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Open the console
+ /*!
+ Opens the console for writing. The console is opened automatically
+ on the first write so calling this method is optional. Uses \c title
+ for the console's title if appropriate for the architecture. Calling
+ this method on an already open console must have no effect.
+ */
+ virtual void openConsole(const char* title) = 0;
+
+ //! Close the console
+ /*!
+ Close the console. Calling this method on an already closed console
+ must have no effect.
+ */
+ virtual void closeConsole() = 0;
+
+ //! Show the console
+ /*!
+ Causes the console to become visible. This generally only makes sense
+ for a console in a graphical user interface. Other implementations
+ will do nothing. Iff \p showIfEmpty is \c false then the implementation
+ may optionally only show the console if it's not empty.
+ */
+ virtual void showConsole(bool showIfEmpty) = 0;
+
+ //! Write to the console
+ /*!
+ Writes the given string to the console, opening it if necessary.
+ */
+ virtual void writeConsole(ELevel, const char*) = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchDaemon.h b/src/lib/arch/IArchDaemon.h
new file mode 100644
index 0000000..a4983d3
--- /dev/null
+++ b/src/lib/arch/IArchDaemon.h
@@ -0,0 +1,128 @@
+/*
+ * 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 "base/String.h"
+
+//! Interface for architecture dependent daemonizing
+/*!
+This interface defines the operations required by barrier for installing
+uninstalling daeamons and daemonizing a process. Each architecture must
+implement this interface.
+*/
+class IArchDaemon : public IInterface {
+public:
+ typedef int (*DaemonFunc)(int argc, const char** argv);
+
+ //! @name manipulators
+ //@{
+
+ //! Install daemon
+ /*!
+ Install a daemon. \c name is the name of the daemon passed to the
+ system and \c description is a short human readable description of
+ the daemon. \c pathname is the path to the daemon executable.
+ \c commandLine should \b not include the name of program as the
+ first argument. If \c allUsers is true then the daemon will be
+ installed to start at boot time, otherwise it will be installed to
+ start when the current user logs in. If \p dependencies is not NULL
+ then it's a concatenation of NUL terminated other daemon names
+ followed by a NUL; the daemon will be configured to startup after
+ the listed daemons. Throws an \c XArchDaemon exception on failure.
+ */
+ virtual void installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies) = 0;
+
+ //! Uninstall daemon
+ /*!
+ Uninstall a daemon. Throws an \c XArchDaemon on failure.
+ */
+ virtual void uninstallDaemon(const char* name) = 0;
+
+ //! Install daemon
+ /*!
+ Installs the default daemon.
+ */
+ virtual void installDaemon() = 0;
+
+ //! Uninstall daemon
+ /*!
+ Uninstalls the default daemon.
+ */
+ virtual void uninstallDaemon() = 0;
+
+ //! Daemonize the process
+ /*!
+ Daemonize. Throw XArchDaemonFailed on error. \c name is the name
+ of the daemon. Once daemonized, \c func is invoked and daemonize
+ returns when and what it does.
+
+ Exactly what happens when daemonizing depends on the platform.
+ <ul>
+ <li>unix:
+ Detaches from terminal. \c func gets passed one argument, the
+ name passed to daemonize().
+ <li>win32:
+ Becomes a service. Argument 0 is the name of the service
+ and the rest are the arguments passed to StartService().
+ \c func is only called when the service is actually started.
+ \c func must call \c ArchMiscWindows::runDaemon() to finally
+ becoming a service. The \c runFunc function passed to \c runDaemon()
+ must call \c ArchMiscWindows::daemonRunning(true) when it
+ enters the main loop (i.e. after initialization) and
+ \c ArchMiscWindows::daemonRunning(false) when it leaves
+ the main loop. The \c stopFunc function passed to \c runDaemon()
+ is called when the daemon must exit the main loop and it must cause
+ \c runFunc to return. \c func should return what \c runDaemon()
+ returns. \c func or \c runFunc can call
+ \c ArchMiscWindows::daemonFailed() to indicate startup failure.
+ </ul>
+ */
+ virtual int daemonize(const char* name, DaemonFunc func) = 0;
+
+ //! Check if user has permission to install the daemon
+ /*!
+ Returns true iff the caller has permission to install or
+ uninstall the daemon. Note that even if this method returns
+ true it's possible that installing/uninstalling the service
+ may still fail. This method ignores whether or not the
+ service is already installed.
+ */
+ virtual bool canInstallDaemon(const char* name) = 0;
+
+ //! Check if the daemon is installed
+ /*!
+ Returns true iff the daemon is installed.
+ */
+ virtual bool isDaemonInstalled(const char* name) = 0;
+
+ //@}
+
+ //! Get the command line
+ /*!
+ Gets the command line with which the application was started.
+ */
+ virtual std::string commandLine() const = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchFile.h b/src/lib/arch/IArchFile.h
new file mode 100644
index 0000000..5fdd288
--- /dev/null
+++ b/src/lib/arch/IArchFile.h
@@ -0,0 +1,105 @@
+/*
+ * 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 "common/stdstring.h"
+#include "base/String.h"
+
+//! Interface for architecture dependent file system operations
+/*!
+This interface defines the file system operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchFile : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Extract base name
+ /*!
+ Find the base name in the given \c pathname.
+ */
+ virtual const char* getBasename(const char* pathname) = 0;
+
+ //! Get user's home directory
+ /*!
+ Returns the user's home directory. Returns the empty string if
+ this cannot be determined.
+ */
+ virtual std::string getUserDirectory() = 0;
+
+ //! Get system directory
+ /*!
+ Returns the ussystem configuration file directory.
+ */
+ virtual std::string getSystemDirectory() = 0;
+
+ //! Get installed directory
+ /*!
+ Returns the directory in which Barrier is installed.
+ */
+ virtual std::string getInstalledDirectory() = 0;
+
+ //! Get log directory
+ /*!
+ Returns the log file directory.
+ */
+ virtual std::string getLogDirectory() = 0;
+
+ //! Get plugins directory
+ /*!
+ Returns the plugin files directory. If no plugin directory is set,
+ this will return the plugin folder within the user's profile.
+ */
+ virtual std::string getPluginDirectory() = 0;
+
+ //! Get user's profile directory
+ /*!
+ Returns the user's profile directory. If no profile directory is set,
+ this will return the user's profile according to the operating system,
+ which will depend on which user launched the program.
+ */
+ virtual std::string getProfileDirectory() = 0;
+
+ //! Concatenate path components
+ /*!
+ Concatenate pathname components with a directory separator
+ between them. This should not check if the resulting path
+ is longer than allowed by the system; we'll rely on the
+ system calls to tell us that.
+ */
+ virtual std::string concatPath(
+ const std::string& prefix,
+ const std::string& suffix) = 0;
+
+ //@}
+ //! Set the user's profile directory
+ /*
+ Returns the user's profile directory.
+ */
+ virtual void setProfileDirectory(const String& s) = 0;
+
+ //@}
+ //! Set the user's plugin directory
+ /*
+ Returns the user's plugin directory.
+ */
+ virtual void setPluginDirectory(const String& s) = 0;
+};
diff --git a/src/lib/arch/IArchLog.h b/src/lib/arch/IArchLog.h
new file mode 100644
index 0000000..165b1df
--- /dev/null
+++ b/src/lib/arch/IArchLog.h
@@ -0,0 +1,63 @@
+/*
+ * 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 "base/ELevel.h"
+
+//! Interface for architecture dependent logging
+/*!
+This interface defines the logging operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchLog : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Open the log
+ /*!
+ Opens the log for writing. The log must be opened before being
+ written to.
+ */
+ virtual void openLog(const char* name) = 0;
+
+ //! Close the log
+ /*!
+ Close the log.
+ */
+ virtual void closeLog() = 0;
+
+ //! Show the log
+ /*!
+ Causes the log to become visible. This generally only makes sense
+ for a log in a graphical user interface. Other implementations
+ will do nothing. Iff \p showIfEmpty is \c false then the implementation
+ may optionally only show the log if it's not empty.
+ */
+ virtual void showLog(bool showIfEmpty) = 0;
+
+ //! Write to the log
+ /*!
+ Writes the given string to the log with the given level.
+ */
+ virtual void writeLog(ELevel, const char*) = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchMultithread.h b/src/lib/arch/IArchMultithread.h
new file mode 100644
index 0000000..e8d358b
--- /dev/null
+++ b/src/lib/arch/IArchMultithread.h
@@ -0,0 +1,273 @@
+/*
+ * 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 ArchCondImpl
+\brief Internal condition variable data.
+An architecture dependent type holding the necessary data for a
+condition variable.
+*/
+class ArchCondImpl;
+
+/*!
+\var ArchCond
+\brief Opaque condition variable type.
+An opaque type representing a condition variable.
+*/
+typedef ArchCondImpl* ArchCond;
+
+/*!
+\class ArchMutexImpl
+\brief Internal mutex data.
+An architecture dependent type holding the necessary data for a mutex.
+*/
+class ArchMutexImpl;
+
+/*!
+\var ArchMutex
+\brief Opaque mutex type.
+An opaque type representing a mutex.
+*/
+typedef ArchMutexImpl* ArchMutex;
+
+/*!
+\class ArchThreadImpl
+\brief Internal thread data.
+An architecture dependent type holding the necessary data for a thread.
+*/
+class ArchThreadImpl;
+
+/*!
+\var ArchThread
+\brief Opaque thread type.
+An opaque type representing a thread.
+*/
+typedef ArchThreadImpl* ArchThread;
+
+//! Interface for architecture dependent multithreading
+/*!
+This interface defines the multithreading operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchMultithread : public IInterface {
+public:
+ //! Type of thread entry point
+ typedef void* (*ThreadFunc)(void*);
+ //! Type of thread identifier
+ typedef unsigned int ThreadID;
+ //! Types of signals
+ /*!
+ Not all platforms support all signals. Unsupported signals are
+ ignored.
+ */
+ enum ESignal {
+ kINTERRUPT, //!< Interrupt (e.g. Ctrl+C)
+ kTERMINATE, //!< Terminate (e.g. Ctrl+Break)
+ kHANGUP, //!< Hangup (SIGHUP)
+ kUSER, //!< User (SIGUSR2)
+ kNUM_SIGNALS
+ };
+ //! Type of signal handler function
+ typedef void (*SignalFunc)(ESignal, void* userData);
+
+ //! @name manipulators
+ //@{
+
+ //
+ // condition variable methods
+ //
+
+ //! Create a condition variable
+ /*!
+ The condition variable is an opaque data type.
+ */
+ virtual ArchCond newCondVar() = 0;
+
+ //! Destroy a condition variable
+ virtual void closeCondVar(ArchCond) = 0;
+
+ //! Signal a condition variable
+ /*!
+ Signalling a condition variable releases one waiting thread.
+ */
+ virtual void signalCondVar(ArchCond) = 0;
+
+ //! Broadcast a condition variable
+ /*!
+ Broadcasting a condition variable releases all waiting threads.
+ */
+ virtual void broadcastCondVar(ArchCond) = 0;
+
+ //! Wait on a condition variable
+ /*!
+ Wait on a conditation variable for up to \c timeout seconds.
+ If \c timeout is < 0 then there is no timeout. The mutex must
+ be locked when this method is called. The mutex is unlocked
+ during the wait and locked again before returning. Returns
+ true if the condition variable was signalled and false on
+ timeout.
+
+ (Cancellation point)
+ */
+ virtual bool waitCondVar(ArchCond, ArchMutex, double timeout) = 0;
+
+ //
+ // mutex methods
+ //
+
+ //! Create a recursive mutex
+ /*!
+ Creates a recursive mutex. A thread may lock a recursive mutex
+ when it already holds a lock on that mutex. The mutex is an
+ opaque data type.
+ */
+ virtual ArchMutex newMutex() = 0;
+
+ //! Destroy a mutex
+ virtual void closeMutex(ArchMutex) = 0;
+
+ //! Lock a mutex
+ virtual void lockMutex(ArchMutex) = 0;
+
+ //! Unlock a mutex
+ virtual void unlockMutex(ArchMutex) = 0;
+
+ //
+ // thread methods
+ //
+
+ //! Start a new thread
+ /*!
+ Creates and starts a new thread, using \c func as the entry point
+ and passing it \c userData. The thread is an opaque data type.
+ */
+ virtual ArchThread newThread(ThreadFunc func, void* userData) = 0;
+
+ //! Get a reference to the calling thread
+ /*!
+ Returns a thread representing the current (i.e. calling) thread.
+ */
+ virtual ArchThread newCurrentThread() = 0;
+
+ //! Copy a thread object
+ /*!
+ Returns a reference to to thread referred to by \c thread.
+ */
+ virtual ArchThread copyThread(ArchThread thread) = 0;
+
+ //! Release a thread reference
+ /*!
+ Deletes the given thread object. This does not destroy the thread
+ the object referred to, even if there are no remaining references.
+ Use cancelThread() and waitThread() to stop a thread and wait for
+ it to exit.
+ */
+ virtual void closeThread(ArchThread) = 0;
+
+ //! Force a thread to exit
+ /*!
+ Causes \c thread to exit when it next calls a cancellation point.
+ A thread avoids cancellation as long as it nevers calls a
+ cancellation point. Once it begins the cancellation process it
+ must always let cancellation go to completion but may take as
+ long as necessary to clean up.
+ */
+ virtual void cancelThread(ArchThread thread) = 0;
+
+ //! Change thread priority
+ /*!
+ Changes the priority of \c thread by \c n. If \c n is positive
+ the thread has a lower priority and if negative a higher priority.
+ Some architectures may not support either or both directions.
+ */
+ virtual void setPriorityOfThread(ArchThread, int n) = 0;
+
+ //! Cancellation point
+ /*!
+ This method does nothing but is a cancellation point. Clients
+ can make their own functions cancellation points by calling this
+ method at appropriate times.
+
+ (Cancellation point)
+ */
+ virtual void testCancelThread() = 0;
+
+ //! Wait for a thread to exit
+ /*!
+ Waits for up to \c timeout seconds for \c thread to exit (normally
+ or by cancellation). Waits forever if \c timeout < 0. Returns
+ true if the thread exited, false otherwise. Waiting on the current
+ thread returns immediately with false.
+
+ (Cancellation point)
+ */
+ virtual bool wait(ArchThread thread, double timeout) = 0;
+
+ //! Compare threads
+ /*!
+ Returns true iff two thread objects refer to the same thread.
+ Note that comparing thread objects directly is meaningless.
+ */
+ virtual bool isSameThread(ArchThread, ArchThread) = 0;
+
+ //! Test if thread exited
+ /*!
+ Returns true iff \c thread has exited.
+ */
+ virtual bool isExitedThread(ArchThread thread) = 0;
+
+ //! Returns the exit code of a thread
+ /*!
+ Waits indefinitely for \c thread to exit (if it hasn't yet) then
+ returns the thread's exit code.
+
+ (Cancellation point)
+ */
+ virtual void* getResultOfThread(ArchThread thread) = 0;
+
+ //! Returns an ID for a thread
+ /*!
+ Returns some ID number for \c thread. This is for logging purposes.
+ All thread objects referring to the same thread return the same ID.
+ However, clients should us isSameThread() to compare thread objects
+ instead of comparing IDs.
+ */
+ virtual ThreadID getIDOfThread(ArchThread thread) = 0;
+
+ //! Set the interrupt handler
+ /*!
+ Sets the function to call on receipt of an external interrupt.
+ By default and when \p func is NULL, the main thread is cancelled.
+ */
+ virtual void setSignalHandler(ESignal, SignalFunc func,
+ void* userData) = 0;
+
+ //! Invoke the signal handler
+ /*!
+ Invokes the signal handler for \p signal, if any. If no handler
+ cancels the main thread for \c kINTERRUPT and \c kTERMINATE and
+ ignores the call otherwise.
+ */
+ virtual void raiseSignal(ESignal signal) = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchNetwork.h b/src/lib/arch/IArchNetwork.h
new file mode 100644
index 0000000..b859506
--- /dev/null
+++ b/src/lib/arch/IArchNetwork.h
@@ -0,0 +1,283 @@
+/*
+ * 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 "common/stdstring.h"
+
+class ArchThreadImpl;
+typedef ArchThreadImpl* ArchThread;
+
+/*!
+\class ArchSocketImpl
+\brief Internal socket data.
+An architecture dependent type holding the necessary data for a socket.
+*/
+class ArchSocketImpl;
+
+/*!
+\var ArchSocket
+\brief Opaque socket type.
+An opaque type representing a socket.
+*/
+typedef ArchSocketImpl* ArchSocket;
+
+/*!
+\class ArchNetAddressImpl
+\brief Internal network address data.
+An architecture dependent type holding the necessary data for a network
+address.
+*/
+class ArchNetAddressImpl;
+
+/*!
+\var ArchNetAddress
+\brief Opaque network address type.
+An opaque type representing a network address.
+*/
+typedef ArchNetAddressImpl* ArchNetAddress;
+
+//! Interface for architecture dependent networking
+/*!
+This interface defines the networking operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchNetwork : public IInterface {
+public:
+ //! Supported address families
+ enum EAddressFamily {
+ kUNKNOWN,
+ kINET,
+ kINET6,
+ };
+
+ //! Supported socket types
+ enum ESocketType {
+ kDGRAM,
+ kSTREAM
+ };
+
+ //! Events for \c poll()
+ /*!
+ Events for \c poll() are bitmasks and can be combined using the
+ bitwise operators.
+ */
+ enum {
+ kPOLLIN = 1, //!< Socket is readable
+ kPOLLOUT = 2, //!< Socket is writable
+ kPOLLERR = 4, //!< The socket is in an error state
+ kPOLLNVAL = 8 //!< The socket is invalid
+ };
+
+ //! A socket query for \c poll()
+ class PollEntry {
+ public:
+ //! The socket to query
+ ArchSocket m_socket;
+
+ //! The events to query for
+ /*!
+ The events to query for can be any combination of kPOLLIN and
+ kPOLLOUT.
+ */
+ unsigned short m_events;
+
+ //! The result events
+ unsigned short m_revents;
+ };
+
+ //! @name manipulators
+ //@{
+
+ //! Create a new socket
+ /*!
+ The socket is an opaque data type.
+ */
+ virtual ArchSocket newSocket(EAddressFamily, ESocketType) = 0;
+
+ //! Copy a socket object
+ /*!
+ Returns a reference to to socket referred to by \c s.
+ */
+ virtual ArchSocket copySocket(ArchSocket s) = 0;
+
+ //! Release a socket reference
+ /*!
+ Deletes the given socket object. This does not destroy the socket
+ the object referred to until there are no remaining references for
+ the socket.
+ */
+ virtual void closeSocket(ArchSocket s) = 0;
+
+ //! Close socket for further reads
+ /*!
+ Calling this disallows future reads on socket \c s.
+ */
+ virtual void closeSocketForRead(ArchSocket s) = 0;
+
+ //! Close socket for further writes
+ /*!
+ Calling this disallows future writes on socket \c s.
+ */
+ virtual void closeSocketForWrite(ArchSocket s) = 0;
+
+ //! Bind socket to address
+ /*!
+ Binds socket \c s to the address \c addr.
+ */
+ virtual void bindSocket(ArchSocket s, ArchNetAddress addr) = 0;
+
+ //! Listen for connections on socket
+ /*!
+ Causes the socket \c s to begin listening for incoming connections.
+ */
+ virtual void listenOnSocket(ArchSocket s) = 0;
+
+ //! Accept connection on socket
+ /*!
+ Accepts a connection on socket \c s, returning a new socket for the
+ connection and filling in \c addr with the address of the remote
+ end. \c addr may be NULL if the remote address isn't required.
+ The original socket \c s is unaffected and remains in the listening
+ state. The new socket shares most of the properties of \c s except
+ it's not in the listening state and it's connected. Returns NULL
+ if there are no pending connection requests.
+ */
+ virtual ArchSocket acceptSocket(ArchSocket s, ArchNetAddress* addr) = 0;
+
+ //! Connect socket
+ /*!
+ Connects the socket \c s to the remote address \c addr. Returns
+ true if the connection succeed immediately, false if the connection
+ is in progress, and throws if the connection failed immediately.
+ If it returns false, \c pollSocket() can be used to wait on the
+ socket for writing to detect when the connection finally succeeds
+ or fails.
+ */
+ virtual bool connectSocket(ArchSocket s, ArchNetAddress addr) = 0;
+
+ //! Check socket state
+ /*!
+ Tests the state of \c num sockets for readability and/or writability.
+ Waits up to \c timeout seconds for some socket to become readable
+ and/or writable (or indefinitely if \c timeout < 0). Returns the
+ number of sockets that were readable (if readability was being
+ queried) or writable (if writablility was being queried) and sets
+ the \c m_revents members of the entries. \c kPOLLERR and \c kPOLLNVAL
+ are set in \c m_revents as appropriate. If a socket indicates
+ \c kPOLLERR then \c throwErrorOnSocket() can be used to determine
+ the type of error. Returns 0 immediately regardless of the \c timeout
+ if no valid sockets are selected for testing.
+
+ (Cancellation point)
+ */
+ virtual int pollSocket(PollEntry[], int num, double timeout) = 0;
+
+ //! Unblock thread in pollSocket()
+ /*!
+ Cause a thread that's in a pollSocket() call to return. This
+ call may return before the thread is unblocked. If the thread is
+ not in a pollSocket() call this call has no effect.
+ */
+ virtual void unblockPollSocket(ArchThread thread) = 0;
+
+ //! Read data from socket
+ /*!
+ Read up to \c len bytes from socket \c s in \c buf and return the
+ number of bytes read. The number of bytes can be less than \c len
+ if not enough data is available. Returns 0 if the remote end has
+ disconnected and/or there is no more queued received data.
+ */
+ virtual size_t readSocket(ArchSocket s, void* buf, size_t len) = 0;
+
+ //! Write data from socket
+ /*!
+ Write up to \c len bytes to socket \c s from \c buf and return the
+ number of bytes written. The number of bytes can be less than
+ \c len if the remote end disconnected or the internal buffers fill
+ up.
+ */
+ virtual size_t writeSocket(ArchSocket s,
+ const void* buf, size_t len) = 0;
+
+ //! Check error on socket
+ /*!
+ If the socket \c s is in an error state then throws an appropriate
+ XArchNetwork exception.
+ */
+ virtual void throwErrorOnSocket(ArchSocket s) = 0;
+
+ //! Turn Nagle algorithm on or off on socket
+ /*!
+ Set socket to send messages immediately (true) or to collect small
+ messages into one packet (false). Returns the previous state.
+ */
+ virtual bool setNoDelayOnSocket(ArchSocket, bool noDelay) = 0;
+
+ //! Turn address reuse on or off on socket
+ /*!
+ Allows the address this socket is bound to to be reused while in the
+ TIME_WAIT state. Returns the previous state.
+ */
+ virtual bool setReuseAddrOnSocket(ArchSocket, bool reuse) = 0;
+
+ //! Return local host's name
+ virtual std::string getHostName() = 0;
+
+ //! Create an "any" network address
+ virtual ArchNetAddress newAnyAddr(EAddressFamily) = 0;
+
+ //! Copy a network address
+ virtual ArchNetAddress copyAddr(ArchNetAddress) = 0;
+
+ //! Convert a name to a network address
+ virtual ArchNetAddress nameToAddr(const std::string&) = 0;
+
+ //! Destroy a network address
+ virtual void closeAddr(ArchNetAddress) = 0;
+
+ //! Convert an address to a host name
+ virtual std::string addrToName(ArchNetAddress) = 0;
+
+ //! Convert an address to a string
+ virtual std::string addrToString(ArchNetAddress) = 0;
+
+ //! Get an address's family
+ virtual EAddressFamily getAddrFamily(ArchNetAddress) = 0;
+
+ //! Set the port of an address
+ virtual void setAddrPort(ArchNetAddress, int port) = 0;
+
+ //! Get the port of an address
+ virtual int getAddrPort(ArchNetAddress) = 0;
+
+ //! Test addresses for equality
+ virtual bool isEqualAddr(ArchNetAddress, ArchNetAddress) = 0;
+
+ //! Test for the "any" address
+ /*!
+ Returns true if \c addr is the "any" address. \c newAnyAddr()
+ returns an "any" address.
+ */
+ virtual bool isAnyAddr(ArchNetAddress addr) = 0;
+
+ //@}
+
+ virtual void init() = 0;
+};
diff --git a/src/lib/arch/IArchSleep.h b/src/lib/arch/IArchSleep.h
new file mode 100644
index 0000000..9999d0e
--- /dev/null
+++ b/src/lib/arch/IArchSleep.h
@@ -0,0 +1,44 @@
+/*
+ * 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"
+
+//! Interface for architecture dependent sleeping
+/*!
+This interface defines the sleep operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchSleep : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Sleep
+ /*!
+ Blocks the calling thread for \c timeout seconds. If
+ \c timeout < 0.0 then the call returns immediately. If \c timeout
+ == 0.0 then the calling thread yields the CPU.
+
+ (cancellation point)
+ */
+ virtual void sleep(double timeout) = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchString.cpp b/src/lib/arch/IArchString.cpp
new file mode 100644
index 0000000..f618c12
--- /dev/null
+++ b/src/lib/arch/IArchString.cpp
@@ -0,0 +1,190 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 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 "arch/IArchString.h"
+#include "arch/Arch.h"
+#include "common/common.h"
+
+#include <climits>
+#include <cstring>
+#include <cstdlib>
+
+static ArchMutex s_mutex = NULL;
+
+//
+// use C library non-reentrant multibyte conversion with mutex
+//
+
+IArchString::~IArchString()
+{
+ if (s_mutex != NULL) {
+ ARCH->closeMutex(s_mutex);
+ s_mutex = NULL;
+ }
+}
+
+int
+IArchString::convStringWCToMB(char* dst,
+ const wchar_t* src, UInt32 n, bool* errors)
+{
+ ptrdiff_t len = 0;
+
+ bool dummyErrors;
+ if (errors == NULL) {
+ errors = &dummyErrors;
+ }
+
+ if (s_mutex == NULL) {
+ s_mutex = ARCH->newMutex();
+ }
+
+ ARCH->lockMutex(s_mutex);
+
+ if (dst == NULL) {
+ char dummy[MB_LEN_MAX];
+ for (const wchar_t* scan = src; n > 0; ++scan, --n) {
+ ptrdiff_t mblen = wctomb(dummy, *scan);
+ if (mblen == -1) {
+ *errors = true;
+ mblen = 1;
+ }
+ len += mblen;
+ }
+ ptrdiff_t mblen = wctomb(dummy, L'\0');
+ if (mblen != -1) {
+ len += mblen - 1;
+ }
+ }
+ else {
+ char* dst0 = dst;
+ for (const wchar_t* scan = src; n > 0; ++scan, --n) {
+ ptrdiff_t mblen = wctomb(dst, *scan);
+ if (mblen == -1) {
+ *errors = true;
+ *dst++ = '?';
+ }
+ else {
+ dst += mblen;
+ }
+ }
+ ptrdiff_t mblen = wctomb(dst, L'\0');
+ if (mblen != -1) {
+ // don't include nul terminator
+ dst += mblen - 1;
+ }
+ len = dst - dst0;
+ }
+ ARCH->unlockMutex(s_mutex);
+
+ return (int)len;
+}
+
+int
+IArchString::convStringMBToWC(wchar_t* dst,
+ const char* src, UInt32 n_param, bool* errors)
+{
+ ptrdiff_t n = (ptrdiff_t)n_param; // fix compiler warning
+ ptrdiff_t len = 0;
+ wchar_t dummy;
+
+ bool dummyErrors;
+ if (errors == NULL) {
+ errors = &dummyErrors;
+ }
+
+ if (s_mutex == NULL) {
+ s_mutex = ARCH->newMutex();
+ }
+
+ ARCH->lockMutex(s_mutex);
+
+ if (dst == NULL) {
+ for (const char* scan = src; n > 0; ) {
+ ptrdiff_t mblen = mbtowc(&dummy, scan, n);
+ switch (mblen) {
+ case -2:
+ // incomplete last character. convert to unknown character.
+ *errors = true;
+ len += 1;
+ n = 0;
+ break;
+
+ case -1:
+ // invalid character. count one unknown character and
+ // start at the next byte.
+ *errors = true;
+ len += 1;
+ scan += 1;
+ n -= 1;
+ break;
+
+ case 0:
+ len += 1;
+ scan += 1;
+ n -= 1;
+ break;
+
+ default:
+ // normal character
+ len += 1;
+ scan += mblen;
+ n -= mblen;
+ break;
+ }
+ }
+ }
+ else {
+ wchar_t* dst0 = dst;
+ for (const char* scan = src; n > 0; ++dst) {
+ ptrdiff_t mblen = mbtowc(dst, scan, n);
+ switch (mblen) {
+ case -2:
+ // incomplete character. convert to unknown character.
+ *errors = true;
+ *dst = (wchar_t)0xfffd;
+ n = 0;
+ break;
+
+ case -1:
+ // invalid character. count one unknown character and
+ // start at the next byte.
+ *errors = true;
+ *dst = (wchar_t)0xfffd;
+ scan += 1;
+ n -= 1;
+ break;
+
+ case 0:
+ *dst = (wchar_t)0x0000;
+ scan += 1;
+ n -= 1;
+ break;
+
+ default:
+ // normal character
+ scan += mblen;
+ n -= mblen;
+ break;
+ }
+ }
+ len = dst - dst0;
+ }
+ ARCH->unlockMutex(s_mutex);
+
+ return (int)len;
+}
diff --git a/src/lib/arch/IArchString.h b/src/lib/arch/IArchString.h
new file mode 100644
index 0000000..ea10b65
--- /dev/null
+++ b/src/lib/arch/IArchString.h
@@ -0,0 +1,72 @@
+/*
+ * 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 "common/basic_types.h"
+
+#include <stdarg.h>
+
+//! Interface for architecture dependent string operations
+/*!
+This interface defines the string operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchString : public IInterface {
+public:
+ virtual ~IArchString();
+
+ //! Wide character encodings
+ /*!
+ The known wide character encodings
+ */
+ enum EWideCharEncoding {
+ kUCS2, //!< The UCS-2 encoding
+ kUCS4, //!< The UCS-4 encoding
+ kUTF16, //!< The UTF-16 encoding
+ kUTF32 //!< The UTF-32 encoding
+ };
+
+ //! @name manipulators
+ //@{
+
+ //! printf() to limited size buffer with va_list
+ /*!
+ This method is equivalent to vsprintf() except it will not write
+ more than \c n bytes to the buffer, returning -1 if the output
+ was truncated and the number of bytes written not including the
+ trailing NUL otherwise.
+ */
+ virtual int vsnprintf(char* str,
+ int size, const char* fmt, va_list ap);
+
+ //! Convert multibyte string to wide character string
+ virtual int convStringMBToWC(wchar_t*,
+ const char*, UInt32 n, bool* errors);
+
+ //! Convert wide character string to multibyte string
+ virtual int convStringWCToMB(char*,
+ const wchar_t*, UInt32 n, bool* errors);
+
+ //! Return the architecture's native wide character encoding
+ virtual EWideCharEncoding
+ getWideCharEncoding() = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchSystem.h b/src/lib/arch/IArchSystem.h
new file mode 100644
index 0000000..9446505
--- /dev/null
+++ b/src/lib/arch/IArchSystem.h
@@ -0,0 +1,66 @@
+/*
+ * 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 "common/IInterface.h"
+#include "common/stdstring.h"
+
+//! Interface for architecture dependent system queries
+/*!
+This interface defines operations for querying system info.
+*/
+class IArchSystem : public IInterface {
+public:
+ //! @name accessors
+ //@{
+
+ //! Identify the OS
+ /*!
+ Returns a string identifying the operating system.
+ */
+ virtual std::string getOSName() const = 0;
+
+ //! Identify the platform
+ /*!
+ Returns a string identifying the platform this OS is running on.
+ */
+ virtual std::string getPlatformName() const = 0;
+ //@}
+
+ //! Get a Barrier setting
+ /*!
+ Reads a Barrier setting from the system.
+ */
+ virtual std::string setting(const std::string& valueName) const = 0;
+ //@}
+
+ //! Set a Barrier setting
+ /*!
+ Writes a Barrier setting from the system.
+ */
+ virtual void setting(const std::string& valueName, const std::string& valueString) const = 0;
+ //@}
+
+ //! Get the pathnames of the libraries used by Barrier
+ /*
+ Returns a string containing the full path names of all loaded libraries at the point it is called.
+ */
+ virtual std::string getLibsUsed(void) const = 0;
+ //@}
+};
diff --git a/src/lib/arch/IArchTaskBar.h b/src/lib/arch/IArchTaskBar.h
new file mode 100644
index 0000000..85a32d8
--- /dev/null
+++ b/src/lib/arch/IArchTaskBar.h
@@ -0,0 +1,63 @@
+/*
+ * 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 "common/IInterface.h"
+
+class IArchTaskBarReceiver;
+
+//! Interface for architecture dependent task bar control
+/*!
+This interface defines the task bar icon operations required
+by barrier. Each architecture must implement this interface
+though each operation can be a no-op.
+*/
+class IArchTaskBar : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Add a receiver
+ /*!
+ Add a receiver object to be notified of user and application
+ events. This should be called before other methods. When
+ the receiver is added to the task bar, its icon appears on
+ the task bar.
+ */
+ virtual void addReceiver(IArchTaskBarReceiver*) = 0;
+
+ //! Remove a receiver
+ /*!
+ Remove a receiver object from the task bar. This removes the
+ icon from the task bar.
+ */
+ virtual void removeReceiver(IArchTaskBarReceiver*) = 0;
+
+ //! Update a receiver
+ /*!
+ Updates the display of the receiver on the task bar. This
+ should be called when the receiver appearance may have changed
+ (e.g. it's icon or tool tip has changed).
+ */
+ virtual void updateReceiver(IArchTaskBarReceiver*) = 0;
+
+ //@}
+
+ virtual void init() = 0;
+};
diff --git a/src/lib/arch/IArchTaskBarReceiver.h b/src/lib/arch/IArchTaskBarReceiver.h
new file mode 100644
index 0000000..8a925b4
--- /dev/null
+++ b/src/lib/arch/IArchTaskBarReceiver.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 "base/String.h"
+#include "common/IInterface.h"
+
+class IScreen;
+class INode;
+
+//! Interface for architecture dependent task bar event handling
+/*!
+This interface defines the task bar icon event handlers required
+by barrier. Each architecture must implement this interface
+though each operation can be a no-op.
+*/
+class IArchTaskBarReceiver : public IInterface {
+public:
+ // Icon data is architecture dependent
+ typedef void* Icon;
+
+ //! @name manipulators
+ //@{
+
+ //! Show status window
+ /*!
+ Open a window displaying current status. This should return
+ immediately without waiting for the window to be closed.
+ */
+ virtual void showStatus() = 0;
+
+ //! Popup menu
+ /*!
+ Popup a menu of operations at or around \c x,y and perform the
+ chosen operation.
+ */
+ virtual void runMenu(int x, int y) = 0;
+
+ //! Perform primary action
+ /*!
+ Perform the primary (default) action.
+ */
+ virtual void primaryAction() = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Lock receiver
+ /*!
+ Locks the receiver from changing state. The receiver should be
+ locked when querying it's state to ensure consistent results.
+ Each call to \c lock() must have a matching \c unlock() and
+ locks cannot be nested.
+ */
+ virtual void lock() const = 0;
+
+ //! Unlock receiver
+ virtual void unlock() const = 0;
+
+ //! Get icon
+ /*!
+ Returns the icon to display in the task bar. The interface
+ to set the icon is left to subclasses. Getting and setting
+ the icon must be thread safe.
+ */
+ virtual const Icon getIcon() const = 0;
+
+ //! Get tooltip
+ /*!
+ Returns the tool tip to display in the task bar. The interface
+ to set the tooltip is left to sublclasses. Getting and setting
+ the icon must be thread safe.
+ */
+ virtual std::string getToolTip() const = 0;
+
+ virtual void updateStatus(INode*, const String& errorMsg) = 0;
+
+ virtual void cleanup() {}
+
+ //@}
+};
diff --git a/src/lib/arch/IArchTime.h b/src/lib/arch/IArchTime.h
new file mode 100644
index 0000000..abb3cdd
--- /dev/null
+++ b/src/lib/arch/IArchTime.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 "common/IInterface.h"
+
+//! Interface for architecture dependent time operations
+/*!
+This interface defines the time operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchTime : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Get the current time
+ /*!
+ Returns the number of seconds since some arbitrary starting time.
+ This should return as high a precision as reasonable.
+ */
+ virtual double time() = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/XArch.h b/src/lib/arch/XArch.h
new file mode 100644
index 0000000..457c620
--- /dev/null
+++ b/src/lib/arch/XArch.h
@@ -0,0 +1,161 @@
+/*
+ * 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/common.h"
+#include "common/stdstring.h"
+#include "common/stdexcept.h"
+
+//! Generic thread exception
+/*!
+Exceptions derived from this class are used by the multithreading
+library to perform stack unwinding when a thread terminates. These
+exceptions must always be rethrown by clients when caught.
+*/
+class XThread { };
+
+//! Thread exception to cancel
+/*!
+Thrown to cancel a thread. Clients must not throw this type, but
+must rethrow it if caught (by XThreadCancel, XThread, or ...).
+*/
+class XThreadCancel : public XThread { };
+
+/*!
+\def RETHROW_XTHREAD
+Convenience macro to rethrow an XThread exception but ignore other
+exceptions. Put this in your catch (...) handler after necessary
+cleanup but before leaving or returning from the handler.
+*/
+#define RETHROW_XTHREAD \
+ try { throw; } catch (XThread&) { throw; } catch (...) { }
+
+//! Lazy error message string evaluation
+/*!
+This class encapsulates platform dependent error string lookup.
+Platforms subclass this type, taking an appropriate error code
+type in the c'tor and overriding eval() to return the error
+string for that error code.
+*/
+class XArchEval {
+public:
+ XArchEval() { }
+ virtual ~XArchEval() _NOEXCEPT { }
+
+ virtual std::string eval() const = 0;
+};
+
+//! Generic exception architecture dependent library
+class XArch : public std::runtime_error {
+public:
+ XArch(XArchEval* adopted) : std::runtime_error(adopted->eval()) { delete adopted; }
+ XArch(const std::string& msg) : std::runtime_error(msg) { }
+ virtual ~XArch() _NOEXCEPT { }
+};
+
+// Macro to declare XArch derived types
+#define XARCH_SUBCLASS(name_, super_) \
+class name_ : public super_ { \
+public: \
+ name_(XArchEval* adoptedEvaluator) : super_(adoptedEvaluator) { } \
+ name_(const std::string& msg) : super_(msg) { } \
+}
+
+//! Generic network exception
+/*!
+Exceptions derived from this class are used by the networking
+library to indicate various errors.
+*/
+XARCH_SUBCLASS(XArchNetwork, XArch);
+
+//! Operation was interrupted
+XARCH_SUBCLASS(XArchNetworkInterrupted, XArchNetwork);
+
+//! Network insufficient permission
+XARCH_SUBCLASS(XArchNetworkAccess, XArchNetwork);
+
+//! Network insufficient resources
+XARCH_SUBCLASS(XArchNetworkResource, XArchNetwork);
+
+//! No support for requested network resource/service
+XARCH_SUBCLASS(XArchNetworkSupport, XArchNetwork);
+
+//! Network I/O error
+XARCH_SUBCLASS(XArchNetworkIO, XArchNetwork);
+
+//! Network address is unavailable or not local
+XARCH_SUBCLASS(XArchNetworkNoAddress, XArchNetwork);
+
+//! Network address in use
+XARCH_SUBCLASS(XArchNetworkAddressInUse, XArchNetwork);
+
+//! No route to address
+XARCH_SUBCLASS(XArchNetworkNoRoute, XArchNetwork);
+
+//! Socket not connected
+XARCH_SUBCLASS(XArchNetworkNotConnected, XArchNetwork);
+
+//! Remote read end of socket has closed
+XARCH_SUBCLASS(XArchNetworkShutdown, XArchNetwork);
+
+//! Remote end of socket has disconnected
+XARCH_SUBCLASS(XArchNetworkDisconnected, XArchNetwork);
+
+//! Remote end of socket refused connection
+XARCH_SUBCLASS(XArchNetworkConnectionRefused, XArchNetwork);
+
+//! Remote end of socket is not responding
+XARCH_SUBCLASS(XArchNetworkTimedOut, XArchNetwork);
+
+//! Generic network name lookup erros
+XARCH_SUBCLASS(XArchNetworkName, XArchNetwork);
+
+//! The named host is unknown
+XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName);
+
+//! The named host is known but has no address
+XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName);
+
+//! Non-recoverable name server error
+XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName);
+
+//! Temporary name server error
+XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName);
+
+//! The named host is known but no supported address
+XARCH_SUBCLASS(XArchNetworkNameUnsupported, XArchNetworkName);
+
+//! Generic daemon exception
+/*!
+Exceptions derived from this class are used by the daemon
+library to indicate various errors.
+*/
+XARCH_SUBCLASS(XArchDaemon, XArch);
+
+//! Could not daemonize
+XARCH_SUBCLASS(XArchDaemonFailed, XArchDaemon);
+
+//! Could not install daemon
+XARCH_SUBCLASS(XArchDaemonInstallFailed, XArchDaemon);
+
+//! Could not uninstall daemon
+XARCH_SUBCLASS(XArchDaemonUninstallFailed, XArchDaemon);
+
+//! Attempted to uninstall a daemon that was not installed
+XARCH_SUBCLASS(XArchDaemonUninstallNotInstalled, XArchDaemonUninstallFailed);
diff --git a/src/lib/arch/multibyte.h b/src/lib/arch/multibyte.h
new file mode 100644
index 0000000..4a4e0ec
--- /dev/null
+++ b/src/lib/arch/multibyte.h
@@ -0,0 +1,56 @@
+/*
+ * 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/common.h"
+#include "arch/Arch.h"
+
+#include <climits>
+#include <cstring>
+#include <cstdlib>
+#if HAVE_LOCALE_H
+# include <locale.h>
+#endif
+#if HAVE_WCHAR_H || defined(_MSC_VER)
+# include <wchar.h>
+#elif __APPLE__
+ // wtf? Darwin puts mbtowc() et al. in stdlib
+# include <cstdlib>
+#else
+ // platform apparently has no wchar_t support. provide dummy
+ // implementations. hopefully at least the C++ compiler has
+ // a built-in wchar_t type.
+
+static inline
+int
+mbtowc(wchar_t* dst, const char* src, int n)
+{
+ *dst = static_cast<wchar_t>(*src);
+ return 1;
+}
+
+static inline
+int
+wctomb(char* dst, wchar_t src)
+{
+ *dst = static_cast<char>(src);
+ return 1;
+}
+
+#endif
diff --git a/src/lib/arch/unix/ArchConsoleUnix.cpp b/src/lib/arch/unix/ArchConsoleUnix.cpp
new file mode 100644
index 0000000..79a4634
--- /dev/null
+++ b/src/lib/arch/unix/ArchConsoleUnix.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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 "arch/unix/ArchConsoleUnix.h"
+
+ArchConsoleUnix::ArchConsoleUnix() { }
+
+ArchConsoleUnix::~ArchConsoleUnix() { }
diff --git a/src/lib/arch/unix/ArchConsoleUnix.h b/src/lib/arch/unix/ArchConsoleUnix.h
new file mode 100644
index 0000000..8326ab5
--- /dev/null
+++ b/src/lib/arch/unix/ArchConsoleUnix.h
@@ -0,0 +1,29 @@
+/*
+ * 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 "arch/ArchConsoleStd.h"
+
+#define ARCH_CONSOLE ArchConsoleUnix
+
+class ArchConsoleUnix : public ArchConsoleStd {
+public:
+ ArchConsoleUnix();
+ virtual ~ArchConsoleUnix();
+};
diff --git a/src/lib/arch/unix/ArchDaemonUnix.cpp b/src/lib/arch/unix/ArchDaemonUnix.cpp
new file mode 100644
index 0000000..a03bf7a
--- /dev/null
+++ b/src/lib/arch/unix/ArchDaemonUnix.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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 "arch/unix/ArchDaemonUnix.h"
+
+#include "arch/unix/XArchUnix.h"
+#include "base/Log.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <cstdlib>
+
+//
+// ArchDaemonUnix
+//
+
+ArchDaemonUnix::ArchDaemonUnix()
+{
+ // do nothing
+}
+
+ArchDaemonUnix::~ArchDaemonUnix()
+{
+ // do nothing
+}
+
+
+#ifdef __APPLE__
+
+// In Mac OS X, fork()'d child processes can't use most APIs (the frameworks
+// that Barrier uses in fact prevent it and make the process just up and die),
+// so need to exec a copy of the program that doesn't fork so isn't limited.
+int
+execSelfNonDaemonized()
+{
+ extern char** NXArgv;
+ char** selfArgv = NXArgv;
+
+ setenv("_BARRIER_DAEMONIZED", "", 1);
+
+ execvp(selfArgv[0], selfArgv);
+ return 0;
+}
+
+bool alreadyDaemonized() {
+ return getenv("_BARRIER_DAEMONIZED") != NULL;
+}
+
+#endif
+
+int
+ArchDaemonUnix::daemonize(const char* name, DaemonFunc func)
+{
+#ifdef __APPLE__
+ if (alreadyDaemonized())
+ return func(1, &name);
+#endif
+
+ // fork so shell thinks we're done and so we're not a process
+ // group leader
+ switch (fork()) {
+ case -1:
+ // failed
+ throw XArchDaemonFailed(new XArchEvalUnix(errno));
+
+ case 0:
+ // child
+ break;
+
+ default:
+ // parent exits
+ exit(0);
+ }
+
+ // become leader of a new session
+ setsid();
+
+#ifndef __APPLE__
+ // NB: don't run chdir on apple; causes strange behaviour.
+ // chdir to root so we don't keep mounted filesystems points busy
+ // TODO: this is a bit of a hack - can we find a better solution?
+ int chdirErr = chdir("/");
+ if (chdirErr)
+ // NB: file logging actually isn't working at this point!
+ LOG((CLOG_ERR "chdir error: %i", chdirErr));
+#endif
+
+ // mask off permissions for any but owner
+ umask(077);
+
+ // close open files. we only expect stdin, stdout, stderr to be open.
+ close(0);
+ close(1);
+ close(2);
+
+ // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use
+ // of standard I/O safely goes in the bit bucket.
+ open("/dev/null", O_RDONLY);
+ open("/dev/null", O_RDWR);
+
+ int dupErr = dup(1);
+
+ if (dupErr < 0) {
+ // NB: file logging actually isn't working at this point!
+ LOG((CLOG_ERR "dup error: %i", dupErr));
+ }
+
+#ifdef __APPLE__
+ return execSelfNonDaemonized();
+#endif
+
+ // invoke function
+ return func(1, &name);
+}
diff --git a/src/lib/arch/unix/ArchDaemonUnix.h b/src/lib/arch/unix/ArchDaemonUnix.h
new file mode 100644
index 0000000..530159a
--- /dev/null
+++ b/src/lib/arch/unix/ArchDaemonUnix.h
@@ -0,0 +1,36 @@
+/*
+ * 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 "arch/ArchDaemonNone.h"
+
+#undef ARCH_DAEMON
+#define ARCH_DAEMON ArchDaemonUnix
+
+//! Unix implementation of IArchDaemon
+class ArchDaemonUnix : public ArchDaemonNone {
+public:
+ ArchDaemonUnix();
+ virtual ~ArchDaemonUnix();
+
+ // IArchDaemon overrides
+ virtual int daemonize(const char* name, DaemonFunc func);
+};
+
+#define CONFIG_FILE "/etc/barrier/barrierd.conf"
diff --git a/src/lib/arch/unix/ArchFileUnix.cpp b/src/lib/arch/unix/ArchFileUnix.cpp
new file mode 100644
index 0000000..d9ae8b2
--- /dev/null
+++ b/src/lib/arch/unix/ArchFileUnix.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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 "arch/unix/ArchFileUnix.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <cstring>
+
+//
+// ArchFileUnix
+//
+
+ArchFileUnix::ArchFileUnix()
+{
+ // do nothing
+}
+
+ArchFileUnix::~ArchFileUnix()
+{
+ // do nothing
+}
+
+const char*
+ArchFileUnix::getBasename(const char* pathname)
+{
+ if (pathname == NULL) {
+ return NULL;
+ }
+
+ const char* basename = strrchr(pathname, '/');
+ if (basename != NULL) {
+ return basename + 1;
+ }
+ else {
+ return pathname;
+ }
+}
+
+std::string
+ArchFileUnix::getUserDirectory()
+{
+ char* buffer = NULL;
+ std::string dir;
+#if HAVE_GETPWUID_R
+ struct passwd pwent;
+ struct passwd* pwentp;
+#if defined(_SC_GETPW_R_SIZE_MAX)
+ long size = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (size == -1) {
+ size = BUFSIZ;
+ }
+#else
+ long size = BUFSIZ;
+#endif
+ buffer = new char[size];
+ getpwuid_r(getuid(), &pwent, buffer, size, &pwentp);
+#else
+ struct passwd* pwentp = getpwuid(getuid());
+#endif
+ if (pwentp != NULL && pwentp->pw_dir != NULL) {
+ dir = pwentp->pw_dir;
+ }
+ delete[] buffer;
+ return dir;
+}
+
+std::string
+ArchFileUnix::getSystemDirectory()
+{
+ return "/etc";
+}
+
+std::string
+ArchFileUnix::getInstalledDirectory()
+{
+#if WINAPI_XWINDOWS
+ return "/usr/bin";
+#else
+ return "/Applications/Barrier.app/Contents/MacOS";
+#endif
+}
+
+std::string
+ArchFileUnix::getLogDirectory()
+{
+ return "/var/log";
+}
+
+std::string
+ArchFileUnix::getPluginDirectory()
+{
+ if (!m_pluginDirectory.empty()) {
+ return m_pluginDirectory;
+ }
+
+#if WINAPI_XWINDOWS
+ return getProfileDirectory().append("/plugins");
+#else
+ return getProfileDirectory().append("/Plugins");
+#endif
+}
+
+std::string
+ArchFileUnix::getProfileDirectory()
+{
+ String dir;
+ if (!m_profileDirectory.empty()) {
+ dir = m_profileDirectory;
+ }
+ else {
+#if WINAPI_XWINDOWS
+ dir = getUserDirectory().append("/.barrier");
+#else
+ dir = getUserDirectory().append("/Library/Application Support/Barrier");
+#endif
+ }
+ return dir;
+
+}
+
+std::string
+ArchFileUnix::concatPath(const std::string& prefix,
+ const std::string& suffix)
+{
+ std::string path;
+ path.reserve(prefix.size() + 1 + suffix.size());
+ path += prefix;
+ if (path.size() == 0 || path[path.size() - 1] != '/') {
+ path += '/';
+ }
+ path += suffix;
+ return path;
+}
+
+void
+ArchFileUnix::setProfileDirectory(const String& s)
+{
+ m_profileDirectory = s;
+}
+
+void
+ArchFileUnix::setPluginDirectory(const String& s)
+{
+ m_pluginDirectory = s;
+}
diff --git a/src/lib/arch/unix/ArchFileUnix.h b/src/lib/arch/unix/ArchFileUnix.h
new file mode 100644
index 0000000..86dea0c
--- /dev/null
+++ b/src/lib/arch/unix/ArchFileUnix.h
@@ -0,0 +1,47 @@
+/*
+ * 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 "arch/IArchFile.h"
+
+#define ARCH_FILE ArchFileUnix
+
+//! Unix implementation of IArchFile
+class ArchFileUnix : public IArchFile {
+public:
+ ArchFileUnix();
+ virtual ~ArchFileUnix();
+
+ // IArchFile overrides
+ virtual const char* getBasename(const char* pathname);
+ virtual std::string getUserDirectory();
+ virtual std::string getSystemDirectory();
+ virtual std::string getInstalledDirectory();
+ virtual std::string getLogDirectory();
+ virtual std::string getPluginDirectory();
+ virtual std::string getProfileDirectory();
+ virtual std::string concatPath(const std::string& prefix,
+ const std::string& suffix);
+ virtual void setProfileDirectory(const String& s);
+ virtual void setPluginDirectory(const String& s);
+
+private:
+ String m_profileDirectory;
+ String m_pluginDirectory;
+};
diff --git a/src/lib/arch/unix/ArchInternetUnix.cpp b/src/lib/arch/unix/ArchInternetUnix.cpp
new file mode 100644
index 0000000..fd1e135
--- /dev/null
+++ b/src/lib/arch/unix/ArchInternetUnix.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 "arch/unix/ArchInternetUnix.h"
+
+#include "arch/XArch.h"
+#include "common/Version.h"
+#include "base/Log.h"
+
+#include <sstream>
+#include <curl/curl.h>
+
+class CurlFacade {
+public:
+ CurlFacade();
+ ~CurlFacade();
+ String get(const String& url);
+ String urlEncode(const String& url);
+
+private:
+ CURL* m_curl;
+};
+
+//
+// ArchInternetUnix
+//
+
+String
+ArchInternetUnix::get(const String& url)
+{
+ CurlFacade curl;
+ return curl.get(url);
+}
+
+String
+ArchInternetUnix::urlEncode(const String& url)
+{
+ CurlFacade curl;
+ return curl.urlEncode(url);
+}
+
+//
+// CurlFacade
+//
+
+static size_t
+curlWriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
+{
+ ((std::string*)userp)->append((char*)contents, size * nmemb);
+ return size * nmemb;
+}
+
+CurlFacade::CurlFacade() :
+ m_curl(NULL)
+{
+ CURLcode init = curl_global_init(CURL_GLOBAL_ALL);
+ if (init != CURLE_OK) {
+ throw XArch("CURL global init failed.");
+ }
+
+ m_curl = curl_easy_init();
+ if (m_curl == NULL) {
+ throw XArch("CURL easy init failed.");
+ }
+}
+
+CurlFacade::~CurlFacade()
+{
+ if (m_curl != NULL) {
+ curl_easy_cleanup(m_curl);
+ }
+
+ curl_global_cleanup();
+}
+
+String
+CurlFacade::get(const String& url)
+{
+ curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
+
+ std::stringstream userAgent;
+ userAgent << "Barrier ";
+ userAgent << kVersion;
+ curl_easy_setopt(m_curl, CURLOPT_USERAGENT, userAgent.str().c_str());
+
+ std::string result;
+ curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &result);
+
+ CURLcode code = curl_easy_perform(m_curl);
+ if (code != CURLE_OK) {
+ LOG((CLOG_ERR "curl perform error: %s", curl_easy_strerror(code)));
+ throw XArch("CURL perform failed.");
+ }
+
+ return result;
+}
+
+String
+CurlFacade::urlEncode(const String& url)
+{
+ char* resultCStr = curl_easy_escape(m_curl, url.c_str(), 0);
+
+ if (resultCStr == NULL) {
+ throw XArch("CURL escape failed.");
+ }
+
+ std::string result(resultCStr);
+ curl_free(resultCStr);
+
+ return result;
+}
diff --git a/src/lib/arch/unix/ArchInternetUnix.h b/src/lib/arch/unix/ArchInternetUnix.h
new file mode 100644
index 0000000..2413d8f
--- /dev/null
+++ b/src/lib/arch/unix/ArchInternetUnix.h
@@ -0,0 +1,28 @@
+/*
+ * 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
+
+#define ARCH_INTERNET ArchInternetUnix
+
+#include "base/String.h"
+
+class ArchInternetUnix {
+public:
+ String get(const String& url);
+ String urlEncode(const String& url);
+};
diff --git a/src/lib/arch/unix/ArchLogUnix.cpp b/src/lib/arch/unix/ArchLogUnix.cpp
new file mode 100644
index 0000000..b1f9089
--- /dev/null
+++ b/src/lib/arch/unix/ArchLogUnix.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "arch/unix/ArchLogUnix.h"
+
+#include <syslog.h>
+
+//
+// ArchLogUnix
+//
+
+ArchLogUnix::ArchLogUnix()
+{
+ // do nothing
+}
+
+ArchLogUnix::~ArchLogUnix()
+{
+ // do nothing
+}
+
+void
+ArchLogUnix::openLog(const char* name)
+{
+ openlog(name, 0, LOG_DAEMON);
+}
+
+void
+ArchLogUnix::closeLog()
+{
+ closelog();
+}
+
+void
+ArchLogUnix::showLog(bool)
+{
+ // do nothing
+}
+
+void
+ArchLogUnix::writeLog(ELevel level, const char* msg)
+{
+ // convert level
+ int priority;
+ switch (level) {
+ case kERROR:
+ priority = LOG_ERR;
+ break;
+
+ case kWARNING:
+ priority = LOG_WARNING;
+ break;
+
+ case kNOTE:
+ priority = LOG_NOTICE;
+ break;
+
+ case kINFO:
+ priority = LOG_INFO;
+ break;
+
+ default:
+ priority = LOG_DEBUG;
+ break;
+ }
+
+ // log it
+ syslog(priority, "%s", msg);
+}
diff --git a/src/lib/arch/unix/ArchLogUnix.h b/src/lib/arch/unix/ArchLogUnix.h
new file mode 100644
index 0000000..cdd733f
--- /dev/null
+++ b/src/lib/arch/unix/ArchLogUnix.h
@@ -0,0 +1,36 @@
+/*
+ * 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 "arch/IArchLog.h"
+
+#define ARCH_LOG ArchLogUnix
+
+//! Unix implementation of IArchLog
+class ArchLogUnix : public IArchLog {
+public:
+ ArchLogUnix();
+ virtual ~ArchLogUnix();
+
+ // IArchLog overrides
+ virtual void openLog(const char* name);
+ virtual void closeLog();
+ virtual void showLog(bool);
+ virtual void writeLog(ELevel, const char*);
+};
diff --git a/src/lib/arch/unix/ArchMultithreadPosix.cpp b/src/lib/arch/unix/ArchMultithreadPosix.cpp
new file mode 100644
index 0000000..ade6c51
--- /dev/null
+++ b/src/lib/arch/unix/ArchMultithreadPosix.cpp
@@ -0,0 +1,812 @@
+/*
+ * 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 "arch/unix/ArchMultithreadPosix.h"
+
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+
+#include <signal.h>
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#include <cerrno>
+
+#define SIGWAKEUP SIGUSR1
+
+#if !HAVE_PTHREAD_SIGNAL
+ // boy, is this platform broken. forget about pthread signal
+ // handling and let signals through to every process. barrier
+ // will not terminate cleanly when it gets SIGTERM or SIGINT.
+# define pthread_sigmask sigprocmask
+# define pthread_kill(tid_, sig_) kill(0, (sig_))
+# define sigwait(set_, sig_)
+# undef HAVE_POSIX_SIGWAIT
+# define HAVE_POSIX_SIGWAIT 1
+#endif
+
+static
+void
+setSignalSet(sigset_t* sigset)
+{
+ sigemptyset(sigset);
+ sigaddset(sigset, SIGHUP);
+ sigaddset(sigset, SIGINT);
+ sigaddset(sigset, SIGTERM);
+ sigaddset(sigset, SIGUSR2);
+}
+
+//
+// ArchThreadImpl
+//
+
+class ArchThreadImpl {
+public:
+ ArchThreadImpl();
+
+public:
+ int m_refCount;
+ IArchMultithread::ThreadID m_id;
+ pthread_t m_thread;
+ IArchMultithread::ThreadFunc m_func;
+ void* m_userData;
+ bool m_cancel;
+ bool m_cancelling;
+ bool m_exited;
+ void* m_result;
+ void* m_networkData;
+};
+
+ArchThreadImpl::ArchThreadImpl() :
+ m_refCount(1),
+ m_id(0),
+ m_func(NULL),
+ m_userData(NULL),
+ m_cancel(false),
+ m_cancelling(false),
+ m_exited(false),
+ m_result(NULL),
+ m_networkData(NULL)
+{
+ // do nothing
+}
+
+
+//
+// ArchMultithreadPosix
+//
+
+ArchMultithreadPosix* ArchMultithreadPosix::s_instance = NULL;
+
+ArchMultithreadPosix::ArchMultithreadPosix() :
+ m_newThreadCalled(false),
+ m_nextID(0)
+{
+ assert(s_instance == NULL);
+
+ s_instance = this;
+
+ // no signal handlers
+ for (size_t i = 0; i < kNUM_SIGNALS; ++i) {
+ m_signalFunc[i] = NULL;
+ m_signalUserData[i] = NULL;
+ }
+
+ // create mutex for thread list
+ m_threadMutex = newMutex();
+
+ // create thread for calling (main) thread and add it to our
+ // list. no need to lock the mutex since we're the only thread.
+ m_mainThread = new ArchThreadImpl;
+ m_mainThread->m_thread = pthread_self();
+ insert(m_mainThread);
+
+ // install SIGWAKEUP handler. this causes SIGWAKEUP to interrupt
+ // system calls. we use that when cancelling a thread to force it
+ // to wake up immediately if it's blocked in a system call. we
+ // won't need this until another thread is created but it's fine
+ // to install it now.
+ struct sigaction act;
+ sigemptyset(&act.sa_mask);
+# if defined(SA_INTERRUPT)
+ act.sa_flags = SA_INTERRUPT;
+# else
+ act.sa_flags = 0;
+# endif
+ act.sa_handler = &threadCancel;
+ sigaction(SIGWAKEUP, &act, NULL);
+
+ // set desired signal dispositions. let SIGWAKEUP through but
+ // ignore SIGPIPE (we'll handle EPIPE).
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGWAKEUP);
+ pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGPIPE);
+ pthread_sigmask(SIG_BLOCK, &sigset, NULL);
+}
+
+ArchMultithreadPosix::~ArchMultithreadPosix()
+{
+ assert(s_instance != NULL);
+
+ closeMutex(m_threadMutex);
+ s_instance = NULL;
+}
+
+void
+ArchMultithreadPosix::setNetworkDataForCurrentThread(void* data)
+{
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = find(pthread_self());
+ thread->m_networkData = data;
+ unlockMutex(m_threadMutex);
+}
+
+void*
+ArchMultithreadPosix::getNetworkDataForThread(ArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* data = thread->m_networkData;
+ unlockMutex(m_threadMutex);
+ return data;
+}
+
+ArchMultithreadPosix*
+ArchMultithreadPosix::getInstance()
+{
+ return s_instance;
+}
+
+ArchCond
+ArchMultithreadPosix::newCondVar()
+{
+ ArchCondImpl* cond = new ArchCondImpl;
+ int status = pthread_cond_init(&cond->m_cond, NULL);
+ (void)status;
+ assert(status == 0);
+ return cond;
+}
+
+void
+ArchMultithreadPosix::closeCondVar(ArchCond cond)
+{
+ int status = pthread_cond_destroy(&cond->m_cond);
+ (void)status;
+ assert(status == 0);
+ delete cond;
+}
+
+void
+ArchMultithreadPosix::signalCondVar(ArchCond cond)
+{
+ int status = pthread_cond_signal(&cond->m_cond);
+ (void)status;
+ assert(status == 0);
+}
+
+void
+ArchMultithreadPosix::broadcastCondVar(ArchCond cond)
+{
+ int status = pthread_cond_broadcast(&cond->m_cond);
+ (void)status;
+ assert(status == 0);
+}
+
+bool
+ArchMultithreadPosix::waitCondVar(ArchCond cond,
+ ArchMutex mutex, double timeout)
+{
+ // we can't wait on a condition variable and also wake it up for
+ // cancellation since we don't use posix cancellation. so we
+ // must wake up periodically to check for cancellation. we
+ // can't simply go back to waiting after the check since the
+ // condition may have changed and we'll have lost the signal.
+ // so we have to return to the caller. since the caller will
+ // always check for spurious wakeups the only drawback here is
+ // performance: we're waking up a lot more than desired.
+ static const double maxCancellationLatency = 0.1;
+ if (timeout < 0.0 || timeout > maxCancellationLatency) {
+ timeout = maxCancellationLatency;
+ }
+
+ // see if we should cancel this thread
+ testCancelThread();
+
+ // get final time
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ struct timespec finalTime;
+ finalTime.tv_sec = now.tv_sec;
+ finalTime.tv_nsec = now.tv_usec * 1000;
+ long timeout_sec = (long)timeout;
+ long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec));
+ finalTime.tv_sec += timeout_sec;
+ finalTime.tv_nsec += timeout_nsec;
+ if (finalTime.tv_nsec >= 1000000000) {
+ finalTime.tv_nsec -= 1000000000;
+ finalTime.tv_sec += 1;
+ }
+
+ // wait
+ int status = pthread_cond_timedwait(&cond->m_cond,
+ &mutex->m_mutex, &finalTime);
+
+ // check for cancel again
+ testCancelThread();
+
+ switch (status) {
+ case 0:
+ // success
+ return true;
+
+ case ETIMEDOUT:
+ return false;
+
+ default:
+ assert(0 && "condition variable wait error");
+ return false;
+ }
+}
+
+ArchMutex
+ArchMultithreadPosix::newMutex()
+{
+ pthread_mutexattr_t attr;
+ int status = pthread_mutexattr_init(&attr);
+ assert(status == 0);
+ ArchMutexImpl* mutex = new ArchMutexImpl;
+ status = pthread_mutex_init(&mutex->m_mutex, &attr);
+ assert(status == 0);
+ return mutex;
+}
+
+void
+ArchMultithreadPosix::closeMutex(ArchMutex mutex)
+{
+ int status = pthread_mutex_destroy(&mutex->m_mutex);
+ (void)status;
+ assert(status == 0);
+ delete mutex;
+}
+
+void
+ArchMultithreadPosix::lockMutex(ArchMutex mutex)
+{
+ int status = pthread_mutex_lock(&mutex->m_mutex);
+
+ switch (status) {
+ case 0:
+ // success
+ return;
+
+ case EDEADLK:
+ assert(0 && "lock already owned");
+ break;
+
+ case EAGAIN:
+ assert(0 && "too many recursive locks");
+ break;
+
+ default:
+ assert(0 && "unexpected error");
+ break;
+ }
+}
+
+void
+ArchMultithreadPosix::unlockMutex(ArchMutex mutex)
+{
+ int status = pthread_mutex_unlock(&mutex->m_mutex);
+
+ switch (status) {
+ case 0:
+ // success
+ return;
+
+ case EPERM:
+ assert(0 && "thread doesn't own a lock");
+ break;
+
+ default:
+ assert(0 && "unexpected error");
+ break;
+ }
+}
+
+ArchThread
+ArchMultithreadPosix::newThread(ThreadFunc func, void* data)
+{
+ assert(func != NULL);
+
+ // initialize signal handler. we do this here instead of the
+ // constructor so we can avoid daemonizing (using fork())
+ // when there are multiple threads. clients can safely
+ // use condition variables and mutexes before creating a
+ // new thread and they can safely use the only thread
+ // they have access to, the main thread, so they really
+ // can't tell the difference.
+ if (!m_newThreadCalled) {
+ m_newThreadCalled = true;
+#if HAVE_PTHREAD_SIGNAL
+ startSignalHandler();
+#endif
+ }
+
+ lockMutex(m_threadMutex);
+
+ // create thread impl for new thread
+ ArchThreadImpl* thread = new ArchThreadImpl;
+ thread->m_func = func;
+ thread->m_userData = data;
+
+ // create the thread. pthread_create() on RedHat 7.2 smp fails
+ // if passed a NULL attr so use a default attr.
+ pthread_attr_t attr;
+ int status = pthread_attr_init(&attr);
+ if (status == 0) {
+ status = pthread_create(&thread->m_thread, &attr,
+ &ArchMultithreadPosix::threadFunc, thread);
+ pthread_attr_destroy(&attr);
+ }
+
+ // check if thread was started
+ if (status != 0) {
+ // failed to start thread so clean up
+ delete thread;
+ thread = NULL;
+ }
+ else {
+ // add thread to list
+ insert(thread);
+
+ // increment ref count to account for the thread itself
+ refThread(thread);
+ }
+
+ // note that the child thread will wait until we release this mutex
+ unlockMutex(m_threadMutex);
+
+ return thread;
+}
+
+ArchThread
+ArchMultithreadPosix::newCurrentThread()
+{
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = find(pthread_self());
+ unlockMutex(m_threadMutex);
+ assert(thread != NULL);
+ return thread;
+}
+
+void
+ArchMultithreadPosix::closeThread(ArchThread thread)
+{
+ assert(thread != NULL);
+
+ // decrement ref count and clean up thread if no more references
+ if (--thread->m_refCount == 0) {
+ // detach from thread (unless it's the main thread)
+ if (thread->m_func != NULL) {
+ pthread_detach(thread->m_thread);
+ }
+
+ // remove thread from list
+ lockMutex(m_threadMutex);
+ assert(findNoRef(thread->m_thread) == thread);
+ erase(thread);
+ unlockMutex(m_threadMutex);
+
+ // done with thread
+ delete thread;
+ }
+}
+
+ArchThread
+ArchMultithreadPosix::copyThread(ArchThread thread)
+{
+ refThread(thread);
+ return thread;
+}
+
+void
+ArchMultithreadPosix::cancelThread(ArchThread thread)
+{
+ assert(thread != NULL);
+
+ // set cancel and wakeup flags if thread can be cancelled
+ bool wakeup = false;
+ lockMutex(m_threadMutex);
+ if (!thread->m_exited && !thread->m_cancelling) {
+ thread->m_cancel = true;
+ wakeup = true;
+ }
+ unlockMutex(m_threadMutex);
+
+ // force thread to exit system calls if wakeup is true
+ if (wakeup) {
+ pthread_kill(thread->m_thread, SIGWAKEUP);
+ }
+}
+
+void
+ArchMultithreadPosix::setPriorityOfThread(ArchThread thread, int /*n*/)
+{
+ assert(thread != NULL);
+
+ // FIXME
+}
+
+void
+ArchMultithreadPosix::testCancelThread()
+{
+ // find current thread
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = findNoRef(pthread_self());
+ unlockMutex(m_threadMutex);
+
+ // test cancel on thread
+ testCancelThreadImpl(thread);
+}
+
+bool
+ArchMultithreadPosix::wait(ArchThread target, double timeout)
+{
+ assert(target != NULL);
+
+ lockMutex(m_threadMutex);
+
+ // find current thread
+ ArchThreadImpl* self = findNoRef(pthread_self());
+
+ // ignore wait if trying to wait on ourself
+ if (target == self) {
+ unlockMutex(m_threadMutex);
+ return false;
+ }
+
+ // ref the target so it can't go away while we're watching it
+ refThread(target);
+
+ unlockMutex(m_threadMutex);
+
+ try {
+ // do first test regardless of timeout
+ testCancelThreadImpl(self);
+ if (isExitedThread(target)) {
+ closeThread(target);
+ return true;
+ }
+
+ // wait and repeat test if there's a timeout
+ if (timeout != 0.0) {
+ const double start = ARCH->time();
+ do {
+ // wait a little
+ ARCH->sleep(0.05);
+
+ // repeat test
+ testCancelThreadImpl(self);
+ if (isExitedThread(target)) {
+ closeThread(target);
+ return true;
+ }
+
+ // repeat wait and test until timed out
+ } while (timeout < 0.0 || (ARCH->time() - start) <= timeout);
+ }
+
+ closeThread(target);
+ return false;
+ }
+ catch (...) {
+ closeThread(target);
+ throw;
+ }
+}
+
+bool
+ArchMultithreadPosix::isSameThread(ArchThread thread1, ArchThread thread2)
+{
+ return (thread1 == thread2);
+}
+
+bool
+ArchMultithreadPosix::isExitedThread(ArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ bool exited = thread->m_exited;
+ unlockMutex(m_threadMutex);
+ return exited;
+}
+
+void*
+ArchMultithreadPosix::getResultOfThread(ArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* result = thread->m_result;
+ unlockMutex(m_threadMutex);
+ return result;
+}
+
+IArchMultithread::ThreadID
+ArchMultithreadPosix::getIDOfThread(ArchThread thread)
+{
+ return thread->m_id;
+}
+
+void
+ArchMultithreadPosix::setSignalHandler(
+ ESignal signal, SignalFunc func, void* userData)
+{
+ lockMutex(m_threadMutex);
+ m_signalFunc[signal] = func;
+ m_signalUserData[signal] = userData;
+ unlockMutex(m_threadMutex);
+}
+
+void
+ArchMultithreadPosix::raiseSignal(ESignal signal)
+{
+ lockMutex(m_threadMutex);
+ if (m_signalFunc[signal] != NULL) {
+ m_signalFunc[signal](signal, m_signalUserData[signal]);
+ pthread_kill(m_mainThread->m_thread, SIGWAKEUP);
+ }
+ else if (signal == kINTERRUPT || signal == kTERMINATE) {
+ ARCH->cancelThread(m_mainThread);
+ }
+ unlockMutex(m_threadMutex);
+}
+
+void
+ArchMultithreadPosix::startSignalHandler()
+{
+ // set signal mask. the main thread blocks these signals and
+ // the signal handler thread will listen for them.
+ sigset_t sigset, oldsigset;
+ setSignalSet(&sigset);
+ pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset);
+
+ // fire up the INT and TERM signal handler thread. we could
+ // instead arrange to catch and handle these signals but
+ // we'd be unable to cancel the main thread since no pthread
+ // calls are allowed in a signal handler.
+ pthread_attr_t attr;
+ int status = pthread_attr_init(&attr);
+ if (status == 0) {
+ status = pthread_create(&m_signalThread, &attr,
+ &ArchMultithreadPosix::threadSignalHandler,
+ NULL);
+ pthread_attr_destroy(&attr);
+ }
+ if (status != 0) {
+ // can't create thread to wait for signal so don't block
+ // the signals.
+ pthread_sigmask(SIG_UNBLOCK, &oldsigset, NULL);
+ }
+}
+
+ArchThreadImpl*
+ArchMultithreadPosix::find(pthread_t thread)
+{
+ ArchThreadImpl* impl = findNoRef(thread);
+ if (impl != NULL) {
+ refThread(impl);
+ }
+ return impl;
+}
+
+ArchThreadImpl*
+ArchMultithreadPosix::findNoRef(pthread_t thread)
+{
+ // linear search
+ for (ThreadList::const_iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if ((*index)->m_thread == thread) {
+ return *index;
+ }
+ }
+ return NULL;
+}
+
+void
+ArchMultithreadPosix::insert(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+
+ // thread shouldn't already be on the list
+ assert(findNoRef(thread->m_thread) == NULL);
+
+ // set thread id. note that we don't worry about m_nextID
+ // wrapping back to 0 and duplicating thread ID's since the
+ // likelihood of barrier running that long is vanishingly
+ // small.
+ thread->m_id = ++m_nextID;
+
+ // append to list
+ m_threadList.push_back(thread);
+}
+
+void
+ArchMultithreadPosix::erase(ArchThreadImpl* thread)
+{
+ for (ThreadList::iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if (*index == thread) {
+ m_threadList.erase(index);
+ break;
+ }
+ }
+}
+
+void
+ArchMultithreadPosix::refThread(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+ assert(findNoRef(thread->m_thread) != NULL);
+ ++thread->m_refCount;
+}
+
+void
+ArchMultithreadPosix::testCancelThreadImpl(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+
+ // update cancel state
+ lockMutex(m_threadMutex);
+ bool cancel = false;
+ if (thread->m_cancel && !thread->m_cancelling) {
+ thread->m_cancelling = true;
+ thread->m_cancel = false;
+ cancel = true;
+ }
+ unlockMutex(m_threadMutex);
+
+ // unwind thread's stack if cancelling
+ if (cancel) {
+ throw XThreadCancel();
+ }
+}
+
+void*
+ArchMultithreadPosix::threadFunc(void* vrep)
+{
+ // get the thread
+ ArchThreadImpl* thread = static_cast<ArchThreadImpl*>(vrep);
+
+ // setup pthreads
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+
+ // run thread
+ s_instance->doThreadFunc(thread);
+
+ // terminate the thread
+ return NULL;
+}
+
+void
+ArchMultithreadPosix::doThreadFunc(ArchThread thread)
+{
+ // default priority is slightly below normal
+ setPriorityOfThread(thread, 1);
+
+ // wait for parent to initialize this object
+ lockMutex(m_threadMutex);
+ unlockMutex(m_threadMutex);
+
+ void* result = NULL;
+ try {
+ // go
+ result = (*thread->m_func)(thread->m_userData);
+ }
+
+ catch (XThreadCancel&) {
+ // client called cancel()
+ }
+ catch (...) {
+ // note -- don't catch (...) to avoid masking bugs
+ lockMutex(m_threadMutex);
+ thread->m_exited = true;
+ unlockMutex(m_threadMutex);
+ closeThread(thread);
+ throw;
+ }
+
+ // thread has exited
+ lockMutex(m_threadMutex);
+ thread->m_result = result;
+ thread->m_exited = true;
+ unlockMutex(m_threadMutex);
+
+ // done with thread
+ closeThread(thread);
+}
+
+void
+ArchMultithreadPosix::threadCancel(int)
+{
+ // do nothing
+}
+
+void*
+ArchMultithreadPosix::threadSignalHandler(void*)
+{
+ // detach
+ pthread_detach(pthread_self());
+
+ // add signal to mask
+ sigset_t sigset;
+ setSignalSet(&sigset);
+
+ // also wait on SIGABRT. on linux (others?) this thread (process)
+ // will persist after all the other threads evaporate due to an
+ // assert unless we wait on SIGABRT. that means our resources (like
+ // the socket we're listening on) are not released and never will be
+ // until the lingering thread is killed. i don't know why sigwait()
+ // should protect the thread from being killed. note that sigwait()
+ // doesn't actually return if we receive SIGABRT and, for some
+ // reason, we don't have to block SIGABRT.
+ sigaddset(&sigset, SIGABRT);
+
+ // we exit the loop via thread cancellation in sigwait()
+ for (;;) {
+ // wait
+#if HAVE_POSIX_SIGWAIT
+ int signal = 0;
+ sigwait(&sigset, &signal);
+#else
+ sigwait(&sigset);
+#endif
+
+ // if we get here then the signal was raised
+ switch (signal) {
+ case SIGINT:
+ ARCH->raiseSignal(kINTERRUPT);
+ break;
+
+ case SIGTERM:
+ ARCH->raiseSignal(kTERMINATE);
+ break;
+
+ case SIGHUP:
+ ARCH->raiseSignal(kHANGUP);
+ break;
+
+ case SIGUSR2:
+ ARCH->raiseSignal(kUSER);
+ break;
+
+ default:
+ // ignore
+ break;
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/lib/arch/unix/ArchMultithreadPosix.h b/src/lib/arch/unix/ArchMultithreadPosix.h
new file mode 100644
index 0000000..98b5eda
--- /dev/null
+++ b/src/lib/arch/unix/ArchMultithreadPosix.h
@@ -0,0 +1,115 @@
+/*
+ * 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 "arch/IArchMultithread.h"
+#include "common/stdlist.h"
+
+#include <pthread.h>
+
+#define ARCH_MULTITHREAD ArchMultithreadPosix
+
+class ArchCondImpl {
+public:
+ pthread_cond_t m_cond;
+};
+
+class ArchMutexImpl {
+public:
+ pthread_mutex_t m_mutex;
+};
+
+//! Posix implementation of IArchMultithread
+class ArchMultithreadPosix : public IArchMultithread {
+public:
+ ArchMultithreadPosix();
+ virtual ~ArchMultithreadPosix();
+
+ //! @name manipulators
+ //@{
+
+ void setNetworkDataForCurrentThread(void*);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ void* getNetworkDataForThread(ArchThread);
+
+ static ArchMultithreadPosix* getInstance();
+
+ //@}
+
+ // IArchMultithread overrides
+ virtual ArchCond newCondVar();
+ virtual void closeCondVar(ArchCond);
+ virtual void signalCondVar(ArchCond);
+ virtual void broadcastCondVar(ArchCond);
+ virtual bool waitCondVar(ArchCond, ArchMutex, double timeout);
+ virtual ArchMutex newMutex();
+ virtual void closeMutex(ArchMutex);
+ virtual void lockMutex(ArchMutex);
+ virtual void unlockMutex(ArchMutex);
+ virtual ArchThread newThread(ThreadFunc, void*);
+ virtual ArchThread newCurrentThread();
+ virtual ArchThread copyThread(ArchThread);
+ virtual void closeThread(ArchThread);
+ virtual void cancelThread(ArchThread);
+ virtual void setPriorityOfThread(ArchThread, int n);
+ virtual void testCancelThread();
+ virtual bool wait(ArchThread, double timeout);
+ virtual bool isSameThread(ArchThread, ArchThread);
+ virtual bool isExitedThread(ArchThread);
+ virtual void* getResultOfThread(ArchThread);
+ virtual ThreadID getIDOfThread(ArchThread);
+ virtual void setSignalHandler(ESignal, SignalFunc, void*);
+ virtual void raiseSignal(ESignal);
+
+private:
+ void startSignalHandler();
+
+ ArchThreadImpl* find(pthread_t thread);
+ ArchThreadImpl* findNoRef(pthread_t thread);
+ void insert(ArchThreadImpl* thread);
+ void erase(ArchThreadImpl* thread);
+
+ void refThread(ArchThreadImpl* rep);
+ void testCancelThreadImpl(ArchThreadImpl* rep);
+
+ void doThreadFunc(ArchThread thread);
+ static void* threadFunc(void* vrep);
+ static void threadCancel(int);
+ static void* threadSignalHandler(void* vrep);
+
+private:
+ typedef std::list<ArchThread> ThreadList;
+
+ static ArchMultithreadPosix* s_instance;
+
+ bool m_newThreadCalled;
+
+ ArchMutex m_threadMutex;
+ ArchThread m_mainThread;
+ ThreadList m_threadList;
+ ThreadID m_nextID;
+
+ pthread_t m_signalThread;
+ SignalFunc m_signalFunc[kNUM_SIGNALS];
+ void* m_signalUserData[kNUM_SIGNALS];
+};
diff --git a/src/lib/arch/unix/ArchNetworkBSD.cpp b/src/lib/arch/unix/ArchNetworkBSD.cpp
new file mode 100644
index 0000000..149218c
--- /dev/null
+++ b/src/lib/arch/unix/ArchNetworkBSD.cpp
@@ -0,0 +1,1005 @@
+/*
+ * 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 "arch/unix/ArchNetworkBSD.h"
+
+#include "arch/unix/ArchMultithreadPosix.h"
+#include "arch/unix/XArchUnix.h"
+#include "arch/Arch.h"
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <netinet/in.h>
+#include <netdb.h>
+#if !defined(TCP_NODELAY)
+# include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#if HAVE_POLL
+# include <poll.h>
+#else
+# if HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# endif
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# endif
+#endif
+
+#if !HAVE_INET_ATON
+# include <stdio.h>
+#endif
+
+static const int s_family[] = {
+ PF_UNSPEC,
+ PF_INET,
+ PF_INET6,
+};
+static const int s_type[] = {
+ SOCK_DGRAM,
+ SOCK_STREAM
+};
+
+#if !HAVE_INET_ATON
+// parse dotted quad addresses. we don't bother with the weird BSD'ism
+// of handling octal and hex and partial forms.
+static
+in_addr_t
+inet_aton(const char* cp, struct in_addr* inp)
+{
+ unsigned int a, b, c, d;
+ if (sscanf(cp, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) {
+ return 0;
+ }
+ if (a >= 256 || b >= 256 || c >= 256 || d >= 256) {
+ return 0;
+ }
+ unsigned char* incp = (unsigned char*)inp;
+ incp[0] = (unsigned char)(a & 0xffu);
+ incp[1] = (unsigned char)(b & 0xffu);
+ incp[2] = (unsigned char)(c & 0xffu);
+ incp[3] = (unsigned char)(d & 0xffu);
+ return inp->s_addr;
+}
+#endif
+
+//
+// ArchNetworkBSD
+//
+
+ArchNetworkBSD::ArchNetworkBSD()
+{
+}
+
+ArchNetworkBSD::~ArchNetworkBSD()
+{
+ ARCH->closeMutex(m_mutex);
+}
+
+void
+ArchNetworkBSD::init()
+{
+ // create mutex to make some calls thread safe
+ m_mutex = ARCH->newMutex();
+}
+
+ArchSocket
+ArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type)
+{
+ // create socket
+ int fd = socket(s_family[family], s_type[type], 0);
+ if (fd == -1) {
+ throwError(errno);
+ }
+ try {
+ setBlockingOnSocket(fd, false);
+ }
+ catch (...) {
+ close(fd);
+ throw;
+ }
+
+ // allocate socket object
+ ArchSocketImpl* newSocket = new ArchSocketImpl;
+ newSocket->m_fd = fd;
+ newSocket->m_refCount = 1;
+ return newSocket;
+}
+
+ArchSocket
+ArchNetworkBSD::copySocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // ref the socket and return it
+ ARCH->lockMutex(m_mutex);
+ ++s->m_refCount;
+ ARCH->unlockMutex(m_mutex);
+ return s;
+}
+
+void
+ArchNetworkBSD::closeSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // unref the socket and note if it should be released
+ ARCH->lockMutex(m_mutex);
+ const bool doClose = (--s->m_refCount == 0);
+ ARCH->unlockMutex(m_mutex);
+
+ // close the socket if necessary
+ if (doClose) {
+ if (close(s->m_fd) == -1) {
+ // close failed. restore the last ref and throw.
+ int err = errno;
+ ARCH->lockMutex(m_mutex);
+ ++s->m_refCount;
+ ARCH->unlockMutex(m_mutex);
+ throwError(err);
+ }
+ delete s;
+ }
+}
+
+void
+ArchNetworkBSD::closeSocketForRead(ArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown(s->m_fd, 0) == -1) {
+ if (errno != ENOTCONN) {
+ throwError(errno);
+ }
+ }
+}
+
+void
+ArchNetworkBSD::closeSocketForWrite(ArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown(s->m_fd, 1) == -1) {
+ if (errno != ENOTCONN) {
+ throwError(errno);
+ }
+ }
+}
+
+void
+ArchNetworkBSD::bindSocket(ArchSocket s, ArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (bind(s->m_fd, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == -1) {
+ throwError(errno);
+ }
+}
+
+void
+ArchNetworkBSD::listenOnSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // hardcoding backlog
+ if (listen(s->m_fd, 3) == -1) {
+ throwError(errno);
+ }
+}
+
+ArchSocket
+ArchNetworkBSD::acceptSocket(ArchSocket s, ArchNetAddress* addr)
+{
+ assert(s != NULL);
+
+ // if user passed NULL in addr then use scratch space
+ ArchNetAddress dummy;
+ if (addr == NULL) {
+ addr = &dummy;
+ }
+
+ // create new socket and address
+ ArchSocketImpl* newSocket = new ArchSocketImpl;
+ *addr = new ArchNetAddressImpl;
+
+ // accept on socket
+ ACCEPT_TYPE_ARG3 len = (ACCEPT_TYPE_ARG3)((*addr)->m_len);
+ int fd = accept(s->m_fd, TYPED_ADDR(struct sockaddr, (*addr)), &len);
+ (*addr)->m_len = (socklen_t)len;
+ if (fd == -1) {
+ int err = errno;
+ delete newSocket;
+ delete *addr;
+ *addr = NULL;
+ if (err == EAGAIN) {
+ return NULL;
+ }
+ throwError(err);
+ }
+
+ try {
+ setBlockingOnSocket(fd, false);
+ }
+ catch (...) {
+ close(fd);
+ delete newSocket;
+ delete *addr;
+ *addr = NULL;
+ throw;
+ }
+
+ // initialize socket
+ newSocket->m_fd = fd;
+ newSocket->m_refCount = 1;
+
+ // discard address if not requested
+ if (addr == &dummy) {
+ ARCH->closeAddr(dummy);
+ }
+
+ return newSocket;
+}
+
+bool
+ArchNetworkBSD::connectSocket(ArchSocket s, ArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (connect(s->m_fd, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == -1) {
+ if (errno == EISCONN) {
+ return true;
+ }
+ if (errno == EINPROGRESS) {
+ return false;
+ }
+ throwError(errno);
+ }
+ return true;
+}
+
+#if HAVE_POLL
+
+int
+ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout)
+{
+ assert(pe != NULL || num == 0);
+
+ // return if nothing to do
+ if (num == 0) {
+ if (timeout > 0.0) {
+ ARCH->sleep(timeout);
+ }
+ return 0;
+ }
+
+ // allocate space for translated query
+ struct pollfd* pfd = new struct pollfd[1 + num];
+
+ // translate query
+ for (int i = 0; i < num; ++i) {
+ pfd[i].fd = (pe[i].m_socket == NULL) ? -1 : pe[i].m_socket->m_fd;
+ pfd[i].events = 0;
+ if ((pe[i].m_events & kPOLLIN) != 0) {
+ pfd[i].events |= POLLIN;
+ }
+ if ((pe[i].m_events & kPOLLOUT) != 0) {
+ pfd[i].events |= POLLOUT;
+ }
+ }
+ int n = num;
+
+ // add the unblock pipe
+ const int* unblockPipe = getUnblockPipe();
+ if (unblockPipe != NULL) {
+ pfd[n].fd = unblockPipe[0];
+ pfd[n].events = POLLIN;
+ ++n;
+ }
+
+ // prepare timeout
+ int t = (timeout < 0.0) ? -1 : static_cast<int>(1000.0 * timeout);
+
+ // do the poll
+ n = poll(pfd, n, t);
+
+ // reset the unblock pipe
+ if (n > 0 && unblockPipe != NULL && (pfd[num].revents & POLLIN) != 0) {
+ // the unblock event was signalled. flush the pipe.
+ char dummy[100];
+ int ignore;
+
+ do {
+ ignore = read(unblockPipe[0], dummy, sizeof(dummy));
+ } while (errno != EAGAIN);
+
+ // don't count this unblock pipe in return value
+ --n;
+ }
+
+ // handle results
+ if (n == -1) {
+ if (errno == EINTR) {
+ // interrupted system call
+ ARCH->testCancelThread();
+ delete[] pfd;
+ return 0;
+ }
+ delete[] pfd;
+ throwError(errno);
+ }
+
+ // translate back
+ for (int i = 0; i < num; ++i) {
+ pe[i].m_revents = 0;
+ if ((pfd[i].revents & POLLIN) != 0) {
+ pe[i].m_revents |= kPOLLIN;
+ }
+ if ((pfd[i].revents & POLLOUT) != 0) {
+ pe[i].m_revents |= kPOLLOUT;
+ }
+ if ((pfd[i].revents & POLLERR) != 0) {
+ pe[i].m_revents |= kPOLLERR;
+ }
+ if ((pfd[i].revents & POLLNVAL) != 0) {
+ pe[i].m_revents |= kPOLLNVAL;
+ }
+ }
+
+ delete[] pfd;
+ return n;
+}
+
+#else
+
+int
+ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout)
+{
+ int i, n;
+
+ // prepare sets for select
+ n = 0;
+ fd_set readSet, writeSet, errSet;
+ fd_set* readSetP = NULL;
+ fd_set* writeSetP = NULL;
+ fd_set* errSetP = NULL;
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_ZERO(&errSet);
+ for (i = 0; i < num; ++i) {
+ // reset return flags
+ pe[i].m_revents = 0;
+
+ // set invalid flag if socket is bogus then go to next socket
+ if (pe[i].m_socket == NULL) {
+ pe[i].m_revents |= kPOLLNVAL;
+ continue;
+ }
+
+ int fdi = pe[i].m_socket->m_fd;
+ if (pe[i].m_events & kPOLLIN) {
+ FD_SET(pe[i].m_socket->m_fd, &readSet);
+ readSetP = &readSet;
+ if (fdi > n) {
+ n = fdi;
+ }
+ }
+ if (pe[i].m_events & kPOLLOUT) {
+ FD_SET(pe[i].m_socket->m_fd, &writeSet);
+ writeSetP = &writeSet;
+ if (fdi > n) {
+ n = fdi;
+ }
+ }
+ if (true) {
+ FD_SET(pe[i].m_socket->m_fd, &errSet);
+ errSetP = &errSet;
+ if (fdi > n) {
+ n = fdi;
+ }
+ }
+ }
+
+ // add the unblock pipe
+ const int* unblockPipe = getUnblockPipe();
+ if (unblockPipe != NULL) {
+ FD_SET(unblockPipe[0], &readSet);
+ readSetP = &readSet;
+ if (unblockPipe[0] > n) {
+ n = unblockPipe[0];
+ }
+ }
+
+ // if there are no sockets then don't block forever
+ if (n == 0 && timeout < 0.0) {
+ timeout = 0.0;
+ }
+
+ // prepare timeout for select
+ struct timeval timeout2;
+ struct timeval* timeout2P;
+ if (timeout < 0.0) {
+ timeout2P = NULL;
+ }
+ else {
+ timeout2P = &timeout2;
+ timeout2.tv_sec = static_cast<int>(timeout);
+ timeout2.tv_usec = static_cast<int>(1.0e+6 *
+ (timeout - timeout2.tv_sec));
+ }
+
+ // do the select
+ n = select((SELECT_TYPE_ARG1) n + 1,
+ SELECT_TYPE_ARG234 readSetP,
+ SELECT_TYPE_ARG234 writeSetP,
+ SELECT_TYPE_ARG234 errSetP,
+ SELECT_TYPE_ARG5 timeout2P);
+
+ // reset the unblock pipe
+ if (n > 0 && unblockPipe != NULL && FD_ISSET(unblockPipe[0], &readSet)) {
+ // the unblock event was signalled. flush the pipe.
+ char dummy[100];
+ do {
+ read(unblockPipe[0], dummy, sizeof(dummy));
+ } while (errno != EAGAIN);
+ }
+
+ // handle results
+ if (n == -1) {
+ if (errno == EINTR) {
+ // interrupted system call
+ ARCH->testCancelThread();
+ return 0;
+ }
+ throwError(errno);
+ }
+ n = 0;
+ for (i = 0; i < num; ++i) {
+ if (pe[i].m_socket != NULL) {
+ if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) {
+ pe[i].m_revents |= kPOLLIN;
+ }
+ if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) {
+ pe[i].m_revents |= kPOLLOUT;
+ }
+ if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) {
+ pe[i].m_revents |= kPOLLERR;
+ }
+ }
+ if (pe[i].m_revents != 0) {
+ ++n;
+ }
+ }
+
+ return n;
+}
+
+#endif
+
+void
+ArchNetworkBSD::unblockPollSocket(ArchThread thread)
+{
+ const int* unblockPipe = getUnblockPipeForThread(thread);
+ if (unblockPipe != NULL) {
+ char dummy = 0;
+ int ignore;
+
+ ignore = write(unblockPipe[1], &dummy, 1);
+ }
+}
+
+size_t
+ArchNetworkBSD::readSocket(ArchSocket s, void* buf, size_t len)
+{
+ assert(s != NULL);
+
+ ssize_t n = read(s->m_fd, buf, len);
+ if (n == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ return 0;
+ }
+ throwError(errno);
+ }
+ return n;
+}
+
+size_t
+ArchNetworkBSD::writeSocket(ArchSocket s, const void* buf, size_t len)
+{
+ assert(s != NULL);
+
+ ssize_t n = write(s->m_fd, buf, len);
+ if (n == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ return 0;
+ }
+ throwError(errno);
+ }
+ return n;
+}
+
+void
+ArchNetworkBSD::throwErrorOnSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // get the error from the socket layer
+ int err = 0;
+ socklen_t size = (socklen_t)sizeof(err);
+ if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR,
+ (optval_t*)&err, &size) == -1) {
+ err = errno;
+ }
+
+ // throw if there's an error
+ if (err != 0) {
+ throwError(err);
+ }
+}
+
+void
+ArchNetworkBSD::setBlockingOnSocket(int fd, bool blocking)
+{
+ assert(fd != -1);
+
+ int mode = fcntl(fd, F_GETFL, 0);
+ if (mode == -1) {
+ throwError(errno);
+ }
+ if (blocking) {
+ mode &= ~O_NONBLOCK;
+ }
+ else {
+ mode |= O_NONBLOCK;
+ }
+ if (fcntl(fd, F_SETFL, mode) == -1) {
+ throwError(errno);
+ }
+}
+
+bool
+ArchNetworkBSD::setNoDelayOnSocket(ArchSocket s, bool noDelay)
+{
+ assert(s != NULL);
+
+ // get old state
+ int oflag;
+ socklen_t size = (socklen_t)sizeof(oflag);
+ if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY,
+ (optval_t*)&oflag, &size) == -1) {
+ throwError(errno);
+ }
+
+ int flag = noDelay ? 1 : 0;
+ size = (socklen_t)sizeof(flag);
+ if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY,
+ (optval_t*)&flag, size) == -1) {
+ throwError(errno);
+ }
+
+ return (oflag != 0);
+}
+
+bool
+ArchNetworkBSD::setReuseAddrOnSocket(ArchSocket s, bool reuse)
+{
+ assert(s != NULL);
+
+ // get old state
+ int oflag;
+ socklen_t size = (socklen_t)sizeof(oflag);
+ if (getsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR,
+ (optval_t*)&oflag, &size) == -1) {
+ throwError(errno);
+ }
+
+ int flag = reuse ? 1 : 0;
+ size = (socklen_t)sizeof(flag);
+ if (setsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR,
+ (optval_t*)&flag, size) == -1) {
+ throwError(errno);
+ }
+
+ return (oflag != 0);
+}
+
+std::string
+ArchNetworkBSD::getHostName()
+{
+ char name[256];
+ if (gethostname(name, sizeof(name)) == -1) {
+ name[0] = '\0';
+ }
+ else {
+ name[sizeof(name) - 1] = '\0';
+ }
+ return name;
+}
+
+ArchNetAddress
+ArchNetworkBSD::newAnyAddr(EAddressFamily family)
+{
+ // allocate address
+ ArchNetAddressImpl* addr = new ArchNetAddressImpl;
+
+ // fill it in
+ switch (family) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ ipAddr->sin_family = AF_INET;
+ ipAddr->sin_port = 0;
+ ipAddr->sin_addr.s_addr = INADDR_ANY;
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
+ break;
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ ipAddr->sin6_family = AF_INET6;
+ ipAddr->sin6_port = 0;
+ memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any));
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in6);
+ break;
+ }
+ default:
+ delete addr;
+ assert(0 && "invalid family");
+ }
+
+ return addr;
+}
+
+ArchNetAddress
+ArchNetworkBSD::copyAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ // allocate and copy address
+ return new ArchNetAddressImpl(*addr);
+}
+
+ArchNetAddress
+ArchNetworkBSD::nameToAddr(const std::string& name)
+{
+ // allocate address
+ ArchNetAddressImpl* addr = new ArchNetAddressImpl;
+
+ char ipstr[INET6_ADDRSTRLEN];
+ struct addrinfo hints;
+ struct addrinfo *p;
+ int ret;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+
+ ARCH->lockMutex(m_mutex);
+ if ((ret = getaddrinfo(name.c_str(), NULL, &hints, &p)) != 0) {
+ ARCH->unlockMutex(m_mutex);
+ delete addr;
+ throwNameError(ret);
+ }
+
+ if (p->ai_family == AF_INET) {
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
+ } else {
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in6);
+ }
+
+ memcpy(&addr->m_addr, p->ai_addr, addr->m_len);
+ freeaddrinfo(p);
+ ARCH->unlockMutex(m_mutex);
+
+ return addr;
+}
+
+void
+ArchNetworkBSD::closeAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ delete addr;
+}
+
+std::string
+ArchNetworkBSD::addrToName(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ // mutexed name lookup (ugh)
+ ARCH->lockMutex(m_mutex);
+ char host[1024];
+ char service[20];
+ int ret = getnameinfo(TYPED_ADDR(struct sockaddr, addr), addr->m_len, host,
+ sizeof(host), service, sizeof(service), 0);
+ if (ret != 0) {
+ ARCH->unlockMutex(m_mutex);
+ throwNameError(ret);
+ }
+
+ // save (primary) name
+ std::string name = host;
+
+ // done with static buffer
+ ARCH->unlockMutex(m_mutex);
+
+ return name;
+}
+
+std::string
+ArchNetworkBSD::addrToString(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ ARCH->lockMutex(m_mutex);
+ std::string s = inet_ntoa(ipAddr->sin_addr);
+ ARCH->unlockMutex(m_mutex);
+ return s;
+ }
+
+ case kINET6: {
+ char strAddr[INET6_ADDRSTRLEN];
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ ARCH->lockMutex(m_mutex);
+ inet_ntop(AF_INET6, &ipAddr->sin6_addr, strAddr, INET6_ADDRSTRLEN);
+ ARCH->unlockMutex(m_mutex);
+ return strAddr;
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return "";
+ }
+}
+
+IArchNetwork::EAddressFamily
+ArchNetworkBSD::getAddrFamily(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (addr->m_addr.ss_family) {
+ case AF_INET:
+ return kINET;
+ case AF_INET6:
+ return kINET6;
+
+ default:
+ return kUNKNOWN;
+ }
+}
+
+void
+ArchNetworkBSD::setAddrPort(ArchNetAddress addr, int port)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ ipAddr->sin_port = htons(port);
+ break;
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ ipAddr->sin6_port = htons(port);
+ break;
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ break;
+ }
+}
+
+int
+ArchNetworkBSD::getAddrPort(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ return ntohs(ipAddr->sin_port);
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ return ntohs(ipAddr->sin6_port);
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return 0;
+ }
+}
+
+bool
+ArchNetworkBSD::isAnyAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ return (ipAddr->sin_addr.s_addr == INADDR_ANY &&
+ addr->m_len == (socklen_t)sizeof(struct sockaddr_in));
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ return (memcmp(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
+ addr->m_len == (socklen_t)sizeof(struct sockaddr_in6));
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return true;
+ }
+}
+
+bool
+ArchNetworkBSD::isEqualAddr(ArchNetAddress a, ArchNetAddress b)
+{
+ return (a->m_len == b->m_len &&
+ memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0);
+}
+
+const int*
+ArchNetworkBSD::getUnblockPipe()
+{
+ ArchMultithreadPosix* mt = ArchMultithreadPosix::getInstance();
+ ArchThread thread = mt->newCurrentThread();
+ const int* p = getUnblockPipeForThread(thread);
+ ARCH->closeThread(thread);
+ return p;
+}
+
+const int*
+ArchNetworkBSD::getUnblockPipeForThread(ArchThread thread)
+{
+ ArchMultithreadPosix* mt = ArchMultithreadPosix::getInstance();
+ int* unblockPipe = (int*)mt->getNetworkDataForThread(thread);
+ if (unblockPipe == NULL) {
+ unblockPipe = new int[2];
+ if (pipe(unblockPipe) != -1) {
+ try {
+ setBlockingOnSocket(unblockPipe[0], false);
+ mt->setNetworkDataForCurrentThread(unblockPipe);
+ }
+ catch (...) {
+ delete[] unblockPipe;
+ unblockPipe = NULL;
+ }
+ }
+ else {
+ delete[] unblockPipe;
+ unblockPipe = NULL;
+ }
+ }
+ return unblockPipe;
+}
+
+void
+ArchNetworkBSD::throwError(int err)
+{
+ switch (err) {
+ case EINTR:
+ ARCH->testCancelThread();
+ throw XArchNetworkInterrupted(new XArchEvalUnix(err));
+
+ case EACCES:
+ case EPERM:
+ throw XArchNetworkAccess(new XArchEvalUnix(err));
+
+ case ENFILE:
+ case EMFILE:
+ case ENODEV:
+ case ENOBUFS:
+ case ENOMEM:
+ case ENETDOWN:
+#if defined(ENOSR)
+ case ENOSR:
+#endif
+ throw XArchNetworkResource(new XArchEvalUnix(err));
+
+ case EPROTOTYPE:
+ case EPROTONOSUPPORT:
+ case EAFNOSUPPORT:
+ case EPFNOSUPPORT:
+ case ESOCKTNOSUPPORT:
+ case EINVAL:
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ case ESHUTDOWN:
+#if defined(ENOPKG)
+ case ENOPKG:
+#endif
+ throw XArchNetworkSupport(new XArchEvalUnix(err));
+
+ case EIO:
+ throw XArchNetworkIO(new XArchEvalUnix(err));
+
+ case EADDRNOTAVAIL:
+ throw XArchNetworkNoAddress(new XArchEvalUnix(err));
+
+ case EADDRINUSE:
+ throw XArchNetworkAddressInUse(new XArchEvalUnix(err));
+
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ throw XArchNetworkNoRoute(new XArchEvalUnix(err));
+
+ case ENOTCONN:
+ throw XArchNetworkNotConnected(new XArchEvalUnix(err));
+
+ case EPIPE:
+ throw XArchNetworkShutdown(new XArchEvalUnix(err));
+
+ case ECONNABORTED:
+ case ECONNRESET:
+ throw XArchNetworkDisconnected(new XArchEvalUnix(err));
+
+ case ECONNREFUSED:
+ throw XArchNetworkConnectionRefused(new XArchEvalUnix(err));
+
+ case EHOSTDOWN:
+ case ETIMEDOUT:
+ throw XArchNetworkTimedOut(new XArchEvalUnix(err));
+
+ default:
+ throw XArchNetwork(new XArchEvalUnix(err));
+ }
+}
+
+void
+ArchNetworkBSD::throwNameError(int err)
+{
+ static const char* s_msg[] = {
+ "The specified host is unknown",
+ "The requested name is valid but does not have an IP address",
+ "A non-recoverable name server error occurred",
+ "A temporary error occurred on an authoritative name server",
+ "An unknown name server error occurred"
+ };
+
+ switch (err) {
+ case HOST_NOT_FOUND:
+ throw XArchNetworkNameUnknown(s_msg[0]);
+
+ case NO_DATA:
+ throw XArchNetworkNameNoAddress(s_msg[1]);
+
+ case NO_RECOVERY:
+ throw XArchNetworkNameFailure(s_msg[2]);
+
+ case TRY_AGAIN:
+ throw XArchNetworkNameUnavailable(s_msg[3]);
+
+ default:
+ throw XArchNetworkName(s_msg[4]);
+ }
+}
diff --git a/src/lib/arch/unix/ArchNetworkBSD.h b/src/lib/arch/unix/ArchNetworkBSD.h
new file mode 100644
index 0000000..3f5679a
--- /dev/null
+++ b/src/lib/arch/unix/ArchNetworkBSD.h
@@ -0,0 +1,105 @@
+/*
+ * 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 "arch/IArchNetwork.h"
+#include "arch/IArchMultithread.h"
+
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+#if !HAVE_SOCKLEN_T
+typedef int socklen_t;
+#endif
+
+// old systems may use char* for [gs]etsockopt()'s optval argument.
+// this should be void on modern systems but char is forwards
+// compatible so we always use it.
+typedef char optval_t;
+
+#define ARCH_NETWORK ArchNetworkBSD
+#define TYPED_ADDR(type_, addr_) (reinterpret_cast<type_*>(&addr_->m_addr))
+
+class ArchSocketImpl {
+public:
+ int m_fd;
+ int m_refCount;
+};
+
+class ArchNetAddressImpl {
+public:
+ ArchNetAddressImpl() : m_len(sizeof(m_addr)) { }
+
+public:
+ struct sockaddr_storage m_addr;
+ socklen_t m_len;
+};
+
+//! Berkeley (BSD) sockets implementation of IArchNetwork
+class ArchNetworkBSD : public IArchNetwork {
+public:
+ ArchNetworkBSD();
+ virtual ~ArchNetworkBSD();
+
+ virtual void init();
+
+ // IArchNetwork overrides
+ virtual ArchSocket newSocket(EAddressFamily, ESocketType);
+ virtual ArchSocket copySocket(ArchSocket s); virtual void closeSocket(ArchSocket s);
+ virtual void closeSocketForRead(ArchSocket s);
+ virtual void closeSocketForWrite(ArchSocket s);
+ virtual void bindSocket(ArchSocket s, ArchNetAddress addr);
+ virtual void listenOnSocket(ArchSocket s);
+ virtual ArchSocket acceptSocket(ArchSocket s, ArchNetAddress* addr);
+ virtual bool connectSocket(ArchSocket s, ArchNetAddress name);
+ virtual int pollSocket(PollEntry[], int num, double timeout);
+ virtual void unblockPollSocket(ArchThread thread);
+ virtual size_t readSocket(ArchSocket s, void* buf, size_t len);
+ virtual size_t writeSocket(ArchSocket s,
+ const void* buf, size_t len);
+ virtual void throwErrorOnSocket(ArchSocket);
+ virtual bool setNoDelayOnSocket(ArchSocket, bool noDelay);
+ virtual bool setReuseAddrOnSocket(ArchSocket, bool reuse);
+ virtual std::string getHostName();
+ virtual ArchNetAddress newAnyAddr(EAddressFamily);
+ virtual ArchNetAddress copyAddr(ArchNetAddress);
+ virtual ArchNetAddress nameToAddr(const std::string&);
+ virtual void closeAddr(ArchNetAddress);
+ virtual std::string addrToName(ArchNetAddress);
+ virtual std::string addrToString(ArchNetAddress);
+ virtual EAddressFamily getAddrFamily(ArchNetAddress);
+ virtual void setAddrPort(ArchNetAddress, int port);
+ virtual int getAddrPort(ArchNetAddress);
+ virtual bool isAnyAddr(ArchNetAddress);
+ virtual bool isEqualAddr(ArchNetAddress, ArchNetAddress);
+
+private:
+ const int* getUnblockPipe();
+ const int* getUnblockPipeForThread(ArchThread);
+ void setBlockingOnSocket(int fd, bool blocking);
+ void throwError(int);
+ void throwNameError(int);
+
+private:
+ ArchMutex m_mutex;
+};
diff --git a/src/lib/arch/unix/ArchSleepUnix.cpp b/src/lib/arch/unix/ArchSleepUnix.cpp
new file mode 100644
index 0000000..48e2600
--- /dev/null
+++ b/src/lib/arch/unix/ArchSleepUnix.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 "arch/unix/ArchSleepUnix.h"
+
+#include "arch/Arch.h"
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#if !HAVE_NANOSLEEP
+# if HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# endif
+# if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+# endif
+# if HAVE_UNISTD_H
+# include <unistd.h>
+# endif
+#endif
+
+//
+// ArchSleepUnix
+//
+
+ArchSleepUnix::ArchSleepUnix()
+{
+ // do nothing
+}
+
+ArchSleepUnix::~ArchSleepUnix()
+{
+ // do nothing
+}
+
+void
+ArchSleepUnix::sleep(double timeout)
+{
+ ARCH->testCancelThread();
+ if (timeout < 0.0) {
+ return;
+ }
+
+#if HAVE_NANOSLEEP
+ // prep timeout
+ struct timespec t;
+ t.tv_sec = (long)timeout;
+ t.tv_nsec = (long)(1.0e+9 * (timeout - (double)t.tv_sec));
+
+ // wait
+ while (nanosleep(&t, &t) < 0)
+ ARCH->testCancelThread();
+#else
+ /* emulate nanosleep() with select() */
+ double startTime = ARCH->time();
+ double timeLeft = timeout;
+ while (timeLeft > 0.0) {
+ struct timeval timeout2;
+ timeout2.tv_sec = static_cast<int>(timeLeft);
+ timeout2.tv_usec = static_cast<int>(1.0e+6 * (timeLeft -
+ timeout2.tv_sec));
+ select((SELECT_TYPE_ARG1) 0,
+ SELECT_TYPE_ARG234 NULL,
+ SELECT_TYPE_ARG234 NULL,
+ SELECT_TYPE_ARG234 NULL,
+ SELECT_TYPE_ARG5 &timeout2);
+ ARCH->testCancelThread();
+ timeLeft = timeout - (ARCH->time() - startTime);
+ }
+#endif
+}
diff --git a/src/lib/arch/unix/ArchSleepUnix.h b/src/lib/arch/unix/ArchSleepUnix.h
new file mode 100644
index 0000000..3e307a5
--- /dev/null
+++ b/src/lib/arch/unix/ArchSleepUnix.h
@@ -0,0 +1,33 @@
+/*
+ * 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 "arch/IArchSleep.h"
+
+#define ARCH_SLEEP ArchSleepUnix
+
+//! Unix implementation of IArchSleep
+class ArchSleepUnix : public IArchSleep {
+public:
+ ArchSleepUnix();
+ virtual ~ArchSleepUnix();
+
+ // IArchSleep overrides
+ virtual void sleep(double timeout);
+};
diff --git a/src/lib/arch/unix/ArchStringUnix.cpp b/src/lib/arch/unix/ArchStringUnix.cpp
new file mode 100644
index 0000000..591c826
--- /dev/null
+++ b/src/lib/arch/unix/ArchStringUnix.cpp
@@ -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/>.
+ */
+
+#include "arch/unix/ArchStringUnix.h"
+
+#include <stdio.h>
+
+//
+// ArchStringUnix
+//
+
+#include "arch/multibyte.h"
+#include "arch/vsnprintf.h"
+
+ArchStringUnix::ArchStringUnix()
+{
+}
+
+ArchStringUnix::~ArchStringUnix()
+{
+}
+
+IArchString::EWideCharEncoding
+ArchStringUnix::getWideCharEncoding()
+{
+ return kUCS4;
+}
diff --git a/src/lib/arch/unix/ArchStringUnix.h b/src/lib/arch/unix/ArchStringUnix.h
new file mode 100644
index 0000000..f7d0035
--- /dev/null
+++ b/src/lib/arch/unix/ArchStringUnix.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 "arch/IArchString.h"
+
+#define ARCH_STRING ArchStringUnix
+
+//! Unix implementation of IArchString
+class ArchStringUnix : public IArchString {
+public:
+ ArchStringUnix();
+ virtual ~ArchStringUnix();
+
+ // IArchString overrides
+ virtual EWideCharEncoding
+ getWideCharEncoding();
+};
diff --git a/src/lib/arch/unix/ArchSystemUnix.cpp b/src/lib/arch/unix/ArchSystemUnix.cpp
new file mode 100644
index 0000000..f51e47f
--- /dev/null
+++ b/src/lib/arch/unix/ArchSystemUnix.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "arch/unix/ArchSystemUnix.h"
+
+#include <sys/utsname.h>
+
+//
+// ArchSystemUnix
+//
+
+ArchSystemUnix::ArchSystemUnix()
+{
+ // do nothing
+}
+
+ArchSystemUnix::~ArchSystemUnix()
+{
+ // do nothing
+}
+
+std::string
+ArchSystemUnix::getOSName() const
+{
+#if defined(HAVE_SYS_UTSNAME_H)
+ struct utsname info;
+ if (uname(&info) == 0) {
+ std::string msg;
+ msg += info.sysname;
+ msg += " ";
+ msg += info.release;
+ return msg;
+ }
+#endif
+ return "Unix";
+}
+
+std::string
+ArchSystemUnix::getPlatformName() const
+{
+#if defined(HAVE_SYS_UTSNAME_H)
+ struct utsname info;
+ if (uname(&info) == 0) {
+ return std::string(info.machine);
+ }
+#endif
+ return "unknown";
+}
+
+std::string
+ArchSystemUnix::setting(const std::string&) const
+{
+ return "";
+}
+
+void
+ArchSystemUnix::setting(const std::string&, const std::string&) const
+{
+}
+
+std::string
+ArchSystemUnix::getLibsUsed(void) const
+{
+ return "not implemented.\nuse lsof on shell";
+}
diff --git a/src/lib/arch/unix/ArchSystemUnix.h b/src/lib/arch/unix/ArchSystemUnix.h
new file mode 100644
index 0000000..aa9c564
--- /dev/null
+++ b/src/lib/arch/unix/ArchSystemUnix.h
@@ -0,0 +1,38 @@
+/*
+ * 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 "arch/IArchSystem.h"
+
+#define ARCH_SYSTEM ArchSystemUnix
+
+//! Unix implementation of IArchString
+class ArchSystemUnix : public IArchSystem {
+public:
+ ArchSystemUnix();
+ virtual ~ArchSystemUnix();
+
+ // IArchSystem overrides
+ virtual std::string getOSName() const;
+ virtual std::string getPlatformName() const;
+ virtual std::string setting(const std::string&) const;
+ virtual void setting(const std::string&, const std::string&) const;
+ virtual std::string getLibsUsed(void) const;
+
+};
diff --git a/src/lib/arch/unix/ArchTaskBarXWindows.cpp b/src/lib/arch/unix/ArchTaskBarXWindows.cpp
new file mode 100644
index 0000000..c3577ad
--- /dev/null
+++ b/src/lib/arch/unix/ArchTaskBarXWindows.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "arch/unix/ArchTaskBarXWindows.h"
+
+//
+// ArchTaskBarXWindows
+//
+
+ArchTaskBarXWindows::ArchTaskBarXWindows()
+{
+ // do nothing
+}
+
+ArchTaskBarXWindows::~ArchTaskBarXWindows()
+{
+ // do nothing
+}
+
+void
+ArchTaskBarXWindows::addReceiver(IArchTaskBarReceiver* /*receiver*/)
+{
+ // do nothing
+}
+
+void
+ArchTaskBarXWindows::removeReceiver(IArchTaskBarReceiver* /*receiver*/)
+{
+ // do nothing
+}
+
+void
+ArchTaskBarXWindows::updateReceiver(IArchTaskBarReceiver* /*receiver*/)
+{
+ // do nothing
+}
diff --git a/src/lib/arch/unix/ArchTaskBarXWindows.h b/src/lib/arch/unix/ArchTaskBarXWindows.h
new file mode 100644
index 0000000..f2c8977
--- /dev/null
+++ b/src/lib/arch/unix/ArchTaskBarXWindows.h
@@ -0,0 +1,35 @@
+/*
+ * 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 "arch/IArchTaskBar.h"
+
+#define ARCH_TASKBAR ArchTaskBarXWindows
+
+//! X11 implementation of IArchTaskBar
+class ArchTaskBarXWindows : public IArchTaskBar {
+public:
+ ArchTaskBarXWindows();
+ virtual ~ArchTaskBarXWindows();
+
+ // IArchTaskBar overrides
+ virtual void addReceiver(IArchTaskBarReceiver*);
+ virtual void removeReceiver(IArchTaskBarReceiver*);
+ virtual void updateReceiver(IArchTaskBarReceiver*);
+};
diff --git a/src/lib/arch/unix/ArchTimeUnix.cpp b/src/lib/arch/unix/ArchTimeUnix.cpp
new file mode 100644
index 0000000..24685aa
--- /dev/null
+++ b/src/lib/arch/unix/ArchTimeUnix.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 "arch/unix/ArchTimeUnix.h"
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+//
+// ArchTimeUnix
+//
+
+ArchTimeUnix::ArchTimeUnix()
+{
+ // do nothing
+}
+
+ArchTimeUnix::~ArchTimeUnix()
+{
+ // do nothing
+}
+
+double
+ArchTimeUnix::time()
+{
+ struct timeval t;
+ gettimeofday(&t, NULL);
+ return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec;
+}
diff --git a/src/lib/arch/unix/ArchTimeUnix.h b/src/lib/arch/unix/ArchTimeUnix.h
new file mode 100644
index 0000000..3c5c0f8
--- /dev/null
+++ b/src/lib/arch/unix/ArchTimeUnix.h
@@ -0,0 +1,33 @@
+/*
+ * 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 "arch/IArchTime.h"
+
+#define ARCH_TIME ArchTimeUnix
+
+//! Generic Unix implementation of IArchTime
+class ArchTimeUnix : public IArchTime {
+public:
+ ArchTimeUnix();
+ virtual ~ArchTimeUnix();
+
+ // IArchTime overrides
+ virtual double time();
+};
diff --git a/src/lib/arch/unix/XArchUnix.cpp b/src/lib/arch/unix/XArchUnix.cpp
new file mode 100644
index 0000000..fc7ff65
--- /dev/null
+++ b/src/lib/arch/unix/XArchUnix.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "arch/unix/XArchUnix.h"
+
+#include <cstring>
+
+//
+// XArchEvalUnix
+//
+
+std::string
+XArchEvalUnix::eval() const
+{
+ // FIXME -- not thread safe
+ return strerror(m_error);
+}
diff --git a/src/lib/arch/unix/XArchUnix.h b/src/lib/arch/unix/XArchUnix.h
new file mode 100644
index 0000000..ae62f4c
--- /dev/null
+++ b/src/lib/arch/unix/XArchUnix.h
@@ -0,0 +1,33 @@
+/*
+ * 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 "arch/XArch.h"
+
+//! Lazy error message string evaluation for unix
+class XArchEvalUnix : public XArchEval {
+public:
+ XArchEvalUnix(int error) : m_error(error) { }
+ virtual ~XArchEvalUnix() _NOEXCEPT { }
+
+ virtual std::string eval() const;
+
+private:
+ int m_error;
+};
diff --git a/src/lib/arch/vsnprintf.h b/src/lib/arch/vsnprintf.h
new file mode 100644
index 0000000..5a4e3dc
--- /dev/null
+++ b/src/lib/arch/vsnprintf.h
@@ -0,0 +1,67 @@
+/*
+ * 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 "arch/IArchString.h"
+
+#if HAVE_VSNPRINTF
+
+#if !defined(ARCH_VSNPRINTF)
+# define ARCH_VSNPRINTF vsnprintf
+#endif
+
+int
+IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap)
+{
+ int n = ::ARCH_VSNPRINTF(str, size, fmt, ap);
+ if (n > size) {
+ n = -1;
+ }
+ return n;
+}
+
+#elif SYSAPI_UNIX // !HAVE_VSNPRINTF
+
+#include <stdio.h>
+
+int
+IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap)
+{
+ static FILE* bitbucket = fopen("/dev/null", "w");
+ if (bitbucket == NULL) {
+ // uh oh
+ if (size > 0) {
+ str[0] = '\0';
+ }
+ return 0;
+ }
+ else {
+ // count the characters using the bitbucket
+ int n = vfprintf(bitbucket, fmt, ap);
+ if (n + 1 <= size) {
+ // it'll fit so print it into str
+ vsprintf(str, fmt, ap);
+ }
+ return n;
+ }
+}
+
+#else // !HAVE_VSNPRINTF && !SYSAPI_UNIX
+
+#error vsnprintf not implemented
+
+#endif // !HAVE_VSNPRINTF
diff --git a/src/lib/arch/win32/ArchConsoleWindows.cpp b/src/lib/arch/win32/ArchConsoleWindows.cpp
new file mode 100644
index 0000000..4514555
--- /dev/null
+++ b/src/lib/arch/win32/ArchConsoleWindows.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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 "arch/win32/ArchConsoleWindows.h"
+
+ArchConsoleWindows::ArchConsoleWindows() { }
+
+ArchConsoleWindows::~ArchConsoleWindows() { }
diff --git a/src/lib/arch/win32/ArchConsoleWindows.h b/src/lib/arch/win32/ArchConsoleWindows.h
new file mode 100644
index 0000000..f1f0cc9
--- /dev/null
+++ b/src/lib/arch/win32/ArchConsoleWindows.h
@@ -0,0 +1,29 @@
+/*
+ * 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 "arch/ArchConsoleStd.h"
+
+#define ARCH_CONSOLE ArchConsoleWindows
+
+class ArchConsoleWindows : public ArchConsoleStd {
+public:
+ ArchConsoleWindows();
+ virtual ~ArchConsoleWindows();
+};
diff --git a/src/lib/arch/win32/ArchDaemonWindows.cpp b/src/lib/arch/win32/ArchDaemonWindows.cpp
new file mode 100644
index 0000000..efcf235
--- /dev/null
+++ b/src/lib/arch/win32/ArchDaemonWindows.cpp
@@ -0,0 +1,704 @@
+/*
+ * 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 "arch/win32/ArchDaemonWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/win32/XArchWindows.h"
+#include "arch/Arch.h"
+#include "common/stdvector.h"
+
+#include <sstream>
+
+//
+// ArchDaemonWindows
+//
+
+ArchDaemonWindows* ArchDaemonWindows::s_daemon = NULL;
+
+ArchDaemonWindows::ArchDaemonWindows() :
+m_daemonThreadID(0)
+{
+ m_quitMessage = RegisterWindowMessage("BarrierDaemonExit");
+}
+
+ArchDaemonWindows::~ArchDaemonWindows()
+{
+ // do nothing
+}
+
+int
+ArchDaemonWindows::runDaemon(RunFunc runFunc)
+{
+ assert(s_daemon != NULL);
+ return s_daemon->doRunDaemon(runFunc);
+}
+
+void
+ArchDaemonWindows::daemonRunning(bool running)
+{
+ if (s_daemon != NULL) {
+ s_daemon->doDaemonRunning(running);
+ }
+}
+
+UINT
+ArchDaemonWindows::getDaemonQuitMessage()
+{
+ if (s_daemon != NULL) {
+ return s_daemon->doGetDaemonQuitMessage();
+ }
+ else {
+ return 0;
+ }
+}
+
+void
+ArchDaemonWindows::daemonFailed(int result)
+{
+ assert(s_daemon != NULL);
+ throw XArchDaemonRunFailed(result);
+}
+
+void
+ArchDaemonWindows::installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies)
+{
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
+ if (mgr == NULL) {
+ // can't open service manager
+ throw XArchDaemonInstallFailed(new XArchEvalWindows);
+ }
+
+ // create the service
+ SC_HANDLE service = CreateService(
+ mgr,
+ name,
+ name,
+ 0,
+ SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
+ SERVICE_AUTO_START,
+ SERVICE_ERROR_NORMAL,
+ pathname,
+ NULL,
+ NULL,
+ dependencies,
+ NULL,
+ NULL);
+
+ if (service == NULL) {
+ // can't create service
+ DWORD err = GetLastError();
+ if (err != ERROR_SERVICE_EXISTS) {
+ CloseServiceHandle(mgr);
+ throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
+ }
+ }
+ else {
+ // done with service (but only try to close if not null)
+ CloseServiceHandle(service);
+ }
+
+ // done with manager
+ CloseServiceHandle(mgr);
+
+ // open the registry key for this service
+ HKEY key = openNTServicesKey();
+ key = ArchMiscWindows::addKey(key, name);
+ if (key == NULL) {
+ // can't open key
+ DWORD err = GetLastError();
+ try {
+ uninstallDaemon(name);
+ }
+ catch (...) {
+ // ignore
+ }
+ throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
+ }
+
+ // set the description
+ ArchMiscWindows::setValue(key, _T("Description"), description);
+
+ // set command line
+ key = ArchMiscWindows::addKey(key, _T("Parameters"));
+ if (key == NULL) {
+ // can't open key
+ DWORD err = GetLastError();
+ ArchMiscWindows::closeKey(key);
+ try {
+ uninstallDaemon(name);
+ }
+ catch (...) {
+ // ignore
+ }
+ throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
+ }
+ ArchMiscWindows::setValue(key, _T("CommandLine"), commandLine);
+
+ // done with registry
+ ArchMiscWindows::closeKey(key);
+}
+
+void
+ArchDaemonWindows::uninstallDaemon(const char* name)
+{
+ // remove parameters for this service. ignore failures.
+ HKEY key = openNTServicesKey();
+ key = ArchMiscWindows::openKey(key, name);
+ if (key != NULL) {
+ ArchMiscWindows::deleteKey(key, _T("Parameters"));
+ ArchMiscWindows::closeKey(key);
+ }
+
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
+ if (mgr == NULL) {
+ // can't open service manager
+ throw XArchDaemonUninstallFailed(new XArchEvalWindows);
+ }
+
+ // open the service. oddly, you must open a service to delete it.
+ SC_HANDLE service = OpenService(mgr, name, DELETE | SERVICE_STOP);
+ if (service == NULL) {
+ DWORD err = GetLastError();
+ CloseServiceHandle(mgr);
+ if (err != ERROR_SERVICE_DOES_NOT_EXIST) {
+ throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
+ }
+ throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
+ }
+
+ // stop the service. we don't care if we fail.
+ SERVICE_STATUS status;
+ ControlService(service, SERVICE_CONTROL_STOP, &status);
+
+ // delete the service
+ const bool okay = (DeleteService(service) == 0);
+ const DWORD err = GetLastError();
+
+ // clean up
+ CloseServiceHandle(service);
+ CloseServiceHandle(mgr);
+
+ // give windows a chance to remove the service before
+ // we check if it still exists.
+ ARCH->sleep(1);
+
+ // handle failure. ignore error if service isn't installed anymore.
+ if (!okay && isDaemonInstalled(name)) {
+ if (err == ERROR_SUCCESS) {
+ // this seems to occur even though the uninstall was successful.
+ // it could be a timing issue, i.e., isDaemonInstalled is
+ // called too soon. i've added a sleep to try and stop this.
+ return;
+ }
+ if (err == ERROR_IO_PENDING) {
+ // this seems to be a spurious error
+ return;
+ }
+ if (err != ERROR_SERVICE_MARKED_FOR_DELETE) {
+ throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
+ }
+ throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
+ }
+}
+
+int
+ArchDaemonWindows::daemonize(const char* name, DaemonFunc func)
+{
+ assert(name != NULL);
+ assert(func != NULL);
+
+ // save daemon function
+ m_daemonFunc = func;
+
+ // construct the service entry
+ SERVICE_TABLE_ENTRY entry[2];
+ entry[0].lpServiceName = const_cast<char*>(name);
+ entry[0].lpServiceProc = &ArchDaemonWindows::serviceMainEntry;
+ entry[1].lpServiceName = NULL;
+ entry[1].lpServiceProc = NULL;
+
+ // hook us up to the service control manager. this won't return
+ // (if successful) until the processes have terminated.
+ s_daemon = this;
+ if (StartServiceCtrlDispatcher(entry) == 0) {
+ // StartServiceCtrlDispatcher failed
+ s_daemon = NULL;
+ throw XArchDaemonFailed(new XArchEvalWindows);
+ }
+
+ s_daemon = NULL;
+ return m_daemonResult;
+}
+
+bool
+ArchDaemonWindows::canInstallDaemon(const char* /*name*/)
+{
+ // check if we can open service manager for write
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
+ if (mgr == NULL) {
+ return false;
+ }
+ CloseServiceHandle(mgr);
+
+ // check if we can open the registry key
+ HKEY key = openNTServicesKey();
+ ArchMiscWindows::closeKey(key);
+
+ return (key != NULL);
+}
+
+bool
+ArchDaemonWindows::isDaemonInstalled(const char* name)
+{
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
+ if (mgr == NULL) {
+ return false;
+ }
+
+ // open the service
+ SC_HANDLE service = OpenService(mgr, name, GENERIC_READ);
+
+ // clean up
+ if (service != NULL) {
+ CloseServiceHandle(service);
+ }
+ CloseServiceHandle(mgr);
+
+ return (service != NULL);
+}
+
+HKEY
+ArchDaemonWindows::openNTServicesKey()
+{
+ static const char* s_keyNames[] = {
+ _T("SYSTEM"),
+ _T("CurrentControlSet"),
+ _T("Services"),
+ NULL
+ };
+
+ return ArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
+}
+
+bool
+ArchDaemonWindows::isRunState(DWORD state)
+{
+ switch (state) {
+ case SERVICE_START_PENDING:
+ case SERVICE_CONTINUE_PENDING:
+ case SERVICE_RUNNING:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+int
+ArchDaemonWindows::doRunDaemon(RunFunc run)
+{
+ // should only be called from DaemonFunc
+ assert(m_serviceMutex != NULL);
+ assert(run != NULL);
+
+ // create message queue for this thread
+ MSG dummy;
+ PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE);
+
+ int result = 0;
+ ARCH->lockMutex(m_serviceMutex);
+ m_daemonThreadID = GetCurrentThreadId();
+ while (m_serviceState != SERVICE_STOPPED) {
+ // wait until we're told to start
+ while (!isRunState(m_serviceState) &&
+ m_serviceState != SERVICE_STOP_PENDING) {
+ ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
+ }
+
+ // run unless told to stop
+ if (m_serviceState != SERVICE_STOP_PENDING) {
+ ARCH->unlockMutex(m_serviceMutex);
+ try {
+ result = run();
+ }
+ catch (...) {
+ ARCH->lockMutex(m_serviceMutex);
+ setStatusError(0);
+ m_serviceState = SERVICE_STOPPED;
+ setStatus(m_serviceState);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ ARCH->unlockMutex(m_serviceMutex);
+ throw;
+ }
+ ARCH->lockMutex(m_serviceMutex);
+ }
+
+ // notify of new state
+ if (m_serviceState == SERVICE_PAUSE_PENDING) {
+ m_serviceState = SERVICE_PAUSED;
+ }
+ else {
+ m_serviceState = SERVICE_STOPPED;
+ }
+ setStatus(m_serviceState);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ }
+ ARCH->unlockMutex(m_serviceMutex);
+ return result;
+}
+
+void
+ArchDaemonWindows::doDaemonRunning(bool running)
+{
+ ARCH->lockMutex(m_serviceMutex);
+ if (running) {
+ m_serviceState = SERVICE_RUNNING;
+ setStatus(m_serviceState);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ }
+ ARCH->unlockMutex(m_serviceMutex);
+}
+
+UINT
+ArchDaemonWindows::doGetDaemonQuitMessage()
+{
+ return m_quitMessage;
+}
+
+void
+ArchDaemonWindows::setStatus(DWORD state)
+{
+ setStatus(state, 0, 0);
+}
+
+void
+ArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint)
+{
+ assert(s_daemon != NULL);
+
+ SERVICE_STATUS status;
+ status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
+ SERVICE_INTERACTIVE_PROCESS;
+ status.dwCurrentState = state;
+ status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+ status.dwWin32ExitCode = NO_ERROR;
+ status.dwServiceSpecificExitCode = 0;
+ status.dwCheckPoint = step;
+ status.dwWaitHint = waitHint;
+ SetServiceStatus(s_daemon->m_statusHandle, &status);
+}
+
+void
+ArchDaemonWindows::setStatusError(DWORD error)
+{
+ assert(s_daemon != NULL);
+
+ SERVICE_STATUS status;
+ status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
+ SERVICE_INTERACTIVE_PROCESS;
+ status.dwCurrentState = SERVICE_STOPPED;
+ status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+ status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
+ status.dwServiceSpecificExitCode = error;
+ status.dwCheckPoint = 0;
+ status.dwWaitHint = 0;
+ SetServiceStatus(s_daemon->m_statusHandle, &status);
+}
+
+void
+ArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn)
+{
+ typedef std::vector<LPCTSTR> ArgList;
+ typedef std::vector<std::string> Arguments;
+ const char** argv = const_cast<const char**>(argvIn);
+
+ // create synchronization objects
+ m_serviceMutex = ARCH->newMutex();
+ m_serviceCondVar = ARCH->newCondVar();
+
+ // register our service handler function
+ m_statusHandle = RegisterServiceCtrlHandler(argv[0],
+ &ArchDaemonWindows::serviceHandlerEntry);
+ if (m_statusHandle == 0) {
+ // cannot start as service
+ m_daemonResult = -1;
+ ARCH->closeCondVar(m_serviceCondVar);
+ ARCH->closeMutex(m_serviceMutex);
+ return;
+ }
+
+ // tell service control manager that we're starting
+ m_serviceState = SERVICE_START_PENDING;
+ setStatus(m_serviceState, 0, 10000);
+
+ std::string commandLine;
+
+ // if no arguments supplied then try getting them from the registry.
+ // the first argument doesn't count because it's the service name.
+ Arguments args;
+ ArgList myArgv;
+ if (argc <= 1) {
+ // read command line
+ HKEY key = openNTServicesKey();
+ key = ArchMiscWindows::openKey(key, argvIn[0]);
+ key = ArchMiscWindows::openKey(key, _T("Parameters"));
+ if (key != NULL) {
+ commandLine = ArchMiscWindows::readValueString(key,
+ _T("CommandLine"));
+ }
+
+ // if the command line isn't empty then parse and use it
+ if (!commandLine.empty()) {
+ // parse, honoring double quoted substrings
+ std::string::size_type i = commandLine.find_first_not_of(" \t");
+ while (i != std::string::npos && i != commandLine.size()) {
+ // find end of string
+ std::string::size_type e;
+ if (commandLine[i] == '\"') {
+ // quoted. find closing quote.
+ ++i;
+ e = commandLine.find("\"", i);
+
+ // whitespace must follow closing quote
+ if (e == std::string::npos ||
+ (e + 1 != commandLine.size() &&
+ commandLine[e + 1] != ' ' &&
+ commandLine[e + 1] != '\t')) {
+ args.clear();
+ break;
+ }
+
+ // extract
+ args.push_back(commandLine.substr(i, e - i));
+ i = e + 1;
+ }
+ else {
+ // unquoted. find next whitespace.
+ e = commandLine.find_first_of(" \t", i);
+ if (e == std::string::npos) {
+ e = commandLine.size();
+ }
+
+ // extract
+ args.push_back(commandLine.substr(i, e - i));
+ i = e + 1;
+ }
+
+ // next argument
+ i = commandLine.find_first_not_of(" \t", i);
+ }
+
+ // service name goes first
+ myArgv.push_back(argv[0]);
+
+ // get pointers
+ for (size_t j = 0; j < args.size(); ++j) {
+ myArgv.push_back(args[j].c_str());
+ }
+
+ // adjust argc/argv
+ argc = (DWORD)myArgv.size();
+ argv = &myArgv[0];
+ }
+ }
+
+ m_commandLine = commandLine;
+
+ try {
+ // invoke daemon function
+ m_daemonResult = m_daemonFunc(static_cast<int>(argc), argv);
+ }
+ catch (XArchDaemonRunFailed& e) {
+ setStatusError(e.m_result);
+ m_daemonResult = -1;
+ }
+ catch (...) {
+ setStatusError(1);
+ m_daemonResult = -1;
+ }
+
+ // clean up
+ ARCH->closeCondVar(m_serviceCondVar);
+ ARCH->closeMutex(m_serviceMutex);
+
+ // we're going to exit now, so set status to stopped
+ m_serviceState = SERVICE_STOPPED;
+ setStatus(m_serviceState, 0, 10000);
+}
+
+void WINAPI
+ArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv)
+{
+ s_daemon->serviceMain(argc, argv);
+}
+
+void
+ArchDaemonWindows::serviceHandler(DWORD ctrl)
+{
+ assert(m_serviceMutex != NULL);
+ assert(m_serviceCondVar != NULL);
+
+ ARCH->lockMutex(m_serviceMutex);
+
+ // ignore request if service is already stopped
+ if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) {
+ if (s_daemon != NULL) {
+ setStatus(m_serviceState);
+ }
+ ARCH->unlockMutex(m_serviceMutex);
+ return;
+ }
+
+ switch (ctrl) {
+ case SERVICE_CONTROL_PAUSE:
+ m_serviceState = SERVICE_PAUSE_PENDING;
+ setStatus(m_serviceState, 0, 5000);
+ PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
+ while (isRunState(m_serviceState)) {
+ ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
+ }
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+ // FIXME -- maybe should flush quit messages from queue
+ m_serviceState = SERVICE_CONTINUE_PENDING;
+ setStatus(m_serviceState, 0, 5000);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ break;
+
+ case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
+ m_serviceState = SERVICE_STOP_PENDING;
+ setStatus(m_serviceState, 0, 5000);
+ PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ while (isRunState(m_serviceState)) {
+ ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
+ }
+ break;
+
+ default:
+ // unknown service command
+ // fall through
+
+ case SERVICE_CONTROL_INTERROGATE:
+ setStatus(m_serviceState);
+ break;
+ }
+
+ ARCH->unlockMutex(m_serviceMutex);
+}
+
+void WINAPI
+ArchDaemonWindows::serviceHandlerEntry(DWORD ctrl)
+{
+ s_daemon->serviceHandler(ctrl);
+}
+
+void
+ArchDaemonWindows::start(const char* name)
+{
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
+ if (mgr == NULL) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // open the service
+ SC_HANDLE service = OpenService(
+ mgr, name, SERVICE_START);
+
+ if (service == NULL) {
+ CloseServiceHandle(mgr);
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // start the service
+ if (!StartService(service, 0, NULL)) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+}
+
+void
+ArchDaemonWindows::stop(const char* name)
+{
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
+ if (mgr == NULL) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // open the service
+ SC_HANDLE service = OpenService(
+ mgr, name,
+ SERVICE_STOP | SERVICE_QUERY_STATUS);
+
+ if (service == NULL) {
+ CloseServiceHandle(mgr);
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // ask the service to stop, asynchronously
+ SERVICE_STATUS ss;
+ if (!ControlService(service, SERVICE_CONTROL_STOP, &ss)) {
+ DWORD dwErrCode = GetLastError();
+ if (dwErrCode != ERROR_SERVICE_NOT_ACTIVE) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+ }
+}
+
+void
+ArchDaemonWindows::installDaemon()
+{
+ // install default daemon if not already installed.
+ if (!isDaemonInstalled(DEFAULT_DAEMON_NAME)) {
+ char path[MAX_PATH];
+ GetModuleFileName(ArchMiscWindows::instanceWin32(), path, MAX_PATH);
+
+ // wrap in quotes so a malicious user can't start \Program.exe as admin.
+ std::stringstream ss;
+ ss << '"';
+ ss << path;
+ ss << '"';
+
+ installDaemon(DEFAULT_DAEMON_NAME, DEFAULT_DAEMON_INFO, ss.str().c_str(), "", "");
+ }
+
+ start(DEFAULT_DAEMON_NAME);
+}
+
+void
+ArchDaemonWindows::uninstallDaemon()
+{
+ // remove service if installed.
+ if (isDaemonInstalled(DEFAULT_DAEMON_NAME)) {
+ uninstallDaemon(DEFAULT_DAEMON_NAME);
+ }
+}
diff --git a/src/lib/arch/win32/ArchDaemonWindows.h b/src/lib/arch/win32/ArchDaemonWindows.h
new file mode 100644
index 0000000..2db9792
--- /dev/null
+++ b/src/lib/arch/win32/ArchDaemonWindows.h
@@ -0,0 +1,151 @@
+/*
+ * 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 "arch/IArchDaemon.h"
+#include "arch/IArchMultithread.h"
+#include "common/stdstring.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <tchar.h>
+
+#define ARCH_DAEMON ArchDaemonWindows
+
+//! Win32 implementation of IArchDaemon
+class ArchDaemonWindows : public IArchDaemon {
+public:
+ typedef int (*RunFunc)(void);
+
+ ArchDaemonWindows();
+ virtual ~ArchDaemonWindows();
+
+ //! Run the daemon
+ /*!
+ When the client calls \c daemonize(), the \c DaemonFunc should call this
+ function after initialization and argument parsing to perform the
+ daemon processing. The \c runFunc should perform the daemon's
+ main loop, calling \c daemonRunning(true) when it enters the main loop
+ (i.e. after initialization) and \c daemonRunning(false) when it leaves
+ the main loop. The \c runFunc is called in a new thread and when the
+ daemon must exit the main loop due to some external control the
+ getDaemonQuitMessage() is posted to the thread. This function returns
+ what \c runFunc returns. \c runFunc should call \c daemonFailed() if
+ the daemon fails.
+ */
+ static int runDaemon(RunFunc runFunc);
+
+ //! Indicate daemon is in main loop
+ /*!
+ The \c runFunc passed to \c runDaemon() should call this function
+ to indicate when it has entered (\c running is \c true) or exited
+ (\c running is \c false) the main loop.
+ */
+ static void daemonRunning(bool running);
+
+ //! Indicate failure of running daemon
+ /*!
+ The \c runFunc passed to \c runDaemon() should call this function
+ to indicate failure. \c result is returned by \c daemonize().
+ */
+ static void daemonFailed(int result);
+
+ //! Get daemon quit message
+ /*!
+ The windows NT daemon tells daemon thread to exit by posting this
+ message to it. The thread must, of course, have a message queue
+ for this to work.
+ */
+ static UINT getDaemonQuitMessage();
+
+ // IArchDaemon overrides
+ virtual void installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies);
+ virtual void uninstallDaemon(const char* name);
+ virtual void installDaemon();
+ virtual void uninstallDaemon();
+ virtual int daemonize(const char* name, DaemonFunc func);
+ virtual bool canInstallDaemon(const char* name);
+ virtual bool isDaemonInstalled(const char* name);
+ std::string commandLine() const { return m_commandLine; }
+
+private:
+ static HKEY openNTServicesKey();
+
+ int doRunDaemon(RunFunc runFunc);
+ void doDaemonRunning(bool running);
+ UINT doGetDaemonQuitMessage();
+
+ static void setStatus(DWORD state);
+ static void setStatus(DWORD state, DWORD step, DWORD waitHint);
+ static void setStatusError(DWORD error);
+
+ static bool isRunState(DWORD state);
+
+ void serviceMain(DWORD, LPTSTR*);
+ static void WINAPI serviceMainEntry(DWORD, LPTSTR*);
+
+ void serviceHandler(DWORD ctrl);
+ static void WINAPI serviceHandlerEntry(DWORD ctrl);
+
+ void start(const char* name);
+ void stop(const char* name);
+
+private:
+ class XArchDaemonRunFailed {
+ public:
+ XArchDaemonRunFailed(int result) : m_result(result) { }
+
+ public:
+ int m_result;
+ };
+
+private:
+ static ArchDaemonWindows* s_daemon;
+
+ ArchMutex m_serviceMutex;
+ ArchCond m_serviceCondVar;
+ DWORD m_serviceState;
+ bool m_serviceHandlerWaiting;
+ bool m_serviceRunning;
+
+ DWORD m_daemonThreadID;
+ DaemonFunc m_daemonFunc;
+ int m_daemonResult;
+
+ SERVICE_STATUS_HANDLE m_statusHandle;
+
+ UINT m_quitMessage;
+
+ std::string m_commandLine;
+};
+
+#define DEFAULT_DAEMON_NAME _T("Barrier")
+#define DEFAULT_DAEMON_INFO _T("Manages the Barrier foreground processes.")
+
+static const TCHAR* const g_daemonKeyPath[] = {
+ _T("SOFTWARE"),
+ _T("The Barrier Project"),
+ _T("Barrier"),
+ _T("Service"),
+ NULL
+};
diff --git a/src/lib/arch/win32/ArchFileWindows.cpp b/src/lib/arch/win32/ArchFileWindows.cpp
new file mode 100644
index 0000000..53b4b59
--- /dev/null
+++ b/src/lib/arch/win32/ArchFileWindows.cpp
@@ -0,0 +1,203 @@
+/*
+ * 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 "arch/win32/ArchFileWindows.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <shlobj.h>
+#include <tchar.h>
+#include <string.h>
+
+//
+// ArchFileWindows
+//
+
+ArchFileWindows::ArchFileWindows()
+{
+ // do nothing
+}
+
+ArchFileWindows::~ArchFileWindows()
+{
+ // do nothing
+}
+
+const char*
+ArchFileWindows::getBasename(const char* pathname)
+{
+ if (pathname == NULL) {
+ return NULL;
+ }
+
+ // check for last slash
+ const char* basename = strrchr(pathname, '/');
+ if (basename != NULL) {
+ ++basename;
+ }
+ else {
+ basename = pathname;
+ }
+
+ // check for last backslash
+ const char* basename2 = strrchr(pathname, '\\');
+ if (basename2 != NULL && basename2 > basename) {
+ basename = basename2 + 1;
+ }
+
+ return basename;
+}
+
+std::string
+ArchFileWindows::getUserDirectory()
+{
+ // try %HOMEPATH%
+ TCHAR dir[MAX_PATH];
+ DWORD size = sizeof(dir) / sizeof(TCHAR);
+ DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size);
+ if (result != 0 && result <= size) {
+ // sanity check -- if dir doesn't appear to start with a
+ // drive letter and isn't a UNC name then don't use it
+ // FIXME -- allow UNC names
+ if (dir[0] != '\0' && (dir[1] == ':' ||
+ ((dir[0] == '\\' || dir[0] == '/') &&
+ (dir[1] == '\\' || dir[1] == '/')))) {
+ return dir;
+ }
+ }
+
+ // get the location of the personal files. that's as close to
+ // a home directory as we're likely to find.
+ ITEMIDLIST* idl;
+ if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) {
+ TCHAR* path = NULL;
+ if (SHGetPathFromIDList(idl, dir)) {
+ DWORD attr = GetFileAttributes(dir);
+ if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ path = dir;
+ }
+
+ IMalloc* shalloc;
+ if (SUCCEEDED(SHGetMalloc(&shalloc))) {
+ shalloc->Free(idl);
+ shalloc->Release();
+ }
+
+ if (path != NULL) {
+ return path;
+ }
+ }
+
+ // use root of C drive as a default
+ return "C:";
+}
+
+std::string
+ArchFileWindows::getSystemDirectory()
+{
+ // get windows directory
+ char dir[MAX_PATH];
+ if (GetWindowsDirectory(dir, sizeof(dir)) != 0) {
+ return dir;
+ }
+ else {
+ // can't get it. use C:\ as a default.
+ return "C:";
+ }
+}
+
+std::string
+ArchFileWindows::getInstalledDirectory()
+{
+ char fileNameBuffer[MAX_PATH];
+ GetModuleFileName(NULL, fileNameBuffer, MAX_PATH);
+ std::string fileName(fileNameBuffer);
+ size_t lastSlash = fileName.find_last_of("\\");
+ fileName = fileName.substr(0, lastSlash);
+
+ return fileName;
+}
+
+std::string
+ArchFileWindows::getLogDirectory()
+{
+ return getInstalledDirectory();
+}
+
+std::string
+ArchFileWindows::getPluginDirectory()
+{
+ if (!m_pluginDirectory.empty()) {
+ return m_pluginDirectory;
+ }
+
+ std::string dir = getProfileDirectory();
+ dir.append("\\Plugins");
+ return dir;
+}
+
+std::string
+ArchFileWindows::getProfileDirectory()
+{
+ String dir;
+ if (!m_profileDirectory.empty()) {
+ dir = m_profileDirectory;
+ }
+ else {
+ TCHAR result[MAX_PATH];
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, result))) {
+ dir = result;
+ }
+ else {
+ dir = getUserDirectory();
+ }
+ }
+
+ // HACK: append program name, this seems wrong.
+ dir.append("\\Barrier");
+
+ return dir;
+}
+
+std::string
+ArchFileWindows::concatPath(const std::string& prefix,
+ const std::string& suffix)
+{
+ std::string path;
+ path.reserve(prefix.size() + 1 + suffix.size());
+ path += prefix;
+ if (path.size() == 0 ||
+ (path[path.size() - 1] != '\\' &&
+ path[path.size() - 1] != '/')) {
+ path += '\\';
+ }
+ path += suffix;
+ return path;
+}
+
+void
+ArchFileWindows::setProfileDirectory(const String& s)
+{
+ m_profileDirectory = s;
+}
+
+void
+ArchFileWindows::setPluginDirectory(const String& s)
+{
+ m_pluginDirectory = s;
+}
diff --git a/src/lib/arch/win32/ArchFileWindows.h b/src/lib/arch/win32/ArchFileWindows.h
new file mode 100644
index 0000000..4747b9c
--- /dev/null
+++ b/src/lib/arch/win32/ArchFileWindows.h
@@ -0,0 +1,47 @@
+/*
+ * 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 "arch/IArchFile.h"
+
+#define ARCH_FILE ArchFileWindows
+
+//! Win32 implementation of IArchFile
+class ArchFileWindows : public IArchFile {
+public:
+ ArchFileWindows();
+ virtual ~ArchFileWindows();
+
+ // IArchFile overrides
+ virtual const char* getBasename(const char* pathname);
+ virtual std::string getUserDirectory();
+ virtual std::string getSystemDirectory();
+ virtual std::string getInstalledDirectory();
+ virtual std::string getLogDirectory();
+ virtual std::string getPluginDirectory();
+ virtual std::string getProfileDirectory();
+ virtual std::string concatPath(const std::string& prefix,
+ const std::string& suffix);
+ virtual void setProfileDirectory(const String& s);
+ virtual void setPluginDirectory(const String& s);
+
+private:
+ String m_profileDirectory;
+ String m_pluginDirectory;
+};
diff --git a/src/lib/arch/win32/ArchInternetWindows.cpp b/src/lib/arch/win32/ArchInternetWindows.cpp
new file mode 100644
index 0000000..7f69c7f
--- /dev/null
+++ b/src/lib/arch/win32/ArchInternetWindows.cpp
@@ -0,0 +1,224 @@
+/*
+ * 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 "arch/win32/ArchInternetWindows.h"
+#include "arch/win32/XArchWindows.h"
+#include "arch/Arch.h"
+#include "common/Version.h"
+
+#include <sstream>
+#include <Wininet.h>
+#include <Shlwapi.h>
+
+struct WinINetUrl {
+ String m_scheme;
+ String m_host;
+ String m_path;
+ INTERNET_PORT m_port;
+ DWORD m_flags;
+};
+
+class WinINetRequest {
+public:
+ WinINetRequest(const String& url);
+ ~WinINetRequest();
+
+ String send();
+ void openSession();
+ void connect();
+ void openRequest();
+
+private:
+ HINTERNET m_session;
+ HINTERNET m_connect;
+ HINTERNET m_request;
+ WinINetUrl m_url;
+ bool m_used;
+};
+
+//
+// ArchInternetWindows
+//
+
+String
+ArchInternetWindows::get(const String& url)
+{
+ WinINetRequest request(url);
+ return request.send();
+}
+
+String
+ArchInternetWindows::urlEncode(const String& url)
+{
+ TCHAR buffer[1024];
+ DWORD bufferSize = sizeof(buffer);
+
+ if (UrlEscape(url.c_str(), buffer, &bufferSize, URL_ESCAPE_UNSAFE) != S_OK) {
+ throw XArch(new XArchEvalWindows());
+ }
+
+ String result(buffer);
+
+ // the win32 url encoding funcitons are pretty useless (to us) and only
+ // escape "unsafe" chars, but not + or =, so we need to replace these
+ // manually (and probably many other chars).
+ barrier::string::findReplaceAll(result, "+", "%2B");
+ barrier::string::findReplaceAll(result, "=", "%3D");
+
+ return result;
+}
+
+//
+// WinINetRequest
+//
+
+static WinINetUrl parseUrl(const String& url);
+
+WinINetRequest::WinINetRequest(const String& url) :
+ m_session(NULL),
+ m_connect(NULL),
+ m_request(NULL),
+ m_used(false),
+ m_url(parseUrl(url))
+{
+}
+
+WinINetRequest::~WinINetRequest()
+{
+ if (m_request != NULL) {
+ InternetCloseHandle(m_request);
+ }
+
+ if (m_connect != NULL) {
+ InternetCloseHandle(m_connect);
+ }
+
+ if (m_session != NULL) {
+ InternetCloseHandle(m_session);
+ }
+}
+
+String
+WinINetRequest::send()
+{
+ if (m_used) {
+ throw XArch("class is one time use.");
+ }
+ m_used = true;
+
+ openSession();
+ connect();
+ openRequest();
+
+ String headers("Content-Type: text/html");
+ if (!HttpSendRequest(m_request, headers.c_str(), (DWORD)headers.length(), NULL, NULL)) {
+ throw XArch(new XArchEvalWindows());
+ }
+
+ std::stringstream result;
+ CHAR buffer[1025];
+ DWORD read = 0;
+
+ while (InternetReadFile(m_request, buffer, sizeof(buffer) - 1, &read) && (read != 0)) {
+ buffer[read] = 0;
+ result << buffer;
+ read = 0;
+ }
+
+ return result.str();
+}
+
+void
+WinINetRequest::openSession()
+{
+ std::stringstream userAgent;
+ userAgent << "Barrier ";
+ userAgent << kVersion;
+
+ m_session = InternetOpen(
+ userAgent.str().c_str(),
+ INTERNET_OPEN_TYPE_PRECONFIG,
+ NULL,
+ NULL,
+ NULL);
+
+ if (m_session == NULL) {
+ throw XArch(new XArchEvalWindows());
+ }
+}
+
+void
+WinINetRequest::connect()
+{
+ m_connect = InternetConnect(
+ m_session,
+ m_url.m_host.c_str(),
+ m_url.m_port,
+ NULL,
+ NULL,
+ INTERNET_SERVICE_HTTP,
+ NULL,
+ NULL);
+
+ if (m_connect == NULL) {
+ throw XArch(new XArchEvalWindows());
+ }
+}
+
+void
+WinINetRequest::openRequest()
+{
+ m_request = HttpOpenRequest(
+ m_connect,
+ "GET",
+ m_url.m_path.c_str(),
+ HTTP_VERSION,
+ NULL,
+ NULL,
+ m_url.m_flags,
+ NULL);
+
+ if (m_request == NULL) {
+ throw XArch(new XArchEvalWindows());
+ }
+}
+
+// nb: i tried to use InternetCrackUrl here, but couldn't quite get that to
+// work. here's some (less robust) code to split the url into components.
+// this works fine with simple urls, but doesn't consider the full url spec.
+static WinINetUrl
+parseUrl(const String& url)
+{
+ WinINetUrl parsed;
+
+ size_t schemeEnd = url.find("://");
+ size_t hostEnd = url.find('/', schemeEnd + 3);
+
+ parsed.m_scheme = url.substr(0, schemeEnd);
+ parsed.m_host = url.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
+ parsed.m_path = url.substr(hostEnd);
+
+ parsed.m_port = INTERNET_DEFAULT_HTTP_PORT;
+ parsed.m_flags = 0;
+
+ if (parsed.m_scheme.find("https") != String::npos) {
+ parsed.m_port = INTERNET_DEFAULT_HTTPS_PORT;
+ parsed.m_flags = INTERNET_FLAG_SECURE;
+ }
+
+ return parsed;
+}
diff --git a/src/lib/arch/win32/ArchInternetWindows.h b/src/lib/arch/win32/ArchInternetWindows.h
new file mode 100644
index 0000000..bab8d3c
--- /dev/null
+++ b/src/lib/arch/win32/ArchInternetWindows.h
@@ -0,0 +1,28 @@
+/*
+ * 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
+
+#define ARCH_INTERNET ArchInternetWindows
+
+#include "base/String.h"
+
+class ArchInternetWindows {
+public:
+ String get(const String& url);
+ String urlEncode(const String& url);
+};
diff --git a/src/lib/arch/win32/ArchLogWindows.cpp b/src/lib/arch/win32/ArchLogWindows.cpp
new file mode 100644
index 0000000..bc17abf
--- /dev/null
+++ b/src/lib/arch/win32/ArchLogWindows.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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 "arch/win32/ArchLogWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+
+#include <string.h>
+
+//
+// ArchLogWindows
+//
+
+ArchLogWindows::ArchLogWindows() : m_eventLog(NULL)
+{
+ // do nothing
+}
+
+ArchLogWindows::~ArchLogWindows()
+{
+ // do nothing
+}
+
+void
+ArchLogWindows::openLog(const char* name)
+{
+ if (m_eventLog == NULL) {
+ m_eventLog = RegisterEventSource(NULL, name);
+ }
+}
+
+void
+ArchLogWindows::closeLog()
+{
+ if (m_eventLog != NULL) {
+ DeregisterEventSource(m_eventLog);
+ m_eventLog = NULL;
+ }
+}
+
+void
+ArchLogWindows::showLog(bool)
+{
+ // do nothing
+}
+
+void
+ArchLogWindows::writeLog(ELevel level, const char* msg)
+{
+ if (m_eventLog != NULL) {
+ // convert priority
+ WORD type;
+ switch (level) {
+ case kERROR:
+ type = EVENTLOG_ERROR_TYPE;
+ break;
+
+ case kWARNING:
+ type = EVENTLOG_WARNING_TYPE;
+ break;
+
+ default:
+ type = EVENTLOG_INFORMATION_TYPE;
+ break;
+ }
+
+ // log it
+ // FIXME -- win32 wants to use a message table to look up event
+ // strings. log messages aren't organized that way so we'll
+ // just dump our string into the raw data section of the event
+ // so users can at least see the message. note that we use our
+ // level as the event category.
+ ReportEvent(m_eventLog, type, static_cast<WORD>(level),
+ 0, // event ID
+ NULL,
+ 0,
+ (DWORD)strlen(msg) + 1, // raw data size
+ NULL,
+ const_cast<char*>(msg));// raw data
+ }
+}
diff --git a/src/lib/arch/win32/ArchLogWindows.h b/src/lib/arch/win32/ArchLogWindows.h
new file mode 100644
index 0000000..3a997f1
--- /dev/null
+++ b/src/lib/arch/win32/ArchLogWindows.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 "arch/IArchLog.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#define ARCH_LOG ArchLogWindows
+
+//! Win32 implementation of IArchLog
+class ArchLogWindows : public IArchLog {
+public:
+ ArchLogWindows();
+ virtual ~ArchLogWindows();
+
+ // IArchLog overrides
+ virtual void openLog(const char* name);
+ virtual void closeLog();
+ virtual void showLog(bool showIfEmpty);
+ virtual void writeLog(ELevel, const char*);
+
+private:
+ HANDLE m_eventLog;
+};
diff --git a/src/lib/arch/win32/ArchMiscWindows.cpp b/src/lib/arch/win32/ArchMiscWindows.cpp
new file mode 100644
index 0000000..924afa2
--- /dev/null
+++ b/src/lib/arch/win32/ArchMiscWindows.cpp
@@ -0,0 +1,524 @@
+/*
+ * 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 "arch/win32/ArchMiscWindows.h"
+#include "arch/win32/ArchDaemonWindows.h"
+#include "base/Log.h"
+#include "common/Version.h"
+
+#include <Wtsapi32.h>
+#pragma warning(disable: 4099)
+#include <Userenv.h>
+#pragma warning(default: 4099)
+
+// parent process name for services in Vista
+#define SERVICE_LAUNCHER "services.exe"
+
+#ifndef ES_SYSTEM_REQUIRED
+#define ES_SYSTEM_REQUIRED ((DWORD)0x00000001)
+#endif
+#ifndef ES_DISPLAY_REQUIRED
+#define ES_DISPLAY_REQUIRED ((DWORD)0x00000002)
+#endif
+#ifndef ES_CONTINUOUS
+#define ES_CONTINUOUS ((DWORD)0x80000000)
+#endif
+typedef DWORD EXECUTION_STATE;
+
+//
+// ArchMiscWindows
+//
+
+ArchMiscWindows::Dialogs* ArchMiscWindows::s_dialogs = NULL;
+DWORD ArchMiscWindows::s_busyState = 0;
+ArchMiscWindows::STES_t ArchMiscWindows::s_stes = NULL;
+HICON ArchMiscWindows::s_largeIcon = NULL;
+HICON ArchMiscWindows::s_smallIcon = NULL;
+HINSTANCE ArchMiscWindows::s_instanceWin32 = NULL;
+
+void
+ArchMiscWindows::cleanup()
+{
+ delete s_dialogs;
+}
+
+void
+ArchMiscWindows::init()
+{
+ // stop windows system error dialogs from showing.
+ SetErrorMode(SEM_FAILCRITICALERRORS);
+
+ s_dialogs = new Dialogs;
+}
+
+void
+ArchMiscWindows::setIcons(HICON largeIcon, HICON smallIcon)
+{
+ s_largeIcon = largeIcon;
+ s_smallIcon = smallIcon;
+}
+
+void
+ArchMiscWindows::getIcons(HICON& largeIcon, HICON& smallIcon)
+{
+ largeIcon = s_largeIcon;
+ smallIcon = s_smallIcon;
+}
+
+int
+ArchMiscWindows::runDaemon(RunFunc runFunc)
+{
+ return ArchDaemonWindows::runDaemon(runFunc);
+}
+
+void
+ArchMiscWindows::daemonRunning(bool running)
+{
+ ArchDaemonWindows::daemonRunning(running);
+}
+
+void
+ArchMiscWindows::daemonFailed(int result)
+{
+ ArchDaemonWindows::daemonFailed(result);
+}
+
+UINT
+ArchMiscWindows::getDaemonQuitMessage()
+{
+ return ArchDaemonWindows::getDaemonQuitMessage();
+}
+
+HKEY
+ArchMiscWindows::openKey(HKEY key, const TCHAR* keyName)
+{
+ return openKey(key, keyName, false);
+}
+
+HKEY
+ArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames)
+{
+ return openKey(key, keyNames, false);
+}
+
+HKEY
+ArchMiscWindows::addKey(HKEY key, const TCHAR* keyName)
+{
+ return openKey(key, keyName, true);
+}
+
+HKEY
+ArchMiscWindows::addKey(HKEY key, const TCHAR* const* keyNames)
+{
+ return openKey(key, keyNames, true);
+}
+
+HKEY
+ArchMiscWindows::openKey(HKEY key, const TCHAR* keyName, bool create)
+{
+ // ignore if parent is NULL
+ if (key == NULL) {
+ return NULL;
+ }
+
+ // open next key
+ HKEY newKey;
+ LONG result = RegOpenKeyEx(key, keyName, 0,
+ KEY_WRITE | KEY_QUERY_VALUE, &newKey);
+ if (result != ERROR_SUCCESS && create) {
+ DWORD disp;
+ result = RegCreateKeyEx(key, keyName, 0, TEXT(""),
+ 0, KEY_WRITE | KEY_QUERY_VALUE,
+ NULL, &newKey, &disp);
+ }
+ if (result != ERROR_SUCCESS) {
+ RegCloseKey(key);
+ return NULL;
+ }
+
+ // switch to new key
+ RegCloseKey(key);
+ return newKey;
+}
+
+HKEY
+ArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames, bool create)
+{
+ for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) {
+ // open next key
+ key = openKey(key, keyNames[i], create);
+ }
+ return key;
+}
+
+void
+ArchMiscWindows::closeKey(HKEY key)
+{
+ assert(key != NULL);
+ if (key==NULL) return;
+ RegCloseKey(key);
+}
+
+void
+ArchMiscWindows::deleteKey(HKEY key, const TCHAR* name)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ if (key==NULL || name==NULL) return;
+ RegDeleteKey(key, name);
+}
+
+void
+ArchMiscWindows::deleteValue(HKEY key, const TCHAR* name)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ if (key==NULL || name==NULL) return;
+ RegDeleteValue(key, name);
+}
+
+bool
+ArchMiscWindows::hasValue(HKEY key, const TCHAR* name)
+{
+ DWORD type;
+ LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL);
+ return (result == ERROR_SUCCESS &&
+ (type == REG_DWORD || type == REG_SZ));
+}
+
+ArchMiscWindows::EValueType
+ArchMiscWindows::typeOfValue(HKEY key, const TCHAR* name)
+{
+ DWORD type;
+ LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL);
+ if (result != ERROR_SUCCESS) {
+ return kNO_VALUE;
+ }
+ switch (type) {
+ case REG_DWORD:
+ return kUINT;
+
+ case REG_SZ:
+ return kSTRING;
+
+ case REG_BINARY:
+ return kBINARY;
+
+ default:
+ return kUNKNOWN;
+ }
+}
+
+void
+ArchMiscWindows::setValue(HKEY key,
+ const TCHAR* name, const std::string& value)
+{
+ assert(key != NULL);
+ if (key == NULL) {
+ // TODO: throw exception
+ return;
+ }
+ RegSetValueEx(key, name, 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(value.c_str()),
+ (DWORD)value.size() + 1);
+}
+
+void
+ArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value)
+{
+ assert(key != NULL);
+ if (key == NULL) {
+ // TODO: throw exception
+ return;
+ }
+ RegSetValueEx(key, name, 0, REG_DWORD,
+ reinterpret_cast<CONST BYTE*>(&value),
+ sizeof(DWORD));
+}
+
+void
+ArchMiscWindows::setValueBinary(HKEY key,
+ const TCHAR* name, const std::string& value)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ if (key == NULL || name == NULL) {
+ // TODO: throw exception
+ return;
+ }
+ RegSetValueEx(key, name, 0, REG_BINARY,
+ reinterpret_cast<const BYTE*>(value.data()),
+ (DWORD)value.size());
+}
+
+std::string
+ArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR* name, DWORD type)
+{
+ // get the size of the string
+ DWORD actualType;
+ DWORD size = 0;
+ LONG result = RegQueryValueEx(key, name, 0, &actualType, NULL, &size);
+ if (result != ERROR_SUCCESS || actualType != type) {
+ return std::string();
+ }
+
+ // if zero size then return empty string
+ if (size == 0) {
+ return std::string();
+ }
+
+ // allocate space
+ char* buffer = new char[size];
+
+ // read it
+ result = RegQueryValueEx(key, name, 0, &actualType,
+ reinterpret_cast<BYTE*>(buffer), &size);
+ if (result != ERROR_SUCCESS || actualType != type) {
+ delete[] buffer;
+ return std::string();
+ }
+
+ // clean up and return value
+ if (type == REG_SZ && buffer[size - 1] == '\0') {
+ // don't include terminating nul; std::string will add one.
+ --size;
+ }
+ std::string value(buffer, size);
+ delete[] buffer;
+ return value;
+}
+
+std::string
+ArchMiscWindows::readValueString(HKEY key, const TCHAR* name)
+{
+ return readBinaryOrString(key, name, REG_SZ);
+}
+
+std::string
+ArchMiscWindows::readValueBinary(HKEY key, const TCHAR* name)
+{
+ return readBinaryOrString(key, name, REG_BINARY);
+}
+
+DWORD
+ArchMiscWindows::readValueInt(HKEY key, const TCHAR* name)
+{
+ DWORD type;
+ DWORD value;
+ DWORD size = sizeof(value);
+ LONG result = RegQueryValueEx(key, name, 0, &type,
+ reinterpret_cast<BYTE*>(&value), &size);
+ if (result != ERROR_SUCCESS || type != REG_DWORD) {
+ return 0;
+ }
+ return value;
+}
+
+void
+ArchMiscWindows::addDialog(HWND hwnd)
+{
+ s_dialogs->insert(hwnd);
+}
+
+void
+ArchMiscWindows::removeDialog(HWND hwnd)
+{
+ s_dialogs->erase(hwnd);
+}
+
+bool
+ArchMiscWindows::processDialog(MSG* msg)
+{
+ for (Dialogs::const_iterator index = s_dialogs->begin();
+ index != s_dialogs->end(); ++index) {
+ if (IsDialogMessage(*index, msg)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+ArchMiscWindows::addBusyState(DWORD busyModes)
+{
+ s_busyState |= busyModes;
+ setThreadExecutionState(s_busyState);
+}
+
+void
+ArchMiscWindows::removeBusyState(DWORD busyModes)
+{
+ s_busyState &= ~busyModes;
+ setThreadExecutionState(s_busyState);
+}
+
+void
+ArchMiscWindows::setThreadExecutionState(DWORD busyModes)
+{
+ // look up function dynamically so we work on older systems
+ if (s_stes == NULL) {
+ HINSTANCE kernel = LoadLibrary("kernel32.dll");
+ if (kernel != NULL) {
+ s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel,
+ "SetThreadExecutionState"));
+ }
+ if (s_stes == NULL) {
+ s_stes = &ArchMiscWindows::dummySetThreadExecutionState;
+ }
+ }
+
+ // convert to STES form
+ EXECUTION_STATE state = 0;
+ if ((busyModes & kSYSTEM) != 0) {
+ state |= ES_SYSTEM_REQUIRED;
+ }
+ if ((busyModes & kDISPLAY) != 0) {
+ state |= ES_DISPLAY_REQUIRED;
+ }
+ if (state != 0) {
+ state |= ES_CONTINUOUS;
+ }
+
+ // do it
+ s_stes(state);
+}
+
+DWORD
+ArchMiscWindows::dummySetThreadExecutionState(DWORD)
+{
+ // do nothing
+ return 0;
+}
+
+void
+ArchMiscWindows::wakeupDisplay()
+{
+ // We can't use ::setThreadExecutionState here because it sets
+ // ES_CONTINUOUS, which we don't want.
+
+ if (s_stes == NULL) {
+ HINSTANCE kernel = LoadLibrary("kernel32.dll");
+ if (kernel != NULL) {
+ s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel,
+ "SetThreadExecutionState"));
+ }
+ if (s_stes == NULL) {
+ s_stes = &ArchMiscWindows::dummySetThreadExecutionState;
+ }
+ }
+
+ s_stes(ES_DISPLAY_REQUIRED);
+
+ // restore the original execution states
+ setThreadExecutionState(s_busyState);
+}
+
+bool
+ArchMiscWindows::wasLaunchedAsService()
+{
+ String name;
+ if (!getParentProcessName(name)) {
+ LOG((CLOG_ERR "cannot determine if process was launched as service"));
+ return false;
+ }
+
+ return (name == SERVICE_LAUNCHER);
+}
+
+bool
+ArchMiscWindows::getParentProcessName(String &name)
+{
+ PROCESSENTRY32 parentEntry;
+ if (!getParentProcessEntry(parentEntry)){
+ LOG((CLOG_ERR "could not get entry for parent process"));
+ return false;
+ }
+
+ name = parentEntry.szExeFile;
+ return true;
+}
+
+BOOL WINAPI
+ArchMiscWindows::getSelfProcessEntry(PROCESSENTRY32& entry)
+{
+ // get entry from current PID
+ return getProcessEntry(entry, GetCurrentProcessId());
+}
+
+BOOL WINAPI
+ArchMiscWindows::getParentProcessEntry(PROCESSENTRY32& entry)
+{
+ // get the current process, so we can get parent PID
+ PROCESSENTRY32 selfEntry;
+ if (!getSelfProcessEntry(selfEntry)) {
+ return FALSE;
+ }
+
+ // get entry from parent PID
+ return getProcessEntry(entry, selfEntry.th32ParentProcessID);
+}
+
+BOOL WINAPI
+ArchMiscWindows::getProcessEntry(PROCESSENTRY32& entry, DWORD processID)
+{
+ // first we need to take a snapshot of the running processes
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (snapshot == INVALID_HANDLE_VALUE) {
+ LOG((CLOG_ERR "could not get process snapshot (error: %i)",
+ GetLastError()));
+ return FALSE;
+ }
+
+ entry.dwSize = sizeof(PROCESSENTRY32);
+
+ // get the first process, and if we can't do that then it's
+ // unlikely we can go any further
+ BOOL gotEntry = Process32First(snapshot, &entry);
+ if (!gotEntry) {
+ LOG((CLOG_ERR "could not get first process entry (error: %i)",
+ GetLastError()));
+ return FALSE;
+ }
+
+ while(gotEntry) {
+
+ if (entry.th32ProcessID == processID) {
+ // found current process
+ return TRUE;
+ }
+
+ // now move on to the next entry (when we reach end, loop will stop)
+ gotEntry = Process32Next(snapshot, &entry);
+ }
+
+ return FALSE;
+}
+
+HINSTANCE
+ArchMiscWindows::instanceWin32()
+{
+ assert(s_instanceWin32 != NULL);
+ return s_instanceWin32;
+}
+
+void
+ArchMiscWindows::setInstanceWin32(HINSTANCE instance)
+{
+ assert(instance != NULL);
+ s_instanceWin32 = instance;
+} \ No newline at end of file
diff --git a/src/lib/arch/win32/ArchMiscWindows.h b/src/lib/arch/win32/ArchMiscWindows.h
new file mode 100644
index 0000000..0ecd79d
--- /dev/null
+++ b/src/lib/arch/win32/ArchMiscWindows.h
@@ -0,0 +1,202 @@
+/*
+ * 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/common.h"
+#include "common/stdstring.h"
+#include "common/stdset.h"
+#include "base/String.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <Tlhelp32.h>
+
+//! Miscellaneous win32 functions.
+class ArchMiscWindows {
+public:
+ enum EValueType {
+ kUNKNOWN,
+ kNO_VALUE,
+ kUINT,
+ kSTRING,
+ kBINARY
+ };
+ enum EBusyModes {
+ kIDLE = 0x0000,
+ kSYSTEM = 0x0001,
+ kDISPLAY = 0x0002
+ };
+
+ typedef int (*RunFunc)(void);
+
+ //! Initialize
+ static void init();
+
+ //! Delete memory
+ static void cleanup();
+
+ //! Set the application icons
+ /*!
+ Set the application icons.
+ */
+ static void setIcons(HICON largeIcon, HICON smallIcon);
+
+ //! Get the application icons
+ /*!
+ Get the application icons.
+ */
+ static void getIcons(HICON& largeIcon, HICON& smallIcon);
+
+ //! Run the daemon
+ /*!
+ Delegates to ArchDaemonWindows.
+ */
+ static int runDaemon(RunFunc runFunc);
+
+ //! Indicate daemon is in main loop
+ /*!
+ Delegates to ArchDaemonWindows.
+ */
+ static void daemonRunning(bool running);
+
+ //! Indicate failure of running daemon
+ /*!
+ Delegates to ArchDaemonWindows.
+ */
+ static void daemonFailed(int result);
+
+ //! Get daemon quit message
+ /*!
+ Delegates to ArchDaemonWindows.
+ */
+ static UINT getDaemonQuitMessage();
+
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* child);
+
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* const* keyPath);
+
+ //! Open/create and return a registry key, closing the parent key
+ static HKEY addKey(HKEY parent, const TCHAR* child);
+
+ //! Open/create and return a registry key, closing the parent key
+ static HKEY addKey(HKEY parent, const TCHAR* const* keyPath);
+
+ //! Close a key
+ static void closeKey(HKEY);
+
+ //! Delete a key (which should have no subkeys)
+ static void deleteKey(HKEY parent, const TCHAR* name);
+
+ //! Delete a value
+ static void deleteValue(HKEY parent, const TCHAR* name);
+
+ //! Test if a value exists
+ static bool hasValue(HKEY key, const TCHAR* name);
+
+ //! Get type of value
+ static EValueType typeOfValue(HKEY key, const TCHAR* name);
+
+ //! Set a string value in the registry
+ static void setValue(HKEY key, const TCHAR* name,
+ const std::string& value);
+
+ //! Set a DWORD value in the registry
+ static void setValue(HKEY key, const TCHAR* name, DWORD value);
+
+ //! Set a BINARY value in the registry
+ /*!
+ Sets the \p name value of \p key to \p value.data().
+ */
+ static void setValueBinary(HKEY key, const TCHAR* name,
+ const std::string& value);
+
+ //! Read a string value from the registry
+ static std::string readValueString(HKEY, const TCHAR* name);
+
+ //! Read a DWORD value from the registry
+ static DWORD readValueInt(HKEY, const TCHAR* name);
+
+ //! Read a BINARY value from the registry
+ static std::string readValueBinary(HKEY, const TCHAR* name);
+
+ //! Add a dialog
+ static void addDialog(HWND);
+
+ //! Remove a dialog
+ static void removeDialog(HWND);
+
+ //! Process dialog message
+ /*!
+ Checks if the message is destined for a dialog. If so the message
+ is passed to the dialog and returns true, otherwise returns false.
+ */
+ static bool processDialog(MSG*);
+
+ //! Disable power saving
+ static void addBusyState(DWORD busyModes);
+
+ //! Enable power saving
+ static void removeBusyState(DWORD busyModes);
+
+ //! Briefly interrupt power saving
+ static void wakeupDisplay();
+
+ //! Returns true if this process was launched via NT service host.
+ static bool wasLaunchedAsService();
+
+ //! Returns true if we got the parent process name.
+ static bool getParentProcessName(String &name);
+
+ static HINSTANCE instanceWin32();
+
+ static void setInstanceWin32(HINSTANCE instance);
+
+ static BOOL WINAPI getProcessEntry(PROCESSENTRY32& entry, DWORD processID);
+ static BOOL WINAPI getSelfProcessEntry(PROCESSENTRY32& entry);
+ static BOOL WINAPI getParentProcessEntry(PROCESSENTRY32& entry);
+
+private:
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* child, bool create);
+
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* const* keyPath,
+ bool create);
+
+ //! Read a string value from the registry
+ static std::string readBinaryOrString(HKEY, const TCHAR* name, DWORD type);
+
+ //! Set thread busy state
+ static void setThreadExecutionState(DWORD);
+
+ static DWORD WINAPI dummySetThreadExecutionState(DWORD);
+
+private:
+ typedef std::set<HWND> Dialogs;
+ typedef DWORD (WINAPI *STES_t)(DWORD);
+
+ static Dialogs* s_dialogs;
+ static DWORD s_busyState;
+ static STES_t s_stes;
+ static HICON s_largeIcon;
+ static HICON s_smallIcon;
+ static HINSTANCE s_instanceWin32;
+};
diff --git a/src/lib/arch/win32/ArchMultithreadWindows.cpp b/src/lib/arch/win32/ArchMultithreadWindows.cpp
new file mode 100644
index 0000000..d3fd059
--- /dev/null
+++ b/src/lib/arch/win32/ArchMultithreadWindows.cpp
@@ -0,0 +1,705 @@
+/*
+ * 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/>.
+ */
+
+#if defined(_MSC_VER) && !defined(_MT)
+# error multithreading compile option is required
+#endif
+
+#include "arch/win32/ArchMultithreadWindows.h"
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+
+#include <process.h>
+
+//
+// note -- implementation of condition variable taken from:
+// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
+// titled "Strategies for Implementing POSIX Condition Variables
+// on Win32." it also provides an implementation that doesn't
+// suffer from the incorrectness problem described in our
+// corresponding header but it is slower, still unfair, and
+// can cause busy waiting.
+//
+
+//
+// ArchThreadImpl
+//
+
+class ArchThreadImpl {
+public:
+ ArchThreadImpl();
+ ~ArchThreadImpl();
+
+public:
+ int m_refCount;
+ HANDLE m_thread;
+ DWORD m_id;
+ IArchMultithread::ThreadFunc m_func;
+ void* m_userData;
+ HANDLE m_cancel;
+ bool m_cancelling;
+ HANDLE m_exit;
+ void* m_result;
+ void* m_networkData;
+};
+
+ArchThreadImpl::ArchThreadImpl() :
+ m_refCount(1),
+ m_thread(NULL),
+ m_id(0),
+ m_func(NULL),
+ m_userData(NULL),
+ m_cancelling(false),
+ m_result(NULL),
+ m_networkData(NULL)
+{
+ m_exit = CreateEvent(NULL, TRUE, FALSE, NULL);
+ m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL);
+}
+
+ArchThreadImpl::~ArchThreadImpl()
+{
+ CloseHandle(m_exit);
+ CloseHandle(m_cancel);
+}
+
+
+//
+// ArchMultithreadWindows
+//
+
+ArchMultithreadWindows* ArchMultithreadWindows::s_instance = NULL;
+
+ArchMultithreadWindows::ArchMultithreadWindows()
+{
+ assert(s_instance == NULL);
+ s_instance = this;
+
+ // no signal handlers
+ for (size_t i = 0; i < kNUM_SIGNALS; ++i) {
+ m_signalFunc[i] = NULL;
+ m_signalUserData[i] = NULL;
+ }
+
+ // create mutex for thread list
+ m_threadMutex = newMutex();
+
+ // create thread for calling (main) thread and add it to our
+ // list. no need to lock the mutex since we're the only thread.
+ m_mainThread = new ArchThreadImpl;
+ m_mainThread->m_thread = NULL;
+ m_mainThread->m_id = GetCurrentThreadId();
+ insert(m_mainThread);
+}
+
+ArchMultithreadWindows::~ArchMultithreadWindows()
+{
+ s_instance = NULL;
+
+ // clean up thread list
+ for (ThreadList::iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ delete *index;
+ }
+
+ // done with mutex
+ delete m_threadMutex;
+}
+
+void
+ArchMultithreadWindows::setNetworkDataForCurrentThread(void* data)
+{
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
+ thread->m_networkData = data;
+ unlockMutex(m_threadMutex);
+}
+
+void*
+ArchMultithreadWindows::getNetworkDataForThread(ArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* data = thread->m_networkData;
+ unlockMutex(m_threadMutex);
+ return data;
+}
+
+HANDLE
+ArchMultithreadWindows::getCancelEventForCurrentThread()
+{
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
+ unlockMutex(m_threadMutex);
+ return thread->m_cancel;
+}
+
+ArchMultithreadWindows*
+ArchMultithreadWindows::getInstance()
+{
+ return s_instance;
+}
+
+ArchCond
+ArchMultithreadWindows::newCondVar()
+{
+ ArchCondImpl* cond = new ArchCondImpl;
+ cond->m_events[ArchCondImpl::kSignal] = CreateEvent(NULL,
+ FALSE, FALSE, NULL);
+ cond->m_events[ArchCondImpl::kBroadcast] = CreateEvent(NULL,
+ TRUE, FALSE, NULL);
+ cond->m_waitCountMutex = newMutex();
+ cond->m_waitCount = 0;
+ return cond;
+}
+
+void
+ArchMultithreadWindows::closeCondVar(ArchCond cond)
+{
+ CloseHandle(cond->m_events[ArchCondImpl::kSignal]);
+ CloseHandle(cond->m_events[ArchCondImpl::kBroadcast]);
+ closeMutex(cond->m_waitCountMutex);
+ delete cond;
+}
+
+void
+ArchMultithreadWindows::signalCondVar(ArchCond cond)
+{
+ // is anybody waiting?
+ lockMutex(cond->m_waitCountMutex);
+ const bool hasWaiter = (cond->m_waitCount > 0);
+ unlockMutex(cond->m_waitCountMutex);
+
+ // wake one thread if anybody is waiting
+ if (hasWaiter) {
+ SetEvent(cond->m_events[ArchCondImpl::kSignal]);
+ }
+}
+
+void
+ArchMultithreadWindows::broadcastCondVar(ArchCond cond)
+{
+ // is anybody waiting?
+ lockMutex(cond->m_waitCountMutex);
+ const bool hasWaiter = (cond->m_waitCount > 0);
+ unlockMutex(cond->m_waitCountMutex);
+
+ // wake all threads if anybody is waiting
+ if (hasWaiter) {
+ SetEvent(cond->m_events[ArchCondImpl::kBroadcast]);
+ }
+}
+
+bool
+ArchMultithreadWindows::waitCondVar(ArchCond cond,
+ ArchMutex mutex, double timeout)
+{
+ // prepare to wait
+ const DWORD winTimeout = (timeout < 0.0) ? INFINITE :
+ static_cast<DWORD>(1000.0 * timeout);
+
+ // make a list of the condition variable events and the cancel event
+ // for the current thread.
+ HANDLE handles[4];
+ handles[0] = cond->m_events[ArchCondImpl::kSignal];
+ handles[1] = cond->m_events[ArchCondImpl::kBroadcast];
+ handles[2] = getCancelEventForCurrentThread();
+
+ // update waiter count
+ lockMutex(cond->m_waitCountMutex);
+ ++cond->m_waitCount;
+ unlockMutex(cond->m_waitCountMutex);
+
+ // release mutex. this should be atomic with the wait so that it's
+ // impossible for another thread to signal us between the unlock and
+ // the wait, which would lead to a lost signal on broadcasts.
+ // however, we're using a manual reset event for broadcasts which
+ // stays set until we reset it, so we don't lose the broadcast.
+ unlockMutex(mutex);
+
+ // wait for a signal or broadcast
+ // TODO: this doesn't always signal when kill signals are sent
+ DWORD result = WaitForMultipleObjects(3, handles, FALSE, winTimeout);
+
+ // cancel takes priority
+ if (result != WAIT_OBJECT_0 + 2 &&
+ WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) {
+ result = WAIT_OBJECT_0 + 2;
+ }
+
+ // update the waiter count and check if we're the last waiter
+ lockMutex(cond->m_waitCountMutex);
+ --cond->m_waitCount;
+ const bool last = (result == WAIT_OBJECT_0 + 1 && cond->m_waitCount == 0);
+ unlockMutex(cond->m_waitCountMutex);
+
+ // reset the broadcast event if we're the last waiter
+ if (last) {
+ ResetEvent(cond->m_events[ArchCondImpl::kBroadcast]);
+ }
+
+ // reacquire the mutex
+ lockMutex(mutex);
+
+ // cancel thread if necessary
+ if (result == WAIT_OBJECT_0 + 2) {
+ ARCH->testCancelThread();
+ }
+
+ // return success or failure
+ return (result == WAIT_OBJECT_0 + 0 ||
+ result == WAIT_OBJECT_0 + 1);
+}
+
+ArchMutex
+ArchMultithreadWindows::newMutex()
+{
+ ArchMutexImpl* mutex = new ArchMutexImpl;
+ InitializeCriticalSection(&mutex->m_mutex);
+ return mutex;
+}
+
+void
+ArchMultithreadWindows::closeMutex(ArchMutex mutex)
+{
+ DeleteCriticalSection(&mutex->m_mutex);
+ delete mutex;
+}
+
+void
+ArchMultithreadWindows::lockMutex(ArchMutex mutex)
+{
+ EnterCriticalSection(&mutex->m_mutex);
+}
+
+void
+ArchMultithreadWindows::unlockMutex(ArchMutex mutex)
+{
+ LeaveCriticalSection(&mutex->m_mutex);
+}
+
+ArchThread
+ArchMultithreadWindows::newThread(ThreadFunc func, void* data)
+{
+ lockMutex(m_threadMutex);
+
+ // create thread impl for new thread
+ ArchThreadImpl* thread = new ArchThreadImpl;
+ thread->m_func = func;
+ thread->m_userData = data;
+
+ // create thread
+ unsigned int id = 0;
+ thread->m_thread = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0,
+ threadFunc, (void*)thread, 0, &id));
+ thread->m_id = static_cast<DWORD>(id);
+
+ // check if thread was started
+ if (thread->m_thread == 0) {
+ // failed to start thread so clean up
+ delete thread;
+ thread = NULL;
+ }
+ else {
+ // add thread to list
+ insert(thread);
+
+ // increment ref count to account for the thread itself
+ refThread(thread);
+ }
+
+ // note that the child thread will wait until we release this mutex
+ unlockMutex(m_threadMutex);
+
+ return thread;
+}
+
+ArchThread
+ArchMultithreadWindows::newCurrentThread()
+{
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = find(GetCurrentThreadId());
+ unlockMutex(m_threadMutex);
+ assert(thread != NULL);
+ return thread;
+}
+
+void
+ArchMultithreadWindows::closeThread(ArchThread thread)
+{
+ assert(thread != NULL);
+
+ // decrement ref count and clean up thread if no more references
+ if (--thread->m_refCount == 0) {
+ // close the handle (main thread has a NULL handle)
+ if (thread->m_thread != NULL) {
+ CloseHandle(thread->m_thread);
+ }
+
+ // remove thread from list
+ lockMutex(m_threadMutex);
+ assert(findNoRefOrCreate(thread->m_id) == thread);
+ erase(thread);
+ unlockMutex(m_threadMutex);
+
+ // done with thread
+ delete thread;
+ }
+}
+
+ArchThread
+ArchMultithreadWindows::copyThread(ArchThread thread)
+{
+ refThread(thread);
+ return thread;
+}
+
+void
+ArchMultithreadWindows::cancelThread(ArchThread thread)
+{
+ assert(thread != NULL);
+
+ // set cancel flag
+ SetEvent(thread->m_cancel);
+}
+
+void
+ArchMultithreadWindows::setPriorityOfThread(ArchThread thread, int n)
+{
+ struct PriorityInfo {
+ public:
+ DWORD m_class;
+ int m_level;
+ };
+ static const PriorityInfo s_pClass[] = {
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_IDLE },
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
+ { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
+ { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
+ { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
+ { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
+ { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
+ { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
+ { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
+ { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
+ { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
+ { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_IDLE },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL}
+ };
+#if defined(_DEBUG)
+ // don't use really high priorities when debugging
+ static const size_t s_pMax = 13;
+#else
+ static const size_t s_pMax = sizeof(s_pClass) / sizeof(s_pClass[0]) - 1;
+#endif
+ static const size_t s_pBase = 8; // index of normal priority
+
+ assert(thread != NULL);
+
+ size_t index;
+ if (n > 0 && s_pBase < (size_t)n) {
+ // lowest priority
+ index = 0;
+ }
+ else {
+ index = (size_t)((int)s_pBase - n);
+ if (index > s_pMax) {
+ // highest priority
+ index = s_pMax;
+ }
+ }
+ SetPriorityClass(GetCurrentProcess(), s_pClass[index].m_class);
+ SetThreadPriority(thread->m_thread, s_pClass[index].m_level);
+}
+
+void
+ArchMultithreadWindows::testCancelThread()
+{
+ // find current thread
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
+ unlockMutex(m_threadMutex);
+
+ // test cancel on thread
+ testCancelThreadImpl(thread);
+}
+
+bool
+ArchMultithreadWindows::wait(ArchThread target, double timeout)
+{
+ assert(target != NULL);
+
+ lockMutex(m_threadMutex);
+
+ // find current thread
+ ArchThreadImpl* self = findNoRef(GetCurrentThreadId());
+
+ // ignore wait if trying to wait on ourself
+ if (target == self) {
+ unlockMutex(m_threadMutex);
+ return false;
+ }
+
+ // ref the target so it can't go away while we're watching it
+ refThread(target);
+
+ unlockMutex(m_threadMutex);
+
+ // convert timeout
+ DWORD t;
+ if (timeout < 0.0) {
+ t = INFINITE;
+ }
+ else {
+ t = (DWORD)(1000.0 * timeout);
+ }
+
+ // wait for this thread to be cancelled or woken up or for the
+ // target thread to terminate.
+ HANDLE handles[2];
+ handles[0] = target->m_exit;
+ handles[1] = self->m_cancel;
+ DWORD result = WaitForMultipleObjects(2, handles, FALSE, t);
+
+ // cancel takes priority
+ if (result != WAIT_OBJECT_0 + 1 &&
+ WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) {
+ result = WAIT_OBJECT_0 + 1;
+ }
+
+ // release target
+ closeThread(target);
+
+ // handle result
+ switch (result) {
+ case WAIT_OBJECT_0 + 0:
+ // target thread terminated
+ return true;
+
+ case WAIT_OBJECT_0 + 1:
+ // this thread was cancelled. does not return.
+ testCancelThreadImpl(self);
+
+ default:
+ // timeout or error
+ return false;
+ }
+}
+
+bool
+ArchMultithreadWindows::isSameThread(ArchThread thread1, ArchThread thread2)
+{
+ return (thread1 == thread2);
+}
+
+bool
+ArchMultithreadWindows::isExitedThread(ArchThread thread)
+{
+ // poll exit event
+ return (WaitForSingleObject(thread->m_exit, 0) == WAIT_OBJECT_0);
+}
+
+void*
+ArchMultithreadWindows::getResultOfThread(ArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* result = thread->m_result;
+ unlockMutex(m_threadMutex);
+ return result;
+}
+
+IArchMultithread::ThreadID
+ArchMultithreadWindows::getIDOfThread(ArchThread thread)
+{
+ return static_cast<ThreadID>(thread->m_id);
+}
+
+void
+ArchMultithreadWindows::setSignalHandler(
+ ESignal signal, SignalFunc func, void* userData)
+{
+ lockMutex(m_threadMutex);
+ m_signalFunc[signal] = func;
+ m_signalUserData[signal] = userData;
+ unlockMutex(m_threadMutex);
+}
+
+void
+ArchMultithreadWindows::raiseSignal(ESignal signal)
+{
+ lockMutex(m_threadMutex);
+ if (m_signalFunc[signal] != NULL) {
+ m_signalFunc[signal](signal, m_signalUserData[signal]);
+ ARCH->unblockPollSocket(m_mainThread);
+ }
+ else if (signal == kINTERRUPT || signal == kTERMINATE) {
+ ARCH->cancelThread(m_mainThread);
+ }
+ unlockMutex(m_threadMutex);
+}
+
+ArchThreadImpl*
+ArchMultithreadWindows::find(DWORD id)
+{
+ ArchThreadImpl* impl = findNoRef(id);
+ if (impl != NULL) {
+ refThread(impl);
+ }
+ return impl;
+}
+
+ArchThreadImpl*
+ArchMultithreadWindows::findNoRef(DWORD id)
+{
+ ArchThreadImpl* impl = findNoRefOrCreate(id);
+ if (impl == NULL) {
+ // create thread for calling thread which isn't in our list and
+ // add it to the list. this won't normally happen but it can if
+ // the system calls us under a new thread, like it does when we
+ // run as a service.
+ impl = new ArchThreadImpl;
+ impl->m_thread = NULL;
+ impl->m_id = GetCurrentThreadId();
+ insert(impl);
+ }
+ return impl;
+}
+
+ArchThreadImpl*
+ArchMultithreadWindows::findNoRefOrCreate(DWORD id)
+{
+ // linear search
+ for (ThreadList::const_iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if ((*index)->m_id == id) {
+ return *index;
+ }
+ }
+ return NULL;
+}
+
+void
+ArchMultithreadWindows::insert(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+
+ // thread shouldn't already be on the list
+ assert(findNoRefOrCreate(thread->m_id) == NULL);
+
+ // append to list
+ m_threadList.push_back(thread);
+}
+
+void
+ArchMultithreadWindows::erase(ArchThreadImpl* thread)
+{
+ for (ThreadList::iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if (*index == thread) {
+ m_threadList.erase(index);
+ break;
+ }
+ }
+}
+
+void
+ArchMultithreadWindows::refThread(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+ assert(findNoRefOrCreate(thread->m_id) != NULL);
+ ++thread->m_refCount;
+}
+
+void
+ArchMultithreadWindows::testCancelThreadImpl(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+
+ // poll cancel event. return if not set.
+ const DWORD result = WaitForSingleObject(thread->m_cancel, 0);
+ if (result != WAIT_OBJECT_0) {
+ return;
+ }
+
+ // update cancel state
+ lockMutex(m_threadMutex);
+ bool cancel = !thread->m_cancelling;
+ thread->m_cancelling = true;
+ ResetEvent(thread->m_cancel);
+ unlockMutex(m_threadMutex);
+
+ // unwind thread's stack if cancelling
+ if (cancel) {
+ throw XThreadCancel();
+ }
+}
+
+unsigned int __stdcall
+ArchMultithreadWindows::threadFunc(void* vrep)
+{
+ // get the thread
+ ArchThreadImpl* thread = static_cast<ArchThreadImpl*>(vrep);
+
+ // run thread
+ s_instance->doThreadFunc(thread);
+
+ // terminate the thread
+ return 0;
+}
+
+void
+ArchMultithreadWindows::doThreadFunc(ArchThread thread)
+{
+ // wait for parent to initialize this object
+ lockMutex(m_threadMutex);
+ unlockMutex(m_threadMutex);
+
+ void* result = NULL;
+ try {
+ // go
+ result = (*thread->m_func)(thread->m_userData);
+ }
+
+ catch (XThreadCancel&) {
+ // client called cancel()
+ }
+ catch (...) {
+ // note -- don't catch (...) to avoid masking bugs
+ SetEvent(thread->m_exit);
+ closeThread(thread);
+ throw;
+ }
+
+ // thread has exited
+ lockMutex(m_threadMutex);
+ thread->m_result = result;
+ unlockMutex(m_threadMutex);
+ SetEvent(thread->m_exit);
+
+ // done with thread
+ closeThread(thread);
+}
diff --git a/src/lib/arch/win32/ArchMultithreadWindows.h b/src/lib/arch/win32/ArchMultithreadWindows.h
new file mode 100644
index 0000000..99aa640
--- /dev/null
+++ b/src/lib/arch/win32/ArchMultithreadWindows.h
@@ -0,0 +1,116 @@
+/*
+ * 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 "arch/IArchMultithread.h"
+#include "common/stdlist.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#define ARCH_MULTITHREAD ArchMultithreadWindows
+
+class ArchCondImpl {
+public:
+ enum { kSignal = 0, kBroadcast };
+
+ HANDLE m_events[2];
+ mutable int m_waitCount;
+ ArchMutex m_waitCountMutex;
+};
+
+class ArchMutexImpl {
+public:
+ CRITICAL_SECTION m_mutex;
+};
+
+//! Win32 implementation of IArchMultithread
+class ArchMultithreadWindows : public IArchMultithread {
+public:
+ ArchMultithreadWindows();
+ virtual ~ArchMultithreadWindows();
+
+ //! @name manipulators
+ //@{
+
+ void setNetworkDataForCurrentThread(void*);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ HANDLE getCancelEventForCurrentThread();
+
+ void* getNetworkDataForThread(ArchThread);
+
+ static ArchMultithreadWindows* getInstance();
+
+ //@}
+
+ // IArchMultithread overrides
+ virtual ArchCond newCondVar();
+ virtual void closeCondVar(ArchCond);
+ virtual void signalCondVar(ArchCond);
+ virtual void broadcastCondVar(ArchCond);
+ virtual bool waitCondVar(ArchCond, ArchMutex, double timeout);
+ virtual ArchMutex newMutex();
+ virtual void closeMutex(ArchMutex);
+ virtual void lockMutex(ArchMutex);
+ virtual void unlockMutex(ArchMutex);
+ virtual ArchThread newThread(ThreadFunc, void*);
+ virtual ArchThread newCurrentThread();
+ virtual ArchThread copyThread(ArchThread);
+ virtual void closeThread(ArchThread);
+ virtual void cancelThread(ArchThread);
+ virtual void setPriorityOfThread(ArchThread, int n);
+ virtual void testCancelThread();
+ virtual bool wait(ArchThread, double timeout);
+ virtual bool isSameThread(ArchThread, ArchThread);
+ virtual bool isExitedThread(ArchThread);
+ virtual void* getResultOfThread(ArchThread);
+ virtual ThreadID getIDOfThread(ArchThread);
+ virtual void setSignalHandler(ESignal, SignalFunc, void*);
+ virtual void raiseSignal(ESignal);
+
+private:
+ ArchThreadImpl* find(DWORD id);
+ ArchThreadImpl* findNoRef(DWORD id);
+ ArchThreadImpl* findNoRefOrCreate(DWORD id);
+ void insert(ArchThreadImpl* thread);
+ void erase(ArchThreadImpl* thread);
+
+ void refThread(ArchThreadImpl* rep);
+ void testCancelThreadImpl(ArchThreadImpl* rep);
+
+ void doThreadFunc(ArchThread thread);
+ static unsigned int __stdcall threadFunc(void* vrep);
+
+private:
+ typedef std::list<ArchThread> ThreadList;
+
+ static ArchMultithreadWindows* s_instance;
+
+ ArchMutex m_threadMutex;
+
+ ThreadList m_threadList;
+ ArchThread m_mainThread;
+
+ SignalFunc m_signalFunc[kNUM_SIGNALS];
+ void* m_signalUserData[kNUM_SIGNALS];
+};
diff --git a/src/lib/arch/win32/ArchNetworkWinsock.cpp b/src/lib/arch/win32/ArchNetworkWinsock.cpp
new file mode 100644
index 0000000..722c4c5
--- /dev/null
+++ b/src/lib/arch/win32/ArchNetworkWinsock.cpp
@@ -0,0 +1,985 @@
+/*
+ * 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 "arch/win32/ArchNetworkWinsock.h"
+#include "arch/win32/ArchMultithreadWindows.h"
+#include "arch/win32/XArchWindows.h"
+#include "arch/IArchMultithread.h"
+#include "arch/Arch.h"
+
+#include <malloc.h>
+
+static const int s_family[] = {
+ PF_UNSPEC,
+ PF_INET,
+ PF_INET6,
+};
+static const int s_type[] = {
+ SOCK_DGRAM,
+ SOCK_STREAM
+};
+
+static SOCKET (PASCAL FAR *accept_winsock)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen);
+static int (PASCAL FAR *bind_winsock)(SOCKET s, const struct sockaddr FAR *addr, int namelen);
+static int (PASCAL FAR *close_winsock)(SOCKET s);
+static int (PASCAL FAR *connect_winsock)(SOCKET s, const struct sockaddr FAR *name, int namelen);
+static int (PASCAL FAR *gethostname_winsock)(char FAR * name, int namelen);
+static int (PASCAL FAR *getsockerror_winsock)(void);
+static int (PASCAL FAR *getsockopt_winsock)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen);
+static u_short (PASCAL FAR *htons_winsock)(u_short v);
+static char FAR * (PASCAL FAR *inet_ntoa_winsock)(struct in_addr in);
+static unsigned long (PASCAL FAR *inet_addr_winsock)(const char FAR * cp);
+static int (PASCAL FAR *ioctl_winsock)(SOCKET s, int cmd, void FAR * data);
+static int (PASCAL FAR *listen_winsock)(SOCKET s, int backlog);
+static u_short (PASCAL FAR *ntohs_winsock)(u_short v);
+static int (PASCAL FAR *recv_winsock)(SOCKET s, void FAR * buf, int len, int flags);
+static int (PASCAL FAR *select_winsock)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout);
+static int (PASCAL FAR *send_winsock)(SOCKET s, const void FAR * buf, int len, int flags);
+static int (PASCAL FAR *setsockopt_winsock)(SOCKET s, int level, int optname, const void FAR * optval, int optlen);
+static int (PASCAL FAR *shutdown_winsock)(SOCKET s, int how);
+static SOCKET (PASCAL FAR *socket_winsock)(int af, int type, int protocol);
+static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * addr, int len, int type);
+static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name);
+static int (PASCAL FAR *WSACleanup_winsock)(void);
+static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR * fdset);
+static WSAEVENT (PASCAL FAR *WSACreateEvent_winsock)(void);
+static BOOL (PASCAL FAR *WSACloseEvent_winsock)(WSAEVENT);
+static BOOL (PASCAL FAR *WSASetEvent_winsock)(WSAEVENT);
+static BOOL (PASCAL FAR *WSAResetEvent_winsock)(WSAEVENT);
+static int (PASCAL FAR *WSAEventSelect_winsock)(SOCKET, WSAEVENT, long);
+static DWORD (PASCAL FAR *WSAWaitForMultipleEvents_winsock)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL);
+static int (PASCAL FAR *WSAEnumNetworkEvents_winsock)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS);
+
+#undef FD_ISSET
+#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set))
+
+#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name)
+
+static HMODULE s_networkModule = NULL;
+
+static
+FARPROC
+netGetProcAddress(HMODULE module, LPCSTR name)
+{
+ FARPROC func = ::GetProcAddress(module, name);
+ if (!func) {
+ throw XArchNetworkSupport("");
+ }
+ return func;
+}
+
+ArchNetAddressImpl*
+ArchNetAddressImpl::alloc(size_t size)
+{
+ size_t totalSize = size + ADDR_HDR_SIZE;
+ ArchNetAddressImpl* addr = (ArchNetAddressImpl*)malloc(totalSize);
+ addr->m_len = (int)size;
+ return addr;
+}
+
+
+//
+// ArchNetworkWinsock
+//
+
+ArchNetworkWinsock::ArchNetworkWinsock() :
+ m_mutex(NULL)
+{
+}
+
+ArchNetworkWinsock::~ArchNetworkWinsock()
+{
+ if (s_networkModule != NULL) {
+ WSACleanup_winsock();
+ ::FreeLibrary(s_networkModule);
+
+ WSACleanup_winsock = NULL;
+ s_networkModule = NULL;
+ }
+ if (m_mutex != NULL) {
+ ARCH->closeMutex(m_mutex);
+ }
+
+ EventList::iterator it;
+ for (it = m_unblockEvents.begin(); it != m_unblockEvents.end(); it++) {
+ delete *it;
+ }
+}
+
+void
+ArchNetworkWinsock::init()
+{
+ static const char* s_library[] = { "ws2_32.dll" };
+
+ assert(WSACleanup_winsock == NULL);
+ assert(s_networkModule == NULL);
+
+ // try each winsock library
+ for (size_t i = 0; i < sizeof(s_library) / sizeof(s_library[0]); ++i) {
+ try {
+ initModule((HMODULE)::LoadLibrary(s_library[i]));
+ m_mutex = ARCH->newMutex();
+ return;
+ }
+ catch (XArchNetwork&) {
+ // ignore
+ }
+ }
+
+ // can't initialize any library
+ throw XArchNetworkSupport("Cannot load winsock library");
+}
+
+void
+ArchNetworkWinsock::initModule(HMODULE module)
+{
+ if (module == NULL) {
+ throw XArchNetworkSupport("");
+ }
+
+ // get startup function address
+ int (PASCAL FAR *startup)(WORD, LPWSADATA);
+ setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA));
+
+ // startup network library
+ WORD version = MAKEWORD(2 /*major*/, 2 /*minor*/);
+ WSADATA data;
+ int err = startup(version, &data);
+ if (data.wVersion != version) {
+ throw XArchNetworkSupport(new XArchEvalWinsock(err));
+ }
+ if (err != 0) {
+ // some other initialization error
+ throwError(err);
+ }
+
+ // get function addresses
+ setfunc(accept_winsock, accept, SOCKET (PASCAL FAR *)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen));
+ setfunc(bind_winsock, bind, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *addr, int namelen));
+ setfunc(close_winsock, closesocket, int (PASCAL FAR *)(SOCKET s));
+ setfunc(connect_winsock, connect, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *name, int namelen));
+ setfunc(gethostname_winsock, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen));
+ setfunc(getsockerror_winsock, WSAGetLastError, int (PASCAL FAR *)(void));
+ setfunc(getsockopt_winsock, getsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen));
+ setfunc(htons_winsock, htons, u_short (PASCAL FAR *)(u_short v));
+ setfunc(inet_ntoa_winsock, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in));
+ setfunc(inet_addr_winsock, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp));
+ setfunc(ioctl_winsock, ioctlsocket, int (PASCAL FAR *)(SOCKET s, int cmd, void FAR *));
+ setfunc(listen_winsock, listen, int (PASCAL FAR *)(SOCKET s, int backlog));
+ setfunc(ntohs_winsock, ntohs, u_short (PASCAL FAR *)(u_short v));
+ setfunc(recv_winsock, recv, int (PASCAL FAR *)(SOCKET s, void FAR * buf, int len, int flags));
+ setfunc(select_winsock, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout));
+ setfunc(send_winsock, send, int (PASCAL FAR *)(SOCKET s, const void FAR * buf, int len, int flags));
+ setfunc(setsockopt_winsock, setsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, const void FAR * optval, int optlen));
+ setfunc(shutdown_winsock, shutdown, int (PASCAL FAR *)(SOCKET s, int how));
+ setfunc(socket_winsock, socket, SOCKET (PASCAL FAR *)(int af, int type, int protocol));
+ setfunc(gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type));
+ setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name));
+ setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void));
+ setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(SOCKET, fd_set FAR *));
+ setfunc(WSACreateEvent_winsock, WSACreateEvent, WSAEVENT (PASCAL FAR *)(void));
+ setfunc(WSACloseEvent_winsock, WSACloseEvent, BOOL (PASCAL FAR *)(WSAEVENT));
+ setfunc(WSASetEvent_winsock, WSASetEvent, BOOL (PASCAL FAR *)(WSAEVENT));
+ setfunc(WSAResetEvent_winsock, WSAResetEvent, BOOL (PASCAL FAR *)(WSAEVENT));
+ setfunc(WSAEventSelect_winsock, WSAEventSelect, int (PASCAL FAR *)(SOCKET, WSAEVENT, long));
+ setfunc(WSAWaitForMultipleEvents_winsock, WSAWaitForMultipleEvents, DWORD (PASCAL FAR *)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL));
+ setfunc(WSAEnumNetworkEvents_winsock, WSAEnumNetworkEvents, int (PASCAL FAR *)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS));
+
+ s_networkModule = module;
+}
+
+ArchSocket
+ArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type)
+{
+ // create socket
+ SOCKET fd = socket_winsock(s_family[family], s_type[type], 0);
+ if (fd == INVALID_SOCKET) {
+ throwError(getsockerror_winsock());
+ }
+ try {
+ setBlockingOnSocket(fd, false);
+ BOOL flag = 0;
+ int size = sizeof(flag);
+ if (setsockopt_winsock(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, size) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+ }
+ catch (...) {
+ close_winsock(fd);
+ throw;
+ }
+
+ // allocate socket object
+ ArchSocketImpl* socket = new ArchSocketImpl;
+ socket->m_socket = fd;
+ socket->m_refCount = 1;
+ socket->m_event = WSACreateEvent_winsock();
+ socket->m_pollWrite = true;
+ return socket;
+}
+
+ArchSocket
+ArchNetworkWinsock::copySocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // ref the socket and return it
+ ARCH->lockMutex(m_mutex);
+ ++s->m_refCount;
+ ARCH->unlockMutex(m_mutex);
+ return s;
+}
+
+void
+ArchNetworkWinsock::closeSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // unref the socket and note if it should be released
+ ARCH->lockMutex(m_mutex);
+ const bool doClose = (--s->m_refCount == 0);
+ ARCH->unlockMutex(m_mutex);
+
+ // close the socket if necessary
+ if (doClose) {
+ if (close_winsock(s->m_socket) == SOCKET_ERROR) {
+ // close failed. restore the last ref and throw.
+ int err = getsockerror_winsock();
+ ARCH->lockMutex(m_mutex);
+ ++s->m_refCount;
+ ARCH->unlockMutex(m_mutex);
+ throwError(err);
+ }
+ WSACloseEvent_winsock(s->m_event);
+ delete s;
+ }
+}
+
+void
+ArchNetworkWinsock::closeSocketForRead(ArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown_winsock(s->m_socket, SD_RECEIVE) == SOCKET_ERROR) {
+ if (getsockerror_winsock() != WSAENOTCONN) {
+ throwError(getsockerror_winsock());
+ }
+ }
+}
+
+void
+ArchNetworkWinsock::closeSocketForWrite(ArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown_winsock(s->m_socket, SD_SEND) == SOCKET_ERROR) {
+ if (getsockerror_winsock() != WSAENOTCONN) {
+ throwError(getsockerror_winsock());
+ }
+ }
+}
+
+void
+ArchNetworkWinsock::bindSocket(ArchSocket s, ArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (bind_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+}
+
+void
+ArchNetworkWinsock::listenOnSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // hardcoding backlog
+ if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+}
+
+ArchSocket
+ArchNetworkWinsock::acceptSocket(ArchSocket s, ArchNetAddress* const addr)
+{
+ assert(s != NULL);
+
+ // create new socket and temporary address
+ ArchSocketImpl* socket = new ArchSocketImpl;
+ ArchNetAddress tmp = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in6));
+
+ // accept on socket
+ SOCKET fd = accept_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, tmp), &tmp->m_len);
+ if (fd == INVALID_SOCKET) {
+ int err = getsockerror_winsock();
+ delete socket;
+ free(tmp);
+ if (addr) {
+ *addr = NULL;
+ }
+ if (err == WSAEWOULDBLOCK) {
+ return NULL;
+ }
+ throwError(err);
+ }
+
+ try {
+ setBlockingOnSocket(fd, false);
+ }
+ catch (...) {
+ close_winsock(fd);
+ delete socket;
+ free(tmp);
+ if (addr) {
+ *addr = NULL;
+ }
+ throw;
+ }
+
+ // initialize socket
+ socket->m_socket = fd;
+ socket->m_refCount = 1;
+ socket->m_event = WSACreateEvent_winsock();
+ socket->m_pollWrite = true;
+
+ // copy address if requested
+ if (addr != NULL) {
+ *addr = ARCH->copyAddr(tmp);
+ }
+
+ free(tmp);
+ return socket;
+}
+
+bool
+ArchNetworkWinsock::connectSocket(ArchSocket s, ArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (connect_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, addr),
+ addr->m_len) == SOCKET_ERROR) {
+ if (getsockerror_winsock() == WSAEISCONN) {
+ return true;
+ }
+ if (getsockerror_winsock() == WSAEWOULDBLOCK) {
+ return false;
+ }
+ throwError(getsockerror_winsock());
+ }
+ return true;
+}
+
+int
+ArchNetworkWinsock::pollSocket(PollEntry pe[], int num, double timeout)
+{
+ int i;
+ DWORD n;
+
+ // prepare sockets and wait list
+ bool canWrite = false;
+ WSAEVENT* events = (WSAEVENT*)alloca((num + 1) * sizeof(WSAEVENT));
+ for (i = 0, n = 0; i < num; ++i) {
+ // reset return flags
+ pe[i].m_revents = 0;
+
+ // set invalid flag if socket is bogus then go to next socket
+ if (pe[i].m_socket == NULL) {
+ pe[i].m_revents |= kPOLLNVAL;
+ continue;
+ }
+
+ // select desired events
+ long socketEvents = 0;
+ if ((pe[i].m_events & kPOLLIN) != 0) {
+ socketEvents |= FD_READ | FD_ACCEPT | FD_CLOSE;
+ }
+ if ((pe[i].m_events & kPOLLOUT) != 0) {
+ socketEvents |= FD_WRITE | FD_CONNECT | FD_CLOSE;
+
+ // if m_pollWrite is false then we assume the socket is
+ // writable. winsock doesn't signal writability except
+ // when the state changes from unwritable.
+ if (!pe[i].m_socket->m_pollWrite) {
+ canWrite = true;
+ pe[i].m_revents |= kPOLLOUT;
+ }
+ }
+
+ // if no events then ignore socket
+ if (socketEvents == 0) {
+ continue;
+ }
+
+ // select socket for desired events
+ WSAEventSelect_winsock(pe[i].m_socket->m_socket,
+ pe[i].m_socket->m_event, socketEvents);
+
+ // add socket event to wait list
+ events[n++] = pe[i].m_socket->m_event;
+ }
+
+ // if no sockets then return immediately
+ if (n == 0) {
+ return 0;
+ }
+
+ // add the unblock event
+ ArchMultithreadWindows* mt = ArchMultithreadWindows::getInstance();
+ ArchThread thread = mt->newCurrentThread();
+ WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread);
+ ARCH->closeThread(thread);
+ if (unblockEvent == NULL) {
+ unblockEvent = new WSAEVENT;
+ m_unblockEvents.push_back(unblockEvent);
+ *unblockEvent = WSACreateEvent_winsock();
+ mt->setNetworkDataForCurrentThread(unblockEvent);
+ }
+ events[n++] = *unblockEvent;
+
+ // prepare timeout
+ DWORD t = (timeout < 0.0) ? INFINITE : (DWORD)(1000.0 * timeout);
+ if (canWrite) {
+ // if we know we can write then don't block
+ t = 0;
+ }
+
+ // wait
+ DWORD result = WSAWaitForMultipleEvents_winsock(n, events, FALSE, t, FALSE);
+
+ // reset the unblock event
+ WSAResetEvent_winsock(*unblockEvent);
+
+ // handle results
+ if (result == WSA_WAIT_FAILED) {
+ if (getsockerror_winsock() == WSAEINTR) {
+ // interrupted system call
+ ARCH->testCancelThread();
+ return 0;
+ }
+ throwError(getsockerror_winsock());
+ }
+ if (result == WSA_WAIT_TIMEOUT && !canWrite) {
+ return 0;
+ }
+ if (result == WSA_WAIT_EVENT_0 + n - 1) {
+ // the unblock event was signalled
+ return 0;
+ }
+ for (i = 0, n = 0; i < num; ++i) {
+ // skip events we didn't check
+ if (pe[i].m_socket == NULL ||
+ (pe[i].m_events & (kPOLLIN | kPOLLOUT)) == 0) {
+ continue;
+ }
+
+ // get events
+ WSANETWORKEVENTS info;
+ if (WSAEnumNetworkEvents_winsock(pe[i].m_socket->m_socket,
+ pe[i].m_socket->m_event, &info) == SOCKET_ERROR) {
+ continue;
+ }
+ if ((info.lNetworkEvents & FD_READ) != 0) {
+ pe[i].m_revents |= kPOLLIN;
+ }
+ if ((info.lNetworkEvents & FD_ACCEPT) != 0) {
+ pe[i].m_revents |= kPOLLIN;
+ }
+ if ((info.lNetworkEvents & FD_WRITE) != 0) {
+ pe[i].m_revents |= kPOLLOUT;
+
+ // socket is now writable so don't bothing polling for
+ // writable until it becomes unwritable.
+ pe[i].m_socket->m_pollWrite = false;
+ }
+ if ((info.lNetworkEvents & FD_CONNECT) != 0) {
+ if (info.iErrorCode[FD_CONNECT_BIT] != 0) {
+ pe[i].m_revents |= kPOLLERR;
+ }
+ else {
+ pe[i].m_revents |= kPOLLOUT;
+ pe[i].m_socket->m_pollWrite = false;
+ }
+ }
+ if ((info.lNetworkEvents & FD_CLOSE) != 0) {
+ if (info.iErrorCode[FD_CLOSE_BIT] != 0) {
+ pe[i].m_revents |= kPOLLERR;
+ }
+ else {
+ if ((pe[i].m_events & kPOLLIN) != 0) {
+ pe[i].m_revents |= kPOLLIN;
+ }
+ if ((pe[i].m_events & kPOLLOUT) != 0) {
+ pe[i].m_revents |= kPOLLOUT;
+ }
+ }
+ }
+ if (pe[i].m_revents != 0) {
+ ++n;
+ }
+ }
+
+ return (int)n;
+}
+
+void
+ArchNetworkWinsock::unblockPollSocket(ArchThread thread)
+{
+ // set the unblock event
+ ArchMultithreadWindows* mt = ArchMultithreadWindows::getInstance();
+ WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread);
+ if (unblockEvent != NULL) {
+ WSASetEvent_winsock(*unblockEvent);
+ }
+}
+
+size_t
+ArchNetworkWinsock::readSocket(ArchSocket s, void* buf, size_t len)
+{
+ assert(s != NULL);
+
+ int n = recv_winsock(s->m_socket, buf, (int)len, 0);
+ if (n == SOCKET_ERROR) {
+ int err = getsockerror_winsock();
+ if (err == WSAEINTR || err == WSAEWOULDBLOCK) {
+ return 0;
+ }
+ throwError(err);
+ }
+ return static_cast<size_t>(n);
+}
+
+size_t
+ArchNetworkWinsock::writeSocket(ArchSocket s, const void* buf, size_t len)
+{
+ assert(s != NULL);
+
+ int n = send_winsock(s->m_socket, buf, (int)len, 0);
+ if (n == SOCKET_ERROR) {
+ int err = getsockerror_winsock();
+ if (err == WSAEINTR) {
+ return 0;
+ }
+ if (err == WSAEWOULDBLOCK) {
+ s->m_pollWrite = true;
+ return 0;
+ }
+ throwError(err);
+ }
+ return static_cast<size_t>(n);
+}
+
+void
+ArchNetworkWinsock::throwErrorOnSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // get the error from the socket layer
+ int err = 0;
+ int size = sizeof(err);
+ if (getsockopt_winsock(s->m_socket, SOL_SOCKET,
+ SO_ERROR, &err, &size) == SOCKET_ERROR) {
+ err = getsockerror_winsock();
+ }
+
+ // throw if there's an error
+ if (err != 0) {
+ throwError(err);
+ }
+}
+
+void
+ArchNetworkWinsock::setBlockingOnSocket(SOCKET s, bool blocking)
+{
+ assert(s != 0);
+
+ int flag = blocking ? 0 : 1;
+ if (ioctl_winsock(s, FIONBIO, &flag) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+}
+
+bool
+ArchNetworkWinsock::setNoDelayOnSocket(ArchSocket s, bool noDelay)
+{
+ assert(s != NULL);
+
+ // get old state
+ BOOL oflag;
+ int size = sizeof(oflag);
+ if (getsockopt_winsock(s->m_socket, IPPROTO_TCP,
+ TCP_NODELAY, &oflag, &size) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+
+ // set new state
+ BOOL flag = noDelay ? 1 : 0;
+ size = sizeof(flag);
+ if (setsockopt_winsock(s->m_socket, IPPROTO_TCP,
+ TCP_NODELAY, &flag, size) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+
+ return (oflag != 0);
+}
+
+bool
+ArchNetworkWinsock::setReuseAddrOnSocket(ArchSocket s, bool reuse)
+{
+ assert(s != NULL);
+
+ // get old state
+ BOOL oflag;
+ int size = sizeof(oflag);
+ if (getsockopt_winsock(s->m_socket, SOL_SOCKET,
+ SO_REUSEADDR, &oflag, &size) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+
+ // set new state
+ BOOL flag = reuse ? 1 : 0;
+ size = sizeof(flag);
+ if (setsockopt_winsock(s->m_socket, SOL_SOCKET,
+ SO_REUSEADDR, &flag, size) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+
+ return (oflag != 0);
+}
+
+std::string
+ArchNetworkWinsock::getHostName()
+{
+ char name[256];
+ if (gethostname_winsock(name, sizeof(name)) == -1) {
+ name[0] = '\0';
+ }
+ else {
+ name[sizeof(name) - 1] = '\0';
+ }
+ return name;
+}
+
+ArchNetAddress
+ArchNetworkWinsock::newAnyAddr(EAddressFamily family)
+{
+ ArchNetAddressImpl* addr = NULL;
+ switch (family) {
+ case kINET: {
+ addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ ipAddr->sin_family = AF_INET;
+ ipAddr->sin_port = 0;
+ ipAddr->sin_addr.s_addr = INADDR_ANY;
+ break;
+ }
+
+ case kINET6: {
+ addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in6));
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ ipAddr->sin6_family = AF_INET6;
+ ipAddr->sin6_port = 0;
+ memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any));
+ break;
+ }
+
+ default:
+ assert(0 && "invalid family");
+ }
+ return addr;
+}
+
+ArchNetAddress
+ArchNetworkWinsock::copyAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ ArchNetAddressImpl* copy = ArchNetAddressImpl::alloc(addr->m_len);
+ memcpy(TYPED_ADDR(void, copy), TYPED_ADDR(void, addr), addr->m_len);
+ return copy;
+}
+
+ArchNetAddress
+ArchNetworkWinsock::nameToAddr(const std::string& name)
+{
+ // allocate address
+
+ ArchNetAddressImpl* addr = new ArchNetAddressImpl;
+
+ struct addrinfo hints;
+ struct addrinfo *p;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ int ret = -1;
+
+ ARCH->lockMutex(m_mutex);
+ if ((ret = getaddrinfo(name.c_str(), NULL, &hints, &p)) != 0) {
+ ARCH->unlockMutex(m_mutex);
+ delete addr;
+ throwNameError(ret);
+ }
+
+ if (p->ai_family == AF_INET) {
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
+ } else {
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in6);
+ }
+
+ memcpy(&addr->m_addr, p->ai_addr, addr->m_len);
+ freeaddrinfo(p);
+ ARCH->unlockMutex(m_mutex);
+ return addr;
+}
+
+void
+ArchNetworkWinsock::closeAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ free(addr);
+}
+
+std::string
+ArchNetworkWinsock::addrToName(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ char host[1024];
+ char service[20];
+ int ret = getnameinfo(TYPED_ADDR(struct sockaddr, addr), addr->m_len, host, sizeof(host), service, sizeof(service), 0);
+
+ if (ret != NULL) {
+ throwNameError(ret);
+ }
+
+ // return (primary) name
+ std::string name = host;
+ return name;
+}
+
+std::string
+ArchNetworkWinsock::addrToString(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ return inet_ntoa_winsock(ipAddr->sin_addr);
+ }
+
+ case kINET6: {
+ char strAddr[INET6_ADDRSTRLEN];
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ inet_ntop(AF_INET6, &ipAddr->sin6_addr, strAddr, INET6_ADDRSTRLEN);
+ return strAddr;
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return "";
+ }
+}
+
+IArchNetwork::EAddressFamily
+ArchNetworkWinsock::getAddrFamily(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (addr->m_addr.ss_family) {
+ case AF_INET:
+ return kINET;
+
+ case AF_INET6:
+ return kINET6;
+
+ default:
+ return kUNKNOWN;
+ }
+}
+
+void
+ArchNetworkWinsock::setAddrPort(ArchNetAddress addr, int port)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ ipAddr->sin_port = htons_winsock(static_cast<u_short>(port));
+ break;
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ ipAddr->sin6_port = htons_winsock(static_cast<u_short>(port));
+ break;
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ break;
+ }
+}
+
+int
+ArchNetworkWinsock::getAddrPort(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ return ntohs_winsock(ipAddr->sin_port);
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ return ntohs_winsock(ipAddr->sin6_port);
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return 0;
+ }
+}
+
+bool
+ArchNetworkWinsock::isAnyAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ return (addr->m_len == sizeof(struct sockaddr_in) &&
+ ipAddr->sin_addr.s_addr == INADDR_ANY);
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ return (addr->m_len == sizeof(struct sockaddr_in) &&
+ memcmp(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any))== 0);
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return true;
+ }
+}
+
+bool
+ArchNetworkWinsock::isEqualAddr(ArchNetAddress a, ArchNetAddress b)
+{
+ return (a == b || (a->m_len == b->m_len &&
+ memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0));
+}
+
+void
+ArchNetworkWinsock::throwError(int err)
+{
+ switch (err) {
+ case WSAEACCES:
+ throw XArchNetworkAccess(new XArchEvalWinsock(err));
+
+ case WSAEMFILE:
+ case WSAENOBUFS:
+ case WSAENETDOWN:
+ throw XArchNetworkResource(new XArchEvalWinsock(err));
+
+ case WSAEPROTOTYPE:
+ case WSAEPROTONOSUPPORT:
+ case WSAEAFNOSUPPORT:
+ case WSAEPFNOSUPPORT:
+ case WSAESOCKTNOSUPPORT:
+ case WSAEINVAL:
+ case WSAENOPROTOOPT:
+ case WSAEOPNOTSUPP:
+ case WSAESHUTDOWN:
+ case WSANOTINITIALISED:
+ case WSAVERNOTSUPPORTED:
+ case WSASYSNOTREADY:
+ throw XArchNetworkSupport(new XArchEvalWinsock(err));
+
+ case WSAEADDRNOTAVAIL:
+ throw XArchNetworkNoAddress(new XArchEvalWinsock(err));
+
+ case WSAEADDRINUSE:
+ throw XArchNetworkAddressInUse(new XArchEvalWinsock(err));
+
+ case WSAEHOSTUNREACH:
+ case WSAENETUNREACH:
+ throw XArchNetworkNoRoute(new XArchEvalWinsock(err));
+
+ case WSAENOTCONN:
+ throw XArchNetworkNotConnected(new XArchEvalWinsock(err));
+
+ case WSAEDISCON:
+ throw XArchNetworkShutdown(new XArchEvalWinsock(err));
+
+ case WSAENETRESET:
+ case WSAECONNABORTED:
+ case WSAECONNRESET:
+ throw XArchNetworkDisconnected(new XArchEvalWinsock(err));
+
+ case WSAECONNREFUSED:
+ throw XArchNetworkConnectionRefused(new XArchEvalWinsock(err));
+
+ case WSAEHOSTDOWN:
+ case WSAETIMEDOUT:
+ throw XArchNetworkTimedOut(new XArchEvalWinsock(err));
+
+ case WSAHOST_NOT_FOUND:
+ throw XArchNetworkNameUnknown(new XArchEvalWinsock(err));
+
+ case WSANO_DATA:
+ throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err));
+
+ case WSANO_RECOVERY:
+ throw XArchNetworkNameFailure(new XArchEvalWinsock(err));
+
+ case WSATRY_AGAIN:
+ throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err));
+
+ default:
+ throw XArchNetwork(new XArchEvalWinsock(err));
+ }
+}
+
+void
+ArchNetworkWinsock::throwNameError(int err)
+{
+ switch (err) {
+ case WSAHOST_NOT_FOUND:
+ throw XArchNetworkNameUnknown(new XArchEvalWinsock(err));
+
+ case WSANO_DATA:
+ throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err));
+
+ case WSANO_RECOVERY:
+ throw XArchNetworkNameFailure(new XArchEvalWinsock(err));
+
+ case WSATRY_AGAIN:
+ throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err));
+
+ default:
+ throw XArchNetworkName(new XArchEvalWinsock(err));
+ }
+}
diff --git a/src/lib/arch/win32/ArchNetworkWinsock.h b/src/lib/arch/win32/ArchNetworkWinsock.h
new file mode 100644
index 0000000..0b01671
--- /dev/null
+++ b/src/lib/arch/win32/ArchNetworkWinsock.h
@@ -0,0 +1,111 @@
+/*
+ * 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 <ws2tcpip.h>
+// declare no functions in winsock2
+#ifndef INCL_WINSOCK_API_PROTOTYPES
+#define INCL_WINSOCK_API_PROTOTYPES 0
+#endif
+#define INCL_WINSOCK_API_TYPEDEFS 0
+
+#include "arch/IArchNetwork.h"
+#include "arch/IArchMultithread.h"
+
+#include <WinSock2.h>
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <list>
+
+#pragma comment(lib, "ws2_32.lib")
+
+#define ARCH_NETWORK ArchNetworkWinsock
+
+class ArchSocketImpl {
+public:
+ SOCKET m_socket;
+ int m_refCount;
+ WSAEVENT m_event;
+ bool m_pollWrite;
+};
+
+class ArchNetAddressImpl {
+public:
+ static ArchNetAddressImpl* alloc(size_t);
+
+public:
+ int m_len;
+ struct sockaddr_storage m_addr;
+};
+#define ADDR_HDR_SIZE offsetof(ArchNetAddressImpl, m_addr)
+#define TYPED_ADDR(type_, addr_) (reinterpret_cast<type_*>(&addr_->m_addr))
+
+//! Win32 implementation of IArchNetwork
+class ArchNetworkWinsock : public IArchNetwork {
+public:
+ ArchNetworkWinsock();
+ virtual ~ArchNetworkWinsock();
+
+ virtual void init();
+
+ // IArchNetwork overrides
+ virtual ArchSocket newSocket(EAddressFamily, ESocketType);
+ virtual ArchSocket copySocket(ArchSocket s);
+ virtual void closeSocket(ArchSocket s);
+ virtual void closeSocketForRead(ArchSocket s);
+ virtual void closeSocketForWrite(ArchSocket s);
+ virtual void bindSocket(ArchSocket s, ArchNetAddress addr);
+ virtual void listenOnSocket(ArchSocket s);
+ virtual ArchSocket acceptSocket(ArchSocket s, ArchNetAddress* addr);
+ virtual bool connectSocket(ArchSocket s, ArchNetAddress name);
+ virtual int pollSocket(PollEntry[], int num, double timeout);
+ virtual void unblockPollSocket(ArchThread thread);
+ virtual size_t readSocket(ArchSocket s, void* buf, size_t len);
+ virtual size_t writeSocket(ArchSocket s,
+ const void* buf, size_t len);
+ virtual void throwErrorOnSocket(ArchSocket);
+ virtual bool setNoDelayOnSocket(ArchSocket, bool noDelay);
+ virtual bool setReuseAddrOnSocket(ArchSocket, bool reuse);
+ virtual std::string getHostName();
+ virtual ArchNetAddress newAnyAddr(EAddressFamily);
+ virtual ArchNetAddress copyAddr(ArchNetAddress);
+ virtual ArchNetAddress nameToAddr(const std::string&);
+ virtual void closeAddr(ArchNetAddress);
+ virtual std::string addrToName(ArchNetAddress);
+ virtual std::string addrToString(ArchNetAddress);
+ virtual EAddressFamily getAddrFamily(ArchNetAddress);
+ virtual void setAddrPort(ArchNetAddress, int port);
+ virtual int getAddrPort(ArchNetAddress);
+ virtual bool isAnyAddr(ArchNetAddress);
+ virtual bool isEqualAddr(ArchNetAddress, ArchNetAddress);
+
+private:
+ void initModule(HMODULE);
+
+ void setBlockingOnSocket(SOCKET, bool blocking);
+
+ void throwError(int);
+ void throwNameError(int);
+
+private:
+ typedef std::list<WSAEVENT> EventList;
+
+ ArchMutex m_mutex;
+ EventList m_unblockEvents;
+};
diff --git a/src/lib/arch/win32/ArchSleepWindows.cpp b/src/lib/arch/win32/ArchSleepWindows.cpp
new file mode 100644
index 0000000..69648a7
--- /dev/null
+++ b/src/lib/arch/win32/ArchSleepWindows.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 "arch/win32/ArchSleepWindows.h"
+#include "arch/Arch.h"
+#include "arch/win32/ArchMultithreadWindows.h"
+
+//
+// ArchSleepWindows
+//
+
+ArchSleepWindows::ArchSleepWindows()
+{
+ // do nothing
+}
+
+ArchSleepWindows::~ArchSleepWindows()
+{
+ // do nothing
+}
+
+void
+ArchSleepWindows::sleep(double timeout)
+{
+ ARCH->testCancelThread();
+ if (timeout < 0.0) {
+ return;
+ }
+
+ // get the cancel event from the current thread. this only
+ // works if we're using the windows multithread object but
+ // this is windows so that's pretty certain; we'll get a
+ // link error if we're not, though.
+ ArchMultithreadWindows* mt = ArchMultithreadWindows::getInstance();
+ if (mt != NULL) {
+ HANDLE cancelEvent = mt->getCancelEventForCurrentThread();
+ WaitForSingleObject(cancelEvent, (DWORD)(1000.0 * timeout));
+ if (timeout == 0.0) {
+ Sleep(0);
+ }
+ }
+ else {
+ Sleep((DWORD)(1000.0 * timeout));
+ }
+ ARCH->testCancelThread();
+}
diff --git a/src/lib/arch/win32/ArchSleepWindows.h b/src/lib/arch/win32/ArchSleepWindows.h
new file mode 100644
index 0000000..d673caf
--- /dev/null
+++ b/src/lib/arch/win32/ArchSleepWindows.h
@@ -0,0 +1,33 @@
+/*
+ * 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 "arch/IArchSleep.h"
+
+#define ARCH_SLEEP ArchSleepWindows
+
+//! Win32 implementation of IArchSleep
+class ArchSleepWindows : public IArchSleep {
+public:
+ ArchSleepWindows();
+ virtual ~ArchSleepWindows();
+
+ // IArchSleep overrides
+ virtual void sleep(double timeout);
+};
diff --git a/src/lib/arch/win32/ArchStringWindows.cpp b/src/lib/arch/win32/ArchStringWindows.cpp
new file mode 100644
index 0000000..deaf536
--- /dev/null
+++ b/src/lib/arch/win32/ArchStringWindows.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 "arch/win32/ArchStringWindows.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <stdio.h>
+
+//
+// ArchStringWindows
+//
+
+#include "arch/multibyte.h"
+#define HAVE_VSNPRINTF 1
+#define ARCH_VSNPRINTF _vsnprintf
+#include "arch/vsnprintf.h"
+
+ArchStringWindows::ArchStringWindows()
+{
+}
+
+ArchStringWindows::~ArchStringWindows()
+{
+}
+
+IArchString::EWideCharEncoding
+ArchStringWindows::getWideCharEncoding()
+{
+ return kUTF16;
+}
diff --git a/src/lib/arch/win32/ArchStringWindows.h b/src/lib/arch/win32/ArchStringWindows.h
new file mode 100644
index 0000000..23812dc
--- /dev/null
+++ b/src/lib/arch/win32/ArchStringWindows.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 "arch/IArchString.h"
+
+#define ARCH_STRING ArchStringWindows
+
+//! Win32 implementation of IArchString
+class ArchStringWindows : public IArchString {
+public:
+ ArchStringWindows();
+ virtual ~ArchStringWindows();
+
+ // IArchString overrides
+ virtual EWideCharEncoding
+ getWideCharEncoding();
+};
diff --git a/src/lib/arch/win32/ArchSystemWindows.cpp b/src/lib/arch/win32/ArchSystemWindows.cpp
new file mode 100644
index 0000000..cf3b066
--- /dev/null
+++ b/src/lib/arch/win32/ArchSystemWindows.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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 "arch/win32/ArchSystemWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/win32/XArchWindows.h"
+
+#include "tchar.h"
+#include <string>
+
+#include <windows.h>
+#include <psapi.h>
+
+static const char* s_settingsKeyNames[] = {
+ _T("SOFTWARE"),
+ _T("Barrier"),
+ NULL
+};
+
+//
+// ArchSystemWindows
+//
+
+ArchSystemWindows::ArchSystemWindows()
+{
+ // do nothing
+}
+
+ArchSystemWindows::~ArchSystemWindows()
+{
+ // do nothing
+}
+
+std::string
+ArchSystemWindows::getOSName() const
+{
+ std::string osName ("Microsoft Windows <unknown>");
+ static const TCHAR* const windowsVersionKeyNames[] = {
+ _T("SOFTWARE"),
+ _T("Microsoft"),
+ _T("Windows NT"),
+ _T("CurrentVersion"),
+ NULL
+ };
+
+ HKEY key = ArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, windowsVersionKeyNames);
+ if (key == NULL) {
+ return osName;
+ }
+
+ std::string productName = ArchMiscWindows::readValueString(key, "ProductName");
+ if (osName.empty()) {
+ return osName;
+ }
+
+ return "Microsoft " + productName;
+}
+
+std::string
+ArchSystemWindows::getPlatformName() const
+{
+#ifdef _X86_
+ if (isWOW64())
+ return "x86 (WOW64)";
+ else
+ return "x86";
+#else
+#ifdef _AMD64_
+ return "x64";
+#else
+ return "Unknown";
+#endif
+#endif
+}
+
+std::string
+ArchSystemWindows::setting(const std::string& valueName) const
+{
+ HKEY key = ArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_settingsKeyNames);
+ if (key == NULL)
+ return "";
+
+ return ArchMiscWindows::readValueString(key, valueName.c_str());
+}
+
+void
+ArchSystemWindows::setting(const std::string& valueName, const std::string& valueString) const
+{
+ HKEY key = ArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_settingsKeyNames);
+ if (key == NULL)
+ throw XArch(std::string("could not access registry key: ") + valueName);
+ ArchMiscWindows::setValue(key, valueName.c_str(), valueString.c_str());
+}
+
+bool
+ArchSystemWindows::isWOW64() const
+{
+#if WINVER >= _WIN32_WINNT_WINXP
+ typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
+ HMODULE hModule = GetModuleHandle(TEXT("kernel32"));
+ if (!hModule) return FALSE;
+
+ LPFN_ISWOW64PROCESS fnIsWow64Process =
+ (LPFN_ISWOW64PROCESS) GetProcAddress(hModule, "IsWow64Process");
+
+ BOOL bIsWow64 = FALSE;
+ if (NULL != fnIsWow64Process &&
+ fnIsWow64Process(GetCurrentProcess(), &bIsWow64) &&
+ bIsWow64)
+ {
+ return true;
+ }
+#endif
+ return false;
+}
+#pragma comment(lib, "psapi")
+
+std::string
+ArchSystemWindows::getLibsUsed(void) const
+{
+ HMODULE hMods[1024];
+ HANDLE hProcess;
+ DWORD cbNeeded;
+ unsigned int i;
+ char hex[16];
+
+ DWORD pid = GetCurrentProcessId();
+
+ std::string msg = "pid:" + std::to_string((unsigned long long)pid) + "\n";
+
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
+
+ if (NULL == hProcess) {
+ return msg;
+ }
+
+ if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
+ for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
+ TCHAR szModName[MAX_PATH];
+ if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR))) {
+ sprintf(hex, "(0x%08llX)", reinterpret_cast<long long>(hMods[i]));
+ msg += szModName;
+ msg.append(hex);
+ msg.append("\n");
+ }
+ }
+ }
+
+ CloseHandle(hProcess);
+ return msg;
+}
diff --git a/src/lib/arch/win32/ArchSystemWindows.h b/src/lib/arch/win32/ArchSystemWindows.h
new file mode 100644
index 0000000..3d45ee6
--- /dev/null
+++ b/src/lib/arch/win32/ArchSystemWindows.h
@@ -0,0 +1,39 @@
+/*
+ * 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 "arch/IArchSystem.h"
+
+#define ARCH_SYSTEM ArchSystemWindows
+
+//! Win32 implementation of IArchString
+class ArchSystemWindows : public IArchSystem {
+public:
+ ArchSystemWindows();
+ virtual ~ArchSystemWindows();
+
+ // IArchSystem overrides
+ virtual std::string getOSName() const;
+ virtual std::string getPlatformName() const;
+ virtual std::string setting(const std::string& valueName) const;
+ virtual void setting(const std::string& valueName, const std::string& valueString) const;
+ virtual std::string getLibsUsed(void) const;
+
+ bool isWOW64() const;
+};
diff --git a/src/lib/arch/win32/ArchTaskBarWindows.cpp b/src/lib/arch/win32/ArchTaskBarWindows.cpp
new file mode 100644
index 0000000..731dc59
--- /dev/null
+++ b/src/lib/arch/win32/ArchTaskBarWindows.cpp
@@ -0,0 +1,514 @@
+/*
+ * 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 "arch/win32/ArchTaskBarWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+#include "barrier/win32/AppUtilWindows.h"
+
+#include <string.h>
+#include <shellapi.h>
+
+static const UINT kAddReceiver = WM_USER + 10;
+static const UINT kRemoveReceiver = WM_USER + 11;
+static const UINT kUpdateReceiver = WM_USER + 12;
+static const UINT kNotifyReceiver = WM_USER + 13;
+static const UINT kFirstReceiverID = WM_USER + 14;
+
+//
+// ArchTaskBarWindows
+//
+
+ArchTaskBarWindows* ArchTaskBarWindows::s_instance = NULL;
+
+ArchTaskBarWindows::ArchTaskBarWindows() :
+ m_mutex(NULL),
+ m_condVar(NULL),
+ m_ready(false),
+ m_result(0),
+ m_thread(NULL),
+ m_hwnd(NULL),
+ m_taskBarRestart(0),
+ m_nextID(kFirstReceiverID)
+{
+ // save the singleton instance
+ s_instance = this;
+}
+
+ArchTaskBarWindows::~ArchTaskBarWindows()
+{
+ if (m_thread != NULL) {
+ PostMessage(m_hwnd, WM_QUIT, 0, 0);
+ ARCH->wait(m_thread, -1.0);
+ ARCH->closeThread(m_thread);
+ }
+ if (m_condVar != NULL) {
+ ARCH->closeCondVar(m_condVar);
+ }
+ if (m_mutex != NULL) {
+ ARCH->closeMutex(m_mutex);
+ }
+ s_instance = NULL;
+}
+
+void
+ArchTaskBarWindows::init()
+{
+ // we need a mutex
+ m_mutex = ARCH->newMutex();
+
+ // and a condition variable which uses the above mutex
+ m_ready = false;
+ m_condVar = ARCH->newCondVar();
+
+ // we're going to want to get a result from the thread we're
+ // about to create to know if it initialized successfully.
+ // so we lock the condition variable.
+ ARCH->lockMutex(m_mutex);
+
+ // open a window and run an event loop in a separate thread.
+ // this has to happen in a separate thread because if we
+ // create a window on the current desktop with the current
+ // thread then the current thread won't be able to switch
+ // desktops if it needs to.
+ m_thread = ARCH->newThread(&ArchTaskBarWindows::threadEntry, this);
+
+ // wait for child thread
+ while (!m_ready) {
+ ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
+ }
+
+ // ready
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::addDialog(HWND hwnd)
+{
+ ArchMiscWindows::addDialog(hwnd);
+}
+
+void
+ArchTaskBarWindows::removeDialog(HWND hwnd)
+{
+ ArchMiscWindows::removeDialog(hwnd);
+}
+
+void
+ArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver)
+{
+ // ignore bogus receiver
+ if (receiver == NULL) {
+ return;
+ }
+
+ // add receiver if necessary
+ ReceiverToInfoMap::iterator index = m_receivers.find(receiver);
+ if (index == m_receivers.end()) {
+ // add it, creating a new message ID for it
+ ReceiverInfo info;
+ info.m_id = getNextID();
+ index = m_receivers.insert(std::make_pair(receiver, info)).first;
+
+ // add ID to receiver mapping
+ m_idTable.insert(std::make_pair(info.m_id, index));
+ }
+
+ // add receiver
+ PostMessage(m_hwnd, kAddReceiver, index->second.m_id, 0);
+}
+
+void
+ArchTaskBarWindows::removeReceiver(IArchTaskBarReceiver* receiver)
+{
+ // find receiver
+ ReceiverToInfoMap::iterator index = m_receivers.find(receiver);
+ if (index == m_receivers.end()) {
+ return;
+ }
+
+ // remove icon. wait for this to finish before returning.
+ SendMessage(m_hwnd, kRemoveReceiver, index->second.m_id, 0);
+
+ // recycle the ID
+ recycleID(index->second.m_id);
+
+ // discard
+ m_idTable.erase(index->second.m_id);
+ m_receivers.erase(index);
+}
+
+void
+ArchTaskBarWindows::updateReceiver(IArchTaskBarReceiver* receiver)
+{
+ // find receiver
+ ReceiverToInfoMap::const_iterator index = m_receivers.find(receiver);
+ if (index == m_receivers.end()) {
+ return;
+ }
+
+ // update icon and tool tip
+ PostMessage(m_hwnd, kUpdateReceiver, index->second.m_id, 0);
+}
+
+UINT
+ArchTaskBarWindows::getNextID()
+{
+ if (m_oldIDs.empty()) {
+ return m_nextID++;
+ }
+ UINT id = m_oldIDs.back();
+ m_oldIDs.pop_back();
+ return id;
+}
+
+void
+ArchTaskBarWindows::recycleID(UINT id)
+{
+ m_oldIDs.push_back(id);
+}
+
+void
+ArchTaskBarWindows::addIcon(UINT id)
+{
+ ARCH->lockMutex(m_mutex);
+ CIDToReceiverMap::const_iterator index = m_idTable.find(id);
+ if (index != m_idTable.end()) {
+ modifyIconNoLock(index->second, NIM_ADD);
+ }
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::removeIcon(UINT id)
+{
+ ARCH->lockMutex(m_mutex);
+ removeIconNoLock(id);
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::updateIcon(UINT id)
+{
+ ARCH->lockMutex(m_mutex);
+ CIDToReceiverMap::const_iterator index = m_idTable.find(id);
+ if (index != m_idTable.end()) {
+ modifyIconNoLock(index->second, NIM_MODIFY);
+ }
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::addAllIcons()
+{
+ ARCH->lockMutex(m_mutex);
+ for (ReceiverToInfoMap::const_iterator index = m_receivers.begin();
+ index != m_receivers.end(); ++index) {
+ modifyIconNoLock(index, NIM_ADD);
+ }
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::removeAllIcons()
+{
+ ARCH->lockMutex(m_mutex);
+ for (ReceiverToInfoMap::const_iterator index = m_receivers.begin();
+ index != m_receivers.end(); ++index) {
+ removeIconNoLock(index->second.m_id);
+ }
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::modifyIconNoLock(
+ ReceiverToInfoMap::const_iterator index, DWORD taskBarMessage)
+{
+ // get receiver
+ UINT id = index->second.m_id;
+ IArchTaskBarReceiver* receiver = index->first;
+
+ // lock receiver so icon and tool tip are guaranteed to be consistent
+ receiver->lock();
+
+ // get icon data
+ HICON icon = static_cast<HICON>(
+ const_cast<IArchTaskBarReceiver::Icon>(receiver->getIcon()));
+
+ // get tool tip
+ std::string toolTip = receiver->getToolTip();
+
+ // done querying
+ receiver->unlock();
+
+ // prepare to add icon
+ NOTIFYICONDATA data;
+ data.cbSize = sizeof(NOTIFYICONDATA);
+ data.hWnd = m_hwnd;
+ data.uID = id;
+ data.uFlags = NIF_MESSAGE;
+ data.uCallbackMessage = kNotifyReceiver;
+ data.hIcon = icon;
+ if (icon != NULL) {
+ data.uFlags |= NIF_ICON;
+ }
+ if (!toolTip.empty()) {
+ strncpy(data.szTip, toolTip.c_str(), sizeof(data.szTip));
+ data.szTip[sizeof(data.szTip) - 1] = '\0';
+ data.uFlags |= NIF_TIP;
+ }
+ else {
+ data.szTip[0] = '\0';
+ }
+
+ // add icon
+ if (Shell_NotifyIcon(taskBarMessage, &data) == 0) {
+ // failed
+ }
+}
+
+void
+ArchTaskBarWindows::removeIconNoLock(UINT id)
+{
+ NOTIFYICONDATA data;
+ data.cbSize = sizeof(NOTIFYICONDATA);
+ data.hWnd = m_hwnd;
+ data.uID = id;
+ if (Shell_NotifyIcon(NIM_DELETE, &data) == 0) {
+ // failed
+ }
+}
+
+void
+ArchTaskBarWindows::handleIconMessage(
+ IArchTaskBarReceiver* receiver, LPARAM lParam)
+{
+ // process message
+ switch (lParam) {
+ case WM_LBUTTONDOWN:
+ receiver->showStatus();
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ receiver->primaryAction();
+ break;
+
+ case WM_RBUTTONUP: {
+ POINT p;
+ GetCursorPos(&p);
+ receiver->runMenu(p.x, p.y);
+ break;
+ }
+
+ case WM_MOUSEMOVE:
+ // currently unused
+ break;
+
+ default:
+ // unused
+ break;
+ }
+}
+
+bool
+ArchTaskBarWindows::processDialogs(MSG* msg)
+{
+ // only one thread can be in this method on any particular object
+ // at any given time. that's not a problem since only our event
+ // loop calls this method and there's just one of those.
+
+ ARCH->lockMutex(m_mutex);
+
+ // remove removed dialogs
+ m_dialogs.erase(false);
+
+ // merge added dialogs into the dialog list
+ for (Dialogs::const_iterator index = m_addedDialogs.begin();
+ index != m_addedDialogs.end(); ++index) {
+ m_dialogs.insert(std::make_pair(index->first, index->second));
+ }
+ m_addedDialogs.clear();
+
+ ARCH->unlockMutex(m_mutex);
+
+ // check message against all dialogs until one handles it.
+ // note that we don't hold a lock while checking because
+ // the message is processed and may make calls to this
+ // object. that's okay because addDialog() and
+ // removeDialog() don't change the map itself (just the
+ // values of some elements).
+ ARCH->lockMutex(m_mutex);
+ for (Dialogs::const_iterator index = m_dialogs.begin();
+ index != m_dialogs.end(); ++index) {
+ if (index->second) {
+ ARCH->unlockMutex(m_mutex);
+ if (IsDialogMessage(index->first, msg)) {
+ return true;
+ }
+ ARCH->lockMutex(m_mutex);
+ }
+ }
+ ARCH->unlockMutex(m_mutex);
+
+ return false;
+}
+
+LRESULT
+ArchTaskBarWindows::wndProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case kNotifyReceiver: {
+ // lookup receiver
+ CIDToReceiverMap::const_iterator index = m_idTable.find((UINT)wParam);
+ if (index != m_idTable.end()) {
+ IArchTaskBarReceiver* receiver = index->second->first;
+ handleIconMessage(receiver, lParam);
+ return 0;
+ }
+ break;
+ }
+
+ case kAddReceiver:
+ addIcon((UINT)wParam);
+ break;
+
+ case kRemoveReceiver:
+ removeIcon((UINT)wParam);
+ break;
+
+ case kUpdateReceiver:
+ updateIcon((UINT)wParam);
+ break;
+
+ default:
+ if (msg == m_taskBarRestart) {
+ // task bar was recreated so re-add our icons
+ addAllIcons();
+ }
+ break;
+ }
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+LRESULT CALLBACK
+ArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ // if msg is WM_NCCREATE, extract the ArchTaskBarWindows* and put
+ // it in the extra window data then forward the call.
+ ArchTaskBarWindows* self = NULL;
+ if (msg == WM_NCCREATE) {
+ CREATESTRUCT* createInfo;
+ createInfo = reinterpret_cast<CREATESTRUCT*>(lParam);
+ self = static_cast<ArchTaskBarWindows*>(
+ createInfo->lpCreateParams);
+ SetWindowLongPtr(hwnd, 0, reinterpret_cast<LONG_PTR>(createInfo->lpCreateParams));
+ }
+ else {
+ // get the extra window data and forward the call
+ LONG_PTR data = GetWindowLongPtr(hwnd, 0);
+ if (data != 0) {
+ self = static_cast<ArchTaskBarWindows*>(reinterpret_cast<void*>(data));
+ }
+ }
+
+ // forward the message
+ if (self != NULL) {
+ return self->wndProc(hwnd, msg, wParam, lParam);
+ }
+ else {
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+}
+
+void
+ArchTaskBarWindows::threadMainLoop()
+{
+ // register the task bar restart message
+ m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
+
+ // register a window class
+ LPCTSTR className = TEXT("BarrierTaskBar");
+ WNDCLASSEX classInfo;
+ classInfo.cbSize = sizeof(classInfo);
+ classInfo.style = CS_NOCLOSE;
+ classInfo.lpfnWndProc = &ArchTaskBarWindows::staticWndProc;
+ classInfo.cbClsExtra = 0;
+ classInfo.cbWndExtra = sizeof(ArchTaskBarWindows*);
+ classInfo.hInstance = instanceWin32();
+ classInfo.hIcon = NULL;
+ classInfo.hCursor = NULL;
+ classInfo.hbrBackground = NULL;
+ classInfo.lpszMenuName = NULL;
+ classInfo.lpszClassName = className;
+ classInfo.hIconSm = NULL;
+ ATOM windowClass = RegisterClassEx(&classInfo);
+
+ // create window
+ m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
+ className,
+ TEXT("Barrier Task Bar"),
+ WS_POPUP,
+ 0, 0, 1, 1,
+ NULL,
+ NULL,
+ instanceWin32(),
+ static_cast<void*>(this));
+
+ // signal ready
+ ARCH->lockMutex(m_mutex);
+ m_ready = true;
+ ARCH->broadcastCondVar(m_condVar);
+ ARCH->unlockMutex(m_mutex);
+
+ // handle failure
+ if (m_hwnd == NULL) {
+ UnregisterClass(className, instanceWin32());
+ return;
+ }
+
+ // main loop
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ if (!processDialogs(&msg)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ // clean up
+ removeAllIcons();
+ DestroyWindow(m_hwnd);
+ UnregisterClass(className, instanceWin32());
+}
+
+void*
+ArchTaskBarWindows::threadEntry(void* self)
+{
+ static_cast<ArchTaskBarWindows*>(self)->threadMainLoop();
+ return NULL;
+}
+
+HINSTANCE ArchTaskBarWindows::instanceWin32()
+{
+ return ArchMiscWindows::instanceWin32();
+} \ No newline at end of file
diff --git a/src/lib/arch/win32/ArchTaskBarWindows.h b/src/lib/arch/win32/ArchTaskBarWindows.h
new file mode 100644
index 0000000..0edddf8
--- /dev/null
+++ b/src/lib/arch/win32/ArchTaskBarWindows.h
@@ -0,0 +1,114 @@
+/*
+ * 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 "arch/IArchTaskBar.h"
+#include "arch/IArchMultithread.h"
+#include "common/stdmap.h"
+#include "common/stdvector.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#define ARCH_TASKBAR ArchTaskBarWindows
+
+//! Win32 implementation of IArchTaskBar
+class ArchTaskBarWindows : public IArchTaskBar {
+public:
+ ArchTaskBarWindows();
+ virtual ~ArchTaskBarWindows();
+
+ virtual void init();
+
+ //! Add a dialog window
+ /*!
+ Tell the task bar event loop about a dialog. Win32 annoyingly
+ requires messages destined for modeless dialog boxes to be
+ dispatched differently than other messages.
+ */
+ static void addDialog(HWND);
+
+ //! Remove a dialog window
+ /*!
+ Remove a dialog window added via \c addDialog().
+ */
+ static void removeDialog(HWND);
+
+ // IArchTaskBar overrides
+ virtual void addReceiver(IArchTaskBarReceiver*);
+ virtual void removeReceiver(IArchTaskBarReceiver*);
+ virtual void updateReceiver(IArchTaskBarReceiver*);
+
+private:
+ class ReceiverInfo {
+ public:
+ UINT m_id;
+ };
+
+ typedef std::map<IArchTaskBarReceiver*, ReceiverInfo> ReceiverToInfoMap;
+ typedef std::map<UINT, ReceiverToInfoMap::iterator> CIDToReceiverMap;
+ typedef std::vector<UINT> CIDStack;
+ typedef std::map<HWND, bool> Dialogs;
+
+ UINT getNextID();
+ void recycleID(UINT);
+
+ void addIcon(UINT);
+ void removeIcon(UINT);
+ void updateIcon(UINT);
+ void addAllIcons();
+ void removeAllIcons();
+ void modifyIconNoLock(ReceiverToInfoMap::const_iterator,
+ DWORD taskBarMessage);
+ void removeIconNoLock(UINT id);
+ void handleIconMessage(IArchTaskBarReceiver*, LPARAM);
+
+ bool processDialogs(MSG*);
+ LRESULT wndProc(HWND, UINT, WPARAM, LPARAM);
+ static LRESULT CALLBACK
+ staticWndProc(HWND, UINT, WPARAM, LPARAM);
+ void threadMainLoop();
+ static void* threadEntry(void*);
+
+ HINSTANCE instanceWin32();
+
+private:
+ static ArchTaskBarWindows* s_instance;
+
+ // multithread data
+ ArchMutex m_mutex;
+ ArchCond m_condVar;
+ bool m_ready;
+ int m_result;
+ ArchThread m_thread;
+
+ // child thread data
+ HWND m_hwnd;
+ UINT m_taskBarRestart;
+
+ // shared data
+ ReceiverToInfoMap m_receivers;
+ CIDToReceiverMap m_idTable;
+ CIDStack m_oldIDs;
+ UINT m_nextID;
+
+ // dialogs
+ Dialogs m_dialogs;
+ Dialogs m_addedDialogs;
+};
diff --git a/src/lib/arch/win32/ArchTimeWindows.cpp b/src/lib/arch/win32/ArchTimeWindows.cpp
new file mode 100644
index 0000000..568a483
--- /dev/null
+++ b/src/lib/arch/win32/ArchTimeWindows.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 "arch/win32/ArchTimeWindows.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#define MMNODRV // Disable: Installable driver support
+#define MMNOSOUND // Disable: Sound support
+#define MMNOWAVE // Disable: Waveform support
+#define MMNOMIDI // Disable: MIDI support
+#define MMNOAUX // Disable: Auxiliary audio support
+#define MMNOMIXER // Disable: Mixer support
+#define MMNOJOY // Disable: Joystick support
+#define MMNOMCI // Disable: MCI support
+#define MMNOMMIO // Disable: Multimedia file I/O support
+#define MMNOMMSYSTEM // Disable: General MMSYSTEM functions
+#include <MMSystem.h>
+
+typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void);
+
+static double s_freq = 0.0;
+static HINSTANCE s_mmInstance = NULL;
+static PTimeGetTime s_tgt = NULL;
+
+
+//
+// ArchTimeWindows
+//
+
+ArchTimeWindows::ArchTimeWindows()
+{
+ assert(s_freq == 0.0 || s_mmInstance == NULL);
+
+ LARGE_INTEGER freq;
+ if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) {
+ s_freq = 1.0 / static_cast<double>(freq.QuadPart);
+ }
+ else {
+ // load winmm.dll and get timeGetTime
+ s_mmInstance = LoadLibrary("winmm");
+ if (s_mmInstance != NULL) {
+ s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime");
+ }
+ }
+}
+
+ArchTimeWindows::~ArchTimeWindows()
+{
+ s_freq = 0.0;
+ if (s_mmInstance == NULL) {
+ FreeLibrary(static_cast<HMODULE>(s_mmInstance));
+ s_tgt = NULL;
+ s_mmInstance = NULL;
+ }
+}
+
+double
+ArchTimeWindows::time()
+{
+ // get time. we try three ways, in order of descending precision
+ if (s_freq != 0.0) {
+ LARGE_INTEGER c;
+ QueryPerformanceCounter(&c);
+ return s_freq * static_cast<double>(c.QuadPart);
+ }
+ else if (s_tgt != NULL) {
+ return 0.001 * static_cast<double>(s_tgt());
+ }
+ else {
+ return 0.001 * static_cast<double>(GetTickCount());
+ }
+}
diff --git a/src/lib/arch/win32/ArchTimeWindows.h b/src/lib/arch/win32/ArchTimeWindows.h
new file mode 100644
index 0000000..42351a1
--- /dev/null
+++ b/src/lib/arch/win32/ArchTimeWindows.h
@@ -0,0 +1,33 @@
+/*
+ * 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 "arch/IArchTime.h"
+
+#define ARCH_TIME ArchTimeWindows
+
+//! Win32 implementation of IArchTime
+class ArchTimeWindows : public IArchTime {
+public:
+ ArchTimeWindows();
+ virtual ~ArchTimeWindows();
+
+ // IArchTime overrides
+ virtual double time();
+};
diff --git a/src/lib/arch/win32/XArchWindows.cpp b/src/lib/arch/win32/XArchWindows.cpp
new file mode 100644
index 0000000..eeec0e1
--- /dev/null
+++ b/src/lib/arch/win32/XArchWindows.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 "arch/win32/XArchWindows.h"
+#include "arch/win32/ArchNetworkWinsock.h"
+#include "base/String.h"
+
+//
+// XArchEvalWindows
+//
+
+std::string
+XArchEvalWindows::eval() const throw()
+{
+ char* cmsg;
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ 0,
+ m_error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&cmsg,
+ 0,
+ NULL) == 0) {
+ cmsg = NULL;
+ return barrier::string::sprintf("Unknown error, code %d", m_error);
+ }
+ std::string smsg(cmsg);
+ LocalFree(cmsg);
+ return smsg;
+}
+
+
+//
+// XArchEvalWinsock
+//
+
+std::string
+XArchEvalWinsock::eval() const throw()
+{
+ // built-in windows function for looking up error message strings
+ // may not look up network error messages correctly. we'll have
+ // to do it ourself.
+ static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = {
+ /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"},
+ /* 10009 */{WSAEBADF, "Bad file handle"},
+ /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"},
+ /* 10014 */{WSAEFAULT, "WSAEFAULT"},
+ /* 10022 */{WSAEINVAL, "WSAEINVAL"},
+ /* 10024 */{WSAEMFILE, "No more file descriptors available"},
+ /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"},
+ /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"},
+ /* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"},
+ /* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"},
+ /* 10039 */{WSAEDESTADDRREQ, "A destination address is required"},
+ /* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"},
+ /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"},
+ /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"},
+ /* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"},
+ /* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"},
+ /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"},
+ /* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"},
+ /* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"},
+ /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"},
+ /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"},
+ /* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"},
+ /* 10051 */{WSAENETUNREACH, "The network can't be reached from this host at this time"},
+ /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"},
+ /* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"},
+ /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"},
+ /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"},
+ /* 10056 */{WSAEISCONN, "The socket is already connected"},
+ /* 10057 */{WSAENOTCONN, "The socket is not connected"},
+ /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"},
+ /* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"},
+ /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"},
+ /* 10061 */{WSAECONNREFUSED, "Connection was refused"},
+ /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"},
+ /* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"},
+ /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"},
+ /* 10065 */{WSAEHOSTUNREACH, "No route to host"},
+ /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"},
+ /* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"},
+ /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"},
+ /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"},
+ /* 10070 */{WSAESTALE, "Undocumented WinSock error code"},
+ /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"},
+ /* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"},
+ /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"},
+ /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"},
+ /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"},
+ /* 11001 */{WSAHOST_NOT_FOUND, "The specified host is unknown"},
+ /* 11002 */{WSATRY_AGAIN, "A temporary error occurred on an authoritative name server"},
+ /* 11003 */{WSANO_RECOVERY, "A non-recoverable name server error occurred"},
+ /* 11004 */{WSANO_DATA, "The requested name is valid but does not have an IP address"},
+ /* end */{0, NULL}
+ };
+
+ for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) {
+ if (s_netErrorCodes[i].m_code == m_error) {
+ return s_netErrorCodes[i].m_msg;
+ }
+ }
+ return "Unknown error";
+}
diff --git a/src/lib/arch/win32/XArchWindows.h b/src/lib/arch/win32/XArchWindows.h
new file mode 100644
index 0000000..10106b1
--- /dev/null
+++ b/src/lib/arch/win32/XArchWindows.h
@@ -0,0 +1,49 @@
+/*
+ * 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 "arch/XArch.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+//! Lazy error message string evaluation for windows
+class XArchEvalWindows : public XArchEval {
+public:
+ XArchEvalWindows() : m_error(GetLastError()) { }
+ XArchEvalWindows(DWORD error) : m_error(error) { }
+ virtual ~XArchEvalWindows() { }
+
+ virtual std::string eval() const;
+
+private:
+ DWORD m_error;
+};
+
+//! Lazy error message string evaluation for winsock
+class XArchEvalWinsock : public XArchEval {
+public:
+ XArchEvalWinsock(int error) : m_error(error) { }
+ virtual ~XArchEvalWinsock() { }
+
+ virtual std::string eval() const;
+
+private:
+ int m_error;
+};