diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 00000000..ea648387
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+See doc/authors.html.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 00000000..26a5086f
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,97 @@
+# synergy -- mouse and keyboard sharing utility
+# 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+## Process this file with automake to produce Makefile.in
+NULL =
+
+SUBDIRS = \
+ lib \
+ cmd \
+ doc \
+ dist \
+ $(NULL)
+
+EXTRA_DIST = \
+ Makefile.win \
+ examples/synergy.conf \
+ win32util/autodep.cpp \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ aclocal.m4 \
+ config.h \
+ config.h.in \
+ config.log \
+ config.status \
+ configure \
+ stamp-h.in \
+ stamp-h1 \
+ $(NULL)
+
+PKG_FILES = \
+ ChangeLog \
+ README \
+ cmd/synergyc/synergyc \
+ cmd/synergys/synergys \
+ examples/synergy.conf \
+ $(NULL)
+PKG_DOC_FILES = \
+ doc/PORTING \
+ doc/*.html \
+ doc/*.css \
+ $(NULL)
+PKG_PROG_FILES = \
+ synergyc \
+ synergys \
+ $(NULL)
+
+# build doxygen documentation
+doxygen:
+ doxygen doc/doxygen.cfg
+
+# build RPMs
+RPMTOPDIR=/var/tmp/@PACKAGE@-@VERSION@
+dist-rpm: dist
+ rm -rf $(RPMTOPDIR)
+ mkdir $(RPMTOPDIR)
+ (cd $(RPMTOPDIR); mkdir BUILD SOURCES SPECS SRPMS RPMS)
+ cp @PACKAGE@-@VERSION@.tar.gz $(RPMTOPDIR)/SOURCES
+ rpm --define '_topdir $(RPMTOPDIR)' -ba dist/rpm/synergy.spec && \
+ mv -f $(RPMTOPDIR)/SRPMS/*.rpm . && \
+ mv -f $(RPMTOPDIR)/RPMS/*/*.rpm . && \
+ rm -rf $(RPMTOPDIR)
+
+# build zip
+# FIXME -- have automake generate this rule for us
+dist-zip: distdir
+ zip -r $(distdir).zip $(distdir)
+ -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir)
+
+# build binary package. owner/group of packaged files will be
+# owner/group of user running make.
+PKGTOPDIR=/var/tmp/@PACKAGE@-@VERSION@
+dist-pkg: all
+ rm -rf $(PKGTOPDIR)
+ mkdir $(PKGTOPDIR)
+ mkdir $(PKGTOPDIR)/@PACKAGE@-@VERSION@
+ mkdir $(PKGTOPDIR)/@PACKAGE@-@VERSION@/doc
+ cp $(PKG_FILES) $(PKGTOPDIR)/@PACKAGE@-@VERSION@
+ cp $(PKG_DOC_FILES) $(PKGTOPDIR)/@PACKAGE@-@VERSION@/doc
+ (cd $(PKGTOPDIR)/@PACKAGE@-@VERSION@; \
+ chmod 644 *; \
+ chmod 755 doc $(PKG_PROG_FILES); \
+ strip $(PKG_PROG_FILES) )
+ type=`uname -s -m | tr '[A-Z] ' '[a-z].'`; \
+ (cd $(PKGTOPDIR); tar cf - @PACKAGE@-@VERSION@ | \
+ gzip - ) > @PACKAGE@-@VERSION@-1.$${type}.tar.gz && \
+ rm -rf $(PKGTOPDIR)
diff --git a/Makefile.win b/Makefile.win
new file mode 100644
index 00000000..d232b58a
--- /dev/null
+++ b/Makefile.win
@@ -0,0 +1,145 @@
+# synergy -- mouse and keyboard sharing utility
+# Copyright (C) 2007 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# Name of this file for recursive make
+MAKEFILE = Makefile.win
+
+# Default build is release is NODEBUG is defined, debug otherwise.
+!if !DEFINED(DEBUG)
+NODEBUG = 1
+!else
+!undef NODEBUG
+!endif
+
+# Build all by default
+default: all
+
+# Redefine implicit rule suffixes
+.SUFFIXES:
+.SUFFIXES: .cpp .rc .obj
+
+# Shut up
+.SILENT:
+
+# Include system macros
+#APPVER = 5.0
+#TARGETOS = WINNT
+!include
+
+# Be explicit about C++ compiler
+cpp = $(cc)
+cppdebug = $(cdebug)
+cppflags = $(cflags)
+cppvarsmt = $(cvarsmt)
+
+# Library tool options
+ildebug =
+ilflags = /nologo
+
+# Handy macro for defining list macros
+NULL =
+
+# System commands
+ECHO = echo
+MKDIR = mkdir
+RM = del /f
+RMR = rmdir /q /s
+
+# Local build utilities
+UTIL_DIR = win32util
+AUTODEP = "$(UTIL_DIR)\autodep.exe"
+
+# Destination for intermediate build targets
+BUILD_DIR = build
+BUILD_DEBUG_DIR = $(BUILD_DIR)\Debug
+BUILD_RELEASE_DIR = $(BUILD_DIR)\Release
+!if DEFINED(NODEBUG)
+BUILD_DST = $(BUILD_RELEASE_DIR)
+!else
+BUILD_DST = $(BUILD_DEBUG_DIR)
+!endif
+
+# Compiler argument changes
+cflags = $(cflags:-W3=-W4) /WX
+cflags = $(cflags) -D_CRT_SECURE_NO_DEPRECATE
+cflags = $(cflags) /GR
+!if !DEFINED(OLDCOMPILER)
+cflags = $(cflags) /EHsc
+!else
+cflags = $(cflags) /GX
+!endif
+!if !DEFINED(NODEBUG)
+!if !DEFINED(OLDCOMPILER)
+cdebug = $(cdebug) /RTC1
+!else
+cdebug = $(cdebug) /GZ
+!endif
+!endif
+
+# Initialize variables for library and program makefiles
+C_FILES =
+CPP_FILES =
+OBJ_FILES =
+LIB_FILES =
+PROGRAMS =
+OPTPROGRAMS = $(AUTODEP)
+
+# Include subdirectory makefiles
+!include lib\common\$(MAKEFILE)
+!include lib\arch\$(MAKEFILE)
+!include lib\base\$(MAKEFILE)
+!include lib\mt\$(MAKEFILE)
+!include lib\io\$(MAKEFILE)
+!include lib\net\$(MAKEFILE)
+!include lib\synergy\$(MAKEFILE)
+!include lib\platform\$(MAKEFILE)
+!include lib\client\$(MAKEFILE)
+!include lib\server\$(MAKEFILE)
+!include cmd\synergyc\$(MAKEFILE)
+!include cmd\synergys\$(MAKEFILE)
+!include cmd\launcher\$(MAKEFILE)
+!include dist\nullsoft\$(MAKEFILE)
+
+# Collect library and program variables
+INTERMEDIATES = $(OBJ_FILES) $(AUTODEP:.exe=.obj)
+TARGETS = $(LIB_FILES) $(PROGRAMS)
+OPTTARGETS = $(OPTPROGRAMS)
+
+# Build release by reinvoking make with NODEBUG defined
+release:
+ @$(MAKE) /nologo /f $(MAKEFILE) NODEBUG=1
+
+# Build debug by reinvoking make with DEBUG defined
+debug:
+ @$(MAKE) /nologo /f $(MAKEFILE) DEBUG=1
+
+# Build all targets
+all: $(TARGETS)
+
+# Clean intermediate targets
+clean:
+ -$(RMR) $(BUILD_DEBUG_DIR)
+ -$(RMR) $(BUILD_RELEASE_DIR)
+
+# Clean all targets
+clobber: clean
+ -$(RMR) $(BUILD_DIR)
+
+# Utility command build rules
+$(AUTODEP): $(AUTODEP:.exe=.cpp)
+!if DEFINED(NODEBUG)
+ @$(ECHO) Build $(@F)
+ $(cpp) $(cppdebug) $(cppflags) $(cppvars) /Fo"$(**:.cpp=.obj)" $**
+ $(link) $(ldebug) $(conflags) -out:$@ $(**:.cpp=.obj) $(conlibs)
+!else
+ @$(MAKE) /nologo /f $(MAKEFILE) NODEBUG=1 $@
+!endif
diff --git a/NEWS b/NEWS
new file mode 100644
index 00000000..e9aa7916
--- /dev/null
+++ b/NEWS
@@ -0,0 +1 @@
+See doc/news.html.
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644
index 00000000..29163d5a
--- /dev/null
+++ b/acinclude.m4
@@ -0,0 +1,571 @@
+dnl synergy -- mouse and keyboard sharing utility
+dnl Copyright (C) 2002 Chris Schoeneman
+dnl
+dnl This package is free software; you can redistribute it and/or
+dnl modify it under the terms of the GNU General Public License
+dnl found in the file COPYING that should have accompanied this file.
+dnl
+dnl This package is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+
+AC_DEFUN([ACX_CHECK_SOCKLEN_T], [
+ AC_MSG_CHECKING([for socklen_t])
+ AC_TRY_COMPILE([
+ #include
+ #include
+ ],
+ [socklen_t len;],[acx_socklen_t_ok=yes],[acx_socklen_t_ok=no])
+ AC_MSG_RESULT($acx_socklen_t_ok)
+ if test x"$acx_socklen_t_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_SOCKLEN_T,1,[Define if your compiler defines socklen_t.]),[$1])
+ :
+ else
+ acx_socklen_t_ok=no
+ $2
+ fi
+])dnl ACX_CHECK_SOCKLEN_T
+
+# HP-UX defines socklen_t but doesn't use it in arg 3 for accept().
+AC_DEFUN([ACX_FUNC_ACCEPT], [
+ AC_MSG_CHECKING([for type of arg 3 for accept])
+ acx_accept_socklen_t_arg3=int
+ if test x"$acx_socklen_t_ok" = xyes; then
+ AC_TRY_COMPILE([
+ #include
+ #include
+ ],
+ [struct sockaddr addr; socklen_t len; accept(0, &addr, &len);],
+ [acx_accept_socklen_t_arg3=socklen_t],
+ [acx_accept_socklen_t_arg3=int])
+ fi
+ AC_MSG_RESULT($acx_accept_socklen_t_arg3)
+ AC_DEFINE_UNQUOTED(ACCEPT_TYPE_ARG3,$acx_accept_socklen_t_arg3,[Define to the base type of arg 3 for `accept'.])
+])dnl ACX_FUNC_ACCEPT
+
+AC_DEFUN([ACX_CHECK_CXX], [
+ AC_MSG_CHECKING([if g++ defines correct C++ macro])
+ AC_TRY_COMPILE(, [
+ #if defined(_LANGUAGE_C) && !defined(_LANGUAGE_C_PLUS_PLUS)
+ #error wrong macro
+ #endif],[acx_cxx_macro_ok=yes],[acx_cxx_macro_ok=no])
+ AC_MSG_RESULT($acx_cxx_macro_ok)
+ if test x"$acx_cxx_macro_ok" = xyes; then
+ SYNERGY_CXXFLAGS=""
+ else
+ SYNERGY_CXXFLAGS="-U_LANGUAGE_C -D_LANGUAGE_C_PLUS_PLUS"
+ fi
+])dnl ACX_CHECK_CXX
+
+AC_DEFUN([ACX_CHECK_CXX_BOOL], [
+ AC_MSG_CHECKING([for bool support])
+ AC_TRY_COMPILE(, [bool t = true, f = false;],
+ [acx_cxx_bool_ok=yes],[acx_cxx_bool_ok=no])
+ AC_MSG_RESULT($acx_cxx_bool_ok)
+ if test x"$acx_cxx_bool_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_CXX_BOOL,1,[Define if your compiler has bool support.]),[$1])
+ :
+ else
+ acx_cxx_bool_ok=no
+ $2
+ fi
+])dnl ACX_CHECK_CXX_BOOL
+
+AC_DEFUN([ACX_CHECK_CXX_EXCEPTIONS], [
+ AC_MSG_CHECKING([for exception support])
+ AC_TRY_COMPILE(, [try{throw int(4);}catch(int){throw;}catch(...){}],
+ [acx_cxx_exception_ok=yes],[acx_cxx_exception_ok=no])
+ AC_MSG_RESULT($acx_cxx_exception_ok)
+ if test x"$acx_cxx_exception_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_CXX_EXCEPTIONS,1,[Define if your compiler has exceptions support.]),[$1])
+ :
+ else
+ acx_cxx_exception_ok=no
+ $2
+ fi
+])dnl ACX_CHECK_CXX_EXCEPTIONS
+
+AC_DEFUN([ACX_CHECK_CXX_CASTS], [
+ AC_MSG_CHECKING([for C++ cast support])
+ AC_TRY_COMPILE(, [const char* f="a";const_cast(f);
+ reinterpret_cast(f);static_cast(4.5);],
+ [acx_cxx_cast_ok=yes],[acx_cxx_cast_ok=no])
+ AC_MSG_RESULT($acx_cxx_cast_ok)
+ if test x"$acx_cxx_cast_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_CXX_CASTS,1,[Define if your compiler has C++ cast support.]),[$1])
+ :
+ else
+ acx_cxx_cast_ok=no
+ $2
+ fi
+])dnl ACX_CHECK_CXX_CASTS
+
+AC_DEFUN([ACX_CHECK_CXX_MUTABLE], [
+ AC_MSG_CHECKING([for mutable support])
+ AC_TRY_COMPILE(, [struct A{mutable int b;void f() const {b=0;}};
+ A a;a.f();],[acx_cxx_mutable_ok=yes],[acx_cxx_mutable_ok=no])
+ AC_MSG_RESULT($acx_cxx_mutable_ok)
+ if test x"$acx_cxx_mutable_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_CXX_MUTABLE,1,[Define if your compiler has mutable support.]),[$1])
+ :
+ else
+ acx_cxx_mutable_ok=no
+ $2
+ fi
+])dnl ACX_CHECK_CXX_MUTABLE
+
+AC_DEFUN([ACX_CHECK_CXX_STDLIB], [
+ AC_MSG_CHECKING([for C++ standard library])
+ AC_TRY_LINK([#include ], [std::set a; a.insert(3);],
+ [acx_cxx_stdlib_ok=yes],[acx_cxx_stdlib_ok=no])
+ AC_MSG_RESULT($acx_cxx_stdlib_ok)
+ if test x"$acx_cxx_stdlib_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_CXX_STDLIB,1,[Define if your compiler has standard C++ library support.]),[$1])
+ :
+ else
+ acx_cxx_stdlib_ok=no
+ $2
+ fi
+])dnl ACX_CHECK_CXX_STDLIB
+
+AC_DEFUN([ACX_CHECK_GETPWUID_R], [
+ AC_MSG_CHECKING([for working getpwuid_r])
+ AC_TRY_LINK([#include ],
+ [char buffer[4096]; struct passwd pwd, *pwdp;
+ getpwuid_r(0, &pwd, buffer, sizeof(buffer), &pwdp);],
+ acx_getpwuid_r_ok=yes, acx_getpwuid_r_ok=no)
+ AC_MSG_RESULT($acx_getpwuid_r_ok)
+ if test x"$acx_getpwuid_r_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_GETPWUID_R,1,[Define if you have a working \`getpwuid_r\' function.]),[$1])
+ :
+ else
+ acx_getpwuid_r_ok=no
+ $2
+ fi
+])dnl ACX_CHECK_GETPWUID_R
+
+AC_DEFUN([ACX_CHECK_POLL], [
+ AC_MSG_CHECKING([for poll])
+ AC_TRY_LINK([#include ],
+ [#if defined(_POLL_EMUL_H_)
+ #error emulated poll
+ #endif
+ struct pollfd ufds[] = { 0, POLLIN, 0 }; poll(ufds, 1, 10);],
+ acx_poll_ok=yes, acx_poll_ok=no)
+ AC_MSG_RESULT($acx_poll_ok)
+ if test x"$acx_poll_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_POLL,1,[Define if you have the \`poll\' function.]),[$1])
+ :
+ else
+ acx_poll_ok=no
+ $2
+ fi
+])dnl ACX_CHECK_POLL
+
+dnl See if we need extra libraries for nanosleep
+AC_DEFUN([ACX_CHECK_NANOSLEEP], [
+ acx_nanosleep_ok=no
+ acx_nanosleep_list=""
+
+ dnl check if user has set NANOSLEEP_LIBS
+ save_user_NANOSLEEP_LIBS="$NANOSLEEP_LIBS"
+ if test x"$NANOSLEEP_LIBS" != x; then
+ acx_nanosleep_list=user
+ fi
+
+ dnl check various libraries (including no extra libraries) for
+ dnl nanosleep. `none' should appear first.
+ acx_nanosleep_list="none $acx_nanosleep_list rt"
+ for flag in $acx_nanosleep_list; do
+ case $flag in
+ none)
+ AC_MSG_CHECKING([for nanosleep])
+ NANOSLEEP_LIBS=""
+ ;;
+
+ user)
+ AC_MSG_CHECKING([for nanosleep in $save_user_NANOSLEEP_LIBS])
+ NANOSLEEP_LIBS="$save_user_NANOSLEEP_LIBS"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for nanosleep in -l$flag])
+ NANOSLEEP_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ LIBS="$NANOSLEEP_LIBS $LIBS"
+ AC_TRY_LINK([#include ],
+ [struct timespec t = { 1, 1000 }; nanosleep(&t, NULL);],
+ acx_nanosleep_ok=yes, acx_nanosleep_ok=no)
+ LIBS="$save_LIBS"
+ AC_MSG_RESULT($acx_nanosleep_ok)
+ if test x"$acx_nanosleep_ok" = xyes; then
+ break;
+ fi
+ NANOSLEEP_LIBS=""
+ done
+
+ AC_SUBST(NANOSLEEP_LIBS)
+
+ # execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+ if test x"$acx_nanosleep_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_NANOSLEEP,1,[Define if you have the \`nanosleep\' function.]),[$1])
+ :
+ else
+ acx_nanosleep_ok=no
+ $2
+ fi
+])dnl ACX_CHECK_NANOSLEEP
+
+dnl See if we need extra libraries for inet_aton
+AC_DEFUN([ACX_CHECK_INET_ATON], [
+ acx_inet_aton_ok=no
+ acx_inet_aton_list=""
+
+ dnl check if user has set INET_ATON_LIBS
+ save_user_INET_ATON_LIBS="$INET_ATON_LIBS"
+ if test x"$INET_ATON_LIBS" != x; then
+ acx_inet_aton_list=user
+ fi
+
+ dnl check various libraries (including no extra libraries) for
+ dnl inet_aton. `none' should appear first.
+ acx_inet_aton_list="none $acx_inet_aton_list resolv"
+ for flag in $acx_inet_aton_list; do
+ case $flag in
+ none)
+ AC_MSG_CHECKING([for inet_aton])
+ INET_ATON_LIBS=""
+ ;;
+
+ user)
+ AC_MSG_CHECKING([for inet_aton in $save_user_INET_ATON_LIBS])
+ INET_ATON_LIBS="$save_user_INET_ATON_LIBS"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for inet_aton in -l$flag])
+ INET_ATON_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ LIBS="$INET_ATON_LIBS $LIBS"
+ AC_TRY_LINK([#include
+ #include
+ #include
+ #include ],
+ [struct in_addr addr; inet_aton("foo.bar", &addr);],
+ acx_inet_aton_ok=yes, acx_inet_aton_ok=no)
+ LIBS="$save_LIBS"
+ AC_MSG_RESULT($acx_inet_aton_ok)
+ if test x"$acx_inet_aton_ok" = xyes; then
+ AC_DEFINE(HAVE_INET_ATON,1,[Define if you have the \`inet_aton\' function.])
+ break;
+ fi
+ INET_ATON_LIBS=""
+ done
+
+ AC_SUBST(INET_ATON_LIBS)
+])dnl ACX_CHECK_INET_ATON
+
+dnl The following macros are from http://www.gnu.org/software/ac-archive/
+dnl which distributes them under the following license:
+dnl
+dnl Every Autoconf macro presented on this web site is free software; you can
+dnl redistribute it and/or modify it under the terms of the GNU General
+dnl Public License as published by the Free Software Foundation; either
+dnl version 2, or (at your option) any later version.
+dnl
+dnl They are distributed in the hope that they will be useful, but WITHOUT
+dnl ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+dnl FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+dnl more details. (You should have received a copy of the GNU General Public
+dnl License along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place -- Suite 330, Boston, MA 02111-1307,
+dnl USA.)
+dnl
+dnl As a special exception, the Free Software Foundation gives unlimited
+dnl permission to copy, distribute and modify the configure scripts that are
+dnl the output of Autoconf. You need not follow the terms of the GNU General
+dnl Public License when using or distributing such scripts, even though
+dnl portions of the text of Autoconf appear in them. The GNU General Public
+dnl License (GPL) does govern all other use of the material that constitutes
+dnl the Autoconf program.
+dnl
+dnl Certain portions of the Autoconf source text are designed to be copied
+dnl (in certain cases, depending on the input) into the output of Autoconf.
+dnl We call these the "data" portions. The rest of the Autoconf source text
+dnl consists of comments plus executable code that decides which of the data
+dnl portions to output in any given case. We call these comments and
+dnl executable code the "non-data" portions. Autoconf never copies any of the
+dnl non-data portions into its output.
+dnl
+dnl This special exception to the GPL applies to versions of Autoconf
+dnl released by the Free Software Foundation. When you make and distribute a
+dnl modified version of Autoconf, you may extend this special exception to
+dnl the GPL to apply to your modified version as well, *unless* your modified
+dnl version has the potential to copy into its output some of the text that
+dnl was the non-data portion of the version that you started with. (In other
+dnl words, unless your change moves or copies text from the non-data portions
+dnl to the data portions.) If your modification has such potential, you must
+dnl delete any notice of this special exception to the GPL from your modified
+dnl version
+
+AC_DEFUN([ACX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_SAVE
+AC_LANG_C
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+ AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthread or
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
+ -*)
+ AC_MSG_CHECKING([whether pthreads work with $flag])
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for the pthreads library -l$flag])
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ AC_TRY_LINK([#include ],
+ [pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
+ [acx_pthread_ok=yes])
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ AC_MSG_CHECKING([for joinable pthread attribute])
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ AC_TRY_LINK([#include ], [int attr=$attr;],
+ [attr_name=$attr; break])
+ done
+ AC_MSG_RESULT($attr_name)
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+ AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+ [Define to necessary symbol if this constant
+ uses a non-standard name on your system.])
+ fi
+
+ AC_MSG_CHECKING([if more special flags are required for pthreads])
+ flag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+ esac
+ AC_MSG_RESULT(${flag})
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ # Detect POSIX sigwait()
+ AC_MSG_CHECKING([for POSIX sigwait])
+ AC_TRY_LINK([#include
+ #include ],
+ [sigset_t sigset; int signal; sigwait(&sigset, &signal);],
+ ok=yes, ok=unknown)
+ if test x"$ok" = xunknown; then
+ save_CFLAGS2="$CFLAGS"
+ CFLAGS="$CFLAGS -D_POSIX_PTHREAD_SEMANTICS"
+ AC_TRY_LINK([#include
+ #include ],
+ [sigset_t sigset; int signal; sigwait(&sigset, &signal);],
+ ok=-D_POSIX_PTHREAD_SEMANTICS, ok=no)
+ CFLAGS="$save_CFLAGS2"
+ fi
+ AC_MSG_RESULT(${ok})
+ if test x"$ok" != xno; then
+ AC_DEFINE(HAVE_POSIX_SIGWAIT,1,[Define if you have a POSIX \`sigwait\' function.])
+ if test x"$ok" != xyes; then
+ PTHREAD_CFLAGS="$ok $PTHREAD_CFLAGS"
+ fi
+ fi
+
+ # Detect pthread signal functions
+ AC_MSG_CHECKING([for pthread signal functions])
+ AC_TRY_LINK([#include
+ #include ],
+ [pthread_kill(pthread_self(), SIGTERM);],
+ ok=yes, ok=no)
+ AC_MSG_RESULT(${ok})
+ if test x"$ok" = xyes; then
+ AC_DEFINE(HAVE_PTHREAD_SIGNAL,1,[Define if you have \`pthread_sigmask\' and \`pthread_kill\' functions.])
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with cc_r
+ AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
+else
+ PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+ :
+else
+ acx_pthread_ok=no
+ $2
+fi
+AC_LANG_RESTORE
+])dnl ACX_PTHREAD
+
+dnl enable maximum compiler warnings. must ignore unknown pragmas to
+dnl build on solaris.
+dnl we only know how to do this for g++
+AC_DEFUN([ACX_CXX_WARNINGS], [
+ AC_MSG_CHECKING([for C++ compiler warning flags])
+ if test "$GXX" = "yes"; then
+ acx_cxx_warnings="-Wall -Wno-unknown-pragmas"
+ fi
+ if test -n "$acx_cxx_warnings"; then
+ CXXFLAGS="$CXXFLAGS $acx_cxx_warnings"
+ else
+ acx_cxx_warnings="unknown"
+ fi
+ AC_MSG_RESULT($acx_cxx_warnings)
+])dnl ACX_CXX_WARNINGS
+
+dnl enable compiler warnings are errors
+dnl we only know how to do this for g++
+AC_DEFUN([ACX_CXX_WARNINGS_ARE_ERRORS], [
+ AC_MSG_CHECKING([for C++ compiler warning are errors flags])
+ if test "$GXX" = "yes"; then
+ acx_cxx_warnings_are_errors="-Werror"
+ fi
+ if test -n "$acx_cxx_warnings_are_errors"; then
+ CXXFLAGS="$CXXFLAGS $acx_cxx_warnings_are_errors"
+ else
+ acx_cxx_warnings_are_errors="unknown"
+ fi
+ AC_MSG_RESULT($acx_cxx_warnings_are_errors)
+])dnl ACX_CXX_WARNINGS_ARE_ERRORS
diff --git a/cmd/Makefile.am b/cmd/Makefile.am
new file mode 100644
index 00000000..1e43b156
--- /dev/null
+++ b/cmd/Makefile.am
@@ -0,0 +1,27 @@
+# synergy -- mouse and keyboard sharing utility
+# 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+## Process this file with automake to produce Makefile.in
+NULL =
+
+SUBDIRS = \
+ launcher \
+ synergyc \
+ synergys \
+ $(NULL)
+
+EXTRA_DIST = \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ $(NULL)
diff --git a/cmd/launcher/CAddScreen.cpp b/cmd/launcher/CAddScreen.cpp
new file mode 100644
index 00000000..5a876b77
--- /dev/null
+++ b/cmd/launcher/CAddScreen.cpp
@@ -0,0 +1,427 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CConfig.h"
+#include "KeyTypes.h"
+#include "OptionTypes.h"
+#include "ProtocolTypes.h"
+#include "CStringUtil.h"
+#include "CArch.h"
+#include "CAddScreen.h"
+#include "LaunchUtil.h"
+#include "resource.h"
+
+struct CModifierInfo {
+public:
+ int m_ctrlID;
+ const char* m_name;
+ KeyModifierID m_modifierID;
+ OptionID m_optionID;
+};
+
+static const CModifierInfo s_modifiers[] = {
+ { IDC_ADD_MOD_SHIFT, "Shift",
+ kKeyModifierIDShift, kOptionModifierMapForShift },
+ { IDC_ADD_MOD_CTRL, "Ctrl",
+ kKeyModifierIDControl, kOptionModifierMapForControl },
+ { IDC_ADD_MOD_ALT, "Alt",
+ kKeyModifierIDAlt, kOptionModifierMapForAlt },
+ { IDC_ADD_MOD_META, "Meta",
+ kKeyModifierIDMeta, kOptionModifierMapForMeta },
+ { IDC_ADD_MOD_SUPER, "Super",
+ kKeyModifierIDSuper, kOptionModifierMapForSuper }
+};
+
+static const KeyModifierID baseModifier = kKeyModifierIDShift;
+
+//
+// CAddScreen
+//
+
+CAddScreen* CAddScreen::s_singleton = NULL;
+
+CAddScreen::CAddScreen(HWND parent, CConfig* config, const CString& name) :
+ m_parent(parent),
+ m_config(config),
+ m_name(name)
+{
+ assert(s_singleton == NULL);
+ s_singleton = this;
+}
+
+CAddScreen::~CAddScreen()
+{
+ s_singleton = NULL;
+}
+
+bool
+CAddScreen::doModal()
+{
+ // do dialog
+ return (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD),
+ m_parent, (DLGPROC)dlgProc, (LPARAM)this) != 0);
+}
+
+CString
+CAddScreen::getName() const
+{
+ return m_name;
+}
+
+void
+CAddScreen::init(HWND hwnd)
+{
+ // set title
+ CString title;
+ if (m_name.empty()) {
+ title = getString(IDS_ADD_SCREEN);
+ }
+ else {
+ title = CStringUtil::format(
+ getString(IDS_EDIT_SCREEN).c_str(),
+ m_name.c_str());
+ }
+ SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)title.c_str());
+
+ // fill in screen name
+ HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT);
+ SendMessage(child, WM_SETTEXT, 0, (LPARAM)m_name.c_str());
+
+ // fill in aliases
+ CString aliases;
+ for (CConfig::all_const_iterator index = m_config->beginAll();
+ index != m_config->endAll(); ++index) {
+ if (CStringUtil::CaselessCmp::equal(index->second, m_name) &&
+ !CStringUtil::CaselessCmp::equal(index->second, index->first)) {
+ if (!aliases.empty()) {
+ aliases += "\r\n";
+ }
+ aliases += index->first;
+ }
+ }
+ child = getItem(hwnd, IDC_ADD_ALIASES_EDIT);
+ SendMessage(child, WM_SETTEXT, 0, (LPARAM)aliases.c_str());
+
+ // set options
+ CConfig::CScreenOptions options;
+ getOptions(options);
+ CConfig::CScreenOptions::const_iterator index;
+ child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK);
+ index = options.find(kOptionHalfDuplexCapsLock);
+ setItemChecked(child, (index != options.end() && index->second != 0));
+ child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK);
+ index = options.find(kOptionHalfDuplexNumLock);
+ setItemChecked(child, (index != options.end() && index->second != 0));
+ child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK);
+ index = options.find(kOptionHalfDuplexScrollLock);
+ setItemChecked(child, (index != options.end() && index->second != 0));
+
+ // modifier options
+ for (UInt32 i = 0; i < sizeof(s_modifiers) /
+ sizeof(s_modifiers[0]); ++i) {
+ child = getItem(hwnd, s_modifiers[i].m_ctrlID);
+
+ // fill in options
+ for (UInt32 j = 0; j < sizeof(s_modifiers) /
+ sizeof(s_modifiers[0]); ++j) {
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)s_modifiers[j].m_name);
+ }
+
+ // choose current value
+ index = options.find(s_modifiers[i].m_optionID);
+ KeyModifierID id = s_modifiers[i].m_modifierID;
+ if (index != options.end()) {
+ id = index->second;
+ }
+ SendMessage(child, CB_SETCURSEL, id - baseModifier, 0);
+ }
+
+ // dead corners
+ UInt32 corners = 0;
+ index = options.find(kOptionScreenSwitchCorners);
+ if (index != options.end()) {
+ corners = index->second;
+ }
+ child = getItem(hwnd, IDC_ADD_DC_TOP_LEFT);
+ setItemChecked(child, (corners & kTopLeftMask) != 0);
+ child = getItem(hwnd, IDC_ADD_DC_TOP_RIGHT);
+ setItemChecked(child, (corners & kTopRightMask) != 0);
+ child = getItem(hwnd, IDC_ADD_DC_BOTTOM_LEFT);
+ setItemChecked(child, (corners & kBottomLeftMask) != 0);
+ child = getItem(hwnd, IDC_ADD_DC_BOTTOM_RIGHT);
+ setItemChecked(child, (corners & kBottomRightMask) != 0);
+ index = options.find(kOptionScreenSwitchCornerSize);
+ SInt32 size = 0;
+ if (index != options.end()) {
+ size = index->second;
+ }
+ char buffer[20];
+ sprintf(buffer, "%d", size);
+ child = getItem(hwnd, IDC_ADD_DC_SIZE);
+ SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer);
+}
+
+bool
+CAddScreen::save(HWND hwnd)
+{
+ // get the old aliases and options
+ CStringList oldAliases;
+ getAliases(oldAliases);
+ CConfig::CScreenOptions options;
+ getOptions(options);
+
+ // extract name and aliases
+ CString newName;
+ HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT);
+ newName = getWindowText(child);
+ CStringList newAliases;
+ child = getItem(hwnd, IDC_ADD_ALIASES_EDIT);
+ tokenize(newAliases, getWindowText(child));
+
+ // name must be valid
+ if (!m_config->isValidScreenName(newName)) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_INVALID_SCREEN_NAME).c_str(),
+ newName.c_str()));
+ return false;
+ }
+
+ // aliases must be valid
+ for (CStringList::const_iterator index = newAliases.begin();
+ index != newAliases.end(); ++index) {
+ if (!m_config->isValidScreenName(*index)) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_INVALID_SCREEN_NAME).c_str(),
+ index->c_str()));
+ return false;
+ }
+ }
+
+ // new name may not be in the new alias list
+ if (isNameInList(newAliases, newName)) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_SCREEN_NAME_IS_ALIAS).c_str(),
+ newName.c_str()));
+ return false;
+ }
+
+ // name must not exist in config but allow same name. also
+ // allow name if it exists in the old alias list but not the
+ // new one.
+ if (m_config->isScreen(newName) &&
+ !CStringUtil::CaselessCmp::equal(newName, m_name) &&
+ !isNameInList(oldAliases, newName)) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_DUPLICATE_SCREEN_NAME).c_str(),
+ newName.c_str()));
+ return false;
+ }
+
+ // aliases must not exist in config but allow same aliases and
+ // allow an alias to be the old name.
+ for (CStringList::const_iterator index = newAliases.begin();
+ index != newAliases.end(); ++index) {
+ if (m_config->isScreen(*index) &&
+ !CStringUtil::CaselessCmp::equal(*index, m_name) &&
+ !isNameInList(oldAliases, *index)) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_DUPLICATE_SCREEN_NAME).c_str(),
+ index->c_str()));
+ return false;
+ }
+ }
+
+ // dead corner size must be non-negative
+ child = getItem(hwnd, IDC_ADD_DC_SIZE);
+ CString valueString = getWindowText(child);
+ int cornerSize = atoi(valueString.c_str());
+ if (cornerSize < 0) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_INVALID_CORNER_SIZE).c_str(),
+ valueString.c_str()));
+ SetFocus(child);
+ return false;
+ }
+
+ // collect options
+ child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK);
+ if (isItemChecked(child)) {
+ options[kOptionHalfDuplexCapsLock] = 1;
+ }
+ else {
+ options.erase(kOptionHalfDuplexCapsLock);
+ }
+ child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK);
+ if (isItemChecked(child)) {
+ options[kOptionHalfDuplexNumLock] = 1;
+ }
+ else {
+ options.erase(kOptionHalfDuplexNumLock);
+ }
+ child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK);
+ if (isItemChecked(child)) {
+ options[kOptionHalfDuplexScrollLock] = 1;
+ }
+ else {
+ options.erase(kOptionHalfDuplexScrollLock);
+ }
+
+ // save modifier options
+ for (UInt32 i = 0; i < sizeof(s_modifiers) /
+ sizeof(s_modifiers[0]); ++i) {
+ child = getItem(hwnd, s_modifiers[i].m_ctrlID);
+ KeyModifierID id = static_cast(
+ SendMessage(child, CB_GETCURSEL, 0, 0) +
+ baseModifier);
+ if (id != s_modifiers[i].m_modifierID) {
+ options[s_modifiers[i].m_optionID] = id;
+ }
+ else {
+ options.erase(s_modifiers[i].m_optionID);
+ }
+ }
+
+ // save dead corner options
+ UInt32 corners = 0;
+ if (isItemChecked(getItem(hwnd, IDC_ADD_DC_TOP_LEFT))) {
+ corners |= kTopLeftMask;
+ }
+ if (isItemChecked(getItem(hwnd, IDC_ADD_DC_TOP_RIGHT))) {
+ corners |= kTopRightMask;
+ }
+ if (isItemChecked(getItem(hwnd, IDC_ADD_DC_BOTTOM_LEFT))) {
+ corners |= kBottomLeftMask;
+ }
+ if (isItemChecked(getItem(hwnd, IDC_ADD_DC_BOTTOM_RIGHT))) {
+ corners |= kBottomRightMask;
+ }
+ options[kOptionScreenSwitchCorners] = corners;
+ options[kOptionScreenSwitchCornerSize] = cornerSize;
+
+ // save new data to config
+ if (m_name.empty()) {
+ // added screen
+ m_config->addScreen(newName);
+ }
+ else {
+ // edited screen
+ m_config->removeAliases(m_name);
+ m_config->removeOptions(m_name);
+ m_config->renameScreen(m_name, newName);
+ }
+ m_name = newName;
+ for (CStringList::const_iterator index = newAliases.begin();
+ index != newAliases.end(); ++index) {
+ m_config->addAlias(m_name, *index);
+ }
+ for (CConfig::CScreenOptions::const_iterator
+ index = options.begin();
+ index != options.end(); ++index) {
+ m_config->addOption(m_name, index->first, index->second);
+ }
+
+ return true;
+}
+
+BOOL
+CAddScreen::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
+{
+ switch (message) {
+ case WM_INITDIALOG:
+ init(hwnd);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ if (save(hwnd)) {
+ EndDialog(hwnd, 1);
+ }
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwnd, 0);
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+BOOL CALLBACK
+CAddScreen::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
+}
+
+void
+CAddScreen::getAliases(CStringList& aliases) const
+{
+ for (CConfig::all_const_iterator index = m_config->beginAll();
+ index != m_config->endAll(); ++index) {
+ if (CStringUtil::CaselessCmp::equal(index->second, m_name) &&
+ !CStringUtil::CaselessCmp::equal(index->second, index->first)) {
+ aliases.push_back(index->first);
+ }
+ }
+}
+
+void
+CAddScreen::getOptions(CConfig::CScreenOptions& optionsOut) const
+{
+ const CConfig::CScreenOptions* options = m_config->getOptions(m_name);
+ if (options == NULL) {
+ optionsOut = CConfig::CScreenOptions();
+ }
+ else {
+ optionsOut = *options;
+ }
+}
+
+void
+CAddScreen::tokenize(CStringList& tokens, const CString& src)
+{
+ // find first non-whitespace
+ CString::size_type x = src.find_first_not_of(" \t\r\n");
+ if (x == CString::npos) {
+ return;
+ }
+
+ // find next whitespace
+ do {
+ CString::size_type y = src.find_first_of(" \t\r\n", x);
+ if (y == CString::npos) {
+ y = src.size();
+ }
+ tokens.push_back(src.substr(x, y - x));
+ x = src.find_first_not_of(" \t\r\n", y);
+ } while (x != CString::npos);
+}
+
+bool
+CAddScreen::isNameInList(const CStringList& names, const CString& name)
+{
+ for (CStringList::const_iterator index = names.begin();
+ index != names.end(); ++index) {
+ if (CStringUtil::CaselessCmp::equal(name, *index)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/cmd/launcher/CAddScreen.h b/cmd/launcher/CAddScreen.h
new file mode 100644
index 00000000..e926c498
--- /dev/null
+++ b/cmd/launcher/CAddScreen.h
@@ -0,0 +1,74 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CADDSCREEN_H
+#define CADDSCREEN_H
+
+#include "CString.h"
+
+#define WINDOWS_LEAN_AND_MEAN
+#include
+
+class CConfig;
+
+//! Add screen dialog for Microsoft Windows launcher
+class CAddScreen {
+public:
+ CAddScreen(HWND parent, CConfig*, const CString& name);
+ ~CAddScreen();
+
+ //! @name manipulators
+ //@{
+
+ //! Run dialog
+ /*!
+ Display and handle the dialog until closed by the user. Return
+ \c true if the user accepted the changes, false otherwise.
+ */
+ bool doModal();
+
+ //@}
+ //! @name accessors
+ //@{
+
+ CString getName() const;
+
+ //@}
+
+private:
+ typedef std::vector CStringList;
+
+ void getAliases(CStringList&) const;
+ void getOptions(CConfig::CScreenOptions&) const;
+
+ static void tokenize(CStringList& tokens, const CString& src);
+ static bool isNameInList(const CStringList& tokens,
+ const CString& src);
+
+ void init(HWND hwnd);
+ bool save(HWND hwnd);
+
+ // message handling
+ BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM);
+ static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
+
+private:
+ static CAddScreen* s_singleton;
+
+ HWND m_parent;
+ CConfig* m_config;
+ CString m_name;
+};
+
+#endif
diff --git a/cmd/launcher/CAdvancedOptions.cpp b/cmd/launcher/CAdvancedOptions.cpp
new file mode 100644
index 00000000..c1ea83ef
--- /dev/null
+++ b/cmd/launcher/CAdvancedOptions.cpp
@@ -0,0 +1,269 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CConfig.h"
+#include "ProtocolTypes.h"
+#include "CStringUtil.h"
+#include "CArch.h"
+#include "CArchMiscWindows.h"
+#include "CAdvancedOptions.h"
+#include "LaunchUtil.h"
+#include "XArch.h"
+#include "resource.h"
+
+//
+// CAdvancedOptions
+//
+
+CAdvancedOptions* CAdvancedOptions::s_singleton = NULL;
+
+CAdvancedOptions::CAdvancedOptions(HWND parent, CConfig* config) :
+ m_parent(parent),
+ m_config(config),
+ m_isClient(false),
+ m_screenName(ARCH->getHostName()),
+ m_port(kDefaultPort),
+ m_interface()
+{
+ assert(s_singleton == NULL);
+ s_singleton = this;
+ init();
+}
+
+CAdvancedOptions::~CAdvancedOptions()
+{
+ s_singleton = NULL;
+}
+
+void
+CAdvancedOptions::doModal(bool isClient)
+{
+ // save state
+ m_isClient = isClient;
+
+ // do dialog
+ DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADVANCED_OPTIONS),
+ m_parent, (DLGPROC)dlgProc, (LPARAM)this);
+}
+
+CString
+CAdvancedOptions::getScreenName() const
+{
+ return m_screenName;
+}
+
+int
+CAdvancedOptions::getPort() const
+{
+ return m_port;
+}
+
+CString
+CAdvancedOptions::getInterface() const
+{
+ return m_interface;
+}
+
+CString
+CAdvancedOptions::getCommandLine(bool isClient, const CString& serverName) const
+{
+ CString cmdLine;
+
+ // screen name
+ if (!m_screenName.empty()) {
+ cmdLine += " --name ";
+ cmdLine += m_screenName;
+ }
+
+ // port
+ char portString[20];
+ sprintf(portString, "%d", m_port);
+ if (isClient) {
+ cmdLine += " ";
+ cmdLine += serverName;
+ cmdLine += ":";
+ cmdLine += portString;
+ }
+ else {
+ cmdLine += " --address ";
+ if (!m_interface.empty()) {
+ cmdLine += m_interface;
+ }
+ cmdLine += ":";
+ cmdLine += portString;
+ }
+
+ return cmdLine;
+}
+
+void
+CAdvancedOptions::init()
+{
+ // get values from registry
+ HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath());
+ if (key != NULL) {
+ DWORD newPort = CArchMiscWindows::readValueInt(key, "port");
+ CString newName = CArchMiscWindows::readValueString(key, "name");
+ CString newInterface =
+ CArchMiscWindows::readValueString(key, "interface");
+ if (newPort != 0) {
+ m_port = static_cast(newPort);
+ }
+ if (!newName.empty()) {
+ m_screenName = newName;
+ }
+ if (!newInterface.empty()) {
+ m_interface = newInterface;
+ }
+ CArchMiscWindows::closeKey(key);
+ }
+}
+
+void
+CAdvancedOptions::doInit(HWND hwnd)
+{
+ // set values in GUI
+ HWND child;
+ char buffer[20];
+ sprintf(buffer, "%d", m_port);
+ child = getItem(hwnd, IDC_ADVANCED_PORT_EDIT);
+ SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer);
+
+ child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT);
+ SendMessage(child, WM_SETTEXT, 0, (LPARAM)m_screenName.c_str());
+
+ child = getItem(hwnd, IDC_ADVANCED_INTERFACE_EDIT);
+ SendMessage(child, WM_SETTEXT, 0, (LPARAM)m_interface.c_str());
+}
+
+bool
+CAdvancedOptions::save(HWND hwnd)
+{
+ SetCursor(LoadCursor(NULL, IDC_WAIT));
+
+ HWND child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT);
+ CString name = getWindowText(child);
+ if (!m_config->isValidScreenName(name)) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_INVALID_SCREEN_NAME).c_str(),
+ name.c_str()));
+ SetFocus(child);
+ return false;
+ }
+ if (!m_isClient && !m_config->isScreen(name)) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_UNKNOWN_SCREEN_NAME).c_str(),
+ name.c_str()));
+ SetFocus(child);
+ return false;
+ }
+
+ child = getItem(hwnd, IDC_ADVANCED_INTERFACE_EDIT);
+ CString iface = getWindowText(child);
+ if (!m_isClient) {
+ try {
+ if (!iface.empty()) {
+ ARCH->nameToAddr(iface);
+ }
+ }
+ catch (XArchNetworkName& e) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_INVALID_INTERFACE_NAME).c_str(),
+ iface.c_str(), e.what().c_str()));
+ SetFocus(child);
+ return false;
+ }
+ }
+
+ // get and verify port
+ child = getItem(hwnd, IDC_ADVANCED_PORT_EDIT);
+ CString portString = getWindowText(child);
+ int port = atoi(portString.c_str());
+ if (port < 1 || port > 65535) {
+ CString defaultPortString = CStringUtil::print("%d", kDefaultPort);
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_INVALID_PORT).c_str(),
+ portString.c_str(),
+ defaultPortString.c_str()));
+ SetFocus(child);
+ return false;
+ }
+
+ // save state
+ m_screenName = name;
+ m_port = port;
+ m_interface = iface;
+
+ // save values to registry
+ HKEY key = CArchMiscWindows::addKey(HKEY_CURRENT_USER, getSettingsPath());
+ if (key != NULL) {
+ CArchMiscWindows::setValue(key, "port", m_port);
+ CArchMiscWindows::setValue(key, "name", m_screenName);
+ CArchMiscWindows::setValue(key, "interface", m_interface);
+ CArchMiscWindows::closeKey(key);
+ }
+
+ return true;
+}
+
+void
+CAdvancedOptions::setDefaults(HWND hwnd)
+{
+ // restore defaults
+ m_screenName = ARCH->getHostName();
+ m_port = kDefaultPort;
+ m_interface = "";
+
+ // update GUI
+ doInit(hwnd);
+}
+
+BOOL
+CAdvancedOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
+{
+ switch (message) {
+ case WM_INITDIALOG:
+ doInit(hwnd);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ if (save(hwnd)) {
+ EndDialog(hwnd, 0);
+ }
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwnd, 0);
+ return TRUE;
+
+ case IDC_ADVANCED_DEFAULTS:
+ setDefaults(hwnd);
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+BOOL CALLBACK
+CAdvancedOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
+}
diff --git a/cmd/launcher/CAdvancedOptions.h b/cmd/launcher/CAdvancedOptions.h
new file mode 100644
index 00000000..1dd9dc44
--- /dev/null
+++ b/cmd/launcher/CAdvancedOptions.h
@@ -0,0 +1,80 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CADVANCEDOPTIONS_H
+#define CADVANCEDOPTIONS_H
+
+#include "CString.h"
+
+#define WINDOWS_LEAN_AND_MEAN
+#include
+
+class CConfig;
+
+//! Advanced options dialog for Microsoft Windows launcher
+class CAdvancedOptions {
+public:
+ CAdvancedOptions(HWND parent, CConfig*);
+ ~CAdvancedOptions();
+
+ //! @name manipulators
+ //@{
+
+ //! Run dialog
+ /*!
+ Display and handle the dialog until closed by the user.
+ */
+ void doModal(bool isClient);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get the screen name
+ CString getScreenName() const;
+
+ //! Get the port
+ int getPort() const;
+
+ //! Get the interface
+ CString getInterface() const;
+
+ //! Convert options to command line string
+ CString getCommandLine(bool isClient,
+ const CString& serverName) const;
+
+ //@}
+
+private:
+ void init();
+ void doInit(HWND hwnd);
+ bool save(HWND hwnd);
+ void setDefaults(HWND hwnd);
+
+ // message handling
+ BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM);
+ static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
+
+private:
+ static CAdvancedOptions* s_singleton;
+
+ HWND m_parent;
+ CConfig* m_config;
+ bool m_isClient;
+ CString m_screenName;
+ int m_port;
+ CString m_interface;
+};
+
+#endif
diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp
new file mode 100644
index 00000000..eb18a6b0
--- /dev/null
+++ b/cmd/launcher/CAutoStart.cpp
@@ -0,0 +1,361 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CLog.h"
+#include "ILogOutputter.h"
+#include "CArch.h"
+#include "CStringUtil.h"
+#include "XArch.h"
+#include "CAutoStart.h"
+#include "LaunchUtil.h"
+#include "resource.h"
+
+static const char* CLIENT_DAEMON_NAME = "Synergy Client";
+static const char* SERVER_DAEMON_NAME = "Synergy Server";
+static const char* CLIENT_DAEMON_INFO = "Uses a shared mouse and keyboard.";
+static const char* SERVER_DAEMON_INFO = "Shares this system's mouse and keyboard with others.";
+
+//
+// CAutoStartOutputter
+//
+// This class detects a message above a certain level and saves it
+//
+
+class CAutoStartOutputter : public ILogOutputter {
+public:
+ CAutoStartOutputter(CString* msg) : m_msg(msg) { }
+ virtual ~CAutoStartOutputter() { }
+
+ // ILogOutputter overrides
+ virtual void open(const char*) { }
+ virtual void close() { }
+ virtual void show(bool) { }
+ virtual bool write(ELevel level, const char* message);
+ virtual const char* getNewline() const { return ""; }
+
+private:
+ CString* m_msg;
+};
+
+bool
+CAutoStartOutputter::write(ELevel level, const char* message)
+{
+ if (level <= CLog::kERROR) {
+ *m_msg = message;
+ }
+ return false;
+}
+
+
+//
+// CAutoStart
+//
+
+CAutoStart* CAutoStart::s_singleton = NULL;
+
+CAutoStart::CAutoStart(HWND parent, bool isServer, const CString& cmdLine) :
+ m_parent(parent),
+ m_isServer(isServer),
+ m_cmdLine(cmdLine),
+ m_name(isServer ? SERVER_DAEMON_NAME : CLIENT_DAEMON_NAME)
+{
+ assert(s_singleton == NULL);
+ s_singleton = this;
+}
+
+CAutoStart::~CAutoStart()
+{
+ s_singleton = NULL;
+}
+
+void
+CAutoStart::doModal()
+{
+ // install our log outputter
+ CLOG->insert(new CAutoStartOutputter(&m_errorMessage));
+
+ // do dialog
+ DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_AUTOSTART),
+ m_parent, (DLGPROC)dlgProc, (LPARAM)this);
+
+ // remove log outputter
+ CLOG->pop_front();
+}
+
+void
+CAutoStart::reinstallDaemon(bool isClient, const CString& cmdLine)
+{
+ // get installation state
+ const char* name = (isClient ? CLIENT_DAEMON_NAME : SERVER_DAEMON_NAME);
+ bool installedSystem = ARCH->isDaemonInstalled(name, true);
+ bool installedUser = ARCH->isDaemonInstalled(name, false);
+
+ // reinstall if anything is installed
+ if (installedSystem || installedUser) {
+ ARCH->installDaemon(name,
+ isClient ? CLIENT_DAEMON_INFO : SERVER_DAEMON_INFO,
+ getAppPath(isClient ? CLIENT_APP : SERVER_APP).c_str(),
+ cmdLine.c_str(),
+ NULL,
+ installedSystem);
+ }
+}
+
+void
+CAutoStart::uninstallDaemons(bool client)
+{
+ if (client) {
+ try {
+ ARCH->uninstallDaemon(CLIENT_DAEMON_NAME, true);
+ }
+ catch (...) {
+ }
+ try {
+ ARCH->uninstallDaemon(CLIENT_DAEMON_NAME, false);
+ }
+ catch (...) {
+ }
+ }
+ else {
+ try {
+ ARCH->uninstallDaemon(SERVER_DAEMON_NAME, true);
+ }
+ catch (...) {
+ }
+ try {
+ ARCH->uninstallDaemon(SERVER_DAEMON_NAME, false);
+ }
+ catch (...) {
+ }
+ }
+}
+
+bool
+CAutoStart::startDaemon()
+{
+ const char* name = NULL;
+ if (ARCH->isDaemonInstalled(CLIENT_DAEMON_NAME, true)) {
+ name = CLIENT_DAEMON_NAME;
+ }
+ else if (ARCH->isDaemonInstalled(SERVER_DAEMON_NAME, true)) {
+ name = SERVER_DAEMON_NAME;
+ }
+ if (name == NULL) {
+ return false;
+ }
+
+ // 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, SERVICE_START);
+ if (service == NULL) {
+ CloseServiceHandle(mgr);
+ return false;
+ }
+
+ // start the service
+ BOOL okay = StartService(service, 0, NULL);
+
+ // clean up
+ CloseServiceHandle(service);
+ CloseServiceHandle(mgr);
+
+ return (okay != 0);
+}
+
+bool
+CAutoStart::isDaemonInstalled()
+{
+ return (ARCH->isDaemonInstalled(CLIENT_DAEMON_NAME, false) ||
+ ARCH->isDaemonInstalled(CLIENT_DAEMON_NAME, true) ||
+ ARCH->isDaemonInstalled(SERVER_DAEMON_NAME, false) ||
+ ARCH->isDaemonInstalled(SERVER_DAEMON_NAME, true));
+}
+
+void
+CAutoStart::update()
+{
+ // get installation state
+ const bool installedSystem = ARCH->isDaemonInstalled(
+ m_name.c_str(), true);
+ const bool installedUser = ARCH->isDaemonInstalled(
+ m_name.c_str(), false);
+
+ // get user's permissions
+ const bool canInstallSystem = ARCH->canInstallDaemon(
+ m_name.c_str(), true);
+ const bool canInstallUser = ARCH->canInstallDaemon(
+ m_name.c_str(), false);
+
+ // update messages
+ CString msg, label;
+ if (canInstallSystem) {
+ if (canInstallUser) {
+ msg = getString(IDS_AUTOSTART_PERMISSION_ALL);
+ }
+ else {
+ msg = getString(IDS_AUTOSTART_PERMISSION_SYSTEM);
+ }
+ }
+ else if (canInstallUser) {
+ msg = getString(IDS_AUTOSTART_PERMISSION_USER);
+ }
+ else {
+ msg = getString(IDS_AUTOSTART_PERMISSION_NONE);
+ }
+ setWindowText(getItem(m_hwnd, IDC_AUTOSTART_PERMISSION_MSG), msg);
+ if (installedSystem) {
+ msg = getString(IDS_AUTOSTART_INSTALLED_SYSTEM);
+ label = getString(IDS_UNINSTALL_LABEL);
+ }
+ else if (installedUser) {
+ msg = getString(IDS_AUTOSTART_INSTALLED_USER);
+ label = getString(IDS_UNINSTALL_LABEL);
+ }
+ else {
+ msg = getString(IDS_AUTOSTART_INSTALLED_NONE);
+ label = getString(IDS_INSTALL_LABEL);
+ }
+ setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALLED_MSG), msg);
+
+ // update buttons
+ setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM), label);
+ setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER), label);
+ if (installedSystem) {
+ enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, canInstallSystem);
+ enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, false);
+ m_install = false;
+ }
+ else if (installedUser) {
+ enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, false);
+ enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, canInstallUser);
+ m_install = false;
+ }
+ else {
+ enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, canInstallSystem);
+ enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, canInstallUser);
+ m_install = true;
+ }
+}
+
+bool
+CAutoStart::onInstall(bool allUsers)
+{
+ if (!m_install) {
+ return onUninstall(allUsers);
+ }
+
+ // get the app path
+ CString appPath = getAppPath(m_isServer ? SERVER_APP : CLIENT_APP);
+
+ // clear error message
+ m_errorMessage = "";
+
+ // install
+ try {
+ ARCH->installDaemon(m_name.c_str(),
+ m_isServer ? SERVER_DAEMON_INFO : CLIENT_DAEMON_INFO,
+ appPath.c_str(), m_cmdLine.c_str(),
+ NULL, allUsers);
+ askOkay(m_hwnd, getString(IDS_INSTALL_TITLE),
+ getString(allUsers ?
+ IDS_INSTALLED_SYSTEM :
+ IDS_INSTALLED_USER));
+ return true;
+ }
+ catch (XArchDaemon& e) {
+ if (m_errorMessage.empty()) {
+ m_errorMessage = CStringUtil::format(
+ getString(IDS_INSTALL_GENERIC_ERROR).c_str(),
+ e.what().c_str());
+ }
+ showError(m_hwnd, m_errorMessage);
+ return false;
+ }
+}
+
+bool
+CAutoStart::onUninstall(bool allUsers)
+{
+ // clear error message
+ m_errorMessage = "";
+
+ // uninstall
+ try {
+ ARCH->uninstallDaemon(m_name.c_str(), allUsers);
+ askOkay(m_hwnd, getString(IDS_UNINSTALL_TITLE),
+ getString(allUsers ?
+ IDS_UNINSTALLED_SYSTEM :
+ IDS_UNINSTALLED_USER));
+ return true;
+ }
+ catch (XArchDaemon& e) {
+ if (m_errorMessage.empty()) {
+ m_errorMessage = CStringUtil::format(
+ getString(IDS_UNINSTALL_GENERIC_ERROR).c_str(),
+ e.what().c_str());
+ }
+ showError(m_hwnd, m_errorMessage);
+ return false;
+ }
+}
+
+BOOL
+CAutoStart::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
+{
+ switch (message) {
+ case WM_INITDIALOG:
+ // save our hwnd
+ m_hwnd = hwnd;
+
+ // update the controls
+ update();
+
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_AUTOSTART_INSTALL_SYSTEM:
+ onInstall(true);
+ update();
+ return TRUE;
+
+ case IDC_AUTOSTART_INSTALL_USER:
+ onInstall(false);
+ update();
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwnd, 0);
+ m_hwnd = NULL;
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+BOOL CALLBACK
+CAutoStart::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
+}
diff --git a/cmd/launcher/CAutoStart.h b/cmd/launcher/CAutoStart.h
new file mode 100644
index 00000000..46aad100
--- /dev/null
+++ b/cmd/launcher/CAutoStart.h
@@ -0,0 +1,90 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAUTOSTART_H
+#define CAUTOSTART_H
+
+#include "CString.h"
+
+#define WINDOWS_LEAN_AND_MEAN
+#include
+
+//! Auto start dialog for Microsoft Windows launcher
+class CAutoStart {
+public:
+ CAutoStart(HWND parent, bool isServer, const CString& cmdLine);
+ ~CAutoStart();
+
+ //! @name manipulators
+ //@{
+
+ //! Run dialog
+ /*!
+ Display and handle the dialog until closed by the user.
+ */
+ void doModal();
+
+ //! Reinstall daemon
+ /*!
+ Reinstalls the currently installed daemon.
+ */
+ static void reinstallDaemon(bool isClient, const CString& cmdLine);
+
+ //! Uninstalls daemon
+ /*!
+ Uninstalls all installed client (\p client is \c true) or server daemons.
+ */
+ static void uninstallDaemons(bool client);
+
+ //! Starts an installed daemon
+ /*!
+ Returns \c true iff a daemon was started. This will only start daemons
+ installed for all users.
+ */
+ static bool startDaemon();
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Tests if any daemons are installed
+ /*!
+ Returns \c true if any daemons are installed.
+ */
+ static bool isDaemonInstalled();
+
+ //@}
+
+private:
+ void update();
+ bool onInstall(bool allUsers);
+ bool onUninstall(bool allUsers);
+
+ // message handling
+ BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM);
+ static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
+
+private:
+ static CAutoStart* s_singleton;
+
+ HWND m_parent;
+ bool m_isServer;
+ CString m_cmdLine;
+ CString m_name;
+ HWND m_hwnd;
+ bool m_install;
+ CString m_errorMessage;
+};
+
+#endif
diff --git a/cmd/launcher/CGlobalOptions.cpp b/cmd/launcher/CGlobalOptions.cpp
new file mode 100644
index 00000000..8237a07f
--- /dev/null
+++ b/cmd/launcher/CGlobalOptions.cpp
@@ -0,0 +1,281 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CConfig.h"
+#include "ProtocolTypes.h"
+#include "CStringUtil.h"
+#include "CArch.h"
+#include "CGlobalOptions.h"
+#include "LaunchUtil.h"
+#include "resource.h"
+
+static const int s_defaultDelay = 250;
+static const int s_defaultHeartbeat = 5000;
+
+//
+// CGlobalOptions
+//
+
+CGlobalOptions* CGlobalOptions::s_singleton = NULL;
+
+CGlobalOptions::CGlobalOptions(HWND parent, CConfig* config) :
+ m_parent(parent),
+ m_config(config),
+ m_delayTime(s_defaultDelay),
+ m_twoTapTime(s_defaultDelay),
+ m_heartbeatTime(s_defaultHeartbeat)
+{
+ assert(s_singleton == NULL);
+ s_singleton = this;
+}
+
+CGlobalOptions::~CGlobalOptions()
+{
+ s_singleton = NULL;
+}
+
+void
+CGlobalOptions::doModal()
+{
+ // do dialog
+ DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_GLOBAL_OPTIONS),
+ m_parent, (DLGPROC)dlgProc, (LPARAM)this);
+}
+
+void
+CGlobalOptions::init(HWND hwnd)
+{
+ HWND child;
+ char buffer[30];
+
+ // reset options
+ sprintf(buffer, "%d", m_delayTime);
+ child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK);
+ setItemChecked(child, false);
+ child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME);
+ setWindowText(child, buffer);
+ sprintf(buffer, "%d", m_twoTapTime);
+ child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK);
+ setItemChecked(child, false);
+ child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME);
+ setWindowText(child, buffer);
+ sprintf(buffer, "%d", m_heartbeatTime);
+ child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK);
+ setItemChecked(child, false);
+ child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME);
+ setWindowText(child, buffer);
+ child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC);
+ setItemChecked(child, true);
+ child = getItem(hwnd, IDC_GLOBAL_RELATIVE_MOVES);
+ setItemChecked(child, false);
+ child = getItem(hwnd, IDC_GLOBAL_LEAVE_FOREGROUND);
+ setItemChecked(child, false);
+
+ // get the global options
+ const CConfig::CScreenOptions* options = m_config->getOptions("");
+ if (options != NULL) {
+ for (CConfig::CScreenOptions::const_iterator index = options->begin();
+ index != options->end(); ++index) {
+ const OptionID id = index->first;
+ const OptionValue value = index->second;
+ if (id == kOptionScreenSwitchDelay) {
+ if (value > 0) {
+ sprintf(buffer, "%d", value);
+ child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK);
+ setItemChecked(child, true);
+ child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME);
+ setWindowText(child, buffer);
+ }
+ }
+ else if (id == kOptionScreenSwitchTwoTap) {
+ if (value > 0) {
+ sprintf(buffer, "%d", value);
+ child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK);
+ setItemChecked(child, true);
+ child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME);
+ setWindowText(child, buffer);
+ }
+ }
+ else if (id == kOptionHeartbeat) {
+ if (value > 0) {
+ sprintf(buffer, "%d", value);
+ child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK);
+ setItemChecked(child, true);
+ child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME);
+ setWindowText(child, buffer);
+ }
+ }
+ else if (id == kOptionScreenSaverSync) {
+ child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC);
+ setItemChecked(child, (value != 0));
+ }
+ else if (id == kOptionRelativeMouseMoves) {
+ child = getItem(hwnd, IDC_GLOBAL_RELATIVE_MOVES);
+ setItemChecked(child, (value != 0));
+ }
+ else if (id == kOptionWin32KeepForeground) {
+ child = getItem(hwnd, IDC_GLOBAL_LEAVE_FOREGROUND);
+ setItemChecked(child, (value != 0));
+ }
+ }
+ }
+}
+
+bool
+CGlobalOptions::save(HWND hwnd)
+{
+ HWND child;
+ int newDelayTime = 0;
+ int newTwoTapTime = 0;
+ int newHeartbeatTime = 0;
+
+ // get requested options
+ child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK);
+ if (isItemChecked(child)) {
+ child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME);
+ newDelayTime = getTime(hwnd, child, true);
+ if (newDelayTime == 0) {
+ return false;
+ }
+ }
+ else {
+ child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME);
+ newDelayTime = getTime(hwnd, child, false);
+ if (newDelayTime == 0) {
+ newDelayTime = s_defaultDelay;
+ }
+ }
+ child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK);
+ if (isItemChecked(child)) {
+ child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME);
+ newTwoTapTime = getTime(hwnd, child, true);
+ if (newTwoTapTime == 0) {
+ return false;
+ }
+ }
+ else {
+ child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME);
+ newTwoTapTime = getTime(hwnd, child, false);
+ if (newTwoTapTime == 0) {
+ newTwoTapTime = s_defaultDelay;
+ }
+ }
+ child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK);
+ if (isItemChecked(child)) {
+ child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME);
+ newHeartbeatTime = getTime(hwnd, child, true);
+ if (newHeartbeatTime == 0) {
+ return false;
+ }
+ }
+ else {
+ child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME);
+ newHeartbeatTime = getTime(hwnd, child, false);
+ if (newHeartbeatTime == 0) {
+ newHeartbeatTime = s_defaultHeartbeat;
+ }
+ }
+
+ // remove existing config options
+ m_config->removeOption("", kOptionScreenSwitchDelay);
+ m_config->removeOption("", kOptionScreenSwitchTwoTap);
+ m_config->removeOption("", kOptionHeartbeat);
+ m_config->removeOption("", kOptionScreenSaverSync);
+ m_config->removeOption("", kOptionRelativeMouseMoves);
+ m_config->removeOption("", kOptionWin32KeepForeground);
+
+ // add requested options
+ child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK);
+ if (isItemChecked(child)) {
+ m_config->addOption("", kOptionScreenSwitchDelay, newDelayTime);
+ }
+ child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK);
+ if (isItemChecked(child)) {
+ m_config->addOption("", kOptionScreenSwitchTwoTap, newTwoTapTime);
+ }
+ child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK);
+ if (isItemChecked(child)) {
+ m_config->addOption("", kOptionHeartbeat, newHeartbeatTime);
+ }
+ child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC);
+ if (!isItemChecked(child)) {
+ m_config->addOption("", kOptionScreenSaverSync, 0);
+ }
+ child = getItem(hwnd, IDC_GLOBAL_RELATIVE_MOVES);
+ if (isItemChecked(child)) {
+ m_config->addOption("", kOptionRelativeMouseMoves, 1);
+ }
+ child = getItem(hwnd, IDC_GLOBAL_LEAVE_FOREGROUND);
+ if (isItemChecked(child)) {
+ m_config->addOption("", kOptionWin32KeepForeground, 1);
+ }
+
+ // save last values
+ m_delayTime = newDelayTime;
+ m_twoTapTime = newTwoTapTime;
+ m_heartbeatTime = newHeartbeatTime;
+ return true;
+}
+
+int
+CGlobalOptions::getTime(HWND hwnd, HWND child, bool reportError)
+{
+ CString valueString = getWindowText(child);
+ int value = atoi(valueString.c_str());
+ if (value < 1) {
+ if (reportError) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_INVALID_TIME).c_str(),
+ valueString.c_str()));
+ SetFocus(child);
+ }
+ return 0;
+ }
+ return value;
+}
+
+BOOL
+CGlobalOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
+{
+ switch (message) {
+ case WM_INITDIALOG:
+ init(hwnd);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ if (save(hwnd)) {
+ EndDialog(hwnd, 0);
+ }
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwnd, 0);
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+BOOL CALLBACK
+CGlobalOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
+}
diff --git a/cmd/launcher/CGlobalOptions.h b/cmd/launcher/CGlobalOptions.h
new file mode 100644
index 00000000..f04f1bae
--- /dev/null
+++ b/cmd/launcher/CGlobalOptions.h
@@ -0,0 +1,67 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CGLOBALOPTIONS_H
+#define CGLOBALOPTIONS_H
+
+#include "CString.h"
+
+#define WINDOWS_LEAN_AND_MEAN
+#include
+
+class CConfig;
+
+//! Global options dialog for Microsoft Windows launcher
+class CGlobalOptions {
+public:
+ CGlobalOptions(HWND parent, CConfig*);
+ ~CGlobalOptions();
+
+ //! @name manipulators
+ //@{
+
+ //! Run dialog
+ /*!
+ Display and handle the dialog until closed by the user.
+ */
+ void doModal();
+
+ //@}
+ //! @name accessors
+ //@{
+
+
+ //@}
+
+private:
+ void init(HWND hwnd);
+ bool save(HWND hwnd);
+
+ int getTime(HWND hwnd, HWND child, bool reportError);
+
+ // message handling
+ BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM);
+ static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
+
+private:
+ static CGlobalOptions* s_singleton;
+
+ HWND m_parent;
+ CConfig* m_config;
+ int m_delayTime;
+ int m_twoTapTime;
+ int m_heartbeatTime;
+};
+
+#endif
diff --git a/cmd/launcher/CHotkeyOptions.cpp b/cmd/launcher/CHotkeyOptions.cpp
new file mode 100644
index 00000000..5aa981e0
--- /dev/null
+++ b/cmd/launcher/CHotkeyOptions.cpp
@@ -0,0 +1,1938 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2006 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchMiscWindows.h"
+#include "CMSWindowsKeyState.h"
+#include "CConfig.h"
+#include "CHotkeyOptions.h"
+#include "CStringUtil.h"
+#include "LaunchUtil.h"
+#include "resource.h"
+
+#if !defined(WM_XBUTTONDOWN)
+#define WM_XBUTTONDOWN 0x020B
+#define WM_XBUTTONUP 0x020C
+#define WM_XBUTTONDBLCLK 0x020D
+#define XBUTTON1 0x0001
+#define XBUTTON2 0x0002
+#endif
+
+//
+// CAdvancedOptions
+//
+
+CHotkeyOptions* CHotkeyOptions::s_singleton = NULL;
+
+CHotkeyOptions::CHotkeyOptions(HWND parent, CConfig* config) :
+ m_parent(parent),
+ m_config(config)
+{
+ assert(s_singleton == NULL);
+ s_singleton = this;
+}
+
+CHotkeyOptions::~CHotkeyOptions()
+{
+ s_singleton = NULL;
+}
+
+void
+CHotkeyOptions::doModal()
+{
+ // do dialog
+ m_inputFilter = m_config->getInputFilter();
+ DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_OPTIONS),
+ m_parent, (DLGPROC)dlgProc, (LPARAM)this);
+}
+
+void
+CHotkeyOptions::doInit(HWND hwnd)
+{
+ m_activeRuleIndex = (UInt32)-1;
+ fillHotkeys(hwnd);
+ openRule(hwnd);
+}
+
+void
+CHotkeyOptions::fillHotkeys(HWND hwnd, UInt32 select)
+{
+ HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
+
+ SendMessage(rules, LB_RESETCONTENT, 0, 0);
+ for (UInt32 i = 0, n = m_inputFilter->getNumRules(); i < n; ++i) {
+ CInputFilter::CRule& rule = m_inputFilter->getRule(i);
+ SendMessage(rules, LB_INSERTSTRING, (WPARAM)-1,
+ (LPARAM)rule.getCondition()->format().c_str());
+ }
+
+ if (select < m_inputFilter->getNumRules()) {
+ SendMessage(rules, LB_SETCURSEL, select, 0);
+ }
+
+ updateHotkeysControls(hwnd);
+}
+
+void
+CHotkeyOptions::updateHotkeysControls(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
+ bool selected = (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR);
+
+ enableItem(hwnd, IDC_HOTKEY_ADD_HOTKEY, TRUE);
+ enableItem(hwnd, IDC_HOTKEY_EDIT_HOTKEY, selected);
+ enableItem(hwnd, IDC_HOTKEY_REMOVE_HOTKEY, selected);
+}
+
+void
+CHotkeyOptions::addHotkey(HWND hwnd)
+{
+ closeRule(hwnd);
+ CInputFilter::CCondition* condition = NULL;
+ if (editCondition(hwnd, condition)) {
+ m_inputFilter->addFilterRule(CInputFilter::CRule(condition));
+ fillHotkeys(hwnd, m_inputFilter->getNumRules() - 1);
+ }
+ else {
+ delete condition;
+ }
+ openRule(hwnd);
+}
+
+void
+CHotkeyOptions::removeHotkey(HWND hwnd)
+{
+ UInt32 ruleIndex = m_activeRuleIndex;
+ closeRule(hwnd);
+
+ m_inputFilter->removeFilterRule(ruleIndex);
+ UInt32 n = m_inputFilter->getNumRules();
+ if (n > 0 && ruleIndex >= n) {
+ ruleIndex = n - 1;
+ }
+ fillHotkeys(hwnd, ruleIndex);
+
+ openRule(hwnd);
+}
+
+void
+CHotkeyOptions::editHotkey(HWND hwnd)
+{
+ // save selected item in action list
+ HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS);
+ LRESULT aIndex = SendMessage(actions, LB_GETCURSEL, 0, 0);
+
+ UInt32 index = m_activeRuleIndex;
+ closeRule(hwnd);
+
+ CInputFilter::CRule& rule = m_inputFilter->getRule(index);
+ CInputFilter::CCondition* condition = rule.getCondition()->clone();
+ if (editCondition(hwnd, condition)) {
+ rule.setCondition(condition);
+ fillHotkeys(hwnd, index);
+ }
+ else {
+ delete condition;
+ }
+
+ openRule(hwnd);
+
+ // restore selected item in action list
+ if (aIndex != LB_ERR) {
+ SendMessage(actions, LB_SETCURSEL, aIndex, 0);
+ }
+}
+
+void
+CHotkeyOptions::fillActions(HWND hwnd, UInt32 select)
+{
+ HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS);
+ SendMessage(actions, LB_RESETCONTENT, 0, 0);
+ if (m_activeRuleIndex != (UInt32)-1) {
+ UInt32 n = m_activeRule.getNumActions(true);
+ UInt32 n2 = m_activeRule.getNumActions(false);
+ for (UInt32 i = 0; i < n; ++i) {
+ const CInputFilter::CAction& action =
+ m_activeRule.getAction(true, i);
+ CString line("A ");
+ line += action.format();
+ SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1,
+ (LPARAM)line.c_str());
+ SendMessage(actions, LB_SETITEMDATA, (WPARAM)i, (LPARAM)i);
+ }
+ for (UInt32 i = 0; i < n2; ++i) {
+ const CInputFilter::CAction& action =
+ m_activeRule.getAction(false, i);
+ CString line("D ");
+ line += action.format();
+ SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1,
+ (LPARAM)line.c_str());
+ SendMessage(actions, LB_SETITEMDATA, (WPARAM)i + n,
+ (LPARAM)(i | 0x80000000u));
+ }
+
+ if (select < n + n2) {
+ SendMessage(actions, LB_SETCURSEL, select, 0);
+ }
+ }
+
+ updateActionsControls(hwnd);
+}
+
+void
+CHotkeyOptions::updateActionsControls(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
+ bool active = (m_activeRuleIndex != (UInt32)-1);
+
+ child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
+ bool selected =
+ (active && (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR));
+
+ enableItem(hwnd, IDC_HOTKEY_ADD_ACTION, active);
+ enableItem(hwnd, IDC_HOTKEY_EDIT_ACTION, selected);
+ enableItem(hwnd, IDC_HOTKEY_REMOVE_ACTION, selected);
+}
+
+void
+CHotkeyOptions::addAction(HWND hwnd)
+{
+ CInputFilter::CAction* action = NULL;
+ bool onActivate = true;
+ if (editAction(hwnd, action, onActivate)) {
+ m_activeRule.adoptAction(action, onActivate);
+
+ UInt32 actionIndex = m_activeRule.getNumActions(true) - 1;
+ if (!onActivate) {
+ actionIndex += m_activeRule.getNumActions(false);
+ }
+ fillActions(hwnd, actionIndex);
+ }
+ else {
+ delete action;
+ }
+}
+
+void
+CHotkeyOptions::removeAction(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
+ LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0);
+ if (index != LB_ERR) {
+ UInt32 actionIndex =
+ (UInt32)SendMessage(child, LB_GETITEMDATA, index, 0);
+ bool onActivate = ((actionIndex & 0x80000000u) == 0);
+ actionIndex &= ~0x80000000u;
+
+ m_activeRule.removeAction(onActivate, actionIndex);
+
+ actionIndex = static_cast(index);
+ UInt32 n = m_activeRule.getNumActions(true) +
+ m_activeRule.getNumActions(false);
+ if (n > 0 && actionIndex >= n) {
+ actionIndex = n - 1;
+ }
+ fillActions(hwnd, actionIndex);
+ }
+}
+
+void
+CHotkeyOptions::editAction(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
+ LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0);
+ if (index != LB_ERR) {
+ UInt32 actionIndex =
+ (UInt32)SendMessage(child, LB_GETITEMDATA, index, 0);
+ bool onActivate = ((actionIndex & 0x80000000u) == 0);
+ actionIndex &= ~0x80000000u;
+
+ CInputFilter::CAction* action =
+ m_activeRule.getAction(onActivate, actionIndex).clone();
+ bool newOnActivate = onActivate;
+ if (editAction(hwnd, action, newOnActivate)) {
+ if (onActivate == newOnActivate) {
+ m_activeRule.replaceAction(action, onActivate, actionIndex);
+ actionIndex = static_cast(index);
+ }
+ else {
+ m_activeRule.removeAction(onActivate, actionIndex);
+ m_activeRule.adoptAction(action, newOnActivate);
+ actionIndex = m_activeRule.getNumActions(true) - 1;
+ if (!newOnActivate) {
+ actionIndex += m_activeRule.getNumActions(false);
+ }
+ }
+ fillActions(hwnd, actionIndex);
+ }
+ else {
+ delete action;
+ }
+ }
+}
+
+bool
+CHotkeyOptions::editCondition(HWND hwnd, CInputFilter::CCondition*& condition)
+{
+ return CConditionDialog::doModal(hwnd, condition);
+}
+
+bool
+CHotkeyOptions::editAction(HWND hwnd, CInputFilter::CAction*& action,
+ bool& onActivate)
+{
+ return CActionDialog::doModal(hwnd, m_config, action, onActivate);
+}
+
+void
+CHotkeyOptions::openRule(HWND hwnd)
+{
+ // get the active rule and copy it, merging down/up pairs of keystroke
+ // and mouse button actions into single actions for the convenience of
+ // of the user.
+ HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
+ LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0);
+ if (index != LB_ERR) {
+ // copy the rule as is
+ m_activeRuleIndex = (SInt32)index;
+ m_activeRule = m_inputFilter->getRule(m_activeRuleIndex);
+
+ // look for actions to combine
+ for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) {
+ // get next activate action
+ const CInputFilter::CAction* action =
+ &m_activeRule.getAction(true, i);
+
+ // check if it's a key or mouse action
+ const CInputFilter::CKeystrokeAction* keyAction =
+ dynamic_cast(action);
+ const CInputFilter::CMouseButtonAction* mouseAction =
+ dynamic_cast(action);
+ if (keyAction == NULL && mouseAction == NULL) {
+ continue;
+ }
+
+ // check for matching deactivate action
+ UInt32 j = (UInt32)-1;
+ CInputFilter::CAction* newAction = NULL;
+ if (keyAction != NULL) {
+ j = findMatchingAction(keyAction);
+ if (j != (UInt32)-1) {
+ // found a match
+ const IPlatformScreen::CKeyInfo* oldInfo =
+ keyAction->getInfo();
+ IPlatformScreen::CKeyInfo* newInfo =
+ IKeyState::CKeyInfo::alloc(*oldInfo);
+ newAction = new CKeystrokeDownUpAction(newInfo);
+ }
+ }
+ else if (mouseAction != NULL) {
+ j = findMatchingAction(mouseAction);
+ if (j != (UInt32)-1) {
+ // found a match
+ const IPlatformScreen::CButtonInfo* oldInfo =
+ mouseAction->getInfo();
+ IPlatformScreen::CButtonInfo* newInfo =
+ IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
+ newAction = new CMouseButtonDownUpAction(newInfo);
+ }
+ }
+
+ // perform merge
+ if (newAction != NULL) {
+ m_activeRule.replaceAction(newAction, true, i);
+ m_activeRule.removeAction(false, j);
+ }
+ }
+ }
+ else {
+ m_activeRuleIndex = (UInt32)-1;
+ }
+ fillActions(hwnd);
+}
+
+void
+CHotkeyOptions::closeRule(HWND)
+{
+ // copy rule back to input filter, expanding merged actions into the
+ // two component actions.
+ if (m_activeRuleIndex != (UInt32)-1) {
+ // expand merged rules
+ for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) {
+ // get action
+ const CInputFilter::CAction* action =
+ &m_activeRule.getAction(true, i);
+
+ // check if it's a merged key or mouse action
+ const CKeystrokeDownUpAction* keyAction =
+ dynamic_cast(action);
+ const CMouseButtonDownUpAction* mouseAction =
+ dynamic_cast(action);
+ if (keyAction == NULL && mouseAction == NULL) {
+ continue;
+ }
+
+ // expand
+ if (keyAction != NULL) {
+ const IPlatformScreen::CKeyInfo* oldInfo =
+ keyAction->getInfo();
+ IPlatformScreen::CKeyInfo* newInfo =
+ IKeyState::CKeyInfo::alloc(*oldInfo);
+ CInputFilter::CKeystrokeAction* downAction =
+ new CInputFilter::CKeystrokeAction(newInfo, true);
+ newInfo = IKeyState::CKeyInfo::alloc(*oldInfo);
+ CInputFilter::CKeystrokeAction* upAction =
+ new CInputFilter::CKeystrokeAction(newInfo, false);
+ m_activeRule.replaceAction(downAction, true, i);
+ m_activeRule.adoptAction(upAction, false);
+ }
+ else if (mouseAction != NULL) {
+ const IPlatformScreen::CButtonInfo* oldInfo =
+ mouseAction->getInfo();
+ IPlatformScreen::CButtonInfo* newInfo =
+ IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
+ CInputFilter::CMouseButtonAction* downAction =
+ new CInputFilter::CMouseButtonAction(newInfo, true);
+ newInfo = IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
+ CInputFilter::CMouseButtonAction* upAction =
+ new CInputFilter::CMouseButtonAction(newInfo, false);
+ m_activeRule.replaceAction(downAction, true, i);
+ m_activeRule.adoptAction(upAction, false);
+ }
+ }
+
+ // copy it back
+ m_inputFilter->getRule(m_activeRuleIndex) = m_activeRule;
+ }
+ m_activeRuleIndex = (UInt32)-1;
+}
+
+UInt32
+CHotkeyOptions::findMatchingAction(
+ const CInputFilter::CKeystrokeAction* src) const
+{
+ for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) {
+ const CInputFilter::CKeystrokeAction* dst =
+ dynamic_cast(
+ &m_activeRule.getAction(false, i));
+ if (dst != NULL &&
+ IKeyState::CKeyInfo::equal(src->getInfo(), dst->getInfo())) {
+ return i;
+ }
+ }
+ return (UInt32)-1;
+}
+
+UInt32
+CHotkeyOptions::findMatchingAction(
+ const CInputFilter::CMouseButtonAction* src) const
+{
+ for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) {
+ const CInputFilter::CMouseButtonAction* dst =
+ dynamic_cast(
+ &m_activeRule.getAction(false, i));
+ if (dst != NULL &&
+ IPrimaryScreen::CButtonInfo::equal(
+ src->getInfo(), dst->getInfo())) {
+ return i;
+ }
+ }
+ return (UInt32)-1;
+}
+
+BOOL
+CHotkeyOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
+{
+ switch (message) {
+ case WM_INITDIALOG:
+ doInit(hwnd);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL:
+ closeRule(hwnd);
+ EndDialog(hwnd, 0);
+ return TRUE;
+
+ case IDC_HOTKEY_HOTKEYS:
+ switch (HIWORD(wParam)) {
+ case LBN_DBLCLK:
+ editHotkey(hwnd);
+ return TRUE;
+
+ case LBN_SELCHANGE: {
+ HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
+ LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0);
+ if (m_activeRuleIndex != (UInt32)index) {
+ closeRule(hwnd);
+ updateHotkeysControls(hwnd);
+ openRule(hwnd);
+ }
+ return TRUE;
+ }
+ }
+ break;
+
+ case IDC_HOTKEY_ADD_HOTKEY:
+ addHotkey(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_REMOVE_HOTKEY:
+ removeHotkey(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_EDIT_HOTKEY:
+ editHotkey(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_ACTIONS:
+ switch (HIWORD(wParam)) {
+ case LBN_DBLCLK:
+ editAction(hwnd);
+ return TRUE;
+
+ case LBN_SELCHANGE:
+ updateActionsControls(hwnd);
+ return TRUE;
+ }
+ break;
+
+ case IDC_HOTKEY_ADD_ACTION:
+ addAction(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_REMOVE_ACTION:
+ removeAction(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_EDIT_ACTION:
+ editAction(hwnd);
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+BOOL CALLBACK
+CHotkeyOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
+}
+
+
+//
+// CHotkeyOptions::CConditionDialog
+//
+
+CInputFilter::CCondition*
+ CHotkeyOptions::CConditionDialog::s_condition = NULL;
+CInputFilter::CCondition*
+ CHotkeyOptions::CConditionDialog::s_lastGoodCondition = NULL;
+WNDPROC CHotkeyOptions::CConditionDialog::s_editWndProc = NULL;
+
+bool
+CHotkeyOptions::CConditionDialog::doModal(HWND parent,
+ CInputFilter::CCondition*& condition)
+{
+ s_condition = condition;
+ if (s_condition != NULL) {
+ s_lastGoodCondition = s_condition->clone();
+ }
+ else {
+ s_lastGoodCondition = NULL;
+ }
+ int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_CONDITION),
+ parent, dlgProc);
+
+ condition = s_condition;
+ delete s_lastGoodCondition;
+ s_condition = NULL;
+ s_lastGoodCondition = NULL;
+
+ // user effectively cancelled if the condition is NULL
+ if (condition == NULL) {
+ n = 0;
+ }
+
+ return (n == 1);
+}
+
+void
+CHotkeyOptions::CConditionDialog::doInit(HWND hwnd)
+{
+ // subclass edit control
+ HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY);
+ s_editWndProc = (WNDPROC)GetWindowLong(child, GWL_WNDPROC);
+ SetWindowLong(child, GWL_WNDPROC, (LONG)editProc);
+
+ // fill control
+ fillHotkey(hwnd);
+}
+
+void
+CHotkeyOptions::CConditionDialog::fillHotkey(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY);
+ if (s_condition != NULL) {
+ setWindowText(child, s_condition->format().c_str());
+ }
+ else {
+ setWindowText(child, "");
+ }
+}
+
+void
+CHotkeyOptions::CConditionDialog::onButton(HWND hwnd, ButtonID button)
+{
+ delete s_condition;
+ s_condition =
+ new CInputFilter::CMouseButtonCondition(button, getModifiers());
+
+ fillHotkey(GetParent(hwnd));
+}
+
+void
+CHotkeyOptions::CConditionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam)
+{
+ // ignore key repeats
+ if ((lParam & 0xc0000000u) == 0x40000000u) {
+ return;
+ }
+
+ // ignore key releases if the condition is complete and for the tab
+ // key (in case we were just tabbed to)
+ if ((lParam & 0x80000000u) != 0) {
+ if (isGoodCondition() || wParam == VK_TAB) {
+ return;
+ }
+ }
+
+ KeyID key = kKeyNone;
+ KeyModifierMask mask = getModifiers();
+ switch (wParam) {
+ case VK_SHIFT:
+ case VK_LSHIFT:
+ case VK_RSHIFT:
+ case VK_CONTROL:
+ case VK_LCONTROL:
+ case VK_RCONTROL:
+ case VK_MENU:
+ case VK_LMENU:
+ case VK_RMENU:
+ case VK_LWIN:
+ case VK_RWIN:
+ break;
+
+ case VK_TAB:
+ // allow tabbing out of control
+ if ((mask & (KeyModifierControl |
+ KeyModifierAlt | KeyModifierSuper)) == 0) {
+ HWND next = hwnd;
+ if ((mask & KeyModifierShift) == 0) {
+ do {
+ next = GetWindow(next, GW_HWNDNEXT);
+ if (next == NULL) {
+ next = GetWindow(hwnd, GW_HWNDFIRST);
+ }
+ } while (next != hwnd &&
+ (!IsWindowVisible(next) ||
+ (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
+ }
+ else {
+ do {
+ next = GetWindow(next, GW_HWNDPREV);
+ if (next == NULL) {
+ next = GetWindow(hwnd, GW_HWNDLAST);
+ }
+ } while (next != hwnd &&
+ (!IsWindowVisible(next) ||
+ (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
+ }
+ SetFocus(next);
+ return;
+ }
+ // fall through
+
+ default:
+ key = CMSWindowsKeyState::getKeyID(wParam,
+ static_cast((lParam & 0x1ff0000u) >> 16));
+ switch (key) {
+ case kKeyNone:
+ // could be a character
+ key = getChar(wParam, lParam);
+ if (key == kKeyNone) {
+ return;
+ }
+ break;
+
+ case kKeyShift_L:
+ case kKeyShift_R:
+ case kKeyControl_L:
+ case kKeyControl_R:
+ case kKeyAlt_L:
+ case kKeyAlt_R:
+ case kKeyMeta_L:
+ case kKeyMeta_R:
+ case kKeySuper_L:
+ case kKeySuper_R:
+ case kKeyCapsLock:
+ case kKeyNumLock:
+ case kKeyScrollLock:
+ // bogus
+ return;
+ }
+ break;
+ }
+
+ delete s_condition;
+ s_condition = new CInputFilter::CKeystrokeCondition(key, mask);
+
+ fillHotkey(GetParent(hwnd));
+}
+
+KeyID
+CHotkeyOptions::CConditionDialog::getChar(WPARAM wParam, LPARAM lParam)
+{
+ BYTE keyState[256];
+ UINT virtualKey = (UINT)wParam;
+ UINT scanCode = (UINT)((lParam & 0x0ff0000u) >> 16);
+ GetKeyboardState(keyState);
+
+ // reset modifier state
+ keyState[VK_SHIFT] = 0;
+ keyState[VK_LSHIFT] = 0;
+ keyState[VK_RSHIFT] = 0;
+ keyState[VK_CONTROL] = 0;
+ keyState[VK_LCONTROL] = 0;
+ keyState[VK_RCONTROL] = 0;
+ keyState[VK_MENU] = 0;
+ keyState[VK_LMENU] = 0;
+ keyState[VK_RMENU] = 0;
+ keyState[VK_LWIN] = 0;
+ keyState[VK_RWIN] = 0;
+
+ // translate virtual key to character
+ int n;
+ KeyID id;
+ if (CArchMiscWindows::isWindows95Family()) {
+ // XXX -- how do we get characters not in Latin-1?
+ WORD ascii;
+ n = ToAscii(virtualKey, scanCode, keyState, &ascii, 0);
+ id = static_cast(ascii & 0xffu);
+ }
+ else {
+ typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey,
+ UINT wScanCode,
+ PBYTE lpKeyState,
+ LPWSTR pwszBuff,
+ int cchBuff,
+ UINT wFlags);
+ ToUnicode_t s_ToUnicode = NULL;
+ if (s_ToUnicode == NULL) {
+ HMODULE userModule = GetModuleHandle("user32.dll");
+ s_ToUnicode =
+ (ToUnicode_t)GetProcAddress(userModule, "ToUnicode");
+ }
+
+ WCHAR unicode[2];
+ n = s_ToUnicode(virtualKey, scanCode, keyState,
+ unicode, sizeof(unicode) / sizeof(unicode[0]),
+ 0);
+ id = static_cast(unicode[0]);
+ }
+ switch (n) {
+ case -1:
+ // no hot keys on dead keys
+ return kKeyNone;
+
+ default:
+ case 0:
+ // unmapped
+ return kKeyNone;
+
+ case 1:
+ return id;
+ }
+}
+
+KeyModifierMask
+CHotkeyOptions::CConditionDialog::getModifiers()
+{
+ KeyModifierMask mask = 0;
+ if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) {
+ mask |= KeyModifierShift;
+ }
+ if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) {
+ mask |= KeyModifierControl;
+ }
+ if ((GetKeyState(VK_MENU) & 0x8000) != 0) {
+ mask |= KeyModifierAlt;
+ }
+ if ((GetKeyState(VK_LWIN) & 0x8000) != 0 ||
+ (GetKeyState(VK_RWIN) & 0x8000) != 0) {
+ mask |= KeyModifierSuper;
+ }
+ return mask;
+}
+
+bool
+CHotkeyOptions::CConditionDialog::isGoodCondition()
+{
+ CInputFilter::CKeystrokeCondition* keyCondition =
+ dynamic_cast(s_condition);
+ return (keyCondition == NULL || keyCondition->getKey() != kKeyNone);
+}
+
+BOOL CALLBACK
+CHotkeyOptions::CConditionDialog::dlgProc(HWND hwnd,
+ UINT message, WPARAM wParam, LPARAM)
+{
+ switch (message) {
+ case WM_INITDIALOG:
+ doInit(hwnd);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ EndDialog(hwnd, 1);
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwnd, 0);
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+LRESULT CALLBACK
+CHotkeyOptions::CConditionDialog::editProc(HWND hwnd,
+ UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_LBUTTONDOWN:
+ if (GetFocus() == hwnd) {
+ onButton(hwnd, kButtonLeft);
+ }
+ else {
+ SetFocus(hwnd);
+ }
+ return 0;
+
+ case WM_MBUTTONDOWN:
+ if (GetFocus() == hwnd) {
+ onButton(hwnd, kButtonMiddle);
+ }
+ return 0;
+
+ case WM_RBUTTONDOWN:
+ if (GetFocus() == hwnd) {
+ onButton(hwnd, kButtonRight);
+ }
+ return 0;
+
+ case WM_XBUTTONDOWN:
+ if (GetFocus() == hwnd) {
+ switch (HIWORD(wParam)) {
+ case XBUTTON1:
+ onButton(hwnd, kButtonExtra0 + 0);
+ break;
+
+ case XBUTTON2:
+ onButton(hwnd, kButtonExtra0 + 1);
+ break;
+ }
+ }
+ return 0;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ onKey(hwnd, wParam, lParam);
+ return 0;
+
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_XBUTTONUP:
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_DEADCHAR:
+ case WM_SYSDEADCHAR:
+ return 0;
+
+ case WM_SETFOCUS:
+ if (s_condition != NULL) {
+ delete s_lastGoodCondition;
+ s_lastGoodCondition = s_condition->clone();
+ }
+ break;
+
+ case WM_KILLFOCUS:
+ if (!isGoodCondition()) {
+ delete s_condition;
+ if (s_lastGoodCondition != NULL) {
+ s_condition = s_lastGoodCondition->clone();
+ }
+ else {
+ s_condition = NULL;
+ }
+ }
+ fillHotkey(GetParent(hwnd));
+ break;
+
+ case WM_GETDLGCODE:
+ return DLGC_WANTALLKEYS;
+
+ default:
+ break;
+ }
+ return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam);
+}
+
+
+//
+// CHotkeyOptions::CActionDialog
+//
+
+CConfig* CHotkeyOptions::CActionDialog::s_config = NULL;
+bool CHotkeyOptions::CActionDialog::s_onActivate = false;
+CInputFilter::CAction*
+ CHotkeyOptions::CActionDialog::s_action = NULL;
+CInputFilter::CAction*
+ CHotkeyOptions::CActionDialog::s_lastGoodAction = NULL;
+std::set
+ CHotkeyOptions::CActionDialog::s_screens;
+WNDPROC CHotkeyOptions::CActionDialog::s_editWndProc = NULL;
+
+bool
+CHotkeyOptions::CActionDialog::doModal(HWND parent, CConfig* config,
+ CInputFilter::CAction*& action, bool& onActivate)
+{
+ s_config = config;
+ s_onActivate = onActivate;
+ s_action = action;
+ if (s_action != NULL) {
+ s_lastGoodAction = s_action->clone();
+ }
+ else {
+ s_lastGoodAction = NULL;
+ }
+
+ int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_ACTION),
+ parent, dlgProc);
+
+ onActivate = s_onActivate;
+ action = s_action;
+ delete s_lastGoodAction;
+ s_action = NULL;
+ s_lastGoodAction = NULL;
+
+ return (n == 1);
+}
+
+void
+CHotkeyOptions::CActionDialog::doInit(HWND hwnd)
+{
+ // subclass edit control
+ HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY);
+ s_editWndProc = (WNDPROC)GetWindowLong(child, GWL_WNDPROC);
+ SetWindowLong(child, GWL_WNDPROC, (LONG)editProc);
+ setWindowText(getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY), "");
+ fillHotkey(hwnd);
+
+ // fill screens
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
+ SendMessage(child, CB_RESETCONTENT, 0, 0);
+ for (CConfig::const_iterator index = s_config->begin();
+ index != s_config->end(); ) {
+ const CString& name = *index;
+ ++index;
+ if (index != s_config->end()) {
+ SendMessage(child, CB_INSERTSTRING,
+ (WPARAM)-1, (LPARAM)name.c_str());
+ }
+ else {
+ SendMessage(child, CB_ADDSTRING, 0, (LPARAM)name.c_str());
+ }
+ }
+ SendMessage(child, CB_SETCURSEL, 0, 0);
+
+ // fill directions
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_EDGE_LEFT).c_str());
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_EDGE_RIGHT).c_str());
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_EDGE_TOP).c_str());
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_EDGE_BOTTOM).c_str());
+ SendMessage(child, CB_SETCURSEL, 0, 0);
+
+ // fill lock modes
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_MODE_OFF).c_str());
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_MODE_ON).c_str());
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_MODE_TOGGLE).c_str());
+ SendMessage(child, CB_SETCURSEL, 0, 0);
+
+ // fill keyboard broadcast modes
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST);
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_MODE_OFF).c_str());
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_MODE_ON).c_str());
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_MODE_TOGGLE).c_str());
+ SendMessage(child, CB_SETCURSEL, 0, 0);
+
+ // select when
+ if (s_onActivate) {
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_ACTIVATE);
+ }
+ else {
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_DEACTIVATE);
+ }
+ setItemChecked(child, true);
+
+ // no screens by default
+ s_screens.clear();
+
+ // select mode
+ child = NULL;
+ CInputFilter::CKeystrokeAction* keyAction =
+ dynamic_cast(s_action);
+ CInputFilter::CMouseButtonAction* mouseAction =
+ dynamic_cast(s_action);
+ CInputFilter::CLockCursorToScreenAction* lockAction =
+ dynamic_cast(s_action);
+ CInputFilter::CSwitchToScreenAction* switchToAction =
+ dynamic_cast(s_action);
+ CInputFilter::CSwitchInDirectionAction* switchInAction =
+ dynamic_cast(s_action);
+ CInputFilter::CKeyboardBroadcastAction* keyboardBroadcastAction=
+ dynamic_cast(s_action);
+ if (keyAction != NULL) {
+ if (dynamic_cast(s_action) != NULL) {
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP);
+ }
+ else if (keyAction->isOnPress()) {
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN);
+ }
+ else {
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_UP);
+ }
+ }
+ else if (mouseAction != NULL) {
+ if (dynamic_cast(s_action) != NULL) {
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP);
+ }
+ else if (keyAction->isOnPress()) {
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN);
+ }
+ else {
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_UP);
+ }
+ }
+ else if (lockAction != NULL) {
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
+ SendMessage(child, CB_SETCURSEL, lockAction->getMode(), 0);
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK);
+ }
+ else if (switchToAction != NULL) {
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
+ DWORD i = SendMessage(child, CB_FINDSTRINGEXACT, (WPARAM)-1,
+ (LPARAM)switchToAction->getScreen().c_str());
+ if (i == CB_ERR) {
+ i = 0;
+ }
+ SendMessage(child, CB_SETCURSEL, i, 0);
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO);
+ }
+ else if (switchInAction != NULL) {
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
+ SendMessage(child, CB_SETCURSEL,
+ switchInAction->getDirection() - kLeft, 0);
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN);
+ }
+ else if (keyboardBroadcastAction != NULL) {
+ // Save the screens we're broadcasting to
+ s_screens = keyboardBroadcastAction->getScreens();
+
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST);
+ SendMessage(child, CB_SETCURSEL, keyboardBroadcastAction->getMode(), 0);
+ child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST);
+ }
+ if (child != NULL) {
+ setItemChecked(child, true);
+ }
+
+ updateControls(hwnd);
+}
+
+void
+CHotkeyOptions::CActionDialog::fillHotkey(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY);
+ CInputFilter::CKeystrokeAction* keyAction =
+ dynamic_cast(s_action);
+ CInputFilter::CMouseButtonAction* mouseAction =
+ dynamic_cast(s_action);
+ if (keyAction != NULL || mouseAction != NULL) {
+ setWindowText(child, s_action->format().c_str());
+ }
+ else {
+ setWindowText(child, "");
+ }
+
+ // can only set screens in key actions
+ enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL);
+}
+
+void
+CHotkeyOptions::CActionDialog::updateControls(HWND hwnd)
+{
+ // determine which mode we're in
+ UInt32 mode = 0;
+ if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP)) ||
+ isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN)) ||
+ isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
+ mode = 1;
+ }
+ else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO))) {
+ mode = 2;
+ }
+ else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN))) {
+ mode = 3;
+ }
+ else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_LOCK))) {
+ mode = 4;
+ }
+ else if (isItemChecked(getItem(hwnd,
+ IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST))) {
+ mode = 5;
+ }
+
+ // enable/disable all mode specific controls
+ enableItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY, mode == 1);
+ enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST, mode == 2);
+ enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST, mode == 3);
+ enableItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST, mode == 4);
+ enableItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST, mode == 5);
+ enableItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS, mode == 5);
+
+ // can only set screens in key actions
+ CInputFilter::CKeystrokeAction* keyAction =
+ dynamic_cast(s_action);
+ enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL);
+}
+
+void
+CHotkeyOptions::CActionDialog::onButton(HWND hwnd, ButtonID button)
+{
+ IPlatformScreen::CButtonInfo* info =
+ IPrimaryScreen::CButtonInfo::alloc(button, getModifiers());
+ delete s_action;
+ HWND parent = GetParent(hwnd);
+ if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) {
+ s_action = new CMouseButtonDownUpAction(info);
+ }
+ else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) {
+ s_action = new CInputFilter::CMouseButtonAction(info, true);
+ }
+ else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) {
+ s_action = new CInputFilter::CMouseButtonAction(info, false);
+ }
+ else {
+ s_action = NULL;
+ }
+
+ fillHotkey(parent);
+}
+
+void
+CHotkeyOptions::CActionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam)
+{
+ // ignore key repeats
+ if ((lParam & 0xc0000000u) == 0x40000000u) {
+ return;
+ }
+
+ // ignore key releases if the action is complete and for the tab
+ // key (in case we were just tabbed to)
+ if ((lParam & 0x80000000u) != 0) {
+ if (isGoodAction() || wParam == VK_TAB) {
+ return;
+ }
+ }
+
+ KeyID key = kKeyNone;
+ KeyModifierMask mask = getModifiers();
+ switch (wParam) {
+ case VK_SHIFT:
+ case VK_LSHIFT:
+ case VK_RSHIFT:
+ case VK_CONTROL:
+ case VK_LCONTROL:
+ case VK_RCONTROL:
+ case VK_MENU:
+ case VK_LMENU:
+ case VK_RMENU:
+ case VK_LWIN:
+ case VK_RWIN:
+ break;
+
+ case VK_TAB:
+ // allow tabbing out of control
+ if ((mask & (KeyModifierControl |
+ KeyModifierAlt | KeyModifierSuper)) == 0) {
+ HWND next = hwnd;
+ if ((mask & KeyModifierShift) == 0) {
+ do {
+ next = GetWindow(next, GW_HWNDNEXT);
+ if (next == NULL) {
+ next = GetWindow(hwnd, GW_HWNDFIRST);
+ }
+ } while (next != hwnd &&
+ (!IsWindowVisible(next) ||
+ (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
+ }
+ else {
+ do {
+ next = GetWindow(next, GW_HWNDPREV);
+ if (next == NULL) {
+ next = GetWindow(hwnd, GW_HWNDLAST);
+ }
+ } while (next != hwnd &&
+ (!IsWindowVisible(next) ||
+ (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
+ }
+ SetFocus(next);
+ return;
+ }
+ // fall through
+
+ default:
+ key = CMSWindowsKeyState::getKeyID(wParam,
+ static_cast((lParam & 0x1ff0000u) >> 16));
+ switch (key) {
+ case kKeyNone:
+ // could be a character
+ key = getChar(wParam, lParam);
+ if (key == kKeyNone) {
+ return;
+ }
+ break;
+
+ case kKeyShift_L:
+ case kKeyShift_R:
+ case kKeyControl_L:
+ case kKeyControl_R:
+ case kKeyAlt_L:
+ case kKeyAlt_R:
+ case kKeyMeta_L:
+ case kKeyMeta_R:
+ case kKeySuper_L:
+ case kKeySuper_R:
+ case kKeyCapsLock:
+ case kKeyNumLock:
+ case kKeyScrollLock:
+ // bogus
+ return;
+ }
+ break;
+ }
+
+ // get old screen list
+ std::set screens;
+ CInputFilter::CKeystrokeAction* keyAction =
+ dynamic_cast(s_action);
+ if (keyAction == NULL) {
+ keyAction =
+ dynamic_cast(s_lastGoodAction);
+ }
+ if (keyAction != NULL) {
+ IKeyState::CKeyInfo::split(keyAction->getInfo()->m_screens, screens);
+ }
+
+ // create new action
+ IPlatformScreen::CKeyInfo* info =
+ IKeyState::CKeyInfo::alloc(key, mask, 0, 0, screens);
+ delete s_action;
+ HWND parent = GetParent(hwnd);
+ if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) {
+ s_action = new CKeystrokeDownUpAction(info);
+ }
+ else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) {
+ s_action = new CInputFilter::CKeystrokeAction(info, true);
+ }
+ else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) {
+ s_action = new CInputFilter::CKeystrokeAction(info, false);
+ }
+ else {
+ s_action = NULL;
+ }
+
+ fillHotkey(parent);
+}
+
+void
+CHotkeyOptions::CActionDialog::onLockAction(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
+ LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0);
+ if (index != CB_ERR) {
+ delete s_action;
+ s_action = new CInputFilter::CLockCursorToScreenAction(
+ (CInputFilter::CLockCursorToScreenAction::Mode)index);
+ }
+}
+
+void
+CHotkeyOptions::CActionDialog::onSwitchToAction(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
+ CString screen = getWindowText(child);
+ delete s_action;
+ s_action = new CInputFilter::CSwitchToScreenAction(screen);
+}
+
+void
+CHotkeyOptions::CActionDialog::onSwitchInAction(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
+ LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0);
+ if (index != CB_ERR) {
+ delete s_action;
+ s_action = new CInputFilter::CSwitchInDirectionAction(
+ (EDirection)(index + kLeft));
+ }
+}
+
+void
+CHotkeyOptions::CActionDialog::onKeyboardBroadcastAction(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST);
+ LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0);
+ if (index != CB_ERR) {
+ delete s_action;
+ s_action = new CInputFilter::CKeyboardBroadcastAction(
+ (CInputFilter::CKeyboardBroadcastAction::Mode)index, s_screens);
+ }
+}
+
+KeyID
+CHotkeyOptions::CActionDialog::getChar(WPARAM wParam, LPARAM lParam)
+{
+ BYTE keyState[256];
+ UINT virtualKey = (UINT)wParam;
+ UINT scanCode = (UINT)((lParam & 0x0ff0000u) >> 16);
+ GetKeyboardState(keyState);
+
+ // reset modifier state
+ keyState[VK_SHIFT] = 0;
+ keyState[VK_LSHIFT] = 0;
+ keyState[VK_RSHIFT] = 0;
+ keyState[VK_CONTROL] = 0;
+ keyState[VK_LCONTROL] = 0;
+ keyState[VK_RCONTROL] = 0;
+ keyState[VK_MENU] = 0;
+ keyState[VK_LMENU] = 0;
+ keyState[VK_RMENU] = 0;
+ keyState[VK_LWIN] = 0;
+ keyState[VK_RWIN] = 0;
+
+ // translate virtual key to character
+ int n;
+ KeyID id;
+ if (CArchMiscWindows::isWindows95Family()) {
+ // XXX -- how do we get characters not in Latin-1?
+ WORD ascii;
+ n = ToAscii(virtualKey, scanCode, keyState, &ascii, 0);
+ id = static_cast(ascii & 0xffu);
+ }
+ else {
+ typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey,
+ UINT wScanCode,
+ PBYTE lpKeyState,
+ LPWSTR pwszBuff,
+ int cchBuff,
+ UINT wFlags);
+ ToUnicode_t s_ToUnicode = NULL;
+ if (s_ToUnicode == NULL) {
+ HMODULE userModule = GetModuleHandle("user32.dll");
+ s_ToUnicode =
+ (ToUnicode_t)GetProcAddress(userModule, "ToUnicode");
+ }
+
+ WCHAR unicode[2];
+ n = s_ToUnicode(virtualKey, scanCode, keyState,
+ unicode, sizeof(unicode) / sizeof(unicode[0]),
+ 0);
+ id = static_cast(unicode[0]);
+ }
+ switch (n) {
+ case -1:
+ // no hot keys on dead keys
+ return kKeyNone;
+
+ default:
+ case 0:
+ // unmapped
+ return kKeyNone;
+
+ case 1:
+ return id;
+ }
+}
+
+KeyModifierMask
+CHotkeyOptions::CActionDialog::getModifiers()
+{
+ KeyModifierMask mask = 0;
+ if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) {
+ mask |= KeyModifierShift;
+ }
+ if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) {
+ mask |= KeyModifierControl;
+ }
+ if ((GetKeyState(VK_MENU) & 0x8000) != 0) {
+ mask |= KeyModifierAlt;
+ }
+ if ((GetKeyState(VK_LWIN) & 0x8000) != 0 ||
+ (GetKeyState(VK_RWIN) & 0x8000) != 0) {
+ mask |= KeyModifierSuper;
+ }
+ return mask;
+}
+
+bool
+CHotkeyOptions::CActionDialog::isGoodAction()
+{
+ CInputFilter::CMouseButtonAction* mouseAction =
+ dynamic_cast(s_action);
+ CInputFilter::CKeystrokeAction* keyAction =
+ dynamic_cast(s_action);
+ return (mouseAction == NULL || keyAction == NULL ||
+ keyAction->getInfo()->m_key != kKeyNone);
+}
+
+void
+CHotkeyOptions::CActionDialog::convertAction(HWND hwnd)
+{
+ if (s_lastGoodAction != NULL) {
+ CInputFilter::CMouseButtonAction* mouseAction =
+ dynamic_cast(s_lastGoodAction);
+ CInputFilter::CKeystrokeAction* keyAction =
+ dynamic_cast(s_lastGoodAction);
+ if (mouseAction != NULL) {
+ IPlatformScreen::CButtonInfo* info =
+ IPrimaryScreen::CButtonInfo::alloc(*mouseAction->getInfo());
+ delete s_action;
+ if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) {
+ s_action = new CMouseButtonDownUpAction(info);
+ }
+ else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) {
+ s_action = new CInputFilter::CMouseButtonAction(info, true);
+ }
+ else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
+ s_action = new CInputFilter::CMouseButtonAction(info, false);
+ }
+ else {
+ free(info);
+ s_action = NULL;
+ }
+ }
+ else if (keyAction != NULL) {
+ IPlatformScreen::CKeyInfo* info =
+ IKeyState::CKeyInfo::alloc(*keyAction->getInfo());
+ delete s_action;
+ if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) {
+ s_action = new CKeystrokeDownUpAction(info);
+ }
+ else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) {
+ s_action = new CInputFilter::CKeystrokeAction(info, true);
+ }
+ else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
+ s_action = new CInputFilter::CKeystrokeAction(info, false);
+ }
+ else {
+ free(info);
+ s_action = NULL;
+ }
+ }
+ }
+}
+
+bool
+CHotkeyOptions::CActionDialog::isDownUpAction()
+{
+ return (dynamic_cast(s_action) != NULL ||
+ dynamic_cast(s_action) != NULL);
+}
+
+BOOL CALLBACK
+CHotkeyOptions::CActionDialog::dlgProc(HWND hwnd,
+ UINT message, WPARAM wParam, LPARAM)
+{
+ switch (message) {
+ case WM_INITDIALOG:
+ doInit(hwnd);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ if (isDownUpAction()) {
+ s_onActivate = true;
+ }
+ EndDialog(hwnd, 1);
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwnd, 0);
+ return TRUE;
+
+ case IDC_HOTKEY_ACTION_ON_ACTIVATE:
+ s_onActivate = true;
+ return TRUE;
+
+ case IDC_HOTKEY_ACTION_ON_DEACTIVATE:
+ s_onActivate = false;
+ return TRUE;
+
+ case IDC_HOTKEY_ACTION_DOWNUP:
+ case IDC_HOTKEY_ACTION_DOWN:
+ case IDC_HOTKEY_ACTION_UP:
+ convertAction(hwnd);
+ fillHotkey(hwnd);
+ updateControls(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_ACTION_LOCK:
+ onLockAction(hwnd);
+ updateControls(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_ACTION_SWITCH_TO:
+ onSwitchToAction(hwnd);
+ updateControls(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_ACTION_SWITCH_IN:
+ onSwitchInAction(hwnd);
+ updateControls(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST:
+ onKeyboardBroadcastAction(hwnd);
+ updateControls(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_ACTION_LOCK_LIST:
+ switch (HIWORD(wParam)) {
+ case LBN_SELCHANGE:
+ onLockAction(hwnd);
+ return TRUE;
+ }
+ break;
+
+ case IDC_HOTKEY_ACTION_SWITCH_TO_LIST:
+ switch (HIWORD(wParam)) {
+ case LBN_SELCHANGE:
+ onSwitchToAction(hwnd);
+ return TRUE;
+ }
+ break;
+
+ case IDC_HOTKEY_ACTION_SWITCH_IN_LIST:
+ switch (HIWORD(wParam)) {
+ case LBN_SELCHANGE:
+ onSwitchInAction(hwnd);
+ return TRUE;
+ }
+ break;
+
+ case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST:
+ switch (HIWORD(wParam)) {
+ case LBN_SELCHANGE:
+ onKeyboardBroadcastAction(hwnd);
+ return TRUE;
+ }
+ break;
+
+ case IDC_HOTKEY_ACTION_SCREENS:
+ CScreensDialog::doModal(hwnd, s_config,
+ dynamic_cast(s_action));
+ fillHotkey(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS: {
+ // convert screens to form that CScreenDialog::doModal() wants
+ IPlatformScreen::CKeyInfo* tmpInfo =
+ IPlatformScreen::CKeyInfo::alloc(0, 0, 0, 1, s_screens);
+ CInputFilter::CKeystrokeAction tmpAction(tmpInfo, true);
+
+ // get the screens
+ CScreensDialog::doModal(hwnd, s_config, &tmpAction);
+
+ // convert screens back
+ IPlatformScreen::CKeyInfo::split(
+ tmpAction.getInfo()->m_screens, s_screens);
+
+ // update
+ onKeyboardBroadcastAction(hwnd);
+ return TRUE;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+LRESULT CALLBACK
+CHotkeyOptions::CActionDialog::editProc(HWND hwnd,
+ UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_LBUTTONDOWN:
+ if (GetFocus() == hwnd) {
+ onButton(hwnd, kButtonLeft);
+ }
+ else {
+ SetFocus(hwnd);
+ }
+ return 0;
+
+ case WM_MBUTTONDOWN:
+ if (GetFocus() == hwnd) {
+ onButton(hwnd, kButtonMiddle);
+ }
+ return 0;
+
+ case WM_RBUTTONDOWN:
+ if (GetFocus() == hwnd) {
+ onButton(hwnd, kButtonRight);
+ }
+ return 0;
+
+ case WM_XBUTTONDOWN:
+ if (GetFocus() == hwnd) {
+ switch (HIWORD(wParam)) {
+ case XBUTTON1:
+ onButton(hwnd, kButtonExtra0 + 0);
+ break;
+
+ case XBUTTON2:
+ onButton(hwnd, kButtonExtra0 + 1);
+ break;
+ }
+ }
+ return 0;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ onKey(hwnd, wParam, lParam);
+ return 0;
+
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_XBUTTONUP:
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_DEADCHAR:
+ case WM_SYSDEADCHAR:
+ return 0;
+
+ case WM_SETFOCUS:
+ if (s_action != NULL) {
+ delete s_lastGoodAction;
+ s_lastGoodAction = s_action->clone();
+ }
+ break;
+
+ case WM_KILLFOCUS:
+ if (!isGoodAction()) {
+ delete s_action;
+ if (s_lastGoodAction != NULL) {
+ s_action = s_lastGoodAction->clone();
+ }
+ else {
+ s_action = NULL;
+ }
+ }
+ else if (s_action != NULL) {
+ delete s_lastGoodAction;
+ s_lastGoodAction = s_action->clone();
+ }
+ fillHotkey(GetParent(hwnd));
+ break;
+
+ case WM_GETDLGCODE:
+ return DLGC_WANTALLKEYS;
+
+ default:
+ break;
+ }
+ return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam);
+}
+
+
+//
+// CHotkeyOptions::CScreensDialog
+//
+
+CConfig* CHotkeyOptions::CScreensDialog::s_config = NULL;
+CInputFilter::CKeystrokeAction*
+ CHotkeyOptions::CScreensDialog::s_action = NULL;
+CHotkeyOptions::CScreensDialog::CScreens
+ CHotkeyOptions::CScreensDialog::s_nonTargets;
+CHotkeyOptions::CScreensDialog::CScreens
+ CHotkeyOptions::CScreensDialog::s_targets;
+CString CHotkeyOptions::CScreensDialog::s_allScreens;
+
+void
+CHotkeyOptions::CScreensDialog::doModal(HWND parent, CConfig* config,
+ CInputFilter::CKeystrokeAction* action)
+{
+ s_allScreens = getString(IDS_ALL_SCREENS);
+ s_config = config;
+ s_action = action;
+ DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_SCREENS),
+ parent, dlgProc);
+ s_config = NULL;
+ s_action = NULL;
+}
+
+void
+CHotkeyOptions::CScreensDialog::doInit(HWND hwnd)
+{
+ s_nonTargets.clear();
+ s_targets.clear();
+
+ // get screens from config
+ s_nonTargets.insert("*");
+ for (CConfig::const_iterator i = s_config->begin();
+ i != s_config->end(); ++i) {
+ s_nonTargets.insert(*i);
+ }
+
+ // get screens in action
+ IKeyState::CKeyInfo::split(s_action->getInfo()->m_screens, s_targets);
+
+ // remove screens in action from screens in config
+ for (CScreens::const_iterator i = s_targets.begin();
+ i != s_targets.end(); ++i) {
+ s_nonTargets.erase(*i);
+ }
+
+ // fill dialog
+ fillScreens(hwnd);
+ updateControls(hwnd);
+}
+
+void
+CHotkeyOptions::CScreensDialog::doFini(HWND)
+{
+ // put screens into action
+ const IPlatformScreen::CKeyInfo* oldInfo = s_action->getInfo();
+ IPlatformScreen::CKeyInfo* newInfo =
+ IKeyState::CKeyInfo::alloc(oldInfo->m_key,
+ oldInfo->m_mask, 0, 0, s_targets);
+ s_action->adoptInfo(newInfo);
+}
+
+void
+CHotkeyOptions::CScreensDialog::fillScreens(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC);
+ SendMessage(child, LB_RESETCONTENT, 0, 0);
+ for (CScreens::const_iterator i = s_nonTargets.begin();
+ i != s_nonTargets.end(); ++i) {
+ CString name = *i;
+ if (name == "*") {
+ name = s_allScreens;
+ }
+ SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
+ (LPARAM)name.c_str());
+ }
+
+ child = getItem(hwnd, IDC_HOTKEY_SCREENS_DST);
+ SendMessage(child, LB_RESETCONTENT, 0, 0);
+ for (CScreens::const_iterator i = s_targets.begin();
+ i != s_targets.end(); ++i) {
+ CString name = *i;
+ if (name == "*") {
+ name = s_allScreens;
+ }
+ SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
+ (LPARAM)name.c_str());
+ }
+ if (s_targets.empty()) {
+ // if no targets then add a special item so the user knows
+ // what'll happen
+ CString activeScreenLabel = getString(IDS_ACTIVE_SCREEN);
+ SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
+ (LPARAM)activeScreenLabel.c_str());
+ }
+}
+
+void
+CHotkeyOptions::CScreensDialog::updateControls(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC);
+ bool canAdd = (SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0);
+ child = getItem(hwnd, IDC_HOTKEY_SCREENS_DST);
+ bool canRemove = (!s_targets.empty() &&
+ (SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0));
+
+ enableItem(hwnd, IDC_HOTKEY_SCREENS_ADD, canAdd);
+ enableItem(hwnd, IDC_HOTKEY_SCREENS_REMOVE, canRemove);
+}
+
+void
+CHotkeyOptions::CScreensDialog::add(HWND hwnd)
+{
+ CScreens selected;
+ getSelected(hwnd, IDC_HOTKEY_SCREENS_SRC, s_nonTargets, selected);
+ for (CScreens::const_iterator i = selected.begin();
+ i != selected.end(); ++i) {
+ s_targets.insert(*i);
+ s_nonTargets.erase(*i);
+ }
+ fillScreens(hwnd);
+ updateControls(hwnd);
+}
+
+void
+CHotkeyOptions::CScreensDialog::remove(HWND hwnd)
+{
+ CScreens selected;
+ getSelected(hwnd, IDC_HOTKEY_SCREENS_DST, s_targets, selected);
+ for (CScreens::const_iterator i = selected.begin();
+ i != selected.end(); ++i) {
+ s_nonTargets.insert(*i);
+ s_targets.erase(*i);
+ }
+ fillScreens(hwnd);
+ updateControls(hwnd);
+}
+
+void
+CHotkeyOptions::CScreensDialog::getSelected(HWND hwnd, UINT id,
+ const CScreens& inScreens, CScreens& outScreens)
+{
+ // get the selected item indices
+ HWND child = getItem(hwnd, id);
+ UInt32 n = (UInt32)SendMessage(child, LB_GETSELCOUNT, 0, 0);
+ int* index = new int[n];
+ SendMessage(child, LB_GETSELITEMS, (WPARAM)n, (LPARAM)index);
+
+ // get the items in a vector
+ std::vector tmpList;
+ for (CScreens::const_iterator i = inScreens.begin();
+ i != inScreens.end(); ++i) {
+ tmpList.push_back(*i);
+ }
+
+ // get selected items into the output set
+ outScreens.clear();
+ for (UInt32 i = 0; i < n; ++i) {
+ outScreens.insert(tmpList[index[i]]);
+ }
+
+ // clean up
+ delete[] index;
+}
+
+BOOL CALLBACK
+CHotkeyOptions::CScreensDialog::dlgProc(HWND hwnd,
+ UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_INITDIALOG:
+ doInit(hwnd);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ doFini(hwnd);
+ EndDialog(hwnd, 0);
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwnd, 0);
+ return TRUE;
+
+ case IDC_HOTKEY_SCREENS_ADD:
+ add(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_SCREENS_REMOVE:
+ remove(hwnd);
+ return TRUE;
+
+ case IDC_HOTKEY_SCREENS_SRC:
+ case IDC_HOTKEY_SCREENS_DST:
+ switch (HIWORD(wParam)) {
+ case LBN_SELCANCEL:
+ case LBN_SELCHANGE:
+ updateControls(hwnd);
+ return TRUE;
+ }
+ break;
+ }
+ break;
+
+ case WM_CTLCOLORLISTBOX:
+ if (s_targets.empty() &&
+ (HWND)lParam == getItem(hwnd, IDC_HOTKEY_SCREENS_DST)) {
+ // override colors
+ HDC dc = (HDC)wParam;
+ SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
+ return (BOOL)GetSysColorBrush(COLOR_WINDOW);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
diff --git a/cmd/launcher/CHotkeyOptions.h b/cmd/launcher/CHotkeyOptions.h
new file mode 100644
index 00000000..dec5367a
--- /dev/null
+++ b/cmd/launcher/CHotkeyOptions.h
@@ -0,0 +1,227 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2006 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CHOTKEYOPTIONS_H
+#define CHOTKEYOPTIONS_H
+
+#include "CString.h"
+#include "KeyTypes.h"
+#include "MouseTypes.h"
+#include "CInputFilter.h"
+
+#define WINDOWS_LEAN_AND_MEAN
+#include
+
+class CConfig;
+
+//! Hotkey options dialog for Microsoft Windows launcher
+class CHotkeyOptions {
+public:
+ CHotkeyOptions(HWND parent, CConfig*);
+ ~CHotkeyOptions();
+
+ //! @name manipulators
+ //@{
+
+ //! Run dialog
+ /*!
+ Display and handle the dialog until closed by the user.
+ */
+ void doModal();
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //@}
+
+private:
+ void doInit(HWND hwnd);
+
+ void fillHotkeys(HWND hwnd, UInt32 select = (UInt32)-1);
+ void updateHotkeysControls(HWND hwnd);
+
+ void addHotkey(HWND hwnd);
+ void removeHotkey(HWND hwnd);
+ void editHotkey(HWND hwnd);
+
+ void fillActions(HWND hwnd, UInt32 select = (UInt32)-1);
+ void updateActionsControls(HWND hwnd);
+
+ void addAction(HWND hwnd);
+ void removeAction(HWND hwnd);
+ void editAction(HWND hwnd);
+
+ bool editCondition(HWND hwnd, CInputFilter::CCondition*&);
+ bool editAction(HWND hwnd, CInputFilter::CAction*&,
+ bool& onActivate);
+
+ void openRule(HWND hwnd);
+ void closeRule(HWND hwnd);
+ UInt32 findMatchingAction(
+ const CInputFilter::CKeystrokeAction*) const;
+ UInt32 findMatchingAction(
+ const CInputFilter::CMouseButtonAction*) const;
+
+ // message handling
+ BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM);
+ static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
+
+ // special actions we use to combine matching down/up actions into a
+ // single action for the convenience of the user.
+ class CKeystrokeDownUpAction : public CInputFilter::CKeystrokeAction {
+ public:
+ CKeystrokeDownUpAction(IPlatformScreen::CKeyInfo* adoptedInfo) :
+ CInputFilter::CKeystrokeAction(adoptedInfo, true) { }
+
+ // CAction overrides
+ virtual CInputFilter::CAction* clone() const
+ {
+ IKeyState::CKeyInfo* info = IKeyState::CKeyInfo::alloc(*getInfo());
+ return new CKeystrokeDownUpAction(info);
+ }
+
+ protected:
+ // CKeystrokeAction overrides
+ virtual const char* formatName() const { return "keystroke"; }
+ };
+ class CMouseButtonDownUpAction : public CInputFilter::CMouseButtonAction {
+ public:
+ CMouseButtonDownUpAction(IPrimaryScreen::CButtonInfo* adoptedInfo) :
+ CInputFilter::CMouseButtonAction(adoptedInfo, true) { }
+
+ // CAction overrides
+ virtual CInputFilter::CAction* clone() const
+ {
+ IPlatformScreen::CButtonInfo* info =
+ IPrimaryScreen::CButtonInfo::alloc(*getInfo());
+ return new CMouseButtonDownUpAction(info);
+ }
+
+ protected:
+ // CMouseButtonAction overrides
+ virtual const char* formatName() const { return "mousebutton"; }
+ };
+
+ class CConditionDialog {
+ public:
+ static bool doModal(HWND parent, CInputFilter::CCondition*&);
+
+ private:
+ static void doInit(HWND hwnd);
+ static void fillHotkey(HWND hwnd);
+
+ static void onButton(HWND hwnd, ButtonID button);
+ static void onKey(HWND hwnd, WPARAM wParam, LPARAM lParam);
+ static KeyID getChar(WPARAM wParam, LPARAM lParam);
+ static KeyModifierMask
+ getModifiers();
+
+ static bool isGoodCondition();
+
+ static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
+ static LRESULT CALLBACK editProc(HWND hwnd, UINT, WPARAM, LPARAM);
+
+ private:
+ static CInputFilter::CCondition*
+ s_condition;
+ static CInputFilter::CCondition*
+ s_lastGoodCondition;
+ static WNDPROC s_editWndProc;
+ };
+
+ class CActionDialog {
+ public:
+ static bool doModal(HWND parent, CConfig* config,
+ CInputFilter::CAction*&, bool& onActivate);
+
+ private:
+ static void doInit(HWND hwnd);
+ static void fillHotkey(HWND hwnd);
+ static void updateControls(HWND hwnd);
+
+ static void onButton(HWND hwnd, ButtonID button);
+ static void onKey(HWND hwnd, WPARAM wParam, LPARAM lParam);
+ static void onLockAction(HWND hwnd);
+ static void onSwitchToAction(HWND hwnd);
+ static void onSwitchInAction(HWND hwnd);
+ static void onKeyboardBroadcastAction(HWND hwnd);
+
+ static KeyID getChar(WPARAM wParam, LPARAM lParam);
+ static KeyModifierMask
+ getModifiers();
+
+ static bool isGoodAction();
+ static void convertAction(HWND hwnd);
+
+ static bool isDownUpAction();
+
+ static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
+ static LRESULT CALLBACK editProc(HWND hwnd, UINT, WPARAM, LPARAM);
+
+ private:
+ static CConfig* s_config;
+ static bool s_onActivate;
+ static CInputFilter::CAction*
+ s_action;
+ static CInputFilter::CAction*
+ s_lastGoodAction;
+ static std::set s_screens;
+ static WNDPROC s_editWndProc;
+ };
+
+// public to allow CActionDialog to use it
+public:
+ class CScreensDialog {
+ public:
+ static void doModal(HWND parent, CConfig* config,
+ CInputFilter::CKeystrokeAction*);
+
+ // public due to compiler brokenness
+ typedef std::set CScreens;
+
+ private:
+
+ static void doInit(HWND hwnd);
+ static void doFini(HWND hwnd);
+ static void fillScreens(HWND hwnd);
+ static void updateControls(HWND hwnd);
+
+ static void add(HWND hwnd);
+ static void remove(HWND hwnd);
+
+ static void getSelected(HWND hwnd, UINT id,
+ const CScreens& inScreens, CScreens& outScreens);
+
+ static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
+
+ private:
+ static CConfig* s_config;
+ static CInputFilter::CKeystrokeAction* s_action;
+ static CScreens s_nonTargets;
+ static CScreens s_targets;
+ static CString s_allScreens;
+ };
+
+private:
+ static CHotkeyOptions* s_singleton;
+
+ HWND m_parent;
+ CConfig* m_config;
+ CInputFilter* m_inputFilter;
+ CInputFilter::CRule m_activeRule;
+ UInt32 m_activeRuleIndex;
+};
+
+#endif
diff --git a/cmd/launcher/CInfo.cpp b/cmd/launcher/CInfo.cpp
new file mode 100644
index 00000000..669da0e2
--- /dev/null
+++ b/cmd/launcher/CInfo.cpp
@@ -0,0 +1,111 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2006 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "ProtocolTypes.h"
+#include "CStringUtil.h"
+#include "Version.h"
+#include "CArch.h"
+#include "CInfo.h"
+#include "LaunchUtil.h"
+#include "resource.h"
+
+//
+// CInfo
+//
+
+CInfo* CInfo::s_singleton = NULL;
+
+CInfo::CInfo(HWND parent) :
+ m_parent(parent)
+{
+ assert(s_singleton == NULL);
+ s_singleton = this;
+}
+
+CInfo::~CInfo()
+{
+ s_singleton = NULL;
+}
+
+void
+CInfo::doModal()
+{
+ // do dialog
+ DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_INFO),
+ m_parent, (DLGPROC)dlgProc, (LPARAM)this);
+}
+
+void
+CInfo::init(HWND hwnd)
+{
+ // collect info
+ CString version =
+ CStringUtil::format(getString(IDS_TITLE).c_str(), VERSION);
+ CString hostname = ARCH->getHostName();
+ CString address = ARCH->addrToString(ARCH->nameToAddr(hostname));
+ CString userConfig = ARCH->getUserDirectory();
+ if (!userConfig.empty()) {
+ userConfig = ARCH->concatPath(userConfig, CONFIG_NAME);
+ }
+ CString sysConfig = ARCH->getSystemDirectory();
+ if (!sysConfig.empty()) {
+ sysConfig = ARCH->concatPath(sysConfig, CONFIG_NAME);
+ }
+
+ // set info
+ HWND child;
+ child = getItem(hwnd, IDC_INFO_VERSION);
+ setWindowText(child, version);
+ child = getItem(hwnd, IDC_INFO_HOSTNAME);
+ setWindowText(child, hostname);
+ child = getItem(hwnd, IDC_INFO_IP_ADDRESS);
+ setWindowText(child, address);
+ child = getItem(hwnd, IDC_INFO_USER_CONFIG);
+ setWindowText(child, userConfig);
+ child = getItem(hwnd, IDC_INFO_SYS_CONFIG);
+ setWindowText(child, sysConfig);
+
+ // focus on okay button
+ SetFocus(getItem(hwnd, IDOK));
+}
+
+BOOL
+CInfo::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
+{
+ switch (message) {
+ case WM_INITDIALOG:
+ init(hwnd);
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(hwnd, 0);
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+BOOL CALLBACK
+CInfo::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
+}
diff --git a/cmd/launcher/CInfo.h b/cmd/launcher/CInfo.h
new file mode 100644
index 00000000..2d85ec7d
--- /dev/null
+++ b/cmd/launcher/CInfo.h
@@ -0,0 +1,57 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2006 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CINFO_H
+#define CINFO_H
+
+#include "CString.h"
+
+#define WINDOWS_LEAN_AND_MEAN
+#include
+
+//! Info dialog for Microsoft Windows launcher
+class CInfo {
+public:
+ CInfo(HWND parent);
+ ~CInfo();
+
+ //! @name manipulators
+ //@{
+
+ //! Run dialog
+ /*!
+ Display and handle the dialog until closed by the user.
+ */
+ void doModal();
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //@}
+
+private:
+ void init(HWND hwnd);
+
+ // message handling
+ BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM);
+ static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
+
+private:
+ static CInfo* s_singleton;
+
+ HWND m_parent;
+};
+
+#endif
diff --git a/cmd/launcher/CScreensLinks.cpp b/cmd/launcher/CScreensLinks.cpp
new file mode 100644
index 00000000..c7e58a04
--- /dev/null
+++ b/cmd/launcher/CScreensLinks.cpp
@@ -0,0 +1,855 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CConfig.h"
+#include "ProtocolTypes.h"
+#include "CStringUtil.h"
+#include "CArch.h"
+#include "CScreensLinks.h"
+#include "CAddScreen.h"
+#include "LaunchUtil.h"
+#include "resource.h"
+
+//
+// CScreensLinks
+//
+
+CScreensLinks* CScreensLinks::s_singleton = NULL;
+
+CScreensLinks::CScreensLinks(HWND parent, CConfig* config) :
+ m_parent(parent),
+ m_mainConfig(config),
+ m_config(&m_scratchConfig)
+{
+ assert(s_singleton == NULL);
+ s_singleton = this;
+
+ // get formatting strings
+ m_linkFormat = getString(IDS_LINK_FORMAT);
+ m_intervalFormat = getString(IDS_LINK_INTERVAL_FORMAT);
+ m_newLinkLabel = getString(IDS_NEW_LINK);
+ m_sideLabel[kLeft - kFirstDirection] = getString(IDS_SIDE_LEFT);
+ m_sideLabel[kRight - kFirstDirection] = getString(IDS_SIDE_RIGHT);
+ m_sideLabel[kTop - kFirstDirection] = getString(IDS_SIDE_TOP);
+ m_sideLabel[kBottom - kFirstDirection] = getString(IDS_SIDE_BOTTOM);
+
+ // GDI objects
+ m_redPen = CreatePen(PS_INSIDEFRAME, 1, RGB(255, 0, 0));
+}
+
+CScreensLinks::~CScreensLinks()
+{
+ DeleteObject(m_redPen);
+ s_singleton = NULL;
+}
+
+void
+CScreensLinks::doModal()
+{
+ // do dialog
+ DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_SCREENS_LINKS),
+ m_parent, (DLGPROC)dlgProc, (LPARAM)this);
+}
+
+void
+CScreensLinks::init(HWND hwnd)
+{
+ // get initial config
+ m_scratchConfig = *m_mainConfig;
+
+ // fill side list box (in EDirection order)
+ HWND child = getItem(hwnd, IDC_SCREENS_SRC_SIDE);
+ SendMessage(child, CB_ADDSTRING, 0, (LPARAM)TEXT("---"));
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_EDGE_LEFT).c_str());
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_EDGE_RIGHT).c_str());
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_EDGE_TOP).c_str());
+ SendMessage(child, CB_ADDSTRING, 0,
+ (LPARAM)getString(IDS_EDGE_BOTTOM).c_str());
+
+ // create error boxes
+ m_srcSideError = createErrorBox(hwnd);
+ m_srcScreenError = createErrorBox(hwnd);
+ m_dstScreenError = createErrorBox(hwnd);
+ resizeErrorBoxes();
+
+ m_selectedLink = -1;
+ m_editedLink = CEdgeLink();
+ m_edgeLinks.clear();
+ updateScreens(hwnd, "");
+ updateScreensControls(hwnd);
+ updateLinks(hwnd);
+ updateLinksControls(hwnd);
+}
+
+bool
+CScreensLinks::save(HWND /*hwnd*/)
+{
+ *m_mainConfig = m_scratchConfig;
+ return true;
+}
+
+BOOL
+CScreensLinks::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_INITDIALOG:
+ init(hwnd);
+ return TRUE;
+
+ case WM_SIZE:
+ resizeErrorBoxes();
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ SetFocus(getItem(hwnd, IDOK));
+ if (save(hwnd)) {
+ EndDialog(hwnd, 0);
+ }
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hwnd, 0);
+ return TRUE;
+
+ case IDC_SCREENS_SCREENS:
+ switch (HIWORD(wParam)) {
+ case LBN_DBLCLK:
+ editScreen(hwnd);
+ return TRUE;
+
+ case LBN_SELCHANGE:
+ updateScreensControls(hwnd);
+ updateLinkView(hwnd);
+ return TRUE;
+
+ case LBN_SELCANCEL:
+ updateScreensControls(hwnd);
+ updateLinkView(hwnd);
+ return TRUE;
+ }
+ break;
+
+ case IDC_SCREENS_ADD_SCREEN:
+ addScreen(hwnd);
+ return TRUE;
+
+ case IDC_SCREENS_REMOVE_SCREEN:
+ removeScreen(hwnd);
+ return TRUE;
+
+ case IDC_SCREENS_EDIT_SCREEN:
+ editScreen(hwnd);
+ return TRUE;
+
+ case IDC_SCREENS_LINKS:
+ switch (HIWORD(wParam)) {
+ case LBN_SELCHANGE:
+ editLink(hwnd);
+ return TRUE;
+
+ case LBN_SELCANCEL:
+ editLink(hwnd);
+ return TRUE;
+ }
+ break;
+
+ case IDC_SCREENS_ADD_LINK:
+ addLink(hwnd);
+ return TRUE;
+
+ case IDC_SCREENS_REMOVE_LINK:
+ removeLink(hwnd);
+ return TRUE;
+
+ case IDC_SCREENS_SRC_SIDE:
+ switch (HIWORD(wParam)) {
+ case CBN_SELCHANGE:
+ changeSrcSide(hwnd);
+ break;
+ }
+ break;
+
+ case IDC_SCREENS_SRC_SCREEN:
+ switch (HIWORD(wParam)) {
+ case CBN_SELCHANGE:
+ changeSrcScreen(hwnd);
+ break;
+ }
+ break;
+
+ case IDC_SCREENS_DST_SCREEN:
+ switch (HIWORD(wParam)) {
+ case CBN_SELCHANGE:
+ changeDstScreen(hwnd);
+ break;
+ }
+ break;
+
+ case IDC_SCREENS_SRC_START:
+ switch (HIWORD(wParam)) {
+ case EN_KILLFOCUS:
+ changeIntervalStart(hwnd, LOWORD(wParam),
+ m_editedLink.m_srcInterval);
+ break;
+ }
+ break;
+
+ case IDC_SCREENS_SRC_END:
+ switch (HIWORD(wParam)) {
+ case EN_KILLFOCUS:
+ changeIntervalEnd(hwnd, LOWORD(wParam),
+ m_editedLink.m_srcInterval);
+ break;
+ }
+ break;
+
+ case IDC_SCREENS_DST_START:
+ switch (HIWORD(wParam)) {
+ case EN_KILLFOCUS:
+ changeIntervalStart(hwnd, LOWORD(wParam),
+ m_editedLink.m_dstInterval);
+ break;
+ }
+ break;
+
+ case IDC_SCREENS_DST_END:
+ switch (HIWORD(wParam)) {
+ case EN_KILLFOCUS:
+ changeIntervalEnd(hwnd, LOWORD(wParam),
+ m_editedLink.m_dstInterval);
+ break;
+ }
+ break;
+ }
+
+ break;
+
+ case WM_CTLCOLORSTATIC:
+ switch (GetDlgCtrlID((HWND)lParam)) {
+ case IDC_SCREENS_OVERLAP_ERROR:
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE));
+ SetTextColor((HDC)wParam, RGB(255, 0, 0));
+ return (BOOL)GetSysColorBrush(COLOR_3DFACE);
+ }
+ break;
+
+ // error outlines
+ case WM_DRAWITEM: {
+ DRAWITEMSTRUCT* di = (DRAWITEMSTRUCT*)lParam;
+ if (di->CtlType == ODT_STATIC) {
+ HGDIOBJ oldPen = SelectObject(di->hDC, m_redPen);
+ HGDIOBJ oldBrush = SelectObject(di->hDC,
+ GetStockObject(NULL_BRUSH));
+ Rectangle(di->hDC, di->rcItem.left, di->rcItem.top,
+ di->rcItem.right, di->rcItem.bottom);
+ SelectObject(di->hDC, oldPen);
+ SelectObject(di->hDC, oldBrush);
+ return TRUE;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+BOOL CALLBACK
+CScreensLinks::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
+}
+
+CString
+CScreensLinks::getSelectedScreen(HWND hwnd) const
+{
+ HWND child = getItem(hwnd, IDC_SCREENS_SCREENS);
+ LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0);
+ if (index == LB_ERR) {
+ return CString();
+ }
+
+ LRESULT size = SendMessage(child, LB_GETTEXTLEN, index, 0);
+ char* buffer = new char[size + 1];
+ SendMessage(child, LB_GETTEXT, index, (LPARAM)buffer);
+ buffer[size] = '\0';
+ CString result(buffer);
+ delete[] buffer;
+ return result;
+}
+
+void
+CScreensLinks::addScreen(HWND hwnd)
+{
+ CAddScreen dialog(hwnd, m_config, "");
+ if (dialog.doModal()) {
+ updateScreens(hwnd, dialog.getName());
+ updateScreensControls(hwnd);
+ updateLinks(hwnd);
+ updateLinksControls(hwnd);
+ }
+}
+
+void
+CScreensLinks::editScreen(HWND hwnd)
+{
+ CString oldName = getSelectedScreen(hwnd);
+ CAddScreen dialog(hwnd, m_config, oldName);
+ if (dialog.doModal()) {
+ CString newName = dialog.getName();
+
+ // rename screens in the edge list
+ if (newName != oldName) {
+ for (size_t i = 0; i < m_edgeLinks.size(); ++i) {
+ m_edgeLinks[i].rename(oldName, newName);
+ }
+ m_editedLink.rename(oldName, newName);
+ }
+
+ updateScreens(hwnd, newName);
+ updateScreensControls(hwnd);
+ updateLinks(hwnd);
+ updateLinksControls(hwnd);
+ }
+}
+
+void
+CScreensLinks::removeScreen(HWND hwnd)
+{
+ // remove screen from config (this also removes aliases)
+ m_config->removeScreen(getSelectedScreen(hwnd));
+
+ // update dialog
+ updateScreens(hwnd, "");
+ updateScreensControls(hwnd);
+ updateLinks(hwnd);
+ updateLinksControls(hwnd);
+}
+
+void
+CScreensLinks::addLink(HWND hwnd)
+{
+ if (m_editedLink.connect(m_config)) {
+ m_editedLink = CEdgeLink();
+ updateLinks(hwnd);
+ updateLinksControls(hwnd);
+ }
+}
+
+void
+CScreensLinks::editLink(HWND hwnd)
+{
+ // get selection
+ HWND child = getItem(hwnd, IDC_SCREENS_LINKS);
+ DWORD i = SendMessage(child, LB_GETCURSEL, 0, 0);
+ if (i != LB_ERR && i != (DWORD)m_edgeLinks.size()) {
+ // existing link
+ m_selectedLink = (SInt32)SendMessage(child, LB_GETITEMDATA, i, 0);
+ m_editedLink = m_edgeLinks[m_selectedLink];
+ }
+ else {
+ // new link
+ m_selectedLink = -1;
+ m_editedLink = CEdgeLink();
+ }
+ updateLinksControls(hwnd);
+}
+
+void
+CScreensLinks::removeLink(HWND hwnd)
+{
+ if (m_editedLink.disconnect(m_config)) {
+ updateLinks(hwnd);
+ updateLinksControls(hwnd);
+ }
+}
+
+void
+CScreensLinks::updateScreens(HWND hwnd, const CString& selectName)
+{
+ HWND child;
+
+ // set screen list
+ child = getItem(hwnd, IDC_SCREENS_SCREENS);
+ SendMessage(child, LB_RESETCONTENT, 0, 0);
+ for (CConfig::const_iterator index = m_config->begin();
+ index != m_config->end(); ) {
+ const CString& name = *index;
+ ++index;
+ if (index != m_config->end()) {
+ SendMessage(child, LB_INSERTSTRING,
+ (WPARAM)-1, (LPARAM)name.c_str());
+ }
+ else {
+ SendMessage(child, LB_ADDSTRING, 0, (LPARAM)name.c_str());
+ }
+ }
+
+ // find the named screen
+ if (!selectName.empty()) {
+ DWORD i = SendMessage(child, LB_FINDSTRINGEXACT,
+ (UINT)-1, (LPARAM)selectName.c_str());
+ if (i != LB_ERR) {
+ SendMessage(child, LB_SETSEL, TRUE, i);
+ }
+ }
+}
+
+void
+CScreensLinks::updateScreensControls(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_SCREENS_SCREENS);
+ bool screenSelected = (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR);
+
+ enableItem(hwnd, IDC_SCREENS_ADD_SCREEN, TRUE);
+ enableItem(hwnd, IDC_SCREENS_EDIT_SCREEN, screenSelected);
+ enableItem(hwnd, IDC_SCREENS_REMOVE_SCREEN, screenSelected);
+}
+
+void
+CScreensLinks::updateLinks(HWND hwnd)
+{
+ HWND links = getItem(hwnd, IDC_SCREENS_LINKS);
+ HWND srcScreens = getItem(hwnd, IDC_SCREENS_SRC_SCREEN);
+ HWND dstScreens = getItem(hwnd, IDC_SCREENS_DST_SCREEN);
+
+ // get old selection
+ CEdgeLink oldLink;
+ if (m_selectedLink != -1) {
+ oldLink = m_edgeLinks[m_selectedLink];
+ }
+
+ // clear links and screens
+ SendMessage(links, LB_RESETCONTENT, 0, 0);
+ SendMessage(srcScreens, CB_RESETCONTENT, 0, 0);
+ SendMessage(dstScreens, CB_RESETCONTENT, 0, 0);
+ m_edgeLinks.clear();
+
+ // add "no screen" items
+ SendMessage(srcScreens, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)TEXT("----"));
+ SendMessage(dstScreens, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)TEXT("----"));
+
+ // add links and screens
+ for (CConfig::const_iterator i = m_config->begin();
+ i != m_config->end(); ++i) {
+ const CString& name = *i;
+
+ // add screen
+ SendMessage(srcScreens, CB_INSERTSTRING, (WPARAM)-1,
+ (LPARAM)name.c_str());
+ SendMessage(dstScreens, CB_INSERTSTRING, (WPARAM)-1,
+ (LPARAM)name.c_str());
+
+ // add links for screen
+ for (CConfig::link_const_iterator j = m_config->beginNeighbor(name),
+ n = m_config->endNeighbor(name);
+ j != n; ++j) {
+ DWORD k = m_edgeLinks.size();
+ m_edgeLinks.push_back(CEdgeLink(name, *j));
+ SendMessage(links, LB_INSERTSTRING, (WPARAM)-1,
+ (LPARAM)formatLink(m_edgeLinks.back()).c_str());
+ SendMessage(links, LB_SETITEMDATA, (WPARAM)k, (LPARAM)k);
+ }
+ }
+
+ // add "new link" item to sort
+ SendMessage(links, LB_ADDSTRING, 0, (LPARAM)m_newLinkLabel.c_str());
+
+ // remove the "new link" item then insert it on the end
+ DWORD i = SendMessage(links, LB_FINDSTRINGEXACT,
+ (UINT)-1, (LPARAM)m_newLinkLabel.c_str());
+ if (i != LB_ERR) {
+ SendMessage(links, LB_DELETESTRING, i, 0);
+ }
+ SendMessage(links, LB_INSERTSTRING, (WPARAM)-1,
+ (LPARAM)getString(IDS_NEW_LINK).c_str());
+ SendMessage(links, LB_SETITEMDATA, (WPARAM)m_edgeLinks.size(),
+ (LPARAM)-1);
+
+ // select the same link as before
+ SendMessage(links, LB_SETCURSEL, (WPARAM)m_edgeLinks.size(), 0);
+ if (m_selectedLink != -1) {
+ m_selectedLink = -1;
+ for (size_t j = 0; j < m_edgeLinks.size(); ++j) {
+ if (m_edgeLinks[j] == oldLink) {
+ // found matching link
+ m_selectedLink = j;
+ for (size_t k = 0; k < m_edgeLinks.size(); ++k) {
+ if (SendMessage(links, LB_GETITEMDATA, k, 0) == (int)j) {
+ SendMessage(links, LB_SETCURSEL, k, 0);
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ // if we can't find the link anymore then reset edited link
+ if (m_selectedLink == -1) {
+ m_editedLink = CEdgeLink();
+ }
+ }
+}
+
+void
+CScreensLinks::updateLinksControls(HWND hwnd)
+{
+ // get selection. select "new link" if nothing is selected.
+ HWND child = getItem(hwnd, IDC_SCREENS_LINKS);
+ if (m_selectedLink == -1) {
+ SendMessage(child, LB_SETCURSEL, m_edgeLinks.size(), 0);
+ }
+
+ // enable/disable remove button
+ enableItem(hwnd, IDC_SCREENS_REMOVE_LINK, m_selectedLink != -1);
+
+ // fill link entry controls from m_editedLink
+ updateLinkEditControls(hwnd, m_editedLink);
+ updateLinkValid(hwnd, m_editedLink);
+ updateLinkView(hwnd);
+}
+
+void
+CScreensLinks::changeSrcSide(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_SCREENS_SRC_SIDE);
+ m_editedLink.m_srcSide = (EDirection)SendMessage(child, CB_GETCURSEL, 0, 0);
+ updateLink(hwnd);
+}
+
+void
+CScreensLinks::changeSrcScreen(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_SCREENS_SRC_SCREEN);
+ m_editedLink.m_srcName = getWindowText(child);
+ updateLink(hwnd);
+}
+
+void
+CScreensLinks::changeDstScreen(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_SCREENS_DST_SCREEN);
+ m_editedLink.m_dstName = getWindowText(child);
+ updateLink(hwnd);
+}
+
+void
+CScreensLinks::changeIntervalStart(HWND hwnd, int id, CConfig::CInterval& i)
+{
+ int x = (int)GetDlgItemInt(hwnd, id, NULL, FALSE);
+ if (x < 0) {
+ x = 0;
+ }
+ else if (x > 99) {
+ x = 99;
+ }
+
+ i.first = 0.01f * (float)x;
+ if (i.first >= i.second) {
+ i.second = 0.01f * (float)(x + 1);
+ }
+
+ updateLinkIntervalControls(hwnd, m_editedLink);
+ updateLink(hwnd);
+}
+
+void
+CScreensLinks::changeIntervalEnd(HWND hwnd, int id, CConfig::CInterval& i)
+{
+ int x = (int)GetDlgItemInt(hwnd, id, NULL, FALSE);
+ if (x < 1) {
+ x = 1;
+ }
+ else if (x > 100) {
+ x = 100;
+ }
+
+ i.second = 0.01f * (float)x;
+ if (i.first >= i.second) {
+ i.first = 0.01f * (float)(x - 1);
+ }
+
+ updateLinkIntervalControls(hwnd, m_editedLink);
+ updateLink(hwnd);
+}
+
+void
+CScreensLinks::selectScreen(HWND hwnd, int id, const CString& name)
+{
+ HWND child = getItem(hwnd, id);
+ DWORD i = SendMessage(child, CB_FINDSTRINGEXACT, (WPARAM)-1,
+ (LPARAM)name.c_str());
+ if (i == CB_ERR) {
+ // no match, select no screen
+ SendMessage(child, CB_SETCURSEL, 0, 0);
+ }
+ else {
+ SendMessage(child, CB_SETCURSEL, i, 0);
+ }
+}
+
+void
+CScreensLinks::updateLinkEditControls(HWND hwnd, const CEdgeLink& link)
+{
+ // fill link entry controls from link
+ HWND child = getItem(hwnd, IDC_SCREENS_SRC_SIDE);
+ SendMessage(child, CB_SETCURSEL, link.m_srcSide, 0);
+ selectScreen(hwnd, IDC_SCREENS_SRC_SCREEN, link.m_srcName);
+ selectScreen(hwnd, IDC_SCREENS_DST_SCREEN, link.m_dstName);
+ updateLinkIntervalControls(hwnd, link);
+}
+
+void
+CScreensLinks::updateLinkIntervalControls(HWND hwnd, const CEdgeLink& link)
+{
+ HWND child;
+
+ // src interval
+ child = getItem(hwnd, IDC_SCREENS_SRC_START);
+ setWindowText(child, formatIntervalValue(link.m_srcInterval.first));
+ child = getItem(hwnd, IDC_SCREENS_SRC_END);
+ setWindowText(child, formatIntervalValue(link.m_srcInterval.second));
+
+ // dst interval
+ child = getItem(hwnd, IDC_SCREENS_DST_START);
+ setWindowText(child, formatIntervalValue(link.m_dstInterval.first));
+ child = getItem(hwnd, IDC_SCREENS_DST_END);
+ setWindowText(child, formatIntervalValue(link.m_dstInterval.second));
+}
+
+void
+CScreensLinks::updateLink(HWND hwnd)
+{
+ updateLinkValid(hwnd, m_editedLink);
+
+ // update link in config
+ if (m_selectedLink != -1 && m_editedLinkIsValid) {
+ // editing an existing link and entry is valid
+ if (m_edgeLinks[m_selectedLink].disconnect(m_config)) {
+ // successfully removed old link
+ if (!m_editedLink.connect(m_config)) {
+ // couldn't set new link so restore old link
+ m_edgeLinks[m_selectedLink].connect(m_config);
+ }
+ else {
+ m_edgeLinks[m_selectedLink] = m_editedLink;
+ updateLinks(hwnd);
+ updateLinkEditControls(hwnd, m_editedLink);
+ }
+ }
+ }
+
+ updateLinkView(hwnd);
+}
+
+void
+CScreensLinks::updateLinkValid(HWND hwnd, const CEdgeLink& link)
+{
+ m_editedLinkIsValid = true;
+
+ // check source side and screen
+ if (link.m_srcSide == kNoDirection) {
+ m_editedLinkIsValid = false;
+ ShowWindow(m_srcSideError, SW_SHOWNA);
+ }
+ else {
+ ShowWindow(m_srcSideError, SW_HIDE);
+ }
+ if (!m_config->isCanonicalName(link.m_srcName)) {
+ m_editedLinkIsValid = false;
+ ShowWindow(m_srcScreenError, SW_SHOWNA);
+ }
+ else {
+ ShowWindow(m_srcScreenError, SW_HIDE);
+ }
+
+ // check for overlap. if editing a link we must remove it, then
+ // check for overlap and restore the old link.
+ bool overlap = false;
+ if (m_editedLinkIsValid) {
+ if (m_selectedLink == -1) {
+ if (link.overlaps(m_config)) {
+ m_editedLinkIsValid = false;
+ overlap = true;
+ }
+ }
+ else {
+ if (m_edgeLinks[m_selectedLink].disconnect(m_config)) {
+ overlap = link.overlaps(m_config);
+ m_edgeLinks[m_selectedLink].connect(m_config);
+ if (overlap) {
+ m_editedLinkIsValid = false;
+ }
+ }
+ }
+ }
+ ShowWindow(getItem(hwnd, IDC_SCREENS_OVERLAP_ERROR),
+ overlap ? SW_SHOWNA : SW_HIDE);
+
+ // check dst screen
+ if (!m_config->isCanonicalName(link.m_dstName)) {
+ m_editedLinkIsValid = false;
+ ShowWindow(m_dstScreenError, SW_SHOWNA);
+ }
+ else {
+ ShowWindow(m_dstScreenError, SW_HIDE);
+ }
+
+ // update add link button
+ enableItem(hwnd, IDC_SCREENS_ADD_LINK,
+ m_selectedLink == -1 && m_editedLinkIsValid);
+}
+
+void
+CScreensLinks::updateLinkView(HWND /*hwnd*/)
+{
+ // XXX -- draw visual of selected screen, highlighting selected link
+}
+
+HWND
+CScreensLinks::createErrorBox(HWND parent)
+{
+ return CreateWindow(TEXT("STATIC"), TEXT(""),
+ WS_CHILD | SS_OWNERDRAW,
+ 0, 0, 1, 1,
+ parent, (HMENU)-1,
+ s_instance, NULL);
+}
+
+void
+CScreensLinks::resizeErrorBoxes()
+{
+ HWND hwnd = GetParent(m_srcSideError);
+ resizeErrorBox(m_srcSideError, getItem(hwnd, IDC_SCREENS_SRC_SIDE));
+ resizeErrorBox(m_srcScreenError, getItem(hwnd, IDC_SCREENS_SRC_SCREEN));
+ resizeErrorBox(m_dstScreenError, getItem(hwnd, IDC_SCREENS_DST_SCREEN));
+}
+
+void
+CScreensLinks::resizeErrorBox(HWND box, HWND assoc)
+{
+ RECT rect;
+ GetWindowRect(assoc, &rect);
+ MapWindowPoints(NULL, GetParent(box), (POINT*)&rect, 2);
+ SetWindowPos(box, HWND_TOP, rect.left - 1, rect.top - 1,
+ rect.right - rect.left + 2,
+ rect.bottom - rect.top + 2, SWP_NOACTIVATE);
+}
+
+CString
+CScreensLinks::formatIntervalValue(float x) const
+{
+ return CStringUtil::print("%d", (int)(x * 100.0f + 0.5f));
+}
+
+CString
+CScreensLinks::formatInterval(const CConfig::CInterval& i) const
+{
+ if (i.first == 0.0f && i.second == 1.0f) {
+ return "";
+ }
+ else {
+ CString start = formatIntervalValue(i.first);
+ CString end = formatIntervalValue(i.second);
+ return CStringUtil::format(m_intervalFormat.c_str(),
+ start.c_str(), end.c_str());
+ }
+}
+
+CString
+CScreensLinks::formatLink(const CEdgeLink& link) const
+{
+ CString srcInterval = formatInterval(link.m_srcInterval);
+ CString dstInterval = formatInterval(link.m_dstInterval);
+ return CStringUtil::format(m_linkFormat.c_str(),
+ link.m_srcName.c_str(), srcInterval.c_str(),
+ m_sideLabel[link.m_srcSide - kFirstDirection].c_str(),
+ link.m_dstName.c_str(), dstInterval.c_str());
+}
+
+//
+// CScreensLinks::CEdgeLink
+//
+
+CScreensLinks::CEdgeLink::CEdgeLink() :
+ m_srcName(),
+ m_srcSide(kNoDirection),
+ m_srcInterval(0.0f, 1.0f),
+ m_dstName(),
+ m_dstInterval(0.0f, 1.0f)
+{
+ // do nothing
+}
+
+CScreensLinks::CEdgeLink::CEdgeLink(const CString& name,
+ const CConfigLink& link) :
+ m_srcName(name),
+ m_srcSide(link.first.getSide()),
+ m_srcInterval(link.first.getInterval()),
+ m_dstName(link.second.getName()),
+ m_dstInterval(link.second.getInterval())
+{
+ // do nothing
+}
+
+bool
+CScreensLinks::CEdgeLink::connect(CConfig* config)
+{
+ return config->connect(m_srcName, m_srcSide,
+ m_srcInterval.first, m_srcInterval.second,
+ m_dstName,
+ m_dstInterval.first, m_dstInterval.second);
+}
+
+bool
+CScreensLinks::CEdgeLink::disconnect(CConfig* config)
+{
+ return config->disconnect(m_srcName, m_srcSide, 0.5f *
+ (m_srcInterval.first + m_srcInterval.second));
+}
+
+void
+CScreensLinks::CEdgeLink::rename(const CString& oldName, const CString& newName)
+{
+ if (m_srcName == oldName) {
+ m_srcName = newName;
+ }
+ if (m_dstName == oldName) {
+ m_dstName = newName;
+ }
+}
+
+bool
+CScreensLinks::CEdgeLink::overlaps(const CConfig* config) const
+{
+ return config->hasNeighbor(m_srcName, m_srcSide,
+ m_srcInterval.first, m_srcInterval.second);
+}
+
+bool
+CScreensLinks::CEdgeLink::operator==(const CEdgeLink& x) const
+{
+ return (m_srcName == x.m_srcName &&
+ m_srcSide == x.m_srcSide &&
+ m_srcInterval == x.m_srcInterval &&
+ m_dstName == x.m_dstName &&
+ m_dstInterval == x.m_dstInterval);
+}
diff --git a/cmd/launcher/CScreensLinks.h b/cmd/launcher/CScreensLinks.h
new file mode 100644
index 00000000..8ffd0089
--- /dev/null
+++ b/cmd/launcher/CScreensLinks.h
@@ -0,0 +1,138 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CSCREENSLINKS_H
+#define CSCREENSLINKS_H
+
+#include "CConfig.h"
+#include "ProtocolTypes.h"
+#include "CString.h"
+
+#define WINDOWS_LEAN_AND_MEAN
+#include
+
+//! Screens and links dialog for Microsoft Windows launcher
+class CScreensLinks {
+public:
+ CScreensLinks(HWND parent, CConfig*);
+ ~CScreensLinks();
+
+ //! @name manipulators
+ //@{
+
+ //! Run dialog
+ /*!
+ Display and handle the dialog until closed by the user.
+ */
+ void doModal();
+
+ //@}
+ //! @name accessors
+ //@{
+
+
+ //@}
+
+private:
+ typedef std::pair CConfigLink;
+ struct CEdgeLink {
+ public:
+ CEdgeLink();
+ CEdgeLink(const CString& name, const CConfigLink&);
+
+ bool connect(CConfig*);
+ bool disconnect(CConfig*);
+ void rename(const CString& oldName, const CString& newName);
+
+ bool overlaps(const CConfig* config) const;
+ bool operator==(const CEdgeLink&) const;
+
+ public:
+ CString m_srcName;
+ EDirection m_srcSide;
+ CConfig::CInterval m_srcInterval;
+ CString m_dstName;
+ CConfig::CInterval m_dstInterval;
+ };
+ typedef std::vector CEdgeLinkList;
+
+ void init(HWND hwnd);
+ bool save(HWND hwnd);
+
+ CString getSelectedScreen(HWND hwnd) const;
+ void addScreen(HWND hwnd);
+ void editScreen(HWND hwnd);
+ void removeScreen(HWND hwnd);
+ void addLink(HWND hwnd);
+ void editLink(HWND hwnd);
+ void removeLink(HWND hwnd);
+
+ void updateScreens(HWND hwnd, const CString& name);
+ void updateScreensControls(HWND hwnd);
+ void updateLinks(HWND hwnd);
+ void updateLinksControls(HWND hwnd);
+
+ void changeSrcSide(HWND hwnd);
+ void changeSrcScreen(HWND hwnd);
+ void changeDstScreen(HWND hwnd);
+ void changeIntervalStart(HWND hwnd, int id,
+ CConfig::CInterval&);
+ void changeIntervalEnd(HWND hwnd, int id,
+ CConfig::CInterval&);
+
+ void selectScreen(HWND hwnd, int id, const CString& name);
+ void updateLinkEditControls(HWND hwnd,
+ const CEdgeLink& link);
+ void updateLinkIntervalControls(HWND hwnd,
+ const CEdgeLink& link);
+ void updateLink(HWND hwnd);
+ void updateLinkValid(HWND hwnd, const CEdgeLink& link);
+
+ void updateLinkView(HWND hwnd);
+
+ HWND createErrorBox(HWND parent);
+ void resizeErrorBoxes();
+ void resizeErrorBox(HWND box, HWND assoc);
+
+ CString formatIntervalValue(float) const;
+ CString formatInterval(const CConfig::CInterval&) const;
+ CString formatLink(const CEdgeLink&) const;
+
+ // message handling
+ BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM);
+ static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
+
+private:
+ static CScreensLinks* s_singleton;
+
+ HWND m_parent;
+ CConfig* m_mainConfig;
+ CConfig m_scratchConfig;
+ CConfig* m_config;
+
+ CString m_linkFormat;
+ CString m_intervalFormat;
+ CString m_newLinkLabel;
+ CString m_sideLabel[kNumDirections];
+ CEdgeLinkList m_edgeLinks;
+ SInt32 m_selectedLink;
+ CEdgeLink m_editedLink;
+ bool m_editedLinkIsValid;
+ HPEN m_redPen;
+ HWND m_srcSideError;
+ HWND m_srcScreenError;
+ HWND m_dstScreenError;
+};
+
+#endif
diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp
new file mode 100644
index 00000000..05b517e7
--- /dev/null
+++ b/cmd/launcher/LaunchUtil.cpp
@@ -0,0 +1,261 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CConfig.h"
+#include "LaunchUtil.h"
+#include "CMSWindowsUtil.h"
+#include "CArch.h"
+#include "resource.h"
+#include "stdfstream.h"
+
+size_t s_showingDialog = 0;
+
+CString
+getString(DWORD id)
+{
+ return CMSWindowsUtil::getString(s_instance, id);
+}
+
+CString
+getErrorString(DWORD error)
+{
+ return CMSWindowsUtil::getErrorString(s_instance, error, IDS_ERROR);
+}
+
+void
+showError(HWND hwnd, const CString& msg)
+{
+ CString title = getString(IDS_ERROR);
+ ++s_showingDialog;
+ MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL);
+ --s_showingDialog;
+}
+
+void
+askOkay(HWND hwnd, const CString& title, const CString& msg)
+{
+ ++s_showingDialog;
+ MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL);
+ --s_showingDialog;
+}
+
+bool
+askVerify(HWND hwnd, const CString& msg)
+{
+ CString title = getString(IDS_VERIFY);
+ ++s_showingDialog;
+ int result = MessageBox(hwnd, msg.c_str(),
+ title.c_str(), MB_OKCANCEL | MB_APPLMODAL);
+ --s_showingDialog;
+ return (result == IDOK);
+}
+
+bool
+isShowingDialog()
+{
+ return (s_showingDialog != 0);
+}
+
+void
+setWindowText(HWND hwnd, const CString& msg)
+{
+ SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)msg.c_str());
+}
+
+CString
+getWindowText(HWND hwnd)
+{
+ LRESULT size = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0);
+ char* buffer = new char[size + 1];
+ SendMessage(hwnd, WM_GETTEXT, size + 1, (LPARAM)buffer);
+ buffer[size] = '\0';
+ CString result(buffer);
+ delete[] buffer;
+ return result;
+}
+
+HWND
+getItem(HWND hwnd, int id)
+{
+ return GetDlgItem(hwnd, id);
+}
+
+void
+enableItem(HWND hwnd, int id, bool enabled)
+{
+ EnableWindow(GetDlgItem(hwnd, id), enabled);
+}
+
+void
+setItemChecked(HWND hwnd, bool checked)
+{
+ SendMessage(hwnd, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0);
+}
+
+bool
+isItemChecked(HWND hwnd)
+{
+ return (SendMessage(hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
+}
+
+CString
+getAppPath(const CString& appName)
+{
+ // prepare path to app
+ char myPathname[MAX_PATH];
+ GetModuleFileName(s_instance, myPathname, MAX_PATH);
+ const char* myBasename = ARCH->getBasename(myPathname);
+ CString appPath = CString(myPathname, myBasename - myPathname);
+ appPath += appName;
+ return appPath;
+}
+
+static
+void
+getFileTime(const CString& path, time_t& t)
+{
+ struct _stat s;
+ if (_stat(path.c_str(), &s) != -1) {
+ t = s.st_mtime;
+ }
+}
+
+bool
+isConfigNewer(time_t& oldTime, bool userConfig)
+{
+ time_t newTime = oldTime;
+ if (userConfig) {
+ CString path = ARCH->getUserDirectory();
+ if (!path.empty()) {
+ path = ARCH->concatPath(path, CONFIG_NAME);
+ getFileTime(path, newTime);
+ }
+ }
+ else {
+ CString path = ARCH->getSystemDirectory();
+ if (!path.empty()) {
+ path = ARCH->concatPath(path, CONFIG_NAME);
+ getFileTime(path, newTime);
+ }
+ }
+ bool result = (newTime > oldTime);
+ oldTime = newTime;
+ return result;
+}
+
+static
+bool
+loadConfig(const CString& pathname, CConfig& config)
+{
+ try {
+ std::ifstream stream(pathname.c_str());
+ if (stream) {
+ stream >> config;
+ return true;
+ }
+ }
+ catch (...) {
+ // ignore
+ }
+ return false;
+}
+
+bool
+loadConfig(CConfig& config, time_t& t, bool& userConfig)
+{
+ // load configuration
+ bool configLoaded = false;
+ CString path = ARCH->getUserDirectory();
+ if (!path.empty()) {
+ // try loading the user's configuration
+ path = ARCH->concatPath(path, CONFIG_NAME);
+ if (loadConfig(path, config)) {
+ configLoaded = true;
+ userConfig = true;
+ getFileTime(path, t);
+ }
+ else {
+ // try the system-wide config file
+ path = ARCH->getSystemDirectory();
+ if (!path.empty()) {
+ path = ARCH->concatPath(path, CONFIG_NAME);
+ if (loadConfig(path, config)) {
+ configLoaded = true;
+ userConfig = false;
+ getFileTime(path, t);
+ }
+ }
+ }
+ }
+ return configLoaded;
+}
+
+static
+bool
+saveConfig(const CString& pathname, const CConfig& config)
+{
+ try {
+ std::ofstream stream(pathname.c_str());
+ if (stream) {
+ stream << config;
+ return !!stream;
+ }
+ }
+ catch (...) {
+ // ignore
+ }
+ return false;
+}
+
+bool
+saveConfig(const CConfig& config, bool sysOnly, time_t& t)
+{
+ // try saving the user's configuration
+ if (!sysOnly) {
+ CString path = ARCH->getUserDirectory();
+ if (!path.empty()) {
+ path = ARCH->concatPath(path, CONFIG_NAME);
+ if (saveConfig(path, config)) {
+ getFileTime(path, t);
+ return true;
+ }
+ }
+ }
+
+ // try the system-wide config file
+ else {
+ CString path = ARCH->getSystemDirectory();
+ if (!path.empty()) {
+ path = ARCH->concatPath(path, CONFIG_NAME);
+ if (saveConfig(path, config)) {
+ getFileTime(path, t);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+const TCHAR* const*
+getSettingsPath()
+{
+ static const TCHAR* s_keyNames[] = {
+ TEXT("Software"),
+ TEXT("Synergy"),
+ TEXT("Synergy"),
+ NULL
+ };
+ return s_keyNames;
+}
diff --git a/cmd/launcher/LaunchUtil.h b/cmd/launcher/LaunchUtil.h
new file mode 100644
index 00000000..75954046
--- /dev/null
+++ b/cmd/launcher/LaunchUtil.h
@@ -0,0 +1,61 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef LAUNCHUTIL_H
+#define LAUNCHUTIL_H
+
+#include "CString.h"
+
+#define WINDOWS_LEAN_AND_MEAN
+#include
+#include
+#include
+
+#define CLIENT_APP "synergyc.exe"
+#define SERVER_APP "synergys.exe"
+#define CONFIG_NAME "synergy.sgc"
+
+class CConfig;
+
+// client must define this and set it before calling any function here
+extern HINSTANCE s_instance;
+
+CString getString(DWORD id);
+CString getErrorString(DWORD error);
+
+void showError(HWND hwnd, const CString& msg);
+void askOkay(HWND hwnd, const CString& title,
+ const CString& msg);
+bool askVerify(HWND hwnd, const CString& msg);
+bool isShowingDialog();
+
+void setWindowText(HWND hwnd, const CString& msg);
+CString getWindowText(HWND hwnd);
+
+HWND getItem(HWND hwnd, int id);
+void enableItem(HWND hwnd, int id, bool enabled);
+
+void setItemChecked(HWND, bool);
+bool isItemChecked(HWND);
+
+CString getAppPath(const CString& appName);
+
+bool isConfigNewer(time_t&, bool userConfig);
+bool loadConfig(CConfig& config, time_t&, bool& userConfig);
+bool saveConfig(const CConfig& config,
+ bool sysOnly, time_t&);
+
+const TCHAR* const* getSettingsPath();
+
+#endif
diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am
new file mode 100644
index 00000000..6fc879e3
--- /dev/null
+++ b/cmd/launcher/Makefile.am
@@ -0,0 +1,75 @@
+# synergy -- mouse and keyboard sharing utility
+# 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+## Process this file with automake to produce Makefile.in
+NULL =
+
+MSWINDOWS_SOURCE_FILES = \
+ CAddScreen.cpp \
+ CAdvancedOptions.cpp \
+ CAutoStart.cpp \
+ CGlobalOptions.cpp \
+ CHotkeyOptions.cpp \
+ CInfo.cpp \
+ CScreensLinks.cpp \
+ LaunchUtil.cpp \
+ launcher.cpp \
+ CAddScreen.h \
+ CAdvancedOptions.h \
+ CAutoStart.h \
+ CGlobalOptions.h \
+ CHotkeyOptions.h \
+ CInfo.h \
+ CScreensLinks.h \
+ LaunchUtil.h \
+ resource.h \
+ launcher.rc \
+ $(NULL)
+
+EXTRA_DIST = \
+ Makefile.win \
+ synergy.ico \
+ $(MSWINDOWS_SOURCE_FILES) \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ $(NULL)
+
+if MSWINDOWS
+bin_PROGRAMS = synergy
+synergy_SOURCES = \
+ $(MSWINDOWS_SOURCE_FILES) \
+ $(NULL)
+endif
+synergy_LDADD = \
+ $(top_builddir)/lib/server/libserver.a \
+ $(top_builddir)/lib/platform/libplatform.a \
+ $(top_builddir)/lib/synergy/libsynergy.a \
+ $(top_builddir)/lib/net/libnet.a \
+ $(top_builddir)/lib/io/libio.a \
+ $(top_builddir)/lib/mt/libmt.a \
+ $(top_builddir)/lib/base/libbase.a \
+ $(top_builddir)/lib/common/libcommon.a \
+ $(top_builddir)/lib/arch/libarch.a \
+ $(NULL)
+INCLUDES = \
+ -I$(top_srcdir)/lib/common \
+ -I$(top_srcdir)/lib/arch \
+ -I$(top_srcdir)/lib/base \
+ -I$(top_srcdir)/lib/mt \
+ -I$(top_srcdir)/lib/io \
+ -I$(top_srcdir)/lib/net \
+ -I$(top_srcdir)/lib/synergy \
+ -I$(top_srcdir)/lib/platform \
+ -I$(top_srcdir)/lib/server \
+ $(NULL)
diff --git a/cmd/launcher/Makefile.win b/cmd/launcher/Makefile.win
new file mode 100644
index 00000000..3d4f277a
--- /dev/null
+++ b/cmd/launcher/Makefile.win
@@ -0,0 +1,101 @@
+# synergy -- mouse and keyboard sharing utility
+# Copyright (C) 2007 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+BIN_LAUNCHER_SRC = cmd\launcher
+BIN_LAUNCHER_DST = $(BUILD_DST)\$(BIN_LAUNCHER_SRC)
+BIN_LAUNCHER_EXE = "$(BUILD_DST)\synergy.exe"
+BIN_LAUNCHER_CPP = \
+ "CAddScreen.cpp" \
+ "CAdvancedOptions.cpp" \
+ "CAutoStart.cpp" \
+ "CGlobalOptions.cpp" \
+ "CHotkeyOptions.cpp" \
+ "CInfo.cpp" \
+ "CScreensLinks.cpp" \
+ "LaunchUtil.cpp" \
+ "launcher.cpp" \
+ $(NULL)
+BIN_LAUNCHER_OBJ = \
+ "$(BIN_LAUNCHER_DST)\CAddScreen.obj" \
+ "$(BIN_LAUNCHER_DST)\CAdvancedOptions.obj" \
+ "$(BIN_LAUNCHER_DST)\CAutoStart.obj" \
+ "$(BIN_LAUNCHER_DST)\CGlobalOptions.obj" \
+ "$(BIN_LAUNCHER_DST)\CHotkeyOptions.obj" \
+ "$(BIN_LAUNCHER_DST)\CInfo.obj" \
+ "$(BIN_LAUNCHER_DST)\CScreensLinks.obj" \
+ "$(BIN_LAUNCHER_DST)\LaunchUtil.obj" \
+ "$(BIN_LAUNCHER_DST)\launcher.obj" \
+ $(NULL)
+BIN_LAUNCHER_RC = "$(BIN_LAUNCHER_SRC)\launcher.rc"
+BIN_LAUNCHER_RES = "$(BIN_LAUNCHER_DST)\launcher.res"
+BIN_LAUNCHER_INC = \
+ /I"lib\common" \
+ /I"lib\arch" \
+ /I"lib\base" \
+ /I"lib\mt" \
+ /I"lib\io" \
+ /I"lib\net" \
+ /I"lib\synergy" \
+ /I"lib\platform" \
+ /I"lib\server" \
+ $(NULL)
+BIN_LAUNCHER_LIB = \
+ $(LIB_SERVER_LIB) \
+ $(LIB_PLATFORM_LIB) \
+ $(LIB_SYNERGY_LIB) \
+ $(LIB_NET_LIB) \
+ $(LIB_IO_LIB) \
+ $(LIB_MT_LIB) \
+ $(LIB_BASE_LIB) \
+ $(LIB_ARCH_LIB) \
+ $(LIB_COMMON_LIB) \
+ $(NULL)
+
+CPP_FILES = $(CPP_FILES) $(BIN_LAUNCHER_CPP)
+OBJ_FILES = $(OBJ_FILES) $(BIN_LAUNCHER_OBJ)
+PROGRAMS = $(PROGRAMS) $(BIN_LAUNCHER_EXE)
+
+# Need shell functions.
+guilibs = $(guilibs) shell32.lib
+
+# Dependency rules
+$(BIN_LAUNCHER_OBJ): $(AUTODEP)
+!if EXIST($(BIN_LAUNCHER_DST)\deps.mak)
+!include $(BIN_LAUNCHER_DST)\deps.mak
+!endif
+
+# Build rules. Use batch-mode rules if possible.
+!if DEFINED(_NMAKE_VER)
+{$(BIN_LAUNCHER_SRC)\}.cpp{$(BIN_LAUNCHER_DST)\}.obj::
+!else
+{$(BIN_LAUNCHER_SRC)\}.cpp{$(BIN_LAUNCHER_DST)\}.obj:
+!endif
+ @$(ECHO) Compile in $(BIN_LAUNCHER_SRC)
+ -@$(MKDIR) $(BIN_LAUNCHER_DST) 2>NUL:
+ $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \
+ $(BIN_LAUNCHER_INC) \
+ /Fo$(BIN_LAUNCHER_DST)\ \
+ /Fd$(BIN_LAUNCHER_DST)\src.pdb \
+ $< | $(AUTODEP) $(BIN_LAUNCHER_SRC) $(BIN_LAUNCHER_DST)
+$(BIN_LAUNCHER_RES): $(BIN_LAUNCHER_RC)
+ @$(ECHO) Compile $(**F)
+ -@$(MKDIR) $(BIN_LAUNCHER_DST) 2>NUL:
+ $(rc) $(rcflags) $(rcvars) \
+ /fo$@ \
+ $**
+$(BIN_LAUNCHER_EXE): $(BIN_LAUNCHER_OBJ) $(BIN_LAUNCHER_RES) $(BIN_LAUNCHER_LIB)
+ @$(ECHO) Link $(@F)
+ $(link) $(ldebug) $(guilflags) $(guilibsmt) \
+ /out:$@ \
+ $**
+ $(AUTODEP) $(BIN_LAUNCHER_SRC) $(BIN_LAUNCHER_DST) \
+ $(BIN_LAUNCHER_OBJ:.obj=.d)
diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp
new file mode 100644
index 00000000..938822da
--- /dev/null
+++ b/cmd/launcher/launcher.cpp
@@ -0,0 +1,755 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CConfig.h"
+#include "KeyTypes.h"
+#include "OptionTypes.h"
+#include "ProtocolTypes.h"
+#include "CLog.h"
+#include "CStringUtil.h"
+#include "CArch.h"
+#include "CArchMiscWindows.h"
+#include "XArch.h"
+#include "Version.h"
+#include "stdvector.h"
+#include "resource.h"
+
+// these must come after the above because it includes windows.h
+#include "LaunchUtil.h"
+#include "CAddScreen.h"
+#include "CAdvancedOptions.h"
+#include "CAutoStart.h"
+#include "CGlobalOptions.h"
+#include "CHotkeyOptions.h"
+#include "CInfo.h"
+#include "CScreensLinks.h"
+
+typedef std::vector CStringList;
+
+class CChildWaitInfo {
+public:
+ HWND m_dialog;
+ HANDLE m_child;
+ DWORD m_childID;
+ HANDLE m_ready;
+ HANDLE m_stop;
+};
+
+static const char* s_debugName[][2] = {
+ { TEXT("Error"), "ERROR" },
+ { TEXT("Warning"), "WARNING" },
+ { TEXT("Note"), "NOTE" },
+ { TEXT("Info"), "INFO" },
+ { TEXT("Debug"), "DEBUG" },
+ { TEXT("Debug1"), "DEBUG1" },
+ { TEXT("Debug2"), "DEBUG2" }
+};
+static const int s_defaultDebug = 1; // WARNING
+static const int s_minTestDebug = 3; // INFO
+
+HINSTANCE s_instance = NULL;
+
+static CGlobalOptions* s_globalOptions = NULL;
+static CAdvancedOptions* s_advancedOptions = NULL;
+static CHotkeyOptions* s_hotkeyOptions = NULL;
+static CScreensLinks* s_screensLinks = NULL;
+static CInfo* s_info = NULL;
+
+static bool s_userConfig = true;
+static time_t s_configTime = 0;
+static CConfig s_lastConfig;
+
+static const TCHAR* s_mainClass = TEXT("GoSynergy");
+static const TCHAR* s_layoutClass = TEXT("SynergyLayout");
+
+enum SaveMode {
+ SAVE_QUITING,
+ SAVE_NORMAL,
+ SAVE_QUIET
+};
+
+//
+// program arguments
+//
+
+#define ARG CArgs::s_instance
+
+class CArgs {
+public:
+ CArgs() { s_instance = this; }
+ ~CArgs() { s_instance = NULL; }
+
+public:
+ static CArgs* s_instance;
+ CConfig m_config;
+ CStringList m_screens;
+};
+
+CArgs* CArgs::s_instance = NULL;
+
+
+static
+BOOL CALLBACK
+addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+static
+bool
+isClientChecked(HWND hwnd)
+{
+ HWND child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO);
+ return isItemChecked(child);
+}
+
+static
+void
+enableMainWindowControls(HWND hwnd)
+{
+ bool client = isClientChecked(hwnd);
+ enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_LABEL, client);
+ enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT, client);
+ enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LABEL, !client);
+ enableItem(hwnd, IDC_MAIN_SCREENS, !client);
+ enableItem(hwnd, IDC_MAIN_OPTIONS, !client);
+ enableItem(hwnd, IDC_MAIN_HOTKEYS, !client);
+}
+
+static
+bool
+execApp(const char* app, const CString& cmdLine, PROCESS_INFORMATION* procInfo)
+{
+ // prepare startup info
+ STARTUPINFO startup;
+ startup.cb = sizeof(startup);
+ startup.lpReserved = NULL;
+ startup.lpDesktop = NULL;
+ startup.lpTitle = NULL;
+ startup.dwX = (DWORD)CW_USEDEFAULT;
+ startup.dwY = (DWORD)CW_USEDEFAULT;
+ startup.dwXSize = (DWORD)CW_USEDEFAULT;
+ startup.dwYSize = (DWORD)CW_USEDEFAULT;
+ startup.dwXCountChars = 0;
+ startup.dwYCountChars = 0;
+ startup.dwFillAttribute = 0;
+ startup.dwFlags = STARTF_FORCEONFEEDBACK;
+ startup.wShowWindow = SW_SHOWDEFAULT;
+ startup.cbReserved2 = 0;
+ startup.lpReserved2 = NULL;
+ startup.hStdInput = NULL;
+ startup.hStdOutput = NULL;
+ startup.hStdError = NULL;
+
+ // prepare path to app
+ CString appPath = getAppPath(app);
+
+ // put path to app in command line
+ CString commandLine = "\"";
+ commandLine += appPath;
+ commandLine += "\" ";
+ commandLine += cmdLine;
+
+ // start child
+ if (CreateProcess(NULL, (char*)commandLine.c_str(),
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_DEFAULT_ERROR_MODE |
+ CREATE_NEW_PROCESS_GROUP |
+ NORMAL_PRIORITY_CLASS,
+ NULL,
+ NULL,
+ &startup,
+ procInfo) == 0) {
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
+static
+CString
+getCommandLine(HWND hwnd, bool testing, bool silent)
+{
+ CString cmdLine;
+
+ // add constant testing args
+ if (testing) {
+ cmdLine += " -z --no-restart --no-daemon";
+ }
+
+ // can't start as service on NT
+ else if (!CArchMiscWindows::isWindows95Family()) {
+ cmdLine += " --no-daemon";
+ }
+
+ // get the server name
+ CString server;
+ bool isClient = isClientChecked(hwnd);
+ if (isClient) {
+ // check server name
+ HWND child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
+ server = getWindowText(child);
+ if (!ARG->m_config.isValidScreenName(server)) {
+ if (!silent) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_INVALID_SERVER_NAME).c_str(),
+ server.c_str()));
+ }
+ SetFocus(child);
+ return CString();
+ }
+
+ // compare server name to local host. a common error
+ // is to provide the client's name for the server. we
+ // don't bother to check the addresses though that'd be
+ // more accurate.
+ if (CStringUtil::CaselessCmp::equal(ARCH->getHostName(), server)) {
+ if (!silent) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_SERVER_IS_CLIENT).c_str(),
+ server.c_str()));
+ }
+ SetFocus(child);
+ return CString();
+ }
+ }
+
+ // debug level. always include this.
+ if (true) {
+ HWND child = getItem(hwnd, IDC_MAIN_DEBUG);
+ int debug = (int)SendMessage(child, CB_GETCURSEL, 0, 0);
+
+ // if testing then we force the debug level to be no less than
+ // s_minTestDebug. what's the point of testing if you can't
+ // see the debugging info?
+ if (testing && debug < s_minTestDebug) {
+ debug = s_minTestDebug;
+ }
+
+ cmdLine += " --debug ";
+ cmdLine += s_debugName[debug][1];
+ }
+
+ // add advanced options
+ cmdLine += s_advancedOptions->getCommandLine(isClient, server);
+
+ return cmdLine;
+}
+
+static
+bool
+launchApp(HWND hwnd, bool testing, HANDLE* thread, DWORD* threadID)
+{
+ if (thread != NULL) {
+ *thread = NULL;
+ }
+ if (threadID != NULL) {
+ *threadID = 0;
+ }
+
+ // start daemon if it's installed and we're not testing
+ if (!testing && CAutoStart::startDaemon()) {
+ return true;
+ }
+
+ // decide if client or server
+ const bool isClient = isClientChecked(hwnd);
+ const char* app = isClient ? CLIENT_APP : SERVER_APP;
+
+ // prepare command line
+ CString cmdLine = getCommandLine(hwnd, testing, false);
+ if (cmdLine.empty()) {
+ return false;
+ }
+
+ // start child
+ PROCESS_INFORMATION procInfo;
+ if (!execApp(app, cmdLine, &procInfo)) {
+ showError(hwnd, CStringUtil::format(
+ getString(IDS_STARTUP_FAILED).c_str(),
+ getErrorString(GetLastError()).c_str()));
+ return false;
+ }
+
+ // don't need process handle
+ CloseHandle(procInfo.hProcess);
+
+ // save thread handle and thread ID if desired
+ if (thread != NULL) {
+ *thread = procInfo.hThread;
+ }
+ if (threadID != NULL) {
+ *threadID = procInfo.dwThreadId;
+ }
+
+ return true;
+}
+
+static
+BOOL CALLBACK
+waitDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ // only one wait dialog at a time!
+ static CChildWaitInfo* info = NULL;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // save info pointer
+ info = reinterpret_cast(lParam);
+
+ // save hwnd
+ info->m_dialog = hwnd;
+
+ // signal ready
+ SetEvent(info->m_ready);
+
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ case IDOK:
+ // signal stop
+ SetEvent(info->m_stop);
+
+ // done
+ EndDialog(hwnd, 0);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static
+DWORD WINAPI
+waitForChildThread(LPVOID vinfo)
+{
+ CChildWaitInfo* info = reinterpret_cast(vinfo);
+
+ // wait for ready
+ WaitForSingleObject(info->m_ready, INFINITE);
+
+ // wait for thread to complete or stop event
+ HANDLE handles[2];
+ handles[0] = info->m_child;
+ handles[1] = info->m_stop;
+ DWORD n = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+
+ // if stop was raised then terminate child and wait for it
+ if (n == WAIT_OBJECT_0 + 1) {
+ PostThreadMessage(info->m_childID, WM_QUIT, 0, 0);
+ WaitForSingleObject(info->m_child, INFINITE);
+ }
+
+ // otherwise post IDOK to dialog box
+ else {
+ PostMessage(info->m_dialog, WM_COMMAND, MAKEWPARAM(IDOK, 0), 0);
+ }
+
+ return 0;
+}
+
+static
+void
+waitForChild(HWND hwnd, HANDLE thread, DWORD threadID)
+{
+ // prepare info for child wait dialog and thread
+ CChildWaitInfo info;
+ info.m_dialog = NULL;
+ info.m_child = thread;
+ info.m_childID = threadID;
+ info.m_ready = CreateEvent(NULL, TRUE, FALSE, NULL);
+ info.m_stop = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ // create a thread to wait on the child thread and event
+ DWORD id;
+ HANDLE waiter = CreateThread(NULL, 0, &waitForChildThread, &info,0, &id);
+
+ // do dialog that let's the user terminate the test
+ DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_WAIT), hwnd,
+ (DLGPROC)waitDlgProc, (LPARAM)&info);
+
+ // force the waiter thread to finish and wait for it
+ SetEvent(info.m_ready);
+ SetEvent(info.m_stop);
+ WaitForSingleObject(waiter, INFINITE);
+
+ // clean up
+ CloseHandle(waiter);
+ CloseHandle(info.m_ready);
+ CloseHandle(info.m_stop);
+}
+
+static
+void
+initMainWindow(HWND hwnd)
+{
+ // append version number to title
+ CString titleFormat = getString(IDS_TITLE);
+ setWindowText(hwnd, CStringUtil::format(titleFormat.c_str(), VERSION));
+
+ // load configuration
+ bool configLoaded =
+ loadConfig(ARG->m_config, s_configTime, s_userConfig);
+ if (configLoaded) {
+ s_lastConfig = ARG->m_config;
+ }
+
+ // get settings from registry
+ bool isServer = configLoaded;
+ int debugLevel = s_defaultDebug;
+ CString server;
+ HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath());
+ if (key != NULL) {
+ if (isServer && CArchMiscWindows::hasValue(key, "isServer")) {
+ isServer = (CArchMiscWindows::readValueInt(key, "isServer") != 0);
+ }
+ if (CArchMiscWindows::hasValue(key, "debug")) {
+ debugLevel = static_cast(
+ CArchMiscWindows::readValueInt(key, "debug"));
+ if (debugLevel < 0) {
+ debugLevel = 0;
+ }
+ else if (debugLevel > CLog::kDEBUG2) {
+ debugLevel = CLog::kDEBUG2;
+ }
+ }
+ server = CArchMiscWindows::readValueString(key, "server");
+ CArchMiscWindows::closeKey(key);
+ }
+
+ // choose client/server radio buttons
+ HWND child;
+ child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO);
+ setItemChecked(child, !isServer);
+ child = getItem(hwnd, IDC_MAIN_SERVER_RADIO);
+ setItemChecked(child, isServer);
+
+ // set server name
+ child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
+ setWindowText(child, server);
+
+ // debug level
+ child = getItem(hwnd, IDC_MAIN_DEBUG);
+ for (unsigned int i = 0; i < sizeof(s_debugName) /
+ sizeof(s_debugName[0]); ++i) {
+ SendMessage(child, CB_ADDSTRING, 0, (LPARAM)s_debugName[i][0]);
+ }
+ SendMessage(child, CB_SETCURSEL, debugLevel, 0);
+
+ // update controls
+ enableMainWindowControls(hwnd);
+}
+
+static
+bool
+saveMainWindow(HWND hwnd, SaveMode mode, CString* cmdLineOut = NULL)
+{
+ DWORD errorID = 0;
+ CString arg;
+ CString cmdLine;
+
+ // save dialog state
+ bool isClient = isClientChecked(hwnd);
+ HKEY key = CArchMiscWindows::addKey(HKEY_CURRENT_USER, getSettingsPath());
+ if (key != NULL) {
+ HWND child;
+ child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
+ CArchMiscWindows::setValue(key, "server", getWindowText(child));
+ child = getItem(hwnd, IDC_MAIN_DEBUG);
+ CArchMiscWindows::setValue(key, "debug",
+ SendMessage(child, CB_GETCURSEL, 0, 0));
+ CArchMiscWindows::setValue(key, "isServer", isClient ? 0 : 1);
+ CArchMiscWindows::closeKey(key);
+ }
+
+ // save user's configuration
+ if (!s_userConfig || ARG->m_config != s_lastConfig) {
+ time_t t;
+ if (!saveConfig(ARG->m_config, false, t)) {
+ errorID = IDS_SAVE_FAILED;
+ arg = getErrorString(GetLastError());
+ goto failed;
+ }
+ if (s_userConfig) {
+ s_configTime = t;
+ s_lastConfig = ARG->m_config;
+ }
+ }
+
+ // save autostart configuration
+ if (CAutoStart::isDaemonInstalled()) {
+ if (s_userConfig || ARG->m_config != s_lastConfig) {
+ time_t t;
+ if (!saveConfig(ARG->m_config, true, t)) {
+ errorID = IDS_AUTOSTART_SAVE_FAILED;
+ arg = getErrorString(GetLastError());
+ goto failed;
+ }
+ if (!s_userConfig) {
+ s_configTime = t;
+ s_lastConfig = ARG->m_config;
+ }
+ }
+ }
+
+ // get autostart command
+ cmdLine = getCommandLine(hwnd, false, mode == SAVE_QUITING);
+ if (cmdLineOut != NULL) {
+ *cmdLineOut = cmdLine;
+ }
+ if (cmdLine.empty()) {
+ return (mode == SAVE_QUITING);
+ }
+
+ // save autostart command
+ if (CAutoStart::isDaemonInstalled()) {
+ try {
+ CAutoStart::reinstallDaemon(isClient, cmdLine);
+ CAutoStart::uninstallDaemons(!isClient);
+ }
+ catch (XArchDaemon& e) {
+ errorID = IDS_INSTALL_GENERIC_ERROR;
+ arg = e.what();
+ goto failed;
+ }
+ }
+
+ return true;
+
+failed:
+ CString errorMessage =
+ CStringUtil::format(getString(errorID).c_str(), arg.c_str());
+ if (mode == SAVE_QUITING) {
+ errorMessage += "\n";
+ errorMessage += getString(IDS_UNSAVED_DATA_REALLY_QUIT);
+ if (askVerify(hwnd, errorMessage)) {
+ return true;
+ }
+ }
+ else if (mode == SAVE_NORMAL) {
+ showError(hwnd, errorMessage);
+ }
+ return false;
+}
+
+static
+LRESULT CALLBACK
+mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) != WA_INACTIVE) {
+ // activated
+
+ // see if the configuration changed
+ if (isConfigNewer(s_configTime, s_userConfig)) {
+ CString message = getString(IDS_CONFIG_CHANGED);
+ if (askVerify(hwnd, message)) {
+ time_t configTime;
+ bool userConfig;
+ CConfig newConfig;
+ if (loadConfig(newConfig, configTime, userConfig) &&
+ userConfig == s_userConfig) {
+ ARG->m_config = newConfig;
+ s_lastConfig = ARG->m_config;
+ }
+ else {
+ message = getString(IDS_LOAD_FAILED);
+ showError(hwnd, message);
+ s_lastConfig = CConfig();
+ }
+ }
+ }
+ }
+ else {
+ // deactivated; write configuration
+ if (!isShowingDialog()) {
+ saveMainWindow(hwnd, SAVE_QUIET);
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ // save data
+ if (saveMainWindow(hwnd, SAVE_QUITING)) {
+ // quit
+ PostQuitMessage(0);
+ }
+ return 0;
+
+ case IDOK:
+ case IDC_MAIN_TEST: {
+ // note if testing
+ const bool testing = (LOWORD(wParam) == IDC_MAIN_TEST);
+
+ // save data
+ if (saveMainWindow(hwnd, SAVE_NORMAL)) {
+ // launch child app
+ DWORD threadID;
+ HANDLE thread;
+ if (!launchApp(hwnd, testing, &thread, &threadID)) {
+ return 0;
+ }
+
+ // handle child program
+ if (testing) {
+ // wait for process to stop, allowing the user to kill it
+ waitForChild(hwnd, thread, threadID);
+
+ // clean up
+ CloseHandle(thread);
+ }
+ else {
+ // don't need thread handle
+ if (thread != NULL) {
+ CloseHandle(thread);
+ }
+
+ // notify of success
+ askOkay(hwnd, getString(IDS_STARTED_TITLE),
+ getString(IDS_STARTED));
+
+ // quit
+ PostQuitMessage(0);
+ }
+ }
+ return 0;
+ }
+
+ case IDC_MAIN_AUTOSTART: {
+ CString cmdLine;
+ if (saveMainWindow(hwnd, SAVE_NORMAL, &cmdLine)) {
+ // run dialog
+ CAutoStart autoStart(hwnd, !isClientChecked(hwnd), cmdLine);
+ autoStart.doModal();
+ }
+ return 0;
+ }
+
+ case IDC_MAIN_CLIENT_RADIO:
+ case IDC_MAIN_SERVER_RADIO:
+ enableMainWindowControls(hwnd);
+ return 0;
+
+ case IDC_MAIN_SCREENS:
+ s_screensLinks->doModal();
+ break;
+
+ case IDC_MAIN_OPTIONS:
+ s_globalOptions->doModal();
+ break;
+
+ case IDC_MAIN_ADVANCED:
+ s_advancedOptions->doModal(isClientChecked(hwnd));
+ break;
+
+ case IDC_MAIN_HOTKEYS:
+ s_hotkeyOptions->doModal();
+ break;
+
+ case IDC_MAIN_INFO:
+ s_info->doModal();
+ break;
+ }
+
+ default:
+ break;
+ }
+ return DefDlgProc(hwnd, message, wParam, lParam);
+}
+
+int WINAPI
+WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmdLine, int nCmdShow)
+{
+ CArch arch(instance);
+ CLOG;
+ CArgs args;
+
+ s_instance = instance;
+
+ // if "/uninstall" is on the command line then just stop and
+ // uninstall the service and quit. this is the only option
+ // but we ignore any others.
+ if (CString(cmdLine).find("/uninstall") != CString::npos) {
+ CAutoStart::uninstallDaemons(false);
+ CAutoStart::uninstallDaemons(true);
+ return 0;
+ }
+
+ // register main window (dialog) class
+ WNDCLASSEX classInfo;
+ classInfo.cbSize = sizeof(classInfo);
+ classInfo.style = CS_HREDRAW | CS_VREDRAW;
+ classInfo.lpfnWndProc = &mainWndProc;
+ classInfo.cbClsExtra = 0;
+ classInfo.cbWndExtra = DLGWINDOWEXTRA;
+ classInfo.hInstance = instance;
+ classInfo.hIcon = (HICON)LoadImage(instance,
+ MAKEINTRESOURCE(IDI_SYNERGY),
+ IMAGE_ICON,
+ 32, 32, LR_SHARED);
+ classInfo.hCursor = LoadCursor(NULL, IDC_ARROW);
+ classInfo.hbrBackground = reinterpret_cast(COLOR_3DFACE + 1);
+ classInfo.lpszMenuName = NULL;
+ classInfo.lpszClassName = s_mainClass;
+ classInfo.hIconSm = (HICON)LoadImage(instance,
+ MAKEINTRESOURCE(IDI_SYNERGY),
+ IMAGE_ICON,
+ 16, 16, LR_SHARED);
+ RegisterClassEx(&classInfo);
+
+ // create main window
+ HWND mainWindow = CreateDialog(s_instance,
+ MAKEINTRESOURCE(IDD_MAIN), 0, NULL);
+
+ // prep windows
+ initMainWindow(mainWindow);
+ s_globalOptions = new CGlobalOptions(mainWindow, &ARG->m_config);
+ s_advancedOptions = new CAdvancedOptions(mainWindow, &ARG->m_config);
+ s_hotkeyOptions = new CHotkeyOptions(mainWindow, &ARG->m_config);
+ s_screensLinks = new CScreensLinks(mainWindow, &ARG->m_config);
+ s_info = new CInfo(mainWindow);
+
+ // show window
+ ShowWindow(mainWindow, nCmdShow);
+
+ // main loop
+ MSG msg;
+ bool done = false;
+ do {
+ switch (GetMessage(&msg, NULL, 0, 0)) {
+ case -1:
+ // error
+ break;
+
+ case 0:
+ // quit
+ done = true;
+ break;
+
+ default:
+ if (!IsDialogMessage(mainWindow, &msg)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ break;
+ }
+ } while (!done);
+
+ return msg.wParam;
+}
diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc
new file mode 100644
index 00000000..ff72d069
--- /dev/null
+++ b/cmd/launcher/launcher.rc
@@ -0,0 +1,617 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include
+#if !defined(IDC_STATIC)
+#define IDC_STATIC (-1)
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include \r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 199
+STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU
+CAPTION "Synergy"
+CLASS "GoSynergy"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "Choose to share or use a shared keyboard and mouse, provide the requested information, then click Test to check your settings or Start to save your settings and start Synergy.",
+ IDC_STATIC,7,7,286,19
+ GROUPBOX "",IDC_STATIC,7,29,286,36
+ GROUPBOX "",IDC_STATIC,7,72,286,36
+ GROUPBOX "Options",IDC_STATIC,7,115,286,56
+ CONTROL "&Use another computer's shared keyboard and mouse (client)",
+ IDC_MAIN_CLIENT_RADIO,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP | WS_TABSTOP,11,29,205,10
+ CONTROL "Share this computer's keyboard and mouse (server)",
+ IDC_MAIN_SERVER_RADIO,"Button",BS_AUTORADIOBUTTON,11,72,
+ 177,10
+ LTEXT "Other Computer's &Host Name:",
+ IDC_MAIN_CLIENT_SERVER_NAME_LABEL,12,46,94,8
+ EDITTEXT IDC_MAIN_CLIENT_SERVER_NAME_EDIT,111,44,106,12,
+ ES_AUTOHSCROLL
+ LTEXT "&Screens && Links:",IDC_MAIN_SERVER_SCREENS_LABEL,12,89,
+ 54,8
+ PUSHBUTTON "Configure...",IDC_MAIN_SCREENS,71,86,50,14
+ PUSHBUTTON "&Options...",IDC_MAIN_OPTIONS,12,129,50,14
+ PUSHBUTTON "Hot &Keys...",IDC_MAIN_HOTKEYS,68,129,50,14
+ PUSHBUTTON "Adva&nced...",IDC_MAIN_ADVANCED,124,129,50,14
+ PUSHBUTTON "&AutoStart...",IDC_MAIN_AUTOSTART,180,129,50,14
+ LTEXT "&Logging Level:",IDC_STATIC,12,154,48,8
+ COMBOBOX IDC_MAIN_DEBUG,68,151,61,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "&Info",IDC_MAIN_INFO,7,178,50,14
+ DEFPUSHBUTTON "&Test",IDC_MAIN_TEST,131,179,50,14
+ PUSHBUTTON "Start",IDOK,187,179,50,14
+ PUSHBUTTON "Quit",IDCANCEL,243,179,50,14
+END
+
+IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 254
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
+CAPTION "Add Screen"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "&Screen Name:",IDC_STATIC,7,9,46,8
+ EDITTEXT IDC_ADD_SCREEN_NAME_EDIT,79,7,106,12,ES_AUTOHSCROLL
+ LTEXT "&Aliases:",IDC_STATIC,7,25,25,8
+ EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,24,ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+ GROUPBOX "Options",IDC_STATIC,7,55,178,54
+ LTEXT "If your Caps, Num, or Scroll Lock keys behave strangely on this client screen then try turning the half-duplex options on and reconnect the client.",
+ IDC_STATIC,13,65,165,25
+ CONTROL "&Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,13,93,51,10
+ CONTROL "&Num Lock",IDC_ADD_HD_NUM_CHECK,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,69,93,51,10
+ CONTROL "Sc&roll Lock",IDC_ADD_HD_SCROLL_CHECK,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,125,93,51,10
+ GROUPBOX "Modifiers",IDC_STATIC,7,113,178,65
+ LTEXT "Shift",IDC_STATIC,13,129,15,8
+ COMBOBOX IDC_ADD_MOD_SHIFT,37,126,48,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "Ctrl",IDC_STATIC,13,144,11,8
+ COMBOBOX IDC_ADD_MOD_CTRL,37,142,48,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "Alt",IDC_STATIC,13,160,9,8
+ COMBOBOX IDC_ADD_MOD_ALT,37,158,48,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "Meta",IDC_STATIC,101,128,17,8
+ COMBOBOX IDC_ADD_MOD_META,125,126,48,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "Super",IDC_STATIC,101,144,20,8
+ COMBOBOX IDC_ADD_MOD_SUPER,125,142,48,60,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Dead Corners",IDC_STATIC,7,183,178,43
+ LTEXT "Don't switch in these corners:",IDC_STATIC,14,198,52,18
+ CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME,68,193,47,28
+ CONTROL "",IDC_ADD_DC_TOP_LEFT,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,76,197,16,8
+ CONTROL "",IDC_ADD_DC_TOP_RIGHT,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,98,197,16,8
+ CONTROL "",IDC_ADD_DC_BOTTOM_LEFT,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,76,210,16,8
+ CONTROL "",IDC_ADD_DC_BOTTOM_RIGHT,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,98,210,16,8
+ LTEXT "Size",IDC_STATIC,120,202,14,8
+ EDITTEXT IDC_ADD_DC_SIZE,139,200,40,12,ES_AUTOHSCROLL | ES_NUMBER
+ DEFPUSHBUTTON "OK",IDOK,79,233,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,135,233,50,14
+END
+
+IDD_WAIT DIALOG DISCARDABLE 0, 0, 186, 54
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
+CAPTION "Running Test..."
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "Stop",IDOK,129,33,50,14
+ LTEXT "Running synergy. Press Stop to end the test.",
+ IDC_STATIC,7,7,172,15
+END
+
+IDD_AUTOSTART DIALOG DISCARDABLE 0, 0, 195, 189
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Auto Start"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "Close",IDCANCEL,138,168,50,14
+ LTEXT "Synergy can be configured to start automatically when you log in. If you have sufficient access rights, you can instead configure synergy to start automatically when your computer starts.",
+ IDC_STATIC,7,7,181,33
+ LTEXT "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself.",
+ IDC_AUTOSTART_PERMISSION_MSG,7,69,181,17
+ LTEXT "Synergy is configured to start automatically when the system starts.",
+ IDC_AUTOSTART_INSTALLED_MSG,7,93,181,17
+ GROUPBOX "When &You Log In",IDC_STATIC,7,119,84,40
+ PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_USER,23,133,50,14
+ GROUPBOX "When &Computer Starts",IDC_STATIC,104,119,84,40
+ PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_SYSTEM,119,134,50,14
+ LTEXT "Synergy can be configured to start automatically when the computer starts or when you log in but not both.",
+ IDC_STATIC,7,43,181,17
+END
+
+IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 290
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Options"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "It's easy to unintentionally switch screens when the pointer is near a screen's edge. Synergy can prevent switching until certain conditions are met to reduce unintentional switching.",
+ IDC_STATIC,7,7,191,26
+ LTEXT "Synergy can wait to switch until the cursor has been at a screen's edge for some amount of time.",
+ IDC_STATIC,7,37,193,16
+ CONTROL "Switch after waiting",IDC_GLOBAL_DELAY_CHECK,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,7,59,77,10
+ EDITTEXT IDC_GLOBAL_DELAY_TIME,112,58,45,12,ES_AUTOHSCROLL |
+ ES_NUMBER
+ LTEXT "ms",IDC_STATIC,159,60,10,8
+ LTEXT "Synergy can switch only when the cursor hits a screen edge twice within some amount of time.",
+ IDC_STATIC,7,77,193,16
+ CONTROL "Switch on double tap within",IDC_GLOBAL_TWO_TAP_CHECK,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,99,103,10
+ EDITTEXT IDC_GLOBAL_TWO_TAP_TIME,112,98,45,12,ES_AUTOHSCROLL |
+ ES_NUMBER
+ LTEXT "ms",IDC_STATIC,159,100,10,8
+ LTEXT "Synergy can periodically check that clients are still alive and connected. Use this only if synergy doesn't detect when clients disconnect.",
+ IDC_STATIC,7,122,193,24
+ CONTROL "Check clients every",IDC_GLOBAL_HEARTBEAT_CHECK,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,7,153,78,10
+ EDITTEXT IDC_GLOBAL_HEARTBEAT_TIME,112,152,45,12,ES_AUTOHSCROLL |
+ ES_NUMBER
+ LTEXT "ms",IDC_STATIC,159,154,10,8
+ LTEXT "Synergy can synchronize screen savers across all screens.",
+ IDC_STATIC,7,176,193,8
+ CONTROL "Synchronize screen savers",IDC_GLOBAL_SCREENSAVER_SYNC,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,192,101,10
+ LTEXT "Relative mouse moves on secondary screens.",IDC_STATIC,
+ 7,213,193,8
+ CONTROL "Use relative mouse moves",IDC_GLOBAL_RELATIVE_MOVES,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,229,99,10
+ CONTROL "Don't take foreground window on Windows servers",
+ IDC_GLOBAL_LEAVE_FOREGROUND,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,7,250,177,10
+ DEFPUSHBUTTON "OK",IDOK,94,269,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,150,269,50,14
+END
+
+IDD_ADVANCED_OPTIONS DIALOG DISCARDABLE 0, 0, 230, 186
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Advanced Options"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "Synergy normally uses this computer's name as its screen name. Enter another name here if you want to use a different screen name.",
+ IDC_STATIC,7,7,216,19
+ LTEXT "Screen &Name:",IDC_STATIC,7,34,46,8
+ EDITTEXT IDC_ADVANCED_NAME_EDIT,63,32,106,12,ES_AUTOHSCROLL
+ LTEXT "Synergy normally uses a particular network port number. Enter an alternative port here. (The server and all clients must use the same port number.)",
+ IDC_STATIC,7,56,216,26
+ LTEXT "&Port:",IDC_STATIC,7,90,16,8
+ EDITTEXT IDC_ADVANCED_PORT_EDIT,63,88,40,12,ES_AUTOHSCROLL |
+ ES_NUMBER
+ LTEXT "The server normally listens for client connections on all network interfaces. Enter the address of a particular interface to listen on just that interface.",
+ IDC_STATIC,7,110,216,26
+ LTEXT "&Interface:",IDC_STATIC,7,144,31,8
+ EDITTEXT IDC_ADVANCED_INTERFACE_EDIT,63,142,81,12,ES_AUTOHSCROLL
+ PUSHBUTTON "&Defaults",IDC_ADVANCED_DEFAULTS,7,165,50,14
+ DEFPUSHBUTTON "OK",IDOK,118,165,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,173,165,50,14
+END
+
+IDD_SCREENS_LINKS DIALOG DISCARDABLE 0, 0, 354, 213
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Screens & Links"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "&Screens:",IDC_STATIC,7,7,29,8
+ LISTBOX IDC_SCREENS_SCREENS,7,18,100,36,LBS_SORT |
+ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "+",IDC_SCREENS_ADD_SCREEN,7,57,17,14
+ PUSHBUTTON "-",IDC_SCREENS_REMOVE_SCREEN,28,57,17,14
+ PUSHBUTTON "Edit",IDC_SCREENS_EDIT_SCREEN,49,57,24,14
+ LTEXT "&Links:",IDC_STATIC,7,83,20,8
+ LISTBOX IDC_SCREENS_LINKS,7,94,339,59,LBS_SORT |
+ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ EDITTEXT IDC_SCREENS_SRC_START,7,156,16,12,ES_AUTOHSCROLL |
+ ES_NUMBER
+ LTEXT "to",IDC_STATIC,25,158,8,8
+ EDITTEXT IDC_SCREENS_SRC_END,33,156,16,12,ES_AUTOHSCROLL |
+ ES_NUMBER
+ LTEXT "% of the",IDC_STATIC,52,158,27,8
+ COMBOBOX IDC_SCREENS_SRC_SIDE,80,156,48,69,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "of",IDC_STATIC,129,158,8,8
+ COMBOBOX IDC_SCREENS_SRC_SCREEN,139,156,59,53,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "goes to",IDC_STATIC,200,158,24,8
+ EDITTEXT IDC_SCREENS_DST_START,225,156,16,12,ES_AUTOHSCROLL |
+ ES_NUMBER
+ LTEXT "to",IDC_STATIC,243,158,8,8
+ EDITTEXT IDC_SCREENS_DST_END,251,156,16,12,ES_AUTOHSCROLL |
+ ES_NUMBER
+ LTEXT "% of",IDC_STATIC,270,158,15,8
+ COMBOBOX IDC_SCREENS_DST_SCREEN,287,156,59,53,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "+",IDC_SCREENS_ADD_LINK,7,172,17,14
+ PUSHBUTTON "-",IDC_SCREENS_REMOVE_LINK,28,172,17,14
+ DEFPUSHBUTTON "OK",IDOK,241,192,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,297,192,50,14
+ LTEXT "Source edge overlaps an existing edge.",
+ IDC_SCREENS_OVERLAP_ERROR,72,175,126,8,NOT WS_VISIBLE |
+ NOT WS_GROUP
+END
+
+IDD_INFO DIALOG DISCARDABLE 0, 0, 186, 95
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Info"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "Version:",IDC_STATIC,7,7,26,8
+ EDITTEXT IDC_INFO_VERSION,52,7,127,12,ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_BORDER
+ LTEXT "Hostname:",IDC_STATIC,7,19,35,8
+ EDITTEXT IDC_INFO_HOSTNAME,52,19,127,12,ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_BORDER
+ LTEXT "IP Address:",IDC_STATIC,7,31,37,8
+ EDITTEXT IDC_INFO_IP_ADDRESS,52,31,127,12,ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_BORDER
+ LTEXT "User Config:",IDC_STATIC,7,43,40,8
+ EDITTEXT IDC_INFO_USER_CONFIG,52,43,127,12,ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_BORDER
+ LTEXT "Sys Config:",IDC_STATIC,7,55,36,8
+ EDITTEXT IDC_INFO_SYS_CONFIG,52,55,127,12,ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_BORDER
+ DEFPUSHBUTTON "OK",IDOK,129,74,50,14
+END
+
+IDD_HOTKEY_OPTIONS DIALOG DISCARDABLE 0, 0, 360, 151
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Hot Keys"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "&Hot Keys:",IDC_STATIC,7,7,32,8
+ LISTBOX IDC_HOTKEY_HOTKEYS,7,18,169,88,LBS_SORT |
+ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "+",IDC_HOTKEY_ADD_HOTKEY,7,109,17,14
+ PUSHBUTTON "-",IDC_HOTKEY_REMOVE_HOTKEY,28,109,17,14
+ PUSHBUTTON "Edit",IDC_HOTKEY_EDIT_HOTKEY,49,109,24,14
+ LTEXT "&Actions:",IDC_STATIC,183,7,26,8
+ LISTBOX IDC_HOTKEY_ACTIONS,183,18,169,88,LBS_SORT |
+ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "+",IDC_HOTKEY_ADD_ACTION,183,109,17,14
+ PUSHBUTTON "-",IDC_HOTKEY_REMOVE_ACTION,204,109,17,14
+ PUSHBUTTON "Edit",IDC_HOTKEY_EDIT_ACTION,225,109,24,14
+ DEFPUSHBUTTON "OK",IDOK,302,130,50,14
+END
+
+IDD_HOTKEY_CONDITION DIALOG DISCARDABLE 0, 0, 183, 58
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Hot Key"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "Enter &new hot key or mouse button:",IDC_STATIC,7,7,113,
+ 8
+ EDITTEXT IDC_HOTKEY_CONDITION_HOTKEY,7,17,169,12,ES_WANTRETURN
+ DEFPUSHBUTTON "OK",IDOK,70,37,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,126,37,50,14
+END
+
+IDD_HOTKEY_ACTION DIALOG DISCARDABLE 0, 0, 183, 218
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Action"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "&Action:",IDC_STATIC,7,7,23,8
+ CONTROL "Press:",IDC_HOTKEY_ACTION_DOWN,"Button",
+ BS_AUTORADIOBUTTON | WS_TABSTOP,7,19,35,10
+ CONTROL "Release:",IDC_HOTKEY_ACTION_UP,"Button",
+ BS_AUTORADIOBUTTON,7,31,44,10
+ CONTROL "Press && Release:",IDC_HOTKEY_ACTION_DOWNUP,"Button",
+ BS_AUTORADIOBUTTON,7,43,69,10
+ CONTROL "Switch To Screen:",IDC_HOTKEY_ACTION_SWITCH_TO,"Button",
+ BS_AUTORADIOBUTTON,7,85,75,10
+ CONTROL "Switch In Direction:",IDC_HOTKEY_ACTION_SWITCH_IN,
+ "Button",BS_AUTORADIOBUTTON,7,101,77,10
+ CONTROL "Lock Cursor to Screen:",IDC_HOTKEY_ACTION_LOCK,"Button",
+ BS_AUTORADIOBUTTON,7,117,89,10
+ CONTROL "Keyboard broadcasting:",
+ IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST,"Button",
+ BS_AUTORADIOBUTTON,7,133,89,10
+ LTEXT "&Hot key or mouse button:",IDC_STATIC,7,55,80,8
+ EDITTEXT IDC_HOTKEY_ACTION_HOTKEY,7,67,152,12,ES_WANTRETURN
+ PUSHBUTTON "...",IDC_HOTKEY_ACTION_SCREENS,162,67,14,12
+ COMBOBOX IDC_HOTKEY_ACTION_SWITCH_TO_LIST,87,83,89,62,
+ CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ COMBOBOX IDC_HOTKEY_ACTION_SWITCH_IN_LIST,106,99,70,66,
+ CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ COMBOBOX IDC_HOTKEY_ACTION_LOCK_LIST,106,115,70,58,
+ CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ COMBOBOX IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST,106,131,53,58,
+ CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "...",IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS,
+ 162,131,14,12
+ LTEXT "Action takes place &when:",IDC_STATIC,7,153,81,8
+ CONTROL "Hot key is pressed",IDC_HOTKEY_ACTION_ON_ACTIVATE,
+ "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,165,74,10
+ CONTROL "Hot key is released",IDC_HOTKEY_ACTION_ON_DEACTIVATE,
+ "Button",BS_AUTORADIOBUTTON,7,177,76,10
+ DEFPUSHBUTTON "OK",IDOK,70,197,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,126,197,50,14
+END
+
+IDD_HOTKEY_SCREENS DIALOG DISCARDABLE 0, 0, 237, 79
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Target Screens"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "&Available screens:",IDC_STATIC,7,7,58,8
+ LISTBOX IDC_HOTKEY_SCREENS_SRC,7,17,100,36,LBS_NOINTEGRALHEIGHT |
+ LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP
+ LISTBOX IDC_HOTKEY_SCREENS_DST,130,17,100,36,
+ LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL |
+ WS_TABSTOP
+ PUSHBUTTON "-->",IDC_HOTKEY_SCREENS_ADD,109,21,17,14
+ PUSHBUTTON "<--",IDC_HOTKEY_SCREENS_REMOVE,109,38,17,14
+ DEFPUSHBUTTON "OK",IDOK,124,58,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,180,58,50,14
+ LTEXT "&Send action to screens:",IDC_STATIC,130,7,76,8
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_MAIN, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 293
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 192
+ END
+
+ IDD_ADD, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 185
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 247
+ END
+
+ IDD_WAIT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 179
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 47
+ END
+
+ IDD_AUTOSTART, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 188
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 182
+ END
+
+ IDD_GLOBAL_OPTIONS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 200
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 283
+ END
+
+ IDD_ADVANCED_OPTIONS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 223
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 179
+ END
+
+ IDD_SCREENS_LINKS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 347
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 206
+ END
+
+ IDD_INFO, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 179
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 88
+ END
+
+ IDD_HOTKEY_OPTIONS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 353
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 144
+ END
+
+ IDD_HOTKEY_CONDITION, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 176
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 51
+ END
+
+ IDD_HOTKEY_ACTION, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 176
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 195
+ END
+
+ IDD_HOTKEY_SCREENS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 230
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 72
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_SYNERGY ICON DISCARDABLE "synergy.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ERROR "Error"
+ IDS_INVALID_SCREEN_NAME "Screen name `%{1}' is invalid."
+ IDS_DUPLICATE_SCREEN_NAME "The screen name `%{1}' is already being used."
+ IDS_SCREEN_NAME_IS_ALIAS "A name may not be an alias of itself."
+ IDS_VERIFY "Confirm"
+ IDS_UNSAVED_DATA_REALLY_QUIT "You have unsaved changes. Really quit?"
+ IDS_UNKNOWN_SCREEN_NAME "The screen name `%{1}' is not in the layout."
+ IDS_INVALID_PORT "The port `%{1}' is invalid. It must be between 1 and 65535 inclusive. %{2} is the standard port."
+ IDS_SAVE_FAILED "Failed to save configuration: %{1}"
+ IDS_STARTUP_FAILED "Failed to start synergy: %{1}"
+ IDS_STARTED_TITLE "Started"
+ IDS_STARTED "Synergy was successfully started. Use the task manager or tray icon to terminate it."
+ IDS_UNINSTALL_TITLE "Removed Auto-Start"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_AUTOSTART_PERMISSION_SYSTEM
+ "You have sufficient access rights to install and uninstall Auto Start for all users."
+ IDS_AUTOSTART_PERMISSION_USER
+ "You have sufficient access rights to install and uninstall Auto Start for just yourself."
+ IDS_AUTOSTART_PERMISSION_NONE
+ "You do not have sufficient access rights to install or uninstall Auto Start."
+ IDS_AUTOSTART_INSTALLED_SYSTEM
+ "Synergy is configured to start automatically when the system starts."
+ IDS_AUTOSTART_INSTALLED_USER
+ "Synergy is configured to start automatically when you log in."
+ IDS_AUTOSTART_INSTALLED_NONE
+ "Synergy is not configured to start automatically."
+ IDS_INSTALL_LABEL "Install"
+ IDS_UNINSTALL_LABEL "Uninstall"
+ IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}"
+ IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}"
+ IDS_INSTALL_TITLE "Installed Auto-Start"
+ IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will automatically start each time you start your computer."
+ IDS_INSTALLED_USER "Installed auto-start. Synergy will automatically start each time you log in."
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_UNINSTALLED_SYSTEM "Removed auto-start. Synergy will not automatically start each time you start or reboot your computer."
+ IDS_UNINSTALLED_USER "Removed auto-start. Synergy will not automatically start each time you log in."
+ IDS_INVALID_SERVER_NAME "Server name `%{1}' is invalid."
+ IDS_TITLE "Synergy - Version %{1}"
+ IDS_SERVER_IS_CLIENT "Please enter the name of the computer sharing a\nkeyboard and mouse, not the name of this computer,\nin the Other Computer's Host Name field."
+ IDS_ADD_SCREEN "Add Screen"
+ IDS_EDIT_SCREEN "Edit Screen %{1}"
+ IDS_ERROR_CODE "Error code: %{1}"
+ IDS_AUTOSTART_PERMISSION_ALL
+ "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself."
+ IDS_INVALID_INTERFACE_NAME "The interface '%{1}' is invalid: %{2}"
+ IDS_INVALID_CORNER_SIZE "The dead corner size %{1} is invalid; it must be 0 or higher."
+ IDS_NEW_LINK "[New Link]"
+ IDS_SIDE_LEFT "left of"
+ IDS_SIDE_RIGHT "right of"
+ IDS_SIDE_TOP "above"
+ IDS_SIDE_BOTTOM "below"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_LINK_FORMAT "%{4}%{5} is %{3} %{1}%{2}"
+ IDS_LINK_INTERVAL_FORMAT "(%{1},%{2})"
+ IDS_EDGE_LEFT "left"
+ IDS_EDGE_RIGHT "right"
+ IDS_EDGE_TOP "top"
+ IDS_EDGE_BOTTOM "bottom"
+ IDS_AUTOSTART_SAVE_FAILED "Failed to save autostart configuration: %{1}"
+ IDS_LOAD_FAILED "Failed to load configuration."
+ IDS_CONFIG_CHANGED "Configuration changed on disk. Reload?"
+ IDS_MODE_OFF "off"
+ IDS_MODE_ON "on"
+ IDS_MODE_TOGGLE "toggle"
+ IDS_ALL_SCREENS "All Screens"
+ IDS_ACTIVE_SCREEN "Active Screen"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h
new file mode 100644
index 00000000..f284fd62
--- /dev/null
+++ b/cmd/launcher/resource.h
@@ -0,0 +1,186 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by launcher.rc
+//
+#define IDS_ERROR 1
+#define IDS_INVALID_SCREEN_NAME 2
+#define IDS_DUPLICATE_SCREEN_NAME 3
+#define IDS_SCREEN_NAME_IS_ALIAS 4
+#define IDS_VERIFY 5
+#define IDS_UNSAVED_DATA_REALLY_QUIT 6
+#define IDS_UNKNOWN_SCREEN_NAME 7
+#define IDS_INVALID_PORT 8
+#define IDS_SAVE_FAILED 9
+#define IDS_STARTUP_FAILED 10
+#define IDS_STARTED_TITLE 11
+#define IDS_STARTED 12
+#define IDS_INSTALL_FAILED 13
+#define IDS_UNINSTALL_TITLE 14
+#define IDS_UNINSTALLED 15
+#define IDS_UNINSTALL_FAILED 16
+#define IDS_CLIENT 17
+#define IDS_SERVER 18
+#define IDS_AUTOSTART_PERMISSION_SYSTEM 19
+#define IDS_AUTOSTART_PERMISSION_USER 20
+#define IDS_AUTOSTART_PERMISSION_NONE 21
+#define IDS_AUTOSTART_INSTALLED_SYSTEM 22
+#define IDS_AUTOSTART_INSTALLED_USER 23
+#define IDS_AUTOSTART_INSTALLED_NONE 24
+#define IDS_INSTALL_LABEL 25
+#define IDS_UNINSTALL_LABEL 26
+#define IDS_INSTALL_GENERIC_ERROR 27
+#define IDS_UNINSTALL_GENERIC_ERROR 28
+#define IDS_INSTALL_TITLE 29
+#define IDS_INSTALLED_SYSTEM 30
+#define IDS_INSTALLED_USER 31
+#define IDS_UNINSTALLED_SYSTEM 32
+#define IDS_UNINSTALLED_USER 33
+#define IDS_INVALID_SERVER_NAME 34
+#define IDS_TITLE 35
+#define IDS_SERVER_IS_CLIENT 36
+#define IDS_ADD_SCREEN 37
+#define IDS_EDIT_SCREEN 38
+#define IDS_INVALID_TIME 39
+#define IDS_ERROR_CODE 39
+#define IDS_AUTOSTART_PERMISSION_ALL 40
+#define IDS_INVALID_INTERFACE_NAME 41
+#define IDS_INVALID_CORNER_SIZE 42
+#define IDS_NEW_LINK 43
+#define IDS_SIDE_LEFT 44
+#define IDS_SIDE_RIGHT 45
+#define IDS_SIDE_TOP 46
+#define IDS_SIDE_BOTTOM 47
+#define IDS_LINK_FORMAT 48
+#define IDS_LINK_INTERVAL_FORMAT 49
+#define IDS_EDGE_LEFT 50
+#define IDS_EDGE_RIGHT 51
+#define IDS_EDGE_TOP 52
+#define IDS_EDGE_BOTTOM 53
+#define IDS_AUTOSTART_SAVE_FAILED 54
+#define IDS_LOAD_FAILED 55
+#define IDS_CONFIG_CHANGED 56
+#define IDS_MODE_OFF 57
+#define IDS_MODE_ON 58
+#define IDS_MODE_TOGGLE 59
+#define IDS_ALL_SCREENS 60
+#define IDS_ACTIVE_SCREEN 61
+#define IDD_MAIN 101
+#define IDD_ADD 102
+#define IDD_WAIT 103
+#define IDI_SYNERGY 104
+#define IDD_AUTOSTART 105
+#define IDD_ADVANCED_OPTIONS 106
+#define IDD_GLOBAL_OPTIONS 107
+#define IDD_SCREENS_LINKS 110
+#define IDD_INFO 111
+#define IDD_HOTKEY_OPTIONS 112
+#define IDD_HOTKEY_CONDITION 113
+#define IDD_HOTKEY_ACTION 114
+#define IDD_HOTKEY_SCREENS 115
+#define IDC_MAIN_CLIENT_RADIO 1000
+#define IDC_MAIN_SERVER_RADIO 1001
+#define IDC_MAIN_CLIENT_SERVER_NAME_LABEL 1002
+#define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1003
+#define IDC_MAIN_SERVER_SCREENS_LABEL 1004
+#define IDC_MAIN_SCREENS 1005
+#define IDC_MAIN_OPTIONS 1006
+#define IDC_MAIN_ADVANCED 1007
+#define IDC_MAIN_AUTOSTART 1008
+#define IDC_MAIN_TEST 1009
+#define IDC_MAIN_SAVE 1010
+#define IDC_MAIN_HOTKEYS 1010
+#define IDC_MAIN_DEBUG 1011
+#define IDC_ADD_SCREEN_NAME_EDIT 1020
+#define IDC_ADD_ALIASES_EDIT 1021
+#define IDC_AUTOSTART_INSTALLED_MSG 1031
+#define IDC_AUTOSTART_PERMISSION_MSG 1032
+#define IDC_AUTOSTART_INSTALL_USER 1033
+#define IDC_AUTOSTART_INSTALL_SYSTEM 1034
+#define IDC_ADD_HD_CAPS_CHECK 1037
+#define IDC_ADD_HD_NUM_CHECK 1038
+#define IDC_ADVANCED_NAME_EDIT 1038
+#define IDC_ADVANCED_PORT_EDIT 1039
+#define IDC_ADD_HD_SCROLL_CHECK 1039
+#define IDC_ADVANCED_INTERFACE_EDIT 1040
+#define IDC_GLOBAL_DELAY_CHECK 1041
+#define IDC_GLOBAL_DELAY_TIME 1042
+#define IDC_GLOBAL_TWO_TAP_CHECK 1043
+#define IDC_ADD_MOD_SHIFT 1043
+#define IDC_GLOBAL_TWO_TAP_TIME 1044
+#define IDC_ADD_MOD_CTRL 1044
+#define IDC_ADD_MOD_ALT 1045
+#define IDC_GLOBAL_HEARTBEAT_CHECK 1045
+#define IDC_ADD_MOD_META 1046
+#define IDC_GLOBAL_HEARTBEAT_TIME 1046
+#define IDC_ADD_MOD_SUPER 1047
+#define IDC_GLOBAL_SCREENSAVER_SYNC 1047
+#define IDC_GLOBAL_RELATIVE_MOVES 1048
+#define IDC_ADVANCED_DEFAULTS 1049
+#define IDC_GLOBAL_LEAVE_FOREGROUND 1049
+#define IDC_ADD_DC_SIZE 1052
+#define IDC_ADD_DC_TOP_LEFT 1053
+#define IDC_ADD_DC_TOP_RIGHT 1054
+#define IDC_ADD_DC_BOTTOM_LEFT 1055
+#define IDC_ADD_DC_BOTTOM_RIGHT 1056
+#define IDC_SCREENS_SRC_SIDE 1057
+#define IDC_SCREENS_SRC_START 1058
+#define IDC_SCREENS_SRC_END 1059
+#define IDC_SCREENS_SRC_SCREEN 1060
+#define IDC_SCREENS_SCREENS 1061
+#define IDC_SCREENS_ADD_SCREEN 1062
+#define IDC_SCREENS_LINKS 1063
+#define IDC_SCREENS_DST_START 1064
+#define IDC_SCREENS_DST_END 1065
+#define IDC_SCREENS_DST_SCREEN 1066
+#define IDC_SCREENS_REMOVE_SCREEN 1067
+#define IDC_SCREENS_EDIT_SCREEN 1068
+#define IDC_SCREENS_ADD_LINK 1069
+#define IDC_SCREENS_REMOVE_LINK 1070
+#define IDC_SCREENS_OVERLAP_ERROR 1071
+#define IDC_INFO_VERSION 1073
+#define IDC_MAIN_INFO 1074
+#define IDC_INFO_HOSTNAME 1076
+#define IDC_HOTKEY_HOTKEYS 1076
+#define IDC_INFO_IP_ADDRESS 1077
+#define IDC_HOTKEY_ADD_HOTKEY 1077
+#define IDC_INFO_USER_CONFIG 1078
+#define IDC_HOTKEY_REMOVE_HOTKEY 1078
+#define IDC_INFO_SYS_CONFIG 1079
+#define IDC_HOTKEY_EDIT_HOTKEY 1079
+#define IDC_HOTKEY_ACTIONS 1080
+#define IDC_HOTKEY_CONDITION_HOTKEY 1080
+#define IDC_HOTKEY_ACTION_DOWNUP 1081
+#define IDC_HOTKEY_ADD_ACTION 1082
+#define IDC_HOTKEY_ACTION_DOWN 1082
+#define IDC_HOTKEY_REMOVE_ACTION 1083
+#define IDC_HOTKEY_ACTION_UP 1083
+#define IDC_HOTKEY_EDIT_ACTION 1084
+#define IDC_HOTKEY_ACTION_HOTKEY 1085
+#define IDC_HOTKEY_ACTION_SWITCH_TO_LIST 1086
+#define IDC_HOTKEY_ACTION_SWITCH_TO 1087
+#define IDC_HOTKEY_ACTION_SWITCH_IN 1088
+#define IDC_HOTKEY_ACTION_LOCK 1089
+#define IDC_HOTKEY_ACTION_SWITCH_IN_LIST 1090
+#define IDC_HOTKEY_ACTION_LOCK_LIST 1091
+#define IDC_HOTKEY_ACTION_ON_ACTIVATE 1092
+#define IDC_HOTKEY_ACTION_ON_DEACTIVATE 1093
+#define IDC_HOTKEY_ACTION_SCREENS 1094
+#define IDC_HOTKEY_SCREENS_SRC 1095
+#define IDC_HOTKEY_SCREENS_DST 1096
+#define IDC_HOTKEY_SCREENS_ADD 1097
+#define IDC_HOTKEY_SCREENS_REMOVE 1098
+#define IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST 1099
+#define IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST 1100
+#define IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS 1101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 116
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1102
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/cmd/launcher/synergy.ico b/cmd/launcher/synergy.ico
new file mode 100644
index 00000000..89f965f4
Binary files /dev/null and b/cmd/launcher/synergy.ico differ
diff --git a/cmd/synergyc/CClientTaskBarReceiver.cpp b/cmd/synergyc/CClientTaskBarReceiver.cpp
new file mode 100644
index 00000000..025b43f6
--- /dev/null
+++ b/cmd/synergyc/CClientTaskBarReceiver.cpp
@@ -0,0 +1,136 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CClientTaskBarReceiver.h"
+#include "CClient.h"
+#include "CLock.h"
+#include "CStringUtil.h"
+#include "IEventQueue.h"
+#include "CArch.h"
+#include "Version.h"
+
+//
+// CClientTaskBarReceiver
+//
+
+CClientTaskBarReceiver::CClientTaskBarReceiver() :
+ m_state(kNotRunning)
+{
+ // do nothing
+}
+
+CClientTaskBarReceiver::~CClientTaskBarReceiver()
+{
+ // do nothing
+}
+
+void
+CClientTaskBarReceiver::updateStatus(CClient* client, const CString& errorMsg)
+{
+ {
+ // update our status
+ m_errorMessage = errorMsg;
+ if (client == NULL) {
+ if (m_errorMessage.empty()) {
+ m_state = kNotRunning;
+ }
+ else {
+ m_state = kNotWorking;
+ }
+ }
+ else {
+ m_server = client->getServerAddress().getHostname();
+
+ if (client->isConnected()) {
+ m_state = kConnected;
+ }
+ else if (client->isConnecting()) {
+ m_state = kConnecting;
+ }
+ else {
+ m_state = kNotConnected;
+ }
+ }
+
+ // let subclasses have a go
+ onStatusChanged(client);
+ }
+
+ // tell task bar
+ ARCH->updateReceiver(this);
+}
+
+CClientTaskBarReceiver::EState
+CClientTaskBarReceiver::getStatus() const
+{
+ return m_state;
+}
+
+const CString&
+CClientTaskBarReceiver::getErrorMessage() const
+{
+ return m_errorMessage;
+}
+
+void
+CClientTaskBarReceiver::quit()
+{
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+}
+
+void
+CClientTaskBarReceiver::onStatusChanged(CClient*)
+{
+ // do nothing
+}
+
+void
+CClientTaskBarReceiver::lock() const
+{
+ // do nothing
+}
+
+void
+CClientTaskBarReceiver::unlock() const
+{
+ // do nothing
+}
+
+std::string
+CClientTaskBarReceiver::getToolTip() const
+{
+ switch (m_state) {
+ case kNotRunning:
+ return CStringUtil::print("%s: Not running", kAppVersion);
+
+ case kNotWorking:
+ return CStringUtil::print("%s: %s",
+ kAppVersion, m_errorMessage.c_str());
+
+ case kNotConnected:
+ return CStringUtil::print("%s: Not connected: %s",
+ kAppVersion, m_errorMessage.c_str());
+
+ case kConnecting:
+ return CStringUtil::print("%s: Connecting to %s...",
+ kAppVersion, m_server.c_str());
+
+ case kConnected:
+ return CStringUtil::print("%s: Connected to %s",
+ kAppVersion, m_server.c_str());
+
+ default:
+ return "";
+ }
+}
diff --git a/cmd/synergyc/CClientTaskBarReceiver.h b/cmd/synergyc/CClientTaskBarReceiver.h
new file mode 100644
index 00000000..0e3440e1
--- /dev/null
+++ b/cmd/synergyc/CClientTaskBarReceiver.h
@@ -0,0 +1,83 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CCLIENTTASKBARRECEIVER_H
+#define CCLIENTTASKBARRECEIVER_H
+
+#include "CString.h"
+#include "IArchTaskBarReceiver.h"
+
+class CClient;
+
+//! Implementation of IArchTaskBarReceiver for the synergy server
+class CClientTaskBarReceiver : public IArchTaskBarReceiver {
+public:
+ CClientTaskBarReceiver();
+ virtual ~CClientTaskBarReceiver();
+
+ //! @name manipulators
+ //@{
+
+ //! Update status
+ /*!
+ Determine the status and query required information from the client.
+ */
+ void updateStatus(CClient*, const CString& errorMsg);
+
+ //@}
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus() = 0;
+ virtual void runMenu(int x, int y) = 0;
+ virtual void primaryAction() = 0;
+ virtual void lock() const;
+ virtual void unlock() const;
+ virtual const Icon getIcon() const = 0;
+ virtual std::string getToolTip() const;
+
+protected:
+ enum EState {
+ kNotRunning,
+ kNotWorking,
+ kNotConnected,
+ kConnecting,
+ kConnected,
+ kMaxState
+ };
+
+ //! Get status
+ EState getStatus() const;
+
+ //! Get error message
+ const CString& getErrorMessage() const;
+
+ //! Quit app
+ /*!
+ Causes the application to quit gracefully
+ */
+ void quit();
+
+ //! Status change notification
+ /*!
+ Called when status changes. The default implementation does nothing.
+ */
+ virtual void onStatusChanged(CClient* client);
+
+private:
+ EState m_state;
+ CString m_errorMessage;
+ CString m_server;
+};
+
+#endif
diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp
new file mode 100644
index 00000000..c002b9f1
--- /dev/null
+++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp
@@ -0,0 +1,346 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CMSWindowsClientTaskBarReceiver.h"
+#include "CClient.h"
+#include "CMSWindowsClipboard.h"
+#include "LogOutputters.h"
+#include "BasicTypes.h"
+#include "CArch.h"
+#include "CArchTaskBarWindows.h"
+#include "resource.h"
+
+//
+// CMSWindowsClientTaskBarReceiver
+//
+
+const UINT CMSWindowsClientTaskBarReceiver::s_stateToIconID[kMaxState] =
+{
+ IDI_TASKBAR_NOT_RUNNING,
+ IDI_TASKBAR_NOT_WORKING,
+ IDI_TASKBAR_NOT_CONNECTED,
+ IDI_TASKBAR_NOT_CONNECTED,
+ IDI_TASKBAR_CONNECTED
+};
+
+CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver(
+ HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) :
+ CClientTaskBarReceiver(),
+ m_appInstance(appInstance),
+ m_window(NULL),
+ m_logBuffer(logBuffer)
+{
+ for (UInt32 i = 0; i < kMaxState; ++i) {
+ m_icon[i] = loadIcon(s_stateToIconID[i]);
+ }
+ m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR));
+
+ // don't create the window yet. we'll create it on demand. this
+ // has the side benefit of being created in the thread used for
+ // the task bar. that's good because it means the existence of
+ // the window won't prevent changing the main thread's desktop.
+
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+CMSWindowsClientTaskBarReceiver::~CMSWindowsClientTaskBarReceiver()
+{
+ ARCH->removeReceiver(this);
+ for (UInt32 i = 0; i < kMaxState; ++i) {
+ deleteIcon(m_icon[i]);
+ }
+ DestroyMenu(m_menu);
+ destroyWindow();
+}
+
+void
+CMSWindowsClientTaskBarReceiver::showStatus()
+{
+ // create the window
+ createWindow();
+
+ // lock self while getting status
+ lock();
+
+ // get the current status
+ std::string status = getToolTip();
+
+ // done getting status
+ unlock();
+
+ // update dialog
+ HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS);
+ SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str());
+
+ if (!IsWindowVisible(m_window)) {
+ // position it by the mouse
+ POINT cursorPos;
+ GetCursorPos(&cursorPos);
+ RECT windowRect;
+ GetWindowRect(m_window, &windowRect);
+ int x = cursorPos.x;
+ int y = cursorPos.y;
+ int fw = GetSystemMetrics(SM_CXDLGFRAME);
+ int fh = GetSystemMetrics(SM_CYDLGFRAME);
+ int ww = windowRect.right - windowRect.left;
+ int wh = windowRect.bottom - windowRect.top;
+ int sw = GetSystemMetrics(SM_CXFULLSCREEN);
+ int sh = GetSystemMetrics(SM_CYFULLSCREEN);
+ if (fw < 1) {
+ fw = 1;
+ }
+ if (fh < 1) {
+ fh = 1;
+ }
+ if (x + ww - fw > sw) {
+ x -= ww - fw;
+ }
+ else {
+ x -= fw;
+ }
+ if (x < 0) {
+ x = 0;
+ }
+ if (y + wh - fh > sh) {
+ y -= wh - fh;
+ }
+ else {
+ y -= fh;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+ SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh,
+ SWP_SHOWWINDOW);
+ }
+}
+
+void
+CMSWindowsClientTaskBarReceiver::runMenu(int x, int y)
+{
+ // do popup menu. we need a window to pass to TrackPopupMenu().
+ // the SetForegroundWindow() and SendMessage() calls around
+ // TrackPopupMenu() are to get the menu to be dismissed when
+ // another window gets activated and are just one of those
+ // win32 weirdnesses.
+ createWindow();
+ SetForegroundWindow(m_window);
+ HMENU menu = GetSubMenu(m_menu, 0);
+ SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE);
+ HMENU logLevelMenu = GetSubMenu(menu, 3);
+ CheckMenuRadioItem(logLevelMenu, 0, 6,
+ CLOG->getFilter() - CLog::kERROR, MF_BYPOSITION);
+ int n = TrackPopupMenu(menu,
+ TPM_NONOTIFY |
+ TPM_RETURNCMD |
+ TPM_LEFTBUTTON |
+ TPM_RIGHTBUTTON,
+ x, y, 0, m_window, NULL);
+ SendMessage(m_window, WM_NULL, 0, 0);
+
+ // perform the requested operation
+ switch (n) {
+ case IDC_TASKBAR_STATUS:
+ showStatus();
+ break;
+
+ case IDC_TASKBAR_LOG:
+ copyLog();
+ break;
+
+ case IDC_TASKBAR_SHOW_LOG:
+ ARCH->showConsole(true);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_ERROR:
+ CLOG->setFilter(CLog::kERROR);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_WARNING:
+ CLOG->setFilter(CLog::kWARNING);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_NOTE:
+ CLOG->setFilter(CLog::kNOTE);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_INFO:
+ CLOG->setFilter(CLog::kINFO);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG:
+ CLOG->setFilter(CLog::kDEBUG);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG1:
+ CLOG->setFilter(CLog::kDEBUG1);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG2:
+ CLOG->setFilter(CLog::kDEBUG2);
+ break;
+
+ case IDC_TASKBAR_QUIT:
+ quit();
+ break;
+ }
+}
+
+void
+CMSWindowsClientTaskBarReceiver::primaryAction()
+{
+ showStatus();
+}
+
+const IArchTaskBarReceiver::Icon
+CMSWindowsClientTaskBarReceiver::getIcon() const
+{
+ return reinterpret_cast(m_icon[getStatus()]);
+}
+
+void
+CMSWindowsClientTaskBarReceiver::copyLog() const
+{
+ if (m_logBuffer != NULL) {
+ // collect log buffer
+ CString data;
+ for (CBufferedLogOutputter::const_iterator index = m_logBuffer->begin();
+ index != m_logBuffer->end(); ++index) {
+ data += *index;
+ data += "\n";
+ }
+
+ // copy log to clipboard
+ if (!data.empty()) {
+ CMSWindowsClipboard clipboard(m_window);
+ clipboard.open(0);
+ clipboard.emptyUnowned();
+ clipboard.add(IClipboard::kText, data);
+ clipboard.close();
+ }
+ }
+}
+
+void
+CMSWindowsClientTaskBarReceiver::onStatusChanged()
+{
+ if (IsWindowVisible(m_window)) {
+ showStatus();
+ }
+}
+
+HICON
+CMSWindowsClientTaskBarReceiver::loadIcon(UINT id)
+{
+ HANDLE icon = LoadImage(m_appInstance,
+ MAKEINTRESOURCE(id),
+ IMAGE_ICON,
+ 0, 0,
+ LR_DEFAULTCOLOR);
+ return reinterpret_cast(icon);
+}
+
+void
+CMSWindowsClientTaskBarReceiver::deleteIcon(HICON icon)
+{
+ if (icon != NULL) {
+ DestroyIcon(icon);
+ }
+}
+
+void
+CMSWindowsClientTaskBarReceiver::createWindow()
+{
+ // ignore if already created
+ if (m_window != NULL) {
+ return;
+ }
+
+ // get the status dialog
+ m_window = CreateDialogParam(m_appInstance,
+ MAKEINTRESOURCE(IDD_TASKBAR_STATUS),
+ NULL,
+ (DLGPROC)&CMSWindowsClientTaskBarReceiver::staticDlgProc,
+ reinterpret_cast(
+ reinterpret_cast(this)));
+
+ // window should appear on top of everything, including (especially)
+ // the task bar.
+ LONG_PTR style = GetWindowLongPtr(m_window, GWL_EXSTYLE);
+ style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
+ SetWindowLongPtr(m_window, GWL_EXSTYLE, style);
+
+ // tell the task bar about this dialog
+ CArchTaskBarWindows::addDialog(m_window);
+}
+
+void
+CMSWindowsClientTaskBarReceiver::destroyWindow()
+{
+ if (m_window != NULL) {
+ CArchTaskBarWindows::removeDialog(m_window);
+ DestroyWindow(m_window);
+ m_window = NULL;
+ }
+}
+
+BOOL
+CMSWindowsClientTaskBarReceiver::dlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ // use default focus
+ return TRUE;
+
+ case WM_ACTIVATE:
+ // hide when another window is activated
+ if (LOWORD(wParam) == WA_INACTIVE) {
+ ShowWindow(hwnd, SW_HIDE);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+BOOL CALLBACK
+CMSWindowsClientTaskBarReceiver::staticDlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ // if msg is WM_INITDIALOG, extract the CMSWindowsClientTaskBarReceiver*
+ // and put it in the extra window data then forward the call.
+ CMSWindowsClientTaskBarReceiver* self = NULL;
+ if (msg == WM_INITDIALOG) {
+ self = reinterpret_cast(
+ reinterpret_cast(lParam));
+ SetWindowLong(hwnd, GWL_USERDATA, lParam);
+ }
+ else {
+ // get the extra window data and forward the call
+ LONG data = GetWindowLong(hwnd, GWL_USERDATA);
+ if (data != 0) {
+ self = reinterpret_cast(
+ reinterpret_cast(data));
+ }
+ }
+
+ // forward the message
+ if (self != NULL) {
+ return self->dlgProc(hwnd, msg, wParam, lParam);
+ }
+ else {
+ return (msg == WM_INITDIALOG) ? TRUE : FALSE;
+ }
+}
diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h
new file mode 100644
index 00000000..72d33be5
--- /dev/null
+++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h
@@ -0,0 +1,64 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CMSWINDOWSCLIENTTASKBARRECEIVER_H
+#define CMSWINDOWSCLIENTTASKBARRECEIVER_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "CClientTaskBarReceiver.h"
+#include
+
+class CBufferedLogOutputter;
+
+//! Implementation of CClientTaskBarReceiver for Microsoft Windows
+class CMSWindowsClientTaskBarReceiver : public CClientTaskBarReceiver {
+public:
+ CMSWindowsClientTaskBarReceiver(HINSTANCE, const CBufferedLogOutputter*);
+ virtual ~CMSWindowsClientTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+
+protected:
+ void copyLog() const;
+
+ // CClientTaskBarReceiver overrides
+ virtual void onStatusChanged();
+
+private:
+ HICON loadIcon(UINT);
+ void deleteIcon(HICON);
+ void createWindow();
+ void destroyWindow();
+
+ BOOL dlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam);
+ static BOOL CALLBACK
+ staticDlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam);
+
+private:
+ HINSTANCE m_appInstance;
+ HWND m_window;
+ HMENU m_menu;
+ HICON m_icon[kMaxState];
+ const CBufferedLogOutputter* m_logBuffer;
+ static const UINT s_stateToIconID[];
+};
+
+#endif
diff --git a/cmd/synergyc/COSXClientTaskBarReceiver.cpp b/cmd/synergyc/COSXClientTaskBarReceiver.cpp
new file mode 100644
index 00000000..c380ac4d
--- /dev/null
+++ b/cmd/synergyc/COSXClientTaskBarReceiver.cpp
@@ -0,0 +1,56 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "COSXClientTaskBarReceiver.h"
+#include "CArch.h"
+
+//
+// COSXClientTaskBarReceiver
+//
+
+COSXClientTaskBarReceiver::COSXClientTaskBarReceiver(
+ const CBufferedLogOutputter*)
+{
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+COSXClientTaskBarReceiver::~COSXClientTaskBarReceiver()
+{
+ ARCH->removeReceiver(this);
+}
+
+void
+COSXClientTaskBarReceiver::showStatus()
+{
+ // do nothing
+}
+
+void
+COSXClientTaskBarReceiver::runMenu(int, int)
+{
+ // do nothing
+}
+
+void
+COSXClientTaskBarReceiver::primaryAction()
+{
+ // do nothing
+}
+
+const IArchTaskBarReceiver::Icon
+COSXClientTaskBarReceiver::getIcon() const
+{
+ return NULL;
+}
diff --git a/cmd/synergyc/COSXClientTaskBarReceiver.h b/cmd/synergyc/COSXClientTaskBarReceiver.h
new file mode 100644
index 00000000..59bca97c
--- /dev/null
+++ b/cmd/synergyc/COSXClientTaskBarReceiver.h
@@ -0,0 +1,35 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef COSXCLIENTTASKBARRECEIVER_H
+#define COSXCLIENTTASKBARRECEIVER_H
+
+#include "CClientTaskBarReceiver.h"
+
+class CBufferedLogOutputter;
+
+//! Implementation of CClientTaskBarReceiver for OS X
+class COSXClientTaskBarReceiver : public CClientTaskBarReceiver {
+public:
+ COSXClientTaskBarReceiver(const CBufferedLogOutputter*);
+ virtual ~COSXClientTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+};
+
+#endif
diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp
new file mode 100644
index 00000000..681f9be5
--- /dev/null
+++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp
@@ -0,0 +1,56 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CXWindowsClientTaskBarReceiver.h"
+#include "CArch.h"
+
+//
+// CXWindowsClientTaskBarReceiver
+//
+
+CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver(
+ const CBufferedLogOutputter*)
+{
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+CXWindowsClientTaskBarReceiver::~CXWindowsClientTaskBarReceiver()
+{
+ ARCH->removeReceiver(this);
+}
+
+void
+CXWindowsClientTaskBarReceiver::showStatus()
+{
+ // do nothing
+}
+
+void
+CXWindowsClientTaskBarReceiver::runMenu(int, int)
+{
+ // do nothing
+}
+
+void
+CXWindowsClientTaskBarReceiver::primaryAction()
+{
+ // do nothing
+}
+
+const IArchTaskBarReceiver::Icon
+CXWindowsClientTaskBarReceiver::getIcon() const
+{
+ return NULL;
+}
diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h
new file mode 100644
index 00000000..fa9da471
--- /dev/null
+++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h
@@ -0,0 +1,35 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CXWINDOWSCLIENTTASKBARRECEIVER_H
+#define CXWINDOWSCLIENTTASKBARRECEIVER_H
+
+#include "CClientTaskBarReceiver.h"
+
+class CBufferedLogOutputter;
+
+//! Implementation of CClientTaskBarReceiver for X Windows
+class CXWindowsClientTaskBarReceiver : public CClientTaskBarReceiver {
+public:
+ CXWindowsClientTaskBarReceiver(const CBufferedLogOutputter*);
+ virtual ~CXWindowsClientTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+};
+
+#endif
diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am
new file mode 100644
index 00000000..b6e8c83d
--- /dev/null
+++ b/cmd/synergyc/Makefile.am
@@ -0,0 +1,98 @@
+# synergy -- mouse and keyboard sharing utility
+# 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+## Process this file with automake to produce Makefile.in
+NULL =
+
+COMMON_SOURCE_FILES = \
+ CClientTaskBarReceiver.cpp \
+ CClientTaskBarReceiver.h \
+ synergyc.cpp \
+ $(NULL)
+XWINDOWS_SOURCE_FILES = \
+ CXWindowsClientTaskBarReceiver.cpp \
+ CXWindowsClientTaskBarReceiver.h \
+ $(NULL)
+MSWINDOWS_SOURCE_FILES = \
+ CMSWindowsClientTaskBarReceiver.cpp \
+ CMSWindowsClientTaskBarReceiver.h \
+ resource.h \
+ synergyc.rc \
+ $(NULL)
+CARBON_SOURCE_FILES = \
+ COSXClientTaskBarReceiver.cpp \
+ COSXClientTaskBarReceiver.h \
+ $(NULL)
+
+EXTRA_DIST = \
+ Makefile.win \
+ synergyc.ico \
+ tb_error.ico \
+ tb_idle.ico \
+ tb_run.ico \
+ tb_wait.ico \
+ $(XWINDOWS_SOURCE_FILES) \
+ $(MSWINDOWS_SOURCE_FILES) \
+ $(CARBON_SOURCE_FILES) \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ $(NULL)
+
+bin_PROGRAMS = synergyc
+if XWINDOWS
+synergyc_SOURCES = \
+ $(COMMON_SOURCE_FILES) \
+ $(XWINDOWS_SOURCE_FILES) \
+ $(NULL)
+endif
+if MSWINDOWS
+synergyc_SOURCES = \
+ $(COMMON_SOURCE_FILES) \
+ $(MSWINDOWS_SOURCE_FILES) \
+ $(NULL)
+endif
+if CARBON
+synergyc_SOURCES = \
+ $(COMMON_SOURCE_FILES) \
+ $(CARBON_SOURCE_FILES) \
+ $(NULL)
+synergyc_LDFLAGS = \
+ -framework ScreenSaver \
+ -framework IOKit \
+ -framework ApplicationServices \
+ -framework Foundation \
+ $(NULL)
+endif
+synergyc_LDADD = \
+ $(top_builddir)/lib/client/libclient.a \
+ $(top_builddir)/lib/platform/libplatform.a \
+ $(top_builddir)/lib/synergy/libsynergy.a \
+ $(top_builddir)/lib/net/libnet.a \
+ $(top_builddir)/lib/io/libio.a \
+ $(top_builddir)/lib/mt/libmt.a \
+ $(top_builddir)/lib/base/libbase.a \
+ $(top_builddir)/lib/common/libcommon.a \
+ $(top_builddir)/lib/arch/libarch.a \
+ $(NULL)
+INCLUDES = \
+ -I$(top_srcdir)/lib/common \
+ -I$(top_srcdir)/lib/arch \
+ -I$(top_srcdir)/lib/base \
+ -I$(top_srcdir)/lib/mt \
+ -I$(top_srcdir)/lib/io \
+ -I$(top_srcdir)/lib/net \
+ -I$(top_srcdir)/lib/synergy \
+ -I$(top_srcdir)/lib/platform \
+ -I$(top_srcdir)/lib/client \
+ $(NULL)
diff --git a/cmd/synergyc/Makefile.win b/cmd/synergyc/Makefile.win
new file mode 100644
index 00000000..29f2e516
--- /dev/null
+++ b/cmd/synergyc/Makefile.win
@@ -0,0 +1,89 @@
+# synergy -- mouse and keyboard sharing utility
+# Copyright (C) 2007 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+BIN_SYNERGYC_SRC = cmd\synergyc
+BIN_SYNERGYC_DST = $(BUILD_DST)\$(BIN_SYNERGYC_SRC)
+BIN_SYNERGYC_EXE = "$(BUILD_DST)\synergyc.exe"
+BIN_SYNERGYC_CPP = \
+ "CClientTaskBarReceiver.cpp" \
+ "CMSWindowsClientTaskBarReceiver.cpp" \
+ "synergyc.cpp" \
+ $(NULL)
+BIN_SYNERGYC_OBJ = \
+ "$(BIN_SYNERGYC_DST)\CClientTaskBarReceiver.obj" \
+ "$(BIN_SYNERGYC_DST)\CMSWindowsClientTaskBarReceiver.obj" \
+ "$(BIN_SYNERGYC_DST)\synergyc.obj" \
+ $(NULL)
+BIN_SYNERGYC_RC = "$(BIN_SYNERGYC_SRC)\synergyc.rc"
+BIN_SYNERGYC_RES = "$(BIN_SYNERGYC_DST)\synergyc.res"
+BIN_SYNERGYC_INC = \
+ /I"lib\common" \
+ /I"lib\arch" \
+ /I"lib\base" \
+ /I"lib\mt" \
+ /I"lib\io" \
+ /I"lib\net" \
+ /I"lib\synergy" \
+ /I"lib\platform" \
+ /I"lib\client" \
+ $(NULL)
+BIN_SYNERGYC_LIB = \
+ $(LIB_CLIENT_LIB) \
+ $(LIB_PLATFORM_LIB) \
+ $(LIB_SYNERGY_LIB) \
+ $(LIB_NET_LIB) \
+ $(LIB_IO_LIB) \
+ $(LIB_MT_LIB) \
+ $(LIB_BASE_LIB) \
+ $(LIB_ARCH_LIB) \
+ $(LIB_COMMON_LIB) \
+ $(NULL)
+
+CPP_FILES = $(CPP_FILES) $(BIN_SYNERGYC_CPP)
+OBJ_FILES = $(OBJ_FILES) $(BIN_SYNERGYC_OBJ)
+PROGRAMS = $(PROGRAMS) $(BIN_SYNERGYC_EXE)
+
+# Need shell functions.
+guilibs = $(guilibs) shell32.lib
+
+# Dependency rules
+$(BIN_SYNERGYC_OBJ): $(AUTODEP)
+!if EXIST($(BIN_SYNERGYC_DST)\deps.mak)
+!include $(BIN_SYNERGYC_DST)\deps.mak
+!endif
+
+# Build rules. Use batch-mode rules if possible.
+!if DEFINED(_NMAKE_VER)
+{$(BIN_SYNERGYC_SRC)\}.cpp{$(BIN_SYNERGYC_DST)\}.obj::
+!else
+{$(BIN_SYNERGYC_SRC)\}.cpp{$(BIN_SYNERGYC_DST)\}.obj:
+!endif
+ @$(ECHO) Compile in $(BIN_SYNERGYC_SRC)
+ -@$(MKDIR) $(BIN_SYNERGYC_DST) 2>NUL:
+ $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \
+ $(BIN_SYNERGYC_INC) \
+ /Fo$(BIN_SYNERGYC_DST)\ \
+ /Fd$(BIN_SYNERGYC_DST)\src.pdb \
+ $< | $(AUTODEP) $(BIN_SYNERGYC_SRC) $(BIN_SYNERGYC_DST)
+$(BIN_SYNERGYC_RES): $(BIN_SYNERGYC_RC)
+ @$(ECHO) Compile $(**F)
+ -@$(MKDIR) $(BIN_SYNERGYC_DST) 2>NUL:
+ $(rc) $(rcflags) $(rcvars) \
+ /fo$@ \
+ $**
+$(BIN_SYNERGYC_EXE): $(BIN_SYNERGYC_OBJ) $(BIN_SYNERGYC_RES) $(BIN_SYNERGYC_LIB)
+ @$(ECHO) Link $(@F)
+ $(link) $(ldebug) $(guilflags) $(guilibsmt) \
+ /out:$@ \
+ $**
+ $(AUTODEP) $(BIN_SYNERGYC_SRC) $(BIN_SYNERGYC_DST) \
+ $(BIN_SYNERGYC_OBJ:.obj=.d)
diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h
new file mode 100644
index 00000000..eeee6e1e
--- /dev/null
+++ b/cmd/synergyc/resource.h
@@ -0,0 +1,37 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by synergyc.rc
+//
+#define IDS_FAILED 1
+#define IDS_INIT_FAILED 2
+#define IDS_UNCAUGHT_EXCEPTION 3
+#define IDI_SYNERGY 101
+#define IDI_TASKBAR_NOT_RUNNING 102
+#define IDI_TASKBAR_NOT_WORKING 103
+#define IDI_TASKBAR_NOT_CONNECTED 104
+#define IDI_TASKBAR_CONNECTED 105
+#define IDR_TASKBAR 107
+#define IDD_TASKBAR_STATUS 108
+#define IDC_TASKBAR_STATUS_STATUS 1000
+#define IDC_TASKBAR_QUIT 40001
+#define IDC_TASKBAR_STATUS 40002
+#define IDC_TASKBAR_LOG 40003
+#define IDC_TASKBAR_SHOW_LOG 40004
+#define IDC_TASKBAR_LOG_LEVEL_ERROR 40009
+#define IDC_TASKBAR_LOG_LEVEL_WARNING 40010
+#define IDC_TASKBAR_LOG_LEVEL_NOTE 40011
+#define IDC_TASKBAR_LOG_LEVEL_INFO 40012
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG 40013
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG1 40014
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG2 40015
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 109
+#define _APS_NEXT_COMMAND_VALUE 40016
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp
new file mode 100644
index 00000000..1a230f0d
--- /dev/null
+++ b/cmd/synergyc/synergyc.cpp
@@ -0,0 +1,910 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CClient.h"
+#include "CScreen.h"
+#include "ProtocolTypes.h"
+#include "Version.h"
+#include "XScreen.h"
+#include "CNetworkAddress.h"
+#include "CSocketMultiplexer.h"
+#include "CTCPSocketFactory.h"
+#include "XSocket.h"
+#include "CThread.h"
+#include "CEventQueue.h"
+#include "CFunctionEventJob.h"
+#include "CFunctionJob.h"
+#include "CLog.h"
+#include "CString.h"
+#include "CStringUtil.h"
+#include "LogOutputters.h"
+#include "CArch.h"
+#include "XArch.h"
+#include
+
+#define DAEMON_RUNNING(running_)
+#if WINAPI_MSWINDOWS
+#include "CArchMiscWindows.h"
+#include "CMSWindowsScreen.h"
+#include "CMSWindowsUtil.h"
+#include "CMSWindowsClientTaskBarReceiver.h"
+#include "resource.h"
+#undef DAEMON_RUNNING
+#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_)
+#elif WINAPI_XWINDOWS
+#include "CXWindowsScreen.h"
+#include "CXWindowsClientTaskBarReceiver.h"
+#elif WINAPI_CARBON
+#include "COSXScreen.h"
+#include "COSXClientTaskBarReceiver.h"
+#endif
+
+// platform dependent name of a daemon
+#if SYSAPI_WIN32
+#define DAEMON_NAME "Synergy Client"
+#elif SYSAPI_UNIX
+#define DAEMON_NAME "synergyc"
+#endif
+
+typedef int (*StartupFunc)(int, char**);
+static bool startClient();
+static void parse(int argc, const char* const* argv);
+
+//
+// program arguments
+//
+
+#define ARG CArgs::s_instance
+
+class CArgs {
+public:
+ CArgs() :
+ m_pname(NULL),
+ m_backend(false),
+ m_restartable(true),
+ m_daemon(true),
+ m_logFilter(NULL),
+ m_display(NULL),
+ m_serverAddress(NULL)
+ { s_instance = this; }
+ ~CArgs() { s_instance = NULL; }
+
+public:
+ static CArgs* s_instance;
+ const char* m_pname;
+ bool m_backend;
+ bool m_restartable;
+ bool m_daemon;
+ const char* m_logFilter;
+ const char* m_display;
+ CString m_name;
+ CNetworkAddress* m_serverAddress;
+};
+
+CArgs* CArgs::s_instance = NULL;
+
+
+//
+// platform dependent factories
+//
+
+static
+CScreen*
+createScreen()
+{
+#if WINAPI_MSWINDOWS
+ return new CScreen(new CMSWindowsScreen(false));
+#elif WINAPI_XWINDOWS
+ return new CScreen(new CXWindowsScreen(ARG->m_display, false));
+#elif WINAPI_CARBON
+ return new CScreen(new COSXScreen(false));
+#endif
+}
+
+static
+CClientTaskBarReceiver*
+createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
+{
+#if WINAPI_MSWINDOWS
+ return new CMSWindowsClientTaskBarReceiver(
+ CMSWindowsScreen::getInstance(), logBuffer);
+#elif WINAPI_XWINDOWS
+ return new CXWindowsClientTaskBarReceiver(logBuffer);
+#elif WINAPI_CARBON
+ return new COSXClientTaskBarReceiver(logBuffer);
+#endif
+}
+
+
+//
+// platform independent main
+//
+
+static CClient* s_client = NULL;
+static CScreen* s_clientScreen = NULL;
+static CClientTaskBarReceiver* s_taskBarReceiver = NULL;
+static double s_retryTime = 0.0;
+static bool s_suspened = false;
+
+static
+void
+updateStatus()
+{
+ s_taskBarReceiver->updateStatus(s_client, "");
+}
+
+static
+void
+updateStatus(const CString& msg)
+{
+ s_taskBarReceiver->updateStatus(s_client, msg);
+}
+
+static
+void
+resetRestartTimeout()
+{
+ s_retryTime = 0.0;
+}
+
+static
+double
+nextRestartTimeout()
+{
+ // choose next restart timeout. we start with rapid retries
+ // then slow down.
+ if (s_retryTime < 1.0) {
+ s_retryTime = 1.0;
+ }
+ else if (s_retryTime < 3.0) {
+ s_retryTime = 3.0;
+ }
+ else if (s_retryTime < 5.0) {
+ s_retryTime = 5.0;
+ }
+ else if (s_retryTime < 15.0) {
+ s_retryTime = 15.0;
+ }
+ else if (s_retryTime < 30.0) {
+ s_retryTime = 30.0;
+ }
+ else {
+ s_retryTime = 60.0;
+ }
+ return s_retryTime;
+}
+
+static
+void
+handleScreenError(const CEvent&, void*)
+{
+ LOG((CLOG_CRIT "error on screen"));
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+}
+
+static
+CScreen*
+openClientScreen()
+{
+ CScreen* screen = createScreen();
+ EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
+ screen->getEventTarget(),
+ new CFunctionEventJob(
+ &handleScreenError));
+ return screen;
+}
+
+static
+void
+closeClientScreen(CScreen* screen)
+{
+ if (screen != NULL) {
+ EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
+ screen->getEventTarget());
+ delete screen;
+ }
+}
+
+static
+void
+handleClientRestart(const CEvent&, void* vtimer)
+{
+ // discard old timer
+ CEventQueueTimer* timer = reinterpret_cast(vtimer);
+ EVENTQUEUE->deleteTimer(timer);
+ EVENTQUEUE->removeHandler(CEvent::kTimer, timer);
+
+ // reconnect
+ startClient();
+}
+
+static
+void
+scheduleClientRestart(double retryTime)
+{
+ // install a timer and handler to retry later
+ LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
+ CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL);
+ EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
+ new CFunctionEventJob(&handleClientRestart, timer));
+}
+
+static
+void
+handleClientConnected(const CEvent&, void*)
+{
+ LOG((CLOG_NOTE "connected to server"));
+ resetRestartTimeout();
+ updateStatus();
+}
+
+static
+void
+handleClientFailed(const CEvent& e, void*)
+{
+ CClient::CFailInfo* info =
+ reinterpret_cast(e.getData());
+
+ updateStatus(CString("Failed to connect to server: ") + info->m_what);
+ if (!ARG->m_restartable || !info->m_retry) {
+ LOG((CLOG_ERR "failed to connect to server: %s", info->m_what));
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+ }
+ else {
+ LOG((CLOG_WARN "failed to connect to server: %s", info->m_what));
+ if (!s_suspened) {
+ scheduleClientRestart(nextRestartTimeout());
+ }
+ }
+}
+
+static
+void
+handleClientDisconnected(const CEvent&, void*)
+{
+ LOG((CLOG_NOTE "disconnected from server"));
+ if (!ARG->m_restartable) {
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+ }
+ else if (!s_suspened) {
+ s_client->connect();
+ }
+ updateStatus();
+}
+
+static
+CClient*
+openClient(const CString& name, const CNetworkAddress& address, CScreen* screen)
+{
+ CClient* client = new CClient(name, address,
+ new CTCPSocketFactory, NULL, screen);
+ EVENTQUEUE->adoptHandler(CClient::getConnectedEvent(),
+ client->getEventTarget(),
+ new CFunctionEventJob(handleClientConnected));
+ EVENTQUEUE->adoptHandler(CClient::getConnectionFailedEvent(),
+ client->getEventTarget(),
+ new CFunctionEventJob(handleClientFailed));
+ EVENTQUEUE->adoptHandler(CClient::getDisconnectedEvent(),
+ client->getEventTarget(),
+ new CFunctionEventJob(handleClientDisconnected));
+ return client;
+}
+
+static
+void
+closeClient(CClient* client)
+{
+ if (client == NULL) {
+ return;
+ }
+
+ EVENTQUEUE->removeHandler(CClient::getConnectedEvent(), client);
+ EVENTQUEUE->removeHandler(CClient::getConnectionFailedEvent(), client);
+ EVENTQUEUE->removeHandler(CClient::getDisconnectedEvent(), client);
+ delete client;
+}
+
+static
+bool
+startClient()
+{
+ double retryTime;
+ CScreen* clientScreen = NULL;
+ try {
+ if (s_clientScreen == NULL) {
+ clientScreen = openClientScreen();
+ s_client = openClient(ARG->m_name,
+ *ARG->m_serverAddress, clientScreen);
+ s_clientScreen = clientScreen;
+ LOG((CLOG_NOTE "started client"));
+ }
+ s_client->connect();
+ updateStatus();
+ return true;
+ }
+ catch (XScreenUnavailable& e) {
+ LOG((CLOG_WARN "cannot open secondary screen: %s", e.what()));
+ closeClientScreen(clientScreen);
+ updateStatus(CString("Cannot open secondary screen: ") + e.what());
+ retryTime = e.getRetryTime();
+ }
+ catch (XScreenOpenFailure& e) {
+ LOG((CLOG_CRIT "cannot open secondary screen: %s", e.what()));
+ closeClientScreen(clientScreen);
+ return false;
+ }
+ catch (XBase& e) {
+ LOG((CLOG_CRIT "failed to start client: %s", e.what()));
+ closeClientScreen(clientScreen);
+ return false;
+ }
+
+ if (ARG->m_restartable) {
+ scheduleClientRestart(retryTime);
+ return true;
+ }
+ else {
+ // don't try again
+ return false;
+ }
+}
+
+static
+void
+stopClient()
+{
+ closeClient(s_client);
+ closeClientScreen(s_clientScreen);
+ s_client = NULL;
+ s_clientScreen = NULL;
+}
+
+static
+int
+mainLoop()
+{
+ // create socket multiplexer. this must happen after daemonization
+ // on unix because threads evaporate across a fork().
+ CSocketMultiplexer multiplexer;
+
+ // create the event queue
+ CEventQueue eventQueue;
+
+ // start the client. if this return false then we've failed and
+ // we shouldn't retry.
+ LOG((CLOG_DEBUG1 "starting client"));
+ if (!startClient()) {
+ return kExitFailed;
+ }
+
+ // run event loop. if startClient() failed we're supposed to retry
+ // later. the timer installed by startClient() will take care of
+ // that.
+ CEvent event;
+ DAEMON_RUNNING(true);
+ EVENTQUEUE->getEvent(event);
+ while (event.getType() != CEvent::kQuit) {
+ EVENTQUEUE->dispatchEvent(event);
+ CEvent::deleteData(event);
+ EVENTQUEUE->getEvent(event);
+ }
+ DAEMON_RUNNING(false);
+
+ // close down
+ LOG((CLOG_DEBUG1 "stopping client"));
+ stopClient();
+ updateStatus();
+ LOG((CLOG_NOTE "stopped client"));
+
+ return kExitSuccess;
+}
+
+static
+int
+daemonMainLoop(int, const char**)
+{
+#if SYSAPI_WIN32
+ CSystemLogger sysLogger(DAEMON_NAME, false);
+#else
+ CSystemLogger sysLogger(DAEMON_NAME, true);
+#endif
+ return mainLoop();
+}
+
+static
+int
+standardStartup(int argc, char** argv)
+{
+ if (!ARG->m_daemon) {
+ ARCH->showConsole(false);
+ }
+
+ // parse command line
+ parse(argc, argv);
+
+ // daemonize if requested
+ if (ARG->m_daemon) {
+ return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop);
+ }
+ else {
+ return mainLoop();
+ }
+}
+
+static
+int
+run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
+{
+ // general initialization
+ ARG->m_serverAddress = new CNetworkAddress;
+ ARG->m_pname = ARCH->getBasename(argv[0]);
+
+ // install caller's output filter
+ if (outputter != NULL) {
+ CLOG->insert(outputter);
+ }
+
+ // save log messages
+ CBufferedLogOutputter logBuffer(1000);
+ CLOG->insert(&logBuffer, true);
+
+ // make the task bar receiver. the user can control this app
+ // through the task bar.
+ s_taskBarReceiver = createTaskBarReceiver(&logBuffer);
+
+ // run
+ int result = startup(argc, argv);
+
+ // done with task bar receiver
+ delete s_taskBarReceiver;
+
+ // done with log buffer
+ CLOG->remove(&logBuffer);
+
+ delete ARG->m_serverAddress;
+ return result;
+}
+
+
+//
+// command line parsing
+//
+
+#define BYE "\nTry `%s --help' for more information."
+
+static void (*bye)(int) = &exit;
+
+static
+void
+version()
+{
+ LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n%s",
+ ARG->m_pname,
+ kVersion,
+ kProtocolMajorVersion,
+ kProtocolMinorVersion,
+ kCopyright));
+}
+
+static
+void
+help()
+{
+#if WINAPI_XWINDOWS
+# define USAGE_DISPLAY_ARG \
+" [--display ]"
+# define USAGE_DISPLAY_INFO \
+" --display connect to the X server at \n"
+#else
+# define USAGE_DISPLAY_ARG
+# define USAGE_DISPLAY_INFO
+#endif
+
+ LOG((CLOG_PRINT
+"Usage: %s"
+" [--daemon|--no-daemon]"
+" [--debug ]"
+USAGE_DISPLAY_ARG
+" [--name ]"
+" [--restart|--no-restart]"
+" "
+"\n\n"
+"Start the synergy mouse/keyboard sharing server.\n"
+"\n"
+" -d, --debug filter out log messages with priorty below level.\n"
+" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n"
+" DEBUG, DEBUG1, DEBUG2.\n"
+USAGE_DISPLAY_INFO
+" -f, --no-daemon run the client in the foreground.\n"
+"* --daemon run the client as a daemon.\n"
+" -n, --name use screen-name instead the hostname to identify\n"
+" ourself to the server.\n"
+" -1, --no-restart do not try to restart the client if it fails for\n"
+" some reason.\n"
+"* --restart restart the client automatically if it fails.\n"
+" -h, --help display this help and exit.\n"
+" --version display version information and exit.\n"
+"\n"
+"* marks defaults.\n"
+"\n"
+"The server address is of the form: [][:]. The hostname\n"
+"must be the address or hostname of the server. The port overrides the\n"
+"default port, %d.\n"
+"\n"
+"Where log messages go depends on the platform and whether or not the\n"
+"client is running as a daemon.",
+ ARG->m_pname, kDefaultPort));
+
+}
+
+static
+bool
+isArg(int argi, int argc, const char* const* argv,
+ const char* name1, const char* name2,
+ int minRequiredParameters = 0)
+{
+ if ((name1 != NULL && strcmp(argv[argi], name1) == 0) ||
+ (name2 != NULL && strcmp(argv[argi], name2) == 0)) {
+ // match. check args left.
+ if (argi + minRequiredParameters >= argc) {
+ LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
+ ARG->m_pname, argv[argi], ARG->m_pname));
+ bye(kExitArgs);
+ }
+ return true;
+ }
+
+ // no match
+ return false;
+}
+
+static
+void
+parse(int argc, const char* const* argv)
+{
+ assert(ARG->m_pname != NULL);
+ assert(argv != NULL);
+ assert(argc >= 1);
+
+ // set defaults
+ ARG->m_name = ARCH->getHostName();
+
+ // parse options
+ int i;
+ for (i = 1; i < argc; ++i) {
+ if (isArg(i, argc, argv, "-d", "--debug", 1)) {
+ // change logging level
+ ARG->m_logFilter = argv[++i];
+ }
+
+ else if (isArg(i, argc, argv, "-n", "--name", 1)) {
+ // save screen name
+ ARG->m_name = argv[++i];
+ }
+
+ else if (isArg(i, argc, argv, NULL, "--camp")) {
+ // ignore -- included for backwards compatibility
+ }
+
+ else if (isArg(i, argc, argv, NULL, "--no-camp")) {
+ // ignore -- included for backwards compatibility
+ }
+
+ else if (isArg(i, argc, argv, "-f", "--no-daemon")) {
+ // not a daemon
+ ARG->m_daemon = false;
+ }
+
+ else if (isArg(i, argc, argv, NULL, "--daemon")) {
+ // daemonize
+ ARG->m_daemon = true;
+ }
+
+#if WINAPI_XWINDOWS
+ else if (isArg(i, argc, argv, "-display", "--display", 1)) {
+ // use alternative display
+ ARG->m_display = argv[++i];
+ }
+#endif
+
+ else if (isArg(i, argc, argv, "-1", "--no-restart")) {
+ // don't try to restart
+ ARG->m_restartable = false;
+ }
+
+ else if (isArg(i, argc, argv, NULL, "--restart")) {
+ // try to restart
+ ARG->m_restartable = true;
+ }
+
+ else if (isArg(i, argc, argv, "-z", NULL)) {
+ ARG->m_backend = true;
+ }
+
+ else if (isArg(i, argc, argv, "-h", "--help")) {
+ help();
+ bye(kExitSuccess);
+ }
+
+ else if (isArg(i, argc, argv, NULL, "--version")) {
+ version();
+ bye(kExitSuccess);
+ }
+
+ else if (isArg(i, argc, argv, "--", NULL)) {
+ // remaining arguments are not options
+ ++i;
+ break;
+ }
+
+ else if (argv[i][0] == '-') {
+ LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
+ ARG->m_pname, argv[i], ARG->m_pname));
+ bye(kExitArgs);
+ }
+
+ else {
+ // this and remaining arguments are not options
+ break;
+ }
+ }
+
+ // exactly one non-option argument (server-address)
+ if (i == argc) {
+ LOG((CLOG_PRINT "%s: a server address or name is required" BYE,
+ ARG->m_pname, ARG->m_pname));
+ bye(kExitArgs);
+ }
+ if (i + 1 != argc) {
+ LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
+ ARG->m_pname, argv[i], ARG->m_pname));
+ bye(kExitArgs);
+ }
+
+ // save server address
+ try {
+ *ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort);
+ ARG->m_serverAddress->resolve();
+ }
+ catch (XSocketAddress& e) {
+ // allow an address that we can't look up if we're restartable.
+ // we'll try to resolve the address each time we connect to the
+ // server. a bad port will never get better. patch by Brent
+ // Priddy.
+ if (!ARG->m_restartable || e.getError() == XSocketAddress::kBadPort) {
+ LOG((CLOG_PRINT "%s: %s" BYE,
+ ARG->m_pname, e.what(), ARG->m_pname));
+ bye(kExitFailed);
+ }
+ }
+
+ // increase default filter level for daemon. the user must
+ // explicitly request another level for a daemon.
+ if (ARG->m_daemon && ARG->m_logFilter == NULL) {
+#if SYSAPI_WIN32
+ if (CArchMiscWindows::isWindows95Family()) {
+ // windows 95 has no place for logging so avoid showing
+ // the log console window.
+ ARG->m_logFilter = "FATAL";
+ }
+ else
+#endif
+ {
+ ARG->m_logFilter = "NOTE";
+ }
+ }
+
+ // set log filter
+ if (!CLOG->setFilter(ARG->m_logFilter)) {
+ LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
+ ARG->m_pname, ARG->m_logFilter, ARG->m_pname));
+ bye(kExitArgs);
+ }
+
+ // identify system
+ LOG((CLOG_INFO "Synergy client %s on %s", kVersion, ARCH->getOSName().c_str()));
+}
+
+
+//
+// platform dependent entry points
+//
+
+#if SYSAPI_WIN32
+
+static bool s_hasImportantLogMessages = false;
+
+//
+// CMessageBoxOutputter
+//
+// This class writes severe log messages to a message box
+//
+
+class CMessageBoxOutputter : public ILogOutputter {
+public:
+ CMessageBoxOutputter() { }
+ virtual ~CMessageBoxOutputter() { }
+
+ // ILogOutputter overrides
+ virtual void open(const char*) { }
+ virtual void close() { }
+ virtual void show(bool) { }
+ virtual bool write(ELevel level, const char* message);
+ virtual const char* getNewline() const { return ""; }
+};
+
+bool
+CMessageBoxOutputter::write(ELevel level, const char* message)
+{
+ // note any important messages the user may need to know about
+ if (level <= CLog::kWARNING) {
+ s_hasImportantLogMessages = true;
+ }
+
+ // FATAL and PRINT messages get a dialog box if not running as
+ // backend. if we're running as a backend the user will have
+ // a chance to see the messages when we exit.
+ if (!ARG->m_backend && level <= CLog::kFATAL) {
+ MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING);
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
+static
+void
+byeThrow(int x)
+{
+ CArchMiscWindows::daemonFailed(x);
+}
+
+static
+int
+daemonNTMainLoop(int argc, const char** argv)
+{
+ parse(argc, argv);
+ ARG->m_backend = false;
+ return CArchMiscWindows::runDaemon(mainLoop);
+}
+
+static
+int
+daemonNTStartup(int, char**)
+{
+ CSystemLogger sysLogger(DAEMON_NAME, false);
+ bye = &byeThrow;
+ return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop);
+}
+
+static
+int
+foregroundStartup(int argc, char** argv)
+{
+ ARCH->showConsole(false);
+
+ // parse command line
+ parse(argc, argv);
+
+ // never daemonize
+ return mainLoop();
+}
+
+static
+void
+showError(HINSTANCE instance, const char* title, UINT id, const char* arg)
+{
+ CString fmt = CMSWindowsUtil::getString(instance, id);
+ CString msg = CStringUtil::format(fmt.c_str(), arg);
+ MessageBox(NULL, msg.c_str(), title, MB_OK | MB_ICONWARNING);
+}
+
+int WINAPI
+WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
+{
+ try {
+ CArchMiscWindows::setIcons((HICON)LoadImage(instance,
+ MAKEINTRESOURCE(IDI_SYNERGY),
+ IMAGE_ICON,
+ 32, 32, LR_SHARED),
+ (HICON)LoadImage(instance,
+ MAKEINTRESOURCE(IDI_SYNERGY),
+ IMAGE_ICON,
+ 16, 16, LR_SHARED));
+ CArch arch(instance);
+ CMSWindowsScreen::init(instance);
+ CLOG;
+ CThread::getCurrentThread().setPriority(-14);
+ CArgs args;
+
+ // set title on log window
+ ARCH->openConsole((CString(kAppVersion) + " " + "Client").c_str());
+
+ // windows NT family starts services using no command line options.
+ // since i'm not sure how to tell the difference between that and
+ // a user providing no options we'll assume that if there are no
+ // arguments and we're on NT then we're being invoked as a service.
+ // users on NT can use `--daemon' or `--no-daemon' to force us out
+ // of the service code path.
+ StartupFunc startup = &standardStartup;
+ if (!CArchMiscWindows::isWindows95Family()) {
+ if (__argc <= 1) {
+ startup = &daemonNTStartup;
+ }
+ else {
+ startup = &foregroundStartup;
+ }
+ }
+
+ // send PRINT and FATAL output to a message box
+ int result = run(__argc, __argv, new CMessageBoxOutputter, startup);
+
+ // let user examine any messages if we're running as a backend
+ // by putting up a dialog box before exiting.
+ if (args.m_backend && s_hasImportantLogMessages) {
+ showError(instance, args.m_pname, IDS_FAILED, "");
+ }
+
+ delete CLOG;
+ return result;
+ }
+ catch (XBase& e) {
+ showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what());
+ //throw;
+ }
+ catch (XArch& e) {
+ showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str());
+ }
+ catch (...) {
+ showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, "");
+ //throw;
+ }
+ return kExitFailed;
+}
+
+#elif SYSAPI_UNIX
+
+int
+main(int argc, char** argv)
+{
+ CArgs args;
+ try {
+ int result;
+ CArch arch;
+ CLOG;
+ CArgs args;
+ result = run(argc, argv, NULL, &standardStartup);
+ delete CLOG;
+ return result;
+ }
+ catch (XBase& e) {
+ LOG((CLOG_CRIT "Uncaught exception: %s\n", e.what()));
+ throw;
+ }
+ catch (XArch& e) {
+ LOG((CLOG_CRIT "Initialization failed: %s" BYE, e.what().c_str()));
+ return kExitFailed;
+ }
+ catch (...) {
+ LOG((CLOG_CRIT "Uncaught exception: \n"));
+ throw;
+ }
+}
+
+#else
+
+#error no main() for platform
+
+#endif
diff --git a/cmd/synergyc/synergyc.ico b/cmd/synergyc/synergyc.ico
new file mode 100644
index 00000000..89f965f4
Binary files /dev/null and b/cmd/synergyc/synergyc.ico differ
diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc
new file mode 100644
index 00000000..7f2a5dc1
--- /dev/null
+++ b/cmd/synergyc/synergyc.rc
@@ -0,0 +1,141 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include
+#if !defined(IDC_STATIC)
+#define IDC_STATIC (-1)
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include \r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_SYNERGY ICON DISCARDABLE "synergyc.ico"
+IDI_TASKBAR_NOT_RUNNING ICON DISCARDABLE "tb_idle.ico"
+IDI_TASKBAR_NOT_WORKING ICON DISCARDABLE "tb_error.ico"
+IDI_TASKBAR_NOT_CONNECTED ICON DISCARDABLE "tb_wait.ico"
+IDI_TASKBAR_CONNECTED ICON DISCARDABLE "tb_run.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_TASKBAR_STATUS DIALOG DISCARDABLE 0, 0, 145, 18
+STYLE DS_MODALFRAME | WS_POPUP
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_BORDER
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_TASKBAR MENU DISCARDABLE
+BEGIN
+ POPUP "Synergy"
+ BEGIN
+ MENUITEM "Show Status", IDC_TASKBAR_STATUS
+ MENUITEM "Show Log", IDC_TASKBAR_SHOW_LOG
+ MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG
+ POPUP "Set Log Level"
+ BEGIN
+ MENUITEM "Error", IDC_TASKBAR_LOG_LEVEL_ERROR
+
+ MENUITEM "Warning", IDC_TASKBAR_LOG_LEVEL_WARNING
+
+ MENUITEM "Note", IDC_TASKBAR_LOG_LEVEL_NOTE
+
+ MENUITEM "Info", IDC_TASKBAR_LOG_LEVEL_INFO
+
+ MENUITEM "Debug", IDC_TASKBAR_LOG_LEVEL_DEBUG
+
+ MENUITEM "Debug1", IDC_TASKBAR_LOG_LEVEL_DEBUG1
+
+ MENUITEM "Debug2", IDC_TASKBAR_LOG_LEVEL_DEBUG2
+
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "Quit", IDC_TASKBAR_QUIT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK."
+ IDS_INIT_FAILED "Synergy failed to initialize: %{1}"
+ IDS_UNCAUGHT_EXCEPTION "Uncaught exception: %{1}"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/cmd/synergyc/tb_error.ico b/cmd/synergyc/tb_error.ico
new file mode 100644
index 00000000..746a87c9
Binary files /dev/null and b/cmd/synergyc/tb_error.ico differ
diff --git a/cmd/synergyc/tb_idle.ico b/cmd/synergyc/tb_idle.ico
new file mode 100644
index 00000000..4e13a264
Binary files /dev/null and b/cmd/synergyc/tb_idle.ico differ
diff --git a/cmd/synergyc/tb_run.ico b/cmd/synergyc/tb_run.ico
new file mode 100644
index 00000000..88e160cb
Binary files /dev/null and b/cmd/synergyc/tb_run.ico differ
diff --git a/cmd/synergyc/tb_wait.ico b/cmd/synergyc/tb_wait.ico
new file mode 100644
index 00000000..257be0a1
Binary files /dev/null and b/cmd/synergyc/tb_wait.ico differ
diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp
new file mode 100644
index 00000000..e332ccea
--- /dev/null
+++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp
@@ -0,0 +1,374 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CMSWindowsServerTaskBarReceiver.h"
+#include "CServer.h"
+#include "CMSWindowsClipboard.h"
+#include "IEventQueue.h"
+#include "LogOutputters.h"
+#include "BasicTypes.h"
+#include "CArch.h"
+#include "CArchTaskBarWindows.h"
+#include "resource.h"
+
+extern CEvent::Type getReloadConfigEvent();
+extern CEvent::Type getForceReconnectEvent();
+
+//
+// CMSWindowsServerTaskBarReceiver
+//
+
+const UINT CMSWindowsServerTaskBarReceiver::s_stateToIconID[kMaxState] =
+{
+ IDI_TASKBAR_NOT_RUNNING,
+ IDI_TASKBAR_NOT_WORKING,
+ IDI_TASKBAR_NOT_CONNECTED,
+ IDI_TASKBAR_CONNECTED
+};
+
+CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver(
+ HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) :
+ CServerTaskBarReceiver(),
+ m_appInstance(appInstance),
+ m_window(NULL),
+ m_logBuffer(logBuffer)
+{
+ for (UInt32 i = 0; i < kMaxState; ++i) {
+ m_icon[i] = loadIcon(s_stateToIconID[i]);
+ }
+ m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR));
+
+ // don't create the window yet. we'll create it on demand. this
+ // has the side benefit of being created in the thread used for
+ // the task bar. that's good because it means the existence of
+ // the window won't prevent changing the main thread's desktop.
+
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+CMSWindowsServerTaskBarReceiver::~CMSWindowsServerTaskBarReceiver()
+{
+ ARCH->removeReceiver(this);
+ for (UInt32 i = 0; i < kMaxState; ++i) {
+ deleteIcon(m_icon[i]);
+ }
+ DestroyMenu(m_menu);
+ destroyWindow();
+}
+
+void
+CMSWindowsServerTaskBarReceiver::showStatus()
+{
+ // create the window
+ createWindow();
+
+ // lock self while getting status
+ lock();
+
+ // get the current status
+ std::string status = getToolTip();
+
+ // get the connect clients, if any
+ const CClients& clients = getClients();
+
+ // done getting status
+ unlock();
+
+ // update dialog
+ HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS);
+ SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str());
+ child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_CLIENTS);
+ SendMessage(child, LB_RESETCONTENT, 0, 0);
+ for (CClients::const_iterator index = clients.begin();
+ index != clients.end(); ) {
+ const char* client = index->c_str();
+ if (++index == clients.end()) {
+ SendMessage(child, LB_ADDSTRING, 0, (LPARAM)client);
+ }
+ else {
+ SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)client);
+ }
+ }
+
+ if (!IsWindowVisible(m_window)) {
+ // position it by the mouse
+ POINT cursorPos;
+ GetCursorPos(&cursorPos);
+ RECT windowRect;
+ GetWindowRect(m_window, &windowRect);
+ int x = cursorPos.x;
+ int y = cursorPos.y;
+ int fw = GetSystemMetrics(SM_CXDLGFRAME);
+ int fh = GetSystemMetrics(SM_CYDLGFRAME);
+ int ww = windowRect.right - windowRect.left;
+ int wh = windowRect.bottom - windowRect.top;
+ int sw = GetSystemMetrics(SM_CXFULLSCREEN);
+ int sh = GetSystemMetrics(SM_CYFULLSCREEN);
+ if (fw < 1) {
+ fw = 1;
+ }
+ if (fh < 1) {
+ fh = 1;
+ }
+ if (x + ww - fw > sw) {
+ x -= ww - fw;
+ }
+ else {
+ x -= fw;
+ }
+ if (x < 0) {
+ x = 0;
+ }
+ if (y + wh - fh > sh) {
+ y -= wh - fh;
+ }
+ else {
+ y -= fh;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+ SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh,
+ SWP_SHOWWINDOW);
+ }
+}
+
+void
+CMSWindowsServerTaskBarReceiver::runMenu(int x, int y)
+{
+ // do popup menu. we need a window to pass to TrackPopupMenu().
+ // the SetForegroundWindow() and SendMessage() calls around
+ // TrackPopupMenu() are to get the menu to be dismissed when
+ // another window gets activated and are just one of those
+ // win32 weirdnesses.
+ createWindow();
+ SetForegroundWindow(m_window);
+ HMENU menu = GetSubMenu(m_menu, 0);
+ SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE);
+ HMENU logLevelMenu = GetSubMenu(menu, 3);
+ CheckMenuRadioItem(logLevelMenu, 0, 6,
+ CLOG->getFilter() - CLog::kERROR, MF_BYPOSITION);
+ int n = TrackPopupMenu(menu,
+ TPM_NONOTIFY |
+ TPM_RETURNCMD |
+ TPM_LEFTBUTTON |
+ TPM_RIGHTBUTTON,
+ x, y, 0, m_window, NULL);
+ SendMessage(m_window, WM_NULL, 0, 0);
+
+ // perform the requested operation
+ switch (n) {
+ case IDC_TASKBAR_STATUS:
+ showStatus();
+ break;
+
+ case IDC_TASKBAR_LOG:
+ copyLog();
+ break;
+
+ case IDC_TASKBAR_SHOW_LOG:
+ ARCH->showConsole(true);
+ break;
+
+ case IDC_RELOAD_CONFIG:
+ EVENTQUEUE->addEvent(CEvent(getReloadConfigEvent(),
+ IEventQueue::getSystemTarget()));
+ break;
+
+ case IDC_FORCE_RECONNECT:
+ EVENTQUEUE->addEvent(CEvent(getForceReconnectEvent(),
+ IEventQueue::getSystemTarget()));
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_ERROR:
+ CLOG->setFilter(CLog::kERROR);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_WARNING:
+ CLOG->setFilter(CLog::kWARNING);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_NOTE:
+ CLOG->setFilter(CLog::kNOTE);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_INFO:
+ CLOG->setFilter(CLog::kINFO);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG:
+ CLOG->setFilter(CLog::kDEBUG);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG1:
+ CLOG->setFilter(CLog::kDEBUG1);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG2:
+ CLOG->setFilter(CLog::kDEBUG2);
+ break;
+
+ case IDC_TASKBAR_QUIT:
+ quit();
+ break;
+ }
+}
+
+void
+CMSWindowsServerTaskBarReceiver::primaryAction()
+{
+ showStatus();
+}
+
+const IArchTaskBarReceiver::Icon
+CMSWindowsServerTaskBarReceiver::getIcon() const
+{
+ return reinterpret_cast(m_icon[getStatus()]);
+}
+
+void
+CMSWindowsServerTaskBarReceiver::copyLog() const
+{
+ if (m_logBuffer != NULL) {
+ // collect log buffer
+ CString data;
+ for (CBufferedLogOutputter::const_iterator index = m_logBuffer->begin();
+ index != m_logBuffer->end(); ++index) {
+ data += *index;
+ data += "\n";
+ }
+
+ // copy log to clipboard
+ if (!data.empty()) {
+ CMSWindowsClipboard clipboard(m_window);
+ clipboard.open(0);
+ clipboard.emptyUnowned();
+ clipboard.add(IClipboard::kText, data);
+ clipboard.close();
+ }
+ }
+}
+
+void
+CMSWindowsServerTaskBarReceiver::onStatusChanged()
+{
+ if (IsWindowVisible(m_window)) {
+ showStatus();
+ }
+}
+
+HICON
+CMSWindowsServerTaskBarReceiver::loadIcon(UINT id)
+{
+ HANDLE icon = LoadImage(m_appInstance,
+ MAKEINTRESOURCE(id),
+ IMAGE_ICON,
+ 0, 0,
+ LR_DEFAULTCOLOR);
+ return reinterpret_cast(icon);
+}
+
+void
+CMSWindowsServerTaskBarReceiver::deleteIcon(HICON icon)
+{
+ if (icon != NULL) {
+ DestroyIcon(icon);
+ }
+}
+
+void
+CMSWindowsServerTaskBarReceiver::createWindow()
+{
+ // ignore if already created
+ if (m_window != NULL) {
+ return;
+ }
+
+ // get the status dialog
+ m_window = CreateDialogParam(m_appInstance,
+ MAKEINTRESOURCE(IDD_TASKBAR_STATUS),
+ NULL,
+ (DLGPROC)&CMSWindowsServerTaskBarReceiver::staticDlgProc,
+ reinterpret_cast(
+ reinterpret_cast(this)));
+
+ // window should appear on top of everything, including (especially)
+ // the task bar.
+ LONG_PTR style = GetWindowLongPtr(m_window, GWL_EXSTYLE);
+ style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
+ SetWindowLongPtr(m_window, GWL_EXSTYLE, style);
+
+ // tell the task bar about this dialog
+ CArchTaskBarWindows::addDialog(m_window);
+}
+
+void
+CMSWindowsServerTaskBarReceiver::destroyWindow()
+{
+ if (m_window != NULL) {
+ CArchTaskBarWindows::removeDialog(m_window);
+ DestroyWindow(m_window);
+ m_window = NULL;
+ }
+}
+
+BOOL
+CMSWindowsServerTaskBarReceiver::dlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ // use default focus
+ return TRUE;
+
+ case WM_ACTIVATE:
+ // hide when another window is activated
+ if (LOWORD(wParam) == WA_INACTIVE) {
+ ShowWindow(hwnd, SW_HIDE);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+BOOL CALLBACK
+CMSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ // if msg is WM_INITDIALOG, extract the CMSWindowsServerTaskBarReceiver*
+ // and put it in the extra window data then forward the call.
+ CMSWindowsServerTaskBarReceiver* self = NULL;
+ if (msg == WM_INITDIALOG) {
+ self = reinterpret_cast(
+ reinterpret_cast(lParam));
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
+ }
+ else {
+ // get the extra window data and forward the call
+ LONG data = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (data != 0) {
+ self = reinterpret_cast(
+ reinterpret_cast(data));
+ }
+ }
+
+ // forward the message
+ if (self != NULL) {
+ return self->dlgProc(hwnd, msg, wParam, lParam);
+ }
+ else {
+ return (msg == WM_INITDIALOG) ? TRUE : FALSE;
+ }
+}
diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h
new file mode 100644
index 00000000..ab679077
--- /dev/null
+++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h
@@ -0,0 +1,64 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CMSWINDOWSSERVERTASKBARRECEIVER_H
+#define CMSWINDOWSSERVERTASKBARRECEIVER_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "CServerTaskBarReceiver.h"
+#include
+
+class CBufferedLogOutputter;
+
+//! Implementation of CServerTaskBarReceiver for Microsoft Windows
+class CMSWindowsServerTaskBarReceiver : public CServerTaskBarReceiver {
+public:
+ CMSWindowsServerTaskBarReceiver(HINSTANCE, const CBufferedLogOutputter*);
+ virtual ~CMSWindowsServerTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+
+protected:
+ void copyLog() const;
+
+ // CServerTaskBarReceiver overrides
+ virtual void onStatusChanged();
+
+private:
+ HICON loadIcon(UINT);
+ void deleteIcon(HICON);
+ void createWindow();
+ void destroyWindow();
+
+ BOOL dlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam);
+ static BOOL CALLBACK
+ staticDlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam);
+
+private:
+ HINSTANCE m_appInstance;
+ HWND m_window;
+ HMENU m_menu;
+ HICON m_icon[kMaxState];
+ const CBufferedLogOutputter* m_logBuffer;
+ static const UINT s_stateToIconID[];
+};
+
+#endif
diff --git a/cmd/synergys/COSXServerTaskBarReceiver.cpp b/cmd/synergys/COSXServerTaskBarReceiver.cpp
new file mode 100644
index 00000000..8195b84f
--- /dev/null
+++ b/cmd/synergys/COSXServerTaskBarReceiver.cpp
@@ -0,0 +1,56 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "COSXServerTaskBarReceiver.h"
+#include "CArch.h"
+
+//
+// COSXServerTaskBarReceiver
+//
+
+COSXServerTaskBarReceiver::COSXServerTaskBarReceiver(
+ const CBufferedLogOutputter*)
+{
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+COSXServerTaskBarReceiver::~COSXServerTaskBarReceiver()
+{
+ ARCH->removeReceiver(this);
+}
+
+void
+COSXServerTaskBarReceiver::showStatus()
+{
+ // do nothing
+}
+
+void
+COSXServerTaskBarReceiver::runMenu(int, int)
+{
+ // do nothing
+}
+
+void
+COSXServerTaskBarReceiver::primaryAction()
+{
+ // do nothing
+}
+
+const IArchTaskBarReceiver::Icon
+COSXServerTaskBarReceiver::getIcon() const
+{
+ return NULL;
+}
diff --git a/cmd/synergys/COSXServerTaskBarReceiver.h b/cmd/synergys/COSXServerTaskBarReceiver.h
new file mode 100644
index 00000000..7f6dc298
--- /dev/null
+++ b/cmd/synergys/COSXServerTaskBarReceiver.h
@@ -0,0 +1,35 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef COSXSERVERTASKBARRECEIVER_H
+#define COSXSERVERTASKBARRECEIVER_H
+
+#include "CServerTaskBarReceiver.h"
+
+class CBufferedLogOutputter;
+
+//! Implementation of CServerTaskBarReceiver for OS X
+class COSXServerTaskBarReceiver : public CServerTaskBarReceiver {
+public:
+ COSXServerTaskBarReceiver(const CBufferedLogOutputter*);
+ virtual ~COSXServerTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+};
+
+#endif
diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/cmd/synergys/CServerTaskBarReceiver.cpp
new file mode 100644
index 00000000..6555b214
--- /dev/null
+++ b/cmd/synergys/CServerTaskBarReceiver.cpp
@@ -0,0 +1,133 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CServerTaskBarReceiver.h"
+#include "CServer.h"
+#include "CLock.h"
+#include "CStringUtil.h"
+#include "IEventQueue.h"
+#include "CArch.h"
+#include "Version.h"
+
+//
+// CServerTaskBarReceiver
+//
+
+CServerTaskBarReceiver::CServerTaskBarReceiver() :
+ m_state(kNotRunning)
+{
+ // do nothing
+}
+
+CServerTaskBarReceiver::~CServerTaskBarReceiver()
+{
+ // do nothing
+}
+
+void
+CServerTaskBarReceiver::updateStatus(CServer* server, const CString& errorMsg)
+{
+ {
+ // update our status
+ m_errorMessage = errorMsg;
+ if (server == NULL) {
+ if (m_errorMessage.empty()) {
+ m_state = kNotRunning;
+ }
+ else {
+ m_state = kNotWorking;
+ }
+ }
+ else {
+ m_clients.clear();
+ server->getClients(m_clients);
+ if (m_clients.size() <= 1) {
+ m_state = kNotConnected;
+ }
+ else {
+ m_state = kConnected;
+ }
+ }
+
+ // let subclasses have a go
+ onStatusChanged(server);
+ }
+
+ // tell task bar
+ ARCH->updateReceiver(this);
+}
+
+CServerTaskBarReceiver::EState
+CServerTaskBarReceiver::getStatus() const
+{
+ return m_state;
+}
+
+const CString&
+CServerTaskBarReceiver::getErrorMessage() const
+{
+ return m_errorMessage;
+}
+
+const CServerTaskBarReceiver::CClients&
+CServerTaskBarReceiver::getClients() const
+{
+ return m_clients;
+}
+
+void
+CServerTaskBarReceiver::quit()
+{
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+}
+
+void
+CServerTaskBarReceiver::onStatusChanged(CServer*)
+{
+ // do nothing
+}
+
+void
+CServerTaskBarReceiver::lock() const
+{
+ // do nothing
+}
+
+void
+CServerTaskBarReceiver::unlock() const
+{
+ // do nothing
+}
+
+std::string
+CServerTaskBarReceiver::getToolTip() const
+{
+ switch (m_state) {
+ case kNotRunning:
+ return CStringUtil::print("%s: Not running", kAppVersion);
+
+ case kNotWorking:
+ return CStringUtil::print("%s: %s",
+ kAppVersion, m_errorMessage.c_str());
+
+ case kNotConnected:
+ return CStringUtil::print("%s: Waiting for clients", kAppVersion);
+
+ case kConnected:
+ return CStringUtil::print("%s: Connected", kAppVersion);
+
+ default:
+ return "";
+ }
+}
diff --git a/cmd/synergys/CServerTaskBarReceiver.h b/cmd/synergys/CServerTaskBarReceiver.h
new file mode 100644
index 00000000..d6ec8571
--- /dev/null
+++ b/cmd/synergys/CServerTaskBarReceiver.h
@@ -0,0 +1,88 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CSERVERTASKBARRECEIVER_H
+#define CSERVERTASKBARRECEIVER_H
+
+#include "CString.h"
+#include "IArchTaskBarReceiver.h"
+#include "stdvector.h"
+
+class CServer;
+
+//! Implementation of IArchTaskBarReceiver for the synergy server
+class CServerTaskBarReceiver : public IArchTaskBarReceiver {
+public:
+ CServerTaskBarReceiver();
+ virtual ~CServerTaskBarReceiver();
+
+ //! @name manipulators
+ //@{
+
+ //! Update status
+ /*!
+ Determine the status and query required information from the server.
+ */
+ void updateStatus(CServer*, const CString& errorMsg);
+
+ //@}
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus() = 0;
+ virtual void runMenu(int x, int y) = 0;
+ virtual void primaryAction() = 0;
+ virtual void lock() const;
+ virtual void unlock() const;
+ virtual const Icon getIcon() const = 0;
+ virtual std::string getToolTip() const;
+
+protected:
+ typedef std::vector CClients;
+ enum EState {
+ kNotRunning,
+ kNotWorking,
+ kNotConnected,
+ kConnected,
+ kMaxState
+ };
+
+ //! Get status
+ EState getStatus() const;
+
+ //! Get error message
+ const CString& getErrorMessage() const;
+
+ //! Get connected clients
+ const CClients& getClients() const;
+
+ //! Quit app
+ /*!
+ Causes the application to quit gracefully
+ */
+ void quit();
+
+ //! Status change notification
+ /*!
+ Called when status changes. The default implementation does
+ nothing.
+ */
+ virtual void onStatusChanged(CServer* server);
+
+private:
+ EState m_state;
+ CString m_errorMessage;
+ CClients m_clients;
+};
+
+#endif
diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp
new file mode 100644
index 00000000..861d2f8c
--- /dev/null
+++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp
@@ -0,0 +1,56 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CXWindowsServerTaskBarReceiver.h"
+#include "CArch.h"
+
+//
+// CXWindowsServerTaskBarReceiver
+//
+
+CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver(
+ const CBufferedLogOutputter*)
+{
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+CXWindowsServerTaskBarReceiver::~CXWindowsServerTaskBarReceiver()
+{
+ ARCH->removeReceiver(this);
+}
+
+void
+CXWindowsServerTaskBarReceiver::showStatus()
+{
+ // do nothing
+}
+
+void
+CXWindowsServerTaskBarReceiver::runMenu(int, int)
+{
+ // do nothing
+}
+
+void
+CXWindowsServerTaskBarReceiver::primaryAction()
+{
+ // do nothing
+}
+
+const IArchTaskBarReceiver::Icon
+CXWindowsServerTaskBarReceiver::getIcon() const
+{
+ return NULL;
+}
diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.h b/cmd/synergys/CXWindowsServerTaskBarReceiver.h
new file mode 100644
index 00000000..73234123
--- /dev/null
+++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.h
@@ -0,0 +1,35 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CXWINDOWSSERVERTASKBARRECEIVER_H
+#define CXWINDOWSSERVERTASKBARRECEIVER_H
+
+#include "CServerTaskBarReceiver.h"
+
+class CBufferedLogOutputter;
+
+//! Implementation of CServerTaskBarReceiver for X Windows
+class CXWindowsServerTaskBarReceiver : public CServerTaskBarReceiver {
+public:
+ CXWindowsServerTaskBarReceiver(const CBufferedLogOutputter*);
+ virtual ~CXWindowsServerTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+};
+
+#endif
diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am
new file mode 100644
index 00000000..4ad48ce5
--- /dev/null
+++ b/cmd/synergys/Makefile.am
@@ -0,0 +1,98 @@
+# synergy -- mouse and keyboard sharing utility
+# 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+## Process this file with automake to produce Makefile.in
+NULL =
+
+COMMON_SOURCE_FILES = \
+ CServerTaskBarReceiver.cpp \
+ CServerTaskBarReceiver.h \
+ synergys.cpp \
+ $(NULL)
+XWINDOWS_SOURCE_FILES = \
+ CXWindowsServerTaskBarReceiver.cpp \
+ CXWindowsServerTaskBarReceiver.h \
+ $(NULL)
+MSWINDOWS_SOURCE_FILES = \
+ CMSWindowsServerTaskBarReceiver.cpp \
+ CMSWindowsServerTaskBarReceiver.h \
+ resource.h \
+ synergys.rc \
+ $(NULL)
+CARBON_SOURCE_FILES = \
+ COSXServerTaskBarReceiver.cpp \
+ COSXServerTaskBarReceiver.h \
+ $(NULL)
+
+EXTRA_DIST = \
+ Makefile.win \
+ synergys.ico \
+ tb_error.ico \
+ tb_idle.ico \
+ tb_run.ico \
+ tb_wait.ico \
+ $(XWINDOWS_SOURCE_FILES) \
+ $(MSWINDOWS_SOURCE_FILES) \
+ $(CARBON_SOURCE_FILES) \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ $(NULL)
+
+bin_PROGRAMS = synergys
+if XWINDOWS
+synergys_SOURCES = \
+ $(COMMON_SOURCE_FILES) \
+ $(XWINDOWS_SOURCE_FILES) \
+ $(NULL)
+endif
+if MSWINDOWS
+synergys_SOURCES = \
+ $(COMMON_SOURCE_FILES) \
+ $(MSWINDOWS_SOURCE_FILES) \
+ $(NULL)
+endif
+if CARBON
+synergys_SOURCES = \
+ $(COMMON_SOURCE_FILES) \
+ $(CARBON_SOURCE_FILES) \
+ $(NULL)
+synergys_LDFLAGS = \
+ -framework ScreenSaver \
+ -framework IOKit \
+ -framework ApplicationServices \
+ -framework Foundation \
+ $(NULL)
+endif
+synergys_LDADD = \
+ $(top_builddir)/lib/server/libserver.a \
+ $(top_builddir)/lib/platform/libplatform.a \
+ $(top_builddir)/lib/synergy/libsynergy.a \
+ $(top_builddir)/lib/net/libnet.a \
+ $(top_builddir)/lib/io/libio.a \
+ $(top_builddir)/lib/mt/libmt.a \
+ $(top_builddir)/lib/base/libbase.a \
+ $(top_builddir)/lib/common/libcommon.a \
+ $(top_builddir)/lib/arch/libarch.a \
+ $(NULL)
+INCLUDES = \
+ -I$(top_srcdir)/lib/common \
+ -I$(top_srcdir)/lib/arch \
+ -I$(top_srcdir)/lib/base \
+ -I$(top_srcdir)/lib/mt \
+ -I$(top_srcdir)/lib/io \
+ -I$(top_srcdir)/lib/net \
+ -I$(top_srcdir)/lib/synergy \
+ -I$(top_srcdir)/lib/platform \
+ -I$(top_srcdir)/lib/server \
+ $(NULL)
diff --git a/cmd/synergys/Makefile.win b/cmd/synergys/Makefile.win
new file mode 100644
index 00000000..09d39958
--- /dev/null
+++ b/cmd/synergys/Makefile.win
@@ -0,0 +1,89 @@
+# synergy -- mouse and keyboard sharing utility
+# Copyright (C) 2007 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+BIN_SYNERGYS_SRC = cmd\synergys
+BIN_SYNERGYS_DST = $(BUILD_DST)\$(BIN_SYNERGYS_SRC)
+BIN_SYNERGYS_EXE = "$(BUILD_DST)\synergys.exe"
+BIN_SYNERGYS_CPP = \
+ "CServerTaskBarReceiver.cpp" \
+ "CMSWindowsServerTaskBarReceiver.cpp" \
+ "synergys.cpp" \
+ $(NULL)
+BIN_SYNERGYS_OBJ = \
+ "$(BIN_SYNERGYS_DST)\CServerTaskBarReceiver.obj" \
+ "$(BIN_SYNERGYS_DST)\CMSWindowsServerTaskBarReceiver.obj" \
+ "$(BIN_SYNERGYS_DST)\synergys.obj" \
+ $(NULL)
+BIN_SYNERGYS_RC = "$(BIN_SYNERGYS_SRC)\synergys.rc"
+BIN_SYNERGYS_RES = "$(BIN_SYNERGYS_DST)\synergys.res"
+BIN_SYNERGYS_INC = \
+ /I"lib\common" \
+ /I"lib\arch" \
+ /I"lib\base" \
+ /I"lib\mt" \
+ /I"lib\io" \
+ /I"lib\net" \
+ /I"lib\synergy" \
+ /I"lib\platform" \
+ /I"lib\server" \
+ $(NULL)
+BIN_SYNERGYS_LIB = \
+ $(LIB_SERVER_LIB) \
+ $(LIB_PLATFORM_LIB) \
+ $(LIB_SYNERGY_LIB) \
+ $(LIB_NET_LIB) \
+ $(LIB_IO_LIB) \
+ $(LIB_MT_LIB) \
+ $(LIB_BASE_LIB) \
+ $(LIB_ARCH_LIB) \
+ $(LIB_COMMON_LIB) \
+ $(NULL)
+
+CPP_FILES = $(CPP_FILES) $(BIN_SYNERGYS_CPP)
+OBJ_FILES = $(OBJ_FILES) $(BIN_SYNERGYS_OBJ)
+PROGRAMS = $(PROGRAMS) $(BIN_SYNERGYS_EXE)
+
+# Need shell functions.
+guilibs = $(guilibs) shell32.lib
+
+# Dependency rules
+$(BIN_SYNERGYS_OBJ): $(AUTODEP)
+!if EXIST($(BIN_SYNERGYS_DST)\deps.mak)
+!include $(BIN_SYNERGYS_DST)\deps.mak
+!endif
+
+# Build rules. Use batch-mode rules if possible.
+!if DEFINED(_NMAKE_VER)
+{$(BIN_SYNERGYS_SRC)\}.cpp{$(BIN_SYNERGYS_DST)\}.obj::
+!else
+{$(BIN_SYNERGYS_SRC)\}.cpp{$(BIN_SYNERGYS_DST)\}.obj:
+!endif
+ @$(ECHO) Compile in $(BIN_SYNERGYS_SRC)
+ -@$(MKDIR) $(BIN_SYNERGYS_DST) 2>NUL:
+ $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \
+ $(BIN_SYNERGYS_INC) \
+ /Fo$(BIN_SYNERGYS_DST)\ \
+ /Fd$(BIN_SYNERGYS_DST)\src.pdb \
+ $< | $(AUTODEP) $(BIN_SYNERGYS_SRC) $(BIN_SYNERGYS_DST)
+$(BIN_SYNERGYS_RES): $(BIN_SYNERGYS_RC)
+ @$(ECHO) Compile $(**F)
+ -@$(MKDIR) $(BIN_SYNERGYS_DST) 2>NUL:
+ $(rc) $(rcflags) $(rcvars) \
+ /fo$@ \
+ $**
+$(BIN_SYNERGYS_EXE): $(BIN_SYNERGYS_OBJ) $(BIN_SYNERGYS_RES) $(BIN_SYNERGYS_LIB)
+ @$(ECHO) Link $(@F)
+ $(link) $(ldebug) $(guilflags) $(guilibsmt) \
+ /out:$@ \
+ $**
+ $(AUTODEP) $(BIN_SYNERGYS_SRC) $(BIN_SYNERGYS_DST) \
+ $(BIN_SYNERGYS_OBJ:.obj=.d)
diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h
new file mode 100644
index 00000000..0ad5868a
--- /dev/null
+++ b/cmd/synergys/resource.h
@@ -0,0 +1,40 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by synergys.rc
+//
+#define IDS_FAILED 1
+#define IDS_INIT_FAILED 2
+#define IDS_UNCAUGHT_EXCEPTION 3
+#define IDI_SYNERGY 101
+#define IDI_TASKBAR_NOT_RUNNING 102
+#define IDI_TASKBAR_NOT_WORKING 103
+#define IDI_TASKBAR_NOT_CONNECTED 104
+#define IDI_TASKBAR_CONNECTED 105
+#define IDR_TASKBAR 107
+#define IDD_TASKBAR_STATUS 108
+#define IDC_TASKBAR_STATUS_STATUS 1000
+#define IDC_TASKBAR_STATUS_CLIENTS 1001
+#define IDC_TASKBAR_QUIT 40003
+#define IDC_TASKBAR_STATUS 40004
+#define IDC_TASKBAR_LOG 40005
+#define IDC_RELOAD_CONFIG 40006
+#define IDC_FORCE_RECONNECT 40007
+#define IDC_TASKBAR_SHOW_LOG 40008
+#define IDC_TASKBAR_LOG_LEVEL_ERROR 40009
+#define IDC_TASKBAR_LOG_LEVEL_WARNING 40010
+#define IDC_TASKBAR_LOG_LEVEL_NOTE 40011
+#define IDC_TASKBAR_LOG_LEVEL_INFO 40012
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG 40013
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG1 40014
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG2 40015
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 109
+#define _APS_NEXT_COMMAND_VALUE 40016
+#define _APS_NEXT_CONTROL_VALUE 1003
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp
new file mode 100644
index 00000000..4319af1e
--- /dev/null
+++ b/cmd/synergys/synergys.cpp
@@ -0,0 +1,1312 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CClientListener.h"
+#include "CClientProxy.h"
+#include "CConfig.h"
+#include "CPrimaryClient.h"
+#include "CServer.h"
+#include "CScreen.h"
+#include "ProtocolTypes.h"
+#include "Version.h"
+#include "XScreen.h"
+#include "CSocketMultiplexer.h"
+#include "CTCPSocketFactory.h"
+#include "XSocket.h"
+#include "CThread.h"
+#include "CEventQueue.h"
+#include "CFunctionEventJob.h"
+#include "CLog.h"
+#include "CString.h"
+#include "CStringUtil.h"
+#include "LogOutputters.h"
+#include "CArch.h"
+#include "XArch.h"
+#include "stdfstream.h"
+#include
+
+#define DAEMON_RUNNING(running_)
+#if WINAPI_MSWINDOWS
+#include "CArchMiscWindows.h"
+#include "CMSWindowsScreen.h"
+#include "CMSWindowsUtil.h"
+#include "CMSWindowsServerTaskBarReceiver.h"
+#include "resource.h"
+#undef DAEMON_RUNNING
+#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_)
+#elif WINAPI_XWINDOWS
+#include "CXWindowsScreen.h"
+#include "CXWindowsServerTaskBarReceiver.h"
+#elif WINAPI_CARBON
+#include "COSXScreen.h"
+#include "COSXServerTaskBarReceiver.h"
+#endif
+
+// platform dependent name of a daemon
+#if SYSAPI_WIN32
+#define DAEMON_NAME "Synergy Server"
+#elif SYSAPI_UNIX
+#define DAEMON_NAME "synergys"
+#endif
+
+// configuration file name
+#if SYSAPI_WIN32
+#define USR_CONFIG_NAME "synergy.sgc"
+#define SYS_CONFIG_NAME "synergy.sgc"
+#elif SYSAPI_UNIX
+#define USR_CONFIG_NAME ".synergy.conf"
+#define SYS_CONFIG_NAME "synergy.conf"
+#endif
+
+typedef int (*StartupFunc)(int, char**);
+static void parse(int argc, const char* const* argv);
+static bool loadConfig(const CString& pathname);
+static void loadConfig();
+
+//
+// program arguments
+//
+
+#define ARG CArgs::s_instance
+
+class CArgs {
+public:
+ CArgs() :
+ m_pname(NULL),
+ m_backend(false),
+ m_restartable(true),
+ m_daemon(true),
+ m_configFile(),
+ m_logFilter(NULL),
+ m_display(NULL),
+ m_synergyAddress(NULL),
+ m_config(NULL)
+ { s_instance = this; }
+ ~CArgs() { s_instance = NULL; }
+
+public:
+ static CArgs* s_instance;
+ const char* m_pname;
+ bool m_backend;
+ bool m_restartable;
+ bool m_daemon;
+ CString m_configFile;
+ const char* m_logFilter;
+ const char* m_display;
+ CString m_name;
+ CNetworkAddress* m_synergyAddress;
+ CConfig* m_config;
+};
+
+CArgs* CArgs::s_instance = NULL;
+
+
+//
+// platform dependent factories
+//
+
+static
+CScreen*
+createScreen()
+{
+#if WINAPI_MSWINDOWS
+ return new CScreen(new CMSWindowsScreen(true));
+#elif WINAPI_XWINDOWS
+ return new CScreen(new CXWindowsScreen(ARG->m_display, true));
+#elif WINAPI_CARBON
+ return new CScreen(new COSXScreen(true));
+#endif
+}
+
+static
+CServerTaskBarReceiver*
+createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
+{
+#if WINAPI_MSWINDOWS
+ return new CMSWindowsServerTaskBarReceiver(
+ CMSWindowsScreen::getInstance(), logBuffer);
+#elif WINAPI_XWINDOWS
+ return new CXWindowsServerTaskBarReceiver(logBuffer);
+#elif WINAPI_CARBON
+ return new COSXServerTaskBarReceiver(logBuffer);
+#endif
+}
+
+
+//
+// platform independent main
+//
+
+enum EServerState {
+ kUninitialized,
+ kInitializing,
+ kInitializingToStart,
+ kInitialized,
+ kStarting,
+ kStarted
+};
+
+static EServerState s_serverState = kUninitialized;
+static CServer* s_server = NULL;
+static CScreen* s_serverScreen = NULL;
+static CPrimaryClient* s_primaryClient = NULL;
+static CClientListener* s_listener = NULL;
+static CServerTaskBarReceiver* s_taskBarReceiver = NULL;
+static CEvent::Type s_reloadConfigEvent = CEvent::kUnknown;
+static CEvent::Type s_forceReconnectEvent = CEvent::kUnknown;
+static bool s_suspended = false;
+static CEventQueueTimer* s_timer = NULL;
+
+CEvent::Type
+getReloadConfigEvent()
+{
+ return CEvent::registerTypeOnce(s_reloadConfigEvent, "reloadConfig");
+}
+
+CEvent::Type
+getForceReconnectEvent()
+{
+ return CEvent::registerTypeOnce(s_forceReconnectEvent, "forceReconnect");
+}
+
+static
+void
+updateStatus()
+{
+ s_taskBarReceiver->updateStatus(s_server, "");
+}
+
+static
+void
+updateStatus(const CString& msg)
+{
+ s_taskBarReceiver->updateStatus(s_server, msg);
+}
+
+static
+void
+handleClientConnected(const CEvent&, void* vlistener)
+{
+ CClientListener* listener = reinterpret_cast(vlistener);
+ CClientProxy* client = listener->getNextClient();
+ if (client != NULL) {
+ s_server->adoptClient(client);
+ updateStatus();
+ }
+}
+
+static
+CClientListener*
+openClientListener(const CNetworkAddress& address)
+{
+ CClientListener* listen =
+ new CClientListener(address, new CTCPSocketFactory, NULL);
+ EVENTQUEUE->adoptHandler(CClientListener::getConnectedEvent(), listen,
+ new CFunctionEventJob(
+ &handleClientConnected, listen));
+ return listen;
+}
+
+static
+void
+closeClientListener(CClientListener* listen)
+{
+ if (listen != NULL) {
+ EVENTQUEUE->removeHandler(CClientListener::getConnectedEvent(), listen);
+ delete listen;
+ }
+}
+
+static
+void
+handleScreenError(const CEvent&, void*)
+{
+ LOG((CLOG_CRIT "error on screen"));
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+}
+
+
+static void handleSuspend(const CEvent& event, void*);
+static void handleResume(const CEvent& event, void*);
+
+static
+CScreen*
+openServerScreen()
+{
+ CScreen* screen = createScreen();
+ EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
+ screen->getEventTarget(),
+ new CFunctionEventJob(
+ &handleScreenError));
+ EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(),
+ screen->getEventTarget(),
+ new CFunctionEventJob(
+ &handleSuspend));
+ EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(),
+ screen->getEventTarget(),
+ new CFunctionEventJob(
+ &handleResume));
+ return screen;
+}
+
+static
+void
+closeServerScreen(CScreen* screen)
+{
+ if (screen != NULL) {
+ EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
+ screen->getEventTarget());
+ EVENTQUEUE->removeHandler(IScreen::getSuspendEvent(),
+ screen->getEventTarget());
+ EVENTQUEUE->removeHandler(IScreen::getResumeEvent(),
+ screen->getEventTarget());
+ delete screen;
+ }
+}
+
+static
+CPrimaryClient*
+openPrimaryClient(const CString& name, CScreen* screen)
+{
+ LOG((CLOG_DEBUG1 "creating primary screen"));
+ return new CPrimaryClient(name, screen);
+}
+
+static
+void
+closePrimaryClient(CPrimaryClient* primaryClient)
+{
+ delete primaryClient;
+}
+
+static
+void
+handleNoClients(const CEvent&, void*)
+{
+ updateStatus();
+}
+
+static
+void
+handleClientsDisconnected(const CEvent&, void*)
+{
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+}
+
+static
+CServer*
+openServer(const CConfig& config, CPrimaryClient* primaryClient)
+{
+ CServer* server = new CServer(config, primaryClient);
+ EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server,
+ new CFunctionEventJob(handleNoClients));
+ return server;
+}
+
+static
+void
+closeServer(CServer* server)
+{
+ if (server == NULL) {
+ return;
+ }
+
+ // tell all clients to disconnect
+ server->disconnect();
+
+ // wait for clients to disconnect for up to timeout seconds
+ double timeout = 3.0;
+ CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL);
+ EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
+ new CFunctionEventJob(handleClientsDisconnected));
+ EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server,
+ new CFunctionEventJob(handleClientsDisconnected));
+ CEvent event;
+ EVENTQUEUE->getEvent(event);
+ while (event.getType() != CEvent::kQuit) {
+ EVENTQUEUE->dispatchEvent(event);
+ CEvent::deleteData(event);
+ EVENTQUEUE->getEvent(event);
+ }
+ EVENTQUEUE->removeHandler(CEvent::kTimer, timer);
+ EVENTQUEUE->deleteTimer(timer);
+ EVENTQUEUE->removeHandler(CServer::getDisconnectedEvent(), server);
+
+ // done with server
+ delete server;
+}
+
+static bool initServer();
+static bool startServer();
+
+static
+void
+stopRetryTimer()
+{
+ if (s_timer != NULL) {
+ EVENTQUEUE->deleteTimer(s_timer);
+ EVENTQUEUE->removeHandler(CEvent::kTimer, NULL);
+ s_timer = NULL;
+ }
+}
+
+static
+void
+retryHandler(const CEvent&, void*)
+{
+ // discard old timer
+ assert(s_timer != NULL);
+ stopRetryTimer();
+
+ // try initializing/starting the server again
+ switch (s_serverState) {
+ case kUninitialized:
+ case kInitialized:
+ case kStarted:
+ assert(0 && "bad internal server state");
+ break;
+
+ case kInitializing:
+ LOG((CLOG_DEBUG1 "retry server initialization"));
+ s_serverState = kUninitialized;
+ if (!initServer()) {
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+ }
+ break;
+
+ case kInitializingToStart:
+ LOG((CLOG_DEBUG1 "retry server initialization"));
+ s_serverState = kUninitialized;
+ if (!initServer()) {
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+ }
+ else if (s_serverState == kInitialized) {
+ LOG((CLOG_DEBUG1 "starting server"));
+ if (!startServer()) {
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+ }
+ }
+ break;
+
+ case kStarting:
+ LOG((CLOG_DEBUG1 "retry starting server"));
+ s_serverState = kInitialized;
+ if (!startServer()) {
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+ }
+ break;
+ }
+}
+
+static
+bool
+initServer()
+{
+ // skip if already initialized or initializing
+ if (s_serverState != kUninitialized) {
+ return true;
+ }
+
+ double retryTime;
+ CScreen* serverScreen = NULL;
+ CPrimaryClient* primaryClient = NULL;
+ try {
+ CString name = ARG->m_config->getCanonicalName(ARG->m_name);
+ serverScreen = openServerScreen();
+ primaryClient = openPrimaryClient(name, serverScreen);
+ s_serverScreen = serverScreen;
+ s_primaryClient = primaryClient;
+ s_serverState = kInitialized;
+ updateStatus();
+ return true;
+ }
+ catch (XScreenUnavailable& e) {
+ LOG((CLOG_WARN "cannot open primary screen: %s", e.what()));
+ closePrimaryClient(primaryClient);
+ closeServerScreen(serverScreen);
+ updateStatus(CString("cannot open primary screen: ") + e.what());
+ retryTime = e.getRetryTime();
+ }
+ catch (XScreenOpenFailure& e) {
+ LOG((CLOG_CRIT "cannot open primary screen: %s", e.what()));
+ closePrimaryClient(primaryClient);
+ closeServerScreen(serverScreen);
+ return false;
+ }
+ catch (XBase& e) {
+ LOG((CLOG_CRIT "failed to start server: %s", e.what()));
+ closePrimaryClient(primaryClient);
+ closeServerScreen(serverScreen);
+ return false;
+ }
+
+ if (ARG->m_restartable) {
+ // install a timer and handler to retry later
+ assert(s_timer == NULL);
+ LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
+ s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL);
+ EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer,
+ new CFunctionEventJob(&retryHandler, NULL));
+ s_serverState = kInitializing;
+ return true;
+ }
+ else {
+ // don't try again
+ return false;
+ }
+}
+
+static
+bool
+startServer()
+{
+ // skip if already started or starting
+ if (s_serverState == kStarting || s_serverState == kStarted) {
+ return true;
+ }
+
+ // initialize if necessary
+ if (s_serverState != kInitialized) {
+ if (!initServer()) {
+ // hard initialization failure
+ return false;
+ }
+ if (s_serverState == kInitializing) {
+ // not ready to start
+ s_serverState = kInitializingToStart;
+ return true;
+ }
+ assert(s_serverState == kInitialized);
+ }
+
+ double retryTime;
+ CClientListener* listener = NULL;
+ try {
+ listener = openClientListener(ARG->m_config->getSynergyAddress());
+ s_server = openServer(*ARG->m_config, s_primaryClient);
+ s_listener = listener;
+ updateStatus();
+ LOG((CLOG_NOTE "started server"));
+ s_serverState = kStarted;
+ return true;
+ }
+ catch (XSocketAddressInUse& e) {
+ LOG((CLOG_WARN "cannot listen for clients: %s", e.what()));
+ closeClientListener(listener);
+ updateStatus(CString("cannot listen for clients: ") + e.what());
+ retryTime = 10.0;
+ }
+ catch (XBase& e) {
+ LOG((CLOG_CRIT "failed to start server: %s", e.what()));
+ closeClientListener(listener);
+ return false;
+ }
+
+ if (ARG->m_restartable) {
+ // install a timer and handler to retry later
+ assert(s_timer == NULL);
+ LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
+ s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL);
+ EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer,
+ new CFunctionEventJob(&retryHandler, NULL));
+ s_serverState = kStarting;
+ return true;
+ }
+ else {
+ // don't try again
+ return false;
+ }
+}
+
+static
+void
+stopServer()
+{
+ if (s_serverState == kStarted) {
+ closeClientListener(s_listener);
+ closeServer(s_server);
+ s_server = NULL;
+ s_listener = NULL;
+ s_serverState = kInitialized;
+ }
+ else if (s_serverState == kStarting) {
+ stopRetryTimer();
+ s_serverState = kInitialized;
+ }
+ assert(s_server == NULL);
+ assert(s_listener == NULL);
+}
+
+static
+void
+cleanupServer()
+{
+ stopServer();
+ if (s_serverState == kInitialized) {
+ closePrimaryClient(s_primaryClient);
+ closeServerScreen(s_serverScreen);
+ s_primaryClient = NULL;
+ s_serverScreen = NULL;
+ s_serverState = kUninitialized;
+ }
+ else if (s_serverState == kInitializing ||
+ s_serverState == kInitializingToStart) {
+ stopRetryTimer();
+ s_serverState = kUninitialized;
+ }
+ assert(s_primaryClient == NULL);
+ assert(s_serverScreen == NULL);
+ assert(s_serverState == kUninitialized);
+}
+
+static
+void
+handleSuspend(const CEvent&, void*)
+{
+ if (!s_suspended) {
+ LOG((CLOG_INFO "suspend"));
+ stopServer();
+ s_suspended = true;
+ }
+}
+
+static
+void
+handleResume(const CEvent&, void*)
+{
+ if (s_suspended) {
+ LOG((CLOG_INFO "resume"));
+ startServer();
+ s_suspended = false;
+ }
+}
+
+static
+void
+reloadSignalHandler(CArch::ESignal, void*)
+{
+ EVENTQUEUE->addEvent(CEvent(getReloadConfigEvent(),
+ IEventQueue::getSystemTarget()));
+}
+
+static
+void
+reloadConfig(const CEvent&, void*)
+{
+ LOG((CLOG_DEBUG "reload configuration"));
+ if (loadConfig(ARG->m_configFile)) {
+ if (s_server != NULL) {
+ s_server->setConfig(*ARG->m_config);
+ }
+ LOG((CLOG_NOTE "reloaded configuration"));
+ }
+}
+
+static
+void
+forceReconnect(const CEvent&, void*)
+{
+ if (s_server != NULL) {
+ s_server->disconnect();
+ }
+}
+
+static
+int
+mainLoop()
+{
+ // create socket multiplexer. this must happen after daemonization
+ // on unix because threads evaporate across a fork().
+ CSocketMultiplexer multiplexer;
+
+ // create the event queue
+ CEventQueue eventQueue;
+
+ // if configuration has no screens then add this system
+ // as the default
+ if (ARG->m_config->begin() == ARG->m_config->end()) {
+ ARG->m_config->addScreen(ARG->m_name);
+ }
+
+ // set the contact address, if provided, in the config.
+ // otherwise, if the config doesn't have an address, use
+ // the default.
+ if (ARG->m_synergyAddress->isValid()) {
+ ARG->m_config->setSynergyAddress(*ARG->m_synergyAddress);
+ }
+ else if (!ARG->m_config->getSynergyAddress().isValid()) {
+ ARG->m_config->setSynergyAddress(CNetworkAddress(kDefaultPort));
+ }
+
+ // canonicalize the primary screen name
+ CString primaryName = ARG->m_config->getCanonicalName(ARG->m_name);
+ if (primaryName.empty()) {
+ LOG((CLOG_CRIT "unknown screen name `%s'", ARG->m_name.c_str()));
+ return kExitFailed;
+ }
+
+ // start the server. if this return false then we've failed and
+ // we shouldn't retry.
+ LOG((CLOG_DEBUG1 "starting server"));
+ if (!startServer()) {
+ return kExitFailed;
+ }
+
+ // handle hangup signal by reloading the server's configuration
+ ARCH->setSignalHandler(CArch::kHANGUP, &reloadSignalHandler, NULL);
+ EVENTQUEUE->adoptHandler(getReloadConfigEvent(),
+ IEventQueue::getSystemTarget(),
+ new CFunctionEventJob(&reloadConfig));
+
+ // handle force reconnect event by disconnecting clients. they'll
+ // reconnect automatically.
+ EVENTQUEUE->adoptHandler(getForceReconnectEvent(),
+ IEventQueue::getSystemTarget(),
+ new CFunctionEventJob(&forceReconnect));
+
+ // run event loop. if startServer() failed we're supposed to retry
+ // later. the timer installed by startServer() will take care of
+ // that.
+ CEvent event;
+ DAEMON_RUNNING(true);
+ EVENTQUEUE->getEvent(event);
+ while (event.getType() != CEvent::kQuit) {
+ EVENTQUEUE->dispatchEvent(event);
+ CEvent::deleteData(event);
+ EVENTQUEUE->getEvent(event);
+ }
+ DAEMON_RUNNING(false);
+
+ // close down
+ LOG((CLOG_DEBUG1 "stopping server"));
+ EVENTQUEUE->removeHandler(getForceReconnectEvent(),
+ IEventQueue::getSystemTarget());
+ EVENTQUEUE->removeHandler(getReloadConfigEvent(),
+ IEventQueue::getSystemTarget());
+ cleanupServer();
+ updateStatus();
+ LOG((CLOG_NOTE "stopped server"));
+
+ return kExitSuccess;
+}
+
+static
+int
+daemonMainLoop(int, const char**)
+{
+#if SYSAPI_WIN32
+ CSystemLogger sysLogger(DAEMON_NAME, false);
+#else
+ CSystemLogger sysLogger(DAEMON_NAME, true);
+#endif
+ return mainLoop();
+}
+
+static
+int
+standardStartup(int argc, char** argv)
+{
+ if (!ARG->m_daemon) {
+ ARCH->showConsole(false);
+ }
+
+ // parse command line
+ parse(argc, argv);
+
+ // load configuration
+ loadConfig();
+
+ // daemonize if requested
+ if (ARG->m_daemon) {
+ return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop);
+ }
+ else {
+ return mainLoop();
+ }
+}
+
+static
+int
+run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
+{
+ // general initialization
+ ARG->m_synergyAddress = new CNetworkAddress;
+ ARG->m_config = new CConfig;
+ ARG->m_pname = ARCH->getBasename(argv[0]);
+
+ // install caller's output filter
+ if (outputter != NULL) {
+ CLOG->insert(outputter);
+ }
+
+ // save log messages
+ CBufferedLogOutputter logBuffer(1000);
+ CLOG->insert(&logBuffer, true);
+
+ // make the task bar receiver. the user can control this app
+ // through the task bar.
+ s_taskBarReceiver = createTaskBarReceiver(&logBuffer);
+
+ // run
+ int result = startup(argc, argv);
+
+ // done with task bar receiver
+ delete s_taskBarReceiver;
+
+ // done with log buffer
+ CLOG->remove(&logBuffer);
+
+ delete ARG->m_config;
+ delete ARG->m_synergyAddress;
+ return result;
+}
+
+
+//
+// command line parsing
+//
+
+#define BYE "\nTry `%s --help' for more information."
+
+static void (*bye)(int) = &exit;
+
+static
+void
+version()
+{
+ LOG((CLOG_PRINT
+"%s %s, protocol version %d.%d\n"
+"%s",
+ ARG->m_pname,
+ kVersion,
+ kProtocolMajorVersion,
+ kProtocolMinorVersion,
+ kCopyright));
+}
+
+static
+void
+help()
+{
+#if WINAPI_XWINDOWS
+# define USAGE_DISPLAY_ARG \
+" [--display ]"
+# define USAGE_DISPLAY_INFO \
+" --display connect to the X server at \n"
+#else
+# define USAGE_DISPLAY_ARG
+# define USAGE_DISPLAY_INFO
+#endif
+
+#if SYSAPI_WIN32
+
+# define PLATFORM_ARGS \
+" [--daemon|--no-daemon]"
+# define PLATFORM_DESC
+# define PLATFORM_EXTRA \
+"At least one command line argument is required. If you don't otherwise\n" \
+"need an argument use `--daemon'.\n" \
+"\n"
+
+#else
+
+# define PLATFORM_ARGS \
+" [--daemon|--no-daemon]"
+# define PLATFORM_DESC
+# define PLATFORM_EXTRA
+
+#endif
+
+ LOG((CLOG_PRINT
+"Usage: %s"
+" [--address ]"
+" [--config ]"
+" [--debug ]"
+USAGE_DISPLAY_ARG
+" [--name ]"
+" [--restart|--no-restart]"
+PLATFORM_ARGS
+"\n\n"
+"Start the synergy mouse/keyboard sharing server.\n"
+"\n"
+" -a, --address listen for clients on the given address.\n"
+" -c, --config use the named configuration file instead.\n"
+" -d, --debug filter out log messages with priorty below level.\n"
+" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n"
+" DEBUG, DEBUG1, DEBUG2.\n"
+USAGE_DISPLAY_INFO
+" -f, --no-daemon run the server in the foreground.\n"
+"* --daemon run the server as a daemon.\n"
+" -n, --name use screen-name instead the hostname to identify\n"
+" this screen in the configuration.\n"
+" -1, --no-restart do not try to restart the server if it fails for\n"
+" some reason.\n"
+"* --restart restart the server automatically if it fails.\n"
+PLATFORM_DESC
+" -h, --help display this help and exit.\n"
+" --version display version information and exit.\n"
+"\n"
+"* marks defaults.\n"
+"\n"
+PLATFORM_EXTRA
+"The argument for --address is of the form: [][:]. The\n"
+"hostname must be the address or hostname of an interface on the system.\n"
+"The default is to listen on all interfaces. The port overrides the\n"
+"default port, %d.\n"
+"\n"
+"If no configuration file pathname is provided then the first of the\n"
+"following to load successfully sets the configuration:\n"
+" %s\n"
+" %s\n"
+"If no configuration file can be loaded then the configuration uses its\n"
+"defaults with just the server screen.\n"
+"\n"
+"Where log messages go depends on the platform and whether or not the\n"
+"server is running as a daemon.",
+ ARG->m_pname,
+ kDefaultPort,
+ ARCH->concatPath(
+ ARCH->getUserDirectory(),
+ USR_CONFIG_NAME).c_str(),
+ ARCH->concatPath(
+ ARCH->getSystemDirectory(),
+ SYS_CONFIG_NAME).c_str()));
+}
+
+static
+bool
+isArg(int argi, int argc, const char* const* argv,
+ const char* name1, const char* name2,
+ int minRequiredParameters = 0)
+{
+ if ((name1 != NULL && strcmp(argv[argi], name1) == 0) ||
+ (name2 != NULL && strcmp(argv[argi], name2) == 0)) {
+ // match. check args left.
+ if (argi + minRequiredParameters >= argc) {
+ LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
+ ARG->m_pname, argv[argi], ARG->m_pname));
+ bye(kExitArgs);
+ }
+ return true;
+ }
+
+ // no match
+ return false;
+}
+
+static
+void
+parse(int argc, const char* const* argv)
+{
+ assert(ARG->m_pname != NULL);
+ assert(argv != NULL);
+ assert(argc >= 1);
+
+ // set defaults
+ ARG->m_name = ARCH->getHostName();
+
+ // parse options
+ int i = 1;
+ for (; i < argc; ++i) {
+ if (isArg(i, argc, argv, "-d", "--debug", 1)) {
+ // change logging level
+ ARG->m_logFilter = argv[++i];
+ }
+
+ else if (isArg(i, argc, argv, "-a", "--address", 1)) {
+ // save listen address
+ try {
+ *ARG->m_synergyAddress = CNetworkAddress(argv[i + 1],
+ kDefaultPort);
+ ARG->m_synergyAddress->resolve();
+ }
+ catch (XSocketAddress& e) {
+ LOG((CLOG_PRINT "%s: %s" BYE,
+ ARG->m_pname, e.what(), ARG->m_pname));
+ bye(kExitArgs);
+ }
+ ++i;
+ }
+
+ else if (isArg(i, argc, argv, "-n", "--name", 1)) {
+ // save screen name
+ ARG->m_name = argv[++i];
+ }
+
+ else if (isArg(i, argc, argv, "-c", "--config", 1)) {
+ // save configuration file path
+ ARG->m_configFile = argv[++i];
+ }
+
+#if WINAPI_XWINDOWS
+ else if (isArg(i, argc, argv, "-display", "--display", 1)) {
+ // use alternative display
+ ARG->m_display = argv[++i];
+ }
+#endif
+
+ else if (isArg(i, argc, argv, "-f", "--no-daemon")) {
+ // not a daemon
+ ARG->m_daemon = false;
+ }
+
+ else if (isArg(i, argc, argv, NULL, "--daemon")) {
+ // daemonize
+ ARG->m_daemon = true;
+ }
+
+ else if (isArg(i, argc, argv, "-1", "--no-restart")) {
+ // don't try to restart
+ ARG->m_restartable = false;
+ }
+
+ else if (isArg(i, argc, argv, NULL, "--restart")) {
+ // try to restart
+ ARG->m_restartable = true;
+ }
+
+ else if (isArg(i, argc, argv, "-z", NULL)) {
+ ARG->m_backend = true;
+ }
+
+ else if (isArg(i, argc, argv, "-h", "--help")) {
+ help();
+ bye(kExitSuccess);
+ }
+
+ else if (isArg(i, argc, argv, NULL, "--version")) {
+ version();
+ bye(kExitSuccess);
+ }
+
+ else if (isArg(i, argc, argv, "--", NULL)) {
+ // remaining arguments are not options
+ ++i;
+ break;
+ }
+
+ else if (argv[i][0] == '-') {
+ LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
+ ARG->m_pname, argv[i], ARG->m_pname));
+ bye(kExitArgs);
+ }
+
+ else {
+ // this and remaining arguments are not options
+ break;
+ }
+ }
+
+ // no non-option arguments are allowed
+ if (i != argc) {
+ LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
+ ARG->m_pname, argv[i], ARG->m_pname));
+ bye(kExitArgs);
+ }
+
+ // increase default filter level for daemon. the user must
+ // explicitly request another level for a daemon.
+ if (ARG->m_daemon && ARG->m_logFilter == NULL) {
+#if SYSAPI_WIN32
+ if (CArchMiscWindows::isWindows95Family()) {
+ // windows 95 has no place for logging so avoid showing
+ // the log console window.
+ ARG->m_logFilter = "FATAL";
+ }
+ else
+#endif
+ {
+ ARG->m_logFilter = "NOTE";
+ }
+ }
+
+ // set log filter
+ if (!CLOG->setFilter(ARG->m_logFilter)) {
+ LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
+ ARG->m_pname, ARG->m_logFilter, ARG->m_pname));
+ bye(kExitArgs);
+ }
+
+ // identify system
+ LOG((CLOG_INFO "Synergy server %s on %s", kVersion, ARCH->getOSName().c_str()));
+}
+
+static
+bool
+loadConfig(const CString& pathname)
+{
+ try {
+ // load configuration
+ LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str()));
+ std::ifstream configStream(pathname.c_str());
+ if (!configStream.is_open()) {
+ // report failure to open configuration as a debug message
+ // since we try several paths and we expect some to be
+ // missing.
+ LOG((CLOG_DEBUG "cannot open configuration \"%s\"",
+ pathname.c_str()));
+ return false;
+ }
+ configStream >> *ARG->m_config;
+ LOG((CLOG_DEBUG "configuration read successfully"));
+ return true;
+ }
+ catch (XConfigRead& e) {
+ // report error in configuration file
+ LOG((CLOG_ERR "cannot read configuration \"%s\": %s",
+ pathname.c_str(), e.what()));
+ }
+ return false;
+}
+
+static
+void
+loadConfig()
+{
+ bool loaded = false;
+
+ // load the config file, if specified
+ if (!ARG->m_configFile.empty()) {
+ loaded = loadConfig(ARG->m_configFile);
+ }
+
+ // load the default configuration if no explicit file given
+ else {
+ // get the user's home directory
+ CString path = ARCH->getUserDirectory();
+ if (!path.empty()) {
+ // complete path
+ path = ARCH->concatPath(path, USR_CONFIG_NAME);
+
+ // now try loading the user's configuration
+ if (loadConfig(path)) {
+ loaded = true;
+ ARG->m_configFile = path;
+ }
+ }
+ if (!loaded) {
+ // try the system-wide config file
+ path = ARCH->getSystemDirectory();
+ if (!path.empty()) {
+ path = ARCH->concatPath(path, SYS_CONFIG_NAME);
+ if (loadConfig(path)) {
+ loaded = true;
+ ARG->m_configFile = path;
+ }
+ }
+ }
+ }
+
+ if (!loaded) {
+ LOG((CLOG_PRINT "%s: no configuration available", ARG->m_pname));
+ bye(kExitConfig);
+ }
+}
+
+
+//
+// platform dependent entry points
+//
+
+#if SYSAPI_WIN32
+
+static bool s_hasImportantLogMessages = false;
+
+//
+// CMessageBoxOutputter
+//
+// This class writes severe log messages to a message box
+//
+
+class CMessageBoxOutputter : public ILogOutputter {
+public:
+ CMessageBoxOutputter() { }
+ virtual ~CMessageBoxOutputter() { }
+
+ // ILogOutputter overrides
+ virtual void open(const char*) { }
+ virtual void close() { }
+ virtual void show(bool) { }
+ virtual bool write(ELevel level, const char* message);
+ virtual const char* getNewline() const { return ""; }
+};
+
+bool
+CMessageBoxOutputter::write(ELevel level, const char* message)
+{
+ // note any important messages the user may need to know about
+ if (level <= CLog::kWARNING) {
+ s_hasImportantLogMessages = true;
+ }
+
+ // FATAL and PRINT messages get a dialog box if not running as
+ // backend. if we're running as a backend the user will have
+ // a chance to see the messages when we exit.
+ if (!ARG->m_backend && level <= CLog::kFATAL) {
+ MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING);
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
+static
+void
+byeThrow(int x)
+{
+ CArchMiscWindows::daemonFailed(x);
+}
+
+static
+int
+daemonNTMainLoop(int argc, const char** argv)
+{
+ parse(argc, argv);
+ ARG->m_backend = false;
+ loadConfig();
+ return CArchMiscWindows::runDaemon(mainLoop);
+}
+
+static
+int
+daemonNTStartup(int, char**)
+{
+ CSystemLogger sysLogger(DAEMON_NAME, false);
+ bye = &byeThrow;
+ return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop);
+}
+
+static
+int
+foregroundStartup(int argc, char** argv)
+{
+ ARCH->showConsole(false);
+
+ // parse command line
+ parse(argc, argv);
+
+ // load configuration
+ loadConfig();
+
+ // never daemonize
+ return mainLoop();
+}
+
+static
+void
+showError(HINSTANCE instance, const char* title, UINT id, const char* arg)
+{
+ CString fmt = CMSWindowsUtil::getString(instance, id);
+ CString msg = CStringUtil::format(fmt.c_str(), arg);
+ MessageBox(NULL, msg.c_str(), title, MB_OK | MB_ICONWARNING);
+}
+
+int WINAPI
+WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
+{
+ try {
+ CArchMiscWindows::setIcons((HICON)LoadImage(instance,
+ MAKEINTRESOURCE(IDI_SYNERGY),
+ IMAGE_ICON,
+ 32, 32, LR_SHARED),
+ (HICON)LoadImage(instance,
+ MAKEINTRESOURCE(IDI_SYNERGY),
+ IMAGE_ICON,
+ 16, 16, LR_SHARED));
+ CArch arch(instance);
+ CMSWindowsScreen::init(instance);
+ CLOG;
+ CThread::getCurrentThread().setPriority(-14);
+ CArgs args;
+
+ // set title on log window
+ ARCH->openConsole((CString(kAppVersion) + " " + "Server").c_str());
+
+ // windows NT family starts services using no command line options.
+ // since i'm not sure how to tell the difference between that and
+ // a user providing no options we'll assume that if there are no
+ // arguments and we're on NT then we're being invoked as a service.
+ // users on NT can use `--daemon' or `--no-daemon' to force us out
+ // of the service code path.
+ StartupFunc startup = &standardStartup;
+ if (!CArchMiscWindows::isWindows95Family()) {
+ if (__argc <= 1) {
+ startup = &daemonNTStartup;
+ }
+ else {
+ startup = &foregroundStartup;
+ }
+ }
+
+ // send PRINT and FATAL output to a message box
+ int result = run(__argc, __argv, new CMessageBoxOutputter, startup);
+
+ // let user examine any messages if we're running as a backend
+ // by putting up a dialog box before exiting.
+ if (args.m_backend && s_hasImportantLogMessages) {
+ showError(instance, args.m_pname, IDS_FAILED, "");
+ }
+
+ delete CLOG;
+ return result;
+ }
+ catch (XBase& e) {
+ showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what());
+ //throw;
+ }
+ catch (XArch& e) {
+ showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str());
+ }
+ catch (...) {
+ showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, "");
+ //throw;
+ }
+ return kExitFailed;
+}
+
+#elif SYSAPI_UNIX
+
+int
+main(int argc, char** argv)
+{
+ CArgs args;
+ try {
+ int result;
+ CArch arch;
+ CLOG;
+ CArgs args;
+ result = run(argc, argv, NULL, &standardStartup);
+ delete CLOG;
+ return result;
+ }
+ catch (XBase& e) {
+ LOG((CLOG_CRIT "Uncaught exception: %s\n", e.what()));
+ throw;
+ }
+ catch (XArch& e) {
+ LOG((CLOG_CRIT "Initialization failed: %s" BYE, e.what().c_str()));
+ return kExitFailed;
+ }
+ catch (...) {
+ LOG((CLOG_CRIT "Uncaught exception: \n"));
+ throw;
+ }
+}
+
+#else
+
+#error no main() for platform
+
+#endif
diff --git a/cmd/synergys/synergys.ico b/cmd/synergys/synergys.ico
new file mode 100644
index 00000000..89f965f4
Binary files /dev/null and b/cmd/synergys/synergys.ico differ
diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc
new file mode 100644
index 00000000..d56a4313
--- /dev/null
+++ b/cmd/synergys/synergys.rc
@@ -0,0 +1,146 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include
+#if !defined(IDC_STATIC)
+#define IDC_STATIC (-1)
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include \r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_SYNERGY ICON DISCARDABLE "synergys.ico"
+IDI_TASKBAR_NOT_RUNNING ICON DISCARDABLE "tb_idle.ico"
+IDI_TASKBAR_NOT_WORKING ICON DISCARDABLE "tb_error.ico"
+IDI_TASKBAR_NOT_CONNECTED ICON DISCARDABLE "tb_wait.ico"
+IDI_TASKBAR_CONNECTED ICON DISCARDABLE "tb_run.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_TASKBAR MENU DISCARDABLE
+BEGIN
+ POPUP "Synergy"
+ BEGIN
+ MENUITEM "Show Status", IDC_TASKBAR_STATUS
+ MENUITEM "Show Log", IDC_TASKBAR_SHOW_LOG
+ MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG
+ POPUP "Set Log Level"
+ BEGIN
+ MENUITEM "Error", IDC_TASKBAR_LOG_LEVEL_ERROR
+
+ MENUITEM "Warning", IDC_TASKBAR_LOG_LEVEL_WARNING
+
+ MENUITEM "Note", IDC_TASKBAR_LOG_LEVEL_NOTE
+
+ MENUITEM "Info", IDC_TASKBAR_LOG_LEVEL_INFO
+
+ MENUITEM "Debug", IDC_TASKBAR_LOG_LEVEL_DEBUG
+
+ MENUITEM "Debug1", IDC_TASKBAR_LOG_LEVEL_DEBUG1
+
+ MENUITEM "Debug2", IDC_TASKBAR_LOG_LEVEL_DEBUG2
+
+ END
+ MENUITEM "Reload Configuration", IDC_RELOAD_CONFIG
+ MENUITEM "Force Reconnect", IDC_FORCE_RECONNECT
+ MENUITEM SEPARATOR
+ MENUITEM "Quit", IDC_TASKBAR_QUIT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_TASKBAR_STATUS DIALOG DISCARDABLE 0, 0, 145, 60
+STYLE DS_MODALFRAME | WS_POPUP
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_BORDER
+ LISTBOX IDC_TASKBAR_STATUS_CLIENTS,3,17,139,40,NOT LBS_NOTIFY |
+ LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_VSCROLL |
+ WS_TABSTOP
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK."
+ IDS_INIT_FAILED "Synergy failed to initialize: %{1}"
+ IDS_UNCAUGHT_EXCEPTION "Uncaught exception: %{1}"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/cmd/synergys/tb_error.ico b/cmd/synergys/tb_error.ico
new file mode 100644
index 00000000..746a87c9
Binary files /dev/null and b/cmd/synergys/tb_error.ico differ
diff --git a/cmd/synergys/tb_idle.ico b/cmd/synergys/tb_idle.ico
new file mode 100644
index 00000000..4e13a264
Binary files /dev/null and b/cmd/synergys/tb_idle.ico differ
diff --git a/cmd/synergys/tb_run.ico b/cmd/synergys/tb_run.ico
new file mode 100644
index 00000000..88e160cb
Binary files /dev/null and b/cmd/synergys/tb_run.ico differ
diff --git a/cmd/synergys/tb_wait.ico b/cmd/synergys/tb_wait.ico
new file mode 100644
index 00000000..257be0a1
Binary files /dev/null and b/cmd/synergys/tb_wait.ico differ
diff --git a/config/config.guess b/config/config.guess
new file mode 100755
index 00000000..6ead80a0
--- /dev/null
+++ b/config/config.guess
@@ -0,0 +1,1327 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+
+timestamp='2001-08-21'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner .
+# Please send patches to .
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to ."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script.
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int dummy(){}" > $dummy.c ;
+ for c in cc gcc c89 ; do
+ ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ;
+ if test $? = 0 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ rm -f $dummy.c $dummy.o $dummy.rel ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case "${UNAME_MACHINE}" in
+ i?86)
+ test -z "$VENDOR" && VENDOR=pc
+ ;;
+ *)
+ test -z "$VENDOR" && VENDOR=unknown
+ ;;
+esac
+test -f /etc/SuSE-release && VENDOR=suse
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # Netbsd (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ # Determine the machine/vendor (is the vendor relevant).
+ case "${UNAME_MACHINE}" in
+ amiga) machine=m68k-unknown ;;
+ arm32) machine=arm-unknown ;;
+ atari*) machine=m68k-atari ;;
+ sun3*) machine=m68k-sun ;;
+ mac68k) machine=m68k-apple ;;
+ macppc) machine=powerpc-apple ;;
+ hp3[0-9][05]) machine=m68k-hp ;;
+ ibmrt|romp-ibm) machine=romp-ibm ;;
+ *) machine=${UNAME_MACHINE}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE}" in
+ i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ cat <$dummy.s
+ .data
+\$Lformat:
+ .byte 37,100,45,37,120,10,0 # "%d-%x\n"
+
+ .text
+ .globl main
+ .align 4
+ .ent main
+main:
+ .frame \$30,16,\$26,0
+ ldgp \$29,0(\$27)
+ .prologue 1
+ .long 0x47e03d80 # implver \$0
+ lda \$2,-1
+ .long 0x47e20c21 # amask \$2,\$1
+ lda \$16,\$Lformat
+ mov \$0,\$17
+ not \$1,\$18
+ jsr \$26,printf
+ ldgp \$29,0(\$26)
+ mov 0,\$16
+ jsr \$26,exit
+ .end main
+EOF
+ eval $set_cc_for_build
+ $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ case `./$dummy` in
+ 0-0)
+ UNAME_MACHINE="alpha"
+ ;;
+ 1-0)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 1-1)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 1-101)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 2-303)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ 2-307)
+ UNAME_MACHINE="alphaev67"
+ ;;
+ 2-1307)
+ UNAME_MACHINE="alphaev68"
+ ;;
+ esac
+ fi
+ rm -f $dummy.s $dummy
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit 0;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ arc64:OpenBSD:*:*)
+ echo mips64el-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hkmips:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:NetBSD:*)
+ echo `uname -p`-unknown-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ eval $set_cc_for_build
+ $CC_FOR_BUILD $dummy.c -o $dummy \
+ && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && rm -f $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ sed 's/^ //' << EOF >$dummy.c
+ #include
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ eval $set_cc_for_build
+ $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ case "${HPUX_REV}" in
+ 11.[0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ esac ;;
+ esac
+ fi ;;
+ esac
+ if [ "${HP_ARCH}" = "" ]; then
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include
+ #include
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ eval $set_cc_for_build
+ (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+ if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi
+ rm -f $dummy.c $dummy
+ fi ;;
+ esac
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ sed 's/^ //' << EOF >$dummy.c
+ #include
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ eval $set_cc_for_build
+ $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ hppa*:OpenBSD:*:*)
+ echo hppa-unknown-openbsd
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*X-MP:*:*:*)
+ echo xmp-cray-unicos
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3D:*:*:*)
+ echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY-2:*:*:*)
+ echo cray2-cray-unicos
+ exit 0 ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i386-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit 0 ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux
+ exit 0 ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux
+ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux
+ exit 0 ;;
+ mips:Linux:*:*)
+ case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in
+ big) echo mips-${VENDOR}-linux && exit 0 ;;
+ little) echo mipsel-${VENDOR}-linux && exit 0 ;;
+ esac
+ case `sed -n '/^system type/s/^.*: \([^ ]*\).*/\1/p' < /proc/cpuinfo` in
+ SGI|sgi) echo mips-${VENDOR}-linux-gnu && exit 0 ;;
+ esac
+ ;;
+ ppc:Linux:*:*|ppc64:Linux:*:*)
+ echo powerpc-${VENDOR}-linux
+ exit 0 ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit 0 ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-${VENDOR}-linux${LIBC}
+ exit 0 ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-${VENDOR}-linux ;;
+ PA8*) echo hppa2.0-${VENDOR}-linux ;;
+ *) echo hppa-${VENDOR}-linux ;;
+ esac
+ exit 0 ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-${VENDOR}-linux
+ exit 0 ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit 0 ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux
+ exit 0 ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux
+ exit 0 ;;
+ x86_64:Linux:*:*)
+ echo x86_64-${VENDOR}-linux
+ exit 0 ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ ld_supported_targets=`cd /; ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-${VENDOR}-linux"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-${VENDOR}-linuxaout"
+ exit 0 ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-${VENDOR}-linuxcoff"
+ exit 0 ;;
+ "")
+ # Either a pre-BFD a.out linker (linuxoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-${VENDOR}-linuxoldld"
+ exit 0 ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ cat >$dummy.c <
+#ifdef __cplusplus
+#include /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __ELF__
+# ifdef __GLIBC__
+# if __GLIBC__ >= 2
+ printf ("%s-${VENDOR}-linux\n", argv[1]);
+# else
+ printf ("%s-${VENDOR}-linuxlibc1\n", argv[1]);
+# endif
+# else
+ printf ("%s-${VENDOR}-linuxlibc1\n", argv[1]);
+# endif
+#else
+ printf ("%s-${VENDOR}-linuxaout\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ eval $set_cc_for_build
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i*86:*:5:[78]*)
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit 0 ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes .
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Darwin:*:*)
+ echo `uname -p`-apple-darwin${UNAME_RELEASE}
+ exit 0 ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ if test "${UNAME_MACHINE}" = "x86pc"; then
+ UNAME_MACHINE=pc
+ fi
+ echo `uname -p`-${UNAME_MACHINE}-nto-qnx
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit 0 ;;
+ NSR-[KW]:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit 0 ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit 0 ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit 0 ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit 0 ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit 0 ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit 0 ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit 0 ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit 0 ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit 0 ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit 0 ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit 0 ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit 0 ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <
+# include
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+eval $set_cc_for_build
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+cat >&2 < in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config/config.sub b/config/config.sub
new file mode 100755
index 00000000..83f4b015
--- /dev/null
+++ b/config/config.sub
@@ -0,0 +1,1410 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+
+timestamp='2001-08-13'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to .
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to ."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit 0;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | c4x | clipper \
+ | d10v | d30v | dsp16xx \
+ | fr30 \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | m32r | m68000 | m68k | m88k | mcore \
+ | mips16 | mips64 | mips64el | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el | mips64vr4300 \
+ | mips64vr4300el | mips64vr5000 | mips64vr5000el \
+ | mipsbe | mipsel | mipsle | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | ns16k | ns32k \
+ | openrisc \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | s390 | s390x \
+ | sh | sh[34] | sh[34]eb | shbe | shle \
+ | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic80 | tron \
+ | v850 \
+ | we32k \
+ | x86 | xscale \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alphapca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armv*-* \
+ | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c54x-* \
+ | clipper-* | cray2-* | cydra-* \
+ | d10v-* | d30v-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | m32r-* \
+ | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \
+ | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipsel-* \
+ | mipsle-* | mipstx39-* | mipstx39el-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | s390-* | s390x-* \
+ | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclite-* \
+ | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* \
+ | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \
+ | v850-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ [cjt]90)
+ basic_machine=${basic_machine}-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mipsel*-linux*)
+ basic_machine=mipsel-unknown
+ ;;
+ mips*-linux*)
+ basic_machine=mips-unknown
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ mmix*)
+ basic_machine=mmix-knuth
+ os=-mmixware
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2)
+ basic_machine=i686-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc64) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sparclite-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=t3e-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ windows32)
+ basic_machine=i386-pc
+ os=-windows32-msvcrt
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ mips)
+ case $os in
+ linux*)
+ basic_machine=mips-unknown
+ ;;
+ *)
+ basic_machine=mips-mips
+ ;;
+ esac
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh3 | sh4 | sh3eb | sh4eb)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ c4x*)
+ basic_machine=c4x-none
+ os=-coff
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto*)
+ os=-nto-qnx
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config/depcomp b/config/depcomp
new file mode 100755
index 00000000..65899658
--- /dev/null
+++ b/config/depcomp
@@ -0,0 +1,411 @@
+#! /bin/sh
+
+# depcomp - compile a program generating dependencies as side-effects
+# Copyright 1999, 2000 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva .
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+ echo "depcomp: Variables source, object and depmode must be set" 1>&2
+ exit 1
+fi
+# `libtool' can also be set to `yes' or `no'.
+
+depfile=${depfile-`echo "$object" | sed 's,\([^/]*\)$,.deps/\1,;s/\.\([^.]*\)$/.P\1/'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags. We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write. Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+ # HP compiler uses -M and no extra arg.
+ gccflag=-M
+ depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+ # This is just like dashmstdout with a different argument.
+ dashmflag=-xM
+ depmode=dashmstdout
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want. Yay! Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff. Hmm.
+ "$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ mv "$tmpdepfile" "$depfile"
+ ;;
+
+gcc)
+## There are various ways to get dependency output from gcc. Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+## up in a subdir. Having to rename by hand is ugly.
+## (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+## -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+## than renaming).
+ if test -z "$gccflag"; then
+ gccflag=-MD,
+ fi
+ "$@" -Wp,"$gccflag$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+ sed -e 's/^[^:]*: / /' \
+ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header). We avoid this by adding
+## dummy dependencies for each header file. Too bad gcc doesn't do
+## this for us directly.
+ tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'. On the theory
+## that the space means something, we add a space to the output as
+## well.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+sgi)
+ if test "$libtool" = yes; then
+ "$@" "-Wp,-MDupdate,$tmpdepfile"
+ else
+ "$@" -MDupdate "$tmpdepfile"
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+
+ if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
+ echo "$object : \\" > "$depfile"
+
+ # Clip off the initial element (the dependent). Don't try to be
+ # clever and replace this with sed code, as IRIX sed won't handle
+ # lines with more than a fixed number of characters (4096 in
+ # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
+ # the IRIX cc adds comments like `#:fec' to the end of the
+ # dependency line.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+ tr '
+' ' ' >> $depfile
+ echo >> $depfile
+
+ # The second pass generates a dummy entry for each header file.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+ >> $depfile
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+aix)
+ # The C for AIX Compiler uses -M and outputs the dependencies
+ # in a .u file. This file always lives in the current directory.
+ # Also, the AIX compiler puts `$object:' at the start of each line;
+ # $object doesn't have directory information.
+ stripped=`echo "$object" | sed -e 's,^.*/,,' -e 's/\(.*\)\..*$/\1/'`
+ tmpdepfile="$stripped.u"
+ outname="$stripped.o"
+ if test "$libtool" = yes; then
+ "$@" -Wc,-M
+ else
+ "$@" -M
+ fi
+
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+
+ if test -f "$tmpdepfile"; then
+ # Each line is of the form `foo.o: dependent.h'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
+ sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+tru64)
+ # The Tru64 AIX compiler uses -MD to generate dependencies as a side
+ # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+ # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+ # dependencies in `foo.d' instead, so we check for that too.
+ # Subdirectories are respected.
+
+ tmpdepfile1="$object.d"
+ tmpdepfile2=`echo "$object" | sed -e 's/.o$/.d/'`
+ if test "$libtool" = yes; then
+ "$@" -Wc,-MD
+ else
+ "$@" -MD
+ fi
+
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2"
+ exit $stat
+ fi
+
+ if test -f "$tmpdepfile1"; then
+ tmpdepfile="$tmpdepfile1"
+ else
+ tmpdepfile="$tmpdepfile2"
+ fi
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ # That's a space and a tab in the [].
+ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+#nosideeffect)
+ # This comment above is used by automake to tell side-effect
+ # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the proprocessed file to stdout, regardless of -o,
+ # because we must use -o when running libtool.
+ test -z "$dashmflag" && dashmflag=-M
+ ( IFS=" "
+ case " $* " in
+ *" --mode=compile "*) # this is libtool, let us make it quiet
+ for arg
+ do # cycle over the arguments
+ case "$arg" in
+ "--mode=compile")
+ # insert --quiet before "--mode=compile"
+ set fnord "$@" --quiet
+ shift # fnord
+ ;;
+ esac
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # "$arg"
+ done
+ ;;
+ esac
+ "$@" $dashmflag | sed 's:^[^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
+ ) &
+ proc=$!
+ "$@"
+ stat=$?
+ wait "$proc"
+ if test "$stat" != 0; then exit $stat; fi
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+dashXmstdout)
+ # This case only exists to satisfy depend.m4. It is never actually
+ # run, as this mode is specially recognized in the preamble.
+ exit 1
+ ;;
+
+makedepend)
+ # X makedepend
+ (
+ shift
+ cleared=no
+ for arg in "$@"; do
+ case $cleared in no)
+ set ""; shift
+ cleared=yes
+ esac
+ case "$arg" in
+ -D*|-I*)
+ set fnord "$@" "$arg"; shift;;
+ -*)
+ ;;
+ *)
+ set fnord "$@" "$arg"; shift;;
+ esac
+ done
+ obj_suffix="`echo $object | sed 's/^.*\././'`"
+ touch "$tmpdepfile"
+ ${MAKEDEPEND-makedepend} 2>/dev/null -o"$obj_suffix" -f"$tmpdepfile" "$@"
+ ) &
+ proc=$!
+ "$@"
+ stat=$?
+ wait "$proc"
+ if test "$stat" != 0; then exit $stat; fi
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ tail +3 "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile" "$tmpdepfile".bak
+ ;;
+
+cpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the proprocessed file to stdout, regardless of -o,
+ # because we must use -o when running libtool.
+ ( IFS=" "
+ case " $* " in
+ *" --mode=compile "*)
+ for arg
+ do # cycle over the arguments
+ case $arg in
+ "--mode=compile")
+ # insert --quiet before "--mode=compile"
+ set fnord "$@" --quiet
+ shift # fnord
+ ;;
+ esac
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # "$arg"
+ done
+ ;;
+ esac
+ "$@" -E |
+ sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+ sed '$ s: \\$::' > "$tmpdepfile"
+ ) &
+ proc=$!
+ "$@"
+ stat=$?
+ wait "$proc"
+ if test "$stat" != 0; then exit $stat; fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ cat < "$tmpdepfile" >> "$depfile"
+ sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvisualcpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the proprocessed file to stdout, regardless of -o,
+ # because we must use -o when running libtool.
+ ( IFS=" "
+ case " $* " in
+ *" --mode=compile "*)
+ for arg
+ do # cycle over the arguments
+ case $arg in
+ "--mode=compile")
+ # insert --quiet before "--mode=compile"
+ set fnord "$@" --quiet
+ shift # fnord
+ ;;
+ esac
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # "$arg"
+ done
+ ;;
+ esac
+ "$@" -E |
+ sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
+ ) &
+ proc=$!
+ "$@"
+ stat=$?
+ wait "$proc"
+ if test "$stat" != 0; then exit $stat; fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
+ echo " " >> "$depfile"
+ . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+none)
+ exec "$@"
+ ;;
+
+*)
+ echo "Unknown depmode $depmode" 1>&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/config/install-sh b/config/install-sh
new file mode 100755
index 00000000..e9de2384
--- /dev/null
+++ b/config/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/config/missing b/config/missing
new file mode 100755
index 00000000..0a7fb5a2
--- /dev/null
+++ b/config/missing
@@ -0,0 +1,283 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard , 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+fi
+
+run=:
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+ configure_ac=configure.ac
+else
+ configure_ac=configure.in
+fi
+
+case "$1" in
+--run)
+ # Try to run requested program, and just exit if it succeeds.
+ run=
+ shift
+ "$@" && exit 0
+ ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case "$1" in
+
+ -h|--h|--he|--hel|--help)
+ echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+ --run try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+ aclocal touch file \`aclocal.m4'
+ autoconf touch file \`configure'
+ autoheader touch file \`config.h.in'
+ automake touch all \`Makefile.in' files
+ bison create \`y.tab.[ch]', if possible, from existing .[ch]
+ flex create \`lex.yy.c', if possible, from existing .c
+ help2man touch the output file
+ lex create \`lex.yy.c', if possible, from existing .c
+ makeinfo touch the output file
+ tar try tar, gnutar, gtar, then tar without non-portable flags
+ yacc create \`y.tab.[ch]', if possible, from existing .[ch]"
+ ;;
+
+ -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+ echo "missing 0.3 - GNU automake"
+ ;;
+
+ -*)
+ echo 1>&2 "$0: Unknown \`$1' option"
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+ ;;
+
+ aclocal)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified \`acinclude.m4' or \`${configure_ac}'. You might want
+ to install the \`Automake' and \`Perl' packages. Grab them from
+ any GNU archive site."
+ touch aclocal.m4
+ ;;
+
+ autoconf)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified \`${configure_ac}'. You might want to install the
+ \`Autoconf' and \`GNU m4' packages. Grab them from any GNU
+ archive site."
+ touch configure
+ ;;
+
+ autoheader)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified \`acconfig.h' or \`${configure_ac}'. You might want
+ to install the \`Autoconf' and \`GNU m4' packages. Grab them
+ from any GNU archive site."
+ files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+ test -z "$files" && files="config.h"
+ touch_files=
+ for f in $files; do
+ case "$f" in
+ *:*) touch_files="$touch_files "`echo "$f" |
+ sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+ *) touch_files="$touch_files $f.in";;
+ esac
+ done
+ touch $touch_files
+ ;;
+
+ automake)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+ You might want to install the \`Automake' and \`Perl' packages.
+ Grab them from any GNU archive site."
+ find . -type f -name Makefile.am -print |
+ sed 's/\.am$/.in/' |
+ while read f; do touch "$f"; done
+ ;;
+
+ bison|yacc)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified a \`.y' file. You may need the \`Bison' package
+ in order for those modifications to take effect. You can get
+ \`Bison' from any GNU archive site."
+ rm -f y.tab.c y.tab.h
+ if [ $# -ne 1 ]; then
+ eval LASTARG="\${$#}"
+ case "$LASTARG" in
+ *.y)
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" y.tab.c
+ fi
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" y.tab.h
+ fi
+ ;;
+ esac
+ fi
+ if [ ! -f y.tab.h ]; then
+ echo >y.tab.h
+ fi
+ if [ ! -f y.tab.c ]; then
+ echo 'main() { return 0; }' >y.tab.c
+ fi
+ ;;
+
+ lex|flex)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified a \`.l' file. You may need the \`Flex' package
+ in order for those modifications to take effect. You can get
+ \`Flex' from any GNU archive site."
+ rm -f lex.yy.c
+ if [ $# -ne 1 ]; then
+ eval LASTARG="\${$#}"
+ case "$LASTARG" in
+ *.l)
+ SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" lex.yy.c
+ fi
+ ;;
+ esac
+ fi
+ if [ ! -f lex.yy.c ]; then
+ echo 'main() { return 0; }' >lex.yy.c
+ fi
+ ;;
+
+ help2man)
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified a dependency of a manual page. You may need the
+ \`Help2man' package in order for those modifications to take
+ effect. You can get \`Help2man' from any GNU archive site."
+
+ file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+ if test -z "$file"; then
+ file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
+ fi
+ if [ -f "$file" ]; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo ".ab help2man is required to generate this page"
+ exit 1
+ fi
+ ;;
+
+ makeinfo)
+ if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then
+ # We have makeinfo, but it failed.
+ exit 1
+ fi
+
+ echo 1>&2 "\
+WARNING: \`$1' is missing on your system. You should only need it if
+ you modified a \`.texi' or \`.texinfo' file, or any other file
+ indirectly affecting the aspect of the manual. The spurious
+ call might also be the consequence of using a buggy \`make' (AIX,
+ DU, IRIX). You might want to install the \`Texinfo' package or
+ the \`GNU make' package. Grab either from any GNU archive site."
+ file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+ if test -z "$file"; then
+ file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+ file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
+ fi
+ touch $file
+ ;;
+
+ tar)
+ shift
+ if test -n "$run"; then
+ echo 1>&2 "ERROR: \`tar' requires --run"
+ exit 1
+ fi
+
+ # We have already tried tar in the generic part.
+ # Look for gnutar/gtar before invocation to avoid ugly error
+ # messages.
+ if (gnutar --version > /dev/null 2>&1); then
+ gnutar ${1+"$@"} && exit 0
+ fi
+ if (gtar --version > /dev/null 2>&1); then
+ gtar ${1+"$@"} && exit 0
+ fi
+ firstarg="$1"
+ if shift; then
+ case "$firstarg" in
+ *o*)
+ firstarg=`echo "$firstarg" | sed s/o//`
+ tar "$firstarg" ${1+"$@"} && exit 0
+ ;;
+ esac
+ case "$firstarg" in
+ *h*)
+ firstarg=`echo "$firstarg" | sed s/h//`
+ tar "$firstarg" ${1+"$@"} && exit 0
+ ;;
+ esac
+ fi
+
+ echo 1>&2 "\
+WARNING: I can't seem to be able to run \`tar' with the given arguments.
+ You may want to install GNU tar or Free paxutils, or check the
+ command line arguments."
+ exit 1
+ ;;
+
+ *)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, and you do not seem to have it handy on your
+ system. You might have modified some files without having the
+ proper tools for further handling them. Check the \`README' file,
+ it often tells you about the needed prerequirements for installing
+ this package. You may also peek at any GNU archive site, in case
+ some other package would contain this missing \`$1' program."
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/config/mkinstalldirs b/config/mkinstalldirs
new file mode 100755
index 00000000..6b3b5fc5
--- /dev/null
+++ b/config/mkinstalldirs
@@ -0,0 +1,40 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman
+# Created: 1993-05-16
+# Public domain
+
+# $Id$
+
+errstatus=0
+
+for file
+do
+ set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+ shift
+
+ pathcomp=
+ for d
+ do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp"
+
+ mkdir "$pathcomp" || lasterr=$?
+
+ if test ! -d "$pathcomp"; then
+ errstatus=$lasterr
+ fi
+ fi
+
+ pathcomp="$pathcomp/"
+ done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/configure.in b/configure.in
new file mode 100644
index 00000000..14367546
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,289 @@
+dnl synergy -- mouse and keyboard sharing utility
+dnl Copyright (C) 2002 Chris Schoeneman
+dnl
+dnl This package is free software; you can redistribute it and/or
+dnl modify it under the terms of the GNU General Public License
+dnl found in the file COPYING that should have accompanied this file.
+dnl
+dnl This package is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+
+dnl Process this file with autoconf to produce a configure script.
+
+dnl initialize
+AC_INIT(lib/common/common.h)
+AC_CONFIG_AUX_DIR(config)
+
+dnl current version, extracted from $srcdir/lib/common/Version.h
+MAJOR_VERSION=`grep '#.*define VERSION "' $srcdir/lib/common/Version.h | sed -e 's/.*"\([0-9]*\)\.[0-9]*\.[0-9]*".*/\1/'`
+MINOR_VERSION=`grep '#.*define VERSION "' $srcdir/lib/common/Version.h | sed -e 's/.*"[0-9]*\.\([0-9]*\)\.[0-9]*".*/\1/'`
+RELEASE_VERSION=`grep '#.*define VERSION "' $srcdir/lib/common/Version.h | sed -e 's/.*"[0-9]*\.[0-9]*\.\([0-9]*\)".*/\1/'`
+
+dnl initialize automake
+AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION)
+AM_CONFIG_HEADER(config.h)
+
+dnl information on the package
+
+dnl decide on platform
+ARCH_LIBS=""
+ARCH_CFLAGS=""
+AC_CANONICAL_HOST
+case $host in
+*-*-mingw32* | *-*-windows*)
+ acx_host_arch="WIN32"
+ acx_host_winapi="MSWINDOWS"
+ ;;
+*-*-darwin*)
+ acx_host_arch="UNIX"
+ acx_host_winapi="CARBON"
+ ;;
+*)
+ acx_host_arch="UNIX"
+ acx_host_winapi="XWINDOWS"
+ ;;
+esac
+ARCH_CFLAGS="$ARCH_CFLAGS -DSYSAPI_$acx_host_arch=1 -DWINAPI_$acx_host_winapi=1"
+AM_CONDITIONAL(WIN32, test x$acx_host_arch = xWIN32)
+AM_CONDITIONAL(UNIX, test x$acx_host_arch = xUNIX)
+AM_CONDITIONAL(MSWINDOWS, test x$acx_host_winapi = xMSWINDOWS)
+AM_CONDITIONAL(CARBON, test x$acx_host_winapi = xCARBON)
+AM_CONDITIONAL(XWINDOWS, test x$acx_host_winapi = xXWINDOWS)
+
+dnl checks for programs
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_RANLIB
+AC_CHECK_PROG(HAVE_DOT, dot, YES, NO)
+
+dnl AC_PROG_OBJC doesn't exist. Borrow some ideas from KDE.
+dnl AC_MSG_CHECKING(for an Objective-C compiler)
+OBJC="${CC}"
+OBJCFLAGS="${CFLAGS}"
+AC_SUBST(OBJC)
+AC_SUBST(OBJCFLAGS)
+_AM_DEPENDENCIES(OBJC)
+
+dnl do checks using C++
+AC_LANG_CPLUSPLUS
+
+dnl our files end in .cpp not .C so tests should also end in .cpp
+ac_ext=cpp
+
+dnl enable debugging or disable asserts
+AC_ARG_ENABLE([debug], [ --enable-debug enable debugging])
+if test "x$enable_debug" != xno; then
+ CXXFLAGS="$CXXFLAGS -g"
+else
+ CXXFLAGS="$CXXFLAGS -DNDEBUG"
+fi
+
+dnl check compiler
+ACX_CHECK_CXX
+
+dnl checks for libraries
+if test x"$acx_host_arch" = xUNIX; then
+ ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy))
+ ARCH_LIBS="$PTHREAD_LIBS $ARCH_LIBS"
+ ARCH_CFLAGS="$ARCH_CFLAGS $PTHREAD_CFLAGS"
+fi
+if test x"$acx_host_winapi" = xCARBON; then
+ ARCH_LIBS="-framework Carbon $ARCH_LIBS"
+fi
+ACX_CHECK_NANOSLEEP
+ACX_CHECK_INET_ATON
+
+dnl checks for header files
+AC_HEADER_STDC
+AC_CHECK_HEADERS([unistd.h sys/time.h sys/types.h locale.h wchar.h])
+AC_CHECK_HEADERS([sys/socket.h sys/select.h])
+AC_CHECK_HEADERS([sys/utsname.h])
+AC_CHECK_HEADERS([istream ostream sstream])
+AC_HEADER_TIME
+if test x"$acx_host_winapi" = xXWINDOWS; then
+ AC_PATH_X
+ AC_PATH_XTRA
+ save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$X_CFLAGS $CPPFLAGS"
+ XEXT_LDADD=
+
+ AC_CHECK_LIB(Xtst,
+ XTestQueryExtension,
+ [XEXT_LDADD="$XEXT_LDADD -lXtst"],
+ AC_MSG_ERROR(You must have the XTest library to build synergy),
+ [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS])
+ AC_CHECK_HEADERS([X11/extensions/XTest.h],
+ ,
+ AC_MSG_ERROR(You must have the XTest headers to compile synergy))
+
+ acx_have_xkb=no
+ AC_CHECK_LIB(X11,
+ XkbQueryExtension,
+ [acx_have_xkb=yes],
+ [acx_have_xkb=no],
+ [$X_LIBS $X_EXTRA_LIBS])
+ if test x"$acx_have_xkb" = xyes; then
+ AC_CHECK_HEADERS([X11/XKBlib.h X11/extensions/XKBstr.h],
+ [acx_have_xkb=yes],
+ [acx_have_xkb=no],
+ [#include ])
+ if test x"$acx_have_xkb" = xyes; then
+ AC_TRY_COMPILE([
+ #include
+ #include
+ ],[
+ XkbQueryExtension(0, 0, 0, 0, 0, 0);
+ ],
+ [acx_have_xkb=yes],
+ [acx_have_xkb=no])
+ fi
+ fi
+ if test x"$acx_have_xkb" = xyes; then
+ AC_DEFINE(HAVE_XKB_EXTENSION, 1,
+ [Define this if the XKB extension is available.])
+ fi
+
+ acx_have_xinerama=yes
+ AC_CHECK_LIB(Xinerama,
+ XineramaQueryExtension,
+ [acx_have_xinerama=yes],
+ [acx_have_xinerama=no],
+ [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS])
+ if test x"$acx_have_xinerama" = xyes; then
+ AC_CHECK_HEADERS([X11/extensions/Xinerama.h],
+ [acx_have_xinerama=yes],
+ [acx_have_xinerama=no],
+ [#include ])
+ fi
+ if test x"$acx_have_xinerama" = xyes; then
+ XEXT_LDADD="$XEXT_LDADD -lXinerama"
+ fi
+
+ X_DPMS_LDADD=
+ acx_have_dpms=no
+ AC_CHECK_LIB(Xext,
+ DPMSQueryExtension,
+ [acx_have_dpms=yes],
+ [acx_have_dpms=no],
+ [$X_LIBS -lX11 $X_EXTRA_LIBS])
+ if test x"$acx_have_dpms" != xyes; then
+ AC_CHECK_LIB(Xdpms,
+ DPMSQueryExtension,
+ [acx_have_dpms=yes; XDPMS_LDADD=-lXdpms],
+ [acx_have_dpms=no],
+ [$X_LIBS -lX11 $X_EXTRA_LIBS])
+ fi
+ if test x"$acx_have_dpms" = xyes; then
+ AC_CHECK_HEADERS([X11/extensions/dpms.h],
+ [acx_have_dpms_h=yes],
+ [acx_have_dpms_h=no],
+ [#include ])
+ if test x"$acx_have_dpms_h" = xyes; then
+ XEXT_LDADD="$XEXT_LDADD $XDPMS_LDADD"
+ AC_MSG_CHECKING(for prototypes in X11/extensions/dpms.h)
+ acx_have_dpms_protos=no
+ AC_TRY_COMPILE([
+ #include
+ extern "C" {
+ #include
+ }
+ ],[
+ int s = DPMSModeOn;
+ DPMSQueryExtension(0, 0, 0);
+ ],
+ [acx_have_dpms_protos=yes])
+ AC_MSG_RESULT($acx_have_dpms_protos)
+ if test x"$acx_have_dpms_protos" = xyes; then
+ AC_DEFINE(HAVE_DPMS_PROTOTYPES,1,[Define if the header file declares function prototypes.])
+ fi
+ fi
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ ARCH_LIBS="$X_LIBS $X_PRE_LIBS $XEXT_LDADD -lXext -lX11 $X_EXTRA_LIBS $ARCH_LIBS"
+ ARCH_CFLAGS="$ARCH_CFLAGS $X_CFLAGS"
+fi
+
+dnl checks for types
+AC_TYPE_SIZE_T
+ACX_CHECK_SOCKLEN_T
+
+dnl checks for structures
+AC_STRUCT_TM
+
+dnl checks for compiler characteristics
+AC_CHECK_SIZEOF(char, 1)
+AC_CHECK_SIZEOF(short, 2)
+AC_CHECK_SIZEOF(int, 2)
+AC_CHECK_SIZEOF(long, 4)
+ACX_CHECK_CXX_BOOL(,AC_MSG_ERROR(Your compiler must support bool to compile synergy))
+ACX_CHECK_CXX_EXCEPTIONS(,AC_MSG_ERROR(Your compiler must support exceptions to compile synergy))
+ACX_CHECK_CXX_CASTS(,AC_MSG_ERROR(Your compiler must support C++ casts to compile synergy))
+ACX_CHECK_CXX_MUTABLE(,AC_MSG_ERROR(Your compiler must support mutable to compile synergy))
+ACX_CHECK_CXX_STDLIB(,AC_MSG_ERROR(Your compiler must support the C++ standard library to compile synergy))
+
+dnl checks for library functions
+dnl AC_TYPE_SIGNAL
+AC_FUNC_MEMCMP
+AC_FUNC_STRFTIME
+AC_CHECK_FUNCS(gmtime_r)
+ACX_CHECK_GETPWUID_R
+AC_CHECK_FUNCS(vsnprintf)
+AC_FUNC_SELECT_ARGTYPES
+ACX_CHECK_POLL
+ACX_FUNC_ACCEPT
+dnl use AC_REPLACE_FUNCS() for stuff in string.h
+
+dnl checks for system services
+
+dnl enable maximum compiler warnings and warnings are errors.
+ACX_CXX_WARNINGS
+ACX_CXX_WARNINGS_ARE_ERRORS
+
+dnl adjust compiler and linker variables
+CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $ARCH_CFLAGS"
+OBJCXXFLAGS="$OBJCXXFLAGS $CXXFLAGS $ARCH_CFLAGS"
+LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $ARCH_LIBS $LIBS"
+
+dnl we need to have an environment variable set when building on OS X.
+dnl i'm not sure of the right way to do that. writing 'export ...' to
+dnl the makefiles isn't portable. here we'll hijack XXXDEPMODE (where
+dnl XXX depends on the language) to insert setting the environment
+dnl variable when running the compiler. we'd like to put that in CC,
+dnl CXX and OBJC but that breaks depcomp. let's hope this works.
+if test x"$acx_host_winapi" = xCARBON; then
+ MACOSX_DEPLOYMENT_TARGET="10.2"
+ CCDEPMODE="MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET $CCDEPMODE"
+ CXXDEPMODE="MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET $CXXDEPMODE"
+ OBJCDEPMODE="MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET $OBJCDEPMODE"
+else
+ MACOSX_DEPLOYMENT_TARGET="5"
+ CXXDEPMODE="FOO=$MACOSX_DEPLOYMENT_TARGET $CXXDEPMODE"
+fi
+
+AC_OUTPUT([
+Makefile
+cmd/Makefile
+cmd/launcher/Makefile
+cmd/synergyc/Makefile
+cmd/synergys/Makefile
+dist/Makefile
+dist/nullsoft/Makefile
+dist/rpm/Makefile
+dist/rpm/synergy.spec
+doc/Makefile
+doc/doxygen.cfg
+lib/Makefile
+lib/arch/Makefile
+lib/base/Makefile
+lib/client/Makefile
+lib/common/Makefile
+lib/io/Makefile
+lib/mt/Makefile
+lib/net/Makefile
+lib/platform/Makefile
+lib/server/Makefile
+lib/synergy/Makefile
+])
diff --git a/dist/Makefile.am b/dist/Makefile.am
new file mode 100644
index 00000000..1af99c18
--- /dev/null
+++ b/dist/Makefile.am
@@ -0,0 +1,26 @@
+# synergy -- mouse and keyboard sharing utility
+# 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+## Process this file with automake to produce Makefile.in
+NULL =
+
+SUBDIRS = \
+ rpm \
+ nullsoft \
+ $(NULL)
+
+EXTRA_DIST = \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ $(NULL)
diff --git a/dist/nullsoft/Makefile.am b/dist/nullsoft/Makefile.am
new file mode 100644
index 00000000..120cd016
--- /dev/null
+++ b/dist/nullsoft/Makefile.am
@@ -0,0 +1,24 @@
+# synergy -- mouse and keyboard sharing utility
+# 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+## Process this file with automake to produce Makefile.in
+NULL =
+
+EXTRA_DIST = \
+ Makefile.win \
+ synergy.nsi \
+ dosify.c \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ $(NULL)
diff --git a/dist/nullsoft/Makefile.win b/dist/nullsoft/Makefile.win
new file mode 100644
index 00000000..91aa68bb
--- /dev/null
+++ b/dist/nullsoft/Makefile.win
@@ -0,0 +1,63 @@
+# synergy -- mouse and keyboard sharing utility
+# Copyright (C) 2007 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+NSIS = "$(PROGRAMFILES)\NSIS\makensis.exe"
+NSIS_FLAGS = /NOCD /V1
+
+BIN_INSTALLER_SRC = dist\nullsoft
+BIN_INSTALLER_DST = $(BUILD_DST)\$(BIN_INSTALLER_SRC)
+BIN_DOSIFY_EXE = "$(BIN_INSTALLER_DST)\dosify.exe"
+BIN_DOSIFY_C = \
+ "$(BIN_INSTALLER_SRC)\dosify.c" \
+ $(NULL)
+BIN_DOSIFY_OBJ = \
+ "$(BIN_INSTALLER_DST)\dosify.obj" \
+ $(NULL)
+
+BIN_INSTALLER_NSI = "$(BIN_INSTALLER_SRC)\synergy.nsi"
+BIN_INSTALLER_EXE = "$(BUILD_DST)\SynergyInstaller.exe"
+BIN_INSTALLER_DOCS = \
+ COPYING \
+ ChangeLog \
+ $(NULL)
+BIN_INSTALLER_DOS_DOCS = \
+ $(BUILD_DST)\COPYING.txt \
+ $(BUILD_DST)\ChangeLog.txt \
+ $(NULL)
+
+C_FILES = $(C_FILES) $(BIN_DOSIFY_C)
+OBJ_FILES = $(OBJ_FILES) $(BIN_DOSIFY_OBJ)
+OPTPROGRAMS = $(OPTPROGRAMS) $(BIN_DOSIFY_EXE)
+
+# Build rules.
+$(BIN_DOSIFY_OBJ): $(BIN_DOSIFY_C)
+ @$(ECHO) Compile $(BIN_DOSIFY_C)
+ -@$(MKDIR) $(BIN_INSTALLER_DST) 2>NUL:
+ $(cc) $(cdebug) $(cflags) $(cvars) /Fo$@ /Fd$(@:.obj=.pdb) $**
+$(BIN_DOSIFY_EXE): $(BIN_DOSIFY_OBJ)
+ @$(ECHO) Link $(@F)
+ $(link) $(ldebug) $(conlflags) $(conlibsmt) /out:$@ $**
+
+# Convert text files from Unix to DOS format.
+$(BIN_INSTALLER_DOS_DOCS): $(BIN_DOSIFY_EXE) $(BIN_INSTALLER_DOCS)
+ @$(ECHO) Convert text files to DOS format
+ $(BIN_DOSIFY_EXE) "." "$(BUILD_DST)" $(BIN_INSTALLER_DOCS)
+
+# Allow installers for both debug and release.
+$(BIN_INSTALLER_EXE): $(BIN_INSTALLER_NSI) all $(BIN_INSTALLER_DOS_DOCS)
+ @$(ECHO) Build $(@F)
+ $(NSIS) $(NSIS_FLAGS) /DOUTPUTDIR=$(@D) /DOUTPUTFILE=$@ $(BIN_INSTALLER_NSI)
+installer: $(BIN_INSTALLER_EXE)
+debug-installer:
+ @$(MAKE) /nologo /f $(MAKEFILE) DEBUG=1 installer
+release-installer:
+ @$(MAKE) /nologo /f $(MAKEFILE) NODEBUG=1 installer
diff --git a/dist/nullsoft/dosify.c b/dist/nullsoft/dosify.c
new file mode 100644
index 00000000..95d0caee
--- /dev/null
+++ b/dist/nullsoft/dosify.c
@@ -0,0 +1,99 @@
+#include
+#include
+#include
+
+static
+char*
+concatPath(const char* dir, const char* name, const char* ext)
+{
+ size_t nDir = (dir != NULL) ? strlen(dir) : 0;
+ size_t nPath = nDir + 1 + strlen(name) + strlen(ext?ext:"") + 1;
+ char* path = malloc(nPath);
+
+ /* directory */
+ if (nDir > 0 && strcmp(dir, ".") != 0) {
+ strcpy(path, dir);
+ if (path[nDir - 1] != '\\' && path[nDir - 1] != '/') {
+ strcat(path, "\\");
+ }
+ }
+ else {
+ strcpy(path, "");
+ }
+
+
+ /* name */
+ strcat(path, name);
+
+ /* extension */
+ if (ext != NULL && strrchr(name, '.') == NULL) {
+ strcat(path, ext);
+ }
+
+ return path;
+}
+
+static
+int
+dosify(const char* srcdir, const char* dstdir, const char* name)
+{
+ FILE* dFile, *sFile;
+ char* dName, *sName;
+
+ sName = concatPath(srcdir, name, NULL);
+ dName = concatPath(dstdir, name, ".txt");
+
+ sFile = fopen(sName, "rb");
+ if (sFile == NULL) {
+ fprintf(stderr, "Can't open \"%s\" for reading\n", sName);
+ return 0;
+ }
+ else {
+ dFile = fopen(dName, "w");
+ if (dFile == NULL) {
+ fclose(sFile);
+ fprintf(stderr, "Can't open \"%s\" for writing\n", dName);
+ return 0;
+ }
+ else {
+ char buffer[1024];
+ while (!ferror(dFile) &&
+ fgets(buffer, sizeof(buffer), sFile) != NULL) {
+ fprintf(dFile, "%s", buffer);
+ }
+ if (ferror(sFile) || ferror(dFile)) {
+ fprintf(stderr,
+ "Error copying \"%s\" to \"%s\"\n", sName, dName);
+ fclose(dFile);
+ fclose(sFile);
+ _unlink(dName);
+ return 0;
+ }
+ }
+ }
+
+ fclose(dFile);
+ fclose(sFile);
+ free(dName);
+ free(sName);
+ return 1;
+}
+
+#include
+int
+main(int argc, char** argv)
+{
+ int i;
+
+ if (argc < 3) {
+ fprintf(stderr, "usage: %s [files]\n", argv[0]);
+ return 1;
+ }
+
+ for (i = 3; i < argc; ++i) {
+ if (!dosify(argv[1], argv[2], argv[i]))
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/dist/nullsoft/synergy.nsi b/dist/nullsoft/synergy.nsi
new file mode 100644
index 00000000..3370d03a
--- /dev/null
+++ b/dist/nullsoft/synergy.nsi
@@ -0,0 +1,179 @@
+; Synergy.nsi
+;
+; This script is based on example1.nsi, but it remember the directory,
+; has uninstall support and (optionally) installs start menu shortcuts.
+;
+; It will install makensisw.exe into a directory that the user selects,
+
+;--------------------------------
+
+!ifndef OUTPUTDIR
+!define OUTPUTDIR "build\Release"
+!endif
+
+; The name of the installer
+Name "Synergy"
+
+; The file to write
+OutFile "${OUTPUTFILE}"
+
+; The default installation directory
+InstallDir $PROGRAMFILES\Synergy
+
+; Registry key to check for directory (so if you install again, it will
+; overwrite the old one automatically)
+InstallDirRegKey HKLM "Software\Synergy" "Install_Dir"
+
+;--------------------------------
+
+; Pages
+
+Page components
+Page license
+Page directory
+Page instfiles
+
+UninstPage uninstConfirm
+UninstPage instfiles
+
+;--------------------------------
+
+; Text
+ComponentText "This will install Synergy on your computer. Select the optional components you want to install."
+DirText "Choose a directory to install Synergy to:"
+UninstallText "This will uninstall Synergy from your computer."
+LicenseText "Synergy is distributed under the GNU GPL:"
+LicenseData ${OUTPUTDIR}\COPYING.txt
+
+;--------------------------------
+
+; The stuff to install
+Section "Synergy (required)"
+
+ SectionIn RO
+
+ ; Set output path to the installation directory.
+ SetOutPath $INSTDIR
+
+ ; Put files there
+ File "${OUTPUTDIR}\synergy.exe"
+ File "${OUTPUTDIR}\synergyc.exe"
+ File "${OUTPUTDIR}\synergys.exe"
+ File "${OUTPUTDIR}\*.dll"
+ File "${OUTPUTDIR}\COPYING.txt"
+ File "${OUTPUTDIR}\ChangeLog.txt"
+ File doc\PORTING
+ File doc\about.html
+ File doc\authors.html
+ File doc\autostart.html
+ File doc\banner.html
+ File doc\compiling.html
+ File doc\configuration.html
+ File doc\contact.html
+ File doc\developer.html
+ File doc\faq.html
+ File doc\history.html
+ File doc\home.html
+ File doc\index.html
+ File doc\license.html
+ File doc\news.html
+ File doc\roadmap.html
+ File doc\running.html
+ File doc\security.html
+ File doc\synergy.css
+ File doc\tips.html
+ File doc\toc.html
+ File doc\trouble.html
+
+ SetOutPath $INSTDIR\images
+ File doc\images\logo.gif
+ File doc\images\warp.gif
+
+ ; Write the installation path into the registry
+ WriteRegStr HKLM SOFTWARE\Synergy "Install_Dir" "$INSTDIR"
+
+ ; Write the uninstall keys for Windows
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Synergy" "DisplayName" "Synergy"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Synergy" "UninstallString" '"$INSTDIR\uninstall.exe"'
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Synergy" "NoModify" 1
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Synergy" "NoRepair" 1
+ WriteUninstaller "uninstall.exe"
+
+SectionEnd
+
+; Optional section (can be disabled by the user)
+Section "Start Menu Shortcuts"
+
+ CreateDirectory "$SMPROGRAMS\Synergy"
+ CreateShortCut "$SMPROGRAMS\Synergy\Synergy.lnk" "$INSTDIR\synergy.exe" "" "$INSTDIR\synergy.exe" 0
+ CreateShortCut "$SMPROGRAMS\Synergy\README.lnk" "$INSTDIR\index.html"
+ CreateShortCut "$SMPROGRAMS\Synergy\Synergy Folder.lnk" "$INSTDIR"
+ CreateShortCut "$SMPROGRAMS\Synergy\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
+
+SectionEnd
+
+; Optional section (can be disabled by the user)
+Section "Desktop Icon"
+
+ CreateShortCut "$DESKTOP\Synergy.lnk" "$INSTDIR\synergy.exe" "" "$INSTDIR\synergy.exe" 0
+
+SectionEnd
+
+;--------------------------------
+
+; Uninstaller
+
+Section "Uninstall"
+ ; Stop and uninstall the daemons
+ ExecWait '"$INSTDIR\synergy.exe" /uninstall'
+
+ ; Remove autorun registry keys for synergy
+ DeleteRegKey HKLM "SYSTEM\CurrentControlSet\Services\Synergy Server"
+ DeleteRegKey HKLM "SYSTEM\CurrentControlSet\Services\Synergy Client"
+ DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\RunServices" "Synergy Server"
+ DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\RunServices" "Synergy Client"
+ DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "Synergy Server"
+ DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "Synergy Client"
+
+ ; not all keys will have existed, so errors WILL have happened
+ ClearErrors
+
+ ; Remove registry keys
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Synergy"
+ DeleteRegKey HKLM SOFTWARE\Synergy
+
+ ClearErrors
+
+ ; First try to remove files that might be locked (if synergy is running)
+ Delete /REBOOTOK $INSTDIR\synergy.exe
+ Delete /REBOOTOK $INSTDIR\synergyc.exe
+ Delete /REBOOTOK $INSTDIR\synergys.exe
+ Delete /REBOOTOK $INSTDIR\synrgyhk.dll
+
+ ; Remove files and directory
+ Delete $INSTDIR\*.*
+ RMDir $INSTDIR
+
+ ; Remove shortcuts, if any
+ Delete "$SMPROGRAMS\Synergy\*.*"
+ Delete "$DESKTOP\Synergy.lnk"
+
+ ; Remove directories used
+ RMDir "$SMPROGRAMS\Synergy"
+ RMDir "$INSTDIR"
+
+ IfRebootFlag 0 EndOfAll
+ MessageBox MB_OKCANCEL "Uninstaller needs to reboot to finish cleaning up. reboot now?" IDCANCEL NoReboot
+ ClearErrors
+ Reboot
+ IfErrors 0 EndOfAll
+ MessageBox MB_OK "Uninstaller could not reboot. Please reboot manually. Thank you."
+ Abort "Uninstaller could not reboot. Please reboot manually. Thank you."
+ NoReboot:
+ DetailPrint ""
+ DetailPrint "Uninstaller could not reboot. Please reboot manually. Thank you."
+ DetailPrint ""
+ SetDetailsView show
+ EndOfAll:
+
+SectionEnd
diff --git a/dist/rpm/Makefile.am b/dist/rpm/Makefile.am
new file mode 100644
index 00000000..0e86d9ba
--- /dev/null
+++ b/dist/rpm/Makefile.am
@@ -0,0 +1,22 @@
+# synergy -- mouse and keyboard sharing utility
+# 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+## Process this file with automake to produce Makefile.in
+NULL =
+
+EXTRA_DIST = \
+ synergy.spec.in \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ $(NULL)
diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in
new file mode 100644
index 00000000..0d2b6f48
--- /dev/null
+++ b/dist/rpm/synergy.spec.in
@@ -0,0 +1,66 @@
+Summary: Mouse and keyboard sharing utility
+Name: @PACKAGE@
+Version: @VERSION@
+Release: 1
+License: GPL
+Packager: Chris Schoeneman
+Group: System Environment/Daemons
+Prefixes: /usr/bin
+Source: @PACKAGE@-@VERSION@.tar.gz
+Buildroot: /var/tmp/@PACKAGE@-@VERSION@-root
+
+%description
+Synergy lets you easily share a single mouse and keyboard between
+multiple computers with different operating systems, each with its
+own display, without special hardware. It's intended for users
+with multiple computers on their desk since each system uses its
+own display.
+
+%prep
+%setup
+CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=/usr
+
+%build
+make
+
+%install
+make install DESTDIR=$RPM_BUILD_ROOT
+strip $RPM_BUILD_ROOT/usr/bin/synergyc
+strip $RPM_BUILD_ROOT/usr/bin/synergys
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-, root, root)
+/usr/bin/synergyc
+/usr/bin/synergys
+%doc AUTHORS
+%doc COPYING
+%doc ChangeLog
+%doc INSTALL
+%doc NEWS
+%doc README
+%doc doc/about.html
+%doc doc/authors.html
+%doc doc/autostart.html
+%doc doc/banner.html
+%doc doc/border.html
+%doc doc/compiling.html
+%doc doc/configuration.html
+%doc doc/contact.html
+%doc doc/developer.html
+%doc doc/faq.html
+%doc doc/history.html
+%doc doc/home.html
+%doc doc/index.html
+%doc doc/license.html
+%doc doc/news.html
+%doc doc/roadmap.html
+%doc doc/running.html
+%doc doc/security.html
+%doc doc/tips.html
+%doc doc/toc.html
+%doc doc/trouble.html
+%doc doc/synergy.css
+%doc examples/synergy.conf
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 00000000..2efec24c
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,49 @@
+# synergy -- mouse and keyboard sharing utility
+# 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+## Process this file with automake to produce Makefile.in
+NULL =
+
+EXTRA_DIST = \
+ PORTING \
+ doxygen.cfg.in \
+ synergy.css \
+ about.html \
+ authors.html \
+ autostart.html \
+ banner.html \
+ border.html \
+ compiling.html \
+ configuration.html \
+ contact.html \
+ developer.html \
+ faq.html \
+ history.html \
+ home.html \
+ index.html \
+ license.html \
+ news.html \
+ roadmap.html \
+ running.html \
+ security.html \
+ tips.html \
+ toc.html \
+ trouble.html \
+ images/logo.gif \
+ images/warp.gif \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ doc/doxygen.cfg \
+ doc/doxygen/html/* \
+ $(NULL)
diff --git a/doc/PORTING b/doc/PORTING
new file mode 100644
index 00000000..4e2744df
--- /dev/null
+++ b/doc/PORTING
@@ -0,0 +1,419 @@
+Synergy Developer and Porting Guide
+===================================
+
+This document is under development.
+
+Code Organization
+-----------------
+
+The synergy source code organization is:
+
+. -- root makefiles, some standard documentation
+cmd -- program source code
+ launcher -- synergy launcher for Windows
+ synergyc -- synergy client
+ synergys -- synergy server
+config -- stuff for autoconf/automake
+dist -- files for creating distributions
+ nullsoft -- files for creating Nullsoft NSIS installer (Windows)
+ rpm -- files for creating RPMs
+doc -- placeholder for documentation
+examples -- example files
+lib -- library source code
+ arch -- platform dependent utility library
+ base -- simple utilities
+ client -- synergy client library
+ common -- commonly needed header files
+ io -- I/O
+ mt -- multithreading
+ net -- networking
+ platform -- platform dependent display/window/event stuff
+ server -- synergy server library
+ synergy -- synergy shared client/server code library
+
+Note how the utility code required by the programs is placed into
+separate library directories. This makes the makefiles a little
+more awkward but makes for a cleaner organization. The top level
+directory has only the standard documentation files and the files
+necessary to configure and build the rest of the project.
+
+
+Coding Style Guide
+------------------
+
+Synergy uses many coding conventions. Contributed code should
+following these guidelines.
+
+- Symbol Naming
+ Names always begin with a letter (never an underscore). The first
+ letter of interior names are always capitalized. Acronyms should
+ be all uppercase. For example: myTextAsASCII.
+
+ Names come it two flavors: leading capital and leading lowercase.
+ The former have the first character capitalized and the latter
+ don't. In the following table, leading capital names are indicated
+ by `Name' and leading lowercase names by `name'.
+
+ The naming convention for various things are:
+
+ * Exceptions -- X + Name XMyException
+ * Interfaces -- I + Name IMyInterface
+ * Template Classes -- T + Name TMyTemplate<>
+ * Other Classes -- C + Name CMyClass
+ * Enumerations -- E + Name EMyEnumeration
+ * Constants -- k + Name kMyConstant
+ * Data Members -- m_ + name m_myDataMember
+ * Methods -- name myMethod
+ * Functions -- name myFunction
+ * Variables -- name myVariable
+
+ Exceptions are types that get thrown and are generally derived
+ (possibly indirectly) from XBase. Interfaces are derived (possibly
+ indirectly) from IInterface and have only pure virtual functions.
+ Other classes are classes that aren't exceptions or interfaces.
+ Constants include global constants and enumerants.
+
+ Method names should usually have the form `verbObject'. For example:
+ * isGameOn()
+ * getBeer()
+ * pressPowerButton()
+ * setChannel()
+ In general, use `get' and `set' to read and write state but use `is'
+ to read boolean state. Note that classes that contain only `is',
+ `get', and `set' are probably plain old data; you might want to
+ consider using public data members only or, better, refactor your
+ design to have classes that actually do something more than just
+ hold data.
+
+- File Naming
+ Each class should have one source and one header file. If the
+ class is named `CMyClass' then the source file should be named
+ `CMyClass.cpp' and the header file `CMyClass.h'.
+
+ Headers files not containing a class should have some meaningful
+ name with a leading capital (e.g. `Version.h').
+
+ Source files without a header file have a leading lowercase name.
+ Only files containing the entry point for an application should
+ lack a header file.
+
+- Dependencies
+ * No circular library dependencies
+ Library dependencies form an acyclic graph. Conceptually
+ libraries can be arranged in layers where each library only
+ references libraries in layers below it, not in the same layer
+ or layers above it. The makefiles build the lowest layer
+ libraries first and work upwards.
+
+ * Avoid circular uses-a relationships
+ When possible, design classes with one-way uses-a relationships
+ and avoid cycles. This makes it easier to understand the code.
+ However, sometimes it's not always practical so it is permitted.
+
+ * Included files in headers
+ Headers should #include only the necessary headers. In
+ particular, if a class is referenced in a header file only as a
+ pointer or a reference then use `class COtherClass;' instead of
+ `#include "COtherClass.h".'
+
+ * #include syntax
+ Non-synergy header files must be included using angle brackets
+ while synergy header files must be included using double quotes.
+ #include "CSynergyHeader.h"
+ #include
+ The file name in a #include must not be a relative path unless
+ it's a system header file and it's customary to use a relative
+ path, e.g. `#include '. Use compiler options to
+ add necessary directories to the include search path.
+
+ * Included file ordering
+ Files should be included in the following order:
+ * Header for source file
+ The first include for CMyClass.cpp must be CMyClass.h.
+ * Other headers in directory, sorted alphabetically
+ * Headers for each library, sorted alphabetically per library
+ Include headers from the library closest in the dependency graph
+ first, then the next farthest, etc. Sort alphabetically within
+ each library.
+ * System headers
+
+- C++
+ * C++ features
+ Synergy uses the following more recent C++ features:
+ * bool
+ * templates
+ * exceptions
+ * mutable
+ * new scoping rules
+ * the standard C++ library
+
+ Do not use the following C++ features:
+ * dynamic_cast
+ * run time type information
+ * namespaces and using (use std:: where necessary)
+
+ The new scoping rules say that the scope of a variable declared
+ in a for statement is limited to the for loop. For example:
+
+ for (int i = 0; i < 10; ++i) {
+ // i is in scope here
+ }
+ // i is not in scope here
+
+ for (int i = -10; i < 0; ++i) {
+ // an entirely new i is in scope here
+ }
+ // i is not in scope here
+
+ This is used routinely in synergy, but only in for loops. There
+ is a macro for `for' in lib/base/common.h when building under
+ Microsoft Visual C++ that works around the fact that that compiler
+ doesn't follow the new scoping rules. Use the macro if your
+ compiler uses the old scoping rules.
+
+ * Standard C++ library
+ The standard C++ library containers should always be used in favor
+ of custom containers wherever reasonable. std::string is used
+ throughout synergy but only as the CString typedef; always use
+ CString, never std::string except in the arch library. Synergy
+ avoids using auto_ptr due to some portability problems. Synergy
+ makes limited use of standard algorithms and streams but they can
+ be freely used in new code.
+
+ * Limited multiple inheritance
+ Classes should inherit implementation from at most one superclass.
+ Inheriting implementation from multiple classes can have unpleasant
+ consequences in C++ due to it's limited capabilities. Classes can
+ inherit from any number of interface classes. An interface class
+ provides only pure virtual methods. Synergy breaks this rule in
+ IInterface which implements the virtual destructor for convenience.
+
+ * No globals
+ Avoid global variables. All global variables must be static, making
+ it visible only with its source file. Most uses of global variables
+ are better served by static data members of a class. Global
+ constants are permitted in some circumstances.
+
+ Also avoid global functions. Use public static member functions in
+ a class instead.
+
+ These rules are violated by the main source file for each program
+ (except that the globals are still static). They could easily be
+ rewritten to put all the variables and functions into a class but
+ there's little to be gained by that.
+
+ * Private data only
+ If a class is plain-old-data (i.e. it has no methods) all of its
+ data members should be public. Otherwise all of its data members
+ should be private, not public or protected. This makes it much
+ easier to track the use of a member when reading code. Protected
+ data is not allowed because `protected' is a synonym for `public
+ to my subclasses' and public data is a Bad Thing. While it might
+ seem okay in this limited situation, the situation is not at all
+ limited since an arbitrary number of classes can be derived,
+ directly or indirectly, from the class and any of those classes
+ have full access to the protected data.
+
+ * Plain old data
+ A class that merely contains data and doesn't perform operations
+ on that data (other than reads and writes) is plain old data (POD).
+ POD should have only public data members and non-copy constructors.
+ It must not have any methods other than constructors, not even a
+ destructor or assignment operators, nor protected or private data.
+ Note that this definition of POD is not the definition used in the
+ C++ standard, which limits the contained data types to types that
+ have no constructors, destructors, or methods.
+
+ * Avoid using friend
+ Avoid declaring friend functions or classes. They're sometimes
+ necessary for operator overloading. If you find it necessary to
+ add friends to some class C, consider creating a utility class U.
+ A utility class is declared as the only friend of C and provides
+ only static methods. Each method forwards to a private method on
+ an object of C type (passed as a parameter to the U's method).
+ This makes maintenance easier since only U has friend access to C
+ and finding any call to U is trivial (they're prefixed by U::).
+
+ * Don't test for NULL when using `delete' or `delete[]'
+ It's unnecessary since delete does it anyway.
+
+- Makefiles
+ Automake's makefiles (named Makefile.am) have a few requirements:
+ * Define the following macro at the top of the file:
+ NULL =
+ * Lists should have one item per line and end in $(NULL). For
+ example:
+ EXTRA_DIST = \
+ kiwi.txt \
+ mango.cpp \
+ papaya.h \
+ $(NULL)
+ Indentation must use tabs in a makefile. Line continuations
+ (backslashes) should be aligned using tabs.
+ * Lists of files should be sorted alphabetically in groups (e..g
+ source files, header files, then other files). Lists of
+ subdirectories must be in the desired build order.
+
+- Source Formatting
+ Every project has its own formatting style and no style satisfies
+ everyone. New code should be consistent with existing code:
+
+ * All files should include the copyright and license notice
+ * Use tabs to indent
+ * Tabs are 4 columns
+ * Lines should not extend past the 80th column
+ * Open braces ({) go on same line as introducing statement
+ `for (i = 0; i < 10; ++i) {' not
+ for (i = 0; i < 10; ++i)
+ {
+ * Close braces line up with introducing statement
+ * Open brace for function is on a line by itself in first column
+ * Close brace for function lines up with open brace
+ * Always use braces on: if, else, for, while, do, switch
+ * `else {' goes on its own line
+ * Always explicitly test pointers against NULL
+ e.g. `if (ptr == NULL)' not `if (ptr)'
+ * Always explicitly test integral values against 0
+ e.g. `if (i == 0)' not `if (i)'
+ * Put spaces around binary operators and after statements
+ e.g. `if (a == b) {' not `if(a==b){'
+ * C'tor initializers are one per line, indented one tab stop
+ * Other indentation should follow existing practice
+ * Use Qt style comments for extraction by doxygen (i.e. //! and /*!)
+ * Mark incomplete or buggy code with `FIXME'
+
+- Other
+ * calls to LOG() should always be all on one line (even past column 80)
+
+
+Class Relationships
+-------------------
+
+The doxygen documentation can help in understanding the relationships
+between objects. Use `make doxygen' in the top level directory to
+create the doxygen documentation into doc/doxygen/html. You must have
+doxygen installed, of course.
+
+FIXME -- high level overview of class relationships
+
+
+Portability
+-----------
+
+Synergy is mostly platform independent code but necessarily has
+platform dependent parts. The mundane platform dependent parts
+come from the usual suspects: networking, multithreading, file
+system, high resolution clocks, system logging, etc. Porting
+these parts is relatively straightforward.
+
+Synergy also has more esoteric platform dependent code. The
+functions for low-level event interception and insertion,
+warping the cursor position, character to keyboard event
+translation, clipboard manipulation, and screen saver control
+are often obscure and poorly documented. Unfortunately, these
+are exactly the functions synergy requires to do its magic.
+
+Porting synergy to a new platform requires the following steps:
+
+- Setting up the build
+- Adjusting lib/common/common.h
+- Implementing lib/arch
+- Implementing lib/platform
+- Tweaks
+
+Setting up the build:
+
+The first phase is simply to create the files necessary to build the
+other files. On Unix, synergy uses autoconf/automake which produces
+a `configure' script that generates makefiles. On Windows, synergy
+uses Visual C++ workspace and project files. If you're porting to
+another Unix variant, you may need to adjust `configure.in',
+`acinclude.m4', and Unix flavor dependent code in lib/arch. Note
+especially the SYSAPI_* and WINAPI_* macro definitions in
+ARCH_CFLAGS. Exactly one of each must be defined. It should also
+add AM_CONDITIONALs if a new SYSAPI_* or WINAPI_* was added.
+
+Adjusting lib/common/common.h:
+
+The lib/common/common.h header file is included directly or indirectly
+by every other file. Its primary job is to include config.h, which
+defines macros depending on what the 'configure' script discovered
+about the system. If the platform does not use the 'configure' script
+it must define the appropriate SYSAPI_* and WINAPI_* macro. It may
+also do other platform specific setup.
+
+Adjusting lib/common/BasicTypes.h:
+
+No changes should be necessary in BasicTypes.h. However, if the
+platform's system header files define SInt8, et al. you may need
+to adjust the typedefs to match the system's definitions.
+
+Implementing lib/arch:
+
+Much platform dependent code lives in lib/arch. There are several
+interface classes there and they must all be implemented for each
+platform. See the interface header files for more information.
+
+Platforms requiring special functions should create a class named
+CArchMiscXXX where XXX is the platform name. The class should have
+only static methods. Clients can include the appropriate header
+file and make calls directly, surrounded by a suitable #ifdef/#endif.
+
+If using automake, the Makefile.am should list the system specific
+files in a XXX_SOURCE_FILES macro where XXX matches the appropriate
+AM_CONDITIONAL symbol. XXX_SOURCE_FILES must be added to EXTRA_DIST
+and the following added above the INCLUDES macro:
+
+ if XXX
+ libarch_a_SOURCES = \
+ $(COMMON_SOURCE_FILES) \
+ $(XXX_SOURCE_FILES) \
+ $(NULL)
+ endif
+
+Implementing lib/platform:
+
+Most of the remaining platform dependent code lives in lib/platform.
+The code there implements platform dependent window, clipboard, keyboard
+and screen saver handling. If a platform is named XXX then the following
+classes should be derived and implemented:
+
+ * CXXXClipboard : IClipboard
+ Provides clipboard operations. Typically, this class will
+ have helper classes for converting between various clipboard
+ data formats.
+
+ * CXXXEventQueueBuffer : IEventQueueBuffer
+ Provides operations for waiting for, posting and retrieving events.
+ Also provides operations for creating and deleting timers.
+
+ * CXXXKeyState : CKeyState
+ Provides operations for synthesizing key events and for mapping a
+ key ID to a sequence of events to generate that key.
+
+ * CXXXScreen : IScreen, IPrimaryScreen, ISecondaryScreen, IPlatformScreen
+ Provides screen operations.
+
+ * CXXXScreenSaver : IScreenSaver
+ Provides screen saver operations.
+
+If using automake, the Makefile.am should list the window system
+specific files in a XXX_SOURCE_FILES macro where XXX matches the
+appropriate AM_CONDITIONAL symbol. XXX_SOURCE_FILES must be added
+to EXTRA_DIST and the following added above the INCLUDES macro:
+
+ if XXX
+ libplatform_a_SOURCES = $(XXX_SOURCE_FILES)
+ endif
+
+Tweaks:
+
+Finally, each platform typically requires various adjustments here
+and there. In particular, synergyc.cpp and synergys.cpp usually
+require platform dependent code for the main entry point, parsing
+arguments, and reporting errors. Also, some platforms may benefit
+from a graphical user interface front end. These are generally
+not portable and synergy doesn't provide any infrastructure for
+the code common to any platform, though it may do so someday.
+There is, however, an implementation of a GUI front end for Windows
+that serves as an example.
diff --git a/doc/about.html b/doc/about.html
new file mode 100644
index 00000000..aadd5764
--- /dev/null
+++ b/doc/about.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+ About Synergy
+
+
+
+With synergy, all the computers on your desktop form a single virtual
+screen. You use the mouse and keyboard of only one of the computers
+while you use all of the monitors on all of the computers.
+You tell synergy how many screens you have and their positions relative
+to one another. Synergy then detects when the mouse moves off
+the edge of a screen and jumps it instantly to the neighboring screen.
+The keyboard works normally on each screen; input goes to whichever
+screen has the cursor.
+
+In this example, the user is moving the mouse from left to right.
+When the cursor reaches the right edge of the left screen it jumps
+instantly to the left edge of the right screen.
+
+
+
+You can arrange screens side-by-side, above and below one another,
+or any combination. You can even have a screen jump to the opposite
+edge of itself. Synergy also understands multiple screens attached
+to the same computer.
+
+Running a game and don't want synergy to jump screens? No problem.
+Just toggle Scroll Lock. Synergy keeps the cursor on the same screen
+when Scroll Lock is on. (This can be configured to another hot key.)
+
+Do you wish you could cut and paste between computers? Now you can!
+Just copy text, HTML, or an image as you normally would on one screen
+then switch to another screen and paste it. It's as if all your
+computers shared a single clipboard (and separate primary selection for
+you X11 users). It even converts newlines to each computer's native
+form so cut and paste between different operating systems works
+seamlessly. And it does it all in Unicode so any text can be copied.
+
+
+Do you use a screen saver? With synergy all your screen savers act in
+concert. When one starts they all start. When one stops they all
+stop. And, if you require a password to unlock the screen, you'll
+only have to enter a password on one screen.
+
+If you regularly use multiple computers on one desk, give synergy a
+try. You'll wonder how you ever lived without it.
+
+You can configure synergy to start automatically when the computer
+starts or when you log in. The steps to do that are different on
+each platform. Note that changing these configurations doesn't
+actually start or stop synergy. The changes take effect the next
+time you start your computer or log in.
+
+
Windows
+
+Start synergy and click the Configure... button
+by the text Automatic Startup. The
+Auto Start dialog will pop up.
+If an error occurs then correct the problem and click
+Configure again.
+
+On the Auto Start dialog you'll configure
+synergy to start or not start automatically when the computer starts
+or when you log in. You need Administrator access rights to start
+synergy automatically when the computer starts. The dialog will let
+you know if you have sufficient permission.
+
+If synergy is already configured to automatically start then there
+will be two Uninstall buttons, at most one
+of which is enabled. Click the enabled button, if any, to tell
+synergy to not start automatically.
+
+If synergy is not configured to start automatically then there will
+be two Install buttons. If you have
+sufficient permission to have synergy start automatically when the
+computer does then the Install button in the
+When Computer Starts box will be enabled.
+Click it to have synergy start for all users when the computer starts.
+In this case, synergy will be available during the login screen.
+Otherwise, click the Install button in the
+When You Log In box to have synergy
+automatically start when you log in.
+
+
Unix
+
+Synergy requires an X server. That means a server must be
+running and synergy must be authorized to connect to that server.
+It's best to have the display manager start synergy. You'll need
+the necessary (probably root) permission to modify the display
+manager configuration files. If you don't have that permission
+you can start synergy after logging in via the
+.xsession file.
+
+Typically, you need to edit three script files. The first file
+will start synergy before a user logs in, the second will kill
+that copy of synergy, and the third will start it again after
+the user logs in.
+
+The contents of the scripts varies greatly between systems so
+there's no one definite place where you should insert your edits.
+However, these scripts often exit before reaching the bottom so
+put the edits near the top of the script.
+
+The location and names of these files depend on the operating
+system and display manager you're using. A good guess for the
+location is /etc/X11. If you use kdm
+then try looking in /etc/kde3 or
+/usr/kde/version/share/config.
+Typical file names are:
+
+
+
+
xdm
kdm
gdm
+
1
xdm/Xsetup
kdm/Xsetup
gdm/Init/Default (*)
+
2
xdm/Xstartup
kdm/Xstartup
gdm/PostLogin/Default (*)
+
3
xdm/Xsession
kdm/Xsession
gdm/Sessions/Default (*, **)
+
+
+
+*) The Default file is used if no other
+suitable file is found. gdm will try
+displayname (e.g. :0)
+and hostname (e.g. somehost),
+in that order, before and instead of Default.
+
+**) gdm may use gdm/Xsession,
+xdm/Xsession or
+dm/Xsession if
+gdm/Sessions/Default doesn't exist.
+
+For a synergy client, add the following to the first file:
+
+ /usr/bin/killall synergyc
+ sleep 1
+ /usr/bin/synergyc [<options>] synergy-server-hostname
+
+Of course, the path to synergyc depends on where you installed it
+so adjust as necessary.
+
+Add to the second file:
+
+ /usr/bin/killall synergyc
+ sleep 1
+
+
+And to the third file:
+
+ /usr/bin/killall synergyc
+ sleep 1
+ /usr/bin/synergyc [<options>]synergy-server-hostname
+
+Note that <options>
+must not include
+-f or --no-daemon or
+the script will never exit and you won't be able to log in.
+
+The changes are the same for the synergy server except replace
+synergyc with synergys
+and use the appropriate synergys command
+line options. Note that the
+first script is run as root so synergys will look for the configuration
+file in root's home directory then in /etc.
+Make sure it exists in one of those places or use the
+--config config-pathname
+option to specify its location.
+
+Note that some display managers (xdm and kdm, but not gdm) grab
+the keyboard and do not release it until the user logs in for
+security reasons. This prevents a synergy server from sharing
+the mouse and keyboard until the user logs in. It doesn't
+prevent a synergy client from synthesizing mouse and keyboard
+input, though.
+
+If you're configuring synergy to start only after you log in then edit
+your .xsession file. Add just what you
+would add to the third file above.
+
+
Mac OS X
+
+[By Tor Slettnes]
+
+There are three different ways to automatically start Synergy
+(client or server) on Mac OS X:
+
+
+
+ The first method involves creating a StartupItem
+ at the system level, which is executed when the machine starts up
+ or shuts down. This script will run in the background, and
+ relaunch synergy as needed.
+
+
+
Pros:
+
+ Synergy is persistent, so this allows for a multi-user
+ setup and interactive logins.
+
+
Cons:
+
+ The synergy process does not have access to the clipboard
+ of the logged-in user.
+
+
+
+
+
+ The second method will launch Synergy from the
+ LoginWindow application, once a particular
+ user has logged in.
+
+
+
Pros:
+
+ The synergy process inherits the
+ $SECURITYSESSIONID environment variable,
+ and therefore copy/paste works.
+
+
Cons:
+
+ Once the user logs out, synergy dies, and no remote
+ control is possible.
+
+
+
+
+
+ The third method is to launch a startup script from the
+ "Startup Items" tab under System Preferences -> Accounts.
+
+
+
Pros:
+
+ Does not require root (Administrator) access
+
+
Cons:
+
+ Once the user logs out, synergy dies, and no remote
+ control is possible.
+
+
+
+
+
+The text below describes how to implement a Synergy client using
+the first two methods simultaneously. This way, Synergy is
+always running, and the clipboard is available when someone is
+logged in. A Mac OS X Synergy server setup will be quite similar.
+
+1. Create a System Level Startup Item
+
+
+
+ Open a Terminal window, and become root:
+
+ $ sudo su -
+
+
+
+ Create a folder for this item:
+
+ # mkdir -p /Library/StartupItems/Synergy
+
+
+
+ In this folder, create a new script file by the same name as
+ the directory itself, Synergy. This script
+ should contain the following text:
+
+ However, replace synergy-server with the actual
+ name or IP address of your Synergy server.
+
+ Note that this scripts takes care not to start
+ Synergy if another instance is currently running. This
+ allows it to run in the background even when synergy is also
+ started independently, e.g. from the LoginWindow
+ application as described below.
+
+
+ Make this script executable:
+
+ # chmod 755 /Library/StartupItems/Synergy/Synergy
+
+
+
+ In the same folder, create a file named
+ StartupParameters.plist containing:
+
+Any errors, as well as output from Synergy, will be shown in
+your terminal window.
+
+Next time you reboot, Synergy should start automatically.
+
+2. Run Synergy When a User Logs In
+
+Each time a user successfully logs in via the console, the
+LoginWindow application creates a unique session
+cookie and stores it in the environment variable
+$SECURITYSESSIONID. For copy and paste operations
+to work, Synergy needs access to this environment variable. In
+other words, Synergy needs to be launched (directly or
+indirectly) via the LoginWindow application.
+
+However, in order to kill any synergy processes started at the
+system level (as described above), we need root access. Thus,
+launching Synergy within the User's environment (e.g. via the
+Startup Items tab in System Preferences -> Accounts) is not an
+option that work in conjunction with the method above.
+
+Fortunately, the LoginWindow application provides
+a "hook" for running a custom program (as root, with the username provided as
+the first and only argument) once a user has authenticated, but
+before the user is logged in.
+
+Unfortunately, only one such hook is available. If you have
+already installed a Login Hook, you may need to add the text
+from below to your existing script, rather than creating a new
+one.
+
+
+
+ Launch a Terminal window, and become root:
+
+ $ sudo su -
+
+
+
+
+ Find out if a LoginHook already exists:
+
+ # defaults read com.apple.loginwindow LoginHook
+
+ This will either show the full path to a script or
+ executable file, or the text:
+
+ The domain/default pair of (com.apple.loginwindow, LoginHook) does not exist
+
+ In the former case, you need to modify your existing script,
+ and/or create a "superscript" which in turn calls your
+ existing script plus the one we will create here.
+
+ The rest of this text assumes that this item did not already
+ exist, and that we will create a new script.
+
+
+ Create a folder in which we will store our custom startup
+ script:
+
+ # mkdir -p /Library/LoginWindow
+
+
+
+ In this folder, create a new script file (let's name it
+ LoginHook.sh), containing the following text:
+
+
+#!/bin/sh
+prog=(/usr/local/bin/synergyc -n $(hostname -s) ip-address-of-server)
+
+### Stop any currently running Synergy client
+killall ${prog[0]##*/}
+
+### Start the new client
+exec "${prog[@]}"
+
+
+
+ Make this script executable:
+
+ # chmod 755 /Library/LoginWindow/LoginHook.sh
+
+
+
+ Create a login hook to call the script you just created:
+
+ # defaults write com.apple.loginwindow LoginHook /Library/LoginWindow/LoginHook.sh
+
+
+
+
+More information on setting up login hooks can be found at
+Apple.
+
+When running the Synergy client, you may need to use the IP
+address of the Synergy server rather than its host name.
+Specifically, unless you have listed the server in your
+local /etc/hosts file or in your local NetInfo
+database, name services (i.e. DNS) may not yet be available by the
+time you log in after power-up. synergyc will
+quit if it cannot resolve the server name.
+
+(This is not an issue with the previous method, because the
+StartupParameters.plist file specifies that this
+script should not be run until "network" is available).
+
+3. Good Luck!
+
+Remember to look in your system log on both your server and your
+client(s) for clues to any problems you may have
+(/var/log/system.log on your OS X box, typically
+/var/log/syslog on Linux boxes).
+
+so synergy can find the X11 includes and libraries.
+
+
Building
+
+
Windows
+
+ Open a command prompt window (cmd.exe or command.exe). If necessary
+ run vcvars.bat, created when VC++ or Visual Studio was installed. (Use
+ search to find it.) It's necessary to run the file if you didn't have
+ the installer set up environment variables for you. Then enter:
+
+ nmake /nologo /f Makefile.win
+
+ This will build the programs into build\Release.
+
+
Unix or Mac OS X
+
+ Simply enter:
+
+ make
+
+ This will build the client and server and leave them in their
+ respective source directories.
+
+
+
+
Installing
+
+
Windows
+
+ You'll need NSIS,
+ the Nullsoft Scriptable Install System. As in the building on Windows
+ description above, enter:
+
+ nmake /nologo /f Makefile.win installer
+
+ to build build\Release\SynergyInstaller.exe. Run
+ this to install synergy.
+
+ Alternatively, you can simply copy the following files from the
+ build\Release
+ directory to a directory you choose (perhaps under the
+ Program Files directory):
+
+
synergy.exe
+
synergyc.exe
+
synergys.exe
+
synrgyhk.dll
+
+
+
Unix or Mac OS X
+
+
+ make install
+
+ will install the client and server into
+ /usr/local/bin unless you
+ specified a different directory when you ran configure.
+
+
+
+
diff --git a/doc/configuration.html b/doc/configuration.html
new file mode 100644
index 00000000..6c1c8baa
--- /dev/null
+++ b/doc/configuration.html
@@ -0,0 +1,686 @@
+
+
+
+
+
+
+
+ Synergy Configuration Guide
+
+
+
+
Synergy Configuration File Format
+
+The synergy server requires configuration. It will try certain
+pathnames to load the configuration file if you don't specify a
+path using the --config command line
+option. synergys --help reports those
+pathnames.
+
+The configuration file is a plain text file. Use any text editor
+to create the configuration file. The file is broken into sections
+and each section has the form:
+
+ section: name
+ args
+ end
+
+Comments are introduced by # and continue to
+the end of the line. name must be one of the
+following:
+
+
screens
+
aliases
+
links
+
options
+
+See below for further explanation of each section type. The
+configuration file is case-sensitive so Section,
+SECTION, and section
+are all different and only the last is valid. Screen names are the
+exception; screen names are case-insensitive.
+
+The file is parsed top to bottom and names cannot be used before
+they've been defined in the screens or
+aliases sections. So the
+links and aliases
+must appear after the screens and links
+cannot refer to aliases unless the aliases
+appear before the links.
+
+
screens
+
+args is a list of screen names, one name per
+line, each followed by a colon. Names are arbitrary strings but they
+must be unique. The hostname of each computer is recommended. (This
+is the computer's network name on win32 and the name reported by the
+program hostname on Unix and OS X. Note
+that OS X may append .local to the name you
+gave your computer; e.g. somehost.local.)
+There must be a screen name for the server and each client. Each
+screen can specify a number of options. Options have the form
+name =
+value and are listed one per line
+after the screen name.
+
+Example:
+
+ section: screens
+ moe:
+ larry:
+ halfDuplexCapsLock = true
+ halfDuplexNumLock = true
+ curly:
+ meta = alt
+ end
+
+This declares three screens named moe,
+larry, and curly.
+Screen larry has half-duplex Caps Lock and
+Num Lock keys (see below) and screen curly
+converts the meta modifier key to the alt modifier key.
+
+A screen can have the following options:
+
+
halfDuplexCapsLock = {true|false}
+
+ This computer has a Caps Lock key that doesn't report a
+ press and a release event when the user presses it but
+ instead reports a press event when it's turned on and a
+ release event when it's turned off. If Caps Lock acts
+ strangely on all screens then you may need to set this
+ option to true
+ on the server screen. If it acts strangely on one
+ screen then that screen may need the option set to
+ true.
+
+
halfDuplexNumLock = {true|false}
+
+ This is identical to halfDuplexCapsLock
+ except it applies to the Num Lock key.
+
+
halfDuplexScrollLock = {true|false}
+
+ This is identical to halfDuplexCapsLock
+ except it applies to the Scroll Lock key. Note that, by default,
+ synergy uses Scroll Lock to keep the cursor on the current screen. That
+ is, when Scroll Lock is toggled on, the cursor is locked to the screen
+ that it's currently on. You can use that to prevent accidental switching.
+ You can also configure other hot keys to do that; see
+ lockCursorToScreen.
+
+ This option works around a bug in the XTest extension
+ when used in combination with Xinerama. It affects
+ X11 clients only. Not all versions of the XTest
+ extension are aware of the Xinerama extension. As a
+ result, they do not move the mouse correctly when
+ using multiple Xinerama screens. This option is
+ currently true by default. If
+ you know your XTest extension is Xinerama aware then set
+ this option to false.
+
+
shift = {shift|ctrl|alt|meta|super|none}
+ ctrl = {shift|ctrl|alt|meta|super|none}
+ alt = {shift|ctrl|alt|meta|super|none}
+ meta = {shift|ctrl|alt|meta|super|none}
+ super = {shift|ctrl|alt|meta|super|none}
+
+ Map a modifier key pressed on the server's keyboard to
+ a different modifier on this client. This option only
+ has an effect on a client screen; it's accepted and
+ ignored on the server screen.
+
+ You can map, say, the shift key to shift (the default),
+ ctrl, alt, meta, super or nothing. Normally, you
+ wouldn't remap shift or ctrl. You might, however, have
+ an X11 server with meta bound to the Alt keys. To use
+ this server effectively with a windows client, which
+ doesn't use meta but uses alt extensively, you'll want
+ the windows client to map meta to alt (using
+ meta = alt).
+
+
+
+
aliases
+
+ args is a list of screen names just like
+ in the screens section except each screen
+ is followed by a list of aliases, one per line, not followed
+ by a colon. An alias is a screen name and must be unique. During
+ screen name lookup each alias is equivalent to the screen name it
+ aliases. So a client can connect using its canonical screen name
+ or any of its aliases.
+
+ Example:
+
+ section: aliases
+ larry:
+ larry.stooges.com
+ curly:
+ shemp
+ end
+
+ Screen larry is also known as
+ larry.stooges.com and can connect as
+ either name. Screen curly is also
+ known as shemp (hey, it's just an example).
+
+
links
+
+ args is a list of screen names just like
+ in the screens section except each screen
+ is followed by a list of links, one per line. Each link has the
+ form {left|right|up|down}[<range>] =
+ name[<range>]. A link indicates which
+ screen is adjacent in the given direction.
+
+ Each side of a link can specify a range which defines a portion
+ of an edge. A range on the direction is the portion of edge you can
+ leave from while a range on the screen is the portion of edge you'll
+ enter into. Ranges are optional and default to the entire edge. All
+ ranges on a particular direction of a particular screen must not
+ overlap.
+
+ A <range> is written as (<start>,<end>).
+ Both start and end
+ are percentages in the range 0 to 100, inclusive. The start must be
+ less than the end. 0 is the left or top of an edge and 100 is the
+ right or bottom.
+
+ Example:
+
+ section: links
+ moe:
+ right = larry
+ up(50,100) = curly(0,50)
+ larry:
+ left = moe
+ up(0,50) = curly(50,100)
+ curly:
+ down(0,50) = moe
+ down(50,100) = larry(0,50)
+ end
+
+ This indicates that screen larry is to
+ the right of screen moe (so moving the
+ cursor off the right edge of moe would
+ make it appear at the left edge of larry),
+ the left half of
+ curly is above the right half of
+ moe,
+ moe is to the left of
+ larry (edges are not necessarily symmetric
+ so you have to provide both directions), the right half of
+ curly is above the left half of
+ larry, all of moe
+ is below the left half of curly, and the
+ left half of larry is below the right half of
+ curly.
+
+ Note that links do not have to be
+ symmetrical; for instance, here the edge between
+ moe and curly
+ maps to different ranges depending on if you're going up or down.
+ In fact links don't have to be bidirectional. You can configure
+ the right of moe to go to
+ larry without a link from the left of
+ larry to moe.
+ It's possible to configure a screen with no outgoing links; the
+ cursor will get stuck on that screen unless you have a hot key
+ configured to switch off of that screen.
+
+
options
+
+ args is a list of lines of the form
+ name = value. These set the global
+ options.
+
+ The server will expect each client to send a message no
+ less than every N milliseconds.
+ If no message arrives from a client within
+ 3N seconds the server forces that
+ client to disconnect.
+
+ If synergy fails to detect clients disconnecting while
+ the server is sleeping or vice versa, try using this
+ option.
+
+
switchCorners = <corners>
+
+ Synergy won't switch screens when the mouse reaches the edge of
+ the screen if it's in a listed corner. The size of all corners
+ is given by the switchCornerSize
+ option.
+
+ Corners are specified by a list using the following names:
+
+
none -- no corners
+
top-left -- the top left corner
+
top-right -- the top right corner
+
bottom-left -- the bottom left corner
+
bottom-right -- the bottom right corner
+
left -- top and bottom left corners
+
right -- top and bottom right corners
+
top -- left and right top corners
+
bottom -- left and right bottom corners
+
all -- all corners
+
+
+ The first name in the list is one of the above names and defines
+ the initial set of corners. Subsequent names are prefixed with
+ + or - to add the corner to or remove the corner from the set,
+ respectively. For example:
+
+
+ all -left +top-left
+
+
+ starts will all corners, removes the left corners (top and bottom)
+ then adds the top-left back in, resulting in the top-left,
+ bottom-left and bottom-right corners.
+
+
switchCornerSize = N
+
+ Sets the size of all corners in pixels. The cursor must be within
+ N pixels of the corner to be considered
+ to be in the corner.
+
+
switchDelay = N
+
+ Synergy won't switch screens when the mouse reaches the
+ edge of a screen unless it stays on the edge for
+ N
+ milliseconds. This helps prevent unintentional
+ switching when working near the edge of a screen.
+
+
switchDoubleTap = N
+
+ Synergy won't switch screens when the mouse reaches the
+ edge of a screen unless it's moved away from the edge
+ and then back to the edge within N
+ milliseconds. With
+ the option you have to quickly tap the edge twice to
+ switch. This helps prevent unintentional switching
+ when working near the edge of a screen.
+
+
screenSaverSync = {true|false}
+
+ If set to false then synergy
+ won't synchronize screen savers. Client screen savers
+ will start according to their individual configurations.
+ The server screen saver won't start if there is input,
+ even if that input is directed toward a client screen.
+
+
relativeMouseMoves = {true|false}
+
+ If set to true then secondary
+ screens move the mouse using relative rather than absolute
+ mouse moves when and only when the cursor is locked to the
+ screen (by Scroll Lock or a configured
+ hot key).
+ This is intended to make synergy work better with certain
+ games. If set to false or not
+ set then all mouse moves are absolute.
+
+
keystroke(key) = actions
+
+ Binds the key combination key to the
+ given actions. key
+ is an optional list of modifiers (shift,
+ control, alt,
+ meta or super)
+ optionally followed by a character or a key name, all separated by
+ + (plus signs). You must have either
+ modifiers or a character/key name or both. See below for
+ valid key names.
+
+ Keyboard hot keys are handled while the cursor is on the primary
+ screen and secondary screens. Separate actions can be assigned
+ to press and release.
+
+
mousebutton(button) = actions
+
+ Binds the modifier and mouse button combination
+ button to the given
+ actions. button
+ is an optional list of modifiers (shift,
+ control, alt,
+ meta or super)
+ followed by a button number. The primary button (the
+ left button for right handed users) is button 1, the middle button
+ is 2, etc.
+
+ Mouse button actions are not handled while the cursor is on the
+ primary screen. You cannot use these to perform an action while
+ on the primary screen. Separate actions can be assigned to press
+ and release.
+
+
+ You can use both the switchDelay and
+ switchDoubleTap options at the same
+ time. Synergy will switch when either requirement is satisfied.
+
+Actions are two lists of individual actions separated
+by commas. The two lists are separated by a semicolon. Either list can be
+empty and if the second list is empty then the semicolon is optional. The
+first list lists actions to take when the condition becomes true (e.g. the
+hot key or mouse button is pressed) and the second lists actions to take
+when the condition becomes false (e.g. the hot key or button is released).
+The condition becoming true is called activation and becoming false is
+called deactivation.
+Allowed individual actions are:
+
+
keystroke(key[,screens])
+
keyDown(key[,screens])
+
keyUp(key[,screens])
+
+ Synthesizes the modifiers and key given in key
+ which has the same form as described in the
+ keystroke option. If given,
+ screens lists the screen or screens to
+ direct the event to, regardless of the active screen. If not
+ given then the event is directed to the active screen only.
+ (Due to a bug, keys cannot be directed to the server while on a
+ client screen.)
+
+ keyDown synthesizes a key press and
+ keyUp synthesizes a key release.
+ keystroke synthesizes a key press on
+ activation and a release on deactivation and is equivalent to
+ a keyDown on activation and
+ keyUp on deactivation.
+
+ screens is either *
+ to indicate all screens or a colon (:) separated list of screen
+ names. (Note that the screen name must have already been encountered
+ in the configuration file so you'll probably want to put actions at
+ the bottom of the file.)
+
+
mousebutton(button)
+
mouseDown(button)
+
mouseUp(button)
+
+ Synthesizes the modifiers and mouse button given in
+ button
+ which has the same form as described in the
+ mousebutton option.
+
+ mouseDown synthesizes a mouse press and
+ mouseUp synthesizes a mouse release.
+ mousebutton synthesizes a mouse press on
+ activation and a release on deactivation and is equivalent to
+ a mouseDown on activation and
+ mouseUp on deactivation.
+
+
lockCursorToScreen(mode)
+
+ Locks the cursor to or unlocks the cursor from the active screen.
+ mode can be off
+ to unlock the cursor, on to lock the
+ cursor, or toggle to toggle the current
+ state. The default is toggle. If the
+ configuration has no lockCursorToScreen
+ action and Scroll Lock is not used as a hot key then Scroll Lock
+ toggles cursor locking.
+
+
switchToScreen(screen)
+
+ Jump to screen with name or alias screen.
+
+
switchInDirection(dir)
+
+ Switch to the screen in the direction dir,
+ which may be one of left,
+ right, up or
+ down.
+
+
keyboardBroadcast(mode[,screens])
+
+ Turns broadcasting of keystrokes to multiple screens on and off. When
+ turned on all key presses and releases are sent to all of the screens
+ listed in screens. If not given, empty or
+ * then keystrokes are broadcast to all screens.
+ (However, due to a bug, keys cannot be sent to the server while on a
+ client screen.)
+
+ mode can be off
+ to turn broadcasting off, on to turn it
+ on, or toggle to toggle the current
+ state. The default is toggle.
+
+ screens is either *
+ to indicate all screens or a colon (:) separated list of screen
+ names. (Note that the screen name must have already been encountered
+ in the configuration file so you'll probably want to put actions at
+ the bottom of the file.)
+
+ Multiple keyboardBroadcast actions may be
+ configured with different screens. The most
+ recently performed action defines the screens to broadcast to.
+
+
+
+Examples:
+
+
keystroke(alt+left) = switchInDirection(left)
+
+ Switches to the screen to left when the left arrow key is pressed
+ in combination with the Alt key.
+
+Additionally, a name of the form \uXXXX where
+XXXX is a hexadecimal number is interpreted as
+a unicode character code.
+Key and modifier names are case-insensitive. Keys that don't exist on
+the keyboard or in the default keyboard layout will not work.
+
+To avoid spam bots, the above email addresses have ".no_spam"
+hidden near the end. If you copy and paste the text be sure to
+remove it.
+
+Please check the
+
+bug list before reporting a bug. You may also find answers at the
+synergy forums.
+Emails for help asking questions answered on this site will go unanswered.
+
+Synergy is reasonably well commented so reading the source code
+should be enough to understand particular pieces. See the
+doc/PORTING
+file in the synergy source code for more high-level information.
+
+
How it works
+
+The theory behind synergy is simple: the server captures mouse,
+keyboard, clipboard, and screen saver events and forwards them to
+one or more clients. If input is directed to the server itself
+then the input is delivered normally. In practice, however, many
+complications arise.
+
+First, different keyboard mappings can produce different characters.
+Synergy attempts to generate the same character on the client as
+would've been generated on the server, including appropriate modifier
+keys (like Control and Alt). Non-character keys like Shift are also
+synthesized if possible. Sometimes the client simply cannot create
+the character or doesn't have a corresponding non-character key and
+synergy must discard the event. Note that synergy won't necessarily
+synthesize an event for the corresponding key on the client's
+keyboard. For example, if the client or server can't distinguish
+between the left and right shift keys then synergy can't be certain
+to synthesize the shift on the same side of the keyboard as the user
+pressed.
+
+Second, different systems have different clipboards and clipboard
+formats. The X window system has a system-wide selection and
+clipboard (and yet other buffers) while Microsoft Windows has only
+a system-wide clipboard. Synergy has to choose which of these
+buffers correspond to one another. Furthermore, different systems
+use different text encodings and line breaks. Synergy mediates and
+converts between them.
+
+Finally, there are no standards across operating systems for some
+operations that synergy requires. Among these are: intercepting
+and synthesizing events; enabling, disabling, starting and stopping
+the screen saver; detecting when the screen saver starts; reading
+and writing the clipboard(s).
+
+All this means that synergy must be customized to each operating
+system (or windowing system in the case of X windows). Synergy
+breaks platform differences into two groups. The first includes
+the mundane platform dependent things: file system stuff,
+multithreading, network I/O, multi-byte and wide character
+conversion, time and sleeping, message display and logging, and
+running a process detached from a terminal. This code lives in
+lib/arch.
+
+The second includes screen and window management handling, user
+event handling, event synthesis, the clipboards, and the screen
+saver. This code lives in lib/platform.
+
+For both groups, there are particular classes or interfaces that
+must be inherited and implemented for each platform. See the
+doc/PORTING file in the synergy source
+code for more information.
+
+
Auto-generated Documentation
+
+Synergy can automatically generate documentation from the comments
+in the code using doxygen.
+Use make doxygen to build it yourself
+from the source code into the doc/doxygen/html
+directory.
+
+
+
+
+
diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in
new file mode 100644
index 00000000..4abf52f9
--- /dev/null
+++ b/doc/doxygen.cfg.in
@@ -0,0 +1,898 @@
+# Doxyfile 1.2.13.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = @PACKAGE@
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/doxygen
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French,
+# German, Greek, Hungarian, Italian, Japanese, Korean, Norwegian, Polish,
+# Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish.
+
+OUTPUT_LANGUAGE = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these class will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH =
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower case letters. If set to YES upper case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consist of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text.
+
+WARN_FORMAT =
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = .
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl
+
+FILE_PATTERNS = *.cpp *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command , where
+# is the value of the INPUT_FILTER tag, and is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse.
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 3
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT =
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+,
+# or Internet explorer 4.0+). Note that for large projects the tree generation
+# can take a very long time. In such cases it is better to disable this feature.
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT =
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimised for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT =
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assigments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT =
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION =
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_XML = NO
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line and do not end with a semicolon. Such function macros are typically
+# used for boiler-plate code, and will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS = NO
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = @HAVE_DOT@
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermedate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
+
+# The CGI_NAME tag should be the name of the CGI script that
+# starts the search engine (doxysearch) with the correct parameters.
+# A script with this name will be generated by doxygen.
+
+CGI_NAME =
+
+# The CGI_URL tag should be the absolute URL to the directory where the
+# cgi binaries are located. See the documentation of your http daemon for
+# details.
+
+CGI_URL =
+
+# The DOC_URL tag should be the absolute URL to the directory where the
+# documentation is located. If left blank the absolute path to the
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL =
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the
+# documentation is located. If left blank the directory on the local machine
+# will be used.
+
+DOC_ABSPATH =
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
+# is installed.
+
+BIN_ABSPATH =
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to
+# documentation generated for other projects. This allows doxysearch to search
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS =
diff --git a/doc/faq.html b/doc/faq.html
new file mode 100644
index 00000000..b9696391
--- /dev/null
+++ b/doc/faq.html
@@ -0,0 +1,266 @@
+
+
+
+
+
+
+
+ Synergy Frequently Asked Questions
+
+
+
+
Why doesn't ctrl+alt+del work on secondary screens?
+
+ Synergy isn't able to capture ctrl+alt+del on PC compatible
+ primary screens because it's handled completely differently than
+ other keystrokes. However, when the mouse is on a client
+ screen, pressing ctrl+alt+pause will simulate ctrl+alt+del
+ on the client. (A client running on Windows NT, 2000, or XP
+ must be configured to autostart when the computer starts for
+ this to work.)
+
+ On a primary screen running on an OS X system, you can use
+ ctrl+command+del. Using the pause key isn't necessary since OS X
+ doesn't treat ctrl+command+del differently. And using the pause
+ key isn't usually possible because there isn't one on most OS X
+ systems. Use command instead of option/alt because
+ the command key, not the option/alt key, maps to alt on windows.
+ The reason is because the command key is in the same physical
+ location and performs the same general function (menu shortcuts)
+ as alt on a windows system. This mapping can be modified in
+ the configuration.
+
+ On mac laptops, the key labeled "delete" is actually backspace
+ and ctrl+command+delete won't work. However fn+delete really
+ is delete so fn+ctrl+command+delete will act as ctrl+alt+del
+ on a windows secondary screen.
+
+
Can the server and client be using different operating systems?
+
+ Yes. The synergy network protocol is platform neutral so
+ synergy doesn't care what operating systems are running on
+ the server and clients.
+
+
What's the difference between synergy and
+x2x, x2vnc, etc?
+
+ Unlike x2x, synergy supports any number of computers and
+ it doesn't require X on Microsoft Windows platforms. It
+ also has more advanced clipboard support and synchronizes
+ screensavers. x2vnc is also limited to two computers,
+ requires the separate vnc package, and is really only
+ appropriate for using an X system to control a non-X system.
+ However, the right tool for the job is whatever tool works
+ best for you.
+
+
What does "Cannot initialize hook library" mean?
+
+ This error can occur on a synergy server running on a
+ Microsoft Windows operating system. It means that synergy
+ is already running or possibly was not shut down properly.
+ If it's running then first end the synergy task. If it's
+ not then try logging off and back on or rebooting then
+ starting synergy again.
+
+
What security/encryption does synergy provide?
+
+ Synergy provides no built-in encryption or authentication.
+ Given that, synergy should not be used on or over any untrusted
+ network, especially the Internet. It's generally fine for home
+ networks. Future versions may provide built-in encryption and
+ authentication.
+
+ Strong encryption and authentication is available through SSH
+ (secure shell). Run the SSH daemon (i.e. server) on the same
+ computer that you run the synergy server. It requires no
+ special configuration to support synergy. On each synergy
+ client system, run SSH with port forwarding:
+
+ where server-hostname is the name of the
+ SSH/synergy server.
+ Once ssh authenticates itself, start the synergy client
+ normally except use localhost or
+ 127.0.0.1 as the server's
+ address. SSH will then encrypt all communication on behalf of
+ synergy. Authentication is handled by the SSH authentication.
+
+ A free implementation of SSH for Linux and many Unix systems is
+ OpenSSH. For
+ Windows there's a port of OpenSSH using
+ Cygwin.
+
+
What should I call my screens in the configuration?
+
+ You can use any unique name in the configuration file for each
+ screen but it's easiest to use the hostname of the computer.
+ That's the computer name not including the domain. For example,
+ a computer with the fully qualified domain name xyz.foo.com has
+ the hostname xyz. There should also be an alias for xyz to
+ xyz.foo.com. If you don't use the computer's hostname, you
+ have to tell synergy the name of the screen using a command line
+ option, or the startup dialog on Windows.
+
+ Some systems are configured to report the fully qualified domain
+ name as the hostname. For those systems it will be easier to use
+ the FQDN as the screen name. Also note that a Mac OS X system
+ named xyz may report its hostname as
+ xyz.local. If that's the case for you
+ then use xyz.local as the screen name.
+
+
Why do my Caps-Lock, Num-Lock, Scroll-Lock keys act funny?
+
+ Some systems treat the Caps-Lock, Num-Lock, and Scroll-Lock keys
+ differently than all the others. Whereas most keys report going down
+ when physically pressed and going up when physically released, on
+ these systems the Caps-Lock and Num-Lock keys report going down
+ when being activated and going up when being deactivated. That
+ is, when you press and release, say, Caps-Lock to activate it, it
+ only reports going down, and when you press and release to
+ deactivate it, it only reports going up. This confuses synergy.
+
+ You can solve the problem by changing your configuration file.
+ In the screens section, following each screen that has the
+ problem, any or all of these lines as appropriate:
+
+ Then restart synergy on the server or reload the configuration.
+
+
Can synergy share the display in addition to the mouse and keyboard?
+
+ No. Synergy is a KM solution not a KVM (keyboard, video, mouse)
+ solution. However, future versions will probably support KVM.
+ Hopefully, this will make synergy suitable for managing large
+ numbers of headless servers.
+
+
Can synergy do drag and drop between computers?
+
+ No. That's a very cool idea and it'll be explored. However, it's
+ also clearly difficult and may take a long time to implement.
+
+
Does AltGr/Mode-Switch/ISO_Level3_Shift work?
+
+ Yes, as of 1.0.12 synergy has full support for AltGr/Mode-switch.
+ That includes support for most (all?) European keyboard layouts.
+ All systems should be using the same keyboard layout, though, for
+ all characters to work. (Any character missing from a client's
+ layout cannot be generated by synergy.) There is experimental
+ support for ISO_Level3_Shift in 1.1.3.
+
+
Why isn't synergy ported to platform XYZ?
+
+ Probably because the developers don't have access to platform XYZ
+ and/or are unfamiliar with development on XYZ. Also, synergy has
+ inherently non-portable aspects so there's a not insignificant
+ effort involved in porting.
+
+
My client can't connect. What's wrong?
+
+ A common mistake when starting the client is to give the wrong
+ server host name. The last synergyc command line option (Unix)
+ or the "Server Host Name" edit field (Windows) should be the
+ host name (or IP address) of the server not the client's host
+ name. If you get the error connection failed: cannot connect
+ socket followed by the attempt to connect was forcefully
+ rejected or connection refused then the server isn't started,
+ can't bind the address, or the client is connecting to the wrong
+ host name/address or port. See the
+ troublshooting page for more help.
+
+ to the configure command line? Solaris puts
+ the X11 includes and libraries in an unusual place and the above lets
+ synergy find them.
+
+
The screen saver never starts. Why not?
+
+ If the synergy server is on X Windows then the screen saver will
+ not start while the mouse is on a client screen. This is a
+ consequence of how X Windows, synergy and xscreensaver work.
+
+
I can't switch screens anymore for no apparent reason. Why?
+
+ This should not happen with 1.1.3 and up. Earlier versions of
+ synergy would not allow switching screens when a key was down and
+ sometimes it would believe a key was down when it was not.
+
+
I get the error 'Xlib: No protocol specified'. Why?
+
+ You're running synergy without authorization to connect to the
+ X display. Typically the reason is running synergy as root when
+ logged in as non-root. Just run synergy as the same user that's
+ logged in.
+
+
The cursor goes to secondary screen but won't come back. Why?
+
+ Your configuration is incorrect. You must indicate the neighbors
+ of every screen. Just because you've configured 'Apple' to be to
+ the left of 'Orange' does not mean that 'Orange' is to the right
+ of 'Apple'. You must provide both in the configuration.
+
+
The cursor wraps from one edge of the screen to the opposite. Why?
+
+ Because you told it to. If you list 'Orange' to be to the left of
+ 'Orange' then moving the mouse off the left edge of 'Orange' will
+ make it jump to the right edge. Remove the offending line from the
+ configuration if you don't want that behavior.
+
+
How do I stop my game from minimizing when I leave the screen?
+
+ Many full screen applications, particularly games, automatically
+ minimize when they're no longer the active (foreground) application
+ on Microsoft Windows. The synergy server normally becomes the foreground
+ when you switch to another screen in order to more reliably capture all
+ user input causing those full screen applications to minimize. To
+ prevent synergy from stealing the foreground just click "Options..."
+ and check "Don't take foreground window on Windows servers." If you
+ turn this on then be aware that synergy may not function correctly when
+ certain programs, particularly the command prompt, are the foreground
+ when you switch to other screens. Simply make a different program the
+ foreground before switching to work around that.
+
+The first incarnation of synergy was CosmoSynergy, created by
+Richard Lee and Adam Feder then at Cosmo Software, Inc., a
+subsidiary of SGI (nee Silicon Graphics, Inc.), at the end of
+1996. They wrote it, and Chris Schoeneman contributed, to
+solve a problem: most of the engineers in Cosmo Software had
+both an Irix and a Windows box on their desks and switchboxes
+were expensive and annoying. CosmoSynergy was a great success
+but Cosmo Software declined to productize it and the company
+was later closed.
+
+Synergy is a from-scratch reimplementation of CosmoSynergy.
+It provides most of the features of the original and adds a
+few improvements.
+
+synergy: [noun] a mutually advantageous conjunction of distinct elements
+
+Synergy lets you easily share a single mouse and keyboard between
+multiple computers with different operating systems, each with its
+own display, without special hardware. It's intended for users
+with multiple computers on their desk since each system uses its
+own monitor(s).
+
+Redirecting the mouse and keyboard is as simple as moving the mouse
+off the edge of your screen. Synergy also merges the clipboards of
+all the systems into one, allowing cut-and-paste between systems.
+Furthermore, it synchronizes screen savers so they all start and stop
+together and, if screen locking is enabled, only one screen requires
+a password to unlock them all. Learn more
+about how it works.
+
Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family)
+
Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family)
+
Mac OS X 10.2 or higher
+
Unix
+
+
X Windows version 11 revision 4 or up
+
XTEST extension
+ (use "xdpyinfo | grep XTEST" to check for XTEST)
+
+
+All systems must support TCP/IP networking.
+
+"Unix" includes Linux, Solaris, Irix and other variants. Synergy has
+only been extensively tested on Linux and may not work completely or
+at all on other versions of Unix. Patches are welcome (including
+patches that package binaries) at the
+patches page.
+
+The Mac OS X port is incomplete. It does not synchronize the screen saver,
+only text clipboard data works (i.e. HTML and bitmap data do not work),
+the cursor won't hide when not on the screen, and there may be problems
+with mouse wheel acceleration. Other problems should be
+filed as bugs.
+
+
+
+
diff --git a/doc/images/logo.gif b/doc/images/logo.gif
new file mode 100644
index 00000000..d9750ba3
Binary files /dev/null and b/doc/images/logo.gif differ
diff --git a/doc/images/warp.gif b/doc/images/warp.gif
new file mode 100644
index 00000000..fd10033d
Binary files /dev/null and b/doc/images/warp.gif differ
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 00000000..9e9ef302
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+ Synergy
+
+
+
+
diff --git a/doc/license.html b/doc/license.html
new file mode 100644
index 00000000..5e748123
--- /dev/null
+++ b/doc/license.html
@@ -0,0 +1,313 @@
+
+
+
+
+
+
+
+ Synergy License and Copyright
+
+
+
+
Synergy License and Copyright
+
+Synergy is copyright (C) 2002 Chris Schoeneman.
+Synergy is distributed under the GNU GENERAL PUBLIC LICENSE.
+
+
GNU GENERAL PUBLIC LICENSE
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+
Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+
GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+
+
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+
+
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE
+IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED
+ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
+SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF
+THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT
+LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
+LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
+PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH
+HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
Hot key screen switching now restores last cursor position
+
Fixed loss of hot keys when reloading configuration
+
Fixed autorepeating on win32 (no longer sending repeating key releases)
+
Fixed autorepeating on X11 (non-repeating keys were repeating)
+
Fixed AltGr issues on X11
+
Fixed modifier mapping bug on OS X client (caused wrong characters)
+
Fixed one way for modifiers to get stuck active on all platforms
+
Fixed bugs in win32 GUI
+
Removed alloca() from unix code (should fix FreeBSD build)
+
Added more debugging output for network problems
+
Fixed failure to detect some errors on X11
+
+
+Mar-22-2006 - Synergy 1.3.0 released
+
+Made following additions:
+
+
Console window on win32 can now be closed (reopened from tray menu)
+
Can now change logging level on the fly from win32 tray menu
+
Added client keep alive (lost connections are now detected reliably)
+
Added support for linking portions of screen edges
+
Added version number to UI in win32
+
Added GUI for hot key configuration on win32
+
Hot keys can now perform actions on press and/or release
+
Added key down, key up, mouse down, and mouse up hot key actions
+
Key actions can be directed to particular screens
+
Hot keys can each perform multiple actions
+
+
+Made following fixes:
+
+
Fixed AltGr key mappings (again)
+
Fixed assertion when pasting on X11
+
Fixed modifier keys in VMware on X11
+
OS X server now treats sends option/alt as AltGr or super depending on key
+
Improved handling of AltGr on win32
+
Fixed not removing client when connection is lost
+
Clients now detect loss of connection to server and reconnect
+
Fixed mouse jumping on OS X multimonitor systems
+
Closing console on win32 no longer quits synergy
+
Fixed Num Lock breaking certain keys
+
Fixed Scroll Lock not locking cursor to screen
+
Fixed mapping of delete key on X11
+
Fixed loss of clipboard after a particular copy/paste sequence
+
Fixed compatibility with windows 95/98/Me (ToUnicodeEx)
+
Fixed bad argument to function on OS X
+
Fixed error parsing comments in configuration
+
Fixed autorepeat on win32 servers
+
Fixed X11 keyboard focus bug when reentering screen
+
Fixed (suppressed) hot key autorepeating
+
Fixed mousebutton action when Caps/Num/Scroll Lock were on
+
Added documentation on firewalls
+
Fixed documentation formatting on IE6
+
+
+Hot keys support has one known major bug: key actions cannot be directed
+to the server (primary) screen. The configuration file syntax has changed
+from earlier versions; users will have to modify the configurations by
+hand.
+
+Dec-18-2005 - Synergy 1.2.7 released
+
+Made following changes:
+
+
Added preliminary support for configurable hot keys (Lorenz Schori)
+
Major rewrite of keyboard handling code
+
Fixed non-US keyboard handling (AltGr and ISO_Level3_Shift)
+
Now supporting all installed keyboard layouts simultaneously
+
Fixed bug in handling remapped caps-lock on X11
+
Fixed control and alt keys getting stuck on on X11
+
Fixed desktop focus problems requiring extra clicks on win32
+
Fixed alt key event getting passed to server when on client on win32
+
Synergy would prevent alt+numpad character entry; this is fixed
+
Fixed suppression of xscreensaver 2.21 on X11
+
Fixed middle mouse button dragging on OSX server (Brian Kendall)
+
Fixed caps/num/scroll lock toggles getting out of sync
+
Enhanced support for converting clipboard text to the Latin-1 encoding
+
Added autostart documentation for KDE users
+
Added more details about using Terminal for OSX users
+
Fixed crash when using --help on certain platforms
+
+
+The hot key support is known to have bugs. The configuration file
+syntax for hot keys is likely to change and the documentation for it
+is minimal. The graphical UI on windows does not provide any support
+for editing hot keys.
+
+Nov-12-2005 - Synergy 1.2.6 released
+
+Made following changes:
+
+
Fixed permission problem saving autostart configuration in win32 launcher
+
Disabled buggy fix for loss of clipboard change detection
+
Restored pthread signal autoconf code
+
+
+Oct-17-2005 - Synergy 1.2.5 released
+
+Made following changes:
+
+
Win32 launcher now saves configuration automatically
+
Fixed failure to save autostart configuration on win32
+
Fixed output bottom-right configuration flag
+
Now properly releasing keys when leaving a client screen
+
Fixed stuck-Alt on win32
+
Fixed 64-bit problem with clipboard on X11
+
Fixed BadAtom bug on X11
+
Fixed loss of detection of clipboard changes on win32
+
Added support for the MightyMouse
+
Added support for buttons 4 and 5 on OSX
+
Now shutting down win32 services when uninstalling
+
+
+Aug-07-2005 - Synergy 1.2.4 released
+
+Made following changes:
+
+
Fixed gcc 4.0 warnings
+
Fixed autoconf/automake problems
+
Fixed scroll-lock on X windows
+
Added option to suppress foreground window grabbing on win32
+
Fixed --daemon option on win32 client
+
Fixed --no-restart on client
+
Updated OS X autostart documentation
+
+
+Jul-27-2005 - Synergy 1.2.3 released
+
+Made following changes:
+
+
Added OS X screensaver synchronization support (Lorenz Schori)
+
Added OS X sleep support (Lorenz Schori)
+
Added OS X fast user switching support (Lorenz Schori)
+
Fixed international keyboard support on OS X (Lorenz Schori)
+
Now capturing global hotkeys (e.g. cmd+tab, etc) on OS X (Lorenz Schori)
+
Added support for SO_REUSEADDR (Don Eisele)
+
Added "dead" corners feature
+
Fixed "resource temporarily unavailable" warning when quiting on OS X
+
Win32 now defaults to WARNING log level to avoid console window
+
Now disabling foreground window on win32 when leaving server (Brent Priddy)
+
+
+Jan-26-2005 - Synergy 1.2.2 released
+
+Made following changes:
+
+
Fixed major OS X modifier key handling bug
+
Fixed handling of ISO_Level3_Shift on X11
+
+
+Jan-04-2005 - Synergy 1.2.1 released
+
+Made following changes:
+
+
Fixed major OS X keyboard handling bug
+
Fixed some minor documentation bugs
+
+
+Dec-30-2004 - Synergy 1.2.0 released
+
+Made following changes:
+
+
Improved support for moving laptops between networks (Brent Priddy)
+
Added ISO_Level3_Shift support on X windows
+
Now doing PageUp/PageDown if no mouse wheel on X windows (Tom Chadwick)
+
Fixed handling of number pad number keys on Windows 95/98/Me
+
Fixed handling of non-existant 4th and 5th mouse buttons on Windows
+
Added support for Unicode keyboard layouts on OS X
+
Fixed memory leak on OS X
+
Added OS X autostart documentation (Tor Slettnes)
+
+
+Nov-12-2004 - Synergy 1.1.10 released
+
+Made following changes:
+
+
Fixed race in condition variable wrapper; caused synergy to hang randomly
+
Fixed modifier key and caps-lock handling on OSX
+
System info log message now filtered like all other messages
+
+
+Nov-07-2004 - Synergy 1.1.9 released
+
+Made following changes:
+
+
Fixed compiler error on gcc 3.4 and later
+
Worked around minor gcc -O3 compiler bug
+
Now logging system info at startup
+
Config file errors now logged as errors rather than debug warnings
+
Added half-duplex scroll lock option
+
Fixed tracking of half-duplex toggle key state
+
Now accepting screen names ending in dot (.) for OS X convenience
+
OS X key mapping now loaded from system resources rather than hard coded
+
Fixed multimonitor OS X pimary screen bug; multimon OS X should now work
+
Added experimental workaround for laggy mouse when running linux -> OS X
+
Fixed bug in win32 installer packaging
+
Fixed unrequested continuous mouse wheel scrolling on win32
+
Added win32 GUI to set server address to listen on
+
Fixed resource leak on win32
+
Fixed screensaver detection on windows 2000 and XP
+
Fixed flickering mouse on multimon windows NT/2000/XP
+
Fixed quiting when powerdvd stops playing (may fix other situations, too)
+
Added tray icon menu item to force clients to reconnect
+
Fixed handling of number pad keys with num-lock off on win32
+
Fixed shift key not working when a console windows has focus on win32 server
+
Improved configure of Xinerama and DPMS
+
Improved portability (removed recursive mutexes and _*_SOURCE defines)
+
Now handling DPMS headers without prototypes
+
Fixed dead key and AltGr+shift handling on X11
+
Fixed use of freed memory on unix
+
Fixed AltGr mapping to Ctrl and not Ctrl+Alt on X11 without Alt_R mapped
+
Worked around win32 command prompt stealing shift key events
+
Fixed handling of pause key on win32
+
Fixed handling of backslash on win32 internation keyboard mapping
+
Fixed handling of ctrl and alt keys on NT/2k/XP
+
Fixed XCode project (removed cross-compile)
+
Worked around select() bug in OS X
+
Worked around bug in ifstream on OS X
+
Fixed handling of modifier keys on OS X synergy server
+
Fixed handling of space key on OS X synergy server
+
Fixed handling of key autorepeat on OS X server
+
Fixed mouse wheel drift on OS X client
+
Reorganized documentation and converted to HTML
+
+
+Jun-13-2004 - Synergy 1.1.7 released
+
+Made following changes:
+
+
Added OS X precompiled header file forgotten in last build
+
Fixed bug in fix for 'unexpected async reply' on X11
+
Removed dependency on "browser" service on win32
+
Fixed assertion failure when connection fails immediately
+
Fixed failure to connect on AIX
+
Fixed error in conversion from multibyte to wide characters
+
Maybe fixed win32 screen saver detection
+
+
+May-26-2004 - Synergy 1.1.6 released
+
+Made following changes:
+
+
Added preliminary Mac OS X support (client and server)
+
Fixed ctrl+alt+del emulation on win32
+
Fixed ctrl+alt+del on win32 server acting on both client and server
+
Fixed handling of screen resolution changes on win32
+
Fixed 'unexpected async reply' on X11
+
Added dependency to win32 service to avoid startup race condition
+
Fixed reference count bug
+
Keyboard input focus now restored on X11 (fixes loss of input in some games)
+
+
+The OS X port does not yet support:
+
+
HTML and bitmap clipboard data
+
Screen saver synchronization
+
Non-US English keyboards
+
+
+May-05-2004 - Synergy 1.1.5 released
+
+Made following changes:
+
+
No longer switching screens when a mouse button is down
+
Worked around win32 mouse hook bug, fixing switch on double tap
+
Added support for HTML and bitmap (image/bmp) clipboard data
+
Physical mouse no longer necessary on win32 secondary screens to see cursor
+
Added experimental relative mouse moves on secondary screen option
+
Fixed win32 lock up when closing server with clients still connected
+
Fixed bug in handling duplicate connections
+
Fixed pthread mutex initialization
+
Made synergy dependent on NetBT on win32 (for service startup order)
+
Automake fixes; now mostly works on darwin and MinGW
+
Fixed builds on Solaris 9, FreeBSD, and OpenBSD
+
Partial support for MSYS/MinGW builds (NOT COMPLETE)
+
Partial merge of OS X port (NOT COMPLETE)
+
+
+Mar-31-2004 - Synergy 1.1.4 released
+
+Made following changes:
+
+
Fixed lookup of hosts by name of win32
+
Reverted tray icon code to 1.0.15 version; seems to fix the bugs
+
Fixed crash when caps, num, or scroll lock not in key map on X11
+
Fixed double tap and wait to switch features
+
+
+Mar-28-2004 - Synergy 1.1.3 released
+
+Made following changes:
+
+
Major code refactoring; reduced use of threads, added event queue
+
Removed unused HTTP support code
+
No longer interfering with mouse when scroll lock is toggled on
+
Fixed minor mispositioning of mouse on win32
+
Unix portability fixes
+
Added support for power management
+
Improved keyboard handling and bug fixes
+
Fixed dead key handling
+
+
+Note: the tray icon on windows is known to not work correctly when
+running the synergy server on Windows 95/95/Me.
+
+Aug-24-2003 - Synergy 1.0.14 released
+
+Made following changes:
+
+
Fixed bugs in setting win32 process/thread priority
+
Fixed resource leak in opening win32 system log
+
Fixed win32 launcher not getting non-default advanced options
+
Synergy log copied to clipboard now transferred to other screens
+
Hack to work around lesstif clipboard removed (fixes pasting on X)
+
+
+Jul-20-2003 - Synergy 1.0.12 released
+
+Made following changes:
+
+This release finally completes support for non-ASCII characters,
+fully supporting most (all?) European keyboard layouts including
+dead key composition. This release includes changes from several
+experimental versions (1.0.9, 1.0.11, 1.1.0, 1.1.1, 1.1.2, and
+1.1.3).
+
+Made following changes:
+
+
Added non-ASCII support to win32 and X11
+
Added dead key support to win32 and X11
+
Fixed AltGr handling
+
Added ctrl+alt+del simulation using ctrl+alt+pause
+
Fixed loss of key event when user releases ctrl+alt+del
+
Fixed incorrect synthesis of pointer-keys event on X11
+
Fixed Xinerama support
+
Made some clipboard fixes on win32 and X11
+
Add tray icon menu item to copy log to clipboard
+
Fixed mouse warping on unconnected client
+
Stopped unconnected client from filling up event logs
+
+
+May-10-2003 - Synergy 1.0.8 released
+
+Made following changes:
+
+
+
Fixed hook forwarding (fixing interaction with objectbar)
+
Fixed "Windows" key handling and added support Win+E, Win+F, etc
+
Added win 95/98/me support for Alt+Tab, Alt+Esc, Ctrl+Esc
+
Fixed scroll lock locking to server screen
+
Fixed screen flashing on X11 and Windows
+
Fixed compile problem on 64 bit systems
+
Fixed Xinerama support
+
Now allowing screen names that include underscores
+
Improved non-ASCII key handling on Windows
+
Fixed lagginess
+
Fixed failure to capture all mouse input on Windows
+
Fixed auto-repeat bugs on X11
+
Added option to disable screen saver synchronization
+
Added support for 4th and 5th mouse buttons on Windows
+
Added support for "Internet" and "Multimedia" keys
+
Fixed jumping from client to itself (mouse wrapping)
+
+
+Apr-26-2003 - Added roadmap
+
+There's now a roadmap for Synergy
+describing the plans for further development.
+
+Apr-26-2003 - Added Paypal donation page
+
+There's now a donate button for those
+who'd like to make a monetary contribution to the further
+development of Synergy.
+
+Apr-26-2003 - Development update
+
+Synergy 1.0.8 will include fixes for the following problems.
+These are already fixed and some are in development version 1.0.7.
+
+
+
Mouse events at edge of screen are stolen
+
Windows key doesn't work on clients
+
Alt+[Shift+]Tab, Alt+[Shift+]Esc, Ctrl+Esc don't work on Win 95/98/Me
+
Scroll lock doesn't lock to Windows server screen
+
Screen flashes every 5 seconds on some X11 systems
+
Synergy doesn't work properly with Xinerama
+
Screen names with underscores are not allowed
+
+
+Synergy 1.0.8 will probably include fixes for these problems:
+
+
+
AltGr/Mode_switch doesn't work
+
Non-ASCII keys aren't supported
+
Synergy performs badly on a busy Windows system
+
Unexpected key repeats on X11 clients
+
+
+Synergy 1.0.8 should be available in the first half of May.
+
+Mar-27-2003 - Synergy 1.0.6 released
+
+Made following changes:
+
+
+
Added tray icon on win32
+
Fixed multi-monitor support on win32
+
Fixed win32 screen saver detection on NT/2k/XP
+
Added per-screen options to remap modifier keys
+
Added global options for restricting screen jumping
+
Added global option for detecting unresponsive clients
+
Added more logging for why screen jump won't happen
+
Fixed problem sending the CLIPBOARD to motif/lesstif apps
+
Win32 launcher now remembers non-config-file state
+
+
+In addition, the version number scheme has been changed. Given a
+version number X.Y.Z, release versions will always have Y and Z
+even while development versions will have Y and Z odd.
+
+Mar-27-2003 - Synergy featured in Linux Journal.
+
+The April 2003 issue of Linux Journal includes an article on Synergy.
+Written by Chris Schoeneman, it describes configuring synergy between
+two linux systems.
+
+Mar-27-2003 - Contributions to Synergy.
+
+Many thanks to Girard Thibaut for providing a version of the win32
+launch dialog translated into French. I hope to integrate these
+changes into future releases.
+
+Thanks also to "wrhodes" who provided source files for
+building an InstallShield installer for Synergy. They'll be
+integrated into an upcoming release.
+
+Feb-18-2003 - Synergy 1.0.3 released
+
+Made following changes:
+
+
+
Added support for X11 keymaps with only uppercase letters
+
Reduced frequency of large cursor jumps when leaving win32 server
+
Changed cursor motion on win32 multimon to relative moves only
+
+
+Jan-25-2003 - Synergy 1.0.2 released
+
+Made following changes:
+
+
+
Fixed out-of-bounds array lookup in the BSD and Windows network code
+
Added ability to set screen options from Windows launch dialog
+
+
+Jan-22-2003 - Synergy 1.0.1 released
+
+Made following changes:
+
+
+
Fixed running as a service on Windows NT family
+
+
+Jan-20-2003 - Synergy 1.0.0 released
+
+Made following changes:
+
+
+
Refactored to centralize platform dependent code
+
Added support for mouse wheel on Windows NT (SP3 and up)
+
Portability improvements
+
Added more documentation
+
Fixes for working with xscreensaver
+
Fixes for circular screen links
+
+
+This release has been tested on Linux and Windows. It builds and
+is believed to run on Solaris and FreeBSD. It is believed to
+build and run on Irix and AIX. It builds but does not work on
+MacOS X.
+
+Dec-25-2002 - Synergy 0.9.14 released
+
+Made following changes:
+
+
+
Fixed solaris compile problems (untested)
+
Fixed irix compile problems (untested)
+
Fixed windows client not reconnecting when server dies bug
+
Fixed loss of ctrl+alt from windows server to non-windows clients
+
Fixed handling of password protected windows client screen saver
+
Now handling any number of pointer buttons on X11
+
Toggle key states now restored when leaving clients
+
Added support for per-screen config options
+
Added config options for half-duplex toggle keys on X11
+
Enabled class diagrams in doxygen documentation
+
+
+Nov-05-2002 - Synergy 0.9.13 released
+
+Made following changes:
+
+
+
Fixed solaris compile problems (untested)
+
Fixed MacOS X compile problems (semi-functional)
+
Fixed gcc-3.2 compile problems
+
Fixed some thread startup and shutdown bugs
+
Server now quits if bind() fails with an error other than in use
+
Fixed bug in moving mouse on Win98 without multiple monitors
+
Fixed bug in handling TCP socket errors on read and write
+
Fixed spurious screen saver activation on X11
+
Unix platforms can now read Win32 configuration files
+
Minor error reporting fixes
+
+
+Sep-14-2002 - Synergy 0.9.12 released
+
+Made following changes:
+
+
+
Win32 was not reporting log messages properly when run from synergy.exe
+
Network error messages weren't reporting useful information
+
Synergy won't build on gcc 3.2; added workaround for known problem
+
X11 wasn't handling some keys/key combinations correctly
+
Added option to change logging level when testing from synergy.exe
+
+
+Sep-04-2002 - Synergy 0.9.11 released
+
+Fixed following bugs:
+
+
+
Worked around missing SendInput() on windows 95/NT 4 prior to SP3
+
Fixed keyboard mapping on X11 synergy client
+
+
+Sep-02-2002 - Synergy 0.9.10 released
+
+Fixed following bugs:
+
+
+
The Pause/Break and keypad Enter buttons were not working correctly on windows
+
Configuration options were being lost on windows after a reboot
+
Added support for AltGr/ModeSwitch keys
+
Added support for auto-start on windows when not administrator
+
Improved autoconf
+
Added workaround for lack of sstream header on g++ 2.95.
+
+
+Aug-18-2002 - Synergy 0.9.9 released
+
+Fixed three bugs:
+
+
+
The PrintScrn button was not working correctly on windows
+
The Win32 server could hang when a client disconnected
+
Using the mouse wheel could hang the X server
+
+
+Aug-11-2002 - Synergy 0.9.8 released
+
+Supports any number of clients under Linux or Windows 95 or NT4
+or later. Includes mouse and keyboard sharing, clipboard
+synchronization and screen saver synchronization. Supports ASCII
+keystrokes, 5 button mouse with wheel, and Unicode text clipboard
+format.
+
+This page describes the planned development of Synergy. There are
+no dates or deadlines. Instead, you'll find the features to come
+and the rough order they'll arrive.
+
+
Short term
+
+Synergy should work seamlessly. When it works correctly, it works
+transparently so you don't even think about it. When it breaks,
+you're forced out of the illusion of a unified desktop. The first
+priority is fixing those bugs that break the illusion.
+
+Some of these bugs are pretty minor and some people would rather
+have new features first. But I'd rather fix the current
+foundation before building on it. That's not to say features
+won't get added until after bug fixes; sometimes it's just too
+tempting to code up a feature.
+
+The highest priority feature is currently splitting synergy into
+front-ends and a back-end. The back-end does the real work. The
+front-ends are console, GUI, or background applications that
+communicate with the back-end, either controlling it or receiving
+notifications from it.
+
+On win32, there'd be a front-end for the tray icon and a dialog to
+start, stop, and control the back-end. OS X and X11 would have
+similar front-ends. Splitting out the front-end has the added
+benefit on X11 of keeping the back-end totally independent of
+choice of GUI toolkit (KDE, Gnome, etc.)
+
+One can also imagine a front-end that does nothing but put monitors
+into power-saving mode when the cursor is not on them. If you have
+one monitor auto-senses two inputs, this would automatically switch
+the display when you move the cursor to one screen or another.
+
+
Medium term
+
+Some features fit well into Synergy's current design and may simply
+enhance it's current capabilities.
+
+
+
Configurable hot key to pop up a screen switch menu
+
Configure screen saver synchronization on or off
+
Graphical interface configuration and control on all platforms
+
Graphical status feedback on all platforms
+
More supported clipboard formats (particularly rich text)
+
+
+A popup menu would be new for Synergy, which currently doesn't have
+to do any drawing. That opens up many possibilities. Ideally,
+front-ends request hot keys from the back-end and then tell the back
+end what to do when they're invoked. This keeps the back-end
+independent of the user interface.
+
+
Long term
+
+Two features stand out as long term goals:
+
+
+
Support N computers on
+M monitors
+
Drag and drop across computers
+
+
+The first feature means sharing a monitor or monitors the way the
+keyboard and mouse are shared. With this, Synergy would be a full
+KVM solution. Not only would it support a few computers sharing
+one screen (still using the mouse to roll from one screen to
+another), but it should also support dozens of computers to provide
+a solution for server farm administrators. In this capacity, it
+may need to support text (as opposed to bitmap graphics) screens.
+
+The second feature would enhance the unified desktop illusion. It
+would make it possible to drag a file and possibly other objects
+to another screen. The object would be copied (or moved). I expect
+this to be a very tricky feature.
+
+Synergy lets you use one keyboard and mouse across multiple computers.
+To do so it requires that all the computers are connected to each other
+via TCP/IP networking. Most systems come with this installed.
+
+
Step 1 - Choose a server
+
+The first step is to pick which keyboard and mouse you want to share.
+The computer with that keyboard and mouse is called the "primary
+screen" and it runs the synergy server. All of the other computers
+are "secondary screens" and run the synergy client.
+
+
Step 2 - Install the software
+
+Second, you install the software. Choose the appropriate package
+and install it. For example, on Windows you would run
+SynergyInstaller. You must install the
+software on all the computers that will share the mouse and keyboard
+(clients and server). On OS X you'll just have a folder with some
+documentation and two programs. You can put this folder anywhere.
+
+
Step 3 - Configure and start the server
+
+Next you configure the server. You'll tell synergy the name of
+the primary and secondary screens, which screens are next to which,
+and choose desired options. On Windows there's a dialog box for
+setting the configuration. On other systems you'll create a simple
+text file.
+
+
+Note that when you tell synergy that screen A
+is to the left of screen B this does not
+imply that B is to the right of
+A. You must explicitly indicate both
+relations. If you don't do both then when you're running synergy you'll
+find you're unable to leave one of the screens.
+
+Windows
+On Windows run synergy by double clicking on the
+synergy file. This brings up a dialog.
+Configure the server:
+
+
Click the Share this computer's keyboard and mouse (server) radio button
+
Click the Screens & Links Configure... button
+
Click the + button to add the server to the
+ Screens list
+
+
Enter the name of server (the computer's name is the recommended name)
+
Optionally enter other names the server is known by
+
Click OK
+
+
Use the + button to add your other computers
+
+
Using a computer's name as its screen name is recommended
+
Choose desired screen options on the Add Screen dialog
+
+
Use the controls under Links to link screens together
+
+
Click (once) on the server's name in the Screens list
+
Choose the screen to the left of the server; use ---
+ if there is no screen to the left of the server
+
Choose the screens to the right, above and below the server
+
Repeat the above steps for all the other screens
+
+
Click OK to close the Screens & Links dialog
+
Use Options... to set desired options
+
If the server's screen name is not the server's computer name:
+
+
Click Advanced...
+
Enter the server's screen name next to
+ Screen Name
+
Click OK
+
+
+
+Now click Test. The server will start and
+you'll see a console window with log messages telling you about synergy's
+progress. If an error occurs you'll get one or more dialog boxes telling
+you what the errors are; read the errors to determine the problem then
+correct them and try Test again. See Step 5
+for typical errors.
+
+Unix or Mac OS X
+Create a text file named synergy.conf with the
+following:
+
+ section: screens
+ screen1:
+ screen2:
+ end
+ section: links
+ screen1:
+ right = screen2
+ screen2:
+ left = screen1
+ end
+
+Replace each occurrence of screen1 with the host name
+of the primary screen computer (as reported by the
+hostname program) and screen2
+with the host name of a secondary screen computer. In the above example,
+screen2 is to the right of
+screen1 and screen1 is to the
+left of screen2. If necessary you should replace
+right and left with
+left, right,
+up, or down. If you
+have more than two computers you can add those too: add each computer's host
+name in the screens section and add the
+appropriate links. See the configuration
+guide for more configuration possibilities.
+
+Now start the server. Normally synergy wants to run "in the background."
+It detaches from the terminal and doesn't have a visible window, effectively
+disappearing from view. Until you're sure your configuration works, you
+should start synergy "in the foreground" using the -f
+command line option.
+
+On unix type the command below in a shell. If synergys is not in your
+PATH then use the full pathname.
+
+ synergys -f --config synergy.conf
+
+On OS X open Terminal in the Utilities folder in the Applications folder.
+Drag the synergys program from the synergy folder onto the Terminal window.
+The path to the synergys program will appear. Add the following to the
+same line, type a space at the end of the line but don't press enter:
+
+ -f --config
+
+Now drag the synergy.conf file onto the Terminal window and press enter.
+Check the reported messages for errors. Use ctrl+c to stop synergy if
+it didn't stop automatically, correct any problems, and start it again.
+
+
Step 4 - Start the clients
+
+Next you start the client on each computer that will share the server's
+keyboard and mouse.
+
+Windows
+On Windows run synergy by double clicking on the
+synergy file. This brings up a dialog.
+Configure the client:
+
+
Click the Use another computer's shared keyboard and mouse (client) radio button
+
Enter the server's computer name next to Other Computer's Host Name
+
+
This is not the server's screen name, unless you made that the
+ server's host name as recommended
+
+
If the client's screen name is not the client's computer name:
+
+
Click Advanced...
+
Enter the client's screen name next to Screen Name
+
Click OK
+
+
+
+Now click Test.
+
+Unix or Mac OS X
+To start a client on unix, enter the following:
+
+ synergyc -f server-host-name
+
+where server-host-name is replaced by the host
+name of the computer running the synergy server. If synergyc is not in
+your PATH then use the full pathname.
+
+On OS X open Terminal in the Utilities folder in the Applications folder.
+Drag the synergyc program from the synergy folder onto the Terminal window.
+The path to the synergys program will appear. Add the following to the
+same line and press enter:
+
+ -f server-host-name
+
+
+When you added the client to the server's configuration you chose a
+name for the client. If that name was not client's host name then
+you must tell the client the name you used. Instead of the above
+command use this instead:
+
+ synergyc -f --name nameserver-host-name
+
+where name is the name for the client in
+the server's configuration. (On OS X drag the synergyc program to the
+Terminal window rather than typing synergyc.)
+
+
Step 5 - Test
+
+Clients should immediately report a successful connection or one or
+more error messages. Some typical problems and possible solutions are
+below. See the troubleshooting and the
+FAQ pages for more help.
+
+
failed to open screen (X11 only)
+
+ Check permission to open the X display;
+ check that the DISPLAY environment variable is set
+ use the --display command line option.
+
+
address already in use
+
+ Another program (maybe another copy of synergy) is using the synergy port;
+ stop the other program or choose a different port in the
+ Advanced... dialog. If you change the port
+ you must make the same change on all of the clients, too.
+
+
connection forcefully rejected
+
+ The synergy client successfully contacted the server but synergy wasn't
+ running or it's running on a different port. You may also see this if
+ there's a firewall blocking the host or port. Make sure synergy is
+ running on the server and check for a firewall.
+
+
already connected
+
+ Check that the synergy client isn't already running.
+
+
refused client
+
+ Add the client to the server's configuration file.
+
+
connection timed out
+
+ Check that server-host-name is correct.
+ Check that you don't have a firewall blocking the server or synergy port.
+
+
connection failed
+
+ Check that server-host-name is correct.
+
+
+If you get the error "Xlib: No protocol specified"
+you're probably running synergy as root while logged in as another user.
+X11 may prevent this for security reasons. Either run synergy as the same
+user that's logged in or (not recommended) use
+"xhost +" to allow anyone to connect
+to the display.
+
+When successful you should be able to move the mouse off the appropriate
+edges of your server's screen and have it appear on a client screen.
+Try to move the mouse to each screen and check all the configured links.
+Check the mouse buttons and wheel and try the keyboard on each client.
+You can also cut-and-paste text, HTML, and images across computers (HTML
+and images are not supported on OS X yet).
+
+
Step 6 - Run
+
+Once everything works correctly, stop all the clients then the server.
+Then start the server with the Start button
+on Windows and without the -f option on Unix
+and Mac OS X. Finally start the clients similarly. On Windows before
+clicking Start you may want to set the
+Logging Level to
+Warning so the logging window doesn't pop
+up (because you currently can't close it, just minimize it).
+
+You can also configure synergy to start automatically when your computer
+starts or when you log in. See the autostart
+guide for more information.
+
+
Command Line Options Guide
+
+Common Command Line Options
+The following options are supported by synergys
+and synergyc.
+
+
+
-d,
+
--debug level
+
use debugging level level
+
+
+
+
--daemon
+
run as a daemon (Unix) or background (Windows)
+
+
+
-f,
+
--no-daemon
+
run in the foreground
+
+
+
+
--display display
+
connect to X server at display (X11 only)
+
+
+
-n,
+
--name name
+
use name instead of the hostname
+
+
+
+
--restart
+
automatically restart on failures
+
+
+
-1,
+
--no-restart
+
do not restart on failure
+
+
+
-h,
+
--help
+
print help and exit
+
+
+
+
--version
+
print version information and exit
+
+
+
+Debug levels are from highest to lowest: FATAL,
+ERROR, WARNING,
+NOTE, INFO,
+DEBUG, DEBUG1, and
+DEBUG2. Only messages at or above the given
+level are logged. Messages are logged to a terminal window when
+running in the foreground. Unix logs messages to syslog when running
+as a daemon. The Windows NT family logs messages to the event log
+when running as a service. The Windows 95 family shows FATAL log
+messages in a message box and others in a terminal window when running
+as a service.
+
+The --name option lets the client or server
+use a name other than its hostname for its screen. This name is used
+when checking the configuration.
+
+Neither the client nor server will automatically restart if an error
+occurs that is sure to happen every time. For example, the server
+will exit immediately if it can't find itself in the configuration.
+On X11 both the client and server will also terminate if the
+connection to the X server is lost (usually because it died).
+
+hostname is a hostname or IP address of a network
+interface on the server system (e.g. somehost
+or 192.168.1.100). port
+is a port number from 1 to 65535. hostname defaults to
+the system's hostname and port defaults to 24800.
+
+Client Command Line Options
+
+
+ synergyc [options] address[:port]
+
+address is the hostname or IP address of
+the server and port is the optional network
+port on the server to connect to. The client accepts the
+common options.
+
+
+
+
diff --git a/doc/security.html b/doc/security.html
new file mode 100644
index 00000000..c8013c27
--- /dev/null
+++ b/doc/security.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+ Synergy Network Security Guide
+
+
+
+
Authentication and Encryption
+Synergy does not do any authentication or encryption. Any computer
+can connect to the synergy server if it provides a screen name known
+to the server, and all data is transferred between the server and the
+clients unencrypted which means that anyone can, say, extract the
+key presses used to type a password. Therefore, synergy should not
+be used on untrusted networks.
+
+However, there are tools that can add authentication and encryption
+to synergy without modifying either those tools or synergy. One
+such tool is SSH (which stands for secure shell). A free implementation
+of SSH is called OpenSSH and runs
+on Linux, many Unixes, and Windows (in combination with
+Cygwin).
+
+
Configuring the Server
+Install the OpenSSH server on the same computer as the synergy server.
+Configure the OpenSSH server as usual (synergy doesn't demand any
+special options in OpenSSH) and start it. Start the synergy server as
+usual; the synergy server requires no special options to work with
+OpenSSH.
+
+
Configuring the Clients
+Install the OpenSSH client on each synergy client computer. Then, on
+each client, start the OpenSSH client using port forwarding:
+
+The server-hostname is the name or address
+of the computer with the OpenSSH and synergy servers.
+The 24800 is the default network port used by synergy; if you use
+a different port then replace both instances of 24800 with the port
+number that you use. Finally, start the synergy client normally
+except use localhost as the server host
+name. For example:
+
+ Be aware that not all keystrokes can be handled by synergy. In
+ particular, ctrl+alt+del is not handled. However, synergy can
+ convert ctrl+alt+pause into ctrl+alt+del on the client side.
+ (Synergy must be configured to autostart when the computer starts
+ on the client for this to work on the Windows NT family.) Some
+ non-standard keys may not work, especially "multimedia" buttons,
+ though several are correctly handled.
+
+
+ A screen can be its own neighbor. That allows a screen to "wrap".
+ For example, if a configuration linked the left and right sides of
+ a screen to itself then moving off the left of the screen would put
+ the mouse at the right of the screen and vice versa.
+
+
+ You cannot switch screens when the Scroll Lock is toggled on. Use
+ this to prevent unintentional switching. You can configure other
+ hot keys to do this instead; see
+ lockCursorToScreen.
+
+
+ Turn off mouse driven virtual desktop switching on X windows. It
+ will interfere with synergy. Use keyboard shortcuts instead.
+
+
+ Synergy's screen saver synchronization works best with xscreensaver
+ under X windows. Synergy works better with xscreensaver if it is
+ using one of the screen saver extensions. Prior to xscreensaver 4.0
+ you can use -mit-extension,
+ -sgi-extension, or
+ -xidle-extension
+ command line options to enable an extension (assuming your server has
+ the extension). Starting with 4.0 you must enable the corresponding
+ option in your .xscreensaver file.
+
+
+ Synergy automatically converts newlines in clipboard text (Unix
+ expects \n to end each line while Windows
+ expects \r\n).
+
+
+ Clients can be started and stopped at any time. When a screen is
+ not connected, the mouse will jump over that screen as if the mouse
+ had moved all the way across it and jumped to the next screen.
+
+
+ A client's keyboard and mouse are fully functional while synergy is
+ running. You can use them in case synergy locks up.
+
+
+ Strong authentication and encryption is available by using SSH. See
+ the security guide for more information.
+ Synergy does not otherwise provide secure communications and it should
+ not be used on or over untrusted networks.
+
+
+ Synergy doesn't work if a 16-bit Windows application has the focus
+ on Windows 95/98/Me. This is due to limitations of Windows. One
+ commonly used 16-bit application is the command prompt
+ (command.exe)
+ and this includes synergy's log window when running in test mode.
+
+
diff --git a/doc/todo.html b/doc/todo.html
new file mode 100644
index 00000000..02a12a07
--- /dev/null
+++ b/doc/todo.html
@@ -0,0 +1,70 @@
+
+
+
+
+ Synergy To Do List
+
+
+
Synergy To Do List
+
+This page describes the planned development of Synergy. There are
+no dates or deadlines. Instead, you'll find the features to come
+and the rough order they can be expected to arrive.
+
+
+
Short term
+
+Synergy should work seamlessly. When it works correctly, it works
+transparently so you don't even think about it. When it breaks,
+you're forced out of the illusion of a unified desktop. The first
+priority is fixing those bugs that break the illusion.
+
+
+Some of these bugs are pretty minor and some people would rather
+have new features first. But I'd rather fix the current
+foundation before building on it. That's not to say features
+won't get added until after bug fixes; sometimes it's just too
+tempting to code up a feature.
+
+
+
Medium term
+
+Some features fit well into Synergy's current design and may simply
+enhance it's current capabilities.
+
+
Configurable hot key screen switching
+
Configurable hot key to lock to a screen
+
Configurable hot key to pop up a screen switch menu
+
Configure screen saver synchronization on or off
+
Graphical interface configuration and control on all platforms
+
Graphical status feedback on all platforms
+
More supported clipboard formats (particularly rich text)
+
+
+
+
Long term
+
+Two features stand out as long term goals:
+
+
Support N computers on
+M monitors
+
Drag and drop across computers
+
+
+
+The first feature means sharing a monitor or monitors the way the
+keyboard and mouse are shared. With this, Synergy would be a full
+KVM solution. Not only would it support a few computers sharing
+one screen (still using the mouse to roll from one screen to
+another), but it should also support dozens of computers to provide
+a solution for server farm administrators. In this capacity, it
+may need to support text (as opposed to bitmap graphics) screens.
+
+
+The second feature would enhance the unified desktop illusion. It
+would make it possible to drag a file and possibly other objects
+to another screen. The object would be copied (or moved). I expect
+this to be a very tricky feature.
+
+There's an error in the configuration file. This error is always
+accompanied by another message describing the problem. Use that
+message and the configuration documentation
+to determine the fix.
+
+
Connection forcefully rejected
+
+The client was able to contact the server computer but the server was
+not listening for clients. Possible reasons are:
+
+
+
The client is using the wrong server
+
+Make sure the client is using the hostname or IP address of the computer
+running the synergy server.
+
+
Synergy isn't running on the server
+
+Make sure the synergy server is running on the server computer. Make
+sure the server is ready to accept connections. If another program is
+using synergy's port (24800 by default) then synergy can't start unless
+you specify a different port.
+
+
The client is using the wrong port
+
+Synergy uses port 24800 by default but you can specify a different port.
+If you do use a different port you must use that port on the server and
+all clients.
+
+
+
Connection timed out
+
+The most likely reasons for this are:
+
+
+
A firewall
+
+A firewall is a program or device that deliberately blocks network
+connections for security reasons. Typically, they'll silently drop
+packets they don't want rather than sending a rejection to the sender.
+This makes it more difficult for intruders to break in.
+
+When synergy traffic hits a firewall and gets dropped, eventually the
+synergy client will give up waiting for a response and time out. To
+allow synergy traffic through first find all the firewalls on the
+network between and on the synergy client and server computers.
+
+A firewall on the server or any network device between the server and
+any client should allow packets to TCP port 24800. (Port 24800 is the
+default; use whichever port you've selected.) You'll have to consult
+the manual for your operating system, device, or firewall software to
+find out how to do this.
+
+Usually you'll won't need to adjust a firewall on client machines.
+That's because firewalls normally allow incoming traffic on any port
+they've initiated a connection on. The reasoning is, of course, if
+you started a conversation you probably want to hear the reply.
+
+
The network is down or busy
+
+Correct the network problem and try again. You might try
+ping to see if the two computers can see
+each other on the network.
+
+
The server is frozen
+
+If the synergy server is running but locked up or very busy then the
+client may get this message. If the server is locked up then you'll
+probably have to restart it. If it's just very busy then the client
+should successfully connect automatically once the server settles down.
+
+
+
Cannot listen for clients
+
+Synergy tried to start listening for clients but the network port is
+unavailable for some reason. Typical reasons are:
+
+
+
No network devices
+
+You must have a TCP/IP network device installed and enabled to use
+synergy.
+
+
A synergy server is already running
+
+Check that a synergy server isn't already running.
+
+
Another program is using synergy's port
+
+Only one program at a time can listen for connections on a given port.
+If the specific error is that the address is already in use and you've
+ruled out the other causes, then it's likely another program is already
+using synergy's port. By default synergy uses port 24800. Try having
+synergy use a different port number, like 24801 or 24900. Note that
+the server and all clients must use the same port number. Alternatively,
+find the other program and stop it or have it use another port.
+
+
+
Unknown screen name "XXX"
+
+This error can be reported when reading the configuration; see
+cannot read configuration. If the configuration
+was read successfully and you get this error then it means that the
+server's screen is not in the configuration. All screens must be listed
+in the configuration.
+
+A common reason for this is when you haven't used the system's hostname
+as its screen name. By default, synergy uses the hostname as the screen
+name. If you used a different screen name in the configuration then you
+must tell synergy what that name is. Let's say the hostname is
+frederick but the configuration defines a screen
+named fred. Then you must tell the server
+that its screen name is fred by using the
+--name fred command line option or setting
+the screen name in the advanced options dialog to
+fred.
+
+Alternatively, you can specify one name as an alias of another. See
+the configuration documentation
+for details.
+
+Another common reason for this is a mismatch between what you think the
+hostname is and what synergy thinks it is. Typically this is a problem
+with fully qualified domain names (FQDN). Perhaps you think your system
+is named fred but synergy thinks it's
+fred.nowhere.com or
+fred.local. You can use either solution above
+to fix this.
+
+
Server refused client with name "XXX"
+ A client with name "XXX" is not in the map
+
+The client is using a screen name not in the server's configuration.
+This is essentially the same problem as Unknown
+screen name "XXX" and has the same solutions: specify another
+screen name or add an alias.
+
+
Server already has a connected client with name "XXX"
+ A client with name "XXX" is already connected
+
+This happens when:
+
+
+
Two clients try use the same screen name
+
+Each client must have a unique screen name. Configure at least one
+client to use a different screen name.
+
+
One client reconnects without cleanly disconnecting
+
+It's possible for a client to disconnect without the server knowing,
+usually by being disconnected from the network or possibly by going
+to sleep or even crashing. The server is left thinking the client is
+still connected so when the client reconnects the server will think
+this is a different client using the same name. Synergy will usually
+detect and correct this problem within a few seconds. If it doesn't
+then restart the server.
+
+
+
Server has incompatible version
+
+You're using different versions of synergy on the client and server.
+You should use the same version on all systems.
+
+
The cursor goes to secondary screen but won't come back
+
+
+
+
diff --git a/examples/synergy.conf b/examples/synergy.conf
new file mode 100644
index 00000000..2586dfaf
--- /dev/null
+++ b/examples/synergy.conf
@@ -0,0 +1,37 @@
+# sample synergy configuration file
+#
+# comments begin with the # character and continue to the end of
+# line. comments may appear anywhere the syntax permits.
+
+section: screens
+ # three hosts named: moe, larry, and curly
+ moe:
+ larry:
+ curly:
+end
+
+section: links
+ # larry is to the right of moe and curly is above moe
+ moe:
+ right = larry
+ up = curly
+
+ # moe is to the left of larry and curly is above larry.
+ # note that curly is above both moe and larry and moe
+ # and larry have a symmetric connection (they're in
+ # opposite directions of each other).
+ larry:
+ left = moe
+ up = curly
+
+ # larry is below curly. if you move up from moe and then
+ # down, you'll end up on larry.
+ curly:
+ down = larry
+end
+
+section: aliases
+ # curly is also known as shemp
+ curly:
+ shemp
+end
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 00000000..2a57133c
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,34 @@
+# synergy -- mouse and keyboard sharing utility
+# 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+## Process this file with automake to produce Makefile.in
+NULL =
+
+SUBDIRS = \
+ common \
+ arch \
+ base \
+ mt \
+ io \
+ net \
+ synergy \
+ platform \
+ client \
+ server \
+ $(NULL)
+
+EXTRA_DIST = \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ $(NULL)
diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp
new file mode 100644
index 00000000..80c613ab
--- /dev/null
+++ b/lib/arch/CArch.cpp
@@ -0,0 +1,639 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "common.h"
+#include "CArch.h"
+
+#undef ARCH_CONSOLE
+#undef ARCH_DAEMON
+#undef ARCH_FILE
+#undef ARCH_LOG
+#undef ARCH_MULTITHREAD
+#undef ARCH_NETWORK
+#undef ARCH_SLEEP
+#undef ARCH_STRING
+#undef ARCH_SYSTEM
+#undef ARCH_TASKBAR
+#undef ARCH_TIME
+
+// include appropriate architecture implementation
+#if SYSAPI_WIN32
+# include "CArchConsoleWindows.h"
+# include "CArchDaemonWindows.h"
+# include "CArchFileWindows.h"
+# include "CArchLogWindows.h"
+# include "CArchMiscWindows.h"
+# include "CArchMultithreadWindows.h"
+# include "CArchNetworkWinsock.h"
+# include "CArchSleepWindows.h"
+# include "CArchStringWindows.h"
+# include "CArchSystemWindows.h"
+# include "CArchTaskBarWindows.h"
+# include "CArchTimeWindows.h"
+#elif SYSAPI_UNIX
+# include "CArchConsoleUnix.h"
+# include "CArchDaemonUnix.h"
+# include "CArchFileUnix.h"
+# include "CArchLogUnix.h"
+# if HAVE_PTHREAD
+# include "CArchMultithreadPosix.h"
+# endif
+# include "CArchNetworkBSD.h"
+# include "CArchSleepUnix.h"
+# include "CArchStringUnix.h"
+# include "CArchSystemUnix.h"
+# include "CArchTaskBarXWindows.h"
+# include "CArchTimeUnix.h"
+#endif
+
+#if !defined(ARCH_CONSOLE)
+# error unsupported platform for console
+#endif
+
+#if !defined(ARCH_DAEMON)
+# error unsupported platform for daemon
+#endif
+
+#if !defined(ARCH_FILE)
+# error unsupported platform for file
+#endif
+
+#if !defined(ARCH_LOG)
+# error unsupported platform for logging
+#endif
+
+#if !defined(ARCH_MULTITHREAD)
+# error unsupported platform for multithreading
+#endif
+
+#if !defined(ARCH_NETWORK)
+# error unsupported platform for network
+#endif
+
+#if !defined(ARCH_SLEEP)
+# error unsupported platform for sleep
+#endif
+
+#if !defined(ARCH_STRING)
+# error unsupported platform for string
+#endif
+
+#if !defined(ARCH_SYSTEM)
+# error unsupported platform for system
+#endif
+
+#if !defined(ARCH_TASKBAR)
+# error unsupported platform for taskbar
+#endif
+
+#if !defined(ARCH_TIME)
+# error unsupported platform for time
+#endif
+
+//
+// CArch
+//
+
+CArch* CArch::s_instance = NULL;
+
+CArch::CArch(ARCH_ARGS* args)
+{
+ // only once instance of CArch
+ assert(s_instance == NULL);
+ s_instance = this;
+
+ // create architecture implementation objects
+ m_mt = new ARCH_MULTITHREAD;
+ m_system = new ARCH_SYSTEM;
+ m_file = new ARCH_FILE;
+ m_log = new ARCH_LOG;
+ m_net = new ARCH_NETWORK;
+ m_sleep = new ARCH_SLEEP;
+ m_string = new ARCH_STRING;
+ m_time = new ARCH_TIME;
+ m_console = new ARCH_CONSOLE(args);
+ m_daemon = new ARCH_DAEMON;
+ m_taskbar = new ARCH_TASKBAR(args);
+
+#if SYSAPI_WIN32
+ CArchMiscWindows::init();
+#endif
+}
+
+CArch::~CArch()
+{
+ // clean up
+ delete m_taskbar;
+ delete m_daemon;
+ delete m_console;
+ delete m_time;
+ delete m_string;
+ delete m_sleep;
+ delete m_net;
+ delete m_log;
+ delete m_file;
+ delete m_system;
+ delete m_mt;
+
+ // no instance
+ s_instance = NULL;
+}
+
+CArch*
+CArch::getInstance()
+{
+ assert(s_instance != NULL);
+
+ return s_instance;
+}
+
+void
+CArch::openConsole(const char* title)
+{
+ m_console->openConsole(title);
+}
+
+void
+CArch::closeConsole()
+{
+ m_console->closeConsole();
+}
+
+void
+CArch::showConsole(bool showIfEmpty)
+{
+ m_console->showConsole(showIfEmpty);
+}
+
+void
+CArch::writeConsole(const char* str)
+{
+ m_console->writeConsole(str);
+}
+
+const char*
+CArch::getNewlineForConsole()
+{
+ return m_console->getNewlineForConsole();
+}
+
+void
+CArch::installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies,
+ bool allUsers)
+{
+ m_daemon->installDaemon(name, description, pathname,
+ commandLine, dependencies, allUsers);
+}
+
+void
+CArch::uninstallDaemon(const char* name, bool allUsers)
+{
+ m_daemon->uninstallDaemon(name, allUsers);
+}
+
+int
+CArch::daemonize(const char* name, DaemonFunc func)
+{
+ return m_daemon->daemonize(name, func);
+}
+
+bool
+CArch::canInstallDaemon(const char* name, bool allUsers)
+{
+ return m_daemon->canInstallDaemon(name, allUsers);
+}
+
+bool
+CArch::isDaemonInstalled(const char* name, bool allUsers)
+{
+ return m_daemon->isDaemonInstalled(name, allUsers);
+}
+
+const char*
+CArch::getBasename(const char* pathname)
+{
+ return m_file->getBasename(pathname);
+}
+
+std::string
+CArch::getUserDirectory()
+{
+ return m_file->getUserDirectory();
+}
+
+std::string
+CArch::getSystemDirectory()
+{
+ return m_file->getSystemDirectory();
+}
+
+std::string
+CArch::concatPath(const std::string& prefix, const std::string& suffix)
+{
+ return m_file->concatPath(prefix, suffix);
+}
+
+void
+CArch::openLog(const char* name)
+{
+ m_log->openLog(name);
+}
+
+void
+CArch::closeLog()
+{
+ m_log->closeLog();
+}
+
+void
+CArch::showLog(bool showIfEmpty)
+{
+ m_log->showLog(showIfEmpty);
+}
+
+void
+CArch::writeLog(ELevel level, const char* msg)
+{
+ m_log->writeLog(level, msg);
+}
+
+CArchCond
+CArch::newCondVar()
+{
+ return m_mt->newCondVar();
+}
+
+void
+CArch::closeCondVar(CArchCond cond)
+{
+ m_mt->closeCondVar(cond);
+}
+
+void
+CArch::signalCondVar(CArchCond cond)
+{
+ m_mt->signalCondVar(cond);
+}
+
+void
+CArch::broadcastCondVar(CArchCond cond)
+{
+ m_mt->broadcastCondVar(cond);
+}
+
+bool
+CArch::waitCondVar(CArchCond cond, CArchMutex mutex, double timeout)
+{
+ return m_mt->waitCondVar(cond, mutex, timeout);
+}
+
+CArchMutex
+CArch::newMutex()
+{
+ return m_mt->newMutex();
+}
+
+void
+CArch::closeMutex(CArchMutex mutex)
+{
+ m_mt->closeMutex(mutex);
+}
+
+void
+CArch::lockMutex(CArchMutex mutex)
+{
+ m_mt->lockMutex(mutex);
+}
+
+void
+CArch::unlockMutex(CArchMutex mutex)
+{
+ m_mt->unlockMutex(mutex);
+}
+
+CArchThread
+CArch::newThread(ThreadFunc func, void* data)
+{
+ return m_mt->newThread(func, data);
+}
+
+CArchThread
+CArch::newCurrentThread()
+{
+ return m_mt->newCurrentThread();
+}
+
+CArchThread
+CArch::copyThread(CArchThread thread)
+{
+ return m_mt->copyThread(thread);
+}
+
+void
+CArch::closeThread(CArchThread thread)
+{
+ m_mt->closeThread(thread);
+}
+
+void
+CArch::cancelThread(CArchThread thread)
+{
+ m_mt->cancelThread(thread);
+}
+
+void
+CArch::setPriorityOfThread(CArchThread thread, int n)
+{
+ m_mt->setPriorityOfThread(thread, n);
+}
+
+void
+CArch::testCancelThread()
+{
+ m_mt->testCancelThread();
+}
+
+bool
+CArch::wait(CArchThread thread, double timeout)
+{
+ return m_mt->wait(thread, timeout);
+}
+
+bool
+CArch::isSameThread(CArchThread thread1, CArchThread thread2)
+{
+ return m_mt->isSameThread(thread1, thread2);
+}
+
+bool
+CArch::isExitedThread(CArchThread thread)
+{
+ return m_mt->isExitedThread(thread);
+}
+
+void*
+CArch::getResultOfThread(CArchThread thread)
+{
+ return m_mt->getResultOfThread(thread);
+}
+
+IArchMultithread::ThreadID
+CArch::getIDOfThread(CArchThread thread)
+{
+ return m_mt->getIDOfThread(thread);
+}
+
+void
+CArch::setSignalHandler(ESignal signal, SignalFunc func, void* userData)
+{
+ m_mt->setSignalHandler(signal, func, userData);
+}
+
+void
+CArch::raiseSignal(ESignal signal)
+{
+ m_mt->raiseSignal(signal);
+}
+
+CArchSocket
+CArch::newSocket(EAddressFamily family, ESocketType type)
+{
+ return m_net->newSocket(family, type);
+}
+
+CArchSocket
+CArch::copySocket(CArchSocket s)
+{
+ return m_net->copySocket(s);
+}
+
+void
+CArch::closeSocket(CArchSocket s)
+{
+ m_net->closeSocket(s);
+}
+
+void
+CArch::closeSocketForRead(CArchSocket s)
+{
+ m_net->closeSocketForRead(s);
+}
+
+void
+CArch::closeSocketForWrite(CArchSocket s)
+{
+ m_net->closeSocketForWrite(s);
+}
+
+void
+CArch::bindSocket(CArchSocket s, CArchNetAddress addr)
+{
+ m_net->bindSocket(s, addr);
+}
+
+void
+CArch::listenOnSocket(CArchSocket s)
+{
+ m_net->listenOnSocket(s);
+}
+
+CArchSocket
+CArch::acceptSocket(CArchSocket s, CArchNetAddress* addr)
+{
+ return m_net->acceptSocket(s, addr);
+}
+
+bool
+CArch::connectSocket(CArchSocket s, CArchNetAddress name)
+{
+ return m_net->connectSocket(s, name);
+}
+
+int
+CArch::pollSocket(CPollEntry pe[], int num, double timeout)
+{
+ return m_net->pollSocket(pe, num, timeout);
+}
+
+void
+CArch::unblockPollSocket(CArchThread thread)
+{
+ m_net->unblockPollSocket(thread);
+}
+
+size_t
+CArch::readSocket(CArchSocket s, void* buf, size_t len)
+{
+ return m_net->readSocket(s, buf, len);
+}
+
+size_t
+CArch::writeSocket(CArchSocket s, const void* buf, size_t len)
+{
+ return m_net->writeSocket(s, buf, len);
+}
+
+void
+CArch::throwErrorOnSocket(CArchSocket s)
+{
+ m_net->throwErrorOnSocket(s);
+}
+
+bool
+CArch::setNoDelayOnSocket(CArchSocket s, bool noDelay)
+{
+ return m_net->setNoDelayOnSocket(s, noDelay);
+}
+
+bool
+CArch::setReuseAddrOnSocket(CArchSocket s, bool reuse)
+{
+ return m_net->setReuseAddrOnSocket(s, reuse);
+}
+
+std::string
+CArch::getHostName()
+{
+ return m_net->getHostName();
+}
+
+CArchNetAddress
+CArch::newAnyAddr(EAddressFamily family)
+{
+ return m_net->newAnyAddr(family);
+}
+
+CArchNetAddress
+CArch::copyAddr(CArchNetAddress addr)
+{
+ return m_net->copyAddr(addr);
+}
+
+CArchNetAddress
+CArch::nameToAddr(const std::string& name)
+{
+ return m_net->nameToAddr(name);
+}
+
+void
+CArch::closeAddr(CArchNetAddress addr)
+{
+ m_net->closeAddr(addr);
+}
+
+std::string
+CArch::addrToName(CArchNetAddress addr)
+{
+ return m_net->addrToName(addr);
+}
+
+std::string
+CArch::addrToString(CArchNetAddress addr)
+{
+ return m_net->addrToString(addr);
+}
+
+IArchNetwork::EAddressFamily
+CArch::getAddrFamily(CArchNetAddress addr)
+{
+ return m_net->getAddrFamily(addr);
+}
+
+void
+CArch::setAddrPort(CArchNetAddress addr, int port)
+{
+ m_net->setAddrPort(addr, port);
+}
+
+int
+CArch::getAddrPort(CArchNetAddress addr)
+{
+ return m_net->getAddrPort(addr);
+}
+
+bool
+CArch::isAnyAddr(CArchNetAddress addr)
+{
+ return m_net->isAnyAddr(addr);
+}
+
+bool
+CArch::isEqualAddr(CArchNetAddress a, CArchNetAddress b)
+{
+ return m_net->isEqualAddr(a, b);
+}
+
+void
+CArch::sleep(double timeout)
+{
+ m_sleep->sleep(timeout);
+}
+
+int
+CArch::vsnprintf(char* str, int size, const char* fmt, va_list ap)
+{
+ return m_string->vsnprintf(str, size, fmt, ap);
+}
+
+int
+CArch::convStringMBToWC(wchar_t* dst, const char* src, UInt32 n, bool* errors)
+{
+ return m_string->convStringMBToWC(dst, src, n, errors);
+}
+
+int
+CArch::convStringWCToMB(char* dst, const wchar_t* src, UInt32 n, bool* errors)
+{
+ return m_string->convStringWCToMB(dst, src, n, errors);
+}
+
+IArchString::EWideCharEncoding
+CArch::getWideCharEncoding()
+{
+ return m_string->getWideCharEncoding();
+}
+
+std::string
+CArch::getOSName() const
+{
+ return m_system->getOSName();
+}
+
+void
+CArch::addReceiver(IArchTaskBarReceiver* receiver)
+{
+ m_taskbar->addReceiver(receiver);
+}
+
+void
+CArch::removeReceiver(IArchTaskBarReceiver* receiver)
+{
+ m_taskbar->removeReceiver(receiver);
+}
+
+void
+CArch::updateReceiver(IArchTaskBarReceiver* receiver)
+{
+ m_taskbar->updateReceiver(receiver);
+}
+
+double
+CArch::time()
+{
+ return m_time->time();
+}
diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h
new file mode 100644
index 00000000..644f015c
--- /dev/null
+++ b/lib/arch/CArch.h
@@ -0,0 +1,218 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCH_H
+#define CARCH_H
+
+#include "IArchConsole.h"
+#include "IArchDaemon.h"
+#include "IArchFile.h"
+#include "IArchLog.h"
+#include "IArchMultithread.h"
+#include "IArchNetwork.h"
+#include "IArchSleep.h"
+#include "IArchString.h"
+#include "IArchSystem.h"
+#include "IArchTaskBar.h"
+#include "IArchTime.h"
+
+/*!
+\def ARCH
+This macro evaluates to the singleton CArch object.
+*/
+#define ARCH (CArch::getInstance())
+
+#define ARCH_ARGS void
+
+//! Delegating mplementation 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 CArch : public IArchConsole,
+ public IArchDaemon,
+ public IArchFile,
+ public IArchLog,
+ public IArchMultithread,
+ public IArchNetwork,
+ public IArchSleep,
+ public IArchString,
+ public IArchSystem,
+ public IArchTaskBar,
+ public IArchTime {
+public:
+ CArch(ARCH_ARGS* args = NULL);
+ ~CArch();
+
+ //
+ // accessors
+ //
+
+ //! Return the singleton instance
+ /*!
+ The client must have instantiated exactly once CArch object before
+ calling this function.
+ */
+ static CArch* getInstance();
+
+ // IArchConsole overrides
+ virtual void openConsole(const char*);
+ virtual void closeConsole();
+ virtual void showConsole(bool showIfEmpty);
+ virtual void writeConsole(const char*);
+ virtual const char* getNewlineForConsole();
+
+ // IArchDaemon overrides
+ virtual void installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies,
+ bool allUsers);
+ virtual void uninstallDaemon(const char* name, bool allUsers);
+ virtual int daemonize(const char* name, DaemonFunc func);
+ virtual bool canInstallDaemon(const char* name, bool allUsers);
+ virtual bool isDaemonInstalled(const char* name, bool allUsers);
+
+ // IArchFile overrides
+ virtual const char* getBasename(const char* pathname);
+ virtual std::string getUserDirectory();
+ virtual std::string getSystemDirectory();
+ virtual std::string concatPath(const std::string& prefix,
+ const std::string& suffix);
+
+ // IArchLog overrides
+ virtual void openLog(const char*);
+ virtual void closeLog();
+ virtual void showLog(bool showIfEmpty);
+ virtual void writeLog(ELevel, const char*);
+
+ // IArchMultithread overrides
+ virtual CArchCond newCondVar();
+ virtual void closeCondVar(CArchCond);
+ virtual void signalCondVar(CArchCond);
+ virtual void broadcastCondVar(CArchCond);
+ virtual bool waitCondVar(CArchCond, CArchMutex, double timeout);
+ virtual CArchMutex newMutex();
+ virtual void closeMutex(CArchMutex);
+ virtual void lockMutex(CArchMutex);
+ virtual void unlockMutex(CArchMutex);
+ virtual CArchThread newThread(ThreadFunc, void*);
+ virtual CArchThread newCurrentThread();
+ virtual CArchThread copyThread(CArchThread);
+ virtual void closeThread(CArchThread);
+ virtual void cancelThread(CArchThread);
+ virtual void setPriorityOfThread(CArchThread, int n);
+ virtual void testCancelThread();
+ virtual bool wait(CArchThread, double timeout);
+ virtual bool isSameThread(CArchThread, CArchThread);
+ virtual bool isExitedThread(CArchThread);
+ virtual void* getResultOfThread(CArchThread);
+ virtual ThreadID getIDOfThread(CArchThread);
+ virtual void setSignalHandler(ESignal, SignalFunc, void*);
+ virtual void raiseSignal(ESignal);
+
+ // IArchNetwork overrides
+ virtual CArchSocket newSocket(EAddressFamily, ESocketType);
+ virtual CArchSocket copySocket(CArchSocket s);
+ virtual void closeSocket(CArchSocket s);
+ virtual void closeSocketForRead(CArchSocket s);
+ virtual void closeSocketForWrite(CArchSocket s);
+ virtual void bindSocket(CArchSocket s, CArchNetAddress addr);
+ virtual void listenOnSocket(CArchSocket s);
+ virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr);
+ virtual bool connectSocket(CArchSocket s, CArchNetAddress name);
+ virtual int pollSocket(CPollEntry[], int num, double timeout);
+ virtual void unblockPollSocket(CArchThread thread);
+ virtual size_t readSocket(CArchSocket s, void* buf, size_t len);
+ virtual size_t writeSocket(CArchSocket s,
+ const void* buf, size_t len);
+ virtual void throwErrorOnSocket(CArchSocket);
+ virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay);
+ virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse);
+ virtual std::string getHostName();
+ virtual CArchNetAddress newAnyAddr(EAddressFamily);
+ virtual CArchNetAddress copyAddr(CArchNetAddress);
+ virtual CArchNetAddress nameToAddr(const std::string&);
+ virtual void closeAddr(CArchNetAddress);
+ virtual std::string addrToName(CArchNetAddress);
+ virtual std::string addrToString(CArchNetAddress);
+ virtual EAddressFamily getAddrFamily(CArchNetAddress);
+ virtual void setAddrPort(CArchNetAddress, int port);
+ virtual int getAddrPort(CArchNetAddress);
+ virtual bool isAnyAddr(CArchNetAddress);
+ virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress);
+
+ // IArchSleep overrides
+ virtual void sleep(double timeout);
+
+ // IArchString overrides
+ virtual int vsnprintf(char* str,
+ int size, const char* fmt, va_list ap);
+ virtual int convStringMBToWC(wchar_t*,
+ const char*, UInt32 n, bool* errors);
+ virtual int convStringWCToMB(char*,
+ const wchar_t*, UInt32 n, bool* errors);
+ virtual EWideCharEncoding
+ getWideCharEncoding();
+
+ // IArchSystem overrides
+ virtual std::string getOSName() const;
+
+ // IArchTaskBar
+ virtual void addReceiver(IArchTaskBarReceiver*);
+ virtual void removeReceiver(IArchTaskBarReceiver*);
+ virtual void updateReceiver(IArchTaskBarReceiver*);
+
+ // IArchTime overrides
+ virtual double time();
+
+private:
+ static CArch* s_instance;
+
+ IArchConsole* m_console;
+ IArchDaemon* m_daemon;
+ IArchFile* m_file;
+ IArchLog* m_log;
+ IArchMultithread* m_mt;
+ IArchNetwork* m_net;
+ IArchSleep* m_sleep;
+ IArchString* m_string;
+ IArchSystem* m_system;
+ IArchTaskBar* m_taskbar;
+ IArchTime* m_time;
+};
+
+//! Convenience object to lock/unlock an arch mutex
+class CArchMutexLock {
+public:
+ CArchMutexLock(CArchMutex mutex) : m_mutex(mutex)
+ {
+ ARCH->lockMutex(m_mutex);
+ }
+ ~CArchMutexLock()
+ {
+ ARCH->unlockMutex(m_mutex);
+ }
+
+private:
+ CArchMutex m_mutex;
+};
+
+#endif
diff --git a/lib/arch/CArchConsoleUnix.cpp b/lib/arch/CArchConsoleUnix.cpp
new file mode 100644
index 00000000..dcb6e961
--- /dev/null
+++ b/lib/arch/CArchConsoleUnix.cpp
@@ -0,0 +1,60 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchConsoleUnix.h"
+#include
+
+//
+// CArchConsoleUnix
+//
+
+CArchConsoleUnix::CArchConsoleUnix(void*)
+{
+ // do nothing
+}
+
+CArchConsoleUnix::~CArchConsoleUnix()
+{
+ // do nothing
+}
+
+void
+CArchConsoleUnix::openConsole(const char*)
+{
+ // do nothing
+}
+
+void
+CArchConsoleUnix::closeConsole()
+{
+ // do nothing
+}
+
+void
+CArchConsoleUnix::showConsole(bool)
+{
+ // do nothing
+}
+
+void
+CArchConsoleUnix::writeConsole(const char* str)
+{
+ fprintf(stderr, "%s", str);
+}
+
+const char*
+CArchConsoleUnix::getNewlineForConsole()
+{
+ return "\n";
+}
diff --git a/lib/arch/CArchConsoleUnix.h b/lib/arch/CArchConsoleUnix.h
new file mode 100644
index 00000000..f93630bd
--- /dev/null
+++ b/lib/arch/CArchConsoleUnix.h
@@ -0,0 +1,36 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHCONSOLEUNIX_H
+#define CARCHCONSOLEUNIX_H
+
+#include "IArchConsole.h"
+
+#define ARCH_CONSOLE CArchConsoleUnix
+
+//! Unix implementation of IArchConsole
+class CArchConsoleUnix : public IArchConsole {
+public:
+ CArchConsoleUnix(void*);
+ virtual ~CArchConsoleUnix();
+
+ // IArchConsole overrides
+ virtual void openConsole(const char* title);
+ virtual void closeConsole();
+ virtual void showConsole(bool);
+ virtual void writeConsole(const char*);
+ virtual const char* getNewlineForConsole();
+};
+
+#endif
diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp
new file mode 100644
index 00000000..14d418ac
--- /dev/null
+++ b/lib/arch/CArchConsoleWindows.cpp
@@ -0,0 +1,438 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchConsoleWindows.h"
+#include "IArchMultithread.h"
+#include "CArch.h"
+#include "CArchMiscWindows.h"
+#include
+
+#define SYNERGY_MSG_CONSOLE_OPEN WM_APP + 0x0021
+#define SYNERGY_MSG_CONSOLE_CLOSE WM_APP + 0x0022
+#define SYNERGY_MSG_CONSOLE_SHOW WM_APP + 0x0023
+#define SYNERGY_MSG_CONSOLE_WRITE WM_APP + 0x0024
+#define SYNERGY_MSG_CONSOLE_CLEAR WM_APP + 0x0025
+
+//
+// CArchConsoleWindows
+//
+
+CArchConsoleWindows* CArchConsoleWindows::s_instance = NULL;
+HINSTANCE CArchConsoleWindows::s_appInstance = NULL;
+
+CArchConsoleWindows::CArchConsoleWindows(void* appInstance) :
+ m_show(false),
+ m_maxLines(1000),
+ m_numCharacters(0),
+ m_maxCharacters(65536)
+{
+ // save the singleton instance
+ s_instance = this;
+
+ // save app instance
+ s_appInstance = reinterpret_cast(appInstance);
+
+ // 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(&CArchConsoleWindows::threadEntry, this);
+
+ // wait for child thread
+ while (!m_ready) {
+ ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
+ }
+
+ // ready
+ ARCH->unlockMutex(m_mutex);
+
+}
+
+CArchConsoleWindows::~CArchConsoleWindows()
+{
+ if (m_thread != NULL) {
+ PostMessage(m_hwnd, WM_QUIT, 0, 0);
+ ARCH->wait(m_thread, -1.0);
+ ARCH->closeThread(m_thread);
+ }
+ ARCH->closeCondVar(m_condVar);
+ ARCH->closeMutex(m_mutex);
+ s_instance = NULL;
+}
+
+void
+CArchConsoleWindows::openConsole(const char* title)
+{
+ SetWindowText(m_frame, title);
+ SendMessage(m_frame, SYNERGY_MSG_CONSOLE_OPEN, 0, 0);
+}
+
+void
+CArchConsoleWindows::closeConsole()
+{
+ SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLOSE, 0, 0);
+ SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLEAR, 0, 0);
+}
+
+void
+CArchConsoleWindows::showConsole(bool showIfEmpty)
+{
+ SendMessage(m_frame, SYNERGY_MSG_CONSOLE_SHOW, showIfEmpty ? 1 : 0, 0);
+}
+
+void
+CArchConsoleWindows::writeConsole(const char* str)
+{
+ SendMessage(m_frame, SYNERGY_MSG_CONSOLE_WRITE,
+ reinterpret_cast(str), 0);
+}
+
+const char*
+CArchConsoleWindows::getNewlineForConsole()
+{
+ return "\r\n";
+}
+
+void
+CArchConsoleWindows::clearBuffer()
+{
+ m_buffer.clear();
+ m_numCharacters = 0;
+ SetWindowText(m_hwnd, "");
+}
+
+void
+CArchConsoleWindows::appendBuffer(const char* msg)
+{
+ bool wasEmpty = m_buffer.empty();
+
+ // get current selection
+ CHARRANGE selection;
+ SendMessage(m_hwnd, EM_EXGETSEL, 0, reinterpret_cast(&selection));
+
+ // remove tail of buffer
+ size_t removedCharacters = 0;
+ while (m_buffer.size() >= m_maxLines) {
+ removedCharacters += m_buffer.front().size();
+ m_buffer.pop_front();
+ }
+
+ // remove lines from top of control
+ if (removedCharacters > 0) {
+ CHARRANGE range;
+ range.cpMin = 0;
+ range.cpMax = static_cast(removedCharacters);
+ SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&range));
+ SendMessage(m_hwnd, EM_REPLACESEL, FALSE, reinterpret_cast(""));
+
+ // adjust selection
+ if (selection.cpMin < static_cast(removedCharacters) ||
+ selection.cpMax < static_cast(removedCharacters)) {
+ selection.cpMin = 0;
+ selection.cpMax = 0;
+ }
+ else {
+ selection.cpMin -= static_cast(removedCharacters);
+ selection.cpMax -= static_cast(removedCharacters);
+ }
+
+ m_numCharacters -= removedCharacters;
+ }
+
+ // append message
+ m_buffer.push_back(msg);
+ size_t newNumCharacters = m_numCharacters + m_buffer.back().size();
+
+ // add line to bottom of control
+ if (newNumCharacters > m_maxCharacters) {
+ m_maxCharacters = newNumCharacters;
+ SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters);
+ }
+ CHARRANGE range;
+ range.cpMin = m_numCharacters;
+ range.cpMax = m_numCharacters;
+ SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&range));
+ SendMessage(m_hwnd, EM_REPLACESEL, FALSE,
+ reinterpret_cast(m_buffer.back().c_str()));
+
+ // adjust selection
+ bool atEnd = false;
+ if (selection.cpMax == static_cast(m_numCharacters)) {
+ selection.cpMin = static_cast(newNumCharacters);
+ selection.cpMax = static_cast(newNumCharacters);
+ atEnd = true;
+ }
+
+ // restore the selection
+ SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&selection));
+ if (atEnd) {
+ SendMessage(m_hwnd, EM_SCROLLCARET, 0, 0);
+ }
+
+ if (wasEmpty && m_show) {
+ ShowWindow(m_frame, TRUE);
+ }
+
+ m_numCharacters = newNumCharacters;
+}
+
+void
+CArchConsoleWindows::setSize(int width, int height)
+{
+ DWORD style = GetWindowLong(m_frame, GWL_STYLE);
+ DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE);
+ RECT rect;
+ rect.left = 100;
+ rect.top = 100;
+ rect.right = rect.left + width * m_wChar;
+ rect.bottom = rect.top + height * m_hChar;
+ AdjustWindowRectEx(&rect, style, FALSE, exStyle);
+ SetWindowPos(m_frame, NULL, 0, 0, rect.right - rect.left,
+ rect.bottom - rect.top,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
+}
+
+LRESULT
+CArchConsoleWindows::wndProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_CLOSE:
+ ShowWindow(m_frame, FALSE);
+ m_show = false;
+ return 0;
+
+ case SYNERGY_MSG_CONSOLE_OPEN:
+ return 0;
+
+ case SYNERGY_MSG_CONSOLE_CLOSE:
+ SendMessage(m_frame, WM_CLOSE, 0, 0);
+ m_show = false;
+ return 0;
+
+ case SYNERGY_MSG_CONSOLE_SHOW:
+ m_show = true;
+ if (wParam != 0 || !m_buffer.empty()) {
+ ShowWindow(m_frame, TRUE);
+ }
+ return 0;
+
+ case SYNERGY_MSG_CONSOLE_WRITE:
+ appendBuffer(reinterpret_cast(wParam));
+ return 0;
+
+ case SYNERGY_MSG_CONSOLE_CLEAR:
+ clearBuffer();
+ return 0;
+
+ case WM_SIZE:
+ if (hwnd == m_frame) {
+ MoveWindow(m_hwnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
+ }
+ break;
+
+ case WM_SIZING:
+ if (hwnd == m_frame) {
+ // get window vs client area info
+ int wBase = 40 * m_wChar;
+ int hBase = 40 * m_hChar;
+ DWORD style = GetWindowLong(m_frame, GWL_STYLE);
+ DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE);
+ RECT rect;
+ rect.left = 100;
+ rect.top = 100;
+ rect.right = rect.left + wBase;
+ rect.bottom = rect.top + hBase;
+ AdjustWindowRectEx(&rect, style, FALSE, exStyle);
+ wBase = rect.right - rect.left - wBase;
+ hBase = rect.bottom - rect.top - hBase;
+
+ // get closest size that's a multiple of the character size
+ RECT* newRect = (RECT*)lParam;
+ int width = (newRect->right - newRect->left - wBase) / m_wChar;
+ int height = (newRect->bottom - newRect->top - hBase) / m_hChar;
+ width = width * m_wChar + wBase;
+ height = height * m_hChar + hBase;
+
+ // adjust sizing rect
+ switch (wParam) {
+ case WMSZ_LEFT:
+ case WMSZ_TOPLEFT:
+ case WMSZ_BOTTOMLEFT:
+ newRect->left = newRect->right - width;
+ break;
+
+ case WMSZ_RIGHT:
+ case WMSZ_TOPRIGHT:
+ case WMSZ_BOTTOMRIGHT:
+ newRect->right = newRect->left + width;
+ break;
+ }
+ switch (wParam) {
+ case WMSZ_TOP:
+ case WMSZ_TOPLEFT:
+ case WMSZ_TOPRIGHT:
+ newRect->top = newRect->bottom - height;
+ break;
+
+ case WMSZ_BOTTOM:
+ case WMSZ_BOTTOMLEFT:
+ case WMSZ_BOTTOMRIGHT:
+ newRect->bottom = newRect->top + height;
+ break;
+ }
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+LRESULT CALLBACK
+CArchConsoleWindows::staticWndProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ // forward the message
+ if (s_instance != NULL) {
+ return s_instance->wndProc(hwnd, msg, wParam, lParam);
+ }
+ else {
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+}
+
+void
+CArchConsoleWindows::threadMainLoop()
+{
+ LoadLibrary("RICHED32.DLL");
+
+ // get the app icons
+ HICON largeIcon, smallIcon;
+ CArchMiscWindows::getIcons(largeIcon, smallIcon);
+
+ // register a window class
+ WNDCLASSEX classInfo;
+ classInfo.cbSize = sizeof(classInfo);
+ classInfo.style = 0;
+ classInfo.lpfnWndProc = &CArchConsoleWindows::staticWndProc;
+ classInfo.cbClsExtra = 0;
+ classInfo.cbWndExtra = sizeof(CArchConsoleWindows*);
+ classInfo.hInstance = s_appInstance;
+ classInfo.hIcon = largeIcon;
+ classInfo.hCursor = NULL;
+ classInfo.hbrBackground = NULL;
+ classInfo.lpszMenuName = NULL;
+ classInfo.lpszClassName = TEXT("SynergyConsole");
+ classInfo.hIconSm = smallIcon;
+ ATOM windowClass = RegisterClassEx(&classInfo);
+
+ // create frame window
+ m_frame = CreateWindowEx(0,
+ reinterpret_cast(windowClass),
+ TEXT("Synergy Log"),
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, 100, 100,
+ NULL,
+ NULL,
+ s_appInstance,
+ NULL);
+
+ // create log window
+ m_hwnd = CreateWindowEx(0,
+ "RichEdit",
+ TEXT(""),
+ WS_CHILD | WS_VISIBLE | WS_VSCROLL |
+ ES_MULTILINE | ES_READONLY,
+ 0, 0, 1, 1,
+ m_frame,
+ (HMENU)1,
+ s_appInstance,
+ NULL);
+
+ // select font and get info
+ HDC hdc = GetDC(m_hwnd);
+ HGDIOBJ oldFont = SelectObject(hdc, GetStockObject(ANSI_FIXED_FONT));
+ TEXTMETRIC metrics;
+ GetTextMetrics(hdc, &metrics);
+ CHARFORMAT format;
+ format.cbSize = sizeof(format);
+ format.dwMask = CFM_CHARSET | CFM_COLOR | CFM_FACE |
+ CFM_OFFSET | CFM_SIZE | CFM_PROTECTED |
+ CFM_BOLD | CFM_ITALIC |
+ CFM_STRIKEOUT | CFM_UNDERLINE;
+ format.dwEffects = 0;
+ format.yHeight = metrics.tmHeight;
+ format.yOffset = 0;
+ format.crTextColor = RGB(0, 0, 0);
+ format.bCharSet = DEFAULT_CHARSET;
+ format.bPitchAndFamily = FIXED_PITCH | FF_MODERN;
+ GetTextFace(hdc, sizeof(format.szFaceName), format.szFaceName);
+ SelectObject(hdc, oldFont);
+ ReleaseDC(m_hwnd, hdc);
+
+ // prep window
+ SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters);
+ SendMessage(m_hwnd, EM_SETCHARFORMAT, 0, reinterpret_cast(&format));
+ SendMessage(m_hwnd, EM_SETBKGNDCOLOR, 0, RGB(255, 255, 255));
+ m_wChar = metrics.tmAveCharWidth;
+ m_hChar = metrics.tmHeight + metrics.tmExternalLeading;
+ setSize(80, 25);
+
+ // signal ready
+ ARCH->lockMutex(m_mutex);
+ m_ready = true;
+ ARCH->broadcastCondVar(m_condVar);
+ ARCH->unlockMutex(m_mutex);
+
+ // handle failure
+ if (m_hwnd == NULL) {
+ UnregisterClass(reinterpret_cast(windowClass), s_appInstance);
+ return;
+ }
+
+ // main loop
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ // clean up
+ DestroyWindow(m_hwnd);
+ UnregisterClass(reinterpret_cast(windowClass), s_appInstance);
+}
+
+void*
+CArchConsoleWindows::threadEntry(void* self)
+{
+ reinterpret_cast(self)->threadMainLoop();
+ return NULL;
+}
diff --git a/lib/arch/CArchConsoleWindows.h b/lib/arch/CArchConsoleWindows.h
new file mode 100644
index 00000000..0d59e6ef
--- /dev/null
+++ b/lib/arch/CArchConsoleWindows.h
@@ -0,0 +1,77 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHCONSOLEWINDOWS_H
+#define CARCHCONSOLEWINDOWS_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "IArchConsole.h"
+#include "IArchMultithread.h"
+#include "stddeque.h"
+#include
+
+#define ARCH_CONSOLE CArchConsoleWindows
+
+//! Win32 implementation of IArchConsole
+class CArchConsoleWindows : public IArchConsole {
+public:
+ CArchConsoleWindows(void*);
+ virtual ~CArchConsoleWindows();
+
+ // IArchConsole overrides
+ virtual void openConsole(const char* title);
+ virtual void closeConsole();
+ virtual void showConsole(bool showIfEmpty);
+ virtual void writeConsole(const char*);
+ virtual const char* getNewlineForConsole();
+
+private:
+ void clearBuffer();
+ void appendBuffer(const char*);
+ void setSize(int width, int height);
+
+ LRESULT wndProc(HWND, UINT, WPARAM, LPARAM);
+ static LRESULT CALLBACK
+ staticWndProc(HWND, UINT, WPARAM, LPARAM);
+ void threadMainLoop();
+ static void* threadEntry(void*);
+
+private:
+ typedef std::deque MessageBuffer;
+
+ static CArchConsoleWindows* s_instance;
+ static HINSTANCE s_appInstance;
+
+ // multithread data
+ CArchMutex m_mutex;
+ CArchCond m_condVar;
+ bool m_ready;
+ CArchThread m_thread;
+
+ // child thread data
+ HWND m_frame;
+ HWND m_hwnd;
+ LONG m_wChar;
+ LONG m_hChar;
+ bool m_show;
+
+ // messages
+ size_t m_maxLines;
+ size_t m_maxCharacters;
+ size_t m_numCharacters;
+ MessageBuffer m_buffer;
+};
+
+#endif
diff --git a/lib/arch/CArchDaemonNone.cpp b/lib/arch/CArchDaemonNone.cpp
new file mode 100644
index 00000000..0281f365
--- /dev/null
+++ b/lib/arch/CArchDaemonNone.cpp
@@ -0,0 +1,66 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchDaemonNone.h"
+
+//
+// CArchDaemonNone
+//
+
+CArchDaemonNone::CArchDaemonNone()
+{
+ // do nothing
+}
+
+CArchDaemonNone::~CArchDaemonNone()
+{
+ // do nothing
+}
+
+void
+CArchDaemonNone::installDaemon(const char*,
+ const char*,
+ const char*,
+ const char*,
+ const char*,
+ bool)
+{
+ // do nothing
+}
+
+void
+CArchDaemonNone::uninstallDaemon(const char*, bool)
+{
+ // do nothing
+}
+
+int
+CArchDaemonNone::daemonize(const char* name, DaemonFunc func)
+{
+ // simply forward the call to func. obviously, this doesn't
+ // do any daemonizing.
+ return func(1, &name);
+}
+
+bool
+CArchDaemonNone::canInstallDaemon(const char*, bool)
+{
+ return false;
+}
+
+bool
+CArchDaemonNone::isDaemonInstalled(const char*, bool)
+{
+ return false;
+}
diff --git a/lib/arch/CArchDaemonNone.h b/lib/arch/CArchDaemonNone.h
new file mode 100644
index 00000000..1c196c5d
--- /dev/null
+++ b/lib/arch/CArchDaemonNone.h
@@ -0,0 +1,47 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHDAEMONNONE_H
+#define CARCHDAEMONNONE_H
+
+#include "IArchDaemon.h"
+
+#define ARCH_DAEMON CArchDaemonNone
+
+//! 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 CArchDaemonNone : public IArchDaemon {
+public:
+ CArchDaemonNone();
+ virtual ~CArchDaemonNone();
+
+ // IArchDaemon overrides
+ virtual void installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies,
+ bool allUsers);
+ virtual void uninstallDaemon(const char* name, bool allUsers);
+ virtual int daemonize(const char* name, DaemonFunc func);
+ virtual bool canInstallDaemon(const char* name, bool allUsers);
+ virtual bool isDaemonInstalled(const char* name, bool allUsers);
+};
+
+#endif
diff --git a/lib/arch/CArchDaemonUnix.cpp b/lib/arch/CArchDaemonUnix.cpp
new file mode 100644
index 00000000..93d50d4d
--- /dev/null
+++ b/lib/arch/CArchDaemonUnix.cpp
@@ -0,0 +1,78 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchDaemonUnix.h"
+#include "XArchUnix.h"
+#include
+#include
+#include
+#include
+#include
+
+//
+// CArchDaemonUnix
+//
+
+CArchDaemonUnix::CArchDaemonUnix()
+{
+ // do nothing
+}
+
+CArchDaemonUnix::~CArchDaemonUnix()
+{
+ // do nothing
+}
+
+int
+CArchDaemonUnix::daemonize(const char* name, DaemonFunc func)
+{
+ // 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();
+
+ // chdir to root so we don't keep mounted filesystems points busy
+ chdir("/");
+
+ // 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);
+ dup(1);
+
+ // invoke function
+ return func(1, &name);
+}
diff --git a/lib/arch/CArchDaemonUnix.h b/lib/arch/CArchDaemonUnix.h
new file mode 100644
index 00000000..923004e1
--- /dev/null
+++ b/lib/arch/CArchDaemonUnix.h
@@ -0,0 +1,33 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHDAEMONUNIX_H
+#define CARCHDAEMONUNIX_H
+
+#include "CArchDaemonNone.h"
+
+#undef ARCH_DAEMON
+#define ARCH_DAEMON CArchDaemonUnix
+
+//! Unix implementation of IArchDaemon
+class CArchDaemonUnix : public CArchDaemonNone {
+public:
+ CArchDaemonUnix();
+ virtual ~CArchDaemonUnix();
+
+ // IArchDaemon overrides
+ virtual int daemonize(const char* name, DaemonFunc func);
+};
+
+#endif
diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp
new file mode 100644
index 00000000..ab42ceab
--- /dev/null
+++ b/lib/arch/CArchDaemonWindows.cpp
@@ -0,0 +1,770 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchDaemonWindows.h"
+#include "CArch.h"
+#include "CArchMiscWindows.h"
+#include "XArchWindows.h"
+#include "stdvector.h"
+
+//
+// CArchDaemonWindows
+//
+
+CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL;
+
+CArchDaemonWindows::CArchDaemonWindows()
+{
+ m_quitMessage = RegisterWindowMessage("SynergyDaemonExit");
+}
+
+CArchDaemonWindows::~CArchDaemonWindows()
+{
+ // do nothing
+}
+
+int
+CArchDaemonWindows::runDaemon(RunFunc runFunc)
+{
+ assert(s_daemon != NULL);
+
+ return s_daemon->doRunDaemon(runFunc);
+}
+
+void
+CArchDaemonWindows::daemonRunning(bool running)
+{
+ // if s_daemon is NULL we assume we're running on the windows
+ // 95 family and we just ignore this call so the caller doesn't
+ // have to go through the trouble of not calling it on the
+ // windows 95 family.
+ if (s_daemon != NULL) {
+ s_daemon->doDaemonRunning(running);
+ }
+}
+
+UINT
+CArchDaemonWindows::getDaemonQuitMessage()
+{
+ if (s_daemon != NULL) {
+ return s_daemon->doGetDaemonQuitMessage();
+ }
+ else {
+ return 0;
+ }
+}
+
+void
+CArchDaemonWindows::daemonFailed(int result)
+{
+ // if s_daemon is NULL we assume we're running on the windows
+ // 95 family and we just ignore this call so the caller doesn't
+ // have to go through the trouble of not calling it on the
+ // windows 95 family.
+ if (s_daemon != NULL) {
+ throw XArchDaemonRunFailed(result);
+ }
+}
+
+void
+CArchDaemonWindows::installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies,
+ bool allUsers)
+{
+ // if not for all users then use the user's autostart registry.
+ // key. if windows 95 family then use windows 95 services key.
+ if (!allUsers || CArchMiscWindows::isWindows95Family()) {
+ // open registry
+ HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
+ open95ServicesKey() : openUserStartupKey();
+ if (key == NULL) {
+ // can't open key
+ throw XArchDaemonInstallFailed(new XArchEvalWindows);
+ }
+
+ // construct entry
+ std::string value;
+ value += "\"";
+ value += pathname;
+ value += "\" ";
+ value += commandLine;
+
+ // install entry
+ CArchMiscWindows::setValue(key, name, value);
+
+ // clean up
+ CArchMiscWindows::closeKey(key);
+ }
+
+ // windows NT family services
+ else {
+ // 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));
+ }
+ }
+
+ // done with service and manager
+ CloseServiceHandle(service);
+ CloseServiceHandle(mgr);
+
+ // open the registry key for this service
+ HKEY key = openNTServicesKey();
+ key = CArchMiscWindows::addKey(key, name);
+ if (key == NULL) {
+ // can't open key
+ DWORD err = GetLastError();
+ try {
+ uninstallDaemon(name, allUsers);
+ }
+ catch (...) {
+ // ignore
+ }
+ throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
+ }
+
+ // set the description
+ CArchMiscWindows::setValue(key, _T("Description"), description);
+
+ // set command line
+ key = CArchMiscWindows::addKey(key, _T("Parameters"));
+ if (key == NULL) {
+ // can't open key
+ DWORD err = GetLastError();
+ CArchMiscWindows::closeKey(key);
+ try {
+ uninstallDaemon(name, allUsers);
+ }
+ catch (...) {
+ // ignore
+ }
+ throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
+ }
+ CArchMiscWindows::setValue(key, _T("CommandLine"), commandLine);
+
+ // done with registry
+ CArchMiscWindows::closeKey(key);
+ }
+}
+
+void
+CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers)
+{
+ // if not for all users then use the user's autostart registry.
+ // key. if windows 95 family then use windows 95 services key.
+ if (!allUsers || CArchMiscWindows::isWindows95Family()) {
+ // open registry
+ HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
+ open95ServicesKey() : openUserStartupKey();
+ if (key == NULL) {
+ // can't open key. daemon is probably not installed.
+ throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows);
+ }
+
+ // remove entry
+ CArchMiscWindows::deleteValue(key, name);
+
+ // clean up
+ CArchMiscWindows::closeKey(key);
+ }
+
+ // windows NT family services
+ else {
+ // remove parameters for this service. ignore failures.
+ HKEY key = openNTServicesKey();
+ key = CArchMiscWindows::openKey(key, name);
+ if (key != NULL) {
+ CArchMiscWindows::deleteKey(key, _T("Parameters"));
+ CArchMiscWindows::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);
+
+ // handle failure. ignore error if service isn't installed anymore.
+ if (!okay && isDaemonInstalled(name, allUsers)) {
+ 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
+CArchDaemonWindows::daemonize(const char* name, DaemonFunc func)
+{
+ assert(name != NULL);
+ assert(func != NULL);
+
+ // windows 95 family services
+ if (CArchMiscWindows::isWindows95Family()) {
+ typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD);
+
+ // mark this process as a service so it's not killed when the
+ // user logs off.
+ HINSTANCE kernel = LoadLibrary("kernel32.dll");
+ if (kernel == NULL) {
+ throw XArchDaemonFailed(new XArchEvalWindows);
+ }
+ RegisterServiceProcessT RegisterServiceProcess =
+ reinterpret_cast(
+ GetProcAddress(kernel,
+ "RegisterServiceProcess"));
+ if (RegisterServiceProcess == NULL) {
+ // missing RegisterServiceProcess function
+ DWORD err = GetLastError();
+ FreeLibrary(kernel);
+ throw XArchDaemonFailed(new XArchEvalWindows(err));
+ }
+ if (RegisterServiceProcess(0, 1) == 0) {
+ // RegisterServiceProcess failed
+ DWORD err = GetLastError();
+ FreeLibrary(kernel);
+ throw XArchDaemonFailed(new XArchEvalWindows(err));
+ }
+ FreeLibrary(kernel);
+
+ // now simply call the daemon function
+ return func(1, &name);
+ }
+
+ // windows NT family services
+ else {
+ // save daemon function
+ m_daemonFunc = func;
+
+ // construct the service entry
+ SERVICE_TABLE_ENTRY entry[2];
+ entry[0].lpServiceName = const_cast(name);
+ entry[0].lpServiceProc = &CArchDaemonWindows::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
+CArchDaemonWindows::canInstallDaemon(const char* /*name*/, bool allUsers)
+{
+ // if not for all users then use the user's autostart registry.
+ // key. if windows 95 family then use windows 95 services key.
+ if (!allUsers || CArchMiscWindows::isWindows95Family()) {
+ // check if we can open the registry key
+ HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
+ open95ServicesKey() : openUserStartupKey();
+ CArchMiscWindows::closeKey(key);
+ return (key != NULL);
+ }
+
+ // windows NT family services
+ else {
+ // 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();
+// key = CArchMiscWindows::addKey(key, name);
+// key = CArchMiscWindows::addKey(key, _T("Parameters"));
+ CArchMiscWindows::closeKey(key);
+
+ return (key != NULL);
+ }
+}
+
+bool
+CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers)
+{
+ // if not for all users then use the user's autostart registry.
+ // key. if windows 95 family then use windows 95 services key.
+ if (!allUsers || CArchMiscWindows::isWindows95Family()) {
+ // check if we can open the registry key
+ HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
+ open95ServicesKey() : openUserStartupKey();
+ if (key == NULL) {
+ return false;
+ }
+
+ // check for entry
+ const bool installed = !CArchMiscWindows::readValueString(key,
+ name).empty();
+
+ // clean up
+ CArchMiscWindows::closeKey(key);
+
+ return installed;
+ }
+
+ // windows NT family services
+ else {
+ // check parameters for this service
+ HKEY key = openNTServicesKey();
+ key = CArchMiscWindows::openKey(key, name);
+ key = CArchMiscWindows::openKey(key, _T("Parameters"));
+ if (key != NULL) {
+ const bool installed = !CArchMiscWindows::readValueString(key,
+ _T("CommandLine")).empty();
+ CArchMiscWindows::closeKey(key);
+ if (!installed) {
+ return false;
+ }
+ }
+
+ // 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
+CArchDaemonWindows::openNTServicesKey()
+{
+ static const char* s_keyNames[] = {
+ _T("SYSTEM"),
+ _T("CurrentControlSet"),
+ _T("Services"),
+ NULL
+ };
+
+ return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
+}
+
+HKEY
+CArchDaemonWindows::open95ServicesKey()
+{
+ static const char* s_keyNames[] = {
+ _T("Software"),
+ _T("Microsoft"),
+ _T("Windows"),
+ _T("CurrentVersion"),
+ _T("RunServices"),
+ NULL
+ };
+
+ return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
+}
+
+HKEY
+CArchDaemonWindows::openUserStartupKey()
+{
+ static const char* s_keyNames[] = {
+ _T("Software"),
+ _T("Microsoft"),
+ _T("Windows"),
+ _T("CurrentVersion"),
+ _T("Run"),
+ NULL
+ };
+
+ return CArchMiscWindows::addKey(HKEY_CURRENT_USER, s_keyNames);
+}
+
+bool
+CArchDaemonWindows::isRunState(DWORD state)
+{
+ switch (state) {
+ case SERVICE_START_PENDING:
+ case SERVICE_CONTINUE_PENDING:
+ case SERVICE_RUNNING:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+int
+CArchDaemonWindows::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
+CArchDaemonWindows::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
+CArchDaemonWindows::doGetDaemonQuitMessage()
+{
+ return m_quitMessage;
+}
+
+void
+CArchDaemonWindows::setStatus(DWORD state)
+{
+ setStatus(state, 0, 0);
+}
+
+void
+CArchDaemonWindows::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
+CArchDaemonWindows::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
+CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn)
+{
+ typedef std::vector ArgList;
+ typedef std::vector Arguments;
+ const char** argv = const_cast(argvIn);
+
+ // create synchronization objects
+ m_serviceMutex = ARCH->newMutex();
+ m_serviceCondVar = ARCH->newCondVar();
+
+ // register our service handler function
+ m_statusHandle = RegisterServiceCtrlHandler(argv[0],
+ &CArchDaemonWindows::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);
+
+ // 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
+ std::string commandLine;
+ HKEY key = openNTServicesKey();
+ key = CArchMiscWindows::openKey(key, argvIn[0]);
+ key = CArchMiscWindows::openKey(key, _T("Parameters"));
+ if (key != NULL) {
+ commandLine = CArchMiscWindows::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 i = 0; i < args.size(); ++i) {
+ myArgv.push_back(args[i].c_str());
+ }
+
+ // adjust argc/argv
+ argc = myArgv.size();
+ argv = &myArgv[0];
+ }
+ }
+
+ try {
+ // invoke daemon function
+ m_daemonResult = m_daemonFunc(static_cast(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);
+}
+
+void WINAPI
+CArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv)
+{
+ s_daemon->serviceMain(argc, argv);
+}
+
+void
+CArchDaemonWindows::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
+CArchDaemonWindows::serviceHandlerEntry(DWORD ctrl)
+{
+ s_daemon->serviceHandler(ctrl);
+}
diff --git a/lib/arch/CArchDaemonWindows.h b/lib/arch/CArchDaemonWindows.h
new file mode 100644
index 00000000..ed09fab9
--- /dev/null
+++ b/lib/arch/CArchDaemonWindows.h
@@ -0,0 +1,134 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHDAEMONWINDOWS_H
+#define CARCHDAEMONWINDOWS_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "IArchDaemon.h"
+#include "IArchMultithread.h"
+#include "stdstring.h"
+#include
+#include
+
+#define ARCH_DAEMON CArchDaemonWindows
+
+//! Win32 implementation of IArchDaemon
+class CArchDaemonWindows : public IArchDaemon {
+public:
+ typedef int (*RunFunc)(void);
+
+ CArchDaemonWindows();
+ virtual ~CArchDaemonWindows();
+
+ //! 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,
+ bool allUsers);
+ virtual void uninstallDaemon(const char* name, bool allUsers);
+ virtual int daemonize(const char* name, DaemonFunc func);
+ virtual bool canInstallDaemon(const char* name, bool allUsers);
+ virtual bool isDaemonInstalled(const char* name, bool allUsers);
+
+private:
+ static HKEY openNTServicesKey();
+ static HKEY open95ServicesKey();
+ static HKEY openUserStartupKey();
+
+ 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);
+
+private:
+ class XArchDaemonRunFailed {
+ public:
+ XArchDaemonRunFailed(int result) : m_result(result) { }
+
+ public:
+ int m_result;
+ };
+
+private:
+ static CArchDaemonWindows* s_daemon;
+
+ CArchMutex m_serviceMutex;
+ CArchCond 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;
+};
+
+#endif
diff --git a/lib/arch/CArchFileUnix.cpp b/lib/arch/CArchFileUnix.cpp
new file mode 100644
index 00000000..89bb51dc
--- /dev/null
+++ b/lib/arch/CArchFileUnix.cpp
@@ -0,0 +1,98 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchFileUnix.h"
+#include
+#include
+#include
+#include
+#include
+
+//
+// CArchFileUnix
+//
+
+CArchFileUnix::CArchFileUnix()
+{
+ // do nothing
+}
+
+CArchFileUnix::~CArchFileUnix()
+{
+ // do nothing
+}
+
+const char*
+CArchFileUnix::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
+CArchFileUnix::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
+CArchFileUnix::getSystemDirectory()
+{
+ return "/etc";
+}
+
+std::string
+CArchFileUnix::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;
+}
diff --git a/lib/arch/CArchFileUnix.h b/lib/arch/CArchFileUnix.h
new file mode 100644
index 00000000..41d00e90
--- /dev/null
+++ b/lib/arch/CArchFileUnix.h
@@ -0,0 +1,36 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHFILEUNIX_H
+#define CARCHFILEUNIX_H
+
+#include "IArchFile.h"
+
+#define ARCH_FILE CArchFileUnix
+
+//! Unix implementation of IArchFile
+class CArchFileUnix : public IArchFile {
+public:
+ CArchFileUnix();
+ virtual ~CArchFileUnix();
+
+ // IArchFile overrides
+ virtual const char* getBasename(const char* pathname);
+ virtual std::string getUserDirectory();
+ virtual std::string getSystemDirectory();
+ virtual std::string concatPath(const std::string& prefix,
+ const std::string& suffix);
+};
+
+#endif
diff --git a/lib/arch/CArchFileWindows.cpp b/lib/arch/CArchFileWindows.cpp
new file mode 100644
index 00000000..5debb17b
--- /dev/null
+++ b/lib/arch/CArchFileWindows.cpp
@@ -0,0 +1,132 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchFileWindows.h"
+#include
+#include
+#include
+#include
+
+//
+// CArchFileWindows
+//
+
+CArchFileWindows::CArchFileWindows()
+{
+ // do nothing
+}
+
+CArchFileWindows::~CArchFileWindows()
+{
+ // do nothing
+}
+
+const char*
+CArchFileWindows::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
+CArchFileWindows::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
+CArchFileWindows::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
+CArchFileWindows::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;
+}
diff --git a/lib/arch/CArchFileWindows.h b/lib/arch/CArchFileWindows.h
new file mode 100644
index 00000000..617b1c40
--- /dev/null
+++ b/lib/arch/CArchFileWindows.h
@@ -0,0 +1,36 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHFILEWINDOWS_H
+#define CARCHFILEWINDOWS_H
+
+#include "IArchFile.h"
+
+#define ARCH_FILE CArchFileWindows
+
+//! Win32 implementation of IArchFile
+class CArchFileWindows : public IArchFile {
+public:
+ CArchFileWindows();
+ virtual ~CArchFileWindows();
+
+ // IArchFile overrides
+ virtual const char* getBasename(const char* pathname);
+ virtual std::string getUserDirectory();
+ virtual std::string getSystemDirectory();
+ virtual std::string concatPath(const std::string& prefix,
+ const std::string& suffix);
+};
+
+#endif
diff --git a/lib/arch/CArchLogUnix.cpp b/lib/arch/CArchLogUnix.cpp
new file mode 100644
index 00000000..093d89f9
--- /dev/null
+++ b/lib/arch/CArchLogUnix.cpp
@@ -0,0 +1,79 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchLogUnix.h"
+#include
+
+//
+// CArchLogUnix
+//
+
+CArchLogUnix::CArchLogUnix()
+{
+ // do nothing
+}
+
+CArchLogUnix::~CArchLogUnix()
+{
+ // do nothing
+}
+
+void
+CArchLogUnix::openLog(const char* name)
+{
+ openlog(name, 0, LOG_DAEMON);
+}
+
+void
+CArchLogUnix::closeLog()
+{
+ closelog();
+}
+
+void
+CArchLogUnix::showLog(bool)
+{
+ // do nothing
+}
+
+void
+CArchLogUnix::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/lib/arch/CArchLogUnix.h b/lib/arch/CArchLogUnix.h
new file mode 100644
index 00000000..91070b45
--- /dev/null
+++ b/lib/arch/CArchLogUnix.h
@@ -0,0 +1,35 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHLOGUNIX_H
+#define CARCHLOGUNIX_H
+
+#include "IArchLog.h"
+
+#define ARCH_LOG CArchLogUnix
+
+//! Unix implementation of IArchLog
+class CArchLogUnix : public IArchLog {
+public:
+ CArchLogUnix();
+ virtual ~CArchLogUnix();
+
+ // IArchLog overrides
+ virtual void openLog(const char* name);
+ virtual void closeLog();
+ virtual void showLog(bool);
+ virtual void writeLog(ELevel, const char*);
+};
+
+#endif
diff --git a/lib/arch/CArchLogWindows.cpp b/lib/arch/CArchLogWindows.cpp
new file mode 100644
index 00000000..0ac89131
--- /dev/null
+++ b/lib/arch/CArchLogWindows.cpp
@@ -0,0 +1,90 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchLogWindows.h"
+#include "CArchMiscWindows.h"
+#include
+
+//
+// CArchLogWindows
+//
+
+CArchLogWindows::CArchLogWindows() : m_eventLog(NULL)
+{
+ // do nothing
+}
+
+CArchLogWindows::~CArchLogWindows()
+{
+ // do nothing
+}
+
+void
+CArchLogWindows::openLog(const char* name)
+{
+ if (m_eventLog == NULL && !CArchMiscWindows::isWindows95Family()) {
+ m_eventLog = RegisterEventSource(NULL, name);
+ }
+}
+
+void
+CArchLogWindows::closeLog()
+{
+ if (m_eventLog != NULL) {
+ DeregisterEventSource(m_eventLog);
+ m_eventLog = NULL;
+ }
+}
+
+void
+CArchLogWindows::showLog(bool)
+{
+ // do nothing
+}
+
+void
+CArchLogWindows::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(level),
+ 0, // event ID
+ NULL,
+ 0,
+ strlen(msg) + 1, // raw data size
+ NULL,
+ const_cast(msg));// raw data
+ }
+}
diff --git a/lib/arch/CArchLogWindows.h b/lib/arch/CArchLogWindows.h
new file mode 100644
index 00000000..e8812536
--- /dev/null
+++ b/lib/arch/CArchLogWindows.h
@@ -0,0 +1,41 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHLOGWINDOWS_H
+#define CARCHLOGWINDOWS_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "IArchLog.h"
+#include
+
+#define ARCH_LOG CArchLogWindows
+
+//! Win32 implementation of IArchLog
+class CArchLogWindows : public IArchLog {
+public:
+ CArchLogWindows();
+ virtual ~CArchLogWindows();
+
+ // 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;
+};
+
+#endif
diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp
new file mode 100644
index 00000000..e2fb2dce
--- /dev/null
+++ b/lib/arch/CArchMiscWindows.cpp
@@ -0,0 +1,416 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchMiscWindows.h"
+#include "CArchDaemonWindows.h"
+
+#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;
+
+//
+// CArchMiscWindows
+//
+
+CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL;
+DWORD CArchMiscWindows::s_busyState = 0;
+CArchMiscWindows::STES_t CArchMiscWindows::s_stes = NULL;
+HICON CArchMiscWindows::s_largeIcon = NULL;
+HICON CArchMiscWindows::s_smallIcon = NULL;
+
+void
+CArchMiscWindows::init()
+{
+ s_dialogs = new CDialogs;
+ isWindows95Family();
+}
+
+bool
+CArchMiscWindows::isWindows95Family()
+{
+ static bool init = false;
+ static bool result = false;
+
+ if (!init) {
+ OSVERSIONINFO version;
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (GetVersionEx(&version) == 0) {
+ // cannot determine OS; assume windows 95 family
+ result = true;
+ }
+ else {
+ result = (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
+ }
+ init = true;
+ }
+ return result;
+}
+
+bool
+CArchMiscWindows::isWindowsModern()
+{
+ static bool init = false;
+ static bool result = false;
+
+ if (!init) {
+ OSVERSIONINFO version;
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (GetVersionEx(&version) == 0) {
+ // cannot determine OS; assume not modern
+ result = false;
+ }
+ else {
+ result = ((version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
+ version.dwMajorVersion == 4 &&
+ version.dwMinorVersion > 0) ||
+ (version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+ version.dwMajorVersion > 4));
+ }
+ init = true;
+ }
+ return result;
+}
+
+void
+CArchMiscWindows::setIcons(HICON largeIcon, HICON smallIcon)
+{
+ s_largeIcon = largeIcon;
+ s_smallIcon = smallIcon;
+}
+
+void
+CArchMiscWindows::getIcons(HICON& largeIcon, HICON& smallIcon)
+{
+ largeIcon = s_largeIcon;
+ smallIcon = s_smallIcon;
+}
+
+int
+CArchMiscWindows::runDaemon(RunFunc runFunc)
+{
+ return CArchDaemonWindows::runDaemon(runFunc);
+}
+
+void
+CArchMiscWindows::daemonRunning(bool running)
+{
+ CArchDaemonWindows::daemonRunning(running);
+}
+
+void
+CArchMiscWindows::daemonFailed(int result)
+{
+ CArchDaemonWindows::daemonFailed(result);
+}
+
+UINT
+CArchMiscWindows::getDaemonQuitMessage()
+{
+ return CArchDaemonWindows::getDaemonQuitMessage();
+}
+
+HKEY
+CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName)
+{
+ return openKey(key, keyName, false);
+}
+
+HKEY
+CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames)
+{
+ return openKey(key, keyNames, false);
+}
+
+HKEY
+CArchMiscWindows::addKey(HKEY key, const TCHAR* keyName)
+{
+ return openKey(key, keyName, true);
+}
+
+HKEY
+CArchMiscWindows::addKey(HKEY key, const TCHAR* const* keyNames)
+{
+ return openKey(key, keyNames, true);
+}
+
+HKEY
+CArchMiscWindows::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
+CArchMiscWindows::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
+CArchMiscWindows::closeKey(HKEY key)
+{
+ assert(key != NULL);
+ RegCloseKey(key);
+}
+
+void
+CArchMiscWindows::deleteKey(HKEY key, const TCHAR* name)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ RegDeleteKey(key, name);
+}
+
+void
+CArchMiscWindows::deleteValue(HKEY key, const TCHAR* name)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ RegDeleteValue(key, name);
+}
+
+bool
+CArchMiscWindows::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));
+}
+
+CArchMiscWindows::EValueType
+CArchMiscWindows::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
+CArchMiscWindows::setValue(HKEY key,
+ const TCHAR* name, const std::string& value)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ RegSetValueEx(key, name, 0, REG_SZ,
+ reinterpret_cast(value.c_str()),
+ value.size() + 1);
+}
+
+void
+CArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ RegSetValueEx(key, name, 0, REG_DWORD,
+ reinterpret_cast(&value),
+ sizeof(DWORD));
+}
+
+void
+CArchMiscWindows::setValueBinary(HKEY key,
+ const TCHAR* name, const std::string& value)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ RegSetValueEx(key, name, 0, REG_BINARY,
+ reinterpret_cast(value.data()),
+ value.size());
+}
+
+std::string
+CArchMiscWindows::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(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
+CArchMiscWindows::readValueString(HKEY key, const TCHAR* name)
+{
+ return readBinaryOrString(key, name, REG_SZ);
+}
+
+std::string
+CArchMiscWindows::readValueBinary(HKEY key, const TCHAR* name)
+{
+ return readBinaryOrString(key, name, REG_BINARY);
+}
+
+DWORD
+CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name)
+{
+ DWORD type;
+ DWORD value;
+ DWORD size = sizeof(value);
+ LONG result = RegQueryValueEx(key, name, 0, &type,
+ reinterpret_cast(&value), &size);
+ if (result != ERROR_SUCCESS || type != REG_DWORD) {
+ return 0;
+ }
+ return value;
+}
+
+void
+CArchMiscWindows::addDialog(HWND hwnd)
+{
+ s_dialogs->insert(hwnd);
+}
+
+void
+CArchMiscWindows::removeDialog(HWND hwnd)
+{
+ s_dialogs->erase(hwnd);
+}
+
+bool
+CArchMiscWindows::processDialog(MSG* msg)
+{
+ for (CDialogs::const_iterator index = s_dialogs->begin();
+ index != s_dialogs->end(); ++index) {
+ if (IsDialogMessage(*index, msg)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+CArchMiscWindows::addBusyState(DWORD busyModes)
+{
+ s_busyState |= busyModes;
+ setThreadExecutionState(s_busyState);
+}
+
+void
+CArchMiscWindows::removeBusyState(DWORD busyModes)
+{
+ s_busyState &= ~busyModes;
+ setThreadExecutionState(s_busyState);
+}
+
+void
+CArchMiscWindows::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(GetProcAddress(kernel,
+ "SetThreadExecutionState"));
+ }
+ if (s_stes == NULL) {
+ s_stes = &CArchMiscWindows::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
+CArchMiscWindows::dummySetThreadExecutionState(DWORD)
+{
+ // do nothing
+ return 0;
+}
diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h
new file mode 100644
index 00000000..95a1d136
--- /dev/null
+++ b/lib/arch/CArchMiscWindows.h
@@ -0,0 +1,191 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHMISCWINDOWS_H
+#define CARCHMISCWINDOWS_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "common.h"
+#include "stdstring.h"
+#include "stdset.h"
+#include
+
+//! Miscellaneous win32 functions.
+class CArchMiscWindows {
+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();
+
+ //! Test if windows 95, et al.
+ /*!
+ Returns true iff the platform is win95/98/me.
+ */
+ static bool isWindows95Family();
+
+ //! Test if windows 95, et al.
+ /*!
+ Returns true iff the platform is win98 or win2k or higher (i.e.
+ not windows 95 or windows NT).
+ */
+ static bool isWindowsModern();
+
+ //! 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 CArchDaemonWindows.
+ */
+ static int runDaemon(RunFunc runFunc);
+
+ //! Indicate daemon is in main loop
+ /*!
+ Delegates to CArchDaemonWindows.
+ */
+ static void daemonRunning(bool running);
+
+ //! Indicate failure of running daemon
+ /*!
+ Delegates to CArchDaemonWindows.
+ */
+ static void daemonFailed(int result);
+
+ //! Get daemon quit message
+ /*!
+ Delegates to CArchDaemonWindows.
+ */
+ 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);
+
+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 CDialogs;
+ typedef DWORD (WINAPI *STES_t)(DWORD);
+
+ static CDialogs* s_dialogs;
+ static DWORD s_busyState;
+ static STES_t s_stes;
+ static HICON s_largeIcon;
+ static HICON s_smallIcon;
+};
+
+#endif
diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp
new file mode 100644
index 00000000..ec11fc50
--- /dev/null
+++ b/lib/arch/CArchMultithreadPosix.cpp
@@ -0,0 +1,806 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchMultithreadPosix.h"
+#include "CArch.h"
+#include "XArch.h"
+#include
+#if TIME_WITH_SYS_TIME
+# include
+# include
+#else
+# if HAVE_SYS_TIME_H
+# include
+# else
+# include
+# endif
+#endif
+#include
+
+#define SIGWAKEUP SIGUSR1
+
+#if !HAVE_PTHREAD_SIGNAL
+ // boy, is this platform broken. forget about pthread signal
+ // handling and let signals through to every process. synergy
+ // 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);
+}
+
+//
+// CArchThreadImpl
+//
+
+class CArchThreadImpl {
+public:
+ CArchThreadImpl();
+
+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;
+};
+
+CArchThreadImpl::CArchThreadImpl() :
+ 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
+}
+
+
+//
+// CArchMultithreadPosix
+//
+
+CArchMultithreadPosix* CArchMultithreadPosix::s_instance = NULL;
+
+CArchMultithreadPosix::CArchMultithreadPosix() :
+ 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 CArchThreadImpl;
+ 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);
+}
+
+CArchMultithreadPosix::~CArchMultithreadPosix()
+{
+ assert(s_instance != NULL);
+
+ closeMutex(m_threadMutex);
+ s_instance = NULL;
+}
+
+void
+CArchMultithreadPosix::setNetworkDataForCurrentThread(void* data)
+{
+ lockMutex(m_threadMutex);
+ CArchThreadImpl* thread = find(pthread_self());
+ thread->m_networkData = data;
+ unlockMutex(m_threadMutex);
+}
+
+void*
+CArchMultithreadPosix::getNetworkDataForThread(CArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* data = thread->m_networkData;
+ unlockMutex(m_threadMutex);
+ return data;
+}
+
+CArchMultithreadPosix*
+CArchMultithreadPosix::getInstance()
+{
+ return s_instance;
+}
+
+CArchCond
+CArchMultithreadPosix::newCondVar()
+{
+ CArchCondImpl* cond = new CArchCondImpl;
+ int status = pthread_cond_init(&cond->m_cond, NULL);
+ (void)status;
+ assert(status == 0);
+ return cond;
+}
+
+void
+CArchMultithreadPosix::closeCondVar(CArchCond cond)
+{
+ int status = pthread_cond_destroy(&cond->m_cond);
+ (void)status;
+ assert(status == 0);
+ delete cond;
+}
+
+void
+CArchMultithreadPosix::signalCondVar(CArchCond cond)
+{
+ int status = pthread_cond_signal(&cond->m_cond);
+ (void)status;
+ assert(status == 0);
+}
+
+void
+CArchMultithreadPosix::broadcastCondVar(CArchCond cond)
+{
+ int status = pthread_cond_broadcast(&cond->m_cond);
+ (void)status;
+ assert(status == 0);
+}
+
+bool
+CArchMultithreadPosix::waitCondVar(CArchCond cond,
+ CArchMutex 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;
+ }
+}
+
+CArchMutex
+CArchMultithreadPosix::newMutex()
+{
+ pthread_mutexattr_t attr;
+ int status = pthread_mutexattr_init(&attr);
+ assert(status == 0);
+ CArchMutexImpl* mutex = new CArchMutexImpl;
+ status = pthread_mutex_init(&mutex->m_mutex, &attr);
+ assert(status == 0);
+ return mutex;
+}
+
+void
+CArchMultithreadPosix::closeMutex(CArchMutex mutex)
+{
+ int status = pthread_mutex_destroy(&mutex->m_mutex);
+ (void)status;
+ assert(status == 0);
+ delete mutex;
+}
+
+void
+CArchMultithreadPosix::lockMutex(CArchMutex 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
+CArchMultithreadPosix::unlockMutex(CArchMutex 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;
+ }
+}
+
+CArchThread
+CArchMultithreadPosix::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
+ CArchThreadImpl* thread = new CArchThreadImpl;
+ 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,
+ &CArchMultithreadPosix::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;
+}
+
+CArchThread
+CArchMultithreadPosix::newCurrentThread()
+{
+ lockMutex(m_threadMutex);
+ CArchThreadImpl* thread = find(pthread_self());
+ unlockMutex(m_threadMutex);
+ assert(thread != NULL);
+ return thread;
+}
+
+void
+CArchMultithreadPosix::closeThread(CArchThread 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;
+ }
+}
+
+CArchThread
+CArchMultithreadPosix::copyThread(CArchThread thread)
+{
+ refThread(thread);
+ return thread;
+}
+
+void
+CArchMultithreadPosix::cancelThread(CArchThread 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
+CArchMultithreadPosix::setPriorityOfThread(CArchThread thread, int /*n*/)
+{
+ assert(thread != NULL);
+
+ // FIXME
+}
+
+void
+CArchMultithreadPosix::testCancelThread()
+{
+ // find current thread
+ lockMutex(m_threadMutex);
+ CArchThreadImpl* thread = findNoRef(pthread_self());
+ unlockMutex(m_threadMutex);
+
+ // test cancel on thread
+ testCancelThreadImpl(thread);
+}
+
+bool
+CArchMultithreadPosix::wait(CArchThread target, double timeout)
+{
+ assert(target != NULL);
+
+ lockMutex(m_threadMutex);
+
+ // find current thread
+ CArchThreadImpl* 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
+CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2)
+{
+ return (thread1 == thread2);
+}
+
+bool
+CArchMultithreadPosix::isExitedThread(CArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ bool exited = thread->m_exited;
+ unlockMutex(m_threadMutex);
+ return exited;
+}
+
+void*
+CArchMultithreadPosix::getResultOfThread(CArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* result = thread->m_result;
+ unlockMutex(m_threadMutex);
+ return result;
+}
+
+IArchMultithread::ThreadID
+CArchMultithreadPosix::getIDOfThread(CArchThread thread)
+{
+ return thread->m_id;
+}
+
+void
+CArchMultithreadPosix::setSignalHandler(
+ ESignal signal, SignalFunc func, void* userData)
+{
+ lockMutex(m_threadMutex);
+ m_signalFunc[signal] = func;
+ m_signalUserData[signal] = userData;
+ unlockMutex(m_threadMutex);
+}
+
+void
+CArchMultithreadPosix::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
+CArchMultithreadPosix::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,
+ &CArchMultithreadPosix::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);
+ }
+}
+
+CArchThreadImpl*
+CArchMultithreadPosix::find(pthread_t thread)
+{
+ CArchThreadImpl* impl = findNoRef(thread);
+ if (impl != NULL) {
+ refThread(impl);
+ }
+ return impl;
+}
+
+CArchThreadImpl*
+CArchMultithreadPosix::findNoRef(pthread_t thread)
+{
+ // linear search
+ for (CThreadList::const_iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if ((*index)->m_thread == thread) {
+ return *index;
+ }
+ }
+ return NULL;
+}
+
+void
+CArchMultithreadPosix::insert(CArchThreadImpl* 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 synergy running that long is vanishingly
+ // small.
+ thread->m_id = ++m_nextID;
+
+ // append to list
+ m_threadList.push_back(thread);
+}
+
+void
+CArchMultithreadPosix::erase(CArchThreadImpl* thread)
+{
+ for (CThreadList::iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if (*index == thread) {
+ m_threadList.erase(index);
+ break;
+ }
+ }
+}
+
+void
+CArchMultithreadPosix::refThread(CArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+ assert(findNoRef(thread->m_thread) != NULL);
+ ++thread->m_refCount;
+}
+
+void
+CArchMultithreadPosix::testCancelThreadImpl(CArchThreadImpl* 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*
+CArchMultithreadPosix::threadFunc(void* vrep)
+{
+ // get the thread
+ CArchThreadImpl* thread = reinterpret_cast(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
+CArchMultithreadPosix::doThreadFunc(CArchThread 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
+CArchMultithreadPosix::threadCancel(int)
+{
+ // do nothing
+}
+
+void*
+CArchMultithreadPosix::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/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h
new file mode 100644
index 00000000..4e587cfc
--- /dev/null
+++ b/lib/arch/CArchMultithreadPosix.h
@@ -0,0 +1,113 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHMULTITHREADPOSIX_H
+#define CARCHMULTITHREADPOSIX_H
+
+#include "IArchMultithread.h"
+#include "stdlist.h"
+#include
+
+#define ARCH_MULTITHREAD CArchMultithreadPosix
+
+class CArchCondImpl {
+public:
+ pthread_cond_t m_cond;
+};
+
+class CArchMutexImpl {
+public:
+ pthread_mutex_t m_mutex;
+};
+
+//! Posix implementation of IArchMultithread
+class CArchMultithreadPosix : public IArchMultithread {
+public:
+ CArchMultithreadPosix();
+ virtual ~CArchMultithreadPosix();
+
+ //! @name manipulators
+ //@{
+
+ void setNetworkDataForCurrentThread(void*);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ void* getNetworkDataForThread(CArchThread);
+
+ static CArchMultithreadPosix* getInstance();
+
+ //@}
+
+ // IArchMultithread overrides
+ virtual CArchCond newCondVar();
+ virtual void closeCondVar(CArchCond);
+ virtual void signalCondVar(CArchCond);
+ virtual void broadcastCondVar(CArchCond);
+ virtual bool waitCondVar(CArchCond, CArchMutex, double timeout);
+ virtual CArchMutex newMutex();
+ virtual void closeMutex(CArchMutex);
+ virtual void lockMutex(CArchMutex);
+ virtual void unlockMutex(CArchMutex);
+ virtual CArchThread newThread(ThreadFunc, void*);
+ virtual CArchThread newCurrentThread();
+ virtual CArchThread copyThread(CArchThread);
+ virtual void closeThread(CArchThread);
+ virtual void cancelThread(CArchThread);
+ virtual void setPriorityOfThread(CArchThread, int n);
+ virtual void testCancelThread();
+ virtual bool wait(CArchThread, double timeout);
+ virtual bool isSameThread(CArchThread, CArchThread);
+ virtual bool isExitedThread(CArchThread);
+ virtual void* getResultOfThread(CArchThread);
+ virtual ThreadID getIDOfThread(CArchThread);
+ virtual void setSignalHandler(ESignal, SignalFunc, void*);
+ virtual void raiseSignal(ESignal);
+
+private:
+ void startSignalHandler();
+
+ CArchThreadImpl* find(pthread_t thread);
+ CArchThreadImpl* findNoRef(pthread_t thread);
+ void insert(CArchThreadImpl* thread);
+ void erase(CArchThreadImpl* thread);
+
+ void refThread(CArchThreadImpl* rep);
+ void testCancelThreadImpl(CArchThreadImpl* rep);
+
+ void doThreadFunc(CArchThread thread);
+ static void* threadFunc(void* vrep);
+ static void threadCancel(int);
+ static void* threadSignalHandler(void* vrep);
+
+private:
+ typedef std::list CThreadList;
+
+ static CArchMultithreadPosix* s_instance;
+
+ bool m_newThreadCalled;
+
+ CArchMutex m_threadMutex;
+ CArchThread m_mainThread;
+ CThreadList m_threadList;
+ ThreadID m_nextID;
+
+ pthread_t m_signalThread;
+ SignalFunc m_signalFunc[kNUM_SIGNALS];
+ void* m_signalUserData[kNUM_SIGNALS];
+};
+
+#endif
diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp
new file mode 100644
index 00000000..3adcd46a
--- /dev/null
+++ b/lib/arch/CArchMultithreadWindows.cpp
@@ -0,0 +1,699 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#if defined(_MSC_VER) && !defined(_MT)
+# error multithreading compile option is required
+#endif
+
+#include "CArchMultithreadWindows.h"
+#include "CArch.h"
+#include "XArch.h"
+#include
+
+//
+// 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.
+//
+
+//
+// CArchThreadImpl
+//
+
+class CArchThreadImpl {
+public:
+ CArchThreadImpl();
+ ~CArchThreadImpl();
+
+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;
+};
+
+CArchThreadImpl::CArchThreadImpl() :
+ 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);
+}
+
+CArchThreadImpl::~CArchThreadImpl()
+{
+ CloseHandle(m_exit);
+ CloseHandle(m_cancel);
+}
+
+
+//
+// CArchMultithreadWindows
+//
+
+CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL;
+
+CArchMultithreadWindows::CArchMultithreadWindows()
+{
+ 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 CArchThreadImpl;
+ m_mainThread->m_thread = NULL;
+ m_mainThread->m_id = GetCurrentThreadId();
+ insert(m_mainThread);
+}
+
+CArchMultithreadWindows::~CArchMultithreadWindows()
+{
+ s_instance = NULL;
+
+ // clean up thread list
+ for (CThreadList::iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ delete *index;
+ }
+
+ // done with mutex
+ delete m_threadMutex;
+}
+
+void
+CArchMultithreadWindows::setNetworkDataForCurrentThread(void* data)
+{
+ lockMutex(m_threadMutex);
+ CArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
+ thread->m_networkData = data;
+ unlockMutex(m_threadMutex);
+}
+
+void*
+CArchMultithreadWindows::getNetworkDataForThread(CArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* data = thread->m_networkData;
+ unlockMutex(m_threadMutex);
+ return data;
+}
+
+HANDLE
+CArchMultithreadWindows::getCancelEventForCurrentThread()
+{
+ lockMutex(m_threadMutex);
+ CArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
+ unlockMutex(m_threadMutex);
+ return thread->m_cancel;
+}
+
+CArchMultithreadWindows*
+CArchMultithreadWindows::getInstance()
+{
+ return s_instance;
+}
+
+CArchCond
+CArchMultithreadWindows::newCondVar()
+{
+ CArchCondImpl* cond = new CArchCondImpl;
+ cond->m_events[CArchCondImpl::kSignal] = CreateEvent(NULL,
+ FALSE, FALSE, NULL);
+ cond->m_events[CArchCondImpl::kBroadcast] = CreateEvent(NULL,
+ TRUE, FALSE, NULL);
+ cond->m_waitCountMutex = newMutex();
+ cond->m_waitCount = 0;
+ return cond;
+}
+
+void
+CArchMultithreadWindows::closeCondVar(CArchCond cond)
+{
+ CloseHandle(cond->m_events[CArchCondImpl::kSignal]);
+ CloseHandle(cond->m_events[CArchCondImpl::kBroadcast]);
+ closeMutex(cond->m_waitCountMutex);
+ delete cond;
+}
+
+void
+CArchMultithreadWindows::signalCondVar(CArchCond 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[CArchCondImpl::kSignal]);
+ }
+}
+
+void
+CArchMultithreadWindows::broadcastCondVar(CArchCond 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[CArchCondImpl::kBroadcast]);
+ }
+}
+
+bool
+CArchMultithreadWindows::waitCondVar(CArchCond cond,
+ CArchMutex mutex, double timeout)
+{
+ // prepare to wait
+ const DWORD winTimeout = (timeout < 0.0) ? INFINITE :
+ static_cast(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[CArchCondImpl::kSignal];
+ handles[1] = cond->m_events[CArchCondImpl::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
+ 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[CArchCondImpl::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);
+}
+
+CArchMutex
+CArchMultithreadWindows::newMutex()
+{
+ CArchMutexImpl* mutex = new CArchMutexImpl;
+ InitializeCriticalSection(&mutex->m_mutex);
+ return mutex;
+}
+
+void
+CArchMultithreadWindows::closeMutex(CArchMutex mutex)
+{
+ DeleteCriticalSection(&mutex->m_mutex);
+ delete mutex;
+}
+
+void
+CArchMultithreadWindows::lockMutex(CArchMutex mutex)
+{
+ EnterCriticalSection(&mutex->m_mutex);
+}
+
+void
+CArchMultithreadWindows::unlockMutex(CArchMutex mutex)
+{
+ LeaveCriticalSection(&mutex->m_mutex);
+}
+
+CArchThread
+CArchMultithreadWindows::newThread(ThreadFunc func, void* data)
+{
+ lockMutex(m_threadMutex);
+
+ // create thread impl for new thread
+ CArchThreadImpl* thread = new CArchThreadImpl;
+ thread->m_func = func;
+ thread->m_userData = data;
+
+ // create thread
+ unsigned int id;
+ thread->m_thread = reinterpret_cast(_beginthreadex(NULL, 0,
+ threadFunc, (void*)thread, 0, &id));
+ thread->m_id = static_cast(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;
+}
+
+CArchThread
+CArchMultithreadWindows::newCurrentThread()
+{
+ lockMutex(m_threadMutex);
+ CArchThreadImpl* thread = find(GetCurrentThreadId());
+ unlockMutex(m_threadMutex);
+ assert(thread != NULL);
+ return thread;
+}
+
+void
+CArchMultithreadWindows::closeThread(CArchThread 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;
+ }
+}
+
+CArchThread
+CArchMultithreadWindows::copyThread(CArchThread thread)
+{
+ refThread(thread);
+ return thread;
+}
+
+void
+CArchMultithreadWindows::cancelThread(CArchThread thread)
+{
+ assert(thread != NULL);
+
+ // set cancel flag
+ SetEvent(thread->m_cancel);
+}
+
+void
+CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n)
+{
+ struct CPriorityInfo {
+ public:
+ DWORD m_class;
+ int m_level;
+ };
+ static const CPriorityInfo 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
+CArchMultithreadWindows::testCancelThread()
+{
+ // find current thread
+ lockMutex(m_threadMutex);
+ CArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
+ unlockMutex(m_threadMutex);
+
+ // test cancel on thread
+ testCancelThreadImpl(thread);
+}
+
+bool
+CArchMultithreadWindows::wait(CArchThread target, double timeout)
+{
+ assert(target != NULL);
+
+ lockMutex(m_threadMutex);
+
+ // find current thread
+ CArchThreadImpl* 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
+CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2)
+{
+ return (thread1 == thread2);
+}
+
+bool
+CArchMultithreadWindows::isExitedThread(CArchThread thread)
+{
+ // poll exit event
+ return (WaitForSingleObject(thread->m_exit, 0) == WAIT_OBJECT_0);
+}
+
+void*
+CArchMultithreadWindows::getResultOfThread(CArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* result = thread->m_result;
+ unlockMutex(m_threadMutex);
+ return result;
+}
+
+IArchMultithread::ThreadID
+CArchMultithreadWindows::getIDOfThread(CArchThread thread)
+{
+ return static_cast(thread->m_id);
+}
+
+void
+CArchMultithreadWindows::setSignalHandler(
+ ESignal signal, SignalFunc func, void* userData)
+{
+ lockMutex(m_threadMutex);
+ m_signalFunc[signal] = func;
+ m_signalUserData[signal] = userData;
+ unlockMutex(m_threadMutex);
+}
+
+void
+CArchMultithreadWindows::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);
+}
+
+CArchThreadImpl*
+CArchMultithreadWindows::find(DWORD id)
+{
+ CArchThreadImpl* impl = findNoRef(id);
+ if (impl != NULL) {
+ refThread(impl);
+ }
+ return impl;
+}
+
+CArchThreadImpl*
+CArchMultithreadWindows::findNoRef(DWORD id)
+{
+ CArchThreadImpl* 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 CArchThreadImpl;
+ impl->m_thread = NULL;
+ impl->m_id = GetCurrentThreadId();
+ insert(impl);
+ }
+ return impl;
+}
+
+CArchThreadImpl*
+CArchMultithreadWindows::findNoRefOrCreate(DWORD id)
+{
+ // linear search
+ for (CThreadList::const_iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if ((*index)->m_id == id) {
+ return *index;
+ }
+ }
+ return NULL;
+}
+
+void
+CArchMultithreadWindows::insert(CArchThreadImpl* 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
+CArchMultithreadWindows::erase(CArchThreadImpl* thread)
+{
+ for (CThreadList::iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if (*index == thread) {
+ m_threadList.erase(index);
+ break;
+ }
+ }
+}
+
+void
+CArchMultithreadWindows::refThread(CArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+ assert(findNoRefOrCreate(thread->m_id) != NULL);
+ ++thread->m_refCount;
+}
+
+void
+CArchMultithreadWindows::testCancelThreadImpl(CArchThreadImpl* 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
+CArchMultithreadWindows::threadFunc(void* vrep)
+{
+ // get the thread
+ CArchThreadImpl* thread = reinterpret_cast(vrep);
+
+ // run thread
+ s_instance->doThreadFunc(thread);
+
+ // terminate the thread
+ return 0;
+}
+
+void
+CArchMultithreadWindows::doThreadFunc(CArchThread 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/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h
new file mode 100644
index 00000000..d009c842
--- /dev/null
+++ b/lib/arch/CArchMultithreadWindows.h
@@ -0,0 +1,115 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHMULTITHREADWINDOWS_H
+#define CARCHMULTITHREADWINDOWS_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "IArchMultithread.h"
+#include "stdlist.h"
+#include
+
+#define ARCH_MULTITHREAD CArchMultithreadWindows
+
+class CArchCondImpl {
+public:
+ enum { kSignal = 0, kBroadcast };
+
+ HANDLE m_events[2];
+ mutable int m_waitCount;
+ CArchMutex m_waitCountMutex;
+};
+
+class CArchMutexImpl {
+public:
+ CRITICAL_SECTION m_mutex;
+};
+
+//! Win32 implementation of IArchMultithread
+class CArchMultithreadWindows : public IArchMultithread {
+public:
+ CArchMultithreadWindows();
+ virtual ~CArchMultithreadWindows();
+
+ //! @name manipulators
+ //@{
+
+ void setNetworkDataForCurrentThread(void*);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ HANDLE getCancelEventForCurrentThread();
+
+ void* getNetworkDataForThread(CArchThread);
+
+ static CArchMultithreadWindows* getInstance();
+
+ //@}
+
+ // IArchMultithread overrides
+ virtual CArchCond newCondVar();
+ virtual void closeCondVar(CArchCond);
+ virtual void signalCondVar(CArchCond);
+ virtual void broadcastCondVar(CArchCond);
+ virtual bool waitCondVar(CArchCond, CArchMutex, double timeout);
+ virtual CArchMutex newMutex();
+ virtual void closeMutex(CArchMutex);
+ virtual void lockMutex(CArchMutex);
+ virtual void unlockMutex(CArchMutex);
+ virtual CArchThread newThread(ThreadFunc, void*);
+ virtual CArchThread newCurrentThread();
+ virtual CArchThread copyThread(CArchThread);
+ virtual void closeThread(CArchThread);
+ virtual void cancelThread(CArchThread);
+ virtual void setPriorityOfThread(CArchThread, int n);
+ virtual void testCancelThread();
+ virtual bool wait(CArchThread, double timeout);
+ virtual bool isSameThread(CArchThread, CArchThread);
+ virtual bool isExitedThread(CArchThread);
+ virtual void* getResultOfThread(CArchThread);
+ virtual ThreadID getIDOfThread(CArchThread);
+ virtual void setSignalHandler(ESignal, SignalFunc, void*);
+ virtual void raiseSignal(ESignal);
+
+private:
+ CArchThreadImpl* find(DWORD id);
+ CArchThreadImpl* findNoRef(DWORD id);
+ CArchThreadImpl* findNoRefOrCreate(DWORD id);
+ void insert(CArchThreadImpl* thread);
+ void erase(CArchThreadImpl* thread);
+
+ void refThread(CArchThreadImpl* rep);
+ void testCancelThreadImpl(CArchThreadImpl* rep);
+
+ void doThreadFunc(CArchThread thread);
+ static unsigned int __stdcall threadFunc(void* vrep);
+
+private:
+ typedef std::list CThreadList;
+
+ static CArchMultithreadWindows* s_instance;
+
+ CArchMutex m_threadMutex;
+
+ CThreadList m_threadList;
+ CArchThread m_mainThread;
+
+ SignalFunc m_signalFunc[kNUM_SIGNALS];
+ void* m_signalUserData[kNUM_SIGNALS];
+};
+
+#endif
diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp
new file mode 100644
index 00000000..af474e86
--- /dev/null
+++ b/lib/arch/CArchNetworkBSD.cpp
@@ -0,0 +1,974 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchNetworkBSD.h"
+#include "CArch.h"
+#include "CArchMultithreadPosix.h"
+#include "XArchUnix.h"
+#if HAVE_UNISTD_H
+# include
+#endif
+#include
+#include
+#if !defined(TCP_NODELAY)
+# include
+#endif
+#include
+#include
+#include
+#include
+
+#if HAVE_POLL
+# include
+#else
+# if HAVE_SYS_SELECT_H
+# include
+# endif
+# if HAVE_SYS_TIME_H
+# include
+# endif
+#endif
+
+#if !HAVE_INET_ATON
+# include
+#endif
+
+static const int s_family[] = {
+ PF_UNSPEC,
+ PF_INET
+};
+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
+
+//
+// CArchNetworkBSD
+//
+
+CArchNetworkBSD::CArchNetworkBSD()
+{
+ // create mutex to make some calls thread safe
+ m_mutex = ARCH->newMutex();
+}
+
+CArchNetworkBSD::~CArchNetworkBSD()
+{
+ ARCH->closeMutex(m_mutex);
+}
+
+CArchSocket
+CArchNetworkBSD::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
+ CArchSocketImpl* newSocket = new CArchSocketImpl;
+ newSocket->m_fd = fd;
+ newSocket->m_refCount = 1;
+ return newSocket;
+}
+
+CArchSocket
+CArchNetworkBSD::copySocket(CArchSocket s)
+{
+ assert(s != NULL);
+
+ // ref the socket and return it
+ ARCH->lockMutex(m_mutex);
+ ++s->m_refCount;
+ ARCH->unlockMutex(m_mutex);
+ return s;
+}
+
+void
+CArchNetworkBSD::closeSocket(CArchSocket 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
+CArchNetworkBSD::closeSocketForRead(CArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown(s->m_fd, 0) == -1) {
+ if (errno != ENOTCONN) {
+ throwError(errno);
+ }
+ }
+}
+
+void
+CArchNetworkBSD::closeSocketForWrite(CArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown(s->m_fd, 1) == -1) {
+ if (errno != ENOTCONN) {
+ throwError(errno);
+ }
+ }
+}
+
+void
+CArchNetworkBSD::bindSocket(CArchSocket s, CArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (bind(s->m_fd, &addr->m_addr, addr->m_len) == -1) {
+ throwError(errno);
+ }
+}
+
+void
+CArchNetworkBSD::listenOnSocket(CArchSocket s)
+{
+ assert(s != NULL);
+
+ // hardcoding backlog
+ if (listen(s->m_fd, 3) == -1) {
+ throwError(errno);
+ }
+}
+
+CArchSocket
+CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr)
+{
+ assert(s != NULL);
+
+ // if user passed NULL in addr then use scratch space
+ CArchNetAddress dummy;
+ if (addr == NULL) {
+ addr = &dummy;
+ }
+
+ // create new socket and address
+ CArchSocketImpl* newSocket = new CArchSocketImpl;
+ *addr = new CArchNetAddressImpl;
+
+ // accept on socket
+ ACCEPT_TYPE_ARG3 len = (ACCEPT_TYPE_ARG3)((*addr)->m_len);
+ int fd = accept(s->m_fd, &(*addr)->m_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
+CArchNetworkBSD::connectSocket(CArchSocket s, CArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (connect(s->m_fd, &addr->m_addr, addr->m_len) == -1) {
+ if (errno == EISCONN) {
+ return true;
+ }
+ if (errno == EINPROGRESS) {
+ return false;
+ }
+ throwError(errno);
+ }
+ return true;
+}
+
+#if HAVE_POLL
+
+int
+CArchNetworkBSD::pollSocket(CPollEntry 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(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];
+ do {
+ 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
+CArchNetworkBSD::pollSocket(CPollEntry 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(timeout);
+ timeout2.tv_usec = static_cast(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
+CArchNetworkBSD::unblockPollSocket(CArchThread thread)
+{
+ const int* unblockPipe = getUnblockPipeForThread(thread);
+ if (unblockPipe != NULL) {
+ char dummy = 0;
+ write(unblockPipe[1], &dummy, 1);
+ }
+}
+
+size_t
+CArchNetworkBSD::readSocket(CArchSocket 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
+CArchNetworkBSD::writeSocket(CArchSocket 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
+CArchNetworkBSD::throwErrorOnSocket(CArchSocket 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
+CArchNetworkBSD::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
+CArchNetworkBSD::setNoDelayOnSocket(CArchSocket 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
+CArchNetworkBSD::setReuseAddrOnSocket(CArchSocket 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
+CArchNetworkBSD::getHostName()
+{
+ char name[256];
+ if (gethostname(name, sizeof(name)) == -1) {
+ name[0] = '\0';
+ }
+ else {
+ name[sizeof(name) - 1] = '\0';
+ }
+ return name;
+}
+
+CArchNetAddress
+CArchNetworkBSD::newAnyAddr(EAddressFamily family)
+{
+ // allocate address
+ CArchNetAddressImpl* addr = new CArchNetAddressImpl;
+
+ // fill it in
+ switch (family) {
+ case kINET: {
+ struct sockaddr_in* ipAddr =
+ reinterpret_cast(&addr->m_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;
+ }
+
+ default:
+ delete addr;
+ assert(0 && "invalid family");
+ }
+
+ return addr;
+}
+
+CArchNetAddress
+CArchNetworkBSD::copyAddr(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ // allocate and copy address
+ return new CArchNetAddressImpl(*addr);
+}
+
+CArchNetAddress
+CArchNetworkBSD::nameToAddr(const std::string& name)
+{
+ // allocate address
+ CArchNetAddressImpl* addr = new CArchNetAddressImpl;
+
+ // try to convert assuming an IPv4 dot notation address
+ struct sockaddr_in inaddr;
+ memset(&inaddr, 0, sizeof(inaddr));
+ if (inet_aton(name.c_str(), &inaddr.sin_addr) != 0) {
+ // it's a dot notation address
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
+ inaddr.sin_family = AF_INET;
+ inaddr.sin_port = 0;
+ memcpy(&addr->m_addr, &inaddr, addr->m_len);
+ }
+
+ else {
+ // mutexed address lookup (ugh)
+ ARCH->lockMutex(m_mutex);
+ struct hostent* info = gethostbyname(name.c_str());
+ if (info == NULL) {
+ ARCH->unlockMutex(m_mutex);
+ delete addr;
+ throwNameError(h_errno);
+ }
+
+ // copy over address (only IPv4 currently supported)
+ if (info->h_addrtype == AF_INET) {
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
+ inaddr.sin_family = info->h_addrtype;
+ inaddr.sin_port = 0;
+ memcpy(&inaddr.sin_addr, info->h_addr_list[0],
+ sizeof(inaddr.sin_addr));
+ memcpy(&addr->m_addr, &inaddr, addr->m_len);
+ }
+ else {
+ ARCH->unlockMutex(m_mutex);
+ delete addr;
+ throw XArchNetworkNameUnsupported(
+ "The requested name is valid but "
+ "does not have a supported address family");
+ }
+
+ // done with static buffer
+ ARCH->unlockMutex(m_mutex);
+ }
+
+ return addr;
+}
+
+void
+CArchNetworkBSD::closeAddr(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ delete addr;
+}
+
+std::string
+CArchNetworkBSD::addrToName(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ // mutexed name lookup (ugh)
+ ARCH->lockMutex(m_mutex);
+ struct hostent* info = gethostbyaddr(
+ reinterpret_cast(&addr->m_addr),
+ addr->m_len, addr->m_addr.sa_family);
+ if (info == NULL) {
+ ARCH->unlockMutex(m_mutex);
+ throwNameError(h_errno);
+ }
+
+ // save (primary) name
+ std::string name = info->h_name;
+
+ // done with static buffer
+ ARCH->unlockMutex(m_mutex);
+
+ return name;
+}
+
+std::string
+CArchNetworkBSD::addrToString(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ struct sockaddr_in* ipAddr =
+ reinterpret_cast(&addr->m_addr);
+ ARCH->lockMutex(m_mutex);
+ std::string s = inet_ntoa(ipAddr->sin_addr);
+ ARCH->unlockMutex(m_mutex);
+ return s;
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return "";
+ }
+}
+
+IArchNetwork::EAddressFamily
+CArchNetworkBSD::getAddrFamily(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (addr->m_addr.sa_family) {
+ case AF_INET:
+ return kINET;
+
+ default:
+ return kUNKNOWN;
+ }
+}
+
+void
+CArchNetworkBSD::setAddrPort(CArchNetAddress addr, int port)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ struct sockaddr_in* ipAddr =
+ reinterpret_cast(&addr->m_addr);
+ ipAddr->sin_port = htons(port);
+ break;
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ break;
+ }
+}
+
+int
+CArchNetworkBSD::getAddrPort(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ struct sockaddr_in* ipAddr =
+ reinterpret_cast(&addr->m_addr);
+ return ntohs(ipAddr->sin_port);
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return 0;
+ }
+}
+
+bool
+CArchNetworkBSD::isAnyAddr(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ struct sockaddr_in* ipAddr =
+ reinterpret_cast(&addr->m_addr);
+ return (ipAddr->sin_addr.s_addr == INADDR_ANY &&
+ addr->m_len == (socklen_t)sizeof(struct sockaddr_in));
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return true;
+ }
+}
+
+bool
+CArchNetworkBSD::isEqualAddr(CArchNetAddress a, CArchNetAddress b)
+{
+ return (a->m_len == b->m_len &&
+ memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0);
+}
+
+const int*
+CArchNetworkBSD::getUnblockPipe()
+{
+ CArchMultithreadPosix* mt = CArchMultithreadPosix::getInstance();
+ CArchThread thread = mt->newCurrentThread();
+ const int* p = getUnblockPipeForThread(thread);
+ ARCH->closeThread(thread);
+ return p;
+}
+
+const int*
+CArchNetworkBSD::getUnblockPipeForThread(CArchThread thread)
+{
+ CArchMultithreadPosix* mt = CArchMultithreadPosix::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
+CArchNetworkBSD::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
+CArchNetworkBSD::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/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h
new file mode 100644
index 00000000..bba60272
--- /dev/null
+++ b/lib/arch/CArchNetworkBSD.h
@@ -0,0 +1,101 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHNETWORKBSD_H
+#define CARCHNETWORKBSD_H
+
+#include "IArchNetwork.h"
+#include "IArchMultithread.h"
+#if HAVE_SYS_TYPES_H
+# include
+#endif
+#if HAVE_SYS_SOCKET_H
+# include
+#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 CArchNetworkBSD
+
+class CArchSocketImpl {
+public:
+ int m_fd;
+ int m_refCount;
+};
+
+class CArchNetAddressImpl {
+public:
+ CArchNetAddressImpl() : m_len(sizeof(m_addr)) { }
+
+public:
+ struct sockaddr m_addr;
+ socklen_t m_len;
+};
+
+//! Berkeley (BSD) sockets implementation of IArchNetwork
+class CArchNetworkBSD : public IArchNetwork {
+public:
+ CArchNetworkBSD();
+ virtual ~CArchNetworkBSD();
+
+ // IArchNetwork overrides
+ virtual CArchSocket newSocket(EAddressFamily, ESocketType);
+ virtual CArchSocket copySocket(CArchSocket s);
+ virtual void closeSocket(CArchSocket s);
+ virtual void closeSocketForRead(CArchSocket s);
+ virtual void closeSocketForWrite(CArchSocket s);
+ virtual void bindSocket(CArchSocket s, CArchNetAddress addr);
+ virtual void listenOnSocket(CArchSocket s);
+ virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr);
+ virtual bool connectSocket(CArchSocket s, CArchNetAddress name);
+ virtual int pollSocket(CPollEntry[], int num, double timeout);
+ virtual void unblockPollSocket(CArchThread thread);
+ virtual size_t readSocket(CArchSocket s, void* buf, size_t len);
+ virtual size_t writeSocket(CArchSocket s,
+ const void* buf, size_t len);
+ virtual void throwErrorOnSocket(CArchSocket);
+ virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay);
+ virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse);
+ virtual std::string getHostName();
+ virtual CArchNetAddress newAnyAddr(EAddressFamily);
+ virtual CArchNetAddress copyAddr(CArchNetAddress);
+ virtual CArchNetAddress nameToAddr(const std::string&);
+ virtual void closeAddr(CArchNetAddress);
+ virtual std::string addrToName(CArchNetAddress);
+ virtual std::string addrToString(CArchNetAddress);
+ virtual EAddressFamily getAddrFamily(CArchNetAddress);
+ virtual void setAddrPort(CArchNetAddress, int port);
+ virtual int getAddrPort(CArchNetAddress);
+ virtual bool isAnyAddr(CArchNetAddress);
+ virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress);
+
+private:
+ const int* getUnblockPipe();
+ const int* getUnblockPipeForThread(CArchThread);
+ void setBlockingOnSocket(int fd, bool blocking);
+ void throwError(int);
+ void throwNameError(int);
+
+private:
+ CArchMutex m_mutex;
+};
+
+#endif
diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp
new file mode 100644
index 00000000..ac40596a
--- /dev/null
+++ b/lib/arch/CArchNetworkWinsock.cpp
@@ -0,0 +1,934 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include "CArchNetworkWinsock.h"
+#include "CArch.h"
+#include "CArchMultithreadWindows.h"
+#include "IArchMultithread.h"
+#include "XArchWindows.h"
+#include
+
+static const int s_family[] = {
+ PF_UNSPEC,
+ PF_INET
+};
+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;
+}
+
+CArchNetAddressImpl*
+CArchNetAddressImpl::alloc(size_t size)
+{
+ size_t totalSize = size + ADDR_HDR_SIZE;
+ CArchNetAddressImpl* addr = (CArchNetAddressImpl*)malloc(totalSize);
+ addr->m_len = size;
+ return addr;
+}
+
+
+//
+// CArchNetworkWinsock
+//
+
+CArchNetworkWinsock::CArchNetworkWinsock()
+{
+ 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 {
+ init((HMODULE)::LoadLibrary(s_library[i]));
+ m_mutex = ARCH->newMutex();
+ return;
+ }
+ catch (XArchNetwork&) {
+ // ignore
+ }
+ }
+
+ // can't initialize any library
+ throw XArchNetworkSupport("Cannot load winsock library");
+}
+
+CArchNetworkWinsock::~CArchNetworkWinsock()
+{
+ if (s_networkModule != NULL) {
+ WSACleanup_winsock();
+ ::FreeLibrary(s_networkModule);
+
+ WSACleanup_winsock = NULL;
+ s_networkModule = NULL;
+ }
+ ARCH->closeMutex(m_mutex);
+}
+
+void
+CArchNetworkWinsock::init(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*/, 0 /*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;
+}
+
+CArchSocket
+CArchNetworkWinsock::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);
+ }
+ catch (...) {
+ close_winsock(fd);
+ throw;
+ }
+
+ // allocate socket object
+ CArchSocketImpl* socket = new CArchSocketImpl;
+ socket->m_socket = fd;
+ socket->m_refCount = 1;
+ socket->m_event = WSACreateEvent_winsock();
+ socket->m_pollWrite = true;
+ return socket;
+}
+
+CArchSocket
+CArchNetworkWinsock::copySocket(CArchSocket s)
+{
+ assert(s != NULL);
+
+ // ref the socket and return it
+ ARCH->lockMutex(m_mutex);
+ ++s->m_refCount;
+ ARCH->unlockMutex(m_mutex);
+ return s;
+}
+
+void
+CArchNetworkWinsock::closeSocket(CArchSocket 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
+CArchNetworkWinsock::closeSocketForRead(CArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown_winsock(s->m_socket, SD_RECEIVE) == SOCKET_ERROR) {
+ if (getsockerror_winsock() != WSAENOTCONN) {
+ throwError(getsockerror_winsock());
+ }
+ }
+}
+
+void
+CArchNetworkWinsock::closeSocketForWrite(CArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown_winsock(s->m_socket, SD_SEND) == SOCKET_ERROR) {
+ if (getsockerror_winsock() != WSAENOTCONN) {
+ throwError(getsockerror_winsock());
+ }
+ }
+}
+
+void
+CArchNetworkWinsock::bindSocket(CArchSocket s, CArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (bind_winsock(s->m_socket, &addr->m_addr, addr->m_len) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+}
+
+void
+CArchNetworkWinsock::listenOnSocket(CArchSocket s)
+{
+ assert(s != NULL);
+
+ // hardcoding backlog
+ if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+}
+
+CArchSocket
+CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr)
+{
+ assert(s != NULL);
+
+ // create new socket and temporary address
+ CArchSocketImpl* socket = new CArchSocketImpl;
+ CArchNetAddress tmp = CArchNetAddressImpl::alloc(sizeof(struct sockaddr));
+
+ // accept on socket
+ SOCKET fd = accept_winsock(s->m_socket, &tmp->m_addr, &tmp->m_len);
+ if (fd == INVALID_SOCKET) {
+ int err = getsockerror_winsock();
+ delete socket;
+ free(tmp);
+ *addr = NULL;
+ if (err == WSAEWOULDBLOCK) {
+ return NULL;
+ }
+ throwError(err);
+ }
+
+ try {
+ setBlockingOnSocket(fd, false);
+ }
+ catch (...) {
+ close_winsock(fd);
+ delete socket;
+ free(tmp);
+ *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
+CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (connect_winsock(s->m_socket, &addr->m_addr,
+ addr->m_len) == SOCKET_ERROR) {
+ if (getsockerror_winsock() == WSAEISCONN) {
+ return true;
+ }
+ if (getsockerror_winsock() == WSAEWOULDBLOCK) {
+ return false;
+ }
+ throwError(getsockerror_winsock());
+ }
+ return true;
+}
+
+int
+CArchNetworkWinsock::pollSocket(CPollEntry 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
+ CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance();
+ CArchThread thread = mt->newCurrentThread();
+ WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread);
+ ARCH->closeThread(thread);
+ if (unblockEvent == NULL) {
+ unblockEvent = new WSAEVENT;
+ *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
+CArchNetworkWinsock::unblockPollSocket(CArchThread thread)
+{
+ // set the unblock event
+ CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance();
+ WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread);
+ if (unblockEvent != NULL) {
+ WSASetEvent_winsock(*unblockEvent);
+ }
+}
+
+size_t
+CArchNetworkWinsock::readSocket(CArchSocket s, void* buf, size_t len)
+{
+ assert(s != NULL);
+
+ int n = recv_winsock(s->m_socket, buf, len, 0);
+ if (n == SOCKET_ERROR) {
+ int err = getsockerror_winsock();
+ if (err == WSAEINTR || err == WSAEWOULDBLOCK) {
+ return 0;
+ }
+ throwError(err);
+ }
+ return static_cast(n);
+}
+
+size_t
+CArchNetworkWinsock::writeSocket(CArchSocket s, const void* buf, size_t len)
+{
+ assert(s != NULL);
+
+ int n = send_winsock(s->m_socket, buf, 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(n);
+}
+
+void
+CArchNetworkWinsock::throwErrorOnSocket(CArchSocket 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
+CArchNetworkWinsock::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
+CArchNetworkWinsock::setNoDelayOnSocket(CArchSocket 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
+CArchNetworkWinsock::setReuseAddrOnSocket(CArchSocket 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
+CArchNetworkWinsock::getHostName()
+{
+ char name[256];
+ if (gethostname_winsock(name, sizeof(name)) == -1) {
+ name[0] = '\0';
+ }
+ else {
+ name[sizeof(name) - 1] = '\0';
+ }
+ return name;
+}
+
+CArchNetAddress
+CArchNetworkWinsock::newAnyAddr(EAddressFamily family)
+{
+ CArchNetAddressImpl* addr = NULL;
+ switch (family) {
+ case kINET: {
+ addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
+ struct sockaddr_in* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ ipAddr->sin_family = AF_INET;
+ ipAddr->sin_port = 0;
+ ipAddr->sin_addr.s_addr = INADDR_ANY;
+ break;
+ }
+
+ default:
+ assert(0 && "invalid family");
+ }
+ return addr;
+}
+
+CArchNetAddress
+CArchNetworkWinsock::copyAddr(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ CArchNetAddressImpl* copy = CArchNetAddressImpl::alloc(addr->m_len);
+ memcpy(TYPED_ADDR(void, copy), TYPED_ADDR(void, addr), addr->m_len);
+ return copy;
+}
+
+CArchNetAddress
+CArchNetworkWinsock::nameToAddr(const std::string& name)
+{
+ // allocate address
+ CArchNetAddressImpl* addr = NULL;
+
+ // try to convert assuming an IPv4 dot notation address
+ struct sockaddr_in inaddr;
+ memset(&inaddr, 0, sizeof(inaddr));
+ inaddr.sin_family = AF_INET;
+ inaddr.sin_port = 0;
+ inaddr.sin_addr.s_addr = inet_addr_winsock(name.c_str());
+ if (inaddr.sin_addr.s_addr != INADDR_NONE) {
+ // it's a dot notation address
+ addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
+ memcpy(TYPED_ADDR(void, addr), &inaddr, addr->m_len);
+ }
+
+ else {
+ // address lookup
+ struct hostent* info = gethostbyname_winsock(name.c_str());
+ if (info == NULL) {
+ throwNameError(getsockerror_winsock());
+ }
+
+ // copy over address (only IPv4 currently supported)
+ if (info->h_addrtype == AF_INET) {
+ addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
+ memcpy(&inaddr.sin_addr, info->h_addr_list[0],
+ sizeof(inaddr.sin_addr));
+ memcpy(TYPED_ADDR(void, addr), &inaddr, addr->m_len);
+ }
+ else {
+ throw XArchNetworkNameUnsupported(
+ "The requested name is valid but "
+ "does not have a supported address family");
+ }
+ }
+
+ return addr;
+}
+
+void
+CArchNetworkWinsock::closeAddr(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ free(addr);
+}
+
+std::string
+CArchNetworkWinsock::addrToName(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ // name lookup
+ struct hostent* info = gethostbyaddr_winsock(
+ reinterpret_cast(&addr->m_addr),
+ addr->m_len, addr->m_addr.sa_family);
+ if (info == NULL) {
+ throwNameError(getsockerror_winsock());
+ }
+
+ // return (primary) name
+ return info->h_name;
+}
+
+std::string
+CArchNetworkWinsock::addrToString(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ struct sockaddr_in* ipAddr =
+ reinterpret_cast(&addr->m_addr);
+ return inet_ntoa_winsock(ipAddr->sin_addr);
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return "";
+ }
+}
+
+IArchNetwork::EAddressFamily
+CArchNetworkWinsock::getAddrFamily(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (addr->m_addr.sa_family) {
+ case AF_INET:
+ return kINET;
+
+ default:
+ return kUNKNOWN;
+ }
+}
+
+void
+CArchNetworkWinsock::setAddrPort(CArchNetAddress addr, int port)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ struct sockaddr_in* ipAddr =
+ reinterpret_cast(&addr->m_addr);
+ ipAddr->sin_port = htons_winsock(static_cast(port));
+ break;
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ break;
+ }
+}
+
+int
+CArchNetworkWinsock::getAddrPort(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ struct sockaddr_in* ipAddr =
+ reinterpret_cast(&addr->m_addr);
+ return ntohs_winsock(ipAddr->sin_port);
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return 0;
+ }
+}
+
+bool
+CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ struct sockaddr_in* ipAddr =
+ reinterpret_cast(&addr->m_addr);
+ return (addr->m_len == sizeof(struct sockaddr_in) &&
+ ipAddr->sin_addr.s_addr == INADDR_ANY);
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return true;
+ }
+}
+
+bool
+CArchNetworkWinsock::isEqualAddr(CArchNetAddress a, CArchNetAddress b)
+{
+ return (a == b || (a->m_len == b->m_len &&
+ memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0));
+}
+
+void
+CArchNetworkWinsock::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
+CArchNetworkWinsock::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/lib/arch/CArchNetworkWinsock.h b/lib/arch/CArchNetworkWinsock.h
new file mode 100644
index 00000000..3912ba5b
--- /dev/null
+++ b/lib/arch/CArchNetworkWinsock.h
@@ -0,0 +1,99 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHNETWORKWINSOCK_H
+#define CARCHNETWORKWINSOCK_H
+
+#define WIN32_LEAN_AND_MEAN
+
+// declare no functions in winsock2
+#define INCL_WINSOCK_API_PROTOTYPES 0
+#define INCL_WINSOCK_API_TYPEDEFS 0
+
+#include "IArchNetwork.h"
+#include "IArchMultithread.h"
+#include
+#include
+
+#define ARCH_NETWORK CArchNetworkWinsock
+
+class CArchSocketImpl {
+public:
+ SOCKET m_socket;
+ int m_refCount;
+ WSAEVENT m_event;
+ bool m_pollWrite;
+};
+
+class CArchNetAddressImpl {
+public:
+ static CArchNetAddressImpl* alloc(size_t);
+
+public:
+ int m_len;
+ struct sockaddr m_addr;
+};
+#define ADDR_HDR_SIZE offsetof(CArchNetAddressImpl, m_addr)
+#define TYPED_ADDR(type_, addr_) (reinterpret_cast(&addr_->m_addr))
+
+//! Win32 implementation of IArchNetwork
+class CArchNetworkWinsock : public IArchNetwork {
+public:
+ CArchNetworkWinsock();
+ virtual ~CArchNetworkWinsock();
+
+ // IArchNetwork overrides
+ virtual CArchSocket newSocket(EAddressFamily, ESocketType);
+ virtual CArchSocket copySocket(CArchSocket s);
+ virtual void closeSocket(CArchSocket s);
+ virtual void closeSocketForRead(CArchSocket s);
+ virtual void closeSocketForWrite(CArchSocket s);
+ virtual void bindSocket(CArchSocket s, CArchNetAddress addr);
+ virtual void listenOnSocket(CArchSocket s);
+ virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr);
+ virtual bool connectSocket(CArchSocket s, CArchNetAddress name);
+ virtual int pollSocket(CPollEntry[], int num, double timeout);
+ virtual void unblockPollSocket(CArchThread thread);
+ virtual size_t readSocket(CArchSocket s, void* buf, size_t len);
+ virtual size_t writeSocket(CArchSocket s,
+ const void* buf, size_t len);
+ virtual void throwErrorOnSocket(CArchSocket);
+ virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay);
+ virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse);
+ virtual std::string getHostName();
+ virtual CArchNetAddress newAnyAddr(EAddressFamily);
+ virtual CArchNetAddress copyAddr(CArchNetAddress);
+ virtual CArchNetAddress nameToAddr(const std::string&);
+ virtual void closeAddr(CArchNetAddress);
+ virtual std::string addrToName(CArchNetAddress);
+ virtual std::string addrToString(CArchNetAddress);
+ virtual EAddressFamily getAddrFamily(CArchNetAddress);
+ virtual void setAddrPort(CArchNetAddress, int port);
+ virtual int getAddrPort(CArchNetAddress);
+ virtual bool isAnyAddr(CArchNetAddress);
+ virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress);
+
+private:
+ void init(HMODULE);
+
+ void setBlockingOnSocket(SOCKET, bool blocking);
+
+ void throwError(int);
+ void throwNameError(int);
+
+private:
+ CArchMutex m_mutex;
+};
+
+#endif
diff --git a/lib/arch/CArchSleepUnix.cpp b/lib/arch/CArchSleepUnix.cpp
new file mode 100644
index 00000000..35010721
--- /dev/null
+++ b/lib/arch/CArchSleepUnix.cpp
@@ -0,0 +1,88 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchSleepUnix.h"
+#include "CArch.h"
+#if TIME_WITH_SYS_TIME
+# include
+# include
+#else
+# if HAVE_SYS_TIME_H
+# include
+# else
+# include
+# endif
+#endif
+#if !HAVE_NANOSLEEP
+# if HAVE_SYS_SELECT_H
+# include
+# endif
+# if HAVE_SYS_TYPES_H
+# include
+# endif
+# if HAVE_UNISTD_H
+# include
+# endif
+#endif
+
+//
+// CArchSleepUnix
+//
+
+CArchSleepUnix::CArchSleepUnix()
+{
+ // do nothing
+}
+
+CArchSleepUnix::~CArchSleepUnix()
+{
+ // do nothing
+}
+
+void
+CArchSleepUnix::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(timeLeft);
+ timeout2.tv_usec = static_cast(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/lib/arch/CArchSleepUnix.h b/lib/arch/CArchSleepUnix.h
new file mode 100644
index 00000000..939ca401
--- /dev/null
+++ b/lib/arch/CArchSleepUnix.h
@@ -0,0 +1,32 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHSLEEPUNIX_H
+#define CARCHSLEEPUNIX_H
+
+#include "IArchSleep.h"
+
+#define ARCH_SLEEP CArchSleepUnix
+
+//! Unix implementation of IArchSleep
+class CArchSleepUnix : public IArchSleep {
+public:
+ CArchSleepUnix();
+ virtual ~CArchSleepUnix();
+
+ // IArchSleep overrides
+ virtual void sleep(double timeout);
+};
+
+#endif
diff --git a/lib/arch/CArchSleepWindows.cpp b/lib/arch/CArchSleepWindows.cpp
new file mode 100644
index 00000000..f6c8bed8
--- /dev/null
+++ b/lib/arch/CArchSleepWindows.cpp
@@ -0,0 +1,57 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchSleepWindows.h"
+#include "CArch.h"
+#include "CArchMultithreadWindows.h"
+
+//
+// CArchSleepWindows
+//
+
+CArchSleepWindows::CArchSleepWindows()
+{
+ // do nothing
+}
+
+CArchSleepWindows::~CArchSleepWindows()
+{
+ // do nothing
+}
+
+void
+CArchSleepWindows::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.
+ CArchMultithreadWindows* mt = CArchMultithreadWindows::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/lib/arch/CArchSleepWindows.h b/lib/arch/CArchSleepWindows.h
new file mode 100644
index 00000000..a5a5fa90
--- /dev/null
+++ b/lib/arch/CArchSleepWindows.h
@@ -0,0 +1,32 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHSLEEPWINDOWS_H
+#define CARCHSLEEPWINDOWS_H
+
+#include "IArchSleep.h"
+
+#define ARCH_SLEEP CArchSleepWindows
+
+//! Win32 implementation of IArchSleep
+class CArchSleepWindows : public IArchSleep {
+public:
+ CArchSleepWindows();
+ virtual ~CArchSleepWindows();
+
+ // IArchSleep overrides
+ virtual void sleep(double timeout);
+};
+
+#endif
diff --git a/lib/arch/CArchStringUnix.cpp b/lib/arch/CArchStringUnix.cpp
new file mode 100644
index 00000000..e0ad3457
--- /dev/null
+++ b/lib/arch/CArchStringUnix.cpp
@@ -0,0 +1,29 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchStringUnix.h"
+#include
+
+//
+// CArchStringUnix
+//
+
+#include "CMultibyte.cpp"
+#include "vsnprintf.cpp"
+
+IArchString::EWideCharEncoding
+CArchStringUnix::getWideCharEncoding()
+{
+ return kUCS4;
+}
diff --git a/lib/arch/CArchStringUnix.h b/lib/arch/CArchStringUnix.h
new file mode 100644
index 00000000..20e5486b
--- /dev/null
+++ b/lib/arch/CArchStringUnix.h
@@ -0,0 +1,39 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHSTRINGUNIX_H
+#define CARCHSTRINGUNIX_H
+
+#include "IArchString.h"
+
+#define ARCH_STRING CArchStringUnix
+
+//! Unix implementation of IArchString
+class CArchStringUnix : public IArchString {
+public:
+ CArchStringUnix();
+ virtual ~CArchStringUnix();
+
+ // IArchString overrides
+ virtual int vsnprintf(char* str,
+ int size, const char* fmt, va_list ap);
+ virtual int convStringMBToWC(wchar_t*,
+ const char*, UInt32 n, bool* errors);
+ virtual int convStringWCToMB(char*,
+ const wchar_t*, UInt32 n, bool* errors);
+ virtual EWideCharEncoding
+ getWideCharEncoding();
+};
+
+#endif
diff --git a/lib/arch/CArchStringWindows.cpp b/lib/arch/CArchStringWindows.cpp
new file mode 100644
index 00000000..a3b90765
--- /dev/null
+++ b/lib/arch/CArchStringWindows.cpp
@@ -0,0 +1,34 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "CArchStringWindows.h"
+#include
+#include
+
+//
+// CArchStringWindows
+//
+
+#include "CMultibyte.cpp"
+#define HAVE_VSNPRINTF 1
+#define ARCH_VSNPRINTF _vsnprintf
+#include "vsnprintf.cpp"
+
+IArchString::EWideCharEncoding
+CArchStringWindows::getWideCharEncoding()
+{
+ return kUTF16;
+}
diff --git a/lib/arch/CArchStringWindows.h b/lib/arch/CArchStringWindows.h
new file mode 100644
index 00000000..a67d8431
--- /dev/null
+++ b/lib/arch/CArchStringWindows.h
@@ -0,0 +1,39 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHSTRINGWINDOWS_H
+#define CARCHSTRINGWINDOWS_H
+
+#include "IArchString.h"
+
+#define ARCH_STRING CArchStringWindows
+
+//! Win32 implementation of IArchString
+class CArchStringWindows : public IArchString {
+public:
+ CArchStringWindows();
+ virtual ~CArchStringWindows();
+
+ // IArchString overrides
+ virtual int vsnprintf(char* str,
+ int size, const char* fmt, va_list ap);
+ virtual int convStringMBToWC(wchar_t*,
+ const char*, UInt32 n, bool* errors);
+ virtual int convStringWCToMB(char*,
+ const wchar_t*, UInt32 n, bool* errors);
+ virtual EWideCharEncoding
+ getWideCharEncoding();
+};
+
+#endif
diff --git a/lib/arch/CArchSystemUnix.cpp b/lib/arch/CArchSystemUnix.cpp
new file mode 100644
index 00000000..541038db
--- /dev/null
+++ b/lib/arch/CArchSystemUnix.cpp
@@ -0,0 +1,50 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchSystemUnix.h"
+#include
+
+//
+// CArchSystemUnix
+//
+
+CArchSystemUnix::CArchSystemUnix()
+{
+ // do nothing
+}
+
+CArchSystemUnix::~CArchSystemUnix()
+{
+ // do nothing
+}
+
+std::string
+CArchSystemUnix::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;
+ msg += " ";
+ msg += info.version;
+ msg += " ";
+ msg += info.machine;
+ return msg;
+ }
+#endif
+ return "Unix ";
+}
diff --git a/lib/arch/CArchSystemUnix.h b/lib/arch/CArchSystemUnix.h
new file mode 100644
index 00000000..525aed1c
--- /dev/null
+++ b/lib/arch/CArchSystemUnix.h
@@ -0,0 +1,32 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHSYSTEMUNIX_H
+#define CARCHSYSTEMUNIX_H
+
+#include "IArchSystem.h"
+
+#define ARCH_SYSTEM CArchSystemUnix
+
+//! Unix implementation of IArchString
+class CArchSystemUnix : public IArchSystem {
+public:
+ CArchSystemUnix();
+ virtual ~CArchSystemUnix();
+
+ // IArchSystem overrides
+ virtual std::string getOSName() const;
+};
+
+#endif
diff --git a/lib/arch/CArchSystemWindows.cpp b/lib/arch/CArchSystemWindows.cpp
new file mode 100644
index 00000000..b634b4bc
--- /dev/null
+++ b/lib/arch/CArchSystemWindows.cpp
@@ -0,0 +1,86 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "CArchSystemWindows.h"
+#include
+
+//
+// CArchSystemWindows
+//
+
+CArchSystemWindows::CArchSystemWindows()
+{
+ // do nothing
+}
+
+CArchSystemWindows::~CArchSystemWindows()
+{
+ // do nothing
+}
+
+std::string
+CArchSystemWindows::getOSName() const
+{
+ OSVERSIONINFO info;
+ info.dwOSVersionInfoSize = sizeof(info);
+ if (GetVersionEx(&info)) {
+ switch (info.dwPlatformId) {
+ case VER_PLATFORM_WIN32_NT:
+ if (info.dwMajorVersion == 5 && info.dwMinorVersion == 2) {
+ return "Microsoft Windows Server 2003";
+ }
+ if (info.dwMajorVersion == 5 && info.dwMinorVersion == 1) {
+ return "Microsoft Windows Server XP";
+ }
+ if (info.dwMajorVersion == 5 && info.dwMinorVersion == 0) {
+ return "Microsoft Windows Server 2000";
+ }
+ if (info.dwMajorVersion <= 4) {
+ return "Microsoft Windows NT";
+ }
+ char buffer[100];
+ sprintf(buffer, "Microsoft Windows %d.%d",
+ info.dwMajorVersion, info.dwMinorVersion);
+ return buffer;
+
+ case VER_PLATFORM_WIN32_WINDOWS:
+ if (info.dwMajorVersion == 4 && info.dwMinorVersion == 0) {
+ if (info.szCSDVersion[1] == 'C' ||
+ info.szCSDVersion[1] == 'B') {
+ return "Microsoft Windows 95 OSR2";
+ }
+ return "Microsoft Windows 95";
+ }
+ if (info.dwMajorVersion == 4 && info.dwMinorVersion == 10) {
+ if (info.szCSDVersion[1] == 'A') {
+ return "Microsoft Windows 98 SE";
+ }
+ return "Microsoft Windows 98";
+ }
+ if (info.dwMajorVersion == 4 && info.dwMinorVersion == 90) {
+ return "Microsoft Windows ME";
+ }
+ if (info.dwMajorVersion == 4) {
+ return "Microsoft Windows unknown 95 family";
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ return "Microsoft Windows ";
+}
diff --git a/lib/arch/CArchSystemWindows.h b/lib/arch/CArchSystemWindows.h
new file mode 100644
index 00000000..e23913d0
--- /dev/null
+++ b/lib/arch/CArchSystemWindows.h
@@ -0,0 +1,32 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHSYSTEMWINDOWS_H
+#define CARCHSYSTEMWINDOWS_H
+
+#include "IArchSystem.h"
+
+#define ARCH_SYSTEM CArchSystemWindows
+
+//! Win32 implementation of IArchString
+class CArchSystemWindows : public IArchSystem {
+public:
+ CArchSystemWindows();
+ virtual ~CArchSystemWindows();
+
+ // IArchSystem overrides
+ virtual std::string getOSName() const;
+};
+
+#endif
diff --git a/lib/arch/CArchTaskBarWindows.cpp b/lib/arch/CArchTaskBarWindows.cpp
new file mode 100644
index 00000000..29c57b66
--- /dev/null
+++ b/lib/arch/CArchTaskBarWindows.cpp
@@ -0,0 +1,492 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchTaskBarWindows.h"
+#include "CArchMiscWindows.h"
+#include "IArchTaskBarReceiver.h"
+#include "CArch.h"
+#include "XArch.h"
+#include
+#include
+
+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;
+
+//
+// CArchTaskBarWindows
+//
+
+CArchTaskBarWindows* CArchTaskBarWindows::s_instance = NULL;
+HINSTANCE CArchTaskBarWindows::s_appInstance = NULL;
+
+CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) :
+ m_nextID(kFirstReceiverID)
+{
+ // save the singleton instance
+ s_instance = this;
+
+ // save app instance
+ s_appInstance = reinterpret_cast(appInstance);
+
+ // 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(&CArchTaskBarWindows::threadEntry, this);
+
+ // wait for child thread
+ while (!m_ready) {
+ ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
+ }
+
+ // ready
+ ARCH->unlockMutex(m_mutex);
+}
+
+CArchTaskBarWindows::~CArchTaskBarWindows()
+{
+ if (m_thread != NULL) {
+ PostMessage(m_hwnd, WM_QUIT, 0, 0);
+ ARCH->wait(m_thread, -1.0);
+ ARCH->closeThread(m_thread);
+ }
+ ARCH->closeCondVar(m_condVar);
+ ARCH->closeMutex(m_mutex);
+ s_instance = NULL;
+}
+
+void
+CArchTaskBarWindows::addDialog(HWND hwnd)
+{
+ CArchMiscWindows::addDialog(hwnd);
+}
+
+void
+CArchTaskBarWindows::removeDialog(HWND hwnd)
+{
+ CArchMiscWindows::removeDialog(hwnd);
+}
+
+void
+CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver)
+{
+ // ignore bogus receiver
+ if (receiver == NULL) {
+ return;
+ }
+
+ // add receiver if necessary
+ CReceiverToInfoMap::iterator index = m_receivers.find(receiver);
+ if (index == m_receivers.end()) {
+ // add it, creating a new message ID for it
+ CReceiverInfo 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
+CArchTaskBarWindows::removeReceiver(IArchTaskBarReceiver* receiver)
+{
+ // find receiver
+ CReceiverToInfoMap::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
+CArchTaskBarWindows::updateReceiver(IArchTaskBarReceiver* receiver)
+{
+ // find receiver
+ CReceiverToInfoMap::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
+CArchTaskBarWindows::getNextID()
+{
+ if (m_oldIDs.empty()) {
+ return m_nextID++;
+ }
+ UINT id = m_oldIDs.back();
+ m_oldIDs.pop_back();
+ return id;
+}
+
+void
+CArchTaskBarWindows::recycleID(UINT id)
+{
+ m_oldIDs.push_back(id);
+}
+
+void
+CArchTaskBarWindows::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
+CArchTaskBarWindows::removeIcon(UINT id)
+{
+ ARCH->lockMutex(m_mutex);
+ removeIconNoLock(id);
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+CArchTaskBarWindows::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
+CArchTaskBarWindows::addAllIcons()
+{
+ ARCH->lockMutex(m_mutex);
+ for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
+ index != m_receivers.end(); ++index) {
+ modifyIconNoLock(index, NIM_ADD);
+ }
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+CArchTaskBarWindows::removeAllIcons()
+{
+ ARCH->lockMutex(m_mutex);
+ for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
+ index != m_receivers.end(); ++index) {
+ removeIconNoLock(index->second.m_id);
+ }
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+CArchTaskBarWindows::modifyIconNoLock(
+ CReceiverToInfoMap::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 = reinterpret_cast(
+ const_cast(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
+CArchTaskBarWindows::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
+CArchTaskBarWindows::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
+CArchTaskBarWindows::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 (CDialogs::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 (CDialogs::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
+CArchTaskBarWindows::wndProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case kNotifyReceiver: {
+ // lookup receiver
+ CIDToReceiverMap::const_iterator index = m_idTable.find(wParam);
+ if (index != m_idTable.end()) {
+ IArchTaskBarReceiver* receiver = index->second->first;
+ handleIconMessage(receiver, lParam);
+ return 0;
+ }
+ break;
+ }
+
+ case kAddReceiver:
+ addIcon(wParam);
+ break;
+
+ case kRemoveReceiver:
+ removeIcon(wParam);
+ break;
+
+ case kUpdateReceiver:
+ updateIcon(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
+CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ // if msg is WM_NCCREATE, extract the CArchTaskBarWindows* and put
+ // it in the extra window data then forward the call.
+ CArchTaskBarWindows* self = NULL;
+ if (msg == WM_NCCREATE) {
+ CREATESTRUCT* createInfo;
+ createInfo = reinterpret_cast(lParam);
+ self = reinterpret_cast(
+ createInfo->lpCreateParams);
+ SetWindowLong(hwnd, 0, reinterpret_cast(self));
+ }
+ else {
+ // get the extra window data and forward the call
+ LONG data = GetWindowLong(hwnd, 0);
+ if (data != 0) {
+ self = reinterpret_cast(
+ reinterpret_cast(data));
+ }
+ }
+
+ // forward the message
+ if (self != NULL) {
+ return self->wndProc(hwnd, msg, wParam, lParam);
+ }
+ else {
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+}
+
+void
+CArchTaskBarWindows::threadMainLoop()
+{
+ // register the task bar restart message
+ m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
+
+ // register a window class
+ WNDCLASSEX classInfo;
+ classInfo.cbSize = sizeof(classInfo);
+ classInfo.style = CS_NOCLOSE;
+ classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc;
+ classInfo.cbClsExtra = 0;
+ classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*);
+ classInfo.hInstance = s_appInstance;
+ classInfo.hIcon = NULL;
+ classInfo.hCursor = NULL;
+ classInfo.hbrBackground = NULL;
+ classInfo.lpszMenuName = NULL;
+ classInfo.lpszClassName = TEXT("SynergyTaskBar");
+ classInfo.hIconSm = NULL;
+ ATOM windowClass = RegisterClassEx(&classInfo);
+
+ // create window
+ m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
+ reinterpret_cast(windowClass),
+ TEXT("Synergy Task Bar"),
+ WS_POPUP,
+ 0, 0, 1, 1,
+ NULL,
+ NULL,
+ s_appInstance,
+ reinterpret_cast(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(reinterpret_cast(windowClass), s_appInstance);
+ 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(reinterpret_cast(windowClass), s_appInstance);
+}
+
+void*
+CArchTaskBarWindows::threadEntry(void* self)
+{
+ reinterpret_cast(self)->threadMainLoop();
+ return NULL;
+}
diff --git a/lib/arch/CArchTaskBarWindows.h b/lib/arch/CArchTaskBarWindows.h
new file mode 100644
index 00000000..67e9af17
--- /dev/null
+++ b/lib/arch/CArchTaskBarWindows.h
@@ -0,0 +1,110 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHTASKBARWINDOWS_H
+#define CARCHTASKBARWINDOWS_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "IArchTaskBar.h"
+#include "IArchMultithread.h"
+#include "stdmap.h"
+#include "stdvector.h"
+#include
+
+#define ARCH_TASKBAR CArchTaskBarWindows
+
+//! Win32 implementation of IArchTaskBar
+class CArchTaskBarWindows : public IArchTaskBar {
+public:
+ CArchTaskBarWindows(void*);
+ virtual ~CArchTaskBarWindows();
+
+ //! 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 CReceiverInfo {
+ public:
+ UINT m_id;
+ };
+
+ typedef std::map CReceiverToInfoMap;
+ typedef std::map CIDToReceiverMap;
+ typedef std::vector CIDStack;
+ typedef std::map CDialogs;
+
+ UINT getNextID();
+ void recycleID(UINT);
+
+ void addIcon(UINT);
+ void removeIcon(UINT);
+ void updateIcon(UINT);
+ void addAllIcons();
+ void removeAllIcons();
+ void modifyIconNoLock(CReceiverToInfoMap::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*);
+
+private:
+ static CArchTaskBarWindows* s_instance;
+ static HINSTANCE s_appInstance;
+
+ // multithread data
+ CArchMutex m_mutex;
+ CArchCond m_condVar;
+ bool m_ready;
+ int m_result;
+ CArchThread m_thread;
+
+ // child thread data
+ HWND m_hwnd;
+ UINT m_taskBarRestart;
+
+ // shared data
+ CReceiverToInfoMap m_receivers;
+ CIDToReceiverMap m_idTable;
+ CIDStack m_oldIDs;
+ UINT m_nextID;
+
+ // dialogs
+ CDialogs m_dialogs;
+ CDialogs m_addedDialogs;
+};
+
+#endif
diff --git a/lib/arch/CArchTaskBarXWindows.cpp b/lib/arch/CArchTaskBarXWindows.cpp
new file mode 100644
index 00000000..6934f271
--- /dev/null
+++ b/lib/arch/CArchTaskBarXWindows.cpp
@@ -0,0 +1,47 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchTaskBarXWindows.h"
+
+//
+// CArchTaskBarXWindows
+//
+
+CArchTaskBarXWindows::CArchTaskBarXWindows(void*)
+{
+ // do nothing
+}
+
+CArchTaskBarXWindows::~CArchTaskBarXWindows()
+{
+ // do nothing
+}
+
+void
+CArchTaskBarXWindows::addReceiver(IArchTaskBarReceiver* /*receiver*/)
+{
+ // do nothing
+}
+
+void
+CArchTaskBarXWindows::removeReceiver(IArchTaskBarReceiver* /*receiver*/)
+{
+ // do nothing
+}
+
+void
+CArchTaskBarXWindows::updateReceiver(IArchTaskBarReceiver* /*receiver*/)
+{
+ // do nothing
+}
diff --git a/lib/arch/CArchTaskBarXWindows.h b/lib/arch/CArchTaskBarXWindows.h
new file mode 100644
index 00000000..abf28012
--- /dev/null
+++ b/lib/arch/CArchTaskBarXWindows.h
@@ -0,0 +1,34 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHTASKBARXWINDOWS_H
+#define CARCHTASKBARXWINDOWS_H
+
+#include "IArchTaskBar.h"
+
+#define ARCH_TASKBAR CArchTaskBarXWindows
+
+//! X11 implementation of IArchTaskBar
+class CArchTaskBarXWindows : public IArchTaskBar {
+public:
+ CArchTaskBarXWindows(void*);
+ virtual ~CArchTaskBarXWindows();
+
+ // IArchTaskBar overrides
+ virtual void addReceiver(IArchTaskBarReceiver*);
+ virtual void removeReceiver(IArchTaskBarReceiver*);
+ virtual void updateReceiver(IArchTaskBarReceiver*);
+};
+
+#endif
diff --git a/lib/arch/CArchTimeUnix.cpp b/lib/arch/CArchTimeUnix.cpp
new file mode 100644
index 00000000..49506bad
--- /dev/null
+++ b/lib/arch/CArchTimeUnix.cpp
@@ -0,0 +1,47 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CArchTimeUnix.h"
+#if TIME_WITH_SYS_TIME
+# include
+# include
+#else
+# if HAVE_SYS_TIME_H
+# include
+# else
+# include
+# endif
+#endif
+
+//
+// CArchTimeUnix
+//
+
+CArchTimeUnix::CArchTimeUnix()
+{
+ // do nothing
+}
+
+CArchTimeUnix::~CArchTimeUnix()
+{
+ // do nothing
+}
+
+double
+CArchTimeUnix::time()
+{
+ struct timeval t;
+ gettimeofday(&t, NULL);
+ return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec;
+}
diff --git a/lib/arch/CArchTimeUnix.h b/lib/arch/CArchTimeUnix.h
new file mode 100644
index 00000000..78c6ff6f
--- /dev/null
+++ b/lib/arch/CArchTimeUnix.h
@@ -0,0 +1,32 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHTIMEUNIX_H
+#define CARCHTIMEUNIX_H
+
+#include "IArchTime.h"
+
+#define ARCH_TIME CArchTimeUnix
+
+//! Generic Unix implementation of IArchTime
+class CArchTimeUnix : public IArchTime {
+public:
+ CArchTimeUnix();
+ virtual ~CArchTimeUnix();
+
+ // IArchTime overrides
+ virtual double time();
+};
+
+#endif
diff --git a/lib/arch/CArchTimeWindows.cpp b/lib/arch/CArchTimeWindows.cpp
new file mode 100644
index 00000000..57aee290
--- /dev/null
+++ b/lib/arch/CArchTimeWindows.cpp
@@ -0,0 +1,86 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+// avoid getting a lot a crap from mmsystem.h that we don't need
+#define MMNODRV // Installable driver support
+#define MMNOSOUND // Sound support
+#define MMNOWAVE // Waveform support
+#define MMNOMIDI // MIDI support
+#define MMNOAUX // Auxiliary audio support
+#define MMNOMIXER // Mixer support
+#define MMNOJOY // Joystick support
+#define MMNOMCI // MCI support
+#define MMNOMMIO // Multimedia file I/O support
+#define MMNOMMSYSTEM // General MMSYSTEM functions
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "CArchTimeWindows.h"
+#include
+#include
+
+typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void);
+
+static double s_freq = 0.0;
+static HINSTANCE s_mmInstance = NULL;
+static PTimeGetTime s_tgt = NULL;
+
+
+//
+// CArchTimeWindows
+//
+
+CArchTimeWindows::CArchTimeWindows()
+{
+ assert(s_freq == 0.0 || s_mmInstance == NULL);
+
+ LARGE_INTEGER freq;
+ if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) {
+ s_freq = 1.0 / static_cast(freq.QuadPart);
+ }
+ else {
+ // load winmm.dll and get timeGetTime
+ s_mmInstance = LoadLibrary("winmm");
+ if (s_mmInstance != NULL) {
+ s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime");
+ }
+ }
+}
+
+CArchTimeWindows::~CArchTimeWindows()
+{
+ s_freq = 0.0;
+ if (s_mmInstance == NULL) {
+ FreeLibrary(reinterpret_cast(s_mmInstance));
+ s_tgt = NULL;
+ s_mmInstance = NULL;
+ }
+}
+
+double
+CArchTimeWindows::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(c.QuadPart);
+ }
+ else if (s_tgt != NULL) {
+ return 0.001 * static_cast(s_tgt());
+ }
+ else {
+ return 0.001 * static_cast(GetTickCount());
+ }
+}
diff --git a/lib/arch/CArchTimeWindows.h b/lib/arch/CArchTimeWindows.h
new file mode 100644
index 00000000..fb9b1e9f
--- /dev/null
+++ b/lib/arch/CArchTimeWindows.h
@@ -0,0 +1,32 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CARCHTIMEWINDOWS_H
+#define CARCHTIMEWINDOWS_H
+
+#include "IArchTime.h"
+
+#define ARCH_TIME CArchTimeWindows
+
+//! Win32 implementation of IArchTime
+class CArchTimeWindows : public IArchTime {
+public:
+ CArchTimeWindows();
+ virtual ~CArchTimeWindows();
+
+ // IArchTime overrides
+ virtual double time();
+};
+
+#endif
diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp
new file mode 100644
index 00000000..517d72d6
--- /dev/null
+++ b/lib/arch/CMultibyte.cpp
@@ -0,0 +1,219 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CMULTIBYTE_H
+#define CMULTIBYTE_H
+
+#include "common.h"
+#include "CArch.h"
+#include
+#include
+#if HAVE_LOCALE_H
+# include
+#endif
+#if HAVE_WCHAR_H || defined(_MSC_VER)
+# include
+#elif __APPLE__
+ // wtf? Darwin puts mbtowc() et al. in stdlib
+# include
+#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(*src);
+ return 1;
+}
+
+static inline
+int
+wctomb(char* dst, wchar_t src)
+{
+ *dst = static_cast(src);
+ return 1;
+}
+
+#endif
+
+//
+// use C library non-reentrant multibyte conversion with mutex
+//
+
+static CArchMutex s_mutex = NULL;
+
+ARCH_STRING::ARCH_STRING()
+{
+ s_mutex = ARCH->newMutex();
+
+#if HAVE_LOCALE_H
+ // see if we can convert a Latin-1 character
+ char mb[MB_LEN_MAX];
+ if (wctomb(mb, 0xe3) == -1) {
+ // can't convert. try another locale so we can convert latin-1.
+ setlocale(LC_CTYPE, "en_US");
+ }
+#endif
+}
+
+ARCH_STRING::~ARCH_STRING()
+{
+ ARCH->closeMutex(s_mutex);
+ s_mutex = NULL;
+}
+
+int
+ARCH_STRING::convStringWCToMB(char* dst,
+ const wchar_t* src, UInt32 n, bool* errors)
+{
+ int len = 0;
+
+ bool dummyErrors;
+ if (errors == NULL) {
+ errors = &dummyErrors;
+ }
+
+ ARCH->lockMutex(s_mutex);
+ if (dst == NULL) {
+ char dummy[MB_LEN_MAX];
+ for (const wchar_t* scan = src; n > 0; ++scan, --n) {
+ int mblen = wctomb(dummy, *scan);
+ if (mblen == -1) {
+ *errors = true;
+ mblen = 1;
+ }
+ len += mblen;
+ }
+ int 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) {
+ int mblen = wctomb(dst, *scan);
+ if (mblen == -1) {
+ *errors = true;
+ *dst++ = '?';
+ }
+ else {
+ dst += mblen;
+ }
+ }
+ int mblen = wctomb(dst, L'\0');
+ if (mblen != -1) {
+ // don't include nul terminator
+ dst += mblen - 1;
+ }
+ len = (int)(dst - dst0);
+ }
+ ARCH->unlockMutex(s_mutex);
+
+ return len;
+}
+
+int
+ARCH_STRING::convStringMBToWC(wchar_t* dst,
+ const char* src, UInt32 n, bool* errors)
+{
+ int len = 0;
+ wchar_t dummy;
+
+ bool dummyErrors;
+ if (errors == NULL) {
+ errors = &dummyErrors;
+ }
+
+ ARCH->lockMutex(s_mutex);
+ if (dst == NULL) {
+ for (const char* scan = src; n > 0; ) {
+ int 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) {
+ int 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 = (int)(dst - dst0);
+ }
+ ARCH->unlockMutex(s_mutex);
+
+ return len;
+}
+
+#endif
diff --git a/lib/arch/IArchConsole.h b/lib/arch/IArchConsole.h
new file mode 100644
index 00000000..2befb196
--- /dev/null
+++ b/lib/arch/IArchConsole.h
@@ -0,0 +1,71 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHCONSOLE_H
+#define IARCHCONSOLE_H
+
+#include "IInterface.h"
+
+//! Interface for architecture dependent console output
+/*!
+This interface defines the console operations required by
+synergy. 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(const char*) = 0;
+
+ //! Returns the newline sequence for the console
+ /*!
+ Different consoles use different character sequences for newlines.
+ This method returns the appropriate newline sequence for the console.
+ */
+ virtual const char* getNewlineForConsole() = 0;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/IArchDaemon.h b/lib/arch/IArchDaemon.h
new file mode 100644
index 00000000..ba6b049b
--- /dev/null
+++ b/lib/arch/IArchDaemon.h
@@ -0,0 +1,107 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHDAEMON_H
+#define IARCHDAEMON_H
+
+#include "IInterface.h"
+
+//! Interface for architecture dependent daemonizing
+/*!
+This interface defines the operations required by synergy 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,
+ bool allUsers) = 0;
+
+ //! Uninstall daemon
+ /*!
+ Uninstall a daemon. Throws an \c XArchDaemon on failure.
+ */
+ virtual void uninstallDaemon(const char* name, bool allUsers) = 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.
+
+
unix:
+ Detaches from terminal. \c func gets passed one argument, the
+ name passed to daemonize().
+
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 CArchMiscWindows::runDaemon() to finally
+ becoming a service. The \c runFunc function passed to \c runDaemon()
+ must call \c CArchMiscWindows::daemonRunning(true) when it
+ enters the main loop (i.e. after initialization) and
+ \c CArchMiscWindows::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 CArchMiscWindows::daemonFailed() to indicate startup failure.
+
+ */
+ 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, bool allUsers) = 0;
+
+ //! Check if the daemon is installed
+ /*!
+ Returns true iff the daemon is installed.
+ */
+ virtual bool isDaemonInstalled(const char* name, bool allUsers) = 0;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/IArchFile.h b/lib/arch/IArchFile.h
new file mode 100644
index 00000000..8594053d
--- /dev/null
+++ b/lib/arch/IArchFile.h
@@ -0,0 +1,64 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHFILE_H
+#define IARCHFILE_H
+
+#include "IInterface.h"
+#include "stdstring.h"
+
+//! Interface for architecture dependent file system operations
+/*!
+This interface defines the file system operations required by
+synergy. 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;
+
+ //! 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;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/IArchLog.h b/lib/arch/IArchLog.h
new file mode 100644
index 00000000..7655ff95
--- /dev/null
+++ b/lib/arch/IArchLog.h
@@ -0,0 +1,73 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHLOG_H
+#define IARCHLOG_H
+
+#include "IInterface.h"
+
+//! Interface for architecture dependent logging
+/*!
+This interface defines the logging operations required by
+synergy. Each architecture must implement this interface.
+*/
+class IArchLog : public IInterface {
+public:
+ //! Log levels
+ /*!
+ The logging priority levels in order of highest to lowest priority.
+ */
+ enum ELevel {
+ kERROR, //!< For serious or fatal errors
+ kWARNING, //!< For minor errors and warnings
+ kNOTE, //!< For messages about notable events
+ kINFO, //!< For informational messages
+ kDEBUG //!< For debugging messages
+ };
+
+ //! @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;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h
new file mode 100644
index 00000000..b7b72293
--- /dev/null
+++ b/lib/arch/IArchMultithread.h
@@ -0,0 +1,272 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHMULTITHREAD_H
+#define IARCHMULTITHREAD_H
+
+#include "IInterface.h"
+
+/*!
+\class CArchCondImpl
+\brief Internal condition variable data.
+An architecture dependent type holding the necessary data for a
+condition variable.
+*/
+class CArchCondImpl;
+
+/*!
+\var CArchCond
+\brief Opaque condition variable type.
+An opaque type representing a condition variable.
+*/
+typedef CArchCondImpl* CArchCond;
+
+/*!
+\class CArchMutexImpl
+\brief Internal mutex data.
+An architecture dependent type holding the necessary data for a mutex.
+*/
+class CArchMutexImpl;
+
+/*!
+\var CArchMutex
+\brief Opaque mutex type.
+An opaque type representing a mutex.
+*/
+typedef CArchMutexImpl* CArchMutex;
+
+/*!
+\class CArchThreadImpl
+\brief Internal thread data.
+An architecture dependent type holding the necessary data for a thread.
+*/
+class CArchThreadImpl;
+
+/*!
+\var CArchThread
+\brief Opaque thread type.
+An opaque type representing a thread.
+*/
+typedef CArchThreadImpl* CArchThread;
+
+//! Interface for architecture dependent multithreading
+/*!
+This interface defines the multithreading operations required by
+synergy. 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 CArchCond newCondVar() = 0;
+
+ //! Destroy a condition variable
+ virtual void closeCondVar(CArchCond) = 0;
+
+ //! Signal a condition variable
+ /*!
+ Signalling a condition variable releases one waiting thread.
+ */
+ virtual void signalCondVar(CArchCond) = 0;
+
+ //! Broadcast a condition variable
+ /*!
+ Broadcasting a condition variable releases all waiting threads.
+ */
+ virtual void broadcastCondVar(CArchCond) = 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(CArchCond, CArchMutex, 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 CArchMutex newMutex() = 0;
+
+ //! Destroy a mutex
+ virtual void closeMutex(CArchMutex) = 0;
+
+ //! Lock a mutex
+ virtual void lockMutex(CArchMutex) = 0;
+
+ //! Unlock a mutex
+ virtual void unlockMutex(CArchMutex) = 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 CArchThread newThread(ThreadFunc func, void* userData) = 0;
+
+ //! Get a reference to the calling thread
+ /*!
+ Returns a thread representing the current (i.e. calling) thread.
+ */
+ virtual CArchThread newCurrentThread() = 0;
+
+ //! Copy a thread object
+ /*!
+ Returns a reference to to thread referred to by \c thread.
+ */
+ virtual CArchThread copyThread(CArchThread 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(CArchThread) = 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(CArchThread 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(CArchThread, 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(CArchThread 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(CArchThread, CArchThread) = 0;
+
+ //! Test if thread exited
+ /*!
+ Returns true iff \c thread has exited.
+ */
+ virtual bool isExitedThread(CArchThread 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(CArchThread 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(CArchThread 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;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h
new file mode 100644
index 00000000..007fb442
--- /dev/null
+++ b/lib/arch/IArchNetwork.h
@@ -0,0 +1,279 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHNETWORK_H
+#define IARCHNETWORK_H
+
+#include "IInterface.h"
+#include "stdstring.h"
+
+class CArchThreadImpl;
+typedef CArchThreadImpl* CArchThread;
+
+/*!
+\class CArchSocketImpl
+\brief Internal socket data.
+An architecture dependent type holding the necessary data for a socket.
+*/
+class CArchSocketImpl;
+
+/*!
+\var CArchSocket
+\brief Opaque socket type.
+An opaque type representing a socket.
+*/
+typedef CArchSocketImpl* CArchSocket;
+
+/*!
+\class CArchNetAddressImpl
+\brief Internal network address data.
+An architecture dependent type holding the necessary data for a network
+address.
+*/
+class CArchNetAddressImpl;
+
+/*!
+\var CArchNetAddress
+\brief Opaque network address type.
+An opaque type representing a network address.
+*/
+typedef CArchNetAddressImpl* CArchNetAddress;
+
+//! Interface for architecture dependent networking
+/*!
+This interface defines the networking operations required by
+synergy. Each architecture must implement this interface.
+*/
+class IArchNetwork : public IInterface {
+public:
+ //! Supported address families
+ enum EAddressFamily {
+ kUNKNOWN,
+ kINET,
+ };
+
+ //! 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 CPollEntry {
+ public:
+ //! The socket to query
+ CArchSocket 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 CArchSocket newSocket(EAddressFamily, ESocketType) = 0;
+
+ //! Copy a socket object
+ /*!
+ Returns a reference to to socket referred to by \c s.
+ */
+ virtual CArchSocket copySocket(CArchSocket 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(CArchSocket s) = 0;
+
+ //! Close socket for further reads
+ /*!
+ Calling this disallows future reads on socket \c s.
+ */
+ virtual void closeSocketForRead(CArchSocket s) = 0;
+
+ //! Close socket for further writes
+ /*!
+ Calling this disallows future writes on socket \c s.
+ */
+ virtual void closeSocketForWrite(CArchSocket s) = 0;
+
+ //! Bind socket to address
+ /*!
+ Binds socket \c s to the address \c addr.
+ */
+ virtual void bindSocket(CArchSocket s, CArchNetAddress addr) = 0;
+
+ //! Listen for connections on socket
+ /*!
+ Causes the socket \c s to begin listening for incoming connections.
+ */
+ virtual void listenOnSocket(CArchSocket 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 CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* 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(CArchSocket s, CArchNetAddress 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(CPollEntry[], 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(CArchThread 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(CArchSocket 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(CArchSocket 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(CArchSocket 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(CArchSocket, 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(CArchSocket, bool reuse) = 0;
+
+ //! Return local host's name
+ virtual std::string getHostName() = 0;
+
+ //! Create an "any" network address
+ virtual CArchNetAddress newAnyAddr(EAddressFamily) = 0;
+
+ //! Copy a network address
+ virtual CArchNetAddress copyAddr(CArchNetAddress) = 0;
+
+ //! Convert a name to a network address
+ virtual CArchNetAddress nameToAddr(const std::string&) = 0;
+
+ //! Destroy a network address
+ virtual void closeAddr(CArchNetAddress) = 0;
+
+ //! Convert an address to a host name
+ virtual std::string addrToName(CArchNetAddress) = 0;
+
+ //! Convert an address to a string
+ virtual std::string addrToString(CArchNetAddress) = 0;
+
+ //! Get an address's family
+ virtual EAddressFamily getAddrFamily(CArchNetAddress) = 0;
+
+ //! Set the port of an address
+ virtual void setAddrPort(CArchNetAddress, int port) = 0;
+
+ //! Get the port of an address
+ virtual int getAddrPort(CArchNetAddress) = 0;
+
+ //! Test addresses for equality
+ virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress) = 0;
+
+ //! Test for the "any" address
+ /*!
+ Returns true if \c addr is the "any" address. \c newAnyAddr()
+ returns an "any" address.
+ */
+ virtual bool isAnyAddr(CArchNetAddress addr) = 0;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/IArchSleep.h b/lib/arch/IArchSleep.h
new file mode 100644
index 00000000..95a2852c
--- /dev/null
+++ b/lib/arch/IArchSleep.h
@@ -0,0 +1,43 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHSLEEP_H
+#define IARCHSLEEP_H
+
+#include "IInterface.h"
+
+//! Interface for architecture dependent sleeping
+/*!
+This interface defines the sleep operations required by
+synergy. 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;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/IArchString.h b/lib/arch/IArchString.h
new file mode 100644
index 00000000..703d64b1
--- /dev/null
+++ b/lib/arch/IArchString.h
@@ -0,0 +1,68 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHSTRING_H
+#define IARCHSTRING_H
+
+#include "IInterface.h"
+#include "BasicTypes.h"
+#include
+
+//! Interface for architecture dependent string operations
+/*!
+This interface defines the string operations required by
+synergy. Each architecture must implement this interface.
+*/
+class IArchString : public IInterface {
+public:
+ //! 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) = 0;
+
+ //! Convert multibyte string to wide character string
+ virtual int convStringMBToWC(wchar_t*,
+ const char*, UInt32 n, bool* errors) = 0;
+
+ //! Convert wide character string to multibyte string
+ virtual int convStringWCToMB(char*,
+ const wchar_t*, UInt32 n, bool* errors) = 0;
+
+ //! Return the architecture's native wide character encoding
+ virtual EWideCharEncoding
+ getWideCharEncoding() = 0;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/IArchSystem.h b/lib/arch/IArchSystem.h
new file mode 100644
index 00000000..7a6c941b
--- /dev/null
+++ b/lib/arch/IArchSystem.h
@@ -0,0 +1,39 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHSYSTEM_H
+#define IARCHSYSTEM_H
+
+#include "IInterface.h"
+#include "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;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/IArchTaskBar.h b/lib/arch/IArchTaskBar.h
new file mode 100644
index 00000000..e9471566
--- /dev/null
+++ b/lib/arch/IArchTaskBar.h
@@ -0,0 +1,60 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHTASKBAR_H
+#define IARCHTASKBAR_H
+
+#include "IInterface.h"
+
+class IArchTaskBarReceiver;
+
+//! Interface for architecture dependent task bar control
+/*!
+This interface defines the task bar icon operations required
+by synergy. 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;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/IArchTaskBarReceiver.h b/lib/arch/IArchTaskBarReceiver.h
new file mode 100644
index 00000000..917f2fbf
--- /dev/null
+++ b/lib/arch/IArchTaskBarReceiver.h
@@ -0,0 +1,90 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHTASKBARRECEIVER_H
+#define IARCHTASKBARRECEIVER_H
+
+#include "IInterface.h"
+#include "stdstring.h"
+
+//! Interface for architecture dependent task bar event handling
+/*!
+This interface defines the task bar icon event handlers required
+by synergy. 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;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/IArchTime.h b/lib/arch/IArchTime.h
new file mode 100644
index 00000000..dade64bb
--- /dev/null
+++ b/lib/arch/IArchTime.h
@@ -0,0 +1,40 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IARCHTIME_H
+#define IARCHTIME_H
+
+#include "IInterface.h"
+
+//! Interface for architecture dependent time operations
+/*!
+This interface defines the time operations required by
+synergy. 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;
+
+ //@}
+};
+
+#endif
diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am
new file mode 100644
index 00000000..8a1749ab
--- /dev/null
+++ b/lib/arch/Makefile.am
@@ -0,0 +1,118 @@
+# synergy -- mouse and keyboard sharing utility
+# 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+## Process this file with automake to produce Makefile.in
+NULL =
+
+COMMON_SOURCE_FILES = \
+ CArch.cpp \
+ CArchDaemonNone.cpp \
+ CArchDaemonNone.h \
+ XArch.cpp \
+ CArch.h \
+ IArchConsole.h \
+ IArchDaemon.h \
+ IArchFile.h \
+ IArchLog.h \
+ IArchMultithread.h \
+ IArchNetwork.h \
+ IArchSleep.h \
+ IArchString.h \
+ IArchSystem.h \
+ IArchTaskBar.h \
+ IArchTaskBarReceiver.h \
+ IArchTime.h \
+ XArch.h \
+ $(NULL)
+UNIX_SOURCE_FILES = \
+ CArchConsoleUnix.cpp \
+ CArchDaemonUnix.cpp \
+ CArchFileUnix.cpp \
+ CArchLogUnix.cpp \
+ CArchMultithreadPosix.cpp \
+ CArchNetworkBSD.cpp \
+ CArchSleepUnix.cpp \
+ CArchStringUnix.cpp \
+ CArchSystemUnix.cpp \
+ CArchTaskBarXWindows.cpp \
+ CArchTimeUnix.cpp \
+ XArchUnix.cpp \
+ CArchConsoleUnix.h \
+ CArchDaemonUnix.h \
+ CArchFileUnix.h \
+ CArchLogUnix.h \
+ CArchMultithreadPosix.h \
+ CArchNetworkBSD.h \
+ CArchSleepUnix.h \
+ CArchStringUnix.h \
+ CArchSystemUnix.h \
+ CArchTaskBarXWindows.h \
+ CArchTimeUnix.h \
+ XArchUnix.h \
+ $(NULL)
+WIN32_SOURCE_FILES = \
+ CArchConsoleWindows.cpp \
+ CArchDaemonWindows.cpp \
+ CArchFileWindows.cpp \
+ CArchLogWindows.cpp \
+ CArchMiscWindows.cpp \
+ CArchMultithreadWindows.cpp \
+ CArchNetworkWinsock.cpp \
+ CArchSleepWindows.cpp \
+ CArchStringWindows.cpp \
+ CArchSystemWindows.cpp \
+ CArchTaskBarWindows.cpp \
+ CArchTimeWindows.cpp \
+ XArchWindows.cpp \
+ CArchConsoleWindows.h \
+ CArchDaemonWindows.h \
+ CArchFileWindows.h \
+ CArchLogWindows.h \
+ CArchMiscWindows.h \
+ CArchMultithreadWindows.h \
+ CArchNetworkWinsock.h \
+ CArchSleepWindows.h \
+ CArchStringWindows.h \
+ CArchSystemWindows.h \
+ CArchTaskBarWindows.h \
+ CArchTimeWindows.h \
+ XArchWindows.h \
+ $(NULL)
+
+EXTRA_DIST = \
+ CMultibyte.cpp \
+ Makefile.win \
+ vsnprintf.cpp \
+ $(UNIX_SOURCE_FILES) \
+ $(WIN32_SOURCE_FILES) \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in \
+ $(NULL)
+
+noinst_LIBRARIES = libarch.a
+if UNIX
+libarch_a_SOURCES = \
+ $(COMMON_SOURCE_FILES) \
+ $(UNIX_SOURCE_FILES) \
+ $(NULL)
+endif
+if WIN32
+libarch_a_SOURCES = \
+ $(COMMON_SOURCE_FILES) \
+ $(WIN32_SOURCE_FILES) \
+ $(NULL)
+endif
+INCLUDES = \
+ -I$(top_srcdir)/lib/common \
+ $(NULL)
diff --git a/lib/arch/Makefile.win b/lib/arch/Makefile.win
new file mode 100644
index 00000000..4e151976
--- /dev/null
+++ b/lib/arch/Makefile.win
@@ -0,0 +1,84 @@
+# synergy -- mouse and keyboard sharing utility
+# Copyright (C) 2007 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 COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+LIB_ARCH_SRC = lib\arch
+LIB_ARCH_DST = $(BUILD_DST)\$(LIB_ARCH_SRC)
+LIB_ARCH_LIB = "$(LIB_ARCH_DST)\arch.lib"
+LIB_ARCH_CPP = \
+ "CArch.cpp" \
+ "CArchDaemonNone.cpp" \
+ "XArch.cpp" \
+ "CArchConsoleWindows.cpp" \
+ "CArchDaemonWindows.cpp" \
+ "CArchFileWindows.cpp" \
+ "CArchLogWindows.cpp" \
+ "CArchMiscWindows.cpp" \
+ "CArchMultithreadWindows.cpp" \
+ "CArchNetworkWinsock.cpp" \
+ "CArchSleepWindows.cpp" \
+ "CArchStringWindows.cpp" \
+ "CArchSystemWindows.cpp" \
+ "CArchTaskBarWindows.cpp" \
+ "CArchTimeWindows.cpp" \
+ "XArchWindows.cpp" \
+ $(NULL)
+LIB_ARCH_OBJ = \
+ "$(LIB_ARCH_DST)\CArch.obj" \
+ "$(LIB_ARCH_DST)\CArchDaemonNone.obj" \
+ "$(LIB_ARCH_DST)\XArch.obj" \
+ "$(LIB_ARCH_DST)\CArchConsoleWindows.obj" \
+ "$(LIB_ARCH_DST)\CArchDaemonWindows.obj" \
+ "$(LIB_ARCH_DST)\CArchFileWindows.obj" \
+ "$(LIB_ARCH_DST)\CArchLogWindows.obj" \
+ "$(LIB_ARCH_DST)\CArchMiscWindows.obj" \
+ "$(LIB_ARCH_DST)\CArchMultithreadWindows.obj" \
+ "$(LIB_ARCH_DST)\CArchNetworkWinsock.obj" \
+ "$(LIB_ARCH_DST)\CArchSleepWindows.obj" \
+ "$(LIB_ARCH_DST)\CArchStringWindows.obj" \
+ "$(LIB_ARCH_DST)\CArchSystemWindows.obj" \
+ "$(LIB_ARCH_DST)\CArchTaskBarWindows.obj" \
+ "$(LIB_ARCH_DST)\CArchTimeWindows.obj" \
+ "$(LIB_ARCH_DST)\XArchWindows.obj" \
+ $(NULL)
+LIB_ARCH_INC = \
+ /I"lib\common" \
+ $(NULL)
+
+CPP_FILES = $(CPP_FILES) $(LIB_ARCH_CPP)
+OBJ_FILES = $(OBJ_FILES) $(LIB_ARCH_OBJ)
+LIB_FILES = $(LIB_FILES) $(LIB_ARCH_LIB)
+
+# Dependency rules
+$(LIB_ARCH_OBJ): $(AUTODEP)
+!if EXIST($(LIB_ARCH_DST)\deps.mak)
+!include $(LIB_ARCH_DST)\deps.mak
+!endif
+
+# Build rules. Use batch-mode rules if possible.
+!if DEFINED(_NMAKE_VER)
+{$(LIB_ARCH_SRC)\}.cpp{$(LIB_ARCH_DST)\}.obj::
+!else
+{$(LIB_ARCH_SRC)\}.cpp{$(LIB_ARCH_DST)\}.obj:
+!endif
+ @$(ECHO) Compile in $(LIB_ARCH_SRC)
+ -@$(MKDIR) $(LIB_ARCH_DST) 2>NUL:
+ $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \
+ $(LIB_ARCH_INC) \
+ /Fo$(LIB_ARCH_DST)\ \
+ /Fd$(LIB_ARCH_LIB:.lib=.pdb) \
+ $< | $(AUTODEP) $(LIB_ARCH_SRC) $(LIB_ARCH_DST)
+$(LIB_ARCH_LIB): $(LIB_ARCH_OBJ)
+ @$(ECHO) Link $(@F)
+ $(implib) $(ildebug) $(ilflags) \
+ /out:$@ \
+ $**
+ $(AUTODEP) $(LIB_ARCH_SRC) $(LIB_ARCH_DST) $(**:.obj=.d)
diff --git a/lib/arch/XArch.cpp b/lib/arch/XArch.cpp
new file mode 100644
index 00000000..9dce5283
--- /dev/null
+++ b/lib/arch/XArch.cpp
@@ -0,0 +1,33 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "XArch.h"
+
+//
+// XArch
+//
+
+std::string
+XArch::what() const throw()
+{
+ try {
+ if (m_what.empty() && m_eval != NULL) {
+ m_what = m_eval->eval();
+ }
+ }
+ catch (...) {
+ // ignore
+ }
+ return m_what;
+}
diff --git a/lib/arch/XArch.h b/lib/arch/XArch.h
new file mode 100644
index 00000000..75083649
--- /dev/null
+++ b/lib/arch/XArch.h
@@ -0,0 +1,170 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef XARCH_H
+#define XARCH_H
+
+#include "common.h"
+#include "stdstring.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() { }
+
+ virtual XArchEval* clone() const throw() = 0;
+
+ virtual std::string eval() const throw() = 0;
+};
+
+//! Generic exception architecture dependent library
+class XArch {
+public:
+ XArch(XArchEval* adoptedEvaluator) : m_eval(adoptedEvaluator) { }
+ XArch(const std::string& msg) : m_eval(NULL), m_what(msg) { }
+ XArch(const XArch& e) : m_eval(e.m_eval != NULL ? e.m_eval->clone() : NULL),
+ m_what(e.m_what) { }
+ ~XArch() { delete m_eval; }
+
+ std::string what() const throw();
+
+private:
+ XArchEval* m_eval;
+ mutable std::string m_what;
+};
+
+// 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);
+
+
+#endif
diff --git a/lib/arch/XArchUnix.cpp b/lib/arch/XArchUnix.cpp
new file mode 100644
index 00000000..6f4047d5
--- /dev/null
+++ b/lib/arch/XArchUnix.cpp
@@ -0,0 +1,33 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "XArchUnix.h"
+#include
+
+//
+// XArchEvalUnix
+//
+
+XArchEval*
+XArchEvalUnix::clone() const throw()
+{
+ return new XArchEvalUnix(m_errno);
+}
+
+std::string
+XArchEvalUnix::eval() const throw()
+{
+ // FIXME -- not thread safe
+ return strerror(m_errno);
+}
diff --git a/lib/arch/XArchUnix.h b/lib/arch/XArchUnix.h
new file mode 100644
index 00000000..19e0df4c
--- /dev/null
+++ b/lib/arch/XArchUnix.h
@@ -0,0 +1,34 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef XARCHUNIX_H
+#define XARCHUNIX_H
+
+#include "XArch.h"
+
+//! Lazy error message string evaluation for unix
+class XArchEvalUnix : public XArchEval {
+public:
+ XArchEvalUnix(int err) : m_errno(err) { }
+ virtual ~XArchEvalUnix() { }
+
+ // XArchEval overrides
+ virtual XArchEval* clone() const throw();
+ virtual std::string eval() const throw();
+
+private:
+ int m_errno;
+};
+
+#endif
diff --git a/lib/arch/XArchWindows.cpp b/lib/arch/XArchWindows.cpp
new file mode 100644
index 00000000..eebd6449
--- /dev/null
+++ b/lib/arch/XArchWindows.cpp
@@ -0,0 +1,127 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "XArchWindows.h"
+#include "CArchNetworkWinsock.h"
+
+//
+// XArchEvalWindows
+//
+
+XArchEval*
+XArchEvalWindows::clone() const throw()
+{
+ return new XArchEvalWindows(m_errno);
+}
+
+std::string
+XArchEvalWindows::eval() const throw()
+{
+ char* cmsg;
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ 0,
+ m_errno,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&cmsg,
+ 0,
+ NULL) == 0) {
+ cmsg = NULL;
+ return "Unknown error";
+ }
+ std::string smsg(cmsg);
+ LocalFree(cmsg);
+ return smsg;
+}
+
+
+//
+// XArchEvalWinsock
+//
+
+XArchEval*
+XArchEvalWinsock::clone() const throw()
+{
+ return new XArchEvalWinsock(m_errno);
+}
+
+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 hos 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, "The attempt to connect was forcefully rejected"},
+ /* 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_errno) {
+ return s_netErrorCodes[i].m_msg;
+ }
+ }
+ return "Unknown error";
+}
diff --git a/lib/arch/XArchWindows.h b/lib/arch/XArchWindows.h
new file mode 100644
index 00000000..56c24007
--- /dev/null
+++ b/lib/arch/XArchWindows.h
@@ -0,0 +1,52 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef XARCHWINDOWS_H
+#define XARCHWINDOWS_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "XArch.h"
+#include
+
+//! Lazy error message string evaluation for windows
+class XArchEvalWindows : public XArchEval {
+public:
+ XArchEvalWindows() : m_errno(GetLastError()) { }
+ XArchEvalWindows(DWORD err) : m_errno(err) { }
+ virtual ~XArchEvalWindows() { }
+
+ // XArchEval overrides
+ virtual XArchEval* clone() const throw();
+ virtual std::string eval() const throw();
+
+private:
+ DWORD m_errno;
+};
+
+//! Lazy error message string evaluation for winsock
+class XArchEvalWinsock : public XArchEval {
+public:
+ XArchEvalWinsock(int err) : m_errno(err) { }
+ virtual ~XArchEvalWinsock() { }
+
+ // XArchEval overrides
+ virtual XArchEval* clone() const throw();
+ virtual std::string eval() const throw();
+
+private:
+ int m_errno;
+};
+
+#endif
diff --git a/lib/arch/vsnprintf.cpp b/lib/arch/vsnprintf.cpp
new file mode 100644
index 00000000..10800ec7
--- /dev/null
+++ b/lib/arch/vsnprintf.cpp
@@ -0,0 +1,61 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#if HAVE_VSNPRINTF
+
+#if !defined(ARCH_VSNPRINTF)
+# define ARCH_VSNPRINTF vsnprintf
+#endif
+
+int
+ARCH_STRING::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
+
+int
+ARCH_STRING::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/lib/base/CEvent.cpp b/lib/base/CEvent.cpp
new file mode 100644
index 00000000..bfdf88ed
--- /dev/null
+++ b/lib/base/CEvent.cpp
@@ -0,0 +1,98 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CEvent.h"
+#include "CEventQueue.h"
+
+//
+// CEvent
+//
+
+CEvent::CEvent() :
+ m_type(kUnknown),
+ m_target(NULL),
+ m_data(NULL),
+ m_flags(0)
+{
+ // do nothing
+}
+
+CEvent::CEvent(Type type, void* target, void* data, Flags flags) :
+ m_type(type),
+ m_target(target),
+ m_data(data),
+ m_flags(flags)
+{
+ // do nothing
+}
+
+CEvent::Type
+CEvent::getType() const
+{
+ return m_type;
+}
+
+void*
+CEvent::getTarget() const
+{
+ return m_target;
+}
+
+void*
+CEvent::getData() const
+{
+ return m_data;
+}
+
+CEvent::Flags
+CEvent::getFlags() const
+{
+ return m_flags;
+}
+
+CEvent::Type
+CEvent::registerType(const char* name)
+{
+ return EVENTQUEUE->registerType(name);
+}
+
+CEvent::Type
+CEvent::registerTypeOnce(Type& type, const char* name)
+{
+ return EVENTQUEUE->registerTypeOnce(type, name);
+}
+
+const char*
+CEvent::getTypeName(Type type)
+{
+ return EVENTQUEUE->getTypeName(type);
+}
+
+void
+CEvent::deleteData(const CEvent& event)
+{
+ switch (event.getType()) {
+ case kUnknown:
+ case kQuit:
+ case kSystem:
+ case kTimer:
+ break;
+
+ default:
+ if ((event.getFlags() & kDontFreeData) == 0) {
+ free(event.getData());
+ }
+ break;
+ }
+}
diff --git a/lib/base/CEvent.h b/lib/base/CEvent.h
new file mode 100644
index 00000000..8b637cef
--- /dev/null
+++ b/lib/base/CEvent.h
@@ -0,0 +1,123 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CEVENT_H
+#define CEVENT_H
+
+#include "BasicTypes.h"
+#include "stdmap.h"
+
+//! Event
+/*!
+A \c CEvent holds an event type and a pointer to event data.
+*/
+class CEvent {
+public:
+ typedef UInt32 Type;
+ enum {
+ kUnknown, //!< The event type is unknown
+ kQuit, //!< The quit event
+ kSystem, //!< The data points to a system event type
+ kTimer, //!< The data points to timer info
+ kLast //!< Must be last
+ };
+
+ typedef UInt32 Flags;
+ enum {
+ kNone = 0x00, //!< No flags
+ kDeliverImmediately = 0x01, //!< Dispatch and free event immediately
+ kDontFreeData = 0x02 //!< Don't free data in deleteData
+ };
+
+ CEvent();
+
+ //! Create \c CEvent with data
+ /*!
+ The \p type must have been registered using \c registerType().
+ The \p data must be POD (plain old data) allocated by malloc(),
+ which means it cannot have a constructor, destructor or be
+ composed of any types that do. \p target is the intended
+ recipient of the event. \p flags is any combination of \c Flags.
+ */
+ CEvent(Type type, void* target = NULL, void* data = NULL,
+ UInt32 flags = kNone);
+
+ //! @name manipulators
+ //@{
+
+ //! Creates a new event type
+ /*!
+ Returns a unique event type id.
+ */
+ static Type registerType(const char* name);
+
+ //! Creates a new event type
+ /*!
+ If \p type contains \c kUnknown then it is set to a unique event
+ type id otherwise it is left alone. The final value of \p type
+ is returned.
+ */
+ static Type registerTypeOnce(Type& type, const char* name);
+
+ //! Get name for event
+ /*!
+ Returns the name for the event \p type. This is primarily for
+ debugging.
+ */
+ static const char* getTypeName(Type type);
+
+ //! Release event data
+ /*!
+ Deletes event data for the given event (using free()).
+ */
+ static void deleteData(const CEvent&);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get event type
+ /*!
+ Returns the event type.
+ */
+ Type getType() const;
+
+ //! Get the event target
+ /*!
+ Returns the event target.
+ */
+ void* getTarget() const;
+
+ //! Get the event data
+ /*!
+ Returns the event data.
+ */
+ void* getData() const;
+
+ //! Get event flags
+ /*!
+ Returns the event flags.
+ */
+ Flags getFlags() const;
+
+ //@}
+
+private:
+ Type m_type;
+ void* m_target;
+ void* m_data;
+ Flags m_flags;
+};
+
+#endif
diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp
new file mode 100644
index 00000000..d0a93391
--- /dev/null
+++ b/lib/base/CEventQueue.cpp
@@ -0,0 +1,526 @@
+;/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CEventQueue.h"
+#include "CLog.h"
+#include "CSimpleEventQueueBuffer.h"
+#include "CStopwatch.h"
+#include "IEventJob.h"
+#include "CArch.h"
+
+// interrupt handler. this just adds a quit event to the queue.
+static
+void
+interrupt(CArch::ESignal, void*)
+{
+ EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
+}
+
+
+//
+// CEventQueue
+//
+
+CEventQueue::CEventQueue() :
+ m_nextType(CEvent::kLast)
+{
+ setInstance(this);
+ m_mutex = ARCH->newMutex();
+ ARCH->setSignalHandler(CArch::kINTERRUPT, &interrupt, NULL);
+ ARCH->setSignalHandler(CArch::kTERMINATE, &interrupt, NULL);
+ m_buffer = new CSimpleEventQueueBuffer;
+}
+
+CEventQueue::~CEventQueue()
+{
+ delete m_buffer;
+ ARCH->setSignalHandler(CArch::kINTERRUPT, NULL, NULL);
+ ARCH->setSignalHandler(CArch::kTERMINATE, NULL, NULL);
+ ARCH->closeMutex(m_mutex);
+ setInstance(NULL);
+}
+
+CEvent::Type
+CEventQueue::registerType(const char* name)
+{
+ CArchMutexLock lock(m_mutex);
+ m_typeMap.insert(std::make_pair(m_nextType, name));
+ LOG((CLOG_DEBUG1 "registered event type %s as %d", name, m_nextType));
+ return m_nextType++;
+}
+
+CEvent::Type
+CEventQueue::registerTypeOnce(CEvent::Type& type, const char* name)
+{
+ CArchMutexLock lock(m_mutex);
+ if (type == CEvent::kUnknown) {
+ m_typeMap.insert(std::make_pair(m_nextType, name));
+ LOG((CLOG_DEBUG1 "registered event type %s as %d", name, m_nextType));
+ type = m_nextType++;
+ }
+ return type;
+}
+
+const char*
+CEventQueue::getTypeName(CEvent::Type type)
+{
+ switch (type) {
+ case CEvent::kUnknown:
+ return "nil";
+
+ case CEvent::kQuit:
+ return "quit";
+
+ case CEvent::kSystem:
+ return "system";
+
+ case CEvent::kTimer:
+ return "timer";
+
+ default:
+ CTypeMap::const_iterator i = m_typeMap.find(type);
+ if (i == m_typeMap.end()) {
+ return "";
+ }
+ else {
+ return i->second;
+ }
+ }
+}
+
+void
+CEventQueue::adoptBuffer(IEventQueueBuffer* buffer)
+{
+ CArchMutexLock lock(m_mutex);
+
+ // discard old buffer and old events
+ delete m_buffer;
+ for (CEventTable::iterator i = m_events.begin(); i != m_events.end(); ++i) {
+ CEvent::deleteData(i->second);
+ }
+ m_events.clear();
+ m_oldEventIDs.clear();
+
+ // use new buffer
+ m_buffer = buffer;
+ if (m_buffer == NULL) {
+ m_buffer = new CSimpleEventQueueBuffer;
+ }
+}
+
+bool
+CEventQueue::getEvent(CEvent& event, double timeout)
+{
+ CStopwatch timer(true);
+retry:
+ // if no events are waiting then handle timers and then wait
+ while (m_buffer->isEmpty()) {
+ // handle timers first
+ if (hasTimerExpired(event)) {
+ return true;
+ }
+
+ // get time remaining in timeout
+ double timeLeft = timeout - timer.getTime();
+ if (timeout >= 0.0 && timeLeft <= 0.0) {
+ return false;
+ }
+
+ // get time until next timer expires. if there is a timer
+ // and it'll expire before the client's timeout then use
+ // that duration for our timeout instead.
+ double timerTimeout = getNextTimerTimeout();
+ if (timeout < 0.0 || (timerTimeout >= 0.0 && timerTimeout < timeLeft)) {
+ timeLeft = timerTimeout;
+ }
+
+ // wait for an event
+ m_buffer->waitForEvent(timeLeft);
+ }
+
+ // get the event
+ UInt32 dataID;
+ IEventQueueBuffer::Type type = m_buffer->getEvent(event, dataID);
+ switch (type) {
+ case IEventQueueBuffer::kNone:
+ if (timeout < 0.0 || timeout <= timer.getTime()) {
+ // don't want to fail if client isn't expecting that
+ // so if getEvent() fails with an infinite timeout
+ // then just try getting another event.
+ goto retry;
+ }
+ return false;
+
+ case IEventQueueBuffer::kSystem:
+ return true;
+
+ case IEventQueueBuffer::kUser:
+ {
+ CArchMutexLock lock(m_mutex);
+ event = removeEvent(dataID);
+ return true;
+ }
+
+ default:
+ assert(0 && "invalid event type");
+ return false;
+ }
+}
+
+bool
+CEventQueue::dispatchEvent(const CEvent& event)
+{
+ void* target = event.getTarget();
+ IEventJob* job = getHandler(event.getType(), target);
+ if (job == NULL) {
+ job = getHandler(CEvent::kUnknown, target);
+ }
+ if (job != NULL) {
+ job->run(event);
+ return true;
+ }
+ return false;
+}
+
+void
+CEventQueue::addEvent(const CEvent& event)
+{
+ // discard bogus event types
+ switch (event.getType()) {
+ case CEvent::kUnknown:
+ case CEvent::kSystem:
+ case CEvent::kTimer:
+ return;
+
+ default:
+ break;
+ }
+
+ if ((event.getFlags() & CEvent::kDeliverImmediately) != 0) {
+ dispatchEvent(event);
+ CEvent::deleteData(event);
+ }
+ else {
+ CArchMutexLock lock(m_mutex);
+
+ // store the event's data locally
+ UInt32 eventID = saveEvent(event);
+
+ // add it
+ if (!m_buffer->addEvent(eventID)) {
+ // failed to send event
+ removeEvent(eventID);
+ CEvent::deleteData(event);
+ }
+ }
+}
+
+CEventQueueTimer*
+CEventQueue::newTimer(double duration, void* target)
+{
+ assert(duration > 0.0);
+
+ CEventQueueTimer* timer = m_buffer->newTimer(duration, false);
+ if (target == NULL) {
+ target = timer;
+ }
+ CArchMutexLock lock(m_mutex);
+ m_timers.insert(timer);
+ // initial duration is requested duration plus whatever's on
+ // the clock currently because the latter will be subtracted
+ // the next time we check for timers.
+ m_timerQueue.push(CTimer(timer, duration,
+ duration + m_time.getTime(), target, false));
+ return timer;
+}
+
+CEventQueueTimer*
+CEventQueue::newOneShotTimer(double duration, void* target)
+{
+ assert(duration > 0.0);
+
+ CEventQueueTimer* timer = m_buffer->newTimer(duration, true);
+ if (target == NULL) {
+ target = timer;
+ }
+ CArchMutexLock lock(m_mutex);
+ m_timers.insert(timer);
+ // initial duration is requested duration plus whatever's on
+ // the clock currently because the latter will be subtracted
+ // the next time we check for timers.
+ m_timerQueue.push(CTimer(timer, duration,
+ duration + m_time.getTime(), target, true));
+ return timer;
+}
+
+void
+CEventQueue::deleteTimer(CEventQueueTimer* timer)
+{
+ CArchMutexLock lock(m_mutex);
+ for (CTimerQueue::iterator index = m_timerQueue.begin();
+ index != m_timerQueue.end(); ++index) {
+ if (index->getTimer() == timer) {
+ m_timerQueue.erase(index);
+ break;
+ }
+ }
+ CTimers::iterator index = m_timers.find(timer);
+ if (index != m_timers.end()) {
+ m_timers.erase(index);
+ }
+ m_buffer->deleteTimer(timer);
+}
+
+void
+CEventQueue::adoptHandler(CEvent::Type type, void* target, IEventJob* handler)
+{
+ CArchMutexLock lock(m_mutex);
+ IEventJob*& job = m_handlers[target][type];
+ delete job;
+ job = handler;
+}
+
+void
+CEventQueue::removeHandler(CEvent::Type type, void* target)
+{
+ IEventJob* handler = NULL;
+ {
+ CArchMutexLock lock(m_mutex);
+ CHandlerTable::iterator index = m_handlers.find(target);
+ if (index != m_handlers.end()) {
+ CTypeHandlerTable& typeHandlers = index->second;
+ CTypeHandlerTable::iterator index2 = typeHandlers.find(type);
+ if (index2 != typeHandlers.end()) {
+ handler = index2->second;
+ typeHandlers.erase(index2);
+ }
+ }
+ }
+ delete handler;
+}
+
+void
+CEventQueue::removeHandlers(void* target)
+{
+ std::vector handlers;
+ {
+ CArchMutexLock lock(m_mutex);
+ CHandlerTable::iterator index = m_handlers.find(target);
+ if (index != m_handlers.end()) {
+ // copy to handlers array and clear table for target
+ CTypeHandlerTable& typeHandlers = index->second;
+ for (CTypeHandlerTable::iterator index2 = typeHandlers.begin();
+ index2 != typeHandlers.end(); ++index2) {
+ handlers.push_back(index2->second);
+ }
+ typeHandlers.clear();
+ }
+ }
+
+ // delete handlers
+ for (std::vector::iterator index = handlers.begin();
+ index != handlers.end(); ++index) {
+ delete *index;
+ }
+}
+
+bool
+CEventQueue::isEmpty() const
+{
+ return (m_buffer->isEmpty() && getNextTimerTimeout() != 0.0);
+}
+
+IEventJob*
+CEventQueue::getHandler(CEvent::Type type, void* target) const
+{
+ CArchMutexLock lock(m_mutex);
+ CHandlerTable::const_iterator index = m_handlers.find(target);
+ if (index != m_handlers.end()) {
+ const CTypeHandlerTable& typeHandlers = index->second;
+ CTypeHandlerTable::const_iterator index2 = typeHandlers.find(type);
+ if (index2 != typeHandlers.end()) {
+ return index2->second;
+ }
+ }
+ return NULL;
+}
+
+UInt32
+CEventQueue::saveEvent(const CEvent& event)
+{
+ // choose id
+ UInt32 id;
+ if (!m_oldEventIDs.empty()) {
+ // reuse an id
+ id = m_oldEventIDs.back();
+ m_oldEventIDs.pop_back();
+ }
+ else {
+ // make a new id
+ id = static_cast(m_events.size());
+ }
+
+ // save data
+ m_events[id] = event;
+ return id;
+}
+
+CEvent
+CEventQueue::removeEvent(UInt32 eventID)
+{
+ // look up id
+ CEventTable::iterator index = m_events.find(eventID);
+ if (index == m_events.end()) {
+ return CEvent();
+ }
+
+ // get data
+ CEvent event = index->second;
+ m_events.erase(index);
+
+ // save old id for reuse
+ m_oldEventIDs.push_back(eventID);
+
+ return event;
+}
+
+bool
+CEventQueue::hasTimerExpired(CEvent& event)
+{
+ // return true if there's a timer in the timer priority queue that
+ // has expired. if returning true then fill in event appropriately
+ // and reset and reinsert the timer.
+ if (m_timerQueue.empty()) {
+ return false;
+ }
+
+ // get time elapsed since last check
+ const double time = m_time.getTime();
+ m_time.reset();
+
+ // countdown elapsed time
+ for (CTimerQueue::iterator index = m_timerQueue.begin();
+ index != m_timerQueue.end(); ++index) {
+ (*index) -= time;
+ }
+
+ // done if no timers are expired
+ if (m_timerQueue.top() > 0.0) {
+ return false;
+ }
+
+ // remove timer from queue
+ CTimer timer = m_timerQueue.top();
+ m_timerQueue.pop();
+
+ // prepare event and reset the timer's clock
+ timer.fillEvent(m_timerEvent);
+ event = CEvent(CEvent::kTimer, timer.getTarget(), &m_timerEvent);
+ timer.reset();
+
+ // reinsert timer into queue if it's not a one-shot
+ if (!timer.isOneShot()) {
+ m_timerQueue.push(timer);
+ }
+
+ return true;
+}
+
+double
+CEventQueue::getNextTimerTimeout() const
+{
+ // return -1 if no timers, 0 if the top timer has expired, otherwise
+ // the time until the top timer in the timer priority queue will
+ // expire.
+ if (m_timerQueue.empty()) {
+ return -1.0;
+ }
+ if (m_timerQueue.top() <= 0.0) {
+ return 0.0;
+ }
+ return m_timerQueue.top();
+}
+
+
+//
+// CEventQueue::CTimer
+//
+
+CEventQueue::CTimer::CTimer(CEventQueueTimer* timer, double timeout,
+ double initialTime, void* target, bool oneShot) :
+ m_timer(timer),
+ m_timeout(timeout),
+ m_target(target),
+ m_oneShot(oneShot),
+ m_time(initialTime)
+{
+ assert(m_timeout > 0.0);
+}
+
+CEventQueue::CTimer::~CTimer()
+{
+ // do nothing
+}
+
+void
+CEventQueue::CTimer::reset()
+{
+ m_time = m_timeout;
+}
+
+CEventQueue::CTimer&
+CEventQueue::CTimer::operator-=(double dt)
+{
+ m_time -= dt;
+ return *this;
+}
+
+CEventQueue::CTimer::operator double() const
+{
+ return m_time;
+}
+
+bool
+CEventQueue::CTimer::isOneShot() const
+{
+ return m_oneShot;
+}
+
+CEventQueueTimer*
+CEventQueue::CTimer::getTimer() const
+{
+ return m_timer;
+}
+
+void*
+CEventQueue::CTimer::getTarget() const
+{
+ return m_target;
+}
+
+void
+CEventQueue::CTimer::fillEvent(CTimerEvent& event) const
+{
+ event.m_timer = m_timer;
+ event.m_count = 0;
+ if (m_time <= 0.0) {
+ event.m_count = static_cast((m_timeout - m_time) / m_timeout);
+ }
+}
+
+bool
+CEventQueue::CTimer::operator<(const CTimer& t) const
+{
+ return m_time < t.m_time;
+}
diff --git a/lib/base/CEventQueue.h b/lib/base/CEventQueue.h
new file mode 100644
index 00000000..a63c7b16
--- /dev/null
+++ b/lib/base/CEventQueue.h
@@ -0,0 +1,123 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CEVENTQUEUE_H
+#define CEVENTQUEUE_H
+
+#include "IEventQueue.h"
+#include "CEvent.h"
+#include "CPriorityQueue.h"
+#include "CStopwatch.h"
+#include "IArchMultithread.h"
+#include "stdmap.h"
+#include "stdset.h"
+
+//! Event queue
+/*!
+An event queue that implements the platform independent parts and
+delegates the platform dependent parts to a subclass.
+*/
+class CEventQueue : public IEventQueue {
+public:
+ CEventQueue();
+ virtual ~CEventQueue();
+
+ // IEventQueue overrides
+ virtual void adoptBuffer(IEventQueueBuffer*);
+ virtual bool getEvent(CEvent& event, double timeout = -1.0);
+ virtual bool dispatchEvent(const CEvent& event);
+ virtual void addEvent(const CEvent& event);
+ virtual CEventQueueTimer*
+ newTimer(double duration, void* target);
+ virtual CEventQueueTimer*
+ newOneShotTimer(double duration, void* target);
+ virtual void deleteTimer(CEventQueueTimer*);
+ virtual void adoptHandler(CEvent::Type type,
+ void* target, IEventJob* handler);
+ virtual void removeHandler(CEvent::Type type, void* target);
+ virtual void removeHandlers(void* target);
+ virtual CEvent::Type
+ registerType(const char* name);
+ virtual CEvent::Type
+ registerTypeOnce(CEvent::Type& type, const char* name);
+ virtual bool isEmpty() const;
+ virtual IEventJob* getHandler(CEvent::Type type, void* target) const;
+ virtual const char* getTypeName(CEvent::Type type);
+
+private:
+ UInt32 saveEvent(const CEvent& event);
+ CEvent removeEvent(UInt32 eventID);
+ bool hasTimerExpired(CEvent& event);
+ double getNextTimerTimeout() const;
+
+private:
+ class CTimer {
+ public:
+ CTimer(CEventQueueTimer*, double timeout, double initialTime,
+ void* target, bool oneShot);
+ ~CTimer();
+
+ void reset();
+
+ CTimer& operator-=(double);
+
+ operator double() const;
+
+ bool isOneShot() const;
+ CEventQueueTimer*
+ getTimer() const;
+ void* getTarget() const;
+ void fillEvent(CTimerEvent&) const;
+
+ bool operator<(const CTimer&) const;
+
+ private:
+ CEventQueueTimer* m_timer;
+ double m_timeout;
+ void* m_target;
+ bool m_oneShot;
+ double m_time;
+ };
+ typedef std::set CTimers;
+ typedef CPriorityQueue CTimerQueue;
+ typedef std::map CEventTable;
+ typedef std::vector CEventIDList;
+ typedef std::map CTypeMap;
+ typedef std::map CTypeHandlerTable;
+ typedef std::map CHandlerTable;
+
+ CArchMutex m_mutex;
+
+ // registered events
+ CEvent::Type m_nextType;
+ CTypeMap m_typeMap;
+
+ // buffer of events
+ IEventQueueBuffer* m_buffer;
+
+ // saved events
+ CEventTable m_events;
+ CEventIDList m_oldEventIDs;
+
+ // timers
+ CStopwatch m_time;
+ CTimers m_timers;
+ CTimerQueue m_timerQueue;
+ CTimerEvent m_timerEvent;
+
+ // event handlers
+ CHandlerTable m_handlers;
+};
+
+#endif
diff --git a/lib/base/CFunctionEventJob.cpp b/lib/base/CFunctionEventJob.cpp
new file mode 100644
index 00000000..5afdc988
--- /dev/null
+++ b/lib/base/CFunctionEventJob.cpp
@@ -0,0 +1,40 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CFunctionEventJob.h"
+
+//
+// CFunctionEventJob
+//
+
+CFunctionEventJob::CFunctionEventJob(
+ void (*func)(const CEvent&, void*), void* arg) :
+ m_func(func),
+ m_arg(arg)
+{
+ // do nothing
+}
+
+CFunctionEventJob::~CFunctionEventJob()
+{
+ // do nothing
+}
+
+void
+CFunctionEventJob::run(const CEvent& event)
+{
+ if (m_func != NULL) {
+ m_func(event, m_arg);
+ }
+}
diff --git a/lib/base/CFunctionEventJob.h b/lib/base/CFunctionEventJob.h
new file mode 100644
index 00000000..517b9c45
--- /dev/null
+++ b/lib/base/CFunctionEventJob.h
@@ -0,0 +1,38 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CFUNCTIONEVENTJOB_H
+#define CFUNCTIONEVENTJOB_H
+
+#include "IEventJob.h"
+
+//! Use a function as an event job
+/*!
+An event job class that invokes a function.
+*/
+class CFunctionEventJob : public IEventJob {
+public:
+ //! run() invokes \c func(arg)
+ CFunctionEventJob(void (*func)(const CEvent&, void*), void* arg = NULL);
+ virtual ~CFunctionEventJob();
+
+ // IEventJob overrides
+ virtual void run(const CEvent&);
+
+private:
+ void (*m_func)(const CEvent&, void*);
+ void* m_arg;
+};
+
+#endif
diff --git a/lib/base/CFunctionJob.cpp b/lib/base/CFunctionJob.cpp
new file mode 100644
index 00000000..bc16c509
--- /dev/null
+++ b/lib/base/CFunctionJob.cpp
@@ -0,0 +1,39 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CFunctionJob.h"
+
+//
+// CFunctionJob
+//
+
+CFunctionJob::CFunctionJob(void (*func)(void*), void* arg) :
+ m_func(func),
+ m_arg(arg)
+{
+ // do nothing
+}
+
+CFunctionJob::~CFunctionJob()
+{
+ // do nothing
+}
+
+void
+CFunctionJob::run()
+{
+ if (m_func != NULL) {
+ m_func(m_arg);
+ }
+}
diff --git a/lib/base/CFunctionJob.h b/lib/base/CFunctionJob.h
new file mode 100644
index 00000000..e30bbfa2
--- /dev/null
+++ b/lib/base/CFunctionJob.h
@@ -0,0 +1,38 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CFUNCTIONJOB_H
+#define CFUNCTIONJOB_H
+
+#include "IJob.h"
+
+//! Use a function as a job
+/*!
+A job class that invokes a function.
+*/
+class CFunctionJob : public IJob {
+public:
+ //! run() invokes \c func(arg)
+ CFunctionJob(void (*func)(void*), void* arg = NULL);
+ virtual ~CFunctionJob();
+
+ // IJob overrides
+ virtual void run();
+
+private:
+ void (*m_func)(void*);
+ void* m_arg;
+};
+
+#endif
diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp
new file mode 100644
index 00000000..7c73ac87
--- /dev/null
+++ b/lib/base/CLog.cpp
@@ -0,0 +1,293 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CLog.h"
+#include "CString.h"
+#include "CStringUtil.h"
+#include "LogOutputters.h"
+#include "CArch.h"
+#include "Version.h"
+#include
+#include
+
+// names of priorities
+static const char* g_priority[] = {
+ "FATAL",
+ "ERROR",
+ "WARNING",
+ "NOTE",
+ "INFO",
+ "DEBUG",
+ "DEBUG1",
+ "DEBUG2"
+ };
+
+// number of priorities
+static const int g_numPriority = (int)(sizeof(g_priority) /
+ sizeof(g_priority[0]));
+
+// the default priority
+#if defined(NDEBUG)
+static const int g_defaultMaxPriority = 4;
+#else
+static const int g_defaultMaxPriority = 5;
+#endif
+
+// length of longest string in g_priority
+static const int g_maxPriorityLength = 7;
+
+// length of suffix string (": ")
+static const int g_prioritySuffixLength = 2;
+
+// amount of padded required to fill in the priority prefix
+static const int g_priorityPad = g_maxPriorityLength +
+ g_prioritySuffixLength;
+
+
+//
+// CLog
+//
+
+CLog* CLog::s_log = NULL;
+
+CLog::CLog()
+{
+ assert(s_log == NULL);
+
+ // create mutex for multithread safe operation
+ m_mutex = ARCH->newMutex();
+
+ // other initalization
+ m_maxPriority = g_defaultMaxPriority;
+ m_maxNewlineLength = 0;
+ insert(new CConsoleLogOutputter);
+}
+
+CLog::~CLog()
+{
+ // clean up
+ for (COutputterList::iterator index = m_outputters.begin();
+ index != m_outputters.end(); ++index) {
+ delete *index;
+ }
+ for (COutputterList::iterator index = m_alwaysOutputters.begin();
+ index != m_alwaysOutputters.end(); ++index) {
+ delete *index;
+ }
+ ARCH->closeMutex(m_mutex);
+ s_log = NULL;
+}
+
+CLog*
+CLog::getInstance()
+{
+ // note -- not thread safe; client must initialize log safely
+ if (s_log == NULL) {
+ s_log = new CLog;
+ }
+ return s_log;
+}
+
+void
+CLog::print(const char* file, int line, const char* fmt, ...) const
+{
+ // check if fmt begins with a priority argument
+ int priority = 4;
+ if (fmt[0] == '%' && fmt[1] == 'z') {
+ priority = fmt[2] - '\060';
+ fmt += 3;
+ }
+
+ // done if below priority threshold
+ if (priority > getFilter()) {
+ return;
+ }
+
+ // compute prefix padding length
+ char stack[1024];
+ int pPad = g_priorityPad;
+ if (file != NULL) {
+ sprintf(stack, "%d", line);
+ pPad += strlen(file) + 1 /* comma */ +
+ strlen(stack) + 1 /* colon */ + 1 /* space */;
+ }
+
+ // compute suffix padding length
+ int sPad = m_maxNewlineLength;
+
+ // print to buffer, leaving space for a newline at the end and prefix
+ // at the beginning.
+ char* buffer = stack;
+ int len = (int)(sizeof(stack) / sizeof(stack[0]));
+ while (true) {
+ // try printing into the buffer
+ va_list args;
+ va_start(args, fmt);
+ int n = ARCH->vsnprintf(buffer + pPad, len - pPad - sPad, fmt, args);
+ va_end(args);
+
+ // if the buffer wasn't big enough then make it bigger and try again
+ if (n < 0 || n > (int)len) {
+ if (buffer != stack) {
+ delete[] buffer;
+ }
+ len *= 2;
+ buffer = new char[len];
+ }
+
+ // if the buffer was big enough then continue
+ else {
+ break;
+ }
+ }
+
+ // print the prefix to the buffer. leave space for priority label.
+ char* message = buffer;
+ if (file != NULL) {
+ sprintf(buffer + g_priorityPad, "%s,%d:", file, line);
+ buffer[pPad - 1] = ' ';
+
+ // discard file and line if priority < 0
+ if (priority < 0) {
+ message += pPad - g_priorityPad;
+ }
+ }
+
+ // output buffer
+ output(priority, message);
+
+ // clean up
+ if (buffer != stack) {
+ delete[] buffer;
+ }
+}
+
+void
+CLog::insert(ILogOutputter* outputter, bool alwaysAtHead)
+{
+ assert(outputter != NULL);
+ assert(outputter->getNewline() != NULL);
+
+ CArchMutexLock lock(m_mutex);
+ if (alwaysAtHead) {
+ m_alwaysOutputters.push_front(outputter);
+ }
+ else {
+ m_outputters.push_front(outputter);
+ }
+ int newlineLength = strlen(outputter->getNewline());
+ if (newlineLength > m_maxNewlineLength) {
+ m_maxNewlineLength = newlineLength;
+ }
+ outputter->open(kAppVersion);
+ outputter->show(false);
+}
+
+void
+CLog::remove(ILogOutputter* outputter)
+{
+ CArchMutexLock lock(m_mutex);
+ m_outputters.remove(outputter);
+ m_alwaysOutputters.remove(outputter);
+}
+
+void
+CLog::pop_front(bool alwaysAtHead)
+{
+ CArchMutexLock lock(m_mutex);
+ COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters;
+ if (!list->empty()) {
+ delete list->front();
+ list->pop_front();
+ }
+}
+
+bool
+CLog::setFilter(const char* maxPriority)
+{
+ if (maxPriority != NULL) {
+ for (int i = 0; i < g_numPriority; ++i) {
+ if (strcmp(maxPriority, g_priority[i]) == 0) {
+ setFilter(i);
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+}
+
+void
+CLog::setFilter(int maxPriority)
+{
+ CArchMutexLock lock(m_mutex);
+ m_maxPriority = maxPriority;
+}
+
+int
+CLog::getFilter() const
+{
+ CArchMutexLock lock(m_mutex);
+ return m_maxPriority;
+}
+
+void
+CLog::output(int priority, char* msg) const
+{
+ assert(priority >= -1 && priority < g_numPriority);
+ assert(msg != NULL);
+
+ // insert priority label
+ int n = -g_prioritySuffixLength;
+ if (priority >= 0) {
+ n = strlen(g_priority[priority]);
+ strcpy(msg + g_maxPriorityLength - n, g_priority[priority]);
+ msg[g_maxPriorityLength + 0] = ':';
+ msg[g_maxPriorityLength + 1] = ' ';
+ msg[g_maxPriorityLength + 1] = ' ';
+ }
+
+ // find end of message
+ char* end = msg + g_priorityPad + strlen(msg + g_priorityPad);
+
+ // write to each outputter
+ CArchMutexLock lock(m_mutex);
+ for (COutputterList::const_iterator index = m_alwaysOutputters.begin();
+ index != m_alwaysOutputters.end();
+ ++index) {
+ // get outputter
+ ILogOutputter* outputter = *index;
+
+ // put an appropriate newline at the end
+ strcpy(end, outputter->getNewline());
+
+ // write message
+ outputter->write(static_cast(priority),
+ msg + g_maxPriorityLength - n);
+ }
+ for (COutputterList::const_iterator index = m_outputters.begin();
+ index != m_outputters.end(); ++index) {
+ // get outputter
+ ILogOutputter* outputter = *index;
+
+ // put an appropriate newline at the end
+ strcpy(end, outputter->getNewline());
+
+ // write message and break out of loop if it returns false
+ if (!outputter->write(static_cast(priority),
+ msg + g_maxPriorityLength - n)) {
+ break;
+ }
+ }
+}
diff --git a/lib/base/CLog.h b/lib/base/CLog.h
new file mode 100644
index 00000000..391480e2
--- /dev/null
+++ b/lib/base/CLog.h
@@ -0,0 +1,202 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CLOG_H
+#define CLOG_H
+
+#include "common.h"
+#include "IArchMultithread.h"
+#include "stdlist.h"
+#include
+
+#define CLOG (CLog::getInstance())
+
+class ILogOutputter;
+
+//! Logging facility
+/*!
+The logging class; all console output should go through this class.
+It supports multithread safe operation, several message priority levels,
+filtering by priority, and output redirection. The macros LOG() and
+LOGC() provide convenient access.
+*/
+class CLog {
+public:
+ //! Log levels
+ /*!
+ The logging priority levels in order of highest to lowest priority.
+ */
+ enum ELevel {
+ kFATAL, //!< For fatal errors
+ kERROR, //!< For serious errors
+ kWARNING, //!< For minor errors and warnings
+ kNOTE, //!< For messages about notable events
+ kINFO, //!< For informational messages
+ kDEBUG, //!< For important debugging messages
+ kDEBUG1, //!< For more detailed debugging messages
+ kDEBUG2 //!< For even more detailed debugging messages
+ };
+
+ ~CLog();
+
+ //! @name manipulators
+ //@{
+
+ //! Add an outputter to the head of the list
+ /*!
+ Inserts an outputter to the head of the outputter list. When the
+ logger writes a message, it goes to the outputter at the head of
+ the outputter list. If that outputter's \c write() method returns
+ true then it also goes to the next outputter, as so on until an
+ outputter returns false or there are no more outputters. Outputters
+ still in the outputter list when the log is destroyed will be
+ deleted. If \c alwaysAtHead is true then the outputter is always
+ called before all outputters with \c alwaysAtHead false and the
+ return value of the outputter is ignored.
+
+ By default, the logger has one outputter installed which writes to
+ the console.
+ */
+ void insert(ILogOutputter* adopted,
+ bool alwaysAtHead = false);
+
+ //! Remove an outputter from the list
+ /*!
+ Removes the first occurrence of the given outputter from the
+ outputter list. It does nothing if the outputter is not in the
+ list. The outputter is not deleted.
+ */
+ void remove(ILogOutputter* orphaned);
+
+ //! Remove the outputter from the head of the list
+ /*!
+ Removes and deletes the outputter at the head of the outputter list.
+ This does nothing if the outputter list is empty. Only removes
+ outputters that were inserted with the matching \c alwaysAtHead.
+ */
+ void pop_front(bool alwaysAtHead = false);
+
+ //! Set the minimum priority filter.
+ /*!
+ Set the filter. Messages below this priority are discarded.
+ The default priority is 4 (INFO) (unless built without NDEBUG
+ in which case it's 5 (DEBUG)). setFilter(const char*) returns
+ true if the priority \c name was recognized; if \c name is NULL
+ then it simply returns true.
+ */
+ bool setFilter(const char* name);
+ void setFilter(int);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Print a log message
+ /*!
+ Print a log message using the printf-like \c format and arguments
+ preceded by the filename and line number. If \c file is NULL then
+ neither the file nor the line are printed.
+ */
+ void print(const char* file, int line,
+ const char* format, ...) const;
+
+ //! Get the minimum priority level.
+ int getFilter() const;
+
+ //! Get the singleton instance of the log
+ static CLog* getInstance();
+
+ //@}
+
+private:
+ CLog();
+
+ void output(int priority, char* msg) const;
+
+private:
+ typedef std::list COutputterList;
+
+ static CLog* s_log;
+
+ CArchMutex m_mutex;
+ COutputterList m_outputters;
+ COutputterList m_alwaysOutputters;
+ int m_maxNewlineLength;
+ int m_maxPriority;
+};
+
+/*!
+\def LOG(arg)
+Write to the log. Because macros cannot accept variable arguments, this
+should be invoked like so:
+\code
+LOG((CLOG_XXX "%d and %d are %s", x, y, x == y ? "equal" : "not equal"));
+\endcode
+In particular, notice the double open and close parentheses. Also note
+that there is no comma after the \c CLOG_XXX. The \c XXX should be
+replaced by one of enumerants in \c CLog::ELevel without the leading
+\c k. For example, \c CLOG_INFO. The special \c CLOG_PRINT level will
+not be filtered and is never prefixed by the filename and line number.
+
+If \c NOLOGGING is defined during the build then this macro expands to
+nothing. If \c NDEBUG is defined during the build then it expands to a
+call to CLog::print. Otherwise it expands to a call to CLog::printt,
+which includes the filename and line number.
+*/
+
+/*!
+\def LOGC(expr, arg)
+Write to the log if and only if expr is true. Because macros cannot accept
+variable arguments, this should be invoked like so:
+\code
+LOGC(x == y, (CLOG_XXX "%d and %d are equal", x, y));
+\endcode
+In particular, notice the parentheses around everything after the boolean
+expression. Also note that there is no comma after the \c CLOG_XXX.
+The \c XXX should be replaced by one of enumerants in \c CLog::ELevel
+without the leading \c k. For example, \c CLOG_INFO. The special
+\c CLOG_PRINT level will not be filtered and is never prefixed by the
+filename and line number.
+
+If \c NOLOGGING is defined during the build then this macro expands to
+nothing. If \c NDEBUG is not defined during the build then it expands
+to a call to CLog::print that prints the filename and line number,
+otherwise it expands to a call that doesn't.
+*/
+
+#if defined(NOLOGGING)
+#define LOG(_a1)
+#define LOGC(_a1, _a2)
+#define CLOG_TRACE
+#elif defined(NDEBUG)
+#define LOG(_a1) CLOG->print _a1
+#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2
+#define CLOG_TRACE NULL, 0,
+#else
+#define LOG(_a1) CLOG->print _a1
+#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2
+#define CLOG_TRACE __FILE__, __LINE__,
+#endif
+
+#define CLOG_PRINT CLOG_TRACE "%z\057"
+#define CLOG_CRIT CLOG_TRACE "%z\060"
+#define CLOG_ERR CLOG_TRACE "%z\061"
+#define CLOG_WARN CLOG_TRACE "%z\062"
+#define CLOG_NOTE CLOG_TRACE "%z\063"
+#define CLOG_INFO CLOG_TRACE "%z\064"
+#define CLOG_DEBUG CLOG_TRACE "%z\065"
+#define CLOG_DEBUG1 CLOG_TRACE "%z\066"
+#define CLOG_DEBUG2 CLOG_TRACE "%z\067"
+
+#endif
diff --git a/lib/base/CPriorityQueue.h b/lib/base/CPriorityQueue.h
new file mode 100644
index 00000000..29129e31
--- /dev/null
+++ b/lib/base/CPriorityQueue.h
@@ -0,0 +1,136 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CPRIORITYQUEUE_H
+#define CPRIORITYQUEUE_H
+
+#include "stdvector.h"
+#include
+#include
+
+//! A priority queue with an iterator
+/*!
+This priority queue is the same as a standard priority queue except:
+it sorts by std::greater, it has a forward iterator through the elements
+(which can appear in any order), and its contents can be swapped.
+*/
+template ,
+#if defined(_MSC_VER)
+ class Compare = std::greater >
+#else
+ class Compare = std::greater >
+#endif
+class CPriorityQueue {
+public:
+ typedef typename Container::value_type value_type;
+ typedef typename Container::size_type size_type;
+ typedef typename Container::iterator iterator;
+ typedef typename Container::const_iterator const_iterator;
+ typedef Container container_type;
+
+ CPriorityQueue() { }
+ CPriorityQueue(Container& swappedIn) { swap(swappedIn); }
+ ~CPriorityQueue() { }
+
+ //! @name manipulators
+ //@{
+
+ //! Add element
+ void push(const value_type& v)
+ {
+ c.push_back(v);
+ std::push_heap(c.begin(), c.end(), comp);
+ }
+
+ //! Remove head element
+ void pop()
+ {
+ std::pop_heap(c.begin(), c.end(), comp);
+ c.pop_back();
+ }
+
+ //! Erase element
+ void erase(iterator i)
+ {
+ c.erase(i);
+ std::make_heap(c.begin(), c.end(), comp);
+ }
+
+ //! Get start iterator
+ iterator begin()
+ {
+ return c.begin();
+ }
+
+ //! Get end iterator
+ iterator end()
+ {
+ return c.end();
+ }
+
+ //! Swap contents with another priority queue
+ void swap(CPriorityQueue& q)
+ {
+ c.swap(q.c);
+ }
+
+ //! Swap contents with another container
+ void swap(Container& c2)
+ {
+ c.swap(c2);
+ std::make_heap(c.begin(), c.end(), comp);
+ }
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Returns true if there are no elements
+ bool empty() const
+ {
+ return c.empty();
+ }
+
+ //! Returns the number of elements
+ size_type size() const
+ {
+ return c.size();
+ }
+
+ //! Returns the head element
+ const value_type& top() const
+ {
+ return c.front();
+ }
+
+ //! Get start iterator
+ const_iterator begin() const
+ {
+ return c.begin();
+ }
+
+ //! Get end iterator
+ const_iterator end() const
+ {
+ return c.end();
+ }
+
+ //@}
+
+private:
+ Container c;
+ Compare comp;
+};
+
+#endif
diff --git a/lib/base/CSimpleEventQueueBuffer.cpp b/lib/base/CSimpleEventQueueBuffer.cpp
new file mode 100644
index 00000000..8f2dbd14
--- /dev/null
+++ b/lib/base/CSimpleEventQueueBuffer.cpp
@@ -0,0 +1,97 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CSimpleEventQueueBuffer.h"
+#include "CStopwatch.h"
+#include "CArch.h"
+
+class CEventQueueTimer { };
+
+//
+// CSimpleEventQueueBuffer
+//
+
+CSimpleEventQueueBuffer::CSimpleEventQueueBuffer()
+{
+ m_queueMutex = ARCH->newMutex();
+ m_queueReadyCond = ARCH->newCondVar();
+ m_queueReady = false;
+}
+
+CSimpleEventQueueBuffer::~CSimpleEventQueueBuffer()
+{
+ ARCH->closeCondVar(m_queueReadyCond);
+ ARCH->closeMutex(m_queueMutex);
+}
+
+void
+CSimpleEventQueueBuffer::waitForEvent(double timeout)
+{
+ CArchMutexLock lock(m_queueMutex);
+ CStopwatch timer(true);
+ while (!m_queueReady) {
+ double timeLeft = timeout;
+ if (timeLeft >= 0.0) {
+ timeLeft -= timer.getTime();
+ if (timeLeft < 0.0) {
+ return;
+ }
+ }
+ ARCH->waitCondVar(m_queueReadyCond, m_queueMutex, timeLeft);
+ }
+}
+
+IEventQueueBuffer::Type
+CSimpleEventQueueBuffer::getEvent(CEvent&, UInt32& dataID)
+{
+ CArchMutexLock lock(m_queueMutex);
+ if (!m_queueReady) {
+ return kNone;
+ }
+ dataID = m_queue.back();
+ m_queue.pop_back();
+ m_queueReady = !m_queue.empty();
+ return kUser;
+}
+
+bool
+CSimpleEventQueueBuffer::addEvent(UInt32 dataID)
+{
+ CArchMutexLock lock(m_queueMutex);
+ m_queue.push_front(dataID);
+ if (!m_queueReady) {
+ m_queueReady = true;
+ ARCH->broadcastCondVar(m_queueReadyCond);
+ }
+ return true;
+}
+
+bool
+CSimpleEventQueueBuffer::isEmpty() const
+{
+ CArchMutexLock lock(m_queueMutex);
+ return !m_queueReady;
+}
+
+CEventQueueTimer*
+CSimpleEventQueueBuffer::newTimer(double, bool) const
+{
+ return new CEventQueueTimer;
+}
+
+void
+CSimpleEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const
+{
+ delete timer;
+}
diff --git a/lib/base/CSimpleEventQueueBuffer.h b/lib/base/CSimpleEventQueueBuffer.h
new file mode 100644
index 00000000..c395fabd
--- /dev/null
+++ b/lib/base/CSimpleEventQueueBuffer.h
@@ -0,0 +1,49 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CSIMPLEEVENTQUEUEBUFFER_H
+#define CSIMPLEEVENTQUEUEBUFFER_H
+
+#include "IEventQueueBuffer.h"
+#include "IArchMultithread.h"
+#include "stddeque.h"
+
+//! In-memory event queue buffer
+/*!
+An event queue buffer provides a queue of events for an IEventQueue.
+*/
+class CSimpleEventQueueBuffer : public IEventQueueBuffer {
+public:
+ CSimpleEventQueueBuffer();
+ ~CSimpleEventQueueBuffer();
+
+ // IEventQueueBuffer overrides
+ virtual void waitForEvent(double timeout);
+ virtual Type getEvent(CEvent& event, UInt32& dataID);
+ virtual bool addEvent(UInt32 dataID);
+ virtual bool isEmpty() const;
+ virtual CEventQueueTimer*
+ newTimer(double duration, bool oneShot) const;
+ virtual void deleteTimer(CEventQueueTimer*) const;
+
+private:
+ typedef std::deque CEventDeque;
+
+ CArchMutex m_queueMutex;
+ CArchCond m_queueReadyCond;
+ bool m_queueReady;
+ CEventDeque m_queue;
+};
+
+#endif
diff --git a/lib/base/CStopwatch.cpp b/lib/base/CStopwatch.cpp
new file mode 100644
index 00000000..89edce2b
--- /dev/null
+++ b/lib/base/CStopwatch.cpp
@@ -0,0 +1,126 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CStopwatch.h"
+#include "CArch.h"
+
+//
+// CStopwatch
+//
+
+CStopwatch::CStopwatch(bool triggered) :
+ m_mark(0.0),
+ m_triggered(triggered),
+ m_stopped(triggered)
+{
+ if (!triggered) {
+ m_mark = ARCH->time();
+ }
+}
+
+CStopwatch::~CStopwatch()
+{
+ // do nothing
+}
+
+double
+CStopwatch::reset()
+{
+ if (m_stopped) {
+ const double dt = m_mark;
+ m_mark = 0.0;
+ return dt;
+ }
+ else {
+ const double t = ARCH->time();
+ const double dt = t - m_mark;
+ m_mark = t;
+ return dt;
+ }
+}
+
+void
+CStopwatch::stop()
+{
+ if (m_stopped) {
+ return;
+ }
+
+ // save the elapsed time
+ m_mark = ARCH->time() - m_mark;
+ m_stopped = true;
+}
+
+void
+CStopwatch::start()
+{
+ m_triggered = false;
+ if (!m_stopped) {
+ return;
+ }
+
+ // set the mark such that it reports the time elapsed at stop()
+ m_mark = ARCH->time() - m_mark;
+ m_stopped = false;
+}
+
+void
+CStopwatch::setTrigger()
+{
+ stop();
+ m_triggered = true;
+}
+
+double
+CStopwatch::getTime()
+{
+ if (m_triggered) {
+ const double dt = m_mark;
+ start();
+ return dt;
+ }
+ else if (m_stopped) {
+ return m_mark;
+ }
+ else {
+ return ARCH->time() - m_mark;
+ }
+}
+
+CStopwatch::operator double()
+{
+ return getTime();
+}
+
+bool
+CStopwatch::isStopped() const
+{
+ return m_stopped;
+}
+
+double
+CStopwatch::getTime() const
+{
+ if (m_stopped) {
+ return m_mark;
+ }
+ else {
+ return ARCH->time() - m_mark;
+ }
+}
+
+CStopwatch::operator double() const
+{
+ return getTime();
+}
diff --git a/lib/base/CStopwatch.h b/lib/base/CStopwatch.h
new file mode 100644
index 00000000..e6a34719
--- /dev/null
+++ b/lib/base/CStopwatch.h
@@ -0,0 +1,108 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CSTOPWATCH_H
+#define CSTOPWATCH_H
+
+#include "common.h"
+
+//! A timer class
+/*!
+This class measures time intervals. All time interval measurement
+should use this class.
+*/
+class CStopwatch {
+public:
+ /*!
+ The default constructor does an implicit reset() or setTrigger().
+ If triggered == false then the clock starts ticking.
+ */
+ CStopwatch(bool triggered = false);
+ ~CStopwatch();
+
+ //! @name manipulators
+ //@{
+
+ //! Reset the timer to zero
+ /*!
+ Set the start time to the current time, returning the time since
+ the last reset. This does not remove the trigger if it's set nor
+ does it start a stopped clock. If the clock is stopped then
+ subsequent reset()'s will return 0.
+ */
+ double reset();
+
+ //! Stop the timer
+ /*!
+ Stop the stopwatch. The time interval while stopped is not
+ counted by the stopwatch. stop() does not remove the trigger.
+ Has no effect if already stopped.
+ */
+ void stop();
+
+ //! Start the timer
+ /*!
+ Start the stopwatch. start() removes the trigger, even if the
+ stopwatch was already started.
+ */
+ void start();
+
+ //! Stop the timer and set the trigger
+ /*!
+ setTrigger() stops the clock like stop() except there's an
+ implicit start() the next time (non-const) getTime() is called.
+ This is useful when you want the clock to start the first time
+ you check it.
+ */
+ void setTrigger();
+
+ //! Get elapsed time
+ /*!
+ Returns the time since the last reset() (or calls reset() and
+ returns zero if the trigger is set).
+ */
+ double getTime();
+ //! Same as getTime()
+ operator double();
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Check if timer is stopped
+ /*!
+ Returns true if the stopwatch is stopped.
+ */
+ bool isStopped() const;
+
+ // return the time since the last reset().
+ //! Get elapsed time
+ /*!
+ Returns the time since the last reset(). This cannot trigger the
+ stopwatch to start and will not clear the trigger.
+ */
+ double getTime() const;
+ //! Same as getTime() const
+ operator double() const;
+ //@}
+
+private:
+ double getClock() const;
+
+private:
+ double m_mark;
+ bool m_triggered;
+ bool m_stopped;
+};
+
+#endif
diff --git a/lib/base/CString.h b/lib/base/CString.h
new file mode 100644
index 00000000..bc905009
--- /dev/null
+++ b/lib/base/CString.h
@@ -0,0 +1,25 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CSTRING_H
+#define CSTRING_H
+
+#include "common.h"
+#include "stdstring.h"
+
+// use standard C++ string class for our string class
+typedef std::string CString;
+
+#endif
+
diff --git a/lib/base/CStringUtil.cpp b/lib/base/CStringUtil.cpp
new file mode 100644
index 00000000..46361932
--- /dev/null
+++ b/lib/base/CStringUtil.cpp
@@ -0,0 +1,194 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CStringUtil.h"
+#include "CArch.h"
+#include "common.h"
+#include "stdvector.h"
+#include
+#include
+#include
+#include
+
+//
+// CStringUtil
+//
+
+CString
+CStringUtil::format(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ CString result = vformat(fmt, args);
+ va_end(args);
+ return result;
+}
+
+CString
+CStringUtil::vformat(const char* fmt, va_list args)
+{
+ // find highest indexed substitution and the locations of substitutions
+ std::vector pos;
+ std::vector width;
+ std::vector index;
+ int maxIndex = 0;
+ for (const char* scan = fmt; *scan != '\0'; ++scan) {
+ if (*scan == '%') {
+ ++scan;
+ if (*scan == '\0') {
+ break;
+ }
+ else if (*scan == '%') {
+ // literal
+ index.push_back(0);
+ pos.push_back(static_cast(scan - 1 - fmt));
+ width.push_back(2);
+ }
+ else if (*scan == '{') {
+ // get argument index
+ char* end;
+ int i = static_cast(strtol(scan + 1, &end, 10));
+ if (*end != '}') {
+ // invalid index -- ignore
+ scan = end - 1;
+ }
+ else {
+ index.push_back(i);
+ pos.push_back(static_cast(scan - 1 - fmt));
+ width.push_back(static_cast(end - scan + 2));
+ if (i > maxIndex) {
+ maxIndex = i;
+ }
+ scan = end;
+ }
+ }
+ else {
+ // improper escape -- ignore
+ }
+ }
+ }
+
+ // get args
+ std::vector value;
+ std::vector length;
+ value.push_back("%");
+ length.push_back(1);
+ for (int i = 0; i < maxIndex; ++i) {
+ const char* arg = va_arg(args, const char*);
+ size_t len = strlen(arg);
+ value.push_back(arg);
+ length.push_back(len);
+ }
+
+ // compute final length
+ size_t resultLength = strlen(fmt);
+ const int n = static_cast(pos.size());
+ for (int i = 0; i < n; ++i) {
+ resultLength -= width[i];
+ resultLength += length[index[i]];
+ }
+
+ // substitute
+ CString result;
+ result.reserve(resultLength);
+ size_t src = 0;
+ for (int i = 0; i < n; ++i) {
+ result.append(fmt + src, pos[i] - src);
+ result.append(value[index[i]]);
+ src = pos[i] + width[i];
+ }
+ result.append(fmt + src);
+
+ return result;
+}
+
+CString
+CStringUtil::print(const char* fmt, ...)
+{
+ char tmp[1024];
+ char* buffer = tmp;
+ int len = (int)(sizeof(tmp) / sizeof(tmp[0]));
+ CString result;
+ while (buffer != NULL) {
+ // try printing into the buffer
+ va_list args;
+ va_start(args, fmt);
+ int n = ARCH->vsnprintf(buffer, len, fmt, args);
+ va_end(args);
+
+ // if the buffer wasn't big enough then make it bigger and try again
+ if (n < 0 || n > len) {
+ if (buffer != tmp) {
+ delete[] buffer;
+ }
+ len *= 2;
+ buffer = new char[len];
+ }
+
+ // if it was big enough then save the string and don't try again
+ else {
+ result = buffer;
+ if (buffer != tmp) {
+ delete[] buffer;
+ }
+ buffer = NULL;
+ }
+ }
+
+ return result;
+}
+
+
+//
+// CStringUtil::CaselessCmp
+//
+
+bool
+CStringUtil::CaselessCmp::cmpEqual(
+ const CString::value_type& a,
+ const CString::value_type& b)
+{
+ // should use std::tolower but not in all versions of libstdc++ have it
+ return tolower(a) == tolower(b);
+}
+
+bool
+CStringUtil::CaselessCmp::cmpLess(
+ const CString::value_type& a,
+ const CString::value_type& b)
+{
+ // should use std::tolower but not in all versions of libstdc++ have it
+ return tolower(a) < tolower(b);
+}
+
+bool
+CStringUtil::CaselessCmp::less(const CString& a, const CString& b)
+{
+ return std::lexicographical_compare(
+ a.begin(), a.end(),
+ b.begin(), b.end(),
+ &CStringUtil::CaselessCmp::cmpLess);
+}
+
+bool
+CStringUtil::CaselessCmp::equal(const CString& a, const CString& b)
+{
+ return !(less(a, b) || less(b, a));
+}
+
+bool
+CStringUtil::CaselessCmp::operator()(const CString& a, const CString& b) const
+{
+ return less(a, b);
+}
diff --git a/lib/base/CStringUtil.h b/lib/base/CStringUtil.h
new file mode 100644
index 00000000..8ee86647
--- /dev/null
+++ b/lib/base/CStringUtil.h
@@ -0,0 +1,77 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CSTRINGUTIL_H
+#define CSTRINGUTIL_H
+
+#include "CString.h"
+#include
+
+//! String utilities
+/*!
+This class provides various functions for string manipulation.
+*/
+class CStringUtil {
+public:
+ //! Format positional arguments
+ /*!
+ Format a string using positional arguments. fmt has literal
+ characters and conversion specifications introduced by `\%':
+ - \c\%\% -- literal `\%'
+ - \c\%{n} -- positional element n, n a positive integer, {} are literal
+
+ All arguments in the variable list are const char*. Positional
+ elements are indexed from 1.
+ */
+ static CString format(const char* fmt, ...);
+
+ //! Format positional arguments
+ /*!
+ Same as format() except takes va_list.
+ */
+ static CString vformat(const char* fmt, va_list);
+
+ //! Print a string using printf-style formatting
+ /*!
+ Equivalent to printf() except the result is returned as a CString.
+ */
+ static CString print(const char* fmt, ...);
+
+ //! Case-insensitive comparisons
+ /*!
+ This class provides case-insensitve comparison functions.
+ */
+ class CaselessCmp {
+ public:
+ //! Same as less()
+ bool operator()(const CString& a, const CString& b) const;
+
+ //! Returns true iff \c a is lexicographically less than \c b
+ static bool less(const CString& a, const CString& b);
+
+ //! Returns true iff \c a is lexicographically equal to \c b
+ static bool equal(const CString& a, const CString& b);
+
+ //! Returns true iff \c a is lexicographically less than \c b
+ static bool cmpLess(const CString::value_type& a,
+ const CString::value_type& b);
+
+ //! Returns true iff \c a is lexicographically equal to \c b
+ static bool cmpEqual(const CString::value_type& a,
+ const CString::value_type& b);
+ };
+};
+
+#endif
+
diff --git a/lib/base/CUnicode.cpp b/lib/base/CUnicode.cpp
new file mode 100644
index 00000000..4dcfcd6f
--- /dev/null
+++ b/lib/base/CUnicode.cpp
@@ -0,0 +1,779 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * 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 COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "CUnicode.h"
+#include "CArch.h"
+#include
+
+//
+// local utility functions
+//
+
+inline
+static
+UInt16
+decode16(const UInt8* n, bool byteSwapped)
+{
+ union x16 {
+ UInt8 n8[2];
+ UInt16 n16;
+ } c;
+ if (byteSwapped) {
+ c.n8[0] = n[1];
+ c.n8[1] = n[0];
+ }
+ else {
+ c.n8[0] = n[0];
+ c.n8[1] = n[1];
+ }
+ return c.n16;
+}
+
+inline
+static
+UInt32
+decode32(const UInt8* n, bool byteSwapped)
+{
+ union x32 {
+ UInt8 n8[4];
+ UInt32 n32;
+ } c;
+ if (byteSwapped) {
+ c.n8[0] = n[3];
+ c.n8[1] = n[2];
+ c.n8[2] = n[1];
+ c.n8[3] = n[0];
+ }
+ else {
+ c.n8[0] = n[0];
+ c.n8[1] = n[1];
+ c.n8[2] = n[2];
+ c.n8[3] = n[3];
+ }
+ return c.n32;
+}
+
+inline
+static
+void
+resetError(bool* errors)
+{
+ if (errors != NULL) {
+ *errors = false;
+ }
+}
+
+inline
+static
+void
+setError(bool* errors)
+{
+ if (errors != NULL) {
+ *errors = true;
+ }
+}
+
+
+//
+// CUnicode
+//
+
+UInt32 CUnicode::s_invalid = 0x0000ffff;
+UInt32 CUnicode::s_replacement = 0x0000fffd;
+
+bool
+CUnicode::isUTF8(const CString& src)
+{
+ // convert and test each character
+ const UInt8* data = reinterpret_cast(src.c_str());
+ for (UInt32 n = src.size(); n > 0; ) {
+ if (fromUTF8(data, n) == s_invalid) {
+ return false;
+ }
+ }
+ return true;
+}
+
+CString
+CUnicode::UTF8ToUCS2(const CString& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = src.size();
+ CString dst;
+ dst.reserve(2 * n);
+
+ // convert each character
+ const UInt8* data = reinterpret_cast(src.c_str());
+ while (n > 0) {
+ UInt32 c = fromUTF8(data, n);
+ if (c == s_invalid) {
+ c = s_replacement;
+ }
+ else if (c >= 0x00010000) {
+ setError(errors);
+ c = s_replacement;
+ }
+ UInt16 ucs2 = static_cast(c);
+ dst.append(reinterpret_cast(&ucs2), 2);
+ }
+
+ return dst;
+}
+
+CString
+CUnicode::UTF8ToUCS4(const CString& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = src.size();
+ CString dst;
+ dst.reserve(4 * n);
+
+ // convert each character
+ const UInt8* data = reinterpret_cast(src.c_str());
+ while (n > 0) {
+ UInt32 c = fromUTF8(data, n);
+ if (c == s_invalid) {
+ c = s_replacement;
+ }
+ dst.append(reinterpret_cast(&c), 4);
+ }
+
+ return dst;
+}
+
+CString
+CUnicode::UTF8ToUTF16(const CString& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = src.size();
+ CString dst;
+ dst.reserve(2 * n);
+
+ // convert each character
+ const UInt8* data = reinterpret_cast(src.c_str());
+ while (n > 0) {
+ UInt32 c = fromUTF8(data, n);
+ if (c == s_invalid) {
+ c = s_replacement;
+ }
+ else if (c >= 0x00110000) {
+ setError(errors);
+ c = s_replacement;
+ }
+ if (c < 0x00010000) {
+ UInt16 ucs2 = static_cast(c);
+ dst.append(reinterpret_cast(&ucs2), 2);
+ }
+ else {
+ c -= 0x00010000;
+ UInt16 utf16h = static_cast((c >> 10) + 0xd800);
+ UInt16 utf16l = static_cast((c & 0x03ff) + 0xdc00);
+ dst.append(reinterpret_cast(&utf16h), 2);
+ dst.append(reinterpret_cast(&utf16l), 2);
+ }
+ }
+
+ return dst;
+}
+
+CString
+CUnicode::UTF8ToUTF32(const CString& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = src.size();
+ CString dst;
+ dst.reserve(4 * n);
+
+ // convert each character
+ const UInt8* data = reinterpret_cast(src.c_str());
+ while (n > 0) {
+ UInt32 c = fromUTF8(data, n);
+ if (c == s_invalid) {
+ c = s_replacement;
+ }
+ else if (c >= 0x00110000) {
+ setError(errors);
+ c = s_replacement;
+ }
+ dst.append(reinterpret_cast(&c), 4);
+ }
+
+ return dst;
+}
+
+CString
+CUnicode::UTF8ToText(const CString& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert to wide char
+ UInt32 size;
+ wchar_t* tmp = UTF8ToWideChar(src, size, errors);
+
+ // convert string to multibyte
+ int len = ARCH->convStringWCToMB(NULL, tmp, size, errors);
+ char* mbs = new char[len + 1];
+ ARCH->convStringWCToMB(mbs, tmp, size, errors);
+ CString text(mbs, len);
+
+ // clean up
+ delete[] mbs;
+ delete[] tmp;
+
+ return text;
+}
+
+CString
+CUnicode::UCS2ToUTF8(const CString& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = src.size() >> 1;
+ return doUCS2ToUTF8(reinterpret_cast(src.data()), n, errors);
+}
+
+CString
+CUnicode::UCS4ToUTF8(const CString& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = src.size() >> 2;
+ return doUCS4ToUTF8(reinterpret_cast(src.data()), n, errors);
+}
+
+CString
+CUnicode::UTF16ToUTF8(const CString& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = src.size() >> 1;
+ return doUTF16ToUTF8(reinterpret_cast(src.data()), n, errors);
+}
+
+CString
+CUnicode::UTF32ToUTF8(const CString& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = src.size() >> 2;
+ return doUTF32ToUTF8(reinterpret_cast(src.data()), n, errors);
+}
+
+CString
+CUnicode::textToUTF8(const CString& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert string to wide characters
+ UInt32 n = src.size();
+ int len = ARCH->convStringMBToWC(NULL, src.c_str(), n, errors);
+ wchar_t* wcs = new wchar_t[len + 1];
+ ARCH->convStringMBToWC(wcs, src.c_str(), n, errors);
+
+ // convert to UTF8
+ CString utf8 = wideCharToUTF8(wcs, len, errors);
+
+ // clean up
+ delete[] wcs;
+
+ return utf8;
+}
+
+wchar_t*
+CUnicode::UTF8ToWideChar(const CString& src, UInt32& size, bool* errors)
+{
+ // convert to platform's wide character encoding
+ CString tmp;
+ switch (ARCH->getWideCharEncoding()) {
+ case IArchString::kUCS2:
+ tmp = UTF8ToUCS2(src, errors);
+ size = tmp.size() >> 1;
+ break;
+
+ case IArchString::kUCS4:
+ tmp = UTF8ToUCS4(src, errors);
+ size = tmp.size() >> 2;
+ break;
+
+ case IArchString::kUTF16:
+ tmp = UTF8ToUTF16(src, errors);
+ size = tmp.size() >> 1;
+ break;
+
+ case IArchString::kUTF32:
+ tmp = UTF8ToUTF32(src, errors);
+ size = tmp.size() >> 2;
+ break;
+
+ default:
+ assert(0 && "unknown wide character encoding");
+ }
+
+ // copy to a wchar_t array
+ wchar_t* dst = new wchar_t[size];
+ ::memcpy(dst, tmp.data(), sizeof(wchar_t) * size);
+ return dst;
+}
+
+CString
+CUnicode::wideCharToUTF8(const wchar_t* src, UInt32 size, bool* errors)
+{
+ // convert from platform's wide character encoding.
+ // note -- this must include a wide nul character (independent of
+ // the CString's nul character).
+ switch (ARCH->getWideCharEncoding()) {
+ case IArchString::kUCS2:
+ return doUCS2ToUTF8(reinterpret_cast(src), size, errors);
+
+ case IArchString::kUCS4:
+ return doUCS4ToUTF8(reinterpret_cast(src), size, errors);
+
+ case IArchString::kUTF16:
+ return doUTF16ToUTF8(reinterpret_cast(src), size, errors);
+
+ case IArchString::kUTF32:
+ return doUTF32ToUTF8(reinterpret_cast(src), size, errors);
+
+ default:
+ assert(0 && "unknown wide character encoding");
+ return CString();
+ }
+}
+
+CString
+CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ CString dst;
+ dst.reserve(n);
+
+ // check if first character is 0xfffe or 0xfeff
+ bool byteSwapped = false;
+ if (n >= 1) {
+ switch (decode16(data, false)) {
+ case 0x0000feff:
+ data += 2;
+ --n;
+ break;
+
+ case 0x0000fffe:
+ byteSwapped = true;
+ data += 2;
+ --n;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // convert each character
+ for (; n > 0; data += 2, --n) {
+ UInt32 c = decode16(data, byteSwapped);
+ toUTF8(dst, c, errors);
+ }
+
+ return dst;
+}
+
+CString
+CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ CString dst;
+ dst.reserve(n);
+
+ // check if first character is 0xfffe or 0xfeff
+ bool byteSwapped = false;
+ if (n >= 1) {
+ switch (decode32(data, false)) {
+ case 0x0000feff:
+ data += 4;
+ --n;
+ break;
+
+ case 0x0000fffe:
+ byteSwapped = true;
+ data += 4;
+ --n;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // convert each character
+ for (; n > 0; data += 4, --n) {
+ UInt32 c = decode32(data, byteSwapped);
+ toUTF8(dst, c, errors);
+ }
+
+ return dst;
+}
+
+CString
+CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ CString dst;
+ dst.reserve(n);
+
+ // check if first character is 0xfffe or 0xfeff
+ bool byteSwapped = false;
+ if (n >= 1) {
+ switch (decode16(data, false)) {
+ case 0x0000feff:
+ data += 2;
+ --n;
+ break;
+
+ case 0x0000fffe:
+ byteSwapped = true;
+ data += 2;
+ --n;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // convert each character
+ for (; n > 0; data += 2, --n) {
+ UInt32 c = decode16(data, byteSwapped);
+ if (c < 0x0000d800 || c > 0x0000dfff) {
+ toUTF8(dst, c, errors);
+ }
+ else if (n == 1) {
+ // error -- missing second word
+ setError(errors);
+ toUTF8(dst, s_replacement, NULL);
+ }
+ else if (c >= 0x0000d800 && c <= 0x0000dbff) {
+ UInt32 c2 = decode16(data, byteSwapped);
+ data += 2;
+ --n;
+ if (c2 < 0x0000dc00 || c2 > 0x0000dfff) {
+ // error -- [d800,dbff] not followed by [dc00,dfff]
+ setError(errors);
+ toUTF8(dst, s_replacement, NULL);
+ }
+ else {
+ c = (((c - 0x0000d800) << 10) | (c2 - 0x0000dc00)) + 0x00010000;
+ toUTF8(dst, c, errors);
+ }
+ }
+ else {
+ // error -- [dc00,dfff] without leading [d800,dbff]
+ setError(errors);
+ toUTF8(dst, s_replacement, NULL);
+ }
+ }
+
+ return dst;
+}
+
+CString
+CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ CString dst;
+ dst.reserve(n);
+
+ // check if first character is 0xfffe or 0xfeff
+ bool byteSwapped = false;
+ if (n >= 1) {
+ switch (decode32(data, false)) {
+ case 0x0000feff:
+ data += 4;
+ --n;
+ break;
+
+ case 0x0000fffe:
+ byteSwapped = true;
+ data += 4;
+ --n;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // convert each character
+ for (; n > 0; data += 4, --n) {
+ UInt32 c = decode32(data, byteSwapped);
+ if (c >= 0x00110000) {
+ setError(errors);
+ c = s_replacement;
+ }
+ toUTF8(dst, c, errors);
+ }
+
+ return dst;
+}
+
+UInt32
+CUnicode::fromUTF8(const UInt8*& data, UInt32& n)
+{
+ assert(data != NULL);
+ assert(n != 0);
+
+ // compute character encoding length, checking for overlong
+ // sequences (i.e. characters that don't use the shortest
+ // possible encoding).
+ UInt32 size;
+ if (data[0] < 0x80) {
+ // 0xxxxxxx
+ size = 1;
+ }
+ else if (data[0] < 0xc0) {
+ // 10xxxxxx -- in the middle of a multibyte character. counts
+ // as one invalid character.
+ --n;
+ ++data;
+ return s_invalid;
+ }
+ else if (data[0] < 0xe0) {
+ // 110xxxxx
+ size = 2;
+ }
+ else if (data[0] < 0xf0) {
+ // 1110xxxx
+ size = 3;
+ }
+ else if (data[0] < 0xf8) {
+ // 11110xxx
+ size = 4;
+ }
+ else if (data[0] < 0xfc) {
+ // 111110xx
+ size = 5;
+ }
+ else if (data[0] < 0xfe) {
+ // 1111110x
+ size = 6;
+ }
+ else {
+ // invalid sequence. dunno how many bytes to skip so skip one.
+ --n;
+ ++data;
+ return s_invalid;
+ }
+
+ // make sure we have enough data
+ if (size > n) {
+ data += n;
+ n = 0;
+ return s_invalid;
+ }
+
+ // extract character
+ UInt32 c;
+ switch (size) {
+ case 1:
+ c = static_cast(data[0]);
+ break;
+
+ case 2:
+ c = ((static_cast(data[0]) & 0x1f) << 6) |
+ ((static_cast(data[1]) & 0x3f) );
+ break;
+
+ case 3:
+ c = ((static_cast(data[0]) & 0x0f) << 12) |
+ ((static_cast(data[1]) & 0x3f) << 6) |
+ ((static_cast(data[2]) & 0x3f) );
+ break;
+
+ case 4:
+ c = ((static_cast(data[0]) & 0x07) << 18) |
+ ((static_cast(data[1]) & 0x3f) << 12) |
+ ((static_cast(data[1]) & 0x3f) << 6) |
+ ((static_cast(data[1]) & 0x3f) );
+ break;
+
+ case 5:
+ c = ((static_cast(data[0]) & 0x03) << 24) |
+ ((static_cast(data[1]) & 0x3f) << 18) |
+ ((static_cast(data[1]) & 0x3f) << 12) |
+ ((static_cast(data[1]) & 0x3f) << 6) |
+ ((static_cast