From 64232c7854d1bad1c27f5d9b0825ada91e86fb97 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 9 Jul 2002 21:22:31 +0000 Subject: [PATCH] updated to new automake and refactored server stuff. the server now speaks to the primary screen and secondary screens almost everywhere the same way through an IClient interface; only special primary screen calls are accessed through a different interface, the CPrimaryClient interface. this simplifies the server since it no longer needs to test whether the active screen is the primary or a secondary in most cases. the server no longer speaks directly to the primary screen; all that goes through the CPrimaryClient, which often just forwards the call. the primary screen no longer speaks directly to the server either, again going through the CPrimaryClient via a IPrimaryReceiver interface. CServerProtocol classes have been replaced by CClientProxy classes which are very similar. the name makes more sense though. --- acsite.m4 => acinclude.m4 | 0 config/depcomp | 411 ++++++++++ config/missing | 109 ++- mt/CThread.cpp | 3 +- mt/CThread.h | 7 +- notes | 109 ++- platform/CXWindowsClipboard.cpp | 2 + server/CClientProxy.cpp | 47 ++ server/CClientProxy.h | 61 ++ server/CClientProxy1_0.cpp | 337 ++++++++ server/CClientProxy1_0.h | 52 ++ server/CPrimaryClient.cpp | 308 +++++++ server/CPrimaryClient.h | 91 +++ server/CServer.cpp | 1270 ++++++++++++++--------------- server/CServer.h | 149 ++-- server/CServerProtocol.cpp | 77 -- server/CServerProtocol.h | 61 -- server/CServerProtocol1_0.cpp | 283 ------- server/CServerProtocol1_0.h | 41 - server/CXWindowsPrimaryScreen.cpp | 78 +- server/CXWindowsPrimaryScreen.h | 13 +- server/IPrimaryReceiver.h | 46 ++ server/IPrimaryScreen.h | 43 +- server/IServerProtocol.h | 53 -- server/Makefile.am | 17 +- synergy/IClient.h | 91 +++ synergy/IServer.h | 43 + synergy/Makefile.am | 3 +- 28 files changed, 2415 insertions(+), 1390 deletions(-) rename acsite.m4 => acinclude.m4 (100%) create mode 100755 config/depcomp create mode 100644 server/CClientProxy.cpp create mode 100644 server/CClientProxy.h create mode 100644 server/CClientProxy1_0.cpp create mode 100644 server/CClientProxy1_0.h create mode 100644 server/CPrimaryClient.cpp create mode 100644 server/CPrimaryClient.h delete mode 100644 server/CServerProtocol.cpp delete mode 100644 server/CServerProtocol.h delete mode 100644 server/CServerProtocol1_0.cpp delete mode 100644 server/CServerProtocol1_0.h create mode 100644 server/IPrimaryReceiver.h delete mode 100644 server/IServerProtocol.h create mode 100644 synergy/IClient.h create mode 100644 synergy/IServer.h diff --git a/acsite.m4 b/acinclude.m4 similarity index 100% rename from acsite.m4 rename to acinclude.m4 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/missing b/config/missing index 7789652e..0a7fb5a2 100755 --- a/config/missing +++ b/config/missing @@ -1,7 +1,7 @@ #! /bin/sh # Common stub for a few missing GNU programs while installing. -# Copyright (C) 1996, 1997 Free Software Foundation, Inc. -# Franc,ois Pinard , 1996. +# 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 @@ -18,11 +18,37 @@ # 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) @@ -35,6 +61,7 @@ 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' @@ -43,13 +70,15 @@ Supported PROGRAM values: 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 - GNU libit 0.0" + echo "missing 0.3 - GNU automake" ;; -*) @@ -61,7 +90,7 @@ Supported PROGRAM values: aclocal) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if - you modified \`acinclude.m4' or \`configure.in'. You might want + 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 @@ -70,7 +99,7 @@ WARNING: \`$1' is missing on your system. You should only need it if autoconf) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if - you modified \`configure.in'. You might want to install the + you modified \`${configure_ac}'. You might want to install the \`Autoconf' and \`GNU m4' packages. Grab them from any GNU archive site." touch configure @@ -79,10 +108,10 @@ WARNING: \`$1' is missing on your system. You should only need it if autoheader) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if - you modified \`acconfig.h' or \`configure.in'. You might want + 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.in` + 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 @@ -98,7 +127,7 @@ WARNING: \`$1' is missing on your system. You should only need it if 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.in'. + 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 | @@ -159,7 +188,32 @@ WARNING: \`$1' is missing on your system. You should only need it if 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 @@ -175,6 +229,45 @@ WARNING: \`$1' is missing on your system. You should only need it if 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 diff --git a/mt/CThread.cpp b/mt/CThread.cpp index 80637a23..2741a4c4 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -127,8 +127,7 @@ CThread::getResult() const void* CThread::getUserData() { - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - return currentRep->getUserData(); + return m_rep->getUserData(); } bool diff --git a/mt/CThread.h b/mt/CThread.h index 264bd2ba..a6243707 100644 --- a/mt/CThread.h +++ b/mt/CThread.h @@ -88,15 +88,14 @@ public: // return a thread object representing the calling thread static CThread getCurrentThread(); - // get the user data passed to the constructor for the current - // thread. - static void* getUserData(); - // testCancel() does nothing but is a cancellation point. call // this to make a function itself a cancellation point. // (cancellation point) static void testCancel(); + // get the user data passed to the constructor for this thread. + void* getUserData(); + // waits for the thread to terminate (by exit() or cancel() or // by returning from the thread job). returns immediately if // the thread has already terminated. returns immediately with diff --git a/notes b/notes index c0ff39ba..eda240ce 100644 --- a/notes +++ b/notes @@ -7,16 +7,27 @@ hangup if invalid query info --> <-- info (size) +--- +nedit doesn't seem to be playing nice with the motif clipboard lock + it's not releasing it + may need to learn more about how the motif clipboard lock works + not sure if this a new problem with synergy or a problem with new nedit + --- enable heartbeat disconnection or remove heartbeat sending in client should either ship with it fully enabled or fully disabled possibly make it part of startup negotiation --- -sometimes jumping too far into secondary screen - probably bogus delta (adding in center pixel coords into delta?) - this is with an X primary and secondary - don't know which matters but probably X primary +getting a stuttering when leaving win32 server screen + +--- +merge platform dependent code into platform independent where possible + also try to simplify where possible + +--- +IServer and IPrimaryReceiver are really similar + can we merge them into one? --- use automake @@ -33,20 +44,59 @@ HTTP stuff handy for debugging at least --- -make generic input devices? - replace mouse and keyboard with button and valuator devices? - could fairly easily add new devices then - valuators would have to come in groups that change together - mouse position would be two ganged valuators - key events are not simply button presses though - they have a non-boolean character, shift state, and repeat count - maybe another device type: {data, state, repeat} +negotiation: + use a generic negotiation message (name/value pair) or specific + messages (name/values)? later allows more parsing to be done by + CProtocolUtil but how can we skip unknown messages? would have + to have a method on CInputPacketStream to discard to end of + message and we'd need to know the stream is a CInputPacketStream. -must handle on-the-fly addition/removal of devices - USB allows hotplugging and we should support, say, an added joystick + how does negotiation proceed? + could have sequence of queries and replies: + server -> client -> server ... end + client -> server -> client ... end + or a sequence of messages: + server -> client ... end + client -> server ... end + probably go with latter because it's more efficient but make it + 3-way: + client -> server ... end + server -> client ... end + client -> server ... end + first messages from client must be version and name. the server + can create the appropriate protocol object right away then. -must have device negotiation - at very least it negotiates mouse/keyboard at start + example: clipboard formats: + # CNEG = negotiation command CNEG%s%s + # CNGE = end of negotiation command + # cbfa = clipboard, format, add (permitted format) + # cbia = clipboard, id, add (clipboard with id exists) + client -> server: CNEG "vers" "1.0" + client -> server: CNEG "name" "foobar" + client -> server: CNGE + server -> client: CNEG "cbfa" "text/plain/LF" + server -> client: CNEG "cbfa" "image/BMP" + server -> client: CNGE + client -> server: CNEG "cbia" "0" + client -> server: CNGE + + server should just ask CProtocol (renamed from CServerProtocol) to + return a protocol. CProtocol should do negotiation, create the + appropriate protocol object, finish negotiation, and return the + new protocol object. unless connection is registered with server, + though, we can't save any screen info. perhaps CProtocol should + also return a screen info object or that should be part of the + protocol object (currently the protocol is part of the screen info). + if screen info is available to CProtocol then it should finish with + requesting the screen info, waiting for and then handling the reply. + the server can then just ask CProtocol for the protocol object then + add it to the connections. + + maybe call the protocol types CClientProxy since that's how the + server uses it: as a stand-in for the client object. the interface + should closely reflect the CClient interface. do the reverse for + a server proxy for the client. maybe even have interface classes + for the client and server methods. --- should add clipboard data type format negotiation @@ -72,6 +122,28 @@ allows compatibility with versions that are identical except for the support clipboard formats, obviating bumping up the protocol version number. +maybe config file can list format shared libraries to load. user +can then pick formats to support. but why would you ever want +fewer formats? + +should the major burden of translation be on client or server? +probably client since it knows the formats preferred by the +platform and may be able to use platform utilities to convert. +server would then just support formats that minimize loss of +information. + +desired formats to support: + text (LF) + text with character set + unicode (LF) + rich text (or some kind of formatting) + bitmap (BMP, PNG?) + sound (WAV) + +note that formats should be added to the win32 clipboard in the +order of most descriptive to least because they're kept in order +and apps will generally use the first suitable match. + --- hot keys should have keyboard shortcuts to jump to screens @@ -130,6 +202,7 @@ xscreensaver won't start if pointer on secondary screen unless using MIT or SGI screen saver extensions otherwise it cannot know when to stop the screen saver cos it can't grab mouse and keyboard + --- all screensavers hidden when showing win32 screensaver lock window win32 kills the screen saver and shows the lock window @@ -396,6 +469,10 @@ gspencer: code, since if you have a key down you aren't supposed to be able to cross over... +gspencer: + bouncy mouse at edge of screen (win32 server). almost certainly + related to changes that fixed mouse delta calculation. + == fixed? === dragging windows is too slow diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 08cb1f98..c2cd225e 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -525,6 +525,7 @@ CXWindowsClipboard::motifLockClipboard() const // fail if anybody owns the lock (even us, so this is non-recursive) Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); if (lockOwner != None) { + log((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner)); return false; } @@ -536,6 +537,7 @@ CXWindowsClipboard::motifLockClipboard() const XSetSelectionOwner(m_display, m_atomMotifClipLock, m_window, time); lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); if (lockOwner != m_window) { + log((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner)); return false; } diff --git a/server/CClientProxy.cpp b/server/CClientProxy.cpp new file mode 100644 index 00000000..372475c1 --- /dev/null +++ b/server/CClientProxy.cpp @@ -0,0 +1,47 @@ +#include "CClientProxy.h" +#include "IInputStream.h" +#include "IOutputStream.h" + +// +// CClientProxy +// + +CClientProxy::CClientProxy(IServer* server, const CString& name, + IInputStream* input, IOutputStream* output) : + m_server(server), + m_name(name), + m_input(input), + m_output(output) +{ + // do nothing +} + +CClientProxy::~CClientProxy() +{ + delete m_output; + delete m_input; +} + +IServer* +CClientProxy::getServer() const +{ + return m_server; +} + +IInputStream* +CClientProxy::getInputStream() const +{ + return m_input; +} + +IOutputStream* +CClientProxy::getOutputStream() const +{ + return m_output; +} + +CString +CClientProxy::getName() const +{ + return m_name; +} diff --git a/server/CClientProxy.h b/server/CClientProxy.h new file mode 100644 index 00000000..1b6c17bb --- /dev/null +++ b/server/CClientProxy.h @@ -0,0 +1,61 @@ +#ifndef CCLIENTPROXY_H +#define CCLIENTPROXY_H + +#include "IClient.h" +#include "CString.h" + +class IInputStream; +class IOutputStream; +class IServer; + +class CClientProxy : public IClient { +public: + CClientProxy(IServer* server, const CString& name, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CClientProxy(); + + // manipulators + + // accessors + + // get the server + IServer* getServer() const; + + // get the input and output streams for the client + IInputStream* getInputStream() const; + IOutputStream* getOutputStream() const; + + // IClient overrides + virtual void open() = 0; + virtual void run() = 0; + virtual void close() = 0; + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool screenSaver) = 0; + virtual bool leave() = 0; + virtual void setClipboard(ClipboardID, const CString&) = 0; + virtual void grabClipboard(ClipboardID) = 0; + virtual void setClipboardDirty(ClipboardID, bool) = 0; + virtual void keyDown(KeyID, KeyModifierMask) = 0; + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void keyUp(KeyID, KeyModifierMask) = 0; + virtual void mouseDown(ButtonID) = 0; + virtual void mouseUp(ButtonID) = 0; + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void mouseWheel(SInt32 delta) = 0; + virtual void screenSaver(bool activate) = 0; + virtual CString getName() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + virtual void getCenter(SInt32& x, SInt32& y) const = 0; + virtual SInt32 getJumpZoneSize() const = 0; + +private: + IServer* m_server; + CString m_name; + IInputStream* m_input; + IOutputStream* m_output; +}; + +#endif diff --git a/server/CClientProxy1_0.cpp b/server/CClientProxy1_0.cpp new file mode 100644 index 00000000..b8139b7a --- /dev/null +++ b/server/CClientProxy1_0.cpp @@ -0,0 +1,337 @@ +#include "CClientProxy1_0.h" +#include "CServer.h" +#include "CClipboard.h" +#include "CProtocolUtil.h" +#include "ProtocolTypes.h" +#include "XSynergy.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" +#include "CStopwatch.h" +#include + +// +// CClientProxy1_0 +// + +CClientProxy1_0::CClientProxy1_0(IServer* server, const CString& name, + IInputStream* input, IOutputStream* output) : + CClientProxy(server, name, input, output) +{ + for (UInt32 i = 0; i < kClipboardEnd; ++i) { + m_clipboardDirty[i] = true; + } +} + +CClientProxy1_0::~CClientProxy1_0() +{ + // do nothing +} + +void +CClientProxy1_0::open() +{ + // send request + log((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgQInfo); + getOutputStream()->flush(); + + // wait for and verify reply + UInt8 code[4]; + for (;;) { + UInt32 n = getInputStream()->read(code, 4, -1.0); + if (n == 4) { + if (memcmp(code, kMsgCNoop, 4) == 0) { + // discard heartbeats + continue; + } + if (memcmp(code, kMsgDInfo, 4) == 0) { + break; + } + } + throw XBadClient(); + } + + // handle reply + recvInfo(false); +} + +void +CClientProxy1_0::run() +{ + // handle messages until the client hangs up or stops sending heartbeats + CStopwatch heartTimer; + for (;;) { + CThread::testCancel(); + + // wait for a message + UInt8 code[4]; + UInt32 n = getInputStream()->read(code, 4, kHeartRate); + CThread::testCancel(); + + // check if client hungup + if (n == 0) { + log((CLOG_NOTE "client \"%s\" disconnected", getName().c_str())); + return; + } + + // check if client has stopped sending heartbeats + if (n == (UInt32)-1) { +/* FIXME -- disabled to avoid masking bugs + if (heartTimer.getTime() > kHeartDeath) { + log((CLOG_NOTE "client \"%s\" is dead", getName().c_str())); + return; + } +*/ + continue; + } + + // got a message so reset heartbeat monitor + heartTimer.reset(); + + // verify we got an entire code + if (n != 4) { + log((CLOG_ERR "incomplete message from \"%s\": %d bytes", getName().c_str(), n)); + + // client sent an incomplete message + throw XBadClient(); + } + + // parse message + log((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); + if (memcmp(code, kMsgDInfo, 4) == 0) { + recvInfo(true); + } + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // discard no-ops + continue; + } + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + recvGrabClipboard(); + } + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + recvClipboard(); + } + // note -- more message handlers go here + else { + log((CLOG_ERR "invalid message from client \"%s\"", getName().c_str())); + + // unknown message + throw XBadClient(); + } + } +} + +void +CClientProxy1_0::close() +{ + log((CLOG_DEBUG1 "send close to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCClose); + + // force the close to be sent before we return + getOutputStream()->flush(); +} + +void +CClientProxy1_0::enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, bool) +{ + log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getName().c_str(), xAbs, yAbs, seqNum, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgCEnter, + xAbs, yAbs, seqNum, mask); +} + +bool +CClientProxy1_0::leave() +{ + log((CLOG_DEBUG1 "send leave to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCLeave); + + // we can never prevent the user from leaving + return true; +} + +void +CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) +{ + // ignore if this clipboard is already clean + CLock lock(&m_mutex); + if (m_clipboardDirty[id]) { + // this clipboard is now clean + m_clipboardDirty[id] = false; + + log((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); + } +} + +void +CClientProxy1_0::grabClipboard(ClipboardID id) +{ + log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); + + // this clipboard is now dirty + CLock lock(&m_mutex); + m_clipboardDirty[id] = true; +} + +void +CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty) +{ + CLock lock(&m_mutex); + m_clipboardDirty[id] = dirty; +} + +void +CClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask) +{ + log((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); +} + +void +CClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count) +{ + log((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getName().c_str(), key, mask, count)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); +} + +void +CClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask) +{ + log((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); +} + +void +CClientProxy1_0::mouseDown(ButtonID button) +{ + log((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getName().c_str(), button)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); +} + +void +CClientProxy1_0::mouseUp(ButtonID button) +{ + log((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getName().c_str(), button)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); +} + +void +CClientProxy1_0::mouseMove(SInt32 xAbs, SInt32 yAbs) +{ + log((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getName().c_str(), xAbs, yAbs)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); +} + +void +CClientProxy1_0::mouseWheel(SInt32 delta) +{ + log((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), delta)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); +} + +void +CClientProxy1_0::screenSaver(bool on) +{ + log((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getName().c_str(), on ? 1 : 0)); + CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); +} + +void +CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + CLock lock(&m_mutex); + x = m_x; + y = m_y; + w = m_w; + h = m_h; +} + +void +CClientProxy1_0::getCenter(SInt32& x, SInt32& y) const +{ + CLock lock(&m_mutex); + x = m_cx; + y = m_cy; +} + +SInt32 +CClientProxy1_0::getJumpZoneSize() const +{ + CLock lock(&m_mutex); + return m_zoneSize; +} + +void +CClientProxy1_0::recvInfo(bool notify) +{ + { + CLock lock(&m_mutex); + + // parse the message + CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, + &m_x, &m_y, &m_w, &m_h, + &m_zoneSize, &m_cx, &m_cy); + log((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), m_x, m_y, m_w, m_h, m_zoneSize, m_cx, m_cy)); + + // validate + if (m_w <= 0 || m_h <= 0 || m_zoneSize < 0) { + throw XBadClient(); + } + if (m_cx < m_x || m_cy < m_y || m_cx >= m_x + m_w || m_cy >= m_y + m_h) { + throw XBadClient(); + } + } + + // tell server of change + if (notify) { + getServer()->onInfoChanged(getName()); + } + + // acknowledge receipt + log((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCInfoAck); +} + +void +CClientProxy1_0::recvClipboard() +{ + // parse message + ClipboardID id; + UInt32 seqNum; + CString data; + CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); + log((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, data.size())); + + // validate + if (id >= kClipboardEnd) { + throw XBadClient(); + } + + // send update. this calls us back to reset our clipboard dirty flag + // so don't hold a lock during the call. + getServer()->onClipboardChanged(id, seqNum, data); +} + +void +CClientProxy1_0::recvGrabClipboard() +{ + // parse message + ClipboardID id; + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getName().c_str(), id, seqNum)); + + // validate + if (id >= kClipboardEnd) { + throw XBadClient(); + } + + // send update. this calls us back to reset our clipboard dirty flag + // so don't hold a lock during the call. + getServer()->onGrabClipboard(id, seqNum, getName()); +} diff --git a/server/CClientProxy1_0.h b/server/CClientProxy1_0.h new file mode 100644 index 00000000..4d43da64 --- /dev/null +++ b/server/CClientProxy1_0.h @@ -0,0 +1,52 @@ +#ifndef CCLIENTPROXY1_0_H +#define CCLIENTPROXY1_0_H + +#include "CClientProxy.h" +#include "CMutex.h" + +class CClientProxy1_0 : public CClientProxy { +public: + CClientProxy1_0(IServer* server, const CString& name, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CClientProxy1_0(); + + // IClient overrides + virtual void open(); + virtual void run(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool screenSaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool); + virtual void keyDown(KeyID, KeyModifierMask); + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void keyUp(KeyID, KeyModifierMask); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseWheel(SInt32 delta); + virtual void screenSaver(bool activate); + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCenter(SInt32& x, SInt32& y) const; + virtual SInt32 getJumpZoneSize() const; + +private: + void recvInfo(bool notify); + void recvClipboard(); + void recvGrabClipboard(); + +private: + CMutex m_mutex; + SInt16 m_x, m_y; + SInt16 m_w, m_h; + SInt16 m_zoneSize; + SInt16 m_cx, m_cy; + bool m_clipboardDirty[kClipboardEnd]; +}; + +#endif diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp new file mode 100644 index 00000000..b646aef1 --- /dev/null +++ b/server/CPrimaryClient.cpp @@ -0,0 +1,308 @@ +#include "CPrimaryClient.h" +#include "IServer.h" +#include "IPrimaryScreen.h" +#include "CClipboard.h" +#include "CLog.h" + +// FIXME -- use factory to create screen +#if WINDOWS_LIKE +#include "CMSWindowsPrimaryScreen.h" +#elif UNIX_LIKE +#include "CXWindowsPrimaryScreen.h" +#endif + +// +// CPrimaryClient +// + +CPrimaryClient::CPrimaryClient(IServer* server, const CString& name) : + m_server(server), + m_name(name), + m_seqNum(0) +{ + assert(m_server != NULL); + + // create screen + log((CLOG_DEBUG1 "creating primary screen")); +#if WINDOWS_LIKE + m_screen = new CMSWindowsPrimaryScreen(this); +#elif UNIX_LIKE + m_screen = new CXWindowsPrimaryScreen(this); +#endif +} + +CPrimaryClient::~CPrimaryClient() +{ + delete m_screen; +} + +void +CPrimaryClient::stop() +{ + m_screen->stop(); +} + +void +CPrimaryClient::reconfigure() +{ + m_screen->reconfigure(); +} + +void +CPrimaryClient::getClipboard(ClipboardID id, CString& data) const +{ + CClipboard clipboard; + m_screen->getClipboard(id, &clipboard); + data = clipboard.marshall(); +} + +bool +CPrimaryClient::isLockedToScreen() const +{ + return m_screen->isLockedToScreen(); +} + +KeyModifierMask +CPrimaryClient::getToggleMask() const +{ + return m_screen->getToggleMask(); +} + +void +CPrimaryClient::onError() +{ + m_server->onError(); +} + +void +CPrimaryClient::onInfoChanged(SInt32 x, SInt32 y, SInt32 w, SInt32 h, + SInt32 zoneSize, SInt32 cx, SInt32 cy) +{ + m_x = x; + m_y = y; + m_w = w; + m_h = h; + m_zoneSize = zoneSize; + m_cx = cx; + m_cy = cy; + m_server->onInfoChanged(getName()); +} + +bool +CPrimaryClient::onGrabClipboard(ClipboardID id) +{ + m_clipboardOwner[id] = m_server->onGrabClipboard(id, m_seqNum, getName()); +} + +bool +CPrimaryClient::onClipboardChanged(ClipboardID id, const CString& data) +{ + m_server->onClipboardChanged(id, m_seqNum, data); +} + +void +CPrimaryClient::onKeyDown(KeyID id, KeyModifierMask mask) +{ + m_server->onKeyDown(id, mask); +} + +void +CPrimaryClient::onKeyUp(KeyID id, KeyModifierMask mask) +{ + m_server->onKeyUp(id, mask); +} + +void +CPrimaryClient::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) +{ + m_server->onKeyRepeat(id, mask, count); +} + +void +CPrimaryClient::onMouseDown(ButtonID id) +{ + m_server->onMouseDown(id); +} + +void +CPrimaryClient::onMouseUp(ButtonID id) +{ + m_server->onMouseUp(id); +} + +bool +CPrimaryClient::onMouseMovePrimary(SInt32 x, SInt32 y) +{ + return m_server->onMouseMovePrimary(x, y); +} + +void +CPrimaryClient::onMouseMoveSecondary(SInt32 dx, SInt32 dy) +{ + m_server->onMouseMoveSecondary(dx, dy); +} + +void +CPrimaryClient::onMouseWheel(SInt32 delta) +{ + m_server->onMouseWheel(delta); +} + +void +CPrimaryClient::onScreenSaver(bool activated) +{ + m_server->onScreenSaver(activated); +} + +void +CPrimaryClient::open() +{ + // all clipboards are clean and owned by us + for (UInt32 i = 0; i < kClipboardEnd; ++i) { + m_clipboardOwner[i] = true; + m_clipboardDirty[i] = false; + } + + // now open the screen + m_screen->open(); +} + +void +CPrimaryClient::run() +{ + m_screen->run(); +} + +void +CPrimaryClient::close() +{ + m_screen->close(); +} + +void +CPrimaryClient::enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask, bool screensaver) +{ + // note -- we must not call any server methods except onError(). + m_seqNum = seqNum; + m_screen->enter(xAbs, yAbs, screensaver); +} + +bool +CPrimaryClient::leave() +{ + // note -- we must not call any server methods except onError(). + return m_screen->leave(); +} + +void +CPrimaryClient::setClipboard(ClipboardID id, const CString& data) +{ + // note -- we must not call any server methods except onError(). + + // ignore if this clipboard is already clean + if (m_clipboardDirty[id]) { + // this clipboard is now clean + m_clipboardDirty[id] = false; + + // unmarshall data + CClipboard clipboard; + clipboard.unmarshall(data, 0); + + // set clipboard + m_screen->setClipboard(id, &clipboard); + } +} + +void +CPrimaryClient::grabClipboard(ClipboardID id) +{ + // grab clipboard + m_screen->grabClipboard(id); + + // clipboard is dirty (because someone else owns it now) + m_clipboardOwner[id] = false; + m_clipboardDirty[id] = true; +} + +void +CPrimaryClient::setClipboardDirty(ClipboardID id, bool dirty) +{ + m_clipboardDirty[id] = dirty; +} + +void +CPrimaryClient::keyDown(KeyID, KeyModifierMask) +{ + // ignore +} + +void +CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32) +{ + // ignore +} + +void +CPrimaryClient::keyUp(KeyID, KeyModifierMask) +{ + // ignore +} + +void +CPrimaryClient::mouseDown(ButtonID) +{ + // ignore +} + +void +CPrimaryClient::mouseUp(ButtonID) +{ + // ignore +} + +void +CPrimaryClient::mouseMove(SInt32 x, SInt32 y) +{ + m_screen->warpCursor(x, y); +} + +void +CPrimaryClient::mouseWheel(SInt32) +{ + // ignore +} + +void +CPrimaryClient::screenSaver(bool) +{ + // ignore +} + +CString +CPrimaryClient::getName() const +{ + return m_name; +} + +void +CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + x = m_x; + y = m_y; + w = m_w; + h = m_h; +} + +void +CPrimaryClient::getCenter(SInt32& x, SInt32& y) const +{ + x = m_cx; + y = m_cy; +} + +SInt32 +CPrimaryClient::getJumpZoneSize() const +{ + return m_zoneSize; +} diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h new file mode 100644 index 00000000..a7017edf --- /dev/null +++ b/server/CPrimaryClient.h @@ -0,0 +1,91 @@ +#ifndef CPRIMARYCLIENT_H +#define CPRIMARYCLIENT_H + +#include "IPrimaryReceiver.h" +#include "IClient.h" + +class IClipboard; +class IPrimaryScreen; +class IServer; + +class CPrimaryClient : public IPrimaryReceiver, public IClient { +public: + CPrimaryClient(IServer*, const CString& name); + ~CPrimaryClient(); + + // manipulators + + // cause run() to return + void stop(); + + // called by server when the configuration changes + void reconfigure(); + + // accessors + + // return the contents of the given clipboard. + void getClipboard(ClipboardID, CString&) const; + + // returns true iff the user is locked to the primary screen + bool isLockedToScreen() const; + + // returns the state of the toggle keys on the primary screen + KeyModifierMask getToggleMask() const; + + // IPrimaryReceiver overrides + virtual void onError(); + virtual void onInfoChanged(SInt32 xScreen, SInt32 yScreen, + SInt32 wScreen, SInt32 hScreen, + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse); + virtual bool onGrabClipboard(ClipboardID); + virtual bool onClipboardChanged(ClipboardID, const CString& data); + virtual void onKeyDown(KeyID, KeyModifierMask); + virtual void onKeyUp(KeyID, KeyModifierMask); + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void onMouseDown(ButtonID); + virtual void onMouseUp(ButtonID); + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); + virtual void onMouseWheel(SInt32 delta); + virtual void onScreenSaver(bool activated); + + // IClient overrides + virtual void open(); + virtual void run(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool screenSaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool); + virtual void keyDown(KeyID, KeyModifierMask); + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void keyUp(KeyID, KeyModifierMask); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseWheel(SInt32 delta); + virtual void screenSaver(bool activate); + virtual CString getName() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCenter(SInt32& x, SInt32& y) const; + virtual SInt32 getJumpZoneSize() const; + +private: + IServer* m_server; + IPrimaryScreen* m_screen; + CString m_name; + UInt32 m_seqNum; + SInt16 m_x, m_y; + SInt16 m_w, m_h; + SInt16 m_zoneSize; + SInt16 m_cx, m_cy; + bool m_clipboardOwner[kClipboardEnd]; + bool m_clipboardDirty[kClipboardEnd]; +}; + +#endif diff --git a/server/CServer.cpp b/server/CServer.cpp index 5e7004ba..f414bfd0 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -2,9 +2,9 @@ #include "CHTTPServer.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" +#include "CPrimaryClient.h" #include "CProtocolUtil.h" -#include "CServerProtocol.h" -#include "IPrimaryScreen.h" +#include "CClientProxy1_0.h" #include "ProtocolTypes.h" #include "XScreen.h" #include "XSynergy.h" @@ -38,9 +38,8 @@ const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; CServer::CServer(const CString& serverName) : m_name(serverName), - m_primary(NULL), m_active(NULL), - m_primaryInfo(NULL), + m_primaryClient(NULL), m_seqNum(0), m_activeSaver(NULL), m_httpServer(NULL), @@ -85,7 +84,7 @@ CServer::run() // check preconditions { CLock lock(&m_mutex); - assert(m_primary != NULL); + assert(m_primaryClient != NULL); } try { @@ -103,36 +102,38 @@ CServer::run() // handle events log((CLOG_DEBUG "starting event handling")); - m_primary->run(); + m_primaryClient->run(); // clean up log((CLOG_NOTE "stopping server")); - stopThreads(); - delete m_httpServer; - m_httpServer = NULL; - closePrimaryScreen(); + + // use a macro to write the stuff that should go into a finally + // block so we can repeat it easily. stroustrup's view that + // "resource acquistion is initialization" is a better solution + // than a finally block is parochial. they both have their + // place. adding finally to C++ would've been a drop in a big + // bucket. +#define FINALLY do { \ + stopThreads(); \ + delete m_httpServer; \ + m_httpServer = NULL; \ + if (m_primaryClient != NULL) { \ + closePrimaryScreen(); \ + } \ + } while (false) + FINALLY; } catch (XBase& e) { log((CLOG_ERR "server error: %s", e.what())); // clean up log((CLOG_NOTE "stopping server")); - stopThreads(); - delete m_httpServer; - m_httpServer = NULL; - if (m_primary != NULL) { - closePrimaryScreen(); - } + FINALLY; } catch (XThread&) { // clean up log((CLOG_NOTE "stopping server")); - stopThreads(); - delete m_httpServer; - m_httpServer = NULL; - if (m_primary != NULL) { - closePrimaryScreen(); - } + FINALLY; throw; } catch (...) { @@ -140,96 +141,41 @@ CServer::run() // clean up log((CLOG_NOTE "stopping server")); - stopThreads(); - delete m_httpServer; - m_httpServer = NULL; - if (m_primary != NULL) { - closePrimaryScreen(); - } + FINALLY; throw; } +#undef FINALLY } void CServer::quit() { - m_primary->stop(); -} - -void -CServer::shutdown() -{ - // stop all running threads but don't wait too long since some - // threads may be unable to proceed until this thread returns. - stopThreads(3.0); - - // done with the HTTP server - delete m_httpServer; - m_httpServer = NULL; - - // note -- we do not attempt to close down the primary screen + m_primaryClient->stop(); } bool CServer::setConfig(const CConfig& config) { - typedef std::vector CThreads; - CThreads threads; + // refuse configuration if it doesn't include the primary screen { CLock lock(&m_mutex); - - // refuse configuration if it doesn't include the primary screen - if (m_primaryInfo != NULL && - !config.isScreen(m_primaryInfo->m_name)) { + if (m_primaryClient != NULL && + !config.isScreen(m_primaryClient->getName())) { return false; } - - // get the set of screens that are connected but are being - // dropped from the configuration (or who's canonical name - // is changing). don't add the primary screen. also tell - // the secondary screen to disconnect. - for (CScreenList::const_iterator index = m_screens.begin(); - index != m_screens.end(); ++index) { - if (index->second != m_primaryInfo && - !config.isCanonicalName(index->first)) { - assert(index->second->m_protocol != NULL); - index->second->m_protocol->sendClose(); - threads.push_back(index->second->m_thread); - } - } } - // wait a moment to allow each secondary screen to close - // its connection before we close it (to avoid having our - // socket enter TIME_WAIT). - if (threads.size() > 0) { - CThread::sleep(1.0); - } - - // cancel the old secondary screen threads - for (CThreads::iterator index = threads.begin(); - index != threads.end(); ++index) { - index->cancel(); - } - - // wait for old secondary screen threads to disconnect. must - // not hold lock while we do this so those threads can finish - // any calls to this object. - for (CThreads::iterator index = threads.begin(); - index != threads.end(); ++index) { - index->wait(); - } - - // clean up thread list - reapThreads(); + // close clients that are connected but being dropped from the + // configuration. + closeClients(config); // cut over CLock lock(&m_mutex); m_config = config; // tell primary screen about reconfiguration - if (m_primary != NULL) { - m_primary->onConfigure(); + if (m_primaryClient != NULL) { + m_primaryClient->reconfigure(); } return true; @@ -275,65 +221,50 @@ CServer::getActivePrimarySides() const } void -CServer::setInfo(SInt32 x, SInt32 y, SInt32 w, SInt32 h, - SInt32 zoneSize, SInt32 mx, SInt32 my) +CServer::onError() { - CLock lock(&m_mutex); - assert(m_primaryInfo != NULL); - setInfoNoLock(m_primaryInfo->m_name, x, y, w, h, zoneSize, mx, my); + // stop all running threads but don't wait too long since some + // threads may be unable to proceed until this thread returns. + stopThreads(3.0); + + // done with the HTTP server + delete m_httpServer; + m_httpServer = NULL; + + // note -- we do not attempt to close down the primary screen } void -CServer::setInfo(const CString& client, - SInt32 x, SInt32 y, SInt32 w, SInt32 h, - SInt32 zoneSize, SInt32 mx, SInt32 my) +CServer::onInfoChanged(const CString& name) { CLock lock(&m_mutex); - setInfoNoLock(client, x, y, w, h, zoneSize, mx, my); -} -void -CServer::setInfoNoLock(const CString& screen, - SInt32 x, SInt32 y, SInt32 w, SInt32 h, - SInt32 zoneSize, SInt32 mx, SInt32 my) -{ - assert(!screen.empty()); - assert(w > 0); - assert(h > 0); - assert(zoneSize >= 0); - - // screen must be connected - CScreenList::iterator index = m_screens.find(screen); - if (index == m_screens.end()) { + // look up client + CClientList::iterator index = m_clients.find(name); + if (index == m_clients.end()) { throw XBadClient(); } + IClient* client = index->second; + assert(client != NULL); - // screen is now ready (i.e. available to user) - CScreenInfo* info = index->second; - info->m_ready = true; - - // update screen info - if (info == m_active) { - // update the remote mouse coordinates - m_x = mx; - m_y = my; + // update the remote mouse coordinates + if (client == m_active) { + client->getCenter(m_x, m_y); } - info->m_x = x; - info->m_y = y; - info->m_w = w; - info->m_h = h; - info->m_zoneSize = zoneSize; - log((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", screen.c_str(), x, y, w, h, zoneSize, mx, my)); - // send acknowledgement (if screen isn't the primary) - if (info->m_protocol != NULL) { - info->m_protocol->sendInfoAcknowledgment(); +#ifndef NDEBUG + { + SInt32 x, y, w, h, mx, my; + client->getShape(x, y, w, h); + client->getCenter(mx, my); + log((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", name.c_str(), x, y, w, h, client->getJumpZoneSize(), m_x, m_y)); } +#endif // handle resolution change to primary screen - else { - if (info == m_active) { - onMouseMovePrimaryNoLock(mx, my); + if (client == m_primaryClient) { + if (client == m_active) { + onMouseMovePrimaryNoLock(m_x, m_y); } else { onMouseMoveSecondaryNoLock(0, 0); @@ -342,109 +273,111 @@ CServer::setInfoNoLock(const CString& screen, } void -CServer::grabClipboard(ClipboardID id) +CServer::onGrabClipboard(ClipboardID id) { CLock lock(&m_mutex); - assert(m_primaryInfo != NULL); - grabClipboardNoLock(id, 0, m_primaryInfo->m_name); + assert(m_primaryClient != NULL); + grabClipboardNoLock(id, 0, m_primaryClient->getName()); } -void -CServer::grabClipboard(ClipboardID id, UInt32 seqNum, const CString& client) +bool +CServer::onGrabClipboard(ClipboardID id, UInt32 seqNum, const CString& client) { CLock lock(&m_mutex); - grabClipboardNoLock(id, seqNum, client); + return grabClipboardNoLock(id, seqNum, client); } -void +bool CServer::grabClipboardNoLock(ClipboardID id, - UInt32 seqNum, const CString& screen) + UInt32 seqNum, const CString& name) { // note -- must be locked on entry CClipboardInfo& clipboard = m_clipboards[id]; // screen must be connected - CScreenList::iterator index = m_screens.find(screen); - if (index == m_screens.end()) { + CClientList::iterator grabber = m_clients.find(name); + if (grabber == m_clients.end()) { throw XBadClient(); } // ignore grab if sequence number is old. always allow primary // screen to grab. - if (screen != m_primaryInfo->m_name && + if (name != m_primaryClient->getName() && seqNum < clipboard.m_clipboardSeqNum) { - log((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", screen.c_str(), id)); - return; + log((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", name.c_str(), id)); + return false; } // mark screen as owning clipboard - log((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", screen.c_str(), id, clipboard.m_clipboardOwner.c_str())); - clipboard.m_clipboardOwner = screen; + log((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", name.c_str(), id, clipboard.m_clipboardOwner.c_str())); + clipboard.m_clipboardOwner = name; clipboard.m_clipboardSeqNum = seqNum; - // no screens have the new clipboard except the sender - clearGotClipboard(id); - index->second->m_gotClipboard[id] = true; + // clear the clipboard data (since it's not known at this point) + if (clipboard.m_clipboard.open(0)) { + clipboard.m_clipboard.empty(); + clipboard.m_clipboard.close(); + } + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); - // tell all other screens to take ownership of clipboard - for (index = m_screens.begin(); index != m_screens.end(); ++index) { - if (index->first != screen) { - CScreenInfo* info = index->second; - if (info->m_protocol == NULL) { - m_primary->grabClipboard(id); - } - else { - info->m_protocol->sendGrabClipboard(id); - } + // tell all other screens to take ownership of clipboard. tell the + // grabber that it's clipboard isn't dirty. + for (CClientList::iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + if (index == grabber) { + client->setClipboardDirty(id, false); + } + else { + client->grabClipboard(id); } } - // get the clipboard data if primary has it, otherwise mark the - // clipboard data as unknown. - if (m_active->m_protocol == NULL) { - // get clipboard immediately from primary screen - m_primary->getClipboard(id, &clipboard.m_clipboard); - clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); - clipboard.m_clipboardReady = true; - } - else { - // clear out the clipboard since existing data is now out of date. - if (clipboard.m_clipboard.open(0)) { - clipboard.m_clipboard.close(); - } - clipboard.m_clipboardReady = false; - } + return true; } void -CServer::setClipboard(ClipboardID id, UInt32 seqNum, const CString& data) +CServer::onClipboardChanged(ClipboardID id, + UInt32 seqNum, const CString& data) { CLock lock(&m_mutex); + onClipboardChangedNoLock(id, seqNum, data); +} + +void +CServer::onClipboardChangedNoLock(ClipboardID id, + UInt32 seqNum, const CString& data) +{ CClipboardInfo& clipboard = m_clipboards[id]; // ignore update if sequence number is old if (seqNum < clipboard.m_clipboardSeqNum) { - log((CLOG_INFO "ignored screen \"%s\" update of clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + log((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", clipboard.m_clipboardOwner.c_str(), id)); + return; + } + + // ignore if data hasn't changed + if (data == clipboard.m_clipboardData) { + log((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id)); return; } // unmarshall into our clipboard buffer log((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); - clipboard.m_clipboardReady = true; - clipboard.m_clipboardData = data; + clipboard.m_clipboardData = data; clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); - // all screens have an out-of-date clipboard except the sender - clearGotClipboard(id); - CScreenList::const_iterator index = - m_screens.find(clipboard.m_clipboardOwner); - if (index != m_screens.end()) { - index->second->m_gotClipboard[id] = true; + // tell all clients except the sender that the clipboard is dirty + CClientList::const_iterator sender = + m_clients.find(clipboard.m_clipboardOwner); + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + client->setClipboardDirty(id, index != sender); } - // send the new clipboard to the active screen (will do nothing if - // the active screen is the one sending the new clipboard) - sendClipboard(id); + // send the new clipboard to the active screen + m_active->setClipboard(id, m_clipboards[id].m_clipboardData); } bool @@ -466,9 +399,7 @@ CServer::onKeyDown(KeyID id, KeyModifierMask mask) } // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendKeyDown(id, mask); - } + m_active->keyDown(id, mask); } void @@ -484,9 +415,7 @@ CServer::onKeyUp(KeyID id, KeyModifierMask mask) } // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendKeyUp(id, mask); - } + m_active->keyUp(id, mask); } void @@ -503,9 +432,7 @@ CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) } // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendKeyRepeat(id, mask, count); - } + m_active->keyRepeat(id, mask, count); } void @@ -516,9 +443,7 @@ CServer::onMouseDown(ButtonID id) assert(m_active != NULL); // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendMouseDown(id); - } + m_active->mouseDown(id); } void @@ -529,9 +454,7 @@ CServer::onMouseUp(ButtonID id) assert(m_active != NULL); // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendMouseUp(id); - } + m_active->mouseUp(id); } bool @@ -546,33 +469,38 @@ bool CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) { // mouse move on primary (server's) screen - assert(m_active != NULL); - assert(m_active->m_protocol == NULL); + assert(m_primaryClient != NULL); + assert(m_active == m_primaryClient); // ignore if mouse is locked to screen if (isLockedToScreenNoLock()) { return false; } + // get screen shape + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + SInt32 zoneSize = m_active->getJumpZoneSize(); + // see if we should change screens CConfig::EDirection dir; - if (x < m_active->m_x + m_active->m_zoneSize) { - x -= m_active->m_zoneSize; + if (x < ax + zoneSize) { + x -= zoneSize; dir = CConfig::kLeft; log((CLOG_DEBUG1 "switch to left")); } - else if (x >= m_active->m_x + m_active->m_w - m_active->m_zoneSize) { - x += m_active->m_zoneSize; + else if (x >= ax + aw - zoneSize) { + x += zoneSize; dir = CConfig::kRight; log((CLOG_DEBUG1 "switch to right")); } - else if (y < m_active->m_y + m_active->m_zoneSize) { - y -= m_active->m_zoneSize; + else if (y < ay + zoneSize) { + y -= zoneSize; dir = CConfig::kTop; log((CLOG_DEBUG1 "switch to top")); } - else if (y >= m_active->m_y + m_active->m_h - m_active->m_zoneSize) { - y += m_active->m_zoneSize; + else if (y >= ay + ah - zoneSize) { + y += zoneSize; dir = CConfig::kBottom; log((CLOG_DEBUG1 "switch to bottom")); } @@ -583,7 +511,7 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) // get jump destination and, if no screen in jump direction, // then ignore the move. - CScreenInfo* newScreen = getNeighbor(m_active, dir, x, y); + IClient* newScreen = getNeighbor(m_active, dir, x, y); if (newScreen == NULL) { return false; } @@ -606,7 +534,7 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) { // mouse move on secondary (client's) screen assert(m_active != NULL); - if (m_active->m_protocol == NULL) { + if (m_active == m_primaryClient) { // we're actually on the primary screen. this can happen // when the primary screen begins processing a mouse move // for a secondary screen, then the active (secondary) @@ -625,22 +553,26 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) m_x += dx; m_y += dy; + // get screen shape + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + // switch screens if the mouse is outside the screen and not // locked to the screen - CScreenInfo* newScreen = NULL; + IClient* newScreen = NULL; if (!isLockedToScreenNoLock()) { // find direction of neighbor CConfig::EDirection dir; - if (m_x < m_active->m_x) { + if (m_x < ax) { dir = CConfig::kLeft; } - else if (m_x > m_active->m_x + m_active->m_w - 1) { + else if (m_x > ax + aw - 1) { dir = CConfig::kRight; } - else if (m_y < m_active->m_y) { + else if (m_y < ay) { dir = CConfig::kTop; } - else if (m_y > m_active->m_y + m_active->m_h - 1) { + else if (m_y > ay + ah - 1) { dir = CConfig::kBottom; } else { @@ -652,42 +584,50 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) // get neighbor if we should switch if (newScreen == NULL) { - log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->m_name.c_str(), CConfig::dirName(dir))); + log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir))); // get new position or clamp to current screen newScreen = getNeighbor(m_active, dir, m_x, m_y); if (newScreen == NULL) { log((CLOG_DEBUG1 "no neighbor; clamping")); - if (m_x < m_active->m_x) - m_x = m_active->m_x; - else if (m_x > m_active->m_x + m_active->m_w - 1) - m_x = m_active->m_x + m_active->m_w - 1; - if (m_y < m_active->m_y) - m_y = m_active->m_y; - else if (m_y > m_active->m_y + m_active->m_h - 1) - m_y = m_active->m_y + m_active->m_h - 1; + if (m_x < ax) { + m_x = ax; + } + else if (m_x > ax + aw - 1) { + m_x = ax + aw - 1; + } + if (m_y < ay) { + m_y = ay; + } + else if (m_y > ay + ah - 1) { + m_y = ay + ah - 1; + } } } } else { // clamp to edge when locked - log((CLOG_DEBUG1 "clamp to \"%s\"", m_active->m_name.c_str())); - if (m_x < m_active->m_x) - m_x = m_active->m_x; - else if (m_x > m_active->m_x + m_active->m_w - 1) - m_x = m_active->m_x + m_active->m_w - 1; - if (m_y < m_active->m_y) - m_y = m_active->m_y; - else if (m_y > m_active->m_y + m_active->m_h - 1) - m_y = m_active->m_y + m_active->m_h - 1; + log((CLOG_DEBUG1 "clamp to \"%s\"", m_active->getName().c_str())); + if (m_x < ax) { + m_x = ax; + } + else if (m_x > ax + aw - 1) { + m_x = ax + aw - 1; + } + if (m_y < ay) { + m_y = ay; + } + else if (m_y > ay + ah - 1) { + m_y = ay + ah - 1; + } } // warp cursor if on same screen if (newScreen == NULL || newScreen == m_active) { // do nothing if mouse didn't move if (m_x != xOld || m_y != yOld) { - log((CLOG_DEBUG2 "move on %s to %d,%d", m_active->m_name.c_str(), m_x, m_y)); - m_active->m_protocol->sendMouseMove(m_x, m_y); + log((CLOG_DEBUG2 "move on %s to %d,%d", m_active->getName().c_str(), m_x, m_y)); + m_active->mouseMove(m_x, m_y); } } @@ -705,9 +645,7 @@ CServer::onMouseWheel(SInt32 delta) assert(m_active != NULL); // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendMouseWheel(delta); - } + m_active->mouseWheel(delta); } void @@ -723,30 +661,31 @@ CServer::onScreenSaver(bool activated) m_ySaver = m_y; // jump to primary screen - if (m_active != m_primaryInfo) { - switchScreen(m_primaryInfo, 0, 0, true); + if (m_active != m_primaryClient) { + switchScreen(m_primaryClient, 0, 0, true); } } else { // jump back to previous screen and position. we must check // that the position is still valid since the screen may have // changed resolutions while the screen saver was running. - if (m_activeSaver != NULL && m_activeSaver != m_primaryInfo) { + if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { // check position - CScreenInfo* screen = m_activeSaver; - if (m_xSaver < screen->m_x + screen->m_zoneSize) { - m_xSaver = screen->m_x + screen->m_zoneSize; + IClient* screen = m_activeSaver; + SInt32 x, y, w, h; + screen->getShape(x, y, w, h); + SInt32 zoneSize = screen->getJumpZoneSize(); + if (m_xSaver < x + zoneSize) { + m_xSaver = x + zoneSize; } - else if (m_xSaver >= screen->m_x + - screen->m_w - screen->m_zoneSize) { - m_xSaver = screen->m_x + screen->m_w - screen->m_zoneSize - 1; + else if (m_xSaver >= x + w - zoneSize) { + m_xSaver = x + w - zoneSize - 1; } - if (m_ySaver < screen->m_y + screen->m_zoneSize) { - m_ySaver = screen->m_y + screen->m_zoneSize; + if (m_ySaver < y + zoneSize) { + m_ySaver = y + zoneSize; } - else if (m_ySaver >= screen->m_y + - screen->m_h - screen->m_zoneSize) { - m_ySaver = screen->m_y + screen->m_h - screen->m_zoneSize - 1; + else if (m_ySaver >= y + h - zoneSize) { + m_ySaver = y + h - zoneSize - 1; } // jump @@ -757,32 +696,24 @@ CServer::onScreenSaver(bool activated) m_activeSaver = NULL; } - // send message to all secondary screens - for (CScreenList::const_iterator index = m_screens.begin(); - index != m_screens.end(); ++index) { - if (index->second->m_protocol != NULL) { - index->second->m_protocol->sendScreenSaver(activated); - } + // send message to all clients + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + client->screenSaver(activated); } } -bool -CServer::isLockedToScreen() const -{ - CLock lock(&m_mutex); - return isLockedToScreenNoLock(); -} - bool CServer::isLockedToScreenNoLock() const { // locked if primary says we're locked - if (m_primary->isLockedToScreen()) { + if (m_primaryClient->isLockedToScreen()) { return true; } // locked if scroll-lock is toggled on - if ((m_primary->getToggleMask() & KeyModifierScrollLock) != 0) { + if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) { return true; } @@ -791,14 +722,19 @@ CServer::isLockedToScreenNoLock() const } void -CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y, bool screenSaver) +CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool screenSaver) { assert(dst != NULL); - assert(x >= dst->m_x && y >= dst->m_y); - assert(x < dst->m_x + dst->m_w && y < dst->m_y + dst->m_h); +#ifndef NDEBUG + { + SInt32 dx, dy, dw, dh; + dst->getShape(dx, dy, dw, dh); + assert(x >= dx && y >= dy && x < dx + dw && y < dy + dh); + } +#endif assert(m_active != NULL); - log((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y)); + log((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y)); // FIXME -- we're not locked here but we probably should be // record new position @@ -809,24 +745,24 @@ CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y, bool screenSaver) // since that's a waste of time we skip that and just warp the // mouse. if (m_active != dst) { - // note if we're leaving the primary screen - const bool leavingPrimary = (m_active->m_protocol == NULL); - // leave active screen - if (leavingPrimary) { - if (!m_primary->leave()) { - // cannot leave primary screen - log((CLOG_WARN "can't leave primary screen")); - return; - } - - // update the clipboards that the primary screen owns - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - updatePrimaryClipboard(id); - } + if (!m_active->leave()) { + // cannot leave screen + log((CLOG_WARN "can't leave screen")); + return; } - else { - m_active->m_protocol->sendLeave(); + + // update the primary client's clipboards if we're leaving the + // primary screen. + if (m_active == m_primaryClient) { + for (UInt32 id = 0; id < kClipboardEnd; ++id) { + CClipboardInfo& clipboard = m_clipboards[id]; + if (clipboard.m_clipboardOwner == m_primaryClient->getName()) { + CString clipboardData; + m_primaryClient->getClipboard(id, clipboardData); + onClipboardChangedNoLock(id, m_seqNum, clipboardData); + } + } } // cut over @@ -836,35 +772,25 @@ CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y, bool screenSaver) ++m_seqNum; // enter new screen - if (m_active->m_protocol == NULL) { - m_primary->enter(x, y, screenSaver); - } - else { - m_active->m_protocol->sendEnter(x, y, m_seqNum, - m_primary->getToggleMask()); - } + m_active->enter(x, y, m_seqNum, + m_primaryClient->getToggleMask(), screenSaver); // send the clipboard data to new active screen for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - sendClipboard(id); + m_active->setClipboard(id, m_clipboards[id].m_clipboardData); } } else { - if (m_active->m_protocol == NULL) { - m_primary->warpCursor(x, y); - } - else { - m_active->m_protocol->sendMouseMove(x, y); - } + m_active->mouseMove(x, y); } } -CServer::CScreenInfo* -CServer::getNeighbor(CScreenInfo* src, CConfig::EDirection dir) const +IClient* +CServer::getNeighbor(IClient* src, CConfig::EDirection dir) const { assert(src != NULL); - CString srcName = src->m_name; + CString srcName = src->getName(); assert(!srcName.empty()); log((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); for (;;) { @@ -880,8 +806,8 @@ CServer::getNeighbor(CScreenInfo* src, CConfig::EDirection dir) const // look up neighbor cell. if the screen is connected and // ready then we can stop. otherwise we skip over an // unconnected screen. - CScreenList::const_iterator index = m_screens.find(dstName); - if (index != m_screens.end() && index->second->m_ready) { + CClientList::const_iterator index = m_clients.find(dstName); + if (index != m_clients.end()) { log((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); return index->second; } @@ -891,94 +817,93 @@ CServer::getNeighbor(CScreenInfo* src, CConfig::EDirection dir) const } } -CServer::CScreenInfo* -CServer::getNeighbor(CScreenInfo* src, +IClient* +CServer::getNeighbor(IClient* src, CConfig::EDirection srcSide, SInt32& x, SInt32& y) const { assert(src != NULL); // get the first neighbor - CScreenInfo* dst = getNeighbor(src, srcSide); + IClient* dst = getNeighbor(src, srcSide); if (dst == NULL) { return NULL; } // get the source screen's size (needed for kRight and kBottom) - SInt32 w = src->m_w, h = src->m_h; + SInt32 sx, sy, sw, sh; + SInt32 dx, dy, dw, dh; + IClient* lastGoodScreen = src; + lastGoodScreen->getShape(sx, sy, sw, sh); + lastGoodScreen->getShape(dx, dy, dw, dh); // find destination screen, adjusting x or y (but not both). the // searches are done in a sort of canonical screen space where // the upper-left corner is 0,0 for each screen. we adjust from // actual to canonical position on entry to and from canonical to // actual on exit from the search. - CScreenInfo* lastGoodScreen = src; switch (srcSide) { case CConfig::kLeft: - x -= src->m_x; + x -= dx; while (dst != NULL && dst != lastGoodScreen) { lastGoodScreen = dst; - w = lastGoodScreen->m_w; - h = lastGoodScreen->m_h; - x += w; + lastGoodScreen->getShape(dx, dy, dw, dh); + x += dw; if (x >= 0) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); - x += lastGoodScreen->m_x; + x += dx; break; case CConfig::kRight: - x -= src->m_x; + x -= dx; while (dst != NULL) { + x -= dw; lastGoodScreen = dst; - x -= w; - w = lastGoodScreen->m_w; - h = lastGoodScreen->m_h; - if (x < w) { + lastGoodScreen->getShape(dx, dy, dw, dh); + if (x < dw) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); - x += lastGoodScreen->m_x; + x += dx; break; case CConfig::kTop: - y -= src->m_y; + y -= dy; while (dst != NULL) { lastGoodScreen = dst; - w = lastGoodScreen->m_w; - h = lastGoodScreen->m_h; - y += h; + lastGoodScreen->getShape(dx, dy, dw, dh); + y += dh; if (y >= 0) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); - y += lastGoodScreen->m_y; + y += dy; break; case CConfig::kBottom: - y -= src->m_y; + y -= dy; while (dst != NULL) { + y -= dh; lastGoodScreen = dst; - y -= h; - w = lastGoodScreen->m_w; - h = lastGoodScreen->m_h; - if (y < h) { + lastGoodScreen->getShape(dx, dy, dw, dh); + if (y < sh) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); - y += lastGoodScreen->m_y; + y += dy; break; } @@ -990,31 +915,31 @@ CServer::getNeighbor(CScreenInfo* src, // to avoid the jump zone. if entering a side that doesn't have // a neighbor (i.e. an asymmetrical side) then we don't need to // move inwards because that side can't provoke a jump. - if (dst->m_protocol == NULL) { - const CString dstName(dst->m_name); + if (dst == m_primaryClient) { + const CString dstName(dst->getName()); switch (srcSide) { case CConfig::kLeft: if (!m_config.getNeighbor(dstName, CConfig::kRight).empty() && - x > dst->m_x + w - 1 - dst->m_zoneSize) - x = dst->m_x + w - 1 - dst->m_zoneSize; + x > dx + dw - 1 - dst->getJumpZoneSize()) + x = dx + dw - 1 - dst->getJumpZoneSize(); break; case CConfig::kRight: if (!m_config.getNeighbor(dstName, CConfig::kLeft).empty() && - x < dst->m_x + dst->m_zoneSize) - x = dst->m_x + dst->m_zoneSize; + x < dx + dst->getJumpZoneSize()) + x = dx + dst->getJumpZoneSize(); break; case CConfig::kTop: if (!m_config.getNeighbor(dstName, CConfig::kBottom).empty() && - y > dst->m_y + h - 1 - dst->m_zoneSize) - y = dst->m_y + h - 1 - dst->m_zoneSize; + y > dy + dh - 1 - dst->getJumpZoneSize()) + y = dy + dh - 1 - dst->getJumpZoneSize(); break; case CConfig::kBottom: if (!m_config.getNeighbor(dstName, CConfig::kTop).empty() && - y < dst->m_y + dst->m_zoneSize) - y = dst->m_y + dst->m_zoneSize; + y < dy + dst->getJumpZoneSize()) + y = dy + dst->getJumpZoneSize(); break; } } @@ -1028,50 +953,110 @@ CServer::getNeighbor(CScreenInfo* src, switch (srcSide) { case CConfig::kLeft: case CConfig::kRight: - y -= src->m_y; + y -= sy; if (y < 0) { y = 0; } - else if (y >= src->m_h) { - y = dst->m_h - 1; + else if (y >= sh) { + y = dh - 1; } else { y = static_cast(0.5 + y * - static_cast(dst->m_h - 1) / - (src->m_h - 1)); + static_cast(dh - 1) / (sh - 1)); } - y += dst->m_y; + y += dy; break; case CConfig::kTop: case CConfig::kBottom: - x -= src->m_x; + x -= sx; if (x < 0) { x = 0; } - else if (x >= src->m_w) { - x = dst->m_w - 1; + else if (x >= sw) { + x = dw - 1; } else { x = static_cast(0.5 + x * - static_cast(dst->m_w - 1) / - (src->m_w - 1)); + static_cast(dw - 1) / (sw - 1)); } - x += dst->m_x; + x += dx; break; } return dst; } +void +CServer::closeClients(const CConfig& config) +{ + CThreadList threads; + { + CLock lock(&m_mutex); + + // get the set of clients that are connected but are being + // dropped from the configuration (or who's canonical name + // is changing) and tell them to disconnect. note that + // since m_clientThreads doesn't include a thread for the + // primary client we will not close it. + for (CClientThreadList::iterator + index = m_clientThreads.begin(); + index != m_clientThreads.end(); ) { + const CString& name = index->first; + if (!config.isCanonicalName(name)) { + // save the thread and remove it from m_clientThreads + threads.push_back(index->second); + m_clientThreads.erase(index++); + + // lookup IClient with name + CClientList::const_iterator index2 = m_clients.find(name); + assert(index2 != m_clients.end()); + + // close that client + assert(index2->second != m_primaryClient); + index2->second->close(); + } + else { + ++index; + } + } + } + + // wait a moment to allow each client to close its connection + // before we close it (to avoid having our socket enter TIME_WAIT). + if (threads.size() > 0) { + CThread::sleep(1.0); + } + + // cancel the old client threads + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + index->cancel(); + } + + // wait for old client threads to terminate. we must not hold + // the lock while we do this so those threads can finish any + // calls to this object. + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + index->wait(); + } + + // clean up thread list + reapThreads(); +} + void CServer::startThread(IJob* job) { CLock lock(&m_mutex); + + // reap completed threads doReapThreads(m_threads); - CThread* thread = new CThread(job); - m_threads.push_back(thread); - log((CLOG_DEBUG1 "started thread %p", thread)); + + // add new thread to list. use the job as user data for logging. + m_threads.push_back(CThread(job, job)); + log((CLOG_DEBUG1 "started thread %p", m_threads.back().getUserData())); } void @@ -1079,6 +1064,12 @@ CServer::stopThreads(double timeout) { log((CLOG_DEBUG1 "stopping threads")); + // close all clients (except the primary) + { + CConfig emptyConfig; + closeClients(emptyConfig); + } + // swap thread list so nobody can mess with it CThreadList threads; { @@ -1089,8 +1080,7 @@ CServer::stopThreads(double timeout) // cancel every thread for (CThreadList::iterator index = threads.begin(); index != threads.end(); ++index) { - CThread* thread = *index; - thread->cancel(); + index->cancel(); } // now wait for the threads @@ -1103,9 +1093,7 @@ CServer::stopThreads(double timeout) // delete remaining threads for (CThreadList::iterator index = threads.begin(); index != threads.end(); ++index) { - CThread* thread = *index; - log((CLOG_DEBUG1 "reaped running thread %p", thread)); - delete thread; + log((CLOG_DEBUG1 "reaped running thread %p", index->getUserData())); } log((CLOG_DEBUG1 "stopped threads")); @@ -1123,12 +1111,10 @@ CServer::doReapThreads(CThreadList& threads) { for (CThreadList::iterator index = threads.begin(); index != threads.end(); ) { - CThread* thread = *index; - if (thread->wait(0.0)) { + if (index->wait(0.0)) { // thread terminated + log((CLOG_DEBUG1 "reaped thread %p", index->getUserData())); index = threads.erase(index); - log((CLOG_DEBUG1 "reaped thread %p", thread)); - delete thread; } else { // thread is running @@ -1182,7 +1168,7 @@ CServer::acceptClients(void*) // start handshake thread startThread(new TMethodJob( - this, &CServer::handshakeClient, socket)); + this, &CServer::runClient, socket)); } } catch (XBase& e) { @@ -1192,147 +1178,212 @@ CServer::acceptClients(void*) } void -CServer::handshakeClient(void* vsocket) +CServer::runClient(void* vsocket) { - log((CLOG_DEBUG1 "negotiating with new client")); - // get the socket pointer from the argument assert(vsocket != NULL); std::auto_ptr socket(reinterpret_cast(vsocket)); + // create proxy + CClientProxy* proxy = handshakeClient(socket.get()); + if (proxy == NULL) { + return; + } + + // add the connection + try { + addConnection(proxy); + + // save this client's thread + CLock lock(&m_mutex); + m_clientThreads.insert(std::make_pair(proxy->getName(), + CThread::getCurrentThread())); + } + catch (XDuplicateClient& e) { + // client has duplicate name + log((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy); + delete proxy; + return; + } + catch (XUnknownClient& e) { + // client has unknown name + log((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown); + delete proxy; + return; + } + catch (...) { + delete proxy; + throw; + } + + // activate screen saver on new client if active on the primary screen + { + CLock lock(&m_mutex); + if (m_activeSaver != NULL) { + proxy->screenSaver(true); + } + } + + // handle client messages + try { + log((CLOG_NOTE "client \"%s\" has connected", proxy->getName().c_str())); + proxy->run(); + } + catch (XBadClient&) { + // client not behaving + // FIXME -- could print network address if socket had suitable method + log((CLOG_WARN "protocol error from client \"%s\"", proxy->getName().c_str())); + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad); + } + catch (XBase& e) { + // misc error + log((CLOG_WARN "error communicating with client \"%s\": %s", proxy->getName().c_str(), e.what())); + // FIXME -- could print network address if socket had suitable method + } + catch (...) { + // run() was probably cancelled + removeConnection(proxy->getName()); + throw; + } + + // remove the connection + removeConnection(proxy->getName()); +} + +CClientProxy* +CServer::handshakeClient(IDataSocket* socket) +{ + log((CLOG_DEBUG1 "negotiating with new client")); + + // get the input and output streams + IInputStream* input = socket->getInputStream(); + IOutputStream* output = socket->getOutputStream(); + bool own = false; + + // attach the encryption layer + if (m_securityFactory != NULL) { +/* FIXME -- implement ISecurityFactory + input = m_securityFactory->createInputFilter(input, own); + output = m_securityFactory->createOutputFilter(output, own); + own = true; +*/ + } + + // attach the packetizing filters + input = new CInputPacketStream(input, own); + output = new COutputPacketStream(output, own); + + CClientProxy* proxy = NULL; CString name(""); try { - // get the input and output streams - IInputStream* srcInput = socket->getInputStream(); - IOutputStream* srcOutput = socket->getOutputStream(); - std::auto_ptr input; - std::auto_ptr output; + // give the client a limited time to complete the handshake + CTimerThread timer(30.0); - // attach the encryption layer - bool own = false; - if (m_securityFactory != NULL) { -/* FIXME -- implement ISecurityFactory - input.reset(m_securityFactory->createInputFilter(srcInput, own)); - output.reset(m_securityFactory->createOutputFilter(srcOutput, own)); - srcInput = input.get(); - srcOutput = output.get(); - own = true; -*/ - } + // limit the maximum length of the hello +// FIXME -- should be in protocol types; may become obsolete anyway + static const UInt32 maxHelloLen = 1024; - // attach the packetizing filters - assign(input, new CInputPacketStream(srcInput, own), IInputStream); - assign(output, new COutputPacketStream(srcOutput, own), IOutputStream); - - bool connected = false; - std::auto_ptr protocol; - try { - { - // give the client a limited time to complete the handshake - CTimerThread timer(30.0); - - // limit the maximum length of the hello - static const UInt32 maxHelloLen = 1024; - - // say hello - log((CLOG_DEBUG1 "saying hello")); - CProtocolUtil::writef(output.get(), "Synergy%2i%2i", + // say hello + log((CLOG_DEBUG1 "saying hello")); + CProtocolUtil::writef(output, "Synergy%2i%2i", kProtocolMajorVersion, kProtocolMinorVersion); - output->flush(); + output->flush(); - // wait for the reply - log((CLOG_DEBUG1 "waiting for hello reply")); - UInt32 n = input->getSize(); - if (n > maxHelloLen) { - throw XBadClient(); - } + // wait for the reply + log((CLOG_DEBUG1 "waiting for hello reply")); + UInt32 n = input->getSize(); + if (n > maxHelloLen) { + throw XBadClient(); + } - // get and parse the reply to hello - SInt16 major, minor; - try { - log((CLOG_DEBUG1 "parsing hello reply")); - CProtocolUtil::readf(input.get(), "Synergy%2i%2i%s", + // get and parse the reply to hello + SInt16 major, minor; + try { + log((CLOG_DEBUG1 "parsing hello reply")); + CProtocolUtil::readf(input, "Synergy%2i%2i%s", &major, &minor, &name); - } - catch (XIO&) { - throw XBadClient(); - } - - // convert name to canonical form (if any) - if (m_config.isScreen(name)) { - name = m_config.getCanonicalName(name); - } - - // create a protocol interpreter for the version - log((CLOG_DEBUG1 "creating interpreter for client \"%s\" version %d.%d", name.c_str(), major, minor)); - assign(protocol, CServerProtocol::create(major, minor, - this, name, input.get(), output.get()), - IServerProtocol); - - // client is now pending - addConnection(name, protocol.get()); - connected = true; - - // ask and wait for the client's info - log((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str())); - protocol->queryInfo(); - - // now connected; client no longer subject to timeout. - } - - // activate screen saver on new client if active on the - // primary screen - if (m_primary->isScreenSaverActive()) { - protocol->sendScreenSaver(true); - } - - // handle messages from client. returns when the client - // disconnects. - log((CLOG_NOTE "client \"%s\" has connected", name.c_str())); - protocol->run(); } - catch (XDuplicateClient& e) { - // client has duplicate name - log((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); - CProtocolUtil::writef(output.get(), kMsgEBusy); - } - catch (XUnknownClient& e) { - // client has unknown name - log((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); - CProtocolUtil::writef(output.get(), kMsgEUnknown); - } - catch (XIncompatibleClient& e) { - // client is incompatible - // FIXME -- could print network address if socket had suitable method - log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); - CProtocolUtil::writef(output.get(), kMsgEIncompatible, - kProtocolMajorVersion, kProtocolMinorVersion); - } - catch (XBadClient&) { - // client not behaving - // FIXME -- could print network address if socket had suitable method - log((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); - CProtocolUtil::writef(output.get(), kMsgEBad); - } - catch (...) { - if (connected) { - removeConnection(name); - } - throw; + catch (XIO&) { + throw XBadClient(); } - // flush any pending output - output.get()->flush(); - if (connected) { - removeConnection(name); + // disallow invalid version numbers + if (major < 0 || minor < 0) { + throw XIncompatibleClient(major, minor); } + + // disallow connection from test versions to release versions + if (major == 0 && kProtocolMajorVersion != 0) { + throw XIncompatibleClient(major, minor); + } + + // hangup (with error) if version isn't supported + if (major > kProtocolMajorVersion || + (major == kProtocolMajorVersion && minor > kProtocolMinorVersion)) { + throw XIncompatibleClient(major, minor); + } + + // convert name to canonical form (if any) + if (m_config.isScreen(name)) { + name = m_config.getCanonicalName(name); + } + + // create client proxy for highest version supported by the client + log((CLOG_DEBUG1 "creating proxy for client \"%s\" version %d.%d", name.c_str(), major, minor)); + proxy = new CClientProxy1_0(this, name, input, output); + + // negotiate + // FIXME + + // ask and wait for the client's info + log((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str())); + proxy->open(); + + return proxy; + } + catch (XIncompatibleClient& e) { + // client is incompatible + // FIXME -- could print network address if socket had suitable method + log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); + CProtocolUtil::writef(output, kMsgEIncompatible, + kProtocolMajorVersion, kProtocolMinorVersion); + } + catch (XBadClient&) { + // client not behaving + // FIXME -- could print network address if socket had suitable method + log((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); + CProtocolUtil::writef(output, kMsgEBad); } catch (XBase& e) { // misc error log((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); // FIXME -- could print network address if socket had suitable method } + catch (...) { + // probably timed out + if (proxy != NULL) { + delete proxy; + } + else { + delete input; + delete output; + } + throw; + } + + // failed + if (proxy != NULL) { + delete proxy; + } + else { + delete input; + delete output; + } + + return NULL; } void @@ -1433,178 +1484,98 @@ CServer::processHTTPRequest(void* vsocket) } } -void -CServer::clearGotClipboard(ClipboardID id) -{ - for (CScreenList::const_iterator index = m_screens.begin(); - index != m_screens.end(); ++index) { - index->second->m_gotClipboard[id] = false; - } -} - -void -CServer::sendClipboard(ClipboardID id) -{ - // do nothing if clipboard was already sent - if (!m_active->m_gotClipboard[id]) { - CClipboardInfo& clipboard = m_clipboards[id]; - if (clipboard.m_clipboardReady) { - // send it - if (m_active->m_protocol == NULL) { - m_primary->setClipboard(id, &clipboard.m_clipboard); - } - else { - m_active->m_protocol->sendClipboard(id, - clipboard.m_clipboardData); - } - - // clipboard has been sent - m_active->m_gotClipboard[id] = true; - } - } -} - -void -CServer::updatePrimaryClipboard(ClipboardID id) -{ - CClipboardInfo& clipboard = m_clipboards[id]; - - // if leaving primary and the primary owns the clipboard - // then update it. - if (clipboard.m_clipboardOwner == m_primaryInfo->m_name) { - assert(clipboard.m_clipboardReady == true); - - // save clipboard time - IClipboard::Time time = clipboard.m_clipboard.getTime(); - - // update - m_primary->getClipboard(id, &clipboard.m_clipboard); - - // if clipboard changed then other screens have an - // out-of-date clipboard. - if (time != clipboard.m_clipboard.getTime()) { - log((CLOG_DEBUG "clipboard %d time changed (%08x to %08x)", id, time, clipboard.m_clipboard.getTime())); - - // marshall data - CString newData = clipboard.m_clipboard.marshall(); - - // compare old and new data. if identical then the clipboard - // hasn't really changed. - if (newData != clipboard.m_clipboardData) { - log((CLOG_DEBUG "clipboard %d changed", id)); - clipboard.m_clipboardData = newData; - clearGotClipboard(id); - } - else { - log((CLOG_DEBUG "clipboard %d unchanged", id)); - } - m_primaryInfo->m_gotClipboard[id] = true; - } - } -} - -// FIXME -- use factory to create screen -#if WINDOWS_LIKE -#include "CMSWindowsPrimaryScreen.h" -#elif UNIX_LIKE -#include "CXWindowsPrimaryScreen.h" -#endif void CServer::openPrimaryScreen() { - assert(m_primary == NULL); + assert(m_primaryClient == NULL); // reset sequence number m_seqNum = 0; - CString primary = m_config.getCanonicalName(m_name); + // canonicalize the primary screen name + CString primary = m_config.getCanonicalName(getPrimaryScreenName()); if (primary.empty()) { - throw XUnknownClient(m_name); + throw XUnknownClient(getPrimaryScreenName()); } - try { - // add connection - m_active = addConnection(primary, NULL); - m_primaryInfo = m_active; - // open screen - log((CLOG_DEBUG1 "creating primary screen")); -#if WINDOWS_LIKE - m_primary = new CMSWindowsPrimaryScreen; -#elif UNIX_LIKE - m_primary = new CXWindowsPrimaryScreen; -#endif + // clear clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + CClipboardInfo& clipboard = m_clipboards[id]; + clipboard.m_clipboardOwner = primary; + clipboard.m_clipboardSeqNum = m_seqNum; + if (clipboard.m_clipboard.open(0)) { + clipboard.m_clipboard.empty(); + clipboard.m_clipboard.close(); + } + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); + } + + // create the primary client and open it + try { + m_primaryClient = new CPrimaryClient(this, primary); + + // add connection + addConnection(m_primaryClient); + m_active = m_primaryClient; + + // open the screen log((CLOG_DEBUG1 "opening primary screen")); - m_primary->open(this); + m_primaryClient->open(); } catch (...) { - if (m_primary != NULL) { + if (m_active != NULL) { removeConnection(primary); - delete m_primary; } - m_primary = NULL; - m_primaryInfo = NULL; - m_active = NULL; + else { + delete m_primaryClient; + } + m_active = NULL; + m_primaryClient = NULL; throw; } - - // set the clipboard owner to the primary screen and then get the - // current clipboard data. - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - CClipboardInfo& clipboard = m_clipboards[id]; - m_primary->getClipboard(id, &clipboard.m_clipboard); - clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); - clipboard.m_clipboardReady = true; - clipboard.m_clipboardOwner = m_active->m_name; - } } void CServer::closePrimaryScreen() { - assert(m_primary != NULL); - - // remove connection - CString primary = m_config.getCanonicalName(m_name); - removeConnection(primary); + assert(m_primaryClient != NULL); // close the primary screen try { log((CLOG_DEBUG1 "closing primary screen")); - m_primary->close(); + m_primaryClient->close(); } catch (...) { // ignore } - // clean up - log((CLOG_DEBUG1 "destroying primary screen")); - delete m_primary; - m_primary = NULL; + // remove connection + removeConnection(m_primaryClient->getName()); + m_primaryClient = NULL; } -CServer::CScreenInfo* -CServer::addConnection(const CString& name, IServerProtocol* protocol) +void +CServer::addConnection(IClient* client) { - log((CLOG_DEBUG "adding connection \"%s\"", name.c_str())); + assert(client != NULL); + + log((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str())); CLock lock(&m_mutex); // name must be in our configuration - if (!m_config.isScreen(name)) { - throw XUnknownClient(name); + if (!m_config.isScreen(client->getName())) { + throw XUnknownClient(client->getName()); } // can only have one screen with a given name at any given time - if (m_screens.count(name) != 0) { - throw XDuplicateClient(name); + if (m_clients.count(client->getName()) != 0) { + throw XDuplicateClient(client->getName()); } // save screen info - CScreenInfo* newScreen = new CScreenInfo(name, protocol); - m_screens.insert(std::make_pair(name, newScreen)); - log((CLOG_DEBUG "added connection \"%s\"", name.c_str())); - - return newScreen; + m_clients.insert(std::make_pair(client->getName(), client)); + log((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str())); } void @@ -1613,28 +1584,27 @@ CServer::removeConnection(const CString& name) log((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); CLock lock(&m_mutex); - // find screen info - CScreenList::iterator index = m_screens.find(name); - assert(index != m_screens.end()); + // find client + CClientList::iterator index = m_clients.find(name); + assert(index != m_clients.end()); // if this is active screen then we have to jump off of it - CScreenInfo* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; - if (active == index->second && active != m_primaryInfo) { + IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; + if (active == index->second && active != m_primaryClient) { // record new position (center of primary screen) -// FIXME -- should have separate "center" pixel reported by screen - m_x = m_primaryInfo->m_x + (m_primaryInfo->m_w >> 1); - m_y = m_primaryInfo->m_y + (m_primaryInfo->m_h >> 1); + m_primaryClient->getCenter(m_x, m_y); // don't notify active screen since it probably already disconnected - log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->m_name.c_str(), m_primaryInfo->m_name.c_str(), m_x, m_y)); + log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); // cut over - m_active = m_primaryInfo; + m_active = m_primaryClient; // enter new screen (unless we already have because of the // screen saver) if (m_activeSaver == NULL) { - m_primary->enter(m_x, m_y, false); + m_primaryClient->enter(m_x, m_y, m_seqNum, + m_primaryClient->getToggleMask(), false); } } @@ -1645,35 +1615,12 @@ CServer::removeConnection(const CString& name) m_activeSaver = NULL; } - // done with screen info + // done with client delete index->second; - m_screens.erase(index); -} + m_clients.erase(index); - -// -// CServer::CScreenInfo -// - -CServer::CScreenInfo::CScreenInfo(const CString& name, - IServerProtocol* protocol) : - m_thread(CThread::getCurrentThread()), - m_name(name), - m_protocol(protocol), - m_ready(false), - m_x(0), - m_y(0), - m_w(0), - m_h(0), - m_zoneSize(0) -{ - for (ClipboardID id = 0; id < kClipboardEnd; ++id) - m_gotClipboard[id] = false; -} - -CServer::CScreenInfo::~CScreenInfo() -{ - // do nothing + // remove any thread for this client + m_clientThreads.erase(name); } @@ -1685,8 +1632,7 @@ CServer::CClipboardInfo::CClipboardInfo() : m_clipboard(), m_clipboardData(), m_clipboardOwner(), - m_clipboardSeqNum(0), - m_clipboardReady(false) + m_clipboardSeqNum(0) { // do nothing } diff --git a/server/CServer.h b/server/CServer.h index ed344095..0baf0696 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -1,6 +1,7 @@ #ifndef CSERVER_H #define CSERVER_H +#include "IServer.h" #include "CConfig.h" #include "CClipboard.h" #include "ClipboardTypes.h" @@ -13,14 +14,17 @@ #include "stdlist.h" #include "stdmap.h" +class CClientProxy; +class CHTTPServer; +class CPrimaryClient; class CThread; +class IClient; +class IDataSocket; class IServerProtocol; class ISocketFactory; class ISecurityFactory; -class IPrimaryScreen; -class CHTTPServer; -class CServer { +class CServer : public IServer { public: CServer(const CString& serverName); ~CServer(); @@ -38,50 +42,12 @@ public: // after a successful open(). void quit(); - // tell the server to shutdown. this is called in an emergency - // when we need to tell the server that we cannot continue. the - // server will attempt to clean up. - void shutdown(); - // update screen map. returns true iff the new configuration was // accepted. bool setConfig(const CConfig&); - // handle events on server's screen. onMouseMovePrimary() returns - // true iff the mouse enters a jump zone and jumps. - void onKeyDown(KeyID, KeyModifierMask); - void onKeyUp(KeyID, KeyModifierMask); - void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); - void onMouseDown(ButtonID); - void onMouseUp(ButtonID); - bool onMouseMovePrimary(SInt32 x, SInt32 y); - void onMouseMoveSecondary(SInt32 dx, SInt32 dy); - void onMouseWheel(SInt32 delta); - void grabClipboard(ClipboardID); - void onScreenSaver(bool activated); - - // handle updates from primary - void setInfo(SInt32 xScreen, SInt32 yScreen, - SInt32 wScreen, SInt32 hScreen, - SInt32 zoneSize, - SInt32 xMouse, SInt32 yMouse); - - // handle messages from clients - void setInfo(const CString& clientName, - SInt32 xScreen, SInt32 yScreen, - SInt32 wScreen, SInt32 hScreen, - SInt32 zoneSize, - SInt32 xMouse, SInt32 yMouse); - void grabClipboard(ClipboardID, - UInt32 seqNum, const CString& clientName); - void setClipboard(ClipboardID, - UInt32 seqNum, const CString& data); - // accessors - // returns true if the mouse should be locked to the current screen - bool isLockedToScreen() const; - // get the current screen map void getConfig(CConfig*) const; @@ -91,64 +57,58 @@ public: // get the sides of the primary screen that have neighbors UInt32 getActivePrimarySides() const; + // IServer overrides + virtual void onError(); + virtual void onInfoChanged(const CString& clientName); + virtual bool onGrabClipboard(ClipboardID, + UInt32 seqNum, const CString& clientName); + virtual void onClipboardChanged(ClipboardID, + UInt32 seqNum, const CString& data); + virtual void onKeyDown(KeyID, KeyModifierMask); + virtual void onKeyUp(KeyID, KeyModifierMask); + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void onMouseDown(ButtonID); + virtual void onMouseUp(ButtonID); + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); + virtual void onMouseWheel(SInt32 delta); + virtual void onGrabClipboard(ClipboardID); + virtual void onScreenSaver(bool activated); + protected: bool onCommandKey(KeyID, KeyModifierMask, bool down); private: - typedef std::list CThreadList; - - class CScreenInfo { - public: - CScreenInfo(const CString& name, IServerProtocol*); - ~CScreenInfo(); - - public: - // the thread handling this screen's connection. used when - // forcing a screen to disconnect. - CThread m_thread; - CString m_name; - IServerProtocol* m_protocol; - bool m_ready; - - // screen shape and jump zone size - SInt32 m_x, m_y; - SInt32 m_w, m_h; - SInt32 m_zoneSize; - - bool m_gotClipboard[kClipboardEnd]; - }; + typedef std::list CThreadList; // handle mouse motion bool onMouseMovePrimaryNoLock(SInt32 x, SInt32 y); void onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy); - // update screen info - void setInfoNoLock(const CString& screenName, - SInt32 xScreen, SInt32 yScreen, - SInt32 wScreen, SInt32 hScreen, - SInt32 zoneSize, - SInt32 xMouse, SInt32 yMouse); - // grab the clipboard - void grabClipboardNoLock(ClipboardID, + bool grabClipboardNoLock(ClipboardID, UInt32 seqNum, const CString& clientName); + // set the clipboard + void onClipboardChangedNoLock(ClipboardID, + UInt32 seqNum, const CString& data); + // returns true iff mouse should be locked to the current screen bool isLockedToScreenNoLock() const; // change the active screen - void switchScreen(CScreenInfo*, + void switchScreen(IClient*, SInt32 x, SInt32 y, bool forScreenSaver); // lookup neighboring screen - CScreenInfo* getNeighbor(CScreenInfo*, CConfig::EDirection) const; + IClient* getNeighbor(IClient*, CConfig::EDirection) const; // lookup neighboring screen. given a position relative to the // source screen, find the screen we should move onto and where. // if the position is sufficiently far from the source then we // cross multiple screens. if there is no suitable screen then // return NULL and x,y are not modified. - CScreenInfo* getNeighbor(CScreenInfo*, + IClient* getNeighbor(IClient*, CConfig::EDirection, SInt32& x, SInt32& y) const; @@ -156,15 +116,13 @@ private: void openPrimaryScreen(); void closePrimaryScreen(); - // clear gotClipboard flags in all screens - void clearGotClipboard(ClipboardID); - - // send clipboard to the active screen if it doesn't already have it - void sendClipboard(ClipboardID); - // update the clipboard if owned by the primary screen void updatePrimaryClipboard(ClipboardID); + // close all clients that are *not* in config, not including the + // primary client. + void closeClients(const CConfig& config); + // start a thread, adding it to the list of threads void startThread(IJob* adopted); @@ -180,8 +138,9 @@ private: // thread method to accept incoming client connections void acceptClients(void*); - // thread method to do startup handshake with client - void handshakeClient(void*); + // thread method to do client interaction + void runClient(void*); + CClientProxy* handshakeClient(IDataSocket*); // thread method to accept incoming HTTP connections void acceptHTTPClients(void*); @@ -190,11 +149,10 @@ private: void processHTTPRequest(void*); // connection list maintenance - CScreenInfo* addConnection(const CString& name, IServerProtocol*); + void addConnection(IClient*); void removeConnection(const CString& name); private: - typedef std::map CScreenList; class CClipboardInfo { public: CClipboardInfo(); @@ -204,7 +162,6 @@ private: CString m_clipboardData; CString m_clipboardOwner; UInt32 m_clipboardSeqNum; - bool m_clipboardReady; }; CMutex m_mutex; @@ -212,6 +169,7 @@ private: // the name of the primary screen CString m_name; + // how long to wait to bind our socket until we give up double m_bindTimeout; ISocketFactory* m_socketFactory; @@ -221,10 +179,21 @@ private: CThreadList m_threads; // the screens - IPrimaryScreen* m_primary; - CScreenList m_screens; - CScreenInfo* m_active; - CScreenInfo* m_primaryInfo; + typedef std::map CClientList; + typedef std::map CClientThreadList; + + // all clients indexed by name + CClientList m_clients; + + // run thread of all secondary screen clients. does not include the + // primary screen's run thread. + CClientThreadList m_clientThreads; + + // the primary screen client + CPrimaryClient* m_primaryClient; + + // the client with focus + IClient* m_active; // the sequence number of enter messages UInt32 m_seqNum; @@ -239,7 +208,7 @@ private: CClipboardInfo m_clipboards[kClipboardEnd]; // state saved when screen saver activates - CScreenInfo* m_activeSaver; + IClient* m_activeSaver; SInt32 m_xSaver, m_ySaver; // HTTP request processing stuff diff --git a/server/CServerProtocol.cpp b/server/CServerProtocol.cpp deleted file mode 100644 index 112db9d7..00000000 --- a/server/CServerProtocol.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "CServerProtocol.h" -#include "CServerProtocol1_0.h" -#include "ProtocolTypes.h" -#include "XSynergy.h" -#include "IOutputStream.h" -#include - -// -// CServerProtocol -// - -CServerProtocol::CServerProtocol(CServer* server, const CString& client, - IInputStream* input, IOutputStream* output) : - m_server(server), - m_client(client), - m_input(input), - m_output(output) -{ - assert(m_server != NULL); - assert(m_input != NULL); - assert(m_output != NULL); -} - -CServerProtocol::~CServerProtocol() -{ - // do nothing -} - -CServer* -CServerProtocol::getServer() const -{ - return m_server; -} - -CString -CServerProtocol::getClient() const -{ - return m_client; -} - -IInputStream* -CServerProtocol::getInputStream() const -{ - return m_input; -} - -IOutputStream* -CServerProtocol::getOutputStream() const -{ - return m_output; -} - -IServerProtocol* -CServerProtocol::create(SInt32 major, SInt32 minor, - CServer* server, const CString& client, - IInputStream* input, IOutputStream* output) -{ - // disallow invalid version numbers - if (major < 0 || minor < 0) { - throw XIncompatibleClient(major, minor); - } - - // disallow connection from test versions to release versions - if (major == 0 && kProtocolMajorVersion != 0) { - throw XIncompatibleClient(major, minor); - } - - // hangup (with error) if version isn't supported - if (major > kProtocolMajorVersion || - (major == kProtocolMajorVersion && minor > kProtocolMinorVersion)) { - throw XIncompatibleClient(major, minor); - } - - // create highest version protocol object not higher than the - // given version. - return new CServerProtocol1_0(server, client, input, output); -} diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h deleted file mode 100644 index 311e5d0f..00000000 --- a/server/CServerProtocol.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef CSERVERPROTOCOL_H -#define CSERVERPROTOCOL_H - -#include "IServerProtocol.h" - -class CServer; -class IInputStream; -class IOutputStream; - -class CServerProtocol : public IServerProtocol { -public: - CServerProtocol(CServer*, const CString& clientName, - IInputStream*, IOutputStream*); - ~CServerProtocol(); - - // manipulators - - // accessors - - virtual CServer* getServer() const; - virtual CString getClient() const; - virtual IInputStream* getInputStream() const; - virtual IOutputStream* getOutputStream() const; - - static IServerProtocol* create(SInt32 major, SInt32 minor, - CServer*, const CString& clientName, - IInputStream*, IOutputStream*); - - // IServerProtocol overrides - virtual void run() = 0; - virtual void queryInfo() = 0; - virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask) = 0; - virtual void sendLeave() = 0; - virtual void sendClipboard(ClipboardID, const CString&) = 0; - virtual void sendGrabClipboard(ClipboardID) = 0; - virtual void sendScreenSaver(bool on) = 0; - virtual void sendInfoAcknowledgment() = 0; - virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; - virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; - virtual void sendMouseDown(ButtonID) = 0; - virtual void sendMouseUp(ButtonID) = 0; - virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) = 0; - virtual void sendMouseWheel(SInt32 delta) = 0; - -protected: - //IServerProtocol overrides - virtual void recvInfo() = 0; - virtual void recvClipboard() = 0; - virtual void recvGrabClipboard() = 0; - -private: - CServer* m_server; - CString m_client; - IInputStream* m_input; - IOutputStream* m_output; -}; - -#endif diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp deleted file mode 100644 index 7c673415..00000000 --- a/server/CServerProtocol1_0.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "CServerProtocol1_0.h" -#include "CServer.h" -#include "CClipboard.h" -#include "CProtocolUtil.h" -#include "ProtocolTypes.h" -#include "XSynergy.h" -#include "IInputStream.h" -#include "IOutputStream.h" -#include "CThread.h" -#include "CLog.h" -#include "CStopwatch.h" -#include - -// -// CServerProtocol1_0 -// - -CServerProtocol1_0::CServerProtocol1_0(CServer* server, const CString& client, - IInputStream* input, IOutputStream* output) : - CServerProtocol(server, client, input, output) -{ - // do nothing -} - -CServerProtocol1_0::~CServerProtocol1_0() -{ - // do nothing -} - -void -CServerProtocol1_0::run() -{ - // handle messages until the client hangs up or stops sending heartbeats - CStopwatch heartTimer; - for (;;) { - CThread::testCancel(); - - // wait for a message - UInt8 code[4]; - UInt32 n = getInputStream()->read(code, 4, kHeartRate); - CThread::testCancel(); - - // check if client hungup - if (n == 0) { - log((CLOG_NOTE "client \"%s\" disconnected", getClient().c_str())); - return; - } - - // check if client has stopped sending heartbeats - if (n == (UInt32)-1) { -/* FIXME -- disabled to avoid masking bugs - if (heartTimer.getTime() > kHeartDeath) { - log((CLOG_NOTE "client \"%s\" is dead", getClient().c_str())); - return; - } -*/ - continue; - } - - // got a message so reset heartbeat monitor - heartTimer.reset(); - - // verify we got an entire code - if (n != 4) { - log((CLOG_ERR "incomplete message from \"%s\": %d bytes", getClient().c_str(), n)); - - // client sent an incomplete message - throw XBadClient(); - } - - // parse message - log((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getClient().c_str(), code[0], code[1], code[2], code[3])); - if (memcmp(code, kMsgDInfo, 4) == 0) { - recvInfo(); - } - else if (memcmp(code, kMsgCNoop, 4) == 0) { - // discard no-ops - continue; - } - else if (memcmp(code, kMsgCClipboard, 4) == 0) { - recvGrabClipboard(); - } - else if (memcmp(code, kMsgDClipboard, 4) == 0) { - recvClipboard(); - } - // FIXME -- more message here - else { - log((CLOG_ERR "invalid message from client \"%s\"", getClient().c_str())); - - // unknown message - throw XBadClient(); - } - } -} - -void -CServerProtocol1_0::queryInfo() -{ - log((CLOG_DEBUG1 "querying client \"%s\" info", getClient().c_str())); - - // send request - CProtocolUtil::writef(getOutputStream(), kMsgQInfo); - - // wait for and verify reply - UInt8 code[4]; - for (;;) { - UInt32 n = getInputStream()->read(code, 4, -1.0); - if (n == 4) { - if (memcmp(code, kMsgCNoop, 4) == 0) { - // discard heartbeats - continue; - } - if (memcmp(code, kMsgDInfo, 4) == 0) { - break; - } - } - throw XBadClient(); - } - - // handle reply - recvInfo(); -} - -void -CServerProtocol1_0::sendClose() -{ - log((CLOG_DEBUG1 "send close to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCClose); - - // force the close to be sent before we return - getOutputStream()->flush(); -} - -void -CServerProtocol1_0::sendEnter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask) -{ - log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getClient().c_str(), xAbs, yAbs, seqNum, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgCEnter, - xAbs, yAbs, seqNum, mask); -} - -void -CServerProtocol1_0::sendLeave() -{ - log((CLOG_DEBUG1 "send leave to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCLeave); -} - -void -CServerProtocol1_0::sendClipboard(ClipboardID id, const CString& data) -{ - log((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getClient().c_str(), data.size())); - CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); -} - -void -CServerProtocol1_0::sendGrabClipboard(ClipboardID id) -{ - log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); -} - -void -CServerProtocol1_0::sendScreenSaver(bool on) -{ - log((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getClient().c_str(), on ? 1 : 0)); - CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); -} - -void -CServerProtocol1_0::sendInfoAcknowledgment() -{ - log((CLOG_DEBUG1 "send info ack to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCInfoAck); -} - -void -CServerProtocol1_0::sendKeyDown(KeyID key, KeyModifierMask mask) -{ - log((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); -} - -void -CServerProtocol1_0::sendKeyRepeat(KeyID key, KeyModifierMask mask, SInt32 count) -{ - log((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getClient().c_str(), key, mask, count)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); -} - -void -CServerProtocol1_0::sendKeyUp(KeyID key, KeyModifierMask mask) -{ - log((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); -} - -void -CServerProtocol1_0::sendMouseDown(ButtonID button) -{ - log((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getClient().c_str(), button)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); -} - -void -CServerProtocol1_0::sendMouseUp(ButtonID button) -{ - log((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getClient().c_str(), button)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); -} - -void -CServerProtocol1_0::sendMouseMove(SInt32 xAbs, SInt32 yAbs) -{ - log((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getClient().c_str(), xAbs, yAbs)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); -} - -void -CServerProtocol1_0::sendMouseWheel(SInt32 delta) -{ - log((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getClient().c_str(), delta)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); -} - -void -CServerProtocol1_0::recvInfo() -{ - // parse the message - SInt16 x, y, w, h, zoneInfo, mx, my; - CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, - &x, &y, &w, &h, &zoneInfo, &mx, &my); - log((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getClient().c_str(), x, y, w, h, zoneInfo, mx, my)); - - // validate - if (w <= 0 || h <= 0 || zoneInfo < 0) { - throw XBadClient(); - } - if (mx < x || my < y || mx >= x + w || my >= y + h) { - throw XBadClient(); - } - - // tell server of change - getServer()->setInfo(getClient(), x, y, w, h, zoneInfo, mx, my); -} - -void -CServerProtocol1_0::recvClipboard() -{ - // parse message - ClipboardID id; - UInt32 seqNum; - CString data; - CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); - log((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getClient().c_str(), id, seqNum, data.size())); - - // validate - if (id >= kClipboardEnd) { - throw XBadClient(); - } - - // send update - getServer()->setClipboard(id, seqNum, data); -} - -void -CServerProtocol1_0::recvGrabClipboard() -{ - // parse message - ClipboardID id; - UInt32 seqNum; - CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); - log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getClient().c_str(), id, seqNum)); - - // validate - if (id >= kClipboardEnd) { - throw XBadClient(); - } - - // send update - getServer()->grabClipboard(id, seqNum, getClient()); -} diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h deleted file mode 100644 index 4e14dac7..00000000 --- a/server/CServerProtocol1_0.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef CSERVERPROTOCOL1_0_H -#define CSERVERPROTOCOL1_0_H - -#include "CServerProtocol.h" - -class CServerProtocol1_0 : public CServerProtocol { -public: - CServerProtocol1_0(CServer*, const CString&, IInputStream*, IOutputStream*); - ~CServerProtocol1_0(); - - // manipulators - - // accessors - - // IServerProtocol overrides - virtual void run(); - virtual void queryInfo(); - virtual void sendClose(); - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask); - virtual void sendLeave(); - virtual void sendClipboard(ClipboardID, const CString&); - virtual void sendGrabClipboard(ClipboardID); - virtual void sendScreenSaver(bool on); - virtual void sendInfoAcknowledgment(); - virtual void sendKeyDown(KeyID, KeyModifierMask); - virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count); - virtual void sendKeyUp(KeyID, KeyModifierMask); - virtual void sendMouseDown(ButtonID); - virtual void sendMouseUp(ButtonID); - virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs); - virtual void sendMouseWheel(SInt32 delta); - -protected: - // IServerProtocol overrides - virtual void recvInfo(); - virtual void recvClipboard(); - virtual void recvGrabClipboard(); -}; - -#endif diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 0a9ecb36..5209a461 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -1,8 +1,9 @@ #include "CXWindowsPrimaryScreen.h" -#include "CServer.h" +#include "IPrimaryReceiver.h" #include "CXWindowsClipboard.h" #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" +#include "CClipboard.h" #include "CThread.h" #include "CLog.h" #include "CStopwatch.h" @@ -19,8 +20,8 @@ // CXWindowsPrimaryScreen // -CXWindowsPrimaryScreen::CXWindowsPrimaryScreen() : - m_server(NULL), +CXWindowsPrimaryScreen::CXWindowsPrimaryScreen(IPrimaryReceiver* receiver) : + m_receiver(receiver), m_active(false), m_window(None) { @@ -65,7 +66,7 @@ CXWindowsPrimaryScreen::run() if (xevent.xclient.message_type == m_atomScreenSaver || xevent.xclient.format == 32) { // screen saver activation/deactivation event - m_server->onScreenSaver(xevent.xclient.data.l[0] != 0); + m_receiver->onScreenSaver(xevent.xclient.data.l[0] != 0); } break; @@ -75,12 +76,12 @@ CXWindowsPrimaryScreen::run() const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { - m_server->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_server->onKeyUp(key, mask | KeyModifierCapsLock); + m_receiver->onKeyUp(key, mask | KeyModifierCapsLock); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_server->onKeyUp(key, mask | KeyModifierNumLock); + m_receiver->onKeyUp(key, mask | KeyModifierNumLock); } } } @@ -110,12 +111,12 @@ CXWindowsPrimaryScreen::run() // no press event follows so it's a plain release log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_server->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_server->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); } - m_server->onKeyUp(key, mask); + m_receiver->onKeyUp(key, mask); } else { // found a press event following so it's a repeat. @@ -123,7 +124,7 @@ CXWindowsPrimaryScreen::run() // repeats but we'll just send a repeat of 1. // note that we discard the press event. log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - m_server->onKeyRepeat(key, mask, 1); + m_receiver->onKeyRepeat(key, mask, 1); } } } @@ -134,7 +135,7 @@ CXWindowsPrimaryScreen::run() log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_server->onMouseDown(button); + m_receiver->onMouseDown(button); } } break; @@ -144,15 +145,15 @@ CXWindowsPrimaryScreen::run() log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_server->onMouseUp(button); + m_receiver->onMouseUp(button); } else if (xevent.xbutton.button == 4) { // wheel forward (away from user) - m_server->onMouseWheel(120); + m_receiver->onMouseWheel(120); } else if (xevent.xbutton.button == 5) { // wheel backward (toward user) - m_server->onMouseWheel(-120); + m_receiver->onMouseWheel(-120); } } break; @@ -183,7 +184,7 @@ CXWindowsPrimaryScreen::run() } else if (!m_active) { // motion on primary screen - m_server->onMouseMovePrimary(m_x, m_y); + m_receiver->onMouseMovePrimary(m_x, m_y); } else { // motion on secondary screen. warp mouse back to @@ -214,7 +215,7 @@ CXWindowsPrimaryScreen::run() // warping to the primary screen's enter position, // effectively overriding it. if (x != 0 || y != 0) { - m_server->onMouseMoveSecondary(x, y); + m_receiver->onMouseMoveSecondary(x, y); } } } @@ -231,17 +232,11 @@ CXWindowsPrimaryScreen::stop() } void -CXWindowsPrimaryScreen::open(CServer* server) +CXWindowsPrimaryScreen::open() { - assert(m_server == NULL); - assert(server != NULL); - // open the display openDisplay(); - // set the server - m_server = server; - // check for peculiarities // FIXME -- may have to get these from some database m_numLockHalfDuplex = false; @@ -283,23 +278,18 @@ CXWindowsPrimaryScreen::open(CServer* server) m_yCenter = y + (h >> 1); // send screen info - m_server->setInfo(x, y, w, h, getJumpZoneSize(), m_x, m_y); + m_receiver->onInfoChanged(x, y, w, h, 1, m_x, m_y); } void CXWindowsPrimaryScreen::close() { - assert(m_server != NULL); - // stop being notified of screen saver activation/deactivation getScreenSaver()->setNotify(None); m_atomScreenSaver = None; // close the display closeDisplay(); - - // done with server - m_server = NULL; } void @@ -394,7 +384,7 @@ CXWindowsPrimaryScreen::leave() } void -CXWindowsPrimaryScreen::onConfigure() +CXWindowsPrimaryScreen::reconfigure() { // do nothing } @@ -475,19 +465,6 @@ CXWindowsPrimaryScreen::grabClipboard(ClipboardID id) setDisplayClipboard(id, NULL); } -void -CXWindowsPrimaryScreen::getShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - getScreenShape(x, y, w, h); -} - -SInt32 -CXWindowsPrimaryScreen::getJumpZoneSize() const -{ - return 1; -} - void CXWindowsPrimaryScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const @@ -555,13 +532,6 @@ CXWindowsPrimaryScreen::isLockedToScreen() const return false; } -bool -CXWindowsPrimaryScreen::isScreenSaverActive() const -{ - CDisplayLock display(this); - return getScreenSaver()->isActive(); -} - void CXWindowsPrimaryScreen::onOpenDisplay(Display* display) { @@ -616,16 +586,14 @@ void CXWindowsPrimaryScreen::onUnexpectedClose() { // tell server to shutdown - if (m_server != NULL) { - m_server->shutdown(); - } + m_receiver->onError(); } void CXWindowsPrimaryScreen::onLostClipboard(ClipboardID id) { // tell server that the clipboard was grabbed locally - m_server->grabClipboard(id); + m_receiver->onGrabClipboard(id); } void diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 8cabc11f..708d8376 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -5,28 +5,27 @@ #include "IPrimaryScreen.h" #include "MouseTypes.h" +class IPrimaryReceiver; + class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { public: - CXWindowsPrimaryScreen(); + CXWindowsPrimaryScreen(IPrimaryReceiver*); virtual ~CXWindowsPrimaryScreen(); // IPrimaryScreen overrides virtual void run(); virtual void stop(); - virtual void open(CServer*); + virtual void open(); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool); virtual bool leave(); - virtual void onConfigure(); + virtual void reconfigure(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); - virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; - virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; - virtual bool isScreenSaverActive() const; protected: // CXWindowsScreen overrides @@ -61,7 +60,7 @@ private: static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: - CServer* m_server; + IPrimaryReceiver* m_receiver; bool m_active; Window m_window; diff --git a/server/IPrimaryReceiver.h b/server/IPrimaryReceiver.h new file mode 100644 index 00000000..081a0c95 --- /dev/null +++ b/server/IPrimaryReceiver.h @@ -0,0 +1,46 @@ +#ifndef IPRIMARYRECEIVER_H +#define IPRIMARYRECEIVER_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CString.h" + +class IPrimaryReceiver : public IInterface { +public: + // manipulators + + // notify of serious error. this implies that the primary screen + // cannot continue to function. + virtual void onError() = 0; + + // notify of info change + virtual void onInfoChanged(SInt32 xScreen, SInt32 yScreen, + SInt32 wScreen, SInt32 hScreen, + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse) = 0; + + // notify of clipboard grab. returns true if the grab was honored, + // false otherwise. + virtual bool onGrabClipboard(ClipboardID) = 0; + + // notify of new clipboard data. returns true if the clipboard data + // was accepted, false if the change was rejected. + virtual bool onClipboardChanged(ClipboardID, + const CString& data) = 0; + + // call to notify of events. onMouseMovePrimary() returns + // true iff the mouse enters a jump zone and jumps. + virtual void onKeyDown(KeyID, KeyModifierMask) = 0; + virtual void onKeyUp(KeyID, KeyModifierMask) = 0; + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void onMouseDown(ButtonID) = 0; + virtual void onMouseUp(ButtonID) = 0; + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; + virtual void onMouseWheel(SInt32 delta) = 0; + virtual void onScreenSaver(bool activated) = 0; +}; + +#endif diff --git a/server/IPrimaryScreen.h b/server/IPrimaryScreen.h index c68d0bad..62af410f 100644 --- a/server/IPrimaryScreen.h +++ b/server/IPrimaryScreen.h @@ -5,7 +5,6 @@ #include "KeyTypes.h" #include "ClipboardTypes.h" -class CServer; class IClipboard; class IPrimaryScreen : public IInterface { @@ -21,7 +20,8 @@ public: // cause run() to return virtual void stop() = 0; - // initialize the screen and start reporting events to the server. + // initialize the screen and start reporting events to the receiver + // (which is set through some interface of the derived class). // events should be reported no matter where on the screen they // occur but do not interfere with normal event dispatch. the // screen saver engaging should be reported as an event. if that @@ -29,10 +29,11 @@ public: // screen saver timer and should start the screen saver after // idling for an appropriate time. // - // open() must call server->setInfo() to notify the server of the - // primary screen's resolution and jump zone size. the mouse - // position is ignored and may be 0,0. - virtual void open(CServer* server) = 0; + // open() must call receiver->onInfoChanged() to notify of the + // primary screen's initial resolution and jump zone size. it + // must also call receiver->onClipboardChanged() for each + // clipboard that the primary screen has. + virtual void open() = 0; // close the screen. should restore the screen saver timer if it // was disabled. @@ -44,24 +45,30 @@ public: // call to leave() which preceeds it, however the screen should // assume an implicit call to enter() in the call to open(). // if warpCursor is false then do not warp the mouse. + // + // enter() must not call any receiver methods except onError(). virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool forScreenSaver) = 0; - // called when the user navigates off the primary screen. hide - // the cursor and grab exclusive access to the input devices. - // return true iff successful. + // called when the user navigates off the primary screen. hide the + // cursor and grab exclusive access to the input devices. return + // true iff successful. + // + // leave() must not call any receiver methods except onError(). virtual bool leave() = 0; // called when the configuration has changed. subclasses may need // to adjust things (like the jump zones) after the configuration // changes. - virtual void onConfigure() = 0; + virtual void reconfigure() = 0; // warp the cursor to the given position virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; // set the screen's clipboard contents. this is usually called // soon after an enter(). + // + // setClipboard() must not call any receiver methods except onError(). virtual void setClipboard(ClipboardID, const IClipboard*) = 0; // synergy should own the clipboard @@ -69,16 +76,9 @@ public: // accessors - // get the screen region - virtual void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const = 0; - - // get the size of jump zone - virtual SInt32 getJumpZoneSize() const = 0; - - // get the screen's clipboard contents. the implementation can - // and should avoid setting the clipboard object if the screen's - // clipboard hasn't changed. + // return the contents of the given clipboard. + // + // getClipboard() must not call any receiver methods except onError(). virtual void getClipboard(ClipboardID, IClipboard*) const = 0; // get the primary screen's current toggle modifier key state. @@ -90,9 +90,6 @@ public: // any other reason that the user should not be allowed to switch // screens. virtual bool isLockedToScreen() const = 0; - - // return true if the screen saver is activated - virtual bool isScreenSaverActive() const = 0; }; #endif diff --git a/server/IServerProtocol.h b/server/IServerProtocol.h deleted file mode 100644 index e329d0a4..00000000 --- a/server/IServerProtocol.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef ISERVERPROTOCOL_H -#define ISERVERPROTOCOL_H - -#include "IInterface.h" -#include "ClipboardTypes.h" -#include "KeyTypes.h" -#include "MouseTypes.h" -#include "CString.h" - -class IClipboard; - -class IServerProtocol : public IInterface { -public: - // manipulators - - // process messages from the client and insert the appropriate - // events into the server's event queue. return when the client - // disconnects. - virtual void run() = 0; - - // send client info query and process reply - virtual void queryInfo() = 0; - - // send various messages to client - virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask) = 0; - virtual void sendLeave() = 0; - virtual void sendClipboard(ClipboardID, const CString&) = 0; - virtual void sendGrabClipboard(ClipboardID) = 0; - virtual void sendScreenSaver(bool on) = 0; - virtual void sendInfoAcknowledgment() = 0; - virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; - virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; - virtual void sendMouseDown(ButtonID) = 0; - virtual void sendMouseUp(ButtonID) = 0; - virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) = 0; - virtual void sendMouseWheel(SInt32 delta) = 0; - - // accessors - -protected: - // manipulators - - virtual void recvInfo() = 0; - virtual void recvClipboard() = 0; - virtual void recvGrabClipboard() = 0; - - // accessors -}; - -#endif diff --git a/server/Makefile.am b/server/Makefile.am index 7c7af55f..02ec9040 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -4,20 +4,23 @@ DEPTH = .. bin_PROGRAMS = synergyd synergyd_SOURCES = \ + CClientProxy.cpp \ + CClientProxy1_0.cpp \ CConfig.cpp \ - CServerProtocol.cpp \ - CServerProtocol1_0.cpp \ - CXWindowsPrimaryScreen.cpp \ - CServer.cpp \ CHTTPServer.cpp \ + CPrimaryClient.cpp \ + CServer.cpp \ + CXWindowsPrimaryScreen.cpp \ server.cpp \ + CClientProxy.h \ + CClientProxy1_0.h \ CConfig.h \ CHTTPServer.h \ + CPrimaryClient.h \ CServer.h \ - CServerProtocol.h \ - CServerProtocol1_0.h \ - CSynergyHook.h \ CXWindowsPrimaryScreen.h \ + IPrimaryReceiver.h \ + IPrimaryScreen.h \ $(NULL) synergyd_LDADD = \ $(DEPTH)/platform/libplatform.a \ diff --git a/synergy/IClient.h b/synergy/IClient.h new file mode 100644 index 00000000..7a151457 --- /dev/null +++ b/synergy/IClient.h @@ -0,0 +1,91 @@ +#ifndef ICLIENT_H +#define ICLIENT_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CString.h" + +class IClient : public IInterface { +public: + // manipulators + + // open client + virtual void open() = 0; + + // service client + virtual void run() = 0; + + // close client + virtual void close() = 0; + + // enter the screen. the cursor should be warped to xAbs,yAbs. + // the client should record seqNum for future reporting of + // clipboard changes. mask is the expected toggle button state. + // screenSaver is true if the screen is being entered because + // the screen saver is starting. + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool screenSaver) = 0; + + // leave the screen. returns false if the user may not leave the + // client's screen. + virtual bool leave() = 0; + + // FIXME -- methods for setting clipboard. on proxy side these + // will set/reset the gotClipboard flag and send the clipboard + // when not gotClipboard is false. grabbing goes in here too. + // basically, figure out semantics of these methods. note that + // ISecondaryScreen wants an IClipboard* passed to set not a + // string; will that be a problem? + + // update the client's clipboard. this implies that the client's + // clipboard is now up to date. if the client's clipboard was + // already known to be up to date then this can do nothing. + virtual void setClipboard(ClipboardID, const CString&) = 0; + + // grab the client's clipboard. since this is called when another + // client takes ownership of the clipboard it implies that the + // client's clipboard is dirty. + virtual void grabClipboard(ClipboardID) = 0; + + // called to set the client's clipboard as dirty or clean + virtual void setClipboardDirty(ClipboardID, bool dirty) = 0; + + // handle input events + virtual void keyDown(KeyID, KeyModifierMask) = 0; + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void keyUp(KeyID, KeyModifierMask) = 0; + virtual void mouseDown(ButtonID) = 0; + virtual void mouseUp(ButtonID) = 0; + // FIXME -- if server uses IClient as interface to primary screen + // (should this class be renamed?) then be careful of absolute/relative + // coordinates here; move on primary and move on secondary take + // different values. probably not relevant, though. + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void mouseWheel(SInt32 delta) = 0; + virtual void screenSaver(bool activate) = 0; + + // accessors + + // get the client's identifier + virtual CString getName() const = 0; + + // get the screen's shape + // FIXME -- may want center pixel too + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + + // get the center pixel + virtual void getCenter(SInt32& x, SInt32& y) const = 0; + + // get the size of jump zone + virtual SInt32 getJumpZoneSize() const = 0; + + // what about getClipboard()? don't really want proxy to ask for it; + // it's a push data model. screen info is cached in proxy so it's + // different. will need to keep onClipboardChanged() in CClient. +}; + +#endif diff --git a/synergy/IServer.h b/synergy/IServer.h new file mode 100644 index 00000000..c109f148 --- /dev/null +++ b/synergy/IServer.h @@ -0,0 +1,43 @@ +#ifndef ISERVER_H +#define ISERVER_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CString.h" + +class IServer : public IInterface { +public: + // manipulators + + // notify of serious error. this implies that the server should + // shutdown. + virtual void onError() = 0; + + // notify of client info change (maybe IClient should be named IScreen) + virtual void onInfoChanged(const CString& clientName) = 0; + + // notify of clipboard grab. returns true if the grab was honored, + // false otherwise. + virtual bool onGrabClipboard(ClipboardID, + UInt32 seqNum, const CString& clientName) = 0; + + // notify of new clipboard data + virtual void onClipboardChanged(ClipboardID, + UInt32 seqNum, const CString& data) = 0; + + // call to notify of events. onMouseMovePrimary() returns + // true iff the mouse enters a jump zone and jumps. + virtual void onKeyDown(KeyID, KeyModifierMask) = 0; + virtual void onKeyUp(KeyID, KeyModifierMask) = 0; + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void onMouseDown(ButtonID) = 0; + virtual void onMouseUp(ButtonID) = 0; + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; + virtual void onMouseWheel(SInt32 delta) = 0; + virtual void onScreenSaver(bool activated) = 0; +}; + +#endif diff --git a/synergy/Makefile.am b/synergy/Makefile.am index 2cb3df73..78a56b29 100644 --- a/synergy/Makefile.am +++ b/synergy/Makefile.am @@ -17,10 +17,11 @@ libsynergy_a_SOURCES = \ CProtocolUtil.h \ CTCPSocketFactory.h \ ClipboardTypes.h \ + IClient.h \ IClipboard.h \ IScreenSaver.h \ ISecondaryScreen.h \ - IServerProtocol.h \ + IServer.h \ ISocketFactory.h \ KeyTypes.h \ MouseTypes.h \