From e0a06cf0250871d0b363b9b5aa0a037bccd55d2e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 2 May 2022 11:03:52 +0100 Subject: [PATCH] Archive of v1.0.14 from 2003/08/24 --- AUTHORS | 4 + BUGS | 12 + COPYING | 283 + ChangeLog | 7045 ++++++++++++++++ FAQ | 187 + HISTORY | 16 + INSTALL | 575 ++ Makefile.am | 107 + Makefile.in | 520 ++ NEWS | 180 + PORTING | 394 + README | 267 + TODO | 61 + acinclude.m4 | 515 ++ aclocal.m4 | 1076 +++ all.dsp | 63 + cmd/Makefile.am | 30 + cmd/Makefile.in | 351 + cmd/exec.dsp | 63 + cmd/launcher/CAdvancedOptions.cpp | 227 + cmd/launcher/CAdvancedOptions.h | 76 + cmd/launcher/CAutoStart.cpp | 288 + cmd/launcher/CAutoStart.h | 78 + cmd/launcher/CGlobalOptions.cpp | 259 + cmd/launcher/CGlobalOptions.h | 67 + cmd/launcher/LaunchUtil.cpp | 228 + cmd/launcher/LaunchUtil.h | 55 + cmd/launcher/Makefile.am | 36 + cmd/launcher/Makefile.in | 239 + cmd/launcher/launcher.cpp | 1211 +++ cmd/launcher/launcher.dsp | 154 + cmd/launcher/launcher.rc | 367 + cmd/launcher/resource.h | 113 + cmd/launcher/synergy.ico | Bin 0 -> 1078 bytes cmd/synergyc/CClientTaskBarReceiver.cpp | 163 + cmd/synergyc/CClientTaskBarReceiver.h | 110 + .../CMSWindowsClientTaskBarReceiver.cpp | 310 + .../CMSWindowsClientTaskBarReceiver.h | 63 + .../CXWindowsClientTaskBarReceiver.cpp | 61 + cmd/synergyc/CXWindowsClientTaskBarReceiver.h | 37 + cmd/synergyc/Makefile.am | 69 + cmd/synergyc/Makefile.in | 397 + cmd/synergyc/resource.h | 27 + cmd/synergyc/synergyc.cpp | 739 ++ cmd/synergyc/synergyc.dsp | 153 + cmd/synergyc/synergyc.ico | Bin 0 -> 1078 bytes cmd/synergyc/synergyc.rc | 118 + cmd/synergyc/tb_error.ico | Bin 0 -> 318 bytes cmd/synergyc/tb_idle.ico | Bin 0 -> 318 bytes cmd/synergyc/tb_run.ico | Bin 0 -> 318 bytes cmd/synergyc/tb_wait.ico | Bin 0 -> 318 bytes .../CMSWindowsServerTaskBarReceiver.cpp | 330 + .../CMSWindowsServerTaskBarReceiver.h | 63 + cmd/synergys/CServerTaskBarReceiver.cpp | 171 + cmd/synergys/CServerTaskBarReceiver.h | 110 + .../CXWindowsServerTaskBarReceiver.cpp | 61 + cmd/synergys/CXWindowsServerTaskBarReceiver.h | 37 + cmd/synergys/Makefile.am | 71 + cmd/synergys/Makefile.in | 400 + cmd/synergys/resource.h | 28 + cmd/synergys/synergys.cpp | 883 ++ cmd/synergys/synergys.dsp | 153 + cmd/synergys/synergys.ico | Bin 0 -> 1078 bytes cmd/synergys/synergys.rc | 121 + cmd/synergys/tb_error.ico | Bin 0 -> 318 bytes cmd/synergys/tb_idle.ico | Bin 0 -> 318 bytes cmd/synergys/tb_run.ico | Bin 0 -> 318 bytes cmd/synergys/tb_wait.ico | Bin 0 -> 318 bytes config.h.in | 158 + config/config.guess | 1327 +++ config/config.sub | 1410 ++++ config/depcomp | 411 + config/install-sh | 251 + config/missing | 283 + config/mkinstalldirs | 40 + configure | 7498 +++++++++++++++++ configure.in | 124 + dist/Makefile.am | 27 + dist/Makefile.in | 348 + dist/rpm/Makefile.am | 23 + dist/rpm/Makefile.in | 228 + dist/rpm/synergy.spec.in | 48 + doc/doxygen.cfg.in | 898 ++ examples/synergy.conf | 37 + lib/Makefile.am | 37 + lib/Makefile.in | 358 + lib/arch/CArch.cpp | 611 ++ lib/arch/CArch.h | 192 + lib/arch/CArchConsoleUnix.cpp | 54 + lib/arch/CArchConsoleUnix.h | 35 + lib/arch/CArchConsoleWindows.cpp | 109 + lib/arch/CArchConsoleWindows.h | 48 + lib/arch/CArchDaemonNone.cpp | 65 + lib/arch/CArchDaemonNone.h | 46 + lib/arch/CArchDaemonUnix.cpp | 81 + lib/arch/CArchDaemonUnix.h | 33 + lib/arch/CArchDaemonWindows.cpp | 801 ++ lib/arch/CArchDaemonWindows.h | 123 + lib/arch/CArchFileUnix.cpp | 94 + lib/arch/CArchFileUnix.h | 36 + lib/arch/CArchFileWindows.cpp | 132 + lib/arch/CArchFileWindows.h | 36 + lib/arch/CArchImpl.cpp | 45 + lib/arch/CArchLogUnix.cpp | 73 + lib/arch/CArchLogUnix.h | 34 + lib/arch/CArchLogWindows.cpp | 84 + lib/arch/CArchLogWindows.h | 40 + lib/arch/CArchMiscWindows.cpp | 183 + lib/arch/CArchMiscWindows.h | 85 + lib/arch/CArchMultithreadPosix.cpp | 771 ++ lib/arch/CArchMultithreadPosix.h | 95 + lib/arch/CArchMultithreadWindows.cpp | 783 ++ lib/arch/CArchMultithreadWindows.h | 101 + lib/arch/CArchNetworkBSD.cpp | 853 ++ lib/arch/CArchNetworkBSD.h | 89 + lib/arch/CArchNetworkWinsock.cpp | 839 ++ lib/arch/CArchNetworkWinsock.h | 92 + lib/arch/CArchSleepUnix.cpp | 88 + lib/arch/CArchSleepUnix.h | 32 + lib/arch/CArchSleepWindows.cpp | 57 + lib/arch/CArchSleepWindows.h | 32 + lib/arch/CArchStringUnix.cpp | 40 + lib/arch/CArchStringUnix.h | 41 + lib/arch/CArchStringWindows.cpp | 44 + lib/arch/CArchStringWindows.h | 41 + lib/arch/CArchTaskBarWindows.cpp | 518 ++ lib/arch/CArchTaskBarWindows.h | 110 + lib/arch/CArchTaskBarXWindows.cpp | 47 + lib/arch/CArchTaskBarXWindows.h | 34 + lib/arch/CArchTimeUnix.cpp | 47 + lib/arch/CArchTimeUnix.h | 32 + lib/arch/CArchTimeWindows.cpp | 86 + lib/arch/CArchTimeWindows.h | 32 + lib/arch/CMultibyte.cpp | 40 + lib/arch/CMultibyteEmu.cpp | 114 + lib/arch/CMultibyteOS.cpp | 68 + lib/arch/IArchConsole.h | 62 + lib/arch/IArchDaemon.h | 104 + lib/arch/IArchFile.h | 64 + lib/arch/IArchLog.h | 64 + lib/arch/IArchMultithread.h | 265 + lib/arch/IArchNetwork.h | 278 + lib/arch/IArchSleep.h | 43 + lib/arch/IArchString.h | 93 + lib/arch/IArchTaskBar.h | 63 + lib/arch/IArchTaskBarReceiver.h | 90 + lib/arch/IArchTime.h | 40 + lib/arch/Makefile.am | 101 + lib/arch/Makefile.in | 433 + lib/arch/XArch.cpp | 33 + lib/arch/XArch.h | 166 + lib/arch/XArchUnix.cpp | 33 + lib/arch/XArchUnix.h | 34 + lib/arch/XArchWindows.cpp | 126 + lib/arch/XArchWindows.h | 52 + lib/arch/arch.dsp | 294 + lib/arch/vsnprintf.cpp | 62 + lib/base/CFunctionJob.cpp | 39 + lib/base/CFunctionJob.h | 38 + lib/base/CJobList.cpp | 113 + lib/base/CJobList.h | 72 + lib/base/CLog.cpp | 313 + lib/base/CLog.h | 207 + lib/base/CStopwatch.cpp | 126 + lib/base/CStopwatch.h | 108 + lib/base/CString.h | 25 + lib/base/CStringUtil.cpp | 211 + lib/base/CStringUtil.h | 99 + lib/base/CUnicode.cpp | 798 ++ lib/base/CUnicode.h | 143 + lib/base/IJob.h | 30 + lib/base/ILogOutputter.h | 72 + lib/base/LogOutputters.cpp | 238 + lib/base/LogOutputters.h | 128 + lib/base/Makefile.am | 52 + lib/base/Makefile.in | 363 + lib/base/TMethodJob.h | 67 + lib/base/XBase.cpp | 69 + lib/base/XBase.h | 121 + lib/base/base.dsp | 174 + lib/client/CClient.cpp | 754 ++ lib/client/CClient.h | 206 + lib/client/CServerProxy.cpp | 760 ++ lib/client/CServerProxy.h | 136 + lib/client/Makefile.am | 42 + lib/client/Makefile.in | 342 + lib/client/client.dsp | 114 + lib/common/BasicTypes.h | 87 + lib/common/IInterface.h | 31 + lib/common/Makefile.am | 44 + lib/common/Makefile.in | 248 + lib/common/Version.h | 45 + lib/common/common.dsp | 162 + lib/common/common.h | 74 + lib/common/stdbitset.h | 17 + lib/common/stddeque.h | 17 + lib/common/stdfstream.h | 18 + lib/common/stdistream.h | 43 + lib/common/stdlist.h | 17 + lib/common/stdmap.h | 17 + lib/common/stdostream.h | 21 + lib/common/stdpost.h | 17 + lib/common/stdpre.h | 27 + lib/common/stdset.h | 17 + lib/common/stdsstream.h | 371 + lib/common/stdstring.h | 17 + lib/common/stdvector.h | 17 + lib/http/CHTTPProtocol.cpp | 658 ++ lib/http/CHTTPProtocol.h | 201 + lib/http/Makefile.am | 39 + lib/http/Makefile.in | 339 + lib/http/XHTTP.cpp | 135 + lib/http/XHTTP.h | 82 + lib/http/http.dsp | 110 + lib/io/CBufferedInputStream.cpp | 132 + lib/io/CBufferedInputStream.h | 96 + lib/io/CBufferedOutputStream.cpp | 97 + lib/io/CBufferedOutputStream.h | 88 + lib/io/CInputStreamFilter.cpp | 39 + lib/io/CInputStreamFilter.h | 52 + lib/io/COutputStreamFilter.cpp | 39 + lib/io/COutputStreamFilter.h | 52 + lib/io/CStreamBuffer.cpp | 136 + lib/io/CStreamBuffer.h | 78 + lib/io/IInputStream.h | 67 + lib/io/IOutputStream.h | 58 + lib/io/IStreamFilterFactory.h | 48 + lib/io/Makefile.am | 49 + lib/io/Makefile.in | 360 + lib/io/XIO.cpp | 36 + lib/io/XIO.h | 42 + lib/io/io.dsp | 154 + lib/mt/CCondVar.cpp | 81 + lib/mt/CCondVar.h | 226 + lib/mt/CLock.cpp | 38 + lib/mt/CLock.h | 48 + lib/mt/CMutex.cpp | 53 + lib/mt/CMutex.h | 78 + lib/mt/CThread.cpp | 184 + lib/mt/CThread.h | 225 + lib/mt/CTimerThread.cpp | 65 + lib/mt/CTimerThread.h | 56 + lib/mt/Makefile.am | 46 + lib/mt/Makefile.in | 353 + lib/mt/XMT.cpp | 25 + lib/mt/XMT.h | 29 + lib/mt/XThread.h | 36 + lib/mt/mt.dsp | 146 + lib/net/CNetworkAddress.cpp | 166 + lib/net/CNetworkAddress.h | 91 + lib/net/CTCPListenSocket.cpp | 98 + lib/net/CTCPListenSocket.h | 41 + lib/net/CTCPSocket.cpp | 379 + lib/net/CTCPSocket.h | 66 + lib/net/CTCPSocketFactory.cpp | 43 + lib/net/CTCPSocketFactory.h | 31 + lib/net/IDataSocket.h | 63 + lib/net/IListenSocket.h | 48 + lib/net/ISocket.h | 46 + lib/net/ISocketFactory.h | 42 + lib/net/Makefile.am | 49 + lib/net/Makefile.in | 357 + lib/net/XSocket.cpp | 111 + lib/net/XSocket.h | 95 + lib/net/net.dsp | 146 + lib/platform/CMSWindowsClipboard.cpp | 205 + lib/platform/CMSWindowsClipboard.h | 104 + .../CMSWindowsClipboardAnyTextConverter.cpp | 145 + .../CMSWindowsClipboardAnyTextConverter.h | 56 + .../CMSWindowsClipboardTextConverter.cpp | 55 + .../CMSWindowsClipboardTextConverter.h | 36 + .../CMSWindowsClipboardUTF16Converter.cpp | 55 + .../CMSWindowsClipboardUTF16Converter.h | 36 + lib/platform/CMSWindowsPrimaryScreen.cpp | 1816 ++++ lib/platform/CMSWindowsPrimaryScreen.h | 140 + lib/platform/CMSWindowsScreen.cpp | 820 ++ lib/platform/CMSWindowsScreen.h | 204 + lib/platform/CMSWindowsScreenSaver.cpp | 377 + lib/platform/CMSWindowsScreenSaver.h | 82 + lib/platform/CMSWindowsSecondaryScreen.cpp | 1604 ++++ lib/platform/CMSWindowsSecondaryScreen.h | 166 + lib/platform/CSynergyHook.cpp | 842 ++ lib/platform/CSynergyHook.h | 75 + lib/platform/CXWindowsClipboard.cpp | 1492 ++++ lib/platform/CXWindowsClipboard.h | 372 + .../CXWindowsClipboardTextConverter.cpp | 74 + .../CXWindowsClipboardTextConverter.h | 41 + .../CXWindowsClipboardUCS2Converter.cpp | 62 + .../CXWindowsClipboardUCS2Converter.h | 41 + .../CXWindowsClipboardUTF8Converter.cpp | 61 + .../CXWindowsClipboardUTF8Converter.h | 41 + lib/platform/CXWindowsPrimaryScreen.cpp | 1044 +++ lib/platform/CXWindowsPrimaryScreen.h | 126 + lib/platform/CXWindowsScreen.cpp | 922 ++ lib/platform/CXWindowsScreen.h | 311 + lib/platform/CXWindowsScreenSaver.cpp | 482 ++ lib/platform/CXWindowsScreenSaver.h | 161 + lib/platform/CXWindowsSecondaryScreen.cpp | 2070 +++++ lib/platform/CXWindowsSecondaryScreen.h | 240 + lib/platform/CXWindowsUtil.cpp | 1111 +++ lib/platform/CXWindowsUtil.h | 141 + lib/platform/IMSWindowsScreenEventHandler.h | 56 + lib/platform/Makefile.am | 75 + lib/platform/Makefile.in | 396 + lib/platform/makehook.dsp | 63 + lib/platform/platform.dsp | 162 + lib/platform/synrgyhk.dsp | 116 + lib/server/CClientProxy.cpp | 67 + lib/server/CClientProxy.h | 105 + lib/server/CClientProxy1_0.cpp | 398 + lib/server/CClientProxy1_0.h | 69 + lib/server/CClientProxy1_1.cpp | 56 + lib/server/CClientProxy1_1.h | 35 + lib/server/CConfig.cpp | 1174 +++ lib/server/CConfig.h | 332 + lib/server/CHTTPServer.cpp | 799 ++ lib/server/CHTTPServer.h | 142 + lib/server/CPrimaryClient.cpp | 295 + lib/server/CPrimaryClient.h | 137 + lib/server/CServer.cpp | 2141 +++++ lib/server/CServer.h | 414 + lib/server/Makefile.am | 53 + lib/server/Makefile.in | 365 + lib/server/server.dsp | 154 + lib/synergy/CClipboard.cpp | 226 + lib/synergy/CClipboard.h | 93 + lib/synergy/CInputPacketStream.cpp | 181 + lib/synergy/CInputPacketStream.h | 51 + lib/synergy/COutputPacketStream.cpp | 72 + lib/synergy/COutputPacketStream.h | 36 + lib/synergy/CPrimaryScreen.cpp | 281 + lib/synergy/CPrimaryScreen.h | 349 + lib/synergy/CProtocolUtil.cpp | 519 ++ lib/synergy/CProtocolUtil.h | 90 + lib/synergy/CSecondaryScreen.cpp | 381 + lib/synergy/CSecondaryScreen.h | 390 + lib/synergy/ClipboardTypes.h | 41 + lib/synergy/IClient.h | 214 + lib/synergy/IClipboard.h | 117 + lib/synergy/IPrimaryScreenFactory.h | 39 + lib/synergy/IPrimaryScreenReceiver.h | 73 + lib/synergy/IScreen.h | 150 + lib/synergy/IScreenEventHandler.h | 79 + lib/synergy/IScreenReceiver.h | 65 + lib/synergy/IScreenSaver.h | 73 + lib/synergy/ISecondaryScreenFactory.h | 38 + lib/synergy/IServer.h | 75 + lib/synergy/KeyTypes.h | 237 + lib/synergy/Makefile.am | 67 + lib/synergy/Makefile.in | 382 + lib/synergy/MouseTypes.h | 35 + lib/synergy/OptionTypes.h | 62 + lib/synergy/ProtocolTypes.h | 286 + lib/synergy/XScreen.cpp | 53 + lib/synergy/XScreen.h | 61 + lib/synergy/XSynergy.cpp | 104 + lib/synergy/XSynergy.h | 105 + lib/synergy/libsynergy.dsp | 222 + stamp-h.in | 0 synergy.dsw | 335 + 360 files changed, 86805 insertions(+) create mode 100644 AUTHORS create mode 100644 BUGS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 FAQ create mode 100644 HISTORY create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.in create mode 100644 NEWS create mode 100644 PORTING create mode 100644 README create mode 100644 TODO create mode 100644 acinclude.m4 create mode 100644 aclocal.m4 create mode 100644 all.dsp create mode 100644 cmd/Makefile.am create mode 100644 cmd/Makefile.in create mode 100644 cmd/exec.dsp create mode 100644 cmd/launcher/CAdvancedOptions.cpp create mode 100644 cmd/launcher/CAdvancedOptions.h create mode 100644 cmd/launcher/CAutoStart.cpp create mode 100644 cmd/launcher/CAutoStart.h create mode 100644 cmd/launcher/CGlobalOptions.cpp create mode 100644 cmd/launcher/CGlobalOptions.h create mode 100644 cmd/launcher/LaunchUtil.cpp create mode 100644 cmd/launcher/LaunchUtil.h create mode 100644 cmd/launcher/Makefile.am create mode 100644 cmd/launcher/Makefile.in create mode 100644 cmd/launcher/launcher.cpp create mode 100644 cmd/launcher/launcher.dsp create mode 100644 cmd/launcher/launcher.rc create mode 100644 cmd/launcher/resource.h create mode 100644 cmd/launcher/synergy.ico create mode 100644 cmd/synergyc/CClientTaskBarReceiver.cpp create mode 100644 cmd/synergyc/CClientTaskBarReceiver.h create mode 100644 cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp create mode 100644 cmd/synergyc/CMSWindowsClientTaskBarReceiver.h create mode 100644 cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp create mode 100644 cmd/synergyc/CXWindowsClientTaskBarReceiver.h create mode 100644 cmd/synergyc/Makefile.am create mode 100644 cmd/synergyc/Makefile.in create mode 100644 cmd/synergyc/resource.h create mode 100644 cmd/synergyc/synergyc.cpp create mode 100644 cmd/synergyc/synergyc.dsp create mode 100644 cmd/synergyc/synergyc.ico create mode 100644 cmd/synergyc/synergyc.rc create mode 100644 cmd/synergyc/tb_error.ico create mode 100644 cmd/synergyc/tb_idle.ico create mode 100644 cmd/synergyc/tb_run.ico create mode 100644 cmd/synergyc/tb_wait.ico create mode 100644 cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp create mode 100644 cmd/synergys/CMSWindowsServerTaskBarReceiver.h create mode 100644 cmd/synergys/CServerTaskBarReceiver.cpp create mode 100644 cmd/synergys/CServerTaskBarReceiver.h create mode 100644 cmd/synergys/CXWindowsServerTaskBarReceiver.cpp create mode 100644 cmd/synergys/CXWindowsServerTaskBarReceiver.h create mode 100644 cmd/synergys/Makefile.am create mode 100644 cmd/synergys/Makefile.in create mode 100644 cmd/synergys/resource.h create mode 100644 cmd/synergys/synergys.cpp create mode 100644 cmd/synergys/synergys.dsp create mode 100644 cmd/synergys/synergys.ico create mode 100644 cmd/synergys/synergys.rc create mode 100644 cmd/synergys/tb_error.ico create mode 100644 cmd/synergys/tb_idle.ico create mode 100644 cmd/synergys/tb_run.ico create mode 100644 cmd/synergys/tb_wait.ico create mode 100644 config.h.in create mode 100644 config/config.guess create mode 100644 config/config.sub create mode 100644 config/depcomp create mode 100644 config/install-sh create mode 100644 config/missing create mode 100644 config/mkinstalldirs create mode 100644 configure create mode 100644 configure.in create mode 100644 dist/Makefile.am create mode 100644 dist/Makefile.in create mode 100644 dist/rpm/Makefile.am create mode 100644 dist/rpm/Makefile.in create mode 100644 dist/rpm/synergy.spec.in create mode 100644 doc/doxygen.cfg.in create mode 100644 examples/synergy.conf create mode 100644 lib/Makefile.am create mode 100644 lib/Makefile.in create mode 100644 lib/arch/CArch.cpp create mode 100644 lib/arch/CArch.h create mode 100644 lib/arch/CArchConsoleUnix.cpp create mode 100644 lib/arch/CArchConsoleUnix.h create mode 100644 lib/arch/CArchConsoleWindows.cpp create mode 100644 lib/arch/CArchConsoleWindows.h create mode 100644 lib/arch/CArchDaemonNone.cpp create mode 100644 lib/arch/CArchDaemonNone.h create mode 100644 lib/arch/CArchDaemonUnix.cpp create mode 100644 lib/arch/CArchDaemonUnix.h create mode 100644 lib/arch/CArchDaemonWindows.cpp create mode 100644 lib/arch/CArchDaemonWindows.h create mode 100644 lib/arch/CArchFileUnix.cpp create mode 100644 lib/arch/CArchFileUnix.h create mode 100644 lib/arch/CArchFileWindows.cpp create mode 100644 lib/arch/CArchFileWindows.h create mode 100644 lib/arch/CArchImpl.cpp create mode 100644 lib/arch/CArchLogUnix.cpp create mode 100644 lib/arch/CArchLogUnix.h create mode 100644 lib/arch/CArchLogWindows.cpp create mode 100644 lib/arch/CArchLogWindows.h create mode 100644 lib/arch/CArchMiscWindows.cpp create mode 100644 lib/arch/CArchMiscWindows.h create mode 100644 lib/arch/CArchMultithreadPosix.cpp create mode 100644 lib/arch/CArchMultithreadPosix.h create mode 100644 lib/arch/CArchMultithreadWindows.cpp create mode 100644 lib/arch/CArchMultithreadWindows.h create mode 100644 lib/arch/CArchNetworkBSD.cpp create mode 100644 lib/arch/CArchNetworkBSD.h create mode 100644 lib/arch/CArchNetworkWinsock.cpp create mode 100644 lib/arch/CArchNetworkWinsock.h create mode 100644 lib/arch/CArchSleepUnix.cpp create mode 100644 lib/arch/CArchSleepUnix.h create mode 100644 lib/arch/CArchSleepWindows.cpp create mode 100644 lib/arch/CArchSleepWindows.h create mode 100644 lib/arch/CArchStringUnix.cpp create mode 100644 lib/arch/CArchStringUnix.h create mode 100644 lib/arch/CArchStringWindows.cpp create mode 100644 lib/arch/CArchStringWindows.h create mode 100644 lib/arch/CArchTaskBarWindows.cpp create mode 100644 lib/arch/CArchTaskBarWindows.h create mode 100644 lib/arch/CArchTaskBarXWindows.cpp create mode 100644 lib/arch/CArchTaskBarXWindows.h create mode 100644 lib/arch/CArchTimeUnix.cpp create mode 100644 lib/arch/CArchTimeUnix.h create mode 100644 lib/arch/CArchTimeWindows.cpp create mode 100644 lib/arch/CArchTimeWindows.h create mode 100644 lib/arch/CMultibyte.cpp create mode 100644 lib/arch/CMultibyteEmu.cpp create mode 100644 lib/arch/CMultibyteOS.cpp create mode 100644 lib/arch/IArchConsole.h create mode 100644 lib/arch/IArchDaemon.h create mode 100644 lib/arch/IArchFile.h create mode 100644 lib/arch/IArchLog.h create mode 100644 lib/arch/IArchMultithread.h create mode 100644 lib/arch/IArchNetwork.h create mode 100644 lib/arch/IArchSleep.h create mode 100644 lib/arch/IArchString.h create mode 100644 lib/arch/IArchTaskBar.h create mode 100644 lib/arch/IArchTaskBarReceiver.h create mode 100644 lib/arch/IArchTime.h create mode 100644 lib/arch/Makefile.am create mode 100644 lib/arch/Makefile.in create mode 100644 lib/arch/XArch.cpp create mode 100644 lib/arch/XArch.h create mode 100644 lib/arch/XArchUnix.cpp create mode 100644 lib/arch/XArchUnix.h create mode 100644 lib/arch/XArchWindows.cpp create mode 100644 lib/arch/XArchWindows.h create mode 100644 lib/arch/arch.dsp create mode 100644 lib/arch/vsnprintf.cpp create mode 100644 lib/base/CFunctionJob.cpp create mode 100644 lib/base/CFunctionJob.h create mode 100644 lib/base/CJobList.cpp create mode 100644 lib/base/CJobList.h create mode 100644 lib/base/CLog.cpp create mode 100644 lib/base/CLog.h create mode 100644 lib/base/CStopwatch.cpp create mode 100644 lib/base/CStopwatch.h create mode 100644 lib/base/CString.h create mode 100644 lib/base/CStringUtil.cpp create mode 100644 lib/base/CStringUtil.h create mode 100644 lib/base/CUnicode.cpp create mode 100644 lib/base/CUnicode.h create mode 100644 lib/base/IJob.h create mode 100644 lib/base/ILogOutputter.h create mode 100644 lib/base/LogOutputters.cpp create mode 100644 lib/base/LogOutputters.h create mode 100644 lib/base/Makefile.am create mode 100644 lib/base/Makefile.in create mode 100644 lib/base/TMethodJob.h create mode 100644 lib/base/XBase.cpp create mode 100644 lib/base/XBase.h create mode 100644 lib/base/base.dsp create mode 100644 lib/client/CClient.cpp create mode 100644 lib/client/CClient.h create mode 100644 lib/client/CServerProxy.cpp create mode 100644 lib/client/CServerProxy.h create mode 100644 lib/client/Makefile.am create mode 100644 lib/client/Makefile.in create mode 100644 lib/client/client.dsp create mode 100644 lib/common/BasicTypes.h create mode 100644 lib/common/IInterface.h create mode 100644 lib/common/Makefile.am create mode 100644 lib/common/Makefile.in create mode 100644 lib/common/Version.h create mode 100644 lib/common/common.dsp create mode 100644 lib/common/common.h create mode 100644 lib/common/stdbitset.h create mode 100644 lib/common/stddeque.h create mode 100644 lib/common/stdfstream.h create mode 100644 lib/common/stdistream.h create mode 100644 lib/common/stdlist.h create mode 100644 lib/common/stdmap.h create mode 100644 lib/common/stdostream.h create mode 100644 lib/common/stdpost.h create mode 100644 lib/common/stdpre.h create mode 100644 lib/common/stdset.h create mode 100644 lib/common/stdsstream.h create mode 100644 lib/common/stdstring.h create mode 100644 lib/common/stdvector.h create mode 100644 lib/http/CHTTPProtocol.cpp create mode 100644 lib/http/CHTTPProtocol.h create mode 100644 lib/http/Makefile.am create mode 100644 lib/http/Makefile.in create mode 100644 lib/http/XHTTP.cpp create mode 100644 lib/http/XHTTP.h create mode 100644 lib/http/http.dsp create mode 100644 lib/io/CBufferedInputStream.cpp create mode 100644 lib/io/CBufferedInputStream.h create mode 100644 lib/io/CBufferedOutputStream.cpp create mode 100644 lib/io/CBufferedOutputStream.h create mode 100644 lib/io/CInputStreamFilter.cpp create mode 100644 lib/io/CInputStreamFilter.h create mode 100644 lib/io/COutputStreamFilter.cpp create mode 100644 lib/io/COutputStreamFilter.h create mode 100644 lib/io/CStreamBuffer.cpp create mode 100644 lib/io/CStreamBuffer.h create mode 100644 lib/io/IInputStream.h create mode 100644 lib/io/IOutputStream.h create mode 100644 lib/io/IStreamFilterFactory.h create mode 100644 lib/io/Makefile.am create mode 100644 lib/io/Makefile.in create mode 100644 lib/io/XIO.cpp create mode 100644 lib/io/XIO.h create mode 100644 lib/io/io.dsp create mode 100644 lib/mt/CCondVar.cpp create mode 100644 lib/mt/CCondVar.h create mode 100644 lib/mt/CLock.cpp create mode 100644 lib/mt/CLock.h create mode 100644 lib/mt/CMutex.cpp create mode 100644 lib/mt/CMutex.h create mode 100644 lib/mt/CThread.cpp create mode 100644 lib/mt/CThread.h create mode 100644 lib/mt/CTimerThread.cpp create mode 100644 lib/mt/CTimerThread.h create mode 100644 lib/mt/Makefile.am create mode 100644 lib/mt/Makefile.in create mode 100644 lib/mt/XMT.cpp create mode 100644 lib/mt/XMT.h create mode 100644 lib/mt/XThread.h create mode 100644 lib/mt/mt.dsp create mode 100644 lib/net/CNetworkAddress.cpp create mode 100644 lib/net/CNetworkAddress.h create mode 100644 lib/net/CTCPListenSocket.cpp create mode 100644 lib/net/CTCPListenSocket.h create mode 100644 lib/net/CTCPSocket.cpp create mode 100644 lib/net/CTCPSocket.h create mode 100644 lib/net/CTCPSocketFactory.cpp create mode 100644 lib/net/CTCPSocketFactory.h create mode 100644 lib/net/IDataSocket.h create mode 100644 lib/net/IListenSocket.h create mode 100644 lib/net/ISocket.h create mode 100644 lib/net/ISocketFactory.h create mode 100644 lib/net/Makefile.am create mode 100644 lib/net/Makefile.in create mode 100644 lib/net/XSocket.cpp create mode 100644 lib/net/XSocket.h create mode 100644 lib/net/net.dsp create mode 100644 lib/platform/CMSWindowsClipboard.cpp create mode 100644 lib/platform/CMSWindowsClipboard.h create mode 100644 lib/platform/CMSWindowsClipboardAnyTextConverter.cpp create mode 100644 lib/platform/CMSWindowsClipboardAnyTextConverter.h create mode 100644 lib/platform/CMSWindowsClipboardTextConverter.cpp create mode 100644 lib/platform/CMSWindowsClipboardTextConverter.h create mode 100644 lib/platform/CMSWindowsClipboardUTF16Converter.cpp create mode 100644 lib/platform/CMSWindowsClipboardUTF16Converter.h create mode 100644 lib/platform/CMSWindowsPrimaryScreen.cpp create mode 100644 lib/platform/CMSWindowsPrimaryScreen.h create mode 100644 lib/platform/CMSWindowsScreen.cpp create mode 100644 lib/platform/CMSWindowsScreen.h create mode 100644 lib/platform/CMSWindowsScreenSaver.cpp create mode 100644 lib/platform/CMSWindowsScreenSaver.h create mode 100644 lib/platform/CMSWindowsSecondaryScreen.cpp create mode 100644 lib/platform/CMSWindowsSecondaryScreen.h create mode 100644 lib/platform/CSynergyHook.cpp create mode 100644 lib/platform/CSynergyHook.h create mode 100644 lib/platform/CXWindowsClipboard.cpp create mode 100644 lib/platform/CXWindowsClipboard.h create mode 100644 lib/platform/CXWindowsClipboardTextConverter.cpp create mode 100644 lib/platform/CXWindowsClipboardTextConverter.h create mode 100644 lib/platform/CXWindowsClipboardUCS2Converter.cpp create mode 100644 lib/platform/CXWindowsClipboardUCS2Converter.h create mode 100644 lib/platform/CXWindowsClipboardUTF8Converter.cpp create mode 100644 lib/platform/CXWindowsClipboardUTF8Converter.h create mode 100644 lib/platform/CXWindowsPrimaryScreen.cpp create mode 100644 lib/platform/CXWindowsPrimaryScreen.h create mode 100644 lib/platform/CXWindowsScreen.cpp create mode 100644 lib/platform/CXWindowsScreen.h create mode 100644 lib/platform/CXWindowsScreenSaver.cpp create mode 100644 lib/platform/CXWindowsScreenSaver.h create mode 100644 lib/platform/CXWindowsSecondaryScreen.cpp create mode 100644 lib/platform/CXWindowsSecondaryScreen.h create mode 100644 lib/platform/CXWindowsUtil.cpp create mode 100644 lib/platform/CXWindowsUtil.h create mode 100644 lib/platform/IMSWindowsScreenEventHandler.h create mode 100644 lib/platform/Makefile.am create mode 100644 lib/platform/Makefile.in create mode 100644 lib/platform/makehook.dsp create mode 100644 lib/platform/platform.dsp create mode 100644 lib/platform/synrgyhk.dsp create mode 100644 lib/server/CClientProxy.cpp create mode 100644 lib/server/CClientProxy.h create mode 100644 lib/server/CClientProxy1_0.cpp create mode 100644 lib/server/CClientProxy1_0.h create mode 100644 lib/server/CClientProxy1_1.cpp create mode 100644 lib/server/CClientProxy1_1.h create mode 100644 lib/server/CConfig.cpp create mode 100644 lib/server/CConfig.h create mode 100644 lib/server/CHTTPServer.cpp create mode 100644 lib/server/CHTTPServer.h create mode 100644 lib/server/CPrimaryClient.cpp create mode 100644 lib/server/CPrimaryClient.h create mode 100644 lib/server/CServer.cpp create mode 100644 lib/server/CServer.h create mode 100644 lib/server/Makefile.am create mode 100644 lib/server/Makefile.in create mode 100644 lib/server/server.dsp create mode 100644 lib/synergy/CClipboard.cpp create mode 100644 lib/synergy/CClipboard.h create mode 100644 lib/synergy/CInputPacketStream.cpp create mode 100644 lib/synergy/CInputPacketStream.h create mode 100644 lib/synergy/COutputPacketStream.cpp create mode 100644 lib/synergy/COutputPacketStream.h create mode 100644 lib/synergy/CPrimaryScreen.cpp create mode 100644 lib/synergy/CPrimaryScreen.h create mode 100644 lib/synergy/CProtocolUtil.cpp create mode 100644 lib/synergy/CProtocolUtil.h create mode 100644 lib/synergy/CSecondaryScreen.cpp create mode 100644 lib/synergy/CSecondaryScreen.h create mode 100644 lib/synergy/ClipboardTypes.h create mode 100644 lib/synergy/IClient.h create mode 100644 lib/synergy/IClipboard.h create mode 100644 lib/synergy/IPrimaryScreenFactory.h create mode 100644 lib/synergy/IPrimaryScreenReceiver.h create mode 100644 lib/synergy/IScreen.h create mode 100644 lib/synergy/IScreenEventHandler.h create mode 100644 lib/synergy/IScreenReceiver.h create mode 100644 lib/synergy/IScreenSaver.h create mode 100644 lib/synergy/ISecondaryScreenFactory.h create mode 100644 lib/synergy/IServer.h create mode 100644 lib/synergy/KeyTypes.h create mode 100644 lib/synergy/Makefile.am create mode 100644 lib/synergy/Makefile.in create mode 100644 lib/synergy/MouseTypes.h create mode 100644 lib/synergy/OptionTypes.h create mode 100644 lib/synergy/ProtocolTypes.h create mode 100644 lib/synergy/XScreen.cpp create mode 100644 lib/synergy/XScreen.h create mode 100644 lib/synergy/XSynergy.cpp create mode 100644 lib/synergy/XSynergy.h create mode 100644 lib/synergy/libsynergy.dsp create mode 100644 stamp-h.in create mode 100644 synergy.dsw diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..a7ebc10 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ +Synergy Authors +=============== + +Chris Schoeneman diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..7cbc437 --- /dev/null +++ b/BUGS @@ -0,0 +1,12 @@ +Known Bugs in Synergy +===================== + +View known bugs at: +http://sourceforge.net/tracker/?group_id=59275&atid=490467 + +Report bugs at: +http://sourceforge.net/tracker/?group_id=59275&atid=490467 + +When reporting bugs, please include the version of the operating +system you're using (on the server and relevant clients) and what +locales you use. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..895e589 --- /dev/null +++ b/COPYING @@ -0,0 +1,283 @@ +Synergy is copyright (C) 2002 Chris Schoeneman. +Synergy is distributed under the following license. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..38bd186 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,7045 @@ +2003/08/24 14:52:02 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.14. + +---------- +2003/08/23 11:41:03 crs +lib/platform/CXWindowsClipboard.cpp + +Removed hack to work around apparent lesstif bug. I'm not seeing +the bug manifest itself and the hack breaks pasting into lesstif +programs that don't have the bug. Added DEBUG2 level logging of +the requestor window's properties in case some users run into +problems. + +---------- +2003/08/06 21:00:57 crs +lib/platform/CMSWindowsScreen.cpp + +Added more debugging log messages to clipboard handling. + +---------- +2003/08/06 21:00:17 crs +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsClipboard.h + +Fixed failure to treat log copied to clipboard as subject to +transfer to clients/server. This prevented copying the log +to the clipboard then pasting it on another screen. + +---------- +2003/08/06 20:59:01 crs +cmd/launcher/CAdvancedOptions.cpp +cmd/launcher/CAdvancedOptions.h + +Fixed failure to get non-default settings from advanced options +dialog in launcher when the dialog hadn't been opened. + +---------- +2003/08/06 20:57:41 crs +lib/arch/CArchLogWindows.cpp + +Fixed memory leak in win32 system log open() call. + +---------- +2003/08/06 20:56:52 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.13. + +---------- +2003/08/03 21:23:29 crs +lib/arch/CArchMultithreadWindows.cpp + +Fixed bugs in setting thread priority. + +---------- +2003/07/19 22:07:13 crs +configure.in +Version.h + +Changed version to 1.0.12. + +---------- +2003/07/19 17:22:06 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/synergyc/synergyc.cpp +configure.in +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/common/Version.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CConfig.cpp +lib/synergy/OptionTypes.h + +Merge synergy 1.1 fixes into 1.0 branch. + +---------- +2003/07/19 17:19:26 crs + +Branched synergy 1.0 from 1.0.11. + +---------- +2003/07/17 21:16:58 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +Fixed handling of a dead key followed by space on win32 and X11. +A dead key followed by space should convert the dead key to a +regular character. + +---------- +2003/07/16 22:38:43 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Fixed handling of some non-ASCII but directly mapped characters +on win32. The o, a, and u with diaeresis in the german keyboard +mapping are examples. + +---------- +2003/07/16 21:40:57 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Fixed handling of shift/ctrl/alt on special keys on win32 server. + +---------- +2003/07/13 20:42:11 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Fixed handling of some keystrokes on win32. Pressing a dead key +and then space should convert the dead key to a non-dead key but +previous the key was discarded. Fixed that but VkKeyScan() fails +in this case so added special case to fix that (assuming AltGr is +required). VkKeyScan() can return the wrong result for characters +that have more than one virtual key mapped to them. AltGr+9 (^) +on the French layout has this problem. Now detecting that problem +and using the current keyboard state to decide if AltGr is +required. + +---------- +2003/07/13 17:03:41 crs +cmd/synergyc/synergyc.cpp + +Forgot to remove --camp and --no-camp from brief usage message. + +---------- +2003/07/13 16:57:08 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Changed XSync() to XFlush() in X windows secondary screen. This +doesn't appear to have any negative consequences and may prevent +synergy from freezing when some X client (probably the window +manager) grabs the server. + +---------- +2003/07/12 17:57:31 crs +cmd/synergyc/synergyc.cpp +lib/client/CClient.cpp +lib/client/CClient.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CXWindowsScreen.cpp + +Prevent INFO level log messages when client is repeatedly trying +to connect. This prevents a log from filling up while the client +can't connect for no useful reason. Also removed --camp option +and cleaned up handling of client connection. Users must now use +--restart instead of --camp. + +---------- +2003/07/08 18:40:46 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h + +Changed windows server to release ctrl and alt keys when it's +sending a key that requires AltGr. That's because AltGr *is* +ctrl and alt but AltGr should be seen on clients as mode +switch without the ctrl and alt. I can't think of a better +way to do this other than to not send modifier keystrokes to +the clients at all. + +---------- +2003/07/05 17:06:18 crs +configure.in +lib/common/Version.h + +Change version to 1.0.11. Skipping version 1.0.10 because there +have been too many major changes since 1.0.8. A new experimental +release will provide a stable starting point for testing. + +---------- +2003/07/05 17:05:12 crs +lib/synergy/CSecondaryScreen.cpp + +Fix to avoid warping mouse until client successfully connects to +the server. + +---------- +2003/07/05 17:04:26 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h + +Keyboard fixes on win32. + +---------- +2003/07/05 17:04:06 crs +lib/server/CConfig.h + +Fix for new template syntax. + +---------- +2003/07/05 14:49:08 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp + +Minor X11 keyboard code cleanup. Also now handling KeyPress with +keycode == 0 generated by XFilterEvent() by using the keycode from +the previous KeyPress. + +---------- +2003/07/05 14:47:41 crs +lib/platform/CXWindowsScreen.cpp + +Compress sequential MappingNotify events into one. + +---------- +2003/07/01 19:35:28 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Rewrote key handling on X11 client. This should fix problems +with applying the incorrect shift and mode switch modifiers to +some keycodes, such as getting Pointer_EnableKeys when pressing +shift with NumLock enabled. + +---------- +2003/06/22 21:27:38 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h + +Added support for input methods. Only handling IMs that don't +need a precompose area or status area. This includes IMs that +do simple dead key composition. This only changes the server. +The client still does not decompose a character it cannot +generate directly into the keysyms to compose the character. + +---------- +2003/06/22 16:39:25 crs +lib/client/CServerProxy.cpp + +More fixes for X11 client keyboard handling. + +---------- +2003/06/22 16:39:02 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +More fixes for X11 client keyboard handling. + +---------- +2003/06/22 15:01:44 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Checkpoint for improving X11 client key handling. Should prevent +unintentional Pointer_EnableKeys (i.e. generating NumLock press +and release around a shift press). + +---------- +2003/06/08 22:20:01 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +Another ctrl+alt+del checkpoint. + +---------- +2003/06/08 22:12:12 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +ctrl+alt+del emulation checkpoint. + +---------- +2003/06/08 16:31:52 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +More DEBUG2 level debugging of keyboard handling. + +---------- +2003/06/08 15:42:05 crs +lib/common/Makefile.am + +Added new file to Makefile. + +---------- +2003/06/02 20:07:16 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Fixed ctrl and alt keys on win32 clients. Was broken by a recent +fix to character handling. + +---------- +2003/06/02 20:06:20 crs +lib/client/CServerProxy.cpp + +Fixed errors in log strings. + +---------- +2003/06/02 20:06:03 crs +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CMSWindowsClientTaskBarReceiver.h +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/CMSWindowsServerTaskBarReceiver.h +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.rc +lib/base/CLog.cpp +lib/base/CLog.h +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h +lib/common/common.dsp +lib/common/stddeque.h + +Added menu item on win32 tray icon to copy the last 1000 lines from +the log to the clipboard. + +---------- +2003/05/26 09:50:35 crs +lib/platform/CXWindowsClipboard.cpp + +Added workaround for broken clipboard owners that report the +type of TARGETS as TARGETS instead of ATOM. + +---------- +2003/05/26 09:49:38 crs +lib/platform/CMSWindowsClipboard.cpp + +No longer installing clibboard format for plain text on windows nt +family because nt automatically converts to and from the unicode +format. This may fix text encoding errors when synergy puts +non-ascii text on the clipboard and other clients prefer CF_TEXT +to CF_UNICODE (which they should not because synergy lists +CF_UNICODE first). + +---------- +2003/05/26 09:46:52 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsScreen.cpp + +Fixed loss of ctrl+alt+del key releases when the Winlogin desktop +is accessible (was already fixed when inaccessible). This change +also ignores press and release of virtual key 0, which should never +happen but does according to one user. + +---------- +2003/05/21 21:22:14 crs +lib/arch/CArchMultithreadWindows.cpp +lib/synergy/CPrimaryScreen.cpp +lib/synergy/CSecondaryScreen.cpp + +Fixed unsigned compare against zero. Changed win32 priority to +maximum. + +---------- +2003/05/21 19:38:11 crs +lib/server/CServer.cpp +lib/server/CServer.h + +Made double tap require moving farther away from the tapped edge +before arming. This should reduce spurious double taps. + +---------- +2003/05/20 19:15:58 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h + +Attempt to improve key event synthesis. This change adds support +for dead keys and attempts to choose the correct code page for the +thread that will (probably) receive synthesized events. + +---------- +2003/05/20 19:14:40 crs +lib/platform/CSynergyHook.cpp + +Minor thread ID compare fix. + +---------- +2003/05/20 19:14:24 crs +lib/arch/CArchMultithreadWindows.cpp + +Reduced maximum priority in debug build. + +---------- +2003/05/17 20:58:27 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/IMSWindowsScreenEventHandler.h +lib/synergy/CPrimaryScreen.cpp + +Fixed getting locked to screen after ctrl+alt+del. Also fixed +cursor not being hidden on win32 server when on client screens +(which happened when using low-level hooks). + +---------- +2003/05/17 14:10:11 crs +INSTALL + +Added documentation for xtestIsXineramaUnaware option. + +---------- +2003/05/17 14:03:32 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Fixed previous fix. Was trying to avoid using XWarpPointer() when +warping on screen 0. That just doesn't work if screen 0 is not at +0,0. So now always use XWarpPointer() if there are multiple +xinerama screens and the appropriate option is enabled. + +---------- +2003/05/17 13:44:24 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CConfig.cpp +lib/synergy/OptionTypes.h + +Added workaround for when XTest is unaware of Xinerama. When that's +true, faking a mouse motion outside screen 0 is clamped onto screen 0. +When the workaround is enabled, we use XWarpPointer() instead of an +XTest fake motion. This isn't perfect but the only real fix requires +patching XTest. + +---------- +2003/05/17 12:48:32 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +Added support for old versions of XF86keysym.h that are missing +some expected #defines. + +---------- +2003/05/10 17:27:05 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.8. + +---------- +2003/05/10 17:26:42 crs +INSTALL +README + +Updated documentation. + +---------- +2003/05/08 21:59:35 crs +lib/server/CServer.cpp + +Fixed jumping to same client screen. It was broken by an earlier +change (probably double tap). Jumping to the same server screen +worked correctly. + +---------- +2003/05/04 21:40:42 crs +configure.in +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/synergy/KeyTypes.h +lib/synergy/MouseTypes.h + +Added support for 4th and 5th (non-mouse-wheel) buttons and +"Internet" keyboard keys. + +---------- +2003/05/03 15:16:30 crs +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added screen saver synchronization option to win32 launcher dialog. + +---------- +2003/05/03 14:54:03 crs +lib/synergy/CSecondaryScreen.cpp + +Removed accidental debugging code. + +---------- +2003/05/03 14:38:36 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/server/CConfig.cpp +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h +lib/synergy/OptionTypes.h + +Added global configuration option to disable screen saver +synchronization. + +---------- +2003/05/03 13:57:52 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Forgot to restore global auto-repeat configuration on exit. + +---------- +2003/05/03 13:50:06 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h + +Now warping mouse to center of screen when leaving client screens. +Some users requested this. Also, the hider window is mapped before +warping the mouse so the active window shouldn't change if the focus +policy is point-to-focus. Showing the window first can also reduce +the likelihood of seeing the cursor briefly in its hidden position. + +---------- +2003/05/03 13:28:21 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Now turning off auto-repeat when on an X11 client. This prevents +the server from auto-repeating fake events, which is undesired +since synergy will do the auto-repeating itself. This also +disables auto-repeat on any keys locally configured on X11 to not +auto-repeat. That's mainly to suppress auto-repeat on modifier +keys, which auto-repeat on win32 but not X11. + +---------- +2003/05/03 12:54:22 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CSynergyHook.h + +Fixed a few win32 keyboard/mouse problems. First, the mouse hook +now captures non-client area mouse messages. Previously, these +were ignored (because i forgot about them) and they caused all +kinds of problems because they weren't forwarded. For example, +clicking on a window border would cause the window to start +resizing when the mouse came back to the server screen. Moving +inside a title bar meant that the mouse wouldn't move on the +client screen. + +Second, because non-client messages are now handled, the full +screen transparent window is no longer necessary to capture +input so it's never displayed. (The window is still necessary +for clipboard ownership so it's still created.) No transparent +window means no screen flashing. It also means we don't have to +become the foreground and active window. This plays better with +apps that minimize or restore when they're no longer the +foreground application/active window. + +Third, fixed the low level keyboard hook to forward toggle key +updates, which it was neglecting to do. + +Finally, keyboard and mouse input is always forwarded from the hook +to the primary screen handler which then shadows the current key +and mouse button state. If we're using low level hooks then this +isn't really necessary and GetKeyState() always returns the right +info but without low level hooks it means we can just use the +shadow state. It also means we don't have to show our window in +order to get the system's key state table up to date, fixing the +screen flash when checking for the scroll lock state. + +---------- +2003/05/03 12:37:03 crs +lib/arch/CArchMultithreadWindows.cpp +lib/synergy/CPrimaryScreen.cpp +lib/synergy/CSecondaryScreen.cpp + +Boosted priority of main synergy threads to be very high (highest +realtime priority). After some testing it appears that anything +less than this can starve synergy in some circumstances, preventing +it from forwarding messages to clients. This is a rather risky +change since synergy can now virtually take over a system if it +behaves badly. This change only affects windows systems since +lib/arch of other platforms don't yet attempt to boost priority. + +---------- +2003/04/27 18:05:32 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/server/Makefile.am +lib/server/server.dsp + +Fixes to previous checkpoint. Non-ascii keys seem to work correctly. +Still not supporting key composition on X11. + +---------- +2003/04/27 17:01:14 crs +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxy1_1.cpp +lib/server/CClientProxy1_1.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/Makefile.am +lib/synergy/CSecondaryScreen.h +lib/synergy/IClient.h +lib/synergy/IPrimaryScreenReceiver.h +lib/synergy/KeyTypes.h +lib/synergy/ProtocolTypes.h + +Checkpointing improved key handling. This change adds non-ASCII +key handling to win32 on both client and server. It also changes +the protocol and adds code to ensure every key pressed also gets +released and that that doesn't get confused when the KeyID for +the press is different from the KeyID of the release (or repeat). + +---------- +2003/04/24 20:11:38 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h + +Added KeySym <-> Unicode mappings. Changed code to use those +mappings to better support Unicode key events. + +---------- +2003/04/24 20:10:13 crs +cmd/Makefile.am + +Added exec.dsp to EXTRA_DIST. + +---------- +2003/04/16 20:59:25 crs +all.dsp +cmd/exec.dsp +lib/platform/makehook.dsp +lib/platform/synrgyhk.dsp +synergy.dsw + +Win32 project configuration fixes. + +---------- +2003/04/16 20:59:14 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/platform/CMSWindowsPrimaryScreen.cpp + +Minor win32 fixes. + +---------- +2003/04/16 20:05:00 crs +lib/server/CConfig.cpp + +Now allowing screen names with underscores. + +---------- +2003/04/14 22:16:21 crs +lib/platform/CXWindowsPrimaryScreen.cpp + +Fixed incorrect initialization of an XMotionEvent. + +---------- +2003/04/14 22:15:56 crs +configure.in +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h + +Added workaround for apparent Xinerama bug when warping the pointer. +This should allow synergy to be used on a system using Xinerama to +create a single logical screen from multiple physical screens. + +---------- +2003/04/13 18:14:01 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Fixed problem with type casting void* to int. + +---------- +2003/04/13 17:13:27 crs +lib/platform/CXWindowsScreenSaver.cpp + +Removed periodic call to XForceScreenSaver() to prevent the built-in +screen saver from activating. It was unnecessary since the built-in +screen saver is disabled as appropriate; this call was just to +ensure that the screen saver wouldn't start if an external program +reactivated the screen saver after synergy disabled it. + +It's possible that this was causing screen flicker under gnome, though +i don't know why. It's also possible that periodically sending events +to xscreensaver is causing the flicker but removing that code is more +difficult because xscreensaver can't be disabled, only deactivated or +killed. + +---------- +2003/04/13 14:59:53 crs +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsClipboard.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CSynergyHook.cpp + +Fixed several win32 bugs. First, synergy wasn't forwarding mouse +events to other hook functions, which broke some tools like objectbar. +Second, windows key processing was fixed. Previously pressing and +release the key would only send a press event, locking the user onto +the client window; also, the win32 server treated as a Meta modifier +instead of a Super modifier, which broke any use of it as any kind of +modifier key. Third, added hacks to support several key combinations +on windows 95/98/me that are treated specially by windows, including +Alt+Tab, Alt+Shift+Tab, Alt+Esc, Alt+Shift+Esc, Ctrl+Esc, and any +combination using the windows key like Win+E and Win+F but not +Ctrl+Alt+Del. Fourth, scroll lock only locking to the client (which +only happened when using a synergy server on windows) has been fixed; +unfortunately the solution causes a lot of screen redraws for some +reason. Finally, there's been a fix to clipboard handling that may +or may not fix a problem where the clipboard would stop transferring +between systems after a little while. I can't be sure if it fixes +the problem because I can't reproduce the problem. + +---------- +2003/04/13 14:39:17 crs +cmd/launcher/launcher.rc + +Added mention of tray icon to launcher start message box. + +---------- +2003/03/26 21:03:58 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.6. + +---------- +2003/03/25 21:31:39 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CSynergyHook.cpp + +This should fix multimon support on win32. + +---------- +2003/03/22 11:49:23 crs +FAQ +INSTALL +PORTING +README +TODO + +Documentation updates. + +---------- +2003/03/22 11:49:13 crs +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/CGlobalOptions.h +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added key modifier and heartbeat options to GUI. + +---------- +2003/03/21 19:34:08 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Oops, included a windows only header in non-windows builds. + +---------- +2003/03/21 19:16:37 crs +lib/platform/CMSWindowsScreenSaver.cpp + +Added check for the screen saver actually being active before +entering the loop waiting for it to deactivate. The failure +to check was causing the screen saver code to kick in when +the screen saver timeout occurred, even if the screen saver +wasn't enabled (because Windows still sends the screen saver +activating message for no good reason when the screen saver +is disabled). + +---------- +2003/03/21 19:14:32 crs +lib/arch/CArchMiscWindows.cpp + +Fixed errors in merge causing infinite loops. + +---------- +2003/03/21 19:14:10 crs +cmd/synergyc/tb_idle.ico +cmd/synergyc/tb_run.ico +cmd/synergyc/tb_wait.ico +cmd/synergys/tb_idle.ico +cmd/synergys/tb_run.ico +cmd/synergys/tb_wait.ico + +Fixed icons. + +---------- +2003/03/21 19:13:15 crs +lib/platform/CXWindowsUtil.cpp + +Fixed getWindowProperty(). It wasn't catching all failure +cases correctly. + +---------- +2003/03/17 22:32:10 crs +cmd/launcher/CAdvancedOptions.cpp +cmd/launcher/CAdvancedOptions.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchDaemonWindows.h +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h + +Added options and advanced options dialogs which should've been +part of an earlier checkin. Also now saving and restoring +options that aren't in the configuration file to/from the +registry. + +---------- +2003/03/17 22:32:01 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.cpp +lib/server/CServer.cpp +lib/synergy/CPrimaryScreen.h + +Added a log message why the user is locked to the screen. + +---------- +2003/03/17 22:31:59 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Added type casts to avoid warning. + +---------- +2003/03/16 17:40:57 crs +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h + +Fixed detection of screen saver shutdown on windows nt. + +---------- +2003/03/16 17:40:56 crs +lib/common/Makefile.am +lib/common/common.dsp +lib/common/stdbitset.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Made releaseKeys() only synthesize key releases for those keys +that synergy synthesized a press for, not keys that the user +is physically pressing. + +---------- +2003/03/16 17:40:47 crs +lib/platform/CSynergyHook.cpp + +Minor hook fixes. + +---------- +2003/03/16 17:40:25 crs +cmd/synergyc/synergyc.rc +cmd/synergys/synergys.rc + +Added resources missing from previous checkin. + +---------- +2003/03/13 20:24:45 crs +lib/platform/CXWindowsScreenSaver.cpp + +Moved comment to more relevant location. + +---------- +2003/03/13 19:20:55 crs +lib/platform/CXWindowsScreen.cpp + +Fixed double locking of mutex. + +---------- +2003/03/12 22:34:07 crs +cmd/launcher/CAdvancedOptions.cpp +cmd/launcher/CAdvancedOptions.h +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/CGlobalOptions.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h +cmd/launcher/Makefile.am +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/resource.h +cmd/synergyc/CClientTaskBarReceiver.cpp +cmd/synergyc/CClientTaskBarReceiver.h +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CMSWindowsClientTaskBarReceiver.h +cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CXWindowsClientTaskBarReceiver.h +cmd/synergyc/Makefile.am +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.dsp +cmd/synergyc/tb_error.ico +cmd/synergyc/tb_idle.ico +cmd/synergyc/tb_run.ico +cmd/synergyc/tb_wait.ico +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/CMSWindowsServerTaskBarReceiver.h +cmd/synergys/CServerTaskBarReceiver.cpp +cmd/synergys/CServerTaskBarReceiver.h +cmd/synergys/CXWindowsServerTaskBarReceiver.cpp +cmd/synergys/CXWindowsServerTaskBarReceiver.h +cmd/synergys/Makefile.am +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.dsp +cmd/synergys/tb_error.ico +cmd/synergys/tb_idle.ico +cmd/synergys/tb_run.ico +cmd/synergys/tb_wait.ico +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleWindows.cpp +lib/arch/CArchConsoleWindows.h +lib/arch/CArchImpl.cpp +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h +lib/arch/CArchTaskBarWindows.cpp +lib/arch/CArchTaskBarWindows.h +lib/arch/CArchTaskBarXWindows.cpp +lib/arch/CArchTaskBarXWindows.h +lib/arch/IArchMultithread.h +lib/arch/IArchTaskBar.h +lib/arch/IArchTaskBarReceiver.h +lib/arch/Makefile.am +lib/arch/arch.dsp +lib/base/CJobList.cpp +lib/base/CJobList.h +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h +lib/base/Makefile.am +lib/base/base.dsp +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/mt/CThread.cpp +lib/mt/CThread.h +lib/mt/CTimerThread.cpp +lib/mt/CTimerThread.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h + +Added switch delay and double-tap options to win32 and added a +tray icon to the client and server that gives status feedback to +the user and allows the user to kill the app. + +---------- +2003/02/23 19:29:08 crs +lib/server/CConfig.cpp +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/OptionTypes.h +lib/synergy/ProtocolTypes.h + +Added support for a user option to require hitting the edge of a +screen twice within a specified amount of time in order to switch +screens. This can help prevent unintended switching. + +---------- +2003/02/22 21:53:25 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CConfig.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/CPrimaryScreen.h +lib/synergy/IPrimaryScreenReceiver.h +lib/synergy/IScreenEventHandler.h +lib/synergy/OptionTypes.h + +Added support on X11 for a global option to delay switching screens +when the mouse reaches a jump zone. + +---------- +2003/02/22 16:41:03 crs +lib/server/CConfig.cpp +lib/server/CConfig.h + +Added global options to CConfig (needed for heartbeat option). + +---------- +2003/02/22 16:20:23 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/server/CClientProxy.cpp +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CServer.cpp +lib/synergy/OptionTypes.h +lib/synergy/ProtocolTypes.h + +Added support for heartbeat global option. + +---------- +2003/02/22 15:04:09 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.5. + +---------- +2003/02/22 15:03:31 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/synergy/KeyTypes.h +lib/synergy/OptionTypes.h + +Changes to support remapping modifier keys on clients. + +---------- +2003/02/17 12:44:37 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.3. + +---------- +2003/02/16 19:55:54 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Changed win32 client side cursor warping to be all relative motion +when not on the primary monitor. This should eliminate the flicker +between virtual display 0,0 and the correct position. While this +allows the user to confuse synergy by using the client's mouse, +synergy recovers quickly and easily from any confusion. + +---------- +2003/02/16 19:53:56 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Added hack to heuristically detect bogus mouse motion caused by +a race condition where the synergy server updates the mouse +position but the synergy hook later receives a mouse update from +before the position change (i.e. out of order). + +---------- +2003/02/16 19:51:46 crs +lib/platform/CSynergyHook.cpp + +Commented out an unnecessary hook and added a compile time +switch to disable grabbing of keyboard on win32 to facilitate +debugging. + +---------- +2003/02/16 19:50:36 crs +lib/platform/CMSWindowsScreen.cpp + +Changed heap to stack allocation in an oft-called function for +data that's never used outside the function. + +---------- +2003/02/16 19:49:44 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/arch/CArchMultithreadWindows.cpp +lib/base/CUnicode.cpp + +Fixed memory leaks. + +---------- +2003/02/12 20:59:25 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +Fixed incorrect mouse button swapping on client screens. + +---------- +2003/02/12 20:59:08 crs +lib/arch/CArchDaemonWindows.cpp + +Fixed error in debug build on win32. + +---------- +2003/02/12 19:50:22 crs +lib/arch/vsnprintf.cpp + +Added a simple implementation of vsnprintf for unix platforms +without it using /dev/null, vfprintf(), and vsprintf(). + +---------- +2003/02/12 19:38:39 crs +lib/arch/CArchMiscWindows.h +lib/arch/XArch.h +lib/base/CLog.h +lib/base/CString.h +lib/base/ILogOutputter.h +lib/mt/CCondVar.h +lib/mt/CLock.h +lib/mt/CThread.h +lib/mt/CTimerThread.h + +Made sure every file includes common.h directly or indirectly. +Also made sure common.h is included before any system headers. + +---------- +2003/02/01 18:10:43 crs +FAQ +INSTALL +README +TODO + +Added info about using SSH for authentication and encryption. + +---------- +2003/01/29 22:16:40 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +To support keymaps with only upper (or lower) case keysyms we now +use Xlib to convert an unmatched keysym to upper and lower case and +use whichever, if any, is not the same as the original keysym. +This supports case conversion in any language that Xlib supports +it in. + +---------- +2003/01/29 19:32:25 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +Applied patch from grmcdorman at users dot sourceforge dot net to +support keymaps that have only uppercase letters, which is the case +by default on the Sun X server (for US keyboards anyway). + +---------- +2003/01/25 13:39:26 crs +NEWS +configure.in +lib/common/Version.h + +Changed version number to 1.0.2. + +---------- +2003/01/25 13:34:51 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +lib/server/CConfig.cpp +lib/server/CConfig.h + +Added ability to set screen options from the windows launch dialog. + +---------- +2003/01/25 13:34:17 crs +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkWinsock.cpp + +Added missing entry in a socket family table. This was a serious +bug and should've failed on all platforms but just happened to +work on linux and windows. + +---------- +2003/01/22 08:37:32 crs +NEWS +configure.in +lib/common/Version.h + +Changed version number to 1.0.1. + +---------- +2003/01/22 08:36:43 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchDaemonWindows.h +lib/arch/CArchLogWindows.cpp +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h + +Fixed running as a service on Windows NT family. + +---------- +2003/01/18 14:36:19 crs +lib/arch/CArchSleepWindows.cpp +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CSynergyHook.cpp + +Fixed stupid errors introduced by last attempt to fix broken +mouse behavior on multimonitor windows systems. Those errors +broke synergy on all windows systems running as a server. +Also added an attempt to reduce the occasional jump that can +occur when switching screens when windows is the server. + +---------- +2003/01/18 14:31:54 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +Was forcing modifier keys that have no effect on the keysym +lookup to be up when synthesizing key events. Now leaving +those modifiers in their current state. + +---------- +2003/01/18 10:49:13 crs +Makefile.am + +Added a dist-pkg target to put the binary distribution files into +a tar gzip file. This is to ease distribution of the binaries on +systems without a packaging system supported by synergy (which +currently supports only RPM). + +---------- +2003/01/16 21:28:15 crs +lib/server/CServer.cpp + +Fixed lookup of neighbor screens. The first problem was an old +code in a conditional for moving left that blew an assert verifying +that the mouse position was really on the screen if the neighbor +screen wasn't connected. + +After that was fixed there was another problem when one screen +linked to another which then linked (in the same direction) to +itself. If the latter screen wasn't connected then it'd get into +an infinite loop. + +---------- +2003/01/14 19:46:41 crs +lib/server/CServer.cpp + +Moved log message into conditionals so it only appears when the +conditions are true. + +---------- +2003/01/14 19:46:17 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CSynergyHook.cpp + +Another try at fixing broken mouse behavior when a windows system +has multiple monitors with 0,0 of the virtual desktop not at the +upper-left. + +---------- +2003/01/12 16:35:54 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added test of using the client's own name as the server name +with an appropriate error message. + +---------- +2003/01/12 16:08:45 crs +lib/server/CServer.cpp + +Now catching and ignoring errors when writing to a socket in those +cases where errors were not being caught, typically when responding +to some other socket or protocol error. + +---------- +2003/01/11 21:06:21 crs +acinclude.m4 +configure.in +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/arch/CArchSleepUnix.cpp +lib/arch/CMultibyteEmu.cpp +lib/common/Makefile.am + +Fixes to support FreeBSD and Darwin. + +---------- +2003/01/11 15:16:41 crs +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h + +Synergy no longer tries to suppress the screen saver once it starts. +It was doing that already if started through synergy but not if +started by something outside of synergy. In particular, if you +use `xscreensaver-command --activate' synergy used to send fake +mouse motion events every 5 seconds to deactivate it. That's +unlikely to be what the user wanted, especially if the locking is +enabled since it would force the password dialog to appear. + +As before, it's recommended that client screens not use locking +because xscreensaver will not deactivate without getting a +password even if we make the request through a programmatic +interface. Presumably that's for security reasons but it makes +life harder for synergy. + +---------- +2003/01/11 14:01:44 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp + +Attempt to fix problems with multimon windows. The mouse position +reported by the synergy hook dll is in a space with 0,0 in the +upper-left which is not necessarily the same as the virtual desktop +space. So the windows primary screen now accounts for that. On +the secondary screen, mouse_event() doesn't seem to accept negative +coordinates even on the windows NT family, making monitors with +negative coordinates inaccessible via absolute moves. So if the +move will be to negative coordinates, use the windows 95 family +fallback of absolute moving to 0,0 then relative moving to the +final position. + +---------- +2003/01/08 22:17:44 crs +FAQ +INSTALL + +Added bit about configuring on Solaris, which requires some +options to find the X11 includes and libraries. + +---------- +2003/01/08 21:36:14 crs +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchNetworkBSD.cpp + +Portability fixes. Now builds on Linux 2.2 and 2.4 and solaris. +Also builds on i386, alpha, G3/G4, and sparc. + +---------- +2003/01/08 21:36:13 crs +lib/client/CClient.cpp +lib/platform/CXWindowsUtil.cpp + +Changed log level of two messages. Now won't spew about reading +window properties and will report connection failure at DEBUG +instead of DEBUG1. + +---------- +2003/01/08 21:36:10 crs +FAQ + +Added a FAQ entry for client being rejected. User probably didn't +start the server or told the client the wrong server host name. + +---------- +2003/01/07 21:47:27 crs +ChangeLog +configure.in +lib/common/Version.h + +Changed version number to 0.9.15. Added 0.9.15 log entries. + +---------- +2003/01/07 21:12:51 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Attempts to improve forcing synergy window to foreground. These +changes don't seem to improve the situation but don't seem to +hurt either. + +---------- +2003/01/07 21:11:54 crs +lib/platform/CSynergyHook.cpp + +Added low-level mouse hook to support mouse wheel on NT (>=SP3). +Thanks to karsten for the patch used as a starting point. + +---------- +2003/01/05 21:52:28 crs +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h + +Added missing files. + +---------- +2003/01/05 21:48:54 crs +PORTING +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +doc/doxygen.cfg.in +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleUnix.h +lib/arch/CArchConsoleWindows.h +lib/arch/CArchDaemonNone.h +lib/arch/CArchDaemonUnix.h +lib/arch/CArchDaemonWindows.h +lib/arch/CArchFileUnix.h +lib/arch/CArchFileWindows.h +lib/arch/CArchLogUnix.h +lib/arch/CArchLogWindows.h +lib/arch/CArchMiscWindows.h +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h +lib/arch/CArchNetworkBSD.h +lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchNetworkWinsock.h +lib/arch/CArchSleepUnix.h +lib/arch/CArchSleepWindows.h +lib/arch/CArchStringUnix.cpp +lib/arch/CArchStringUnix.h +lib/arch/CArchStringWindows.cpp +lib/arch/CArchStringWindows.h +lib/arch/CArchTimeUnix.h +lib/arch/CArchTimeWindows.h +lib/arch/IArchConsole.h +lib/arch/IArchFile.h +lib/arch/IArchLog.h +lib/arch/IArchMultithread.h +lib/arch/IArchNetwork.h +lib/arch/IArchSleep.h +lib/arch/IArchString.h +lib/arch/IArchTime.h +lib/arch/Makefile.am +lib/arch/XArchImpl.h +lib/arch/arch.dsp +lib/base/CLog.cpp +lib/base/CUnicode.cpp +lib/base/XBase.cpp +lib/base/XBase.h +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/client/ISecondaryScreenFactory.h +lib/client/Makefile.am +lib/client/client.dsp +lib/http/XHTTP.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/platform/Makefile.am +lib/platform/platform.dsp +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CMSWindowsPrimaryScreen.h +lib/server/CPrimaryScreen.cpp +lib/server/CPrimaryScreen.h +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/server/IPrimaryScreenFactory.h +lib/server/Makefile.am +lib/server/server.dsp +lib/synergy/CPrimaryScreen.cpp +lib/synergy/CPrimaryScreen.h +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h +lib/synergy/IPrimaryScreenFactory.h +lib/synergy/ISecondaryScreenFactory.h +lib/synergy/Makefile.am +lib/synergy/libsynergy.dsp + +Moved CPrimaryScreen and CSecondaryScreen to the lib/synergy +and the platform specific implementations to lib/platform. +Added an lib/arch method to query the platform's native wide +character encoding and changed CUnicode to use it. All +platform dependent code is now in lib/arch, lib/platform, +and the programs under cmd. Also added more documentation. + +---------- +2003/01/04 22:01:32 crs +PORTING +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/synergyc/Makefile.am +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.dsp +cmd/synergys/Makefile.am +cmd/synergys/synergys.cpp +cmd/synergys/synergys.dsp +configure.in +lib/Makefile.am +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleUnix.cpp +lib/arch/CArchConsoleUnix.h +lib/arch/CArchConsoleWindows.cpp +lib/arch/CArchConsoleWindows.h +lib/arch/CArchDaemonNone.cpp +lib/arch/CArchDaemonNone.h +lib/arch/CArchDaemonUnix.cpp +lib/arch/CArchDaemonUnix.h +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchDaemonWindows.h +lib/arch/CArchFileUnix.cpp +lib/arch/CArchFileUnix.h +lib/arch/CArchFileWindows.cpp +lib/arch/CArchFileWindows.h +lib/arch/CArchImpl.cpp +lib/arch/CArchLogUnix.cpp +lib/arch/CArchLogUnix.h +lib/arch/CArchLogWindows.cpp +lib/arch/CArchLogWindows.h +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchNetworkWinsock.h +lib/arch/CArchSleepUnix.cpp +lib/arch/CArchSleepUnix.h +lib/arch/CArchSleepWindows.cpp +lib/arch/CArchSleepWindows.h +lib/arch/CArchStringUnix.cpp +lib/arch/CArchStringUnix.h +lib/arch/CArchStringWindows.cpp +lib/arch/CArchStringWindows.h +lib/arch/CArchTimeUnix.cpp +lib/arch/CArchTimeUnix.h +lib/arch/CArchTimeWindows.cpp +lib/arch/CArchTimeWindows.h +lib/arch/CMultibyte.cpp +lib/arch/CMultibyteEmu.cpp +lib/arch/CMultibyteOS.cpp +lib/arch/IArchConsole.h +lib/arch/IArchDaemon.h +lib/arch/IArchFile.h +lib/arch/IArchLog.h +lib/arch/IArchMultithread.h +lib/arch/IArchNetwork.h +lib/arch/IArchSleep.h +lib/arch/IArchString.h +lib/arch/IArchTime.h +lib/arch/Makefile.am +lib/arch/XArch.cpp +lib/arch/XArch.h +lib/arch/XArchImpl.h +lib/arch/XArchUnix.cpp +lib/arch/XArchUnix.h +lib/arch/XArchWindows.cpp +lib/arch/XArchWindows.h +lib/arch/arch.dsp +lib/arch/vsnprintf.cpp +lib/base/BasicTypes.h +lib/base/CLog.cpp +lib/base/CLog.h +lib/base/CStopwatch.cpp +lib/base/CString.cpp +lib/base/CString.h +lib/base/CStringUtil.cpp +lib/base/CStringUtil.h +lib/base/CUnicode.cpp +lib/base/CUnicode.h +lib/base/IInterface.h +lib/base/ILogOutputter.h +lib/base/Makefile.am +lib/base/Version.h +lib/base/XBase.cpp +lib/base/XBase.h +lib/base/base.dsp +lib/base/common.h +lib/base/stdfstream.h +lib/base/stdistream.h +lib/base/stdlist.h +lib/base/stdmap.h +lib/base/stdostream.h +lib/base/stdpost.h +lib/base/stdpre.h +lib/base/stdset.h +lib/base/stdsstream.h +lib/base/stdvector.h +lib/client/CClient.cpp +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/Makefile.am +lib/client/client.dsp +lib/common/BasicTypes.h +lib/common/IInterface.h +lib/common/Makefile.am +lib/common/Version.h +lib/common/common.dsp +lib/common/common.h +lib/common/stdfstream.h +lib/common/stdistream.h +lib/common/stdlist.h +lib/common/stdmap.h +lib/common/stdostream.h +lib/common/stdpost.h +lib/common/stdpre.h +lib/common/stdset.h +lib/common/stdsstream.h +lib/common/stdstring.h +lib/common/stdvector.h +lib/http/CHTTPProtocol.h +lib/http/Makefile.am +lib/http/XHTTP.cpp +lib/http/http.dsp +lib/io/CUnicode.cpp +lib/io/CUnicode.h +lib/io/Makefile.am +lib/io/XIO.cpp +lib/io/XIO.h +lib/io/io.dsp +lib/mt/CCondVar.cpp +lib/mt/CCondVar.h +lib/mt/CMutex.cpp +lib/mt/CMutex.h +lib/mt/CThread.cpp +lib/mt/CThread.h +lib/mt/CThreadRep.cpp +lib/mt/CThreadRep.h +lib/mt/CTimerThread.cpp +lib/mt/Makefile.am +lib/mt/XMT.cpp +lib/mt/XMT.h +lib/mt/XThread.h +lib/mt/mt.dsp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp +lib/net/CNetworkAddress.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPListenSocket.h +lib/net/CTCPSocket.cpp +lib/net/CTCPSocket.h +lib/net/Makefile.am +lib/net/XNetwork.cpp +lib/net/XNetwork.h +lib/net/XSocket.cpp +lib/net/XSocket.h +lib/net/net.dsp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CPlatform.cpp +lib/platform/CPlatform.h +lib/platform/CUnixPlatform.cpp +lib/platform/CUnixPlatform.h +lib/platform/CWin32Platform.cpp +lib/platform/CWin32Platform.h +lib/platform/CXWindowsClipboard.cpp +lib/platform/IPlatform.h +lib/platform/Makefile.am +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/CConfig.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CServer.cpp +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/Makefile.am +lib/server/server.dsp +lib/synergy/Makefile.am +lib/synergy/XScreen.cpp +lib/synergy/XScreen.h +lib/synergy/XSynergy.cpp +lib/synergy/XSynergy.h +lib/synergy/libsynergy.dsp +synergy.dsw + +Refactored some platform dependent code into a new library, +lib/arch. This should make porting easier. Will probably +continue to refactor a little more, moving platform dependent +event handling stuff into lib/platform. + +---------- +2002/12/26 18:40:22 crs +FAQ + +More FAQs. + +---------- +2002/12/25 23:49:42 crs +BUGS +FAQ +INSTALL +README +TODO + +Documentation update. + +---------- +2002/12/25 22:56:09 crs +lib/server/CMSWindowsPrimaryScreen.cpp + +Made synrgyhk.dll error messages less cryptic. + +---------- +2002/12/25 19:21:17 crs +NEWS +configure.in +lib/base/Version.h + +Changed version number to 0.9.14. Added NEWS item. + +---------- +2002/12/25 18:44:54 crs +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreenSaver.cpp + +Improved handling of screen saver handling when windows 2k is +the client and the screen saver is password protected. It used +to immediately turn off the screen saver (unintentionally) in +that case. + +---------- +2002/12/25 10:35:59 crs +acinclude.m4 +config/config.guess +config/config.sub +configure.in +lib/base/common.h +lib/mt/CThreadRep.cpp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/platform/CUnixPlatform.cpp + +Changes to support building on solaris, irix, and darwin. Also +removed test for working fork (AC_FORK). + +---------- +2002/12/23 14:47:44 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h + +Added code to process set/reset options messages from server. + +---------- +2002/12/23 13:55:21 crs +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CMSWindowsPrimaryScreen.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CPrimaryScreen.cpp +lib/server/CPrimaryScreen.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/synergy/CProtocolUtil.cpp +lib/synergy/CProtocolUtil.h +lib/synergy/IClient.h +lib/synergy/Makefile.am +lib/synergy/OptionTypes.h +lib/synergy/ProtocolTypes.h + +Added support for per-screen options in the configuration file +and sending those options to the appropriate client screens. +Currently, two options are supported: halfDuplexCapsLock and +halfDuplexNumLock mark the caps lock and num lock keys, +respectively, as being half-duplex. + +---------- +2002/12/22 14:51:41 crs +configure.in +doc/doxygen.cfg.in + +Doxygen config file now sets HAVE_DOT to YES only if dot is found +by configure. + +---------- +2002/12/15 22:39:59 crs +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h + +Now handling any number of pointer buttons. + +---------- +2002/12/15 22:17:18 crs +lib/server/CXWindowsPrimaryScreen.cpp + +Now ignoring half-duplex keys that are down when deciding if +the mouse is locked to the screen. We can't tell if a half- +duplex key is physically down and logically down just means +it's active so there's no point in letting it lock the mouse +to the screen. + +---------- +2002/12/15 22:14:49 crs +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h + +Now restoring toggle key states on leaving a client screen to +their state when the screen was entered. Previously when +leaving a client screen the toggle keys kept their state so, +say, caps lock, would remain on. This was inconvenient if +you then used the client's keyboard directly. + +---------- +2002/12/15 20:00:52 crs +lib/server/CMSWindowsPrimaryScreen.cpp + +Fixed loss of ctrl+alt when transmitted to non-windows platforms +from a windows server. Was converting ctrl+alt on windows to +mode switch on the server. No longer doing that; windows clients +will interpret ctrl+alt as AltGr and other clients will just see +ctrl+alt. Also made the right alt key mode switch on windows +servers in case the user wants to force a mode switch, but that +means the right alt key no longer acts as alt on clients. + +---------- +2002/12/15 19:58:41 crs +lib/platform/CMSWindowsScreen.cpp + +Fixed client not reconnecting when server dies bug. + +---------- +2002/12/15 19:57:28 crs +lib/client/CXWindowsSecondaryScreen.cpp + +Cleanup and changed some DEBUG1 messages to DEBUG2. + +---------- +2002/12/15 11:12:39 crs +doc/doxygen.cfg.in + +Enabled using dot and class diagrams. + +---------- +2002/11/05 19:23:05 crs +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h + +Fixed bug in detecting screen saver activation. Was using || instead +of && in conditional. + +---------- +2002/11/03 18:09:28 crs +acinclude.m4 +configure.in +lib/io/CUnicode.h +lib/net/CNetwork.h +lib/platform/CUnixPlatform.cpp + +Merged fixes for building on MacOS X. It dies on one file with +an internal compiler error; building that file without +optimization works around the compiler bug. Sadly, synergy can +only interact with X windows, not native MacOS windows. + +---------- +2002/10/30 22:22:16 crs +acinclude.m4 + +Escaped quotes to satisfy older autoheader versions. + +---------- +2002/10/30 22:16:30 crs +lib/net/CNetwork.h +lib/net/CTCPSocket.cpp + +Fixed bugs in error handling in CTCPSocket; previously was not +handling read errors at all and error handling for writes was +never being used. Now the socket disconnects if a read or write +fails on the socket for any reason except EINTR. Also added + to includes in CNetwork.h because it's needed on +some platforms. + +---------- +2002/10/29 22:07:55 crs +all.dsp +lib/base/base.dsp +lib/client/client.dsp +lib/http/http.dsp +lib/io/CUnicode.cpp +lib/io/io.dsp +lib/mt/mt.dsp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/net.dsp +lib/platform/makehook.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/server.dsp +lib/synergy/libsynergy.dsp + +Ported recent changes to win32 and fixed CRLF problems with project +files (most had CRCRCRLF). + +---------- +2002/10/28 22:49:21 crs +acinclude.m4 +configure.in +lib/mt/CThreadRep.cpp + +solaris configure and build fixes. without having solaris i +can only hope that these changes actually work. + +---------- +2002/10/22 22:35:13 crs +configure.in +lib/io/CUnicode.cpp + +Added workarounds for missing reentrant versions of wide char +to/from multi-byte conversion functions. + +---------- +2002/10/22 21:30:48 crs +lib/base/CUnicode.cpp +lib/base/CUnicode.h +lib/base/Makefile.am +lib/io/CUnicode.cpp +lib/io/CUnicode.h +lib/io/Makefile.am +lib/platform/Makefile.am + +Moved CUnicode to lib/io. That's a reasonable place for it +that's after lib/mt. It needs to be after lib/mt in preparation +for supporting platforms without the reentrant wide char and +multi-byte functions. + +---------- +2002/10/20 22:39:54 crs +lib/client/CMSWindowsSecondaryScreen.cpp + +Fixed conditional to test for multimon to do nasty win32 mouse +positioning hack. Was doing hack if *not* a multiple monitor +system but should've been doing it if is *is* a multimon system. + +---------- +2002/10/20 22:36:24 crs +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp + +Replaced inet_addr() with inet_aton(), which is a better function +anyway but isn't implemented in winsock, removed use of INADDR_NONE +which some platforms don't define except on winsock which does +define it, and changed SOL_TCP to IPPROTO_TCP which should work on +more platforms. + +---------- +2002/10/17 21:37:41 crs +lib/platform/CXWindowsScreen.cpp + +Fixed CXWindowsScreen to force the event loop to wake up when +exitMainLoop() is called. + +---------- +2002/10/17 21:37:37 crs +lib/mt/CThreadRep.cpp + +Fixed CThreadRep to not raise a signal on the thread if it's +already dead. Otherwise the signal could propagate to the +parent thread (at least on linux threads) and cause havoc. + +---------- +2002/10/17 21:37:31 crs +lib/server/CServer.cpp +lib/server/CServer.h + +Changed server to fail with an error if in can't bind() the listen +socket for any reason other than it's in use. + +---------- +2002/10/17 20:56:28 crs +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp + +Changed non-reentrant network functions to be reentrant and +thread safe. + +---------- +2002/10/16 22:01:50 crs +acinclude.m4 +configure.in +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CTCPSocket.cpp +lib/platform/CXWindowsScreen.cpp + +Added support for using select() instead of poll(). + +---------- +2002/10/15 22:17:41 crs +lib/server/CConfig.cpp + +CConfig now accepts and discards \r at the end of a line. This +allows the unix server to read configuration files created on +microsoft windows platforms. + +---------- +2002/10/15 22:08:10 crs +cmd/synergys/synergys.cpp +lib/server/CConfig.cpp + +Fixed use of %s instead of %{1} in format() call. + +---------- +2002/10/15 22:01:41 crs +lib/client/CClient.cpp +lib/mt/CThreadRep.cpp +lib/mt/Makefile.am +lib/mt/XMT.cpp +lib/mt/XMT.h +lib/mt/XThread.h +lib/server/CServer.cpp + +Renamed XThreadUnavailable to XMTThreadUnavailable and derived it +from XBase so it can be caught normally. Changed client and server +to handle unavailable threads (in main loop, anyway). + +---------- +2002/10/15 21:35:12 crs +lib/mt/CThreadRep.cpp + +Workaround for pthread bug on RedHat 7.2 on multiprocessor +systems. + +---------- +2002/10/15 21:29:44 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/base/CLog.h +lib/client/CClient.cpp +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CSecondaryScreen.cpp +lib/client/CServerProxy.cpp +lib/client/CXWindowsSecondaryScreen.cpp +lib/http/CHTTPProtocol.cpp +lib/mt/CMutex.cpp +lib/mt/CThread.cpp +lib/mt/CThreadRep.cpp +lib/mt/CTimerThread.cpp +lib/net/CNetwork.cpp +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CWin32Platform.cpp +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsUtil.cpp +lib/server/CClientProxy1_0.cpp +lib/server/CHTTPServer.cpp +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryScreen.cpp +lib/server/CServer.cpp +lib/server/CXWindowsPrimaryScreen.cpp +lib/synergy/CProtocolUtil.cpp + +Changed log() and logc() macros to LOG() and LOGC(), respectively. +This avoids a conflict with the standard math library log() +function. + +---------- +2002/09/14 21:31:35 crs +lib/base/XBase.cpp +lib/base/XBase.h + +removed std::exception from base class list of XBase. this +is a workaround for gcc 3.2 until everything necessary has +throw() specifiers. + +---------- +2002/09/14 20:56:50 crs +lib/server/CServer.cpp + +now logging bind failures as warnings. + +---------- +2002/09/14 20:56:28 crs +lib/base/XBase.cpp +lib/base/XBase.h +lib/io/XIO.cpp +lib/io/XIO.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPSocket.cpp +lib/net/XSocket.cpp +lib/net/XSocket.h + +added better network error message support. + +---------- +2002/09/14 12:07:02 crs +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/synergy/KeyTypes.h + +Rewrote handling of key press on X11 client; it should be much +more robust now. Also added handling of Super modifier key and +changed windows keys to map to Super instead of Meta, which is +the default on my keyboard. + +---------- +2002/09/14 12:05:06 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added debug level combo box and version number to title bar of win32 +launcher. + +---------- +2002/09/14 12:03:43 crs +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.rc + +Fixed backend mode. Now reports log messages and, if any are +serious, shows a message box before exiting. + +---------- +2002/09/04 21:17:01 crs +NEWS +configure.in +lib/base/Version.h + +Changed version number to 0.9.11. Added NEWS item. + +---------- +2002/09/04 21:14:18 crs +lib/client/CMSWindowsSecondaryScreen.cpp + +now looking up SendEvent() using GetProcAddress() so win95 +systems can run the synergy client. + +---------- +2002/09/04 20:17:54 crs +lib/client/CXWindowsSecondaryScreen.cpp + +fixed bug that caused the wrong keycode to be used for most, +possibly all, keysyms. was reading past the end of an array +of keysyms. + +---------- +2002/09/02 17:36:25 crs +configure.in +lib/base/Version.h + +Changed version number to 0.9.10. + +---------- +2002/09/02 17:30:04 crs +BUGS +INSTALL +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +lib/client/CClient.cpp +lib/client/CMSWindowsSecondaryScreen.cpp +lib/platform/CUnixPlatform.cpp +lib/platform/CUnixPlatform.h +lib/platform/CWin32Platform.cpp +lib/platform/CWin32Platform.h +lib/platform/IPlatform.h +lib/server/CMSWindowsPrimaryScreen.cpp + +Fixed win32 config saving, keyboard mapping, and AltGr bugs. +Made extensive changes to the launcher to provide more control +over setting up auto-start and it now saves configuration to +the user's documents directory if auto-starting at login and +saves to the system directory if auto-starting at boot. +Replaced MapVirtualKey() with table lookup to work around that +function's lack of support for extended keyboard scan codes. +Added first cut at support for AltGr. + +---------- +2002/09/01 15:30:00 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/KeyTypes.h + +Added support for mode switch key to X11 screens. + +---------- +2002/09/01 10:31:10 crs +acinclude.m4 +configure.in +lib/base/stdsstream.h + +added more tests to autoconf. also now handling missing sstream +header in gcc 2.95 by including sstream header backported from v3. + +---------- +2002/09/01 09:28:54 crs +lib/platform/CXWindowsUtil.cpp + +lowered severity of some debug messages. + +---------- +2002/08/18 17:45:59 crs +configure.in +lib/base/Version.h + +Changed version number to 0.9.9. + +---------- +2002/08/18 17:40:10 crs +lib/server/CMSWindowsPrimaryScreen.cpp + +fixed win32 deadlock. when a client disconnects the server will +warp the mouse to the primary screen. entering the primary +screen causes the primary screen's window to be hidden. the +deadlock occurs because hiding the window seems to post a +message then wait for it to be handled (or possibly it won't +send a message while a posted message is being handled). +thread A locks the mutex, warps the mouse, the hides the window. +thread B begins processing the mouse warp then tries to lock +the mutex. thread A is waiting on the event loop owned by B +while B is waiting on the mutex owned by A. this fix simply +hides the window asynchronously. however, there may be other +ways to cause a similar deadlock that have not been found. + +---------- +2002/08/18 17:35:10 crs +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/server/CMSWindowsPrimaryScreen.cpp + +fixed PrintScrn handling; it was being changed to keypad multiply. + +---------- +2002/08/18 17:31:48 crs +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h + +no longer sending fake events for unmapped logical buttons. + +---------- +2002/08/11 22:43:07 crs +BUGS +INSTALL +NEWS +PORTING +README +cmd/Makefile.am +cmd/launcher/launcher.cpp +cmd/synergy/Makefile.am +cmd/synergy/resource.h +cmd/synergy/synergy.cpp +cmd/synergy/synergy.dsp +cmd/synergy/synergy.ico +cmd/synergy/synergy.rc +cmd/synergyc/Makefile.am +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.dsp +cmd/synergyc/synergyc.ico +cmd/synergyc/synergyc.rc +cmd/synergyd/Makefile.am +cmd/synergyd/resource.h +cmd/synergyd/synergy.ico +cmd/synergyd/synergyd.cpp +cmd/synergyd/synergyd.dsp +cmd/synergyd/synergyd.rc +cmd/synergys/Makefile.am +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.dsp +cmd/synergys/synergys.ico +cmd/synergys/synergys.rc +configure.in +dist/rpm/synergy.spec.in +synergy.dsw + +Moved synergy client to cmd/synergyc and renamed it synergyc. +Moved synergy server to cmd/synergys and renamed it synergys. +Updated documentation to reflect that and the win32 launcher. + +---------- +2002/08/11 11:50:49 crs +Makefile.am +TODO + +added TODO file and top-level rule to make zip file of distribution +files. + +---------- +2002/08/03 11:50:07 crs +lib/mt/CCondVar.h + +removed pre-instantiation of templates in header file. + +---------- +2002/08/03 11:49:36 crs +all.dsp +cmd/Makefile.am +cmd/launcher/Makefile.am +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/launcher/synergy.ico +cmd/synergy/Makefile.am +cmd/synergy/resource.h +cmd/synergy/synergy.cpp +cmd/synergy/synergy.dsp +cmd/synergy/synergy.ico +cmd/synergy/synergy.rc +cmd/synergyd/Makefile.am +cmd/synergyd/resource.h +cmd/synergyd/synergy.ico +cmd/synergyd/synergyd.cpp +cmd/synergyd/synergyd.dsp +cmd/synergyd/synergyd.rc +configure.in +lib/base/CString.cpp +lib/base/Version.h +lib/base/base.dsp +lib/client/client.dsp +lib/http/http.dsp +lib/io/io.dsp +lib/mt/mt.dsp +lib/net/net.dsp +lib/platform/CMSWindowsScreen.cpp +lib/platform/makehook.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/server.dsp +lib/synergy/libsynergy.dsp +synergy.dsw + +added win32 launcher program. also changed VC++ dsp and dsw +files to binary form so \r\n aren't converted. added icons +to client and server apps on win32. + +---------- +2002/08/02 17:57:54 crs +Makefile.am +configure.in +dist/Makefile.am +dist/rpm/Makefile.am +dist/rpm/synergy.spec.in + +added build rule to create RPMs. + +---------- +2002/08/02 17:53:23 crs +AUTHORS +BUGS +FAQ +HISTORY +INSTALL +NEWS +PORTING +README + +minor documentation updates. added HISTORY and PORTING. + +---------- +2002/08/01 18:07:32 crs +AUTHORS +BUGS +COPYING +ChangeLog +FAQ +INSTALL +NEWS +README + +added files for release. + +---------- +2002/08/01 11:45:21 crs +configure.in +lib/base/CStopwatch.cpp +lib/mt/CCondVar.cpp +lib/platform/CUnixPlatform.cpp + +minor automake fixes. + +---------- +2002/07/31 17:40:36 crs +Makefile.am + +added simple rule to build doxygen. + +---------- +2002/07/31 17:40:21 crs +lib/synergy/XSynergy.h + +fixed comment. + +---------- +2002/07/31 17:35:43 crs +Makefile.am + +removed two programs from files to clean. + +---------- +2002/07/31 17:34:05 crs +Makefile.am +cmd/Makefile.am +cmd/synergy/Makefile.am +cmd/synergyd/Makefile.am +lib/Makefile.am +lib/base/Makefile.am +lib/client/Makefile.am +lib/http/Makefile.am +lib/io/Makefile.am +lib/mt/Makefile.am +lib/net/Makefile.am +lib/platform/Makefile.am +lib/server/Makefile.am +lib/synergy/Makefile.am + +fixes to get vpath builds working (necessary for `make distcheck'). + +---------- +2002/07/31 16:57:26 crs +Makefile.am +cmd/Makefile.am +cmd/synergy/Makefile.am +cmd/synergy/synergy.cpp +cmd/synergyd/Makefile.am +cmd/synergyd/synergyd.cpp +configure.in +lib/Makefile.am +lib/base/Makefile.am +lib/base/Version.h +lib/client/Makefile.am +lib/http/Makefile.am +lib/io/Makefile.am +lib/mt/Makefile.am +lib/net/Makefile.am +lib/platform/Makefile.am +lib/server/Makefile.am +lib/synergy/Makefile.am +lib/synergy/ProtocolTypes.h +lib/synergy/Version.h + +Moved version header to base and it now uses VERSION macro +from config.h if available (which means version is now a +string, not three integers). Changed version to 1.0.0 and +protocol version to 1.0. And added MAINTAINERCLEANFILES +to makefiles to remove generated files. + +---------- +2002/07/31 16:27:06 crs +Makefile.am +cmd/synergy/Makefile.am +cmd/synergyd/Makefile.am +examples/synergy.linux.init +examples/synergyd.linux.init +lib/base/Makefile.am +lib/client/Makefile.am +lib/http/Makefile.am +lib/io/Makefile.am +lib/mt/Makefile.am +lib/net/Makefile.am +lib/platform/Makefile.am +lib/server/Makefile.am +lib/synergy/Makefile.am +nodist/notes +notes + +added EXTRA_* files to get `make dist' doing the right thing. + +---------- +2002/07/31 16:24:45 crs +notes + +checkpoint notes. + +---------- +2002/07/31 13:56:59 crs +lib/base/CLog.cpp + +removed now unnecssary #define. + +---------- +2002/07/31 13:41:58 crs +lib/http/http.dsp +lib/platform/CMSWindowsClipboardAnyTextConverter.cpp +lib/platform/CMSWindowsClipboardAnyTextConverter.h +lib/platform/CMSWindowsClipboardTextConverter.cpp +lib/platform/CMSWindowsClipboardTextConverter.h +lib/platform/CMSWindowsClipboardUTF16Converter.cpp +lib/platform/CMSWindowsClipboardUTF16Converter.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h +lib/platform/IMSWindowsScreenEventHandler.h + +okay, now the files should no longer be executable. + +---------- +2002/07/31 13:34:18 crs +lib/http/http.dsp +lib/platform/CMSWindowsClipboardAnyTextConverter.cpp +lib/platform/CMSWindowsClipboardAnyTextConverter.h +lib/platform/CMSWindowsClipboardTextConverter.cpp +lib/platform/CMSWindowsClipboardTextConverter.h +lib/platform/CMSWindowsClipboardUTF16Converter.cpp +lib/platform/CMSWindowsClipboardUTF16Converter.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h +lib/platform/IMSWindowsScreenEventHandler.h +notes + +removed unintentional executable flag. + +---------- +2002/07/31 13:29:33 crs +notes + +checkpoint notes. + +---------- +2002/07/31 13:18:27 crs +README + +added comment about large motif clipboard items to README. + +---------- +2002/07/31 13:10:15 crs +README + +updated README. + +---------- +2002/07/31 12:40:41 crs +lib/platform/CSynergyHook.cpp +lib/platform/synrgyhk.dsp + +now building hook dll for release without linking in standard +C runtime. need C runtime for debug build for asserts. + +---------- +2002/07/31 12:39:34 crs +cmd/synergy/synergy.cpp +cmd/synergyd/synergyd.cpp +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsScreen.cpp +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/IClient.h +lib/synergy/IScreen.h +lib/synergy/XScreen.cpp +lib/synergy/XScreen.h + +fixed problem with opening client and server. in some cases it +would fail to open in such a way that it could never succeed +but it'd never stop retrying. now terminating when open fails +such that it'll never succeed. + +---------- +2002/07/30 19:03:40 crs +lib/client/client.dsp +lib/io/io.dsp +lib/net/net.dsp +lib/server/server.dsp +lib/synergy/libsynergy.dsp + +added new files to projects and added two project files that +should've been adding in change 530. + +---------- +2002/07/30 18:49:31 crs +lib/client/CServerProxy.cpp +lib/server/CClientProxy1_0.cpp +lib/synergy/ProtocolTypes.h + +made it so a negative kHeartRate disables heartbeats and set +kHeartRate to -1. + +---------- +2002/07/30 18:31:21 crs +lib/mt/CThreadRep.cpp +lib/mt/XThread.h + +moved exception definition to header file. + +---------- +2002/07/30 18:31:00 crs +cmd/synergy/synergy.cpp +cmd/synergyd/synergyd.cpp +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/ISecondaryScreenFactory.h +lib/client/Makefile.am +lib/io/IStreamFilterFactory.h +lib/io/Makefile.am +lib/net/CTCPSocketFactory.cpp +lib/net/CTCPSocketFactory.h +lib/net/ISocketFactory.h +lib/net/Makefile.am +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/IPrimaryScreenFactory.h +lib/server/Makefile.am +lib/synergy/CTCPSocketFactory.cpp +lib/synergy/CTCPSocketFactory.h +lib/synergy/ISocketFactory.h +lib/synergy/Makefile.am +lib/synergy/ProtocolTypes.h + +now using class factories to move some decisions from the libraries +into the application. + +---------- +2002/07/30 16:52:46 crs +Makefile.am +base/BasicTypes.h +base/CFunctionJob.cpp +base/CFunctionJob.h +base/CLog.cpp +base/CLog.h +base/CStopwatch.cpp +base/CStopwatch.h +base/CString.cpp +base/CString.h +base/CUnicode.cpp +base/CUnicode.h +base/IInterface.h +base/IJob.h +base/Makefile.am +base/TMethodJob.h +base/XBase.cpp +base/XBase.h +base/base.dsp +base/common.h +base/stdfstream.h +base/stdistream.h +base/stdlist.h +base/stdmap.h +base/stdostream.h +base/stdpost.h +base/stdpre.h +base/stdset.h +base/stdsstream.h +base/stdvector.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/Makefile.am +client/client.cpp +client/client.dsp +client/client.rc +client/resource.h +cmd/Makefile.am +cmd/synergy/Makefile.am +cmd/synergy/resource.h +cmd/synergy/synergy.cpp +cmd/synergy/synergy.dsp +cmd/synergy/synergy.rc +cmd/synergyd/Makefile.am +cmd/synergyd/resource.h +cmd/synergyd/synergyd.cpp +cmd/synergyd/synergyd.dsp +cmd/synergyd/synergyd.rc +configure.in +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/Makefile.am +http/XHTTP.cpp +http/XHTTP.h +http/http.dsp +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/CInputStreamFilter.h +io/COutputStreamFilter.cpp +io/COutputStreamFilter.h +io/CStreamBuffer.cpp +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/Makefile.am +io/XIO.cpp +io/XIO.h +io/io.dsp +lib/Makefile.am +lib/base/BasicTypes.h +lib/base/CFunctionJob.cpp +lib/base/CFunctionJob.h +lib/base/CLog.cpp +lib/base/CLog.h +lib/base/CStopwatch.cpp +lib/base/CStopwatch.h +lib/base/CString.cpp +lib/base/CString.h +lib/base/CUnicode.cpp +lib/base/CUnicode.h +lib/base/IInterface.h +lib/base/IJob.h +lib/base/Makefile.am +lib/base/TMethodJob.h +lib/base/XBase.cpp +lib/base/XBase.h +lib/base/base.dsp +lib/base/common.h +lib/base/stdfstream.h +lib/base/stdistream.h +lib/base/stdlist.h +lib/base/stdmap.h +lib/base/stdostream.h +lib/base/stdpost.h +lib/base/stdpre.h +lib/base/stdset.h +lib/base/stdsstream.h +lib/base/stdvector.h +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/client/Makefile.am +lib/http/CHTTPProtocol.cpp +lib/http/CHTTPProtocol.h +lib/http/Makefile.am +lib/http/XHTTP.cpp +lib/http/XHTTP.h +lib/http/http.dsp +lib/io/CBufferedInputStream.cpp +lib/io/CBufferedInputStream.h +lib/io/CBufferedOutputStream.cpp +lib/io/CBufferedOutputStream.h +lib/io/CInputStreamFilter.cpp +lib/io/CInputStreamFilter.h +lib/io/COutputStreamFilter.cpp +lib/io/COutputStreamFilter.h +lib/io/CStreamBuffer.cpp +lib/io/CStreamBuffer.h +lib/io/IInputStream.h +lib/io/IOutputStream.h +lib/io/Makefile.am +lib/io/XIO.cpp +lib/io/XIO.h +lib/io/io.dsp +lib/mt/CCondVar.cpp +lib/mt/CCondVar.h +lib/mt/CLock.cpp +lib/mt/CLock.h +lib/mt/CMutex.cpp +lib/mt/CMutex.h +lib/mt/CThread.cpp +lib/mt/CThread.h +lib/mt/CThreadRep.cpp +lib/mt/CThreadRep.h +lib/mt/CTimerThread.cpp +lib/mt/CTimerThread.h +lib/mt/Makefile.am +lib/mt/XThread.h +lib/mt/mt.dsp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp +lib/net/CNetworkAddress.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPListenSocket.h +lib/net/CTCPSocket.cpp +lib/net/CTCPSocket.h +lib/net/IDataSocket.h +lib/net/IListenSocket.h +lib/net/ISocket.h +lib/net/Makefile.am +lib/net/XNetwork.cpp +lib/net/XNetwork.h +lib/net/XSocket.cpp +lib/net/XSocket.h +lib/net/net.dsp +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsClipboard.h +lib/platform/CMSWindowsClipboardAnyTextConverter.cpp +lib/platform/CMSWindowsClipboardAnyTextConverter.h +lib/platform/CMSWindowsClipboardTextConverter.cpp +lib/platform/CMSWindowsClipboardTextConverter.h +lib/platform/CMSWindowsClipboardUTF16Converter.cpp +lib/platform/CMSWindowsClipboardUTF16Converter.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h +lib/platform/CPlatform.cpp +lib/platform/CPlatform.h +lib/platform/CSynergyHook.cpp +lib/platform/CSynergyHook.h +lib/platform/CUnixPlatform.cpp +lib/platform/CUnixPlatform.h +lib/platform/CWin32Platform.cpp +lib/platform/CWin32Platform.h +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsClipboard.h +lib/platform/CXWindowsClipboardTextConverter.cpp +lib/platform/CXWindowsClipboardTextConverter.h +lib/platform/CXWindowsClipboardUCS2Converter.cpp +lib/platform/CXWindowsClipboardUCS2Converter.h +lib/platform/CXWindowsClipboardUTF8Converter.cpp +lib/platform/CXWindowsClipboardUTF8Converter.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h +lib/platform/IMSWindowsScreenEventHandler.h +lib/platform/IPlatform.h +lib/platform/Makefile.am +lib/platform/makehook.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/CClientProxy.cpp +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CHTTPServer.cpp +lib/server/CHTTPServer.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CMSWindowsPrimaryScreen.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CPrimaryScreen.cpp +lib/server/CPrimaryScreen.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/server/Makefile.am +lib/synergy/CClipboard.cpp +lib/synergy/CClipboard.h +lib/synergy/CInputPacketStream.cpp +lib/synergy/CInputPacketStream.h +lib/synergy/COutputPacketStream.cpp +lib/synergy/COutputPacketStream.h +lib/synergy/CProtocolUtil.cpp +lib/synergy/CProtocolUtil.h +lib/synergy/CTCPSocketFactory.cpp +lib/synergy/CTCPSocketFactory.h +lib/synergy/ClipboardTypes.h +lib/synergy/IClient.h +lib/synergy/IClipboard.h +lib/synergy/IPrimaryScreenReceiver.h +lib/synergy/IScreen.h +lib/synergy/IScreenEventHandler.h +lib/synergy/IScreenReceiver.h +lib/synergy/IScreenSaver.h +lib/synergy/IServer.h +lib/synergy/ISocketFactory.h +lib/synergy/KeyTypes.h +lib/synergy/Makefile.am +lib/synergy/MouseTypes.h +lib/synergy/ProtocolTypes.h +lib/synergy/Version.h +lib/synergy/XScreen.cpp +lib/synergy/XScreen.h +lib/synergy/XSynergy.cpp +lib/synergy/XSynergy.h +lib/synergy/libsynergy.dsp +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.cpp +mt/CTimerThread.h +mt/Makefile.am +mt/XThread.h +mt/mt.dsp +net/CNetwork.cpp +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IDataSocket.h +net/IListenSocket.h +net/ISocket.h +net/Makefile.am +net/XNetwork.cpp +net/XNetwork.h +net/XSocket.cpp +net/XSocket.h +net/net.dsp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsClipboard.h +platform/CMSWindowsClipboardAnyTextConverter.cpp +platform/CMSWindowsClipboardAnyTextConverter.h +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardTextConverter.h +platform/CMSWindowsClipboardUTF16Converter.cpp +platform/CMSWindowsClipboardUTF16Converter.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CMSWindowsScreenSaver.cpp +platform/CMSWindowsScreenSaver.h +platform/CPlatform.cpp +platform/CPlatform.h +platform/CSynergyHook.cpp +platform/CSynergyHook.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsClipboardTextConverter.cpp +platform/CXWindowsClipboardTextConverter.h +platform/CXWindowsClipboardUCS2Converter.cpp +platform/CXWindowsClipboardUCS2Converter.h +platform/CXWindowsClipboardUTF8Converter.cpp +platform/CXWindowsClipboardUTF8Converter.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +platform/IMSWindowsScreenEventHandler.h +platform/IPlatform.h +platform/Makefile.am +platform/makehook.dsp +platform/platform.dsp +platform/synrgyhk.dsp +server/CClientProxy.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile.am +server/resource.h +server/server.cpp +server/server.dsp +server/server.rc +synergy.dsw +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/COutputPacketStream.cpp +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/ClipboardTypes.h +synergy/IClient.h +synergy/IClipboard.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IScreenSaver.h +synergy/IServer.h +synergy/ISocketFactory.h +synergy/KeyTypes.h +synergy/Makefile.am +synergy/MouseTypes.h +synergy/ProtocolTypes.h +synergy/Version.h +synergy/XScreen.cpp +synergy/XScreen.h +synergy/XSynergy.cpp +synergy/XSynergy.h +synergy/synergy.dsp + +Reorganized source tree. Moved client.cpp into cmd/synergy as +synergy.cpp and server.cpp into cmd/synergyd as synergyd.cpp. +Moved and renamed related files. Moved remaining source files +into lib/.... Modified and added makefiles as appropriate. +Result is that library files are under lib with each library +in its own directory and program files are under cmd with each +command in its own directory. + +---------- +2002/07/30 15:17:44 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/server.cpp +synergy/IClient.h + +Replaced method name `run' with `mainLoop', and `stop' and `quit' +with `exitMainLoop' in most places. + +---------- +2002/07/30 14:59:36 crs +client/CClient.h +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.h +server/CClientProxy.h +server/CClientProxy1_0.h +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.h +server/CPrimaryScreen.h +server/CServer.h +server/CXWindowsPrimaryScreen.h +synergy/IClient.h +synergy/IScreen.h +synergy/IScreenReceiver.h +synergy/IServer.h + +Added doxygen comments for all relevant headers in client and server. + +---------- +2002/07/29 17:03:55 crs +platform/CMSWindowsClipboard.h +platform/CMSWindowsClipboardAnyTextConverter.h +platform/CMSWindowsClipboardTextConverter.h +platform/CMSWindowsClipboardUTF16Converter.h +platform/CMSWindowsScreen.h +platform/CMSWindowsScreenSaver.h +platform/CPlatform.h +platform/CSynergyHook.h +platform/CUnixPlatform.h +platform/CWin32Platform.h +platform/CXWindowsClipboard.h +platform/CXWindowsClipboardTextConverter.h +platform/CXWindowsClipboardUCS2Converter.h +platform/CXWindowsClipboardUTF8Converter.h +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.h +platform/CXWindowsUtil.h +platform/IMSWindowsScreenEventHandler.h +platform/IPlatform.h + +Added doxygen comments for all relevant headers in platform. + +---------- +2002/07/29 16:07:26 crs +synergy/CClipboard.h +synergy/CInputPacketStream.h +synergy/COutputPacketStream.h +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.h +synergy/ClipboardTypes.h +synergy/IClient.h +synergy/IClipboard.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IScreenSaver.h +synergy/IServer.h +synergy/ISocketFactory.h +synergy/KeyTypes.h +synergy/MouseTypes.h +synergy/ProtocolTypes.h +synergy/Version.h +synergy/XScreen.h +synergy/XSynergy.h + +Added doxygen comments for all relevant headers in synergy. + +---------- +2002/07/29 16:06:52 crs +platform/CMSWindowsScreen.cpp +server/CPrimaryClient.cpp + +moved try/catch block from CMSWindowsScreen to CPrimaryClient. +this means CMSWindowsScreen doesn't need to include XSynergy.h. + +---------- +2002/07/29 16:05:59 crs +doc/doxygen.cfg + +changed doxygen configuration. + +---------- +2002/07/28 19:06:52 crs +net/CNetwork.h +net/CNetworkAddress.h +net/CTCPListenSocket.h +net/CTCPSocket.h +net/IDataSocket.h +net/IListenSocket.h +net/ISocket.h +net/XNetwork.h +net/XSocket.cpp +net/XSocket.h + +Added doxygen comments for all relevant headers in net. + +---------- +2002/07/28 17:55:59 crs +http/CHTTPProtocol.h +http/XHTTP.cpp +http/XHTTP.h + +Added doxygen comments for all relevant headers in http. + +---------- +2002/07/28 17:25:13 crs +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.h +io/COutputStreamFilter.h +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/XIO.h + +Added doxygen comments for all relevant headers in io. + +---------- +2002/07/28 13:34:19 crs +mt/CCondVar.h +mt/CLock.h +mt/CMutex.h +mt/CThread.h +mt/CThreadRep.h +mt/CTimerThread.h +mt/XThread.h + +Added doxygen comments for all relevant headers in mt. + +---------- +2002/07/26 18:28:18 crs +base/CFunctionJob.h +base/CLog.h +base/CStopwatch.h +base/CString.h +base/CUnicode.h +base/IInterface.h +base/IJob.h +base/TMethodJob.h +base/XBase.h +base/common.h + +added doxygen comments for all relevant headers in base. + +---------- +2002/07/26 18:27:31 crs +platform/CXWindowsUtil.cpp + +fixed type mismatch (SInt32 vs int) in definition of +getWindowProperty(). + +---------- +2002/07/26 16:05:59 crs +doc/doxygen.cfg + +added configuration file for building doxygen documentation. +the code is not yet doxygen documented, though. + +---------- +2002/07/26 15:22:25 crs +platform/CXWindowsUtil.cpp + +now deleting property when so requested even if read failed. + +---------- +2002/07/25 18:08:00 crs +notes + +checkpoint. + +---------- +2002/07/25 17:58:01 crs +client/client.cpp +net/XSocket.cpp +server/server.cpp + +improved error messages for bad addresses. + +---------- +2002/07/25 17:52:40 crs +base/XBase.cpp +http/XHTTP.cpp +io/XIO.cpp +net/XNetwork.cpp +net/XSocket.cpp +server/CConfig.cpp +server/server.cpp +synergy/CProtocolUtil.cpp +synergy/XScreen.cpp +synergy/XSynergy.cpp + +made all getWhat() methods on exceptions consistent. they now +all use format() the same way. also changed format() to actually +do formatting. however, it doesn't try looking up formatting +strings by id, it just uses the fallback format string. + +---------- +2002/07/25 17:23:35 crs +base/CLog.cpp +base/CLog.h +base/CString.cpp +base/CString.h + +moved string formatting into CStringUtil from CLog and added +methods for format positional string arguments. + +---------- +2002/07/25 09:55:01 crs +platform/CXWindowsScreen.cpp + +added unix specific implementation of CXWindowsScreen::mainLoop() +that uses poll() to process events more efficiently. it won't +wake up nor sleep any more than necessary, unlike the platform +independent implementation that polls and sleeps. + +---------- +2002/07/25 09:23:24 crs +platform/CXWindowsClipboard.cpp + +finished INCR transfer changes. also made motifGetTime() return +icccmGetTime() because it seems motif does TIMESTAMP like ICCCM. + +---------- +2002/07/25 08:57:46 crs +platform/CXWindowsClipboard.cpp + +checkpoint. working on INCR transfers. + +---------- +2002/07/24 19:26:18 crs +platform/CMSWindowsScreen.cpp +platform/CWin32Platform.cpp +platform/CWin32Platform.h + +fixes for win32 due to changes in how s_restartable is handled. +the main change is that WM_QUIT now causes the thread to be +cancelled instead of mainLoop() just returning. this also +requires runDaemon() to call the run function in a new thread +each time it calls it because it could can cancelled. + +---------- +2002/07/24 19:24:21 crs +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardUTF16Converter.cpp + +fixes for win32 clipboard due to CUnicode nul terminator changes. + +---------- +2002/07/24 19:23:46 crs +base/CUnicode.cpp + +fixed an off-by-one error in UTF8ToText(). + +---------- +2002/07/24 17:39:52 crs +base/CUnicode.cpp + +fixed an off-by-one error in textToUTF8(). + +---------- +2002/07/24 17:30:32 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +fixed type of TARGETS target. + +---------- +2002/07/24 17:22:01 crs +base/CUnicode.cpp +base/CUnicode.h +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardUTF16Converter.cpp + +made handling of nul terminators in CUnicode more sane. + +---------- +2002/07/24 17:07:52 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +some fixes for motif clipboard. still not handling incremental +transfer through root window property because not sure of the +protocol. + +---------- +2002/07/24 13:01:18 crs +client/CClient.cpp +client/client.cpp +net/CTCPSocket.cpp +net/XSocket.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsScreen.cpp +platform/CXWindowsUtil.cpp +platform/IPlatform.h +server/CPrimaryClient.cpp +server/CServer.cpp +server/server.cpp +synergy/ProtocolTypes.h +synergy/Version.h + +removed restart function from platform. no longer trying to +restart if the X server connection was lost; since synergy +is likely to be started by xdm or the user's xsession, it's +better for synergy to simply terminate when the connection +is lost. synergy will still restart due to other errors. +also fixed numerous other minor bugs and cleaned some stuff +up (like app error codes are now consistent and enumerated +in Version.h, for lack of a better place). and boosted +version and protocol numbers. + +---------- +2002/07/23 19:00:01 crs +notes + +checkpoint. + +---------- +2002/07/23 18:59:44 crs +platform/CMSWindowsClipboard.cpp + +fixed a bug in clipboard conversion (was using wrong converter or +no converter when one was available). + +---------- +2002/07/23 18:59:15 crs +client/CMSWindowsSecondaryScreen.cpp +server/CMSWindowsPrimaryScreen.cpp +synergy/KeyTypes.h + +converted win32 to use unicode based KeyID. + +---------- +2002/07/23 17:04:41 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/KeyTypes.h + +checkpoint. converting KeyID to use UTF-32 encoding instead of +X11 keysyms. + +---------- +2002/07/23 15:34:05 crs +synergy/CClipboard.cpp +synergy/IClipboard.h + +no longer attempting to unmarshall clipboard formats that aren't +known to the caller. if the client supports more formats than +the server then the server could get a clipboard format greater +than kNumFormats. with this change the server discards the +extra formats instead of crashing. + +---------- +2002/07/23 15:26:40 crs +base/CUnicode.cpp +base/CUnicode.h +base/base.dsp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsClipboard.h +platform/CMSWindowsClipboardAnyTextConverter.cpp +platform/CMSWindowsClipboardAnyTextConverter.h +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardTextConverter.h +platform/CMSWindowsClipboardUTF16Converter.cpp +platform/CMSWindowsClipboardUTF16Converter.h +platform/platform.dsp + +unicode clipboard changes for win32 plus some bug fixes. + +---------- +2002/07/23 12:35:36 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +removed unnecessary atoms from X clipboard object. + +---------- +2002/07/23 12:08:30 crs +base/CUnicode.cpp + +checkpoint. more CUnicode fixes. + +---------- +2002/07/23 11:51:13 crs +base/CUnicode.cpp + +checkpoint. fixed the other cases in the same function as the +previous checkin. also prevented the errors flag from getting +reset after the multibyte to wide character conversion. + +---------- +2002/07/23 11:42:54 crs +base/CUnicode.cpp + +checkpoint. fixed cases for mbrtowc (was using 1 and 2 instead +of -1 and -2). + +---------- +2002/07/23 11:36:18 crs +base/CUnicode.cpp +base/CUnicode.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboardTextConverter.cpp + +checkpoint. more UTF8 clipboard stuff. + +---------- +2002/07/23 09:33:50 crs +base/CUnicode.cpp +base/CUnicode.h +platform/CXWindowsClipboard.cpp + +checkpoint. more UTF8 clipboard testing. + +---------- +2002/07/22 18:46:57 crs +base/CUnicode.cpp +platform/CXWindowsClipboard.cpp + +checkpoint. more UTF8 clipboard stuff. + +---------- +2002/07/22 18:17:21 crs +platform/CXWindowsClipboard.cpp + +checkpoint. more UTF8 clipboard stuff. + +---------- +2002/07/22 18:03:44 crs +platform/CXWindowsClipboard.cpp + +checkpoint. working on UTF8 clipboard transfer. + +---------- +2002/07/22 17:32:51 crs +base/CUnicode.cpp +base/CUnicode.h +base/Makefile.am +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsClipboardTextConverter.cpp +platform/CXWindowsClipboardTextConverter.h +platform/CXWindowsClipboardUCS2Converter.cpp +platform/CXWindowsClipboardUCS2Converter.h +platform/CXWindowsClipboardUTF8Converter.cpp +platform/CXWindowsClipboardUTF8Converter.h +platform/Makefile.am +synergy/IClipboard.h + +checkpoint. adding support for unicode in clipboard. + +---------- +2002/07/19 21:27:59 crs +README + +changed notes about how to startup configure synergy. it now +discourages using boot scripts, which can't handle X servers +requiring authorization, and suggests modifying xdm's Xsetup. + +---------- +2002/07/19 20:44:57 crs +examples/synergy.linux.init +examples/synergyd.linux.init + +updated init.d scripts to work with SuSE. however, it looks as +if they cannot be used on an X server using authentication +because the daemons they start are not authorized to connect to +the X server. X users should modify Xsetup or Xsession. + +---------- +2002/07/19 18:12:41 crs +server/CXWindowsPrimaryScreen.cpp + +formatting. + +---------- +2002/07/19 17:39:45 crs +server/CPrimaryScreen.cpp + +removed from previous change. + +---------- +2002/07/19 17:38:34 crs +server/CPrimaryScreen.cpp + +reordered operations to reduce cursor flashing when entering +primary screen. + +---------- +2002/07/18 17:03:10 crs +platform/CSynergyHook.cpp +server/CMSWindowsPrimaryScreen.cpp + +fixed handling of calling init() when a previous process did not +call cleanup(). if that process still appears to exist then the +init() fails. otherwise some cleanup is performed and the init() +proceeds. a synergy server started while another is running will +now exit immediately without interfering the original server. + +---------- +2002/07/18 17:00:48 crs +server/CServer.cpp +server/CServer.h + +now cancelling and waiting for the accept client thread before +cancelling any other threads. this prevents a race condition +where we disconnect a client but it reconnects before we +manage to shutdown. that might leave a thread running and +the connection won't be closed down properly. + +---------- +2002/07/18 16:58:08 crs +mt/CThreadRep.cpp +platform/CMSWindowsScreen.cpp + +changed waitForEvent() to handle a peculiar feature of +MsgWaitForMultipleObjects(): it will not return immediately +if an event already in the queue when it's called was already +in the queue during the last call to GetMessage()/PeekMessage(). +also now discarding screen saver events if there are any other +screen saver events in the queue already. this prevents these +events from piling up in the queue, which they'd do because we +sleep for 250ms when handling each one. + +---------- +2002/07/18 08:54:17 crs +synergy.dsw + +fixed incorrect paths to makehook and synrgyhk project files. + +---------- +2002/07/17 17:27:41 crs +platform/CMSWindowsScreen.cpp +platform/CSynergyHook.cpp +platform/CSynergyHook.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h + +attempt to fix stuttering when leaving win32 screen. seems to +work but will let testers make the final call. also fixed +desktop synchronization by setting a variable that was +mistakenly left unset. and tried to work around an apparent +bug in MsgWaitForMultipleObjects() that prevented the service +from closing down properly. start/pause/continue/stop +sequence still doesn't shut down correctly. start/pause/stop +and start/stop work fine. + +---------- +2002/07/17 17:24:44 crs +client/CClient.cpp + +removed unnecessary local variable. + +---------- +2002/07/16 19:07:15 crs +base/stdistream.h +io/CStreamBuffer.cpp + +fixes to get it compiling on .NET. + +---------- +2002/07/16 16:52:26 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CXWindowsScreen.cpp +platform/IMSWindowsScreenEventHandler.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IServer.h + +moved onError() method to IScreenReceiver from IPrimaryScreenReceiver. +also implemented onError in CClient which previously did not have +any way to handle display disconnection. + +---------- +2002/07/15 15:03:04 crs +platform/CSynergyHook.cpp +platform/synrgyhk.dsp + +completing previous checkin. + +---------- +2002/07/15 15:01:36 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.dsp +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CSynergyHook.cpp +platform/CSynergyHook.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/IMSWindowsScreenEventHandler.h +platform/makehook.dsp +platform/platform.dsp +platform/synrgyhk.dsp +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/makehook.dsp +server/server.dsp +server/synrgyhk.dsp +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/ProtocolTypes.h +synergy/synergy.dsp + +checkpoint. refactored win32 code. had to edit and rename some +files so this is only a checkpoint. + +---------- +2002/07/13 22:00:38 crs +client/CClient.cpp +client/CClient.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/ISecondaryScreen.h +client/Makefile.am +client/client.cpp +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryScreen.h +server/Makefile.am +server/server.cpp +synergy/IClient.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IServer.h +synergy/Makefile.am + +checkpoint. still refactoring. merged common code from primary +screens into CPrimaryScreen and merged common code from secondary +screens into CSecondaryScreen. changed is-a relationship to a +has-a between the primary and secondary screen classes and the +generic platform dependent screen class to avoid multiple +inheritance of implementation. also standardized the interface +for those generic screen classes. adding a platform now involves +implementing simpler interfaces: IScreen for the generic screen, +IScreenEventHandler and some methods of CPrimaryScreen for the +primary screen, and IScreenEventHandler and some methods of +CSecondaryScreen for the secondary screen. did X11 platform +but not win32 platform. + +---------- +2002/07/12 20:41:23 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +refactoring. refactored stuff in client (with changes to server +as necessary). + +---------- +2002/07/11 18:58:49 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +checkpoint. making win32 and X primary screen code more similar +in order to share code later. + +---------- +2002/07/11 13:13:37 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CServerProxy.cpp +client/client.dsp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryScreen.h +server/server.dsp +synergy/synergy.dsp + +applied refactoring to win32 code. + +---------- +2002/07/10 21:22:28 crs +client/CClient.cpp +client/CClient.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/ISecondaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreenReceiver.h +synergy/IServer.h +synergy/Makefile.am + +more refactoring. + +---------- +2002/07/10 20:18:32 crs +client/CClient.cpp +client/CClient.h +client/CServerProxy.cpp +client/CServerProxy.h +client/ISecondaryScreen.h +client/Makefile.am +client/client.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +synergy/IClient.h +synergy/ISecondaryScreen.h +synergy/Makefile.am + +refactored client code. it now uses IClient and IServer and +has a CServerProxy, making it's design similar to the server +code. + +---------- +2002/07/10 14:29:50 crs +synergy/IClient.h + +removed some obsolete comments. + +---------- +2002/07/10 14:15:17 crs +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryReceiver.h +server/Makefile.am +synergy/IServer.h +synergy/ProtocolTypes.h + +removed IPrimaryReceiver in favor of IServer, which required a few +minor changes to support IPrimaryReciever's functionality. this +does mean that the IPrimaryScreen class will be calling some +methods with dummy arguments. those are documented in +CPrimaryClient. + +---------- +2002/07/09 21:22:31 crs +acinclude.m4 +acsite.m4 +config/depcomp +config/install-sh +config/missing +config/mkinstalldirs +mt/CThread.cpp +mt/CThread.h +notes +platform/CXWindowsClipboard.cpp +server/CClientProxy.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryReceiver.h +server/IPrimaryScreen.h +server/IServerProtocol.h +server/Makefile.am +synergy/IClient.h +synergy/IServer.h +synergy/Makefile.am + +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. + +---------- +2002/07/09 17:31:45 crs +server/IPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/Makefile.am + +checkpoint. moved IPrimaryScreen.h. + +---------- +2002/07/07 15:15:34 crs +server/IServerProtocol.h +synergy/IServerProtocol.h + +moved IServerProtocol to server from synergy directory. + +---------- +2002/07/03 16:25:36 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +fixed spurious mouse motions when entering/leaving primary +screen on X11. + +---------- +2002/07/01 15:05:49 crs +server/CMSWindowsPrimaryScreen.cpp + +mistakenly removed mouse button checks when on secondary screens +from isLockedToScreen() in earlier checkin. + +---------- +2002/07/01 14:01:23 crs +README +notes + +checkpoint. + +---------- +2002/07/01 13:03:16 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +synergy/ISecondaryScreen.h + +now synthesizing key release events for each pressed key when +the client screen is closed. this fixes the bug where the +client's keyboard was left with some keys logically pressed +when the client died (e.g. using ctrl+c on the client program +from the server's keyboard would leave the ctrl key logically +pressed). + +---------- +2002/07/01 13:01:16 crs +server/CServerProtocol1_0.cpp + +disabled removing client if no heartbeat is received. we don't +want that while testing because it might hide bugs. + +---------- +2002/07/01 13:00:12 crs +server/CMSWindowsPrimaryScreen.cpp + +fixed locking to screen on win32. was using GetKeyboardState() +to query keys but that doesn't give us up-to-date information. +now using GetAsyncKeyState() if on primary and m_keys if on +secondary. + +---------- +2002/07/01 12:58:52 crs +platform/CMSWindowsScreenSaver.cpp +platform/CMSWindowsScreenSaver.h + +added win32 screen saver class forgotten in previous checkins. + +---------- +2002/06/26 16:31:48 crs +client/CClient.cpp +http/CHTTPProtocol.cpp +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CInputStreamFilter.h +io/IInputStream.h +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp +server/CSynergyHook.cpp +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/ProtocolTypes.h + +synergy hook DLL will now restart itself if a client tries to +init() it while it's already running. fixed an uninitialized +pointer bug in CServer and some cleanup-on-error code in +CMSWindowsPrimaryScreen. also added timeout to read() on +IInputStream and a heartbeat sent by clients so the server +can disconnect clients that are dead but never reset the TCP +connection. previously the server would keep these dead +clients around forever and if the user was locked on the +client screen for some reason then the server would have to +be rebooted (or the server would have to be killed via a +remote login). + +---------- +2002/06/26 13:48:08 crs +client/CClient.cpp +client/CClient.h + +client now compresses mouse motion events. this fixes slow +dragging on grace, possibly on win32 too. + +---------- +2002/06/26 13:31:06 crs +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h + +fixed getSize() to be non-blocking in CInputPacketStream. + +---------- +2002/06/26 12:44:52 crs +platform/CXWindowsScreen.cpp +platform/CXWindowsScreenSaver.h + +fixed re-entrant calls to X bug. + +---------- +2002/06/23 23:24:22 crs +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h + +fixed handling of jumping to primary when screen saver starts +and back to secondary when it stops. also now redirecting +keyboard input to root window when screen saver starts; this +allows the user to type in the lock dialog and also effectively +discards any input used to deactivate the screen saver. + +---------- +2002/06/23 21:54:05 crs +notes + +checkpoint. + +---------- +2002/06/23 21:53:31 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/platform.dsp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CSynergyHook.cpp +server/CSynergyHook.h + +win32 screen saver now handled. + +---------- +2002/06/23 21:48:33 crs +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h + +now disabling disable job timer when forcing screen saver +activation. previously the timer would deactivate the screen +saver shortly after activation. job timer is restored when +the screen saver is deactivated. + +---------- +2002/06/23 15:43:40 crs +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +platform/CXWindowsUtil.cpp +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile.am +synergy/IPrimaryScreen.h + +checkpoint screensaver changes. now handling xscreensaver +dying and restarting or starting after synergy does. also +now disabling the screen saver on the client. next step: +win32 support. + +---------- +2002/06/22 20:29:59 crs +platform/CXWindowsScreen.cpp +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h + +fixes to get xscreensaver integration working. + +---------- +2002/06/22 19:47:27 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +server/CXWindowsPrimaryScreen.cpp + +CXWindowsUtil::CErrorLock wasn't XSync()'ing the display before +installing and uninstalling the new error handler, causing +errors before the lock to be caught and errors during the lock +to not be caught. had to add Display* as argument to c'tor. + +---------- +2002/06/22 19:20:21 crs +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +platform/Makefile.am +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/IScreenSaver.h +synergy/ISecondaryScreen.h +synergy/Makefile.am + +checkpoint. adding screen saver support. only on X so far +and untested. also some known problems: not detecting an +xscreensaver started after us and not detecting built-in +screen saver activation (not sure if we can without using +ugly extensions). + +---------- +2002/06/22 17:31:24 crs +client/Makefile.am +http/Makefile.am +io/Makefile.am +mt/Makefile.am +net/Makefile.am +platform/Makefile.am +server/Makefile.am +synergy/Makefile.am + +added header files to _SOURCES. + +---------- +2002/06/22 13:55:45 crs +server/CXWindowsPrimaryScreen.cpp + +added comments. + +---------- +2002/06/22 12:09:49 crs +client/CXWindowsSecondaryScreen.cpp + +cleanup. + +---------- +2002/06/21 17:55:47 crs +client/CClient.cpp +client/CClient.h +client/CXWindowsSecondaryScreen.cpp +client/client.cpp +server/CServer.cpp +server/CServer.h +server/server.cpp + +cleaned up some minor bugs. + +---------- +2002/06/21 17:54:22 crs +net/CNetwork.cpp +net/CNetwork.h + +ported network changes to win32. + +---------- +2002/06/21 16:29:35 crs +net/CNetworkAddress.cpp + +now trying to convert hostname as a dot notation address before +trying name lookup. not all platforms will do this for us in +gethostbyname(). + +---------- +2002/06/21 16:19:08 crs +net/CNetwork.cpp +net/CNetwork.h +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp + +fixed CTCPSocket::connect() to allow cancellation. + +---------- +2002/06/21 15:18:01 crs +server/CServer.cpp +server/CServer.h + +some cleanup. also fixed a race condition when adding threads +to the thread list: the child thread would add itself to the +list which means there could be a time interval in the parent +where the child thread exists but isn't on the list. the +parent now does the adding and removing. + +---------- +2002/06/21 15:15:34 crs +platform/CUnixPlatform.cpp + +now blocking SIGINT and SIGTERM in restart function. the child +should handle the signal and terminate. then the restart +function will exit. + +---------- +2002/06/21 15:14:32 crs +mt/CThreadRep.cpp + +signal handler thread now dies when SIGABRT is raised. ignoring +SIGABRT in sigwait() seems to be a bug in the linux pthread +library. + +---------- +2002/06/20 16:27:49 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +fixed bug introduced by previous checkin. calling XCheckIfEvent() +multiple times is *not* the same as calling XIfEvent() because the +former will re-encounter events that it didn't process previously. +to make things simple it now pulls events off the queue and saves +them if not processed for selection transfer and puts them back +afterwards. + +---------- +2002/06/20 14:01:44 crs +platform/CXWindowsClipboard.cpp + +speeded up clipboard transfer by avoiding a selection request +when it wasn't necessary. (in particular, we were getting the +clipboard update time from the owner then emptying the clipboard, +so we didn't need to get the time. worse, most owners don't +support getting the time and we often timed out.) + +also fixed a multithread bug using the X display. we were using +a CThread to send an event after a timeout while we were waiting +in XIfEvent(). this necessarily involved two threads calling +into Xlib at once, which is not allowed. now using polling to +do the timeout because Xlib doesn't have a function to get +events with a timeout. + +---------- +2002/06/20 13:35:28 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +checkpoint. trying to fix a delay when sending clipboards on X. + +---------- +2002/06/20 11:13:37 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h + +added workaround for bug windows 98 (Me?) and multiple displays: +absolute mouse_event() moves don't work except for primary +display. + +---------- +2002/06/20 09:19:55 crs +server/CXWindowsPrimaryScreen.cpp + +work around for bug with mouse driver on lombard powerbook. + +---------- +2002/06/19 20:24:35 crs +client/CMSWindowsSecondaryScreen.cpp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +fixed bugs in mouse motion. wasn't taking care to capture all +motion events relative to the previous mouse position. for +example, if two mouse events arrive, the first at x+1,y and +the second at x+2,y, we used to compute deltas of 1,0 and 2,0 +instead of 1,0 and 1,0. that's fixed. also worked around a +bug (probably) in windows that caused a motion event after a +SetCursorPos() to be lost or reported one pixel off from the +correct position. now using mouse_event() which doesn't +have that problem. also fixed calculation of normalized +coordinates for mouse_event() when there are multiple +displays. + +---------- +2002/06/19 17:03:29 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol1_0.cpp +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/ProtocolTypes.h + +checkpoint. initial support for multiple displays on win32. + +---------- +2002/06/19 14:45:22 crs +client/Makefile.am +configure.in +server/Makefile.am + +fixed addition of X11 -L and -l options on link lines. + +---------- +2002/06/19 12:21:26 crs +configure.in +platform/CUnixPlatform.cpp + +checkpoint. automake changes for wait(). + +---------- +2002/06/19 11:58:48 crs +configure.in +http/CHTTPProtocol.cpp +platform/CUnixPlatform.cpp + +checkpoint. automake changes for reentrant functions. + +---------- +2002/06/19 11:23:49 crs +base/BasicTypes.h +base/CLog.cpp +base/CLog.h +base/CStopwatch.cpp +base/XBase.cpp +base/common.h +base/stdistream.h +base/stdostream.h +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +client/client.cpp +configure.in +mt/CCondVar.cpp +mt/CCondVar.h +mt/CMutex.cpp +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +net/CNetwork.cpp +net/CNetwork.h +platform/CPlatform.cpp +platform/CPlatform.h +platform/CXWindowsClipboard.h +platform/CXWindowsScreen.h +platform/CXWindowsUtil.h +server/CServer.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/server.cpp +synergy/CProtocolUtil.h + +checkpoint. more conversion to automake. + +---------- +2002/06/19 08:23:08 crs +config/install-sh +config/missing +config/mkinstalldirs +configure.in +install-sh +missing +mkinstalldirs + +moved auxillary automake files into config directory. + +---------- +2002/06/18 19:47:52 crs +install-sh +missing +mkinstalldirs + +added automake required tools. + +---------- +2002/06/18 19:44:34 crs +Make-linux +Make-solaris +Makecommon +Makefile +Makefile.am +acsite.m4 +base/Makefile +base/Makefile.am +client/Makefile +client/Makefile.am +configure.in +http/Makefile +http/Makefile.am +io/Makefile +io/Makefile.am +mt/Makefile +mt/Makefile.am +net/Makefile +net/Makefile.am +platform/Makefile +platform/Makefile.am +server/Makefile +server/Makefile.am +synergy/Makefile +synergy/Makefile.am +tools/depconv + +started to convert to autoconf/automake. + +---------- +2002/06/18 18:34:55 crs +base/CLog.cpp + +now checking vsnprintf result against < 0 instead of == -1 +for portability. + +---------- +2002/06/18 18:33:59 crs +net/XSocket.cpp + +added FIXME to commented out code. + +---------- +2002/06/17 15:44:45 crs +README +client/client.cpp +server/server.cpp + +made command line parsing a little more sane with respect to +windows NT services. + +---------- +2002/06/17 14:10:25 crs +README + +updates + +---------- +2002/06/17 13:31:21 crs +base/CLog.cpp +base/CString.cpp +base/XBase.cpp +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.cpp +client/client.cpp +http/CHTTPProtocol.cpp +http/XHTTP.cpp +io/CBufferedInputStream.cpp +io/CBufferedOutputStream.cpp +io/CInputStreamFilter.cpp +io/COutputStreamFilter.cpp +io/CStreamBuffer.cpp +mt/CCondVar.cpp +mt/CMutex.cpp +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CTimerThread.cpp +net/CNetwork.cpp +net/CNetworkAddress.cpp +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp +net/XNetwork.cpp +net/XSocket.cpp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsScreen.cpp +platform/CUnixPlatform.cpp +platform/CWin32Platform.cpp +platform/CXWindowsClipboard.cpp +platform/CXWindowsScreen.cpp +platform/CXWindowsUtil.cpp +server/CConfig.cpp +server/CHTTPServer.cpp +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CServerProtocol.cpp +server/CServerProtocol1_0.cpp +server/CSynergyHook.cpp +server/CXWindowsPrimaryScreen.cpp +server/server.cpp +synergy/CClipboard.cpp +synergy/CInputPacketStream.cpp +synergy/COutputPacketStream.cpp +synergy/CProtocolUtil.cpp +synergy/XSynergy.cpp + +formatting changes. + +---------- +2002/06/17 12:02:26 crs +client/CClient.cpp +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IDataSocket.h +net/IListenSocket.h +net/ISocket.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CServer.cpp +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/ISocketFactory.h + +refactored ISocket into IDataSocket. the latter and IListenSocket +now derive from ISocket. + +---------- +2002/06/14 18:08:20 crs +base/CFunctionJob.cpp +base/CFunctionJob.h +base/CLog.cpp +base/TMethodJob.h +base/common.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/client.cpp +io/CStreamBuffer.cpp +io/CStreamBuffer.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +platform/CMSWindowsScreen.cpp +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/IPlatform.h +server/server.cpp + +performance fixes on win32 plus clean up of some warnings. also +improved error messages when uninstalling service. + +---------- +2002/06/11 20:10:49 crs +README + +added a blurb about synrgyhk.dll and that the service manager +will look for the binary wherever it was when --install was +used. + +---------- +2002/06/11 20:09:59 crs +base/stdpre.h +platform/CMSWindowsScreen.h + +windows fixes needed for formatting changes. + +---------- +2002/06/11 18:33:03 crs +client/CXWindowsSecondaryScreen.cpp + +commented out half-duplex flags that should never have been +uncommented. + +---------- +2002/06/11 18:31:06 crs +server/CServer.cpp + +fixed bug with switching screens on primary when there's no +link in that direction (it would assert). introduced bug +when adding support for wrapping. now ignores attempts to +move in a direction with no link. + +---------- +2002/06/11 18:30:08 crs +platform/CUnixPlatform.cpp + +added missing #include . + +---------- +2002/06/10 22:06:45 crs +base/BasicTypes.h +base/CFunctionJob.cpp +base/CLog.cpp +base/CLog.h +base/CString.cpp +base/CString.h +base/TMethodJob.h +base/XBase.cpp +base/XBase.h +base/common.h +base/stdistream.h +base/stdostream.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.cpp +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/XHTTP.cpp +http/XHTTP.h +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/COutputStreamFilter.cpp +io/CStreamBuffer.cpp +io/IInputStream.h +io/IOutputStream.h +io/XIO.cpp +io/XIO.h +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CTimerThread.cpp +mt/CTimerThread.h +mt/XThread.h +net/CNetwork.cpp +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +net/Makefile +net/XNetwork.cpp +net/XNetwork.h +net/XSocket.cpp +net/XSocket.h +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CPlatform.cpp +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +platform/IPlatform.h +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/server.cpp +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CInputPacketStream.cpp +synergy/COutputPacketStream.cpp +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.cpp +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h +synergy/XScreen.cpp +synergy/XSynergy.cpp + +indentation and other formatting changes. also cleaned up +#includes. + +---------- +2002/06/10 16:49:46 crs +base/CLog.cpp +base/CStopwatch.cpp +mt/CCondVar.cpp +mt/CMutex.cpp +mt/CThreadRep.h +net/CNetwork.cpp +net/CNetwork.h +platform/CMSWindowsClipboard.h +platform/CMSWindowsScreen.h +platform/CWin32Platform.h +server/CSynergyHook.h +server/synrgyhk.dsp + +win32 changes. now including windows.h with WIN32_LEAN_AND_MEAN +to avoid including some stuff we don't want (like winsock). + +---------- +2002/06/10 11:09:02 crs +README + +fixes. + +---------- +2002/06/10 11:08:02 crs +README + +updates. + +---------- +2002/06/10 11:00:55 crs +README +examples/synergy.conf +examples/synergy.linux.init +examples/synergyd.linux.init + +added example files and a README. + +---------- +2002/06/10 10:08:36 crs +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp + +now allowing a screen to be its own neighbor to allow wrapping. +also no longer warping mouse to 0,0 when setting server screen +info. that was causing the mouse to jump if the server screen +had itself as its left or top neighbor (directly or indirectly) +once a screen could be its own neighbor. + +---------- +2002/06/10 09:49:21 crs +server/CConfig.cpp + +fixed stripping of comments from configuration streams. + +---------- +2002/06/10 09:49:03 crs +client/client.cpp +server/server.cpp + +changed "permitted" to "supported" in error messages. + +---------- +2002/06/09 23:08:18 crs +client/CClient.cpp + +no longer camps if the server sends an error message. + +---------- +2002/06/09 22:20:28 crs +client/CClient.cpp +client/CClient.h +client/client.cpp + +added support for camping, i.e. repeatly trying to connect to the +server until we succeed. + +---------- +2002/06/09 22:20:01 crs +mt/CTimerThread.cpp +mt/CTimerThread.h + +CTimerThread now allows zero and negative timeouts. a negative +timeout never times out and CTimerThread is a no-op. + +---------- +2002/06/09 18:03:32 crs +platform/CXWindowsScreen.cpp + +now using ":0.0" as the display if DISPLAY isn't set. + +---------- +2002/06/09 18:00:03 crs +notes + +checkpoint. + +---------- +2002/06/09 17:59:32 crs +client/client.cpp +server/CServer.cpp +server/CServer.h +server/server.cpp + +added command line option to choose the screen name. also now +using the hostname as the default name. this is on both client +and server. + +---------- +2002/06/09 17:35:28 crs +mt/CThreadRep.cpp + +added FIXME comment. + +---------- +2002/06/09 17:21:33 crs +server/CServer.cpp + +fixed problem with setConfig(). if the new config didn't +include a screen that was already connected under an alias +then that screen wouldn't be disconnected and removed from +the screen list until the screen voluntarily disconnected. +at that time removeConnection() would assert because the +screen name would not be found. now using the canonical +name in the protocol object as well as CServer. this +allows setConfig() to always detect removed screens and +disconnect them. + +---------- +2002/06/09 16:53:57 crs +platform/CUnixPlatform.cpp + +now exits instead of restarting if child dies due to an +unexpected signal. + +---------- +2002/06/09 16:53:25 crs +client/client.cpp +net/CNetwork.cpp +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/XSocket.cpp +net/XSocket.h +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CServer.cpp +server/CServer.h +server/server.cpp +synergy/ProtocolTypes.h + +added command line and configuration file arguments to choose +the address and port to listen on or connect to. changed the +default port and put it in ProtocolTypes.h. the HTTP port is +now no longer opened unless the --http argument is supplied +or the config file includes it. + +---------- +2002/06/08 23:24:40 crs +server/CConfig.cpp +server/CConfig.h +server/CServer.cpp +server/server.cpp + +added aliases to configuration. an alias is another name for +a screen. it's expected that the server will want to accept +a given client under several names (e.g. the hostname and the +FQDN). + +---------- +2002/06/08 21:48:16 crs +notes + +checkpoint. + +---------- +2002/06/08 21:48:00 crs +all.dsp +base/CLog.cpp +base/CLog.h +base/base.dsp +base/common.h +base/stdpre.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/client.cpp +client/client.dsp +http/http.dsp +io/io.dsp +mt/CThreadRep.cpp +mt/mt.dsp +net/net.dsp +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/IPlatform.h +platform/platform.dsp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CSynergyHook.cpp +server/CSynergyHook.h +server/makehook.dsp +server/server.cpp +server/server.dsp +server/synrgyhk.dsp +synergy/IPrimaryScreen.h +synergy/synergy.dsp + +win32 changes. changed names of binaries. added support for +running as (and installing/installing) a service. added +support for multiple desktops (NT only, 95 doesn't support +multiple desktops). + +---------- +2002/06/04 12:26:23 crs +Makefile +client/Makefile +client/client.cpp +client/client.dsp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsClipboard.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CPlatform.cpp +platform/CPlatform.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +platform/IPlatform.h +platform/Makefile +server/Makefile +server/server.cpp +server/server.dsp +synergy.dsw +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/CXWindowsUtil.cpp +synergy/CXWindowsUtil.h +synergy/Makefile +synergy/synergy.dsp +test.cpp + +refactored some common platform dependent stuff into a new +library: platform. also removed test.cpp. + +---------- +2002/06/04 11:06:26 crs +client/CClient.cpp +client/client.cpp +server/CServer.cpp +server/CServerProtocol.cpp +server/server.cpp +synergy/ProtocolTypes.h +synergy/Version.h + +added command line parsing, restartability, and daemonizing to +client. broke win32 stuff though. also moved version and +copyright constants into a new file and renamed protocol +version constants. + +---------- +2002/06/04 11:03:34 crs +base/CLog.cpp + +fixed delete bug in printt -- when skipping file and line the +deleted pointer was wrong. + +---------- +2002/06/04 11:02:33 crs +synergy/CXWindowsClipboard.cpp + +fixed timeout when getting selection -- forgot to set flag to +terminate event loop. + +---------- +2002/06/03 18:53:18 crs +base/CLog.cpp +base/CLog.h +server/CConfig.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/server.cpp +synergy/CXWindowsScreen.cpp +synergy/IPrimaryScreen.h + +changes to add command line arguments. also added automatic +restarting and daemonizing on unix. daemon sends log messages +to syslog. unix now reads config file from file named on +command line; if no command line arg then uses effective +user's config file and if that's not there it finally tries +/etc/synergy.conf. if there are no screens configured then +one is added for the primary screen. broke some startup +stuff on win32. + +also now timing out if X primary screen can't grab the mouse +and keyboard. the server will just give up trying to switch +screens. the grabs will fail is some other app has a grab +and won't release it. note that kdm grabs the keyboard for +the duration that the login window is displayed, effectively +disabling synergy. + +---------- +2002/06/03 16:36:45 crs +base/CLog.cpp +base/CLog.h + +added a method to set the filter given a priority string (instead +of a number). fixed a comment related to what those priority +strings are. added a CLOG_PRINT priority which is never filtered +and suppresses the trace info and the priority level message. +it's intended as a way to output a message through the logger +without getting extra output. + +---------- +2002/06/03 16:34:22 crs +base/CString.cpp +base/CString.h +base/Makefile +base/base.dsp +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h + +moved case insensitive comparison utility functions into CString +from CHTTPProtocol. + +---------- +2002/06/03 13:45:30 crs +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/Makefile +synergy/XScreen.cpp +synergy/XScreen.h +synergy/synergy.dsp + +added better handling of X server disconnecting unexpectedly. +the apps still exit but they do it in a mostly controlled +manner. in particular, the server threads except the one +processing primary screen events will terminate gracefully. +this will be important should the server ever allow HTTP +clients to rewrite the configuration file. + +note that X makes it effectively impossible to continue once +the X server disconnects. even if it didn't it would be +difficult for synergy to recover. users will have to add +synergy to the X display manager's startup script if they +expect the server to be restarted. alternatively, we could +add code to fork synergy at startup; the child would do +the normal work while the parent would simply wait for the +child to exit and restart it. + +---------- +2002/06/02 23:07:57 crs +net/CTCPListenSocket.cpp + +shortened poll() timeout. + +---------- +2002/06/02 22:57:50 crs +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +net/CTCPSocket.cpp + +changed buffered output stream to wait() when flush()ing instead +of polling/sleeping. changed CTCPSocket to not use thread +cancellation but to instead use m_connected to exit the thread. +also shortened poll timeout. + +---------- +2002/06/02 21:35:20 crs +synergy/CMSWindowsScreen.cpp +synergy/CXWindowsScreen.cpp + +make sleep shorter in poll/sleep getEvent() loops. + +---------- +2002/06/02 21:03:38 crs +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsUtil.cpp + +removed poll/sleep code to improve performance. + +---------- +2002/06/02 19:04:24 crs +client/CXWindowsSecondaryScreen.cpp + +now ignores key if there's no key mapped for a required modifier. +was asserting (on the wrong expression). + +---------- +2002/06/02 18:49:35 crs +client/CClient.cpp +client/client.cpp +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +server/CServer.cpp +server/server.cpp + +added SIGINT and SIGTERM handling to unix client and server. +either signal causes the main thread to be cancelled. added +necessary code to make main thread cancellation clean up +nicely. + +---------- +2002/06/02 13:34:35 crs +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +server/CHTTPServer.cpp +server/CHTTPServer.h + +added a maximum request size to CHTTPProtocol so we can bail +on clients that cause us to use too much memory. also put +methods in CHTTPRequest to get/set headers and changed the +data structure used to store them. fixed a couple of other +miscellaneous bugs in CHTTPProtocol.cpp. + +---------- +2002/06/02 11:49:46 crs +mt/CCondVar.h +server/CServer.cpp +server/CServer.h + +now limiting number of simultaneous HTTP requests being handled +at once. this is to prevent denial of service. + +---------- +2002/06/01 19:26:11 crs +base/CLog.cpp +base/CLog.h +base/CString.h +base/XBase.h +base/base.dsp +base/common.h +base/stdfstream.h +base/stdistream.h +base/stdlist.h +base/stdmap.h +base/stdostream.h +base/stdpost.h +base/stdpre.h +base/stdset.h +base/stdsstream.h +base/stdvector.h +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.h +client/client.cpp +client/client.dsp +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/XHTTP.cpp +http/http.dsp +io/CStreamBuffer.h +io/io.dsp +mt/mt.dsp +net/net.dsp +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/makehook.dsp +server/server.cpp +server/server.dsp +server/synrgyhk.dsp +synergy.dsw +synergy/CXWindowsClipboard.h +synergy/IPrimaryScreen.h +synergy/synergy.dsp + +fixes, mainly for windows. first, had to add a notification from +CServer to the primary screen when the configuration changes so it +can make necessary adjustments (the win32 primary screen must tell +the hook dll about the new jump zones). + +changed includes of some std c++ library files to go through +our own include files. these wrap the include with stuff to +keep vc++ quiet when compiling at warning level 4, which is +what it does now. it also works around missing and + on g++2.96. + +added missing std:: where necessary. g++ doesn't really support +namespaces so it lets references without the namespace slip +through. + +added workaround or fix. not sure if istringstream::str(string) +should reset eofbit. it does on g++ but does not on vc++. +added clear() after str() so it works either way. + +added low-level keyboard hook to win32. if available (it's only +available on NT SP3 and up) it allows us to catch and handle +alt+tab, alt+esc, ctrl+esc, and windows key hot keys. i think +that leaves only ctrl+alt+del and accessibility functions +uncaught on those systems. + +---------- +2002/06/01 10:52:02 crs +server/CServer.cpp +server/CServer.h +server/CServerProtocol1_0.cpp + +added mutex to all public methods that didn't already have it. +fixed two blown assertions. first, if user tried to switch to +a client that had connected but hadn't yet sent the first info +message it would assert on the zero size screen. second, if +the primary screen was handling a mouse motion on behalf of a +secondary screen when that secondary screen disconnected then +an assert would blow because the primary screen would call +onMouseMoveSecondary() but m_protocol on the active screen is +NULL because disconnecting the active secondary screen caused +the mouse to jump to the primary screen. + +---------- +2002/05/31 18:35:53 crs +server/CConfig.h + +changed iterator to use iterator_traits directly instead of +std::iterator to support the old STL on grace. + +---------- +2002/05/31 18:18:29 crs +client/CClient.cpp +client/CClient.h +server/CServer.cpp +synergy/ProtocolTypes.h +synergy/XSynergy.cpp +synergy/XSynergy.h + +server now rejects clients that are not in the configuration. +added a protocol message to indicate this. + +---------- +2002/05/31 18:09:43 crs +server/CServer.cpp +server/CServer.h +server/CServerProtocol1_0.cpp + +fixed setConfig() to disconnect secondary screens that aren't +in the new configuration. + +---------- +2002/05/31 18:08:08 crs +server/CConfig.cpp +server/CConfig.h + +made isScreen() a const method. + +---------- +2002/05/31 17:32:26 crs +server/CConfig.cpp +server/CConfig.h +server/server.cpp + +added I/O for configuration files and changed the server to use +an external file for its configuration (was hard coding a config +for testing). + +---------- +2002/05/31 14:44:54 crs +server/CConfig.cpp +server/CScreenMap.cpp +server/Makefile +server/server.dsp + +finished renaming CScreenMap to CConfig. + +---------- +2002/05/31 14:43:23 crs +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CScreenMap.cpp +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/server.cpp + +checkpoint. changed CScreenMap to CConfig. must still change +CScreenMap.cpp to CConfig.cpp. + +---------- +2002/05/31 14:34:16 crs +server/CConfig.h +server/CHTTPServer.cpp +server/CScreenMap.cpp +server/CScreenMap.h +server/CServer.h +server/CSynergyHook.cpp +server/server.cpp + +checkpoint. renamed CScreenMap.h to CConfig.h. will be +changing CScreenMap to CConfig everywhere. + +---------- +2002/05/31 14:25:26 crs +base/CLog.cpp +base/CLog.h +client/client.cpp +server/server.cpp + +added methods to CLog for getting the outputter, getting and +setting the priority filter, and added code for thread safety. +added code to apps to enable thread safety in CLog. + +---------- +2002/05/30 16:13:16 crs +Makefile +base/common.h +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/Makefile +http/XHTTP.cpp +http/XHTTP.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CScreenMap.cpp +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/Makefile + +added basic support for an embedded HTTP server. server +currently supports editing the screen map but changing +the map won't behave correctly if there are connected +screens. + +---------- +2002/05/30 16:11:59 crs +net/CTCPSocket.cpp +net/CTCPSocket.h + +fixed bug in closing down a socket. + +---------- +2002/05/27 18:55:51 crs +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h + +updated win32 clipboard to match new model. + +---------- +2002/05/27 18:35:14 crs +notes + +checkpoint + +---------- +2002/05/27 18:30:13 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsScreen.h +synergy/CXWindowsUtil.cpp +synergy/CXWindowsUtil.h + +removed getEventMask() from primary screen. added a class to +CXWindowsUtil that installs/uninstalls an X error hander. +using that in primary screen, clipboard, and util to ensure +that certain errors don't kill the app. + +---------- +2002/05/27 18:28:06 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h + +removed getEventMask() and fixed some comments. also now using +toggle key states in updateModifiers(). + +---------- +2002/05/27 17:05:34 crs +synergy/CXWindowsClipboard.cpp + +changed lesstif hack to only apply to the CLIPBOARD selection. +apprently the PRIMARY selection must follow the ICCCM protocol +correctly. + +---------- +2002/05/27 16:51:07 crs +synergy/CXWindowsUtil.cpp +synergy/CXWindowsUtil.h + +added missing files from previous submit. + +---------- +2002/05/27 16:22:59 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/Makefile + +checkpoint. changed clipboard model. the clipboard can only +be accessed now between open()/close(). ownership of the +clipboard is asserted via the empty() method. this parallels +the win32 model (but the win32 code hasn't been updated yet). + +refactored X11 clipboard code. moved the bulk of it into +CXWindowsClipboard and moved some comment event handling into +CXWindowsScreen. changed how requests are processed into a +hopefully easier to understand model. added support for getting +clipboard from and sending clipboard to motif (or at least +lesstif) clients. sending to lesstif required a hack to work +around an apparent bug in lesstif. + +---------- +2002/05/24 17:54:34 crs +notes + +checkpoint + +---------- +2002/05/24 17:54:28 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +added screen locking support to win32. added support for +resolution changing (only semi-supported on X because that +has no means for resizing screen anyway). also fixed some +clipboard problems on win32. + +---------- +2002/05/24 14:37:12 crs +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h + +added support for locking to a screen when the sroll lock is +toggled on or when any key or button is pressed. fully +implemented on X but stubbed out for now on win32. + +---------- +2002/05/23 18:35:15 crs +notes + +checkpoint + +---------- +2002/05/23 18:35:08 crs +server/CMSWindowsPrimaryScreen.cpp +server/CSynergyHook.cpp +server/CSynergyHook.h + +added support for mouse wheel on win32. + +---------- +2002/05/23 15:50:38 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp + +added support for mouse wheel on X. + +---------- +2002/05/23 15:00:39 crs +server/server.cpp + +added a third screen to hard coded map for testing purposes. + +---------- +2002/05/23 15:00:13 crs +server/CServer.cpp + +fixed log message. + +---------- +2002/05/23 14:56:03 crs +client/CClient.cpp +client/CClient.h +server/CServer.cpp +server/CServerProtocol.cpp +server/CServerProtocol1_0.cpp +synergy/ProtocolTypes.h +synergy/XSynergy.cpp +synergy/XSynergy.h + +server no longer asserts when a client connects with a name that's +already in use by another client. also added reporting of errors +from the server to clients so clients can report meaningful +messages to users. + +---------- +2002/05/23 14:04:43 crs +notes + +checkpoint + +---------- +2002/05/23 14:04:35 crs +net/CNetwork.h +synergy/CXWindowsScreen.h + +changed structs to classes. there should be no more structs now. + +---------- +2002/05/22 17:09:08 crs +notes + +checkpoint. + +---------- +2002/05/22 17:08:37 crs +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CProtocolUtil.cpp + +removed unnecessary call in screen class, added logging calls +in clipboard class, and added another cast in protocol util +to avoid warning on win32. + +---------- +2002/05/22 17:05:26 crs +server/CSynergyHook.cpp + +now letting some key events filter through. this allows the +keyboard lights to track toggle changes. however, it also +seems to let through keyboard events that shouldn't get +through. + +---------- +2002/05/22 17:02:58 crs +server/CScreenMap.cpp + +fixed incorrect for-loop over directions conditional. + +---------- +2002/05/22 17:01:17 crs +base/CLog.cpp +base/CLog.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h + +win32 changes. replaced log dialog hack with a windows console +window. now attaching thread input queues as necessary. shifted +code around so toggling toggle keys is immediately reflected by +secondary screen's keyboard. now setting extended key flag for +keys that need it. fixed handling of shift + caps-lock. added +handling of keys that should distinguish between left and right +but don't. fixed get/set of active window on leave/enter of +primary screen. replaced 1x1 primary window with a full screen +window to work around a problem with losing key events. changed +calculation of mouse move deltas. + +---------- +2002/05/22 16:56:06 crs +net/CTCPListenSocket.cpp + +fixed type of socket handle (from int to CNetwork::Socket). + +---------- +2002/05/22 16:55:19 crs +mt/CTimerThread.cpp + +removed blank line. + +---------- +2002/05/22 16:55:05 crs +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp + +changed un-inlined code to avoid bogus VC++ level 4 warnings. +added support for more win32 thread priorities. + +---------- +2002/05/22 16:51:59 crs +client/client.cpp + +fixed parameter type for socket port. + +---------- +2002/05/22 16:43:14 crs +base/common.h + +changed set of disabled win32 warnings. + +---------- +2002/05/22 16:42:48 crs +client/CClient.cpp + +fixed NULL dereference. + +---------- +2002/05/22 16:41:24 crs +base/common.h + +changed set of disabled win32 warnings. + +---------- +2002/05/22 16:40:51 crs +base/CLog.cpp +base/CLog.h + +replaced logging dialog hack with a windows console window. + +---------- +2002/05/22 16:40:38 crs +client/CClient.cpp + +fixed NULL dereference. + +---------- +2002/05/05 23:37:12 crs +net/CTCPSocket.cpp + +removed setting send buffer to zero size. it just reduced +performance. + +---------- +2002/05/05 19:52:03 crs +client/CXWindowsSecondaryScreen.cpp + +replaced True/False with true/false when assigning to m_repeat. +also should now work if the first element of a modifier +keymapping is 0. that won't normally be the case but xmodmap +was doing weird things on grace. if the first element is 0 +it'll try the second element. if that's also zero then that +modifier will be ignored. + +---------- +2002/05/05 19:38:09 crs +client/CMSWindowsSecondaryScreen.cpp +server/CMSWindowsPrimaryScreen.cpp + +fixes for win32 keyboard. + +---------- +2002/05/04 19:43:20 crs +client/CXWindowsSecondaryScreen.cpp + +fixed caps-lock handling. + +---------- +2002/05/04 18:33:48 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +checkpoint. added half duplex for num lock. + +---------- +2002/05/04 18:31:54 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h + +checkpoint. fixing up handling of half-duplex num-lock. + +---------- +2002/05/04 18:09:02 crs +client/CXWindowsSecondaryScreen.cpp + +checkpoint. changed when toggle keys toggle (now always on +release). must see if this works. + +---------- +2002/05/04 18:08:22 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +net/CTCPSocket.cpp +server/CMSWindowsPrimaryScreen.cpp + +Fixes for win32 key handling. + +---------- +2002/05/04 11:23:11 crs +client/CXWindowsSecondaryScreen.cpp + +fixed handling of shift + caps-lock. those two modifiers should +cancel out if the keysym is subject to case conversion, but not +otherwise. also added logging of key lookup code. + +---------- +2002/05/03 12:23:48 crs +client/CXWindowsSecondaryScreen.cpp + +fixed handling of shift+tab on a system that can map ISO_Left_Tab. +now tries to map ISO_Left_Tab without shift first then falls back +to Tab (note that if ISO_Left_Tab can be mapped but requires a +modifier then the modifier will be added). also changed attempt +to map ISO_Left_Tab as a backup to Tab to request the shift +modifier whether or not the primary screen requested it. + +---------- +2002/05/03 12:14:55 crs +client/CXWindowsSecondaryScreen.cpp + +fixed handling of ISO_Left_Tab when that is not mapped to a +keycode by mapping it to tab with shift pressed. + +---------- +2002/05/03 11:49:30 crs +client/CXWindowsSecondaryScreen.cpp + +removed attempt to make release/press of a repeating key use +the same server time. was getting what appears to be deadlock +but not sure why. + +---------- +2002/05/03 11:26:44 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +checkpoint. made changes to support key autorepeats on X. + +---------- +2002/05/02 11:44:21 crs +synergy/COutputPacketStream.cpp + +Indentation change. + +---------- +2002/05/02 11:43:52 crs +io/CStreamBuffer.cpp +net/CTCPSocket.cpp + +Fixed bug in stream buffer that could cause data to be +inserted out of order. Also removed unnecessary limit +on writes to the TCP socket. + +---------- +2002/05/02 11:33:34 crs +io/CStreamBuffer.cpp + +checkpoint debugging of stream buffer. + +---------- +2002/05/01 16:30:20 crs +synergy/CXWindowsScreen.cpp + +Was trying to avoid sending clipboard if timestamp wasn't +changed but clipboard owners may not update that timestamp +when the selection is changed. Disabled the timestamp check. + +---------- +2002/05/01 16:17:57 crs +server/CServer.cpp +synergy/CXWindowsScreen.cpp + +Added more checks to avoid sending unchanged clipboard data. +Still takes too long to query the clipboard owner for info +(maybe 1/10th second) but not sure why or if that can be +improved. + +---------- +2002/05/01 15:31:47 crs +Make-linux +net/CNetwork.h +net/CTCPSocket.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +checkpoint. turned off nagle and send buffering. also +added test to skip clipboard conversion if a previous +conversion from that owner failed. + +---------- +2002/05/01 14:36:52 crs +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp + +Fixed uninitialized variable when computing toggle mask. Also +reduced priority of some mouse motion log messages. + +---------- +2002/05/01 14:35:55 crs +net/CSocketInputStream.cpp +net/CSocketInputStream.h +net/CSocketOutputStream.cpp +net/CSocketOutputStream.h +net/CSocketStreamBuffer.cpp +net/CSocketStreamBuffer.h +net/net.dsp + +removed obsolete files. + +---------- +2002/04/30 18:30:05 crs +client/CXWindowsSecondaryScreen.cpp + +added fallback for missing numpad movement keys (if there's no +mapping for those keys then the non-keypad versions are tried). + +---------- +2002/04/30 17:48:11 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +checkpoint. now sending toggle modifier state when entering +a screen. this allows the secondary screen to set it's +modifier state to match the primary screen's state. this is +not strictly necessary since each keystroke should adjust the +modifier state as needed to get the right result. + +---------- +2002/04/30 16:25:29 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h + +Added logging and handling of "half-duplex" caps-lock key. + +---------- +2002/04/30 16:23:30 crs +Make-linux +Make-solaris + +Changed name for auto-generated dependency files from +Makedepend to .depend. + +---------- +2002/04/30 16:23:03 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServerProtocol1_0.cpp +server/server.rc +synergy/CClipboard.cpp +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/synergy.dsp + +Fixes to get win32 client and server up to date. + +---------- +2002/04/29 14:40:01 crs +base/CFunctionJob.h +base/CLog.h +base/CStopwatch.cpp +base/CStopwatch.h +base/CString.h +base/IInterface.h +base/IJob.h +base/TMethodJob.h +base/XBase.h +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +io/CBufferedInputStream.h +io/CBufferedOutputStream.h +io/CInputStreamFilter.h +io/COutputStreamFilter.h +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/XIO.h +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.h +mt/XThread.h +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CSocketInputStream.h +net/CSocketOutputStream.h +net/CSocketStreamBuffer.h +net/CTCPListenSocket.h +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +net/XNetwork.h +net/XSocket.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.h +server/CSynergyHook.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CClipboard.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.h +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.h +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h +synergy/XSynergy.h + +Indentation changes. + +---------- +2002/04/29 14:25:24 crs +client/CClient.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp + +Added some validation of protocol message parameters. + +---------- +2002/04/29 14:12:48 crs +synergy/CXWindowsScreen.cpp + +Shortened timeout on waiting for clipboard response. + +---------- +2002/04/29 14:08:48 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +Made event selection a little more robust. Also fixed failure +to marshall clipboard data when updating primary clipboards. + +---------- +2002/04/29 13:49:56 crs +client/CXWindowsSecondaryScreen.cpp + +Added missing event mask. + +---------- +2002/04/29 13:31:44 crs +client/CClient.cpp +client/CClient.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +checkpoint. changed protocol to better handle clipboards. now +sending a sequence number with enter messages. screens use that +sequence number in clipboard grab and data messages. the server +uses the sequence number to order messages across clients. also +changed secondary screens to send clipboard updates on leaving +(or when grab occurs when not active) instead of on a query from +the server. primary effectively does the same. the query +message has been removed. + +---------- +2002/04/29 11:58:17 crs +client/CClient.cpp + +changed logging levels. + +---------- +2002/04/28 00:46:15 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +Clipboard improvements. Still not working right. Nedit +doesn't work at all but at least now there's a timeout to +prevent synergy from hanging waiting on a reply. + +---------- +2002/04/27 18:49:03 crs +base/CLog.cpp +base/CLog.h +client/CClient.cpp +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CTimerThread.cpp +net/CNetwork.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CProtocolUtil.cpp +synergy/CXWindowsScreen.cpp + +Added more debug levels and moved some annoying debug messages +to those levels. Default log level is now DEBUG for debug +builds and INFO for release builds. + +---------- +2002/04/27 18:06:40 crs +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CProtocolUtil.cpp +synergy/CXWindowsScreen.cpp +synergy/ProtocolTypes.h + +checkpoint. changed CProtocolUtil::readf() to store 1 and 2 +byte integers into pointers to 1 and 2 byte integers. was +always assuming pointers to 4 byte integers. + +---------- +2002/04/27 14:19:53 crs +client/CClient.cpp +client/CClient.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/ClipboardTypes.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +Added support for multiple clipboards. This is mainly to +support both PRIMARY and CLIPBOARD selections on X windows. + +---------- +2002/04/27 14:19:19 crs +Makecommon + +set TARGETS macro to BIN and LIB targets. + +---------- +2002/04/26 20:15:59 crs +notes + +updated + +---------- +2002/04/26 20:14:46 crs +client/CXWindowsSecondaryScreen.cpp + +Fixed caps-lock and num-lock behavior. It seems to work okay +now but did notice one problem: when powerbook is primary and +num-lock is on the keypad works fine until shift is pressed +(and released); after that the keypad only works while the +shift key is down. + +---------- +2002/04/26 20:12:55 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +Added hack to handle "half-duplex" caps-lock key on powerbook. +That key only reports press when pressed and released when +caps-lock is activated and only reports release when pressed +and released when caps-lock is deactivated. I don't know of a +way to detect this behavior so it may have to be configured by +the user. The code assumes normal behavior; will have to add +code to set the flag (perhaps from a user configuration). + +---------- +2002/04/26 17:38:01 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/KeyTypes.h + +changed processing of key events in X. secondary screen now +activates/deactivates modifiers as necessary to get a keycode +interpreted as the expected keysym. still some work and +testing to do on this. + +---------- +2002/04/25 10:44:01 crs +notes + +Added notes on keyboard handling. + +---------- +2002/04/25 10:43:53 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +added handling for DestroyNotify of clipboard requestors. + +---------- +2001/11/26 22:36:51 crs +synergy/CXWindowsScreen.cpp + +checkpoint. improvements to clipboard transfer on X windows. +not detecting a change to clipboard when synergy window isn't +the owner (since there's no event for this; we'll have to +check when we leave the screen i guess). large transfers +don't seem to work. + +---------- +2001/11/26 22:09:53 crs +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +checkpoint. testing clipboard transfer on X windows. + +---------- +2001/11/25 22:20:41 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +checkpoint. implementing clipboard owner in x windows. + +---------- +2001/11/25 18:44:13 crs +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h + +fixed function signature. + +---------- +2001/11/25 18:42:13 crs +Make-linux +Make-solaris +Makecommon +client/Makefile +server/Makefile + +executables are now built into a common area on unix (and they +already were on win32). + +---------- +2001/11/25 18:32:41 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.cpp +client/client.dsp +net/CNetwork.cpp +notes +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/makehook.dsp +server/server.cpp +server/server.dsp +server/synrgyhk.dsp +synergy.dsw +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/Makefile +synergy/ProtocolTypes.h +synergy/synergy.dsp + +added platform independent clipboard transfer stuff +clipboard owner support (MS windows done, X windows partial) +added key transfer on ms windows +mutex fixes in CClient (had race conditions) +faster debug output in ms windows +changed temporary screen name to "secondary" +network fixes on ms windows (poll returned wrong result) +fixed transparent cursor on ms windows + +---------- +2001/11/19 00:33:36 crs +Make-linux +all.dsp +base/BasicTypes.h +base/CLog.cpp +base/CLog.h +base/XBase.cpp +base/base.dsp +base/common.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/Makefile +client/client.cpp +client/client.dsp +client/client.rc +client/resource.h +io/io.dsp +mt/CCondVar.cpp +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/mt.dsp +net/CNetwork.cpp +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/Makefile +net/XNetwork.cpp +net/XNetwork.h +net/net.dsp +notes +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile +server/makehook.dsp +server/resource.h +server/server.cpp +server/server.dsp +server/server.rc +server/synrgyhk.dsp +synergy.dsw +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/synergy.dsp + +checkpoint. merging win32 code. server on X is currently broken +and client probably is. + +---------- +2001/11/18 23:14:28 crs +Makefile +client/CClient.cpp +client/CClient.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/Makefile +client/client.cpp +server/CScreenMap.cpp +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile +server/server.cpp +synergy/CClient.cpp +synergy/CClient.h +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServer.h +synergy/CServerProtocol.cpp +synergy/CServerProtocol.h +synergy/CServerProtocol1_0.cpp +synergy/CServerProtocol1_0.h +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/Makefile +synergy/client.cpp +synergy/server.cpp + +moved client and server files into their own respective +directories. + +---------- +2001/11/13 23:34:12 crs +synergy/CServer.cpp +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/Makefile + +added preliminary support for getting the X selection. + +---------- +2001/11/11 21:27:36 crs +synergy/CServer.cpp + +fixed clamping when mapping to a different screen when beyond +bottom or right of source screen. + +---------- +2001/11/11 21:15:30 crs +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/Makefile + +factored common X windows screen stuff into a common base class. + +---------- +2001/11/10 22:28:37 crs +notes + +updated notes. + +---------- +2001/11/10 22:28:30 crs +Makefile + +added main app directory to build. + +---------- +2001/10/25 22:17:17 crs +io/CBufferedInputStream.cpp +mt/CCondVar.cpp +mt/CMutex.cpp +mt/CThreadRep.cpp +net/CNetworkAddress.cpp +net/CSocketInputStream.cpp +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsSecondaryScreen.cpp + +removed unnecessary global scoping operators. + +---------- +2001/10/25 22:09:27 crs +synergy/CXWindowsSecondaryScreen.cpp + +changed hider window to move underneath mouse when leaving the +screen. this makes it so if the mouse is moved locally, it'll +reappear where it was last seen. + +---------- +2001/10/25 21:40:29 crs +synergy/CClient.cpp +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/ISecondaryScreen.h + +changed some method names and removed warpCursor() from +secondary screen interface. + +---------- +2001/10/24 23:29:29 crs +synergy/CServer.cpp +synergy/CServer.h + +now handling disconnect of secondary screen that has the cursor +by jumping back to the primary screen (without trying to notify +the now disconnected secondary screen). also fixed blown assert +in mapPosition(). + +---------- +2001/10/24 22:33:24 crs +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h + +made calls to X thread safe. + +---------- +2001/10/23 22:45:59 crs +notes + +more notes. + +---------- +2001/10/23 22:41:46 crs +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h + +added cursor hiding. + +---------- +2001/10/23 21:23:29 crs +base/CLog.cpp + +can now filter logging by level. + +---------- +2001/10/23 21:13:08 crs +synergy/CServer.cpp + +fixed blown assert trying to find neighbor when there was none. + +---------- +2001/10/21 00:21:21 crs +synergy/CClient.cpp + +fixed handling of stream ownership. + +---------- +2001/10/21 00:21:02 crs +io/CBufferedInputStream.cpp +io/CBufferedOutputStream.cpp +mt/CThreadRep.cpp +net/CTCPSocket.cpp +net/CTCPSocket.h +synergy/CServer.cpp +synergy/server.cpp + +fixed bugs in handling streams. + +---------- +2001/10/20 20:43:31 crs +Make-linux +mt/CThreadRep.cpp + +threading fixes. had sigmask set in wrong place. was setting +m_exit flag potentially after the object had been destroyed. +most importantly, RTTI must be enabled on PPC to avoid SIGILL. + +---------- +2001/10/14 19:16:54 crs +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CTimerThread.cpp + +some debugging code. + +---------- +2001/10/14 18:29:43 crs +base/CLog.h +mt/CMutex.cpp +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.cpp +synergy/CClient.cpp +synergy/CClient.h +synergy/CProtocolUtil.cpp +synergy/CServerProtocol1_0.cpp +synergy/client.cpp +synergy/server.cpp + +fixed timeout bug in CThreadRep::wait() (negative timeout wouldn't +wait forever). also fixed early return from sleep due to signal. +now forcing client to initialize CThread to ensure global mutex +gets initialized before threads are used. + +---------- +2001/10/14 16:58:01 crs +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/CInputStreamFilter.h +io/COutputStreamFilter.cpp +io/COutputStreamFilter.h +io/CStreamBuffer.cpp +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CSocketInputStream.cpp +net/CSocketInputStream.h +net/CSocketOutputStream.cpp +net/CSocketOutputStream.h +net/CSocketStreamBuffer.cpp +net/CSocketStreamBuffer.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/COutputPacketStream.cpp +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServer.h +synergy/CServerProtocol.cpp +synergy/CServerProtocol.h +synergy/CServerProtocol1_0.cpp +synergy/CServerProtocol1_0.h +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h + +removed exception specifications. thread exceptions weren't +being listed and they'd have to be added to every one. just +doesn't seem worth the trouble. + +---------- +2001/10/14 14:56:06 crs +synergy/CProtocolUtil.cpp + +stupid bug fixes. writef() used the wrong variable as the number +of bytes to write. readf() forgot to prepare the va_list. + +---------- +2001/10/14 14:38:45 crs +base/CLog.cpp +base/CLog.h + +forgot to add the logger files. + +---------- +2001/10/14 14:37:41 crs +Make-linux +base/Makefile +synergy/CClient.cpp +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServerProtocol1_0.cpp +synergy/CXWindowsPrimaryScreen.cpp + +added logging facility and added a bunch of log messages. + +---------- +2001/10/08 19:24:46 crs +Makefile +notes +synergy/CClient.cpp +synergy/CClient.h +synergy/CServer.cpp +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/ISecondaryScreen.h +synergy/Makefile +synergy/client.cpp +synergy/server.cpp + +checkpoint. first cut of client and server apps. not tested +yet but they compile and *should* work as is. + +---------- +2001/10/06 14:18:01 crs +Make-linux +Makefile + +updated old files to new implementation + +---------- +2001/10/06 14:13:28 crs +BasicTypes.h +CClient.cpp +CClient.h +CEvent.h +CEventQueue.cpp +CEventQueue.h +CMessageSocket.cpp +CMessageSocket.h +CProtocol.h +CScreenProxy.cpp +CScreenProxy.h +CServer.cpp +CServer.h +CSocket.cpp +CSocket.h +CSocketFactory.cpp +CSocketFactory.h +CString.h +CTrace.cpp +CTrace.h +CUnixEventQueue.cpp +CUnixEventQueue.h +CUnixTCPSocket.cpp +CUnixTCPSocket.h +CUnixXScreen.cpp +CUnixXScreen.h +CXScreen.cpp +CXScreen.h +IClient.h +IClipboard.h +IEventQueue.h +IJob.h +IScreen.h +IServer.h +ISocket.h +KeyTypes.h +Make-linux +Make-solaris +Makecommon +Makefile +MouseTypes.h +TMethodJob.h +XBase.cpp +XBase.h +XSocket.h +base/BasicTypes.h +base/CFunctionJob.cpp +base/CFunctionJob.h +base/CStopwatch.cpp +base/CStopwatch.h +base/CString.h +base/IInterface.h +base/IJob.h +base/Makefile +base/TMethodJob.h +base/XBase.cpp +base/XBase.h +base/common.h +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/CInputStreamFilter.h +io/COutputStreamFilter.cpp +io/COutputStreamFilter.h +io/CStreamBuffer.cpp +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/Makefile +io/XIO.cpp +io/XIO.h +main.cpp +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.cpp +mt/CTimerThread.h +mt/Makefile +mt/XThread.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CSocketInputStream.cpp +net/CSocketInputStream.h +net/CSocketOutputStream.cpp +net/CSocketOutputStream.h +net/CSocketStreamBuffer.cpp +net/CSocketStreamBuffer.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +net/Makefile +net/XSocket.cpp +net/XSocket.h +notes +synergy/CClient.cpp +synergy/CClient.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/COutputPacketStream.cpp +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServer.h +synergy/CServerProtocol.cpp +synergy/CServerProtocol.h +synergy/CServerProtocol1_0.cpp +synergy/CServerProtocol1_0.h +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h +synergy/KeyTypes.h +synergy/Makefile +synergy/MouseTypes.h +synergy/ProtocolTypes.h +synergy/XSynergy.cpp +synergy/XSynergy.h +test.cpp + +Started over. + +---------- +2001/05/14 21:14:49 crs +MouseTypes.h + +flipped order of buttons to match default X setup. + +---------- +2001/05/14 21:14:25 crs +CClient.cpp +CEvent.h +CScreenProxy.cpp +CScreenProxy.h +CServer.cpp +CXScreen.cpp +CXScreen.h +IScreen.h +KeyTypes.h + +added other mouse and key event handling to CXScreen. key repeat +isn't implemented and modifier masks are ignored. modifier masks +are new; they indicate the modifier key (shift, ctrl, etc) state +at the time of the key event. + +---------- +2001/05/13 12:43:16 crs +CUnixTCPSocket.cpp +CUnixTCPSocket.h + +more fixes to reduce latency. nagle agorithm doesn't seem to +stay off on a socket on linux because a connection clearly +doesn't send data as often as possible. will have to implement +a UDP socket to reduce overhead and avoid these delays. wanted +to do that anyway. + +---------- +2001/05/13 12:21:11 crs +CUnixTCPSocket.cpp +CXScreen.cpp + +fixes to avoid update delays. + +---------- +2001/05/13 12:07:32 crs +CMessageSocket.cpp + +fixed bug in read() that miscalculated the message length. + +---------- +2001/05/13 11:40:29 crs +BasicTypes.h +CClient.cpp +CClient.h +CEvent.h +CEventQueue.cpp +CEventQueue.h +CMessageSocket.cpp +CMessageSocket.h +CProtocol.h +CScreenProxy.cpp +CScreenProxy.h +CServer.cpp +CServer.h +CSocket.cpp +CSocket.h +CSocketFactory.cpp +CSocketFactory.h +CString.h +CTrace.cpp +CTrace.h +CUnixEventQueue.cpp +CUnixEventQueue.h +CUnixTCPSocket.cpp +CUnixTCPSocket.h +CUnixXScreen.cpp +CUnixXScreen.h +CXScreen.cpp +CXScreen.h +IClient.h +IClipboard.h +IEventQueue.h +IJob.h +IScreen.h +IServer.h +ISocket.h +KeyTypes.h +Make-linux +Makefile +MouseTypes.h +TMethodJob.h +XBase.cpp +XBase.h +XSocket.h +main.cpp +tools/depconv + +initial revision of synergy. currently semi-supports X windows +on unix, but client screens don't simulate events other than +mouse move. also not supporting clipboard at all yet and the +main app is just a temporary framework to test with. must +clean up protocol and communication. + +---------- diff --git a/FAQ b/FAQ new file mode 100644 index 0000000..0962f02 --- /dev/null +++ b/FAQ @@ -0,0 +1,187 @@ +Synergy Frequently Asked Questions +================================== + +Questions +--------- +1. Why doesn't ctrl+alt+del work on secondary screens? +2. Can the server and client be using different operating systems? +3. What's the difference between synergy and x2x, x2vnc, etc? +4. What does "Cannot initialize hook library" mean? +5. What security/encryption does synergy provide? +6. What should I call my screens in the configuration? +7. Why do my CapsLock and NumLock keys act funny? +8. Can synergy share the display in addition to the mouse and keyboard? +9. Can synergy do drag and drop between computers? +10. Do AltGr or Mode-Switch work? +11. Why isn't synergy ported to platform XYZ? +12. My client can't connect. What's wrong? +13. Linking fails on Solaris. What's wrong? +14. The screen saver never starts. Why not? +15. I can't switch screens anymore for no apparent reason. Why? + +Answers +------- +1. Why doesn't ctrl+alt+del work on secondary screens? + + Synergy isn't able to capture ctrl+alt+del on PC compatible + systems because it's handled completely differently than + other keystrokes. However, when the mouse is on a client + screen, pressing ctrl+alt+pause will simulate ctrl+alt+del + on the client. (A client running on Windows NT, 2000, or XP + must be running as a service for this to work.) + +2. Can the server and client be using different operating systems? + + Yes. The synergy network protocol is platform neutral so + synergy doesn't care what operating systems are running on + the server and clients. + +3. What's the difference between synergy and x2x, x2vnc, etc? + + Unlike x2x, synergy supports any number of computers and + it doesn't require X on Microsoft Windows platforms. It + also has more advanced clipboard support and synchronizes + screensavers. x2vnc is also limited to two computers, + requires the separate vnc package, and is really only + appropriate for using an X system to control a non-X system. + However, the right tool for the job is whatever tool works + best for you. + +4. What does "Cannot initialize hook library" mean? + + This error can occur on a synergy server running on a + Microsoft Windows operating system. It means that synergy + is already running or possibly was not shut down properly. + If it's running then first end the synergy task. If it's + not then try logging off and back on or rebooting then + starting synergy again. + +5. What security/encryption does synergy provide? + + Synergy provides no built-in encryption or authentication. + Given that, synergy should not be used on or over any untrusted + network, especially the Internet. It's generally fine for home + networks. Future versions may provide built-in encryption and + authentication. + + Strong encryption and authentication is available through SSH + (secure shell). Run the SSH daemon (i.e. server) on the same + computer that you run the synergy server. It requires no + special configuration to support synergy. On each synergy + client system, run SSH with port forwarding: + + ssh -f -N -L 24800::24800 + + where is the name of the SSH/synergy server. + Once ssh authenticates itself, start the synergy client + normally except use `localhost' or `127.0.0.1' as the server's + address. SSH will then encrypt all communication on behalf of + synergy. Authentication is handled by the SSH authentication. + + A free implementation of SSH for Linux and many Unix systems + called OpenSSH is available from http://www.openssh.com/. For + Windows there's a port of OpenSSH using Cygwin + (http://www.cygwin.com/). + +6. What should I call my screens in the configuration? + + You can use any unique name in the configuration file for each + screen but it's easiest to use the hostname of the computer. + That's the computer name not including the domain. For example, + a computer with the fully qualified domain name `xyz.foo.com' has + the hostname `xyz'. There should also be an alias for `xyz' to + `xyz.foo.com'. If you don't use the computer's hostname, you + have to tell synergy the name of the screen using a command line + option, or the startup dialog on Windows. + +7. Why do my CapsLock and NumLock keys act funny? + + Some systems treat the Caps-Lock and Num-Lock keys differently + than all the others. Whereas most keys report going down when + physically pressed and going up when physically released, on + these systems the Caps-Lock and Num-Lock keys report going down + when being activated and going up when being deactivated. That + is, when you press and release, say, Caps-Lock to activate it, it + only reports going down, and when you press and release to + deactivate it, it only reports going up. This confuses synergy. + + You can solve the problem by changing your configuration file. + In the screens section, following each screen that has the + problem, add either or both of these lines as appropriate: + + halfDuplexCapsLock = true + halfDuplexNumLock = true + + Then restart synergy on the server. + +8. Can synergy share the display in addition to the mouse and keyboard? + + No. Synergy is a KM solution not a KVM (keyboard, video, mouse) + solution. However, future versions will probably support KVM. + Hopefully, this will make synergy suitable for managing large + numbers of headless servers. + +9. Can synergy do drag and drop between computers? + + No. That's a very cool idea and it'll be explored. However, it's + also clearly difficult and may take a long time to implement. + +10. Does AltGr/Mode-Switch work? + + Yes, as of 1.0.12 synergy has full support for AltGr/Mode-switch. + That includes support for most (all?) European keyboard layouts. + All systems should be using the same keyboard layout, though, for + all characters to work. (Any character missing from a client's + layout cannot be generated by synergy.) + +11. Why isn't synergy ported to platform XYZ? + + Probably because the developers don't have access to platform XYZ + and/or are unfamiliar with development on XYZ. Also, synergy has + inherently non-portable aspects so there's a not insignificant + effort involved in porting. + +12. My client can't connect. What's wrong? + + A common mistake when starting the client is to give the wrong + server host name. The last synergyc command line option (Unix) + or the "Server Host Name" edit field (Windows) should be the + host name (or IP address) of the server *not* the client's host + name. If you get the error "connection failed: cannot connect + socket" followed by "the attempt to connect was forcefully + rejected" or "connection refused" then the server isn't started, + can't bind the address, or the client is connecting to the wrong + host name/address or port. + +13. Linking fails on Solaris. What's wrong? + + Did you add `--x-includes=/usr/openwin/include + --x-libraries=/usr/openwin/lib' (without the linebreak) to the + `configure' command line? Solaris puts the X11 includes and + libraries in an unusual place and the above lets synergy find + them. + +14. The screen saver never starts. Why not? + + If the synergy server is on X Windows then the screen saver will + not start while the mouse is on a client screen. This is a + consequence of how X Windows, synergy and xscreensaver work. + +15. I can't switch screens anymore for no apparent reason. Why? + + This sometimes happens but all the causes aren't yet known. One + known cause is if the synergy server in running on Windows + 95/98/Me and a 16-bit application is in the foreground when the + screen saver starts. Windows fails to notify synergy that the + screen saver has started in this situation (which is a bug in + Windows, not synergy) and synergy may intercept some keyboard + input and divert it to a client when the screen saver is running. + As a result, it's possible for the server system to believe a key + is pressed when it really isn't. Typically, it's the return key + and simply tapping it will allow synergy to switch screens again. + + If this problem happens to you, try tapping the enter key to see + if that solves the problem. If not, you can try running with + debug logging (--debug DEBUG) and synergy will report exactly why + it refuses to switch screens. If it claims a key is down then + try tapping that key and see if that solves the problem. diff --git a/HISTORY b/HISTORY new file mode 100644 index 0000000..64c306d --- /dev/null +++ b/HISTORY @@ -0,0 +1,16 @@ +History of Synergy +================== + +The first incarnation of synergy was CosmoSynergy, created by +Richard Lee and Adam Feder then at Cosmo Software, Inc., a +subsidiary of SGI (nee Silicon Graphics, Inc.), at the end of +1996. They wrote it, and Chris Schoeneman contributed, to +solve a problem: most of the engineers in Cosmo Software had +both an Irix and a Windows box on their desks and switchboxes +were expensive and annoying. CosmoSynergy was a great success +but Cosmo Software declined to productize it and the company +was later closed. + +Synergy is a from-scratch reimplementation of CosmoSynergy. +It provides most of the features of the original and adds a +few improvements. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..f33265a --- /dev/null +++ b/INSTALL @@ -0,0 +1,575 @@ +Synergy Installation Instructions +================================= + +Prerequisites for building +-------------------------- + +To build synergy from the sources you'll need the following: + + Windows: + * VC++ 6.0 or up + + Unix: + * gcc 2.95 (or up) + * X11R4 or up headers and libraries + +In this document, "Unix" means any of the supported Unix or Unix-like +(e.g. Linux) operating systems. + + +Configuring the build +--------------------- + +This step is only necessary when building on Unix. + +To configure the build for your platform use the configure script: + + ./configure + +For a list of options to configure use: + + ./configure --help + +On Solaris you may need to use: + + ./configure --x-includes=/usr/openwin/include --x-libraries=/usr/openwin/lib + +so synergy can find the X11 includes and libraries. + + +Building +-------- + +Windows: + Start VC++ and open `synergy.dsw'. Set the active configuration + (Build | Set Active Configuration) to `All - Debug' or `All - Release' + then build. Binaries are built into ./Debug or ./Release. + +Unix: + Simply enter: + + make + + This will build the client and server and leave them in their + respective source directories. + + +Installing +---------- + +Windows: + There is no support for creating an installer for synergy or installing + the files in a particular location. Instead, just copy the following + files from the Debug or Release directory to a directory you choose + (perhaps under the Program Files directory): + + * synergy.exe + * synergyc.exe + * synergys.exe + * synrgyhk.dll + +Unix: + make install + + will install the client and server into /usr/local/bin unless you + specified a different directory when you ran configure. + +See `Starting Automatically on ...' below for details on how to have +synergy start up automatically when the computer starts. + + +Running on Windows +------------------ + +Double click `synergy' on the server computer. The server is the +computer who's mouse and keyboard will be shared. This brings up a +dialog that lets you configure the server then test out the +configuration or start the server. + +First configure the server. Click the `Server' radio button + + * Click the `Server' radio button + * Click `Add' to add the server to the `Screens' list + * Enter the name of server (the computer name is recommended) + * Enter other names the server is known by + * Click OK + * Use `Add' to add your other computers + * Using a computer's name as its screen name is recommended + * Choose desired screen options on the `Add' dialog + * Use the controls under `Layout' to link screens together + * Click (once) on the server's name in the `Screens' list + * Choose the screen to the left of the server + * Use `---' if there is no screen to the left of the server + * Choose the screens to the right, above and below the server + * Repeat the above steps for all the other screens + * Use `Options...' to set desired options + * If the server's screen name is not the server's computer name: + * Click `Advanced...' + * Enter the server's screen name next to `Screen Name' + * Click `OK' + * Click `Test' + +The server will start and you'll see a console window with log messages +telling you about synergy's progress. If an error occurs you'll get one +or more dialog boxes telling you what the errors are; read the errors +to determine the problem then correct them and try `Test' again. + +Now that the server is running, you'll need to start a client. On any +client computer, double click `synergy'. Of course, you'll need to +have installed the four files listed under `Installing' above on the +client computer. Then configure the client: + + * Click the `Client' radio button + * Enter the server's computer name in `Server Host Name' + * Do not use any of the server's screen names, unless one of those + is also the computer name + * If the client's screen name is not the client's computer name: + * Click `Advanced...' + * Enter the client's screen name next to `Screen Name' + * Click `OK' + * Click `Test' + +If all goes well, the client connects to the server successfully and +the mouse and keyboard are shared. If an error occurs you'll get one +or more dialog boxes telling you what the errors are; read the errors +to determine the problem then correct them and try `Test' again. When +everything is working correctly, install the software on the other +client computers (if any) and repeat the steps for configuring the +client on each. + +Once the clients and server are working you can stop the clients and +server by clicking the `Stop' button on each computer or by right +clicking on the tray icon (by the clock in the task bar) and choosing +`Quit'. Then click `Start' on the server computer then on each of +the clients. Synergy will start and the dialog window will close. +You can stop synergy or check on its status using the tray icon. + +See `Starting Automatically on Windows' below for configuring synergy +to start automatically when the computer starts. + + +Configuring the Server on Unix +------------------------------ + +The synergy server requires configuration. The configuration file is a +plain text file broken into sections. Each section has the form: + + section: + + end + +Comments are introduced by `#' and continue to the end of the line. +The file can have the following sections. The `screens' section must +appear before the `links' and `aliases' sections. + + * screens + is a list of screen names, one name per line, each + followed by a colon. Names are arbitrary strings but they + must be unique. The hostname of each computer is recommended. + There must be a screen name for the server and each client. + Each screen can specify a number of options. Options have the + form `name = value' and a listed one per line after the screen + name. + + Example: + + section: screens + moe: + larry: + halfDuplexCapsLock = true + halfDuplexNumLock = true + curly: + meta = alt + end + + This declares three screens named: moe, larry, and curly. + Screen `larry' has half-duplex caps lock and num lock keys + (see below) and screen `curly' converts the meta modifier key + to the alt key. + + Screen can have the following options: + + halfDuplexCapsLock = {true|false} + This computer has a caps lock key that doesn't report a + press and a release event when the user presses it but + instead reports a press event when it's turned on and a + release event when it's turned off. If caps lock acts + strangely on all screens then you may need this option + on the server screen. If it acts strangely on one + screen then that screen may need the option. + + halfDuplexNumLock = {true|false} + This is identical to halfDuplexCapsLock except it + applies to the num lock key. + + xtestIsXineramaUnaware = {true|false} + This option works around a bug in the XTest extension + when used in combination with Xinerama. It affects + X11 clients only. Not all versions of the XTest + extension are aware of the Xinerama extension. As a + result, they do not move the mouse correctly when + using multiple Xinerama screens. This option is + currently true by default. If you know your XTest + extension is Xinerama aware then set this option to + false. + + shift = {shift|ctrl|alt|meta|super|none} + ctrl = {shift|ctrl|alt|meta|super|none} + alt = {shift|ctrl|alt|meta|super|none} + meta = {shift|ctrl|alt|meta|super|none} + super = {shift|ctrl|alt|meta|super|none} + Map a modifier key pressed on the server's keyboard to + a different modifier on this client. This option only + has an effect on a client screen; it's accepted and + ignored on the server screen. + + You can map, say, the shift key to shift (the default), + ctrl, alt, meta, super or nothing. Normally, you + wouldn't remap shift or ctrl. You might, however, have + an X11 server with meta bound to the Alt keys. To use + this server effectively with a windows client, which + doesn't use meta but uses alt extensively, you'll want + the windows client to map meta to alt (using `meta = + alt'). + + * links + is a list of screen names just like in the `screens' + section except each screen is followed by a list of links, + one per line. Each link has the form ` = + '. A link indicates which screen is adjacent in the + given direction. + + + Example: + + section: links + moe: + right = larry + up = curly + larry: + left = moe + up = curly + curly: + down = larry + end + + This indicates that screen `larry' is to the right of screen + `moe' (so moving the cursor off the right edge of moe would + make it appear at the left edge of larry), `curly' is above + 'moe', `moe' is to the left of `larry', `curly' is above + `larry', and `larry' is below `curly'. Note that links do + not have to be symmetrical; moving up from moe then down + from curly lands the cursor on larry. + + * aliases + is a list of screen names just like in the `screens' + section except each screen is followed by a list of aliases, + one per line *not* followed by a colon. An alias is a + screen name and must be unique. During screen name lookup + each alias is equivalent to the screen name it aliases. So + a client can connect using its canonical screen name or any + of its aliases. + + Example: + + section: aliases + larry: + larry.stooges.com + curly: + shemp + end + + Screen `larry' is also known as `larry.stooges.com' and can + connect as either name. Screen `curly' is also known as + `shemp'. (Hey, it's just an example.) + + * options + is a list of lines of the form `name = value'. These + set the global options. + + Example: + + section: options + heatbeat = 5000 + switchDelay = 500 + end + + You can use the following options: + + heartbeat = N + The server will expect each client to send a message no + less than every N milliseconds. If no message arrives + from a client within 3N seconds the server forces that + client to disconnect. + + If synergy fails to detect clients disconnecting while + the server is sleeping or vice versa, try using this + option. + + switchDelay = N + Synergy won't switch screens when the mouse reaches the + edge of a screen unless it stays on the edge for N + milliseconds. This helps prevent unintentional + switching when working near the edge of a screen. + + switchDoubleTap = N + Synergy won't switch screens when the mouse reaches the + edge of a screen unless it's moved away from the edge + and then back to the edge within N milliseconds. With + the option you have to quickly tap the edge twice to + switch. This helps prevent unintentional switching + when working near the edge of a screen. + + screenSaverSync = {true|false} + If set to false then synergy won't synchronize screen + savers. Client screen savers will start according to + their individual configurations. The server screen + saver won't start if there is input, even if that input + is directed toward a client screen. + + You can use both the switchDelay and switchDoubleTap options at + the same time. Synergy will switch when either requirement is + satisfied. + +The synergy server will try certain pathnames to load the configuration +file if the user doesn't specify a path using the `--config' command +line option. `synergys --help' reports those pathnames. + + +Running the Server on Unix +-------------------------- + +Run the server on the computer that has the keyboard and mouse to +be shared. You must have prepared a configuration file before +starting the server. The server should be started before the +clients but that's not required. + +Run the synergy server on the server system using the following +command line: + + synergys -f --config + +Replace with the path to the configuration file. +The `-f' option causes synergys to run in the foreground. This is +recommended until you've verified that the configuration works. +If you didn't include the system's hostname in the configuration +file (either as a screen name or an alias) then you'll have to add +`--name ' to the command line, where is +a name in the configuration file. You can use `synergys --help' +for a list of command line options. + +See `Starting Automatically on Unix' below for running synergy +automatically when the X server starts. + + +Running the Client on Unix +-------------------------- + +Run the client on all computers that aren't the server using the +following command line: + + synergyc -f --no-camp + +Replace with the hostname or address of the +server system. The `-f' option causes synergy to run in the +foreground. The `--no-camp' prevents synergy from retrying to +connect to the server until it succeeds. Both are recommended +until you've verified that the configuration works. If you +didn't include the system's hostname in the configuration file +(either as a screen name or an alias) then you'll have to add +`--name ' to the command line, where +is a name in the configuration file. + +The client should quickly report `connected to server'. If it +does not but doesn't print an error and exit immediately then +it's trying to connect to the server but cannot. It will time +out in 30 seconds and exit (use ctrl+c to exit earlier). You +should check that the server is running and is reachable over +the network and try again. + +If the client fails and exits it should print an error describing +the problem. Here are typical problems and possible solutions: + + * failed to open screen: + check permission to open the X display; + check that the DISPLAY environment variable is set. + * already connected: + check that the synergy client isn't already running. + * refused client: + add client to the server's configuration file. + * connection failed: + check ; + the server cannot open the desired port, stop the + program using that port (24800) and restart the + server. + +Once all the clients are running, try moving the mouse to each +screen. Be sure to check all the configured links. + +See `Starting Automatically on Unix' below for running synergy +automatically when the X server starts. + + +Starting Automatically on Windows +--------------------------------- + +When all the clients work you're ready to have synergy start +automatically each time the system (re)starts. Click `Stop' on all +the clients then on the server'. Now click the `Configure...' button +by the text `Automatic Startup'. The `Auto Start' dialog will pop up. +If an error occurs then correct the problem and click `Configure' +again. + +On the `Auto Start' dialog you'll configure synergy to start +automatically when the computer starts or when you log in. You can +also configure synergy to not start automatically. You can only +start synergy automatically when the computer starts if you have +sufficient access rights. The dialog will let you know if you have +sufficient permission. + +If synergy is already configured to automatically start then there +will be two `Uninstall' buttons, at most one of which is enabled. +Click the enabled button, if any, to configure synergy to not start +automatically. + +If synergy is not configured to start automatically then there will +be two `Install' buttons. If you have sufficient permission to +have synergy start automatically when the computer does then the +`Install' button in the `When Computer Starts' box will be enabled. +Click it to have synergy start for all users when the computer starts. +In this case, synergy will be available during the login screen. +Otherwise, click the `Install' button in the `When You Log In' box +to have synergy automatically start when you log in. + + +Starting Automatically on Unix +------------------------------ + +Synergy requires an X server. That means a server must be +running and synergy must be authorized to connect to that server. +It's best to have the display manager start synergy. You'll need +the necessary (probably root) permission to modify the display +manager configuration files. If you don't have that permission +you can start synergy after logging in via the .xsession file. + +To have the display manager start synergy, edit the Xsetup script. +The location of this file depends on your installation. It might +be /etc/X11/xdm/Xsetup. Near the end of the file but before +anyplace the script calls exit, start the client with something +like: + + /usr/bin/killall synergyc + /usr/local/bin/synergyc [] + + must not include `-f' or `--no-daemon'. Change the +paths as necessary. It's important to make sure no old copies +of synergy are running so they can't interfere with the new one. + +To start the server use something like: + + /usr/bin/killall synergys + /usr/local/bin/synergys [] --config + +Again, must not include `-f' or `--no-daemon'. If +the configuration pathname is one of the default locations then +you don't need the `--config' option. + +Note that some display managers (xdm and kdm, but not gdm) grab +the keyboard and do not release it until the user logs in, for +security reasons. This prevents a synergy server from sharing +the mouse and keyboard until the user logs in. It doesn't +prevent a synergy client from synthesizing mouse and keyboard +input, though. + + +Network Security +---------------- + +Synergy has no built-in support for encryption or authentication. +The server accepts connections from any computer. The server and +clients send all data unencrypted which means the clipboard and +mouse and keyboard events (e.g. typed passwords) are easily +examined by anyone listening on the network. Therefore, do not +run synergy on untrusted networks except as follows. + +You can use SSH (secure shell) to provide strong authentication +and encryption to synergy without modifying either SSH or synergy. +On Linux and Unix a free implementation of SSH called OpenSSH is +available at http://www.openssh.com/. On Windows you can use the +Cygwin version of OpenSSH. + +First, install the SSH server (sshd) on the computer running the +synergy server. Next, install the SSH client (ssh) on each +synergy client computer. Start the SSH and synergy servers +normally. Then, for each client, start the SSH client with port +forwarding: + + ssh -f -N -L 24800::24800 + +where is the name or address of the SSH and +synergy server host. 24800 is the default synergy port; replace +it with whichever port you use if you don't use the default. Once +ssh authenticates with the server, start the synergy client as +usual except use `localhost' or `127.0.0.1' for the server +address. Synergy will then pass all communication through SSH +which encrypts it, passes it over the network, decrypts it, and +hands it back to synergy. Authentication is provided by SSH's +authentication. + + + +Common Command Line Options +--------------------------- + -d, --debug use debugging level + --daemon run as a daemon (Unix) or background (Windows) + -f, --no-daemon run in the foreground + -n, --name use instead of the hostname + --restart automatically restart on failures + -1, --no-restart do not restart on failure + -h, --help print help and exit + --version print version information and exit + +Debug levels are from highest to lowest: FATAL, ERROR, WARNING, NOTE, +INFO, DEBUG, DEBUG1, and DEBUG2. Only messages at or above the given +level are logged. Messages are logged to a terminal window when +running in the foreground. Unix logs messages to syslog when running +as a daemon. The Windows NT family logs messages to the event log +when running as a service. The Windows 95 family shows FATAL log +messages in a message box and others in a terminal window when running +as a service. + +The `--name' option lets the client or server use a name other than +its hostname for its screen. This name is used when checking the +configuration. + +Neither the client nor server will automatically restart if an error +occurs that is sure to happen every time. For example, the server +will exit immediately if it can't find itself in the configuration. +On X11 both the client and server will also terminate if the +connection to the X server is lost. Since xdm will normally restart +the X server and synergy, this is the correct behavior. + + +Server Command Line Options +--------------------------- + -a, --address
listen for connections on the given address + -c, --config read configuration from + +
has one of the following forms: + + : + : + is a hostname or address of a network interface on the +server system. is a port number from 1 to 65535. +defaults to the system's hostname and defaults to 24800. + + +Client Command Line Options +--------------------------- + --camp retry connection to server until successful + --no-camp try connection to server only once +
address of server + +see the "server command line options" for a description of
+but note that there is no default though there is a +default . diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..edf9fec --- /dev/null +++ b/Makefile.am @@ -0,0 +1,107 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = . +VDEPTH = ./$(VPATH) + +SUBDIRS = \ + lib \ + cmd \ + dist \ + $(NULL) + +EXTRA_DIST = \ + BUGS \ + FAQ \ + HISTORY \ + PORTING \ + TODO \ + all.dsp \ + synergy.dsw \ + doc/doxygen.cfg.in \ + examples/synergy.conf \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + aclocal.m4 \ + config.h \ + config.h.in \ + config.log \ + config.status \ + configure \ + stamp-h.in \ + stamp-h1 \ + doc/doxygen.cfg \ + doc/doxygen/html/* \ + $(NULL) + +PKG_FILES = \ + AUTHORS \ + BUGS \ + COPYING \ + ChangeLog \ + FAQ \ + HISTORY \ + INSTALL \ + NEWS \ + README \ + TODO \ + cmd/synergyc/synergyc \ + cmd/synergys/synergys \ + examples/synergy.conf \ + $(NULL) +PKG_PROG_FILES = \ + synergyc \ + synergys \ + $(NULL) + +# build doxygen documentation +doxygen: + doxygen doc/doxygen.cfg + +# build RPMs +RPMTOPDIR=/var/tmp/@PACKAGE@-@VERSION@ +dist-rpm: dist + rm -rf $(RPMTOPDIR) + mkdir $(RPMTOPDIR) + (cd $(RPMTOPDIR); mkdir BUILD SOURCES SPECS SRPMS RPMS) + cp @PACKAGE@-@VERSION@.tar.gz $(RPMTOPDIR)/SOURCES + rpm --define '_topdir $(RPMTOPDIR)' -ba dist/rpm/synergy.spec && \ + mv -f $(RPMTOPDIR)/SRPMS/*.rpm . && \ + mv -f $(RPMTOPDIR)/RPMS/*/*.rpm . && \ + rm -rf $(RPMTOPDIR) + +# build zip +# FIXME -- have automake generate this rule for us +dist-zip: distdir + zip -r $(distdir).zip $(distdir) + -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) + +# build binary package. owner/group of packaged files will be +# owner/group of user running make. +PKGTOPDIR=/var/tmp/@PACKAGE@-@VERSION@ +dist-pkg: all + rm -rf $(PKGTOPDIR) + mkdir $(PKGTOPDIR) + mkdir $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + cp $(PKG_FILES) $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + (cd $(PKGTOPDIR)/@PACKAGE@-@VERSION@; \ + chmod 644 *; \ + chmod 755 $(PKG_PROG_FILES); \ + strip $(PKG_PROG_FILES) ) + type=`uname -s -m | tr '[A-Z] ' '[a-z].'`; \ + (cd $(PKGTOPDIR); tar cf - @PACKAGE@-@VERSION@ | \ + gzip - ) > @PACKAGE@-@VERSION@-1.$${type}.tar.gz && \ + rm -rf $(PKGTOPDIR) diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..5e99167 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,520 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = . + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = . +VDEPTH = ./$(VPATH) + +SUBDIRS = \ + lib \ + cmd \ + dist \ + $(NULL) + + +EXTRA_DIST = \ + BUGS \ + FAQ \ + HISTORY \ + PORTING \ + TODO \ + all.dsp \ + synergy.dsw \ + doc/doxygen.cfg.in \ + examples/synergy.conf \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + aclocal.m4 \ + config.h \ + config.h.in \ + config.log \ + config.status \ + configure \ + stamp-h.in \ + stamp-h1 \ + doc/doxygen.cfg \ + doc/doxygen/html/* \ + $(NULL) + + +PKG_FILES = \ + AUTHORS \ + BUGS \ + COPYING \ + ChangeLog \ + FAQ \ + HISTORY \ + INSTALL \ + NEWS \ + README \ + TODO \ + cmd/synergyc/synergyc \ + cmd/synergys/synergys \ + examples/synergy.conf \ + $(NULL) + +PKG_PROG_FILES = \ + synergyc \ + synergys \ + $(NULL) + + +# build RPMs +RPMTOPDIR = /var/tmp/@PACKAGE@-@VERSION@ + +# build binary package. owner/group of packaged files will be +# owner/group of user running make. +PKGTOPDIR = /var/tmp/@PACKAGE@-@VERSION@ +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = doc/doxygen.cfg +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = README ./stamp-h.in AUTHORS COPYING ChangeLog INSTALL \ + Makefile.am Makefile.in NEWS TODO acinclude.m4 aclocal.m4 \ + config.h.in config/config.guess config/config.sub \ + config/depcomp config/install-sh config/missing \ + config/mkinstalldirs configure configure.in +DIST_SUBDIRS = $(SUBDIRS) +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$@ $(SHELL) ./config.status + +$(top_builddir)/config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck +$(srcdir)/configure: $(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES) + cd $(srcdir) && $(AUTOCONF) + +$(ACLOCAL_M4): configure.in acinclude.m4 + cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +config.h: stamp-h + @if test ! -f $@; then \ + rm -f stamp-h; \ + $(MAKE) stamp-h; \ + else :; fi +stamp-h: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h stamp-hT + @echo timestamp > stamp-hT 2> /dev/null + cd $(top_builddir) \ + && CONFIG_FILES= CONFIG_HEADERS=config.h \ + $(SHELL) ./config.status + @mv stamp-hT stamp-h +$(srcdir)/config.h.in: $(srcdir)/./stamp-h.in + @if test ! -f $@; then \ + rm -f $(srcdir)/./stamp-h.in; \ + $(MAKE) $(srcdir)/./stamp-h.in; \ + else :; fi +$(srcdir)/./stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4) + @rm -f $(srcdir)/./stamp-h.in $(srcdir)/./stamp-h.inT + @echo timestamp > $(srcdir)/./stamp-h.inT 2> /dev/null + cd $(top_srcdir) && $(AUTOHEADER) + @mv $(srcdir)/./stamp-h.inT $(srcdir)/./stamp-h.in + +distclean-hdr: + -rm -f config.h +doc/doxygen.cfg: $(top_builddir)/config.status $(top_srcdir)/doc/doxygen.cfg.in + cd $(top_builddir) && CONFIG_FILES=$@ CONFIG_HEADERS= CONFIG_LINKS= $(SHELL) ./config.status +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)config.h.in$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags config.h.in $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = . +# Avoid unsightly `./'. +distdir = $(PACKAGE)-$(VERSION) + +GZIP_ENV = --best + +distdir: $(DISTFILES) + -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) + mkdir $(distdir) + $(mkinstalldirs) $(distdir)/config $(distdir)/dist/rpm $(distdir)/doc $(distdir)/examples + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r $(distdir) +dist: distdir + $(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + -chmod -R a+w $(distdir) > /dev/null 2>&1; rm -rf $(distdir) + GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(AMTAR) xf - + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/=build + mkdir $(distdir)/=inst + chmod a-w $(distdir) + dc_install_base=`CDPATH=: && cd $(distdir)/=inst && pwd` \ + && cd $(distdir)/=build \ + && ../configure --srcdir=.. --prefix=$$dc_install_base \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && (test `find $$dc_install_base -type f -print | wc -l` -le 1 \ + || (echo "Error: files left after uninstall" 1>&2; \ + exit 1) ) \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && $(MAKE) $(AM_MAKEFLAGS) distclean \ + && rm -f $(distdir).tar.gz \ + && (test `find . -type f -print | wc -l` -eq 0 \ + || (echo "Error: files left after distclean" 1>&2; \ + exit 1) ) + -chmod -R a+w $(distdir) > /dev/null 2>&1; rm -rf $(distdir) + @echo "$(distdir).tar.gz is ready for distribution" | \ + sed 'h;s/./=/g;p;x;p;x' +check-am: all-am +check: check-recursive +all-am: Makefile config.h +installdirs: installdirs-recursive +installdirs-am: + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +dist-all: distdir + $(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) +distclean: distclean-recursive + -rm -f config.status config.cache config.log +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive dist dist-all distcheck distclean \ + distclean-generic distclean-hdr distclean-recursive \ + distclean-tags distdir dvi dvi-am dvi-recursive info info-am \ + info-recursive install install-am install-data install-data-am \ + install-data-recursive install-exec install-exec-am \ + install-exec-recursive install-info install-info-am \ + install-info-recursive install-man install-recursive \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am installdirs-recursive maintainer-clean \ + maintainer-clean-generic maintainer-clean-recursive mostlyclean \ + mostlyclean-generic mostlyclean-recursive tags tags-recursive \ + uninstall uninstall-am uninstall-info-am \ + uninstall-info-recursive uninstall-recursive + + +# build doxygen documentation +doxygen: + doxygen doc/doxygen.cfg +dist-rpm: dist + rm -rf $(RPMTOPDIR) + mkdir $(RPMTOPDIR) + (cd $(RPMTOPDIR); mkdir BUILD SOURCES SPECS SRPMS RPMS) + cp @PACKAGE@-@VERSION@.tar.gz $(RPMTOPDIR)/SOURCES + rpm --define '_topdir $(RPMTOPDIR)' -ba dist/rpm/synergy.spec && \ + mv -f $(RPMTOPDIR)/SRPMS/*.rpm . && \ + mv -f $(RPMTOPDIR)/RPMS/*/*.rpm . && \ + rm -rf $(RPMTOPDIR) + +# build zip +# FIXME -- have automake generate this rule for us +dist-zip: distdir + zip -r $(distdir).zip $(distdir) + -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) +dist-pkg: all + rm -rf $(PKGTOPDIR) + mkdir $(PKGTOPDIR) + mkdir $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + cp $(PKG_FILES) $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + (cd $(PKGTOPDIR)/@PACKAGE@-@VERSION@; \ + chmod 644 *; \ + chmod 755 $(PKG_PROG_FILES); \ + strip $(PKG_PROG_FILES) ) + type=`uname -s -m | tr '[A-Z] ' '[a-z].'`; \ + (cd $(PKGTOPDIR); tar cf - @PACKAGE@-@VERSION@ | \ + gzip - ) > @PACKAGE@-@VERSION@-1.$${type}.tar.gz && \ + rm -rf $(PKGTOPDIR) +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..109501c --- /dev/null +++ b/NEWS @@ -0,0 +1,180 @@ +Synergy News +============ + +* Aug-24-2003 - Synergy 1.0.14 released + + Made following changes: + * Fixed bugs in setting win32 process/thread priority + * Fixed resource leak in opening win32 system log + * Fixed win32 launcher not getting non-default advanced options + * Synergy log copied to clipboard now transferred to other screens + * Hack to work around lesstif clipboard removed (fixes pasting on X) + +* Jul-20-2003 - Synergy 1.0.12 released + + This release finally completes support for non-ASCII characters, + fully supporting most (all?) European keyboard layouts including + dead key composition. This release includes changes from several + experimental versions (1.0.9, 1.0.11, 1.1.0, 1.1.1, 1.1.2, and + 1.1.3). + + Made following changes: + * Added non-ASCII support to win32 and X11 + * Added dead key support to win32 and X11 + * Fixed AltGr handling + * Added ctrl+alt+del simulation using ctrl+alt+pause + * Fixed loss of key event when user releases ctrl+alt+del + * Fixed incorrect synthesis of pointer-keys event on X11 + * Fixed Xinerama support + * Made some clipboard fixes on win32 and X11 + * Add tray icon menu item to copy log to clipboard + * Fixed mouse warping on unconnected client + * Stopped unconnected client from filling up event logs + +* May-10-2003 - Synergy 1.0.8 released + + Made following changes: + * Fixed hook forwarding (fixing interaction with objectbar) + * Fixed "Windows" key handling and added support Win+E, Win+F, etc + * Added win 95/98/me support for Alt+Tab, Alt+Esc, Ctrl+Esc + * Fixed scroll lock locking to server screen + * Fixed screen flashing on X11 and Windows + * Fixed compile problem on 64 bit systems + * Fixed Xinerama support + * Now allowing screen names that include underscores + * Improved non-ASCII key handling on Windows + * Fixed lagginess + * Fixed failure to capture all mouse input on Windows + * Fixed auto-repeat bugs on X11 + * Added option to disable screen saver synchronization + * Added support for 4th and 5th mouse buttons on Windows + * Added support for "Internet" and "Multimedia" keys + * Fixed jumping from client to itself (mouse wrapping) + +* Mar-27-2003 - Synergy 1.0.6 released + + Made following changes: + * Added tray icon on win32 + * Fixed multi-monitor support on win32 + * Fixed win32 screen saver detection on NT/2k/XP + * Added per-screen options to remap modifier keys + * Added global options for restricting screen jumping + * Added global option for detecting unresponsive clients + * Added more logging for why screen jump won't happen + * Fixed problem sending the CLIPBOARD to motif/lesstif apps + * Win32 launcher now remembers non-config-file state + +* Feb-18-2003 - Synergy 1.0.3 released + + Made following changes: + * Support for X11 keymaps with only uppercase letters + * Fixed memory leaks + * Added documentation on using synergy with SSH + * Fixed unnecessary left-handed mouse button swapping + * Fixed debug build error on win32 + * Reduced frequency of large cursor jumps when leaving win32 server + * Changed cursor motion on win32 multimon to relative moves only + + +* Jan-25-2003 - Synergy 1.0.2 released + + Made following changes: + * Fixed out-of-bounds array lookup in the BSD and Windows network code + * Added ability to set screen options from Windows launch dialog + + +* Jan-22-2003 - Synergy 1.0.1 released + + Made following changes: + * Fixed running as a service on Windows NT family + + +* Jan-20-2003 - Synergy 1.0.0 released + + Made following changes: + * Refactored to centralize platform dependent code + * Added support for mouse wheel on Windows NT (SP3 and up) + * Portability improvements + * Added more documentation + * Fixes for working with xscreensaver + * Fixes for circular screen links + + This release has been tested on Linux and Windows. It builds and + is believed to run on Solaris and FreeBSD. It is believed to + build and run on Irix and AIX. It builds but does not work on + MacOS X. + + +* Dec-25-2002 - Synergy 0.9.14 released + + Made following changes: + * Fixed solaris compile problems (untested) + * Fixed irix compile problems (untested) + * Fixed windows client not reconnecting when server dies bug + * Fixed loss of ctrl+alt from windows server to non-windows clients + * Fixed handling of password protected windows client screen saver + * Now handling any number of pointer buttons on X11 + * Toggle key states now restored when leaving clients + * Added support for per-screen config options + * Added config options for half-duplex toggle keys on X11 + * Enabled class diagrams in doxygen documentation + + +* Nov-05-2002 - Synergy 0.9.13 released + + Made following changes: + * Fixed solaris compile problems (untested) + * Fixed MacOS X compile problems (semi-functional) + * Fixed gcc-3.2 compile problems + * Fixed some thread startup and shutdown bugs + * Server now quits if bind() fails with an error other than in use + * Fixed bug in moving mouse on Win98 without multiple monitors + * Fixed bug in handling TCP socket errors on read and write + * Fixed spurious screen saver activation on X11 + * Unix platforms can now read Win32 configuration files + * Minor error reporting fixes + + +* Sep-14-2002 - Synergy 0.9.12 released + + Made following changes: + * Win32 was not reporting log messages properly when run from synergy.exe + * Network error messages weren't reporting useful information + * Synergy won't build on gcc 3.2; added workaround for known problem + * X11 wasn't handling some keys/key combinations correctly + * Added option to change logging level when testing from synergy.exe + + +* Sep-04-2002 - Synergy 0.9.11 released + + Fixed following bugs: + * Worked around missing SendInput() on windows 95/NT 4 prior to SP3 + * Fixed keyboard mapping on X11 synergy client + + +* Sep-02-2002 - Synergy 0.9.10 released + + Fixed following bugs: + * The Pause/Break and KP_Enter buttons were not working correctly on windows + * Configuration options were being lost on windows after a reboot + * Added support for AltGr/ModeSwitch keys + * Added support for auto-start on windows when not administrator + * Improved autoconf + * Added workaround for lack of sstream header on g++ 2.95. + + +* Aug-18-2002 - Synergy 0.9.9 released + + Fixed three bugs: + * The PrintScrn button was not working correctly on windows + * The Win32 server could hang when a client disconnected + * Using the mouse wheel could hang the X server + + +* Aug-11-2002 - Synergy 0.9.8 released + + Supports any number of clients under Linux or Windows 95 or NT4 + or later. Includes mouse and keyboard sharing, clipboard + synchronization and screen saver synchronization. Supports ASCII + keystrokes, 5 button mouse with wheel, and Unicode text clipboard + format. diff --git a/PORTING b/PORTING new file mode 100644 index 0000000..3281822 --- /dev/null +++ b/PORTING @@ -0,0 +1,394 @@ +Synergy Developer and Porting Guide +=================================== + +This document is under development. + +Code Organization +----------------- + +The synergy source code organization is: + +. -- root makefiles, some standard documentation +cmd -- program source code + launcher -- synergy launcher for Windows + synergyc -- synergy client + synergys -- synergy server +config -- stuff for autoconf/automake +dist -- files for creating distributions + rpm -- files for creating RPMs +doc -- placeholder for documentation +examples -- example files +lib -- library source code + arch -- platform dependent utility library + base -- simple utilities + client -- synergy client library + common -- commonly needed header files + http -- simple http tools + io -- I/O + mt -- multithreading + net -- networking + platform -- platform dependent display/window/event stuff + server -- synergy server library + synergy -- synergy shared client/server code library + +Note how the utility code required by the programs is placed into +separate library directories. This makes the makefiles a little +more awkward but makes for a cleaner organization. The top level +directory has only the standard documentation files and the files +necessary to configure and build the rest of the project. + + +Coding Style Guide +------------------ + +Synergy uses many coding conventions. Contributed code should +following these guidelines. + +- Symbol Naming + Names always begin with a letter (never an underscore). The first + letter of interior names are always capitalized. Acronyms should + be all uppercase. For example: myTextAsASCII. + + Names come it two flavors: leading capital and leading lowercase. + The former have the first character capitalized and the latter + don't. In the following table, leading capital names are indicated + by `Name' and leading lowercase names by `name'. + + The naming convention for various things are: + + * Exceptions -- X + Name XMyException + * Interfaces -- I + Name IMyInterface + * Template Classes -- T + Name TMyTemplate<> + * Other Classes -- C + Name CMyClass + * Enumerations -- E + Name EMyEnumeration + * Constants -- k + Name kMyConstant + * Data Members -- m_ + name m_myDataMember + * Methods -- name myMethod + * Functions -- name myFunction + * Variables -- name myVariable + + Exceptions are types that get thrown and are generally derived + (possibly indirectly) from XBase. Interfaces are derived (possibly + indirectly) from IInterface and have only pure virtual functions. + Other classes are classes that aren't exceptions or interfaces. + Constants include global constants and enumerants. + + Method names should usually have the form `verbObject'. For example: + * isGameOn() + * getBeer() + * pressPowerButton() + * setChannel() + In general, use `get' and `set' to read and write state but use `is' + to read boolean state. Note that classes that contain only `is', + `get', and `set' are probably plain old data; you might want to + consider using public data members only or, better, refactor your + design to have classes that actually do something more than just + hold data. + +- File Naming + Each class should have one source and one header file. If the + class is named `CMyClass' then the source file should be named + `CMyClass.cpp' and the header file `CMyClass.h'. + + Headers files not containing a class should have some meaningful + name with a leading capital (e.g. `Version.h'). + + Source files without a header file have a leading lowercase name. + Only files containing the entry point for an application should + lack a header file. + +- Dependencies + * No circular library dependencies + Library dependencies form an acyclic graph. Conceptually + libraries can be arranged in layers where each library only + references libraries in layers below it, not in the same layer + or layers above it. The makefiles build the lowest layer + libraries first and work upwards. + + * Avoid circular uses-a relationships + When possible, design classes with one-way uses-a relationships + and avoid cycles. This makes it easier to understand the code. + However, sometimes it's not always practical so it is permitted. + + * Included files in headers + Headers should #include only the necessary headers. In + particular, if a class is referenced in a header file only as a + pointer or a reference then use `class COtherClass;' instead of + `#include "COtherClass.h".' + + * #include syntax + Non-synergy header files must be included using angle brackets + while synergy header files must be included using double quotes. + #include "CSynergyHeader.h" + #include + The file name in a #include must not be a relative path unless + it's a system header file and it's customary to use a relative + path, e.g. `#include '. Use compiler options to + add necessary directories to the include search path. + + * Included file ordering + Files should be included in the following order: + * Header for source file + The first include for CMyClass.cpp must be CMyClass.h. + * Other headers in directory, sorted alphabetically + * Headers for each library, sorted alphabetically per library + Include headers from the library closest in the dependency graph + first, then the next farthest, etc. Sort alphabetically within + each library. + * System headers + +- C++ + * C++ features + Synergy uses the following more recent C++ features: + * bool + * templates + * exceptions + * mutable + * new scoping rules + * the standard C++ library + + Do not use the following C++ features: + * dynamic_cast + * run time type information + * namespaces and using (use std:: where necessary) + + The new scoping rules say that the scope of a variable declared + in a for statement is limited to the for loop. For example: + + for (int i = 0; i < 10; ++i) { + // i is in scope here + } + // i is not in scope here + + for (int i = -10; i < 0; ++i) { + // an entirely new i is in scope here + } + // i is not in scope here + + This is used routinely in synergy, but only in for loops. There + is a macro for `for' in lib/base/common.h when building under + Microsoft Visual C++ that works around the fact that that compiler + doesn't follow the new scoping rules. Use the macro if your + compiler uses the old scoping rules. + + * Standard C++ library + The standard C++ library containers should always be used in favor + of custom containers wherever reasonable. std::string is used + throughout synergy but only as the CString typedef; always use + CString, never std::string except in the arch library. Synergy + avoids using auto_ptr due to some portability problems. Synergy + makes limited use of standard algorithms and streams but they can + be freely used in new code. + + * Limited multiple inheritance + Classes should inherit implementation from at most one superclass. + Inheriting implementation from multiple classes can have unpleasant + consequences in C++ due to it's limited capabilities. Classes can + inherit from any number of interface classes. An interface class + provides only pure virtual methods. Synergy breaks this rule in + IInterface which implements the virtual destructor for convenience. + + * No globals + Avoid global variables. All global variables must be static, making + it visible only with its source file. Most uses of global variables + are better served by static data members of a class. Global + constants are permitted in some circumstances. + + Also avoid global functions. Use public static member functions in + a class instead. + + These rules are violated by the main source file for each program + (except that the globals are still static). They could easily be + rewritten to put all the variables and functions into a class but + there's little to be gained by that. + + * Private data only + If a class is plain-old-data (i.e. it has no methods) all of its + data members should be public. Otherwise all of its data members + should be private, not public or protected. This makes it much + easier to track the use of a member when reading code. Protected + data is not allowed because `protected' is a synonym for `public + to my subclasses' and public data is a Bad Thing. While it might + seem okay in this limited situation, the situation is not at all + limited since an arbitrary number of classes can be derived, + directly or indirectly, from the class and any of those classes + have full access to the protected data. + + * Plain old data + A class that merely contains data and doesn't perform operations + on that data (other than reads and writes) is plain old data (POD). + POD should have only public data members and non-copy constructors. + It must not have any methods other than constructors, not even a + destructor or assignment operators, nor protected or private data. + Note that this definition of POD is not the definition used in the + C++ standard, which limits the contained data types to types that + have no constructors, destructors, or methods. + + * Avoid using friend + Avoid declaring friend functions or classes. They're sometimes + necessary for operator overloading. If you find it necessary to + add friends to some class C, consider creating a utility class U. + A utility class is declared as the only friend of C and provides + only static methods. Each method forwards to a private method on + an object of C type (passed as a parameter to the U's method). + This makes maintenance easier since only U has friend access to C + and finding any call to U is trivial (they're prefixed by U::). + + * Don't test for NULL when using `delete' or `delete[]' + It's unnecessary since delete does it anyway. + +- Makefiles + Automake's makefiles (named Makefile.am) have a few requirements: + * Define the following macros at the top of the file: + NULL = + DEPTH = + VDEPTH = ./$(VPATH)/$(DEPTH) + is `..', `../..', `../../..', etc, + whichever references the top directory of the synergy tree. For + example, for a subdirectory of the top level use `..', for a + subdirectory of a subdirectory of the top level use `../..'. + * Lists should have one item per line and end in $(NULL). For + example: + EXTRA_DIST = \ + kiwi.txt \ + mango.cpp \ + papaya.h \ + $(NULL) + Indentation must use tabs in a makefile. Line continuations + (backslashes) should be aligned using tabs. + * Lists of files should be sorted alphabetically. Lists of + subdirectories must be in the desired build order. + +- Source Formatting + Every project has its own formatting style and no style satisfies + everyone. New code should be consistent with existing code: + + * All files should include the copyright and license notice + * Use tabs to indent + * Tabs are 4 columns + * Lines should not extend past the 80th column + * Open braces ({) go on same line as introducing statement + `for (i = 0; i < 10; ++i) {' not + for (i = 0; i < 10; ++i) + { + * Close braces line up with introducing statement + * Open brace for function is on a line by itself in first column + * Close brace for function lines up with open brace + * Always use braces on: if, else, for, while, do, switch + * `else {' goes on its own line + * Always explicitly test pointers against NULL + e.g. `if (ptr == NULL)' not `if (ptr)' + * Always explicitly test integral values against 0 + e.g. `if (i == 0)' not `if (i)' + * Put spaces around binary operators and after statements + e.g. `if (a == b) {' not `if(a==b){' + * C'tor initializers are one per line, indented one tab stop + * Other indentation should follow existing practice + * Use Qt style comments for extraction by doxygen (i.e. //! and /*!) + * Mark incomplete or buggy code with `FIXME' + +- Other + * calls to LOG() should always be all on one line (even past column 80) + + +Class Relationships +------------------- + +The doxygen documentation can help in understanding the relationships +between objects. Use `make doxygen' in the top level directory to +create the doxygen documentation into doc/doxygen/html. You must have +doxygen installed, of course. + +FIXME -- high level overview of class relationships + + +Portability +----------- + +Synergy is mostly platform independent code but necessarily has +platform dependent parts. The mundane platform dependent parts +come from the usual suspects: networking, multithreading, file +system, high resolution clocks, system logging, etc. Porting +these parts is relatively straightforward. + +Synergy also has more esoteric platform dependent code. The +functions for low-level event interception and insertion, +warping the cursor position, character to keyboard event +translation, clipboard manipulation, and screen saver control +are often obscure and poorly documented. Unfortunately, these +are exactly the functions synergy requires to do its magic. + +Porting synergy to a new platform requires the following steps: + +- Setting up the build +- Adjusting lib/common/common.h +- Implementing lib/arch +- Implementing lib/platform +- Tweaks + +Setting up the build: + +The first phase is simply to create the files necessary to build the +other files. On Unix, synergy uses autoconf/automake which produces +a `configure' script that generates makefiles. On Windows, synergy +uses Visual C++ workspace and project files. If you're porting to +another Unix variant, you may need to adjust `configure.in', +`acinclude.m4', and Unix flavor dependent code in lib/arch. + +Adjusting lib/common/common.h: + +The lib/common/common.h header file is included directly or indirectly +by every other file. It prepares some platform dependent macros for +integer sizes and defines a macro for conveniently testing which +platform we're building on. That macro is named *_LIKE (e.g. UNIX_LIKE) +and has the value `1'. Exactly one *_LIKE macro must be defined by +common.h. + +Implementing lib/arch: + +Much platform dependent code lives in lib/arch. There are several +interface classes there and they must all be implemented for each +platform. See the interface header files for more information. + +Platforms requiring special functions should create a class named +CArchMiscXXX where XXX is the platform name. The class should have +only static methods. Clients can include the appropriate header +file and make calls directly, surrounded by a suitable #ifdef/#endif. + +Implementing lib/platform: + +Most of the remaining platform dependent code lives in lib/platform. +The code there implements platform dependent window, clipboard, and +screen saver handling. If a platform is named XXX then the following +classes should be derived and implemented: + + * CXXXClipboard : IClipboard + Provides clipboard operations. Typically, this class will + have helper classes for converting between various clipboard + data formats. + + * CXXXScreen : IScreen + Provide screen operations common to the server and clients. + The CXXXPrimaryScreen and CXXXSecondaryScreen classes use a + CXXXScreen. + + * CXXXPrimaryScreen : CPrimaryScreen, IScreenEventHandler + Provides server screen operations. + + * CXXXSecondaryScreen : CSecondaryScreen, IScreenEventHandler + Provides client screen operations. + + * CXXXScreenSaver : IScreenSaver + Provides screen saver operations. + +Tweaks: + +Finally, each platform typically requires various adjustments here +and there. In particular, synergyc.cpp and synergys.cpp usually +require platform dependent code for the main entry point, parsing +arguments, and reporting errors. Also, some platforms may benefit +from a graphical user interface front end. These are generally +not portable and synergy doesn't provide any infrastructure for +the code common to any platform, though it may do so someday. +There is, however, an implementation of a GUI front end for Windows +that serves as an example. diff --git a/README b/README new file mode 100644 index 0000000..072c9f2 --- /dev/null +++ b/README @@ -0,0 +1,267 @@ +Synergy +======= + +synergy: [noun] a mutually advantageous conjunction of distinct elements + +Synergy lets you easily share a single mouse and keyboard between +multiple computers with different operating systems, each with its +own display, without special hardware. It's intended for users +with multiple computers on their desk since each system uses its +own display. + +Redirecting the mouse and keyboard is as simple as moving the mouse +off the edge of your screen. Synergy also merges the clipboards of +all the systems into one, allowing cut-and-paste between systems. +Furthermore, it synchronizes screen savers so they all start and stop +together and, if screen locking is enabled, only one screen requires +a password to unlock them all. + +Synergy is open source and released under the GNU Public License (GPL). + +The synergy home page is: +http://synergy2.sourceforge.net/ + +The synergy project page is: +http://sourceforge.net/projects/synergy2/ + +Report bugs to: +http://sourceforge.net/tracker/?func=add&group_id=59275&atid=490467 + + +Please see the following files for more information: +AUTHORS -- The list of synergy's authors +BUGS -- A list of known bugs and limitations +COPYING -- The license synergy is release under +FAQ -- Frequently asked questions about synergy +HISTORY -- A brief history of synergy +INSTALL -- Detailed build and installation instructions +NEWS -- News about the synergy project +PORTING -- Porting guide for developers +TODO -- List of things to add to synergy + + +System Requirements +------------------- + +* All operating systems: + keyboard, + mouse, + TCP/IP networking; + +* Microsoft Windows 95, Windows 98, Windows Me (the Windows 96 family); + +* Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family); + +* Unix: + X Windows version 11 revision 4 or up with the XTEST extension + (use `xdpyinfo | grep XTEST' to check for XTEST). + +In this document, "Unix" means any of the following: Linux, Solaris, +Irix. Synergy may compile and run on other Unix variants, too. Patches +for other platforms are welcome (including patches that package binaries); +See the contact information available off of the synergy home page or use +the patch page on sourceforge. + + +Installation +------------ + +See INSTALL for detailed build and installation instructions and for +more information on configuring synergy. + + +Quick Start +----------- +Synergy lets you use one keyboard and mouse across multiple computers. +To do so it requires that all the computers are connected to each other +via TCP/IP networking. Most systems come with this installed. + +The first step is to pick which keyboard and mouse you want to share. +The computer with that keyboard and mouse is called the "primary +screen" and it runs the synergy server. All of the other computers +are "secondary screens" and run the synergy client. The Windows NT +family, starting with NT 4 with service pack 3, and Unix are the best +choices. The Windows version provides a convenient GUI for +configuration. + +Second, you install the software. Choose the appropriate package +and install it. On Windows you should unzip the files into the +`Program Files' directory; all the files will be put into a new +directory named `Synergy' under `Program Files'. You must install +the software on all the computers that will share the mouse and +keyboard. + +Third, you configure and start the server. + Windows + ------- + Run `synergy' by double clicking on it. This brings up a dialog. + Configure the server: + + * Click the `Server' radio button + * Click `Add' to add the server to the `Screens' list + * Enter the name of server (the computer name is recommended) + * Enter other names the server is known by + * Click OK + * Use `Add' to add your other computers + * Using a computer's name as its screen name is recommended + * Use the controls under `Layout' to link screens together + * Click (once) on the server's name in the `Screens' list + * Choose the screen to the left of the server + * Use `---' if there is no screen to the left of the server + * Choose the screens to the right, above and below the server + * Repeat the above steps for all the other screens + * Use `Options...' to set desired options. + * If the server's screen name is not the server's computer name: + * Click `Advanced...' + * Enter the server's screen name next to `Screen Name' + * Click `OK' + + Now click `Test'. The server will start and you'll see a console window + with log messages telling you about synergy's progress. If an error + occurs you'll get a dialog box telling you synergy is about to quit; + read the log messages to determine the problem then correct it and try + `Test' again. + + Unix + ---- + Create a text file named synergy.conf with the following: + + section: screens + : + : + end + section: links + : + right = + : + left = + end + + Replace each occurrence of `' with the host name of the + primary screen computer and `' with the host name of a + secondary screen computer. In the above example, is to + the right of and is to the left of . + If necessary you should replace `right' and `left' with `left', + `right', 'up', or `down'. If you have more than two computers + you can add those too: add each computer's host name in the + `screens' section and add the appropriate links. + + Now start the server. Normally synergy wants to run "in the + background." It detaches from the terminal and doesn't have a + visible window, effectively disappearing from view. Until you're + sure your configuration works you should start synergy "in the + foreground" using the `-f' command line option: + + synergys -f --config synergy.conf + + Check the reported messages for errors. Use ctrl+c to stop synergy, + correct any problems, and start it again. + +Finally, start the clients. + Windows + ------- + Run `synergy' on the client by double clicking on it. Configure the + client: + + * Click the `Client' radio button + * Enter the server's computer name in `Server Host Name' + * Do not use any of the server's screen names, unless one of those + is also the computer name + * If the client's screen name is not the client's computer name: + * Click `Advanced...' + * Enter the client's screen name next to `Screen Name' + * Click `OK' + * Click `Test' + + If an error occurs you'll get a dialog box telling you synergy is + about to quit; read the log messages to determine the problem then + correct it and try `Test' again. + + Unix + ---- + + To start a client, enter the following: + + synergyc -f + + where `' is replaced by the name of the computer + running the synergy server. If an error is reported use ctrl+c to + stop synergy, fix the error, and try again. + +Both the client and server should immediately report the connection +or an error. If successful, you should now be able to move the +mouse off the appropriate edge of your server's screen and have it +appear on the client's screen. If you're running the synery server +on Windows 95, 98, or Me then make sure the synergy log window is +not the active window; just click on another window, like synergy's +`Running Test...' window, if it is. Use the mouse and keyboard +normally except use the edge of the screens to jump to other screens. +You can also cut-and-paste across computers. Currently, only text +transfers between computers. Start the remaining clients. + +Once the configuration is verified, see the instructions in INSTALL +under `Starting Automatically on ...' for details on running synergy +in the background and on starting synergy automatically when you start +your computers. + + +Tips and Tricks +--------------- +* Be aware that not all keystrokes can be handled by synergy. In + particular, ctrl+alt+del is not handled. However, synergy can + convert ctrl+alt+pause into ctrl+alt+del on the client side. + (Synergy must be installed as a service on the client for this to + work on the Windows NT family.) Some non-standard keys may not + work, especially "multimedia" buttons, though several are + correctly handled. + +* A screen can be its own neighbor. That allows a screen to "wrap". + For example, if a configuration linked the left and right sides of + a screen to itself then moving off the left of the screen would put + the mouse at the right of the screen and vice versa. + +* You cannot switch screens when a key or mouse button is pressed. + +* You cannot switch screens when the scroll lock it toggled on. Use + this to prevent unintentional switching. + +* Turn off mouse driven virtual desktop switching on X windows. It + will interfere with synergy. Use keyboard shortcuts instead. + +* Synergy's screen saver synchronization works best with xscreensaver + under X windows. Synergy works better with xscreensaver if it is + using one of the screen saver extensions. Prior to xscreensaver 4.0 + you can use `-mit-extension', `-sgi-extension', or `-xidle-extension' + command line options to enable an extension (assuming your server has + the extension). Starting with 4.0 you must enable the corresponding + option in your .xscreensaver file. + +* Synergy automatically converts newlines in clipboard text (Unix + expects \n to end each line while Windows expects \r\n). + +* Clients can be started and stopped at any time. When a screen is + not connected, the mouse will jump over that screen as if the mouse + had moved all the way across it and jumped to the next screen. + +* A client's keyboard and mouse are fully functional while synergy is + running. You can use them in case synergy locks up. + +* Strong authentication and encryption is available by using SSH. See + the INSTALL file for more information. Synergy does not otherwise + provide secure communications and it should not be used on or over + untrusted networks. + +* Synergy doesn't work if a 16-bit Windows application has the focus + on Windows 95/98/Me. This is due to limitations of Windows. One + commonly used 16-bit application is the command prompt (command.exe) + and this includes synergy's log window when running in test mode. + + +Bug Reports +----------- + +Synergy is being improved all the time but we can only fix problems +that we know about. Please let us know of any problems you encounter, +including confusing or unhelpful documentation. File reports at: + + http://sourceforge.net/tracker/?func=add&group_id=59275&atid=490467 diff --git a/TODO b/TODO new file mode 100644 index 0000000..78b3b80 --- /dev/null +++ b/TODO @@ -0,0 +1,61 @@ +Synergy To Do List +================== + +Things to do to synergy, in no particular order: + +* Provide GUI configuration + + There's a GUI tool on win32 but no other platforms. Also, it'd be + nice if the tool allowed users to drag screen icons around to set + the links between them (though this assumes links are symmetrical + and synergy supports asymmetrical links). + +* Provide taskbar feedback + + There's a tray icon on win32 for checking synergy's current status + and to quit synergy. It'd be nice to have something similar on + X11. + +* Port to other platforms + + Most desired is MacOS X. + +* Write man/html pages + +* Provide a win32 installer/uninstaller + + Synergy doesn't have any special needs so even just unzipping is + satisfactory, but a proper installer would be nice. And, more + importantly, it should provide an uninstaller. + +* Add more clipboard formats + + Synergy currently supports only text on the clipboard. It should + support more formats, such as images and sound. For each format, + some canonical type must be chosen. For text, that's UTF-8 with + \n for newlines. For images, it might be BMP or PNG. Whatever it + is it should losslessly support any type it might be converted to. + The type is converted to each platform's native type. For example, + BMP for images on win32. + +Then there are major new features: + +* Provide a KVM mode + + In this mode synergy would share the monitor in addition to the + keyboard and mouse. + +* Support for limited drag and drop between systems + +* Support for (virtual) terminals on unix + + This would be useful in KVM mode to administer several remote + headless systems that you don't want running X just so synergy + can work. + +* Configurable keys + + This includes shortcuts to jump to different screens, always + directing certain keystrokes to the same system, never sending + certain keystrokes to some systems, and remapping keys on the + server to other keys on the clients. diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..2ebdc27 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,515 @@ +dnl synergy -- mouse and keyboard sharing utility +dnl Copyright (C) 2002 Chris Schoeneman +dnl +dnl This package is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU General Public License +dnl found in the file COPYING that should have accompanied this file. +dnl +dnl This package is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. + +AC_DEFUN([ACX_CHECK_SOCKLEN_T], [ + AC_MSG_CHECKING([for socklen_t]) + AC_TRY_COMPILE([ + #include + #include + ], + [socklen_t len;],[acx_socklen_t_ok=yes],[acx_socklen_t_ok=no]) + AC_MSG_RESULT($acx_socklen_t_ok) + if test x"$acx_socklen_t_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_SOCKLEN_T,1,[Define if your compiler defines socklen_t.]),[$1]) + : + else + acx_socklen_t_ok=no + $2 + fi +])dnl ACX_CHECK_SOCKLEN_T + +AC_DEFUN([ACX_CHECK_CXX], [ + AC_MSG_CHECKING([if g++ defines correct C++ macro]) + AC_TRY_COMPILE(, [ + #if defined(_LANGUAGE_C) && !defined(_LANGUAGE_C_PLUS_PLUS) + #error wrong macro + #endif],[acx_cxx_macro_ok=yes],[acx_cxx_macro_ok=no]) + AC_MSG_RESULT($acx_cxx_macro_ok) + if test x"$acx_cxx_macro_ok" = xyes; then + SYNERGY_CXXFLAGS="" + else + SYNERGY_CXXFLAGS="-U_LANGUAGE_C -D_LANGUAGE_C_PLUS_PLUS" + fi +])dnl ACX_CHECK_CXX + +AC_DEFUN([ACX_CHECK_CXX_BOOL], [ + AC_MSG_CHECKING([for bool support]) + AC_TRY_COMPILE(, [bool t = true, f = false;], + [acx_cxx_bool_ok=yes],[acx_cxx_bool_ok=no]) + AC_MSG_RESULT($acx_cxx_bool_ok) + if test x"$acx_cxx_bool_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_BOOL,1,[Define if your compiler has bool support.]),[$1]) + : + else + acx_cxx_bool_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_BOOL + +AC_DEFUN([ACX_CHECK_CXX_EXCEPTIONS], [ + AC_MSG_CHECKING([for exception support]) + AC_TRY_COMPILE(, [try{throw int(4);}catch(int){throw;}catch(...){}], + [acx_cxx_exception_ok=yes],[acx_cxx_exception_ok=no]) + AC_MSG_RESULT($acx_cxx_exception_ok) + if test x"$acx_cxx_exception_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_EXCEPTIONS,1,[Define if your compiler has exceptions support.]),[$1]) + : + else + acx_cxx_exception_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_EXCEPTIONS + +AC_DEFUN([ACX_CHECK_CXX_CASTS], [ + AC_MSG_CHECKING([for C++ cast support]) + AC_TRY_COMPILE(, [const char* f="a";const_cast(f); + reinterpret_cast(f);static_cast(4.5);], + [acx_cxx_cast_ok=yes],[acx_cxx_cast_ok=no]) + AC_MSG_RESULT($acx_cxx_cast_ok) + if test x"$acx_cxx_cast_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_CASTS,1,[Define if your compiler has C++ cast support.]),[$1]) + : + else + acx_cxx_cast_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_CASTS + +AC_DEFUN([ACX_CHECK_CXX_MUTABLE], [ + AC_MSG_CHECKING([for mutable support]) + AC_TRY_COMPILE(, [struct A{mutable int b;void f() const {b=0;}}; + A a;a.f();],[acx_cxx_mutable_ok=yes],[acx_cxx_mutable_ok=no]) + AC_MSG_RESULT($acx_cxx_mutable_ok) + if test x"$acx_cxx_mutable_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_MUTABLE,1,[Define if your compiler has mutable support.]),[$1]) + : + else + acx_cxx_mutable_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_MUTABLE + +AC_DEFUN([ACX_CHECK_CXX_STDLIB], [ + AC_MSG_CHECKING([for C++ standard library]) + AC_TRY_LINK([#include ], [std::set a; a.insert(3);], + [acx_cxx_stdlib_ok=yes],[acx_cxx_stdlib_ok=no]) + AC_MSG_RESULT($acx_cxx_stdlib_ok) + if test x"$acx_cxx_stdlib_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_STDLIB,1,[Define if your compiler has standard C++ library support.]),[$1]) + : + else + acx_cxx_stdlib_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_STDLIB + +AC_DEFUN([ACX_CHECK_GETPWUID_R], [ + AC_MSG_CHECKING([for working getpwuid_r]) + AC_TRY_LINK([#include ], + [char buffer[4096]; struct passwd pwd, *pwdp; + getpwuid_r(0, &pwd, buffer, sizeof(buffer), &pwdp);], + acx_getpwuid_r_ok=yes, acx_getpwuid_r_ok=no) + AC_MSG_RESULT($acx_getpwuid_r_ok) + if test x"$acx_getpwuid_r_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_GETPWUID_R,1,[Define if you have a working \`getpwuid_r\' function.]),[$1]) + : + else + acx_getpwuid_r_ok=no + $2 + fi +])dnl ACX_CHECK_GETPWUID_R + +AC_DEFUN([ACX_CHECK_POLL], [ + AC_MSG_CHECKING([for poll]) + AC_TRY_LINK([#include ], + [struct pollfd ufds[] = { 0, POLLIN, 0 }; poll(ufds, 1, 10);], + acx_poll_ok=yes, acx_poll_ok=no) + AC_MSG_RESULT($acx_poll_ok) + if test x"$acx_poll_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_POLL,1,[Define if you have the \`poll\' function.]),[$1]) + : + else + acx_poll_ok=no + $2 + fi +])dnl ACX_CHECK_POLL + +dnl See if we need extra libraries for nanosleep +AC_DEFUN([ACX_CHECK_NANOSLEEP], [ + acx_nanosleep_ok=no + acx_nanosleep_list="" + + dnl check if user has set NANOSLEEP_LIBS + save_user_NANOSLEEP_LIBS="$NANOSLEEP_LIBS" + if test x"$NANOSLEEP_LIBS" != x; then + acx_nanosleep_list=user + fi + + dnl check various libraries (including no extra libraries) for + dnl nanosleep. `none' should appear first. + acx_nanosleep_list="none $acx_nanosleep_list rt" + for flag in $acx_nanosleep_list; do + case $flag in + none) + AC_MSG_CHECKING([for nanosleep]) + NANOSLEEP_LIBS="" + ;; + + user) + AC_MSG_CHECKING([for nanosleep in $save_user_NANOSLEEP_LIBS]) + NANOSLEEP_LIBS="$save_user_NANOSLEEP_LIBS" + ;; + + *) + AC_MSG_CHECKING([for nanosleep in -l$flag]) + NANOSLEEP_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$NANOSLEEP_LIBS $LIBS" + AC_TRY_LINK([#include ], + [struct timespec t = { 1, 1000 }; nanosleep(&t, NULL);], + acx_nanosleep_ok=yes, acx_nanosleep_ok=no) + LIBS="$save_LIBS" + AC_MSG_RESULT($acx_nanosleep_ok) + if test x"$acx_nanosleep_ok" = xyes; then + break; + fi + NANOSLEEP_LIBS="" + done + + AC_SUBST(NANOSLEEP_LIBS) + + # execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: + if test x"$acx_nanosleep_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_NANOSLEEP,1,[Define if you have the \`nanosleep\' function.]),[$1]) + : + else + acx_nanosleep_ok=no + $2 + fi +])dnl ACX_CHECK_NANOSLEEP + +dnl See if we need extra libraries for inet_aton +AC_DEFUN([ACX_CHECK_INET_ATON], [ + acx_inet_aton_ok=no + acx_inet_aton_list="" + + dnl check if user has set INET_ATON_LIBS + save_user_INET_ATON_LIBS="$INET_ATON_LIBS" + if test x"$INET_ATON_LIBS" != x; then + acx_inet_aton_list=user + fi + + dnl check various libraries (including no extra libraries) for + dnl inet_aton. `none' should appear first. + acx_inet_aton_list="none $acx_inet_aton_list resolv" + for flag in $acx_inet_aton_list; do + case $flag in + none) + AC_MSG_CHECKING([for inet_aton]) + INET_ATON_LIBS="" + ;; + + user) + AC_MSG_CHECKING([for inet_aton in $save_user_INET_ATON_LIBS]) + INET_ATON_LIBS="$save_user_INET_ATON_LIBS" + ;; + + *) + AC_MSG_CHECKING([for inet_aton in -l$flag]) + INET_ATON_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$INET_ATON_LIBS $LIBS" + AC_TRY_LINK([#include + #include + #include + #include ], + [struct in_addr addr; inet_aton("foo.bar", &addr);], + acx_inet_aton_ok=yes, acx_inet_aton_ok=no) + LIBS="$save_LIBS" + AC_MSG_RESULT($acx_inet_aton_ok) + if test x"$acx_inet_aton_ok" = xyes; then + break; + fi + INET_ATON_LIBS="" + done + + AC_SUBST(INET_ATON_LIBS) +])dnl ACX_CHECK_INET_ATON + +dnl The following macros are from http://www.gnu.org/software/ac-archive/ +dnl which distributes them under the following license: +dnl +dnl Every Autoconf macro presented on this web site is free software; you can +dnl redistribute it and/or modify it under the terms of the GNU General +dnl Public License as published by the Free Software Foundation; either +dnl version 2, or (at your option) any later version. +dnl +dnl They are distributed in the hope that they will be useful, but WITHOUT +dnl ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +dnl FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +dnl more details. (You should have received a copy of the GNU General Public +dnl License along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place -- Suite 330, Boston, MA 02111-1307, +dnl USA.) +dnl +dnl As a special exception, the Free Software Foundation gives unlimited +dnl permission to copy, distribute and modify the configure scripts that are +dnl the output of Autoconf. You need not follow the terms of the GNU General +dnl Public License when using or distributing such scripts, even though +dnl portions of the text of Autoconf appear in them. The GNU General Public +dnl License (GPL) does govern all other use of the material that constitutes +dnl the Autoconf program. +dnl +dnl Certain portions of the Autoconf source text are designed to be copied +dnl (in certain cases, depending on the input) into the output of Autoconf. +dnl We call these the "data" portions. The rest of the Autoconf source text +dnl consists of comments plus executable code that decides which of the data +dnl portions to output in any given case. We call these comments and +dnl executable code the "non-data" portions. Autoconf never copies any of the +dnl non-data portions into its output. +dnl +dnl This special exception to the GPL applies to versions of Autoconf +dnl released by the Free Software Foundation. When you make and distribute a +dnl modified version of Autoconf, you may extend this special exception to +dnl the GPL to apply to your modified version as well, *unless* your modified +dnl version has the potential to copy into its output some of the text that +dnl was the non-data portion of the version that you started with. (In other +dnl words, unless your change moves or copies text from the non-data portions +dnl to the data portions.) If your modification has such potential, you must +dnl delete any notice of this special exception to the GPL from your modified +dnl version + +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CXXFLAGS="$CXXFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: threads are created detached by default + # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + AC_MSG_CHECKING([for joinable pthread attribute]) + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_JOINABLE;], + ok=PTHREAD_CREATE_JOINABLE, ok=unknown) + if test x"$ok" = xunknown; then + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_UNDETACHED;], + ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) + fi + if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then + AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, + [Define to the necessary symbol if this constant + uses a non-standard name on your system.]) + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" = xunknown; then + AC_MSG_WARN([we do not know how to create joinable pthreads]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + alpha*-osf*) flag="-D_REENTRANT";; + *solaris*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + # Detect POSIX sigwait() + AC_MSG_CHECKING([for POSIX sigwait]) + AC_TRY_LINK([#include + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=yes, ok=unknown) + if test x"$ok" = xunknown; then + save_CXXFLAGS2="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -D_POSIX_PTHREAD_SEMANTICS" + AC_TRY_LINK([#include + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=-D_POSIX_PTHREAD_SEMANTICS, ok=no) + CXXFLAGS="$save_CXXFLAGS2" + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" != xno; then + AC_DEFINE(HAVE_POSIX_SIGWAIT,1,[Define if you have a POSIX \`sigwait\' function.]) + if test x"$ok" != xyes; then + PTHREAD_CFLAGS="$ok $PTHREAD_CFLAGS" + fi + fi + + # Detect pthread signal functions + AC_MSG_CHECKING([for pthread signal functions]) + AC_TRY_LINK([#include + #include ], + [pthread_kill(pthread_self(), SIGTERM);], + ok=yes, ok=no) + AC_MSG_RESULT(${ok}) + if test x"$ok" = xyes; then + AC_DEFINE(HAVE_PTHREAD_SIGNAL,1,[Define if you have \`pthread_sigmask\' and \`pthread_kill\' functions.]) + fi + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +])dnl ACX_PTHREAD diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..7b27d1e --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,1076 @@ +# aclocal.m4 generated automatically by aclocal 1.5 + +# Copyright 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +dnl synergy -- mouse and keyboard sharing utility +dnl Copyright (C) 2002 Chris Schoeneman +dnl +dnl This package is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU General Public License +dnl found in the file COPYING that should have accompanied this file. +dnl +dnl This package is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. + +AC_DEFUN([ACX_CHECK_SOCKLEN_T], [ + AC_MSG_CHECKING([for socklen_t]) + AC_TRY_COMPILE([ + #include + #include + ], + [socklen_t len;],[acx_socklen_t_ok=yes],[acx_socklen_t_ok=no]) + AC_MSG_RESULT($acx_socklen_t_ok) + if test x"$acx_socklen_t_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_SOCKLEN_T,1,[Define if your compiler defines socklen_t.]),[$1]) + : + else + acx_socklen_t_ok=no + $2 + fi +])dnl ACX_CHECK_SOCKLEN_T + +AC_DEFUN([ACX_CHECK_CXX], [ + AC_MSG_CHECKING([if g++ defines correct C++ macro]) + AC_TRY_COMPILE(, [ + #if defined(_LANGUAGE_C) && !defined(_LANGUAGE_C_PLUS_PLUS) + #error wrong macro + #endif],[acx_cxx_macro_ok=yes],[acx_cxx_macro_ok=no]) + AC_MSG_RESULT($acx_cxx_macro_ok) + if test x"$acx_cxx_macro_ok" = xyes; then + SYNERGY_CXXFLAGS="" + else + SYNERGY_CXXFLAGS="-U_LANGUAGE_C -D_LANGUAGE_C_PLUS_PLUS" + fi +])dnl ACX_CHECK_CXX + +AC_DEFUN([ACX_CHECK_CXX_BOOL], [ + AC_MSG_CHECKING([for bool support]) + AC_TRY_COMPILE(, [bool t = true, f = false;], + [acx_cxx_bool_ok=yes],[acx_cxx_bool_ok=no]) + AC_MSG_RESULT($acx_cxx_bool_ok) + if test x"$acx_cxx_bool_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_BOOL,1,[Define if your compiler has bool support.]),[$1]) + : + else + acx_cxx_bool_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_BOOL + +AC_DEFUN([ACX_CHECK_CXX_EXCEPTIONS], [ + AC_MSG_CHECKING([for exception support]) + AC_TRY_COMPILE(, [try{throw int(4);}catch(int){throw;}catch(...){}], + [acx_cxx_exception_ok=yes],[acx_cxx_exception_ok=no]) + AC_MSG_RESULT($acx_cxx_exception_ok) + if test x"$acx_cxx_exception_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_EXCEPTIONS,1,[Define if your compiler has exceptions support.]),[$1]) + : + else + acx_cxx_exception_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_EXCEPTIONS + +AC_DEFUN([ACX_CHECK_CXX_CASTS], [ + AC_MSG_CHECKING([for C++ cast support]) + AC_TRY_COMPILE(, [const char* f="a";const_cast(f); + reinterpret_cast(f);static_cast(4.5);], + [acx_cxx_cast_ok=yes],[acx_cxx_cast_ok=no]) + AC_MSG_RESULT($acx_cxx_cast_ok) + if test x"$acx_cxx_cast_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_CASTS,1,[Define if your compiler has C++ cast support.]),[$1]) + : + else + acx_cxx_cast_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_CASTS + +AC_DEFUN([ACX_CHECK_CXX_MUTABLE], [ + AC_MSG_CHECKING([for mutable support]) + AC_TRY_COMPILE(, [struct A{mutable int b;void f() const {b=0;}}; + A a;a.f();],[acx_cxx_mutable_ok=yes],[acx_cxx_mutable_ok=no]) + AC_MSG_RESULT($acx_cxx_mutable_ok) + if test x"$acx_cxx_mutable_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_MUTABLE,1,[Define if your compiler has mutable support.]),[$1]) + : + else + acx_cxx_mutable_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_MUTABLE + +AC_DEFUN([ACX_CHECK_CXX_STDLIB], [ + AC_MSG_CHECKING([for C++ standard library]) + AC_TRY_LINK([#include ], [std::set a; a.insert(3);], + [acx_cxx_stdlib_ok=yes],[acx_cxx_stdlib_ok=no]) + AC_MSG_RESULT($acx_cxx_stdlib_ok) + if test x"$acx_cxx_stdlib_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_STDLIB,1,[Define if your compiler has standard C++ library support.]),[$1]) + : + else + acx_cxx_stdlib_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_STDLIB + +AC_DEFUN([ACX_CHECK_GETPWUID_R], [ + AC_MSG_CHECKING([for working getpwuid_r]) + AC_TRY_LINK([#include ], + [char buffer[4096]; struct passwd pwd, *pwdp; + getpwuid_r(0, &pwd, buffer, sizeof(buffer), &pwdp);], + acx_getpwuid_r_ok=yes, acx_getpwuid_r_ok=no) + AC_MSG_RESULT($acx_getpwuid_r_ok) + if test x"$acx_getpwuid_r_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_GETPWUID_R,1,[Define if you have a working \`getpwuid_r\' function.]),[$1]) + : + else + acx_getpwuid_r_ok=no + $2 + fi +])dnl ACX_CHECK_GETPWUID_R + +AC_DEFUN([ACX_CHECK_POLL], [ + AC_MSG_CHECKING([for poll]) + AC_TRY_LINK([#include ], + [struct pollfd ufds[] = { 0, POLLIN, 0 }; poll(ufds, 1, 10);], + acx_poll_ok=yes, acx_poll_ok=no) + AC_MSG_RESULT($acx_poll_ok) + if test x"$acx_poll_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_POLL,1,[Define if you have the \`poll\' function.]),[$1]) + : + else + acx_poll_ok=no + $2 + fi +])dnl ACX_CHECK_POLL + +dnl See if we need extra libraries for nanosleep +AC_DEFUN([ACX_CHECK_NANOSLEEP], [ + acx_nanosleep_ok=no + acx_nanosleep_list="" + + dnl check if user has set NANOSLEEP_LIBS + save_user_NANOSLEEP_LIBS="$NANOSLEEP_LIBS" + if test x"$NANOSLEEP_LIBS" != x; then + acx_nanosleep_list=user + fi + + dnl check various libraries (including no extra libraries) for + dnl nanosleep. `none' should appear first. + acx_nanosleep_list="none $acx_nanosleep_list rt" + for flag in $acx_nanosleep_list; do + case $flag in + none) + AC_MSG_CHECKING([for nanosleep]) + NANOSLEEP_LIBS="" + ;; + + user) + AC_MSG_CHECKING([for nanosleep in $save_user_NANOSLEEP_LIBS]) + NANOSLEEP_LIBS="$save_user_NANOSLEEP_LIBS" + ;; + + *) + AC_MSG_CHECKING([for nanosleep in -l$flag]) + NANOSLEEP_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$NANOSLEEP_LIBS $LIBS" + AC_TRY_LINK([#include ], + [struct timespec t = { 1, 1000 }; nanosleep(&t, NULL);], + acx_nanosleep_ok=yes, acx_nanosleep_ok=no) + LIBS="$save_LIBS" + AC_MSG_RESULT($acx_nanosleep_ok) + if test x"$acx_nanosleep_ok" = xyes; then + break; + fi + NANOSLEEP_LIBS="" + done + + AC_SUBST(NANOSLEEP_LIBS) + + # execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: + if test x"$acx_nanosleep_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_NANOSLEEP,1,[Define if you have the \`nanosleep\' function.]),[$1]) + : + else + acx_nanosleep_ok=no + $2 + fi +])dnl ACX_CHECK_NANOSLEEP + +dnl See if we need extra libraries for inet_aton +AC_DEFUN([ACX_CHECK_INET_ATON], [ + acx_inet_aton_ok=no + acx_inet_aton_list="" + + dnl check if user has set INET_ATON_LIBS + save_user_INET_ATON_LIBS="$INET_ATON_LIBS" + if test x"$INET_ATON_LIBS" != x; then + acx_inet_aton_list=user + fi + + dnl check various libraries (including no extra libraries) for + dnl inet_aton. `none' should appear first. + acx_inet_aton_list="none $acx_inet_aton_list resolv" + for flag in $acx_inet_aton_list; do + case $flag in + none) + AC_MSG_CHECKING([for inet_aton]) + INET_ATON_LIBS="" + ;; + + user) + AC_MSG_CHECKING([for inet_aton in $save_user_INET_ATON_LIBS]) + INET_ATON_LIBS="$save_user_INET_ATON_LIBS" + ;; + + *) + AC_MSG_CHECKING([for inet_aton in -l$flag]) + INET_ATON_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$INET_ATON_LIBS $LIBS" + AC_TRY_LINK([#include + #include + #include + #include ], + [struct in_addr addr; inet_aton("foo.bar", &addr);], + acx_inet_aton_ok=yes, acx_inet_aton_ok=no) + LIBS="$save_LIBS" + AC_MSG_RESULT($acx_inet_aton_ok) + if test x"$acx_inet_aton_ok" = xyes; then + break; + fi + INET_ATON_LIBS="" + done + + AC_SUBST(INET_ATON_LIBS) +])dnl ACX_CHECK_INET_ATON + +dnl The following macros are from http://www.gnu.org/software/ac-archive/ +dnl which distributes them under the following license: +dnl +dnl Every Autoconf macro presented on this web site is free software; you can +dnl redistribute it and/or modify it under the terms of the GNU General +dnl Public License as published by the Free Software Foundation; either +dnl version 2, or (at your option) any later version. +dnl +dnl They are distributed in the hope that they will be useful, but WITHOUT +dnl ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +dnl FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +dnl more details. (You should have received a copy of the GNU General Public +dnl License along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place -- Suite 330, Boston, MA 02111-1307, +dnl USA.) +dnl +dnl As a special exception, the Free Software Foundation gives unlimited +dnl permission to copy, distribute and modify the configure scripts that are +dnl the output of Autoconf. You need not follow the terms of the GNU General +dnl Public License when using or distributing such scripts, even though +dnl portions of the text of Autoconf appear in them. The GNU General Public +dnl License (GPL) does govern all other use of the material that constitutes +dnl the Autoconf program. +dnl +dnl Certain portions of the Autoconf source text are designed to be copied +dnl (in certain cases, depending on the input) into the output of Autoconf. +dnl We call these the "data" portions. The rest of the Autoconf source text +dnl consists of comments plus executable code that decides which of the data +dnl portions to output in any given case. We call these comments and +dnl executable code the "non-data" portions. Autoconf never copies any of the +dnl non-data portions into its output. +dnl +dnl This special exception to the GPL applies to versions of Autoconf +dnl released by the Free Software Foundation. When you make and distribute a +dnl modified version of Autoconf, you may extend this special exception to +dnl the GPL to apply to your modified version as well, *unless* your modified +dnl version has the potential to copy into its output some of the text that +dnl was the non-data portion of the version that you started with. (In other +dnl words, unless your change moves or copies text from the non-data portions +dnl to the data portions.) If your modification has such potential, you must +dnl delete any notice of this special exception to the GPL from your modified +dnl version + +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CXXFLAGS="$CXXFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: threads are created detached by default + # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + AC_MSG_CHECKING([for joinable pthread attribute]) + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_JOINABLE;], + ok=PTHREAD_CREATE_JOINABLE, ok=unknown) + if test x"$ok" = xunknown; then + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_UNDETACHED;], + ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) + fi + if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then + AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, + [Define to the necessary symbol if this constant + uses a non-standard name on your system.]) + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" = xunknown; then + AC_MSG_WARN([we do not know how to create joinable pthreads]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + alpha*-osf*) flag="-D_REENTRANT";; + *solaris*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + # Detect POSIX sigwait() + AC_MSG_CHECKING([for POSIX sigwait]) + AC_TRY_LINK([#include + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=yes, ok=unknown) + if test x"$ok" = xunknown; then + save_CXXFLAGS2="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -D_POSIX_PTHREAD_SEMANTICS" + AC_TRY_LINK([#include + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=-D_POSIX_PTHREAD_SEMANTICS, ok=no) + CXXFLAGS="$save_CXXFLAGS2" + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" != xno; then + AC_DEFINE(HAVE_POSIX_SIGWAIT,1,[Define if you have a POSIX \`sigwait\' function.]) + if test x"$ok" != xyes; then + PTHREAD_CFLAGS="$ok $PTHREAD_CFLAGS" + fi + fi + + # Detect pthread signal functions + AC_MSG_CHECKING([for pthread signal functions]) + AC_TRY_LINK([#include + #include ], + [pthread_kill(pthread_self(), SIGTERM);], + ok=yes, ok=no) + AC_MSG_RESULT(${ok}) + if test x"$ok" = xyes; then + AC_DEFINE(HAVE_PTHREAD_SIGNAL,1,[Define if you have \`pthread_sigmask\' and \`pthread_kill\' functions.]) + fi + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +])dnl ACX_PTHREAD + +# Do all the work for Automake. This macro actually does too much -- +# some checks are only needed if your package does certain things. +# But this isn't really a big deal. + +# serial 5 + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# We require 2.13 because we rely on SHELL being computed by configure. +AC_PREREQ([2.13]) + +# AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED) +# ----------------------------------------------------------- +# If MACRO-NAME is provided do IF-PROVIDED, else IF-NOT-PROVIDED. +# The purpose of this macro is to provide the user with a means to +# check macros which are provided without letting her know how the +# information is coded. +# If this macro is not defined by Autoconf, define it here. +ifdef([AC_PROVIDE_IFELSE], + [], + [define([AC_PROVIDE_IFELSE], + [ifdef([AC_PROVIDE_$1], + [$2], [$3])])]) + + +# AM_INIT_AUTOMAKE(PACKAGE,VERSION, [NO-DEFINE]) +# ---------------------------------------------- +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_REQUIRE([AC_PROG_INSTALL])dnl +# test to see if srcdir already configured +if test "`CDPATH=:; cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run \"make distclean\" there first]) +fi + +# Define the identity of the package. +PACKAGE=$1 +AC_SUBST(PACKAGE)dnl +VERSION=$2 +AC_SUBST(VERSION)dnl +ifelse([$3],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) +AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])]) + +# Autoconf 2.50 wants to disallow AM_ names. We explicitly allow +# the ones we care about. +ifdef([m4_pattern_allow], + [m4_pattern_allow([^AM_[A-Z]+FLAGS])])dnl + +# Autoconf 2.50 always computes EXEEXT. However we need to be +# compatible with 2.13, for now. So we always define EXEEXT, but we +# don't compute it. +AC_SUBST(EXEEXT) +# Similar for OBJEXT -- only we only use OBJEXT if the user actually +# requests that it be used. This is a bit dumb. +: ${OBJEXT=o} +AC_SUBST(OBJEXT) + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_MISSING_PROG(AMTAR, tar) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl +AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_PROVIDE_IFELSE([AC_PROG_][CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_][CC], + defn([AC_PROG_][CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_][CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_][CXX], + defn([AC_PROG_][CXX])[_AM_DEPENDENCIES(CXX)])])dnl +]) + +# +# Check to make sure that the build environment is sane. +# + +# serial 3 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + + +# serial 2 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + am_backtick='`' + AC_MSG_WARN([${am_backtick}missing' script is too old or missing]) +fi +]) + +# AM_AUX_DIR_EXPAND + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], [ +# expand $ac_aux_dir to an absolute path +am_aux_dir=`CDPATH=:; cd $ac_aux_dir && pwd` +]) + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"$am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# serial 4 -*- Autoconf -*- + + + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + + +# _AM_DEPENDENCIES(NAME) +# --------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX" or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'] + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + for depmode in $am_compiler_list; do + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + echo '#include "conftest.h"' > conftest.c + echo 'int i;' > conftest.h + echo "${am__include} ${am__quote}conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=conftest.c object=conftest.o \ + depfile=conftest.Po tmpdepfile=conftest.TPo \ + $SHELL ./depcomp $depcc -c conftest.c -o conftest.o >/dev/null 2>&1 && + grep conftest.h conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +$1DEPMODE="depmode=$am_cv_$1_dependencies_compiler_type" +AC_SUBST([$1DEPMODE]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[rm -f .deps 2>/dev/null +mkdir .deps 2>/dev/null +if test -d .deps; then + DEPDIR=.deps +else + # MS-DOS does not allow filenames that begin with a dot. + DEPDIR=_deps +fi +rmdir .deps 2>/dev/null +AC_SUBST(DEPDIR) +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking Speeds up one-time builds + --enable-dependency-tracking Do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +pushdef([subst], defn([AC_SUBST])) +subst(AMDEPBACKSLASH) +popdef([subst]) +]) + +# Generate code to set up dependency tracking. +# This macro should only be invoked once -- use via AC_REQUIRE. +# Usage: +# AM_OUTPUT_DEPENDENCY_COMMANDS + +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],[ +AC_OUTPUT_COMMANDS([ +test x"$AMDEP_TRUE" != x"" || +for mf in $CONFIG_FILES; do + case "$mf" in + Makefile) dirpart=.;; + */Makefile) dirpart=`echo "$mf" | sed -e 's|/[^/]*$||'`;; + *) continue;; + esac + grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue + # Extract the definition of DEP_FILES from the Makefile without + # running `make'. + DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"` + test -z "$DEPDIR" && continue + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n -e '/^U = / s///p' < "$mf"` + test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" + # We invoke sed twice because it is the simplest approach to + # changing $(DEPDIR) to its actual value in the expansion. + for file in `sed -n -e ' + /^DEP_FILES = .*\\\\$/ { + s/^DEP_FILES = // + :loop + s/\\\\$// + p + n + /\\\\$/ b loop + p + } + /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`echo "$file" | sed -e 's|/[^/]*$||'` + $ac_aux_dir/mkinstalldirs "$dirpart/$fdir" > /dev/null 2>&1 + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +], [AMDEP_TRUE="$AMDEP_TRUE" +ac_aux_dir="$ac_aux_dir"])]) + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +doit: + @echo done +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include='#' +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | fgrep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote='"' + _am_result=BSD + fi +fi +AC_SUBST(am__include) +AC_SUBST(am__quote) +AC_MSG_RESULT($_am_result) +rm -f confinc confmf +]) + +# serial 3 + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +# +# FIXME: Once using 2.50, use this: +# m4_match([$1], [^TRUE\|FALSE$], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_DEFUN([AM_CONDITIONAL], +[ifelse([$1], [TRUE], + [errprint(__file__:__line__: [$0: invalid condition: $1 +])dnl +m4exit(1)])dnl +ifelse([$1], [FALSE], + [errprint(__file__:__line__: [$0: invalid condition: $1 +])dnl +m4exit(1)])dnl +AC_SUBST([$1_TRUE]) +AC_SUBST([$1_FALSE]) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi]) + +# Like AC_CONFIG_HEADER, but automatically create stamp file. + +# serial 3 + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. We must strip everything past the first ":", +# and everything past the last "/". + +AC_PREREQ([2.12]) + +AC_DEFUN([AM_CONFIG_HEADER], +[ifdef([AC_FOREACH],dnl + [dnl init our file count if it isn't already + m4_ifndef([_AM_Config_Header_Index], m4_define([_AM_Config_Header_Index], [0])) + dnl prepare to store our destination file list for use in config.status + AC_FOREACH([_AM_File], [$1], + [m4_pushdef([_AM_Dest], m4_patsubst(_AM_File, [:.*])) + m4_define([_AM_Config_Header_Index], m4_incr(_AM_Config_Header_Index)) + dnl and add it to the list of files AC keeps track of, along + dnl with our hook + AC_CONFIG_HEADERS(_AM_File, +dnl COMMANDS, [, INIT-CMDS] +[# update the timestamp +echo timestamp >"AS_ESCAPE(_AM_DIRNAME(]_AM_Dest[))/stamp-h]_AM_Config_Header_Index[" +][$2]m4_ifval([$3], [, [$3]]))dnl AC_CONFIG_HEADERS + m4_popdef([_AM_Dest])])],dnl +[AC_CONFIG_HEADER([$1]) + AC_OUTPUT_COMMANDS( + ifelse(patsubst([$1], [[^ ]], []), + [], + [test -z "$CONFIG_HEADERS" || echo timestamp >dnl + patsubst([$1], [^\([^:]*/\)?.*], [\1])stamp-h]),dnl +[am_indx=1 +for am_file in $1; do + case " \$CONFIG_HEADERS " in + *" \$am_file "*) + am_dir=\`echo \$am_file |sed 's%:.*%%;s%[^/]*\$%%'\` + if test -n "\$am_dir"; then + am_tmpdir=\`echo \$am_dir |sed 's%^\(/*\).*\$%\1%'\` + for am_subdir in \`echo \$am_dir |sed 's%/% %'\`; do + am_tmpdir=\$am_tmpdir\$am_subdir/ + if test ! -d \$am_tmpdir; then + mkdir \$am_tmpdir + fi + done + fi + echo timestamp > "\$am_dir"stamp-h\$am_indx + ;; + esac + am_indx=\`expr \$am_indx + 1\` +done]) +])]) # AM_CONFIG_HEADER + +# _AM_DIRNAME(PATH) +# ----------------- +# Like AS_DIRNAME, only do it during macro expansion +AC_DEFUN([_AM_DIRNAME], + [m4_if(m4_regexp([$1], [^.*[^/]//*[^/][^/]*/*$]), -1, + m4_if(m4_regexp([$1], [^//\([^/]\|$\)]), -1, + m4_if(m4_regexp([$1], [^/.*]), -1, + [.], + m4_patsubst([$1], [^\(/\).*], [\1])), + m4_patsubst([$1], [^\(//\)\([^/].*\|$\)], [\1])), + m4_patsubst([$1], [^\(.*[^/]\)//*[^/][^/]*/*$], [\1]))[]dnl +]) # _AM_DIRNAME + diff --git a/all.dsp b/all.dsp new file mode 100644 index 0000000..83d50f6 --- /dev/null +++ b/all.dsp @@ -0,0 +1,63 @@ +# Microsoft Developer Studio Project File - Name="all" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=all - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "all.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "all.mak" CFG="all - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "all - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "all - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "all - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "all - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "all - Win32 Release" +# Name "all - Win32 Debug" +# End Target +# End Project diff --git a/cmd/Makefile.am b/cmd/Makefile.am new file mode 100644 index 0000000..3d02899 --- /dev/null +++ b/cmd/Makefile.am @@ -0,0 +1,30 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + launcher \ + synergyc \ + synergys \ + $(NULL) + +EXTRA_DIST = \ + exec.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/cmd/Makefile.in b/cmd/Makefile.in new file mode 100644 index 0000000..7b24794 --- /dev/null +++ b/cmd/Makefile.in @@ -0,0 +1,351 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + launcher \ + synergyc \ + synergys \ + $(NULL) + + +EXTRA_DIST = \ + exec.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +subdir = cmd +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu cmd/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive distclean distclean-generic \ + distclean-recursive distclean-tags distdir dvi dvi-am \ + dvi-recursive info info-am info-recursive install install-am \ + install-data install-data-am install-data-recursive \ + install-exec install-exec-am install-exec-recursive \ + install-info install-info-am install-info-recursive install-man \ + install-recursive install-strip installcheck installcheck-am \ + installdirs installdirs-am installdirs-recursive \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive tags tags-recursive uninstall \ + uninstall-am uninstall-info-am uninstall-info-recursive \ + uninstall-recursive + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/cmd/exec.dsp b/cmd/exec.dsp new file mode 100644 index 0000000..10237f6 --- /dev/null +++ b/cmd/exec.dsp @@ -0,0 +1,63 @@ +# Microsoft Developer Studio Project File - Name="exec" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=exec - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "exec.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "exec.mak" CFG="exec - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "exec - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "exec - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "exec - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "exec - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "exec - Win32 Release" +# Name "exec - Win32 Debug" +# End Target +# End Project diff --git a/cmd/launcher/CAdvancedOptions.cpp b/cmd/launcher/CAdvancedOptions.cpp new file mode 100644 index 0000000..bd4009b --- /dev/null +++ b/cmd/launcher/CAdvancedOptions.cpp @@ -0,0 +1,227 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "ProtocolTypes.h" +#include "CStringUtil.h" +#include "CArch.h" +#include "CArchMiscWindows.h" +#include "CAdvancedOptions.h" +#include "LaunchUtil.h" +#include "resource.h" + +// +// CAdvancedOptions +// + +CAdvancedOptions* CAdvancedOptions::s_singleton = NULL; + +CAdvancedOptions::CAdvancedOptions(HWND parent, CConfig* config) : + m_parent(parent), + m_config(config), + m_isClient(false), + m_screenName(ARCH->getHostName()), + m_port(kDefaultPort) +{ + assert(s_singleton == NULL); + s_singleton = this; + init(); +} + +CAdvancedOptions::~CAdvancedOptions() +{ + s_singleton = NULL; +} + +void +CAdvancedOptions::doModal(bool isClient) +{ + // save state + m_isClient = isClient; + + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADVANCED_OPTIONS), + m_parent, dlgProc, (LPARAM)this); +} + +CString +CAdvancedOptions::getScreenName() const +{ + return m_screenName; +} + +int +CAdvancedOptions::getPort() const +{ + return m_port; +} + +CString +CAdvancedOptions::getCommandLine(bool isClient, const CString& serverName) const +{ + CString cmdLine; + + // screen name + if (!m_screenName.empty()) { + cmdLine += " --name "; + cmdLine += m_screenName; + } + + // port + char portString[20]; + sprintf(portString, "%d", m_port); + if (isClient) { + cmdLine += " "; + cmdLine += serverName; + cmdLine += ":"; + cmdLine += portString; + } + else { + cmdLine += " --address :"; + cmdLine += portString; + } + + return cmdLine; +} + +void +CAdvancedOptions::init() +{ + // get values from registry + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + DWORD newPort = CArchMiscWindows::readValueInt(key, "port"); + CString newName = CArchMiscWindows::readValueString(key, "name"); + if (newPort != 0) { + m_port = static_cast(newPort); + } + if (!newName.empty()) { + m_screenName = newName; + } + CArchMiscWindows::closeKey(key); + } +} + +void +CAdvancedOptions::doInit(HWND hwnd) +{ + // set values in GUI + HWND child; + char buffer[20]; + sprintf(buffer, "%d", m_port); + child = getItem(hwnd, IDC_ADVANCED_PORT_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); + + child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)m_screenName.c_str()); +} + +bool +CAdvancedOptions::save(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT); + CString name = getWindowText(child); + if (!m_config->isValidScreenName(name)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + name.c_str())); + SetFocus(child); + return false; + } + if (!m_isClient && !m_config->isScreen(name)) { + showError(hwnd, CStringUtil::format( + getString(IDS_UNKNOWN_SCREEN_NAME).c_str(), + name.c_str())); + SetFocus(child); + return false; + } + + // get and verify port + child = getItem(hwnd, IDC_ADVANCED_PORT_EDIT); + CString portString = getWindowText(child); + int port = atoi(portString.c_str()); + if (port < 1 || port > 65535) { + CString defaultPortString = CStringUtil::print("%d", kDefaultPort); + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_PORT).c_str(), + portString.c_str(), + defaultPortString.c_str())); + SetFocus(child); + return false; + } + + // save state + m_screenName = name; + m_port = port; + + // save values to registry + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + CArchMiscWindows::setValue(key, "port", m_port); + CArchMiscWindows::setValue(key, "name", m_screenName); + CArchMiscWindows::closeKey(key); + } + + return true; +} + +void +CAdvancedOptions::setDefaults(HWND hwnd) +{ + // restore defaults + m_screenName = ARCH->getHostName(); + m_port = kDefaultPort; + + // update GUI + doInit(hwnd); +} + +BOOL +CAdvancedOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + doInit(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + if (save(hwnd)) { + EndDialog(hwnd, 0); + } + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + + case IDC_ADVANCED_DEFAULTS: + setDefaults(hwnd); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CAdvancedOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} diff --git a/cmd/launcher/CAdvancedOptions.h b/cmd/launcher/CAdvancedOptions.h new file mode 100644 index 0000000..c86bf38 --- /dev/null +++ b/cmd/launcher/CAdvancedOptions.h @@ -0,0 +1,76 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CADVANCEDOPTIONS_H +#define CADVANCEDOPTIONS_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Advanced options dialog for Microsoft Windows launcher +class CAdvancedOptions { +public: + CAdvancedOptions(HWND parent, CConfig*); + ~CAdvancedOptions(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(bool isClient); + + //@} + //! @name accessors + //@{ + + //! Get the screen name + CString getScreenName() const; + + //! Get the port + int getPort() const; + + //! Convert options to command line string + CString getCommandLine(bool isClient, + const CString& serverName) const; + + //@} + +private: + void init(); + void doInit(HWND hwnd); + bool save(HWND hwnd); + void setDefaults(HWND hwnd); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CAdvancedOptions* s_singleton; + + HWND m_parent; + CConfig* m_config; + bool m_isClient; + CString m_screenName; + int m_port; +}; + +#endif diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp new file mode 100644 index 0000000..1391063 --- /dev/null +++ b/cmd/launcher/CAutoStart.cpp @@ -0,0 +1,288 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CLog.h" +#include "ILogOutputter.h" +#include "CArch.h" +#include "CStringUtil.h" +#include "XArch.h" +#include "CAutoStart.h" +#include "LaunchUtil.h" +#include "resource.h" + +#define CLIENT_DAEMON_NAME "Synergy Client" +#define SERVER_DAEMON_NAME "Synergy Server" +#define CLIENT_DAEMON_INFO "Shares this system's mouse and keyboard with others." +#define SERVER_DAEMON_INFO "Shares this system's mouse and keyboard with others." + +// +// CAutoStartOutputter +// +// This class detects a message above a certain level and saves it +// + +class CAutoStartOutputter : public ILogOutputter { +public: + CAutoStartOutputter(CString* msg) : m_msg(msg) { } + virtual ~CAutoStartOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } + +private: + CString* m_msg; +}; + +bool +CAutoStartOutputter::write(ELevel level, const char* message) +{ + if (level <= CLog::kERROR) { + *m_msg = message; + } + return false; +} + + +// +// CAutoStart +// + +CAutoStart* CAutoStart::s_singleton = NULL; + +CAutoStart::CAutoStart(HWND parent, CConfig* config, const CString& cmdLine) : + m_parent(parent), + m_config(config), + m_isServer(config != NULL), + m_cmdLine(cmdLine), + m_name((config != NULL) ? SERVER_DAEMON_NAME : CLIENT_DAEMON_NAME), + m_userConfigSaved(false) + +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CAutoStart::~CAutoStart() +{ + s_singleton = NULL; +} + +void +CAutoStart::doModal() +{ + // install our log outputter + CLOG->insert(new CAutoStartOutputter(&m_errorMessage)); + + // reset saved flag + m_userConfigSaved = false; + + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_AUTOSTART), + m_parent, dlgProc, (LPARAM)this); + + // remove log outputter + CLOG->pop_front(); +} + +bool +CAutoStart::wasUserConfigSaved() const +{ + return m_userConfigSaved; +} + +void +CAutoStart::update() +{ + // get installation state + const bool installedSystem = ARCH->isDaemonInstalled( + m_name.c_str(), true); + const bool installedUser = ARCH->isDaemonInstalled( + m_name.c_str(), false); + + // get user's permissions + const bool canInstallSystem = ARCH->canInstallDaemon( + m_name.c_str(), true); + const bool canInstallUser = ARCH->canInstallDaemon( + m_name.c_str(), false); + + // update messages + CString msg, label; + if (canInstallSystem) { + msg = getString(IDS_AUTOSTART_PERMISSION_SYSTEM); + } + else if (canInstallUser) { + msg = getString(IDS_AUTOSTART_PERMISSION_USER); + } + else { + msg = getString(IDS_AUTOSTART_PERMISSION_NONE); + } + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_PERMISSION_MSG), msg); + if (installedSystem) { + msg = getString(IDS_AUTOSTART_INSTALLED_SYSTEM); + label = getString(IDS_UNINSTALL_LABEL); + } + else if (installedUser) { + msg = getString(IDS_AUTOSTART_INSTALLED_USER); + label = getString(IDS_UNINSTALL_LABEL); + } + else { + msg = getString(IDS_AUTOSTART_INSTALLED_NONE); + label = getString(IDS_INSTALL_LABEL); + } + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALLED_MSG), msg); + + // update buttons + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM), label); + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER), label); + if (installedSystem) { + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, canInstallSystem); + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, false); + m_install = false; + } + else if (installedUser) { + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, false); + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, canInstallUser); + m_install = false; + } + else { + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, canInstallSystem); + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, canInstallUser); + m_install = true; + } +} + +bool +CAutoStart::onInstall(bool allUsers) +{ + if (!m_install) { + return onUninstall(allUsers); + } + + // try saving configuration. if we can't then don't try + // installing the daemon. + if (m_config != NULL) { + if (!saveConfig(*m_config, allUsers)) { + showError(m_hwnd, CStringUtil::format( + getString(IDS_SAVE_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + return false; + } + + // note if we've saved the user's configuration + if (!allUsers) { + m_userConfigSaved = true; + } + } + + // get the app path + CString appPath = getAppPath(m_isServer ? SERVER_APP : CLIENT_APP); + + // clear error message + m_errorMessage = ""; + + // install + try { + ARCH->installDaemon(m_name.c_str(), + m_isServer ? SERVER_DAEMON_INFO : CLIENT_DAEMON_INFO, + appPath.c_str(), m_cmdLine.c_str(), allUsers); + askOkay(m_hwnd, getString(IDS_INSTALL_TITLE), + getString(allUsers ? + IDS_INSTALLED_SYSTEM : + IDS_INSTALLED_USER)); + return true; + } + catch (XArchDaemon& e) { + if (m_errorMessage.empty()) { + m_errorMessage = CStringUtil::format( + getString(IDS_INSTALL_GENERIC_ERROR).c_str(), + e.what().c_str()); + } + showError(m_hwnd, m_errorMessage); + return false; + } +} + +bool +CAutoStart::onUninstall(bool allUsers) +{ + // clear error message + m_errorMessage = ""; + + // uninstall + try { + ARCH->uninstallDaemon(m_name.c_str(), allUsers); + askOkay(m_hwnd, getString(IDS_UNINSTALL_TITLE), + getString(allUsers ? + IDS_UNINSTALLED_SYSTEM : + IDS_UNINSTALLED_USER)); + return true; + } + catch (XArchDaemon& e) { + if (m_errorMessage.empty()) { + m_errorMessage = CStringUtil::format( + getString(IDS_UNINSTALL_GENERIC_ERROR).c_str(), + e.what().c_str()); + } + showError(m_hwnd, m_errorMessage); + return false; + } +} + +BOOL +CAutoStart::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + // save our hwnd + m_hwnd = hwnd; + + // update the controls + update(); + + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_AUTOSTART_INSTALL_SYSTEM: + onInstall(true); + update(); + return TRUE; + + case IDC_AUTOSTART_INSTALL_USER: + onInstall(false); + update(); + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + m_hwnd = NULL; + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CAutoStart::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} diff --git a/cmd/launcher/CAutoStart.h b/cmd/launcher/CAutoStart.h new file mode 100644 index 0000000..e931530 --- /dev/null +++ b/cmd/launcher/CAutoStart.h @@ -0,0 +1,78 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CAUTOSTART_H +#define CAUTOSTART_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Auto start dialog for Microsoft Windows launcher +class CAutoStart { +public: + // if config == NULL then it's assumed we're installing/uninstalling + // the client, otherwise the server. + CAutoStart(HWND parent, CConfig* config, const CString& cmdLine); + ~CAutoStart(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + //! Test if user configuration was saved + /*! + Returns true if the user's configuration (as opposed to the system-wide + configuration) was saved successfully while in doModal(). + */ + bool wasUserConfigSaved() const; + + //@} + +private: + void update(); + bool onInstall(bool allUsers); + bool onUninstall(bool allUsers); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CAutoStart* s_singleton; + + HWND m_parent; + CConfig* m_config; + bool m_isServer; + CString m_cmdLine; + CString m_name; + HWND m_hwnd; + bool m_install; + CString m_errorMessage; + bool m_userConfigSaved; +}; + +#endif diff --git a/cmd/launcher/CGlobalOptions.cpp b/cmd/launcher/CGlobalOptions.cpp new file mode 100644 index 0000000..8a44643 --- /dev/null +++ b/cmd/launcher/CGlobalOptions.cpp @@ -0,0 +1,259 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "ProtocolTypes.h" +#include "CStringUtil.h" +#include "CArch.h" +#include "CGlobalOptions.h" +#include "LaunchUtil.h" +#include "resource.h" + +static const int s_defaultDelay = 250; +static const int s_defaultHeartbeat = 5000; + +// +// CGlobalOptions +// + +CGlobalOptions* CGlobalOptions::s_singleton = NULL; + +CGlobalOptions::CGlobalOptions(HWND parent, CConfig* config) : + m_parent(parent), + m_config(config), + m_delayTime(s_defaultDelay), + m_twoTapTime(s_defaultDelay), + m_heartbeatTime(s_defaultHeartbeat) +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CGlobalOptions::~CGlobalOptions() +{ + s_singleton = NULL; +} + +void +CGlobalOptions::doModal() +{ + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_GLOBAL_OPTIONS), + m_parent, dlgProc, (LPARAM)this); +} + +void +CGlobalOptions::init(HWND hwnd) +{ + HWND child; + char buffer[30]; + + // reset options + sprintf(buffer, "%d", m_delayTime); + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + setItemChecked(child, false); + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + setWindowText(child, buffer); + sprintf(buffer, "%d", m_twoTapTime); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + setItemChecked(child, false); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + setWindowText(child, buffer); + sprintf(buffer, "%d", m_heartbeatTime); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + setItemChecked(child, false); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + setWindowText(child, buffer); + child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC); + setItemChecked(child, true); + + // get the global options + const CConfig::CScreenOptions* options = m_config->getOptions(""); + if (options != NULL) { + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + const OptionID id = index->first; + const OptionValue value = index->second; + if (id == kOptionScreenSwitchDelay) { + if (value > 0) { + sprintf(buffer, "%d", value); + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + setItemChecked(child, true); + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + setWindowText(child, buffer); + } + } + else if (id == kOptionScreenSwitchTwoTap) { + if (value > 0) { + sprintf(buffer, "%d", value); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + setItemChecked(child, true); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + setWindowText(child, buffer); + } + } + else if (id == kOptionHeartbeat) { + if (value > 0) { + sprintf(buffer, "%d", value); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + setItemChecked(child, true); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + setWindowText(child, buffer); + } + } + else if (id == kOptionScreenSaverSync) { + child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC); + setItemChecked(child, (value != 0)); + } + } + } +} + +bool +CGlobalOptions::save(HWND hwnd) +{ + HWND child; + int newDelayTime = 0; + int newTwoTapTime = 0; + int newHeartbeatTime = 0; + + // get requested options + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + if (isItemChecked(child)) { + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + newDelayTime = getTime(hwnd, child, true); + if (newDelayTime == 0) { + return false; + } + } + else { + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + newDelayTime = getTime(hwnd, child, false); + if (newDelayTime == 0) { + newDelayTime = s_defaultDelay; + } + } + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + if (isItemChecked(child)) { + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + newTwoTapTime = getTime(hwnd, child, true); + if (newTwoTapTime == 0) { + return false; + } + } + else { + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + newTwoTapTime = getTime(hwnd, child, false); + if (newTwoTapTime == 0) { + newTwoTapTime = s_defaultDelay; + } + } + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + if (isItemChecked(child)) { + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + newHeartbeatTime = getTime(hwnd, child, true); + if (newHeartbeatTime == 0) { + return false; + } + } + else { + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + newHeartbeatTime = getTime(hwnd, child, false); + if (newHeartbeatTime == 0) { + newHeartbeatTime = s_defaultHeartbeat; + } + } + + // remove existing config options + m_config->removeOption("", kOptionScreenSwitchDelay); + m_config->removeOption("", kOptionScreenSwitchTwoTap); + m_config->removeOption("", kOptionHeartbeat); + m_config->removeOption("", kOptionScreenSaverSync); + + // add requested options + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + if (isItemChecked(child)) { + m_config->addOption("", kOptionScreenSwitchDelay, newDelayTime); + } + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + if (isItemChecked(child)) { + m_config->addOption("", kOptionScreenSwitchTwoTap, newTwoTapTime); + } + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + if (isItemChecked(child)) { + m_config->addOption("", kOptionHeartbeat, newHeartbeatTime); + } + child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC); + if (!isItemChecked(child)) { + m_config->addOption("", kOptionScreenSaverSync, 0); + } + + // save last values + m_delayTime = newDelayTime; + m_twoTapTime = newTwoTapTime; + m_heartbeatTime = newHeartbeatTime; + return true; +} + +int +CGlobalOptions::getTime(HWND hwnd, HWND child, bool reportError) +{ + CString valueString = getWindowText(child); + int value = atoi(valueString.c_str()); + if (value < 1) { + if (reportError) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_TIME).c_str(), + valueString.c_str())); + SetFocus(child); + } + return 0; + } + return value; +} + +BOOL +CGlobalOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + init(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + if (save(hwnd)) { + EndDialog(hwnd, 0); + } + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CGlobalOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} diff --git a/cmd/launcher/CGlobalOptions.h b/cmd/launcher/CGlobalOptions.h new file mode 100644 index 0000000..f04f1ba --- /dev/null +++ b/cmd/launcher/CGlobalOptions.h @@ -0,0 +1,67 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CGLOBALOPTIONS_H +#define CGLOBALOPTIONS_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Global options dialog for Microsoft Windows launcher +class CGlobalOptions { +public: + CGlobalOptions(HWND parent, CConfig*); + ~CGlobalOptions(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + + //@} + +private: + void init(HWND hwnd); + bool save(HWND hwnd); + + int getTime(HWND hwnd, HWND child, bool reportError); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CGlobalOptions* s_singleton; + + HWND m_parent; + CConfig* m_config; + int m_delayTime; + int m_twoTapTime; + int m_heartbeatTime; +}; + +#endif diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp new file mode 100644 index 0000000..46c8ef9 --- /dev/null +++ b/cmd/launcher/LaunchUtil.cpp @@ -0,0 +1,228 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "LaunchUtil.h" +#include "CArch.h" +#include "resource.h" +#include "stdfstream.h" + +#define CONFIG_NAME "synergy.sgc" + +CString +getString(DWORD id) +{ + char buffer[1024]; + buffer[0] = '\0'; + LoadString(s_instance, id, buffer, sizeof(buffer) / sizeof(buffer[0])); + return buffer; +} + +CString +getErrorString(DWORD error) +{ + char* buffer; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&buffer, + 0, + NULL) == 0) { + return getString(IDS_ERROR); + } + else { + CString result(buffer); + LocalFree(buffer); + return result; + } +} + +void +showError(HWND hwnd, const CString& msg) +{ + CString title = getString(IDS_ERROR); + MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); +} + +void +askOkay(HWND hwnd, const CString& title, const CString& msg) +{ + MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); +} + +bool +askVerify(HWND hwnd, const CString& msg) +{ + CString title = getString(IDS_VERIFY); + int result = MessageBox(hwnd, msg.c_str(), + title.c_str(), MB_OKCANCEL | MB_APPLMODAL); + return (result == IDOK); +} + +void +setWindowText(HWND hwnd, const CString& msg) +{ + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)msg.c_str()); +} + +CString +getWindowText(HWND hwnd) +{ + LRESULT size = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0); + char* buffer = new char[size + 1]; + SendMessage(hwnd, WM_GETTEXT, size + 1, (LPARAM)buffer); + buffer[size] = '\0'; + CString result(buffer); + delete[] buffer; + return result; +} + +HWND +getItem(HWND hwnd, int id) +{ + return GetDlgItem(hwnd, id); +} + +void +enableItem(HWND hwnd, int id, bool enabled) +{ + EnableWindow(GetDlgItem(hwnd, id), enabled); +} + +void +setItemChecked(HWND hwnd, bool checked) +{ + SendMessage(hwnd, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0); +} + +bool +isItemChecked(HWND hwnd) +{ + return (SendMessage(hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED); +} + +CString +getAppPath(const CString& appName) +{ + // prepare path to app + char myPathname[MAX_PATH]; + GetModuleFileName(s_instance, myPathname, MAX_PATH); + const char* myBasename = ARCH->getBasename(myPathname); + CString appPath = CString(myPathname, myBasename - myPathname); + appPath += appName; + return appPath; +} + +static +bool +loadConfig(const CString& pathname, CConfig& config) +{ + try { + std::ifstream stream(pathname.c_str()); + if (stream) { + stream >> config; + return true; + } + } + catch (...) { + // ignore + } + return false; +} + +bool +loadConfig(CConfig& config) +{ + // load configuration + bool configLoaded = false; + CString path = ARCH->getUserDirectory(); + if (!path.empty()) { + // try loading the user's configuration + path = ARCH->concatPath(path, CONFIG_NAME); + if (loadConfig(path, config)) { + configLoaded = true; + } + else { + // try the system-wide config file + path = ARCH->getSystemDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, CONFIG_NAME); + if (loadConfig(path, config)) { + configLoaded = true; + } + } + } + } + return configLoaded; +} + +static +bool +saveConfig(const CString& pathname, const CConfig& config) +{ + try { + std::ofstream stream(pathname.c_str()); + if (stream) { + stream << config; + return !!stream; + } + } + catch (...) { + // ignore + } + return false; +} + +bool +saveConfig(const CConfig& config, bool sysOnly) +{ + // try saving the user's configuration + if (!sysOnly) { + CString path = ARCH->getUserDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, CONFIG_NAME); + if (saveConfig(path, config)) { + return true; + } + } + } + + // try the system-wide config file + else { + CString path = ARCH->getSystemDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, CONFIG_NAME); + if (saveConfig(path, config)) { + return true; + } + } + } + + return false; +} + +const TCHAR* const* +getSettingsPath() +{ + static const TCHAR* s_keyNames[] = { + TEXT("Software"), + TEXT("Synergy"), + TEXT("Synergy"), + NULL + }; + return s_keyNames; +} diff --git a/cmd/launcher/LaunchUtil.h b/cmd/launcher/LaunchUtil.h new file mode 100644 index 0000000..4888926 --- /dev/null +++ b/cmd/launcher/LaunchUtil.h @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef LAUNCHUTIL_H +#define LAUNCHUTIL_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +#define CLIENT_APP "synergyc.exe" +#define SERVER_APP "synergys.exe" + +class CConfig; + +// client must define this and set it before calling any function here +extern HINSTANCE s_instance; + +CString getString(DWORD id); +CString getErrorString(DWORD error); + +void showError(HWND hwnd, const CString& msg); +void askOkay(HWND hwnd, const CString& title, + const CString& msg); +bool askVerify(HWND hwnd, const CString& msg); + +void setWindowText(HWND hwnd, const CString& msg); +CString getWindowText(HWND hwnd); + +HWND getItem(HWND hwnd, int id); +void enableItem(HWND hwnd, int id, bool enabled); + +void setItemChecked(HWND, bool); +bool isItemChecked(HWND); + +CString getAppPath(const CString& appName); + +bool loadConfig(CConfig& config); +bool saveConfig(const CConfig& config, bool sysOnly); + +const TCHAR* const* getSettingsPath(); + +#endif diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am new file mode 100644 index 0000000..782d771 --- /dev/null +++ b/cmd/launcher/Makefile.am @@ -0,0 +1,36 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CAdvancedOptions.cpp \ + CAdvancedOptions.h \ + CAutoStart.cpp \ + CAutoStart.h \ + CGlobalOptions.cpp \ + CGlobalOptions.h \ + LaunchUtil.cpp \ + LaunchUtil.h \ + launcher.cpp \ + launcher.dsp \ + launcher.rc \ + resource.h \ + synergy.ico \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/cmd/launcher/Makefile.in b/cmd/launcher/Makefile.in new file mode 100644 index 0000000..f9e5317 --- /dev/null +++ b/cmd/launcher/Makefile.in @@ -0,0 +1,239 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CAdvancedOptions.cpp \ + CAdvancedOptions.h \ + CAutoStart.cpp \ + CAutoStart.h \ + CGlobalOptions.cpp \ + CGlobalOptions.h \ + LaunchUtil.cpp \ + LaunchUtil.h \ + launcher.cpp \ + launcher.dsp \ + launcher.rc \ + resource.h \ + synergy.ico \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +subdir = cmd/launcher +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = +DIST_COMMON = Makefile.am Makefile.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu cmd/launcher/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: +tags: TAGS +TAGS: + + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp new file mode 100644 index 0000000..0c4b459 --- /dev/null +++ b/cmd/launcher/launcher.cpp @@ -0,0 +1,1211 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "KeyTypes.h" +#include "OptionTypes.h" +#include "ProtocolTypes.h" +#include "CLog.h" +#include "CStringUtil.h" +#include "CArch.h" +#include "CArchMiscWindows.h" +#include "Version.h" +#include "stdvector.h" +#include "resource.h" + +// these must come after the above because it includes windows.h +#include "LaunchUtil.h" +#include "CAutoStart.h" +#include "CGlobalOptions.h" +#include "CAdvancedOptions.h" + +#define CONFIG_NAME "synergy.sgc" +#define CLIENT_APP "synergyc.exe" +#define SERVER_APP "synergys.exe" + +typedef std::vector CStringList; + +class CScreenInfo { +public: + CString m_screen; + CStringList m_aliases; + CConfig::CScreenOptions m_options; +}; + +class CChildWaitInfo { +public: + HWND m_dialog; + HANDLE m_child; + DWORD m_childID; + HANDLE m_ready; + HANDLE m_stop; +}; + +struct CModifierInfo { +public: + int m_ctrlID; + const char* m_name; + KeyModifierID m_modifierID; + OptionID m_optionID; +}; + +static const CModifierInfo s_modifiers[] = { + { IDC_ADD_MOD_SHIFT, "Shift", + kKeyModifierIDShift, kOptionModifierMapForShift }, + { IDC_ADD_MOD_CTRL, "Ctrl", + kKeyModifierIDControl, kOptionModifierMapForControl }, + { IDC_ADD_MOD_ALT, "Alt", + kKeyModifierIDAlt, kOptionModifierMapForAlt }, + { IDC_ADD_MOD_META, "Meta", + kKeyModifierIDMeta, kOptionModifierMapForMeta }, + { IDC_ADD_MOD_SUPER, "Super", + kKeyModifierIDSuper, kOptionModifierMapForSuper } +}; + +static const KeyModifierID baseModifier = kKeyModifierIDShift; + +static const char* s_debugName[][2] = { + { TEXT("Error"), "ERROR" }, + { TEXT("Warning"), "WARNING" }, + { TEXT("Note"), "NOTE" }, + { TEXT("Info"), "INFO" }, + { TEXT("Debug"), "DEBUG" }, + { TEXT("Debug1"), "DEBUG1" }, + { TEXT("Debug2"), "DEBUG2" } +}; +static const int s_defaultDebug = 3; // INFO + +HINSTANCE s_instance = NULL; + +static CGlobalOptions* s_globalOptions = NULL; +static CAdvancedOptions* s_advancedOptions = NULL; + +static const TCHAR* s_mainClass = TEXT("GoSynergy"); +static const TCHAR* s_layoutClass = TEXT("SynergyLayout"); + +// +// program arguments +// + +#define ARG CArgs::s_instance + +class CArgs { +public: + CArgs() { s_instance = this; } + ~CArgs() { s_instance = NULL; } + +public: + static CArgs* s_instance; + CConfig m_config; + CConfig m_oldConfig; + CStringList m_screens; +}; + +CArgs* CArgs::s_instance = NULL; + + +static +BOOL CALLBACK +addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +static +void +tokenize(CStringList& tokens, const CString& src) +{ + // find first non-whitespace + CString::size_type x = src.find_first_not_of(" \t\r\n"); + if (x == CString::npos) { + return; + } + + // find next whitespace + do { + CString::size_type y = src.find_first_of(" \t\r\n", x); + if (y == CString::npos) { + y = src.size(); + } + tokens.push_back(src.substr(x, y - x)); + x = src.find_first_not_of(" \t\r\n", y); + } while (x != CString::npos); +} + +static +bool +isNameInList(const CStringList& names, const CString& name) +{ + for (CStringList::const_iterator index = names.begin(); + index != names.end(); ++index) { + if (CStringUtil::CaselessCmp::equal(name, *index)) { + return true; + } + } + return false; +} + +static +bool +isClientChecked(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO); + return isItemChecked(child); +} + +static +void +enableSaveControls(HWND hwnd) +{ + enableItem(hwnd, IDC_MAIN_SAVE, ARG->m_config != ARG->m_oldConfig); +} + +static +void +enableScreensControls(HWND hwnd) +{ + // decide if edit and remove buttons should be enabled + bool client = isClientChecked(hwnd); + bool screenSelected = false; + if (!client) { + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + if (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR) { + screenSelected = true; + } + } + + // enable/disable controls + enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LABEL, !client); + enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST, !client); + enableItem(hwnd, IDC_MAIN_SERVER_ADD_BUTTON, !client); + enableItem(hwnd, IDC_MAIN_SERVER_EDIT_BUTTON, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_REMOVE_BUTTON, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_LAYOUT_LABEL, !client); + enableItem(hwnd, IDC_MAIN_SERVER_LEFT_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_RIGHT_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_TOP_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_BOTTOM_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_LEFT_LABEL, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_RIGHT_LABEL, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_TOP_LABEL, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_BOTTOM_LABEL, screenSelected); +} + +static +void +enableMainWindowControls(HWND hwnd) +{ + bool client = isClientChecked(hwnd); + enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_LABEL, client); + enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT, client); + enableScreensControls(hwnd); + enableSaveControls(hwnd); +} + +static +void +updateNeighbor(HWND hwnd, const CString& screen, EDirection direction) +{ + // remove all neighbors from combo box + SendMessage(hwnd, CB_RESETCONTENT, 0, 0); + + // add all screens to combo box + if (!screen.empty()) { + for (CConfig::const_iterator index = ARG->m_config.begin(); + index != ARG->m_config.end(); ++index) { + SendMessage(hwnd, CB_INSERTSTRING, + (WPARAM)-1, (LPARAM)index->c_str()); + } + } + + // add empty neighbor to combo box + SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)TEXT("---")); + + // select neighbor in combo box + LRESULT index = 0; + if (!screen.empty()) { + const CString& neighbor = ARG->m_config.getNeighbor(screen, direction); + if (!neighbor.empty()) { + index = SendMessage(hwnd, CB_FINDSTRINGEXACT, + 0, (LPARAM)neighbor.c_str()); + if (index == LB_ERR) { + index = 0; + } + } + } + SendMessage(hwnd, CB_SETCURSEL, index, 0); +} + +static +void +updateNeighbors(HWND hwnd) +{ + // get selected screen name or empty string if no selection + CString screen; + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index != LB_ERR) { + screen = ARG->m_screens[index]; + } + + // set neighbor combo boxes + child = getItem(hwnd, IDC_MAIN_SERVER_LEFT_COMBO); + updateNeighbor(child, screen, kLeft); + child = getItem(hwnd, IDC_MAIN_SERVER_RIGHT_COMBO); + updateNeighbor(child, screen, kRight); + child = getItem(hwnd, IDC_MAIN_SERVER_TOP_COMBO); + updateNeighbor(child, screen, kTop); + child = getItem(hwnd, IDC_MAIN_SERVER_BOTTOM_COMBO); + updateNeighbor(child, screen, kBottom); +} + +static +void +addScreen(HWND hwnd) +{ + // empty screen info + CScreenInfo info; + + // run dialog + if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), + hwnd, addDlgProc, (LPARAM)&info) != 0) { + // get current number of screens + UInt32 i = ARG->m_screens.size(); + + // add screen to list control + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + CString item = CStringUtil::print("%d. %s", + i + 1, info.m_screen.c_str()); + SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); + + // add screen to screen list + ARG->m_screens.push_back(info.m_screen); + + // add screen to config + ARG->m_config.addScreen(info.m_screen); + + // add aliases to config + for (CStringList::const_iterator index = info.m_aliases.begin(); + index != info.m_aliases.end(); ++index) { + ARG->m_config.addAlias(info.m_screen, *index); + } + + // set options + ARG->m_config.removeOptions(info.m_screen); + for (CConfig::CScreenOptions::const_iterator + index = info.m_options.begin(); + index != info.m_options.end(); ++index) { + ARG->m_config.addOption(info.m_screen, index->first, index->second); + } + + // update neighbors + updateNeighbors(hwnd); + enableScreensControls(hwnd); + enableSaveControls(hwnd); + } +} + +static +void +editScreen(HWND hwnd) +{ + // get selected list item + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + // no selection + return; + } + + // fill in screen info + CScreenInfo info; + info.m_screen = ARG->m_screens[index]; + for (CConfig::all_const_iterator index = ARG->m_config.beginAll(); + index != ARG->m_config.endAll(); ++index) { + if (CStringUtil::CaselessCmp::equal(index->second, info.m_screen) && + !CStringUtil::CaselessCmp::equal(index->second, index->first)) { + info.m_aliases.push_back(index->first); + } + } + const CConfig::CScreenOptions* options = + ARG->m_config.getOptions(info.m_screen); + if (options != NULL) { + info.m_options = *options; + } + + // save current info + CScreenInfo oldInfo = info; + + // run dialog + if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), + hwnd, addDlgProc, (LPARAM)&info) != 0) { + // replace screen + ARG->m_screens[index] = info.m_screen; + + // remove old aliases + for (CStringList::const_iterator index = oldInfo.m_aliases.begin(); + index != oldInfo.m_aliases.end(); ++index) { + ARG->m_config.removeAlias(*index); + } + + // replace name + ARG->m_config.renameScreen(oldInfo.m_screen, info.m_screen); + + // add new aliases + for (CStringList::const_iterator index = info.m_aliases.begin(); + index != info.m_aliases.end(); ++index) { + ARG->m_config.addAlias(info.m_screen, *index); + } + + // set options + ARG->m_config.removeOptions(info.m_screen); + for (CConfig::CScreenOptions::const_iterator + index = info.m_options.begin(); + index != info.m_options.end(); ++index) { + ARG->m_config.addOption(info.m_screen, index->first, index->second); + } + + // update list + CString item = CStringUtil::print("%d. %s", + index + 1, info.m_screen.c_str()); + SendMessage(child, LB_DELETESTRING, index, 0); + SendMessage(child, LB_INSERTSTRING, index, + (LPARAM)item.c_str()); + SendMessage(child, LB_SETCURSEL, index, 0); + + // update neighbors + updateNeighbors(hwnd); + enableSaveControls(hwnd); + } +} + +static +void +removeScreen(HWND hwnd) +{ + // get selected list item + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + // no selection + return; + } + + // get screen name + CString name = ARG->m_screens[index]; + + // remove screen from list control + SendMessage(child, LB_DELETESTRING, index, 0); + + // remove screen from screen list + ARG->m_screens.erase(&ARG->m_screens[index]); + + // remove screen from config (this also removes aliases) + ARG->m_config.removeScreen(name); + + // update neighbors + updateNeighbors(hwnd); + enableScreensControls(hwnd); + enableSaveControls(hwnd); +} + +static +void +changeNeighbor(HWND hwnd, HWND combo, EDirection direction) +{ + // get selected screen + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + // no selection + return; + } + + // get screen name + CString screen = ARG->m_screens[index]; + + // get selected neighbor + index = SendMessage(combo, CB_GETCURSEL, 0, 0); + + // remove old connection + ARG->m_config.disconnect(screen, direction); + + // add new connection + if (index != LB_ERR && index != 0) { + LRESULT size = SendMessage(combo, CB_GETLBTEXTLEN, index, 0); + char* neighbor = new char[size + 1]; + SendMessage(combo, CB_GETLBTEXT, index, (LPARAM)neighbor); + ARG->m_config.connect(screen, direction, CString(neighbor)); + delete[] neighbor; + } + + enableSaveControls(hwnd); +} + +static +bool +execApp(const char* app, const CString& cmdLine, PROCESS_INFORMATION* procInfo) +{ + // prepare startup info + STARTUPINFO startup; + startup.cb = sizeof(startup); + startup.lpReserved = NULL; + startup.lpDesktop = NULL; + startup.lpTitle = NULL; + startup.dwX = (DWORD)CW_USEDEFAULT; + startup.dwY = (DWORD)CW_USEDEFAULT; + startup.dwXSize = (DWORD)CW_USEDEFAULT; + startup.dwYSize = (DWORD)CW_USEDEFAULT; + startup.dwXCountChars = 0; + startup.dwYCountChars = 0; + startup.dwFillAttribute = 0; + startup.dwFlags = STARTF_FORCEONFEEDBACK; + startup.wShowWindow = SW_SHOWDEFAULT; + startup.cbReserved2 = 0; + startup.lpReserved2 = NULL; + startup.hStdInput = NULL; + startup.hStdOutput = NULL; + startup.hStdError = NULL; + + // prepare path to app + CString appPath = getAppPath(app); + + // put path to app in command line + CString commandLine = "\""; + commandLine += appPath; + commandLine += "\" "; + commandLine += cmdLine; + + // start child + if (CreateProcess(NULL, (char*)commandLine.c_str(), + NULL, + NULL, + FALSE, + CREATE_DEFAULT_ERROR_MODE | + CREATE_NEW_PROCESS_GROUP | + NORMAL_PRIORITY_CLASS, + NULL, + NULL, + &startup, + procInfo) == 0) { + return false; + } + else { + return true; + } +} + +static +CString +getCommandLine(HWND hwnd, bool testing) +{ + CString cmdLine; + + // add constant testing args + if (testing) { + cmdLine += " -z --no-restart --no-daemon"; + } + + // get the server name + CString server; + bool isClient = isClientChecked(hwnd); + if (isClient) { + // check server name + HWND child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); + server = getWindowText(child); + if (!ARG->m_config.isValidScreenName(server)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SERVER_NAME).c_str(), + server.c_str())); + SetFocus(child); + return CString(); + } + + // compare server name to local host. a common error + // is to provide the client's name for the server. we + // don't bother to check the addresses though that'd be + // more accurate. + if (CStringUtil::CaselessCmp::equal(ARCH->getHostName(), server)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SERVER_IS_CLIENT).c_str(), + server.c_str())); + SetFocus(child); + return CString(); + } + } + + // debug level. always include this. + if (true) { + HWND child = getItem(hwnd, IDC_MAIN_DEBUG); + DWORD debug = SendMessage(child, CB_GETCURSEL, 0, 0); + cmdLine += " --debug "; + cmdLine += s_debugName[debug][1]; + } + + // add advanced options + cmdLine += s_advancedOptions->getCommandLine(isClient, server); + + return cmdLine; +} + +static +HANDLE +launchApp(HWND hwnd, bool testing, DWORD* threadID) +{ + // decide if client or server + const bool isClient = isClientChecked(hwnd); + const char* app = isClient ? CLIENT_APP : SERVER_APP; + + // prepare command line + CString cmdLine = getCommandLine(hwnd, testing); + if (cmdLine.empty()) { + return NULL; + } + + // start child + PROCESS_INFORMATION procInfo; + if (!execApp(app, cmdLine, &procInfo)) { + showError(hwnd, CStringUtil::format( + getString(IDS_STARTUP_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + return NULL; + } + + // don't need process handle + CloseHandle(procInfo.hProcess); + + // save thread ID if desired + if (threadID != NULL) { + *threadID = procInfo.dwThreadId; + } + + // return thread handle + return procInfo.hThread; +} + +static +BOOL CALLBACK +waitDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + // only one wait dialog at a time! + static CChildWaitInfo* info = NULL; + + switch (message) { + case WM_INITDIALOG: + // save info pointer + info = reinterpret_cast(lParam); + + // save hwnd + info->m_dialog = hwnd; + + // signal ready + SetEvent(info->m_ready); + + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + case IDOK: + // signal stop + SetEvent(info->m_stop); + + // done + EndDialog(hwnd, 0); + return TRUE; + } + } + + return FALSE; +} + +static +DWORD WINAPI +waitForChildThread(LPVOID vinfo) +{ + CChildWaitInfo* info = reinterpret_cast(vinfo); + + // wait for ready + WaitForSingleObject(info->m_ready, INFINITE); + + // wait for thread to complete or stop event + HANDLE handles[2]; + handles[0] = info->m_child; + handles[1] = info->m_stop; + DWORD n = WaitForMultipleObjects(2, handles, FALSE, INFINITE); + + // if stop was raised then terminate child and wait for it + if (n == WAIT_OBJECT_0 + 1) { + PostThreadMessage(info->m_childID, WM_QUIT, 0, 0); + WaitForSingleObject(info->m_child, INFINITE); + } + + // otherwise post IDOK to dialog box + else { + PostMessage(info->m_dialog, WM_COMMAND, MAKEWPARAM(IDOK, 0), 0); + } + + return 0; +} + +static +void +waitForChild(HWND hwnd, HANDLE thread, DWORD threadID) +{ + // prepare info for child wait dialog and thread + CChildWaitInfo info; + info.m_dialog = NULL; + info.m_child = thread; + info.m_childID = threadID; + info.m_ready = CreateEvent(NULL, TRUE, FALSE, NULL); + info.m_stop = CreateEvent(NULL, TRUE, FALSE, NULL); + + // create a thread to wait on the child thread and event + DWORD id; + HANDLE waiter = CreateThread(NULL, 0, &waitForChildThread, &info,0, &id); + + // do dialog that let's the user terminate the test + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_WAIT), hwnd, + waitDlgProc, (LPARAM)&info); + + // force the waiter thread to finish and wait for it + SetEvent(info.m_ready); + SetEvent(info.m_stop); + WaitForSingleObject(waiter, INFINITE); + + // clean up + CloseHandle(waiter); + CloseHandle(info.m_ready); + CloseHandle(info.m_stop); +} + +static +void +initMainWindow(HWND hwnd) +{ + // append version number to title + CString titleFormat = getString(IDS_TITLE); + setWindowText(hwnd, CStringUtil::format(titleFormat.c_str(), VERSION)); + + // load configuration + bool configLoaded = loadConfig(ARG->m_config); + ARG->m_oldConfig = ARG->m_config; + enableSaveControls(hwnd); + + // get settings from registry + bool isServer = configLoaded; + int debugLevel = s_defaultDebug; + CString server; + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + if (isServer && CArchMiscWindows::hasValue(key, "isServer")) { + isServer = (CArchMiscWindows::readValueInt(key, "isServer") != 0); + } + if (CArchMiscWindows::hasValue(key, "debug")) { + debugLevel = static_cast( + CArchMiscWindows::readValueInt(key, "debug")); + if (debugLevel < 0) { + debugLevel = 0; + } + else if (debugLevel > CLog::kDEBUG2) { + debugLevel = CLog::kDEBUG2; + } + } + server = CArchMiscWindows::readValueString(key, "server"); + CArchMiscWindows::closeKey(key); + } + + // choose client/server radio buttons + HWND child; + child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO); + setItemChecked(child, !isServer); + child = getItem(hwnd, IDC_MAIN_SERVER_RADIO); + setItemChecked(child, isServer); + + // set server name + child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); + setWindowText(child, server); + + // if config is loaded then initialize server controls + if (configLoaded) { + int i = 1; + child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + for (CConfig::const_iterator index = ARG->m_config.begin(); + index != ARG->m_config.end(); ++i, ++index) { + ARG->m_screens.push_back(*index); + CString item = CStringUtil::print("%d. %s", i, index->c_str()); + SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); + } + } + + // debug level + child = getItem(hwnd, IDC_MAIN_DEBUG); + for (unsigned int i = 0; i < sizeof(s_debugName) / + sizeof(s_debugName[0]); ++i) { + SendMessage(child, CB_ADDSTRING, 0, (LPARAM)s_debugName[i][0]); + } + SendMessage(child, CB_SETCURSEL, debugLevel, 0); + + // update neighbor combo boxes + enableMainWindowControls(hwnd); + updateNeighbors(hwnd); +} + +static +void +saveMainWindow(HWND hwnd) +{ + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + HWND child; + child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); + CArchMiscWindows::setValue(key, "server", getWindowText(child)); + child = getItem(hwnd, IDC_MAIN_DEBUG); + CArchMiscWindows::setValue(key, "debug", + SendMessage(child, CB_GETCURSEL, 0, 0)); + CArchMiscWindows::setValue(key, "isServer", + isClientChecked(hwnd) ? 0 : 1); + CArchMiscWindows::closeKey(key); + } +} + +static +BOOL CALLBACK +addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + // only one add dialog at a time! + static CScreenInfo* info = NULL; + + switch (message) { + case WM_INITDIALOG: { + info = (CScreenInfo*)lParam; + + // set title + CString title; + if (info->m_screen.empty()) { + title = getString(IDS_ADD_SCREEN); + } + else { + title = CStringUtil::format( + getString(IDS_EDIT_SCREEN).c_str(), + info->m_screen.c_str()); + } + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)title.c_str()); + + // fill in screen name + HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)info->m_screen.c_str()); + + // fill in aliases + CString aliases; + for (CStringList::const_iterator index = info->m_aliases.begin(); + index != info->m_aliases.end(); ++index) { + if (!aliases.empty()) { + aliases += "\r\n"; + } + aliases += *index; + } + child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)aliases.c_str()); + + // set options + CConfig::CScreenOptions::const_iterator index; + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + index = info->m_options.find(kOptionHalfDuplexCapsLock); + setItemChecked(child, (index != info->m_options.end() && + index->second != 0)); + child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); + index = info->m_options.find(kOptionHalfDuplexNumLock); + setItemChecked(child, (index != info->m_options.end() && + index->second != 0)); + + // modifier options + for (UInt32 i = 0; i < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++i) { + child = getItem(hwnd, s_modifiers[i].m_ctrlID); + + // fill in options + for (UInt32 j = 0; j < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++j) { + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)s_modifiers[j].m_name); + } + + // choose current value + index = info->m_options.find(s_modifiers[i].m_optionID); + KeyModifierID id = s_modifiers[i].m_modifierID; + if (index != info->m_options.end()) { + id = index->second; + } + SendMessage(child, CB_SETCURSEL, id - baseModifier, 0); + } + + return TRUE; + } + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: { + CString newName; + CStringList newAliases; + + // extract name and aliases + HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); + newName = getWindowText(child); + child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); + tokenize(newAliases, getWindowText(child)); + + // name must be valid + if (!ARG->m_config.isValidScreenName(newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + newName.c_str())); + return TRUE; + } + + // aliases must be valid + for (CStringList::const_iterator index = newAliases.begin(); + index != newAliases.end(); ++index) { + if (!ARG->m_config.isValidScreenName(*index)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + index->c_str())); + return TRUE; + } + } + + // new name may not be in the new alias list + if (isNameInList(newAliases, newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SCREEN_NAME_IS_ALIAS).c_str(), + newName.c_str())); + return TRUE; + } + + // name must not exist in config but allow same name. also + // allow name if it exists in the old alias list but not the + // new one. + if (ARG->m_config.isScreen(newName) && + !CStringUtil::CaselessCmp::equal(newName, info->m_screen) && + !isNameInList(info->m_aliases, newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), + newName.c_str())); + return TRUE; + } + + // aliases must not exist in config but allow same aliases and + // allow an alias to be the old name. + for (CStringList::const_iterator index = newAliases.begin(); + index != newAliases.end(); ++index) { + if (ARG->m_config.isScreen(*index) && + !CStringUtil::CaselessCmp::equal(*index, info->m_screen) && + !isNameInList(info->m_aliases, *index)) { + showError(hwnd, CStringUtil::format( + getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), + index->c_str())); + return TRUE; + } + } + + // save name data + info->m_screen = newName; + info->m_aliases = newAliases; + + // save options + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + if (isItemChecked(child)) { + info->m_options[kOptionHalfDuplexCapsLock] = 1; + } + else { + info->m_options.erase(kOptionHalfDuplexCapsLock); + } + child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); + if (isItemChecked(child)) { + info->m_options[kOptionHalfDuplexNumLock] = 1; + } + else { + info->m_options.erase(kOptionHalfDuplexNumLock); + } + + // save modifier options + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + for (UInt32 i = 0; i < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++i) { + child = getItem(hwnd, s_modifiers[i].m_ctrlID); + KeyModifierID id = static_cast( + SendMessage(child, CB_GETCURSEL, 0, 0) + + baseModifier); + if (id != s_modifiers[i].m_modifierID) { + info->m_options[s_modifiers[i].m_optionID] = id; + } + else { + info->m_options.erase(s_modifiers[i].m_optionID); + } + } + + // success + EndDialog(hwnd, 1); + info = NULL; + return TRUE; + } + + case IDCANCEL: + EndDialog(hwnd, 0); + info = NULL; + return TRUE; + } + + default: + break; + } + + return FALSE; +} + +static +LRESULT CALLBACK +mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + // test for unsaved data + if (ARG->m_config != ARG->m_oldConfig) { + if (!askVerify(hwnd, getString(IDS_UNSAVED_DATA_REALLY_QUIT))) { + return 0; + } + } + + // quit + PostQuitMessage(0); + return 0; + + case IDOK: + case IDC_MAIN_TEST: { + // note if testing + const bool testing = (LOWORD(wParam) == IDC_MAIN_TEST); + + // save data + if (ARG->m_config != ARG->m_oldConfig) { + if (!saveConfig(ARG->m_config, false)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SAVE_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + return 0; + } + ARG->m_oldConfig = ARG->m_config; + enableSaveControls(hwnd); + } + + // launch child app + DWORD threadID; + HANDLE thread = launchApp(hwnd, testing, &threadID); + if (thread == NULL) { + return 0; + } + + // handle child program + if (testing) { + // wait for process to stop, allowing the user to kill it + waitForChild(hwnd, thread, threadID); + + // clean up + CloseHandle(thread); + } + else { + // don't need thread handle + CloseHandle(thread); + + // notify of success + askOkay(hwnd, getString(IDS_STARTED_TITLE), + getString(IDS_STARTED)); + + // quit + PostQuitMessage(0); + } + return 0; + } + + case IDC_MAIN_AUTOSTART: { + // construct command line + CString cmdLine = getCommandLine(hwnd, false); + if (!cmdLine.empty()) { + // run dialog + CAutoStart autoStart(hwnd, + isClientChecked(hwnd) ? NULL : &ARG->m_config, + cmdLine); + autoStart.doModal(); + if (autoStart.wasUserConfigSaved()) { + ARG->m_oldConfig = ARG->m_config; + enableSaveControls(hwnd); + } + } + return 0; + } + + case IDC_MAIN_SAVE: + if (!saveConfig(ARG->m_config, false)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SAVE_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + } + else { + ARG->m_oldConfig = ARG->m_config; + enableSaveControls(hwnd); + } + return 0; + + case IDC_MAIN_CLIENT_RADIO: + case IDC_MAIN_SERVER_RADIO: + enableMainWindowControls(hwnd); + return 0; + + case IDC_MAIN_SERVER_ADD_BUTTON: + addScreen(hwnd); + return 0; + + case IDC_MAIN_SERVER_EDIT_BUTTON: + editScreen(hwnd); + return 0; + + case IDC_MAIN_SERVER_REMOVE_BUTTON: + removeScreen(hwnd); + return 0; + + case IDC_MAIN_SERVER_SCREENS_LIST: + if (HIWORD(wParam) == LBN_SELCHANGE) { + enableScreensControls(hwnd); + updateNeighbors(hwnd); + } + else if (HIWORD(wParam) == LBN_DBLCLK) { + editScreen(hwnd); + return 0; + } + break; + + case IDC_MAIN_SERVER_LEFT_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kLeft); + return 0; + } + break; + + case IDC_MAIN_SERVER_RIGHT_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kRight); + return 0; + } + break; + + case IDC_MAIN_SERVER_TOP_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kTop); + return 0; + } + break; + + case IDC_MAIN_SERVER_BOTTOM_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kBottom); + return 0; + } + break; + + case IDC_MAIN_OPTIONS: + s_globalOptions->doModal(); + enableSaveControls(hwnd); + break; + + case IDC_MAIN_ADVANCED: + s_advancedOptions->doModal(isClientChecked(hwnd)); + enableSaveControls(hwnd); + break; + } + + default: + break; + } + return DefDlgProc(hwnd, message, wParam, lParam); +} + +int WINAPI +WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) +{ + CArch arch(instance); + CLOG; + CArgs args; + + s_instance = instance; + + // register main window (dialog) class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_HREDRAW | CS_VREDRAW; + classInfo.lpfnWndProc = &mainWndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = DLGWINDOWEXTRA; + classInfo.hInstance = instance; + classInfo.hIcon = (HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 32, 32, LR_SHARED); + classInfo.hCursor = LoadCursor(NULL, IDC_ARROW); + classInfo.hbrBackground = reinterpret_cast(COLOR_3DFACE + 1); + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = s_mainClass; + classInfo.hIconSm = (HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 16, 16, LR_SHARED); + RegisterClassEx(&classInfo); + + // create main window + HWND mainWindow = CreateDialog(s_instance, + MAKEINTRESOURCE(IDD_MAIN), 0, NULL); + + // prep windows + initMainWindow(mainWindow); + s_globalOptions = new CGlobalOptions(mainWindow, &ARG->m_config); + s_advancedOptions = new CAdvancedOptions(mainWindow, &ARG->m_config); + + // show window + ShowWindow(mainWindow, nCmdShow); + + // main loop + MSG msg; + bool done = false; + do { + switch (GetMessage(&msg, NULL, 0, 0)) { + case -1: + // error + break; + + case 0: + // quit + done = true; + break; + + default: + if (!IsDialogMessage(mainWindow, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + break; + } + } while (!done); + + // save values to registry + saveMainWindow(mainWindow); + + return msg.wParam; +} diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp new file mode 100644 index 0000000..fecdcf8 --- /dev/null +++ b/cmd/launcher/launcher.dsp @@ -0,0 +1,154 @@ +# Microsoft Developer Studio Project File - Name="launcher" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=launcher - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "launcher.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "launcher.mak" CFG="launcher - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "launcher - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "launcher - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "launcher - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../../Release/synergy.exe" +# SUBTRACT LINK32 /map + +!ELSEIF "$(CFG)" == "launcher - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../../Debug/synergy.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "launcher - Win32 Release" +# Name "launcher - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CAdvancedOptions.cpp +# End Source File +# Begin Source File + +SOURCE=.\CAutoStart.cpp +# End Source File +# Begin Source File + +SOURCE=.\CGlobalOptions.cpp +# End Source File +# Begin Source File + +SOURCE=.\launcher.cpp +# End Source File +# Begin Source File + +SOURCE=.\launcher.rc +# End Source File +# Begin Source File + +SOURCE=.\LaunchUtil.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CAdvancedOptions.h +# End Source File +# Begin Source File + +SOURCE=.\CAutoStart.h +# End Source File +# Begin Source File + +SOURCE=.\CGlobalOptions.h +# End Source File +# Begin Source File + +SOURCE=.\LaunchUtil.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\synergy.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc new file mode 100644 index 0000000..a8bf8c7 --- /dev/null +++ b/cmd/launcher/launcher.rc @@ -0,0 +1,367 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 262 +STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU +CAPTION "Synergy" +CLASS "GoSynergy" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Choose to start either the Client or Server and provide the requested information. Then click Test to check your settings or Start to save your settings and start Synergy.", + IDC_STATIC,7,7,286,19 + GROUPBOX "",IDC_STATIC,7,29,286,31 + GROUPBOX "",IDC_STATIC,7,67,286,103 + GROUPBOX "Options",IDC_STATIC,7,177,286,56 + CONTROL "&Client",IDC_MAIN_CLIENT_RADIO,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,29,33,10 + CONTROL "Server",IDC_MAIN_SERVER_RADIO,"Button", + BS_AUTORADIOBUTTON,11,67,37,10 + LTEXT "Server &Host Name:",IDC_MAIN_CLIENT_SERVER_NAME_LABEL, + 12,41,61,8 + EDITTEXT IDC_MAIN_CLIENT_SERVER_NAME_EDIT,79,39,106,12, + ES_AUTOHSCROLL + LTEXT "&Screens:",IDC_MAIN_SERVER_SCREENS_LABEL,12,79,29,8 + LISTBOX IDC_MAIN_SERVER_SCREENS_LIST,12,91,106,36, + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&Add",IDC_MAIN_SERVER_ADD_BUTTON,12,132,50,14 + PUSHBUTTON "&Edit",IDC_MAIN_SERVER_EDIT_BUTTON,68,132,50,14 + PUSHBUTTON "&Remove",IDC_MAIN_SERVER_REMOVE_BUTTON,12,150,50,14 + LTEXT "&Layout:",IDC_MAIN_SERVER_LAYOUT_LABEL,138,79,24,8 + LTEXT "Left:",IDC_MAIN_SERVER_LEFT_LABEL,144,93,15,8 + COMBOBOX IDC_MAIN_SERVER_LEFT_COMBO,175,91,111,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Right:",IDC_MAIN_SERVER_RIGHT_LABEL,144,109,20,8 + COMBOBOX IDC_MAIN_SERVER_RIGHT_COMBO,175,107,112,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Above:",IDC_MAIN_SERVER_TOP_LABEL,144,125,24,8 + COMBOBOX IDC_MAIN_SERVER_TOP_COMBO,175,123,112,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Below:",IDC_MAIN_SERVER_BOTTOM_LABEL,144,141,22,8 + COMBOBOX IDC_MAIN_SERVER_BOTTOM_COMBO,175,139,112,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&Options...",IDC_MAIN_OPTIONS,12,191,50,14 + PUSHBUTTON "Adva&nced...",IDC_MAIN_ADVANCED,68,191,50,14 + LTEXT "Automatic Startup:",IDC_STATIC,138,193,59,8 + PUSHBUTTON "Con&figure...",IDC_MAIN_AUTOSTART,202,191,50,14 + LTEXT "Lo&gging Level:",IDC_STATIC,12,216,48,8 + COMBOBOX IDC_MAIN_DEBUG,68,213,61,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Sa&ve",IDC_MAIN_SAVE,75,241,50,14 + DEFPUSHBUTTON "&Test",IDC_MAIN_TEST,131,241,50,14 + PUSHBUTTON "Start",IDOK,187,241,50,14 + PUSHBUTTON "Quit",IDCANCEL,243,241,50,14 +END + +IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 236 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION +CAPTION "Add Screen" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Screen Name:",IDC_STATIC,7,9,46,8 + EDITTEXT IDC_ADD_SCREEN_NAME_EDIT,79,7,106,12,ES_AUTOHSCROLL + LTEXT "&Aliases:",IDC_STATIC,7,25,25,8 + EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,40,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + GROUPBOX "Options",IDC_STATIC,7,72,178,64 + LTEXT "If your Caps Lock or Num Lock keys behave strangely on this client screen then try turning the half-duplex options on and reconnect the client.", + IDC_STATIC,13,82,165,25 + CONTROL "Half-duplex &Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,110,165,10 + CONTROL "Half-duplex &Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,122,165,10 + GROUPBOX "Modifiers",IDC_STATIC,7,139,178,65 + LTEXT "Shift",IDC_STATIC,13,155,15,8 + COMBOBOX IDC_ADD_MOD_SHIFT,37,152,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Ctrl",IDC_STATIC,13,170,11,8 + COMBOBOX IDC_ADD_MOD_CTRL,37,168,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Alt",IDC_STATIC,13,186,9,8 + COMBOBOX IDC_ADD_MOD_ALT,37,184,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Meta",IDC_STATIC,101,154,17,8 + COMBOBOX IDC_ADD_MOD_META,125,152,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Super",IDC_STATIC,101,170,20,8 + COMBOBOX IDC_ADD_MOD_SUPER,125,168,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,79,215,50,14 + PUSHBUTTON "Cancel",IDCANCEL,135,215,50,14 +END + +IDD_WAIT DIALOG DISCARDABLE 0, 0, 186, 54 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION +CAPTION "Running Test..." +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Stop",IDOK,129,33,50,14 + LTEXT "Running synergy. Press Stop to end the test.", + IDC_STATIC,7,7,172,15 +END + +IDD_AUTOSTART DIALOG DISCARDABLE 0, 0, 195, 189 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Auto Start" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDCANCEL,138,168,50,14 + LTEXT "Synergy can be configured to start automatically when you log in. If you have sufficient access rights, you can instead configure synergy to start automatically when your computer starts.", + IDC_STATIC,7,7,181,33 + LTEXT "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself.", + IDC_AUTOSTART_PERMISSION_MSG,7,69,181,17 + LTEXT "Synergy is configured to start automatically when the system starts.", + IDC_AUTOSTART_INSTALLED_MSG,7,93,181,17 + GROUPBOX "When &You Log In",IDC_STATIC,7,119,84,40 + PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_USER,23,133,50,14 + GROUPBOX "When &Computer Starts",IDC_STATIC,104,119,84,40 + PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_SYSTEM,119,134,50,14 + LTEXT "Synergy can be configured to start automatically when the computer starts or when you log in but not both.", + IDC_STATIC,7,43,181,17 +END + +IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 233 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "It's easy to unintentionally switch screens when the pointer is near a screen's edge. Synergy can prevent switching until certain conditions are met to reduce unintentional switching.", + IDC_STATIC,7,7,191,26 + LTEXT "Synergy can wait to switch until the cursor has been at a screen's edge for some amount of time.", + IDC_STATIC,7,37,193,16 + CONTROL "Switch after waiting",IDC_GLOBAL_DELAY_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,59,77,10 + EDITTEXT IDC_GLOBAL_DELAY_TIME,112,58,45,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms",IDC_STATIC,159,60,10,8 + LTEXT "Synergy can switch only when the cursor hits a screen edge twice within some amount of time.", + IDC_STATIC,7,77,193,16 + CONTROL "Switch on double tap within",IDC_GLOBAL_TWO_TAP_CHECK, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,99,103,10 + EDITTEXT IDC_GLOBAL_TWO_TAP_TIME,112,98,45,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms",IDC_STATIC,159,100,10,8 + LTEXT "Synergy can periodically check that clients are still alive and connected. Use this only if synergy doesn't detect when clients disconnect.", + IDC_STATIC,7,122,193,24 + CONTROL "Check clients every",IDC_GLOBAL_HEARTBEAT_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,153,78,10 + EDITTEXT IDC_GLOBAL_HEARTBEAT_TIME,112,152,45,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms",IDC_STATIC,159,154,10,8 + LTEXT "Synergy can synchronize screen savers across all screens.", + IDC_STATIC,7,176,193,8 + CONTROL "Synchronize screen savers",IDC_GLOBAL_SCREENSAVER_SYNC, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,192,101,10 + DEFPUSHBUTTON "OK",IDOK,94,212,50,14 + PUSHBUTTON "Cancel",IDCANCEL,150,212,50,14 +END + +IDD_ADVANCED_OPTIONS DIALOG DISCARDABLE 0, 0, 230, 133 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Advanced Options" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Synergy normally uses this computer's name as its screen name. Enter another name here if you want to use a different screen name.", + IDC_STATIC,7,7,216,19 + LTEXT "Screen &Name:",IDC_STATIC,7,34,46,8 + EDITTEXT IDC_ADVANCED_NAME_EDIT,63,32,106,12,ES_AUTOHSCROLL + LTEXT "Synergy normally uses a particular network port number. Enter an alternative port here. (The server and all clients must use the same port number.)", + IDC_STATIC,7,56,216,26 + LTEXT "&Port:",IDC_STATIC,7,90,16,8 + EDITTEXT IDC_ADVANCED_PORT_EDIT,63,88,40,12,ES_AUTOHSCROLL | + ES_NUMBER + PUSHBUTTON "&Defaults",IDC_ADVANCED_DEFAULTS,7,112,50,14 + DEFPUSHBUTTON "OK",IDOK,118,112,50,14 + PUSHBUTTON "Cancel",IDCANCEL,173,112,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_MAIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 255 + END + + IDD_ADD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 185 + TOPMARGIN, 7 + BOTTOMMARGIN, 229 + END + + IDD_WAIT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 47 + END + + IDD_AUTOSTART, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 188 + TOPMARGIN, 7 + BOTTOMMARGIN, 182 + END + + IDD_GLOBAL_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 200 + TOPMARGIN, 7 + BOTTOMMARGIN, 226 + END + + IDD_ADVANCED_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 223 + TOPMARGIN, 7 + BOTTOMMARGIN, 126 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergy.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_ERROR "Error" + IDS_INVALID_SCREEN_NAME "Screen name `%{1}' is invalid." + IDS_DUPLICATE_SCREEN_NAME "The screen name `%{1}' is already being used." + IDS_SCREEN_NAME_IS_ALIAS "A name may not be an alias of itself." + IDS_VERIFY "Confirm" + IDS_UNSAVED_DATA_REALLY_QUIT "You have unsaved changes. Really quit?" + IDS_UNKNOWN_SCREEN_NAME "The screen name `%{1}' is not in the layout." + IDS_INVALID_PORT "The port `%{1}' is invalid. It must be between 1 and 65535 inclusive. %{2} is the standard port." + IDS_SAVE_FAILED "Failed to save configuration: %{1}" + IDS_STARTUP_FAILED "Failed to start synergy: %{1}" + IDS_STARTED_TITLE "Started" + IDS_STARTED "Synergy was successfully started. Use the task manager or tray icon to terminate it." + IDS_UNINSTALL_TITLE "Removed Auto-Start" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_AUTOSTART_PERMISSION_SYSTEM + "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself." + IDS_AUTOSTART_PERMISSION_USER + "You have sufficient access rights to install and uninstall Auto Start for just yourself." + IDS_AUTOSTART_PERMISSION_NONE + "You do not have sufficient access rights to install or uninstall Auto Start." + IDS_AUTOSTART_INSTALLED_SYSTEM + "Synergy is configured to start automatically when the system starts." + IDS_AUTOSTART_INSTALLED_USER + "Synergy is configured to start automatically when you log in." + IDS_AUTOSTART_INSTALLED_NONE + "Synergy is not configured to start automatically." + IDS_INSTALL_LABEL "Install" + IDS_UNINSTALL_LABEL "Uninstall" + IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}." + IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}." + IDS_INSTALL_TITLE "Installed Auto-Start" + IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer." + IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in." +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_UNINSTALLED_SYSTEM "Removed auto-start. Synergy will not automatically start each time you start or reboot your computer." + IDS_UNINSTALLED_USER "Removed auto-start. Synergy will not automatically start each time you log in." + IDS_INVALID_SERVER_NAME "Server name `%{1}' is invalid." + IDS_TITLE "Synergy - Version %{1}" + IDS_SERVER_IS_CLIENT "Please enter the computer name of the synergy server, not\nthe name of this computer, in the Server Host Name field." + IDS_ADD_SCREEN "Add Screen" + IDS_EDIT_SCREEN "Edit Screen %{1}" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h new file mode 100644 index 0000000..2885b2f --- /dev/null +++ b/cmd/launcher/resource.h @@ -0,0 +1,113 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by launcher.rc +// +#define IDS_ERROR 1 +#define IDS_INVALID_SCREEN_NAME 2 +#define IDS_DUPLICATE_SCREEN_NAME 3 +#define IDS_SCREEN_NAME_IS_ALIAS 4 +#define IDS_VERIFY 5 +#define IDS_UNSAVED_DATA_REALLY_QUIT 6 +#define IDS_UNKNOWN_SCREEN_NAME 7 +#define IDS_INVALID_PORT 8 +#define IDS_SAVE_FAILED 9 +#define IDS_STARTUP_FAILED 10 +#define IDS_STARTED_TITLE 11 +#define IDS_STARTED 12 +#define IDS_INSTALL_FAILED 13 +#define IDS_UNINSTALL_TITLE 14 +#define IDS_UNINSTALLED 15 +#define IDS_UNINSTALL_FAILED 16 +#define IDS_CLIENT 17 +#define IDS_SERVER 18 +#define IDS_AUTOSTART_PERMISSION_SYSTEM 19 +#define IDS_AUTOSTART_PERMISSION_USER 20 +#define IDS_AUTOSTART_PERMISSION_NONE 21 +#define IDS_AUTOSTART_INSTALLED_SYSTEM 22 +#define IDS_AUTOSTART_INSTALLED_USER 23 +#define IDS_AUTOSTART_INSTALLED_NONE 24 +#define IDS_INSTALL_LABEL 25 +#define IDS_UNINSTALL_LABEL 26 +#define IDS_INSTALL_GENERIC_ERROR 27 +#define IDS_UNINSTALL_GENERIC_ERROR 28 +#define IDS_INSTALL_TITLE 29 +#define IDS_INSTALLED_SYSTEM 30 +#define IDS_INSTALLED_USER 31 +#define IDS_UNINSTALLED_SYSTEM 32 +#define IDS_UNINSTALLED_USER 33 +#define IDS_INVALID_SERVER_NAME 34 +#define IDS_TITLE 35 +#define IDS_SERVER_IS_CLIENT 36 +#define IDS_ADD_SCREEN 37 +#define IDS_EDIT_SCREEN 38 +#define IDS_INVALID_TIME 39 +#define IDD_MAIN 101 +#define IDD_ADD 102 +#define IDD_WAIT 103 +#define IDI_SYNERGY 104 +#define IDD_AUTOSTART 105 +#define IDD_ADVANCED_OPTIONS 106 +#define IDD_GLOBAL_OPTIONS 107 +#define IDC_MAIN_CLIENT_RADIO 1000 +#define IDC_MAIN_SERVER_RADIO 1001 +#define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1002 +#define IDC_MAIN_ADVANCED_NAME_EDIT 1006 +#define IDC_MAIN_ADVANCED_PORT_EDIT 1008 +#define IDC_MAIN_TEST 1009 +#define IDC_MAIN_SAVE 1010 +#define IDC_MAIN_CLIENT_SERVER_NAME_LABEL 1011 +#define IDC_MAIN_SERVER_SCREENS_LIST 1012 +#define IDC_MAIN_SERVER_SCREENS_LABEL 1013 +#define IDC_MAIN_SERVER_LAYOUT_LABEL 1014 +#define IDC_MAIN_SERVER_ADD_BUTTON 1018 +#define IDC_MAIN_SERVER_EDIT_BUTTON 1019 +#define IDC_ADD_SCREEN_NAME_EDIT 1020 +#define IDC_MAIN_SERVER_REMOVE_BUTTON 1020 +#define IDC_ADD_ALIASES_EDIT 1021 +#define IDC_MAIN_SERVER_OPTIONS_BUTTON 1021 +#define IDC_MAIN_SERVER_LEFT_COMBO 1022 +#define IDC_MAIN_SERVER_RIGHT_COMBO 1023 +#define IDC_MAIN_SERVER_TOP_COMBO 1024 +#define IDC_MAIN_SERVER_BOTTOM_COMBO 1025 +#define IDC_MAIN_SERVER_LEFT_LABEL 1026 +#define IDC_MAIN_SERVER_RIGHT_LABEL 1027 +#define IDC_MAIN_SERVER_TOP_LABEL 1028 +#define IDC_MAIN_SERVER_BOTTOM_LABEL 1029 +#define IDC_MAIN_UNINSTALL 1030 +#define IDC_AUTOSTART_INSTALLED_MSG 1031 +#define IDC_AUTOSTART_PERMISSION_MSG 1032 +#define IDC_AUTOSTART_INSTALL_USER 1033 +#define IDC_AUTOSTART_INSTALL_SYSTEM 1034 +#define IDC_MAIN_AUTOSTART 1035 +#define IDC_MAIN_OPTIONS 1036 +#define IDC_ADD_HD_CAPS_CHECK 1037 +#define IDC_MAIN_ADVANCED 1037 +#define IDC_ADD_HD_NUM_CHECK 1038 +#define IDC_ADVANCED_NAME_EDIT 1038 +#define IDC_ADVANCED_PORT_EDIT 1039 +#define IDC_MAIN_DEBUG 1040 +#define IDC_GLOBAL_DELAY_CHECK 1041 +#define IDC_GLOBAL_DELAY_TIME 1042 +#define IDC_GLOBAL_TWO_TAP_CHECK 1043 +#define IDC_ADD_MOD_SHIFT 1043 +#define IDC_GLOBAL_TWO_TAP_TIME 1044 +#define IDC_ADD_MOD_CTRL 1044 +#define IDC_ADD_MOD_ALT 1045 +#define IDC_GLOBAL_HEARTBEAT_CHECK 1045 +#define IDC_ADD_MOD_META 1046 +#define IDC_GLOBAL_HEARTBEAT_TIME 1046 +#define IDC_ADD_MOD_SUPER 1047 +#define IDC_GLOBAL_SCREENSAVER_SYNC 1047 +#define IDC_ADVANCED_DEFAULTS 1049 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 110 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1052 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/launcher/synergy.ico b/cmd/launcher/synergy.ico new file mode 100644 index 0000000000000000000000000000000000000000..39e0ff8d3a06d50e01aeab55c816f77d949c772d GIT binary patch literal 1078 zcmcIjF>ZrE5FBhIED^>0AXJf`ktfl)y)G-^$50||bEOL&;nF2tnn+}JFIZr?iL8vh zyEE(_9Op5RVS~D^V7P98ch;&(`j7#yTj0QJHRFSkA!h~xXCEW5$xaBVFq0z2$ZCzr zznCUt41VmArGJJv>>-Jz+yJ}8xQA)_q84#F7>Wj+$hY&P%aul{OC3v1I=iZr3+-$OY_kFlMUs(<&!6Qm*oKUsy=X4hhYoZPM#moz)C`vDH;Q(pi8 literal 0 HcmV?d00001 diff --git a/cmd/synergyc/CClientTaskBarReceiver.cpp b/cmd/synergyc/CClientTaskBarReceiver.cpp new file mode 100644 index 0000000..9c60c5b --- /dev/null +++ b/cmd/synergyc/CClientTaskBarReceiver.cpp @@ -0,0 +1,163 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientTaskBarReceiver.h" +#include "CClient.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CArch.h" + +// +// CClientTaskBarReceiver +// + +CClientTaskBarReceiver::CClientTaskBarReceiver() : + m_quit(NULL), + m_state(kNotRunning), + m_client(NULL) +{ + // create a job for getting notification when the client's + // status changes. + m_job = new TMethodJob(this, + &CClientTaskBarReceiver::statusChanged, NULL); +} + +CClientTaskBarReceiver::~CClientTaskBarReceiver() +{ + if (m_client != NULL) { + m_client->removeStatusJob(m_job); + } + delete m_job; + delete m_quit; +} + +void +CClientTaskBarReceiver::setClient(CClient* client) +{ + { + CLock lock(&m_mutex); + if (m_client != client) { + if (m_client != NULL) { + m_client->removeStatusJob(m_job); + } + m_client = client; + if (m_client != NULL) { + m_client->addStatusJob(m_job); + } + } + } + ARCH->updateReceiver(this); +} + +void +CClientTaskBarReceiver::setState(EState state) +{ + { + CLock lock(&m_mutex); + m_state = state; + } + ARCH->updateReceiver(this); +} + +void +CClientTaskBarReceiver::setQuitJob(IJob* job) +{ + CLock lock(&m_mutex); + delete m_quit; + m_quit = job; +} + +CClientTaskBarReceiver::EState +CClientTaskBarReceiver::getState() const +{ + return m_state; +} + +CClient* +CClientTaskBarReceiver::getClient() const +{ + return m_client; +} + +void +CClientTaskBarReceiver::lock() const +{ + m_mutex.lock(); +} + +void +CClientTaskBarReceiver::unlock() const +{ + m_mutex.unlock(); +} + +std::string +CClientTaskBarReceiver::getToolTip() const +{ + switch (m_state) { + case kNotRunning: + return "Synergy: Not running"; + + case kNotWorking: + return CString("Synergy: ") + m_errorMessage; + + case kNotConnected: + return "Synergy: Waiting for clients"; + + case kConnected: + return "Synergy: Connected"; + + default: + return ""; + } +} + +void +CClientTaskBarReceiver::quit() +{ + if (m_quit != NULL) { + m_quit->run(); + } +} + +void +CClientTaskBarReceiver::onStatusChanged() +{ + // do nothing +} + +void +CClientTaskBarReceiver::statusChanged(void*) +{ + // update our status + switch (m_client->getStatus(&m_errorMessage)) { + case CClient::kNotRunning: + setState(kNotRunning); + break; + + case CClient::kRunning: + setState(kConnected); + break; + + case CClient::kError: + setState(kNotWorking); + break; + + default: + break; + } + + // let subclasses have a go + onStatusChanged(); +} diff --git a/cmd/synergyc/CClientTaskBarReceiver.h b/cmd/synergyc/CClientTaskBarReceiver.h new file mode 100644 index 0000000..aa52b9d --- /dev/null +++ b/cmd/synergyc/CClientTaskBarReceiver.h @@ -0,0 +1,110 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTTASKBARRECEIVER_H +#define CCLIENTTASKBARRECEIVER_H + +#include "CMutex.h" +#include "CString.h" +#include "IArchTaskBarReceiver.h" + +class CClient; +class IJob; + +//! Implementation of IArchTaskBarReceiver for the synergy server +class CClientTaskBarReceiver : public IArchTaskBarReceiver { +public: + enum EState { + kNotRunning, + kNotWorking, + kNotConnected, + kConnected, + kMaxState + }; + + CClientTaskBarReceiver(); + virtual ~CClientTaskBarReceiver(); + + //! @name manipulators + //@{ + + //! Set server + /*! + Sets the server. The receiver will query state from this server. + */ + void setClient(CClient*); + + //! Set state + /*! + Sets the current server state. + */ + void setState(EState); + + //! Set the quit job that causes the server to quit + /*! + Set the job that causes the server to quit. + */ + void setQuitJob(IJob* adopted); + + //@} + //! @name accessors + //@{ + + //! Get state + /*! + Returns the current server state. The receiver is not locked + by this call; the caller must do the locking. + */ + EState getState() const; + + //! Get server + /*! + Returns the server set by \c setClient(). + */ + CClient* getClient() const; + + //@} + + // IArchTaskBarReceiver overrides + virtual void showStatus() = 0; + virtual void runMenu(int x, int y) = 0; + virtual void primaryAction() = 0; + virtual void lock() const; + virtual void unlock() const; + virtual const Icon getIcon() const = 0; + virtual std::string getToolTip() const; + +protected: + void quit(); + + //! Status change notification + /*! + Called when status changes. The default implementation does + nothing. + */ + virtual void onStatusChanged(); + +private: + void statusChanged(void*); + +private: + CMutex m_mutex; + IJob* m_quit; + EState m_state; + CClient* m_client; + IJob* m_job; + CString m_errorMessage; +}; + +#endif diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp new file mode 100644 index 0000000..3bfd581 --- /dev/null +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -0,0 +1,310 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClientTaskBarReceiver.h" +#include "CClient.h" +#include "CMSWindowsClipboard.h" +#include "LogOutputters.h" +#include "BasicTypes.h" +#include "CArch.h" +#include "CArchTaskBarWindows.h" +#include "resource.h" + +static const UINT g_stateToIconID[CMSWindowsClientTaskBarReceiver::kMaxState] = +{ + IDI_TASKBAR_NOT_RUNNING, + IDI_TASKBAR_NOT_WORKING, + IDI_TASKBAR_NOT_CONNECTED, + IDI_TASKBAR_CONNECTED +}; + +// +// CMSWindowsClientTaskBarReceiver +// + +CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver( + HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : + CClientTaskBarReceiver(), + m_appInstance(appInstance), + m_window(NULL), + m_logBuffer(logBuffer) +{ + for (UInt32 i = 0; i < kMaxState; ++i) { + m_icon[i] = loadIcon(g_stateToIconID[i]); + } + m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR)); + + // don't create the window yet. we'll create it on demand. this + // has the side benefit of being created in the thread used for + // the task bar. that's good because it means the existence of + // the window won't prevent changing the main thread's desktop. + + // add ourself to the task bar + ARCH->addReceiver(this); +} + +CMSWindowsClientTaskBarReceiver::~CMSWindowsClientTaskBarReceiver() +{ + ARCH->removeReceiver(this); + for (UInt32 i = 0; i < kMaxState; ++i) { + deleteIcon(m_icon[i]); + } + DestroyMenu(m_menu); + destroyWindow(); +} + +void +CMSWindowsClientTaskBarReceiver::showStatus() +{ + // create the window + createWindow(); + + // lock self while getting status + lock(); + + // get the current status + std::string status = getToolTip(); + + // done getting status + unlock(); + + // update dialog + HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str()); + + if (!IsWindowVisible(m_window)) { + // position it by the mouse + POINT cursorPos; + GetCursorPos(&cursorPos); + RECT windowRect; + GetWindowRect(m_window, &windowRect); + int x = cursorPos.x; + int y = cursorPos.y; + int fw = GetSystemMetrics(SM_CXDLGFRAME); + int fh = GetSystemMetrics(SM_CYDLGFRAME); + int ww = windowRect.right - windowRect.left; + int wh = windowRect.bottom - windowRect.top; + int sw = GetSystemMetrics(SM_CXFULLSCREEN); + int sh = GetSystemMetrics(SM_CYFULLSCREEN); + if (fw < 1) { + fw = 1; + } + if (fh < 1) { + fh = 1; + } + if (x + ww - fw > sw) { + x -= ww - fw; + } + else { + x -= fw; + } + if (x < 0) { + x = 0; + } + if (y + wh - fh > sh) { + y -= wh - fh; + } + else { + y -= fh; + } + if (y < 0) { + y = 0; + } + SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh, + SWP_SHOWWINDOW); + } +} + +void +CMSWindowsClientTaskBarReceiver::runMenu(int x, int y) +{ + // do popup menu. we need a window to pass to TrackPopupMenu(). + // the SetForegroundWindow() and SendMessage() calls around + // TrackPopupMenu() are to get the menu to be dismissed when + // another window gets activated and are just one of those + // win32 weirdnesses. + createWindow(); + SetForegroundWindow(m_window); + HMENU menu = GetSubMenu(m_menu, 0); + SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE); + int n = TrackPopupMenu(menu, + TPM_NONOTIFY | + TPM_RETURNCMD | + TPM_LEFTBUTTON | + TPM_RIGHTBUTTON, + x, y, 0, m_window, NULL); + SendMessage(m_window, WM_NULL, 0, 0); + + // perform the requested operation + switch (n) { + case IDC_TASKBAR_STATUS: + showStatus(); + break; + + case IDC_TASKBAR_LOG: + copyLog(); + break; + + case IDC_TASKBAR_QUIT: + quit(); + break; + } +} + +void +CMSWindowsClientTaskBarReceiver::primaryAction() +{ + showStatus(); +} + +const IArchTaskBarReceiver::Icon +CMSWindowsClientTaskBarReceiver::getIcon() const +{ + return reinterpret_cast(m_icon[getState()]); +} + +void +CMSWindowsClientTaskBarReceiver::copyLog() const +{ + if (m_logBuffer != NULL) { + // collect log buffer + CString data; + for (CBufferedLogOutputter::const_iterator index = m_logBuffer->begin(); + index != m_logBuffer->end(); ++index) { + data += *index; + data += "\n"; + } + + // copy log to clipboard + if (!data.empty()) { + CMSWindowsClipboard clipboard(m_window); + clipboard.open(0); + clipboard.emptyUnowned(); + clipboard.add(IClipboard::kText, data); + clipboard.close(); + } + } +} + +void +CMSWindowsClientTaskBarReceiver::onStatusChanged() +{ + if (IsWindowVisible(m_window)) { + showStatus(); + } +} + +HICON +CMSWindowsClientTaskBarReceiver::loadIcon(UINT id) +{ + HANDLE icon = LoadImage(m_appInstance, + MAKEINTRESOURCE(id), + IMAGE_ICON, + 0, 0, + LR_DEFAULTCOLOR); + return reinterpret_cast(icon); +} + +void +CMSWindowsClientTaskBarReceiver::deleteIcon(HICON icon) +{ + if (icon != NULL) { + DestroyIcon(icon); + } +} + +void +CMSWindowsClientTaskBarReceiver::createWindow() +{ + // ignore if already created + if (m_window != NULL) { + return; + } + + // get the status dialog + m_window = CreateDialogParam(m_appInstance, + MAKEINTRESOURCE(IDD_TASKBAR_STATUS), + NULL, + &CMSWindowsClientTaskBarReceiver::staticDlgProc, + reinterpret_cast( + reinterpret_cast(this))); + + // window should appear on top of everything, including (especially) + // the task bar. + DWORD style = GetWindowLong(m_window, GWL_EXSTYLE); + style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; + SetWindowLong(m_window, GWL_EXSTYLE, style); + + // tell the task bar about this dialog + CArchTaskBarWindows::addDialog(m_window); +} + +void +CMSWindowsClientTaskBarReceiver::destroyWindow() +{ + if (m_window != NULL) { + CArchTaskBarWindows::removeDialog(m_window); + DestroyWindow(m_window); + m_window = NULL; + } +} + +BOOL +CMSWindowsClientTaskBarReceiver::dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM) +{ + switch (msg) { + case WM_INITDIALOG: + // use default focus + return TRUE; + + case WM_ACTIVATE: + // hide when another window is activated + if (LOWORD(wParam) == WA_INACTIVE) { + ShowWindow(hwnd, SW_HIDE); + } + break; + } + return FALSE; +} + +BOOL CALLBACK +CMSWindowsClientTaskBarReceiver::staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + // if msg is WM_INITDIALOG, extract the CMSWindowsClientTaskBarReceiver* + // and put it in the extra window data then forward the call. + CMSWindowsClientTaskBarReceiver* self = NULL; + if (msg == WM_INITDIALOG) { + self = reinterpret_cast( + reinterpret_cast(lParam)); + SetWindowLong(hwnd, GWL_USERDATA, lParam); + } + else { + // get the extra window data and forward the call + LONG data = GetWindowLong(hwnd, GWL_USERDATA); + if (data != 0) { + self = reinterpret_cast( + reinterpret_cast(data)); + } + } + + // forward the message + if (self != NULL) { + return self->dlgProc(hwnd, msg, wParam, lParam); + } + else { + return (msg == WM_INITDIALOG) ? TRUE : FALSE; + } +} diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h new file mode 100644 index 0000000..56a3f16 --- /dev/null +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h @@ -0,0 +1,63 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIENTTASKBARRECEIVER_H +#define CMSWINDOWSCLIENTTASKBARRECEIVER_H + +#define WIN32_LEAN_AND_MEAN + +#include "CClientTaskBarReceiver.h" +#include + +class CBufferedLogOutputter; + +//! Implementation of CClientTaskBarReceiver for Microsoft Windows +class CMSWindowsClientTaskBarReceiver : public CClientTaskBarReceiver { +public: + CMSWindowsClientTaskBarReceiver(HINSTANCE, const CBufferedLogOutputter*); + virtual ~CMSWindowsClientTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + void copyLog() const; + + // CClientTaskBarReceiver overrides + virtual void onStatusChanged(); + +private: + HICON loadIcon(UINT); + void deleteIcon(HICON); + void createWindow(); + void destroyWindow(); + + BOOL dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + static BOOL CALLBACK + staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + +private: + HINSTANCE m_appInstance; + HWND m_window; + HMENU m_menu; + HICON m_icon[kMaxState]; + const CBufferedLogOutputter* m_logBuffer; +}; + +#endif diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp new file mode 100644 index 0000000..f60585e --- /dev/null +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClientTaskBarReceiver.h" +#include "CArch.h" + +// +// CXWindowsClientTaskBarReceiver +// + +CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver() +{ + // add ourself to the task bar + ARCH->addReceiver(this); +} + +CXWindowsClientTaskBarReceiver::~CXWindowsClientTaskBarReceiver() +{ + ARCH->removeReceiver(this); +} + +void +CXWindowsClientTaskBarReceiver::showStatus() +{ + // do nothing +} + +void +CXWindowsClientTaskBarReceiver::runMenu(int, int) +{ + // do nothing +} + +void +CXWindowsClientTaskBarReceiver::primaryAction() +{ + // do nothing +} + +const IArchTaskBarReceiver::Icon +CXWindowsClientTaskBarReceiver::getIcon() const +{ + return NULL; +} + +void +CXWindowsClientTaskBarReceiver::onStatusChanged() +{ + // do nothing +} diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h new file mode 100644 index 0000000..e6028ce --- /dev/null +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h @@ -0,0 +1,37 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIENTTASKBARRECEIVER_H +#define CXWINDOWSCLIENTTASKBARRECEIVER_H + +#include "CClientTaskBarReceiver.h" + +//! Implementation of CClientTaskBarReceiver for X Windows +class CXWindowsClientTaskBarReceiver : public CClientTaskBarReceiver { +public: + CXWindowsClientTaskBarReceiver(); + virtual ~CXWindowsClientTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + // CClientTaskBarReceiver overrides + virtual void onStatusChanged(); +}; + +#endif diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am new file mode 100644 index 0000000..6b2510c --- /dev/null +++ b/cmd/synergyc/Makefile.am @@ -0,0 +1,69 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CMSWindowsClientTaskBarReceiver.cpp \ + CMSWindowsClientTaskBarReceiver.h \ + resource.h \ + synergyc.dsp \ + synergyc.ico \ + synergyc.rc \ + tb_error.ico \ + tb_idle.ico \ + tb_run.ico \ + tb_wait.ico \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +bin_PROGRAMS = synergyc +synergyc_SOURCES = \ + CClientTaskBarReceiver.cpp \ + CClientTaskBarReceiver.h \ + CXWindowsClientTaskBarReceiver.cpp \ + CXWindowsClientTaskBarReceiver.h \ + synergyc.cpp \ + $(NULL) +synergyc_LDADD = \ + $(DEPTH)/lib/client/libclient.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a \ + $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + -I$(VDEPTH)/lib/client \ + $(NULL) diff --git a/cmd/synergyc/Makefile.in b/cmd/synergyc/Makefile.in new file mode 100644 index 0000000..78f4ef9 --- /dev/null +++ b/cmd/synergyc/Makefile.in @@ -0,0 +1,397 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CMSWindowsClientTaskBarReceiver.cpp \ + CMSWindowsClientTaskBarReceiver.h \ + resource.h \ + synergyc.dsp \ + synergyc.ico \ + synergyc.rc \ + tb_error.ico \ + tb_idle.ico \ + tb_run.ico \ + tb_wait.ico \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +bin_PROGRAMS = synergyc +synergyc_SOURCES = \ + CClientTaskBarReceiver.cpp \ + CClientTaskBarReceiver.h \ + CXWindowsClientTaskBarReceiver.cpp \ + CXWindowsClientTaskBarReceiver.h \ + synergyc.cpp \ + $(NULL) + +synergyc_LDADD = \ + $(DEPTH)/lib/client/libclient.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a \ + $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + -I$(VDEPTH)/lib/client \ + $(NULL) + +subdir = cmd/synergyc +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +bin_PROGRAMS = synergyc$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) + +am_synergyc_OBJECTS = CClientTaskBarReceiver.$(OBJEXT) \ + CXWindowsClientTaskBarReceiver.$(OBJEXT) synergyc.$(OBJEXT) +synergyc_OBJECTS = $(am_synergyc_OBJECTS) +synergyc_DEPENDENCIES = $(DEPTH)/lib/client/libclient.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/io/libio.a $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a $(DEPTH)/lib/arch/libarch.a +synergyc_LDFLAGS = + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CClientTaskBarReceiver.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsClientTaskBarReceiver.Po \ +@AMDEP_TRUE@ $(DEPDIR)/synergyc.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(synergyc_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(synergyc_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu cmd/synergyc/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo $$p1|sed '$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$f"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$f; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f $(DESTDIR)$(bindir)/$$f"; \ + rm -f $(DESTDIR)$(bindir)/$$f; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +synergyc$(EXEEXT): $(synergyc_OBJECTS) $(synergyc_DEPENDENCIES) + @rm -f synergyc$(EXEEXT) + $(CXXLINK) $(synergyc_LDFLAGS) $(synergyc_OBJECTS) $(synergyc_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClientTaskBarReceiver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsClientTaskBarReceiver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/synergyc.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(bindir) + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic distclean distclean-compile distclean-depend \ + distclean-generic distclean-tags distdir dvi dvi-am info \ + info-am install install-am install-binPROGRAMS install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h new file mode 100644 index 0000000..1da7860 --- /dev/null +++ b/cmd/synergyc/resource.h @@ -0,0 +1,27 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by synergyc.rc +// +#define IDS_FAILED 1 +#define IDI_SYNERGY 101 +#define IDI_TASKBAR_NOT_RUNNING 102 +#define IDI_TASKBAR_NOT_WORKING 103 +#define IDI_TASKBAR_NOT_CONNECTED 104 +#define IDI_TASKBAR_CONNECTED 105 +#define IDR_TASKBAR 107 +#define IDD_TASKBAR_STATUS 108 +#define IDC_TASKBAR_STATUS_STATUS 1000 +#define IDC_TASKBAR_QUIT 40003 +#define IDC_TASKBAR_STATUS 40004 +#define IDC_TASKBAR_LOG 40005 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp new file mode 100644 index 0000000..ce2dca0 --- /dev/null +++ b/cmd/synergyc/synergyc.cpp @@ -0,0 +1,739 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClient.h" +#include "ISecondaryScreenFactory.h" +#include "ProtocolTypes.h" +#include "Version.h" +#include "XScreen.h" +#include "CNetworkAddress.h" +#include "CTCPSocketFactory.h" +#include "XSocket.h" +#include "CCondVar.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "XThread.h" +#include "CFunctionJob.h" +#include "CLog.h" +#include "LogOutputters.h" +#include "CString.h" +#include "CArch.h" +#include + +#define DAEMON_RUNNING(running_) +#if WINDOWS_LIKE +#include "CMSWindowsScreen.h" +#include "CMSWindowsSecondaryScreen.h" +#include "CArchMiscWindows.h" +#include "CMSWindowsClientTaskBarReceiver.h" +#include "resource.h" +#undef DAEMON_RUNNING +#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) +#elif UNIX_LIKE +#include "CXWindowsSecondaryScreen.h" +#include "CXWindowsClientTaskBarReceiver.h" +#endif + +// platform dependent name of a daemon +#if WINDOWS_LIKE +#define DAEMON_NAME "Synergy Client" +#elif UNIX_LIKE +#define DAEMON_NAME "synergyc" +#endif + +// +// program arguments +// + +#define ARG CArgs::s_instance + +class CArgs { +public: + CArgs() : + m_pname(NULL), + m_backend(false), + m_restartable(true), + m_daemon(true), + m_logFilter(NULL) + { s_instance = this; } + ~CArgs() { s_instance = NULL; } + +public: + static CArgs* s_instance; + const char* m_pname; + bool m_backend; + bool m_restartable; + bool m_daemon; + const char* m_logFilter; + CString m_name; + CNetworkAddress m_serverAddress; +}; + +CArgs* CArgs::s_instance = NULL; + + +// +// platform dependent factories +// + +//! Factory for creating secondary screens +/*! +Objects of this type create secondary screens appropriate for the +platform. +*/ +class CSecondaryScreenFactory : public ISecondaryScreenFactory { +public: + CSecondaryScreenFactory() { } + virtual ~CSecondaryScreenFactory() { } + + // ISecondaryScreenFactory overrides + virtual CSecondaryScreen* + create(IScreenReceiver*); +}; + +CSecondaryScreen* +CSecondaryScreenFactory::create(IScreenReceiver* receiver) +{ +#if WINDOWS_LIKE + return new CMSWindowsSecondaryScreen(receiver); +#elif UNIX_LIKE + return new CXWindowsSecondaryScreen(receiver); +#endif +} + + +//! CQuitJob +/*! +A job that cancels a given thread. +*/ +class CQuitJob : public IJob { +public: + CQuitJob(const CThread& thread); + ~CQuitJob(); + + // IJob overrides + virtual void run(); + +private: + CThread m_thread; +}; + +CQuitJob::CQuitJob(const CThread& thread) : + m_thread(thread) +{ + // do nothing +} + +CQuitJob::~CQuitJob() +{ + // do nothing +} + +void +CQuitJob::run() +{ + m_thread.cancel(); +} + + +// +// platform independent main +// + +static CClient* s_client = NULL; +static CClientTaskBarReceiver* s_taskBarReceiver = NULL; + +static +int +realMain(void) +{ + int result = kExitSuccess; + do { + bool opened = false; + bool locked = true; + try { + // create client + s_client = new CClient(ARG->m_name); + s_client->setAddress(ARG->m_serverAddress); + s_client->setScreenFactory(new CSecondaryScreenFactory); + s_client->setSocketFactory(new CTCPSocketFactory); + s_client->setStreamFilterFactory(NULL); + + // open client + try { + s_taskBarReceiver->setClient(s_client); + s_client->open(); + opened = true; + + // run client + DAEMON_RUNNING(true); + locked = false; + s_client->mainLoop(); + locked = true; + DAEMON_RUNNING(false); + + // get client status + if (s_client->wasRejected()) { + // try again later. we don't want to bother + // the server very often if it doesn't want us. + throw XScreenUnavailable(60.0); + } + + // clean up +#define FINALLY do { \ + if (!locked) { \ + DAEMON_RUNNING(false); \ + locked = true; \ + } \ + if (opened) { \ + s_client->close(); \ + } \ + s_taskBarReceiver->setClient(NULL); \ + delete s_client; \ + s_client = NULL; \ + } while (false) + FINALLY; + } + catch (XScreenUnavailable& e) { + // wait before retrying if we're going to retry + if (ARG->m_restartable) { + LOG((CLOG_DEBUG "waiting %.0f seconds to retry", e.getRetryTime())); + ARCH->sleep(e.getRetryTime()); + } + else { + result = kExitFailed; + } + FINALLY; + } + catch (XThread&) { + FINALLY; + throw; + } + catch (...) { + // don't try to restart and fail + ARG->m_restartable = false; + result = kExitFailed; + FINALLY; + } +#undef FINALLY + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed: %s", e.what())); + } + catch (XThread&) { + // terminated + ARG->m_restartable = false; + result = kExitTerminated; + } + } while (ARG->m_restartable); + + return result; +} + +static +void +realMainEntry(void* vresult) +{ + *reinterpret_cast(vresult) = realMain(); +} + +static +int +runMainInThread(void) +{ + int result = 0; + CThread appThread(new CFunctionJob(&realMainEntry, &result)); + try { +#if WINDOWS_LIKE + MSG msg; + while (appThread.waitForEvent(-1.0) == CThread::kEvent) { + // check for a quit event + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + CThread::getCurrentThread().cancel(); + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +#else + appThread.wait(-1.0); +#endif + return result; + } + catch (XThread&) { + appThread.cancel(); + appThread.wait(-1.0); + throw; + } +} + + +// +// command line parsing +// + +#define BYE "\nTry `%s --help' for more information." + +static void (*bye)(int) = &exit; + +static +void +version() +{ + LOG((CLOG_PRINT +"%s %s, protocol version %d.%d\n" +"%s", + ARG->m_pname, + kVersion, + kProtocolMajorVersion, + kProtocolMinorVersion, + kCopyright)); +} + +static +void +help() +{ + LOG((CLOG_PRINT +"Usage: %s" +" [--daemon|--no-daemon]" +" [--debug ]" +" [--name ]" +" [--restart|--no-restart]" +" \n" +"\n" +"Start the synergy mouse/keyboard sharing server.\n" +"\n" +" -d, --debug filter out log messages with priorty below level.\n" +" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" +" DEBUG, DEBUG1, DEBUG2.\n" +" -f, --no-daemon run the client in the foreground.\n" +"* --daemon run the client as a daemon.\n" +" -n, --name use screen-name instead the hostname to identify\n" +" ourself to the server.\n" +" -1, --no-restart do not try to restart the client if it fails for\n" +" some reason.\n" +"* --restart restart the client automatically if it fails.\n" +" -h, --help display this help and exit.\n" +" --version display version information and exit.\n" +"\n" +"* marks defaults.\n" +"\n" +"The server address is of the form: [][:]. The hostname\n" +"must be the address or hostname of the server. The port overrides the\n" +"default port, %d.\n" +"\n" +"Where log messages go depends on the platform and whether or not the\n" +"client is running as a daemon.", + ARG->m_pname, kDefaultPort)); + +} + +static +bool +isArg(int argi, int argc, const char* const* argv, + const char* name1, const char* name2, + int minRequiredParameters = 0) +{ + if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || + (name2 != NULL && strcmp(argv[argi], name2) == 0)) { + // match. check args left. + if (argi + minRequiredParameters >= argc) { + LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, + ARG->m_pname, argv[argi], ARG->m_pname)); + bye(kExitArgs); + } + return true; + } + + // no match + return false; +} + +static +void +parse(int argc, const char* const* argv) +{ + assert(ARG->m_pname != NULL); + assert(argv != NULL); + assert(argc >= 1); + + // set defaults + ARG->m_name = ARCH->getHostName(); + + // parse options + int i; + for (i = 1; i < argc; ++i) { + if (isArg(i, argc, argv, "-d", "--debug", 1)) { + // change logging level + ARG->m_logFilter = argv[++i]; + } + + else if (isArg(i, argc, argv, "-n", "--name", 1)) { + // save screen name + ARG->m_name = argv[++i]; + } + + else if (isArg(i, argc, argv, NULL, "--camp")) { + // ignore -- included for backwards compatibility + } + + else if (isArg(i, argc, argv, NULL, "--no-camp")) { + // ignore -- included for backwards compatibility + } + + else if (isArg(i, argc, argv, "-f", "--no-daemon")) { + // not a daemon + ARG->m_daemon = false; + } + + else if (isArg(i, argc, argv, NULL, "--daemon")) { + // daemonize + ARG->m_daemon = true; + } + + else if (isArg(i, argc, argv, "-1", "--no-restart")) { + // don't try to restart + ARG->m_restartable = false; + } + + else if (isArg(i, argc, argv, NULL, "--restart")) { + // try to restart + ARG->m_restartable = true; + } + + else if (isArg(i, argc, argv, "-z", NULL)) { + ARG->m_backend = true; + } + + else if (isArg(i, argc, argv, "-h", "--help")) { + help(); + bye(kExitSuccess); + } + + else if (isArg(i, argc, argv, NULL, "--version")) { + version(); + bye(kExitSuccess); + } + + else if (isArg(i, argc, argv, "--", NULL)) { + // remaining arguments are not options + ++i; + break; + } + + else if (argv[i][0] == '-') { + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + ARG->m_pname, argv[i], ARG->m_pname)); + bye(kExitArgs); + } + + else { + // this and remaining arguments are not options + break; + } + } + + // exactly one non-option argument (server-address) + if (i == argc) { + LOG((CLOG_PRINT "%s: a server address or name is required" BYE, + ARG->m_pname, ARG->m_pname)); + bye(kExitArgs); + } + if (i + 1 != argc) { + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + ARG->m_pname, argv[i], ARG->m_pname)); + bye(kExitArgs); + } + + // save server address + try { + ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); + } + catch (XSocketAddress& e) { + LOG((CLOG_PRINT "%s: %s" BYE, + ARG->m_pname, e.what(), ARG->m_pname)); + bye(kExitFailed); + } + + // increase default filter level for daemon. the user must + // explicitly request another level for a daemon. + if (ARG->m_daemon && ARG->m_logFilter == NULL) { +#if WINDOWS_LIKE + if (CArchMiscWindows::isWindows95Family()) { + // windows 95 has no place for logging so avoid showing + // the log console window. + ARG->m_logFilter = "FATAL"; + } + else +#endif + { + ARG->m_logFilter = "NOTE"; + } + } + + // set log filter + if (!CLOG->setFilter(ARG->m_logFilter)) { + LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, + ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); + bye(kExitArgs); + } +} + + +// +// platform dependent entry points +// + +#if WINDOWS_LIKE + +static bool s_hasImportantLogMessages = false; + +// +// CMessageBoxOutputter +// +// This class writes severe log messages to a message box +// + +class CMessageBoxOutputter : public ILogOutputter { +public: + CMessageBoxOutputter() { } + virtual ~CMessageBoxOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } +}; + +bool +CMessageBoxOutputter::write(ELevel level, const char* message) +{ + // note any important messages the user may need to know about + if (level <= CLog::kWARNING) { + s_hasImportantLogMessages = true; + } + + // FATAL and PRINT messages get a dialog box if not running as + // backend. if we're running as a backend the user will have + // a chance to see the messages when we exit. + if (!ARG->m_backend && level <= CLog::kFATAL) { + MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING); + return false; + } + else { + return true; + } +} + +static +void +byeThrow(int x) +{ + CArchMiscWindows::daemonFailed(x); +} + +static +int +daemonStartup(int argc, const char** argv) +{ + CSystemLogger sysLogger(DAEMON_NAME); + + // have to cancel this thread to quit + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + // catch errors that would normally exit + bye = &byeThrow; + + // parse command line + parse(argc, argv); + + // cannot run as backend if running as a service + ARG->m_backend = false; + + // run as a service + return CArchMiscWindows::runDaemon(realMain); +} + +static +int +daemonStartup95(int, const char**) +{ + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); +} + +static +int +run(int argc, char** argv) +{ + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + + // parse command line + parse(argc, argv); + + // daemonize if requested + if (ARG->m_daemon) { + // start as a daemon + if (CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup95); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + else { + // cannot start a service from the command line so just + // run normally (except with log messages redirected). + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); + } + } + else { + // run + return runMainInThread(); + } +} + +int WINAPI +WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) +{ + CArch arch(instance); + CLOG; + CArgs args; + + // save instance + CMSWindowsScreen::init(instance); + + // get program name + ARG->m_pname = ARCH->getBasename(__argv[0]); + + // send PRINT and FATAL output to a message box + CLOG->insert(new CMessageBoxOutputter); + + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance, + &logBuffer); + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + int result; + try { + // run in foreground or as a daemon + result = run(__argc, __argv); + } + catch (...) { + // note that we don't rethrow thread cancellation. we'll + // be exiting soon so it doesn't matter. what we'd like + // is for everything after this try/catch to be in a + // finally block. + result = kExitFailed; + } + + // done with task bar receiver + delete s_taskBarReceiver; + + // done with log buffer + CLOG->remove(&logBuffer); + + // let user examine any messages if we're running as a backend + // by putting up a dialog box before exiting. + if (ARG->m_backend && s_hasImportantLogMessages) { + char msg[1024]; + msg[0] = '\0'; + LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); + MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); + } + + delete CLOG; + return result; +} + +#elif UNIX_LIKE + +static +int +daemonStartup(int, const char**) +{ + CSystemLogger sysLogger(DAEMON_NAME); + return realMain(); +} + +int +main(int argc, char** argv) +{ + CArch arch; + CLOG; + CArgs args; + + // get program name + ARG->m_pname = ARCH->getBasename(argv[0]); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CXWindowsClientTaskBarReceiver; + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + // parse command line + parse(argc, argv); + + // daemonize if requested + int result; + if (ARG->m_daemon) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon&) { + LOG((CLOG_CRIT "failed to daemonize")); + result = kExitFailed; + } + } + else { + result = realMain(); + } + + // done with task bar receiver + delete s_taskBarReceiver; + + return result; +} + +#else + +#error no main() for platform + +#endif diff --git a/cmd/synergyc/synergyc.dsp b/cmd/synergyc/synergyc.dsp new file mode 100644 index 0000000..3321561 --- /dev/null +++ b/cmd/synergyc/synergyc.dsp @@ -0,0 +1,153 @@ +# Microsoft Developer Studio Project File - Name="synergyc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=synergyc - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "synergyc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "synergyc.mak" CFG="synergyc - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synergyc - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "synergyc - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synergyc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "synergyc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "synergyc - Win32 Release" +# Name "synergyc - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClientTaskBarReceiver.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClientTaskBarReceiver.cpp +# End Source File +# Begin Source File + +SOURCE=.\synergyc.cpp +# End Source File +# Begin Source File + +SOURCE=.\synergyc.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClientTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClientTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\synergyc.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_error.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_idle.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_run.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_wait.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/cmd/synergyc/synergyc.ico b/cmd/synergyc/synergyc.ico new file mode 100644 index 0000000000000000000000000000000000000000..23d9a0906462ab9c0d97b7493376c20036ea471e GIT binary patch literal 1078 zcmcIjF>ZrE5FC6+xQh8fs3Mn0+fY(a_&Id6Ct)f3ES};rr3*_b$?RURz;Y8=8GCnU z*gZJTV<5v0RaL_9wF5p_%QER>2D}B}$ZHj&Wn{>ifymkCh-|VGV=By~n5K!<8nb^f z&&C-1*da;TyJ!;jP^Q6N@Bgk5}L6UBBn>K0sicl|7 z>c~aHzIP$D4S{V|pr=T(rQ-7!*>yJK8teUVc(XssjjkWGz2AEMUHkF#ec5+9I81*g z>e-HOCD}%iv@d!g`Jq1~9Im|o1qc%Zpk4onNYgUHDh}lDlX-3f^Lfdr7>my*^5=;3 zI6L&T7iQ9ze6GqH(2HHmL*4TyYI|+Gsx*5&cPi41(wy8OavnuT5$#UelluIqoTrDl j{)%DX{(iHu1?m5W`_UE`Ag|c9SRp63>%}F_F6({(d|mCB literal 0 HcmV?d00001 diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc new file mode 100644 index 0000000..9fd0020 --- /dev/null +++ b/cmd/synergyc/synergyc.rc @@ -0,0 +1,118 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergyc.ico" +IDI_TASKBAR_NOT_RUNNING ICON DISCARDABLE "tb_idle.ico" +IDI_TASKBAR_NOT_WORKING ICON DISCARDABLE "tb_error.ico" +IDI_TASKBAR_NOT_CONNECTED ICON DISCARDABLE "tb_wait.ico" +IDI_TASKBAR_CONNECTED ICON DISCARDABLE "tb_run.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_TASKBAR_STATUS DIALOG DISCARDABLE 0, 0, 145, 18 +STYLE DS_MODALFRAME | WS_POPUP +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_TASKBAR MENU DISCARDABLE +BEGIN + POPUP "Synergy" + BEGIN + MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG + MENUITEM SEPARATOR + MENUITEM "Quit", IDC_TASKBAR_QUIT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/cmd/synergyc/tb_error.ico b/cmd/synergyc/tb_error.ico new file mode 100644 index 0000000000000000000000000000000000000000..a304dc1815fa0ababb136f6f1ba13735d2fbb5a6 GIT binary patch literal 318 zcmaKmI}U?D3`8Fa1(GSKQd6eP(da0%M{>W753q;H0Z_=8bSPr%nek^=GIWY*nn*l4 zxTlLbnZ=G?3UTN^DWIg3pGj2`5%OH@N3!o(iHaPhoKX)@YIUo1XqN6xF32N$#zwk% rh~{<7+ME_FZfttD==auOF(2NnD_ToW$P?mr8eX*h4C50%ue=|=dPPd3 literal 0 HcmV?d00001 diff --git a/cmd/synergyc/tb_idle.ico b/cmd/synergyc/tb_idle.ico new file mode 100644 index 0000000000000000000000000000000000000000..9ba599de07841a8899275e54b91383ebdba39fb1 GIT binary patch literal 318 zcmZurAr8YZ5OcLkQ#4XEy23qgEVQs=fwj_)L?NM-@}RNK*GMJXU2NYux?F%xISd0V zo*i&E70elh4!syC2mhl9(7`k{5vxQgMQRuOaj@@J2}-k!&TwR~h6-SQi@40yOtNl# zA(87AkhwT%PDBb@%#1m$E;{BcpK8Iik~cV3LGuCL+q>RFM%&XC+ZR5MR$mHmRoS=p Mmq%Hp**0UdUu;rNsQ>@~ literal 0 HcmV?d00001 diff --git a/cmd/synergyc/tb_run.ico b/cmd/synergyc/tb_run.ico new file mode 100644 index 0000000000000000000000000000000000000000..86aa9f3ac989981768b950663e06469635252f8e GIT binary patch literal 318 zcmZ{fJqp7x428ddp@9UNsdL62FI!fRK2CNoI)D$6Bjf;u^vNdBp^^N4kR@b8r<|q< zk9P+zSHUZ@&=JI7mH$x$9b#-Emc`7ZesR{rdAbvnUUIsLlp^#g69w)PJ`2R?tWuD+=8@5=TLcpmWBg&BfSKQ6f<$ap>Mt@oPy0w4@W Ai2wiq literal 0 HcmV?d00001 diff --git a/cmd/synergyc/tb_wait.ico b/cmd/synergyc/tb_wait.ico new file mode 100644 index 0000000000000000000000000000000000000000..3e2edd5f305fecd3883d8d44892dcf091156b8ee GIT binary patch literal 318 zcmZ{fJr08~423^Lq%u@0Q)kAGNWK6A2V!H*m}BJt9D*ZofJ*c^5mJZN&ij#L%Lz2f zzVGpPHSly5yfO$4K@7R@FN&Z;j8(+4addReceiver(this); +} + +CMSWindowsServerTaskBarReceiver::~CMSWindowsServerTaskBarReceiver() +{ + ARCH->removeReceiver(this); + for (UInt32 i = 0; i < kMaxState; ++i) { + deleteIcon(m_icon[i]); + } + DestroyMenu(m_menu); + destroyWindow(); +} + +void +CMSWindowsServerTaskBarReceiver::showStatus() +{ + // create the window + createWindow(); + + // lock self while getting status + lock(); + + // get the current status + std::string status = getToolTip(); + + // get the connect clients, if any + typedef std::vector CClientList; + CClientList clients; + CServer* server = getServer(); + if (server != NULL) { + server->getClients(clients); + } + + // done getting status + unlock(); + + // update dialog + HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str()); + child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_CLIENTS); + SendMessage(child, LB_RESETCONTENT, 0, 0); + for (CClientList::const_iterator index = clients.begin(); + index != clients.end(); ) { + const char* client = index->c_str(); + if (++index == clients.end()) { + SendMessage(child, LB_ADDSTRING, 0, (LPARAM)client); + } + else { + SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)client); + } + } + + if (!IsWindowVisible(m_window)) { + // position it by the mouse + POINT cursorPos; + GetCursorPos(&cursorPos); + RECT windowRect; + GetWindowRect(m_window, &windowRect); + int x = cursorPos.x; + int y = cursorPos.y; + int fw = GetSystemMetrics(SM_CXDLGFRAME); + int fh = GetSystemMetrics(SM_CYDLGFRAME); + int ww = windowRect.right - windowRect.left; + int wh = windowRect.bottom - windowRect.top; + int sw = GetSystemMetrics(SM_CXFULLSCREEN); + int sh = GetSystemMetrics(SM_CYFULLSCREEN); + if (fw < 1) { + fw = 1; + } + if (fh < 1) { + fh = 1; + } + if (x + ww - fw > sw) { + x -= ww - fw; + } + else { + x -= fw; + } + if (x < 0) { + x = 0; + } + if (y + wh - fh > sh) { + y -= wh - fh; + } + else { + y -= fh; + } + if (y < 0) { + y = 0; + } + SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh, + SWP_SHOWWINDOW); + } +} + +void +CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) +{ + // do popup menu. we need a window to pass to TrackPopupMenu(). + // the SetForegroundWindow() and SendMessage() calls around + // TrackPopupMenu() are to get the menu to be dismissed when + // another window gets activated and are just one of those + // win32 weirdnesses. + createWindow(); + SetForegroundWindow(m_window); + HMENU menu = GetSubMenu(m_menu, 0); + SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE); + int n = TrackPopupMenu(menu, + TPM_NONOTIFY | + TPM_RETURNCMD | + TPM_LEFTBUTTON | + TPM_RIGHTBUTTON, + x, y, 0, m_window, NULL); + SendMessage(m_window, WM_NULL, 0, 0); + + // perform the requested operation + switch (n) { + case IDC_TASKBAR_STATUS: + showStatus(); + break; + + case IDC_TASKBAR_LOG: + copyLog(); + break; + + case IDC_TASKBAR_QUIT: + quit(); + break; + } +} + +void +CMSWindowsServerTaskBarReceiver::primaryAction() +{ + showStatus(); +} + +const IArchTaskBarReceiver::Icon +CMSWindowsServerTaskBarReceiver::getIcon() const +{ + return reinterpret_cast(m_icon[getState()]); +} + +void +CMSWindowsServerTaskBarReceiver::copyLog() const +{ + if (m_logBuffer != NULL) { + // collect log buffer + CString data; + for (CBufferedLogOutputter::const_iterator index = m_logBuffer->begin(); + index != m_logBuffer->end(); ++index) { + data += *index; + data += "\n"; + } + + // copy log to clipboard + if (!data.empty()) { + CMSWindowsClipboard clipboard(m_window); + clipboard.open(0); + clipboard.emptyUnowned(); + clipboard.add(IClipboard::kText, data); + clipboard.close(); + } + } +} + +void +CMSWindowsServerTaskBarReceiver::onStatusChanged() +{ + if (IsWindowVisible(m_window)) { + showStatus(); + } +} + +HICON +CMSWindowsServerTaskBarReceiver::loadIcon(UINT id) +{ + HANDLE icon = LoadImage(m_appInstance, + MAKEINTRESOURCE(id), + IMAGE_ICON, + 0, 0, + LR_DEFAULTCOLOR); + return reinterpret_cast(icon); +} + +void +CMSWindowsServerTaskBarReceiver::deleteIcon(HICON icon) +{ + if (icon != NULL) { + DestroyIcon(icon); + } +} + +void +CMSWindowsServerTaskBarReceiver::createWindow() +{ + // ignore if already created + if (m_window != NULL) { + return; + } + + // get the status dialog + m_window = CreateDialogParam(m_appInstance, + MAKEINTRESOURCE(IDD_TASKBAR_STATUS), + NULL, + &CMSWindowsServerTaskBarReceiver::staticDlgProc, + reinterpret_cast( + reinterpret_cast(this))); + + // window should appear on top of everything, including (especially) + // the task bar. + DWORD style = GetWindowLong(m_window, GWL_EXSTYLE); + style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; + SetWindowLong(m_window, GWL_EXSTYLE, style); + + // tell the task bar about this dialog + CArchTaskBarWindows::addDialog(m_window); +} + +void +CMSWindowsServerTaskBarReceiver::destroyWindow() +{ + if (m_window != NULL) { + CArchTaskBarWindows::removeDialog(m_window); + DestroyWindow(m_window); + m_window = NULL; + } +} + +BOOL +CMSWindowsServerTaskBarReceiver::dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM) +{ + switch (msg) { + case WM_INITDIALOG: + // use default focus + return TRUE; + + case WM_ACTIVATE: + // hide when another window is activated + if (LOWORD(wParam) == WA_INACTIVE) { + ShowWindow(hwnd, SW_HIDE); + } + break; + } + return FALSE; +} + +BOOL CALLBACK +CMSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + // if msg is WM_INITDIALOG, extract the CMSWindowsServerTaskBarReceiver* + // and put it in the extra window data then forward the call. + CMSWindowsServerTaskBarReceiver* self = NULL; + if (msg == WM_INITDIALOG) { + self = reinterpret_cast( + reinterpret_cast(lParam)); + SetWindowLong(hwnd, GWL_USERDATA, lParam); + } + else { + // get the extra window data and forward the call + LONG data = GetWindowLong(hwnd, GWL_USERDATA); + if (data != 0) { + self = reinterpret_cast( + reinterpret_cast(data)); + } + } + + // forward the message + if (self != NULL) { + return self->dlgProc(hwnd, msg, wParam, lParam); + } + else { + return (msg == WM_INITDIALOG) ? TRUE : FALSE; + } +} diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h new file mode 100644 index 0000000..d1c3dc6 --- /dev/null +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h @@ -0,0 +1,63 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSSERVERTASKBARRECEIVER_H +#define CMSWINDOWSSERVERTASKBARRECEIVER_H + +#define WIN32_LEAN_AND_MEAN + +#include "CServerTaskBarReceiver.h" +#include + +class CBufferedLogOutputter; + +//! Implementation of CServerTaskBarReceiver for Microsoft Windows +class CMSWindowsServerTaskBarReceiver : public CServerTaskBarReceiver { +public: + CMSWindowsServerTaskBarReceiver(HINSTANCE, const CBufferedLogOutputter*); + virtual ~CMSWindowsServerTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + void copyLog() const; + + // CServerTaskBarReceiver overrides + virtual void onStatusChanged(); + +private: + HICON loadIcon(UINT); + void deleteIcon(HICON); + void createWindow(); + void destroyWindow(); + + BOOL dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + static BOOL CALLBACK + staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + +private: + HINSTANCE m_appInstance; + HWND m_window; + HMENU m_menu; + HICON m_icon[kMaxState]; + const CBufferedLogOutputter* m_logBuffer; +}; + +#endif diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/cmd/synergys/CServerTaskBarReceiver.cpp new file mode 100644 index 0000000..8349a13 --- /dev/null +++ b/cmd/synergys/CServerTaskBarReceiver.cpp @@ -0,0 +1,171 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CServerTaskBarReceiver.h" +#include "CServer.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CArch.h" + +// +// CServerTaskBarReceiver +// + +CServerTaskBarReceiver::CServerTaskBarReceiver() : + m_quit(NULL), + m_state(kNotRunning), + m_server(NULL) +{ + // create a job for getting notification when the server's + // status changes. + m_job = new TMethodJob(this, + &CServerTaskBarReceiver::statusChanged, NULL); +} + +CServerTaskBarReceiver::~CServerTaskBarReceiver() +{ + if (m_server != NULL) { + m_server->removeStatusJob(m_job); + } + delete m_job; + delete m_quit; +} + +void +CServerTaskBarReceiver::setServer(CServer* server) +{ + { + CLock lock(&m_mutex); + if (m_server != server) { + if (m_server != NULL) { + m_server->removeStatusJob(m_job); + } + m_server = server; + if (m_server != NULL) { + m_server->addStatusJob(m_job); + } + } + } + ARCH->updateReceiver(this); +} + +void +CServerTaskBarReceiver::setState(EState state) +{ + { + CLock lock(&m_mutex); + m_state = state; + } + ARCH->updateReceiver(this); +} + +void +CServerTaskBarReceiver::setQuitJob(IJob* job) +{ + CLock lock(&m_mutex); + delete m_quit; + m_quit = job; +} + +CServerTaskBarReceiver::EState +CServerTaskBarReceiver::getState() const +{ + return m_state; +} + +CServer* +CServerTaskBarReceiver::getServer() const +{ + return m_server; +} + +void +CServerTaskBarReceiver::lock() const +{ + m_mutex.lock(); +} + +void +CServerTaskBarReceiver::unlock() const +{ + m_mutex.unlock(); +} + +std::string +CServerTaskBarReceiver::getToolTip() const +{ + switch (m_state) { + case kNotRunning: + return "Synergy: Not running"; + + case kNotWorking: + return CString("Synergy: ") + m_errorMessage; + + case kNotConnected: + return "Synergy: Waiting for clients"; + + case kConnected: + return "Synergy: Connected"; + + default: + return ""; + } +} + +void +CServerTaskBarReceiver::quit() +{ + if (m_quit != NULL) { + m_quit->run(); + } +} + +void +CServerTaskBarReceiver::onStatusChanged() +{ + // do nothing +} + +void +CServerTaskBarReceiver::statusChanged(void*) +{ + // update our status + switch (m_server->getStatus(&m_errorMessage)) { + case CServer::kNotRunning: + setState(kNotRunning); + break; + + case CServer::kRunning: + if (m_server->getNumClients() > 1) + setState(kConnected); + else + setState(kNotConnected); + break; + + case CServer::kServerNameUnknown: + m_errorMessage = "Server name is not in configuration"; + setState(kNotWorking); + break; + + case CServer::kError: + setState(kNotWorking); + break; + + default: + break; + } + + // let subclasses have a go + onStatusChanged(); +} diff --git a/cmd/synergys/CServerTaskBarReceiver.h b/cmd/synergys/CServerTaskBarReceiver.h new file mode 100644 index 0000000..0591a09 --- /dev/null +++ b/cmd/synergys/CServerTaskBarReceiver.h @@ -0,0 +1,110 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSERVERTASKBARRECEIVER_H +#define CSERVERTASKBARRECEIVER_H + +#include "CMutex.h" +#include "CString.h" +#include "IArchTaskBarReceiver.h" + +class CServer; +class IJob; + +//! Implementation of IArchTaskBarReceiver for the synergy server +class CServerTaskBarReceiver : public IArchTaskBarReceiver { +public: + enum EState { + kNotRunning, + kNotWorking, + kNotConnected, + kConnected, + kMaxState + }; + + CServerTaskBarReceiver(); + virtual ~CServerTaskBarReceiver(); + + //! @name manipulators + //@{ + + //! Set server + /*! + Sets the server. The receiver will query state from this server. + */ + void setServer(CServer*); + + //! Set state + /*! + Sets the current server state. + */ + void setState(EState); + + //! Set the quit job that causes the server to quit + /*! + Set the job that causes the server to quit. + */ + void setQuitJob(IJob* adopted); + + //@} + //! @name accessors + //@{ + + //! Get state + /*! + Returns the current server state. The receiver is not locked + by this call; the caller must do the locking. + */ + EState getState() const; + + //! Get server + /*! + Returns the server set by \c setServer(). + */ + CServer* getServer() const; + + //@} + + // IArchTaskBarReceiver overrides + virtual void showStatus() = 0; + virtual void runMenu(int x, int y) = 0; + virtual void primaryAction() = 0; + virtual void lock() const; + virtual void unlock() const; + virtual const Icon getIcon() const = 0; + virtual std::string getToolTip() const; + +protected: + void quit(); + + //! Status change notification + /*! + Called when status changes. The default implementation does + nothing. + */ + virtual void onStatusChanged(); + +private: + void statusChanged(void*); + +private: + CMutex m_mutex; + IJob* m_quit; + EState m_state; + CServer* m_server; + IJob* m_job; + CString m_errorMessage; +}; + +#endif diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp new file mode 100644 index 0000000..2011873 --- /dev/null +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsServerTaskBarReceiver.h" +#include "CArch.h" + +// +// CXWindowsServerTaskBarReceiver +// + +CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver() +{ + // add ourself to the task bar + ARCH->addReceiver(this); +} + +CXWindowsServerTaskBarReceiver::~CXWindowsServerTaskBarReceiver() +{ + ARCH->removeReceiver(this); +} + +void +CXWindowsServerTaskBarReceiver::showStatus() +{ + // do nothing +} + +void +CXWindowsServerTaskBarReceiver::runMenu(int, int) +{ + // do nothing +} + +void +CXWindowsServerTaskBarReceiver::primaryAction() +{ + // do nothing +} + +const IArchTaskBarReceiver::Icon +CXWindowsServerTaskBarReceiver::getIcon() const +{ + return NULL; +} + +void +CXWindowsServerTaskBarReceiver::onStatusChanged() +{ + // do nothing +} diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.h b/cmd/synergys/CXWindowsServerTaskBarReceiver.h new file mode 100644 index 0000000..a05a430 --- /dev/null +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.h @@ -0,0 +1,37 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSSERVERTASKBARRECEIVER_H +#define CXWINDOWSSERVERTASKBARRECEIVER_H + +#include "CServerTaskBarReceiver.h" + +//! Implementation of CServerTaskBarReceiver for X Windows +class CXWindowsServerTaskBarReceiver : public CServerTaskBarReceiver { +public: + CXWindowsServerTaskBarReceiver(); + virtual ~CXWindowsServerTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + // CServerTaskBarReceiver overrides + virtual void onStatusChanged(); +}; + +#endif diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am new file mode 100644 index 0000000..b3384ce --- /dev/null +++ b/cmd/synergys/Makefile.am @@ -0,0 +1,71 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CMSWindowsServerTaskBarReceiver.cpp \ + CMSWindowsServerTaskBarReceiver.h \ + resource.h \ + synergys.ico \ + synergys.dsp \ + synergys.rc \ + tb_error.ico \ + tb_idle.ico \ + tb_run.ico \ + tb_wait.ico \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +bin_PROGRAMS = synergys +synergys_SOURCES = \ + CServerTaskBarReceiver.cpp \ + CServerTaskBarReceiver.h \ + CXWindowsServerTaskBarReceiver.cpp \ + CXWindowsServerTaskBarReceiver.h \ + synergys.cpp \ + $(NULL) +synergys_LDADD = \ + $(DEPTH)/lib/server/libserver.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a \ + $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/http/libhttp.a \ + $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/http \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + -I$(VDEPTH)/lib/server \ + $(NULL) diff --git a/cmd/synergys/Makefile.in b/cmd/synergys/Makefile.in new file mode 100644 index 0000000..7674cc6 --- /dev/null +++ b/cmd/synergys/Makefile.in @@ -0,0 +1,400 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CMSWindowsServerTaskBarReceiver.cpp \ + CMSWindowsServerTaskBarReceiver.h \ + resource.h \ + synergys.ico \ + synergys.dsp \ + synergys.rc \ + tb_error.ico \ + tb_idle.ico \ + tb_run.ico \ + tb_wait.ico \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +bin_PROGRAMS = synergys +synergys_SOURCES = \ + CServerTaskBarReceiver.cpp \ + CServerTaskBarReceiver.h \ + CXWindowsServerTaskBarReceiver.cpp \ + CXWindowsServerTaskBarReceiver.h \ + synergys.cpp \ + $(NULL) + +synergys_LDADD = \ + $(DEPTH)/lib/server/libserver.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a \ + $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/http/libhttp.a \ + $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/http \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + -I$(VDEPTH)/lib/server \ + $(NULL) + +subdir = cmd/synergys +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +bin_PROGRAMS = synergys$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) + +am_synergys_OBJECTS = CServerTaskBarReceiver.$(OBJEXT) \ + CXWindowsServerTaskBarReceiver.$(OBJEXT) synergys.$(OBJEXT) +synergys_OBJECTS = $(am_synergys_OBJECTS) +synergys_DEPENDENCIES = $(DEPTH)/lib/server/libserver.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/http/libhttp.a $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a +synergys_LDFLAGS = + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CServerTaskBarReceiver.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsServerTaskBarReceiver.Po \ +@AMDEP_TRUE@ $(DEPDIR)/synergys.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(synergys_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(synergys_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu cmd/synergys/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo $$p1|sed '$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$f"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$f; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f $(DESTDIR)$(bindir)/$$f"; \ + rm -f $(DESTDIR)$(bindir)/$$f; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +synergys$(EXEEXT): $(synergys_OBJECTS) $(synergys_DEPENDENCIES) + @rm -f synergys$(EXEEXT) + $(CXXLINK) $(synergys_LDFLAGS) $(synergys_OBJECTS) $(synergys_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CServerTaskBarReceiver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsServerTaskBarReceiver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/synergys.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(bindir) + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic distclean distclean-compile distclean-depend \ + distclean-generic distclean-tags distdir dvi dvi-am info \ + info-am install install-am install-binPROGRAMS install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h new file mode 100644 index 0000000..5ac2470 --- /dev/null +++ b/cmd/synergys/resource.h @@ -0,0 +1,28 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by synergys.rc +// +#define IDS_FAILED 1 +#define IDI_SYNERGY 101 +#define IDI_TASKBAR_NOT_RUNNING 102 +#define IDI_TASKBAR_NOT_WORKING 103 +#define IDI_TASKBAR_NOT_CONNECTED 104 +#define IDI_TASKBAR_CONNECTED 105 +#define IDR_TASKBAR 107 +#define IDD_TASKBAR_STATUS 108 +#define IDC_TASKBAR_STATUS_STATUS 1000 +#define IDC_TASKBAR_STATUS_CLIENTS 1001 +#define IDC_TASKBAR_QUIT 40003 +#define IDC_TASKBAR_STATUS 40004 +#define IDC_TASKBAR_LOG 40005 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 109 +#define _APS_NEXT_COMMAND_VALUE 40006 +#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp new file mode 100644 index 0000000..e3fb74d --- /dev/null +++ b/cmd/synergys/synergys.cpp @@ -0,0 +1,883 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CServer.h" +#include "CConfig.h" +#include "IPrimaryScreenFactory.h" +#include "ProtocolTypes.h" +#include "Version.h" +#include "XScreen.h" +#include "CTCPSocketFactory.h" +#include "XSocket.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "XThread.h" +#include "CFunctionJob.h" +#include "CLog.h" +#include "LogOutputters.h" +#include "CArch.h" +#include "stdfstream.h" +#include + +#define DAEMON_RUNNING(running_) +#if WINDOWS_LIKE +#include "CMSWindowsScreen.h" +#include "CMSWindowsPrimaryScreen.h" +#include "CArchMiscWindows.h" +#include "CMSWindowsServerTaskBarReceiver.h" +#include "resource.h" +#undef DAEMON_RUNNING +#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) +#elif UNIX_LIKE +#include "CXWindowsPrimaryScreen.h" +#include "CXWindowsServerTaskBarReceiver.h" +#endif + +// platform dependent name of a daemon +#if WINDOWS_LIKE +#define DAEMON_NAME "Synergy Server" +#elif UNIX_LIKE +#define DAEMON_NAME "synergys" +#endif + +// configuration file name +#if WINDOWS_LIKE +#define USR_CONFIG_NAME "synergy.sgc" +#define SYS_CONFIG_NAME "synergy.sgc" +#elif UNIX_LIKE +#define USR_CONFIG_NAME ".synergy.conf" +#define SYS_CONFIG_NAME "synergy.conf" +#endif + +// +// program arguments +// + +#define ARG CArgs::s_instance + +class CArgs { +public: + CArgs() : + m_pname(NULL), + m_backend(false), + m_restartable(true), + m_daemon(true), + m_configFile(NULL), + m_logFilter(NULL) + { s_instance = this; } + ~CArgs() { s_instance = NULL; } + +public: + static CArgs* s_instance; + const char* m_pname; + bool m_backend; + bool m_restartable; + bool m_daemon; + const char* m_configFile; + const char* m_logFilter; + CString m_name; + CNetworkAddress m_synergyAddress; + CNetworkAddress m_httpAddress; + CConfig m_config; +}; + +CArgs* CArgs::s_instance = NULL; + + +// +// platform dependent factories +// + +//! Factory for creating primary screens +/*! +Objects of this type create primary screens appropriate for the +platform. +*/ +class CPrimaryScreenFactory : public IPrimaryScreenFactory { +public: + CPrimaryScreenFactory() { } + virtual ~CPrimaryScreenFactory() { } + + // IPrimaryScreenFactory overrides + virtual CPrimaryScreen* + create(IScreenReceiver*, IPrimaryScreenReceiver*); +}; + +CPrimaryScreen* +CPrimaryScreenFactory::create(IScreenReceiver* receiver, + IPrimaryScreenReceiver* primaryReceiver) +{ +#if WINDOWS_LIKE + return new CMSWindowsPrimaryScreen(receiver, primaryReceiver); +#elif UNIX_LIKE + return new CXWindowsPrimaryScreen(receiver, primaryReceiver); +#endif +} + + +//! CQuitJob +/*! +A job that cancels a given thread. +*/ +class CQuitJob : public IJob { +public: + CQuitJob(const CThread& thread); + ~CQuitJob(); + + // IJob overrides + virtual void run(); + +private: + CThread m_thread; +}; + +CQuitJob::CQuitJob(const CThread& thread) : + m_thread(thread) +{ + // do nothing +} + +CQuitJob::~CQuitJob() +{ + // do nothing +} + +void +CQuitJob::run() +{ + m_thread.cancel(); +} + + +// +// platform independent main +// + +static CServer* s_server = NULL; +static CServerTaskBarReceiver* s_taskBarReceiver = NULL; + +static +int +realMain(void) +{ + int result = kExitSuccess; + do { + bool opened = false; + bool locked = true; + try { + // if configuration has no screens then add this system + // as the default + if (ARG->m_config.begin() == ARG->m_config.end()) { + ARG->m_config.addScreen(ARG->m_name); + } + + // set the contact address, if provided, in the config. + // otherwise, if the config doesn't have an address, use + // the default. + if (ARG->m_synergyAddress.isValid()) { + ARG->m_config.setSynergyAddress(ARG->m_synergyAddress); + } + else if (!ARG->m_config.getSynergyAddress().isValid()) { + ARG->m_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); + } + + // set HTTP address if provided + if (ARG->m_httpAddress.isValid()) { + ARG->m_config.setHTTPAddress(ARG->m_httpAddress); + } + + // create server + s_server = new CServer(ARG->m_name); + s_server->setConfig(ARG->m_config); + s_server->setScreenFactory(new CPrimaryScreenFactory); + s_server->setSocketFactory(new CTCPSocketFactory); + s_server->setStreamFilterFactory(NULL); + + // open server + try { + s_taskBarReceiver->setServer(s_server); + s_server->open(); + opened = true; + + // run server + DAEMON_RUNNING(true); + locked = false; + s_server->mainLoop(); + + // clean up +#define FINALLY do { \ + if (!locked) { \ + DAEMON_RUNNING(false); \ + locked = true; \ + } \ + if (opened) { \ + s_server->close(); \ + } \ + s_taskBarReceiver->setServer(NULL); \ + delete s_server; \ + s_server = NULL; \ + } while (false) + FINALLY; + } + catch (XScreenUnavailable& e) { + // wait before retrying if we're going to retry + if (ARG->m_restartable) { + ARCH->sleep(e.getRetryTime()); + } + else { + result = kExitFailed; + } + FINALLY; + } + catch (XThread&) { + FINALLY; + throw; + } + catch (...) { + // don't try to restart and fail + ARG->m_restartable = false; + result = kExitFailed; + FINALLY; + } +#undef FINALLY + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed: %s", e.what())); + } + catch (XThread&) { + // terminated + ARG->m_restartable = false; + result = kExitTerminated; + } + } while (ARG->m_restartable); + + return result; +} + +static +void +realMainEntry(void* vresult) +{ + *reinterpret_cast(vresult) = realMain(); +} + +static +int +runMainInThread(void) +{ + int result = 0; + CThread appThread(new CFunctionJob(&realMainEntry, &result)); + try { +#if WINDOWS_LIKE + MSG msg; + while (appThread.waitForEvent(-1.0) == CThread::kEvent) { + // check for a quit event + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + CThread::getCurrentThread().cancel(); + } + } + } +#else + appThread.wait(-1.0); +#endif + return result; + } + catch (XThread&) { + appThread.cancel(); + appThread.wait(-1.0); + throw; + } +} + + +// +// command line parsing +// + +#define BYE "\nTry `%s --help' for more information." + +static void (*bye)(int) = &exit; + +static +void +version() +{ + LOG((CLOG_PRINT +"%s %s, protocol version %d.%d\n" +"%s", + ARG->m_pname, + kVersion, + kProtocolMajorVersion, + kProtocolMinorVersion, + kCopyright)); +} + +static +void +help() +{ +#if WINDOWS_LIKE + +# define PLATFORM_ARGS \ +" {--daemon|--no-daemon}" +# define PLATFORM_DESC +# define PLATFORM_EXTRA \ +"At least one command line argument is required. If you don't otherwise\n" \ +"need an argument use `--daemon'.\n" \ +"\n" + +#else + +# define PLATFORM_ARGS \ +" [--daemon|--no-daemon]" +# define PLATFORM_DESC +# define PLATFORM_EXTRA + +#endif + + LOG((CLOG_PRINT +"Usage: %s" +" [--address
]" +" [--config ]" +" [--debug ]" +" [--name ]" +" [--restart|--no-restart]\n" +PLATFORM_ARGS +"\n" +"Start the synergy mouse/keyboard sharing server.\n" +"\n" +" -a, --address
listen for clients on the given address.\n" +" -c, --config use the named configuration file instead.\n" +" -d, --debug filter out log messages with priorty below level.\n" +" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" +" DEBUG, DEBUG1, DEBUG2.\n" +" -f, --no-daemon run the server in the foreground.\n" +"* --daemon run the server as a daemon.\n" +" -n, --name use screen-name instead the hostname to identify\n" +" this screen in the configuration.\n" +" -1, --no-restart do not try to restart the server if it fails for\n" +" some reason.\n" +"* --restart restart the server automatically if it fails.\n" +PLATFORM_DESC +" -h, --help display this help and exit.\n" +" --version display version information and exit.\n" +"\n" +"* marks defaults.\n" +"\n" +PLATFORM_EXTRA +"The argument for --address is of the form: [][:]. The\n" +"hostname must be the address or hostname of an interface on the system.\n" +"The default is to listen on all interfaces. The port overrides the\n" +"default port, %d.\n" +"\n" +"If no configuration file pathname is provided then the first of the\n" +"following to load successfully sets the configuration:\n" +" %s\n" +" %s\n" +"If no configuration file can be loaded then the configuration uses its\n" +"defaults with just the server screen.\n" +"\n" +"Where log messages go depends on the platform and whether or not the\n" +"server is running as a daemon.", + ARG->m_pname, + kDefaultPort, + ARCH->concatPath( + ARCH->getUserDirectory(), + USR_CONFIG_NAME).c_str(), + ARCH->concatPath( + ARCH->getSystemDirectory(), + SYS_CONFIG_NAME).c_str())); +} + +static +bool +isArg(int argi, int argc, const char* const* argv, + const char* name1, const char* name2, + int minRequiredParameters = 0) +{ + if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || + (name2 != NULL && strcmp(argv[argi], name2) == 0)) { + // match. check args left. + if (argi + minRequiredParameters >= argc) { + LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, + ARG->m_pname, argv[argi], ARG->m_pname)); + bye(kExitArgs); + } + return true; + } + + // no match + return false; +} + +static +void +parse(int argc, const char* const* argv) +{ + assert(ARG->m_pname != NULL); + assert(argv != NULL); + assert(argc >= 1); + + // set defaults + ARG->m_name = ARCH->getHostName(); + + // parse options + int i; + for (i = 1; i < argc; ++i) { + if (isArg(i, argc, argv, "-d", "--debug", 1)) { + // change logging level + ARG->m_logFilter = argv[++i]; + } + + else if (isArg(i, argc, argv, "-a", "--address", 1)) { + // save listen address + try { + ARG->m_synergyAddress = CNetworkAddress(argv[i + 1], + kDefaultPort); + } + catch (XSocketAddress& e) { + LOG((CLOG_PRINT "%s: %s" BYE, + ARG->m_pname, e.what(), ARG->m_pname)); + bye(kExitArgs); + } + ++i; + } + + else if (isArg(i, argc, argv, NULL, "--http", 1)) { + // save listen address + try { + ARG->m_httpAddress = CNetworkAddress(argv[i + 1], + kDefaultPort + 1); + } + catch (XSocketAddress& e) { + LOG((CLOG_PRINT "%s: %s" BYE, + ARG->m_pname, e.what(), ARG->m_pname)); + bye(kExitArgs); + } + ++i; + } + + else if (isArg(i, argc, argv, "-n", "--name", 1)) { + // save screen name + ARG->m_name = argv[++i]; + } + + else if (isArg(i, argc, argv, "-c", "--config", 1)) { + // save configuration file path + ARG->m_configFile = argv[++i]; + } + + else if (isArg(i, argc, argv, "-f", "--no-daemon")) { + // not a daemon + ARG->m_daemon = false; + } + + else if (isArg(i, argc, argv, NULL, "--daemon")) { + // daemonize + ARG->m_daemon = true; + } + + else if (isArg(i, argc, argv, "-1", "--no-restart")) { + // don't try to restart + ARG->m_restartable = false; + } + + else if (isArg(i, argc, argv, NULL, "--restart")) { + // try to restart + ARG->m_restartable = true; + } + + else if (isArg(i, argc, argv, "-z", NULL)) { + ARG->m_backend = true; + } + + else if (isArg(i, argc, argv, "-h", "--help")) { + help(); + bye(kExitSuccess); + } + + else if (isArg(i, argc, argv, NULL, "--version")) { + version(); + bye(kExitSuccess); + } + + else if (isArg(i, argc, argv, "--", NULL)) { + // remaining arguments are not options + ++i; + break; + } + + else if (argv[i][0] == '-') { + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + ARG->m_pname, argv[i], ARG->m_pname)); + bye(kExitArgs); + } + + else { + // this and remaining arguments are not options + break; + } + } + + // no non-option arguments are allowed + if (i != argc) { + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + ARG->m_pname, argv[i], ARG->m_pname)); + bye(kExitArgs); + } + + // increase default filter level for daemon. the user must + // explicitly request another level for a daemon. + if (ARG->m_daemon && ARG->m_logFilter == NULL) { +#if WINDOWS_LIKE + if (CArchMiscWindows::isWindows95Family()) { + // windows 95 has no place for logging so avoid showing + // the log console window. + ARG->m_logFilter = "FATAL"; + } + else +#endif + { + ARG->m_logFilter = "NOTE"; + } + } + + // set log filter + if (!CLOG->setFilter(ARG->m_logFilter)) { + LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, + ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); + bye(kExitArgs); + } +} + +static +bool +loadConfig(const char* pathname, bool require) +{ + assert(pathname != NULL); + + try { + // load configuration + LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname)); + std::ifstream configStream(pathname); + if (!configStream) { + throw XConfigRead("cannot open file"); + } + configStream >> ARG->m_config; + LOG((CLOG_DEBUG "configuration read successfully")); + return true; + } + catch (XConfigRead& e) { + if (require) { + LOG((CLOG_PRINT "%s: cannot read configuration '%s': %s", + ARG->m_pname, pathname, e.what())); + bye(kExitConfig); + } + else { + LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s", + pathname, e.what())); + } + } + return false; +} + +static +void +loadConfig() +{ + // load the config file, if specified + if (ARG->m_configFile != NULL) { + // require the user specified file to load correctly + loadConfig(ARG->m_configFile, true); + } + + // load the default configuration if no explicit file given + else { + // get the user's home directory. use the effective user id + // so a user can't get a setuid root program to load his file. + bool loaded = false; + CString path = ARCH->getUserDirectory(); + if (!path.empty()) { + // complete path + path = ARCH->concatPath(path, USR_CONFIG_NAME); + + // now try loading the user's configuration + loaded = loadConfig(path.c_str(), false); + } + if (!loaded) { + // try the system-wide config file + path = ARCH->getSystemDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, SYS_CONFIG_NAME); + loadConfig(path.c_str(), false); + } + } + } +} + + +// +// platform dependent entry points +// + +#if WINDOWS_LIKE + +static bool s_hasImportantLogMessages = false; + +// +// CMessageBoxOutputter +// +// This class writes severe log messages to a message box +// + +class CMessageBoxOutputter : public ILogOutputter { +public: + CMessageBoxOutputter() { } + virtual ~CMessageBoxOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } +}; + +bool +CMessageBoxOutputter::write(ELevel level, const char* message) +{ + // note any important messages the user may need to know about + if (level <= CLog::kWARNING) { + s_hasImportantLogMessages = true; + } + + // FATAL and PRINT messages get a dialog box if not running as + // backend. if we're running as a backend the user will have + // a chance to see the messages when we exit. + if (!ARG->m_backend && level <= CLog::kFATAL) { + MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING); + return false; + } + else { + return true; + } +} + +static +void +byeThrow(int x) +{ + CArchMiscWindows::daemonFailed(x); +} + +static +int +daemonStartup(int argc, const char** argv) +{ + CSystemLogger sysLogger(DAEMON_NAME); + + // have to cancel this thread to quit + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + // catch errors that would normally exit + bye = &byeThrow; + + // parse command line + parse(argc, argv); + + // cannot run as backend if running as a service + ARG->m_backend = false; + + // load configuration + loadConfig(); + + // run as a service + return CArchMiscWindows::runDaemon(realMain); +} + +static +int +daemonStartup95(int, const char**) +{ + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); +} + +static +int +run(int argc, char** argv) +{ + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + + // parse command line + parse(argc, argv); + + // load configuration + loadConfig(); + + // daemonize if requested + if (ARG->m_daemon) { + // start as a daemon + if (CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup95); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + else { + // cannot start a service from the command line so just + // run normally (except with log messages redirected). + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); + } + } + else { + // run + return runMainInThread(); + } +} + +int WINAPI +WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) +{ + CArch arch(instance); + CLOG; + CArgs args; + + // save instance + CMSWindowsScreen::init(instance); + + // get program name + ARG->m_pname = ARCH->getBasename(__argv[0]); + + // send PRINT and FATAL output to a message box + CLOG->insert(new CMessageBoxOutputter); + + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance, + &logBuffer); + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + int result; + try { + // run in foreground or as a daemon + result = run(__argc, __argv); + } + catch (...) { + // note that we don't rethrow thread cancellation. we'll + // be exiting soon so it doesn't matter. what we'd like + // is for everything after this try/catch to be in a + // finally block. + result = kExitFailed; + } + + // done with task bar receiver + delete s_taskBarReceiver; + + // done with log buffer + CLOG->remove(&logBuffer); + + // let user examine any messages if we're running as a backend + // by putting up a dialog box before exiting. + if (ARG->m_backend && s_hasImportantLogMessages) { + char msg[1024]; + msg[0] = '\0'; + LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); + MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); + } + + delete CLOG; + return result; +} + +#elif UNIX_LIKE + +static +int +daemonStartup(int, const char**) +{ + CSystemLogger sysLogger(DAEMON_NAME); + return realMain(); +} + +int +main(int argc, char** argv) +{ + CArch arch; + CLOG; + CArgs args; + + // get program name + ARG->m_pname = ARCH->getBasename(argv[0]); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CXWindowsServerTaskBarReceiver; + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + // parse command line + parse(argc, argv); + + // load configuration + loadConfig(); + + // daemonize if requested + int result; + if (ARG->m_daemon) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon&) { + LOG((CLOG_CRIT "failed to daemonize")); + result = kExitFailed; + } + } + else { + result = realMain(); + } + + // done with task bar receiver + delete s_taskBarReceiver; + + return result; +} + +#else + +#error no main() for platform + +#endif diff --git a/cmd/synergys/synergys.dsp b/cmd/synergys/synergys.dsp new file mode 100644 index 0000000..a7ad81d --- /dev/null +++ b/cmd/synergys/synergys.dsp @@ -0,0 +1,153 @@ +# Microsoft Developer Studio Project File - Name="synergys" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=synergys - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "synergys.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "synergys.mak" CFG="synergys - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synergys - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "synergys - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synergys - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "synergys - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "synergys - Win32 Release" +# Name "synergys - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CMSWindowsServerTaskBarReceiver.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerTaskBarReceiver.cpp +# End Source File +# Begin Source File + +SOURCE=.\synergys.cpp +# End Source File +# Begin Source File + +SOURCE=.\synergys.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CMSWindowsServerTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\CServerTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\synergys.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_error.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_idle.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_run.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_wait.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/cmd/synergys/synergys.ico b/cmd/synergys/synergys.ico new file mode 100644 index 0000000000000000000000000000000000000000..23d9a0906462ab9c0d97b7493376c20036ea471e GIT binary patch literal 1078 zcmcIjF>ZrE5FC6+xQh8fs3Mn0+fY(a_&Id6Ct)f3ES};rr3*_b$?RURz;Y8=8GCnU z*gZJTV<5v0RaL_9wF5p_%QER>2D}B}$ZHj&Wn{>ifymkCh-|VGV=By~n5K!<8nb^f z&&C-1*da;TyJ!;jP^Q6N@Bgk5}L6UBBn>K0sicl|7 z>c~aHzIP$D4S{V|pr=T(rQ-7!*>yJK8teUVc(XssjjkWGz2AEMUHkF#ec5+9I81*g z>e-HOCD}%iv@d!g`Jq1~9Im|o1qc%Zpk4onNYgUHDh}lDlX-3f^Lfdr7>my*^5=;3 zI6L&T7iQ9ze6GqH(2HHmL*4TyYI|+Gsx*5&cPi41(wy8OavnuT5$#UelluIqoTrDl j{)%DX{(iHu1?m5W`_UE`Ag|c9SRp63>%}F_F6({(d|mCB literal 0 HcmV?d00001 diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc new file mode 100644 index 0000000..9f19075 --- /dev/null +++ b/cmd/synergys/synergys.rc @@ -0,0 +1,121 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergys.ico" +IDI_TASKBAR_NOT_RUNNING ICON DISCARDABLE "tb_idle.ico" +IDI_TASKBAR_NOT_WORKING ICON DISCARDABLE "tb_error.ico" +IDI_TASKBAR_NOT_CONNECTED ICON DISCARDABLE "tb_wait.ico" +IDI_TASKBAR_CONNECTED ICON DISCARDABLE "tb_run.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_TASKBAR MENU DISCARDABLE +BEGIN + POPUP "Synergy" + BEGIN + MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG + MENUITEM SEPARATOR + MENUITEM "Quit", IDC_TASKBAR_QUIT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_TASKBAR_STATUS DIALOG DISCARDABLE 0, 0, 145, 60 +STYLE DS_MODALFRAME | WS_POPUP +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + LISTBOX IDC_TASKBAR_STATUS_CLIENTS,3,17,139,40,NOT LBS_NOTIFY | + LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_VSCROLL | + WS_TABSTOP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/cmd/synergys/tb_error.ico b/cmd/synergys/tb_error.ico new file mode 100644 index 0000000000000000000000000000000000000000..a304dc1815fa0ababb136f6f1ba13735d2fbb5a6 GIT binary patch literal 318 zcmaKmI}U?D3`8Fa1(GSKQd6eP(da0%M{>W753q;H0Z_=8bSPr%nek^=GIWY*nn*l4 zxTlLbnZ=G?3UTN^DWIg3pGj2`5%OH@N3!o(iHaPhoKX)@YIUo1XqN6xF32N$#zwk% rh~{<7+ME_FZfttD==auOF(2NnD_ToW$P?mr8eX*h4C50%ue=|=dPPd3 literal 0 HcmV?d00001 diff --git a/cmd/synergys/tb_idle.ico b/cmd/synergys/tb_idle.ico new file mode 100644 index 0000000000000000000000000000000000000000..9ba599de07841a8899275e54b91383ebdba39fb1 GIT binary patch literal 318 zcmZurAr8YZ5OcLkQ#4XEy23qgEVQs=fwj_)L?NM-@}RNK*GMJXU2NYux?F%xISd0V zo*i&E70elh4!syC2mhl9(7`k{5vxQgMQRuOaj@@J2}-k!&TwR~h6-SQi@40yOtNl# zA(87AkhwT%PDBb@%#1m$E;{BcpK8Iik~cV3LGuCL+q>RFM%&XC+ZR5MR$mHmRoS=p Mmq%Hp**0UdUu;rNsQ>@~ literal 0 HcmV?d00001 diff --git a/cmd/synergys/tb_run.ico b/cmd/synergys/tb_run.ico new file mode 100644 index 0000000000000000000000000000000000000000..86aa9f3ac989981768b950663e06469635252f8e GIT binary patch literal 318 zcmZ{fJqp7x428ddp@9UNsdL62FI!fRK2CNoI)D$6Bjf;u^vNdBp^^N4kR@b8r<|q< zk9P+zSHUZ@&=JI7mH$x$9b#-Emc`7ZesR{rdAbvnUUIsLlp^#g69w)PJ`2R?tWuD+=8@5=TLcpmWBg&BfSKQ6f<$ap>Mt@oPy0w4@W Ai2wiq literal 0 HcmV?d00001 diff --git a/cmd/synergys/tb_wait.ico b/cmd/synergys/tb_wait.ico new file mode 100644 index 0000000000000000000000000000000000000000..e94db3c30602626ad420b8a3a37d1bc0750baff8 GIT binary patch literal 318 zcmZ{fJr08~423^Lq_UN%Gh;_2Ux5R4W6hXj`2ZY(BQSKQ&xw#aw07Q)99vGHQ;y?E z#Iu7(s1TJ&=tyGl%D*Us4mmdw%i`|RxH#+KJi`gfCo? zvsu=yM(T~;jKz7$*8V=`Am$I&H5N7gUfJFO_XBRbFhLUP$0b)JnW(3|^ header file. */ +#undef HAVE_INTTYPES_H + +/* Define if you have the header file. */ +#undef HAVE_ISTREAM + +/* Define if you have the `mbrtowc' function. */ +#undef HAVE_MBRTOWC + +/* Define if you have the `mbsinit' function. */ +#undef HAVE_MBSINIT + +/* Define if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have the `nanosleep\' function. */ +#undef HAVE_NANOSLEEP + +/* Define if you have the header file. */ +#undef HAVE_OSTREAM + +/* Define if you have the `poll\' function. */ +#undef HAVE_POLL + +/* Define if you have a POSIX `sigwait\' function. */ +#undef HAVE_POSIX_SIGWAIT + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Define if you have `pthread_sigmask\' and `pthread_kill\' functions. */ +#undef HAVE_PTHREAD_SIGNAL + +/* Define if your compiler defines socklen_t. */ +#undef HAVE_SOCKLEN_T + +/* Define if you have the header file. */ +#undef HAVE_SSTREAM + +/* Define if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define if you have the header file. */ +#undef HAVE_STRING_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define if you have the header file. */ +#undef HAVE_WCHAR_H + +/* Define if you have the `wcrtomb' function. */ +#undef HAVE_WCRTOMB + +/* Define if you have the header file. */ +#undef HAVE_X11_EXTENSIONS_XINERAMA_H + +/* Define if you have the header file. */ +#undef HAVE_X11_EXTENSIONS_XTEST_H + +/* Define if you have the header file. */ +#undef HAVE_X11_XF86KEYSYM_H + +/* Name of package */ +#undef PACKAGE + +/* Define to the necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* Define to the type of arg 1 for `select'. */ +#undef SELECT_TYPE_ARG1 + +/* Define to the type of args 2, 3 and 4 for `select'. */ +#undef SELECT_TYPE_ARG234 + +/* Define to the type of arg 5 for `select'. */ +#undef SELECT_TYPE_ARG5 + +/* The size of a `char', as computed by sizeof. */ +#undef SIZEOF_CHAR + +/* The size of a `int', as computed by sizeof. */ +#undef SIZEOF_INT + +/* The size of a `long', as computed by sizeof. */ +#undef SIZEOF_LONG + +/* The size of a `short', as computed by sizeof. */ +#undef SIZEOF_SHORT + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* Version number of package */ +#undef VERSION + +/* Define if the X Window System is missing or not being used. */ +#undef X_DISPLAY_MISSING + +/* Define to `unsigned' if does not define. */ +#undef size_t diff --git a/config/config.guess b/config/config.guess new file mode 100644 index 0000000..6ead80a --- /dev/null +++ b/config/config.guess @@ -0,0 +1,1327 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-08-21' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Written by Per Bothner . +# Please send patches to . +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + + +dummy=dummy-$$ +trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int dummy(){}" > $dummy.c ; + for c in cc gcc c89 ; do + ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ; + if test $? = 0 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + rm -f $dummy.c $dummy.o $dummy.rel ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_MACHINE}" in + i?86) + test -z "$VENDOR" && VENDOR=pc + ;; + *) + test -z "$VENDOR" && VENDOR=unknown + ;; +esac +test -f /etc/SuSE-release && VENDOR=suse + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # Netbsd (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # Determine the machine/vendor (is the vendor relevant). + case "${UNAME_MACHINE}" in + amiga) machine=m68k-unknown ;; + arm32) machine=arm-unknown ;; + atari*) machine=m68k-atari ;; + sun3*) machine=m68k-sun ;; + mac68k) machine=m68k-apple ;; + macppc) machine=powerpc-apple ;; + hp3[0-9][05]) machine=m68k-hp ;; + ibmrt|romp-ibm) machine=romp-ibm ;; + *) machine=${UNAME_MACHINE}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE}" in + i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + cat <$dummy.s + .data +\$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + + .text + .globl main + .align 4 + .ent main +main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + case `./$dummy` in + 0-0) + UNAME_MACHINE="alpha" + ;; + 1-0) + UNAME_MACHINE="alphaev5" + ;; + 1-1) + UNAME_MACHINE="alphaev56" + ;; + 1-101) + UNAME_MACHINE="alphapca56" + ;; + 2-303) + UNAME_MACHINE="alphaev6" + ;; + 2-307) + UNAME_MACHINE="alphaev67" + ;; + 2-1307) + UNAME_MACHINE="alphaev68" + ;; + esac + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + arc64:OpenBSD:*:*) + echo mips64el-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hkmips:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + sparc*:NetBSD:*) + echo `uname -p`-unknown-netbsd${UNAME_RELEASE} + exit 0 ;; + atari*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + sun3*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy \ + && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + case "${HPUX_REV}" in + 11.[0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + esac ;; + esac + fi ;; + esac + if [ "${HP_ARCH}" = "" ]; then + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + eval $set_cc_for_build + (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy` + if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi + rm -f $dummy.c $dummy + fi ;; + esac + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + hppa*:OpenBSD:*:*) + echo hppa-unknown-openbsd + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3D:*:*:*) + echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i386-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + mips:Linux:*:*) + case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in + big) echo mips-${VENDOR}-linux && exit 0 ;; + little) echo mipsel-${VENDOR}-linux && exit 0 ;; + esac + case `sed -n '/^system type/s/^.*: \([^ ]*\).*/\1/p' < /proc/cpuinfo` in + SGI|sgi) echo mips-${VENDOR}-linux-gnu && exit 0 ;; + esac + ;; + ppc:Linux:*:*|ppc64:Linux:*:*) + echo powerpc-${VENDOR}-linux + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-${VENDOR}-linux${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-${VENDOR}-linux ;; + PA8*) echo hppa2.0-${VENDOR}-linux ;; + *) echo hppa-${VENDOR}-linux ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-${VENDOR}-linux + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-${VENDOR}-linux + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + ld_supported_targets=`cd /; ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-${VENDOR}-linux" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-${VENDOR}-linuxaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-${VENDOR}-linuxcoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linuxoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-${VENDOR}-linuxoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + cat >$dummy.c < +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif +#ifdef __ELF__ +# ifdef __GLIBC__ +# if __GLIBC__ >= 2 + printf ("%s-${VENDOR}-linux\n", argv[1]); +# else + printf ("%s-${VENDOR}-linuxlibc1\n", argv[1]); +# endif +# else + printf ("%s-${VENDOR}-linuxlibc1\n", argv[1]); +# endif +#else + printf ("%s-${VENDOR}-linuxaout\n", argv[1]); +#endif + return 0; +} +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + echo `uname -p`-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + if test "${UNAME_MACHINE}" = "x86pc"; then + UNAME_MACHINE=pc + fi + echo `uname -p`-${UNAME_MACHINE}-nto-qnx + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[KW]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +eval $set_cc_for_build +$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0 +rm -f $dummy.c $dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config/config.sub b/config/config.sub new file mode 100644 index 0000000..83f4b01 --- /dev/null +++ b/config/config.sub @@ -0,0 +1,1410 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-08-13' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dsp16xx \ + | fr30 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | m32r | m68000 | m68k | m88k | mcore \ + | mips16 | mips64 | mips64el | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el | mips64vr4300 \ + | mips64vr4300el | mips64vr5000 | mips64vr5000el \ + | mipsbe | mipsel | mipsle | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | ns16k | ns32k \ + | openrisc \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | s390 | s390x \ + | sh | sh[34] | sh[34]eb | shbe | shle \ + | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic80 | tron \ + | v850 \ + | we32k \ + | x86 | xscale \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alphapca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armv*-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c54x-* \ + | clipper-* | cray2-* | cydra-* \ + | d10v-* | d30v-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | m32r-* \ + | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \ + | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipsel-* \ + | mipsle-* | mipstx39-* | mipstx39el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | s390-* | s390x-* \ + | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclite-* \ + | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* \ + | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \ + | v850-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [cjt]90) + basic_machine=${basic_machine}-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mipsel*-linux*) + basic_machine=mipsel-unknown + ;; + mips*-linux*) + basic_machine=mips-unknown + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + basic_machine=i686-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc64) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sparclite-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=t3e-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + windows32) + basic_machine=i386-pc + os=-windows32-msvcrt + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + mips) + case $os in + linux*) + basic_machine=mips-unknown + ;; + *) + basic_machine=mips-mips + ;; + esac + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh3eb | sh4eb) + basic_machine=sh-unknown + ;; + sparc | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + c4x*) + basic_machine=c4x-none + os=-coff + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto*) + os=-nto-qnx + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config/depcomp b/config/depcomp new file mode 100644 index 0000000..6589965 --- /dev/null +++ b/config/depcomp @@ -0,0 +1,411 @@ +#! /bin/sh + +# depcomp - compile a program generating dependencies as side-effects +# Copyright 1999, 2000 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi +# `libtool' can also be set to `yes' or `no'. + +depfile=${depfile-`echo "$object" | sed 's,\([^/]*\)$,.deps/\1,;s/\.\([^.]*\)$/.P\1/'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. This file always lives in the current directory. + # Also, the AIX compiler puts `$object:' at the start of each line; + # $object doesn't have directory information. + stripped=`echo "$object" | sed -e 's,^.*/,,' -e 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + outname="$stripped.o" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 AIX compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + + tmpdepfile1="$object.d" + tmpdepfile2=`echo "$object" | sed -e 's/.o$/.d/'` + if test "$libtool" = yes; then + "$@" -Wc,-MD + else + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + if test -f "$tmpdepfile1"; then + tmpdepfile="$tmpdepfile1" + else + tmpdepfile="$tmpdepfile2" + fi + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a space and a tab in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + test -z "$dashmflag" && dashmflag=-M + ( IFS=" " + case " $* " in + *" --mode=compile "*) # this is libtool, let us make it quiet + for arg + do # cycle over the arguments + case "$arg" in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + "$@" $dashmflag | sed 's:^[^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + # X makedepend + ( + shift + cleared=no + for arg in "$@"; do + case $cleared in no) + set ""; shift + cleared=yes + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift;; + -*) + ;; + *) + set fnord "$@" "$arg"; shift;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} 2>/dev/null -o"$obj_suffix" -f"$tmpdepfile" "$@" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tail +3 "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + ( IFS=" " + case " $* " in + *" --mode=compile "*) + for arg + do # cycle over the arguments + case $arg in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + "$@" -E | + sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + ( IFS=" " + case " $* " in + *" --mode=compile "*) + for arg + do # cycle over the arguments + case $arg in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 diff --git a/config/install-sh b/config/install-sh new file mode 100644 index 0000000..e9de238 --- /dev/null +++ b/config/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/config/missing b/config/missing new file mode 100644 index 0000000..0a7fb5a --- /dev/null +++ b/config/missing @@ -0,0 +1,283 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. +# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch]" + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing 0.3 - GNU automake" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then + # We have makeinfo, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + tar) + shift + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + fi + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar ${1+"$@"} && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar ${1+"$@"} && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" ${1+"$@"} && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" ${1+"$@"} && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequirements for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 diff --git a/config/mkinstalldirs b/config/mkinstalldirs new file mode 100644 index 0000000..4f58503 --- /dev/null +++ b/config/mkinstalldirs @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +# $Id: mkinstalldirs,v 1.13 1999/01/05 03:18:55 bje Exp $ + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/configure b/configure new file mode 100644 index 0000000..df7ecee --- /dev/null +++ b/configure @@ -0,0 +1,7498 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by Autoconf 2.52. +# +# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Name of the executable. +as_me=`echo "$0" |sed 's,.*[\\/],,'` + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +# NLS nuisances. +$as_unset LANG || test "${LANG+set}" != set || { LANG=C; export LANG; } +$as_unset LC_ALL || test "${LC_ALL+set}" != set || { LC_ALL=C; export LC_ALL; } +$as_unset LC_TIME || test "${LC_TIME+set}" != set || { LC_TIME=C; export LC_TIME; } +$as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set || { LC_CTYPE=C; export LC_CTYPE; } +$as_unset LANGUAGE || test "${LANGUAGE+set}" != set || { LANGUAGE=C; export LANGUAGE; } +$as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set || { LC_COLLATE=C; export LC_COLLATE; } +$as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set || { LC_NUMERIC=C; export LC_NUMERIC; } +$as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set || { LC_MESSAGES=C; export LC_MESSAGES; } + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=:; export CDPATH; } + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +cross_compiling=no +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +ac_unique_file="lib/common/common.h" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute path for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute path for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: should be removed in autoconf 3.0. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo "$ac_prog" | sed 's%[\\/][^\\/][^\\/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CXX_set=${CXX+set} +ac_env_CXX_value=$CXX +ac_cv_env_CXX_set=${CXX+set} +ac_cv_env_CXX_value=$CXX +ac_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_env_CXXFLAGS_value=$CXXFLAGS +ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_cv_env_CXXFLAGS_value=$CXXFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CXXCPP_set=${CXXCPP+set} +ac_env_CXXCPP_value=$CXXCPP +ac_cv_env_CXXCPP_set=${CXXCPP+set} +ac_cv_env_CXXCPP_value=$CXXCPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat < if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CXXCPP C++ preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +EOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_subdir in : $ac_subdirs_all; do test "x$ac_subdir" = x: && continue + cd $ac_subdir + # A "../" for each directory in /$ac_subdir. + ac_dots=`echo $ac_subdir | + sed 's,^\./,,;s,[^/]$,&/,;s,[^/]*/,../,g'` + + case $srcdir in + .) # No --srcdir option. We are building in place. + ac_sub_srcdir=$srcdir ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_sub_srcdir=$srcdir/$ac_subdir ;; + *) # Relative path. + ac_sub_srcdir=$ac_dots$srcdir/$ac_subdir ;; + esac + + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_sub_srcdir/configure.gnu; then + echo + $SHELL $ac_sub_srcdir/configure.gnu --help=recursive + elif test -f $ac_sub_srcdir/configure; then + echo + $SHELL $ac_sub_srcdir/configure --help=recursive + elif test -f $ac_sub_srcdir/configure.ac || + test -f $ac_sub_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_subdir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\EOF + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +EOF + exit 0 +fi +exec 5>config.log +cat >&5 </dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +PATH = $PATH + +_ASUNAME +} >&5 + +cat >&5 <\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + ac_sep=" " ;; + *) ac_configure_args="$ac_configure_args$ac_sep$ac_arg" + ac_sep=" " ;; + esac + # Get rid of the leading space. +done + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + echo >&5 + echo "## ----------------- ##" >&5 + echo "## Cache variables. ##" >&5 + echo "## ----------------- ##" >&5 + echo >&5 + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} >&5 + sed "/^$/d" confdefs.h >conftest.log + if test -s conftest.log; then + echo >&5 + echo "## ------------ ##" >&5 + echo "## confdefs.h. ##" >&5 + echo "## ------------ ##" >&5 + echo >&5 + cat conftest.log >&5 + fi + (echo; echo) >&5 + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" >&5 + echo "$as_me: exit $exit_status" >&5 + rm -rf conftest* confdefs* core core.* *.core conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:879: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + cat "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:890: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:898: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:914: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:918: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:924: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:926: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:928: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. It doesn't matter if + # we pass some twice (in addition to the command line arguments). + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + *) ac_configure_args="$ac_configure_args $ac_var=$ac_new_val" + ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:947: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:949: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac +echo "#! $SHELL" >conftest.sh +echo "exit 0" >>conftest.sh +chmod +x conftest.sh +if { (echo "$as_me:969: PATH=\".;.\"; conftest.sh") >&5 + (PATH=".;."; conftest.sh) 2>&5 + ac_status=$? + echo "$as_me:972: \$? = $ac_status" >&5 + (exit $ac_status); }; then + ac_path_separator=';' +else + ac_path_separator=: +fi +PATH_SEPARATOR="$ac_path_separator" +rm -f conftest.sh + +ac_aux_dir= +for ac_dir in config $srcdir/config; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:998: error: cannot find install-sh or install.sh in config $srcdir/config" >&5 +echo "$as_me: error: cannot find install-sh or install.sh in config $srcdir/config" >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +MAJOR_VERSION=1 +MINOR_VERSION=0 +RELEASE_VERSION=14 + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:1022: checking for a BSD compatible install" >&5 +echo $ECHO_N "checking for a BSD compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_IFS=$IFS; IFS=$ac_path_separator + for ac_dir in $PATH; do + IFS=$ac_save_IFS + # Account for people who put trailing slashes in PATH elements. + case $ac_dir/ in + / | ./ | .// | /cC/* \ + | /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* \ + | /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if $as_executable_p "$ac_dir/$ac_prog"; then + if test $ac_prog = install && + grep dspmsg "$ac_dir/$ac_prog" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$ac_dir/$ac_prog" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:1071: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:1082: checking whether build environment is sane" >&5 +echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6 +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { { echo "$as_me:1106: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { echo "$as_me:1119: error: newly created file is older than distributed files! +Check your system clock" >&5 +echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:1125: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +test "$program_prefix" != NONE && + program_transform_name="s,^,$program_prefix,;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$,$program_suffix,;$program_transform_name" +# Double any \ or $. echo might interpret backslashes. +# By default was `s,x,x', remove it if useless. +cat <<\_ACEOF >conftest.sed +s/[\\$]/&&/g;s/;s,x,x,$// +_ACEOF +program_transform_name=`echo $program_transform_name | sed -f conftest.sed` +rm conftest.sed + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`CDPATH=:; cd $ac_aux_dir && pwd` + +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + am_backtick='`' + { echo "$as_me:1150: WARNING: ${am_backtick}missing' script is too old or missing" >&5 +echo "$as_me: WARNING: ${am_backtick}missing' script is too old or missing" >&2;} +fi + +for ac_prog in mawk gawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:1158: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AWK+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_AWK="$ac_prog" +echo "$as_me:1173: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + echo "$as_me:1181: result: $AWK" >&5 +echo "${ECHO_T}$AWK" >&6 +else + echo "$as_me:1184: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$AWK" && break +done + +echo "$as_me:1191: checking whether ${MAKE-make} sets \${MAKE}" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \${MAKE}... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,./+-,__p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:1211: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:1215: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Check whether --enable-dependency-tracking or --disable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then + enableval="$enable_dependency_tracking" + +fi; +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi + +if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + +rm -f .deps 2>/dev/null +mkdir .deps 2>/dev/null +if test -d .deps; then + DEPDIR=.deps +else + # MS-DOS does not allow filenames that begin with a dot. + DEPDIR=_deps +fi +rmdir .deps 2>/dev/null + +# test to see if srcdir already configured +if test "`CDPATH=:; cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + { { echo "$as_me:1251: error: source directory already configured; run \"make distclean\" there first" >&5 +echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } +fi + +# Define the identity of the package. +PACKAGE=synergy +VERSION=$MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION + +cat >>confdefs.h <>confdefs.h <&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" +echo "$as_me:1327: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + echo "$as_me:1335: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6 +else + echo "$as_me:1338: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:1351: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CXX="$ac_prog" +echo "$as_me:1366: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + echo "$as_me:1374: result: $ac_ct_CXX" >&5 +echo "${ECHO_T}$ac_ct_CXX" >&6 +else + echo "$as_me:1377: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CXX" && break +done +test -n "$ac_ct_CXX" || ac_ct_CXX="g++" + + CXX=$ac_ct_CXX +fi + +# Provide some information about the compiler. +echo "$as_me:1389:" \ + "checking for C++ compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:1392: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:1395: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1397: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:1400: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1402: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:1405: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +#line 1409 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:1425: checking for C++ compiler default output" >&5 +echo $ECHO_N "checking for C++ compiler default output... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:1428: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:1431: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. +for ac_file in `ls a.exe conftest.exe 2>/dev/null; + ls a.out conftest 2>/dev/null; + ls a.* conftest.* 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb ) ;; + a.out ) # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool --akim. + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:1454: error: C++ compiler cannot create executables" >&5 +echo "$as_me: error: C++ compiler cannot create executables" >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:1460: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:1465: checking whether the C++ compiler works" >&5 +echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:1471: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1474: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:1481: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'." >&5 +echo "$as_me: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:1489: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:1496: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:1498: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:1501: checking for executable suffix" >&5 +echo $ECHO_N "checking for executable suffix... $ECHO_C" >&6 +if { (eval echo "$as_me:1503: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:1506: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in `(ls conftest.exe; ls conftest; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:1522: error: cannot compute EXEEXT: cannot compile and link" >&5 +echo "$as_me: error: cannot compute EXEEXT: cannot compile and link" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:1528: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:1534: checking for object suffix" >&5 +echo $ECHO_N "checking for object suffix... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1540 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:1552: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1555: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:1567: error: cannot compute OBJEXT: cannot compile" >&5 +echo "$as_me: error: cannot compute OBJEXT: cannot compile" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:1574: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:1578: checking whether we are using the GNU C++ compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6 +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1584 "configure" +#include "confdefs.h" + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1599: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1602: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1605: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1608: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_compiler_gnu=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:1620: result: $ac_cv_cxx_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6 +GXX=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +CXXFLAGS="-g" +echo "$as_me:1626: checking whether $CXX accepts -g" >&5 +echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cxx_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1632 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1644: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1647: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1650: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1653: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_prog_cxx_g=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:1663: result: $ac_cv_prog_cxx_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6 +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +for ac_declaration in \ + ''\ + '#include ' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +#line 1690 "configure" +#include "confdefs.h" +#include +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1703: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1706: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1709: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1712: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +continue +fi +rm -f conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +#line 1722 "configure" +#include "confdefs.h" +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1734: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1737: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1740: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1743: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_config_commands="$ac_config_commands default-1" + +am_make=${MAKE-make} +cat > confinc << 'END' +doit: + @echo done +END +# If we don't find an include directive, just comment out the code. +echo "$as_me:1773: checking for style of include used by $am_make" >&5 +echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6 +am__include='#' +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | fgrep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote='"' + _am_result=BSD + fi +fi + +echo "$as_me:1800: result: $_am_result" >&5 +echo "${ECHO_T}$_am_result" >&6 +rm -f confinc confmf + +depcc="$CXX" am_compiler_list= + +echo "$as_me:1806: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + echo '#include "conftest.h"' > conftest.c + echo 'int i;' > conftest.h + echo "${am__include} ${am__quote}conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=conftest.c object=conftest.o \ + depfile=conftest.Po tmpdepfile=conftest.TPo \ + $SHELL ./depcomp $depcc -c conftest.c -o conftest.o >/dev/null 2>&1 && + grep conftest.h conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +echo "$as_me:1868: result: $am_cv_CXX_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CXX_dependencies_compiler_type" >&6 +CXXDEPMODE="depmode=$am_cv_CXX_dependencies_compiler_type" + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +echo "$as_me:1875: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" +echo "$as_me:1890: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:1898: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:1901: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo "$as_me:1910: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_RANLIB="ranlib" +echo "$as_me:1925: found $ac_dir/$ac_word" >&5 +break +done + + test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:1934: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:1937: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +# Extract the first word of "dot", so it can be a program name with args. +set dummy dot; ac_word=$2 +echo "$as_me:1948: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_HAVE_DOT+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$HAVE_DOT"; then + ac_cv_prog_HAVE_DOT="$HAVE_DOT" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_HAVE_DOT="YES" +echo "$as_me:1963: found $ac_dir/$ac_word" >&5 +break +done + + test -z "$ac_cv_prog_HAVE_DOT" && ac_cv_prog_HAVE_DOT="NO" +fi +fi +HAVE_DOT=$ac_cv_prog_HAVE_DOT +if test -n "$HAVE_DOT"; then + echo "$as_me:1972: result: $HAVE_DOT" >&5 +echo "${ECHO_T}$HAVE_DOT" >&6 +else + echo "$as_me:1975: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +ac_ext=cpp + + echo "$as_me:1987: checking if g++ defines correct C++ macro" >&5 +echo $ECHO_N "checking if g++ defines correct C++ macro... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 1990 "configure" +#include "confdefs.h" + +int +main () +{ + + #if defined(_LANGUAGE_C) && !defined(_LANGUAGE_C_PLUS_PLUS) + #error wrong macro + #endif + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:2005: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:2008: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:2011: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2014: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_macro_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_macro_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:2023: result: $acx_cxx_macro_ok" >&5 +echo "${ECHO_T}$acx_cxx_macro_ok" >&6 + if test x"$acx_cxx_macro_ok" = xyes; then + SYNERGY_CXXFLAGS="" + else + SYNERGY_CXXFLAGS="-U_LANGUAGE_C -D_LANGUAGE_C_PLUS_PLUS" + fi + +# Make sure we can run config.sub. +$ac_config_sub sun4 >/dev/null 2>&1 || + { { echo "$as_me:2033: error: cannot run $ac_config_sub" >&5 +echo "$as_me: error: cannot run $ac_config_sub" >&2;} + { (exit 1); exit 1; }; } + +echo "$as_me:2037: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6 +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_build_alias=$build_alias +test -z "$ac_cv_build_alias" && + ac_cv_build_alias=`$ac_config_guess` +test -z "$ac_cv_build_alias" && + { { echo "$as_me:2046: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || + { { echo "$as_me:2050: error: $ac_config_sub $ac_cv_build_alias failed." >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed." >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:2055: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6 +build=$ac_cv_build +build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +test -z "$build_alias" && + build_alias=$ac_cv_build + +echo "$as_me:2065: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6 +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_host_alias=$host_alias +test -z "$ac_cv_host_alias" && + ac_cv_host_alias=$ac_cv_build_alias +ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || + { { echo "$as_me:2074: error: $ac_config_sub $ac_cv_host_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:2079: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6 +host=$ac_cv_host +host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +test -z "$host_alias" && + host_alias=$ac_cv_host + +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + echo "$as_me:2103: checking for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS" >&5 +echo $ECHO_N "checking for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 2106 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pthread_join (); +int +main () +{ +pthread_join (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2125: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2128: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2131: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2134: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_pthread_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + echo "$as_me:2142: result: $acx_pthread_ok" >&5 +echo "${ECHO_T}$acx_pthread_ok" >&6 + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + echo "$as_me:2201: checking whether pthreads work without any flags" >&5 +echo $ECHO_N "checking whether pthreads work without any flags... $ECHO_C" >&6 + ;; + + -*) + echo "$as_me:2206: checking whether pthreads work with $flag" >&5 +echo $ECHO_N "checking whether pthreads work with $flag... $ECHO_C" >&6 + PTHREAD_CFLAGS="$flag" + ;; + + *) + echo "$as_me:2212: checking for the pthreads library -l$flag" >&5 +echo $ECHO_N "checking for the pthreads library -l$flag... $ECHO_C" >&6 + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CXXFLAGS="$CXXFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + cat >conftest.$ac_ext <<_ACEOF +#line 2233 "configure" +#include "confdefs.h" +#include +int +main () +{ +pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2247: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2250: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2253: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2256: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_pthread_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + echo "$as_me:2268: result: $acx_pthread_ok" >&5 +echo "${ECHO_T}$acx_pthread_ok" >&6 + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: threads are created detached by default + # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + echo "$as_me:2288: checking for joinable pthread attribute" >&5 +echo $ECHO_N "checking for joinable pthread attribute... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 2291 "configure" +#include "confdefs.h" +#include +int +main () +{ +int attr=PTHREAD_CREATE_JOINABLE; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2303: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2306: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2309: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2312: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ok=PTHREAD_CREATE_JOINABLE +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ok=unknown +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + if test x"$ok" = xunknown; then + cat >conftest.$ac_ext <<_ACEOF +#line 2323 "configure" +#include "confdefs.h" +#include +int +main () +{ +int attr=PTHREAD_CREATE_UNDETACHED; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2335: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2338: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2341: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2344: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ok=PTHREAD_CREATE_UNDETACHED +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ok=unknown +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + fi + if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then + +cat >>confdefs.h <<\EOF +#define PTHREAD_CREATE_JOINABLE $ok +EOF + + fi + echo "$as_me:2361: result: ${ok}" >&5 +echo "${ECHO_T}${ok}" >&6 + if test x"$ok" = xunknown; then + { echo "$as_me:2364: WARNING: we do not know how to create joinable pthreads" >&5 +echo "$as_me: WARNING: we do not know how to create joinable pthreads" >&2;} + fi + + echo "$as_me:2368: checking if more special flags are required for pthreads" >&5 +echo $ECHO_N "checking if more special flags are required for pthreads... $ECHO_C" >&6 + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + alpha*-osf*) flag="-D_REENTRANT";; + *solaris*) flag="-D_REENTRANT";; + esac + echo "$as_me:2376: result: ${flag}" >&5 +echo "${ECHO_T}${flag}" >&6 + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + # Detect POSIX sigwait() + echo "$as_me:2383: checking for POSIX sigwait" >&5 +echo $ECHO_N "checking for POSIX sigwait... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 2386 "configure" +#include "confdefs.h" +#include + #include +int +main () +{ +sigset_t sigset; int signal; sigwait(&sigset, &signal); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2399: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2402: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2405: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2408: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ok=unknown +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + if test x"$ok" = xunknown; then + save_CXXFLAGS2="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -D_POSIX_PTHREAD_SEMANTICS" + cat >conftest.$ac_ext <<_ACEOF +#line 2421 "configure" +#include "confdefs.h" +#include + #include +int +main () +{ +sigset_t sigset; int signal; sigwait(&sigset, &signal); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2434: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2437: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2440: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2443: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ok=-D_POSIX_PTHREAD_SEMANTICS +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + CXXFLAGS="$save_CXXFLAGS2" + fi + echo "$as_me:2454: result: ${ok}" >&5 +echo "${ECHO_T}${ok}" >&6 + if test x"$ok" != xno; then + +cat >>confdefs.h <<\EOF +#define HAVE_POSIX_SIGWAIT 1 +EOF + + if test x"$ok" != xyes; then + PTHREAD_CFLAGS="$ok $PTHREAD_CFLAGS" + fi + fi + + # Detect pthread signal functions + echo "$as_me:2468: checking for pthread signal functions" >&5 +echo $ECHO_N "checking for pthread signal functions... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 2471 "configure" +#include "confdefs.h" +#include + #include +int +main () +{ +pthread_kill(pthread_self(), SIGTERM); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2484: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2487: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2490: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2493: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + echo "$as_me:2502: result: ${ok}" >&5 +echo "${ECHO_T}${ok}" >&6 + if test x"$ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_PTHREAD_SIGNAL 1 +EOF + + fi + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + # More AIX lossage: must compile with cc_r + # Extract the first word of "cc_r", so it can be a program name with args. +set dummy cc_r; ac_word=$2 +echo "$as_me:2518: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_PTHREAD_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$PTHREAD_CC"; then + ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_PTHREAD_CC="cc_r" +echo "$as_me:2533: found $ac_dir/$ac_word" >&5 +break +done + + test -z "$ac_cv_prog_PTHREAD_CC" && ac_cv_prog_PTHREAD_CC="${CC}" +fi +fi +PTHREAD_CC=$ac_cv_prog_PTHREAD_CC +if test -n "$PTHREAD_CC"; then + echo "$as_me:2542: result: $PTHREAD_CC" >&5 +echo "${ECHO_T}$PTHREAD_CC" >&6 +else + echo "$as_me:2545: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +else + PTHREAD_CC="$CC" +fi + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_PTHREAD 1 +EOF + + : +else + acx_pthread_ok=no + { { echo "$as_me:2563: error: You must have pthreads to compile synergy" >&5 +echo "$as_me: error: You must have pthreads to compile synergy" >&2;} + { (exit 1); exit 1; }; } +fi + + acx_nanosleep_ok=no + acx_nanosleep_list="" + + save_user_NANOSLEEP_LIBS="$NANOSLEEP_LIBS" + if test x"$NANOSLEEP_LIBS" != x; then + acx_nanosleep_list=user + fi + + acx_nanosleep_list="none $acx_nanosleep_list rt" + for flag in $acx_nanosleep_list; do + case $flag in + none) + echo "$as_me:2580: checking for nanosleep" >&5 +echo $ECHO_N "checking for nanosleep... $ECHO_C" >&6 + NANOSLEEP_LIBS="" + ;; + + user) + echo "$as_me:2586: checking for nanosleep in $save_user_NANOSLEEP_LIBS" >&5 +echo $ECHO_N "checking for nanosleep in $save_user_NANOSLEEP_LIBS... $ECHO_C" >&6 + NANOSLEEP_LIBS="$save_user_NANOSLEEP_LIBS" + ;; + + *) + echo "$as_me:2592: checking for nanosleep in -l$flag" >&5 +echo $ECHO_N "checking for nanosleep in -l$flag... $ECHO_C" >&6 + NANOSLEEP_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$NANOSLEEP_LIBS $LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line 2601 "configure" +#include "confdefs.h" +#include +int +main () +{ +struct timespec t = { 1, 1000 }; nanosleep(&t, NULL); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2613: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2616: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2619: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2622: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_nanosleep_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_nanosleep_ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + LIBS="$save_LIBS" + echo "$as_me:2632: result: $acx_nanosleep_ok" >&5 +echo "${ECHO_T}$acx_nanosleep_ok" >&6 + if test x"$acx_nanosleep_ok" = xyes; then + break; + fi + NANOSLEEP_LIBS="" + done + + # execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: + if test x"$acx_nanosleep_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_NANOSLEEP 1 +EOF + + : + else + acx_nanosleep_ok=no + + fi + + acx_inet_aton_ok=no + acx_inet_aton_list="" + + save_user_INET_ATON_LIBS="$INET_ATON_LIBS" + if test x"$INET_ATON_LIBS" != x; then + acx_inet_aton_list=user + fi + + acx_inet_aton_list="none $acx_inet_aton_list resolv" + for flag in $acx_inet_aton_list; do + case $flag in + none) + echo "$as_me:2665: checking for inet_aton" >&5 +echo $ECHO_N "checking for inet_aton... $ECHO_C" >&6 + INET_ATON_LIBS="" + ;; + + user) + echo "$as_me:2671: checking for inet_aton in $save_user_INET_ATON_LIBS" >&5 +echo $ECHO_N "checking for inet_aton in $save_user_INET_ATON_LIBS... $ECHO_C" >&6 + INET_ATON_LIBS="$save_user_INET_ATON_LIBS" + ;; + + *) + echo "$as_me:2677: checking for inet_aton in -l$flag" >&5 +echo $ECHO_N "checking for inet_aton in -l$flag... $ECHO_C" >&6 + INET_ATON_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$INET_ATON_LIBS $LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line 2686 "configure" +#include "confdefs.h" +#include + #include + #include + #include +int +main () +{ +struct in_addr addr; inet_aton("foo.bar", &addr); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2701: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2704: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2707: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2710: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_inet_aton_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_inet_aton_ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + LIBS="$save_LIBS" + echo "$as_me:2720: result: $acx_inet_aton_ok" >&5 +echo "${ECHO_T}$acx_inet_aton_ok" >&6 + if test x"$acx_inet_aton_ok" = xyes; then + break; + fi + INET_ATON_LIBS="" + done + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +echo "$as_me:2733: checking how to run the C++ preprocessor" >&5 +echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6 +if test -z "$CXXCPP"; then + if test "${ac_cv_prog_CXXCPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line 2750 "configure" +#include "confdefs.h" +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:2755: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2761: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line 2784 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:2788: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2794: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +echo "$as_me:2831: result: $CXXCPP" >&5 +echo "${ECHO_T}$CXXCPP" >&6 +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line 2841 "configure" +#include "confdefs.h" +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:2846: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2852: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line 2875 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:2879: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2885: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:2913: error: C++ preprocessor \"$CXXCPP\" fails sanity check" >&5 +echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +echo "$as_me:2924: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 2930 "configure" +#include "confdefs.h" +#include +#include +#include +#include + +_ACEOF +if { (eval echo "$as_me:2938: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2944: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line 2966 "configure" +#include "confdefs.h" +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line 2984 "configure" +#include "confdefs.h" +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +#line 3005 "configure" +#include "confdefs.h" +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:3031: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3034: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:3036: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3039: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_header_stdc=no +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:3052: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +for ac_header in unistd.h sys/time.h sys/types.h wchar.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:3065: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3071 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:3075: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:3081: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:3100: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3119 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:3123: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:3129: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:3148: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3167 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:3171: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:3177: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:3196: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3212 "configure" +#include "confdefs.h" +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:3228: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:3231: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:3234: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3237: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_header_time=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:3247: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\EOF +#define TIME_WITH_SYS_TIME 1 +EOF + +fi + +echo "$as_me:3257: checking for X" >&5 +echo $ECHO_N "checking for X... $ECHO_C" >&6 + +# Check whether --with-x or --without-x was given. +if test "${with_x+set}" = set; then + withval="$with_x" + +fi; +# $have_x is `yes', `no', `disabled', or empty when we do not yet know. +if test "x$with_x" = xno; then + # The user explicitly disabled X. + have_x=disabled +else + if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then + # Both variables are already set. + have_x=yes + else + if test "${ac_cv_have_x+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # One or both of the vars are not set, and there is no cached value. +ac_x_includes=no ac_x_libraries=no +rm -fr conftest.dir +if mkdir conftest.dir; then + cd conftest.dir + # Make sure to not put "make" in the Imakefile rules, since we grep it out. + cat >Imakefile <<'EOF' +acfindx: + @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"' +EOF + if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then + # GNU make sometimes prints "make[1]: Entering...", which would confuse us. + eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` + # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. + for ac_extension in a so sl; do + if test ! -f $ac_im_usrlibdir/libX11.$ac_extension && + test -f $ac_im_libdir/libX11.$ac_extension; then + ac_im_usrlibdir=$ac_im_libdir; break + fi + done + # Screen out bogus values from the imake configuration. They are + # bogus both because they are the default anyway, and because + # using them would break gcc on systems where it needs fixed includes. + case $ac_im_incroot in + /usr/include) ;; + *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; + esac + case $ac_im_usrlibdir in + /usr/lib | /lib) ;; + *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; + esac + fi + cd .. + rm -fr conftest.dir +fi + +# Standard set of common directories for X headers. +# Check X11 before X11Rn because it is often a symlink to the current release. +ac_x_header_dirs=' +/usr/X11/include +/usr/X11R6/include +/usr/X11R5/include +/usr/X11R4/include + +/usr/include/X11 +/usr/include/X11R6 +/usr/include/X11R5 +/usr/include/X11R4 + +/usr/local/X11/include +/usr/local/X11R6/include +/usr/local/X11R5/include +/usr/local/X11R4/include + +/usr/local/include/X11 +/usr/local/include/X11R6 +/usr/local/include/X11R5 +/usr/local/include/X11R4 + +/usr/X386/include +/usr/x386/include +/usr/XFree86/include/X11 + +/usr/include +/usr/local/include +/usr/unsupported/include +/usr/athena/include +/usr/local/x11r5/include +/usr/lpp/Xamples/include + +/usr/openwin/include +/usr/openwin/share/include' + +if test "$ac_x_includes" = no; then + # Guess where to find include files, by looking for Intrinsic.h. + # First, try using that file with no special directory specified. + cat >conftest.$ac_ext <<_ACEOF +#line 3354 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:3358: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:3364: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # We can compile using X headers with no special include directory. +ac_x_includes= +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + for ac_dir in $ac_x_header_dirs; do + if test -r "$ac_dir/X11/Intrinsic.h"; then + ac_x_includes=$ac_dir + break + fi +done +fi +rm -f conftest.err conftest.$ac_ext +fi # $ac_x_includes = no + +if test "$ac_x_libraries" = no; then + # Check for the libraries. + # See if we find them without any special options. + # Don't add to $LIBS permanently. + ac_save_LIBS=$LIBS + LIBS="-lXt $LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line 3397 "configure" +#include "confdefs.h" +#include +int +main () +{ +XtMalloc (0) + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3409: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3412: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3415: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3418: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + LIBS=$ac_save_LIBS +# We can link X programs with no special library path. +ac_x_libraries= +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +LIBS=$ac_save_LIBS +for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` +do + # Don't even attempt the hair of trying to link an X program! + for ac_extension in a so sl; do + if test -r $ac_dir/libXt.$ac_extension; then + ac_x_libraries=$ac_dir + break 2 + fi + done +done +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi # $ac_x_libraries = no + +if test "$ac_x_includes" = no || test "$ac_x_libraries" = no; then + # Didn't find X anywhere. Cache the known absence of X. + ac_cv_have_x="have_x=no" +else + # Record where we found X for the cache. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries" +fi +fi + + fi + eval "$ac_cv_have_x" +fi # $with_x != no + +if test "$have_x" != yes; then + echo "$as_me:3456: result: $have_x" >&5 +echo "${ECHO_T}$have_x" >&6 + no_x=yes +else + # If each of the values was on the command line, it overrides each guess. + test "x$x_includes" = xNONE && x_includes=$ac_x_includes + test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries + # Update the cache value to reflect the command line values. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$x_includes ac_x_libraries=$x_libraries" + echo "$as_me:3466: result: libraries $x_libraries, headers $x_includes" >&5 +echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6 +fi + +if test "$no_x" = yes; then + # Not all programs may use this symbol, but it does not hurt to define it. + +cat >>confdefs.h <<\EOF +#define X_DISPLAY_MISSING 1 +EOF + + X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS= +else + if test -n "$x_includes"; then + X_CFLAGS="$X_CFLAGS -I$x_includes" + fi + + # It would also be nice to do this for all -L options, not just this one. + if test -n "$x_libraries"; then + X_LIBS="$X_LIBS -L$x_libraries" + # For Solaris; some versions of Sun CC require a space after -R and + # others require no space. Words are not sufficient . . . . + case `(uname -sr) 2>/dev/null` in + "SunOS 5"*) + echo "$as_me:3490: checking whether -R must be followed by a space" >&5 +echo $ECHO_N "checking whether -R must be followed by a space... $ECHO_C" >&6 + ac_xsave_LIBS=$LIBS; LIBS="$LIBS -R$x_libraries" + cat >conftest.$ac_ext <<_ACEOF +#line 3494 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3506: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3509: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3512: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3515: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_R_nospace=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_R_nospace=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + if test $ac_R_nospace = yes; then + echo "$as_me:3525: result: no" >&5 +echo "${ECHO_T}no" >&6 + X_LIBS="$X_LIBS -R$x_libraries" + else + LIBS="$ac_xsave_LIBS -R $x_libraries" + cat >conftest.$ac_ext <<_ACEOF +#line 3531 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3543: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3546: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3549: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3552: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_R_space=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_R_space=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + if test $ac_R_space = yes; then + echo "$as_me:3562: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + X_LIBS="$X_LIBS -R $x_libraries" + else + echo "$as_me:3566: result: neither works" >&5 +echo "${ECHO_T}neither works" >&6 + fi + fi + LIBS=$ac_xsave_LIBS + esac + fi + + # Check for system-dependent libraries X programs must link with. + # Do this before checking for the system-independent R6 libraries + # (-lICE), since we may need -lsocket or whatever for X linking. + + if test "$ISC" = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet" + else + # Martyn Johnson says this is needed for Ultrix, if the X + # libraries were built with DECnet support. And Karl Berry says + # the Alpha needs dnet_stub (dnet does not exist). + ac_xsave_LIBS="$LIBS"; LIBS="$LIBS $X_LIBS -lX11" + cat >conftest.$ac_ext <<_ACEOF +#line 3586 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char XOpenDisplay (); +int +main () +{ +XOpenDisplay (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3605: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3608: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3611: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3614: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +echo "$as_me:3620: checking for dnet_ntoa in -ldnet" >&5 +echo $ECHO_N "checking for dnet_ntoa in -ldnet... $ECHO_C" >&6 +if test "${ac_cv_lib_dnet_dnet_ntoa+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldnet $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3628 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dnet_ntoa (); +int +main () +{ +dnet_ntoa (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3647: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3650: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3653: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3656: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dnet_dnet_ntoa=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_dnet_dnet_ntoa=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3667: result: $ac_cv_lib_dnet_dnet_ntoa" >&5 +echo "${ECHO_T}$ac_cv_lib_dnet_dnet_ntoa" >&6 +if test $ac_cv_lib_dnet_dnet_ntoa = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet" +fi + + if test $ac_cv_lib_dnet_dnet_ntoa = no; then + echo "$as_me:3674: checking for dnet_ntoa in -ldnet_stub" >&5 +echo $ECHO_N "checking for dnet_ntoa in -ldnet_stub... $ECHO_C" >&6 +if test "${ac_cv_lib_dnet_stub_dnet_ntoa+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldnet_stub $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3682 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dnet_ntoa (); +int +main () +{ +dnet_ntoa (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3701: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3704: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3707: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3710: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dnet_stub_dnet_ntoa=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_dnet_stub_dnet_ntoa=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3721: result: $ac_cv_lib_dnet_stub_dnet_ntoa" >&5 +echo "${ECHO_T}$ac_cv_lib_dnet_stub_dnet_ntoa" >&6 +if test $ac_cv_lib_dnet_stub_dnet_ntoa = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub" +fi + + fi +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + LIBS="$ac_xsave_LIBS" + + # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT, + # to get the SysV transport functions. + # Chad R. Larson says the Pyramis MIS-ES running DC/OSx (SVR4) + # needs -lnsl. + # The nsl library prevents programs from opening the X display + # on Irix 5.2, according to T.E. Dickey. + # The functions gethostbyname, getservbyname, and inet_addr are + # in -lbsd on LynxOS 3.0.1/i386, according to Lars Hecking. + echo "$as_me:3740: checking for gethostbyname" >&5 +echo $ECHO_N "checking for gethostbyname... $ECHO_C" >&6 +if test "${ac_cv_func_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3746 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char gethostbyname (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname) +choke me +#else +f = gethostbyname; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3777: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3780: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3783: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3786: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_gethostbyname=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:3796: result: $ac_cv_func_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_func_gethostbyname" >&6 + + if test $ac_cv_func_gethostbyname = no; then + echo "$as_me:3800: checking for gethostbyname in -lnsl" >&5 +echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6 +if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnsl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3808 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3827: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3830: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3833: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3836: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_nsl_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_nsl_gethostbyname=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3847: result: $ac_cv_lib_nsl_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6 +if test $ac_cv_lib_nsl_gethostbyname = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl" +fi + + if test $ac_cv_lib_nsl_gethostbyname = no; then + echo "$as_me:3854: checking for gethostbyname in -lbsd" >&5 +echo $ECHO_N "checking for gethostbyname in -lbsd... $ECHO_C" >&6 +if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbsd $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3862 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3881: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3884: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3887: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3890: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_bsd_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_bsd_gethostbyname=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3901: result: $ac_cv_lib_bsd_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_lib_bsd_gethostbyname" >&6 +if test $ac_cv_lib_bsd_gethostbyname = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd" +fi + + fi + fi + + # lieder@skyler.mavd.honeywell.com says without -lsocket, + # socket/setsockopt and other routines are undefined under SCO ODT + # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary + # on later versions), says Simon Leinen: it contains gethostby* + # variants that don't use the nameserver (or something). -lsocket + # must be given before -lnsl if both are needed. We assume that + # if connect needs -lnsl, so does gethostbyname. + echo "$as_me:3917: checking for connect" >&5 +echo $ECHO_N "checking for connect... $ECHO_C" >&6 +if test "${ac_cv_func_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3923 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char connect (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_connect) || defined (__stub___connect) +choke me +#else +f = connect; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3954: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3957: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3960: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3963: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_connect=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_connect=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:3973: result: $ac_cv_func_connect" >&5 +echo "${ECHO_T}$ac_cv_func_connect" >&6 + + if test $ac_cv_func_connect = no; then + echo "$as_me:3977: checking for connect in -lsocket" >&5 +echo $ECHO_N "checking for connect in -lsocket... $ECHO_C" >&6 +if test "${ac_cv_lib_socket_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $X_EXTRA_LIBS $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3985 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect (); +int +main () +{ +connect (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4004: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4007: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4010: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4013: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_socket_connect=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_socket_connect=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4024: result: $ac_cv_lib_socket_connect" >&5 +echo "${ECHO_T}$ac_cv_lib_socket_connect" >&6 +if test $ac_cv_lib_socket_connect = yes; then + X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS" +fi + + fi + + # Guillermo Gomez says -lposix is necessary on A/UX. + echo "$as_me:4033: checking for remove" >&5 +echo $ECHO_N "checking for remove... $ECHO_C" >&6 +if test "${ac_cv_func_remove+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4039 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char remove (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char remove (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_remove) || defined (__stub___remove) +choke me +#else +f = remove; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4070: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4073: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4076: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4079: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_remove=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_remove=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:4089: result: $ac_cv_func_remove" >&5 +echo "${ECHO_T}$ac_cv_func_remove" >&6 + + if test $ac_cv_func_remove = no; then + echo "$as_me:4093: checking for remove in -lposix" >&5 +echo $ECHO_N "checking for remove in -lposix... $ECHO_C" >&6 +if test "${ac_cv_lib_posix_remove+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lposix $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 4101 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char remove (); +int +main () +{ +remove (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4120: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4123: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4126: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4129: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_posix_remove=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_posix_remove=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4140: result: $ac_cv_lib_posix_remove" >&5 +echo "${ECHO_T}$ac_cv_lib_posix_remove" >&6 +if test $ac_cv_lib_posix_remove = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix" +fi + + fi + + # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay. + echo "$as_me:4149: checking for shmat" >&5 +echo $ECHO_N "checking for shmat... $ECHO_C" >&6 +if test "${ac_cv_func_shmat+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4155 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char shmat (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shmat (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_shmat) || defined (__stub___shmat) +choke me +#else +f = shmat; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4186: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4189: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4192: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4195: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_shmat=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_shmat=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:4205: result: $ac_cv_func_shmat" >&5 +echo "${ECHO_T}$ac_cv_func_shmat" >&6 + + if test $ac_cv_func_shmat = no; then + echo "$as_me:4209: checking for shmat in -lipc" >&5 +echo $ECHO_N "checking for shmat in -lipc... $ECHO_C" >&6 +if test "${ac_cv_lib_ipc_shmat+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lipc $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 4217 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shmat (); +int +main () +{ +shmat (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4236: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4239: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4242: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4245: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ipc_shmat=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_ipc_shmat=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4256: result: $ac_cv_lib_ipc_shmat" >&5 +echo "${ECHO_T}$ac_cv_lib_ipc_shmat" >&6 +if test $ac_cv_lib_ipc_shmat = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc" +fi + + fi + fi + + # Check for libraries that X11R6 Xt/Xaw programs need. + ac_save_LDFLAGS=$LDFLAGS + test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries" + # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to + # check for ICE first), but we must link in the order -lSM -lICE or + # we get undefined symbols. So assume we have SM if we have ICE. + # These have to be linked with before -lX11, unlike the other + # libraries we check for below, so use a different variable. + # John Interrante, Karl Berry + echo "$as_me:4274: checking for IceConnectionNumber in -lICE" >&5 +echo $ECHO_N "checking for IceConnectionNumber in -lICE... $ECHO_C" >&6 +if test "${ac_cv_lib_ICE_IceConnectionNumber+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lICE $X_EXTRA_LIBS $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 4282 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char IceConnectionNumber (); +int +main () +{ +IceConnectionNumber (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4301: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4304: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4307: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4310: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ICE_IceConnectionNumber=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_ICE_IceConnectionNumber=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4321: result: $ac_cv_lib_ICE_IceConnectionNumber" >&5 +echo "${ECHO_T}$ac_cv_lib_ICE_IceConnectionNumber" >&6 +if test $ac_cv_lib_ICE_IceConnectionNumber = yes; then + X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE" +fi + + LDFLAGS=$ac_save_LDFLAGS + +fi + +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$X_CFLAGS $CPPFLAGS" + +for ac_header in X11/extensions/XTest.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:4337: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4343 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:4347: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:4353: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:4372: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4391 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:4395: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:4401: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:4420: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for XineramaQueryExtension in -lXinerama... $ECHO_C" >&6 +if test "${ac_cv_lib_Xinerama_XineramaQueryExtension+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lXinerama $X_LIBS -lXext -lX11 $X_EXTRA_LIBS $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 4438 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char XineramaQueryExtension (); +int +main () +{ +XineramaQueryExtension (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4457: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4460: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4463: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4466: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_Xinerama_XineramaQueryExtension=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_Xinerama_XineramaQueryExtension=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4477: result: $ac_cv_lib_Xinerama_XineramaQueryExtension" >&5 +echo "${ECHO_T}$ac_cv_lib_Xinerama_XineramaQueryExtension" >&6 +if test $ac_cv_lib_Xinerama_XineramaQueryExtension = yes; then + +for ac_header in X11/extensions/Xinerama.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:4484: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4490 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:4494: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:4500: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:4519: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4545 "configure" +#include "confdefs.h" +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4551: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4554: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4557: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4560: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:4570: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4586 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4601: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4604: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4607: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4610: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_size_t=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:4620: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <&5 +echo $ECHO_N "checking for socklen_t... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 4635 "configure" +#include "confdefs.h" + + #include + #include + +int +main () +{ +socklen_t len; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4650: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4653: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4656: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4659: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_socklen_t_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_socklen_t_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:4668: result: $acx_socklen_t_ok" >&5 +echo "${ECHO_T}$acx_socklen_t_ok" >&6 + if test x"$acx_socklen_t_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_SOCKLEN_T 1 +EOF + + : + else + acx_socklen_t_ok=no + + fi + +echo "$as_me:4682: checking whether struct tm is in sys/time.h or time.h" >&5 +echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 +if test "${ac_cv_struct_tm+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4688 "configure" +#include "confdefs.h" +#include +#include + +int +main () +{ +struct tm *tp; tp->tm_sec; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4702: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4705: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4708: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4711: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_struct_tm=time.h +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_struct_tm=sys/time.h +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:4721: result: $ac_cv_struct_tm" >&5 +echo "${ECHO_T}$ac_cv_struct_tm" >&6 +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + +echo "$as_me:4731: checking for char" >&5 +echo $ECHO_N "checking for char... $ECHO_C" >&6 +if test "${ac_cv_type_char+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4737 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +if ((char *) 0) + return 0; +if (sizeof (char)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4752: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4755: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4758: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4761: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_char=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_char=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:4771: result: $ac_cv_type_char" >&5 +echo "${ECHO_T}$ac_cv_type_char" >&6 + +echo "$as_me:4774: checking size of char" >&5 +echo $ECHO_N "checking size of char... $ECHO_C" >&6 +if test "${ac_cv_sizeof_char+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_char" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 4783 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4795: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4798: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4801: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4804: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 4809 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4821: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4824: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4827: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4830: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 4846 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4858: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4861: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4864: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4867: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 4883 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4895: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4898: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4901: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4904: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_char=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:4917: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 4922 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (char))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:4938: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4941: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:4943: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4946: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_char=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_char=0 +fi +fi +echo "$as_me:4962: result: $ac_cv_sizeof_char" >&5 +echo "${ECHO_T}$ac_cv_sizeof_char" >&6 +cat >>confdefs.h <&5 +echo $ECHO_N "checking for short... $ECHO_C" >&6 +if test "${ac_cv_type_short+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4974 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +if ((short *) 0) + return 0; +if (sizeof (short)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4989: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4992: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4995: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4998: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_short=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_short=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:5008: result: $ac_cv_type_short" >&5 +echo "${ECHO_T}$ac_cv_type_short" >&6 + +echo "$as_me:5011: checking size of short" >&5 +echo $ECHO_N "checking size of short... $ECHO_C" >&6 +if test "${ac_cv_sizeof_short+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_short" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 5020 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5032: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5035: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5038: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5041: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5046 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5058: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5061: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5064: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5067: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5083 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5095: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5098: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5101: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5104: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 5120 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5132: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5135: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5138: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5141: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_short=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:5154: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 5159 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (short))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5175: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5178: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5180: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5183: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_short=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_short=0 +fi +fi +echo "$as_me:5199: result: $ac_cv_sizeof_short" >&5 +echo "${ECHO_T}$ac_cv_sizeof_short" >&6 +cat >>confdefs.h <&5 +echo $ECHO_N "checking for int... $ECHO_C" >&6 +if test "${ac_cv_type_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 5211 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +if ((int *) 0) + return 0; +if (sizeof (int)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5226: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5229: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5232: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5235: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_int=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_int=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:5245: result: $ac_cv_type_int" >&5 +echo "${ECHO_T}$ac_cv_type_int" >&6 + +echo "$as_me:5248: checking size of int" >&5 +echo $ECHO_N "checking size of int... $ECHO_C" >&6 +if test "${ac_cv_sizeof_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_int" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 5257 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5269: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5272: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5275: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5278: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5283 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5295: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5298: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5301: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5304: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5320 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5332: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5335: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5338: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5341: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 5357 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5369: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5372: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5375: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5378: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_int=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:5391: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 5396 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (int))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5412: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5415: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5417: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5420: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_int=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_int=0 +fi +fi +echo "$as_me:5436: result: $ac_cv_sizeof_int" >&5 +echo "${ECHO_T}$ac_cv_sizeof_int" >&6 +cat >>confdefs.h <&5 +echo $ECHO_N "checking for long... $ECHO_C" >&6 +if test "${ac_cv_type_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 5448 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +if ((long *) 0) + return 0; +if (sizeof (long)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5463: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5466: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5469: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5472: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_long=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:5482: result: $ac_cv_type_long" >&5 +echo "${ECHO_T}$ac_cv_type_long" >&6 + +echo "$as_me:5485: checking size of long" >&5 +echo $ECHO_N "checking size of long... $ECHO_C" >&6 +if test "${ac_cv_sizeof_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_long" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 5494 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5506: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5509: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5512: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5515: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5520 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5532: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5535: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5538: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5541: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5557 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5569: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5572: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5575: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5578: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 5594 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5606: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5609: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5612: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5615: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_long=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:5628: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 5633 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (long))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5649: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5652: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5654: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5657: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_long=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_long=0 +fi +fi +echo "$as_me:5673: result: $ac_cv_sizeof_long" >&5 +echo "${ECHO_T}$ac_cv_sizeof_long" >&6 +cat >>confdefs.h <&5 +echo $ECHO_N "checking for bool support... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 5682 "configure" +#include "confdefs.h" + +int +main () +{ +bool t = true, f = false; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5694: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5697: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5700: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5703: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_bool_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_bool_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:5712: result: $acx_cxx_bool_ok" >&5 +echo "${ECHO_T}$acx_cxx_bool_ok" >&6 + if test x"$acx_cxx_bool_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_CXX_BOOL 1 +EOF + + : + else + acx_cxx_bool_ok=no + { { echo "$as_me:5723: error: Your compiler must support bool to compile synergy" >&5 +echo "$as_me: error: Your compiler must support bool to compile synergy" >&2;} + { (exit 1); exit 1; }; } + fi + + echo "$as_me:5728: checking for exception support" >&5 +echo $ECHO_N "checking for exception support... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 5731 "configure" +#include "confdefs.h" + +int +main () +{ +try{throw int(4);}catch(int){throw;}catch(...){} + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5743: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5746: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5749: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5752: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_exception_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_exception_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:5761: result: $acx_cxx_exception_ok" >&5 +echo "${ECHO_T}$acx_cxx_exception_ok" >&6 + if test x"$acx_cxx_exception_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_CXX_EXCEPTIONS 1 +EOF + + : + else + acx_cxx_exception_ok=no + { { echo "$as_me:5772: error: Your compiler must support exceptions to compile synergy" >&5 +echo "$as_me: error: Your compiler must support exceptions to compile synergy" >&2;} + { (exit 1); exit 1; }; } + fi + + echo "$as_me:5777: checking for C++ cast support" >&5 +echo $ECHO_N "checking for C++ cast support... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 5780 "configure" +#include "confdefs.h" + +int +main () +{ +const char* f="a";const_cast(f); + reinterpret_cast(f);static_cast(4.5); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5793: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5796: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5799: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5802: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_cast_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_cast_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:5811: result: $acx_cxx_cast_ok" >&5 +echo "${ECHO_T}$acx_cxx_cast_ok" >&6 + if test x"$acx_cxx_cast_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_CXX_CASTS 1 +EOF + + : + else + acx_cxx_cast_ok=no + { { echo "$as_me:5822: error: Your compiler must support C++ casts to compile synergy" >&5 +echo "$as_me: error: Your compiler must support C++ casts to compile synergy" >&2;} + { (exit 1); exit 1; }; } + fi + + echo "$as_me:5827: checking for mutable support" >&5 +echo $ECHO_N "checking for mutable support... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 5830 "configure" +#include "confdefs.h" + +int +main () +{ +struct A{mutable int b;void f() const {b=0;}}; + A a;a.f(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5843: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5846: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5849: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5852: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_mutable_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_mutable_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:5861: result: $acx_cxx_mutable_ok" >&5 +echo "${ECHO_T}$acx_cxx_mutable_ok" >&6 + if test x"$acx_cxx_mutable_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_CXX_MUTABLE 1 +EOF + + : + else + acx_cxx_mutable_ok=no + { { echo "$as_me:5872: error: Your compiler must support mutable to compile synergy" >&5 +echo "$as_me: error: Your compiler must support mutable to compile synergy" >&2;} + { (exit 1); exit 1; }; } + fi + + echo "$as_me:5877: checking for C++ standard library" >&5 +echo $ECHO_N "checking for C++ standard library... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 5880 "configure" +#include "confdefs.h" +#include +int +main () +{ +std::set a; a.insert(3); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:5892: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5895: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:5898: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5901: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_stdlib_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_stdlib_ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + echo "$as_me:5910: result: $acx_cxx_stdlib_ok" >&5 +echo "${ECHO_T}$acx_cxx_stdlib_ok" >&6 + if test x"$acx_cxx_stdlib_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_CXX_STDLIB 1 +EOF + + : + else + acx_cxx_stdlib_ok=no + { { echo "$as_me:5921: error: Your compiler must support the C++ standard library to compile synergy" >&5 +echo "$as_me: error: Your compiler must support the C++ standard library to compile synergy" >&2;} + { (exit 1); exit 1; }; } + fi + +echo "$as_me:5926: checking for working memcmp" >&5 +echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6 +if test "${ac_cv_func_memcmp_working+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_working=no +else + cat >conftest.$ac_ext <<_ACEOF +#line 5935 "configure" +#include "confdefs.h" + +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = 0x40, c1 = 0x80, c2 = 0x81; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + exit (1); + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + exit (1); + } + exit (0); + } + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5971: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5974: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5976: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5979: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_memcmp_working=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_memcmp_working=no +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:5991: result: $ac_cv_func_memcmp_working" >&5 +echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6 +test $ac_cv_func_memcmp_working = no && LIBOBJS="$LIBOBJS memcmp.$ac_objext" + +for ac_func in strftime +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:5998: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6004 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +f = $ac_func; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6035: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6038: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6041: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6044: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:6054: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6 +if test "${ac_cv_lib_intl_strftime+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lintl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 6071 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime (); +int +main () +{ +strftime (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6090: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6093: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6096: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6099: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_intl_strftime=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_intl_strftime=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:6110: result: $ac_cv_lib_intl_strftime" >&5 +echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6 +if test $ac_cv_lib_intl_strftime = yes; then + cat >>confdefs.h <<\EOF +#define HAVE_STRFTIME 1 +EOF + +LIBS="-lintl $LIBS" +fi + +fi +done + +for ac_func in gmtime_r +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:6126: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6132 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +f = $ac_func; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6163: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6166: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6169: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6172: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:6182: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for working getpwuid_r... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 6195 "configure" +#include "confdefs.h" +#include +int +main () +{ +char buffer[4096]; struct passwd pwd, *pwdp; + getpwuid_r(0, &pwd, buffer, sizeof(buffer), &pwdp); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6208: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6211: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6214: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6217: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_getpwuid_r_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_getpwuid_r_ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + echo "$as_me:6226: result: $acx_getpwuid_r_ok" >&5 +echo "${ECHO_T}$acx_getpwuid_r_ok" >&6 + if test x"$acx_getpwuid_r_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_GETPWUID_R 1 +EOF + + : + else + acx_getpwuid_r_ok=no + + fi + +for ac_func in vsnprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:6243: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6249 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +f = $ac_func; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6280: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6283: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6286: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6289: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:6299: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6318 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +f = $ac_func; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6349: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6352: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6355: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6358: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:6368: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6387 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:6391: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:6397: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:6416: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking types of arguments for select... $ECHO_C" >&6 +if test "${ac_cv_func_select_args+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + for ac_arg234 in 'fd_set *' 'int *' 'void *'; do + for ac_arg1 in 'int' 'size_t' 'unsigned long' 'unsigned'; do + for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do + cat >conftest.$ac_ext <<_ACEOF +#line 6435 "configure" +#include "confdefs.h" +$ac_includes_default +#if HAVE_SYS_SELECT_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif + +int +main () +{ +extern int select ($ac_arg1, + $ac_arg234, $ac_arg234, $ac_arg234, + $ac_arg5); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:6456: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:6459: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:6462: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6465: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3 +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done + done +done +# Provide a safe default value. +: ${ac_cv_func_select_args='int,int *,struct timeval *'} + +fi +echo "$as_me:6480: result: $ac_cv_func_select_args" >&5 +echo "${ECHO_T}$ac_cv_func_select_args" >&6 +ac_save_IFS=$IFS; IFS=',' +set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'` +IFS=$ac_save_IFS +shift + +cat >>confdefs.h <>confdefs.h <>confdefs.h <&5 +echo $ECHO_N "checking for poll... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 6504 "configure" +#include "confdefs.h" +#include +int +main () +{ +struct pollfd ufds[] = { 0, POLLIN, 0 }; poll(ufds, 1, 10); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6516: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6519: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6522: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6525: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_poll_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_poll_ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + echo "$as_me:6534: result: $acx_poll_ok" >&5 +echo "${ECHO_T}$acx_poll_ok" >&6 + if test x"$acx_poll_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_POLL 1 +EOF + + : + else + acx_poll_ok=no + + fi + +CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" +LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $PTHREAD_LIBS $LIBS" + +ac_config_files="$ac_config_files Makefile lib/Makefile lib/arch/Makefile lib/base/Makefile lib/common/Makefile lib/mt/Makefile lib/io/Makefile lib/http/Makefile lib/net/Makefile lib/synergy/Makefile lib/platform/Makefile lib/client/Makefile lib/server/Makefile cmd/Makefile cmd/launcher/Makefile cmd/synergyc/Makefile cmd/synergys/Makefile dist/Makefile dist/rpm/Makefile dist/rpm/synergy.spec doc/doxygen.cfg" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overriden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if cmp -s $cache_file confcache; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:6631: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +SHELL=\${CONFIG_SHELL-$SHELL} +ac_cs_invocation="\$0 \$@" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Name of the executable. +as_me=`echo "$0" |sed 's,.*[\\/],,'` + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +# NLS nuisances. +$as_unset LANG || test "${LANG+set}" != set || { LANG=C; export LANG; } +$as_unset LC_ALL || test "${LC_ALL+set}" != set || { LC_ALL=C; export LC_ALL; } +$as_unset LC_TIME || test "${LC_TIME+set}" != set || { LC_TIME=C; export LC_TIME; } +$as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set || { LC_CTYPE=C; export LC_CTYPE; } +$as_unset LANGUAGE || test "${LANGUAGE+set}" != set || { LANGUAGE=C; export LANGUAGE; } +$as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set || { LC_COLLATE=C; export LC_COLLATE; } +$as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set || { LC_NUMERIC=C; export LC_NUMERIC; } +$as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set || { LC_MESSAGES=C; export LC_MESSAGES; } + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=:; export CDPATH; } + +exec 6>&1 + +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\EOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." +EOF + +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + shift + set dummy "$ac_option" "$ac_optarg" ${1+"$@"} + shift + ;; + -*);; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_need_defaults=false;; + esac + + case $1 in + # Handling of the options. +EOF +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:6807: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + shift + CONFIG_FILES="$CONFIG_FILES $1" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + shift + CONFIG_HEADERS="$CONFIG_HEADERS $1" + ac_need_defaults=false;; + + # This is an error. + -*) { { echo "$as_me:6826: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +exec 5>>config.log +cat >&5 << _ACEOF + +## ----------------------- ## +## Running config.status. ## +## ----------------------- ## + +This file was extended by $as_me 2.52, executed with + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + > $ac_cs_invocation +on `(hostname || uname -n) 2>/dev/null | sed 1q` + +_ACEOF +EOF + +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "lib/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; + "lib/arch/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/arch/Makefile" ;; + "lib/base/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/base/Makefile" ;; + "lib/common/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/common/Makefile" ;; + "lib/mt/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/mt/Makefile" ;; + "lib/io/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/io/Makefile" ;; + "lib/http/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/http/Makefile" ;; + "lib/net/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/net/Makefile" ;; + "lib/synergy/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/synergy/Makefile" ;; + "lib/platform/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/platform/Makefile" ;; + "lib/client/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/client/Makefile" ;; + "lib/server/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/server/Makefile" ;; + "cmd/Makefile" ) CONFIG_FILES="$CONFIG_FILES cmd/Makefile" ;; + "cmd/launcher/Makefile" ) CONFIG_FILES="$CONFIG_FILES cmd/launcher/Makefile" ;; + "cmd/synergyc/Makefile" ) CONFIG_FILES="$CONFIG_FILES cmd/synergyc/Makefile" ;; + "cmd/synergys/Makefile" ) CONFIG_FILES="$CONFIG_FILES cmd/synergys/Makefile" ;; + "dist/Makefile" ) CONFIG_FILES="$CONFIG_FILES dist/Makefile" ;; + "dist/rpm/Makefile" ) CONFIG_FILES="$CONFIG_FILES dist/rpm/Makefile" ;; + "dist/rpm/synergy.spec" ) CONFIG_FILES="$CONFIG_FILES dist/rpm/synergy.spec" ;; + "doc/doxygen.cfg" ) CONFIG_FILES="$CONFIG_FILES doc/doxygen.cfg" ;; + "default-1" ) CONFIG_COMMANDS="$CONFIG_COMMANDS default-1" ;; + "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + *) { { echo "$as_me:6894: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. +: ${TMPDIR=/tmp} +{ + tmp=`(umask 077 && mktemp -d -q "$TMPDIR/csXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=$TMPDIR/cs$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in $TMPDIR" >&2 + { (exit 1); exit 1; } +} + +EOF + +cat >>$CONFIG_STATUS <\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@DEFS@,$DEFS,;t t +s,@LIBS@,$LIBS,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@PACKAGE@,$PACKAGE,;t t +s,@VERSION@,$VERSION,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@ACLOCAL@,$ACLOCAL,;t t +s,@AUTOCONF@,$AUTOCONF,;t t +s,@AUTOMAKE@,$AUTOMAKE,;t t +s,@AUTOHEADER@,$AUTOHEADER,;t t +s,@MAKEINFO@,$MAKEINFO,;t t +s,@AMTAR@,$AMTAR,;t t +s,@install_sh@,$install_sh,;t t +s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t +s,@AWK@,$AWK,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t +s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t +s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t +s,@DEPDIR@,$DEPDIR,;t t +s,@CXX@,$CXX,;t t +s,@CXXFLAGS@,$CXXFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CXX@,$ac_ct_CXX,;t t +s,@am__include@,$am__include,;t t +s,@am__quote@,$am__quote,;t t +s,@CXXDEPMODE@,$CXXDEPMODE,;t t +s,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@HAVE_DOT@,$HAVE_DOT,;t t +s,@build@,$build,;t t +s,@build_cpu@,$build_cpu,;t t +s,@build_vendor@,$build_vendor,;t t +s,@build_os@,$build_os,;t t +s,@host@,$host,;t t +s,@host_cpu@,$host_cpu,;t t +s,@host_vendor@,$host_vendor,;t t +s,@host_os@,$host_os,;t t +s,@PTHREAD_CC@,$PTHREAD_CC,;t t +s,@PTHREAD_LIBS@,$PTHREAD_LIBS,;t t +s,@PTHREAD_CFLAGS@,$PTHREAD_CFLAGS,;t t +s,@NANOSLEEP_LIBS@,$NANOSLEEP_LIBS,;t t +s,@INET_ATON_LIBS@,$INET_ATON_LIBS,;t t +s,@CXXCPP@,$CXXCPP,;t t +s,@X_CFLAGS@,$X_CFLAGS,;t t +s,@X_PRE_LIBS@,$X_PRE_LIBS,;t t +s,@X_LIBS@,$X_LIBS,;t t +s,@X_EXTRA_LIBS@,$X_EXTRA_LIBS,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +CEOF + +EOF + + cat >>$CONFIG_STATUS <<\EOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +EOF +cat >>$CONFIG_STATUS <<\EOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + { case "$ac_dir" in + [\\/]* | ?:[\\/]* ) as_incr_dir=;; + *) as_incr_dir=.;; +esac +as_dummy="$ac_dir" +for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do + case $as_mkdir_dir in + # Skip DOS drivespec + ?:) as_incr_dir=$as_mkdir_dir ;; + *) + as_incr_dir=$as_incr_dir/$as_mkdir_dir + test -d "$as_incr_dir" || mkdir "$as_incr_dir" + ;; + esac +done; } + + ac_dir_suffix="/`echo $ac_dir|sed 's,^\./,,'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo "$ac_dir_suffix" | sed 's,/[^/]*,../,g'` + else + ac_dir_suffix= ac_dots= + fi + + case $srcdir in + .) ac_srcdir=. + if test -z "$ac_dots"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_dots | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_dots$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_dots$srcdir ;; + esac + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_dots$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:7141: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated automatically by config.status. */ + configure_input="Generated automatically from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:7159: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:7172: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +EOF +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +EOF +cat >>$CONFIG_STATUS <<\EOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:7233: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:7244: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:7257: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +EOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\EOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\(\([^ (][^ (]*\)([^)]*)\)[ ]*\(.*\)$,${ac_dA}\2${ac_dB}\1${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +EOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\EOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +EOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if egrep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # egrep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\EOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated automatically by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated automatically by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated automatically by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if cmp -s $ac_file $tmp/config.h 2>/dev/null; then + { echo "$as_me:7374: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + { case "$ac_dir" in + [\\/]* | ?:[\\/]* ) as_incr_dir=;; + *) as_incr_dir=.;; +esac +as_dummy="$ac_dir" +for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do + case $as_mkdir_dir in + # Skip DOS drivespec + ?:) as_incr_dir=$as_mkdir_dir ;; + *) + as_incr_dir=$as_incr_dir/$as_mkdir_dir + test -d "$as_incr_dir" || mkdir "$as_incr_dir" + ;; + esac +done; } + + fi + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi + # Run the commands associated with the file. + case $ac_file in + config.h ) # update the timestamp +echo timestamp >"./stamp-h1" + ;; + esac +done +EOF +cat >>$CONFIG_STATUS <<\EOF + +# +# CONFIG_COMMANDS section. +# +for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue + ac_dest=`echo "$ac_file" | sed 's,:.*,,'` + ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'` + + case $ac_dest in + default-1 ) +test x"$AMDEP_TRUE" != x"" || +for mf in $CONFIG_FILES; do + case "$mf" in + Makefile) dirpart=.;; + */Makefile) dirpart=`echo "$mf" | sed -e 's|/[^/]*$||'`;; + *) continue;; + esac + grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue + # Extract the definition of DEP_FILES from the Makefile without + # running `make'. + DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"` + test -z "$DEPDIR" && continue + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n -e '/^U = / s///p' < "$mf"` + test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" + # We invoke sed twice because it is the simplest approach to + # changing $(DEPDIR) to its actual value in the expansion. + for file in `sed -n -e ' + /^DEP_FILES = .*\\\\$/ { + s/^DEP_FILES = // + :loop + s/\\\\$// + p + n + /\\\\$/ b loop + p + } + /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`echo "$file" | sed -e 's|/[^/]*$||'` + $ac_aux_dir/mkinstalldirs "$dirpart/$fdir" > /dev/null 2>&1 + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done + ;; + esac +done +EOF + +cat >>$CONFIG_STATUS <<\EOF + +{ (exit 0); exit 0; } +EOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + exec 5>/dev/null + $SHELL $CONFIG_STATUS || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..22e44f7 --- /dev/null +++ b/configure.in @@ -0,0 +1,124 @@ +dnl synergy -- mouse and keyboard sharing utility +dnl Copyright (C) 2002 Chris Schoeneman +dnl +dnl This package is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU General Public License +dnl found in the file COPYING that should have accompanied this file. +dnl +dnl This package is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. + +dnl Process this file with autoconf to produce a configure script. + +dnl initialize +AC_INIT(lib/common/common.h) +AC_CONFIG_AUX_DIR(config) + +dnl current version +MAJOR_VERSION=1 +MINOR_VERSION=0 +RELEASE_VERSION=14 + +dnl initialize automake +AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) +AM_CONFIG_HEADER(config.h) + +dnl information on the package + +dnl checks for programs +AC_PROG_CXX +AC_PROG_RANLIB +AC_CHECK_PROG(HAVE_DOT, dot, YES, NO) + +dnl do checks using C++ +AC_LANG_CPLUSPLUS + +dnl our files end in .cpp not .C so tests should also end in .cpp +ac_ext=cpp + +dnl check compiler +ACX_CHECK_CXX + +dnl checks for libraries +ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) +ACX_CHECK_NANOSLEEP +ACX_CHECK_INET_ATON + +dnl checks for header files +AC_HEADER_STDC +AC_CHECK_HEADERS([unistd.h sys/time.h sys/types.h wchar.h]) +AC_CHECK_HEADERS([sys/socket.h sys/select.h]) +AC_CHECK_HEADERS([istream ostream sstream]) +AC_HEADER_TIME +AC_PATH_X +AC_PATH_XTRA +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$X_CFLAGS $CPPFLAGS" +AC_CHECK_HEADERS([X11/extensions/XTest.h]) +AC_CHECK_HEADERS([X11/XF86keysym.h]) + +AC_CHECK_LIB(Xinerama, XineramaQueryExtension, AC_CHECK_HEADERS([X11/extensions/Xinerama.h]) [X_LIBS="$X_LIBS -lXinerama"], , [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) +CPPFLAGS="$save_CPPFLAGS" + +dnl checks for types +AC_TYPE_SIZE_T +ACX_CHECK_SOCKLEN_T + +dnl checks for structures +AC_STRUCT_TM + +dnl checks for compiler characteristics +AC_CHECK_SIZEOF(char, 1) +AC_CHECK_SIZEOF(short, 2) +AC_CHECK_SIZEOF(int, 2) +AC_CHECK_SIZEOF(long, 4) +ACX_CHECK_CXX_BOOL(,AC_MSG_ERROR(Your compiler must support bool to compile synergy)) +ACX_CHECK_CXX_EXCEPTIONS(,AC_MSG_ERROR(Your compiler must support exceptions to compile synergy)) +ACX_CHECK_CXX_CASTS(,AC_MSG_ERROR(Your compiler must support C++ casts to compile synergy)) +ACX_CHECK_CXX_MUTABLE(,AC_MSG_ERROR(Your compiler must support mutable to compile synergy)) +ACX_CHECK_CXX_STDLIB(,AC_MSG_ERROR(Your compiler must support the C++ standard library to compile synergy)) + +dnl checks for library functions +dnl AC_TYPE_SIGNAL +AC_FUNC_MEMCMP +AC_FUNC_STRFTIME +AC_CHECK_FUNCS(gmtime_r) +ACX_CHECK_GETPWUID_R +AC_CHECK_FUNCS(vsnprintf) +AC_CHECK_FUNCS(wcrtomb mbrtowc mbsinit) +AC_FUNC_SELECT_ARGTYPES +ACX_CHECK_POLL +dnl use AC_REPLACE_FUNCS() for stuff in string.h + +dnl checks for system services + + +dnl adjust variables for X11 and pthreads +CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" +LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $PTHREAD_LIBS $LIBS" + +AC_OUTPUT([ +Makefile +lib/Makefile +lib/arch/Makefile +lib/base/Makefile +lib/common/Makefile +lib/mt/Makefile +lib/io/Makefile +lib/http/Makefile +lib/net/Makefile +lib/synergy/Makefile +lib/platform/Makefile +lib/client/Makefile +lib/server/Makefile +cmd/Makefile +cmd/launcher/Makefile +cmd/synergyc/Makefile +cmd/synergys/Makefile +dist/Makefile +dist/rpm/Makefile +dist/rpm/synergy.spec +doc/doxygen.cfg +]) diff --git a/dist/Makefile.am b/dist/Makefile.am new file mode 100644 index 0000000..bf906cb --- /dev/null +++ b/dist/Makefile.am @@ -0,0 +1,27 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + rpm \ + $(NULL) + +EXTRA_DIST = \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/dist/Makefile.in b/dist/Makefile.in new file mode 100644 index 0000000..7062214 --- /dev/null +++ b/dist/Makefile.in @@ -0,0 +1,348 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + rpm \ + $(NULL) + + +EXTRA_DIST = \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +subdir = dist +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu dist/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive distclean distclean-generic \ + distclean-recursive distclean-tags distdir dvi dvi-am \ + dvi-recursive info info-am info-recursive install install-am \ + install-data install-data-am install-data-recursive \ + install-exec install-exec-am install-exec-recursive \ + install-info install-info-am install-info-recursive install-man \ + install-recursive install-strip installcheck installcheck-am \ + installdirs installdirs-am installdirs-recursive \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive tags tags-recursive uninstall \ + uninstall-am uninstall-info-am uninstall-info-recursive \ + uninstall-recursive + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/dist/rpm/Makefile.am b/dist/rpm/Makefile.am new file mode 100644 index 0000000..1f0c046 --- /dev/null +++ b/dist/rpm/Makefile.am @@ -0,0 +1,23 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/dist/rpm/Makefile.in b/dist/rpm/Makefile.in new file mode 100644 index 0000000..77f9c60 --- /dev/null +++ b/dist/rpm/Makefile.in @@ -0,0 +1,228 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +subdir = dist/rpm +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = synergy.spec +DIST_SOURCES = +DIST_COMMON = Makefile.am Makefile.in synergy.spec.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu dist/rpm/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +synergy.spec: $(top_builddir)/config.status synergy.spec.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= CONFIG_LINKS= $(SHELL) ./config.status +uninstall-info-am: +tags: TAGS +TAGS: + + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in new file mode 100644 index 0000000..073f2f6 --- /dev/null +++ b/dist/rpm/synergy.spec.in @@ -0,0 +1,48 @@ +Summary: Mouse and keyboard sharing utility +Name: @PACKAGE@ +Version: @VERSION@ +Release: 1 +License: GPL +Packager: Chris Schoeneman +Group: System Environment/Daemons +Prefixes: /usr/bin +Source: @PACKAGE@-@VERSION@.tar.gz +Buildroot: /var/tmp/@PACKAGE@-@VERSION@-root + +%description +Synergy lets you easily share a single mouse and keyboard between +multiple computers with different operating systems, each with its +own display, without special hardware. It's intended for users +with multiple computers on their desk since each system uses its +own display. + +%prep +%setup +CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=/usr + +%build +make + +%install +make install DESTDIR=$RPM_BUILD_ROOT +strip $RPM_BUILD_ROOT/usr/bin/synergyc +strip $RPM_BUILD_ROOT/usr/bin/synergys + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-, root, root) +/usr/bin/synergyc +/usr/bin/synergys +%doc AUTHORS +%doc BUGS +%doc COPYING +%doc ChangeLog +%doc FAQ +%doc HISTORY +%doc INSTALL +%doc NEWS +%doc README +%doc TODO +%doc examples/synergy.conf diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in new file mode 100644 index 0000000..4abf52f --- /dev/null +++ b/doc/doxygen.cfg.in @@ -0,0 +1,898 @@ +# Doxyfile 1.2.13.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = @PACKAGE@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @VERSION@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc/doxygen + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French, +# German, Greek, Hungarian, Italian, Japanese, Korean, Norwegian, Polish, +# Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish. + +OUTPUT_LANGUAGE = English + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower case letters. If set to YES upper case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consist of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl + +FILE_PATTERNS = *.cpp *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse. + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 3 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the Html help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, +# or Internet explorer 4.0+). Note that for large projects the tree generation +# can take a very long time. In such cases it is better to disable this feature. +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line and do not end with a semicolon. Such function macros are typically +# used for boiler-plate code, and will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yield more powerful graphs. + +CLASS_DIAGRAMS = NO + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = @HAVE_DOT@ + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermedate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/examples/synergy.conf b/examples/synergy.conf new file mode 100644 index 0000000..2586dfa --- /dev/null +++ b/examples/synergy.conf @@ -0,0 +1,37 @@ +# sample synergy configuration file +# +# comments begin with the # character and continue to the end of +# line. comments may appear anywhere the syntax permits. + +section: screens + # three hosts named: moe, larry, and curly + moe: + larry: + curly: +end + +section: links + # larry is to the right of moe and curly is above moe + moe: + right = larry + up = curly + + # moe is to the left of larry and curly is above larry. + # note that curly is above both moe and larry and moe + # and larry have a symmetric connection (they're in + # opposite directions of each other). + larry: + left = moe + up = curly + + # larry is below curly. if you move up from moe and then + # down, you'll end up on larry. + curly: + down = larry +end + +section: aliases + # curly is also known as shemp + curly: + shemp +end diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..cababc3 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,37 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + common \ + arch \ + base \ + mt \ + io \ + http \ + net \ + synergy \ + platform \ + client \ + server \ + $(NULL) + +EXTRA_DIST = \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..47c129c --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,358 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + common \ + arch \ + base \ + mt \ + io \ + http \ + net \ + synergy \ + platform \ + client \ + server \ + $(NULL) + + +EXTRA_DIST = \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +subdir = lib +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive distclean distclean-generic \ + distclean-recursive distclean-tags distdir dvi dvi-am \ + dvi-recursive info info-am info-recursive install install-am \ + install-data install-data-am install-data-recursive \ + install-exec install-exec-am install-exec-recursive \ + install-info install-info-am install-info-recursive install-man \ + install-recursive install-strip installcheck installcheck-am \ + installdirs installdirs-am installdirs-recursive \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive tags tags-recursive uninstall \ + uninstall-am uninstall-info-am uninstall-info-recursive \ + uninstall-recursive + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp new file mode 100644 index 0000000..b8322c3 --- /dev/null +++ b/lib/arch/CArch.cpp @@ -0,0 +1,611 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "common.h" +#include "CArch.h" + +#undef ARCH_CONSOLE +#undef ARCH_DAEMON +#undef ARCH_FILE +#undef ARCH_LOG +#undef ARCH_MULTITHREAD +#undef ARCH_NETWORK +#undef ARCH_SLEEP +#undef ARCH_STRING +#undef ARCH_TASKBAR +#undef ARCH_TIME + +// include appropriate architecture implementation +#if WINDOWS_LIKE +# include "CArchConsoleWindows.h" +# include "CArchDaemonWindows.h" +# include "CArchFileWindows.h" +# include "CArchLogWindows.h" +# include "CArchMultithreadWindows.h" +# include "CArchNetworkWinsock.h" +# include "CArchSleepWindows.h" +# include "CArchStringWindows.h" +# include "CArchTaskBarWindows.h" +# include "CArchTimeWindows.h" +#elif UNIX_LIKE +# include "CArchConsoleUnix.h" +# include "CArchDaemonUnix.h" +# include "CArchFileUnix.h" +# include "CArchLogUnix.h" +# if HAVE_PTHREAD +# include "CArchMultithreadPosix.h" +# endif +# include "CArchNetworkBSD.h" +# include "CArchSleepUnix.h" +# include "CArchStringUnix.h" +# include "CArchTaskBarXWindows.h" +# include "CArchTimeUnix.h" +#endif + +#if !defined(ARCH_CONSOLE) +# error unsupported platform for console +#endif + +#if !defined(ARCH_DAEMON) +# error unsupported platform for daemon +#endif + +#if !defined(ARCH_FILE) +# error unsupported platform for file +#endif + +#if !defined(ARCH_LOG) +# error unsupported platform for logging +#endif + +#if !defined(ARCH_MULTITHREAD) +# error unsupported platform for multithreading +#endif + +#if !defined(ARCH_NETWORK) +# error unsupported platform for network +#endif + +#if !defined(ARCH_SLEEP) +# error unsupported platform for sleep +#endif + +#if !defined(ARCH_STRING) +# error unsupported platform for string +#endif + +#if !defined(ARCH_TASKBAR) +# error unsupported platform for taskbar +#endif + +#if !defined(ARCH_TIME) +# error unsupported platform for time +#endif + +// +// CArch +// + +CArch* CArch::s_instance = NULL; + +CArch::CArch(ARCH_ARGS* args) +{ + // only once instance of CArch + assert(s_instance == NULL); + s_instance = this; + + // create architecture implementation objects + m_mt = new ARCH_MULTITHREAD; + m_file = new ARCH_FILE; + m_log = new ARCH_LOG; + m_net = new ARCH_NETWORK; + m_sleep = new ARCH_SLEEP; + m_string = new ARCH_STRING; + m_time = new ARCH_TIME; + m_console = new ARCH_CONSOLE; + m_daemon = new ARCH_DAEMON; + m_taskbar = new ARCH_TASKBAR(args); +} + +CArch::~CArch() +{ + // clean up + delete m_taskbar; + delete m_daemon; + delete m_console; + delete m_time; + delete m_string; + delete m_sleep; + delete m_net; + delete m_log; + delete m_file; + delete m_mt; + + // no instance + s_instance = NULL; +} + +CArch* +CArch::getInstance() +{ + assert(s_instance != NULL); + + return s_instance; +} + +void +CArch::openConsole(const char* title) +{ + m_console->openConsole(title); +} + +void +CArch::closeConsole() +{ + m_console->closeConsole(); +} + +void +CArch::writeConsole(const char* str) +{ + m_console->writeConsole(str); +} + +const char* +CArch::getNewlineForConsole() +{ + return m_console->getNewlineForConsole(); +} + +void +CArch::installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) +{ + m_daemon->installDaemon(name, description, pathname, commandLine, allUsers); +} + +void +CArch::uninstallDaemon(const char* name, bool allUsers) +{ + m_daemon->uninstallDaemon(name, allUsers); +} + +int +CArch::daemonize(const char* name, DaemonFunc func) +{ + return m_daemon->daemonize(name, func); +} + +bool +CArch::canInstallDaemon(const char* name, bool allUsers) +{ + return m_daemon->canInstallDaemon(name, allUsers); +} + +bool +CArch::isDaemonInstalled(const char* name, bool allUsers) +{ + return m_daemon->isDaemonInstalled(name, allUsers); +} + +const char* +CArch::getBasename(const char* pathname) +{ + return m_file->getBasename(pathname); +} + +std::string +CArch::getUserDirectory() +{ + return m_file->getUserDirectory(); +} + +std::string +CArch::getSystemDirectory() +{ + return m_file->getSystemDirectory(); +} + +std::string +CArch::concatPath(const std::string& prefix, const std::string& suffix) +{ + return m_file->concatPath(prefix, suffix); +} + +void +CArch::openLog(const char* name) +{ + m_log->openLog(name); +} + +void +CArch::closeLog() +{ + m_log->closeLog(); +} + +void +CArch::writeLog(ELevel level, const char* msg) +{ + m_log->writeLog(level, msg); +} + +CArchCond +CArch::newCondVar() +{ + return m_mt->newCondVar(); +} + +void +CArch::closeCondVar(CArchCond cond) +{ + m_mt->closeCondVar(cond); +} + +void +CArch::signalCondVar(CArchCond cond) +{ + m_mt->signalCondVar(cond); +} + +void +CArch::broadcastCondVar(CArchCond cond) +{ + m_mt->broadcastCondVar(cond); +} + +bool +CArch::waitCondVar(CArchCond cond, CArchMutex mutex, double timeout) +{ + return m_mt->waitCondVar(cond, mutex, timeout); +} + +CArchMutex +CArch::newMutex() +{ + return m_mt->newMutex(); +} + +void +CArch::closeMutex(CArchMutex mutex) +{ + m_mt->closeMutex(mutex); +} + +void +CArch::lockMutex(CArchMutex mutex) +{ + m_mt->lockMutex(mutex); +} + +void +CArch::unlockMutex(CArchMutex mutex) +{ + m_mt->unlockMutex(mutex); +} + +CArchThread +CArch::newThread(ThreadFunc func, void* data) +{ + return m_mt->newThread(func, data); +} + +CArchThread +CArch::newCurrentThread() +{ + return m_mt->newCurrentThread(); +} + +CArchThread +CArch::copyThread(CArchThread thread) +{ + return m_mt->copyThread(thread); +} + +void +CArch::closeThread(CArchThread thread) +{ + m_mt->closeThread(thread); +} + +void +CArch::cancelThread(CArchThread thread) +{ + m_mt->cancelThread(thread); +} + +void +CArch::setPriorityOfThread(CArchThread thread, int n) +{ + m_mt->setPriorityOfThread(thread, n); +} + +void +CArch::testCancelThread() +{ + m_mt->testCancelThread(); +} + +bool +CArch::wait(CArchThread thread, double timeout) +{ + return m_mt->wait(thread, timeout); +} + +IArchMultithread::EWaitResult +CArch::waitForEvent(CArchThread thread, double timeout) +{ + return m_mt->waitForEvent(thread, timeout); +} + +bool +CArch::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return m_mt->isSameThread(thread1, thread2); +} + +bool +CArch::isExitedThread(CArchThread thread) +{ + return m_mt->isExitedThread(thread); +} + +void* +CArch::getResultOfThread(CArchThread thread) +{ + return m_mt->getResultOfThread(thread); +} + +IArchMultithread::ThreadID +CArch::getIDOfThread(CArchThread thread) +{ + return m_mt->getIDOfThread(thread); +} + +CArchSocket +CArch::newSocket(EAddressFamily family, ESocketType type) +{ + return m_net->newSocket(family, type); +} + +CArchSocket +CArch::copySocket(CArchSocket s) +{ + return m_net->copySocket(s); +} + +void +CArch::closeSocket(CArchSocket s) +{ + m_net->closeSocket(s); +} + +void +CArch::closeSocketForRead(CArchSocket s) +{ + m_net->closeSocketForRead(s); +} + +void +CArch::closeSocketForWrite(CArchSocket s) +{ + m_net->closeSocketForWrite(s); +} + +void +CArch::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + m_net->bindSocket(s, addr); +} + +void +CArch::listenOnSocket(CArchSocket s) +{ + m_net->listenOnSocket(s); +} + +CArchSocket +CArch::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + return m_net->acceptSocket(s, addr); +} + +void +CArch::connectSocket(CArchSocket s, CArchNetAddress name) +{ + m_net->connectSocket(s, name); +} + +int +CArch::pollSocket(CPollEntry pe[], int num, double timeout) +{ + return m_net->pollSocket(pe, num, timeout); +} + +size_t +CArch::readSocket(CArchSocket s, void* buf, size_t len) +{ + return m_net->readSocket(s, buf, len); +} + +size_t +CArch::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + return m_net->writeSocket(s, buf, len); +} + +void +CArch::throwErrorOnSocket(CArchSocket s) +{ + m_net->throwErrorOnSocket(s); +} + +bool +CArch::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + return m_net->setBlockingOnSocket(s, blocking); +} + +bool +CArch::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + return m_net->setNoDelayOnSocket(s, noDelay); +} + +std::string +CArch::getHostName() +{ + return m_net->getHostName(); +} + +CArchNetAddress +CArch::newAnyAddr(EAddressFamily family) +{ + return m_net->newAnyAddr(family); +} + +CArchNetAddress +CArch::copyAddr(CArchNetAddress addr) +{ + return m_net->copyAddr(addr); +} + +CArchNetAddress +CArch::nameToAddr(const std::string& name) +{ + return m_net->nameToAddr(name); +} + +void +CArch::closeAddr(CArchNetAddress addr) +{ + m_net->closeAddr(addr); +} + +std::string +CArch::addrToName(CArchNetAddress addr) +{ + return m_net->addrToName(addr); +} + +std::string +CArch::addrToString(CArchNetAddress addr) +{ + return m_net->addrToString(addr); +} + +IArchNetwork::EAddressFamily +CArch::getAddrFamily(CArchNetAddress addr) +{ + return m_net->getAddrFamily(addr); +} + +void +CArch::setAddrPort(CArchNetAddress addr, int port) +{ + m_net->setAddrPort(addr, port); +} + +int +CArch::getAddrPort(CArchNetAddress addr) +{ + return m_net->getAddrPort(addr); +} + +bool +CArch::isAnyAddr(CArchNetAddress addr) +{ + return m_net->isAnyAddr(addr); +} + +void +CArch::sleep(double timeout) +{ + m_sleep->sleep(timeout); +} + +int +CArch::vsnprintf(char* str, int size, const char* fmt, va_list ap) +{ + return m_string->vsnprintf(str, size, fmt, ap); +} + +CArchMBState +CArch::newMBState() +{ + return m_string->newMBState(); +} + +void +CArch::closeMBState(CArchMBState state) +{ + m_string->closeMBState(state); +} + +void +CArch::initMBState(CArchMBState state) +{ + m_string->initMBState(state); +} + +bool +CArch::isInitMBState(CArchMBState state) +{ + return m_string->isInitMBState(state); +} + +int +CArch::convMBToWC(wchar_t* dst, const char* src, int n, CArchMBState state) +{ + return m_string->convMBToWC(dst, src, n, state); +} + +int +CArch::convWCToMB(char* dst, wchar_t src, CArchMBState state) +{ + return m_string->convWCToMB(dst, src, state); +} + +IArchString::EWideCharEncoding +CArch::getWideCharEncoding() +{ + return m_string->getWideCharEncoding(); +} + +void +CArch::addReceiver(IArchTaskBarReceiver* receiver) +{ + m_taskbar->addReceiver(receiver); +} + +void +CArch::removeReceiver(IArchTaskBarReceiver* receiver) +{ + m_taskbar->removeReceiver(receiver); +} + +void +CArch::updateReceiver(IArchTaskBarReceiver* receiver) +{ + m_taskbar->updateReceiver(receiver); +} + +double +CArch::time() +{ + return m_time->time(); +} diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h new file mode 100644 index 0000000..61315b6 --- /dev/null +++ b/lib/arch/CArch.h @@ -0,0 +1,192 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCH_H +#define CARCH_H + +#include "IArchConsole.h" +#include "IArchDaemon.h" +#include "IArchFile.h" +#include "IArchLog.h" +#include "IArchMultithread.h" +#include "IArchNetwork.h" +#include "IArchSleep.h" +#include "IArchString.h" +#include "IArchTaskBar.h" +#include "IArchTime.h" + +/*! +\def ARCH +This macro evaluates to the singleton CArch object. +*/ +#define ARCH (CArch::getInstance()) + +#define ARCH_ARGS void + +//! Delegating mplementation of architecture dependent interfaces +/*! +This class is a centralized interface to all architecture dependent +interface implementations (except miscellaneous functions). It +instantiates an implementation of each interface and delegates calls +to each method to those implementations. Clients should use the +\c ARCH macro to access this object. Clients must also instantiate +exactly one of these objects before attempting to call any method, +typically at the beginning of \c main(). +*/ +class CArch : public IArchConsole, + public IArchDaemon, + public IArchFile, + public IArchLog, + public IArchMultithread, + public IArchNetwork, + public IArchSleep, + public IArchString, + public IArchTaskBar, + public IArchTime { +public: + CArch(ARCH_ARGS* args = NULL); + ~CArch(); + + // + // accessors + // + + //! Return the singleton instance + /*! + The client must have instantiated exactly once CArch object before + calling this function. + */ + static CArch* getInstance(); + + // IArchConsole overrides + virtual void openConsole(const char*); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); + + // IArchLog overrides + virtual void openLog(const char*); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual EWaitResult waitForEvent(CArchThread, double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + + // IArchSleep overrides + virtual void sleep(double timeout); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual EWideCharEncoding + getWideCharEncoding(); + + // IArchTaskBar + virtual void addReceiver(IArchTaskBarReceiver*); + virtual void removeReceiver(IArchTaskBarReceiver*); + virtual void updateReceiver(IArchTaskBarReceiver*); + + // IArchTime overrides + virtual double time(); + +private: + static CArch* s_instance; + + IArchConsole* m_console; + IArchDaemon* m_daemon; + IArchFile* m_file; + IArchLog* m_log; + IArchMultithread* m_mt; + IArchNetwork* m_net; + IArchSleep* m_sleep; + IArchString* m_string; + IArchTaskBar* m_taskbar; + IArchTime* m_time; +}; + +#endif diff --git a/lib/arch/CArchConsoleUnix.cpp b/lib/arch/CArchConsoleUnix.cpp new file mode 100644 index 0000000..79c4ae2 --- /dev/null +++ b/lib/arch/CArchConsoleUnix.cpp @@ -0,0 +1,54 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchConsoleUnix.h" +#include + +// +// CArchConsoleUnix +// + +CArchConsoleUnix::CArchConsoleUnix() +{ + // do nothing +} + +CArchConsoleUnix::~CArchConsoleUnix() +{ + // do nothing +} + +void +CArchConsoleUnix::openConsole(const char*) +{ + // do nothing +} + +void +CArchConsoleUnix::closeConsole() +{ + // do nothing +} + +void +CArchConsoleUnix::writeConsole(const char* str) +{ + fprintf(stderr, "%s", str); +} + +const char* +CArchConsoleUnix::getNewlineForConsole() +{ + return "\n"; +} diff --git a/lib/arch/CArchConsoleUnix.h b/lib/arch/CArchConsoleUnix.h new file mode 100644 index 0000000..5e560fe --- /dev/null +++ b/lib/arch/CArchConsoleUnix.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHCONSOLEUNIX_H +#define CARCHCONSOLEUNIX_H + +#include "IArchConsole.h" + +#define ARCH_CONSOLE CArchConsoleUnix + +//! Unix implementation of IArchConsole +class CArchConsoleUnix : public IArchConsole { +public: + CArchConsoleUnix(); + virtual ~CArchConsoleUnix(); + + // IArchConsole overrides + virtual void openConsole(const char* title); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); +}; + +#endif diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp new file mode 100644 index 0000000..a89a42f --- /dev/null +++ b/lib/arch/CArchConsoleWindows.cpp @@ -0,0 +1,109 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchConsoleWindows.h" +#include "IArchMultithread.h" +#include "CArch.h" +#include + +// +// CArchConsoleWindows +// + +CArchThread CArchConsoleWindows::s_thread = 0; + +CArchConsoleWindows::CArchConsoleWindows() : + m_output(NULL) +{ + s_thread = ARCH->newCurrentThread(); + + m_mutex = ARCH->newMutex(); +} + +CArchConsoleWindows::~CArchConsoleWindows() +{ + ARCH->closeMutex(m_mutex); + ARCH->closeThread(s_thread); +} + +void +CArchConsoleWindows::openConsole(const char* title) +{ + ARCH->lockMutex(m_mutex); + if (m_output == NULL) { + if (AllocConsole()) { + // get console output handle + m_output = GetStdHandle(STD_ERROR_HANDLE); + + // set console title + if (title != NULL) { + SetConsoleTitle(title); + } + + // prep console. windows 95 and its ilk have braindead + // consoles that can't even resize independently of the + // buffer size. use a 25 line buffer for those systems. + OSVERSIONINFO osInfo; + COORD size = { 80, 1000 }; + osInfo.dwOSVersionInfoSize = sizeof(osInfo); + if (GetVersionEx(&osInfo) && + osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + size.Y = 25; + SetConsoleScreenBufferSize(m_output, size); + SetConsoleTextAttribute(m_output, + FOREGROUND_RED | + FOREGROUND_GREEN | + FOREGROUND_BLUE); + + // catch console signals + SetConsoleCtrlHandler(&CArchConsoleWindows::signalHandler, TRUE); + + // reopen stderr to point at console + freopen("con", "w", stderr); + } + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchConsoleWindows::closeConsole() +{ + ARCH->lockMutex(m_mutex); + if (m_output != NULL) { + if (FreeConsole()) { + m_output = NULL; + } + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchConsoleWindows::writeConsole(const char* str) +{ + fprintf(stderr, "%s", str); +} + +const char* +CArchConsoleWindows::getNewlineForConsole() +{ + return "\r\n"; +} + +BOOL WINAPI +CArchConsoleWindows::signalHandler(DWORD) +{ + // terminate thread and skip remaining handlers + ARCH->cancelThread(s_thread); + return TRUE; +} diff --git a/lib/arch/CArchConsoleWindows.h b/lib/arch/CArchConsoleWindows.h new file mode 100644 index 0000000..bf9f0a3 --- /dev/null +++ b/lib/arch/CArchConsoleWindows.h @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHCONSOLEWINDOWS_H +#define CARCHCONSOLEWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchConsole.h" +#include "IArchMultithread.h" +#include + +#define ARCH_CONSOLE CArchConsoleWindows + +//! Win32 implementation of IArchConsole +class CArchConsoleWindows : public IArchConsole { +public: + CArchConsoleWindows(); + virtual ~CArchConsoleWindows(); + + // IArchConsole overrides + virtual void openConsole(const char* title); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); + +private: + static BOOL WINAPI signalHandler(DWORD); + +private: + static CArchThread s_thread; + + CArchMutex m_mutex; + HANDLE m_output; +}; + +#endif diff --git a/lib/arch/CArchDaemonNone.cpp b/lib/arch/CArchDaemonNone.cpp new file mode 100644 index 0000000..35a5823 --- /dev/null +++ b/lib/arch/CArchDaemonNone.cpp @@ -0,0 +1,65 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchDaemonNone.h" + +// +// CArchDaemonNone +// + +CArchDaemonNone::CArchDaemonNone() +{ + // do nothing +} + +CArchDaemonNone::~CArchDaemonNone() +{ + // do nothing +} + +void +CArchDaemonNone::installDaemon(const char*, + const char*, + const char*, + const char*, + bool) +{ + // do nothing +} + +void +CArchDaemonNone::uninstallDaemon(const char*, bool) +{ + // do nothing +} + +int +CArchDaemonNone::daemonize(const char* name, DaemonFunc func) +{ + // simply forward the call to func. obviously, this doesn't + // do any daemonizing. + return func(1, &name); +} + +bool +CArchDaemonNone::canInstallDaemon(const char*, bool) +{ + return false; +} + +bool +CArchDaemonNone::isDaemonInstalled(const char* name, bool allUsers) +{ + return false; +} diff --git a/lib/arch/CArchDaemonNone.h b/lib/arch/CArchDaemonNone.h new file mode 100644 index 0000000..5b54060 --- /dev/null +++ b/lib/arch/CArchDaemonNone.h @@ -0,0 +1,46 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONNONE_H +#define CARCHDAEMONNONE_H + +#include "IArchDaemon.h" + +#define ARCH_DAEMON CArchDaemonNone + +//! Dummy implementation of IArchDaemon +/*! +This class implements IArchDaemon for a platform that does not have +daemons. The install and uninstall functions do nothing, the query +functions return false, and \c daemonize() simply calls the passed +function and returns its result. +*/ +class CArchDaemonNone : public IArchDaemon { +public: + CArchDaemonNone(); + virtual ~CArchDaemonNone(); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); +}; + +#endif diff --git a/lib/arch/CArchDaemonUnix.cpp b/lib/arch/CArchDaemonUnix.cpp new file mode 100644 index 0000000..748780d --- /dev/null +++ b/lib/arch/CArchDaemonUnix.cpp @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchDaemonUnix.h" +#include "XArchUnix.h" +#include +#include +#include +#include +#include + +// we derive from CArchDaemonNone +#include "CArchDaemonNone.cpp" + +// +// CArchDaemonUnix +// + +CArchDaemonUnix::CArchDaemonUnix() +{ + // do nothing +} + +CArchDaemonUnix::~CArchDaemonUnix() +{ + // do nothing +} + +int +CArchDaemonUnix::daemonize(const char* name, DaemonFunc func) +{ + // fork so shell thinks we're done and so we're not a process + // group leader + switch (fork()) { + case -1: + // failed + throw XArchDaemonFailed(new XArchEvalUnix(errno)); + + case 0: + // child + break; + + default: + // parent exits + exit(0); + } + + // become leader of a new session + setsid(); + + // chdir to root so we don't keep mounted filesystems points busy + chdir("/"); + + // mask off permissions for any but owner + umask(077); + + // close open files. we only expect stdin, stdout, stderr to be open. + close(0); + close(1); + close(2); + + // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use + // of standard I/O safely goes in the bit bucket. + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDWR); + dup(1); + + // invoke function + return func(1, &name); +} diff --git a/lib/arch/CArchDaemonUnix.h b/lib/arch/CArchDaemonUnix.h new file mode 100644 index 0000000..923004e --- /dev/null +++ b/lib/arch/CArchDaemonUnix.h @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONUNIX_H +#define CARCHDAEMONUNIX_H + +#include "CArchDaemonNone.h" + +#undef ARCH_DAEMON +#define ARCH_DAEMON CArchDaemonUnix + +//! Unix implementation of IArchDaemon +class CArchDaemonUnix : public CArchDaemonNone { +public: + CArchDaemonUnix(); + virtual ~CArchDaemonUnix(); + + // IArchDaemon overrides + virtual int daemonize(const char* name, DaemonFunc func); +}; + +#endif diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp new file mode 100644 index 0000000..54dc5b4 --- /dev/null +++ b/lib/arch/CArchDaemonWindows.cpp @@ -0,0 +1,801 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchDaemonWindows.h" +#include "CArch.h" +#include "CArchMiscWindows.h" +#include "XArchWindows.h" +#include "stdvector.h" + +// +// CArchDaemonWindows +// + +CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL; + +CArchDaemonWindows::CArchDaemonWindows() : + m_daemonThread(NULL) +{ + // do nothing +} + +CArchDaemonWindows::~CArchDaemonWindows() +{ + // do nothing +} + +int +CArchDaemonWindows::runDaemon(RunFunc runFunc) +{ + assert(s_daemon != NULL); + + return s_daemon->doRunDaemon(runFunc); +} + +void +CArchDaemonWindows::daemonRunning(bool running) +{ + // if s_daemon is NULL we assume we're running on the windows + // 95 family and we just ignore this call so the caller doesn't + // have to go through the trouble of not calling it on the + // windows 95 family. + if (s_daemon != NULL) { + s_daemon->doDaemonRunning(running); + } +} + +void +CArchDaemonWindows::daemonFailed(int result) +{ + // if s_daemon is NULL we assume we're running on the windows + // 95 family and we just ignore this call so the caller doesn't + // have to go through the trouble of not calling it on the + // windows 95 family. + if (s_daemon != NULL) { + throw XArchDaemonRunFailed(result); + } +} + +void +CArchDaemonWindows::installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // open registry + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); + if (key == NULL) { + // can't open key + throw XArchDaemonInstallFailed(new XArchEvalWindows); + } + + // construct entry + std::string value; + value += "\""; + value += pathname; + value += "\" "; + value += commandLine; + + // install entry + CArchMiscWindows::setValue(key, name, value); + + // clean up + CArchMiscWindows::closeKey(key); + } + + // windows NT family services + else { + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + // can't open service manager + throw XArchDaemonInstallFailed(new XArchEvalWindows); + } + + // create the service + SC_HANDLE service = CreateService(mgr, + name, + name, + 0, + SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + pathname, + NULL, + NULL, + NULL, + NULL, + NULL); + if (service == NULL) { + // can't create service + // FIXME -- handle ERROR_SERVICE_EXISTS + DWORD err = GetLastError(); + CloseServiceHandle(mgr); + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } + + // done with service and manager + CloseServiceHandle(service); + CloseServiceHandle(mgr); + + // open the registry key for this service + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, name); + if (key == NULL) { + // can't open key + DWORD err = GetLastError(); + try { + uninstallDaemon(name, allUsers); + } + catch (...) { + // ignore + } + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } + + // set the description + CArchMiscWindows::setValue(key, _T("Description"), description); + + // set command line + key = CArchMiscWindows::openKey(key, _T("Parameters")); + if (key == NULL) { + // can't open key + DWORD err = GetLastError(); + CArchMiscWindows::closeKey(key); + try { + uninstallDaemon(name, allUsers); + } + catch (...) { + // ignore + } + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } + CArchMiscWindows::setValue(key, _T("CommandLine"), commandLine); + + // done with registry + CArchMiscWindows::closeKey(key); + } +} + +void +CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // open registry + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); + if (key == NULL) { + // can't open key. daemon is probably not installed. + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows); + } + + // remove entry + CArchMiscWindows::deleteValue(key, name); + + // clean up + CArchMiscWindows::closeKey(key); + } + + // windows NT family services + else { + // remove parameters for this service. ignore failures. + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, name); + if (key != NULL) { + CArchMiscWindows::deleteKey(key, _T("Parameters")); + CArchMiscWindows::closeKey(key); + } + + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + // can't open service manager + throw XArchDaemonUninstallFailed(new XArchEvalWindows); + } + + // open the service. oddly, you must open a service to delete it. + SC_HANDLE service = OpenService(mgr, name, DELETE); + if (service == NULL) { + DWORD err = GetLastError(); + CloseServiceHandle(mgr); + if (err != ERROR_SERVICE_DOES_NOT_EXIST) { + throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); + } + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); + } + + // delete the service + const bool okay = (DeleteService(service) == 0); + const DWORD err = GetLastError(); + + // clean up + CloseServiceHandle(service); + CloseServiceHandle(mgr); + + // handle failure. ignore error if service isn't installed anymore. + if (!okay && isDaemonInstalled(name, allUsers)) { + if (err != ERROR_SERVICE_MARKED_FOR_DELETE) { + throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); + } + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); + } + } +} + +int +CArchDaemonWindows::daemonize(const char* name, DaemonFunc func) +{ + assert(name != NULL); + assert(func != NULL); + + // windows 95 family services + if (CArchMiscWindows::isWindows95Family()) { + typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD); + + // mark this process as a service so it's not killed when the + // user logs off. + HINSTANCE kernel = LoadLibrary("kernel32.dll"); + if (kernel == NULL) { + throw XArchDaemonFailed(new XArchEvalWindows); + } + RegisterServiceProcessT RegisterServiceProcess = + reinterpret_cast( + GetProcAddress(kernel, + "RegisterServiceProcess")); + if (RegisterServiceProcess == NULL) { + // missing RegisterServiceProcess function + DWORD err = GetLastError(); + FreeLibrary(kernel); + throw XArchDaemonFailed(new XArchEvalWindows(err)); + } + if (RegisterServiceProcess(NULL, 1) == 0) { + // RegisterServiceProcess failed + DWORD err = GetLastError(); + FreeLibrary(kernel); + throw XArchDaemonFailed(new XArchEvalWindows(err)); + } + FreeLibrary(kernel); + + // now simply call the daemon function + return func(1, &name); + } + + // windows NT family services + else { + // save daemon function + m_daemonFunc = func; + + // construct the service entry + SERVICE_TABLE_ENTRY entry[2]; + entry[0].lpServiceName = const_cast(name); + entry[0].lpServiceProc = &CArchDaemonWindows::serviceMainEntry; + entry[1].lpServiceName = NULL; + entry[1].lpServiceProc = NULL; + + // hook us up to the service control manager. this won't return + // (if successful) until the processes have terminated. + s_daemon = this; + if (StartServiceCtrlDispatcher(entry) == 0) { + // StartServiceCtrlDispatcher failed + s_daemon = NULL; + throw XArchDaemonFailed(new XArchEvalWindows); + } + + s_daemon = NULL; + return m_daemonResult; + } +} + +bool +CArchDaemonWindows::canInstallDaemon(const char* name, bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // check if we can open the registry key + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); + CArchMiscWindows::closeKey(key); + return (key != NULL); + } + + // windows NT family services + else { + // check if we can open service manager for write + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + return false; + } + CloseServiceHandle(mgr); + + // check if we can open the registry key for this service + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, name); + key = CArchMiscWindows::openKey(key, _T("Parameters")); + CArchMiscWindows::closeKey(key); + + return (key != NULL); + } +} + +bool +CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // check if we can open the registry key + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); + if (key == NULL) { + return false; + } + + // check for entry + const bool installed = !CArchMiscWindows::readValueString(key, + name).empty(); + + // clean up + CArchMiscWindows::closeKey(key); + + return installed; + } + + // windows NT family services + else { + // check parameters for this service + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, name); + key = CArchMiscWindows::openKey(key, _T("Parameters")); + if (key != NULL) { + const bool installed = !CArchMiscWindows::readValueString(key, + _T("CommandLine")).empty(); + CArchMiscWindows::closeKey(key); + if (!installed) { + return false; + } + } + + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ); + if (mgr == NULL) { + return false; + } + + // open the service + SC_HANDLE service = OpenService(mgr, name, GENERIC_READ); + + // clean up + if (service != NULL) { + CloseServiceHandle(service); + } + CloseServiceHandle(mgr); + + return (service != NULL); + } +} + +HKEY +CArchDaemonWindows::openNTServicesKey() +{ + static const char* s_keyNames[] = { + _T("SYSTEM"), + _T("CurrentControlSet"), + _T("Services"), + NULL + }; + + return CArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_keyNames); +} + +HKEY +CArchDaemonWindows::open95ServicesKey() +{ + static const char* s_keyNames[] = { + _T("Software"), + _T("Microsoft"), + _T("Windows"), + _T("CurrentVersion"), + _T("RunServices"), + NULL + }; + + return CArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_keyNames); +} + +HKEY +CArchDaemonWindows::openUserStartupKey() +{ + static const char* s_keyNames[] = { + _T("Software"), + _T("Microsoft"), + _T("Windows"), + _T("CurrentVersion"), + _T("Run"), + NULL + }; + + return CArchMiscWindows::openKey(HKEY_CURRENT_USER, s_keyNames); +} + +int +CArchDaemonWindows::doRunDaemon(RunFunc run) +{ + // should only be called from DaemonFunc + assert(m_serviceMutex != NULL); + assert(run != NULL); + + ARCH->lockMutex(m_serviceMutex); + try { + int result; + m_serviceHandlerWaiting = false; + m_serviceRunning = false; + for (;;) { + // mark server as running + setStatus(SERVICE_RUNNING); + + // run callback in another thread + m_serviceRunning = true; + m_daemonThread = ARCH->newThread( + &CArchDaemonWindows::runDaemonThreadEntry, run); + ARCH->wait(m_daemonThread, -1.0); + result = reinterpret_cast( + ARCH->getResultOfThread(m_daemonThread)); + m_serviceRunning = false; + + // notify handler that the server stopped. if handler + // isn't waiting then we stopped unexpectedly and we + // quit. + if (m_serviceHandlerWaiting) { + m_serviceHandlerWaiting = false; + ARCH->broadcastCondVar(m_serviceCondVar); + } + else { + break; + } + + // wait until we're told what to do next + while (m_serviceState != SERVICE_RUNNING && + m_serviceState != SERVICE_STOPPED) { + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); + } + + // exit loop if we've been told to stop + if (m_serviceState == SERVICE_STOPPED) { + break; + } + + // done with callback thread + ARCH->closeThread(m_daemonThread); + m_daemonThread = NULL; + } + + // prevent daemonHandler from changing state + m_serviceState = SERVICE_STOPPED; + + // tell service control that the service is stopped. + // FIXME -- hopefully this will ensure that our handler won't + // be called again but i can't find documentation that + // verifies that. if it does it'll crash on the mutex that + // we're about to destroy. + setStatus(m_serviceState); + + // clean up + if (m_daemonThread != NULL) { + ARCH->closeThread(m_daemonThread); + m_daemonThread = NULL; + } + ARCH->unlockMutex(m_serviceMutex); + + return result; + } + catch (...) { + // FIXME -- report error + + // prevent serviceHandler from changing state + m_serviceState = SERVICE_STOPPED; + + // set status + setStatusError(0); + + // wake up serviceHandler if it's waiting then wait for it + if (m_serviceHandlerWaiting) { + m_serviceHandlerWaiting = false; + ARCH->broadcastCondVar(m_serviceCondVar); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); + // serviceHandler has exited by now + } + + ARCH->unlockMutex(m_serviceMutex); + throw; + } +} + +void +CArchDaemonWindows::doDaemonRunning(bool running) +{ + if (running) { + ARCH->unlockMutex(m_serviceMutex); + } + else { + ARCH->lockMutex(m_serviceMutex); + } +} + +void* +CArchDaemonWindows::runDaemonThread(RunFunc run) +{ + return reinterpret_cast(run()); +} + +void* +CArchDaemonWindows::runDaemonThreadEntry(void* vrun) +{ + assert(s_daemon != NULL); + + return s_daemon->runDaemonThread(reinterpret_cast(vrun)); +} + +void +CArchDaemonWindows::setStatus(DWORD state) +{ + setStatus(state, 0, 0); +} + +void +CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint) +{ + assert(s_daemon != NULL); + + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS; + status.dwCurrentState = state; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = step; + status.dwWaitHint = waitHint; + SetServiceStatus(s_daemon->m_statusHandle, &status); +} + +void +CArchDaemonWindows::setStatusError(DWORD error) +{ + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS; + status.dwCurrentState = SERVICE_STOPPED; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + status.dwServiceSpecificExitCode = error; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + SetServiceStatus(s_daemon->m_statusHandle, &status); +} + +void +CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) +{ + typedef std::vector ArgList; + typedef std::vector Arguments; + const char** argv = const_cast(argvIn); + + // create synchronization objects + m_serviceMutex = ARCH->newMutex(); + m_serviceCondVar = ARCH->newCondVar(); + m_serviceState = SERVICE_RUNNING; + + // register our service handler functiom + m_statusHandle = RegisterServiceCtrlHandler(argv[0], + &CArchDaemonWindows::serviceHandlerEntry); + if (m_statusHandle == NULL) { + // cannot start as service + m_daemonResult = -1; + ARCH->closeCondVar(m_serviceCondVar); + ARCH->closeMutex(m_serviceMutex); + return; + } + + // tell service control manager that we're starting + setStatus(SERVICE_START_PENDING, 0, 10000); + + // if no arguments supplied then try getting them from the registry. + // the first argument doesn't count because it's the service name. + Arguments args; + ArgList myArgv; + if (argc <= 1) { + // read command line + std::string commandLine; + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, argvIn[0]); + key = CArchMiscWindows::openKey(key, _T("Parameters")); + if (key != NULL) { + commandLine = CArchMiscWindows::readValueString(key, + _T("CommandLine")); + } + + // if the command line isn't empty then parse and use it + if (!commandLine.empty()) { + // parse, honoring double quoted substrings + std::string::size_type i = commandLine.find_first_not_of(" \t"); + while (i != std::string::npos && i != commandLine.size()) { + // find end of string + std::string::size_type e; + if (commandLine[i] == '\"') { + // quoted. find closing quote. + ++i; + e = commandLine.find("\"", i); + + // whitespace must follow closing quote + if (e == std::string::npos || + (e + 1 != commandLine.size() && + commandLine[e + 1] != ' ' && + commandLine[e + 1] != '\t')) { + args.clear(); + break; + } + + // extract + args.push_back(commandLine.substr(i, e - i)); + i = e + 1; + } + else { + // unquoted. find next whitespace. + e = commandLine.find_first_of(" \t", i); + if (e == std::string::npos) { + e = commandLine.size(); + } + + // extract + args.push_back(commandLine.substr(i, e - i)); + i = e + 1; + } + + // next argument + i = commandLine.find_first_not_of(" \t", i); + } + + // service name goes first + myArgv.push_back(argv[0]); + + // get pointers + for (size_t i = 0; i < args.size(); ++i) { + myArgv.push_back(args[i].c_str()); + } + + // adjust argc/argv + argc = myArgv.size(); + argv = &myArgv[0]; + } + } + + try { + // invoke daemon function + m_daemonResult = m_daemonFunc(static_cast(argc), argv); + } + catch (XArchDaemonRunFailed& e) { + setStatusError(e.m_result); + m_daemonResult = -1; + } + catch (...) { + setStatusError(1); + m_daemonResult = -1; + } + + // clean up + ARCH->closeCondVar(m_serviceCondVar); + ARCH->closeMutex(m_serviceMutex); +} + +void WINAPI +CArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv) +{ + s_daemon->serviceMain(argc, argv); +} + +void +CArchDaemonWindows::serviceHandler(DWORD ctrl) +{ + assert(m_serviceMutex != NULL); + assert(m_serviceCondVar != NULL); + + ARCH->lockMutex(m_serviceMutex); + + // ignore request if service is already stopped + if (m_serviceState == SERVICE_STOPPED) { + setStatus(m_serviceState); + ARCH->unlockMutex(m_serviceMutex); + return; + } + + switch (ctrl) { + case SERVICE_CONTROL_PAUSE: + // update state + m_serviceState = SERVICE_PAUSE_PENDING; + setStatus(m_serviceState, 0, 5000); + + // stop run callback if running and wait for it to finish + if (m_serviceRunning) { + m_serviceHandlerWaiting = true; + ARCH->cancelThread(m_daemonThread); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); + } + + // update state if service hasn't stopped while we were waiting + if (m_serviceState != SERVICE_STOPPED) { + m_serviceState = SERVICE_PAUSED; + } + ARCH->broadcastCondVar(m_serviceCondVar); + break; + + case SERVICE_CONTROL_CONTINUE: + // required status update + setStatus(m_serviceState); + + // update state but let main loop send RUNNING notification + m_serviceState = SERVICE_RUNNING; + ARCH->broadcastCondVar(m_serviceCondVar); + ARCH->unlockMutex(m_serviceMutex); + return; + + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + // update state + m_serviceState = SERVICE_STOP_PENDING; + setStatus(m_serviceState, 0, 5000); + + // stop run callback if running and wait for it to finish + if (m_serviceRunning) { + m_serviceHandlerWaiting = true; + ARCH->cancelThread(m_daemonThread); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); + } + + // update state + m_serviceState = SERVICE_STOPPED; + ARCH->broadcastCondVar(m_serviceCondVar); + break; + + default: + // unknown service command + // fall through + + case SERVICE_CONTROL_INTERROGATE: + break; + } + + // send update + setStatus(m_serviceState); + + ARCH->unlockMutex(m_serviceMutex); +} + +void WINAPI +CArchDaemonWindows::serviceHandlerEntry(DWORD ctrl) +{ + s_daemon->serviceHandler(ctrl); +} diff --git a/lib/arch/CArchDaemonWindows.h b/lib/arch/CArchDaemonWindows.h new file mode 100644 index 0000000..57fe74f --- /dev/null +++ b/lib/arch/CArchDaemonWindows.h @@ -0,0 +1,123 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONWINDOWS_H +#define CARCHDAEMONWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchDaemon.h" +#include "IArchMultithread.h" +#include "stdstring.h" +#include +#include + +#define ARCH_DAEMON CArchDaemonWindows + +//! Win32 implementation of IArchDaemon +class CArchDaemonWindows : public IArchDaemon { +public: + typedef int (*RunFunc)(void); + + CArchDaemonWindows(); + virtual ~CArchDaemonWindows(); + + //! Run the daemon + /*! + When the client calls \c daemonize(), the \c DaemonFunc should call this + function after initialization and argument parsing to perform the + daemon processing. The \c runFunc should perform the daemon's + main loop, calling \c daemonRunning(true) when it enters the main loop + (i.e. after initialization) and \c daemonRunning(false) when it leaves + the main loop. The \c runFunc is called in a new thread and when the + daemon must exit the main loop due to some external control the + thread is cancelled on behalf of the client. This function returns + what \c runFunc returns. \c runFunc should call \c daemonFailed() if + the daemon fails. + */ + static int runDaemon(RunFunc runFunc); + + //! Indicate daemon is in main loop + /*! + The \c runFunc passed to \c runDaemon() should call this function + to indicate when it has entered (\c running is \c true) or exited + (\c running is \c false) the main loop. + */ + static void daemonRunning(bool running); + + //! Indicate failure of running daemon + /*! + The \c runFunc passed to \c runDaemon() should call this function + to indicate failure. \c result is returned by \c daemonize(). + */ + static void daemonFailed(int result); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); + +private: + static HKEY openNTServicesKey(); + static HKEY open95ServicesKey(); + static HKEY openUserStartupKey(); + + int doRunDaemon(RunFunc runFunc); + void doDaemonRunning(bool running); + + static void setStatus(DWORD state); + static void setStatus(DWORD state, DWORD step, DWORD waitHint); + static void setStatusError(DWORD error); + + void* runDaemonThread(RunFunc); + static void* runDaemonThreadEntry(void*); + + void serviceMain(DWORD, LPTSTR*); + static void WINAPI serviceMainEntry(DWORD, LPTSTR*); + + void serviceHandler(DWORD ctrl); + static void WINAPI serviceHandlerEntry(DWORD ctrl); + +private: + class XArchDaemonRunFailed { + public: + XArchDaemonRunFailed(int result) : m_result(result) { } + + public: + int m_result; + }; + +private: + static CArchDaemonWindows* s_daemon; + + CArchMutex m_serviceMutex; + CArchCond m_serviceCondVar; + DWORD m_serviceState; + bool m_serviceHandlerWaiting; + bool m_serviceRunning; + + CArchThread m_daemonThread; + DaemonFunc m_daemonFunc; + int m_daemonResult; + + SERVICE_STATUS_HANDLE m_statusHandle; +}; + +#endif diff --git a/lib/arch/CArchFileUnix.cpp b/lib/arch/CArchFileUnix.cpp new file mode 100644 index 0000000..a708cc8 --- /dev/null +++ b/lib/arch/CArchFileUnix.cpp @@ -0,0 +1,94 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchFileUnix.h" +#include +#include +#include +#include + +// +// CArchFileUnix +// + +CArchFileUnix::CArchFileUnix() +{ + // do nothing +} + +CArchFileUnix::~CArchFileUnix() +{ + // do nothing +} + +const char* +CArchFileUnix::getBasename(const char* pathname) +{ + if (pathname == NULL) { + return NULL; + } + + const char* basename = strrchr(pathname, '/'); + if (basename != NULL) { + return basename + 1; + } + else { + return pathname; + } +} + +std::string +CArchFileUnix::getUserDirectory() +{ +#if HAVE_GETPWUID_R + struct passwd pwent; + struct passwd* pwentp; +#if defined(_SC_GETPW_R_SIZE_MAX) + long size = sysconf(_SC_GETPW_R_SIZE_MAX); +#else + long size = BUFSIZ; +#endif + char* buffer = new char[size]; + getpwuid_r(getuid(), &pwent, buffer, size, &pwentp); + delete[] buffer; +#else + struct passwd* pwentp = getpwuid(getuid()); +#endif + if (pwentp != NULL && pwentp->pw_dir != NULL) { + return pwentp->pw_dir; + } + else { + return std::string(); + } +} + +std::string +CArchFileUnix::getSystemDirectory() +{ + return "/etc"; +} + +std::string +CArchFileUnix::concatPath(const std::string& prefix, + const std::string& suffix) +{ + std::string path; + path.reserve(prefix.size() + 1 + suffix.size()); + path += prefix; + if (path.size() == 0 || path[path.size() - 1] != '/') { + path += '/'; + } + path += suffix; + return path; +} diff --git a/lib/arch/CArchFileUnix.h b/lib/arch/CArchFileUnix.h new file mode 100644 index 0000000..41d00e9 --- /dev/null +++ b/lib/arch/CArchFileUnix.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHFILEUNIX_H +#define CARCHFILEUNIX_H + +#include "IArchFile.h" + +#define ARCH_FILE CArchFileUnix + +//! Unix implementation of IArchFile +class CArchFileUnix : public IArchFile { +public: + CArchFileUnix(); + virtual ~CArchFileUnix(); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); +}; + +#endif diff --git a/lib/arch/CArchFileWindows.cpp b/lib/arch/CArchFileWindows.cpp new file mode 100644 index 0000000..7d9fd51 --- /dev/null +++ b/lib/arch/CArchFileWindows.cpp @@ -0,0 +1,132 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchFileWindows.h" +#include +#include +#include +#include + +// +// CArchFileWindows +// + +CArchFileWindows::CArchFileWindows() +{ + // do nothing +} + +CArchFileWindows::~CArchFileWindows() +{ + // do nothing +} + +const char* +CArchFileWindows::getBasename(const char* pathname) +{ + if (pathname == NULL) { + return NULL; + } + + // check for last slash + const char* basename = strrchr(pathname, '/'); + if (basename != NULL) { + ++basename; + } + else { + basename = pathname; + } + + // check for last backslash + const char* basename2 = strrchr(pathname, '\\'); + if (basename2 != NULL && basename2 > basename) { + basename = basename2 + 1; + } + + return basename; +} + +std::string +CArchFileWindows::getUserDirectory() +{ + // try %HOMEPATH% + TCHAR dir[MAX_PATH]; + DWORD size = sizeof(dir) / sizeof(TCHAR); + DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size); + if (result != 0 && result <= size) { + // sanity check -- if dir doesn't appear to start with a + // drive letter and isn't a UNC name then don't use it + // FIXME -- allow UNC names + if (dir[0] != '\0' && (dir[1] == ':' || + ((dir[0] == '\\' || dir[0] == '/') && + (dir[1] == '\\' || dir[1] == '/')))) { + return dir; + } + } + + // get the location of the personal files. that's as close to + // a home directory as we're likely to find. + ITEMIDLIST* idl; + if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) { + TCHAR* path = NULL; + if (SHGetPathFromIDList(idl, dir)) { + DWORD attr = GetFileAttributes(dir); + if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + path = dir; + } + + IMalloc* shalloc; + if (SUCCEEDED(SHGetMalloc(&shalloc))) { + shalloc->Free(idl); + shalloc->Release(); + } + + if (path != NULL) { + return path; + } + } + + // use root of C drive as a default + return "C:"; +} + +std::string +CArchFileWindows::getSystemDirectory() +{ + // get windows directory + char dir[MAX_PATH]; + if (GetWindowsDirectory(dir, sizeof(dir)) != 0) { + return dir; + } + else { + // can't get it. use C:\ as a default. + return "C:"; + } +} + +std::string +CArchFileWindows::concatPath(const std::string& prefix, + const std::string& suffix) +{ + std::string path; + path.reserve(prefix.size() + 1 + suffix.size()); + path += prefix; + if (path.size() == 0 || + (path[path.size() - 1] != '\\' && + path[path.size() - 1] != '/')) { + path += '\\'; + } + path += suffix; + return path; +} diff --git a/lib/arch/CArchFileWindows.h b/lib/arch/CArchFileWindows.h new file mode 100644 index 0000000..617b1c4 --- /dev/null +++ b/lib/arch/CArchFileWindows.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHFILEWINDOWS_H +#define CARCHFILEWINDOWS_H + +#include "IArchFile.h" + +#define ARCH_FILE CArchFileWindows + +//! Win32 implementation of IArchFile +class CArchFileWindows : public IArchFile { +public: + CArchFileWindows(); + virtual ~CArchFileWindows(); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); +}; + +#endif diff --git a/lib/arch/CArchImpl.cpp b/lib/arch/CArchImpl.cpp new file mode 100644 index 0000000..9e57d0a --- /dev/null +++ b/lib/arch/CArchImpl.cpp @@ -0,0 +1,45 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "common.h" + +// include appropriate architecture implementation +#if WINDOWS_LIKE +# include "CArchMiscWindows.cpp" +# include "CArchConsoleWindows.cpp" +# include "CArchDaemonWindows.cpp" +# include "CArchFileWindows.cpp" +# include "CArchLogWindows.cpp" +# include "CArchMultithreadWindows.cpp" +# include "CArchNetworkWinsock.cpp" +# include "CArchSleepWindows.cpp" +# include "CArchStringWindows.cpp" +# include "CArchTaskBarWindows.cpp" +# include "CArchTimeWindows.cpp" +# include "XArchWindows.cpp" +#elif UNIX_LIKE +# include "CArchConsoleUnix.cpp" +# include "CArchDaemonUnix.cpp" +# include "CArchFileUnix.cpp" +# include "CArchLogUnix.cpp" +# if HAVE_PTHREAD +# include "CArchMultithreadPosix.cpp" +# endif +# include "CArchNetworkBSD.cpp" +# include "CArchSleepUnix.cpp" +# include "CArchStringUnix.cpp" +# include "CArchTaskBarXWindows.cpp" +# include "CArchTimeUnix.cpp" +# include "XArchUnix.cpp" +#endif diff --git a/lib/arch/CArchLogUnix.cpp b/lib/arch/CArchLogUnix.cpp new file mode 100644 index 0000000..c75696e --- /dev/null +++ b/lib/arch/CArchLogUnix.cpp @@ -0,0 +1,73 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchLogUnix.h" +#include + +// +// CArchLogUnix +// + +CArchLogUnix::CArchLogUnix() +{ + // do nothing +} + +CArchLogUnix::~CArchLogUnix() +{ + // do nothing +} + +void +CArchLogUnix::openLog(const char* name) +{ + openlog(name, 0, LOG_DAEMON); +} + +void +CArchLogUnix::closeLog() +{ + closelog(); +} + +void +CArchLogUnix::writeLog(ELevel level, const char* msg) +{ + // convert level + int priority; + switch (level) { + case kERROR: + priority = LOG_ERR; + break; + + case kWARNING: + priority = LOG_WARNING; + break; + + case kNOTE: + priority = LOG_NOTICE; + break; + + case kINFO: + priority = LOG_INFO; + break; + + default: + priority = LOG_DEBUG; + break; + } + + // log it + syslog(priority, "%s", msg); +} diff --git a/lib/arch/CArchLogUnix.h b/lib/arch/CArchLogUnix.h new file mode 100644 index 0000000..b371784 --- /dev/null +++ b/lib/arch/CArchLogUnix.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHLOGUNIX_H +#define CARCHLOGUNIX_H + +#include "IArchLog.h" + +#define ARCH_LOG CArchLogUnix + +//! Unix implementation of IArchLog +class CArchLogUnix : public IArchLog { +public: + CArchLogUnix(); + virtual ~CArchLogUnix(); + + // IArchLog overrides + virtual void openLog(const char* name); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); +}; + +#endif diff --git a/lib/arch/CArchLogWindows.cpp b/lib/arch/CArchLogWindows.cpp new file mode 100644 index 0000000..6d21d6b --- /dev/null +++ b/lib/arch/CArchLogWindows.cpp @@ -0,0 +1,84 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchLogWindows.h" +#include "CArchMiscWindows.h" +#include + +// +// CArchLogWindows +// + +CArchLogWindows::CArchLogWindows() : m_eventLog(NULL) +{ + // do nothing +} + +CArchLogWindows::~CArchLogWindows() +{ + // do nothing +} + +void +CArchLogWindows::openLog(const char* name) +{ + if (m_eventLog == NULL && !CArchMiscWindows::isWindows95Family()) { + m_eventLog = RegisterEventSource(NULL, name); + } +} + +void +CArchLogWindows::closeLog() +{ + if (m_eventLog != NULL) { + DeregisterEventSource(m_eventLog); + m_eventLog = NULL; + } +} + +void +CArchLogWindows::writeLog(ELevel level, const char* msg) +{ + if (m_eventLog != NULL) { + // convert priority + WORD type; + switch (level) { + case kERROR: + type = EVENTLOG_ERROR_TYPE; + break; + + case kWARNING: + type = EVENTLOG_WARNING_TYPE; + break; + + default: + type = EVENTLOG_INFORMATION_TYPE; + break; + } + + // log it + // FIXME -- win32 wants to use a message table to look up event + // strings. log messages aren't organized that way so we'll + // just dump our string into the raw data section of the event + // so users can at least see the message. note that we use our + // level as the event category. + ReportEvent(m_eventLog, type, static_cast(level), + 0, // event ID + NULL, + 0, + strlen(msg) + 1, // raw data size + NULL, + const_cast(msg));// raw data + } +} diff --git a/lib/arch/CArchLogWindows.h b/lib/arch/CArchLogWindows.h new file mode 100644 index 0000000..d238576 --- /dev/null +++ b/lib/arch/CArchLogWindows.h @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHLOGWINDOWS_H +#define CARCHLOGWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchLog.h" +#include + +#define ARCH_LOG CArchLogWindows + +//! Win32 implementation of IArchLog +class CArchLogWindows : public IArchLog { +public: + CArchLogWindows(); + virtual ~CArchLogWindows(); + + // IArchLog overrides + virtual void openLog(const char* name); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); + +private: + HANDLE m_eventLog; +}; + +#endif diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp new file mode 100644 index 0000000..f76b42f --- /dev/null +++ b/lib/arch/CArchMiscWindows.cpp @@ -0,0 +1,183 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchMiscWindows.h" +#include "CArchDaemonWindows.h" + +// +// CArchMiscWindows +// + +bool +CArchMiscWindows::isWindows95Family() +{ + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if (GetVersionEx(&version) == 0) { + // cannot determine OS; assume windows 95 family + return true; + } + return (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); +} + +int +CArchMiscWindows::runDaemon(RunFunc runFunc) +{ + return CArchDaemonWindows::runDaemon(runFunc); +} + +void +CArchMiscWindows::daemonRunning(bool running) +{ + CArchDaemonWindows::daemonRunning(running); +} + +void +CArchMiscWindows::daemonFailed(int result) +{ + CArchDaemonWindows::daemonFailed(result); +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) +{ + // ignore if parent is NULL + if (key == NULL) { + return NULL; + } + + // open next key + HKEY newKey; + LONG result = RegOpenKeyEx(key, keyName, 0, + KEY_WRITE | KEY_QUERY_VALUE, &newKey); + if (result != ERROR_SUCCESS) { + DWORD disp; + result = RegCreateKeyEx(key, keyName, 0, TEXT(""), + 0, KEY_WRITE | KEY_QUERY_VALUE, + NULL, &newKey, &disp); + } + if (result != ERROR_SUCCESS) { + RegCloseKey(key); + return NULL; + } + + // switch to new key + RegCloseKey(key); + return newKey; +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames) +{ + for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) { + // open next key + key = openKey(key, keyNames[i]); + } + return key; +} + +void +CArchMiscWindows::closeKey(HKEY key) +{ + assert(key != NULL); + RegCloseKey(key); +} + +void +CArchMiscWindows::deleteKey(HKEY key, const TCHAR* name) +{ + assert(key != NULL); + assert(name != NULL); + RegDeleteKey(key, name); +} + +void +CArchMiscWindows::deleteValue(HKEY key, const TCHAR* name) +{ + assert(key != NULL); + assert(name != NULL); + RegDeleteValue(key, name); +} + +bool +CArchMiscWindows::hasValue(HKEY key, const TCHAR* name) +{ + DWORD type; + LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL); + return (result == ERROR_SUCCESS && + (type == REG_DWORD || type == REG_SZ)); +} + +void +CArchMiscWindows::setValue(HKEY key, + const TCHAR* name, const std::string& value) +{ + assert(key != NULL); + assert(name != NULL); + RegSetValueEx(key, name, 0, REG_SZ, + reinterpret_cast(value.c_str()), + value.size() + 1); +} + +void +CArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value) +{ + assert(key != NULL); + assert(name != NULL); + RegSetValueEx(key, name, 0, REG_DWORD, + reinterpret_cast(&value), + sizeof(DWORD)); +} + +std::string +CArchMiscWindows::readValueString(HKEY key, const TCHAR* name) +{ + // get the size of the string + DWORD type; + DWORD size = 0; + LONG result = RegQueryValueEx(key, name, 0, &type, NULL, &size); + if (result != ERROR_SUCCESS || type != REG_SZ) { + return std::string(); + } + + // allocate space + char* buffer = new char[size]; + + // read it + result = RegQueryValueEx(key, name, 0, &type, + reinterpret_cast(buffer), &size); + if (result != ERROR_SUCCESS || type != REG_SZ) { + delete[] buffer; + return std::string(); + } + + // clean up and return value + std::string value(buffer); + delete[] buffer; + return value; +} + +DWORD +CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name) +{ + DWORD type; + DWORD value; + DWORD size = sizeof(value); + LONG result = RegQueryValueEx(key, name, 0, &type, + reinterpret_cast(&value), &size); + if (result != ERROR_SUCCESS || type != REG_DWORD) { + return 0; + } + return value; +} diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h new file mode 100644 index 0000000..dc99336 --- /dev/null +++ b/lib/arch/CArchMiscWindows.h @@ -0,0 +1,85 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMISCWINDOWS_H +#define CARCHMISCWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "common.h" +#include "stdstring.h" +#include + +//! Miscellaneous win32 functions. +class CArchMiscWindows { +public: + typedef int (*RunFunc)(void); + + //! Test if windows 95, et al. + /*! + Returns true iff the platform is win95/98/me. + */ + static bool isWindows95Family(); + + //! Run the daemon + /*! + Delegates to CArchDaemonWindows. + */ + static int runDaemon(RunFunc runFunc); + + //! Indicate daemon is in main loop + /*! + Delegates to CArchDaemonWindows. + */ + static void daemonRunning(bool running); + + //! Indicate failure of running daemon + /*! + Delegates to CArchDaemonWindows. + */ + static void daemonFailed(int result); + + //! Open and return a registry key, closing the parent key + static HKEY openKey(HKEY parent, const TCHAR* child); + + //! Open and return a registry key, closing the parent key + static HKEY openKey(HKEY parent, const TCHAR* const* keyPath); + + //! Close a key + static void closeKey(HKEY); + + //! Delete a key (which should have no subkeys) + static void deleteKey(HKEY parent, const TCHAR* name); + + //! Delete a value + static void deleteValue(HKEY parent, const TCHAR* name); + + //! Test if a value exists + static bool hasValue(HKEY key, const TCHAR* name); + + //! Set a string value in the registry + static void setValue(HKEY key, const TCHAR* name, + const std::string& value); + + //! Set a DWORD value in the registry + static void setValue(HKEY key, const TCHAR* name, DWORD value); + + //! Read a string value from the registry + static std::string readValueString(HKEY, const TCHAR* name); + + //! Read a DWORD value from the registry + static DWORD readValueInt(HKEY, const TCHAR* name); +}; + +#endif diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp new file mode 100644 index 0000000..bef806b --- /dev/null +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -0,0 +1,771 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchMultithreadPosix.h" +#include "CArch.h" +#include "XArch.h" +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include + +#define SIGWAKEUP SIGUSR1 + +#if !HAVE_PTHREAD_SIGNAL + // boy, is this platform broken. forget about pthread signal + // handling and let signals through to every process. synergy + // will not terminate cleanly when it gets SIGTERM or SIGINT. +# define pthread_sigmask sigprocmask +# define pthread_kill(tid_, sig_) kill(0, (sig_)) +# define sigwait(set_, sig_) +# undef HAVE_POSIX_SIGWAIT +# define HAVE_POSIX_SIGWAIT 1 +#endif + +// +// CArchThreadImpl +// + +class CArchThreadImpl { +public: + CArchThreadImpl(); + +public: + int m_refCount; + IArchMultithread::ThreadID m_id; + pthread_t m_thread; + IArchMultithread::ThreadFunc m_func; + void* m_userData; + bool m_cancel; + bool m_cancelling; + bool m_exited; + void* m_result; +}; + +CArchThreadImpl::CArchThreadImpl() : + m_refCount(1), + m_id(0), + m_func(NULL), + m_userData(NULL), + m_cancel(false), + m_cancelling(false), + m_exited(false), + m_result(NULL) +{ + // do nothing +} + + +// +// CArchMultithreadPosix +// + +CArchMultithreadPosix* CArchMultithreadPosix::s_instance = NULL; + +CArchMultithreadPosix::CArchMultithreadPosix() : + m_newThreadCalled(false), + m_nextID(0) +{ + assert(s_instance == NULL); + + s_instance = this; + + // create mutex for thread list + m_threadMutex = newMutex(); + + // create thread for calling (main) thread and add it to our + // list. no need to lock the mutex since we're the only thread. + m_mainThread = new CArchThreadImpl; + m_mainThread->m_thread = pthread_self(); + insert(m_mainThread); + + // install SIGWAKEUP handler. this causes SIGWAKEUP to interrupt + // system calls. we use that when cancelling a thread to force it + // to wake up immediately if it's blocked in a system call. we + // won't need this until another thread is created but it's fine + // to install it now. + struct sigaction act; + sigemptyset(&act.sa_mask); +# if defined(SA_INTERRUPT) + act.sa_flags = SA_INTERRUPT; +# else + act.sa_flags = 0; +# endif + act.sa_handler = &threadCancel; + sigaction(SIGWAKEUP, &act, NULL); + + // set desired signal dispositions. let SIGWAKEUP through but + // ignore SIGPIPE (we'll handle EPIPE). + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGWAKEUP); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + sigemptyset(&sigset); + sigaddset(&sigset, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); +} + +CArchMultithreadPosix::~CArchMultithreadPosix() +{ + assert(s_instance != NULL); + + closeMutex(m_threadMutex); + s_instance = NULL; +} + +CArchCond +CArchMultithreadPosix::newCondVar() +{ + CArchCondImpl* cond = new CArchCondImpl; + int status = pthread_cond_init(&cond->m_cond, NULL); + assert(status == 0); + return cond; +} + +void +CArchMultithreadPosix::closeCondVar(CArchCond cond) +{ + int status = pthread_cond_destroy(&cond->m_cond); + assert(status == 0); + delete cond; +} + +void +CArchMultithreadPosix::signalCondVar(CArchCond cond) +{ + int status = pthread_cond_signal(&cond->m_cond); + assert(status == 0); +} + +void +CArchMultithreadPosix::broadcastCondVar(CArchCond cond) +{ + int status = pthread_cond_broadcast(&cond->m_cond); + assert(status == 0); +} + +bool +CArchMultithreadPosix::waitCondVar(CArchCond cond, + CArchMutex mutex, double timeout) +{ + // get final time + struct timeval now; + gettimeofday(&now, NULL); + struct timespec finalTime; + finalTime.tv_sec = now.tv_sec; + finalTime.tv_nsec = now.tv_usec * 1000; + if (timeout >= 0.0) { + const long timeout_sec = (long)timeout; + const long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec)); + finalTime.tv_sec += timeout_sec; + finalTime.tv_nsec += timeout_nsec; + if (finalTime.tv_nsec >= 1000000000) { + finalTime.tv_nsec -= 1000000000; + finalTime.tv_sec += 1; + } + } + + // repeat until we reach the final time + int status; + for (;;) { + // get current time + gettimeofday(&now, NULL); + struct timespec endTime; + endTime.tv_sec = now.tv_sec; + endTime.tv_nsec = now.tv_usec * 1000; + + // done if past final timeout + if (timeout >= 0.0) { + if (endTime.tv_sec > finalTime.tv_sec || + (endTime.tv_sec == finalTime.tv_sec && + endTime.tv_nsec >= finalTime.tv_nsec)) { + status = ETIMEDOUT; + break; + } + } + + // compute the next timeout + endTime.tv_nsec += 50000000; + if (endTime.tv_nsec >= 1000000000) { + endTime.tv_nsec -= 1000000000; + endTime.tv_sec += 1; + } + + // don't wait past final timeout + if (timeout >= 0.0) { + if (endTime.tv_sec > finalTime.tv_sec || + (endTime.tv_sec == finalTime.tv_sec && + endTime.tv_nsec >= finalTime.tv_nsec)) { + endTime = finalTime; + } + } + + // see if we should cancel this thread + testCancelThread(); + + // wait + status = pthread_cond_timedwait(&cond->m_cond, + &mutex->m_mutex, &endTime); + + // check for cancel again + testCancelThread(); + + // check wait status + if (status != ETIMEDOUT && status != EINTR) { + break; + } + } + + switch (status) { + case 0: + // success + return true; + + case ETIMEDOUT: + return false; + + default: + assert(0 && "condition variable wait error"); + return false; + } +} + +CArchMutex +CArchMultithreadPosix::newMutex() +{ + CArchMutexImpl* mutex = new CArchMutexImpl; + int status = pthread_mutex_init(&mutex->m_mutex, NULL); + assert(status == 0); +/* + status = pthread_mutexattr_settype(&mutex->m_mutex, + PTHREAD_MUTEX_RECURSIVE); + assert(status == 0); +*/ + return mutex; +} + +void +CArchMultithreadPosix::closeMutex(CArchMutex mutex) +{ + int status = pthread_mutex_destroy(&mutex->m_mutex); + assert(status == 0); + delete mutex; +} + +void +CArchMultithreadPosix::lockMutex(CArchMutex mutex) +{ + int status = pthread_mutex_lock(&mutex->m_mutex); + + switch (status) { + case 0: + // success + return; + + case EDEADLK: + assert(0 && "lock already owned"); + break; + + case EAGAIN: + assert(0 && "too many recursive locks"); + break; + + default: + assert(0 && "unexpected error"); + break; + } +} + +void +CArchMultithreadPosix::unlockMutex(CArchMutex mutex) +{ + int status = pthread_mutex_unlock(&mutex->m_mutex); + + switch (status) { + case 0: + // success + return; + + case EPERM: + assert(0 && "thread doesn't own a lock"); + break; + + default: + assert(0 && "unexpected error"); + break; + } +} + +CArchThread +CArchMultithreadPosix::newThread(ThreadFunc func, void* data) +{ + assert(func != NULL); + + // initialize signal handler. we do this here instead of the + // constructor so we can avoid daemonizing (using fork()) + // when there are multiple threads. clients can safely + // use condition variables and mutexes before creating a + // new thread and they can safely use the only thread + // they have access to, the main thread, so they really + // can't tell the difference. + if (!m_newThreadCalled) { + m_newThreadCalled = true; +#if HAVE_PTHREAD_SIGNAL + startSignalHandler(); +#endif + } + + lockMutex(m_threadMutex); + + // create thread impl for new thread + CArchThreadImpl* thread = new CArchThreadImpl; + thread->m_func = func; + thread->m_userData = data; + + // mask some signals in all threads except the main thread + sigset_t sigset, oldsigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); + + // create the thread. pthread_create() on RedHat 7.2 smp fails + // if passed a NULL attr so use a default attr. + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + if (status == 0) { + status = pthread_create(&thread->m_thread, &attr, + &CArchMultithreadPosix::threadFunc, thread); + pthread_attr_destroy(&attr); + } + + // restore signals + pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); + + // check if thread was started + if (status != 0) { + // failed to start thread so clean up + delete thread; + thread = NULL; + } + else { + // add thread to list + insert(thread); + + // increment ref count to account for the thread itself + refThread(thread); + } + + // note that the child thread will wait until we release this mutex + unlockMutex(m_threadMutex); + + return thread; +} + +CArchThread +CArchMultithreadPosix::newCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = find(pthread_self()); + unlockMutex(m_threadMutex); + assert(thread != NULL); + return thread; +} + +void +CArchMultithreadPosix::closeThread(CArchThread thread) +{ + assert(thread != NULL); + + // decrement ref count and clean up thread if no more references + if (--thread->m_refCount == 0) { + // detach from thread (unless it's the main thread) + if (thread->m_func != NULL) { + pthread_detach(thread->m_thread); + } + + // remove thread from list + lockMutex(m_threadMutex); + assert(findNoRef(thread->m_thread) == thread); + erase(thread); + unlockMutex(m_threadMutex); + + // done with thread + delete thread; + } +} + +CArchThread +CArchMultithreadPosix::copyThread(CArchThread thread) +{ + refThread(thread); + return thread; +} + +void +CArchMultithreadPosix::cancelThread(CArchThread thread) +{ + assert(thread != NULL); + + // set cancel and wakeup flags if thread can be cancelled + bool wakeup = false; + lockMutex(m_threadMutex); + if (!thread->m_exited && !thread->m_cancelling) { + thread->m_cancel = true; + wakeup = true; + } + unlockMutex(m_threadMutex); + + // force thread to exit system calls if wakeup is true + if (wakeup) { + pthread_kill(thread->m_thread, SIGWAKEUP); + } +} + +void +CArchMultithreadPosix::setPriorityOfThread(CArchThread thread, int /*n*/) +{ + assert(thread != NULL); + + // FIXME +} + +void +CArchMultithreadPosix::testCancelThread() +{ + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(pthread_self()); + unlockMutex(m_threadMutex); + + // test cancel on thread + testCancelThreadImpl(thread); +} + +bool +CArchMultithreadPosix::wait(CArchThread target, double timeout) +{ + assert(target != NULL); + + lockMutex(m_threadMutex); + + // find current thread + CArchThreadImpl* self = findNoRef(pthread_self()); + + // ignore wait if trying to wait on ourself + if (target == self) { + unlockMutex(m_threadMutex); + return false; + } + + // ref the target so it can't go away while we're watching it + refThread(target); + + unlockMutex(m_threadMutex); + + try { + // do first test regardless of timeout + testCancelThreadImpl(self); + if (isExitedThread(target)) { + closeThread(target); + return true; + } + + // wait and repeat test if there's a timeout + if (timeout != 0.0) { + const double start = ARCH->time(); + do { + // wait a little + ARCH->sleep(0.05); + + // repeat test + testCancelThreadImpl(self); + if (isExitedThread(target)) { + closeThread(target); + return true; + } + + // repeat wait and test until timed out + } while (timeout < 0.0 || (ARCH->time() - start) <= timeout); + } + + closeThread(target); + return false; + } + catch (...) { + closeThread(target); + throw; + } +} + +IArchMultithread::EWaitResult +CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/) +{ + // not implemented + return kTimeout; +} + +bool +CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return (thread1 == thread2); +} + +bool +CArchMultithreadPosix::isExitedThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + bool exited = thread->m_exited; + unlockMutex(m_threadMutex); + return exited; +} + +void* +CArchMultithreadPosix::getResultOfThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + void* result = thread->m_result; + unlockMutex(m_threadMutex); + return result; +} + +IArchMultithread::ThreadID +CArchMultithreadPosix::getIDOfThread(CArchThread thread) +{ + return thread->m_id; +} + +void +CArchMultithreadPosix::startSignalHandler() +{ + // set signal mask. the main thread blocks these signals and + // the signal handler thread will listen for them. + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); + + // fire up the INT and TERM signal handler thread. we could + // instead arrange to catch and handle these signals but + // we'd be unable to cancel the main thread since no pthread + // calls are allowed in a signal handler. + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + if (status == 0) { + status = pthread_create(&m_signalThread, &attr, + &CArchMultithreadPosix::threadSignalHandler, + m_mainThread); + pthread_attr_destroy(&attr); + } + if (status != 0) { + // can't create thread to wait for signal so don't block + // the signals. + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + } +} + +CArchThreadImpl* +CArchMultithreadPosix::find(pthread_t thread) +{ + CArchThreadImpl* impl = findNoRef(thread); + if (impl != NULL) { + refThread(impl); + } + return impl; +} + +CArchThreadImpl* +CArchMultithreadPosix::findNoRef(pthread_t thread) +{ + // linear search + for (CThreadList::const_iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if ((*index)->m_thread == thread) { + return *index; + } + } + return NULL; +} + +void +CArchMultithreadPosix::insert(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // thread shouldn't already be on the list + assert(findNoRef(thread->m_thread) == NULL); + + // set thread id. note that we don't worry about m_nextID + // wrapping back to 0 and duplicating thread ID's since the + // likelihood of synergy running that long is vanishingly + // small. + thread->m_id = ++m_nextID; + + // append to list + m_threadList.push_back(thread); +} + +void +CArchMultithreadPosix::erase(CArchThreadImpl* thread) +{ + for (CThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if (*index == thread) { + m_threadList.erase(index); + break; + } + } +} + +void +CArchMultithreadPosix::refThread(CArchThreadImpl* thread) +{ + assert(thread != NULL); + assert(findNoRef(thread->m_thread) != NULL); + ++thread->m_refCount; +} + +void +CArchMultithreadPosix::testCancelThreadImpl(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // update cancel state + lockMutex(m_threadMutex); + bool cancel = false; + if (thread->m_cancel && !thread->m_cancelling) { + thread->m_cancelling = true; + thread->m_cancel = false; + cancel = true; + } + unlockMutex(m_threadMutex); + + // unwind thread's stack if cancelling + if (cancel) { + throw XThreadCancel(); + } +} + +void* +CArchMultithreadPosix::threadFunc(void* vrep) +{ + // get the thread + CArchThreadImpl* thread = reinterpret_cast(vrep); + + // setup pthreads + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + // run thread + s_instance->doThreadFunc(thread); + + // terminate the thread + return NULL; +} + +void +CArchMultithreadPosix::doThreadFunc(CArchThread thread) +{ + // default priority is slightly below normal + setPriorityOfThread(thread, 1); + + // wait for parent to initialize this object + lockMutex(m_threadMutex); + unlockMutex(m_threadMutex); + + void* result = NULL; + try { + // go + result = (*thread->m_func)(thread->m_userData); + } + + catch (XThreadCancel&) { + // client called cancel() + } + catch (...) { + // note -- don't catch (...) to avoid masking bugs + lockMutex(m_threadMutex); + thread->m_exited = true; + unlockMutex(m_threadMutex); + closeThread(thread); + throw; + } + + // thread has exited + lockMutex(m_threadMutex); + thread->m_result = result; + thread->m_exited = true; + unlockMutex(m_threadMutex); + + // done with thread + closeThread(thread); +} + +void +CArchMultithreadPosix::threadCancel(int) +{ + // do nothing +} + +void* +CArchMultithreadPosix::threadSignalHandler(void* vrep) +{ + CArchThreadImpl* mainThread = reinterpret_cast(vrep); + + // detach + pthread_detach(pthread_self()); + + // add signal to mask + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + + // also wait on SIGABRT. on linux (others?) this thread (process) + // will persist after all the other threads evaporate due to an + // assert unless we wait on SIGABRT. that means our resources (like + // the socket we're listening on) are not released and never will be + // until the lingering thread is killed. i don't know why sigwait() + // should protect the thread from being killed. note that sigwait() + // doesn't actually return if we receive SIGABRT and, for some + // reason, we don't have to block SIGABRT. + sigaddset(&sigset, SIGABRT); + + // we exit the loop via thread cancellation in sigwait() + for (;;) { + // wait +#if HAVE_POSIX_SIGWAIT + int signal; + sigwait(&sigset, &signal); +#else + sigwait(&sigset); +#endif + + // if we get here then the signal was raised. cancel the main + // thread so it can shut down cleanly. + ARCH->cancelThread(mainThread); + } +} diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h new file mode 100644 index 0000000..d62e2d4 --- /dev/null +++ b/lib/arch/CArchMultithreadPosix.h @@ -0,0 +1,95 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMULTITHREADPOSIX_H +#define CARCHMULTITHREADPOSIX_H + +#include "IArchMultithread.h" +#include "stdlist.h" +#include + +#define ARCH_MULTITHREAD CArchMultithreadPosix + +class CArchCondImpl { +public: + pthread_cond_t m_cond; +}; + +class CArchMutexImpl { +public: + pthread_mutex_t m_mutex; +}; + +//! Posix implementation of IArchMultithread +class CArchMultithreadPosix : public IArchMultithread { +public: + CArchMultithreadPosix(); + virtual ~CArchMultithreadPosix(); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual EWaitResult waitForEvent(CArchThread, double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + +private: + void startSignalHandler(); + + CArchThreadImpl* find(pthread_t thread); + CArchThreadImpl* findNoRef(pthread_t thread); + void insert(CArchThreadImpl* thread); + void erase(CArchThreadImpl* thread); + + void refThread(CArchThreadImpl* rep); + void testCancelThreadImpl(CArchThreadImpl* rep); + + void doThreadFunc(CArchThread thread); + static void* threadFunc(void* vrep); + static void threadCancel(int); + static void* threadSignalHandler(void* vrep); + +private: + typedef std::list CThreadList; + + static CArchMultithreadPosix* s_instance; + + bool m_newThreadCalled; + + CArchMutex m_threadMutex; + CArchThread m_mainThread; + CThreadList m_threadList; + ThreadID m_nextID; + + pthread_t m_signalThread; +}; + +#endif diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp new file mode 100644 index 0000000..2a457a5 --- /dev/null +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -0,0 +1,783 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if !defined(_MT) +# error multithreading compile option is required +#endif + +#include "CArchMultithreadWindows.h" +#include "CArch.h" +#include "XArch.h" +#include + +// +// note -- implementation of condition variable taken from: +// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html +// titled "Strategies for Implementing POSIX Condition Variables +// on Win32." it also provides an implementation that doesn't +// suffer from the incorrectness problem described in our +// corresponding header but it is slower, still unfair, and +// can cause busy waiting. +// + +// +// CArchThreadImpl +// + +class CArchThreadImpl { +public: + CArchThreadImpl(); + ~CArchThreadImpl(); + +public: + int m_refCount; + HANDLE m_thread; + DWORD m_id; + IArchMultithread::ThreadFunc m_func; + void* m_userData; + HANDLE m_cancel; + bool m_cancelling; + HANDLE m_exit; + void* m_result; +}; + +CArchThreadImpl::CArchThreadImpl() : + m_refCount(1), + m_thread(NULL), + m_id(0), + m_func(NULL), + m_userData(NULL), + m_cancelling(false), + m_result(NULL) +{ + m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); + m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +CArchThreadImpl::~CArchThreadImpl() +{ + CloseHandle(m_exit); + CloseHandle(m_cancel); +} + + +// +// CArchMultithreadWindows +// + +CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL; + +CArchMultithreadWindows::CArchMultithreadWindows() +{ + assert(s_instance == NULL); + s_instance = this; + + // create mutex for thread list + m_threadMutex = newMutex(); + + // create thread for calling (main) thread and add it to our + // list. no need to lock the mutex since we're the only thread. + CArchThreadImpl* mainThread = new CArchThreadImpl; + mainThread->m_thread = NULL; + mainThread->m_id = GetCurrentThreadId(); + insert(mainThread); +} + +CArchMultithreadWindows::~CArchMultithreadWindows() +{ + s_instance = NULL; + + // clean up thread list + for (CThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + delete *index; + } + + // done with mutex + delete m_threadMutex; +} + +HANDLE +CArchMultithreadWindows::getCancelEventForCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + return thread->m_cancel; +} + +CArchMultithreadWindows* +CArchMultithreadWindows::getInstance() +{ + return s_instance; +} + +CArchCond +CArchMultithreadWindows::newCondVar() +{ + CArchCondImpl* cond = new CArchCondImpl; + cond->m_events[CArchCondImpl::kSignal] = CreateEvent(NULL, + FALSE, FALSE, NULL); + cond->m_events[CArchCondImpl::kBroadcast] = CreateEvent(NULL, + TRUE, FALSE, NULL); + cond->m_waitCountMutex = newMutex(); + cond->m_waitCount = 0; + return cond; +} + +void +CArchMultithreadWindows::closeCondVar(CArchCond cond) +{ + CloseHandle(cond->m_events[CArchCondImpl::kSignal]); + CloseHandle(cond->m_events[CArchCondImpl::kBroadcast]); + closeMutex(cond->m_waitCountMutex); + delete cond; +} + +void +CArchMultithreadWindows::signalCondVar(CArchCond cond) +{ + // is anybody waiting? + lockMutex(cond->m_waitCountMutex); + const bool hasWaiter = (cond->m_waitCount > 0); + unlockMutex(cond->m_waitCountMutex); + + // wake one thread if anybody is waiting + if (hasWaiter) { + SetEvent(cond->m_events[CArchCondImpl::kSignal]); + } +} + +void +CArchMultithreadWindows::broadcastCondVar(CArchCond cond) +{ + // is anybody waiting? + lockMutex(cond->m_waitCountMutex); + const bool hasWaiter = (cond->m_waitCount > 0); + unlockMutex(cond->m_waitCountMutex); + + // wake all threads if anybody is waiting + if (hasWaiter) { + SetEvent(cond->m_events[CArchCondImpl::kBroadcast]); + } +} + +bool +CArchMultithreadWindows::waitCondVar(CArchCond cond, + CArchMutex mutex, double timeout) +{ + // prepare to wait + const DWORD winTimeout = (timeout < 0.0) ? INFINITE : + static_cast(1000.0 * timeout); + + // make a list of the condition variable events and the cancel event + // for the current thread. + HANDLE handles[3]; + handles[0] = cond->m_events[CArchCondImpl::kSignal]; + handles[1] = cond->m_events[CArchCondImpl::kBroadcast]; + handles[2] = getCancelEventForCurrentThread(); + + // update waiter count + lockMutex(cond->m_waitCountMutex); + ++cond->m_waitCount; + unlockMutex(cond->m_waitCountMutex); + + // release mutex. this should be atomic with the wait so that it's + // impossible for another thread to signal us between the unlock and + // the wait, which would lead to a lost signal on broadcasts. + // however, we're using a manual reset event for broadcasts which + // stays set until we reset it, so we don't lose the broadcast. + unlockMutex(mutex); + + // wait for a signal or broadcast + DWORD result = WaitForMultipleObjects(3, handles, FALSE, winTimeout); + + // cancel takes priority + if (result != WAIT_OBJECT_0 + 2 && + WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) { + result = WAIT_OBJECT_0 + 2; + } + + // update the waiter count and check if we're the last waiter + lockMutex(cond->m_waitCountMutex); + --cond->m_waitCount; + const bool last = (result == WAIT_OBJECT_0 + 1 && cond->m_waitCount == 0); + unlockMutex(cond->m_waitCountMutex); + + // reset the broadcast event if we're the last waiter + if (last) { + ResetEvent(cond->m_events[CArchCondImpl::kBroadcast]); + } + + // reacquire the mutex + lockMutex(mutex); + + // cancel thread if necessary + if (result == WAIT_OBJECT_0 + 2) { + ARCH->testCancelThread(); + } + + // return success or failure + return (result == WAIT_OBJECT_0 + 0 || + result == WAIT_OBJECT_0 + 1); +} + +CArchMutex +CArchMultithreadWindows::newMutex() +{ + CArchMutexImpl* mutex = new CArchMutexImpl; + InitializeCriticalSection(&mutex->m_mutex); + return mutex; +} + +void +CArchMultithreadWindows::closeMutex(CArchMutex mutex) +{ + DeleteCriticalSection(&mutex->m_mutex); + delete mutex; +} + +void +CArchMultithreadWindows::lockMutex(CArchMutex mutex) +{ + EnterCriticalSection(&mutex->m_mutex); +} + +void +CArchMultithreadWindows::unlockMutex(CArchMutex mutex) +{ + LeaveCriticalSection(&mutex->m_mutex); +} + +CArchThread +CArchMultithreadWindows::newThread(ThreadFunc func, void* data) +{ + lockMutex(m_threadMutex); + + // create thread impl for new thread + CArchThreadImpl* thread = new CArchThreadImpl; + thread->m_func = func; + thread->m_userData = data; + + // create thread + unsigned int id; + thread->m_thread = reinterpret_cast(_beginthreadex(NULL, 0, + threadFunc, (void*)thread, 0, &id)); + thread->m_id = static_cast(id); + + // check if thread was started + if (thread->m_thread == 0) { + // failed to start thread so clean up + delete thread; + thread = NULL; + } + else { + // add thread to list + insert(thread); + + // increment ref count to account for the thread itself + refThread(thread); + } + + // note that the child thread will wait until we release this mutex + unlockMutex(m_threadMutex); + + return thread; +} + +CArchThread +CArchMultithreadWindows::newCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = find(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + assert(thread != NULL); + return thread; +} + +void +CArchMultithreadWindows::closeThread(CArchThread thread) +{ + assert(thread != NULL); + + // decrement ref count and clean up thread if no more references + if (--thread->m_refCount == 0) { + // close the handle (main thread has a NULL handle) + if (thread->m_thread != NULL) { + CloseHandle(thread->m_thread); + } + + // remove thread from list + lockMutex(m_threadMutex); + assert(findNoRefOrCreate(thread->m_id) == thread); + erase(thread); + unlockMutex(m_threadMutex); + + // done with thread + delete thread; + } +} + +CArchThread +CArchMultithreadWindows::copyThread(CArchThread thread) +{ + refThread(thread); + return thread; +} + +void +CArchMultithreadWindows::cancelThread(CArchThread thread) +{ + assert(thread != NULL); + + // set cancel flag + SetEvent(thread->m_cancel); +} + +void +CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n) +{ + struct CPriorityInfo { + public: + DWORD m_class; + int m_level; + }; + static const CPriorityInfo s_pClass[] = { + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_IDLE }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_IDLE }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL} + }; +#if defined(_DEBUG) + // don't use really high priorities when debugging + static const size_t s_pMax = 13; +#else + static const size_t s_pMax = sizeof(s_pClass) / sizeof(s_pClass[0]) - 1; +#endif + static const size_t s_pBase = 8; // index of normal priority + + assert(thread != NULL); + + size_t index; + if (n > 0 && s_pBase < n) { + // lowest priority + index = 0; + } + else { + index = s_pBase - n; + if (index > s_pMax) { + // highest priority + index = s_pMax; + } + } + SetPriorityClass(GetCurrentProcess(), s_pClass[index].m_class); + SetThreadPriority(thread->m_thread, s_pClass[index].m_level); +} + +void +CArchMultithreadWindows::testCancelThread() +{ + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + + // test cancel on thread + testCancelThreadImpl(thread); +} + +bool +CArchMultithreadWindows::wait(CArchThread target, double timeout) +{ + assert(target != NULL); + + lockMutex(m_threadMutex); + + // find current thread + CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); + + // ignore wait if trying to wait on ourself + if (target == self) { + unlockMutex(m_threadMutex); + return false; + } + + // ref the target so it can't go away while we're watching it + refThread(target); + + unlockMutex(m_threadMutex); + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for the target thread to + // terminate. + HANDLE handles[2]; + handles[0] = target->m_exit; + handles[1] = self->m_cancel; + DWORD result = WaitForMultipleObjects(2, handles, FALSE, t); + + // cancel takes priority + if (result != WAIT_OBJECT_0 + 1 && + WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) { + result = WAIT_OBJECT_0 + 1; + } + + // release target + closeThread(target); + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 0: + // target thread terminated + return true; + + case WAIT_OBJECT_0 + 1: + // this thread was cancelled. does not return. + testCancelThreadImpl(self); + + default: + // timeout or error + return false; + } +} + +IArchMultithread::EWaitResult +CArchMultithreadWindows::waitForEvent(CArchThread target, double timeout) +{ + // find current thread. ref the target so it can't go away while + // we're watching it. + lockMutex(m_threadMutex); + CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); + assert(self != NULL); + if (target != NULL) { + refThread(target); + } + unlockMutex(m_threadMutex); + + // see if we've been cancelled before checking if any events + // are pending. + DWORD result = WaitForSingleObject(self->m_cancel, 0); + if (result == WAIT_OBJECT_0) { + if (target != NULL) { + closeThread(target); + } + testCancelThreadImpl(self); + } + + // check if messages are available first. if we don't do this then + // MsgWaitForMultipleObjects() will block even if the queue isn't + // empty if the messages in the queue were there before the last + // call to GetMessage()/PeekMessage(). + if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { + return kEvent; + } + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for the target thread to + // terminate. + DWORD n = (target == NULL || target == self) ? 1 : 2; + HANDLE handles[2]; + handles[0] = self->m_cancel; + handles[1] = (n == 2) ? target->m_exit : NULL; + result = MsgWaitForMultipleObjects(n, handles, FALSE, t, QS_ALLINPUT); + + // cancel takes priority + if (result != WAIT_OBJECT_0 + 0 && + WaitForSingleObject(handles[0], 0) == WAIT_OBJECT_0) { + result = WAIT_OBJECT_0 + 0; + } + + // release target + if (target != NULL) { + closeThread(target); + } + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 0: + // this thread was cancelled. does not return. + testCancelThreadImpl(self); + + case WAIT_OBJECT_0 + 1: + // target thread terminated + if (n == 2) { + return kExit; + } + // fall through + + case WAIT_OBJECT_0 + 2: + // message is available + return kEvent; + + default: + // timeout or error + return kTimeout; + } +} + +/* +bool +CArchMultithreadWindows::waitForEvent(double timeout) +{ + // check if messages are available first. if we don't do this then + // MsgWaitForMultipleObjects() will block even if the queue isn't + // empty if the messages in the queue were there before the last + // call to GetMessage()/PeekMessage(). + if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { + return true; + } + + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + assert(self != NULL); + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for a message + HANDLE handles[1]; + handles[0] = self->m_cancel; + DWORD result = MsgWaitForMultipleObjects(1, handles, FALSE, t, QS_ALLINPUT); + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 1: + // message is available + return true; + + case WAIT_OBJECT_0 + 0: + // this thread was cancelled. does not return. + testCancelThreadImpl(self); + + default: + // timeout or error + return false; + } +} +*/ + +bool +CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return (thread1 == thread2); +} + +bool +CArchMultithreadWindows::isExitedThread(CArchThread thread) +{ + // poll exit event + return (WaitForSingleObject(thread->m_exit, 0) == WAIT_OBJECT_0); +} + +void* +CArchMultithreadWindows::getResultOfThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + void* result = thread->m_result; + unlockMutex(m_threadMutex); + return result; +} + +IArchMultithread::ThreadID +CArchMultithreadWindows::getIDOfThread(CArchThread thread) +{ + return static_cast(thread->m_id); +} + +CArchThreadImpl* +CArchMultithreadWindows::find(DWORD id) +{ + CArchThreadImpl* impl = findNoRef(id); + if (impl != NULL) { + refThread(impl); + } + return impl; +} + +CArchThreadImpl* +CArchMultithreadWindows::findNoRef(DWORD id) +{ + CArchThreadImpl* impl = findNoRefOrCreate(id); + if (impl == NULL) { + // create thread for calling thread which isn't in our list and + // add it to the list. this won't normally happen but it can if + // the system calls us under a new thread, like it does when we + // run as a service. + impl = new CArchThreadImpl; + impl->m_thread = NULL; + impl->m_id = GetCurrentThreadId(); + insert(impl); + } + return impl; +} + +CArchThreadImpl* +CArchMultithreadWindows::findNoRefOrCreate(DWORD id) +{ + // linear search + for (CThreadList::const_iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if ((*index)->m_id == id) { + return *index; + } + } + return NULL; +} + +void +CArchMultithreadWindows::insert(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // thread shouldn't already be on the list + assert(findNoRefOrCreate(thread->m_id) == NULL); + + // append to list + m_threadList.push_back(thread); +} + +void +CArchMultithreadWindows::erase(CArchThreadImpl* thread) +{ + for (CThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if (*index == thread) { + m_threadList.erase(index); + break; + } + } +} + +void +CArchMultithreadWindows::refThread(CArchThreadImpl* thread) +{ + assert(thread != NULL); + assert(findNoRefOrCreate(thread->m_id) != NULL); + ++thread->m_refCount; +} + +void +CArchMultithreadWindows::testCancelThreadImpl(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // poll cancel event. return if not set. + const DWORD result = WaitForSingleObject(thread->m_cancel, 0); + if (result != WAIT_OBJECT_0) { + return; + } + + // update cancel state + lockMutex(m_threadMutex); + bool cancel = !thread->m_cancelling; + thread->m_cancelling = true; + ResetEvent(thread->m_cancel); + unlockMutex(m_threadMutex); + + // unwind thread's stack if cancelling + if (cancel) { + throw XThreadCancel(); + } +} + +unsigned int __stdcall +CArchMultithreadWindows::threadFunc(void* vrep) +{ + // get the thread + CArchThreadImpl* thread = reinterpret_cast(vrep); + + // run thread + s_instance->doThreadFunc(thread); + + // terminate the thread + return 0; +} + +void +CArchMultithreadWindows::doThreadFunc(CArchThread thread) +{ + // wait for parent to initialize this object + lockMutex(m_threadMutex); + unlockMutex(m_threadMutex); + + // default priority is slightly below normal + setPriorityOfThread(thread, 1); + + void* result = NULL; + try { + // go + result = (*thread->m_func)(thread->m_userData); + } + + catch (XThreadCancel&) { + // client called cancel() + } + catch (...) { + // note -- don't catch (...) to avoid masking bugs + SetEvent(thread->m_exit); + closeThread(thread); + throw; + } + + // thread has exited + lockMutex(m_threadMutex); + thread->m_result = result; + unlockMutex(m_threadMutex); + SetEvent(thread->m_exit); + + // done with thread + closeThread(thread); +} diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h new file mode 100644 index 0000000..2a8d546 --- /dev/null +++ b/lib/arch/CArchMultithreadWindows.h @@ -0,0 +1,101 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMULTITHREADWINDOWS_H +#define CARCHMULTITHREADWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchMultithread.h" +#include "stdlist.h" +#include + +#define ARCH_MULTITHREAD CArchMultithreadWindows + +class CArchCondImpl { +public: + enum { kSignal = 0, kBroadcast }; + + HANDLE m_events[2]; + mutable int m_waitCount; + CArchMutex m_waitCountMutex; +}; + +class CArchMutexImpl { +public: + CRITICAL_SECTION m_mutex; +}; + +//! Win32 implementation of IArchMultithread +class CArchMultithreadWindows : public IArchMultithread { +public: + CArchMultithreadWindows(); + virtual ~CArchMultithreadWindows(); + + // + // accessors + // + + HANDLE getCancelEventForCurrentThread(); + + static CArchMultithreadWindows* getInstance(); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual EWaitResult waitForEvent(CArchThread, double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + +private: + CArchThreadImpl* find(DWORD id); + CArchThreadImpl* findNoRef(DWORD id); + CArchThreadImpl* findNoRefOrCreate(DWORD id); + void insert(CArchThreadImpl* thread); + void erase(CArchThreadImpl* thread); + + void refThread(CArchThreadImpl* rep); + void testCancelThreadImpl(CArchThreadImpl* rep); + + void doThreadFunc(CArchThread thread); + static unsigned int __stdcall threadFunc(void* vrep); + +private: + typedef std::list CThreadList; + + static CArchMultithreadWindows* s_instance; + + CArchMutex m_threadMutex; + + CThreadList m_threadList; +}; + +#endif diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp new file mode 100644 index 0000000..9b6eec6 --- /dev/null +++ b/lib/arch/CArchNetworkBSD.cpp @@ -0,0 +1,853 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchNetworkBSD.h" +#include "CArch.h" +#include "XArchUnix.h" +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif +#include +#include +#if !defined(TCP_NODELAY) +# include +#endif +#include +#include +#include + +#if HAVE_POLL +# include +#else +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TIME_H +# include +# endif +#endif + +static const int s_family[] = { + PF_UNSPEC, + PF_INET +}; +static const int s_type[] = { + SOCK_DGRAM, + SOCK_STREAM +}; + +// +// CArchNetworkBSD +// + +CArchNetworkBSD::CArchNetworkBSD() +{ + // create mutex to make some calls thread safe + m_mutex = ARCH->newMutex(); +} + +CArchNetworkBSD::~CArchNetworkBSD() +{ + ARCH->closeMutex(m_mutex); +} + +CArchSocket +CArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type) +{ + // allocate socket object + CArchSocketImpl* newSocket = new CArchSocketImpl; + + // create socket + int fd = socket(s_family[family], s_type[type], 0); + if (fd == -1) { + throwError(errno); + } + + newSocket->m_fd = fd; + newSocket->m_connected = false; + newSocket->m_refCount = 1; + return newSocket; +} + +CArchSocket +CArchNetworkBSD::copySocket(CArchSocket s) +{ + assert(s != NULL); + + // ref the socket and return it + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + return s; +} + +void +CArchNetworkBSD::closeSocket(CArchSocket s) +{ + assert(s != NULL); + + // unref the socket and note if it should be released + ARCH->lockMutex(m_mutex); + const bool doClose = (--s->m_refCount == 0); + ARCH->unlockMutex(m_mutex); + + // close the socket if necessary + if (doClose) { + do { + if (close(s->m_fd) == -1) { + // close failed + int err = errno; + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + // restore the last ref and throw + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + throwError(err); + } + } while (false); + delete s; + } +} + +void +CArchNetworkBSD::closeSocketForRead(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown(s->m_fd, 0) == -1) { + if (errno != ENOTCONN) { + throwError(errno); + } + } +} + +void +CArchNetworkBSD::closeSocketForWrite(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown(s->m_fd, 1) == -1) { + if (errno != ENOTCONN) { + throwError(errno); + } + } +} + +void +CArchNetworkBSD::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + if (bind(s->m_fd, &addr->m_addr, addr->m_len) == -1) { + throwError(errno); + } +} + +void +CArchNetworkBSD::listenOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // hardcoding backlog + if (listen(s->m_fd, 3) == -1) { + throwError(errno); + } +} + +CArchSocket +CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + assert(s != NULL); + + // if user passed NULL in addr then use scratch space + CArchNetAddress dummy; + if (addr == NULL) { + addr = &dummy; + } + + // create new socket and address + CArchSocketImpl* newSocket = new CArchSocketImpl; + *addr = new CArchNetAddressImpl; + + // accept on socket + int fd; + do { + fd = accept(s->m_fd, &(*addr)->m_addr, &(*addr)->m_len); + if (fd == -1) { + int err = errno; + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + if (err == ECONNABORTED) { + // connection was aborted; try again + ARCH->testCancelThread(); + continue; + } + delete newSocket; + delete *addr; + *addr = NULL; + throwError(err); + } + } while (false); + + // initialize socket + newSocket->m_fd = fd; + newSocket->m_connected = true; + newSocket->m_refCount = 1; + + // discard address if not requested + if (addr == &dummy) { + ARCH->closeAddr(dummy); + } + + return newSocket; +} + +void +CArchNetworkBSD::connectSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + do { + if (connect(s->m_fd, &addr->m_addr, addr->m_len) == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + if (errno == EISCONN) { + // already connected + break; + } + + if (errno == EAGAIN) { + // connecting + throw XArchNetworkConnecting(new XArchEvalUnix(errno)); + } + + throwError(errno); + } + } while (false); + + ARCH->lockMutex(m_mutex); + s->m_connected = true; + ARCH->unlockMutex(m_mutex); +} + +#if HAVE_POLL + +int +CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) +{ + assert(pe != NULL || num == 0); + + // return if nothing to do + if (num == 0) { + if (timeout > 0.0) { + ARCH->sleep(timeout); + } + return 0; + } + + // allocate space for translated query + struct pollfd* pfd = new struct pollfd[num]; + + // translate query + for (int i = 0; i < num; ++i) { + pfd[i].fd = (pe[i].m_socket == NULL) ? -1 : pe[i].m_socket->m_fd; + pfd[i].events = 0; + if ((pe[i].m_events & kPOLLIN) != 0) { + pfd[i].events |= POLLIN; + } + if ((pe[i].m_events & kPOLLOUT) != 0) { + pfd[i].events |= POLLOUT; + } + } + + // do the poll + int n; + do { + n = poll(pfd, num, static_cast(1000.0 * timeout)); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + delete[] pfd; + throwError(errno); + } + } while (false); + + // translate back + for (int i = 0; i < num; ++i) { + pe[i].m_revents = 0; + if ((pfd[i].revents & POLLIN) != 0) { + pe[i].m_revents |= kPOLLIN; + } + if ((pfd[i].revents & POLLOUT) != 0) { + pe[i].m_revents |= kPOLLOUT; + } + if ((pfd[i].revents & POLLERR) != 0) { + pe[i].m_revents |= kPOLLERR; + } + if ((pfd[i].revents & POLLNVAL) != 0) { + pe[i].m_revents |= kPOLLNVAL; + } + } + + // done with translated query + delete[] pfd; + + return n; +} + +#else + +int +CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) +{ + int i, n; + + do { + // prepare sets for select + n = 0; + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; + + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; + } + + int fdi = pe[i].m_socket->m_fd; + if (pe[i].m_events & kPOLLIN) { + FD_SET(pe[i].m_socket->m_fd, &readSet); + readSetP = &readSet; + if (fdi > n) { + n = fdi; + } + } + if (pe[i].m_events & kPOLLOUT) { + FD_SET(pe[i].m_socket->m_fd, &writeSet); + writeSetP = &writeSet; + if (fdi > n) { + n = fdi; + } + } + if (true) { + FD_SET(pe[i].m_socket->m_fd, &errSet); + errSetP = &errSet; + if (fdi > n) { + n = fdi; + } + } + } + + // if there are no sockets then don't block forever + if (n == 0 && timeout < 0.0) { + timeout = 0.0; + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = static_cast(timeout); + timeout2.tv_usec = static_cast(1.0e+6 * + (timeout - timeout2.tv_sec)); + } + + // do the select + n = select((SELECT_TYPE_ARG1) n + 1, + SELECT_TYPE_ARG234 readSetP, + SELECT_TYPE_ARG234 writeSetP, + SELECT_TYPE_ARG234 errSetP, + SELECT_TYPE_ARG5 timeout2P); + + // handle results + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + n = 0; + for (i = 0; i < num; ++i) { + if (pe[i].m_socket != NULL) { + if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) { + pe[i].m_revents |= kPOLLIN; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) { + pe[i].m_revents |= kPOLLOUT; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) { + pe[i].m_revents |= kPOLLERR; + } + } + if (pe[i].m_revents != 0) { + ++n; + } + } + } while (false); + + return n; +} + +#endif + +size_t +CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len) +{ + assert(s != NULL); + + ssize_t n; + do { + n = read(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + } while (false); + ARCH->testCancelThread(); + return n; +} + +size_t +CArchNetworkBSD::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + assert(s != NULL); + + ssize_t n; + do { + n = write(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + } while (false); + ARCH->testCancelThread(); + return n; +} + +void +CArchNetworkBSD::throwErrorOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // get the error from the socket layer + int err = 0; + socklen_t size = sizeof(err); + if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR, &err, &size) == -1) { + err = errno; + } + + // throw if there's an error + if (err != 0) { + throwError(err); + } +} + +bool +CArchNetworkBSD::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + assert(s != NULL); + + int mode = fcntl(s->m_fd, F_GETFL, 0); + if (mode == -1) { + throwError(errno); + } + bool old = ((mode & O_NDELAY) == 0); + if (blocking) { + mode &= ~O_NDELAY; + } + else { + mode |= O_NDELAY; + } + if (fcntl(s->m_fd, F_SETFL, mode) == -1) { + throwError(errno); + } + return old; +} + +bool +CArchNetworkBSD::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + assert(s != NULL); + + // get old state + int oflag; + socklen_t size = sizeof(oflag); + if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, &oflag, &size) == -1) { + throwError(errno); + } + + int flag = noDelay ? 1 : 0; + size = sizeof(flag); + if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, &flag, size) == -1) { + throwError(errno); + } + + return (oflag != 0); +} + +std::string +CArchNetworkBSD::getHostName() +{ + char name[256]; + if (gethostname(name, sizeof(name)) == -1) { + name[0] = '\0'; + } + else { + name[sizeof(name) - 1] = '\0'; + } + return name; +} + +CArchNetAddress +CArchNetworkBSD::newAnyAddr(EAddressFamily family) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // fill it in + switch (family) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_family = AF_INET; + ipAddr->sin_port = 0; + ipAddr->sin_addr.s_addr = INADDR_ANY; + addr->m_len = sizeof(struct sockaddr_in); + break; + } + + default: + delete addr; + assert(0 && "invalid family"); + } + + return addr; +} + +CArchNetAddress +CArchNetworkBSD::copyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + // allocate and copy address + return new CArchNetAddressImpl(*addr); +} + +CArchNetAddress +CArchNetworkBSD::nameToAddr(const std::string& name) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // try to convert assuming an IPv4 dot notation address + struct sockaddr_in inaddr; + memset(&inaddr, 0, sizeof(inaddr)); + if (inet_aton(name.c_str(), &inaddr.sin_addr) != 0) { + // it's a dot notation address + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = AF_INET; + inaddr.sin_port = 0; + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + else { + // mutexed address lookup (ugh) + ARCH->lockMutex(m_mutex); + struct hostent* info = gethostbyname(name.c_str()); + if (info == NULL) { + ARCH->unlockMutex(m_mutex); + delete addr; + throwNameError(h_errno); + } + + // copy over address (only IPv4 currently supported) + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = info->h_addrtype; + inaddr.sin_port = 0; + memcpy(&inaddr.sin_addr, info->h_addr_list[0], info->h_length); + memcpy(&addr->m_addr, &inaddr, addr->m_len); + + // done with static buffer + ARCH->unlockMutex(m_mutex); + } + + return addr; +} + +void +CArchNetworkBSD::closeAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + delete addr; +} + +std::string +CArchNetworkBSD::addrToName(CArchNetAddress addr) +{ + assert(addr != NULL); + + // mutexed name lookup (ugh) + ARCH->lockMutex(m_mutex); + struct hostent* info = gethostbyaddr( + reinterpret_cast(&addr->m_addr), + addr->m_len, addr->m_addr.sa_family); + if (info == NULL) { + ARCH->unlockMutex(m_mutex); + throwNameError(h_errno); + } + + // save (primary) name + std::string name = info->h_name; + + // done with static buffer + ARCH->unlockMutex(m_mutex); + + return name; +} + +std::string +CArchNetworkBSD::addrToString(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ARCH->lockMutex(m_mutex); + std::string s = inet_ntoa(ipAddr->sin_addr); + ARCH->unlockMutex(m_mutex); + return s; + } + + default: + assert(0 && "unknown address family"); + return ""; + } +} + +IArchNetwork::EAddressFamily +CArchNetworkBSD::getAddrFamily(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (addr->m_addr.sa_family) { + case AF_INET: + return kINET; + + default: + return kUNKNOWN; + } +} + +void +CArchNetworkBSD::setAddrPort(CArchNetAddress addr, int port) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_port = htons(port); + break; + } + + default: + assert(0 && "unknown address family"); + break; + } +} + +int +CArchNetworkBSD::getAddrPort(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return ntohs(ipAddr->sin_port); + } + + default: + assert(0 && "unknown address family"); + return 0; + } +} + +bool +CArchNetworkBSD::isAnyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return (ipAddr->sin_addr.s_addr == INADDR_ANY && + addr->m_len == sizeof(struct sockaddr_in)); + } + + default: + assert(0 && "unknown address family"); + return true; + } +} + +void +CArchNetworkBSD::throwError(int err) +{ + switch (err) { + case EAGAIN: + throw XArchNetworkWouldBlock(new XArchEvalUnix(err)); + + case EACCES: + case EPERM: + throw XArchNetworkAccess(new XArchEvalUnix(err)); + + case ENFILE: + case EMFILE: + case ENODEV: + case ENOBUFS: + case ENOMEM: + case ENETDOWN: +#if defined(ENOSR) + case ENOSR: +#endif + throw XArchNetworkResource(new XArchEvalUnix(err)); + + case EPROTOTYPE: + case EPROTONOSUPPORT: + case EAFNOSUPPORT: + case EPFNOSUPPORT: + case ESOCKTNOSUPPORT: + case EINVAL: + case ENOPROTOOPT: + case EOPNOTSUPP: + case ESHUTDOWN: +#if defined(ENOPKG) + case ENOPKG: +#endif + throw XArchNetworkSupport(new XArchEvalUnix(err)); + + case EIO: + throw XArchNetworkIO(new XArchEvalUnix(err)); + + case EADDRNOTAVAIL: + throw XArchNetworkNoAddress(new XArchEvalUnix(err)); + + case EADDRINUSE: + throw XArchNetworkAddressInUse(new XArchEvalUnix(err)); + + case EHOSTUNREACH: + case ENETUNREACH: + throw XArchNetworkNoRoute(new XArchEvalUnix(err)); + + case ENOTCONN: + throw XArchNetworkNotConnected(new XArchEvalUnix(err)); + + case EPIPE: + case ECONNABORTED: + case ECONNRESET: + throw XArchNetworkDisconnected(new XArchEvalUnix(err)); + + case ECONNREFUSED: + throw XArchNetworkConnectionRefused(new XArchEvalUnix(err)); + + case EINPROGRESS: + case EALREADY: + throw XArchNetworkConnecting(new XArchEvalUnix(err)); + + case EHOSTDOWN: + case ETIMEDOUT: + throw XArchNetworkTimedOut(new XArchEvalUnix(err)); + + default: + throw XArchNetwork(new XArchEvalUnix(err)); + } +} + +void +CArchNetworkBSD::throwNameError(int err) +{ + static const char* s_msg[] = { + "The specified host is unknown", + "The requested name is valid but does not have an IP address", + "A non-recoverable name server error occurred", + "A temporary error occurred on an authoritative name server", + "An unknown name server error occurred" + }; + + switch (err) { + case HOST_NOT_FOUND: + throw XArchNetworkNameUnknown(s_msg[0]); + + case NO_DATA: + throw XArchNetworkNameNoAddress(s_msg[1]); + + case NO_RECOVERY: + throw XArchNetworkNameFailure(s_msg[2]); + + case TRY_AGAIN: + throw XArchNetworkNameUnavailable(s_msg[3]); + + default: + throw XArchNetworkName(s_msg[4]); + } +} diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h new file mode 100644 index 0000000..9718d71 --- /dev/null +++ b/lib/arch/CArchNetworkBSD.h @@ -0,0 +1,89 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHNETWORKBSD_H +#define CARCHNETWORKBSD_H + +#include "IArchNetwork.h" +#include "IArchMultithread.h" +#if HAVE_SYS_SOCKET_H +# include +#endif + +#if !defined(HAVE_SOCKLEN_T) +typedef int socklen_t; +#endif + +#define ARCH_NETWORK CArchNetworkBSD + +class CArchSocketImpl { +public: + int m_fd; + bool m_connected; + int m_refCount; +}; + +class CArchNetAddressImpl { +public: + CArchNetAddressImpl() : m_len(sizeof(m_addr)) { } + +public: + struct sockaddr m_addr; + socklen_t m_len; +}; + +//! Berkeley (BSD) sockets implementation of IArchNetwork +class CArchNetworkBSD : public IArchNetwork { +public: + CArchNetworkBSD(); + virtual ~CArchNetworkBSD(); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + +private: + void throwError(int); + void throwNameError(int); + +private: + CArchMutex m_mutex; +}; + +#endif diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp new file mode 100644 index 0000000..09b507a --- /dev/null +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -0,0 +1,839 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include "CArchNetworkWinsock.h" +#include "CArch.h" +#include "XArchWindows.h" + +static const int s_family[] = { + PF_UNSPEC, + PF_INET +}; +static const int s_type[] = { + SOCK_DGRAM, + SOCK_STREAM +}; + +static SOCKET (PASCAL FAR *accept_winsock)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen); +static int (PASCAL FAR *bind_winsock)(SOCKET s, const struct sockaddr FAR *addr, int namelen); +static int (PASCAL FAR *close_winsock)(SOCKET s); +static int (PASCAL FAR *connect_winsock)(SOCKET s, const struct sockaddr FAR *name, int namelen); +static int (PASCAL FAR *gethostname_winsock)(char FAR * name, int namelen); +static int (PASCAL FAR *getsockerror_winsock)(void); +static int (PASCAL FAR *getsockopt_winsock)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen); +static u_short (PASCAL FAR *htons_winsock)(u_short v); +static char FAR * (PASCAL FAR *inet_ntoa_winsock)(struct in_addr in); +static unsigned long (PASCAL FAR *inet_addr_winsock)(const char FAR * cp); +static int (PASCAL FAR *ioctl_winsock)(SOCKET s, int cmd, void FAR * data); +static int (PASCAL FAR *listen_winsock)(SOCKET s, int backlog); +static u_short (PASCAL FAR *ntohs_winsock)(u_short v); +static int (PASCAL FAR *recv_winsock)(SOCKET s, void FAR * buf, int len, int flags); +static int (PASCAL FAR *select_winsock)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); +static int (PASCAL FAR *send_winsock)(SOCKET s, const void FAR * buf, int len, int flags); +static int (PASCAL FAR *setsockopt_winsock)(SOCKET s, int level, int optname, const void FAR * optval, int optlen); +static int (PASCAL FAR *shutdown_winsock)(SOCKET s, int how); +static SOCKET (PASCAL FAR *socket_winsock)(int af, int type, int protocol); +static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * addr, int len, int type); +static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name); +static int (PASCAL FAR *WSACleanup_winsock)(void); +static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR * fdset); + +#undef FD_ISSET +#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set)) + +#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name) + +static HMODULE s_networkModule = NULL; + +static +FARPROC +netGetProcAddress(HMODULE module, LPCSTR name) +{ + FARPROC func = ::GetProcAddress(module, name); + if (!func) { + throw XArchNetworkSupport(""); + } + return func; +} + +// +// CArchNetworkWinsock +// + +CArchNetworkWinsock::CArchNetworkWinsock() +{ + static const char* s_library[] = { "ws2_32.dll", "wsock32.dll" }; + + assert(WSACleanup_winsock == NULL); + assert(s_networkModule == NULL); + + // try each winsock library + for (size_t i = 0; i < sizeof(s_library) / sizeof(s_library[0]); ++i) { + try { + init((HMODULE)::LoadLibrary(s_library[i])); + m_mutex = ARCH->newMutex(); + return; + } + catch (XArchNetwork&) { + // ignore + } + } + + // can't initialize any library + throw XArchNetworkSupport("Cannot load winsock library"); +} + +CArchNetworkWinsock::~CArchNetworkWinsock() +{ + if (s_networkModule != NULL) { + WSACleanup_winsock(); + ::FreeLibrary(s_networkModule); + + WSACleanup_winsock = NULL; + s_networkModule = NULL; + } + ARCH->closeMutex(m_mutex); +} + +void +CArchNetworkWinsock::init(HMODULE module) +{ + assert(module != NULL); + + // get startup function address + int (PASCAL FAR *startup)(WORD, LPWSADATA); + setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA)); + + // startup network library + WORD version = MAKEWORD(1 /*major*/, 1 /*minor*/); + WSADATA data; + int err = startup(version, &data); + if (data.wVersion != version) { + throw XArchNetworkSupport(new XArchEvalWinsock(err)); + } + if (err != 0) { + // some other initialization error + throwError(err); + } + + // get function addresses + setfunc(accept_winsock, accept, SOCKET (PASCAL FAR *)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen)); + setfunc(bind_winsock, bind, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *addr, int namelen)); + setfunc(close_winsock, closesocket, int (PASCAL FAR *)(SOCKET s)); + setfunc(connect_winsock, connect, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *name, int namelen)); + setfunc(gethostname_winsock, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); + setfunc(getsockerror_winsock, WSAGetLastError, int (PASCAL FAR *)(void)); + setfunc(getsockopt_winsock, getsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen)); + setfunc(htons_winsock, htons, u_short (PASCAL FAR *)(u_short v)); + setfunc(inet_ntoa_winsock, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(inet_addr_winsock, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(ioctl_winsock, ioctlsocket, int (PASCAL FAR *)(SOCKET s, int cmd, void FAR *)); + setfunc(listen_winsock, listen, int (PASCAL FAR *)(SOCKET s, int backlog)); + setfunc(ntohs_winsock, ntohs, u_short (PASCAL FAR *)(u_short v)); + setfunc(recv_winsock, recv, int (PASCAL FAR *)(SOCKET s, void FAR * buf, int len, int flags)); + setfunc(select_winsock, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); + setfunc(send_winsock, send, int (PASCAL FAR *)(SOCKET s, const void FAR * buf, int len, int flags)); + setfunc(setsockopt_winsock, setsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, const void FAR * optval, int optlen)); + setfunc(shutdown_winsock, shutdown, int (PASCAL FAR *)(SOCKET s, int how)); + setfunc(socket_winsock, socket, SOCKET (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void)); + setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(SOCKET, fd_set FAR *)); + + s_networkModule = module; +} + +CArchSocket +CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type) +{ + // allocate socket object + CArchSocketImpl* socket = new CArchSocketImpl; + + // create socket + SOCKET fd = socket_winsock(s_family[family], s_type[type], 0); + if (fd == INVALID_SOCKET) { + throwError(getsockerror_winsock()); + } + + socket->m_socket = fd; + socket->m_connected = false; + socket->m_refCount = 1; + return socket; +} + +CArchSocket +CArchNetworkWinsock::copySocket(CArchSocket s) +{ + assert(s != NULL); + + // ref the socket and return it + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + return s; +} + +void +CArchNetworkWinsock::closeSocket(CArchSocket s) +{ + assert(s != NULL); + + // unref the socket and note if it should be released + ARCH->lockMutex(m_mutex); + const bool doClose = (--s->m_refCount == 0); + ARCH->unlockMutex(m_mutex); + + // close the socket if necessary + if (doClose) { + do { + if (close_winsock(s->m_socket) == SOCKET_ERROR) { + // close failed + int err = getsockerror_winsock(); + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + // restore the last ref and throw + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + throwError(err); + } + } while (false); + delete s; + } +} + +void +CArchNetworkWinsock::closeSocketForRead(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown_winsock(s->m_socket, SD_RECEIVE) == SOCKET_ERROR) { + if (getsockerror_winsock() != WSAENOTCONN) { + throwError(getsockerror_winsock()); + } + } +} + +void +CArchNetworkWinsock::closeSocketForWrite(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown_winsock(s->m_socket, SD_SEND) == SOCKET_ERROR) { + if (getsockerror_winsock() != WSAENOTCONN) { + throwError(getsockerror_winsock()); + } + } +} + +void +CArchNetworkWinsock::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + if (bind_winsock(s->m_socket, &addr->m_addr, addr->m_len) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } +} + +void +CArchNetworkWinsock::listenOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // hardcoding backlog + if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } +} + +CArchSocket +CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + assert(s != NULL); + + // if user passed NULL in addr then use scratch space + CArchNetAddress dummy; + if (addr == NULL) { + addr = &dummy; + } + + // create new socket and address + CArchSocketImpl* socket = new CArchSocketImpl; + *addr = new CArchNetAddressImpl; + + // accept on socket + SOCKET fd; + do { + fd = accept_winsock(s->m_socket, &(*addr)->m_addr, &(*addr)->m_len); + if (fd == INVALID_SOCKET) { + int err = getsockerror_winsock(); + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + if (err == WSAECONNABORTED) { + // connection was aborted; try again + ARCH->testCancelThread(); + continue; + } + delete socket; + delete *addr; + *addr = NULL; + throwError(err); + } + } while (false); + + // initialize socket + socket->m_socket = fd; + socket->m_connected = true; + socket->m_refCount = 1; + + // discard address if not requested + if (addr == &dummy) { + ARCH->closeAddr(dummy); + } + + return socket; +} + +void +CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + do { + if (connect_winsock(s->m_socket, &addr->m_addr, + addr->m_len) == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + if (getsockerror_winsock() == WSAEISCONN) { + // already connected + break; + } + + if (getsockerror_winsock() == WSAEWOULDBLOCK) { + // connecting + throw XArchNetworkConnecting(new XArchEvalWinsock( + getsockerror_winsock())); + } + + throwError(getsockerror_winsock()); + } + } while (false); + + ARCH->lockMutex(m_mutex); + s->m_connected = true; + ARCH->unlockMutex(m_mutex); +} + +int +CArchNetworkWinsock::pollSocket(CPollEntry pe[], int num, double timeout) +{ + int i, n; + + do { + // prepare sets for select + n = 0; + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; + + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; + } + + if (pe[i].m_events & kPOLLIN) { + FD_SET(pe[i].m_socket->m_socket, &readSet); + readSetP = &readSet; + n = 1; + } + if (pe[i].m_events & kPOLLOUT) { + FD_SET(pe[i].m_socket->m_socket, &writeSet); + writeSetP = &writeSet; + n = 1; + } + if (true) { + FD_SET(pe[i].m_socket->m_socket, &errSet); + errSetP = &errSet; + n = 1; + } + } + + // if there are no sockets then don't block forever + if (n == 0 && timeout < 0.0) { + timeout = 0.0; + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = static_cast(timeout); + timeout2.tv_usec = static_cast(1.0e+6 * + (timeout - timeout2.tv_sec)); + } + + // do the select + n = select_winsock(0, readSetP, writeSetP, errSetP, timeout2P); + + // handle results + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + n = 0; + for (i = 0; i < num; ++i) { + if (pe[i].m_socket != NULL) { + if (FD_ISSET(pe[i].m_socket->m_socket, &readSet)) { + pe[i].m_revents |= kPOLLIN; + } + if (FD_ISSET(pe[i].m_socket->m_socket, &writeSet)) { + pe[i].m_revents |= kPOLLOUT; + } + if (FD_ISSET(pe[i].m_socket->m_socket, &errSet)) { + pe[i].m_revents |= kPOLLERR; + } + } + if (pe[i].m_revents != 0) { + ++n; + } + } + } while (false); + + return n; +} + +size_t +CArchNetworkWinsock::readSocket(CArchSocket s, void* buf, size_t len) +{ + assert(s != NULL); + + int n; + do { + n = recv_winsock(s->m_socket, buf, len, 0); + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + } while (false); + ARCH->testCancelThread(); + return static_cast(n); +} + +size_t +CArchNetworkWinsock::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + assert(s != NULL); + + int n; + do { + n = send_winsock(s->m_socket, buf, len, 0); + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + } while (false); + ARCH->testCancelThread(); + return static_cast(n); +} + +void +CArchNetworkWinsock::throwErrorOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // get the error from the socket layer + int err = 0; + int size = sizeof(err); + if (getsockopt_winsock(s->m_socket, SOL_SOCKET, + SO_ERROR, &err, &size) == SOCKET_ERROR) { + err = getsockerror_winsock(); + } + + // throw if there's an error + if (err != 0) { + throwError(err); + } +} + +bool +CArchNetworkWinsock::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + assert(s != NULL); + + int flag = blocking ? 0 : 1; + if (ioctl_winsock(s->m_socket, FIONBIO, &flag) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + // FIXME -- can't get the current blocking state of socket? + return true; +} + +bool +CArchNetworkWinsock::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + assert(s != NULL); + + // get old state + BOOL oflag; + int size = sizeof(oflag); + if (getsockopt_winsock(s->m_socket, IPPROTO_TCP, + TCP_NODELAY, &oflag, &size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + // set new state + BOOL flag = noDelay ? 1 : 0; + size = sizeof(flag); + if (setsockopt_winsock(s->m_socket, IPPROTO_TCP, + TCP_NODELAY, &flag, size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + return (oflag != 0); +} + +std::string +CArchNetworkWinsock::getHostName() +{ + char name[256]; + if (gethostname_winsock(name, sizeof(name)) == -1) { + name[0] = '\0'; + } + else { + name[sizeof(name) - 1] = '\0'; + } + return name; +} + +CArchNetAddress +CArchNetworkWinsock::newAnyAddr(EAddressFamily family) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // fill it in + switch (family) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_family = AF_INET; + ipAddr->sin_port = 0; + ipAddr->sin_addr.s_addr = INADDR_ANY; + addr->m_len = sizeof(struct sockaddr_in); + break; + } + + default: + delete addr; + assert(0 && "invalid family"); + } + + return addr; +} + +CArchNetAddress +CArchNetworkWinsock::copyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + // allocate and copy address + return new CArchNetAddressImpl(*addr); +} + +CArchNetAddress +CArchNetworkWinsock::nameToAddr(const std::string& name) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // try to convert assuming an IPv4 dot notation address + struct sockaddr_in inaddr; + memset(&inaddr, 0, sizeof(inaddr)); + inaddr.sin_addr.s_addr = inet_addr_winsock(name.c_str()); + if (inaddr.sin_addr.s_addr != INADDR_NONE) { + // it's a dot notation address + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = AF_INET; + inaddr.sin_port = 0; + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + else { + // address lookup + struct hostent* info = gethostbyname_winsock(name.c_str()); + if (info == NULL) { + delete addr; + throwNameError(getsockerror_winsock()); + } + + // copy over address (only IPv4 currently supported) + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = info->h_addrtype; + inaddr.sin_port = 0; + memcpy(&inaddr.sin_addr, info->h_addr_list[0], info->h_length); + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + return addr; +} + +void +CArchNetworkWinsock::closeAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + delete addr; +} + +std::string +CArchNetworkWinsock::addrToName(CArchNetAddress addr) +{ + assert(addr != NULL); + + // name lookup + struct hostent* info = gethostbyaddr_winsock( + reinterpret_cast(&addr->m_addr), + addr->m_len, addr->m_addr.sa_family); + if (info == NULL) { + throwNameError(getsockerror_winsock()); + } + + // return (primary) name + return info->h_name; +} + +std::string +CArchNetworkWinsock::addrToString(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return inet_ntoa_winsock(ipAddr->sin_addr); + } + + default: + assert(0 && "unknown address family"); + return ""; + } +} + +IArchNetwork::EAddressFamily +CArchNetworkWinsock::getAddrFamily(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (addr->m_addr.sa_family) { + case AF_INET: + return kINET; + + default: + return kUNKNOWN; + } +} + +void +CArchNetworkWinsock::setAddrPort(CArchNetAddress addr, int port) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_port = htons_winsock(static_cast(port)); + break; + } + + default: + assert(0 && "unknown address family"); + break; + } +} + +int +CArchNetworkWinsock::getAddrPort(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return ntohs_winsock(ipAddr->sin_port); + } + + default: + assert(0 && "unknown address family"); + return 0; + } +} + +bool +CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return (ipAddr->sin_addr.s_addr == INADDR_ANY && + addr->m_len == sizeof(struct sockaddr_in)); + } + + default: + assert(0 && "unknown address family"); + return true; + } +} + +void +CArchNetworkWinsock::throwError(int err) +{ + switch (err) { + case WSAEWOULDBLOCK: + throw XArchNetworkWouldBlock(new XArchEvalWinsock(err)); + + case WSAEACCES: + throw XArchNetworkAccess(new XArchEvalWinsock(err)); + + case WSAEMFILE: + case WSAENOBUFS: + case WSAENETDOWN: + throw XArchNetworkResource(new XArchEvalWinsock(err)); + + case WSAEPROTOTYPE: + case WSAEPROTONOSUPPORT: + case WSAEAFNOSUPPORT: + case WSAEPFNOSUPPORT: + case WSAESOCKTNOSUPPORT: + case WSAEINVAL: + case WSAENOPROTOOPT: + case WSAEOPNOTSUPP: + case WSAESHUTDOWN: + case WSANOTINITIALISED: + case WSAVERNOTSUPPORTED: + case WSASYSNOTREADY: + throw XArchNetworkSupport(new XArchEvalWinsock(err)); + + case WSAEADDRNOTAVAIL: + throw XArchNetworkNoAddress(new XArchEvalWinsock(err)); + + case WSAEADDRINUSE: + throw XArchNetworkAddressInUse(new XArchEvalWinsock(err)); + + case WSAEHOSTUNREACH: + case WSAENETUNREACH: + throw XArchNetworkNoRoute(new XArchEvalWinsock(err)); + + case WSAENOTCONN: + throw XArchNetworkNotConnected(new XArchEvalWinsock(err)); + + case WSAENETRESET: + case WSAEDISCON: + case WSAECONNABORTED: + case WSAECONNRESET: + throw XArchNetworkDisconnected(new XArchEvalWinsock(err)); + + case WSAECONNREFUSED: + throw XArchNetworkConnectionRefused(new XArchEvalWinsock(err)); + + case WSAEINPROGRESS: + case WSAEALREADY: + throw XArchNetworkConnecting(new XArchEvalWinsock(err)); + + case WSAEHOSTDOWN: + case WSAETIMEDOUT: + throw XArchNetworkTimedOut(new XArchEvalWinsock(err)); + + case WSAHOST_NOT_FOUND: + throw XArchNetworkNameUnknown(new XArchEvalWinsock(err)); + + case WSANO_DATA: + throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err)); + + case WSANO_RECOVERY: + throw XArchNetworkNameFailure(new XArchEvalWinsock(err)); + + case WSATRY_AGAIN: + throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err)); + + default: + throw XArchNetwork(new XArchEvalWinsock(err)); + } +} + +void +CArchNetworkWinsock::throwNameError(int err) +{ + switch (err) { + case WSAHOST_NOT_FOUND: + throw XArchNetworkNameUnknown(new XArchEvalWinsock(err)); + + case WSANO_DATA: + throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err)); + + case WSANO_RECOVERY: + throw XArchNetworkNameFailure(new XArchEvalWinsock(err)); + + case WSATRY_AGAIN: + throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err)); + + default: + throw XArchNetworkName(new XArchEvalWinsock(err)); + } +} diff --git a/lib/arch/CArchNetworkWinsock.h b/lib/arch/CArchNetworkWinsock.h new file mode 100644 index 0000000..58ce9ca --- /dev/null +++ b/lib/arch/CArchNetworkWinsock.h @@ -0,0 +1,92 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHNETWORKWINSOCK_H +#define CARCHNETWORKWINSOCK_H + +#define WIN32_LEAN_AND_MEAN + +// declare no functions in winsock2 +#define INCL_WINSOCK_API_PROTOTYPES 0 +#define INCL_WINSOCK_API_TYPEDEFS 0 + +#include "IArchNetwork.h" +#include "IArchMultithread.h" +#include +#include + +#define ARCH_NETWORK CArchNetworkWinsock + +class CArchSocketImpl { +public: + SOCKET m_socket; + bool m_connected; + int m_refCount; +}; + +class CArchNetAddressImpl { +public: + CArchNetAddressImpl() : m_len(sizeof(m_addr)) { } + +public: + struct sockaddr m_addr; + int m_len; +}; + +//! Win32 implementation of IArchNetwork +class CArchNetworkWinsock : public IArchNetwork { +public: + CArchNetworkWinsock(); + virtual ~CArchNetworkWinsock(); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + +private: + void init(HMODULE); + + void throwError(int); + void throwNameError(int); + +private: + CArchMutex m_mutex; +}; + +#endif diff --git a/lib/arch/CArchSleepUnix.cpp b/lib/arch/CArchSleepUnix.cpp new file mode 100644 index 0000000..3501072 --- /dev/null +++ b/lib/arch/CArchSleepUnix.cpp @@ -0,0 +1,88 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchSleepUnix.h" +#include "CArch.h" +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#if !HAVE_NANOSLEEP +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_UNISTD_H +# include +# endif +#endif + +// +// CArchSleepUnix +// + +CArchSleepUnix::CArchSleepUnix() +{ + // do nothing +} + +CArchSleepUnix::~CArchSleepUnix() +{ + // do nothing +} + +void +CArchSleepUnix::sleep(double timeout) +{ + ARCH->testCancelThread(); + if (timeout < 0.0) { + return; + } + +#if HAVE_NANOSLEEP + // prep timeout + struct timespec t; + t.tv_sec = (long)timeout; + t.tv_nsec = (long)(1.0e+9 * (timeout - (double)t.tv_sec)); + + // wait + while (nanosleep(&t, &t) < 0) + ARCH->testCancelThread(); +#else + /* emulate nanosleep() with select() */ + double startTime = ARCH->time(); + double timeLeft = timeout; + while (timeLeft > 0.0) { + struct timeval timeout2; + timeout2.tv_sec = static_cast(timeLeft); + timeout2.tv_usec = static_cast(1.0e+6 * (timeLeft - + timeout2.tv_sec)); + select((SELECT_TYPE_ARG1) 0, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 &timeout2); + ARCH->testCancelThread(); + timeLeft = timeout - (ARCH->time() - startTime); + } +#endif +} diff --git a/lib/arch/CArchSleepUnix.h b/lib/arch/CArchSleepUnix.h new file mode 100644 index 0000000..939ca40 --- /dev/null +++ b/lib/arch/CArchSleepUnix.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSLEEPUNIX_H +#define CARCHSLEEPUNIX_H + +#include "IArchSleep.h" + +#define ARCH_SLEEP CArchSleepUnix + +//! Unix implementation of IArchSleep +class CArchSleepUnix : public IArchSleep { +public: + CArchSleepUnix(); + virtual ~CArchSleepUnix(); + + // IArchSleep overrides + virtual void sleep(double timeout); +}; + +#endif diff --git a/lib/arch/CArchSleepWindows.cpp b/lib/arch/CArchSleepWindows.cpp new file mode 100644 index 0000000..f6c8bed --- /dev/null +++ b/lib/arch/CArchSleepWindows.cpp @@ -0,0 +1,57 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchSleepWindows.h" +#include "CArch.h" +#include "CArchMultithreadWindows.h" + +// +// CArchSleepWindows +// + +CArchSleepWindows::CArchSleepWindows() +{ + // do nothing +} + +CArchSleepWindows::~CArchSleepWindows() +{ + // do nothing +} + +void +CArchSleepWindows::sleep(double timeout) +{ + ARCH->testCancelThread(); + if (timeout < 0.0) { + return; + } + + // get the cancel event from the current thread. this only + // works if we're using the windows multithread object but + // this is windows so that's pretty certain; we'll get a + // link error if we're not, though. + CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance(); + if (mt != NULL) { + HANDLE cancelEvent = mt->getCancelEventForCurrentThread(); + WaitForSingleObject(cancelEvent, (DWORD)(1000.0 * timeout)); + if (timeout == 0.0) { + Sleep(0); + } + } + else { + Sleep((DWORD)(1000.0 * timeout)); + } + ARCH->testCancelThread(); +} diff --git a/lib/arch/CArchSleepWindows.h b/lib/arch/CArchSleepWindows.h new file mode 100644 index 0000000..a5a5fa9 --- /dev/null +++ b/lib/arch/CArchSleepWindows.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSLEEPWINDOWS_H +#define CARCHSLEEPWINDOWS_H + +#include "IArchSleep.h" + +#define ARCH_SLEEP CArchSleepWindows + +//! Win32 implementation of IArchSleep +class CArchSleepWindows : public IArchSleep { +public: + CArchSleepWindows(); + virtual ~CArchSleepWindows(); + + // IArchSleep overrides + virtual void sleep(double timeout); +}; + +#endif diff --git a/lib/arch/CArchStringUnix.cpp b/lib/arch/CArchStringUnix.cpp new file mode 100644 index 0000000..32b2ad4 --- /dev/null +++ b/lib/arch/CArchStringUnix.cpp @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchStringUnix.h" +#include + +#include "CMultibyte.cpp" + +// +// CArchStringUnix +// + +CArchStringUnix::CArchStringUnix() +{ + initMB(); +} + +CArchStringUnix::~CArchStringUnix() +{ + cleanMB(); +} + +#include "vsnprintf.cpp" + +IArchString::EWideCharEncoding +CArchStringUnix::getWideCharEncoding() +{ + return kUCS4; +} diff --git a/lib/arch/CArchStringUnix.h b/lib/arch/CArchStringUnix.h new file mode 100644 index 0000000..b9fddcb --- /dev/null +++ b/lib/arch/CArchStringUnix.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSTRINGUNIX_H +#define CARCHSTRINGUNIX_H + +#include "IArchString.h" + +#define ARCH_STRING CArchStringUnix + +//! Unix implementation of IArchString +class CArchStringUnix : public IArchString { +public: + CArchStringUnix(); + virtual ~CArchStringUnix(); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual EWideCharEncoding + getWideCharEncoding(); +}; + +#endif diff --git a/lib/arch/CArchStringWindows.cpp b/lib/arch/CArchStringWindows.cpp new file mode 100644 index 0000000..4aefda2 --- /dev/null +++ b/lib/arch/CArchStringWindows.cpp @@ -0,0 +1,44 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define WIN32_LEAN_AND_MEAN + +#include "CArchStringWindows.h" +#include + +#include "CMultibyte.cpp" + +// +// CArchStringWindows +// + +CArchStringWindows::CArchStringWindows() +{ + initMB(); +} + +CArchStringWindows::~CArchStringWindows() +{ + cleanMB(); +} + +#define HAVE_VSNPRINTF 1 +#define ARCH_VSNPRINTF _vsnprintf +#include "vsnprintf.cpp" + +IArchString::EWideCharEncoding +CArchStringWindows::getWideCharEncoding() +{ + return kUTF16; +} diff --git a/lib/arch/CArchStringWindows.h b/lib/arch/CArchStringWindows.h new file mode 100644 index 0000000..c130352 --- /dev/null +++ b/lib/arch/CArchStringWindows.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSTRINGWINDOWS_H +#define CARCHSTRINGWINDOWS_H + +#include "IArchString.h" + +#define ARCH_STRING CArchStringWindows + +//! Win32 implementation of IArchString +class CArchStringWindows : public IArchString { +public: + CArchStringWindows(); + virtual ~CArchStringWindows(); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual EWideCharEncoding + getWideCharEncoding(); +}; + +#endif diff --git a/lib/arch/CArchTaskBarWindows.cpp b/lib/arch/CArchTaskBarWindows.cpp new file mode 100644 index 0000000..987ac6c --- /dev/null +++ b/lib/arch/CArchTaskBarWindows.cpp @@ -0,0 +1,518 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchTaskBarWindows.h" +#include "IArchTaskBarReceiver.h" +#include "CArch.h" +#include "XArch.h" +#include +#include + +static const UINT kAddReceiver = WM_USER + 10; +static const UINT kRemoveReceiver = WM_USER + 11; +static const UINT kUpdateReceiver = WM_USER + 12; +static const UINT kNotifyReceiver = WM_USER + 13; +static const UINT kFirstReceiverID = WM_USER + 14; + +// +// CArchTaskBarWindows +// + +CArchTaskBarWindows* CArchTaskBarWindows::s_instance = NULL; +HINSTANCE CArchTaskBarWindows::s_appInstance = NULL; + +CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) : + m_nextID(kFirstReceiverID) +{ + // save the singleton instance + s_instance = this; + + // save app instance + s_appInstance = reinterpret_cast(appInstance); + + // we need a mutex + m_mutex = ARCH->newMutex(); + + // and a condition variable which uses the above mutex + m_ready = false; + m_condVar = ARCH->newCondVar(); + + // we're going to want to get a result from the thread we're + // about to create to know if it initialized successfully. + // so we lock the condition variable. + ARCH->lockMutex(m_mutex); + + // open a window and run an event loop in a separate thread. + // this has to happen in a separate thread because if we + // create a window on the current desktop with the current + // thread then the current thread won't be able to switch + // desktops if it needs to. + m_thread = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this); + + // wait for child thread + while (!m_ready) { + ARCH->waitCondVar(m_condVar, m_mutex, -1.0); + } + + // ready + ARCH->unlockMutex(m_mutex); +} + +CArchTaskBarWindows::~CArchTaskBarWindows() +{ + if (m_thread != NULL) { + ARCH->cancelThread(m_thread); + ARCH->wait(m_thread, -1.0); + ARCH->closeThread(m_thread); + } + ARCH->closeCondVar(m_condVar); + ARCH->closeMutex(m_mutex); + s_instance = NULL; +} + +void +CArchTaskBarWindows::addDialog(HWND hwnd) +{ + // add dialog to added dialogs list + ARCH->lockMutex(s_instance->m_mutex); + s_instance->m_addedDialogs.insert(std::make_pair(hwnd, true)); + ARCH->unlockMutex(s_instance->m_mutex); +} + +void +CArchTaskBarWindows::removeDialog(HWND hwnd) +{ + // mark dialog as removed + ARCH->lockMutex(s_instance->m_mutex); + CDialogs::iterator index = s_instance->m_dialogs.find(hwnd); + if (index != s_instance->m_dialogs.end()) { + index->second = false; + } + s_instance->m_addedDialogs.erase(hwnd); + ARCH->unlockMutex(s_instance->m_mutex); +} + +void +CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver) +{ + // ignore bogus receiver + if (receiver == NULL) { + return; + } + + // add receiver if necessary + CReceiverToInfoMap::iterator index = m_receivers.find(receiver); + if (index == m_receivers.end()) { + // add it, creating a new message ID for it + CReceiverInfo info; + info.m_id = getNextID(); + index = m_receivers.insert(std::make_pair(receiver, info)).first; + + // add ID to receiver mapping + m_idTable.insert(std::make_pair(info.m_id, index)); + } + + // add receiver + PostMessage(m_hwnd, kAddReceiver, index->second.m_id, 0); +} + +void +CArchTaskBarWindows::removeReceiver(IArchTaskBarReceiver* receiver) +{ + // find receiver + CReceiverToInfoMap::iterator index = m_receivers.find(receiver); + if (index == m_receivers.end()) { + return; + } + + // remove icon. wait for this to finish before returning. + SendMessage(m_hwnd, kRemoveReceiver, index->second.m_id, 0); + + // recycle the ID + recycleID(index->second.m_id); + + // discard + m_idTable.erase(index->second.m_id); + m_receivers.erase(index); +} + +void +CArchTaskBarWindows::updateReceiver(IArchTaskBarReceiver* receiver) +{ + // find receiver + CReceiverToInfoMap::const_iterator index = m_receivers.find(receiver); + if (index == m_receivers.end()) { + return; + } + + // update icon and tool tip + PostMessage(m_hwnd, kUpdateReceiver, index->second.m_id, 0); +} + +UINT +CArchTaskBarWindows::getNextID() +{ + if (m_oldIDs.empty()) { + return m_nextID++; + } + UINT id = m_oldIDs.back(); + m_oldIDs.pop_back(); + return id; +} + +void +CArchTaskBarWindows::recycleID(UINT id) +{ + m_oldIDs.push_back(id); +} + +void +CArchTaskBarWindows::addIcon(UINT id) +{ + ARCH->lockMutex(m_mutex); + CIDToReceiverMap::const_iterator index = m_idTable.find(id); + if (index != m_idTable.end()) { + modifyIconNoLock(index->second, NIM_ADD); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::removeIcon(UINT id) +{ + ARCH->lockMutex(m_mutex); + removeIconNoLock(id); + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::updateIcon(UINT id) +{ + ARCH->lockMutex(m_mutex); + CIDToReceiverMap::const_iterator index = m_idTable.find(id); + if (index != m_idTable.end()) { + modifyIconNoLock(index->second, NIM_MODIFY); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::addAllIcons() +{ + ARCH->lockMutex(m_mutex); + for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); + index != m_receivers.end(); ++index) { + modifyIconNoLock(index, NIM_ADD); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::removeAllIcons() +{ + ARCH->lockMutex(m_mutex); + for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); + index != m_receivers.end(); ++index) { + removeIconNoLock(index->second.m_id); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::modifyIconNoLock( + CReceiverToInfoMap::const_iterator index, DWORD taskBarMessage) +{ + // get receiver + UINT id = index->second.m_id; + IArchTaskBarReceiver* receiver = index->first; + + // lock receiver so icon and tool tip are guaranteed to be consistent + receiver->lock(); + + // get icon data + HICON icon = reinterpret_cast( + const_cast(receiver->getIcon())); + + // get tool tip + std::string toolTip = receiver->getToolTip(); + + // done querying + receiver->unlock(); + + // prepare to add icon + NOTIFYICONDATA data; + data.cbSize = sizeof(NOTIFYICONDATA); + data.hWnd = m_hwnd; + data.uID = id; + data.uFlags = NIF_MESSAGE; + data.uCallbackMessage = kNotifyReceiver; + data.hIcon = icon; + if (icon != NULL) { + data.uFlags |= NIF_ICON; + } + if (!toolTip.empty()) { + strncpy(data.szTip, toolTip.c_str(), sizeof(data.szTip)); + data.szTip[sizeof(data.szTip) - 1] = '\0'; + data.uFlags |= NIF_TIP; + } + else { + data.szTip[0] = '\0'; + } + + // add icon + if (Shell_NotifyIcon(taskBarMessage, &data) == 0) { + // failed + } +} + +void +CArchTaskBarWindows::removeIconNoLock(UINT id) +{ + NOTIFYICONDATA data; + data.cbSize = sizeof(NOTIFYICONDATA); + data.hWnd = m_hwnd; + data.uID = id; + if (Shell_NotifyIcon(NIM_DELETE, &data) == 0) { + // failed + } +} + +void +CArchTaskBarWindows::handleIconMessage( + IArchTaskBarReceiver* receiver, LPARAM lParam) +{ + // process message + switch (lParam) { + case WM_LBUTTONDOWN: + receiver->showStatus(); + break; + + case WM_LBUTTONDBLCLK: + receiver->primaryAction(); + break; + + case WM_RBUTTONUP: { + POINT p; + GetCursorPos(&p); + receiver->runMenu(p.x, p.y); + break; + } + + case WM_MOUSEMOVE: + // currently unused + break; + + default: + // unused + break; + } +} + +bool +CArchTaskBarWindows::processDialogs(MSG* msg) +{ + // only one thread can be in this method on any particular object + // at any given time. that's not a problem since only our event + // loop calls this method and there's just one of those. + + ARCH->lockMutex(m_mutex); + + // remove removed dialogs + m_dialogs.erase(false); + + // merge added dialogs into the dialog list + for (CDialogs::const_iterator index = m_addedDialogs.begin(); + index != m_addedDialogs.end(); ++index) { + m_dialogs.insert(std::make_pair(index->first, index->second)); + } + m_addedDialogs.clear(); + + ARCH->unlockMutex(m_mutex); + + // check message against all dialogs until one handles it. + // note that we don't hold a lock while checking because + // the message is processed and may make calls to this + // object. that's okay because addDialog() and + // removeDialog() don't change the map itself (just the + // values of some elements). + ARCH->lockMutex(m_mutex); + for (CDialogs::const_iterator index = m_dialogs.begin(); + index != m_dialogs.end(); ++index) { + if (index->second) { + ARCH->unlockMutex(m_mutex); + if (IsDialogMessage(index->first, msg)) { + return true; + } + ARCH->lockMutex(m_mutex); + } + } + ARCH->unlockMutex(m_mutex); + + return false; +} + +LRESULT +CArchTaskBarWindows::wndProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case kNotifyReceiver: { + // lookup receiver + CIDToReceiverMap::const_iterator index = m_idTable.find(wParam); + if (index != m_idTable.end()) { + IArchTaskBarReceiver* receiver = index->second->first; + handleIconMessage(receiver, lParam); + return 0; + } + break; + } + + case kAddReceiver: + addIcon(wParam); + break; + + case kRemoveReceiver: + removeIcon(wParam); + break; + + case kUpdateReceiver: + updateIcon(wParam); + break; + + default: + if (msg == m_taskBarRestart) { + // task bar was recreated so re-add our icons + addAllIcons(); + } + break; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +LRESULT CALLBACK +CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + // if msg is WM_NCCREATE, extract the CArchTaskBarWindows* and put + // it in the extra window data then forward the call. + CArchTaskBarWindows* self = NULL; + if (msg == WM_NCCREATE) { + CREATESTRUCT* createInfo; + createInfo = reinterpret_cast(lParam); + self = reinterpret_cast( + createInfo->lpCreateParams); + SetWindowLong(hwnd, 0, reinterpret_cast(self)); + } + else { + // get the extra window data and forward the call + LONG data = GetWindowLong(hwnd, 0); + if (data != 0) { + self = reinterpret_cast( + reinterpret_cast(data)); + } + } + + // forward the message + if (self != NULL) { + return self->wndProc(hwnd, msg, wParam, lParam); + } + else { + return DefWindowProc(hwnd, msg, wParam, lParam); + } +} + +void +CArchTaskBarWindows::threadMainLoop() +{ + // register the task bar restart message + m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_NOCLOSE; + classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*); + classInfo.hInstance = s_appInstance; + classInfo.hIcon = NULL; + classInfo.hCursor = NULL; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = TEXT("SynergyTaskBar"); + classInfo.hIconSm = NULL; + ATOM windowClass = RegisterClassEx(&classInfo); + + // create window + m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, + reinterpret_cast(windowClass), + TEXT("Synergy Task Bar"), + WS_POPUP, + 0, 0, 1, 1, + NULL, + NULL, + s_appInstance, + reinterpret_cast(this)); + + // signal ready + ARCH->lockMutex(m_mutex); + m_ready = true; + ARCH->broadcastCondVar(m_condVar); + ARCH->unlockMutex(m_mutex); + + // handle failure + if (m_hwnd == NULL) { + UnregisterClass((LPCTSTR)windowClass, s_appInstance); + return; + } + + try { + // main loop + MSG msg; + for (;;) { + // wait for message + if (ARCH->waitForEvent(NULL, -1.0) != IArchMultithread::kEvent) { + continue; + } + + // peek for message and remove it. we don't GetMessage() + // because we should never block here, only in waitForEvent(). + if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + continue; + } + + // check message against dialogs + if (!processDialogs(&msg)) { + // process message + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + catch (XThread&) { + // clean up + removeAllIcons(); + DestroyWindow(m_hwnd); + UnregisterClass((LPCTSTR)windowClass, s_appInstance); + throw; + } +} + +void* +CArchTaskBarWindows::threadEntry(void* self) +{ + reinterpret_cast(self)->threadMainLoop(); + return NULL; +} diff --git a/lib/arch/CArchTaskBarWindows.h b/lib/arch/CArchTaskBarWindows.h new file mode 100644 index 0000000..67e9af1 --- /dev/null +++ b/lib/arch/CArchTaskBarWindows.h @@ -0,0 +1,110 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTASKBARWINDOWS_H +#define CARCHTASKBARWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchTaskBar.h" +#include "IArchMultithread.h" +#include "stdmap.h" +#include "stdvector.h" +#include + +#define ARCH_TASKBAR CArchTaskBarWindows + +//! Win32 implementation of IArchTaskBar +class CArchTaskBarWindows : public IArchTaskBar { +public: + CArchTaskBarWindows(void*); + virtual ~CArchTaskBarWindows(); + + //! Add a dialog window + /*! + Tell the task bar event loop about a dialog. Win32 annoyingly + requires messages destined for modeless dialog boxes to be + dispatched differently than other messages. + */ + static void addDialog(HWND); + + //! Remove a dialog window + /*! + Remove a dialog window added via \c addDialog(). + */ + static void removeDialog(HWND); + + // IArchTaskBar overrides + virtual void addReceiver(IArchTaskBarReceiver*); + virtual void removeReceiver(IArchTaskBarReceiver*); + virtual void updateReceiver(IArchTaskBarReceiver*); + +private: + class CReceiverInfo { + public: + UINT m_id; + }; + + typedef std::map CReceiverToInfoMap; + typedef std::map CIDToReceiverMap; + typedef std::vector CIDStack; + typedef std::map CDialogs; + + UINT getNextID(); + void recycleID(UINT); + + void addIcon(UINT); + void removeIcon(UINT); + void updateIcon(UINT); + void addAllIcons(); + void removeAllIcons(); + void modifyIconNoLock(CReceiverToInfoMap::const_iterator, + DWORD taskBarMessage); + void removeIconNoLock(UINT id); + void handleIconMessage(IArchTaskBarReceiver*, LPARAM); + + bool processDialogs(MSG*); + LRESULT wndProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK + staticWndProc(HWND, UINT, WPARAM, LPARAM); + void threadMainLoop(); + static void* threadEntry(void*); + +private: + static CArchTaskBarWindows* s_instance; + static HINSTANCE s_appInstance; + + // multithread data + CArchMutex m_mutex; + CArchCond m_condVar; + bool m_ready; + int m_result; + CArchThread m_thread; + + // child thread data + HWND m_hwnd; + UINT m_taskBarRestart; + + // shared data + CReceiverToInfoMap m_receivers; + CIDToReceiverMap m_idTable; + CIDStack m_oldIDs; + UINT m_nextID; + + // dialogs + CDialogs m_dialogs; + CDialogs m_addedDialogs; +}; + +#endif diff --git a/lib/arch/CArchTaskBarXWindows.cpp b/lib/arch/CArchTaskBarXWindows.cpp new file mode 100644 index 0000000..6934f27 --- /dev/null +++ b/lib/arch/CArchTaskBarXWindows.cpp @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchTaskBarXWindows.h" + +// +// CArchTaskBarXWindows +// + +CArchTaskBarXWindows::CArchTaskBarXWindows(void*) +{ + // do nothing +} + +CArchTaskBarXWindows::~CArchTaskBarXWindows() +{ + // do nothing +} + +void +CArchTaskBarXWindows::addReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} + +void +CArchTaskBarXWindows::removeReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} + +void +CArchTaskBarXWindows::updateReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} diff --git a/lib/arch/CArchTaskBarXWindows.h b/lib/arch/CArchTaskBarXWindows.h new file mode 100644 index 0000000..abf2801 --- /dev/null +++ b/lib/arch/CArchTaskBarXWindows.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTASKBARXWINDOWS_H +#define CARCHTASKBARXWINDOWS_H + +#include "IArchTaskBar.h" + +#define ARCH_TASKBAR CArchTaskBarXWindows + +//! X11 implementation of IArchTaskBar +class CArchTaskBarXWindows : public IArchTaskBar { +public: + CArchTaskBarXWindows(void*); + virtual ~CArchTaskBarXWindows(); + + // IArchTaskBar overrides + virtual void addReceiver(IArchTaskBarReceiver*); + virtual void removeReceiver(IArchTaskBarReceiver*); + virtual void updateReceiver(IArchTaskBarReceiver*); +}; + +#endif diff --git a/lib/arch/CArchTimeUnix.cpp b/lib/arch/CArchTimeUnix.cpp new file mode 100644 index 0000000..49506ba --- /dev/null +++ b/lib/arch/CArchTimeUnix.cpp @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchTimeUnix.h" +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +// +// CArchTimeUnix +// + +CArchTimeUnix::CArchTimeUnix() +{ + // do nothing +} + +CArchTimeUnix::~CArchTimeUnix() +{ + // do nothing +} + +double +CArchTimeUnix::time() +{ + struct timeval t; + gettimeofday(&t, NULL); + return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec; +} diff --git a/lib/arch/CArchTimeUnix.h b/lib/arch/CArchTimeUnix.h new file mode 100644 index 0000000..78c6ff6 --- /dev/null +++ b/lib/arch/CArchTimeUnix.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTIMEUNIX_H +#define CARCHTIMEUNIX_H + +#include "IArchTime.h" + +#define ARCH_TIME CArchTimeUnix + +//! Generic Unix implementation of IArchTime +class CArchTimeUnix : public IArchTime { +public: + CArchTimeUnix(); + virtual ~CArchTimeUnix(); + + // IArchTime overrides + virtual double time(); +}; + +#endif diff --git a/lib/arch/CArchTimeWindows.cpp b/lib/arch/CArchTimeWindows.cpp new file mode 100644 index 0000000..57aee29 --- /dev/null +++ b/lib/arch/CArchTimeWindows.cpp @@ -0,0 +1,86 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +// avoid getting a lot a crap from mmsystem.h that we don't need +#define MMNODRV // Installable driver support +#define MMNOSOUND // Sound support +#define MMNOWAVE // Waveform support +#define MMNOMIDI // MIDI support +#define MMNOAUX // Auxiliary audio support +#define MMNOMIXER // Mixer support +#define MMNOJOY // Joystick support +#define MMNOMCI // MCI support +#define MMNOMMIO // Multimedia file I/O support +#define MMNOMMSYSTEM // General MMSYSTEM functions + +#define WIN32_LEAN_AND_MEAN + +#include "CArchTimeWindows.h" +#include +#include + +typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void); + +static double s_freq = 0.0; +static HINSTANCE s_mmInstance = NULL; +static PTimeGetTime s_tgt = NULL; + + +// +// CArchTimeWindows +// + +CArchTimeWindows::CArchTimeWindows() +{ + assert(s_freq == 0.0 || s_mmInstance == NULL); + + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) { + s_freq = 1.0 / static_cast(freq.QuadPart); + } + else { + // load winmm.dll and get timeGetTime + s_mmInstance = LoadLibrary("winmm"); + if (s_mmInstance != NULL) { + s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime"); + } + } +} + +CArchTimeWindows::~CArchTimeWindows() +{ + s_freq = 0.0; + if (s_mmInstance == NULL) { + FreeLibrary(reinterpret_cast(s_mmInstance)); + s_tgt = NULL; + s_mmInstance = NULL; + } +} + +double +CArchTimeWindows::time() +{ + // get time. we try three ways, in order of descending precision + if (s_freq != 0.0) { + LARGE_INTEGER c; + QueryPerformanceCounter(&c); + return s_freq * static_cast(c.QuadPart); + } + else if (s_tgt != NULL) { + return 0.001 * static_cast(s_tgt()); + } + else { + return 0.001 * static_cast(GetTickCount()); + } +} diff --git a/lib/arch/CArchTimeWindows.h b/lib/arch/CArchTimeWindows.h new file mode 100644 index 0000000..fb9b1e9 --- /dev/null +++ b/lib/arch/CArchTimeWindows.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTIMEWINDOWS_H +#define CARCHTIMEWINDOWS_H + +#include "IArchTime.h" + +#define ARCH_TIME CArchTimeWindows + +//! Win32 implementation of IArchTime +class CArchTimeWindows : public IArchTime { +public: + CArchTimeWindows(); + virtual ~CArchTimeWindows(); + + // IArchTime overrides + virtual double time(); +}; + +#endif diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp new file mode 100644 index 0000000..43b8b41 --- /dev/null +++ b/lib/arch/CMultibyte.cpp @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMULTIBYTE_H +#define CMULTIBYTE_H + +#include "common.h" + +#if (HAVE_MBSINIT && HAVE_MBRTOWC && HAVE_WCRTOMB) || WINDOWS_LIKE +#include "CMultibyteOS.cpp" +#else +#include "CMultibyteEmu.cpp" +#endif + +CArchMBState +ARCH_STRING::newMBState() +{ + CArchMBState state = new CArchMBStateImpl; + initMBState(state); + return state; +} + +void +ARCH_STRING::closeMBState(CArchMBState state) +{ + delete state; +} + +#endif diff --git a/lib/arch/CMultibyteEmu.cpp b/lib/arch/CMultibyteEmu.cpp new file mode 100644 index 0000000..0fd3087 --- /dev/null +++ b/lib/arch/CMultibyteEmu.cpp @@ -0,0 +1,114 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArch.h" +#include +#if HAVE_WCHAR_H +# include +#elif __APPLE__ + // wtf? Darwin puts mbtowc() et al. in stdlib +# include +#else + // platform apparently has no wchar_t support. provide dummy + // implementations. hopefully at least the C++ compiler has + // a built-in wchar_t type. +# undef HAVE_MBSINIT + +static inline +int +mbtowc(wchar_t* dst, const char* src, int n) +{ + *dst = static_cast(*src); + return 1; +} + +static inline +int +wctomb(char* dst, wchar_t src) +{ + *dst = static_cast(src); + return 1; +} + +#endif + +class CArchMBStateImpl { +public: +#if HAVE_MBSINIT + mbstate_t m_mbstate; +#else + int m_mbstate; // dummy data +#endif +}; + +// +// use C library non-reentrant multibyte conversion with mutex +// + +static CArchMutex s_mutex; + +static +void +initMB() +{ + s_mutex = ARCH->newMutex(); +} + +static +void +cleanMB() +{ + ARCH->closeMutex(s_mutex); +} + +void +ARCH_STRING::initMBState(CArchMBState state) +{ + memset(&state->m_mbstate, 0, sizeof(state->m_mbstate)); +} + +bool +ARCH_STRING::isInitMBState(CArchMBState state) +{ + // if we're using this file mbsinit() probably doesn't exist + // but use it if it does +#if HAVE_MBSINIT + return (mbsinit(&state->m_mbstate) != 0); +#else + return true; +#endif +} + +int +ARCH_STRING::convMBToWC(wchar_t* dst, const char* src, int n, CArchMBState) +{ + wchar_t dummy; + ARCH->lockMutex(s_mutex); + int result = mbtowc(dst != NULL ? dst : &dummy, src, n); + ARCH->unlockMutex(s_mutex); + if (result < 0) + return -1; + else + return result; +} + +int +ARCH_STRING::convWCToMB(char* dst, wchar_t src, CArchMBState) +{ + char dummy[MB_LEN_MAX]; + ARCH->lockMutex(s_mutex); + int n = wctomb(dst != NULL ? dst : dummy, src); + ARCH->unlockMutex(s_mutex); + return n; +} diff --git a/lib/arch/CMultibyteOS.cpp b/lib/arch/CMultibyteOS.cpp new file mode 100644 index 0000000..49a8522 --- /dev/null +++ b/lib/arch/CMultibyteOS.cpp @@ -0,0 +1,68 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +class CArchMBStateImpl { +public: + mbstate_t m_mbstate; +}; + +// +// use C library reentrant multibyte conversion +// + +static +void +initMB() +{ + // do nothing +} + +static +void +cleanMB() +{ + // do nothing +} + +void +ARCH_STRING::initMBState(CArchMBState state) +{ + memset(&state->m_mbstate, 0, sizeof(state->m_mbstate)); +} + +bool +ARCH_STRING::isInitMBState(CArchMBState state) +{ + return (mbsinit(&state->m_mbstate) != 0); +} + +int +ARCH_STRING::convMBToWC(wchar_t* dst, const char* src, + int n, CArchMBState state) +{ + wchar_t dummy; + return static_cast(mbrtowc(dst != NULL ? dst : &dummy, + src, static_cast(n), &state->m_mbstate)); +} + +int +ARCH_STRING::convWCToMB(char* dst, wchar_t src, CArchMBState state) +{ + char dummy[MB_LEN_MAX]; + return static_cast(wcrtomb(dst != NULL ? dst : dummy, + src, &state->m_mbstate)); +} diff --git a/lib/arch/IArchConsole.h b/lib/arch/IArchConsole.h new file mode 100644 index 0000000..c9da410 --- /dev/null +++ b/lib/arch/IArchConsole.h @@ -0,0 +1,62 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHCONSOLE_H +#define IARCHCONSOLE_H + +#include "IInterface.h" + +//! Interface for architecture dependent console output +/*! +This interface defines the console operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchConsole : public IInterface { +public: + //! @name manipulators + //@{ + + //! Open the console + /*! + Opens the console for writing. The console is opened automatically + on the first write so calling this method is optional. Uses \c title + for the console's title if appropriate for the architecture. Calling + this method on an already open console must have no effect. + */ + virtual void openConsole(const char* title) = 0; + + //! Close the console + /*! + Close the console. Calling this method on an already closed console + must have no effect. + */ + virtual void closeConsole() = 0; + + //! Write to the console + /*! + Writes the given string to the console, opening it if necessary. + */ + virtual void writeConsole(const char*) = 0; + + //! Returns the newline sequence for the console + /*! + Different consoles use different character sequences for newlines. + This method returns the appropriate newline sequence for the console. + */ + virtual const char* getNewlineForConsole() = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchDaemon.h b/lib/arch/IArchDaemon.h new file mode 100644 index 0000000..ce04eb4 --- /dev/null +++ b/lib/arch/IArchDaemon.h @@ -0,0 +1,104 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHDAEMON_H +#define IARCHDAEMON_H + +#include "IInterface.h" + +//! Interface for architecture dependent daemonizing +/*! +This interface defines the operations required by synergy for installing +uninstalling daeamons and daemonizing a process. Each architecture must +implement this interface. +*/ +class IArchDaemon : public IInterface { +public: + typedef int (*DaemonFunc)(int argc, const char** argv); + + //! @name manipulators + //@{ + + //! Install daemon + /*! + Install a daemon. \c name is the name of the daemon passed to the + system and \c description is a short human readable description of + the daemon. \c pathname is the path to the daemon executable. + \c commandLine should \b not include the name of program as the + first argument. If \c allUsers is true then the daemon will be + installed to start at boot time, otherwise it will be installed to + start when the current user logs in. Throws an \c XArchDaemon + exception on failure. + */ + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) = 0; + + //! Uninstall daemon + /*! + Uninstall a daemon. Throws an \c XArchDaemon on failure. + */ + virtual void uninstallDaemon(const char* name, bool allUsers) = 0; + + //! Daemonize the process + /*! + Daemonize. Throw XArchDaemonFailed on error. \c name is the name + of the daemon. Once daemonized, \c func is invoked and daemonize + returns when and what it does. + + Exactly what happens when daemonizing depends on the platform. +
    +
  • unix: + Detaches from terminal. \c func gets passed one argument, the + name passed to daemonize(). +
  • win32: + Becomes a service. Argument 0 is the name of the service + and the rest are the arguments passed to StartService(). + \c func is only called when the service is actually started. + \c func must call \c CArchMiscWindows::runDaemon() to finally + becoming a service. The \c runFunc function passed to \c runDaemon() + must call \c CArchMiscWindows::daemonRunning(true) when it + enters the main loop (i.e. after initialization) and + \c CArchMiscWindows::daemonRunning(false) when it leaves + the main loop. The \c stopFunc function passed to \c runDaemon() + is called when the daemon must exit the main loop and it must cause + \c runFunc to return. \c func should return what \c runDaemon() + returns. \c func or \c runFunc can call + \c CArchMiscWindows::daemonFailed() to indicate startup failure. +
+ */ + virtual int daemonize(const char* name, DaemonFunc func) = 0; + + //! Check if user has permission to install the daemon + /*! + Returns true iff the caller has permission to install or + uninstall the daemon. Note that even if this method returns + true it's possible that installing/uninstalling the service + may still fail. This method ignores whether or not the + service is already installed. + */ + virtual bool canInstallDaemon(const char* name, bool allUsers) = 0; + + //! Check if the daemon is installed + /*! + Returns true iff the daemon is installed. + */ + virtual bool isDaemonInstalled(const char* name, bool allUsers) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchFile.h b/lib/arch/IArchFile.h new file mode 100644 index 0000000..8594053 --- /dev/null +++ b/lib/arch/IArchFile.h @@ -0,0 +1,64 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHFILE_H +#define IARCHFILE_H + +#include "IInterface.h" +#include "stdstring.h" + +//! Interface for architecture dependent file system operations +/*! +This interface defines the file system operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchFile : public IInterface { +public: + //! @name manipulators + //@{ + + //! Extract base name + /*! + Find the base name in the given \c pathname. + */ + virtual const char* getBasename(const char* pathname) = 0; + + //! Get user's home directory + /*! + Returns the user's home directory. Returns the empty string if + this cannot be determined. + */ + virtual std::string getUserDirectory() = 0; + + //! Get system directory + /*! + Returns the ussystem configuration file directory. + */ + virtual std::string getSystemDirectory() = 0; + + //! Concatenate path components + /*! + Concatenate pathname components with a directory separator + between them. This should not check if the resulting path + is longer than allowed by the system; we'll rely on the + system calls to tell us that. + */ + virtual std::string concatPath( + const std::string& prefix, + const std::string& suffix) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchLog.h b/lib/arch/IArchLog.h new file mode 100644 index 0000000..0c25918 --- /dev/null +++ b/lib/arch/IArchLog.h @@ -0,0 +1,64 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHLOG_H +#define IARCHLOG_H + +#include "IInterface.h" + +//! Interface for architecture dependent logging +/*! +This interface defines the logging operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchLog : public IInterface { +public: + //! Log levels + /*! + The logging priority levels in order of highest to lowest priority. + */ + enum ELevel { + kERROR, //!< For serious or fatal errors + kWARNING, //!< For minor errors and warnings + kNOTE, //!< For messages about notable events + kINFO, //!< For informational messages + kDEBUG //!< For debugging messages + }; + + //! @name manipulators + //@{ + + //! Open the log + /*! + Opens the log for writing. The log must be opened before being + written to. + */ + virtual void openLog(const char* name) = 0; + + //! Close the log + /*! + Close the log. + */ + virtual void closeLog() = 0; + + //! Write to the log + /*! + Writes the given string to the log with the given level. + */ + virtual void writeLog(ELevel, const char*) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h new file mode 100644 index 0000000..ee37677 --- /dev/null +++ b/lib/arch/IArchMultithread.h @@ -0,0 +1,265 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHMULTITHREAD_H +#define IARCHMULTITHREAD_H + +#include "IInterface.h" + +/*! +\class CArchCondImpl +\brief Internal condition variable data. +An architecture dependent type holding the necessary data for a +condition variable. +*/ +class CArchCondImpl; + +/*! +\var CArchCond +\brief Opaque condition variable type. +An opaque type representing a condition variable. +*/ +typedef CArchCondImpl* CArchCond; + +/*! +\class CArchMutexImpl +\brief Internal mutex data. +An architecture dependent type holding the necessary data for a mutex. +*/ +class CArchMutexImpl; + +/*! +\var CArchMutex +\brief Opaque mutex type. +An opaque type representing a mutex. +*/ +typedef CArchMutexImpl* CArchMutex; + +/*! +\class CArchThreadImpl +\brief Internal thread data. +An architecture dependent type holding the necessary data for a thread. +*/ +class CArchThreadImpl; + +/*! +\var CArchThread +\brief Opaque thread type. +An opaque type representing a thread. +*/ +typedef CArchThreadImpl* CArchThread; + +//! Interface for architecture dependent multithreading +/*! +This interface defines the multithreading operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchMultithread : public IInterface { +public: + //! Result of waitForEvent() + enum EWaitResult { + kEvent, //!< An event is pending + kExit, //!< Thread exited + kTimeout //!< Wait timed out + }; + + //! Type of thread entry point + typedef void* (*ThreadFunc)(void*); + //! Type of thread identifier + typedef unsigned int ThreadID; + + //! @name manipulators + //@{ + + // + // condition variable methods + // + + //! Create a condition variable + /*! + The condition variable is an opaque data type. + */ + virtual CArchCond newCondVar() = 0; + + //! Destroy a condition variable + virtual void closeCondVar(CArchCond) = 0; + + //! Signal a condition variable + /*! + Signalling a condition variable releases one waiting thread. + */ + virtual void signalCondVar(CArchCond) = 0; + + //! Broadcast a condition variable + /*! + Broadcasting a condition variable releases all waiting threads. + */ + virtual void broadcastCondVar(CArchCond) = 0; + + //! Wait on a condition variable + /*! + Wait on a conditation variable for up to \c timeout seconds. + If \c timeout is < 0 then there is no timeout. The mutex must + be locked when this method is called. The mutex is unlocked + during the wait and locked again before returning. Returns + true if the condition variable was signalled and false on + timeout. + + (Cancellation point) + */ + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout) = 0; + + // + // mutex methods + // + + //! Create a non-recursive mutex + /*! + Creates a non-recursive mutex. A thread must not lock a + non-recursive mutex when it already holds a lock on that mutex. + If it does it will deadlock. The mutex is an opaque data type. + */ + virtual CArchMutex newMutex() = 0; + + //! Destroy a mutex + virtual void closeMutex(CArchMutex) = 0; + + //! Lock a mutex + /*! + (Cancellation point) + */ + virtual void lockMutex(CArchMutex) = 0; + + //! Unlock a mutex + virtual void unlockMutex(CArchMutex) = 0; + + // + // thread methods + // + + //! Start a new thread + /*! + Creates and starts a new thread, using \c func as the entry point + and passing it \c userData. The thread is an opaque data type. + */ + virtual CArchThread newThread(ThreadFunc func, void* userData) = 0; + + //! Get a reference to the calling thread + /*! + Returns a thread representing the current (i.e. calling) thread. + */ + virtual CArchThread newCurrentThread() = 0; + + //! Copy a thread object + /*! + Returns a reference to to thread referred to by \c thread. + */ + virtual CArchThread copyThread(CArchThread thread) = 0; + + //! Release a thread reference + /*! + Deletes the given thread object. This does not destroy the thread + the object referred to, even if there are no remaining references. + Use cancelThread() and waitThread() to stop a thread and wait for + it to exit. + */ + virtual void closeThread(CArchThread) = 0; + + //! Force a thread to exit + /*! + Causes \c thread to exit when it next calls a cancellation point. + A thread avoids cancellation as long as it nevers calls a + cancellation point. Once it begins the cancellation process it + must always let cancellation go to completion but may take as + long as necessary to clean up. + */ + virtual void cancelThread(CArchThread thread) = 0; + + //! Change thread priority + /*! + Changes the priority of \c thread by \c n. If \c n is positive + the thread has a lower priority and if negative a higher priority. + Some architectures may not support either or both directions. + */ + virtual void setPriorityOfThread(CArchThread, int n) = 0; + + //! Cancellation point + /*! + This method does nothing but is a cancellation point. Clients + can make their own functions cancellation points by calling this + method at appropriate times. + */ + virtual void testCancelThread() = 0; + + //! Wait for a thread to exit + /*! + Waits for up to \c timeout seconds for \c thread to exit (normally + or by cancellation). Waits forever if \c timeout < 0. Returns + true if the thread exited, false otherwise. Waiting on the current + thread returns immediately with false. + + (Cancellation point) + */ + virtual bool wait(CArchThread thread, double timeout) = 0; + + //! Wait for a user event + /*! + Waits for up to \c timeout seconds for a pending user event or + \c thread to exit (normally or by cancellation). Waits forever + if \c timeout < 0. Returns kEvent if an event occurred, kExit + if \c thread exited, or kTimeout if the timeout expired. If + \c thread is NULL then it doesn't wait for any thread to exit + and it will not return kExit. + + This method is not required by all platforms. + + (Cancellation point) + */ + virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0; + + //! Compare threads + /*! + Returns true iff two thread objects refer to the same thread. + Note that comparing thread objects directly is meaningless. + */ + virtual bool isSameThread(CArchThread, CArchThread) = 0; + + //! Test if thread exited + /*! + Returns true iff \c thread has exited. + */ + virtual bool isExitedThread(CArchThread thread) = 0; + + //! Returns the exit code of a thread + /*! + Waits indefinitely for \c thread to exit (if it hasn't yet) then + returns the thread's exit code. + + (Cancellation point) + */ + virtual void* getResultOfThread(CArchThread thread) = 0; + + //! Returns an ID for a thread + /*! + Returns some ID number for \c thread. This is for logging purposes. + All thread objects referring to the same thread return the same ID. + However, clients should us isSameThread() to compare thread objects + instead of comparing IDs. + */ + virtual ThreadID getIDOfThread(CArchThread thread) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h new file mode 100644 index 0000000..1af8cd7 --- /dev/null +++ b/lib/arch/IArchNetwork.h @@ -0,0 +1,278 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHNETWORK_H +#define IARCHNETWORK_H + +#include "IInterface.h" +#include "stdstring.h" + +/*! +\class CArchSocketImpl +\brief Internal socket data. +An architecture dependent type holding the necessary data for a socket. +*/ +class CArchSocketImpl; + +/*! +\var CArchSocket +\brief Opaque socket type. +An opaque type representing a socket. +*/ +typedef CArchSocketImpl* CArchSocket; + +/*! +\class CArchNetAddressImpl +\brief Internal network address data. +An architecture dependent type holding the necessary data for a network +address. +*/ +class CArchNetAddressImpl; + +/*! +\var CArchNetAddress +\brief Opaque network address type. +An opaque type representing a network address. +*/ +typedef CArchNetAddressImpl* CArchNetAddress; + +//! Interface for architecture dependent networking +/*! +This interface defines the networking operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchNetwork : public IInterface { +public: + //! Supported address families + enum EAddressFamily { + kUNKNOWN, + kINET, + }; + + //! Supported socket types + enum ESocketType { + kDGRAM, + kSTREAM + }; + + //! Events for \c poll() + /*! + Events for \c poll() are bitmasks and can be combined using the + bitwise operators. + */ + enum { + kPOLLIN = 1, //!< Socket is readable + kPOLLOUT = 2, //!< Socket is writable + kPOLLERR = 4, //!< The socket is in an error state + kPOLLNVAL = 8 //!< The socket is invalid + }; + + //! A socket query for \c poll() + class CPollEntry { + public: + //! The socket to query + CArchSocket m_socket; + + //! The events to query for + /*! + The events to query for can be any combination of kPOLLIN and + kPOLLOUT. + */ + unsigned short m_events; + + //! The result events + unsigned short m_revents; + }; + + //! @name manipulators + //@{ + + //! Create a new socket + /*! + The socket is an opaque data type. + */ + virtual CArchSocket newSocket(EAddressFamily, ESocketType) = 0; + + //! Copy a socket object + /*! + Returns a reference to to socket referred to by \c s. + */ + virtual CArchSocket copySocket(CArchSocket s) = 0; + + //! Release a socket reference + /*! + Deletes the given socket object. This does not destroy the socket + the object referred to until there are no remaining references for + the socket. + */ + virtual void closeSocket(CArchSocket s) = 0; + + //! Close socket for further reads + /*! + Calling this disallows future reads on socket \c s. + */ + virtual void closeSocketForRead(CArchSocket s) = 0; + + //! Close socket for further writes + /*! + Calling this disallows future writes on socket \c s. + */ + virtual void closeSocketForWrite(CArchSocket s) = 0; + + //! Bind socket to address + /*! + Binds socket \c s to the address \c addr. + */ + virtual void bindSocket(CArchSocket s, CArchNetAddress addr) = 0; + + //! Listen for connections on socket + /*! + Causes the socket \c s to begin listening for incoming connections. + */ + virtual void listenOnSocket(CArchSocket s) = 0; + + //! Accept connection on socket + /*! + Accepts a connection on socket \c s, returning a new socket for the + connection and filling in \c addr with the address of the remote + end. \c addr may be NULL if the remote address isn't required. + The original socket \c s is unaffected and remains in the listening + state. The new socket shares most of the properties of \c s except + it's not in the listening state, it's connected, and is not + non-blocking even is \c s is. + + This call blocks if \c s is not non-blocking and there are no + pending connection requests. + + (Cancellation point) + */ + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr) = 0; + + //! Connect socket + /*! + Connects the socket \c s to the remote address \c addr. This call + blocks if \c s is not non-blocking. If \c s is non-blocking then + the client can \c poll() for writability to detect a connection. + + (Cancellation point) + */ + virtual void connectSocket(CArchSocket s, CArchNetAddress addr) = 0; + + //! Check socket state + /*! + Tests the state of \c num sockets for readability and/or writability. + Waits up to \c timeout seconds for some socket to become readable + and/or writable (or indefinitely if \c timeout < 0). Returns the + number of sockets that were readable (if readability was being + queried) or writable (if writablility was being queried) and sets + the \c m_revents members of the entries. \c kPOLLERR and \c kPOLLNVAL + are set in \c m_revents as appropriate. If a socket indicates + \c kPOLLERR then \c throwErrorOnSocket() can be used to determine + the type of error. + + (Cancellation point) + */ + virtual int pollSocket(CPollEntry[], int num, double timeout) = 0; + + //! Read data from socket + /*! + Read up to \c len bytes from socket \c s in \c buf and return the + number of bytes read. The number of bytes can be less than \c len + if not enough data is available. Returns 0 if the remote end has + disconnected and there is no more queued received data. Blocks if + the socket is not non-blocking and there is no queued received data. + If non-blocking and there is no queued received data then throws + XArchNetworkWouldBlock. + + (Cancellation point) + */ + virtual size_t readSocket(CArchSocket s, void* buf, size_t len) = 0; + + //! Write data from socket + /*! + Write up to \c len bytes to socket \c s from \c buf and return the + number of bytes written. The number of bytes can be less than + \c len if the remote end disconnected or the socket is non-blocking + and the internal buffers are full. If non-blocking and the internal + buffers are full before any data is written then throws + XArchNetworkWouldBlock. + + (Cancellation point) + */ + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len) = 0; + + //! Check error on socket + /*! + If the socket \c s is in an error state then throws an appropriate + XArchNetwork exception. + */ + virtual void throwErrorOnSocket(CArchSocket s) = 0; + + //! Set socket to (non-)blocking operation + /*! + Set socket to block or not block on accept, connect, poll, read and + write (i.e. calls that may take an arbitrary amount of time). + Returns the previous state. + */ + virtual bool setBlockingOnSocket(CArchSocket, bool blocking) = 0; + + //! Turn Nagle algorithm on or off on socket + /*! + Set socket to send messages immediately (true) or to collect small + messages into one packet (false). Returns the previous state. + */ + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay) = 0; + + //! Return local host's name + virtual std::string getHostName() = 0; + + //! Create an "any" network address + virtual CArchNetAddress newAnyAddr(EAddressFamily) = 0; + + //! Copy a network address + virtual CArchNetAddress copyAddr(CArchNetAddress) = 0; + + //! Convert a name to a network address + virtual CArchNetAddress nameToAddr(const std::string&) = 0; + + //! Destroy a network address + virtual void closeAddr(CArchNetAddress) = 0; + + //! Convert an address to a host name + virtual std::string addrToName(CArchNetAddress) = 0; + + //! Convert an address to a string + virtual std::string addrToString(CArchNetAddress) = 0; + + //! Get an address's family + virtual EAddressFamily getAddrFamily(CArchNetAddress) = 0; + + //! Set the port of an address + virtual void setAddrPort(CArchNetAddress, int port) = 0; + + //! Get the port of an address + virtual int getAddrPort(CArchNetAddress) = 0; + + //! Test for the "any" address + /*! + Returns true if \c addr is the "any" address. \c newAnyAddr() + returns an "any" address. + */ + virtual bool isAnyAddr(CArchNetAddress addr) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchSleep.h b/lib/arch/IArchSleep.h new file mode 100644 index 0000000..95a2852 --- /dev/null +++ b/lib/arch/IArchSleep.h @@ -0,0 +1,43 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHSLEEP_H +#define IARCHSLEEP_H + +#include "IInterface.h" + +//! Interface for architecture dependent sleeping +/*! +This interface defines the sleep operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchSleep : public IInterface { +public: + //! @name manipulators + //@{ + + //! Sleep + /*! + Blocks the calling thread for \c timeout seconds. If + \c timeout < 0.0 then the call returns immediately. If \c timeout + == 0.0 then the calling thread yields the CPU. + + (cancellation point) + */ + virtual void sleep(double timeout) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchString.h b/lib/arch/IArchString.h new file mode 100644 index 0000000..2d9a507 --- /dev/null +++ b/lib/arch/IArchString.h @@ -0,0 +1,93 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHSTRING_H +#define IARCHSTRING_H + +#include "IInterface.h" +#include + +/*! +\class CArchMBStateImpl +\brief Internal multibyte conversion data. +An architecture dependent type holding the necessary data for a +multibyte to/from wide character conversion. +*/ +class CArchMBStateImpl; + +/*! +\var CArchMBState +\brief Opaque multibyte conversion state type. +An opaque type representing multibyte conversion state. +*/ +typedef CArchMBStateImpl* CArchMBState; + +//! Interface for architecture dependent string operations +/*! +This interface defines the string operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchString : public IInterface { +public: + //! Wide character encodings + /*! + The known wide character encodings + */ + enum EWideCharEncoding { + kUCS2, //!< The UCS-2 encoding + kUCS4, //!< The UCS-4 encoding + kUTF16, //!< The UTF-16 encoding + kUTF32 //!< The UTF-32 encoding + }; + + //! @name manipulators + //@{ + + //! printf() to limited size buffer with va_list + /*! + This method is equivalent to vsprintf() except it will not write + more than \c n bytes to the buffer, returning -1 if the output + was truncated and the number of bytes written not including the + trailing NUL otherwise. + */ + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap) = 0; + + //! Create a new multibyte conversion state + virtual CArchMBState newMBState() = 0; + + //! Destroy a multibyte conversion state + virtual void closeMBState(CArchMBState) = 0; + + //! Initialize a multibyte conversion state + virtual void initMBState(CArchMBState) = 0; + + //! Test a multibyte conversion state + virtual bool isInitMBState(CArchMBState) = 0; + + //! Convert multibyte to wide character + virtual int convMBToWC(wchar_t*, + const char*, int, CArchMBState) = 0; + + //! Convert wide character to multibyte + virtual int convWCToMB(char*, wchar_t, CArchMBState) = 0; + + //! Return the architecture's native wide character encoding + virtual EWideCharEncoding + getWideCharEncoding() = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchTaskBar.h b/lib/arch/IArchTaskBar.h new file mode 100644 index 0000000..2cd20de --- /dev/null +++ b/lib/arch/IArchTaskBar.h @@ -0,0 +1,63 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHTASKBAR_H +#define IARCHTASKBAR_H + +#include "IInterface.h" + +class IArchTaskBarReceiver; + +//! Interface for architecture dependent task bar control +/*! +This interface defines the task bar icon operations required +by synergy. Each architecture must implement this interface +though each operation can be a no-op. +*/ +class IArchTaskBar : public IInterface { +public: + // Event data is architecture dependent + typedef void* Event; + + //! @name manipulators + //@{ + + //! Add a receiver + /*! + Add a receiver object to be notified of user and application + events. This should be called before other methods. When + the receiver is added to the task bar, its icon appears on + the task bar. + */ + virtual void addReceiver(IArchTaskBarReceiver*) = 0; + + //! Remove a receiver + /*! + Remove a receiver object from the task bar. This removes the + icon from the task bar. + */ + virtual void removeReceiver(IArchTaskBarReceiver*) = 0; + + //! Update a receiver + /*! + Updates the display of the receiver on the task bar. This + should be called when the receiver appearance may have changed + (e.g. it's icon or tool tip has changed). + */ + virtual void updateReceiver(IArchTaskBarReceiver*) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchTaskBarReceiver.h b/lib/arch/IArchTaskBarReceiver.h new file mode 100644 index 0000000..917f2fb --- /dev/null +++ b/lib/arch/IArchTaskBarReceiver.h @@ -0,0 +1,90 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHTASKBARRECEIVER_H +#define IARCHTASKBARRECEIVER_H + +#include "IInterface.h" +#include "stdstring.h" + +//! Interface for architecture dependent task bar event handling +/*! +This interface defines the task bar icon event handlers required +by synergy. Each architecture must implement this interface +though each operation can be a no-op. +*/ +class IArchTaskBarReceiver : public IInterface { +public: + // Icon data is architecture dependent + typedef void* Icon; + + //! @name manipulators + //@{ + + //! Show status window + /*! + Open a window displaying current status. This should return + immediately without waiting for the window to be closed. + */ + virtual void showStatus() = 0; + + //! Popup menu + /*! + Popup a menu of operations at or around \c x,y and perform the + chosen operation. + */ + virtual void runMenu(int x, int y) = 0; + + //! Perform primary action + /*! + Perform the primary (default) action. + */ + virtual void primaryAction() = 0; + + //@} + //! @name accessors + //@{ + + //! Lock receiver + /*! + Locks the receiver from changing state. The receiver should be + locked when querying it's state to ensure consistent results. + Each call to \c lock() must have a matching \c unlock() and + locks cannot be nested. + */ + virtual void lock() const = 0; + + //! Unlock receiver + virtual void unlock() const = 0; + + //! Get icon + /*! + Returns the icon to display in the task bar. The interface + to set the icon is left to subclasses. Getting and setting + the icon must be thread safe. + */ + virtual const Icon getIcon() const = 0; + + //! Get tooltip + /*! + Returns the tool tip to display in the task bar. The interface + to set the tooltip is left to sublclasses. Getting and setting + the icon must be thread safe. + */ + virtual std::string getToolTip() const = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchTime.h b/lib/arch/IArchTime.h new file mode 100644 index 0000000..dade64b --- /dev/null +++ b/lib/arch/IArchTime.h @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHTIME_H +#define IARCHTIME_H + +#include "IInterface.h" + +//! Interface for architecture dependent time operations +/*! +This interface defines the time operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchTime : public IInterface { +public: + //! @name manipulators + //@{ + + //! Get the current time + /*! + Returns the number of seconds since some arbitrary starting time. + This should return as high a precision as reasonable. + */ + virtual double time() = 0; + + //@} +}; + +#endif diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am new file mode 100644 index 0000000..2b32473 --- /dev/null +++ b/lib/arch/Makefile.am @@ -0,0 +1,101 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + arch.dsp \ + CArchConsoleWindows.cpp \ + CArchDaemonWindows.cpp \ + CArchFileWindows.cpp \ + CArchLogWindows.cpp \ + CArchMiscWindows.cpp \ + CArchMultithreadWindows.cpp \ + CArchNetworkWinsock.cpp \ + CArchSleepWindows.cpp \ + CArchStringWindows.cpp \ + CArchTaskBarWindows.cpp \ + CArchTimeWindows.cpp \ + XArchWindows.cpp \ + CArchConsoleWindows.h \ + CArchDaemonWindows.h \ + CArchFileWindows.h \ + CArchLogWindows.h \ + CArchMiscWindows.h \ + CArchMultithreadWindows.h \ + CArchNetworkWinsock.h \ + CArchSleepWindows.h \ + CArchStringWindows.h \ + CArchTaskBarWindows.h \ + CArchTimeWindows.h \ + XArchWindows.h \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libarch.a +libarch_a_SOURCES = \ + CArch.cpp \ + CArchImpl.cpp \ + XArch.cpp \ + CArch.h \ + IArchConsole.h \ + IArchDaemon.h \ + IArchFile.h \ + IArchLog.h \ + IArchMultithread.h \ + IArchNetwork.h \ + IArchSleep.h \ + IArchString.h \ + IArchTaskBar.h \ + IArchTaskBarReceiver.h \ + IArchTime.h \ + XArch.h \ + $(NULL) +EXTRA_libarch_a_SOURCES = \ + CArchConsoleUnix.cpp \ + CArchDaemonNone.cpp \ + CArchDaemonUnix.cpp \ + CArchFileUnix.cpp \ + CArchLogUnix.cpp \ + CArchMultithreadPosix.cpp \ + CArchNetworkBSD.cpp \ + CArchSleepUnix.cpp \ + CArchStringUnix.cpp \ + CArchTaskBarXWindows.cpp \ + CArchTimeUnix.cpp \ + CMultibyte.cpp \ + CMultibyteOS.cpp \ + CMultibyteEmu.cpp \ + XArchUnix.cpp \ + vsnprintf.cpp \ + CArchConsoleUnix.h \ + CArchDaemonNone.h \ + CArchDaemonUnix.h \ + CArchFileUnix.h \ + CArchLogUnix.h \ + CArchMultithreadPosix.h \ + CArchNetworkBSD.h \ + CArchSleepUnix.h \ + CArchStringUnix.h \ + CArchTaskBarXWindows.h \ + CArchTimeUnix.h \ + XArchUnix.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + $(NULL) diff --git a/lib/arch/Makefile.in b/lib/arch/Makefile.in new file mode 100644 index 0000000..621b7cd --- /dev/null +++ b/lib/arch/Makefile.in @@ -0,0 +1,433 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + arch.dsp \ + CArchConsoleWindows.cpp \ + CArchDaemonWindows.cpp \ + CArchFileWindows.cpp \ + CArchLogWindows.cpp \ + CArchMiscWindows.cpp \ + CArchMultithreadWindows.cpp \ + CArchNetworkWinsock.cpp \ + CArchSleepWindows.cpp \ + CArchStringWindows.cpp \ + CArchTaskBarWindows.cpp \ + CArchTimeWindows.cpp \ + XArchWindows.cpp \ + CArchConsoleWindows.h \ + CArchDaemonWindows.h \ + CArchFileWindows.h \ + CArchLogWindows.h \ + CArchMiscWindows.h \ + CArchMultithreadWindows.h \ + CArchNetworkWinsock.h \ + CArchSleepWindows.h \ + CArchStringWindows.h \ + CArchTaskBarWindows.h \ + CArchTimeWindows.h \ + XArchWindows.h \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libarch.a +libarch_a_SOURCES = \ + CArch.cpp \ + CArchImpl.cpp \ + XArch.cpp \ + CArch.h \ + IArchConsole.h \ + IArchDaemon.h \ + IArchFile.h \ + IArchLog.h \ + IArchMultithread.h \ + IArchNetwork.h \ + IArchSleep.h \ + IArchString.h \ + IArchTaskBar.h \ + IArchTaskBarReceiver.h \ + IArchTime.h \ + XArch.h \ + $(NULL) + +EXTRA_libarch_a_SOURCES = \ + CArchConsoleUnix.cpp \ + CArchDaemonNone.cpp \ + CArchDaemonUnix.cpp \ + CArchFileUnix.cpp \ + CArchLogUnix.cpp \ + CArchMultithreadPosix.cpp \ + CArchNetworkBSD.cpp \ + CArchSleepUnix.cpp \ + CArchStringUnix.cpp \ + CArchTaskBarXWindows.cpp \ + CArchTimeUnix.cpp \ + CMultibyte.cpp \ + CMultibyteOS.cpp \ + CMultibyteEmu.cpp \ + XArchUnix.cpp \ + vsnprintf.cpp \ + CArchConsoleUnix.h \ + CArchDaemonNone.h \ + CArchDaemonUnix.h \ + CArchFileUnix.h \ + CArchLogUnix.h \ + CArchMultithreadPosix.h \ + CArchNetworkBSD.h \ + CArchSleepUnix.h \ + CArchStringUnix.h \ + CArchTaskBarXWindows.h \ + CArchTimeUnix.h \ + XArchUnix.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + $(NULL) + +subdir = lib/arch +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libarch_a_AR = $(AR) cru +libarch_a_LIBADD = +am_libarch_a_OBJECTS = CArch.$(OBJEXT) CArchImpl.$(OBJEXT) \ + XArch.$(OBJEXT) +libarch_a_OBJECTS = $(am_libarch_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CArch.Po $(DEPDIR)/CArchConsoleUnix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchDaemonNone.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchDaemonUnix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchFileUnix.Po $(DEPDIR)/CArchImpl.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchLogUnix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchMultithreadPosix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchNetworkBSD.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchSleepUnix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchStringUnix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchTaskBarXWindows.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchTimeUnix.Po $(DEPDIR)/CMultibyte.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CMultibyteEmu.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CMultibyteOS.Po $(DEPDIR)/XArch.Po \ +@AMDEP_TRUE@ $(DEPDIR)/XArchUnix.Po $(DEPDIR)/vsnprintf.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libarch_a_SOURCES) $(EXTRA_libarch_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libarch_a_SOURCES) $(EXTRA_libarch_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/arch/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libarch.a: $(libarch_a_OBJECTS) $(libarch_a_DEPENDENCIES) + -rm -f libarch.a + $(libarch_a_AR) libarch.a $(libarch_a_OBJECTS) $(libarch_a_LIBADD) + $(RANLIB) libarch.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchConsoleUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchDaemonNone.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchDaemonUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchFileUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchImpl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchLogUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchMultithreadPosix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchNetworkBSD.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchSleepUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchStringUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchTaskBarXWindows.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchTimeUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CMultibyte.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CMultibyteEmu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CMultibyteOS.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XArch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XArchUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/vsnprintf.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/arch/XArch.cpp b/lib/arch/XArch.cpp new file mode 100644 index 0000000..9dce528 --- /dev/null +++ b/lib/arch/XArch.cpp @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArch.h" + +// +// XArch +// + +std::string +XArch::what() const throw() +{ + try { + if (m_what.empty() && m_eval != NULL) { + m_what = m_eval->eval(); + } + } + catch (...) { + // ignore + } + return m_what; +} diff --git a/lib/arch/XArch.h b/lib/arch/XArch.h new file mode 100644 index 0000000..44c9394 --- /dev/null +++ b/lib/arch/XArch.h @@ -0,0 +1,166 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCH_H +#define XARCH_H + +#include "common.h" +#include "stdstring.h" + +//! Generic thread exception +/*! +Exceptions derived from this class are used by the multithreading +library to perform stack unwinding when a thread terminates. These +exceptions must always be rethrown by clients when caught. +*/ +class XThread { }; + +//! Thread exception to cancel +/*! +Thrown to cancel a thread. Clients must not throw this type, but +must rethrow it if caught (by XThreadCancel, XThread, or ...). +*/ +class XThreadCancel : public XThread { }; + +/*! +\def RETHROW_XTHREAD +Convenience macro to rethrow an XThread exception but ignore other +exceptions. Put this in your catch (...) handler after necessary +cleanup but before leaving or returning from the handler. +*/ +#define RETHROW_XTHREAD \ + try { throw; } catch (XThread&) { throw; } catch (...) { } + +//! Lazy error message string evaluation +/*! +This class encapsulates platform dependent error string lookup. +Platforms subclass this type, taking an appropriate error code +type in the c'tor and overriding eval() to return the error +string for that error code. +*/ +class XArchEval { +public: + XArchEval() { } + virtual ~XArchEval() { } + + virtual XArchEval* clone() const throw() = 0; + + virtual std::string eval() const throw() = 0; +}; + +//! Generic exception architecture dependent library +class XArch { +public: + XArch(XArchEval* adoptedEvaluator) : m_eval(adoptedEvaluator) { } + XArch(const std::string& msg) : m_eval(NULL), m_what(msg) { } + XArch(const XArch& e) : m_eval(e.m_eval->clone()), m_what(e.m_what) { } + ~XArch() { delete m_eval; } + + std::string what() const throw(); + +private: + XArchEval* m_eval; + mutable std::string m_what; +}; + +// Macro to declare XArch derived types +#define XARCH_SUBCLASS(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_(XArchEval* adoptedEvaluator) : super_(adoptedEvaluator) { } \ + name_(const std::string& msg) : super_(msg) { } \ +} + +//! Generic network exception +/*! +Exceptions derived from this class are used by the networking +library to indicate various errors. +*/ +XARCH_SUBCLASS(XArchNetwork, XArch); + +//! Network insufficient permission +XARCH_SUBCLASS(XArchNetworkWouldBlock, XArchNetwork); + +//! Network insufficient permission +XARCH_SUBCLASS(XArchNetworkAccess, XArchNetwork); + +//! Network insufficient resources +XARCH_SUBCLASS(XArchNetworkResource, XArchNetwork); + +//! No support for requested network resource/service +XARCH_SUBCLASS(XArchNetworkSupport, XArchNetwork); + +//! Network I/O error +XARCH_SUBCLASS(XArchNetworkIO, XArchNetwork); + +//! Network address is unavailable or not local +XARCH_SUBCLASS(XArchNetworkNoAddress, XArchNetwork); + +//! Network address in use +XARCH_SUBCLASS(XArchNetworkAddressInUse, XArchNetwork); + +//! No route to address +XARCH_SUBCLASS(XArchNetworkNoRoute, XArchNetwork); + +//! Socket not connected +XARCH_SUBCLASS(XArchNetworkNotConnected, XArchNetwork); + +//! Remote end of socket has disconnected +XARCH_SUBCLASS(XArchNetworkDisconnected, XArchNetwork); + +//! Remote end of socket refused connection +XARCH_SUBCLASS(XArchNetworkConnectionRefused, XArchNetwork); + +//! Connection is in progress +XARCH_SUBCLASS(XArchNetworkConnecting, XArchNetwork); + +//! Remote end of socket is not responding +XARCH_SUBCLASS(XArchNetworkTimedOut, XArchNetwork); + +//! Generic network name lookup erros +XARCH_SUBCLASS(XArchNetworkName, XArchNetwork); + +//! The named host is unknown +XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName); + +//! The named host is known but has to address +XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName); + +//! Non-recoverable name server error +XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName); + +//! Temporary name server error +XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName); + +//! Generic daemon exception +/*! +Exceptions derived from this class are used by the daemon +library to indicate various errors. +*/ +XARCH_SUBCLASS(XArchDaemon, XArch); + +//! Could not daemonize +XARCH_SUBCLASS(XArchDaemonFailed, XArchDaemon); + +//! Could not install daemon +XARCH_SUBCLASS(XArchDaemonInstallFailed, XArchDaemon); + +//! Could not uninstall daemon +XARCH_SUBCLASS(XArchDaemonUninstallFailed, XArchDaemon); + +//! Attempted to uninstall a daemon that was not installed +XARCH_SUBCLASS(XArchDaemonUninstallNotInstalled, XArchDaemonUninstallFailed); + + +#endif diff --git a/lib/arch/XArchUnix.cpp b/lib/arch/XArchUnix.cpp new file mode 100644 index 0000000..6f4047d --- /dev/null +++ b/lib/arch/XArchUnix.cpp @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArchUnix.h" +#include + +// +// XArchEvalUnix +// + +XArchEval* +XArchEvalUnix::clone() const throw() +{ + return new XArchEvalUnix(m_errno); +} + +std::string +XArchEvalUnix::eval() const throw() +{ + // FIXME -- not thread safe + return strerror(m_errno); +} diff --git a/lib/arch/XArchUnix.h b/lib/arch/XArchUnix.h new file mode 100644 index 0000000..19e0df4 --- /dev/null +++ b/lib/arch/XArchUnix.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCHUNIX_H +#define XARCHUNIX_H + +#include "XArch.h" + +//! Lazy error message string evaluation for unix +class XArchEvalUnix : public XArchEval { +public: + XArchEvalUnix(int err) : m_errno(err) { } + virtual ~XArchEvalUnix() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + int m_errno; +}; + +#endif diff --git a/lib/arch/XArchWindows.cpp b/lib/arch/XArchWindows.cpp new file mode 100644 index 0000000..a57bc71 --- /dev/null +++ b/lib/arch/XArchWindows.cpp @@ -0,0 +1,126 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArchWindows.h" + +// +// XArchEvalWindows +// + +XArchEval* +XArchEvalWindows::clone() const throw() +{ + return new XArchEvalWindows(m_errno); +} + +std::string +XArchEvalWindows::eval() const throw() +{ + char* cmsg; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + m_errno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&cmsg, + 0, + NULL) == 0) { + cmsg = NULL; + return "Unknown error"; + } + std::string smsg(cmsg); + LocalFree(cmsg); + return smsg; +} + + +// +// XArchEvalWinsock +// + +XArchEval* +XArchEvalWinsock::clone() const throw() +{ + return new XArchEvalWinsock(m_errno); +} + +std::string +XArchEvalWinsock::eval() const throw() +{ + // built-in windows function for looking up error message strings + // may not look up network error messages correctly. we'll have + // to do it ourself. + static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = { + /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"}, + /* 10009 */{WSAEBADF, "Bad file handle"}, + /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"}, + /* 10014 */{WSAEFAULT, "WSAEFAULT"}, + /* 10022 */{WSAEINVAL, "WSAEINVAL"}, + /* 10024 */{WSAEMFILE, "No more file descriptors available"}, + /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"}, + /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"}, + /* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"}, + /* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"}, + /* 10039 */{WSAEDESTADDRREQ, "A destination address is required"}, + /* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"}, + /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"}, + /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"}, + /* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"}, + /* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"}, + /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"}, + /* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"}, + /* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"}, + /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"}, + /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"}, + /* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"}, + /* 10051 */{WSAENETUNREACH, "The network can't be reached from this hos at this time"}, + /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"}, + /* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"}, + /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"}, + /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"}, + /* 10056 */{WSAEISCONN, "The socket is already connected"}, + /* 10057 */{WSAENOTCONN, "The socket is not connected"}, + /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"}, + /* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"}, + /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"}, + /* 10061 */{WSAECONNREFUSED, "The attempt to connect was forcefully rejected"}, + /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"}, + /* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"}, + /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"}, + /* 10065 */{WSAEHOSTUNREACH, "No route to host"}, + /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"}, + /* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"}, + /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"}, + /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"}, + /* 10070 */{WSAESTALE, "Undocumented WinSock error code"}, + /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"}, + /* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"}, + /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"}, + /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"}, + /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"}, + /* 11001 */{WSAHOST_NOT_FOUND, "The specified host is unknown"}, + /* 11002 */{WSATRY_AGAIN, "A temporary error occurred on an authoritative name server"}, + /* 11003 */{WSANO_RECOVERY, "A non-recoverable name server error occurred"}, + /* 11004 */{WSANO_DATA, "The requested name is valid but does not have an IP address"}, + /* end */{0, NULL} + }; + + for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) { + if (s_netErrorCodes[i].m_code == m_errno) { + return s_netErrorCodes[i].m_msg; + } + } + return "Unknown error"; +} diff --git a/lib/arch/XArchWindows.h b/lib/arch/XArchWindows.h new file mode 100644 index 0000000..56c2400 --- /dev/null +++ b/lib/arch/XArchWindows.h @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCHWINDOWS_H +#define XARCHWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "XArch.h" +#include + +//! Lazy error message string evaluation for windows +class XArchEvalWindows : public XArchEval { +public: + XArchEvalWindows() : m_errno(GetLastError()) { } + XArchEvalWindows(DWORD err) : m_errno(err) { } + virtual ~XArchEvalWindows() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + DWORD m_errno; +}; + +//! Lazy error message string evaluation for winsock +class XArchEvalWinsock : public XArchEval { +public: + XArchEvalWinsock(int err) : m_errno(err) { } + virtual ~XArchEvalWinsock() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + int m_errno; +}; + +#endif diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp new file mode 100644 index 0000000..db750f4 --- /dev/null +++ b/lib/arch/arch.dsp @@ -0,0 +1,294 @@ +# Microsoft Developer Studio Project File - Name="arch" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=arch - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "arch.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "arch.mak" CFG="arch - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "arch - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "arch - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "arch - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "arch - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "arch - Win32 Release" +# Name "arch - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CArch.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchImpl.cpp +# End Source File +# Begin Source File + +SOURCE=.\XArch.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CArch.h +# End Source File +# Begin Source File + +SOURCE=.\CArchConsoleWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchDaemonWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchFileWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchImpl.h +# End Source File +# Begin Source File + +SOURCE=.\CArchLogWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchMiscWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchMultithreadWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchNetworkWinsock.h +# End Source File +# Begin Source File + +SOURCE=.\CArchSleepWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchStringWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchTaskBarWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchTimeWindows.h +# End Source File +# Begin Source File + +SOURCE=.\IArchConsole.h +# End Source File +# Begin Source File + +SOURCE=.\IArchDaemon.h +# End Source File +# Begin Source File + +SOURCE=.\IArchFile.h +# End Source File +# Begin Source File + +SOURCE=.\IArchLog.h +# End Source File +# Begin Source File + +SOURCE=.\IArchMultithread.h +# End Source File +# Begin Source File + +SOURCE=.\IArchNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\IArchSleep.h +# End Source File +# Begin Source File + +SOURCE=.\IArchString.h +# End Source File +# Begin Source File + +SOURCE=.\IArchTaskBar.h +# End Source File +# Begin Source File + +SOURCE=.\IArchTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\IArchTime.h +# End Source File +# Begin Source File + +SOURCE=.\XArch.h +# End Source File +# Begin Source File + +SOURCE=.\XArchWindows.h +# End Source File +# End Group +# Begin Group "Included Files" + +# PROP Default_Filter "inc" +# Begin Source File + +SOURCE=.\CArchConsoleWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchDaemonWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchFileWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchLogWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchMiscWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchMultithreadWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchNetworkWinsock.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchSleepWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchStringWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchTaskBarWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchTimeWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyte.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyteEmu.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyteOS.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\vsnprintf.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\XArchWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/arch/vsnprintf.cpp b/lib/arch/vsnprintf.cpp new file mode 100644 index 0000000..f06c0a3 --- /dev/null +++ b/lib/arch/vsnprintf.cpp @@ -0,0 +1,62 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if HAVE_VSNPRINTF + +#if !defined(ARCH_VSNPRINTF) +# define ARCH_VSNPRINTF vsnprintf +#endif + +int +ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap) +{ + int n = ::ARCH_VSNPRINTF(str, size, fmt, ap); + if (n > size) { + n = -1; + } + return n; +} + +#elif UNIX_LIKE // !HAVE_VSNPRINTF + +#include + +int +ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap) +{ + static FILE* bitbucket = fopen("/dev/null", "w"); + if (bitbucket == NULL) { + // uh oh + if (size > 0) { + str[0] = '\0'; + } + return 0; + } + else { + // count the characters using the bitbucket + int n = vfprintf(bitbucket, fmt, ap); + if (n + 1 <= size) { + // it'll fit so print it into str + vsprintf(str, fmt, ap); + } + return n; + } +} + +#else // !HAVE_VSNPRINTF && !UNIX_LIKE + +// FIXME +#error vsnprintf not implemented + +#endif // !HAVE_VSNPRINTF diff --git a/lib/base/CFunctionJob.cpp b/lib/base/CFunctionJob.cpp new file mode 100644 index 0000000..bc16c50 --- /dev/null +++ b/lib/base/CFunctionJob.cpp @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CFunctionJob.h" + +// +// CFunctionJob +// + +CFunctionJob::CFunctionJob(void (*func)(void*), void* arg) : + m_func(func), + m_arg(arg) +{ + // do nothing +} + +CFunctionJob::~CFunctionJob() +{ + // do nothing +} + +void +CFunctionJob::run() +{ + if (m_func != NULL) { + m_func(m_arg); + } +} diff --git a/lib/base/CFunctionJob.h b/lib/base/CFunctionJob.h new file mode 100644 index 0000000..e30bbfa --- /dev/null +++ b/lib/base/CFunctionJob.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CFUNCTIONJOB_H +#define CFUNCTIONJOB_H + +#include "IJob.h" + +//! Use a function as a job +/*! +A job class that invokes a function. +*/ +class CFunctionJob : public IJob { +public: + //! run() invokes \c func(arg) + CFunctionJob(void (*func)(void*), void* arg = NULL); + virtual ~CFunctionJob(); + + // IJob overrides + virtual void run(); + +private: + void (*m_func)(void*); + void* m_arg; +}; + +#endif diff --git a/lib/base/CJobList.cpp b/lib/base/CJobList.cpp new file mode 100644 index 0000000..3735889 --- /dev/null +++ b/lib/base/CJobList.cpp @@ -0,0 +1,113 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CJobList.h" +#include "IJob.h" +#include "CArch.h" + +// +// CJobList +// + +CJobList::CJobList() +{ + m_mutex = ARCH->newMutex(); +} + +CJobList::~CJobList() +{ + ARCH->closeMutex(m_mutex); +} + +void +CJobList::addJob(IJob* job) +{ + // ignore bogus job + if (job == NULL) { + return; + } + + // make a temporary list with the job. later we'll splice this + // list into our jobs list. since splice() never throws we + // don't have to try/catch to unlock the mutex. + CJobs tmpList; + tmpList.push_front(job); + + // add job to list + ARCH->lockMutex(m_mutex); + m_jobs.splice(m_jobs.begin(), tmpList); + ARCH->unlockMutex(m_mutex); +} + +void +CJobList::removeJob(IJob* job) +{ + if (job != NULL) { + ARCH->lockMutex(m_mutex); + m_jobs.remove(job); + ARCH->unlockMutex(m_mutex); + } +} + +void +CJobList::runJobs() const +{ + // this is a little tricky. to allow any number of threads to + // traverse the list while jobs are added and removed while + // not holding the mutex when running a job (so other threads + // or the running job can add and remove jobs), we insert a + // new element into the list. this element has a NULL job and + // is a "safe place" while we traverse the list. the safe place + // is inserted at the start of the list (with the mutex locked) + // then, for each job, we lock the mutex and move the safe place + // past the next job, unlock the mutex, run the job and repeat + // until there are no more jobs. the safe place will not be + // removed by any other thread and is after every job that has + // been run and before every job that still needs to run. when + // all the jobs have been run we remove the safe place. + + // add the safe place + CJobs tmpList; + tmpList.push_front(NULL); + ARCH->lockMutex(m_mutex); + m_jobs.splice(m_jobs.begin(), tmpList); + CJobs::iterator safePlace = m_jobs.begin(); + + // find the next non-NULL job (NULL jobs are safe places) + CJobs::iterator next = safePlace; + while (next != m_jobs.end() && *next == NULL) + ++next; + while (next != m_jobs.end()) { + // found a job. run it without holding a lock. note the + // race condition here: we release the lock, allowing + // removeJob() to remove this job before we run the job. + // therefore the caller cannot safely destroy the job + // until all runJobs() complete. + IJob* job = *next; + ++next; + m_jobs.splice(next, m_jobs, safePlace); + ARCH->unlockMutex(m_mutex); + job->run(); + + // next real job + ARCH->lockMutex(m_mutex); + next = safePlace; + while (next != m_jobs.end() && *next == NULL) + ++next; + } + + // remove the safe place + m_jobs.erase(safePlace); + ARCH->unlockMutex(m_mutex); +} diff --git a/lib/base/CJobList.h b/lib/base/CJobList.h new file mode 100644 index 0000000..780d19d --- /dev/null +++ b/lib/base/CJobList.h @@ -0,0 +1,72 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CJOBLIST_H +#define CJOBLIST_H + +#include "IArchMultithread.h" +#include "stdlist.h" + +class IJob; + +class CJobList { +public: + CJobList(); + ~CJobList(); + + //! @name manipulators + //@{ + + //! Add a job + /*! + Add a job to the list. The client keeps ownership of the job. + Jobs can be safely added while \c runJobs() is executing. + */ + void addJob(IJob*); + + //! Remove a job + /*! + Remove a job from the list. The client keeps ownership of the job. + Jobs can be safely removed while \c runJobs() is executing. + */ + void removeJob(IJob*); + + //@} + //! @name accessors + //@{ + + //! Run all jobs + /*! + Run all jobs in the list. Any number of threads can call + \c runJobs() at once. Jobs can be added and removed while + \c runJobs() is executing in the same or another thread. + Any job added after \c runJobs() starts will not be run + by that call to runJobs(). Destroying a removed job + while \c runJobs() is executing is not safe unless the + removed completed before \c runJobs() started. + */ + void runJobs() const; + + //@} + +private: + typedef std::list CJobs; + typedef CJobs::iterator iterator; + + CArchMutex m_mutex; + mutable CJobs m_jobs; +}; + +#endif + diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp new file mode 100644 index 0000000..4df3058 --- /dev/null +++ b/lib/base/CLog.cpp @@ -0,0 +1,313 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CLog.h" +#include "CString.h" +#include "CStringUtil.h" +#include "LogOutputters.h" +#include "CArch.h" +#include "Version.h" +#include +#include + +// names of priorities +static const char* g_priority[] = { + "FATAL", + "ERROR", + "WARNING", + "NOTE", + "INFO", + "DEBUG", + "DEBUG1", + "DEBUG2" + }; + +// number of priorities +static const int g_numPriority = (int)(sizeof(g_priority) / + sizeof(g_priority[0])); + +// the default priority +#if defined(NDEBUG) +static const int g_defaultMaxPriority = 4; +#else +static const int g_defaultMaxPriority = 5; +#endif + +// length of longest string in g_priority +static const int g_maxPriorityLength = 7; + +// length of suffix string (": ") +static const int g_prioritySuffixLength = 2; + +// amount of padded required to fill in the priority prefix +static const int g_priorityPad = g_maxPriorityLength + + g_prioritySuffixLength; + +//! Convenience object to lock/unlock a mutex +class CLogLock { +public: + CLogLock(CArchMutex mutex) : m_mutex(mutex) { ARCH->lockMutex(m_mutex); } + ~CLogLock() { ARCH->unlockMutex(m_mutex); } + +private: + CArchMutex m_mutex; +}; + + +// +// CLog +// + +CLog* CLog::s_log = NULL; + +CLog::CLog() +{ + assert(s_log == NULL); + + // create mutex for multithread safe operation + m_mutex = ARCH->newMutex(); + + // other initalization + m_maxPriority = g_defaultMaxPriority; + m_maxNewlineLength = 0; + insert(new CConsoleLogOutputter); +} + +CLog::~CLog() +{ + // clean up + for (COutputterList::iterator index = m_outputters.begin(); + index != m_outputters.end(); ++index) { + delete *index; + } + for (COutputterList::iterator index = m_alwaysOutputters.begin(); + index != m_alwaysOutputters.end(); ++index) { + delete *index; + } + ARCH->closeMutex(m_mutex); + s_log = NULL; +} + +CLog* +CLog::getInstance() +{ + // note -- not thread safe; client must initialize log safely + if (s_log == NULL) { + s_log = new CLog; + } + return s_log; +} + +void +CLog::print(const char* fmt, ...) const +{ + // check if fmt begins with a priority argument + int priority = 4; + if (fmt[0] == '%' && fmt[1] == 'z') { + priority = fmt[2] - '\060'; + fmt += 3; + } + + // done if below priority threshold + if (priority > getFilter()) { + return; + } + + // compute prefix padding length + int pad = g_priorityPad; + + // print to buffer + char stack[1024]; + va_list args; + va_start(args, fmt); + char* buffer = CStringUtil::vsprint(stack, + sizeof(stack) / sizeof(stack[0]), + pad, m_maxNewlineLength, fmt, args); + va_end(args); + + // output buffer + output(priority, buffer); + + // clean up + if (buffer != stack) + delete[] buffer; +} + +void +CLog::printt(const char* file, int line, const char* fmt, ...) const +{ + // check if fmt begins with a priority argument + int priority = 4; + if (fmt[0] == '%' && fmt[1] == 'z') { + priority = fmt[2] - '\060'; + fmt += 3; + } + + // done if below priority threshold + if (priority > getFilter()) { + return; + } + + // compute prefix padding length + char stack[1024]; + sprintf(stack, "%d", line); + int pad = strlen(file) + 1 /* comma */ + + strlen(stack) + 1 /* colon */ + 1 /* space */ + + g_priorityPad; + + // print to buffer, leaving space for a newline at the end + va_list args; + va_start(args, fmt); + char* buffer = CStringUtil::vsprint(stack, + sizeof(stack) / sizeof(stack[0]), + pad, m_maxNewlineLength, fmt, args); + va_end(args); + + // print the prefix to the buffer. leave space for priority label. + sprintf(buffer + g_priorityPad, "%s,%d:", file, line); + buffer[pad - 1] = ' '; + + // discard file and line if priority < 0 + char* message = buffer; + if (priority < 0) { + message += pad - g_priorityPad; + } + + // output buffer + output(priority, message); + + // clean up + if (buffer != stack) + delete[] buffer; +} + +void +CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) +{ + assert(outputter != NULL); + assert(outputter->getNewline() != NULL); + + CLogLock lock(m_mutex); + if (alwaysAtHead) { + m_alwaysOutputters.push_front(outputter); + } + else { + m_outputters.push_front(outputter); + } + int newlineLength = strlen(outputter->getNewline()); + if (newlineLength > m_maxNewlineLength) { + m_maxNewlineLength = newlineLength; + } +} + +void +CLog::remove(ILogOutputter* outputter) +{ + CLogLock lock(m_mutex); + m_outputters.remove(outputter); + m_alwaysOutputters.remove(outputter); +} + +void +CLog::pop_front(bool alwaysAtHead) +{ + CLogLock lock(m_mutex); + COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters; + if (!list->empty()) { + delete list->front(); + list->pop_front(); + } +} + +bool +CLog::setFilter(const char* maxPriority) +{ + if (maxPriority != NULL) { + for (int i = 0; i < g_numPriority; ++i) { + if (strcmp(maxPriority, g_priority[i]) == 0) { + setFilter(i); + return true; + } + } + return false; + } + return true; +} + +void +CLog::setFilter(int maxPriority) +{ + CLogLock lock(m_mutex); + m_maxPriority = maxPriority; +} + +int +CLog::getFilter() const +{ + CLogLock lock(m_mutex); + return m_maxPriority; +} + +void +CLog::output(int priority, char* msg) const +{ + assert(priority >= -1 && priority < g_numPriority); + assert(msg != NULL); + + // insert priority label + int n = -g_prioritySuffixLength; + if (priority >= 0) { + n = strlen(g_priority[priority]); + strcpy(msg + g_maxPriorityLength - n, g_priority[priority]); + msg[g_maxPriorityLength + 0] = ':'; + msg[g_maxPriorityLength + 1] = ' '; + msg[g_maxPriorityLength + 1] = ' '; + } + + // write to each outputter + CLogLock lock(m_mutex); + for (COutputterList::const_iterator index = m_alwaysOutputters.begin(); + index != m_alwaysOutputters.end(); + ++index) { + // get outputter + ILogOutputter* outputter = *index; + + // put an appropriate newline at the end + strcat(msg + g_priorityPad, outputter->getNewline()); + + // open the outputter + outputter->open(kApplication); + + // write message + outputter->write(static_cast(priority), + msg + g_maxPriorityLength - n); + } + for (COutputterList::const_iterator index = m_outputters.begin(); + index != m_outputters.end(); ++index) { + // get outputter + ILogOutputter* outputter = *index; + + // put an appropriate newline at the end + strcat(msg + g_priorityPad, outputter->getNewline()); + + // open the outputter + outputter->open(kApplication); + + // write message and break out of loop if it returns false + if (!outputter->write(static_cast(priority), + msg + g_maxPriorityLength - n)) { + break; + } + } +} diff --git a/lib/base/CLog.h b/lib/base/CLog.h new file mode 100644 index 0000000..b25e0ba --- /dev/null +++ b/lib/base/CLog.h @@ -0,0 +1,207 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CLOG_H +#define CLOG_H + +#include "common.h" +#include "IArchMultithread.h" +#include "stdlist.h" +#include + +#define CLOG (CLog::getInstance()) + +class ILogOutputter; + +//! Logging facility +/*! +The logging class; all console output should go through this class. +It supports multithread safe operation, several message priority levels, +filtering by priority, and output redirection. The macros LOG() and +LOGC() provide convenient access. +*/ +class CLog { +public: + //! Log levels + /*! + The logging priority levels in order of highest to lowest priority. + */ + enum ELevel { + kFATAL, //!< For fatal errors + kERROR, //!< For serious errors + kWARNING, //!< For minor errors and warnings + kNOTE, //!< For messages about notable events + kINFO, //!< For informational messages + kDEBUG, //!< For important debugging messages + kDEBUG1, //!< For more detailed debugging messages + kDEBUG2 //!< For even more detailed debugging messages + }; + + ~CLog(); + + //! @name manipulators + //@{ + + //! Add an outputter to the head of the list + /*! + Inserts an outputter to the head of the outputter list. When the + logger writes a message, it goes to the outputter at the head of + the outputter list. If that outputter's \c write() method returns + true then it also goes to the next outputter, as so on until an + outputter returns false or there are no more outputters. Outputters + still in the outputter list when the log is destroyed will be + deleted. If \c alwaysAtHead is true then the outputter is always + called before all outputters with \c alwaysAtHead false and the + return value of the outputter is ignored. + + By default, the logger has one outputter installed which writes to + the console. + */ + void insert(ILogOutputter* adopted, + bool alwaysAtHead = false); + + //! Remove an outputter from the list + /*! + Removes the first occurrence of the given outputter from the + outputter list. It does nothing if the outputter is not in the + list. The outputter is not deleted. + */ + void remove(ILogOutputter* orphaned); + + //! Remove the outputter from the head of the list + /*! + Removes and deletes the outputter at the head of the outputter list. + This does nothing if the outputter list is empty. Only removes + outputters that were inserted with the matching \c alwaysAtHead. + */ + void pop_front(bool alwaysAtHead = false); + + //! Set the minimum priority filter. + /*! + Set the filter. Messages below this priority are discarded. + The default priority is 4 (INFO) (unless built without NDEBUG + in which case it's 5 (DEBUG)). setFilter(const char*) returns + true if the priority \c name was recognized; if \c name is NULL + then it simply returns true. + */ + bool setFilter(const char* name); + void setFilter(int); + + //@} + //! @name accessors + //@{ + + //! Print a log message + /*! + Print a log message using the printf-like \c format and arguments. + */ + void print(const char* format, ...) const; + + //! Print a log message + /*! + Print a log message using the printf-like \c format and arguments + preceded by the filename and line number. + */ + void printt(const char* file, int line, + const char* format, ...) const; + + //! Get the minimum priority level. + int getFilter() const; + + //! Get the singleton instance of the log + static CLog* getInstance(); + + //@} + +private: + CLog(); + + void output(int priority, char* msg) const; + +private: + typedef std::list COutputterList; + + static CLog* s_log; + + CArchMutex m_mutex; + COutputterList m_outputters; + COutputterList m_alwaysOutputters; + int m_maxNewlineLength; + int m_maxPriority; +}; + +/*! +\def LOG(arg) +Write to the log. Because macros cannot accept variable arguments, this +should be invoked like so: +\code +LOG((CLOG_XXX "%d and %d are %s", x, y, x == y ? "equal" : "not equal")); +\endcode +In particular, notice the double open and close parentheses. Also note +that there is no comma after the \c CLOG_XXX. The \c XXX should be +replaced by one of enumerants in \c CLog::ELevel without the leading +\c k. For example, \c CLOG_INFO. The special \c CLOG_PRINT level will +not be filtered and is never prefixed by the filename and line number. + +If \c NOLOGGING is defined during the build then this macro expands to +nothing. If \c NDEBUG is defined during the build then it expands to a +call to CLog::print. Otherwise it expands to a call to CLog::printt, +which includes the filename and line number. +*/ + +/*! +\def LOGC(expr, arg) +Write to the log if and only if expr is true. Because macros cannot accept +variable arguments, this should be invoked like so: +\code +LOGC(x == y, (CLOG_XXX "%d and %d are equal", x, y)); +\endcode +In particular, notice the parentheses around everything after the boolean +expression. Also note that there is no comma after the \c CLOG_XXX. +The \c XXX should be replaced by one of enumerants in \c CLog::ELevel +without the leading \c k. For example, \c CLOG_INFO. The special +\c CLOG_PRINT level will not be filtered and is never prefixed by the +filename and line number. + +If \c NOLOGGING is defined during the build then this macro expands to +nothing. If \c NDEBUG is defined during the build then it expands to a +call to CLog::print. Otherwise it expands to a call to CLog::printt, +which includes the filename and line number. +*/ + +#if defined(NOLOGGING) +#define LOG(_a1) +#define LOGC(_a1, _a2) +#define CLOG_TRACE +#elif defined(NDEBUG) +#define LOG(_a1) CLOG->print _a1 +#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2 +#define CLOG_TRACE +#else +#define LOG(_a1) CLOG->printt _a1 +#define LOGC(_a1, _a2) if (_a1) CLOG->printt _a2 +#define CLOG_TRACE __FILE__, __LINE__, +#endif + +#define CLOG_PRINT CLOG_TRACE "%z\057" +#define CLOG_CRIT CLOG_TRACE "%z\060" +#define CLOG_ERR CLOG_TRACE "%z\061" +#define CLOG_WARN CLOG_TRACE "%z\062" +#define CLOG_NOTE CLOG_TRACE "%z\063" +#define CLOG_INFO CLOG_TRACE "%z\064" +#define CLOG_DEBUG CLOG_TRACE "%z\065" +#define CLOG_DEBUG1 CLOG_TRACE "%z\066" +#define CLOG_DEBUG2 CLOG_TRACE "%z\067" + +#endif diff --git a/lib/base/CStopwatch.cpp b/lib/base/CStopwatch.cpp new file mode 100644 index 0000000..89edce2 --- /dev/null +++ b/lib/base/CStopwatch.cpp @@ -0,0 +1,126 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CStopwatch.h" +#include "CArch.h" + +// +// CStopwatch +// + +CStopwatch::CStopwatch(bool triggered) : + m_mark(0.0), + m_triggered(triggered), + m_stopped(triggered) +{ + if (!triggered) { + m_mark = ARCH->time(); + } +} + +CStopwatch::~CStopwatch() +{ + // do nothing +} + +double +CStopwatch::reset() +{ + if (m_stopped) { + const double dt = m_mark; + m_mark = 0.0; + return dt; + } + else { + const double t = ARCH->time(); + const double dt = t - m_mark; + m_mark = t; + return dt; + } +} + +void +CStopwatch::stop() +{ + if (m_stopped) { + return; + } + + // save the elapsed time + m_mark = ARCH->time() - m_mark; + m_stopped = true; +} + +void +CStopwatch::start() +{ + m_triggered = false; + if (!m_stopped) { + return; + } + + // set the mark such that it reports the time elapsed at stop() + m_mark = ARCH->time() - m_mark; + m_stopped = false; +} + +void +CStopwatch::setTrigger() +{ + stop(); + m_triggered = true; +} + +double +CStopwatch::getTime() +{ + if (m_triggered) { + const double dt = m_mark; + start(); + return dt; + } + else if (m_stopped) { + return m_mark; + } + else { + return ARCH->time() - m_mark; + } +} + +CStopwatch::operator double() +{ + return getTime(); +} + +bool +CStopwatch::isStopped() const +{ + return m_stopped; +} + +double +CStopwatch::getTime() const +{ + if (m_stopped) { + return m_mark; + } + else { + return ARCH->time() - m_mark; + } +} + +CStopwatch::operator double() const +{ + return getTime(); +} diff --git a/lib/base/CStopwatch.h b/lib/base/CStopwatch.h new file mode 100644 index 0000000..e6a3471 --- /dev/null +++ b/lib/base/CStopwatch.h @@ -0,0 +1,108 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSTOPWATCH_H +#define CSTOPWATCH_H + +#include "common.h" + +//! A timer class +/*! +This class measures time intervals. All time interval measurement +should use this class. +*/ +class CStopwatch { +public: + /*! + The default constructor does an implicit reset() or setTrigger(). + If triggered == false then the clock starts ticking. + */ + CStopwatch(bool triggered = false); + ~CStopwatch(); + + //! @name manipulators + //@{ + + //! Reset the timer to zero + /*! + Set the start time to the current time, returning the time since + the last reset. This does not remove the trigger if it's set nor + does it start a stopped clock. If the clock is stopped then + subsequent reset()'s will return 0. + */ + double reset(); + + //! Stop the timer + /*! + Stop the stopwatch. The time interval while stopped is not + counted by the stopwatch. stop() does not remove the trigger. + Has no effect if already stopped. + */ + void stop(); + + //! Start the timer + /*! + Start the stopwatch. start() removes the trigger, even if the + stopwatch was already started. + */ + void start(); + + //! Stop the timer and set the trigger + /*! + setTrigger() stops the clock like stop() except there's an + implicit start() the next time (non-const) getTime() is called. + This is useful when you want the clock to start the first time + you check it. + */ + void setTrigger(); + + //! Get elapsed time + /*! + Returns the time since the last reset() (or calls reset() and + returns zero if the trigger is set). + */ + double getTime(); + //! Same as getTime() + operator double(); + //@} + //! @name accessors + //@{ + + //! Check if timer is stopped + /*! + Returns true if the stopwatch is stopped. + */ + bool isStopped() const; + + // return the time since the last reset(). + //! Get elapsed time + /*! + Returns the time since the last reset(). This cannot trigger the + stopwatch to start and will not clear the trigger. + */ + double getTime() const; + //! Same as getTime() const + operator double() const; + //@} + +private: + double getClock() const; + +private: + double m_mark; + bool m_triggered; + bool m_stopped; +}; + +#endif diff --git a/lib/base/CString.h b/lib/base/CString.h new file mode 100644 index 0000000..bc90500 --- /dev/null +++ b/lib/base/CString.h @@ -0,0 +1,25 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSTRING_H +#define CSTRING_H + +#include "common.h" +#include "stdstring.h" + +// use standard C++ string class for our string class +typedef std::string CString; + +#endif + diff --git a/lib/base/CStringUtil.cpp b/lib/base/CStringUtil.cpp new file mode 100644 index 0000000..2e61c04 --- /dev/null +++ b/lib/base/CStringUtil.cpp @@ -0,0 +1,211 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CStringUtil.h" +#include "CArch.h" +#include "common.h" +#include "stdvector.h" +#include +#include +#include +#include + +// +// CStringUtil +// + +CString +CStringUtil::format(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + CString result = vformat(fmt, args); + va_end(args); + return result; +} + +CString +CStringUtil::vformat(const char* fmt, va_list args) +{ + // find highest indexed substitution and the locations of substitutions + std::vector pos; + std::vector width; + std::vector index; + int maxIndex = 0; + for (const char* scan = fmt; *scan != '\0'; ++scan) { + if (*scan == '%') { + ++scan; + if (*scan == '\0') { + break; + } + else if (*scan == '%') { + // literal + index.push_back(0); + pos.push_back(static_cast(scan - 1 - fmt)); + width.push_back(2); + } + else if (*scan == '{') { + // get argument index + char* end; + int i = static_cast(strtol(scan + 1, &end, 10)); + if (*end != '}') { + // invalid index -- ignore + scan = end - 1; + } + else { + index.push_back(i); + pos.push_back(static_cast(scan - 1 - fmt)); + width.push_back(static_cast(end - scan + 2)); + if (i > maxIndex) { + maxIndex = i; + } + scan = end; + } + } + else { + // improper escape -- ignore + } + } + } + + // get args + std::vector value; + std::vector length; + value.push_back("%"); + length.push_back(1); + for (int i = 0; i < maxIndex; ++i) { + const char* arg = va_arg(args, const char*); + size_t len = strlen(arg); + value.push_back(arg); + length.push_back(len); + } + + // compute final length + size_t resultLength = strlen(fmt); + const int n = static_cast(pos.size()); + for (int i = 0; i < n; ++i) { + resultLength -= width[i]; + resultLength += length[index[i]]; + } + + // substitute + CString result; + result.reserve(resultLength); + size_t src = 0; + for (int i = 0; i < n; ++i) { + result.append(fmt + src, pos[i] - src); + result.append(value[index[i]]); + src = pos[i] + width[i]; + } + result.append(fmt + src); + + return result; +} + +CString +CStringUtil::print(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + CString result = vprint(fmt, args); + va_end(args); + return result; +} + +CString +CStringUtil::vprint(const char* fmt, va_list args) +{ + char tmp[1024]; + char* buffer = vsprint(tmp, sizeof(tmp) / sizeof(tmp[0]), 0, 0, fmt, args); + if (buffer == tmp) { + return buffer; + } + else { + CString result(buffer); + delete[] buffer; + return result; + } +} + +char* +CStringUtil::vsprint(char* buffer, int len, + int prefix, int suffix, const char* fmt, va_list args) +{ + assert(len > 0); + + // try writing to input buffer + int n; + if (buffer != NULL && len >= prefix + suffix) { + n = ARCH->vsnprintf(buffer + prefix, + len - (prefix + suffix), fmt, args); + if (n >= 0 && n <= len - (prefix + suffix)) + return buffer; + } + + // start allocating buffers until we write the whole string + buffer = NULL; + do { + delete[] buffer; + len *= 2; + buffer = new char[len + (prefix + suffix)]; + n = ARCH->vsnprintf(buffer + prefix, + len - (prefix + suffix), fmt, args); + } while (n < 0 || n > len - (prefix + suffix)); + + return buffer; +} + + +// +// CStringUtil::CaselessCmp +// + +bool +CStringUtil::CaselessCmp::cmpEqual( + const CString::value_type& a, + const CString::value_type& b) +{ + // FIXME -- use std::tolower but not in all versions of libstdc++ have it + return tolower(a) == tolower(b); +} + +bool +CStringUtil::CaselessCmp::cmpLess( + const CString::value_type& a, + const CString::value_type& b) +{ + // FIXME -- use std::tolower but not in all versions of libstdc++ have it + return tolower(a) < tolower(b); +} + +bool +CStringUtil::CaselessCmp::less(const CString& a, const CString& b) +{ + return std::lexicographical_compare( + a.begin(), a.end(), + b.begin(), b.end(), + &CStringUtil::CaselessCmp::cmpLess); +} + +bool +CStringUtil::CaselessCmp::equal(const CString& a, const CString& b) +{ + return !(less(a, b) || less(b, a)); +} + +bool +CStringUtil::CaselessCmp::operator()(const CString& a, const CString& b) const +{ + return less(a, b); +} diff --git a/lib/base/CStringUtil.h b/lib/base/CStringUtil.h new file mode 100644 index 0000000..1ad5824 --- /dev/null +++ b/lib/base/CStringUtil.h @@ -0,0 +1,99 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSTRINGUTIL_H +#define CSTRINGUTIL_H + +#include "CString.h" +#include + +//! String utilities +/*! +This class provides various functions for string manipulation. +*/ +class CStringUtil { +public: + //! Format positional arguments + /*! + Format a string using positional arguments. fmt has literal + characters and conversion specifications introduced by `\%': + - \c\%\% -- literal `\%' + - \c\%{n} -- positional element n, n a positive integer, {} are literal + + All arguments in the variable list are const char*. Positional + elements are indexed from 1. + */ + static CString format(const char* fmt, ...); + + //! Format positional arguments + /*! + Same as format() except takes va_list. + */ + static CString vformat(const char* fmt, va_list); + + //! Print a string using printf-style formatting + /*! + Equivalent to printf() except the result is returned as a CString. + */ + static CString print(const char* fmt, ...); + + //! Print a string using printf-style formatting + /*! + Same as print() except takes va_list. + */ + static CString vprint(const char* fmt, va_list); + + //! Print a string using printf-style formatting into a buffer + /*! + This is like print but print into a given buffer. If the resulting + string will not fit into \c buffer then a new buffer is allocated and + returned, otherwise \c buffer is returned. the caller must delete[] + the returned memory if is not \c buffer. + + \c prefix and \c suffix must be >= 0. Exactly \c prefix characters and + at least \c suffix characters are available in the buffer before + and after the printed string, respectively. \c bufferLength is the + length of buffer and should not be adjusted by the caller to + account for \c prefix or \c suffix. + */ + static char* vsprint(char* buffer, int bufferLength, + int prefix, int suffix, const char* fmt, va_list); + + //! Case-insensitive comparisons + /*! + This class provides case-insensitve comparison functions. + */ + class CaselessCmp { + public: + //! Same as less() + bool operator()(const CString& a, const CString& b) const; + + //! Returns true iff \c a is lexicographically less than \c b + static bool less(const CString& a, const CString& b); + + //! Returns true iff \c a is lexicographically equal to \c b + static bool equal(const CString& a, const CString& b); + + //! Returns true iff \c a is lexicographically less than \c b + static bool cmpLess(const CString::value_type& a, + const CString::value_type& b); + + //! Returns true iff \c a is lexicographically equal to \c b + static bool cmpEqual(const CString::value_type& a, + const CString::value_type& b); + }; +}; + +#endif + diff --git a/lib/base/CUnicode.cpp b/lib/base/CUnicode.cpp new file mode 100644 index 0000000..9d8fd8d --- /dev/null +++ b/lib/base/CUnicode.cpp @@ -0,0 +1,798 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CUnicode.h" +#include "CArch.h" +#include + +// +// local utility functions +// + +inline +static +UInt16 +decode16(const UInt8* n) +{ + union x16 { + UInt8 n8[2]; + UInt16 n16; + } c; + c.n8[0] = n[0]; + c.n8[1] = n[1]; + return c.n16; +} + +inline +static +UInt32 +decode32(const UInt8* n) +{ + union x32 { + UInt8 n8[4]; + UInt32 n32; + } c; + c.n8[0] = n[0]; + c.n8[1] = n[1]; + c.n8[2] = n[2]; + c.n8[3] = n[3]; + return c.n32; +} + +inline +static +void +resetError(bool* errors) +{ + if (errors != NULL) { + *errors = false; + } +} + +inline +static +void +setError(bool* errors) +{ + if (errors != NULL) { + *errors = true; + } +} + + +// +// CUnicode +// + +UInt32 CUnicode::s_invalid = 0x0000ffff; +UInt32 CUnicode::s_replacement = 0x0000fffd; + +bool +CUnicode::isUTF8(const CString& src) +{ + // convert and test each character + const UInt8* data = reinterpret_cast(src.c_str()); + for (UInt32 n = src.size(); n > 0; ) { + if (fromUTF8(data, n) == s_invalid) { + return false; + } + } + return true; +} + +CString +CUnicode::UTF8ToUCS2(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // get size of input string and reserve some space in output + UInt32 n = src.size(); + CString dst; + dst.reserve(2 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c == s_invalid) { + c = s_replacement; + } + else if (c >= 0x00010000) { + setError(errors); + c = s_replacement; + } + UInt16 ucs2 = static_cast(c); + dst.append(reinterpret_cast(&ucs2), 2); + } + + return dst; +} + +CString +CUnicode::UTF8ToUCS4(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // get size of input string and reserve some space in output + UInt32 n = src.size(); + CString dst; + dst.reserve(4 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c == s_invalid) { + c = s_replacement; + } + dst.append(reinterpret_cast(&c), 4); + } + + return dst; +} + +CString +CUnicode::UTF8ToUTF16(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // get size of input string and reserve some space in output + UInt32 n = src.size(); + CString dst; + dst.reserve(2 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c == s_invalid) { + c = s_replacement; + } + else if (c >= 0x00110000) { + setError(errors); + c = s_replacement; + } + if (c < 0x00010000) { + UInt16 ucs2 = static_cast(c); + dst.append(reinterpret_cast(&ucs2), 2); + } + else { + c -= 0x00010000; + UInt16 utf16h = static_cast((c >> 10) + 0xd800); + UInt16 utf16l = static_cast((c & 0x03ff) + 0xdc00); + dst.append(reinterpret_cast(&utf16h), 2); + dst.append(reinterpret_cast(&utf16l), 2); + } + } + + return dst; +} + +CString +CUnicode::UTF8ToUTF32(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // get size of input string and reserve some space in output + UInt32 n = src.size(); + CString dst; + dst.reserve(4 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c == s_invalid) { + c = s_replacement; + } + else if (c >= 0x00110000) { + setError(errors); + c = s_replacement; + } + dst.append(reinterpret_cast(&c), 4); + } + + return dst; +} + +CString +CUnicode::UTF8ToText(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // convert to wide char + UInt32 size; + wchar_t* tmp = UTF8ToWideChar(src, size, errors); + + // get length of multibyte string + int mblen; + CArchMBState state = ARCH->newMBState(); + size_t len = 0; + UInt32 n = size; + for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { + mblen = ARCH->convWCToMB(NULL, *scan, state); + if (mblen == -1) { + // unconvertable character + setError(errors); + len += 1; + } + else { + len += mblen; + } + } + + // handle nul terminator + mblen = ARCH->convWCToMB(NULL, L'\0', state); + if (mblen != -1) { + len += mblen; + } + assert(ARCH->isInitMBState(state) != 0); + + // allocate multibyte string + char* mbs = new char[len]; + + // convert to multibyte + char* dst = mbs; + n = size; + for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { + mblen = ARCH->convWCToMB(dst, *scan, state); + if (mblen == -1) { + // unconvertable character + *dst++ = '?'; + } + else { + dst += mblen; + } + } + mblen = ARCH->convWCToMB(dst, L'\0', state); + if (mblen != -1) { + // don't include nul terminator + dst += mblen - 1; + } + CString text(mbs, dst - mbs); + + // clean up + delete[] mbs; + delete[] tmp; + ARCH->closeMBState(state); + + return text; +} + +CString +CUnicode::UCS2ToUTF8(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // convert + UInt32 n = src.size() >> 1; + return doUCS2ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::UCS4ToUTF8(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // convert + UInt32 n = src.size() >> 2; + return doUCS4ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::UTF16ToUTF8(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // convert + UInt32 n = src.size() >> 1; + return doUTF16ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::UTF32ToUTF8(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // convert + UInt32 n = src.size() >> 2; + return doUTF32ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::textToUTF8(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // get length of multibyte string + UInt32 n = src.size(); + size_t len = 0; + CArchMBState state = ARCH->newMBState(); + for (const char* scan = src.c_str(); n > 0; ) { + int mblen = ARCH->convMBToWC(NULL, scan, n, state); + switch (mblen) { + case -2: + // incomplete last character. convert to unknown character. + setError(errors); + len += 1; + n = 0; + break; + + case -1: + // invalid character. count one unknown character and + // start at the next byte. + setError(errors); + len += 1; + scan += 1; + n -= 1; + break; + + case 0: + len += 1; + scan += 1; + n -= 1; + break; + + default: + // normal character + len += 1; + scan += mblen; + n -= mblen; + break; + } + } + ARCH->initMBState(state); + + // allocate wide character string + wchar_t* wcs = new wchar_t[len]; + + // convert multibyte to wide char + n = src.size(); + wchar_t* dst = wcs; + for (const char* scan = src.c_str(); n > 0; ++dst) { + int mblen = ARCH->convMBToWC(dst, scan, n, state); + switch (mblen) { + case -2: + // incomplete character. convert to unknown character. + *dst = (wchar_t)0xfffd; + n = 0; + break; + + case -1: + // invalid character. count one unknown character and + // start at the next byte. + *dst = (wchar_t)0xfffd; + scan += 1; + n -= 1; + break; + + case 0: + *dst = (wchar_t)0x0000; + scan += 1; + n -= 1; + break; + + default: + // normal character + scan += mblen; + n -= mblen; + break; + } + } + + // convert to UTF8 + CString utf8 = wideCharToUTF8(wcs, len, errors); + + // clean up + delete[] wcs; + ARCH->closeMBState(state); + + return utf8; +} + +wchar_t* +CUnicode::UTF8ToWideChar(const CString& src, UInt32& size, bool* errors) +{ + // convert to platform's wide character encoding + CString tmp; + switch (ARCH->getWideCharEncoding()) { + case IArchString::kUCS2: + tmp = UTF8ToUCS2(src, errors); + size = tmp.size() >> 1; + break; + + case IArchString::kUCS4: + tmp = UTF8ToUCS4(src, errors); + size = tmp.size() >> 2; + break; + + case IArchString::kUTF16: + tmp = UTF8ToUTF16(src, errors); + size = tmp.size() >> 1; + break; + + case IArchString::kUTF32: + tmp = UTF8ToUTF32(src, errors); + size = tmp.size() >> 2; + break; + + default: + assert(0 && "unknown wide character encoding"); + } + + // copy to a wchar_t array + wchar_t* dst = new wchar_t[size]; + ::memcpy(dst, tmp.data(), sizeof(wchar_t) * size); + return dst; +} + +CString +CUnicode::wideCharToUTF8(const wchar_t* src, UInt32 size, bool* errors) +{ + // convert from platform's wide character encoding. + // note -- this must include a wide nul character (independent of + // the CString's nul character). + switch (ARCH->getWideCharEncoding()) { + case IArchString::kUCS2: + return doUCS2ToUTF8(reinterpret_cast(src), size, errors); + + case IArchString::kUCS4: + return doUCS4ToUTF8(reinterpret_cast(src), size, errors); + + case IArchString::kUTF16: + return doUTF16ToUTF8(reinterpret_cast(src), size, errors); + + case IArchString::kUTF32: + return doUTF32ToUTF8(reinterpret_cast(src), size, errors); + + default: + assert(0 && "unknown wide character encoding"); + return CString(); + } +} + +CString +CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 2, --n) { + UInt32 c = decode16(data); + toUTF8(dst, c, errors); + } + + return dst; +} + +CString +CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 4, --n) { + UInt32 c = decode32(data); + toUTF8(dst, c, errors); + } + + return dst; +} + +CString +CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 2, --n) { + UInt32 c = decode16(data); + if (c < 0x0000d800 || c > 0x0000dfff) { + toUTF8(dst, c, errors); + } + else if (n == 1) { + // error -- missing second word + setError(errors); + toUTF8(dst, s_replacement, NULL); + } + else if (c >= 0x0000d800 && c <= 0x0000dbff) { + UInt32 c2 = decode16(data); + data += 2; + --n; + if (c2 < 0x0000dc00 || c2 > 0x0000dfff) { + // error -- [d800,dbff] not followed by [dc00,dfff] + setError(errors); + toUTF8(dst, s_replacement, NULL); + } + else { + c = (((c - 0x0000d800) << 10) | (c2 - 0x0000dc00)) + 0x00010000; + toUTF8(dst, c, errors); + } + } + else { + // error -- [dc00,dfff] without leading [d800,dbff] + setError(errors); + toUTF8(dst, s_replacement, NULL); + } + } + + return dst; +} + +CString +CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n, bool* errors) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 4, --n) { + UInt32 c = decode32(data); + if (c >= 0x00110000) { + setError(errors); + c = s_replacement; + } + toUTF8(dst, c, errors); + } + + return dst; +} + +UInt32 +CUnicode::fromUTF8(const UInt8*& data, UInt32& n) +{ + assert(data != NULL); + assert(n != 0); + + // compute character encoding length, checking for overlong + // sequences (i.e. characters that don't use the shortest + // possible encoding). + UInt32 size; + if (data[0] < 0x80) { + // 0xxxxxxx + size = 1; + } + else if (data[0] < 0xc0) { + // 10xxxxxx -- in the middle of a multibyte character. counts + // as one invalid character. + --n; + ++data; + return s_invalid; + } + else if (data[0] < 0xe0) { + // 110xxxxx + size = 2; + } + else if (data[0] < 0xf0) { + // 1110xxxx + size = 3; + } + else if (data[0] < 0xf8) { + // 11110xxx + size = 4; + } + else if (data[0] < 0xfc) { + // 111110xx + size = 5; + } + else if (data[0] < 0xfe) { + // 1111110x + size = 6; + } + else { + // invalid sequence. dunno how many bytes to skip so skip one. + --n; + ++data; + return s_invalid; + } + + // make sure we have enough data + if (size > n) { + data += n; + n = 0; + return s_invalid; + } + + // extract character + UInt32 c; + switch (size) { + case 1: + c = static_cast(data[0]); + break; + + case 2: + c = ((static_cast(data[0]) & 0x1f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 3: + c = ((static_cast(data[0]) & 0x0f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[2]) & 0x3f) ); + break; + + case 4: + c = ((static_cast(data[0]) & 0x07) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 5: + c = ((static_cast(data[0]) & 0x03) << 24) | + ((static_cast(data[1]) & 0x3f) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 6: + c = ((static_cast(data[0]) & 0x01) << 30) | + ((static_cast(data[1]) & 0x3f) << 24) | + ((static_cast(data[1]) & 0x3f) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + default: + assert(0 && "invalid size"); + return s_invalid; + } + + // check that all bytes after the first have the pattern 10xxxxxx. + // truncated sequences are treated as a single malformed character. + bool truncated = false; + switch (size) { + case 6: + if ((data[5] & 0xc0) != 0x80) { + truncated = true; + size = 5; + } + // fall through + + case 5: + if ((data[4] & 0xc0) != 0x80) { + truncated = true; + size = 4; + } + // fall through + + case 4: + if ((data[3] & 0xc0) != 0x80) { + truncated = true; + size = 3; + } + // fall through + + case 3: + if ((data[2] & 0xc0) != 0x80) { + truncated = true; + size = 2; + } + // fall through + + case 2: + if ((data[1] & 0xc0) != 0x80) { + truncated = true; + size = 1; + } + } + + // update parameters + data += size; + n -= size; + + // invalid if sequence was truncated + if (truncated) { + return s_invalid; + } + + // check for characters that didn't use the smallest possible encoding + static UInt32 s_minChar[] = { + 0, + 0x00000000, + 0x00000080, + 0x00000800, + 0x00010000, + 0x00200000, + 0x04000000 + }; + if (c < s_minChar[size]) { + return s_invalid; + } + + // check for characters not in ISO-10646 + if (c >= 0x0000d800 && c <= 0x0000dfff) { + return s_invalid; + } + if (c >= 0x0000fffe && c <= 0x0000ffff) { + return s_invalid; + } + + return c; +} + +void +CUnicode::toUTF8(CString& dst, UInt32 c, bool* errors) +{ + UInt8 data[6]; + + // handle characters outside the valid range + if ((c >= 0x0000d800 && c <= 0x0000dfff) || c >= 0x80000000) { + setError(errors); + c = s_replacement; + } + + // convert to UTF-8 + if (c < 0x00000080) { + data[0] = static_cast(c); + dst.append(reinterpret_cast(data), 1); + } + else if (c < 0x00000800) { + data[0] = static_cast(((c >> 6) & 0x0000001f) + 0xc0); + data[1] = static_cast((c & 0x0000003f) + 0x80); + dst.append(reinterpret_cast(data), 2); + } + else if (c < 0x00010000) { + data[0] = static_cast(((c >> 12) & 0x0000000f) + 0xe0); + data[1] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[2] = static_cast((c & 0x0000003f) + 0x80); + dst.append(reinterpret_cast(data), 3); + } + else if (c < 0x00200000) { + data[0] = static_cast(((c >> 18) & 0x00000007) + 0xf0); + data[1] = static_cast(((c >> 12) & 0x0000003f) + 0x80); + data[2] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[3] = static_cast((c & 0x0000003f) + 0x80); + dst.append(reinterpret_cast(data), 4); + } + else if (c < 0x04000000) { + data[0] = static_cast(((c >> 24) & 0x00000003) + 0xf8); + data[1] = static_cast(((c >> 18) & 0x0000003f) + 0x80); + data[2] = static_cast(((c >> 12) & 0x0000003f) + 0x80); + data[3] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[4] = static_cast((c & 0x0000003f) + 0x80); + dst.append(reinterpret_cast(data), 5); + } + else if (c < 0x80000000) { + data[0] = static_cast(((c >> 30) & 0x00000001) + 0xfc); + data[1] = static_cast(((c >> 24) & 0x0000003f) + 0x80); + data[2] = static_cast(((c >> 18) & 0x0000003f) + 0x80); + data[3] = static_cast(((c >> 12) & 0x0000003f) + 0x80); + data[4] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[5] = static_cast((c & 0x0000003f) + 0x80); + dst.append(reinterpret_cast(data), 6); + } + else { + assert(0 && "character out of range"); + } +} diff --git a/lib/base/CUnicode.h b/lib/base/CUnicode.h new file mode 100644 index 0000000..85afbfd --- /dev/null +++ b/lib/base/CUnicode.h @@ -0,0 +1,143 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CUNICODE_H +#define CUNICODE_H + +#include "CString.h" +#include "BasicTypes.h" + +//! Unicode utility functions +/*! +This class provides functions for converting between various Unicode +encodings and the current locale encoding. +*/ +class CUnicode { +public: + //! @name accessors + //@{ + + //! Test UTF-8 string for validity + /*! + Returns true iff the string contains a valid sequence of UTF-8 + encoded characters. + */ + static bool isUTF8(const CString&); + + //! Convert from UTF-8 to UCS-2 encoding + /*! + Convert from UTF-8 to UCS-2. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UCS-2. + Decoding errors do not set *errors. + */ + static CString UTF8ToUCS2(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to UCS-4 encoding + /*! + Convert from UTF-8 to UCS-4. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UCS-4. + Decoding errors do not set *errors. + */ + static CString UTF8ToUCS4(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to UTF-16 encoding + /*! + Convert from UTF-8 to UTF-16. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UTF-16. + Decoding errors do not set *errors. + */ + static CString UTF8ToUTF16(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to UTF-32 encoding + /*! + Convert from UTF-8 to UTF-32. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UTF-32. + Decoding errors do not set *errors. + */ + static CString UTF8ToUTF32(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to the current locale encoding + /*! + Convert from UTF-8 to the current locale encoding. If errors is not + NULL then *errors is set to true iff any character could not be encoded. + Decoding errors do not set *errors. + */ + static CString UTF8ToText(const CString&, bool* errors = NULL); + + //! Convert from UCS-2 to UTF-8 + /*! + Convert from UCS-2 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ + static CString UCS2ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from UCS-4 to UTF-8 + /*! + Convert from UCS-4 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ + static CString UCS4ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from UTF-16 to UTF-8 + /*! + Convert from UTF-16 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ + static CString UTF16ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from UTF-32 to UTF-8 + /*! + Convert from UTF-32 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ + static CString UTF32ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from the current locale encoding to UTF-8 + /*! + Convert from the current locale encoding to UTF-8. If errors is not + NULL then *errors is set to true iff any character could not be decoded. + */ + static CString textToUTF8(const CString&, bool* errors = NULL); + + //@} + +private: + // convert UTF8 to wchar_t string (using whatever encoding is native + // to the platform). caller must delete[] the returned string. the + // string is *not* nul terminated; the length (in characters) is + // returned in size. + static wchar_t* UTF8ToWideChar(const CString&, + UInt32& size, bool* errors); + + // convert nul terminated wchar_t string (in platform's native + // encoding) to UTF8. + static CString wideCharToUTF8(const wchar_t*, + UInt32 size, bool* errors); + + // internal conversion to UTF8 + static CString doUCS2ToUTF8(const UInt8* src, UInt32 n, bool* errors); + static CString doUCS4ToUTF8(const UInt8* src, UInt32 n, bool* errors); + static CString doUTF16ToUTF8(const UInt8* src, UInt32 n, bool* errors); + static CString doUTF32ToUTF8(const UInt8* src, UInt32 n, bool* errors); + + // convert characters to/from UTF8 + static UInt32 fromUTF8(const UInt8*& src, UInt32& size); + static void toUTF8(CString& dst, UInt32 c, bool* errors); + +private: + static UInt32 s_invalid; + static UInt32 s_replacement; +}; + +#endif diff --git a/lib/base/IJob.h b/lib/base/IJob.h new file mode 100644 index 0000000..30ef5f5 --- /dev/null +++ b/lib/base/IJob.h @@ -0,0 +1,30 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IJOB_H +#define IJOB_H + +#include "IInterface.h" + +//! Job interface +/*! +A job is an interface for executing some function. +*/ +class IJob : public IInterface { +public: + //! Run the job + virtual void run() = 0; +}; + +#endif diff --git a/lib/base/ILogOutputter.h b/lib/base/ILogOutputter.h new file mode 100644 index 0000000..585598c --- /dev/null +++ b/lib/base/ILogOutputter.h @@ -0,0 +1,72 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ILOGOUTPUTTER_H +#define ILOGOUTPUTTER_H + +#include "IInterface.h" +#include "CLog.h" + +//! Outputter interface +/*! +Type of outputter interface. The logger performs all output through +outputters. ILogOutputter overrides must not call any log functions +directly or indirectly. +*/ +class ILogOutputter : public IInterface { +public: + typedef CLog::ELevel ELevel; + + //! @name manipulators + //@{ + + //! Open the outputter + /*! + Opens the outputter for writing. Calling this method on an + already open outputter must have no effect. + */ + virtual void open(const char* title) = 0; + + //! Close the outputter + /*! + Close the outputter. Calling this method on an already closed + outputter must have no effect. + */ + virtual void close() = 0; + + //! Write a message with level + /*! + Writes \c message, which has the given \c level, to a log. + If this method returns true then CLog will stop passing the + message to all outputters in the outputter chain, otherwise + it continues. Most implementations should return true. + */ + virtual bool write(ELevel level, const char* message) = 0; + + //@} + //! @name accessors + //@{ + + //! Returns the newline sequence for the outputter + /*! + Different outputters use different character sequences for newlines. + This method returns the appropriate newline sequence for this + outputter. + */ + virtual const char* getNewline() const = 0; + + //@} +}; + +#endif diff --git a/lib/base/LogOutputters.cpp b/lib/base/LogOutputters.cpp new file mode 100644 index 0000000..5f7a40b --- /dev/null +++ b/lib/base/LogOutputters.cpp @@ -0,0 +1,238 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "LogOutputters.h" +#include "CArch.h" + +// +// CStopLogOutputter +// + +CStopLogOutputter::CStopLogOutputter() +{ + // do nothing +} + +CStopLogOutputter::~CStopLogOutputter() +{ + // do nothing +} + +void +CStopLogOutputter::open(const char*) +{ + // do nothing +} + +void +CStopLogOutputter::close() +{ + // do nothing +} + +bool +CStopLogOutputter::write(ELevel, const char*) +{ + return false; +} + +const char* +CStopLogOutputter::getNewline() const +{ + return ""; +} + + +// +// CConsoleLogOutputter +// + +CConsoleLogOutputter::CConsoleLogOutputter() +{ + // do nothing +} + +CConsoleLogOutputter::~CConsoleLogOutputter() +{ + // do nothing +} + +void +CConsoleLogOutputter::open(const char* title) +{ + ARCH->openConsole(title); +} + +void +CConsoleLogOutputter::close() +{ + ARCH->closeConsole(); +} + +bool +CConsoleLogOutputter::write(ELevel, const char* msg) +{ + ARCH->writeConsole(msg); + return true; +} + +const char* +CConsoleLogOutputter::getNewline() const +{ + return ARCH->getNewlineForConsole(); +} + + +// +// CSystemLogOutputter +// + +CSystemLogOutputter::CSystemLogOutputter() +{ + // do nothing +} + +CSystemLogOutputter::~CSystemLogOutputter() +{ + // do nothing +} + +void +CSystemLogOutputter::open(const char* title) +{ + ARCH->openLog(title); +} + +void +CSystemLogOutputter::close() +{ + ARCH->closeLog(); +} + +bool +CSystemLogOutputter::write(ELevel level, const char* msg) +{ + IArchLog::ELevel archLogLevel; + switch (level) { + case CLog::kFATAL: + case CLog::kERROR: + archLogLevel = IArchLog::kERROR; + break; + + case CLog::kWARNING: + archLogLevel = IArchLog::kWARNING; + break; + + case CLog::kNOTE: + archLogLevel = IArchLog::kNOTE; + break; + + case CLog::kINFO: + archLogLevel = IArchLog::kINFO; + break; + + default: + archLogLevel = IArchLog::kDEBUG; + break; + + }; + ARCH->writeLog(archLogLevel, msg); + return true; +} + +const char* +CSystemLogOutputter::getNewline() const +{ + return ""; +} + + +// +// CSystemLogger +// + +CSystemLogger::CSystemLogger(const char* title) +{ + // redirect log messages + m_syslog = new CSystemLogOutputter; + m_stop = new CStopLogOutputter; + m_syslog->open(title); + CLOG->insert(m_stop); + CLOG->insert(m_syslog); +} + +CSystemLogger::~CSystemLogger() +{ + CLOG->remove(m_syslog); + CLOG->remove(m_stop); + delete m_stop; + delete m_syslog; +} + + +// +// CBufferedLogOutputter +// + +CBufferedLogOutputter::CBufferedLogOutputter(UInt32 maxBufferSize) : + m_maxBufferSize(maxBufferSize) +{ + // do nothing +} + +CBufferedLogOutputter::~CBufferedLogOutputter() +{ + // do nothing +} + +CBufferedLogOutputter::const_iterator +CBufferedLogOutputter::begin() const +{ + return m_buffer.begin(); +} + +CBufferedLogOutputter::const_iterator +CBufferedLogOutputter::end() const +{ + return m_buffer.end(); +} + +void +CBufferedLogOutputter::open(const char*) +{ + // do nothing +} + +void +CBufferedLogOutputter::close() +{ + // remove all elements from the buffer + m_buffer.clear(); +} + +bool +CBufferedLogOutputter::write(ELevel, const char* message) +{ + while (m_buffer.size() >= m_maxBufferSize) { + m_buffer.pop_front(); + } + m_buffer.push_back(CString(message)); + return true; +} + +const char* +CBufferedLogOutputter::getNewline() const +{ + return ""; +} diff --git a/lib/base/LogOutputters.h b/lib/base/LogOutputters.h new file mode 100644 index 0000000..64befe5 --- /dev/null +++ b/lib/base/LogOutputters.h @@ -0,0 +1,128 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef LOGOUTPUTTERS_H +#define LOGOUTPUTTERS_H + +#include "BasicTypes.h" +#include "ILogOutputter.h" +#include "CString.h" +#include "stddeque.h" + +//! Stop traversing log chain outputter +/*! +This outputter performs no output and returns false from \c write(), +causing the logger to stop traversing the outputter chain. Insert +this to prevent already inserted outputters from writing. +*/ +class CStopLogOutputter : public ILogOutputter { +public: + CStopLogOutputter(); + virtual ~CStopLogOutputter(); + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; +}; + +//! Write log to console +/*! +This outputter writes output to the console. The level for each +message is ignored. +*/ +class CConsoleLogOutputter : public ILogOutputter { +public: + CConsoleLogOutputter(); + virtual ~CConsoleLogOutputter(); + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; +}; + +//! Write log to system log +/*! +This outputter writes output to the system log. +*/ +class CSystemLogOutputter : public ILogOutputter { +public: + CSystemLogOutputter(); + virtual ~CSystemLogOutputter(); + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; +}; + +//! Write log to system log only +/*! +Creating an object of this type inserts a CStopLogOutputter followed +by a CSystemLogOutputter into CLog. The destructor removes those +outputters. Add one of these to any scope that needs to write to +the system log (only) and restore the old outputters when exiting +the scope. +*/ +class CSystemLogger { +public: + CSystemLogger(const char* title); + ~CSystemLogger(); + +private: + ILogOutputter* m_syslog; + ILogOutputter* m_stop; +}; + +//! Save log history +/*! +This outputter records the last N log messages. +*/ +class CBufferedLogOutputter : public ILogOutputter { +private: + typedef std::deque CBuffer; + +public: + typedef CBuffer::const_iterator const_iterator; + + CBufferedLogOutputter(UInt32 maxBufferSize); + virtual ~CBufferedLogOutputter(); + + //! @name accessors + //@{ + + //! Get start of buffer + const_iterator begin() const; + + //! Get end of buffer + const_iterator end() const; + + //@} + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; + +private: + UInt32 m_maxBufferSize; + CBuffer m_buffer; +}; + +#endif diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am new file mode 100644 index 0000000..f750ba7 --- /dev/null +++ b/lib/base/Makefile.am @@ -0,0 +1,52 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + base.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libbase.a +libbase_a_SOURCES = \ + CFunctionJob.cpp \ + CJobList.cpp \ + CLog.cpp \ + CStopwatch.cpp \ + CStringUtil.cpp \ + CUnicode.cpp \ + LogOutputters.cpp \ + XBase.cpp \ + CFunctionJob.h \ + CJobList.h \ + CLog.h \ + CStopwatch.h \ + CString.h \ + CStringUtil.h \ + CUnicode.h \ + IJob.h \ + ILogOutputter.h \ + LogOutputters.h \ + TMethodJob.h \ + XBase.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + $(NULL) diff --git a/lib/base/Makefile.in b/lib/base/Makefile.in new file mode 100644 index 0000000..b9a6af1 --- /dev/null +++ b/lib/base/Makefile.in @@ -0,0 +1,363 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + base.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libbase.a +libbase_a_SOURCES = \ + CFunctionJob.cpp \ + CJobList.cpp \ + CLog.cpp \ + CStopwatch.cpp \ + CStringUtil.cpp \ + CUnicode.cpp \ + LogOutputters.cpp \ + XBase.cpp \ + CFunctionJob.h \ + CJobList.h \ + CLog.h \ + CStopwatch.h \ + CString.h \ + CStringUtil.h \ + CUnicode.h \ + IJob.h \ + ILogOutputter.h \ + LogOutputters.h \ + TMethodJob.h \ + XBase.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + $(NULL) + +subdir = lib/base +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libbase_a_AR = $(AR) cru +libbase_a_LIBADD = +am_libbase_a_OBJECTS = CFunctionJob.$(OBJEXT) CJobList.$(OBJEXT) \ + CLog.$(OBJEXT) CStopwatch.$(OBJEXT) CStringUtil.$(OBJEXT) \ + CUnicode.$(OBJEXT) LogOutputters.$(OBJEXT) XBase.$(OBJEXT) +libbase_a_OBJECTS = $(am_libbase_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CFunctionJob.Po $(DEPDIR)/CJobList.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CLog.Po $(DEPDIR)/CStopwatch.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CStringUtil.Po $(DEPDIR)/CUnicode.Po \ +@AMDEP_TRUE@ $(DEPDIR)/LogOutputters.Po $(DEPDIR)/XBase.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libbase_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libbase_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/base/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libbase.a: $(libbase_a_OBJECTS) $(libbase_a_DEPENDENCIES) + -rm -f libbase.a + $(libbase_a_AR) libbase.a $(libbase_a_OBJECTS) $(libbase_a_LIBADD) + $(RANLIB) libbase.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CFunctionJob.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CJobList.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CLog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CStopwatch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CStringUtil.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CUnicode.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/LogOutputters.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XBase.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/base/TMethodJob.h b/lib/base/TMethodJob.h new file mode 100644 index 0000000..54cd936 --- /dev/null +++ b/lib/base/TMethodJob.h @@ -0,0 +1,67 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMETHODJOB_H +#define CMETHODJOB_H + +#include "IJob.h" + +//! Use a function as a job +/*! +A job class that invokes a member function. +*/ +template +class TMethodJob : public IJob { +public: + //! run() invokes \c object->method(arg) + TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL); + virtual ~TMethodJob(); + + // IJob overrides + virtual void run(); + +private: + T* m_object; + void (T::*m_method)(void*); + void* m_arg; +}; + +template +inline +TMethodJob::TMethodJob(T* object, void (T::*method)(void*), void* arg) : + m_object(object), + m_method(method), + m_arg(arg) +{ + // do nothing +} + +template +inline +TMethodJob::~TMethodJob() +{ + // do nothing +} + +template +inline +void +TMethodJob::run() +{ + if (m_object != NULL) { + (m_object->*m_method)(m_arg); + } +} + +#endif diff --git a/lib/base/XBase.cpp b/lib/base/XBase.cpp new file mode 100644 index 0000000..022be30 --- /dev/null +++ b/lib/base/XBase.cpp @@ -0,0 +1,69 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XBase.h" +#include "CStringUtil.h" +#include +#include + +// +// XBase +// + +XBase::XBase() : + m_what() +{ + // do nothing +} + +XBase::XBase(const CString& msg) : + m_what(msg) +{ + // do nothing +} + +XBase::~XBase() +{ + // do nothing +} + +const char* +XBase::what() const +{ + if (m_what.empty()) { + m_what = getWhat(); + } + return m_what.c_str(); +} + +CString +XBase::format(const char* /*id*/, const char* fmt, ...) const throw() +{ + // FIXME -- lookup message string using id as an index. set + // fmt to that string if it exists. + + // format + CString result; + va_list args; + va_start(args, fmt); + try { + result = CStringUtil::vformat(fmt, args); + } + catch (...) { + // ignore + } + va_end(args); + + return result; +} diff --git a/lib/base/XBase.h b/lib/base/XBase.h new file mode 100644 index 0000000..e9161d7 --- /dev/null +++ b/lib/base/XBase.h @@ -0,0 +1,121 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XBASE_H +#define XBASE_H + +#include "CString.h" + +//! Exception base class +/*! +This is the base class of most exception types. +*/ +class XBase { +public: + //! Use getWhat() as the result of what() + XBase(); + //! Use \c msg as the result of what() + XBase(const CString& msg); + virtual ~XBase(); + + //! Reason for exception + virtual const char* what() const; + +protected: + //! Get a human readable string describing the exception + virtual CString getWhat() const throw() = 0; + + //! Format a string + /*! + Looks up a message format using \c id, using \c defaultFormat if + no format can be found, then replaces positional parameters in + the format string and returns the result. + */ + virtual CString format(const char* id, + const char* defaultFormat, ...) const throw(); + +private: + mutable CString m_what; +}; + +/*! +\def XBASE_SUBCLASS +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. getWhat() is not +declared. +*/ +#define XBASE_SUBCLASS(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_() : super_() { } \ + name_(const CString& msg) : super_(msg) { } \ +} + +/*! +\def XBASE_SUBCLASS +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. getWhat() must be +implemented. +*/ +#define XBASE_SUBCLASS_WHAT(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_() : super_() { } \ + name_(const CString& msg) : super_(msg) { } \ + \ +protected: \ + virtual CString getWhat() const throw(); \ +} + +/*! +\def XBASE_SUBCLASS_FORMAT +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. what() is overridden +to call getWhat() when first called; getWhat() can format the +error message and can call what() to get the message passed to the +c'tor. +*/ +#define XBASE_SUBCLASS_FORMAT(name_, super_) \ +class name_ : public super_ { \ +private: \ + enum EState { kFirst, kFormat, kDone }; \ + \ +public: \ + name_() : super_(), m_state(kDone) { } \ + name_(const CString& msg) : super_(msg), m_state(kFirst) { } \ + \ + virtual const char* what() const \ + { \ + if (m_state == kFirst) { \ + m_state = kFormat; \ + m_formatted = getWhat(); \ + m_state = kDone; \ + } \ + if (m_state == kDone) { \ + return m_formatted.c_str(); \ + } \ + else { \ + return super_::what(); \ + } \ + } \ + \ +protected: \ + virtual CString getWhat() const throw(); \ + \ +private: \ + mutable EState m_state; \ + mutable std::string m_formatted; \ +} + +#endif diff --git a/lib/base/base.dsp b/lib/base/base.dsp new file mode 100644 index 0000000..991f51c --- /dev/null +++ b/lib/base/base.dsp @@ -0,0 +1,174 @@ +# Microsoft Developer Studio Project File - Name="base" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=base - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "base.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "base.mak" CFG="base - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "base - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "base - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "base - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "base - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "base - Win32 Release" +# Name "base - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CFunctionJob.cpp +# End Source File +# Begin Source File + +SOURCE=.\CJobList.cpp +# End Source File +# Begin Source File + +SOURCE=.\CLog.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStopwatch.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStringUtil.cpp +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.cpp +# End Source File +# Begin Source File + +SOURCE=.\LogOutputters.cpp +# End Source File +# Begin Source File + +SOURCE=.\XBase.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CFunctionJob.h +# End Source File +# Begin Source File + +SOURCE=.\CJobList.h +# End Source File +# Begin Source File + +SOURCE=.\CLog.h +# End Source File +# Begin Source File + +SOURCE=.\CStopwatch.h +# End Source File +# Begin Source File + +SOURCE=.\CString.h +# End Source File +# Begin Source File + +SOURCE=.\CStringUtil.h +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.h +# End Source File +# Begin Source File + +SOURCE=.\IJob.h +# End Source File +# Begin Source File + +SOURCE=.\ILogOutputter.h +# End Source File +# Begin Source File + +SOURCE=.\LogOutputters.h +# End Source File +# Begin Source File + +SOURCE=.\TMethodJob.h +# End Source File +# Begin Source File + +SOURCE=.\XBase.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp new file mode 100644 index 0000000..8ada111 --- /dev/null +++ b/lib/client/CClient.cpp @@ -0,0 +1,754 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClient.h" +#include "CServerProxy.h" +#include "ISecondaryScreenFactory.h" +#include "CClipboard.h" +#include "CInputPacketStream.h" +#include "COutputPacketStream.h" +#include "CProtocolUtil.h" +#include "CSecondaryScreen.h" +#include "IServer.h" +#include "ProtocolTypes.h" +#include "XScreen.h" +#include "XSynergy.h" +#include "IDataSocket.h" +#include "ISocketFactory.h" +#include "XSocket.h" +#include "IStreamFilterFactory.h" +#include "CLock.h" +#include "CThread.h" +#include "CTimerThread.h" +#include "XMT.h" +#include "XThread.h" +#include "CLog.h" +#include "CStopwatch.h" +#include "TMethodJob.h" +#include "CArch.h" + +// +// CClient +// + +CClient::CClient(const CString& clientName) : + m_name(clientName), + m_screen(NULL), + m_server(NULL), + m_screenFactory(NULL), + m_socketFactory(NULL), + m_streamFilterFactory(NULL), + m_session(NULL), + m_active(false), + m_rejected(true), + m_status(kNotRunning) +{ + // do nothing +} + +CClient::~CClient() +{ + delete m_screenFactory; + delete m_socketFactory; + delete m_streamFilterFactory; +} + +void +CClient::setAddress(const CNetworkAddress& serverAddress) +{ + CLock lock(&m_mutex); + m_serverAddress = serverAddress; +} + +void +CClient::setScreenFactory(ISecondaryScreenFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_screenFactory; + m_screenFactory = adopted; +} + +void +CClient::setSocketFactory(ISocketFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_socketFactory; + m_socketFactory = adopted; +} + +void +CClient::setStreamFilterFactory(IStreamFilterFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_streamFilterFactory; + m_streamFilterFactory = adopted; +} + +void +CClient::exitMainLoop() +{ + m_screen->exitMainLoop(); +} + +void +CClient::addStatusJob(IJob* job) +{ + m_statusJobs.addJob(job); +} + +void +CClient::removeStatusJob(IJob* job) +{ + m_statusJobs.removeJob(job); +} + +bool +CClient::wasRejected() const +{ + return m_rejected; +} + +CClient::EStatus +CClient::getStatus(CString* msg) const +{ + CLock lock(&m_mutex); + if (msg != NULL) { + *msg = m_statusMessage; + } + return m_status; +} + +void +CClient::runStatusJobs() const +{ + m_statusJobs.runJobs(); +} + +void +CClient::setStatus(EStatus status, const char* msg) +{ + { + CLock lock(&m_mutex); + m_status = status; + if (m_status == kError) { + m_statusMessage = (msg == NULL) ? "Error" : msg; + } + else { + m_statusMessage = (msg == NULL) ? "" : msg; + } + } + runStatusJobs(); +} + +void +CClient::onError() +{ + setStatus(kError); + + // close down session but don't wait too long + deleteSession(3.0); +} + +void +CClient::onInfoChanged(const CClientInfo& info) +{ + LOG((CLOG_DEBUG "resolution changed")); + + CLock lock(&m_mutex); + if (m_server != NULL) { + m_server->onInfoChanged(info); + } +} + +bool +CClient::onGrabClipboard(ClipboardID id) +{ + CLock lock(&m_mutex); + if (m_server == NULL) { + // m_server can be NULL if the screen calls this method + // before we've gotten around to connecting to the server. + // we simply ignore the clipboard change in that case. + return false; + } + + // grab ownership + m_server->onGrabClipboard(id); + + // we now own the clipboard and it has not been sent to the server + m_ownClipboard[id] = true; + m_timeClipboard[id] = 0; + + // if we're not the active screen then send the clipboard now, + // otherwise we'll wait until we leave. + if (!m_active) { + sendClipboard(id); + } + + return true; +} + +void +CClient::onClipboardChanged(ClipboardID, const CString&) +{ + // ignore -- we'll check the clipboard when we leave +} + +void +CClient::open() +{ + // open the screen + try { + LOG((CLOG_DEBUG "opening screen")); + openSecondaryScreen(); + setStatus(kNotRunning); + } + catch (XScreenOpenFailure& e) { + // can't open screen + setStatus(kError, e.what()); + LOG((CLOG_DEBUG "failed to open screen")); + throw; + } +} + +void +CClient::mainLoop() +{ + { + CLock lock(&m_mutex); + + // check preconditions + assert(m_screen != NULL); + assert(m_server == NULL); + + // connection starts as unsuccessful + m_rejected = true; + } + + try { + setStatus(kNotRunning); + LOG((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); + + // start server interactions + { + CLock lock(&m_mutex); + m_session = new CThread(new TMethodJob( + this, &CClient::runSession)); + } + + // handle events + m_screen->mainLoop(); + + // clean up + deleteSession(); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); + } + catch (XMT& e) { + LOG((CLOG_ERR "client error: %s", e.what())); + setStatus(kError, e.what()); + + // clean up + deleteSession(); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); + throw; + } + catch (XBase& e) { + LOG((CLOG_ERR "client error: %s", e.what())); + setStatus(kError, e.what()); + + // clean up + deleteSession(); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); + CLock lock(&m_mutex); + m_rejected = false; + } + catch (XThread&) { + setStatus(kNotRunning); + + // clean up + deleteSession(); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); + throw; + } + catch (...) { + LOG((CLOG_ERR "client error: ")); + setStatus(kError); + + // clean up + deleteSession(); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); + throw; + } +} + +void +CClient::close() +{ + closeSecondaryScreen(); + LOG((CLOG_DEBUG "closed screen")); +} + +void +CClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool) +{ + { + CLock lock(&m_mutex); + m_active = true; + } + + m_screen->enter(xAbs, yAbs, mask); +} + +bool +CClient::leave() +{ + m_screen->leave(); + + CLock lock(&m_mutex); + m_active = false; + + // send clipboards that we own and that have changed + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_ownClipboard[id]) { + sendClipboard(id); + } + } + + return true; +} + +void +CClient::setClipboard(ClipboardID id, const CString& data) +{ + // unmarshall + CClipboard clipboard; + clipboard.unmarshall(data, 0); + + // set screen's clipboard + m_screen->setClipboard(id, &clipboard); +} + +void +CClient::grabClipboard(ClipboardID id) +{ + // we no longer own the clipboard + { + CLock lock(&m_mutex); + m_ownClipboard[id] = false; + } + + m_screen->grabClipboard(id); +} + +void +CClient::setClipboardDirty(ClipboardID, bool) +{ + assert(0 && "shouldn't be called"); +} + +void +CClient::keyDown(KeyID id, KeyModifierMask mask, KeyButton button) +{ + m_screen->keyDown(id, mask, button); +} + +void +CClient::keyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + m_screen->keyRepeat(id, mask, count, button); +} + +void +CClient::keyUp(KeyID id, KeyModifierMask mask, KeyButton button) +{ + m_screen->keyUp(id, mask, button); +} + +void +CClient::mouseDown(ButtonID id) +{ + m_screen->mouseDown(id); +} + +void +CClient::mouseUp(ButtonID id) +{ + m_screen->mouseUp(id); +} + +void +CClient::mouseMove(SInt32 x, SInt32 y) +{ + m_screen->mouseMove(x, y); +} + +void +CClient::mouseWheel(SInt32 delta) +{ + m_screen->mouseWheel(delta); +} + +void +CClient::screensaver(bool activate) +{ + m_screen->screensaver(activate); +} + +void +CClient::resetOptions() +{ + m_screen->resetOptions(); +} + +void +CClient::setOptions(const COptionsList& options) +{ + m_screen->setOptions(options); +} + +CString +CClient::getName() const +{ + return m_name; +} + +SInt32 +CClient::getJumpZoneSize() const +{ + return m_screen->getJumpZoneSize(); +} + +void +CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + m_screen->getShape(x, y, w, h); +} + +void +CClient::getCursorPos(SInt32& x, SInt32& y) const +{ + m_screen->getCursorPos(x, y); +} + +void +CClient::getCursorCenter(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + +void +CClient::openSecondaryScreen() +{ + assert(m_screen == NULL); + + // not active + m_active = false; + + // reset clipboard state + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_ownClipboard[id] = false; + m_timeClipboard[id] = 0; + } + + // create screen + LOG((CLOG_DEBUG1 "creating secondary screen")); + if (m_screenFactory != NULL) { + m_screen = m_screenFactory->create(this); + } + if (m_screen == NULL) { + throw XScreenOpenFailure(); + } + + // open screen + try { + LOG((CLOG_DEBUG1 "opening secondary screen")); + m_screen->open(); + } + catch (...) { + LOG((CLOG_DEBUG1 "destroying secondary screen")); + delete m_screen; + m_screen = NULL; + throw; + } +} + +void +CClient::closeSecondaryScreen() +{ + // close the secondary screen + try { + if (m_screen != NULL) { + LOG((CLOG_DEBUG1 "closing secondary screen")); + m_screen->close(); + } + } + catch (...) { + // ignore + } + + // clean up + LOG((CLOG_DEBUG1 "destroying secondary screen")); + delete m_screen; + m_screen = NULL; +} + +void +CClient::sendClipboard(ClipboardID id) +{ + // note -- m_mutex must be locked on entry + assert(m_screen != NULL); + assert(m_server != NULL); + + // get clipboard data. set the clipboard time to the last + // clipboard time before getting the data from the screen + // as the screen may detect an unchanged clipboard and + // avoid copying the data. + CClipboard clipboard; + if (clipboard.open(m_timeClipboard[id])) { + clipboard.close(); + } + m_screen->getClipboard(id, &clipboard); + + // check time + if (m_timeClipboard[id] == 0 || + clipboard.getTime() != m_timeClipboard[id]) { + // save new time + m_timeClipboard[id] = clipboard.getTime(); + + // marshall the data + CString data = clipboard.marshall(); + + // save and send data if different + if (data != m_dataClipboard[id]) { + m_dataClipboard[id] = data; + m_server->onClipboardChanged(id, data); + } + } +} + +void +CClient::runSession(void*) +{ + try { + LOG((CLOG_DEBUG "starting server proxy")); + runServer(); + m_screen->exitMainLoop(); + LOG((CLOG_DEBUG "stopping server proxy")); + } + catch (...) { + m_screen->exitMainLoop(); + LOG((CLOG_DEBUG "stopping server proxy")); + throw; + } +} + +void +CClient::deleteSession(double timeout) +{ + // get session thread object + CThread* thread; + { + CLock lock(&m_mutex); + thread = m_session; + m_session = NULL; + } + + // shut it down + if (thread != NULL) { + thread->cancel(); + thread->wait(timeout); + delete thread; + } +} + +void +CClient::runServer() +{ + IDataSocket* socket = NULL; + CServerProxy* proxy = NULL; + bool timedOut; + try { + // allow connect and handshake this much time to succeed + CTimerThread timer(15.0, &timedOut); + + // create socket and attempt to connect to server + LOG((CLOG_DEBUG1 "connecting to server")); + if (m_socketFactory != NULL) { + socket = m_socketFactory->create(); + } + assert(socket != NULL); + socket->connect(m_serverAddress); + + // create proxy + LOG((CLOG_INFO "connected to server")); + LOG((CLOG_DEBUG1 "negotiating with server")); + proxy = handshakeServer(socket); + } + catch (XThread&) { + if (timedOut) { + LOG((CLOG_ERR "connection timed out")); + setStatus(kError, "connection timed out"); + } + else { + // cancelled by some thread other than the timer + } + delete proxy; + delete socket; + throw; + } + catch (XSocketConnect& e) { + LOG((CLOG_ERR "connection failed: %s", e.what())); + setStatus(kError, e.what()); + delete socket; + return; + } + catch (XBase& e) { + LOG((CLOG_ERR "connection failed: %s", e.what())); + setStatus(kError, e.what()); + LOG((CLOG_INFO "disconnecting from server")); + delete socket; + return; + } + catch (...) { + LOG((CLOG_ERR "connection failed: ")); + setStatus(kError); + LOG((CLOG_INFO "disconnecting from server")); + delete socket; + return; + } + + // saver server proxy object + { + CLock lock(&m_mutex); + m_server = proxy; + } + + try { + // prepare for remote control + m_screen->remoteControl(); + + // process messages + bool rejected = true; + if (proxy != NULL) { + LOG((CLOG_DEBUG1 "communicating with server")); + setStatus(kRunning); + rejected = !proxy->mainLoop(); + setStatus(kNotRunning); + } + + // prepare for local control + m_screen->localControl(); + + // clean up + CLock lock(&m_mutex); + m_rejected = rejected; + m_server = NULL; + delete proxy; + LOG((CLOG_DEBUG "disconnecting from server")); + socket->close(); + delete socket; + } + catch (...) { + setStatus(kNotRunning); + m_screen->localControl(); + CLock lock(&m_mutex); + m_rejected = false; + m_server = NULL; + delete proxy; + LOG((CLOG_DEBUG "disconnecting from server")); + socket->close(); + delete socket; + throw; + } +} + +CServerProxy* +CClient::handshakeServer(IDataSocket* socket) +{ + // get the input and output streams + IInputStream* input = socket->getInputStream(); + IOutputStream* output = socket->getOutputStream(); + bool own = false; + + // attach filters + if (m_streamFilterFactory != NULL) { + input = m_streamFilterFactory->createInput(input, own); + output = m_streamFilterFactory->createOutput(output, own); + own = true; + } + + // attach the packetizing filters + input = new CInputPacketStream(input, own); + output = new COutputPacketStream(output, own); + own = true; + + CServerProxy* proxy = NULL; + try { + // wait for hello from server + LOG((CLOG_DEBUG1 "wait for hello")); + SInt16 major, minor; + CProtocolUtil::readf(input, kMsgHello, &major, &minor); + + // check versions + LOG((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); + if (major < kProtocolMajorVersion || + (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) { + throw XIncompatibleClient(major, minor); + } + + // say hello back + LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); + CProtocolUtil::writef(output, kMsgHelloBack, + kProtocolMajorVersion, + kProtocolMinorVersion, &m_name); + + // create server proxy + proxy = new CServerProxy(this, input, output); + + // negotiate + // FIXME + + return proxy; + } + catch (XIncompatibleClient& e) { + LOG((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); + setStatus(kError, e.what()); + } + catch (XBase& e) { + LOG((CLOG_WARN "error communicating with server: %s", e.what())); + setStatus(kError, e.what()); + } + catch (...) { + // probably timed out + if (proxy != NULL) { + delete proxy; + } + else if (own) { + delete input; + delete output; + } + throw; + } + + // failed + if (proxy != NULL) { + delete proxy; + } + else if (own) { + delete input; + delete output; + } + + return NULL; +} diff --git a/lib/client/CClient.h b/lib/client/CClient.h new file mode 100644 index 0000000..5647e7e --- /dev/null +++ b/lib/client/CClient.h @@ -0,0 +1,206 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENT_H +#define CCLIENT_H + +#include "IScreenReceiver.h" +#include "IClient.h" +#include "IClipboard.h" +#include "CNetworkAddress.h" +#include "CMutex.h" +#include "CJobList.h" + +class CSecondaryScreen; +class CServerProxy; +class CThread; +class IDataSocket; +class IScreenReceiver; +class ISecondaryScreenFactory; +class ISocketFactory; +class IStreamFilterFactory; + +//! Synergy client +/*! +This class implements the top-level client algorithms for synergy. +*/ +class CClient : public IScreenReceiver, public IClient { +public: + enum EStatus { + kNotRunning, + kRunning, + kError, + kMaxStatus + }; + + /*! + This client will attempt to connect the server using \c clientName + as its name. + */ + CClient(const CString& clientName); + ~CClient(); + + //! @name manipulators + //@{ + + //! Set server address + /*! + Sets the server's address that the client should connect to. + */ + void setAddress(const CNetworkAddress& serverAddress); + + //! Set secondary screen factory + /*! + Sets the factory for creating secondary screens. This must be + set before calling open(). This object takes ownership of the + factory. + */ + void setScreenFactory(ISecondaryScreenFactory*); + + //! Set socket factory + /*! + Sets the factory used to create a socket to connect to the server. + This must be set before calling mainLoop(). This object takes + ownership of the factory. + */ + void setSocketFactory(ISocketFactory*); + + //! Set stream filter factory + /*! + Sets the factory used to filter the socket streams used to + communicate with the server. This object takes ownership + of the factory. + */ + void setStreamFilterFactory(IStreamFilterFactory*); + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). This may only be + called between a successful open() and close(). + */ + void exitMainLoop(); + + //! Add a job to notify of status changes + /*! + The added job is run whenever the server's status changes in + certain externally visible ways. The client keeps ownership + of the job. + */ + void addStatusJob(IJob*); + + //! Remove a job to notify of status changes + /*! + Removes a previously added status notification job. A job can + remove itself when called but must not remove any other jobs. + The client keeps ownership of the job. + */ + void removeStatusJob(IJob*); + + //@} + //! @name accessors + //@{ + + //! + /*! + Returns true if the server rejected our connection. + */ + bool wasRejected() const; + + //! Get the status + /*! + Returns the current status and status message. + */ + EStatus getStatus(CString* = NULL) const; + + //@} + + // IScreenReceiver overrides + virtual void onError(); + virtual void onInfoChanged(const CClientInfo&); + virtual bool onGrabClipboard(ClipboardID); + virtual void onClipboardChanged(ClipboardID, const CString&); + + // IClient overrides + virtual void open(); + virtual void mainLoop(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool dirty); + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); + 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 resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; + +private: + // notify status jobs of a change + void runStatusJobs() const; + + // set new status + void setStatus(EStatus, const char* msg = NULL); + + // open/close the secondary screen + void openSecondaryScreen(); + void closeSecondaryScreen(); + + // send the clipboard to the server + void sendClipboard(ClipboardID); + + // handle server messaging + void runSession(void*); + void deleteSession(double timeout = -1.0); + void runServer(); + CServerProxy* handshakeServer(IDataSocket*); + +private: + CMutex m_mutex; + CString m_name; + CSecondaryScreen* m_screen; + IScreenReceiver* m_server; + CNetworkAddress m_serverAddress; + ISecondaryScreenFactory* m_screenFactory; + ISocketFactory* m_socketFactory; + IStreamFilterFactory* m_streamFilterFactory; + CThread* m_session; + bool m_active; + bool m_rejected; + bool m_ownClipboard[kClipboardEnd]; + IClipboard::Time m_timeClipboard[kClipboardEnd]; + CString m_dataClipboard[kClipboardEnd]; + + // the status change jobs and status + CJobList m_statusJobs; + EStatus m_status; + CString m_statusMessage; +}; + +#endif diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp new file mode 100644 index 0000000..435cdc5 --- /dev/null +++ b/lib/client/CServerProxy.cpp @@ -0,0 +1,760 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CServerProxy.h" +#include "CProtocolUtil.h" +#include "IClient.h" +#include "OptionTypes.h" +#include "ProtocolTypes.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CLock.h" +#include "CLog.h" +#include "CStopwatch.h" +#include "XBase.h" +#include + +// +// CServerProxy +// + +CServerProxy::CServerProxy(IClient* client, + IInputStream* adoptedInput, IOutputStream* adoptedOutput) : + m_client(client), + m_input(adoptedInput), + m_output(adoptedOutput), + m_seqNum(0), + m_heartRate(kHeartRate) +{ + assert(m_client != NULL); + assert(m_input != NULL); + assert(m_output != NULL); + + // initialize modifier translation table + for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) + m_modifierTranslationTable[id] = id; +} + +CServerProxy::~CServerProxy() +{ + delete m_input; + delete m_output; +} + +bool +CServerProxy::mainLoop() +{ + bool failedToConnect = false; + try { + // no compressed mouse motion yet + m_compressMouse = false; + + // not ignoring mouse motions + m_ignoreMouse = false; + + // reset sequence number + m_seqNum = 0; + + // handle messages from server + CStopwatch heartbeat; + for (;;) { + // if no input is pending then flush compressed mouse motion + if (getInputStream()->getSize() == 0) { + flushCompressedMouse(); + } + + // wait for a message + LOG((CLOG_DEBUG2 "waiting for message")); + UInt8 code[4]; + UInt32 n = getInputStream()->read(code, 4, m_heartRate); + + // check if server hungup + if (n == 0) { + LOG((CLOG_NOTE "server disconnected")); + break; + } + + // check for time out + if (n == (UInt32)-1 || + (m_heartRate >= 0.0 && heartbeat.getTime() > m_heartRate)) { + // send heartbeat + CLock lock(&m_mutex); + CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + heartbeat.reset(); + if (n == (UInt32)-1) { + // no message to process + continue; + } + } + + // verify we got an entire code + if (n != 4) { + // client sent an incomplete message + LOG((CLOG_ERR "incomplete message from server")); + break; + } + + // parse message + LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); + if (memcmp(code, kMsgDMouseMove, 4) == 0) { + mouseMove(); + } + + else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { + mouseWheel(); + } + + else if (memcmp(code, kMsgDKeyDown, 4) == 0) { + keyDown(); + } + + else if (memcmp(code, kMsgDKeyUp, 4) == 0) { + keyUp(); + } + + else if (memcmp(code, kMsgDMouseDown, 4) == 0) { + mouseDown(); + } + + else if (memcmp(code, kMsgDMouseUp, 4) == 0) { + mouseUp(); + } + + else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { + keyRepeat(); + } + + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // accept and discard no-op + } + + else if (memcmp(code, kMsgCEnter, 4) == 0) { + enter(); + } + + else if (memcmp(code, kMsgCLeave, 4) == 0) { + leave(); + } + + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + grabClipboard(); + } + + else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { + screensaver(); + } + + else if (memcmp(code, kMsgQInfo, 4) == 0) { + queryInfo(); + } + + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + infoAcknowledgment(); + } + + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + setClipboard(); + } + + else if (memcmp(code, kMsgCResetOptions, 4) == 0) { + resetOptions(); + } + + else if (memcmp(code, kMsgDSetOptions, 4) == 0) { + setOptions(); + } + + else if (memcmp(code, kMsgCClose, 4) == 0) { + // server wants us to hangup + LOG((CLOG_DEBUG1 "recv close")); + break; + } + + else if (memcmp(code, kMsgEIncompatible, 4) == 0) { + SInt32 major, minor; + CProtocolUtil::readf(getInputStream(), + kMsgEIncompatible + 4, &major, &minor); + LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor)); + failedToConnect = true; + break; + } + + else if (memcmp(code, kMsgEBusy, 4) == 0) { + LOG((CLOG_ERR "server already has a connected client with name \"%s\"", getName().c_str())); + failedToConnect = true; + break; + } + + else if (memcmp(code, kMsgEUnknown, 4) == 0) { + LOG((CLOG_ERR "server refused client with name \"%s\"", getName().c_str())); + failedToConnect = true; + break; + } + + else if (memcmp(code, kMsgEBad, 4) == 0) { + LOG((CLOG_ERR "server disconnected due to a protocol error")); + failedToConnect = true; + break; + } + + else { + // unknown message + LOG((CLOG_ERR "unknown message from server")); + LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3])); + failedToConnect = true; + break; + } + } + } + catch (XBase& e) { + LOG((CLOG_ERR "error: %s", e.what())); + } + catch (...) { + throw; + } + + return !failedToConnect; +} + +IClient* +CServerProxy::getClient() const +{ + return m_client; +} + +CString +CServerProxy::getName() const +{ + return m_client->getName(); +} + +IInputStream* +CServerProxy::getInputStream() const +{ + return m_input; +} + +IOutputStream* +CServerProxy::getOutputStream() const +{ + return m_output; +} + +void +CServerProxy::onError() +{ + // ignore +} + +void +CServerProxy::onInfoChanged(const CClientInfo& info) +{ + // ignore mouse motion until we receive acknowledgment of our info + // change message. + CLock lock(&m_mutex); + m_ignoreMouse = true; + + // send info update + sendInfo(info); +} + +bool +CServerProxy::onGrabClipboard(ClipboardID id) +{ + LOG((CLOG_DEBUG1 "sending clipboard %d changed", id)); + CLock lock(&m_mutex); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, m_seqNum); + return true; +} + +void +CServerProxy::onClipboardChanged(ClipboardID id, const CString& data) +{ + CLock lock(&m_mutex); + LOG((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, m_seqNum, &data); +} + +void +CServerProxy::flushCompressedMouse() +{ + bool send = false; + SInt32 x = 0, y = 0; + { + CLock lock(&m_mutex); + if (m_compressMouse) { + m_compressMouse = false; + x = m_xMouse; + y = m_yMouse; + send = true; + } + } + + if (send) { + getClient()->mouseMove(x, y); + } +} + +void +CServerProxy::sendInfo(const CClientInfo& info) +{ + // note -- m_mutex should be locked on entry + LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); + CProtocolUtil::writef(getOutputStream(), kMsgDInfo, + info.m_x, info.m_y, + info.m_w, info.m_h, + info.m_zoneSize, + info.m_mx, info.m_my); +} + +KeyID +CServerProxy::translateKey(KeyID id) const +{ + static const KeyID s_translationTable[kKeyModifierIDLast][2] = { + { kKeyNone, kKeyNone }, + { kKeyShift_L, kKeyShift_R }, + { kKeyControl_L, kKeyControl_R }, + { kKeyAlt_L, kKeyAlt_R }, + { kKeyMeta_L, kKeyMeta_R }, + { kKeySuper_L, kKeySuper_R } + }; + + KeyModifierID id2 = kKeyModifierIDNull; + UInt32 side = 0; + switch (id) { + case kKeyShift_L: + id2 = kKeyModifierIDShift; + side = 0; + break; + + case kKeyShift_R: + id2 = kKeyModifierIDShift; + side = 1; + break; + + case kKeyControl_L: + id2 = kKeyModifierIDControl; + side = 0; + break; + + case kKeyControl_R: + id2 = kKeyModifierIDControl; + side = 1; + break; + + case kKeyAlt_L: + id2 = kKeyModifierIDAlt; + side = 0; + break; + + case kKeyAlt_R: + id2 = kKeyModifierIDAlt; + side = 1; + break; + + case kKeyMeta_L: + id2 = kKeyModifierIDMeta; + side = 0; + break; + + case kKeyMeta_R: + id2 = kKeyModifierIDMeta; + side = 1; + break; + + case kKeySuper_L: + id2 = kKeyModifierIDSuper; + side = 0; + break; + + case kKeySuper_R: + id2 = kKeyModifierIDSuper; + side = 1; + break; + } + + if (id2 != kKeyModifierIDNull) { + return s_translationTable[m_modifierTranslationTable[id2]][side]; + } + else { + return id; + } +} + +KeyModifierMask +CServerProxy::translateModifierMask(KeyModifierMask mask) const +{ + static const KeyModifierMask s_masks[kKeyModifierIDLast] = { + 0x0000, + KeyModifierShift, + KeyModifierControl, + KeyModifierAlt, + KeyModifierMeta, + KeyModifierSuper + }; + + KeyModifierMask newMask = mask & ~(KeyModifierShift | + KeyModifierControl | + KeyModifierAlt | + KeyModifierMeta | + KeyModifierSuper); + if ((mask & KeyModifierShift) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDShift]]; + } + if ((mask & KeyModifierControl) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDControl]]; + } + if ((mask & KeyModifierAlt) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDAlt]]; + } + if ((mask & KeyModifierMeta) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDMeta]]; + } + if ((mask & KeyModifierSuper) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDSuper]]; + } + return newMask; +} + +void +CServerProxy::enter() +{ + // parse + SInt16 x, y; + UInt16 mask; + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), + kMsgCEnter + 4, &x, &y, &seqNum, &mask); + LOG((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask)); + + // discard old compressed mouse motion, if any + { + CLock lock(&m_mutex); + m_compressMouse = false; + m_seqNum = seqNum; + } + + // forward + getClient()->enter(x, y, seqNum, static_cast(mask), false); +} + +void +CServerProxy::leave() +{ + // parse + LOG((CLOG_DEBUG1 "recv leave")); + + // send last mouse motion + flushCompressedMouse(); + + // forward + getClient()->leave(); +} + +void +CServerProxy::setClipboard() +{ + // parse + ClipboardID id; + UInt32 seqNum; + CString data; + CProtocolUtil::readf(getInputStream(), + kMsgDClipboard + 4, &id, &seqNum, &data); + LOG((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); + + // validate + if (id >= kClipboardEnd) { + return; + } + + // forward + getClient()->setClipboard(id, data); +} + +void +CServerProxy::grabClipboard() +{ + // parse + ClipboardID id; + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + LOG((CLOG_DEBUG "recv grab clipboard %d", id)); + + // validate + if (id >= kClipboardEnd) { + return; + } + + // forward + getClient()->grabClipboard(id); +} + +void +CServerProxy::keyDown() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask, button; + CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, + &id, &mask, &button); + LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); + + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( + static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + + // forward + getClient()->keyDown(id2, mask2, button); +} + +void +CServerProxy::keyRepeat() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask, count, button; + CProtocolUtil::readf(getInputStream(), kMsgDKeyRepeat + 4, + &id, &mask, &count, &button); + LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button)); + + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( + static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key repeat translated to id=%d, mask=0x%04x", id2, mask2)); + + // forward + getClient()->keyRepeat(id2, mask2, count, button); +} + +void +CServerProxy::keyUp() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask, button; + CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask, &button); + LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); + + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( + static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key up translated to id=%d, mask=0x%04x", id2, mask2)); + + // forward + getClient()->keyUp(id2, mask2, button); +} + +void +CServerProxy::mouseDown() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt8 id; + CProtocolUtil::readf(getInputStream(), kMsgDMouseDown + 4, &id); + LOG((CLOG_DEBUG1 "recv mouse down id=%d", id)); + + // forward + getClient()->mouseDown(static_cast(id)); +} + +void +CServerProxy::mouseUp() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt8 id; + CProtocolUtil::readf(getInputStream(), kMsgDMouseUp + 4, &id); + LOG((CLOG_DEBUG1 "recv mouse up id=%d", id)); + + // forward + getClient()->mouseUp(static_cast(id)); +} + +void +CServerProxy::mouseMove() +{ + // parse + bool ignore; + SInt16 x, y; + CProtocolUtil::readf(getInputStream(), kMsgDMouseMove + 4, &x, &y); + + { + // note if we should ignore the move + CLock lock(&m_mutex); + ignore = m_ignoreMouse; + + // compress mouse motion events if more input follows + if (!ignore && !m_compressMouse && getInputStream()->getSize() > 0) { + m_compressMouse = true; + } + + // if compressing then ignore the motion but record it + if (m_compressMouse) { + ignore = true; + m_xMouse = x; + m_yMouse = y; + } + } + LOG((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); + + // forward + if (!ignore) { + getClient()->mouseMove(x, y); + } +} + +void +CServerProxy::mouseWheel() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt16 delta; + CProtocolUtil::readf(getInputStream(), kMsgDMouseWheel + 4, &delta); + LOG((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); + + // forward + getClient()->mouseWheel(delta); +} + +void +CServerProxy::screensaver() +{ + // parse + SInt8 on; + CProtocolUtil::readf(getInputStream(), kMsgCScreenSaver + 4, &on); + LOG((CLOG_DEBUG1 "recv screen saver on=%d", on)); + + // forward + getClient()->screensaver(on != 0); +} + +void +CServerProxy::resetOptions() +{ + // parse + LOG((CLOG_DEBUG1 "recv reset options")); + + // forward + getClient()->resetOptions(); + + CLock lock(&m_mutex); + + // reset heart rate + m_heartRate = kHeartRate; + + // reset modifier translation table + for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) { + m_modifierTranslationTable[id] = id; + } + + // send heartbeat if necessary + if (m_heartRate >= 0.0) { + CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + } +} + +void +CServerProxy::setOptions() +{ + // parse + COptionsList options; + CProtocolUtil::readf(getInputStream(), kMsgDSetOptions + 4, &options); + LOG((CLOG_DEBUG1 "recv set options size=%d", options.size())); + + // forward + getClient()->setOptions(options); + + CLock lock(&m_mutex); + + // update modifier table + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + KeyModifierID id = kKeyModifierIDNull; + if (options[i] == kOptionModifierMapForShift) { + id = kKeyModifierIDShift; + } + else if (options[i] == kOptionModifierMapForControl) { + id = kKeyModifierIDControl; + } + else if (options[i] == kOptionModifierMapForAlt) { + id = kKeyModifierIDAlt; + } + else if (options[i] == kOptionModifierMapForMeta) { + id = kKeyModifierIDMeta; + } + else if (options[i] == kOptionModifierMapForSuper) { + id = kKeyModifierIDSuper; + } + else if (options[i] == kOptionHeartbeat) { + // update heart rate + m_heartRate = 1.0e-3 * static_cast(options[i + 1]); + + // send heartbeat if necessary + if (m_heartRate >= 0.0) { + CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + } + } + if (id != kKeyModifierIDNull) { + m_modifierTranslationTable[id] = + static_cast(options[i + 1]); + LOG((CLOG_DEBUG1 "modifier %d mapped to %d", id, m_modifierTranslationTable[id])); + } + } +} + +void +CServerProxy::queryInfo() +{ + // get current info + CClientInfo info; + getClient()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getClient()->getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = getClient()->getJumpZoneSize(); + + // send it + CLock lock(&m_mutex); + sendInfo(info); +} + +void +CServerProxy::infoAcknowledgment() +{ + // parse + LOG((CLOG_DEBUG1 "recv info acknowledgment")); + + // now allow mouse motion + CLock lock(&m_mutex); + m_ignoreMouse = false; +} diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h new file mode 100644 index 0000000..cbb58f1 --- /dev/null +++ b/lib/client/CServerProxy.h @@ -0,0 +1,136 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSERVERPROXY_H +#define CSERVERPROXY_H + +#include "IScreenReceiver.h" +#include "KeyTypes.h" +#include "CMutex.h" + +class IClient; +class IInputStream; +class IOutputStream; + +//! Proxy for server +/*! +This class acts a proxy for the server, converting calls into messages +to the server and messages from the server to calls on the client. +*/ +class CServerProxy : public IScreenReceiver { +public: + /*! \c adoptedInput is the stream from the server and + \c adoptedOutput is the stream to the server. This object + takes ownership of both and destroys them in the d'tor. + Messages from the server are converted to calls on \c client. + */ + CServerProxy(IClient* client, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CServerProxy(); + + //! @name manipulators + //@{ + + //! Run event loop + /*! + Run the event loop and return when the server disconnects or + requests the client to disconnect. Return true iff the server + didn't reject our connection. + + (cancellation point) + */ + bool mainLoop(); + + //@} + //! @name accessors + //@{ + + //! Get client + /*! + Returns the client passed to the c'tor. + */ + IClient* getClient() const; + + //! Get input stream + /*! + Return the input stream passed to the c'tor. + */ + IInputStream* getInputStream() const; + + //! Get output stream + /*! + Return the output stream passed to the c'tor. + */ + IOutputStream* getOutputStream() const; + + //@} + + // IScreenReceiver overrides + virtual void onError(); + virtual void onInfoChanged(const CClientInfo&); + virtual bool onGrabClipboard(ClipboardID); + virtual void onClipboardChanged(ClipboardID, const CString& data); + +private: + + // get the client name (from the client) + CString getName() const; + + // if compressing mouse motion then send the last motion now + void flushCompressedMouse(); + + void sendInfo(const CClientInfo&); + + // modifier key translation + KeyID translateKey(KeyID) const; + KeyModifierMask translateModifierMask(KeyModifierMask) const; + + // message handlers + void enter(); + void leave(); + void setClipboard(); + void grabClipboard(); + void keyDown(); + void keyRepeat(); + void keyUp(); + void mouseDown(); + void mouseUp(); + void mouseMove(); + void mouseWheel(); + void screensaver(); + void resetOptions(); + void setOptions(); + void queryInfo(); + void infoAcknowledgment(); + +private: + CMutex m_mutex; + + IClient* m_client; + IInputStream* m_input; + IOutputStream* m_output; + + UInt32 m_seqNum; + + bool m_compressMouse; + SInt32 m_xMouse, m_yMouse; + + bool m_ignoreMouse; + + KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; + double m_heartRate; +}; + +#endif diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am new file mode 100644 index 0000000..9d6ba46 --- /dev/null +++ b/lib/client/Makefile.am @@ -0,0 +1,42 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + client.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libclient.a +libclient_a_SOURCES = \ + CClient.cpp \ + CServerProxy.cpp \ + CClient.h \ + CServerProxy.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + $(NULL) diff --git a/lib/client/Makefile.in b/lib/client/Makefile.in new file mode 100644 index 0000000..3e6f4d8 --- /dev/null +++ b/lib/client/Makefile.in @@ -0,0 +1,342 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + client.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libclient.a +libclient_a_SOURCES = \ + CClient.cpp \ + CServerProxy.cpp \ + CClient.h \ + CServerProxy.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + $(NULL) + +subdir = lib/client +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libclient_a_AR = $(AR) cru +libclient_a_LIBADD = +am_libclient_a_OBJECTS = CClient.$(OBJEXT) CServerProxy.$(OBJEXT) +libclient_a_OBJECTS = $(am_libclient_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CClient.Po $(DEPDIR)/CServerProxy.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libclient_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libclient_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/client/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libclient.a: $(libclient_a_OBJECTS) $(libclient_a_DEPENDENCIES) + -rm -f libclient.a + $(libclient_a_AR) libclient.a $(libclient_a_OBJECTS) $(libclient_a_LIBADD) + $(RANLIB) libclient.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClient.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CServerProxy.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/client/client.dsp b/lib/client/client.dsp new file mode 100644 index 0000000..eb8e2e6 --- /dev/null +++ b/lib/client/client.dsp @@ -0,0 +1,114 @@ +# Microsoft Developer Studio Project File - Name="client" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=client - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "client.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "client.mak" CFG="client - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "client - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "client - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "client - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "client - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "client - Win32 Release" +# Name "client - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClient.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerProxy.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClient.h +# End Source File +# Begin Source File + +SOURCE=.\CServerProxy.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/common/BasicTypes.h b/lib/common/BasicTypes.h new file mode 100644 index 0000000..c717663 --- /dev/null +++ b/lib/common/BasicTypes.h @@ -0,0 +1,87 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef BASICTYPES_H +#define BASICTYPES_H + +#include "common.h" + +// +// pick types of particular sizes +// + +#if !defined(TYPE_OF_SIZE_1) +# if SIZEOF_CHAR == 1 +# define TYPE_OF_SIZE_1 char +# endif +#endif + +#if !defined(TYPE_OF_SIZE_2) +# if SIZEOF_INT == 2 +# define TYPE_OF_SIZE_2 int +# else +# define TYPE_OF_SIZE_2 short +# endif +#endif + +#if !defined(TYPE_OF_SIZE_4) +# if SIZEOF_INT == 4 +# define TYPE_OF_SIZE_4 int +# else +# define TYPE_OF_SIZE_4 long +# endif +#endif + +// +// verify existence of required types +// + +#if !defined(TYPE_OF_SIZE_1) +# error No 1 byte integer type +#endif +#if !defined(TYPE_OF_SIZE_2) +# error No 2 byte integer type +#endif +#if !defined(TYPE_OF_SIZE_4) +# error No 4 byte integer type +#endif + + +// +// make typedefs +// +// except for SInt8 and UInt8 these types are only guaranteed to be +// at least as big as indicated (in bits). that is, they may be +// larger than indicated. +// + +typedef signed TYPE_OF_SIZE_1 SInt8; +typedef signed TYPE_OF_SIZE_2 SInt16; +typedef signed TYPE_OF_SIZE_4 SInt32; + +typedef unsigned TYPE_OF_SIZE_1 UInt8; +typedef unsigned TYPE_OF_SIZE_2 UInt16; +typedef unsigned TYPE_OF_SIZE_4 UInt32; + +// +// clean up +// + +#undef TYPE_OF_SIZE_1 +#undef TYPE_OF_SIZE_2 +#undef TYPE_OF_SIZE_4 + +#endif + + diff --git a/lib/common/IInterface.h b/lib/common/IInterface.h new file mode 100644 index 0000000..97df31c --- /dev/null +++ b/lib/common/IInterface.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IINTERFACE_H +#define IINTERFACE_H + +#include "common.h" + +//! Base class of interfaces +/*! +This is the base class of all interface classes. An interface class has +only pure virtual methods. +*/ +class IInterface { +public: + //! Interface destructor does nothing + virtual ~IInterface() { } +}; + +#endif diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am new file mode 100644 index 0000000..cfaafeb --- /dev/null +++ b/lib/common/Makefile.am @@ -0,0 +1,44 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + common.dsp \ + BasicTypes.h \ + IInterface.h \ + Version.h \ + common.h \ + stdbitset.h \ + stddeque.h \ + stdfstream.h \ + stdistream.h \ + stdlist.h \ + stdmap.h \ + stdostream.h \ + stdpost.h \ + stdpre.h \ + stdset.h \ + stdsstream.h \ + stdstring.h \ + stdvector.h \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +INCLUDES = \ + $(NULL) diff --git a/lib/common/Makefile.in b/lib/common/Makefile.in new file mode 100644 index 0000000..8925b7c --- /dev/null +++ b/lib/common/Makefile.in @@ -0,0 +1,248 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + common.dsp \ + BasicTypes.h \ + IInterface.h \ + Version.h \ + common.h \ + stdbitset.h \ + stddeque.h \ + stdfstream.h \ + stdistream.h \ + stdlist.h \ + stdmap.h \ + stdostream.h \ + stdpost.h \ + stdpre.h \ + stdset.h \ + stdsstream.h \ + stdstring.h \ + stdvector.h \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +INCLUDES = \ + $(NULL) + +subdir = lib/common +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = +DIST_COMMON = Makefile.am Makefile.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/common/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: +tags: TAGS +TAGS: + + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/common/Version.h b/lib/common/Version.h new file mode 100644 index 0000000..d7a67b1 --- /dev/null +++ b/lib/common/Version.h @@ -0,0 +1,45 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef VERSION_H +#define VERSION_H + +#include "common.h" + +// set version macro if not set yet +#if !defined(VERSION) +# define VERSION "1.0.14" +#endif + +// important strings +static const char* kApplication = "synergy"; +static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; +static const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; +static const char* kWebsite = "http://synergy2.sourceforge.net/"; + +// build version. follows linux kernel style: an even minor number implies +// a release version, odd implies development version. +static const char* kVersion = VERSION; + +// application version +static const char* kAppVersion = "synergy " VERSION; + +// exit codes +static const int kExitSuccess = 0; // successful completion +static const int kExitFailed = 1; // general failure +static const int kExitTerminated = 2; // killed by signal +static const int kExitArgs = 3; // bad arguments +static const int kExitConfig = 4; // cannot read configuration + +#endif diff --git a/lib/common/common.dsp b/lib/common/common.dsp new file mode 100644 index 0000000..ca7f513 --- /dev/null +++ b/lib/common/common.dsp @@ -0,0 +1,162 @@ +# Microsoft Developer Studio Project File - Name="common" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=common - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "common.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "common.mak" CFG="common - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "common - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "common - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "common - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "common - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "common - Win32 Release" +# Name "common - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\BasicTypes.h +# End Source File +# Begin Source File + +SOURCE=.\common.h +# End Source File +# Begin Source File + +SOURCE=.\IInterface.h +# End Source File +# Begin Source File + +SOURCE=.\stdbitset.h +# End Source File +# Begin Source File + +SOURCE=.\stddeque.h +# End Source File +# Begin Source File + +SOURCE=.\stdfstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdistream.h +# End Source File +# Begin Source File + +SOURCE=.\stdlist.h +# End Source File +# Begin Source File + +SOURCE=.\stdmap.h +# End Source File +# Begin Source File + +SOURCE=.\stdostream.h +# End Source File +# Begin Source File + +SOURCE=.\stdpost.h +# End Source File +# Begin Source File + +SOURCE=.\stdpre.h +# End Source File +# Begin Source File + +SOURCE=.\stdset.h +# End Source File +# Begin Source File + +SOURCE=.\stdsstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdstring.h +# End Source File +# Begin Source File + +SOURCE=.\stdvector.h +# End Source File +# Begin Source File + +SOURCE=.\Version.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/common/common.h b/lib/common/common.h new file mode 100644 index 0000000..74b8f1d --- /dev/null +++ b/lib/common/common.h @@ -0,0 +1,74 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COMMON_H +#define COMMON_H + +// this file should be included, directly or indirectly by every other. + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +// check if win32 platform +#if defined(_WIN32) +# define WINDOWS_LIKE 1 + + // VC++ specific +# if (_MSC_VER >= 1200) + // work around for statement scoping bug +# define for if (false) { } else for + + // turn off bonehead warnings +# pragma warning(disable: 4786) // identifier truncated in debug info +# pragma warning(disable: 4514) // unreferenced inline function removed + + // this one's a little too aggressive +# pragma warning(disable: 4127) // conditional expression is constant + + // emitted incorrectly under release build in some circumstances +# if defined(NDEBUG) +# pragma warning(disable: 4702) // unreachable code +# pragma warning(disable: 4701) // variable maybe used uninitialized +# endif + +# endif // (_MSC_VER >= 1200) + + // VC++ has built-in sized types +# if defined(_MSC_VER) +# define TYPE_OF_SIZE_1 __int8 +# define TYPE_OF_SIZE_2 __int16 +# define TYPE_OF_SIZE_4 __int32 +# else +# define SIZE_OF_CHAR 1 +# define SIZE_OF_SHORT 2 +# define SIZE_OF_INT 4 +# define SIZE_OF_LONG 4 +# endif +#endif // defined(_WIN32) + +// unix-like if not like anything else +#if (!defined(WINDOWS_LIKE) || WINDOWS_LIKE == 0) +# define UNIX_LIKE 1 +#endif + +// define NULL +#ifndef NULL +#define NULL 0 +#endif + +// make assert available since we use it a lot +#include + +#endif diff --git a/lib/common/stdbitset.h b/lib/common/stdbitset.h new file mode 100644 index 0000000..529772c --- /dev/null +++ b/lib/common/stdbitset.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stddeque.h b/lib/common/stddeque.h new file mode 100644 index 0000000..06bdafd --- /dev/null +++ b/lib/common/stddeque.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stdfstream.h b/lib/common/stdfstream.h new file mode 100644 index 0000000..9d1f5aa --- /dev/null +++ b/lib/common/stdfstream.h @@ -0,0 +1,18 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" +#include "stdistream.h" diff --git a/lib/common/stdistream.h b/lib/common/stdistream.h new file mode 100644 index 0000000..bd90357 --- /dev/null +++ b/lib/common/stdistream.h @@ -0,0 +1,43 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#if defined(HAVE_ISTREAM) +#include +#else +#include +#endif +#include "stdpost.h" + +#if defined(_MSC_VER) && _MSC_VER <= 1200 +// VC++6 istream has no overloads for __int* types, .NET does +inline +std::istream& operator>>(std::istream& s, SInt8& i) +{ return s >> (signed char&)i; } +inline +std::istream& operator>>(std::istream& s, SInt16& i) +{ return s >> (short&)i; } +inline +std::istream& operator>>(std::istream& s, SInt32& i) +{ return s >> (int&)i; } +inline +std::istream& operator>>(std::istream& s, UInt8& i) +{ return s >> (unsigned char&)i; } +inline +std::istream& operator>>(std::istream& s, UInt16& i) +{ return s >> (unsigned short&)i; } +inline +std::istream& operator>>(std::istream& s, UInt32& i) +{ return s >> (unsigned int&)i; } +#endif diff --git a/lib/common/stdlist.h b/lib/common/stdlist.h new file mode 100644 index 0000000..6f0df8e --- /dev/null +++ b/lib/common/stdlist.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stdmap.h b/lib/common/stdmap.h new file mode 100644 index 0000000..da50161 --- /dev/null +++ b/lib/common/stdmap.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stdostream.h b/lib/common/stdostream.h new file mode 100644 index 0000000..e7496f5 --- /dev/null +++ b/lib/common/stdostream.h @@ -0,0 +1,21 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#if defined(HAVE_OSTREAM) +#include +#else +#include +#endif +#include "stdpost.h" diff --git a/lib/common/stdpost.h b/lib/common/stdpost.h new file mode 100644 index 0000000..0f06b74 --- /dev/null +++ b/lib/common/stdpost.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/lib/common/stdpre.h b/lib/common/stdpre.h new file mode 100644 index 0000000..10fa7d5 --- /dev/null +++ b/lib/common/stdpre.h @@ -0,0 +1,27 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if defined(_MSC_VER) +#pragma warning(disable: 4786) // identifier truncated +#pragma warning(disable: 4514) // unreferenced inline +#pragma warning(disable: 4710) // not inlined +#pragma warning(disable: 4663) // C++ change, template specialization +#pragma warning(disable: 4503) // decorated name length too long +#pragma warning(push, 3) +#pragma warning(disable: 4018) // signed/unsigned mismatch +#pragma warning(disable: 4284) +#pragma warning(disable: 4146) // unary minus on unsigned value +#pragma warning(disable: 4127) // conditional expression is constant +#pragma warning(disable: 4701) // variable possibly used uninitialized +#endif diff --git a/lib/common/stdset.h b/lib/common/stdset.h new file mode 100644 index 0000000..aeb491b --- /dev/null +++ b/lib/common/stdset.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stdsstream.h b/lib/common/stdsstream.h new file mode 100644 index 0000000..ed8e723 --- /dev/null +++ b/lib/common/stdsstream.h @@ -0,0 +1,371 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" + +#if defined(HAVE_SSTREAM) || !defined(__GNUC__) || (__GNUC__ >= 3) + +#include + +#elif defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 95) +// g++ 2.95 didn't ship with sstream. the following is a backport +// by Magnus Fromreide of the sstream in g++ 3.0. + +/* This is part of libio/iostream, providing -*- C++ -*- input/output. +Copyright (C) 2000 Free Software Foundation + +This file is part of the GNU IO Library. This library 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 library is distributed in the hope that it will be useful, +but WITHOUT 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 library; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +As a special exception, if you link this library with files +compiled with a GNU compiler to produce an executable, this does not cause +the resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why +the executable file might be covered by the GNU General Public License. */ + +/* Written by Magnus Fromreide (magfr@lysator.liu.se). */ +/* seekoff and ideas for overflow is largely borrowed from libstdc++-v3 */ + +#include +#include +#include + +namespace std +{ + class stringbuf : public streambuf + { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + stringbuf(int which=ios::in|ios::out) + : streambuf(), mode(static_cast(which)), + stream(NULL), stream_len(0) + { + stringbuf_init(); + } + + explicit + stringbuf(const string &str, int which=ios::in|ios::out) + : streambuf(), mode(static_cast(which)), + stream(NULL), stream_len(0) + { + if (mode & (ios::in|ios::out)) + { + stream_len = str.size(); + stream = new char_type[stream_len]; + str.copy(stream, stream_len); + } + stringbuf_init(); + } + + virtual + ~stringbuf() + { + delete[] stream; + } + + string + str() const + { + if (pbase() != 0) + return string(stream, pptr()-pbase()); + else + return string(); + } + + void + str(const string& str) + { + delete[] stream; + stream_len = str.size(); + stream = new char_type[stream_len]; + str.copy(stream, stream_len); + stringbuf_init(); + } + + protected: + // The buffer is already in gptr, so if it ends then it is out of data. + virtual int + underflow() + { + return EOF; + } + + virtual int + overflow(int c = EOF) + { + int res; + if (mode & ios::out) + { + if (c != EOF) + { + streamsize old_stream_len = stream_len; + stream_len += 1; + char_type* new_stream = new char_type[stream_len]; + memcpy(new_stream, stream, old_stream_len); + delete[] stream; + stream = new_stream; + stringbuf_sync(gptr()-eback(), pptr()-pbase()); + sputc(c); + res = c; + } + else + res = EOF; + } + else + res = 0; + return res; + } + + virtual streambuf* + setbuf(char_type* s, streamsize n) + { + if (n != 0) + { + delete[] stream; + stream = new char_type[n]; + memcpy(stream, s, n); + stream_len = n; + stringbuf_sync(0, 0); + } + return this; + } + + virtual pos_type + seekoff(off_type off, ios::seek_dir way, int which = ios::in | ios::out) + { + pos_type ret = pos_type(off_type(-1)); + bool testin = which & ios::in && mode & ios::in; + bool testout = which & ios::out && mode & ios::out; + bool testboth = testin && testout && way != ios::cur; + + if (stream_len && ((testin != testout) || testboth)) + { + char_type* beg = stream; + char_type* curi = NULL; + char_type* curo = NULL; + char_type* endi = NULL; + char_type* endo = NULL; + + if (testin) + { + curi = gptr(); + endi = egptr(); + } + if (testout) + { + curo = pptr(); + endo = epptr(); + } + + off_type newoffi = 0; + off_type newoffo = 0; + if (way == ios::beg) + { + newoffi = beg - curi; + newoffo = beg - curo; + } + else if (way == ios::end) + { + newoffi = endi - curi; + newoffo = endo - curo; + } + + if (testin && newoffi + off + curi - beg >= 0 && + endi - beg >= newoffi + off + curi - beg) + { + gbump(newoffi + off); + ret = pos_type(newoffi + off + curi); + } + if (testout && newoffo + off + curo - beg >= 0 && + endo - beg >= newoffo + off + curo - beg) + { + pbump(newoffo + off); + ret = pos_type(newoffo + off + curo); + } + } + return ret; + } + + virtual pos_type + seekpos(pos_type sp, int which = ios::in | ios::out) + { + pos_type ret = seekoff(sp, ios::beg, which); + return ret; + } + + private: + void + stringbuf_sync(streamsize i, streamsize o) + { + if (mode & ios::in) + setg(stream, stream + i, stream + stream_len); + if (mode & ios::out) + { + setp(stream, stream + stream_len); + pbump(o); + } + } + void + stringbuf_init() + { + if (mode & ios::ate) + stringbuf_sync(0, stream_len); + else + stringbuf_sync(0, 0); + } + + private: + ios::open_mode mode; + char_type* stream; + streamsize stream_len; + }; + + class istringstream : public istream { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + istringstream(int which=ios::in) + : istream(&sb), sb(which | ios::in) + { } + + explicit + istringstream(const string& str, int which=ios::in) + : istream(&sb), sb(str, which | ios::in) + { } + + stringbuf* + rdbuf() const + { + return const_cast(&sb); + } + + string + str() const + { + return rdbuf()->str(); + } + void + str(const string& s) + { + rdbuf()->str(s); + } + private: + stringbuf sb; + }; + + class ostringstream : public ostream { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + ostringstream(int which=ios::out) + : ostream(&sb), sb(which | ios::out) + { } + + explicit + ostringstream(const string& str, int which=ios::out) + : ostream(&sb), sb(str, which | ios::out) + { } + + stringbuf* + rdbuf() const + { + return const_cast(&sb); + } + + string + str() const + { + return rdbuf()->str(); + } + + void str(const string& s) + { + rdbuf()->str(s); + } + private: + stringbuf sb; + }; + + class stringstream : public iostream { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + stringstream(int which=ios::out|ios::in) + : iostream(&sb), sb(which) + { } + + explicit + stringstream(const string& str, int which=ios::out|ios::in) + : iostream(&sb), sb(str, which) + { } + + stringbuf* + rdbuf() const + { + return const_cast(&sb); + } + + string + str() const + { + return rdbuf()->str(); + } + + void + str(const string& s) + { + rdbuf()->str(s); + } + private: + stringbuf sb; + }; +}; + +#else /* not g++ 2.95 and no */ + +#error "Standard C++ library is missing required sstream header." + +#endif /* not g++ 2.95 and no */ + +#include "stdpost.h" +#include "stdistream.h" diff --git a/lib/common/stdstring.h b/lib/common/stdstring.h new file mode 100644 index 0000000..3d83c03 --- /dev/null +++ b/lib/common/stdstring.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stdvector.h b/lib/common/stdvector.h new file mode 100644 index 0000000..1056bb4 --- /dev/null +++ b/lib/common/stdvector.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/http/CHTTPProtocol.cpp b/lib/http/CHTTPProtocol.cpp new file mode 100644 index 0000000..ae385d6 --- /dev/null +++ b/lib/http/CHTTPProtocol.cpp @@ -0,0 +1,658 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CHTTPProtocol.h" +#include "XHTTP.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CLog.h" +#include "stdsstream.h" +#include +#include +#include + +// +// CHTTPRequest +// + +CHTTPRequest::CHTTPRequest() +{ + // do nothing +} + +CHTTPRequest::~CHTTPRequest() +{ + // do nothing +} + +void +CHTTPRequest::insertHeader(const CString& name, const CString& value) +{ + CHeaderMap::iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + index->second->second = value; + } + else { + CHeaderList::iterator pos = m_headers.insert( + m_headers.end(), std::make_pair(name, value)); + m_headerByName.insert(std::make_pair(name, pos)); + } +} + +void +CHTTPRequest::appendHeader(const CString& name, const CString& value) +{ + CHeaderMap::iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + index->second->second += ","; + index->second->second += value; + } + else { + CHeaderList::iterator pos = m_headers.insert( + m_headers.end(), std::make_pair(name, value)); + m_headerByName.insert(std::make_pair(name, pos)); + } +} + +void +CHTTPRequest::eraseHeader(const CString& name) +{ + CHeaderMap::iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + m_headers.erase(index->second); + } +} + +bool +CHTTPRequest::isHeader(const CString& name) const +{ + return (m_headerByName.find(name) != m_headerByName.end()); +} + +CString +CHTTPRequest::getHeader(const CString& name) const +{ + CHeaderMap::const_iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + return index->second->second; + } + else { + return CString(); + } +} + + +// +// CHTTPProtocol +// + +CHTTPRequest* +CHTTPProtocol::readRequest(IInputStream* stream, UInt32 maxSize) +{ + CString scratch; + + // note if we should limit the request size + const bool checkSize = (maxSize > 0); + + // parse request line by line + CHTTPRequest* request = new CHTTPRequest; + try { + CString line; + + // read request line. accept and discard leading empty lines. + do { + line = readLine(stream, scratch); + if (checkSize) { + if (line.size() + 2 > maxSize) { + throw XHTTP(413); + } + maxSize -= line.size() + 2; + } + } while (line.empty()); + + // parse request line: + { + std::istringstream s(line); + s.exceptions(std::ios::goodbit); + CString version; + s >> request->m_method >> request->m_uri >> version; + if (!s || request->m_uri.empty() || version.find("HTTP/") != 0) { + LOG((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); + throw XHTTP(400); + } + + // parse version + char dot; + s.str(version); + s.clear(); + s.ignore(5); + s >> request->m_majorVersion; + s.get(dot); + s >> request->m_minorVersion; + if (!s || dot != '.') { + LOG((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); + throw XHTTP(400); + } + } + if (!isValidToken(request->m_method)) { + LOG((CLOG_DEBUG1 "invalid HTTP method: %s", line.c_str())); + throw XHTTP(400); + } + if (request->m_majorVersion < 1 || request->m_minorVersion < 0) { + LOG((CLOG_DEBUG1 "invalid HTTP version: %s", line.c_str())); + throw XHTTP(400); + } + + // parse headers + readHeaders(stream, request, false, scratch, + checkSize ? &maxSize : NULL); + + // HTTP/1.1 requests must have a Host header + if (request->m_majorVersion > 1 || + (request->m_majorVersion == 1 && request->m_minorVersion >= 1)) { + if (request->isHeader("Host") == 0) { + LOG((CLOG_DEBUG1 "Host header missing")); + throw XHTTP(400); + } + } + + // some methods may not have a body. ensure that the headers + // that indicate the body length do not exist for those methods + // and do exist for others. + if ((request->isHeader("Transfer-Encoding") || + request->isHeader("Content-Length")) == + (request->m_method == "GET" || + request->m_method == "HEAD")) { + LOG((CLOG_DEBUG1 "HTTP method (%s)/body mismatch", request->m_method.c_str())); + throw XHTTP(400); + } + + // prepare to read the body. the length of the body is + // determined using, in order: + // 1. Transfer-Encoding indicates a "chunked" transfer + // 2. Content-Length is present + // Content-Length is ignored for "chunked" transfers. + CString header; + if (!(header = request->getHeader("Transfer-Encoding")).empty()) { + // we only understand "chunked" encodings + if (!CStringUtil::CaselessCmp::equal(header, "chunked")) { + LOG((CLOG_DEBUG1 "unsupported Transfer-Encoding %s", header.c_str())); + throw XHTTP(501); + } + + // chunked encoding + UInt32 oldSize; + do { + oldSize = request->m_body.size(); + request->m_body += readChunk(stream, scratch, + checkSize ? &maxSize : NULL); + } while (request->m_body.size() != oldSize); + + // read footer + readHeaders(stream, request, true, scratch, + checkSize ? &maxSize : NULL); + + // remove "chunked" from Transfer-Encoding and set the + // Content-Length. + std::ostringstream s; + s << std::dec << request->m_body.size(); + request->eraseHeader("Transfer-Encoding"); + request->insertHeader("Content-Length", s.str()); + } + else if (!(header = request->getHeader("Content-Length")).empty()) { + // parse content-length + UInt32 length; + { + std::istringstream s(header); + s.exceptions(std::ios::goodbit); + s >> length; + if (!s) { + LOG((CLOG_DEBUG1 "cannot parse Content-Length", header.c_str())); + throw XHTTP(400); + } + } + + // check against expected size + if (checkSize && length > maxSize) { + throw XHTTP(413); + } + + // use content length + request->m_body = readBlock(stream, length, scratch); + if (request->m_body.size() != length) { + // length must match size of body + LOG((CLOG_DEBUG1 "Content-Length/actual length mismatch (%d vs %d)", length, request->m_body.size())); + throw XHTTP(400); + } + } + } + catch (...) { + delete request; + throw; + } + + return request; +} + +void +CHTTPProtocol::reply(IOutputStream* stream, CHTTPReply& reply) +{ + // suppress body for certain replies + bool hasBody = true; + if ((reply.m_status / 100) == 1 || + reply.m_status == 204 || + reply.m_status == 304) { + hasBody = false; + } + + // adjust headers + for (CHTTPReply::CHeaderList::iterator + index = reply.m_headers.begin(); + index != reply.m_headers.end(); ) { + const CString& header = index->first; + + // remove certain headers + if (CStringUtil::CaselessCmp::equal(header, "Content-Length") || + CStringUtil::CaselessCmp::equal(header, "Date") || + CStringUtil::CaselessCmp::equal(header, "Transfer-Encoding")) { + // FIXME -- Transfer-Encoding should be left as-is if + // not "chunked" and if the version is 1.1 or up. + index = reply.m_headers.erase(index); + } + + // keep as-is + else { + ++index; + } + } + + // write reply header + std::ostringstream s; + s << "HTTP/" << reply.m_majorVersion << "." << + reply.m_minorVersion << " " << + reply.m_status << " " << + reply.m_reason << "\r\n"; + + // get date + // FIXME -- should use C++ locale stuff but VC++ time_put is broken. + // FIXME -- double check that VC++ is broken + char date[30]; + { + const char* oldLocale = setlocale(LC_TIME, "C"); + time_t t = time(NULL); +#if HAVE_GMTIME_R + struct tm tm; + struct tm* tmp = &tm; + gmtime_r(&t, tmp); +#else + struct tm* tmp = gmtime(&t); +#endif + strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", tmp); + setlocale(LC_TIME, oldLocale); + } + + // write headers + s << "Date: " << date << "\r\n"; + for (CHTTPReply::CHeaderList::const_iterator + index = reply.m_headers.begin(); + index != reply.m_headers.end(); ++index) { + s << index->first << ": " << index->second << "\r\n"; + } + if (hasBody) { + s << "Content-Length: " << reply.m_body.size() << "\r\n"; + } + s << "Connection: close\r\n"; + + // write end of headers + s << "\r\n"; + + // write to stream + stream->write(s.str().data(), s.str().size()); + + // write body. replies to HEAD method never have a body (though + // they do have the Content-Length header). + if (hasBody && reply.m_method != "HEAD") { + stream->write(reply.m_body.data(), reply.m_body.size()); + } +} + +bool +CHTTPProtocol::parseFormData(const CHTTPRequest& request, CFormParts& parts) +{ + static const char formData[] = "multipart/form-data"; + static const char boundary[] = "boundary="; + static const char disposition[] = "Content-Disposition:"; + static const char nameAttr[] = "name="; + static const char quote[] = "\""; + + // find the Content-Type header + const CString contentType = request.getHeader("Content-Type"); + if (contentType.empty()) { + // missing required Content-Type header + return false; + } + + // parse type + CString::const_iterator index = std::search( + contentType.begin(), contentType.end(), + formData, formData + sizeof(formData) - 1, + CStringUtil::CaselessCmp::cmpEqual); + if (index == contentType.end()) { + // not form-data + return false; + } + index += sizeof(formData) - 1; + index = std::search(index, contentType.end(), + boundary, boundary + sizeof(boundary) - 1, + CStringUtil::CaselessCmp::cmpEqual); + if (index == contentType.end()) { + // no boundary + return false; + } + CString delimiter = contentType.c_str() + + (index - contentType.begin()) + + sizeof(boundary) - 1; + + // find first delimiter + const CString& body = request.m_body; + CString::size_type partIndex = body.find(delimiter); + if (partIndex == CString::npos) { + return false; + } + + // skip over it + partIndex += delimiter.size(); + + // prepend CRLF-- to delimiter + delimiter = "\r\n--" + delimiter; + + // parse parts until there are no more + for (;;) { + // is it the last part? + if (body.size() >= partIndex + 2 && + body[partIndex ] == '-' && + body[partIndex + 1] == '-') { + // found last part. ignore trailing data, if any. + return true; + } + + // find the end of this part + CString::size_type nextPart = body.find(delimiter, partIndex); + if (nextPart == CString::npos) { + // no terminator + return false; + } + + // find end of headers + CString::size_type endOfHeaders = body.find("\r\n\r\n", partIndex); + if (endOfHeaders == CString::npos || endOfHeaders > nextPart) { + // bad part + return false; + } + endOfHeaders += 2; + + // now find Content-Disposition + index = std::search(body.begin() + partIndex, + body.begin() + endOfHeaders, + disposition, + disposition + sizeof(disposition) - 1, + CStringUtil::CaselessCmp::cmpEqual); + if (index == contentType.begin() + endOfHeaders) { + // bad part + return false; + } + + // find the name in the Content-Disposition + CString::size_type endOfHeader = body.find("\r\n", + index - body.begin()); + if (endOfHeader >= endOfHeaders) { + // bad part + return false; + } + index = std::search(index, body.begin() + endOfHeader, + nameAttr, nameAttr + sizeof(nameAttr) - 1, + CStringUtil::CaselessCmp::cmpEqual); + if (index == body.begin() + endOfHeader) { + // no name + return false; + } + + // extract the name + CString name; + index += sizeof(nameAttr) - 1; + if (*index == quote[0]) { + // quoted name + ++index; + CString::size_type namePos = index - body.begin(); + index = std::search(index, body.begin() + endOfHeader, + quote, quote + 1, + CStringUtil::CaselessCmp::cmpEqual); + if (index == body.begin() + endOfHeader) { + // missing close quote + return false; + } + name = body.substr(namePos, index - body.begin() - namePos); + } + else { + // unquoted name + name = body.substr(index - body.begin(), + body.find_first_of(" \t\r\n")); + } + + // save part. add 2 to endOfHeaders to skip CRLF. + parts.insert(std::make_pair(name, body.substr(endOfHeaders + 2, + nextPart - (endOfHeaders + 2)))); + + // move to next part + partIndex = nextPart + delimiter.size(); + } + + // should've found the last delimiter inside the loop but we did not + return false; +} + +CString +CHTTPProtocol::readLine(IInputStream* stream, CString& tmpBuffer) +{ + // read up to and including a CRLF from stream, using whatever + // is in tmpBuffer as if it were at the head of the stream. + + for (;;) { + // scan tmpBuffer for CRLF + CString::size_type newline = tmpBuffer.find("\r\n"); + if (newline != CString::npos) { + // copy line without the CRLF + CString line = tmpBuffer.substr(0, newline); + + // discard line and CRLF from tmpBuffer + tmpBuffer.erase(0, newline + 2); + return line; + } + + // read more from stream + char buffer[4096]; + UInt32 n = stream->read(buffer, sizeof(buffer), -1.0); + if (n == 0) { + // stream is empty. return what's leftover. + CString line = tmpBuffer; + tmpBuffer.erase(); + return line; + } + + // append stream data + tmpBuffer.append(buffer, n); + } +} + +CString +CHTTPProtocol::readBlock(IInputStream* stream, + UInt32 numBytes, CString& tmpBuffer) +{ + CString data; + + // read numBytes from stream, using whatever is in tmpBuffer as + // if it were at the head of the stream. + if (tmpBuffer.size() > 0) { + // ignore stream if there's enough data in tmpBuffer + if (tmpBuffer.size() >= numBytes) { + data = tmpBuffer.substr(0, numBytes); + tmpBuffer.erase(0, numBytes); + return data; + } + + // move everything out of tmpBuffer into data + data = tmpBuffer; + tmpBuffer.erase(); + } + + // account for bytes read so far + assert(data.size() < numBytes); + numBytes -= data.size(); + + // read until we have all the requested data + while (numBytes > 0) { + // read max(4096, bytes_left) bytes into buffer + char buffer[4096]; + UInt32 n = sizeof(buffer); + if (n > numBytes) { + n = numBytes; + } + n = stream->read(buffer, n, -1.0); + + // if stream is empty then return what we've got so far + if (n == 0) { + break; + } + + // append stream data + data.append(buffer, n); + numBytes -= n; + } + + return data; +} + +CString +CHTTPProtocol::readChunk(IInputStream* stream, + CString& tmpBuffer, UInt32* maxSize) +{ + CString line; + + // get chunk header + line = readLine(stream, tmpBuffer); + + // parse chunk size + UInt32 size; + { + std::istringstream s(line); + s.exceptions(std::ios::goodbit); + s >> std::hex >> size; + if (!s) { + LOG((CLOG_DEBUG1 "cannot parse chunk size", line.c_str())); + throw XHTTP(400); + } + } + if (size == 0) { + return CString(); + } + + // check size + if (maxSize != NULL) { + if (line.size() + 2 + size + 2 > *maxSize) { + throw XHTTP(413); + } + maxSize -= line.size() + 2 + size + 2; + } + + // read size bytes + CString data = readBlock(stream, size, tmpBuffer); + if (data.size() != size) { + LOG((CLOG_DEBUG1 "expected/actual chunk size mismatch", size, data.size())); + throw XHTTP(400); + } + + // read an discard CRLF + line = readLine(stream, tmpBuffer); + if (!line.empty()) { + LOG((CLOG_DEBUG1 "missing CRLF after chunk")); + throw XHTTP(400); + } + + return data; +} + +void +CHTTPProtocol::readHeaders(IInputStream* stream, + CHTTPRequest* request, bool isFooter, + CString& tmpBuffer, UInt32* maxSize) +{ + // parse headers. done with headers when we get a blank line. + CString name; + CString line = readLine(stream, tmpBuffer); + while (!line.empty()) { + // check size + if (maxSize != NULL) { + if (line.size() + 2 > *maxSize) { + throw XHTTP(413); + } + *maxSize -= line.size() + 2; + } + + // if line starts with space or tab then append it to the + // previous header. if there is no previous header then + // throw. + if (line[0] == ' ' || line[0] == '\t') { + if (name.empty()) { + LOG((CLOG_DEBUG1 "first header is a continuation")); + throw XHTTP(400); + } + request->appendHeader(name, line); + } + + // line should have the form: :[] + else { + // parse + CString value; + std::istringstream s(line); + s.exceptions(std::ios::goodbit); + std::getline(s, name, ':'); + if (!s || !isValidToken(name)) { + LOG((CLOG_DEBUG1 "invalid header: %s", line.c_str())); + throw XHTTP(400); + } + std::getline(s, value); + + // check validity of name + if (isFooter) { + // FIXME -- only certain names are allowed in footers + // but which ones? + } + + request->appendHeader(name, value); + } + + // next header + line = readLine(stream, tmpBuffer); + } +} + +bool +CHTTPProtocol::isValidToken(const CString& token) +{ + return (token.find("()<>@,;:\\\"/[]?={} " + "\0\1\2\3\4\5\6\7" + "\10\11\12\13\14\15\16\17" + "\20\21\22\23\24\25\26\27" + "\30\31\32\33\34\35\36\37\177") == CString::npos); +} diff --git a/lib/http/CHTTPProtocol.h b/lib/http/CHTTPProtocol.h new file mode 100644 index 0000000..8c86645 --- /dev/null +++ b/lib/http/CHTTPProtocol.h @@ -0,0 +1,201 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CHTTPPROTOCOL_H +#define CHTTPPROTOCOL_H + +#include "CString.h" +#include "CStringUtil.h" +#include "BasicTypes.h" +#include "stdlist.h" +#include "stdmap.h" +#include "stdvector.h" + +class IInputStream; +class IOutputStream; + +//! HTTP request type +/*! +This class encapsulates an HTTP request. +*/ +class CHTTPRequest { +private: + typedef std::list > CHeaderList; +public: + //! Iterator on headers + /*! + An iterator on the headers. Each element is a std::pair; first is + the header name as a CString, second is the header value as a CString. + */ + typedef CHeaderList::const_iterator const_iterator; + + CHTTPRequest(); + ~CHTTPRequest(); + + //! @name manipulators + //@{ + + //! Insert header + /*! + Add a header by name replacing the existing header, if any. + Headers are sent in the order they're inserted. Replacing + a header does not change its original position in the order. + */ + void insertHeader(const CString& name, const CString& value); + + //! Append header + /*! + Append a header. Equivalent to insertHeader() if the header + doesn't exist, otherwise it appends a comma and the value to + the existing header. + */ + void appendHeader(const CString& name, const CString& value); + + //! Remove header + /*! + Remove a header by name. Does nothing if the header doesn't exist. + */ + void eraseHeader(const CString& name); + + //@} + //! @name accessors + //@{ + + //! Check header existence + /*! + Returns true iff the header exists. + */ + bool isHeader(const CString& name) const; + + //! Get header + /*! + Get a header by name. Returns the empty string if the header + doesn't exist. + */ + CString getHeader(const CString& name) const; + + // headers are iterated in the order they were added. + //! Get beginning header iterator + const_iterator begin() const { return m_headers.begin(); } + //! Get ending header iterator + const_iterator end() const { return m_headers.end(); } + + //@} + +public: + // note -- these members are public for convenience + //! The HTTP method + CString m_method; + //! The HTTP URI + CString m_uri; + //! The HTTP major version number + SInt32 m_majorVersion; + //! The HTTP minor version number + SInt32 m_minorVersion; + //! The HTTP body, after transfer decoding + CString m_body; + +private: + typedef std::map CHeaderMap; + + CHeaderList m_headers; + CHeaderMap m_headerByName; +}; + +//! HTTP reply type +/*! +This class encapsulates an HTTP reply. +*/ +class CHTTPReply { +public: + //! Header list + /*! + The type of the reply header list. Each pair is the header name + and value, respectively for first and second. + */ + typedef std::vector > CHeaderList; + + // note -- these members are public for convenience + //! The HTTP major version number + SInt32 m_majorVersion; + //! The HTTP minor version number + SInt32 m_minorVersion; + //! The HTTP status code + SInt32 m_status; + //! The HTTP reason phrase + CString m_reason; + //! The HTTP method + CString m_method; + //! The HTTP headers + CHeaderList m_headers; + //! The HTTP body + CString m_body; +}; + +//! HTTP protocol utilities +/*! +This class provides utility functions for HTTP. +*/ +class CHTTPProtocol { +public: + //! Multipart form parts + /*! + Each element is the contents of a multipart form part indexed by + it's name. + */ + typedef std::map CFormParts; + + //! Read HTTP request + /*! + Read and parse an HTTP request. The result is returned in a + CHTTPRequest which the client must delete. Throws an + XHTTP if there was a parse error. Throws an XIO exception + if there was a read error. If \c maxSize is greater than + zero and the request is larger than \c maxSize bytes then + throws XHTTP(413) (request entity too large). + */ + static CHTTPRequest* readRequest(IInputStream*, UInt32 maxSize = 0); + + //! Send HTTP response + /*! + Send an HTTP reply. The Content-Length and Date headers are set + automatically. + */ + static void reply(IOutputStream*, CHTTPReply&); + + //! Parse multipart form data + /*! + Parse a multipart/form-data body into its parts. Returns true + iff the entire body was correctly parsed. + */ + // FIXME -- name/value pairs insufficient to save part headers + static bool parseFormData(const CHTTPRequest&, + CFormParts& parts); + +private: + static CString readLine(IInputStream*, CString& tmpBuffer); + static CString readBlock(IInputStream*, + UInt32 numBytes, CString& tmpBuffer); + static CString readChunk(IInputStream*, CString& tmpBuffer, + UInt32* maxSize); + static void readHeaders(IInputStream*, + CHTTPRequest*, bool isFooter, + CString& tmpBuffer, + UInt32* maxSize); + + static bool isValidToken(const CString&); +}; + +#endif diff --git a/lib/http/Makefile.am b/lib/http/Makefile.am new file mode 100644 index 0000000..c9abe53 --- /dev/null +++ b/lib/http/Makefile.am @@ -0,0 +1,39 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + http.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libhttp.a +libhttp_a_SOURCES = \ + CHTTPProtocol.cpp \ + XHTTP.cpp \ + CHTTPProtocol.h \ + XHTTP.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + $(NULL) diff --git a/lib/http/Makefile.in b/lib/http/Makefile.in new file mode 100644 index 0000000..6beef7f --- /dev/null +++ b/lib/http/Makefile.in @@ -0,0 +1,339 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + http.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libhttp.a +libhttp_a_SOURCES = \ + CHTTPProtocol.cpp \ + XHTTP.cpp \ + CHTTPProtocol.h \ + XHTTP.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + $(NULL) + +subdir = lib/http +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libhttp_a_AR = $(AR) cru +libhttp_a_LIBADD = +am_libhttp_a_OBJECTS = CHTTPProtocol.$(OBJEXT) XHTTP.$(OBJEXT) +libhttp_a_OBJECTS = $(am_libhttp_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CHTTPProtocol.Po $(DEPDIR)/XHTTP.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libhttp_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libhttp_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/http/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libhttp.a: $(libhttp_a_OBJECTS) $(libhttp_a_DEPENDENCIES) + -rm -f libhttp.a + $(libhttp_a_AR) libhttp.a $(libhttp_a_OBJECTS) $(libhttp_a_LIBADD) + $(RANLIB) libhttp.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CHTTPProtocol.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XHTTP.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/http/XHTTP.cpp b/lib/http/XHTTP.cpp new file mode 100644 index 0000000..9b14609 --- /dev/null +++ b/lib/http/XHTTP.cpp @@ -0,0 +1,135 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XHTTP.h" +#include "CHTTPProtocol.h" +#include "CStringUtil.h" +#include "stdsstream.h" + +// +// XHTTP +// + +XHTTP::XHTTP(SInt32 statusCode) : + XBase(), + m_status(statusCode), + m_reason(getReason(statusCode)) +{ + // do nothing +} + +XHTTP::XHTTP(SInt32 statusCode, const CString& reasonPhrase) : + XBase(), + m_status(statusCode), + m_reason(reasonPhrase) +{ + // do nothing +} + +XHTTP::~XHTTP() +{ + // do nothing +} + +SInt32 +XHTTP::getStatus() const +{ + return m_status; +} + +CString +XHTTP::getReason() const +{ + return m_reason; +} + +void +XHTTP::addHeaders(CHTTPReply&) const +{ + // do nothing +} + +CString +XHTTP::getWhat() const throw() +{ + const char* reason; + if (m_reason.empty()) { + reason = getReason(m_status); + } + else { + reason = m_reason.c_str(); + } + return format("XHTTP", "%{1} %{2}", + CStringUtil::print("%d", m_status).c_str(), + reason); +} + +const char* +XHTTP::getReason(SInt32 status) +{ + switch (status) { + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Moved Temporarily"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Time-out"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Request Entity Too Large"; + case 414: return "Request-URI Too Large"; + case 415: return "Unsupported Media Type"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Time-out"; + case 505: return "HTTP Version not supported"; + default: return ""; + } +} + + +// +// XHTTPAllow +// + +XHTTPAllow::XHTTPAllow(const CString& allowedMethods) : + XHTTP(405), + m_allowed(allowedMethods) +{ + // do nothing +} + +XHTTPAllow::~XHTTPAllow() +{ + // do nothing +} + +void +XHTTPAllow::addHeaders(CHTTPReply& reply) const +{ + reply.m_headers.push_back(std::make_pair(CString("Allow"), m_allowed)); +} diff --git a/lib/http/XHTTP.h b/lib/http/XHTTP.h new file mode 100644 index 0000000..2bd6ed7 --- /dev/null +++ b/lib/http/XHTTP.h @@ -0,0 +1,82 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XHTTP_H +#define XHTTP_H + +#include "BasicTypes.h" +#include "XBase.h" + +class CHTTPReply; + +//! Generic HTTP exception +class XHTTP : public XBase { +public: + /*! + Use the HTTP \c statusCode as the failure reason. + */ + XHTTP(SInt32 statusCode); + /*! + Use the HTTP \c statusCode as the failure reason. Use \c reasonPhrase + as the human readable reason for the failure. + */ + XHTTP(SInt32 statusCode, const CString& reasonPhrase); + ~XHTTP(); + + //@{ + //! @name accessors + + //! Get the HTTP status code + SInt32 getStatus() const; + + //! Get the reason phrase + CString getReason() const; + + //! Modify reply for error + /*! + Override to modify an HTTP reply to further describe the error. + */ + virtual void addHeaders(CHTTPReply&) const; + + //@} + +protected: + virtual CString getWhat() const throw(); + +private: + static const char* getReason(SInt32 status); + +private: + SInt32 m_status; + CString m_reason; +}; + +//! HTTP exception indicating an unsupported method +class XHTTPAllow : public XHTTP { +public: + /*! + \c allowedMethods is added as an `Allow' header to a reply in + addHeaders(). + */ + XHTTPAllow(const CString& allowedMethods); + ~XHTTPAllow(); + + // XHTTP overrides + virtual void addHeaders(CHTTPReply&) const; + +private: + CString m_allowed; +}; + +#endif diff --git a/lib/http/http.dsp b/lib/http/http.dsp new file mode 100644 index 0000000..feff3d0 --- /dev/null +++ b/lib/http/http.dsp @@ -0,0 +1,110 @@ +# Microsoft Developer Studio Project File - Name="http" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=http - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "http.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "http.mak" CFG="http - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "http - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "http - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "http - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "http - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "http - Win32 Release" +# Name "http - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CHTTPProtocol.cpp +# End Source File +# Begin Source File + +SOURCE=.\XHTTP.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CHTTPProtocol.h +# End Source File +# Begin Source File + +SOURCE=.\XHTTP.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/io/CBufferedInputStream.cpp b/lib/io/CBufferedInputStream.cpp new file mode 100644 index 0000000..27b7cb6 --- /dev/null +++ b/lib/io/CBufferedInputStream.cpp @@ -0,0 +1,132 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CBufferedInputStream.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "CStopwatch.h" +#include "IJob.h" +#include "XIO.h" +#include + +// +// CBufferedInputStream +// + +CBufferedInputStream::CBufferedInputStream( + CMutex* mutex, IJob* adoptedCloseCB) : + m_mutex(mutex), + m_empty(mutex, true), + m_closeCB(adoptedCloseCB), + m_closed(false), + m_hungup(false) +{ + assert(m_mutex != NULL); +} + +CBufferedInputStream::~CBufferedInputStream() +{ + delete m_closeCB; +} + +void +CBufferedInputStream::write(const void* buffer, UInt32 n) +{ + if (!m_hungup && n > 0) { + m_buffer.write(buffer, n); + m_empty = (m_buffer.getSize() == 0); + m_empty.broadcast(); + } +} + +void +CBufferedInputStream::hangup() +{ + m_hungup = true; + m_empty.broadcast(); +} + +UInt32 +CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout) +{ + if (m_closed) { + throw XIOClosed(); + } + + // wait for data, hangup, or timeout + CStopwatch timer(true); + while (!m_hungup && m_empty == true) { + if (!m_empty.wait(timer, timeout)) { + // timed out + return (UInt32)-1; + } + } + + // read data + const UInt32 count = m_buffer.getSize(); + if (n > count) { + n = count; + } + if (n > 0) { + if (buffer != NULL) { + memcpy(buffer, m_buffer.peek(n), n); + } + m_buffer.pop(n); + } + + // update empty state + if (m_buffer.getSize() == 0) { + m_empty = true; + m_empty.broadcast(); + } + return n; +} + +UInt32 +CBufferedInputStream::getSizeNoLock() const +{ + return m_buffer.getSize(); +} + +void +CBufferedInputStream::close() +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_closed = true; + m_hungup = true; + m_buffer.pop(m_buffer.getSize()); + m_empty.broadcast(); + if (m_closeCB != NULL) { + m_closeCB->run(); + } +} + +UInt32 +CBufferedInputStream::read(void* buffer, UInt32 n, double timeout) +{ + CLock lock(m_mutex); + return readNoLock(buffer, n, timeout); +} + +UInt32 +CBufferedInputStream::getSize() const +{ + CLock lock(m_mutex); + return getSizeNoLock(); +} diff --git a/lib/io/CBufferedInputStream.h b/lib/io/CBufferedInputStream.h new file mode 100644 index 0000000..132d172 --- /dev/null +++ b/lib/io/CBufferedInputStream.h @@ -0,0 +1,96 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CBUFFEREDINPUTSTREAM_H +#define CBUFFEREDINPUTSTREAM_H + +#include "IInputStream.h" +#include "CStreamBuffer.h" +#include "CCondVar.h" + +class CMutex; +class IJob; + +//! Memory buffer input stream +/*! +This class provides an input stream that reads from a memory buffer. +It also provides a means for the owner to ensure thread safe access. +Typically, an owner object will make this object visible to clients +that need access to an IInputStream while using the CBufferedInputStream +methods to write data to the stream. +*/ +class CBufferedInputStream : public IInputStream { +public: + /*! + The \c mutex must not be NULL and will be used to ensure thread + safe access. If \c adoptedCloseCB is not NULL it will be called + when close() is called, allowing the creator to detect the close. + */ + CBufferedInputStream(CMutex* mutex, IJob* adoptedCloseCB); + ~CBufferedInputStream(); + + //! @name manipulators + //@{ + + //! Write data to stream + /*! + Write \c n bytes from \c buffer to the stream. The mutex must + be locked before calling this. + */ + void write(const void* buffer, UInt32 n); + + //! Hangup stream + /*! + Causes read() to always return immediately. If there is no + more data to read then it returns 0. Further writes are discarded. + The mutex must be locked before calling this. + */ + void hangup(); + + //! Read from stream + /*! + This is the same as read() but the mutex must be locked before + calling this. + */ + UInt32 readNoLock(void*, UInt32 n, double timeout); + + //@} + //! @name accessors + //@{ + + //! Get remaining size of stream + /*! + This is the same as getSize() but the mutex must be locked before + calling this. + */ + UInt32 getSizeNoLock() const; + + //@} + + // IInputStream overrides + // these all lock the mutex for their duration + virtual void close(); + virtual UInt32 read(void*, UInt32 n, double timeout); + virtual UInt32 getSize() const; + +private: + CMutex* m_mutex; + CCondVar m_empty; + IJob* m_closeCB; + CStreamBuffer m_buffer; + bool m_closed; + bool m_hungup; +}; + +#endif diff --git a/lib/io/CBufferedOutputStream.cpp b/lib/io/CBufferedOutputStream.cpp new file mode 100644 index 0000000..3406232 --- /dev/null +++ b/lib/io/CBufferedOutputStream.cpp @@ -0,0 +1,97 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CBufferedOutputStream.h" +#include "XIO.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "IJob.h" + +// +// CBufferedOutputStream +// + +CBufferedOutputStream::CBufferedOutputStream( + CMutex* mutex, IJob* adoptedCloseCB) : + m_mutex(mutex), + m_closeCB(adoptedCloseCB), + m_empty(mutex, true), + m_closed(false) +{ + assert(m_mutex != NULL); +} + +CBufferedOutputStream::~CBufferedOutputStream() +{ + delete m_closeCB; +} + +const void* +CBufferedOutputStream::peek(UInt32 n) +{ + return m_buffer.peek(n); +} + +void +CBufferedOutputStream::pop(UInt32 n) +{ + m_buffer.pop(n); + if (m_buffer.getSize() == 0) { + m_empty.broadcast(); + } +} + +UInt32 +CBufferedOutputStream::getSize() const +{ + return m_buffer.getSize(); +} + +void +CBufferedOutputStream::close() +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_closed = true; + m_buffer.pop(m_buffer.getSize()); + if (m_closeCB != NULL) { + m_closeCB->run(); + } +} + +UInt32 +CBufferedOutputStream::write(const void* buffer, UInt32 n) +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_buffer.write(buffer, n); + return n; +} + +void +CBufferedOutputStream::flush() +{ + // wait until all data is written + CLock lock(m_mutex); + while (m_buffer.getSize() > 0) { + m_empty.wait(); + } +} diff --git a/lib/io/CBufferedOutputStream.h b/lib/io/CBufferedOutputStream.h new file mode 100644 index 0000000..675aaac --- /dev/null +++ b/lib/io/CBufferedOutputStream.h @@ -0,0 +1,88 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CBUFFEREDOUTPUTSTREAM_H +#define CBUFFEREDOUTPUTSTREAM_H + +#include "IOutputStream.h" +#include "CStreamBuffer.h" +#include "CCondVar.h" + +class CMutex; +class IJob; + +//! Memory buffer output stream +/*! +This class provides an output stream that writes to a memory buffer. +It also provides a means for the owner to ensure thread safe access. +Typically, an owner object will make this object visible to clients +that need access to an IOutputStream while using the CBufferedOutputStream +methods to read the data written to the stream. +*/ +class CBufferedOutputStream : public IOutputStream { +public: + /*! + The \c mutex must not be NULL and will be used to ensure thread + safe access. If \c adoptedCloseCB is not NULL it will be called + when close() is called, allowing the creator to detect the close. + */ + CBufferedOutputStream(CMutex* mutex, IJob* adoptedCloseCB); + ~CBufferedOutputStream(); + + //! @name manipulators + //@{ + + //! Read data without removing from buffer + /*! + Returns a buffer of \c n bytes (which must be <= getSize()). The + caller must not modify the buffer nor delete it. The mutex must + be locked before calling this. + */ + const void* peek(UInt32 n); + + //! Discard data + /*! + Discards the next \c n bytes. If \c n >= getSize() then the buffer + is cleared. The mutex must be locked before calling this. + */ + void pop(UInt32 n); + + //@} + //! @name accessors + //@{ + + //! Get size of buffer + /*! + Returns the number of bytes in the buffer. The mutex must be locked + before calling this. + */ + UInt32 getSize() const; + + //@} + + // IOutputStream overrides + // these all lock the mutex for their duration + virtual void close(); + virtual UInt32 write(const void*, UInt32 n); + virtual void flush(); + +private: + CMutex* m_mutex; + IJob* m_closeCB; + CCondVar m_empty; + CStreamBuffer m_buffer; + bool m_closed; +}; + +#endif diff --git a/lib/io/CInputStreamFilter.cpp b/lib/io/CInputStreamFilter.cpp new file mode 100644 index 0000000..6d058b4 --- /dev/null +++ b/lib/io/CInputStreamFilter.cpp @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CInputStreamFilter.h" + +// +// CInputStreamFilter +// + +CInputStreamFilter::CInputStreamFilter(IInputStream* stream, bool adopted) : + m_stream(stream), + m_adopted(adopted) +{ + assert(m_stream != NULL); +} + +CInputStreamFilter::~CInputStreamFilter() +{ + if (m_adopted) { + delete m_stream; + } +} + +IInputStream* +CInputStreamFilter::getStream() const +{ + return m_stream; +} diff --git a/lib/io/CInputStreamFilter.h b/lib/io/CInputStreamFilter.h new file mode 100644 index 0000000..9274a81 --- /dev/null +++ b/lib/io/CInputStreamFilter.h @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CINPUTSTREAMFILTER_H +#define CINPUTSTREAMFILTER_H + +#include "IInputStream.h" + +//! A filtering input stream +/*! +This class wraps an input stream. Subclasses provide indirect access +to the stream, typically performing some filtering. +*/ +class CInputStreamFilter : public IInputStream { +public: + /*! + Create a wrapper around \c stream. Iff \c adoptStream is true then + this object takes ownership of the stream and will delete it in the + d'tor. + */ + CInputStreamFilter(IInputStream* stream, bool adoptStream = true); + ~CInputStreamFilter(); + + // IInputStream overrides + virtual void close() = 0; + virtual UInt32 read(void*, UInt32 n, double timeout) = 0; + virtual UInt32 getSize() const = 0; + +protected: + //! Get the stream + /*! + Returns the stream passed to the c'tor. + */ + IInputStream* getStream() const; + +private: + IInputStream* m_stream; + bool m_adopted; +}; + +#endif diff --git a/lib/io/COutputStreamFilter.cpp b/lib/io/COutputStreamFilter.cpp new file mode 100644 index 0000000..d90465a --- /dev/null +++ b/lib/io/COutputStreamFilter.cpp @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COutputStreamFilter.h" + +// +// COutputStreamFilter +// + +COutputStreamFilter::COutputStreamFilter(IOutputStream* stream, bool adopted) : + m_stream(stream), + m_adopted(adopted) +{ + assert(m_stream != NULL); +} + +COutputStreamFilter::~COutputStreamFilter() +{ + if (m_adopted) { + delete m_stream; + } +} + +IOutputStream* +COutputStreamFilter::getStream() const +{ + return m_stream; +} diff --git a/lib/io/COutputStreamFilter.h b/lib/io/COutputStreamFilter.h new file mode 100644 index 0000000..027e32d --- /dev/null +++ b/lib/io/COutputStreamFilter.h @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COUTPUTSTREAMFILTER_H +#define COUTPUTSTREAMFILTER_H + +#include "IOutputStream.h" + +//! A filtering output stream +/*! +This class wraps an output stream. Subclasses provide indirect access +to the stream, typically performing some filtering. +*/ +class COutputStreamFilter : public IOutputStream { +public: + /*! + Create a wrapper around \c stream. Iff \c adoptStream is true then + this object takes ownership of the stream and will delete it in the + d'tor. + */ + COutputStreamFilter(IOutputStream* stream, bool adoptStream = true); + ~COutputStreamFilter(); + + // IOutputStream overrides + virtual void close() = 0; + virtual UInt32 write(const void*, UInt32 count) = 0; + virtual void flush() = 0; + +protected: + //! Get the stream + /*! + Returns the stream passed to the c'tor. + */ + IOutputStream* getStream() const; + +private: + IOutputStream* m_stream; + bool m_adopted; +}; + +#endif diff --git a/lib/io/CStreamBuffer.cpp b/lib/io/CStreamBuffer.cpp new file mode 100644 index 0000000..2f10cac --- /dev/null +++ b/lib/io/CStreamBuffer.cpp @@ -0,0 +1,136 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CStreamBuffer.h" + +// +// CStreamBuffer +// + +const UInt32 CStreamBuffer::kChunkSize = 4096; + +CStreamBuffer::CStreamBuffer() : + m_size(0), + m_headUsed(0) +{ + // do nothing +} + +CStreamBuffer::~CStreamBuffer() +{ + // do nothing +} + +const void* +CStreamBuffer::peek(UInt32 n) +{ + assert(n <= m_size); + + // reserve space in first chunk + ChunkList::iterator head = m_chunks.begin(); + head->reserve(n + m_headUsed); + + // consolidate chunks into the first chunk until it has n bytes + ChunkList::iterator scan = head; + ++scan; + while (head->size() - m_headUsed < n && scan != m_chunks.end()) { + head->insert(head->end(), scan->begin(), scan->end()); + scan = m_chunks.erase(scan); + } + + return reinterpret_cast(&(head->begin()[m_headUsed])); +} + +void +CStreamBuffer::pop(UInt32 n) +{ + // discard all chunks if n is greater than or equal to m_size + if (n >= m_size) { + m_size = 0; + m_headUsed = 0; + m_chunks.clear(); + return; + } + + // update size + m_size -= n; + + // discard chunks until more than n bytes would've been discarded + ChunkList::iterator scan = m_chunks.begin(); + assert(scan != m_chunks.end()); + while (scan->size() - m_headUsed <= n) { + n -= scan->size() - m_headUsed; + m_headUsed = 0; + scan = m_chunks.erase(scan); + assert(scan != m_chunks.end()); + } + + // remove left over bytes from the head chunk + if (n > 0) { + m_headUsed += n; + } +} + +void +CStreamBuffer::write(const void* vdata, UInt32 n) +{ + assert(vdata != NULL); + + // ignore if no data, otherwise update size + if (n == 0) { + return; + } + m_size += n; + + // cast data to bytes + const UInt8* data = reinterpret_cast(vdata); + + // point to last chunk if it has space, otherwise append an empty chunk + ChunkList::iterator scan = m_chunks.end(); + if (scan != m_chunks.begin()) { + --scan; + if (scan->size() >= kChunkSize) { + ++scan; + } + } + if (scan == m_chunks.end()) { + scan = m_chunks.insert(scan, Chunk()); + } + + // append data in chunks + while (n > 0) { + // choose number of bytes for next chunk + assert(scan->size() <= kChunkSize); + UInt32 count = kChunkSize - scan->size(); + if (count > n) + count = n; + + // transfer data + scan->insert(scan->end(), data, data + count); + n -= count; + data += count; + + // append another empty chunk if we're not done yet + if (n > 0) { + ++scan; + scan = m_chunks.insert(scan, Chunk()); + } + } +} + +UInt32 +CStreamBuffer::getSize() const +{ + return m_size; +} diff --git a/lib/io/CStreamBuffer.h b/lib/io/CStreamBuffer.h new file mode 100644 index 0000000..fafcc72 --- /dev/null +++ b/lib/io/CStreamBuffer.h @@ -0,0 +1,78 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSTREAMBUFFER_H +#define CSTREAMBUFFER_H + +#include "BasicTypes.h" +#include "stdlist.h" +#include "stdvector.h" + +//! FIFO of bytes +/*! +This class maintains a FIFO (first-in, last-out) buffer of bytes. +*/ +class CStreamBuffer { +public: + CStreamBuffer(); + ~CStreamBuffer(); + + //! @name manipulators + //@{ + + //! Read data without removing from buffer + /*! + Return a pointer to memory with the next \c n bytes in the buffer + (which must be <= getSize()). The caller must not modify the returned + memory nor delete it. + */ + const void* peek(UInt32 n); + + //! Discard data + /*! + Discards the next \c n bytes. If \c n >= getSize() then the buffer + is cleared. + */ + void pop(UInt32 n); + + //! Write data to buffer + /*! + Appends \c n bytes from \c data to the buffer. + */ + void write(const void* data, UInt32 n); + + //@} + //! @name accessors + //@{ + + //! Get size of buffer + /*! + Returns the number of bytes in the buffer. + */ + UInt32 getSize() const; + + //@} + +private: + static const UInt32 kChunkSize; + + typedef std::vector Chunk; + typedef std::list ChunkList; + + ChunkList m_chunks; + UInt32 m_size; + UInt32 m_headUsed; +}; + +#endif diff --git a/lib/io/IInputStream.h b/lib/io/IInputStream.h new file mode 100644 index 0000000..a1b250d --- /dev/null +++ b/lib/io/IInputStream.h @@ -0,0 +1,67 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IINPUTSTREAM_H +#define IINPUTSTREAM_H + +#include "IInterface.h" +#include "BasicTypes.h" + +//! Input stream interface +/*! +Defines the interface for all input streams. +*/ +class IInputStream : public IInterface { +public: + //! @name manipulators + //@{ + + //! Close the stream + /*! + Closes the stream. Attempting to read() after close() throws + XIOClosed and getSize() always returns zero. + */ + virtual void close() = 0; + + //! Read from stream + /*! + Read up to \c n bytes into buffer, returning the number read. + Blocks for up to \c timeout seconds if no data is available but does + not wait if any data is available, even if less than \c n bytes. + If \c timeout < 0 then it blocks indefinitely until data is available. + If \c buffer is NULL then the data is discarded. Returns (UInt32)-1 if + it times out and 0 if no data is available and the other end of the + stream has hungup. + + (cancellation point) + */ + virtual UInt32 read(void* buffer, UInt32 n, double timeout) = 0; + + //@} + //! @name accessors + //@{ + + //! Get remaining size of stream + /*! + Returns a conservative estimate of the available bytes to read + (i.e. a number not greater than the actual number of bytes). + Some streams may not be able to determine this and will always + return zero. + */ + virtual UInt32 getSize() const = 0; + + //@} +}; + +#endif diff --git a/lib/io/IOutputStream.h b/lib/io/IOutputStream.h new file mode 100644 index 0000000..5540ce0 --- /dev/null +++ b/lib/io/IOutputStream.h @@ -0,0 +1,58 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IOUTPUTSTREAM_H +#define IOUTPUTSTREAM_H + +#include "IInterface.h" +#include "BasicTypes.h" + +//! Output stream interface +/*! +Defines the interface for all output streams. +*/ +class IOutputStream : public IInterface { +public: + //! @name manipulators + //@{ + + //! Close the stream + /*! + Closes the stream. Attempting to write() after close() throws + XIOClosed. + */ + virtual void close() = 0; + + //! Write to stream + /*! + Write \c n bytes from \c buffer to the stream. If this can't + complete immeditely it will block. If cancelled, an indeterminate + amount of data may have been written. + + (cancellation point) + */ + virtual UInt32 write(const void* buffer, UInt32 n) = 0; + + //! Flush the stream + /*! + Waits until all buffered data has been written to the stream. + + (cancellation point) + */ + virtual void flush() = 0; + + //@} +}; + +#endif diff --git a/lib/io/IStreamFilterFactory.h b/lib/io/IStreamFilterFactory.h new file mode 100644 index 0000000..2f459ef --- /dev/null +++ b/lib/io/IStreamFilterFactory.h @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISTREAMFILTERFACTORY_H +#define ISTREAMFILTERFACTORY_H + +#include "IInterface.h" + +class CInputStreamFilter; +class COutputStreamFilter; +class IInputStream; +class IOutputStream; + +//! Stream filter factory interface +/*! +This interface provides factory methods to create stream filters. +*/ +class IStreamFilterFactory : public IInterface { +public: + //! Create input filter + /*! + Create and return an input stream filter. The caller must delete the + returned object. + */ + virtual CInputStreamFilter* + createInput(IInputStream*, bool adoptStream) = 0; + + //! Create output filter + /*! + Create and return an output stream filter. The caller must delete the + returned object. + */ + virtual COutputStreamFilter* + createOutput(IOutputStream*, bool adoptStream) = 0; +}; + +#endif diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am new file mode 100644 index 0000000..8bac4c8 --- /dev/null +++ b/lib/io/Makefile.am @@ -0,0 +1,49 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + io.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libio.a +libio_a_SOURCES = \ + CBufferedInputStream.cpp \ + CBufferedOutputStream.cpp \ + CInputStreamFilter.cpp \ + COutputStreamFilter.cpp \ + CStreamBuffer.cpp \ + XIO.cpp \ + CBufferedInputStream.h \ + CBufferedOutputStream.h \ + CInputStreamFilter.h \ + COutputStreamFilter.h \ + CStreamBuffer.h \ + IInputStream.h \ + IOutputStream.h \ + IStreamFilterFactory.h \ + XIO.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + $(NULL) diff --git a/lib/io/Makefile.in b/lib/io/Makefile.in new file mode 100644 index 0000000..2113fb9 --- /dev/null +++ b/lib/io/Makefile.in @@ -0,0 +1,360 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + io.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libio.a +libio_a_SOURCES = \ + CBufferedInputStream.cpp \ + CBufferedOutputStream.cpp \ + CInputStreamFilter.cpp \ + COutputStreamFilter.cpp \ + CStreamBuffer.cpp \ + XIO.cpp \ + CBufferedInputStream.h \ + CBufferedOutputStream.h \ + CInputStreamFilter.h \ + COutputStreamFilter.h \ + CStreamBuffer.h \ + IInputStream.h \ + IOutputStream.h \ + IStreamFilterFactory.h \ + XIO.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + $(NULL) + +subdir = lib/io +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libio_a_AR = $(AR) cru +libio_a_LIBADD = +am_libio_a_OBJECTS = CBufferedInputStream.$(OBJEXT) \ + CBufferedOutputStream.$(OBJEXT) CInputStreamFilter.$(OBJEXT) \ + COutputStreamFilter.$(OBJEXT) CStreamBuffer.$(OBJEXT) \ + XIO.$(OBJEXT) +libio_a_OBJECTS = $(am_libio_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CBufferedInputStream.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CBufferedOutputStream.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CInputStreamFilter.Po \ +@AMDEP_TRUE@ $(DEPDIR)/COutputStreamFilter.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CStreamBuffer.Po $(DEPDIR)/XIO.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libio_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libio_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/io/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libio.a: $(libio_a_OBJECTS) $(libio_a_DEPENDENCIES) + -rm -f libio.a + $(libio_a_AR) libio.a $(libio_a_OBJECTS) $(libio_a_LIBADD) + $(RANLIB) libio.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CBufferedInputStream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CBufferedOutputStream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CInputStreamFilter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/COutputStreamFilter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CStreamBuffer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XIO.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/io/XIO.cpp b/lib/io/XIO.cpp new file mode 100644 index 0000000..710c419 --- /dev/null +++ b/lib/io/XIO.cpp @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XIO.h" + +// +// XIOClosed +// + +CString +XIOClosed::getWhat() const throw() +{ + return format("XIOClosed", "already closed"); +} + + +// +// XIOEndOfStream +// + +CString +XIOEndOfStream::getWhat() const throw() +{ + return format("XIOEndOfStream", "reached end of stream"); +} diff --git a/lib/io/XIO.h b/lib/io/XIO.h new file mode 100644 index 0000000..31871bf --- /dev/null +++ b/lib/io/XIO.h @@ -0,0 +1,42 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XIO_H +#define XIO_H + +#include "XBase.h" + +//! Generic I/O exception +XBASE_SUBCLASS(XIO, XBase); + +//! I/O closing exception +/*! +Thrown if a stream cannot be closed. +*/ +XBASE_SUBCLASS(XIOClose, XIO); + +//! I/O already closed exception +/*! +Thrown when attempting to close or perform I/O on an already closed. +stream. +*/ +XBASE_SUBCLASS_WHAT(XIOClosed, XIO); + +//! I/O end of stream exception +/*! +Thrown when attempting to read beyond the end of a stream. +*/ +XBASE_SUBCLASS_WHAT(XIOEndOfStream, XIO); + +#endif diff --git a/lib/io/io.dsp b/lib/io/io.dsp new file mode 100644 index 0000000..a46f158 --- /dev/null +++ b/lib/io/io.dsp @@ -0,0 +1,154 @@ +# Microsoft Developer Studio Project File - Name="io" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=io - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "io.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "io.mak" CFG="io - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "io - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "io - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "io - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "io - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "io - Win32 Release" +# Name "io - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CBufferedInputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBufferedOutputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CInputStreamFilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\COutputStreamFilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStreamBuffer.cpp +# End Source File +# Begin Source File + +SOURCE=.\XIO.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CBufferedInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CBufferedOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CInputStreamFilter.h +# End Source File +# Begin Source File + +SOURCE=.\COutputStreamFilter.h +# End Source File +# Begin Source File + +SOURCE=.\CStreamBuffer.h +# End Source File +# Begin Source File + +SOURCE=.\IInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\IOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\IStreamFilterFactory.h +# End Source File +# Begin Source File + +SOURCE=.\XIO.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/mt/CCondVar.cpp b/lib/mt/CCondVar.cpp new file mode 100644 index 0000000..d13aedf --- /dev/null +++ b/lib/mt/CCondVar.cpp @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CCondVar.h" +#include "CStopwatch.h" +#include "CArch.h" + +// +// CCondVarBase +// + +CCondVarBase::CCondVarBase(CMutex* mutex) : + m_mutex(mutex) +{ + assert(m_mutex != NULL); + m_cond = ARCH->newCondVar(); +} + +CCondVarBase::~CCondVarBase() +{ + ARCH->closeCondVar(m_cond); +} + +void +CCondVarBase::lock() const +{ + m_mutex->lock(); +} + +void +CCondVarBase::unlock() const +{ + m_mutex->unlock(); +} + +void +CCondVarBase::signal() +{ + ARCH->signalCondVar(m_cond); +} + +void +CCondVarBase::broadcast() +{ + ARCH->broadcastCondVar(m_cond); +} + +bool +CCondVarBase::wait(CStopwatch& timer, double timeout) const +{ + // check timeout against timer + if (timeout >= 0.0) { + timeout -= timer.getTime(); + if (timeout < 0.0) + return false; + } + return wait(timeout); +} + +bool +CCondVarBase::wait(double timeout) const +{ + return ARCH->waitCondVar(m_cond, m_mutex->m_mutex, timeout); +} + +CMutex* +CCondVarBase::getMutex() const +{ + return m_mutex; +} diff --git a/lib/mt/CCondVar.h b/lib/mt/CCondVar.h new file mode 100644 index 0000000..78fbfe9 --- /dev/null +++ b/lib/mt/CCondVar.h @@ -0,0 +1,226 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCONDVAR_H +#define CCONDVAR_H + +#include "CMutex.h" +#include "BasicTypes.h" + +class CStopwatch; + +//! Generic condition variable +/*! +This class provides functionality common to all condition variables +but doesn't provide the actual variable storage. A condition variable +is a multiprocessing primitive that can be waited on. Every condition +variable has an associated mutex. +*/ +class CCondVarBase { +public: + /*! + \c mutex must not be NULL. All condition variables have an + associated mutex. The mutex needn't be unique to one condition + variable. + */ + CCondVarBase(CMutex* mutex); + ~CCondVarBase(); + + //! @name manipulators + //@{ + + //! Lock the condition variable's mutex + /*! + Lock the condition variable's mutex. The condition variable should + be locked before reading or writing it. It must be locked for a + call to wait(). Locks are not recursive; locking a locked mutex + will deadlock the thread. + */ + void lock() const; + + //! Unlock the condition variable's mutex + void unlock() const; + + //! Signal the condition variable + /*! + Wake up one waiting thread, if there are any. Which thread gets + woken is undefined. + */ + void signal(); + + //! Signal the condition variable + /*! + Wake up all waiting threads, if any. + */ + void broadcast(); + + //@} + //! @name accessors + //@{ + + //! Wait on the condition variable + /*! + Wait on the condition variable. If \c timeout < 0 then wait until + signalled, otherwise up to \c timeout seconds or until signalled, + whichever comes first. Returns true if the object was signalled + during the wait, false otherwise. + + The proper way to wait for a condition is: + \code + cv.lock(); + while (cv-expr) { + cv.wait(); + } + cv.unlock(); + \endcode + where \c cv-expr involves the value of \c cv and is false when the + condition is satisfied. + + (cancellation point) + */ + bool wait(double timeout = -1.0) const; + + //! Wait on the condition variable + /*! + Same as \c wait(double) but use \c timer to compare against \timeout. + Since clients normally wait on condition variables in a loop, clients + can use this to avoid recalculating \c timeout on each iteration. + Passing a stopwatch with a negative \c timeout is pointless (it will + never time out) but permitted. + + (cancellation point) + */ + bool wait(CStopwatch& timer, double timeout) const; + + //! Get the mutex + /*! + Get the mutex passed to the c'tor. + */ + CMutex* getMutex() const; + + //@} + +private: + // not implemented + CCondVarBase(const CCondVarBase&); + CCondVarBase& operator=(const CCondVarBase&); + +private: + CMutex* m_mutex; + CArchCond m_cond; +}; + +//! Condition variable +/*! +A condition variable with storage for type \c T. +*/ +template +class CCondVar : public CCondVarBase { +public: + //! Initialize using \c value + CCondVar(CMutex* mutex, const T& value); + //! Initialize using another condition variable's value + CCondVar(const CCondVar&); + ~CCondVar(); + + //! @name manipulators + //@{ + + //! Assigns the value of \c cv to this + /*! + Set the variable's value. The condition variable should be locked + before calling this method. + */ + CCondVar& operator=(const CCondVar& cv); + + //! Assigns \c value to this + /*! + Set the variable's value. The condition variable should be locked + before calling this method. + */ + CCondVar& operator=(const T& v); + + //@} + //! @name accessors + //@{ + + //! Get the variable's value + /*! + Get the variable's value. The condition variable should be locked + before calling this method. + */ + operator const T&() const; + + //@} + +private: + T m_data; +}; + +template +inline +CCondVar::CCondVar( + CMutex* mutex, + const T& data) : + CCondVarBase(mutex), + m_data(data) +{ + // do nothing +} + +template +inline +CCondVar::CCondVar( + const CCondVar& cv) : + CCondVarBase(cv.getMutex()), + m_data(cv.m_data) +{ + // do nothing +} + +template +inline +CCondVar::~CCondVar() +{ + // do nothing +} + +template +inline +CCondVar& +CCondVar::operator=( + const CCondVar& cv) +{ + m_data = cv.m_data; + return *this; +} + +template +inline +CCondVar& +CCondVar::operator=( + const T& data) +{ + m_data = data; + return *this; +} + +template +inline +CCondVar::operator const T&() const +{ + return m_data; +} + +#endif diff --git a/lib/mt/CLock.cpp b/lib/mt/CLock.cpp new file mode 100644 index 0000000..c943395 --- /dev/null +++ b/lib/mt/CLock.cpp @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CLock.h" +#include "CCondVar.h" +#include "CMutex.h" + +// +// CLock +// + +CLock::CLock(const CMutex* mutex) : + m_mutex(mutex) +{ + m_mutex->lock(); +} + +CLock::CLock(const CCondVarBase* cv) : + m_mutex(cv->getMutex()) +{ + m_mutex->lock(); +} + +CLock::~CLock() +{ + m_mutex->unlock(); +} diff --git a/lib/mt/CLock.h b/lib/mt/CLock.h new file mode 100644 index 0000000..f00e0cd --- /dev/null +++ b/lib/mt/CLock.h @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CLOCK_H +#define CLOCK_H + +#include "common.h" + +class CMutex; +class CCondVarBase; + +//! Mutual exclusion lock utility +/*! +This class locks a mutex or condition variable in the c'tor and unlocks +it in the d'tor. It's easier and safer than manually locking and +unlocking since unlocking must usually be done no matter how a function +exits (including by unwinding due to an exception). +*/ +class CLock { +public: + //! Lock the mutex \c mutex + CLock(const CMutex* mutex); + //! Lock the condition variable \c cv + CLock(const CCondVarBase* cv); + //! Unlock the mutex or condition variable + ~CLock(); + +private: + // not implemented + CLock(const CLock&); + CLock& operator=(const CLock&); + +private: + const CMutex* m_mutex; +}; + +#endif diff --git a/lib/mt/CMutex.cpp b/lib/mt/CMutex.cpp new file mode 100644 index 0000000..0d2bab3 --- /dev/null +++ b/lib/mt/CMutex.cpp @@ -0,0 +1,53 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMutex.h" +#include "CArch.h" + +// +// CMutex +// + +CMutex::CMutex() +{ + m_mutex = ARCH->newMutex(); +} + +CMutex::CMutex(const CMutex&) +{ + m_mutex = ARCH->newMutex(); +} + +CMutex::~CMutex() +{ + ARCH->closeMutex(m_mutex); +} + +CMutex& +CMutex::operator=(const CMutex&) +{ + return *this; +} + +void +CMutex::lock() const +{ + ARCH->lockMutex(m_mutex); +} + +void +CMutex::unlock() const +{ + ARCH->unlockMutex(m_mutex); +} diff --git a/lib/mt/CMutex.h b/lib/mt/CMutex.h new file mode 100644 index 0000000..c11eeee --- /dev/null +++ b/lib/mt/CMutex.h @@ -0,0 +1,78 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMUTEX_H +#define CMUTEX_H + +#include "IArchMultithread.h" + +//! Mutual exclusion +/*! +A non-recursive mutual exclusion object. Only one thread at a time can +hold a lock on a mutex. Any thread that attempts to lock a locked mutex +will block until the mutex is unlocked. At that time, if any threads are +blocked, exactly one waiting thread will acquire the lock and continue +running. A thread may not lock a mutex it already owns the lock on; if +it tries it will deadlock itself. +*/ +class CMutex { +public: + CMutex(); + //! Equivalent to default c'tor + /*! + Copy c'tor doesn't copy anything. It just makes it possible to + copy objects that contain a mutex. + */ + CMutex(const CMutex&); + ~CMutex(); + + //! @name manipulators + //@{ + + //! Does nothing + /*! + This does nothing. It just makes it possible to assign objects + that contain a mutex. + */ + CMutex& operator=(const CMutex&); + + //@} + //! @name accessors + //@{ + + //! Lock the mutex + /*! + Locks the mutex, which must not have been previously locked by the + calling thread. This blocks if the mutex is already locked by another + thread. + + (cancellation point) + */ + void lock() const; + + //! Unlock the mutex + /*! + Unlocks the mutex, which must have been previously locked by the + calling thread. + */ + void unlock() const; + + //@} + +private: + friend class CCondVarBase; + CArchMutex m_mutex; +}; + +#endif diff --git a/lib/mt/CThread.cpp b/lib/mt/CThread.cpp new file mode 100644 index 0000000..fcf6832 --- /dev/null +++ b/lib/mt/CThread.cpp @@ -0,0 +1,184 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CThread.h" +#include "XMT.h" +#include "XThread.h" +#include "CLog.h" +#include "IJob.h" +#include "CArch.h" + +// +// CThread +// + +CThread::CThread(IJob* job) +{ + m_thread = ARCH->newThread(&CThread::threadFunc, job); + if (m_thread == NULL) { + // couldn't create thread + delete job; + throw XMTThreadUnavailable(); + } +} + +CThread::CThread(const CThread& thread) +{ + m_thread = ARCH->copyThread(thread.m_thread); +} + +CThread::CThread(CArchThread adoptedThread) +{ + m_thread = adoptedThread; +} + +CThread::~CThread() +{ + ARCH->closeThread(m_thread); +} + +CThread& +CThread::operator=(const CThread& thread) +{ + // copy given thread and release ours + CArchThread copy = ARCH->copyThread(thread.m_thread); + ARCH->closeThread(m_thread); + + // cut over + m_thread = copy; + + return *this; +} + +void +CThread::exit(void* result) +{ + throw XThreadExit(result); +} + +void +CThread::cancel() +{ + ARCH->cancelThread(m_thread); +} + +void +CThread::setPriority(int n) +{ + ARCH->setPriorityOfThread(m_thread, n); +} + +CThread +CThread::getCurrentThread() +{ + return CThread(ARCH->newCurrentThread()); +} + +void +CThread::testCancel() +{ + ARCH->testCancelThread(); +} + +bool +CThread::wait(double timeout) const +{ + return ARCH->wait(m_thread, timeout); +} + +CThread::EWaitResult +CThread::waitForEvent(double timeout) const +{ + // IArchMultithread EWaitResults map directly to our EWaitResults + static const EWaitResult s_map[] = { + kEvent, + kExit, + kTimeout + }; + return s_map[ARCH->waitForEvent(m_thread, timeout)]; +} + +void* +CThread::getResult() const +{ + if (wait()) + return ARCH->getResultOfThread(m_thread); + else + return NULL; +} + +IArchMultithread::ThreadID +CThread::getID() const +{ + return ARCH->getIDOfThread(m_thread); +} + +bool +CThread::operator==(const CThread& thread) const +{ + return ARCH->isSameThread(m_thread, thread.m_thread); +} + +bool +CThread::operator!=(const CThread& thread) const +{ + return !ARCH->isSameThread(m_thread, thread.m_thread); +} + +void* +CThread::threadFunc(void* vjob) +{ + // get this thread's id for logging + IArchMultithread::ThreadID id; + { + CArchThread thread = ARCH->newCurrentThread(); + id = ARCH->getIDOfThread(thread); + ARCH->closeThread(thread); + } + + // get job + IJob* job = reinterpret_cast(vjob); + + // run job + void* result = NULL; + try { + // go + LOG((CLOG_DEBUG1 "thread 0x%08x entry", id)); + job->run(); + LOG((CLOG_DEBUG1 "thread 0x%08x exit", id)); + } + + catch (XThreadCancel&) { + // client called cancel() + LOG((CLOG_DEBUG1 "caught cancel on thread 0x%08x", id)); + delete job; + throw; + } + catch (XThreadExit& e) { + // client called exit() + result = e.m_result; + LOG((CLOG_DEBUG1 "caught exit on thread 0x%08x, result %p", id, result)); + } + catch (...) { + LOG((CLOG_DEBUG1 "exception on thread 0x%08x", id)); + delete job; + throw; + } + + // done with job + delete job; + + // return exit result + return result; +} diff --git a/lib/mt/CThread.h b/lib/mt/CThread.h new file mode 100644 index 0000000..7398261 --- /dev/null +++ b/lib/mt/CThread.h @@ -0,0 +1,225 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CTHREAD_H +#define CTHREAD_H + +#include "IArchMultithread.h" + +class IJob; + +//! Thread handle +/*! +Creating a CThread creates a new context of execution (i.e. thread) that +runs simulatenously with the calling thread. A CThread is only a handle +to a thread; deleting a CThread does not cancel or destroy the thread it +refers to and multiple CThread objects can refer to the same thread. + +Threads can terminate themselves but cannot be forced to terminate by +other threads. However, other threads can signal a thread to terminate +itself by cancelling it. And a thread can wait (block) on another thread +to terminate. + +Most functions that can block for an arbitrary time are cancellation +points. A cancellation point is a function that can be interrupted by +a request to cancel the thread. Cancellation points are noted in the +documentation. +*/ +// note -- do not derive from this class +class CThread { +public: + //! Result of waitForEvent() + enum EWaitResult { + kEvent, //!< An event is pending + kExit, //!< Thread exited + kTimeout //!< Wait timed out + }; + + //! Run \c adoptedJob in a new thread + /*! + Create and start a new thread executing the \c adoptedJob. The + new thread takes ownership of \c adoptedJob and will delete it. + */ + CThread(IJob* adoptedJob); + + //! Duplicate a thread handle + /*! + Make a new thread object that refers to an existing thread. + This does \b not start a new thread. + */ + CThread(const CThread&); + + //! Release a thread handle + /*! + Release a thread handle. This does not terminate the thread. A thread + will keep running until the job completes or calls exit() or allows + itself to be cancelled. + */ + ~CThread(); + + //! @name manipulators + //@{ + + //! Assign thread handle + /*! + Assign a thread handle. This has no effect on the threads, it simply + makes this thread object refer to another thread. It does \b not + start a new thread. + */ + CThread& operator=(const CThread&); + + //! Terminate the calling thread + /*! + Terminate the calling thread. This function does not return but + the stack is unwound and automatic objects are destroyed, as if + exit() threw an exception (which is, in fact, what it does). The + argument is saved as the result returned by getResult(). If you + have \c catch(...) blocks then you should add the following before + each to avoid catching the exit: + \code + catch(CThreadExit&) { throw; } + \endcode + or add the \c RETHROW_XTHREAD macro to the \c catch(...) block. + */ + static void exit(void*); + + //! Cancel thread + /*! + Cancel the thread. cancel() never waits for the thread to + terminate; it just posts the cancel and returns. A thread will + terminate when it enters a cancellation point with cancellation + enabled. If cancellation is disabled then the cancel is + remembered but not acted on until the first call to a + cancellation point after cancellation is enabled. + + A cancellation point is a function that can act on cancellation. + A cancellation point does not return if there's a cancel pending. + Instead, it unwinds the stack and destroys automatic objects, as + if cancel() threw an exception (which is, in fact, what it does). + Threads must take care to unlock and clean up any resources they + may have, especially mutexes. They can \c catch(XThreadCancel) to + do that then rethrow the exception or they can let it happen + automatically by doing clean up in the d'tors of automatic + objects (like CLock). Clients are strongly encouraged to do the latter. + During cancellation, further cancel() calls are ignored (i.e. + a thread cannot be interrupted by a cancel during cancellation). + + Clients that \c catch(XThreadCancel) must always rethrow the + exception. Clients that \c catch(...) must either rethrow the + exception or include a \c catch(XThreadCancel) handler that + rethrows. The \c RETHROW_XTHREAD macro may be useful for that. + */ + void cancel(); + + //! Change thread priority + /*! + Change the priority of the thread. Normal priority is 0, 1 is + the next lower, etc. -1 is the next higher, etc. but boosting + the priority may not be permitted and will be silenty ignored. + */ + void setPriority(int n); + + //@} + //! @name accessors + //@{ + + //! Get current thread's handle + /*! + Return a CThread object representing the calling thread. + */ + static CThread getCurrentThread(); + + //! Test for cancellation + /*! + testCancel() does nothing but is a cancellation point. Call + this to make a function itself a cancellation point. If the + thread was cancelled and cancellation is enabled this will + cause the thread to unwind the stack and terminate. + + (cancellation point) + */ + static void testCancel(); + + //! Wait for thread to terminate + /*! + Waits for the thread to terminate (by exit() or cancel() or + by returning from the thread job) for up to \c timeout seconds, + returning true if the thread terminated and false otherwise. + This returns immediately with false if called by a thread on + itself and immediately with true if the thread has already + terminated. This will wait forever if \c timeout < 0.0. + + (cancellation point) + */ + bool wait(double timeout = -1.0) const; + + //! Wait for an event (win32) + /*! + Wait for the message queue to contain a message or for the thread + to exit for up to \c timeout seconds. This returns immediately if + any message is available (including messages that were already in + the queue during the last call to \c GetMessage() or + \c PeekMessage() or waitForEvent(). Returns kEvent if a message + is available, kExit if the thread exited, and kTimeout otherwise. + This will wait forever if \c timeout < 0.0. + + This method is available under win32 only. + + (cancellation point) + */ + EWaitResult waitForEvent(double timeout = -1.0) const; + + //! Get the exit result + /*! + Returns the exit result. This does an implicit wait(). It returns + NULL immediately if called by a thread on itself or on a thread that + was cancelled. + + (cancellation point) + */ + void* getResult() const; + + //! Get the thread id + /*! + Returns an integer id for this thread. This id must not be used to + check if two CThread objects refer to the same thread. Use + operator==() for that. + */ + IArchMultithread::ThreadID + getID() const; + + //! Compare thread handles + /*! + Returns true if two CThread objects refer to the same thread. + */ + bool operator==(const CThread&) const; + + //! Compare thread handles + /*! + Returns true if two CThread objects do not refer to the same thread. + */ + bool operator!=(const CThread&) const; + + //@} + +private: + CThread(CArchThread); + + static void* threadFunc(void*); + +private: + CArchThread m_thread; +}; + +#endif diff --git a/lib/mt/CTimerThread.cpp b/lib/mt/CTimerThread.cpp new file mode 100644 index 0000000..2ac6352 --- /dev/null +++ b/lib/mt/CTimerThread.cpp @@ -0,0 +1,65 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CTimerThread.h" +#include "CThread.h" +#include "TMethodJob.h" +#include "CLog.h" +#include "CArch.h" + +// +// CTimerThread +// + +CTimerThread::CTimerThread(double timeout, bool* timedOut) : + m_timeout(timeout), + m_timedOut(timedOut) +{ + if (m_timedOut != NULL) { + *m_timedOut = false; + } + if (m_timeout >= 0.0) { + m_callingThread = new CThread(CThread::getCurrentThread()); + m_timingThread = new CThread(new TMethodJob( + this, &CTimerThread::timer)); + } + else { + m_callingThread = NULL; + m_timingThread = NULL; + } +} + +CTimerThread::~CTimerThread() +{ + if (m_timingThread != NULL) { + LOG((CLOG_DEBUG1 "cancelling timeout")); + m_timingThread->cancel(); + m_timingThread->wait(); + LOG((CLOG_DEBUG1 "cancelled timeout")); + delete m_timingThread; + delete m_callingThread; + } +} + +void +CTimerThread::timer(void*) +{ + LOG((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); + ARCH->sleep(m_timeout); + LOG((CLOG_DEBUG1 "timeout")); + if (m_timedOut != NULL) { + *m_timedOut = true; + } + m_callingThread->cancel(); +} diff --git a/lib/mt/CTimerThread.h b/lib/mt/CTimerThread.h new file mode 100644 index 0000000..1a0a2b5 --- /dev/null +++ b/lib/mt/CTimerThread.h @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CTIMERTHREAD_H +#define CTIMERTHREAD_H + +#include "common.h" + +class CThread; + +//! A timer thread +/*! +An object of this class cancels the thread that called the c'tor unless +the object is destroyed before a given timeout. +*/ +class CTimerThread { +public: + //! Cancel calling thread after \c timeout seconds + /*! + Cancels the calling thread after \c timeout seconds unless destroyed + before then. If \c timeout is less than zero then it never times + out and this is a no-op. If \c timedOutFlag is not NULL then it's + set to false in the c'tor and to true if the timeout exipires before + it's cancelled. + */ + CTimerThread(double timeout, bool* timedOutFlag = NULL); + //! Cancel the timer thread + ~CTimerThread(); + +private: + void timer(void*); + + // not implemented + CTimerThread(const CTimerThread&); + CTimerThread& operator=(const CTimerThread&); + +private: + double m_timeout; + bool* m_timedOut; + CThread* m_callingThread; + CThread* m_timingThread; +}; + +#endif + diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am new file mode 100644 index 0000000..6eda91d --- /dev/null +++ b/lib/mt/Makefile.am @@ -0,0 +1,46 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + mt.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libmt.a +libmt_a_SOURCES = \ + CLock.cpp \ + CMutex.cpp \ + CCondVar.cpp \ + CThread.cpp \ + CTimerThread.cpp \ + XMT.cpp \ + CCondVar.h \ + CLock.h \ + CMutex.h \ + CThread.h \ + CTimerThread.h \ + XMT.h \ + XThread.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + $(NULL) diff --git a/lib/mt/Makefile.in b/lib/mt/Makefile.in new file mode 100644 index 0000000..8a51b70 --- /dev/null +++ b/lib/mt/Makefile.in @@ -0,0 +1,353 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + mt.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libmt.a +libmt_a_SOURCES = \ + CLock.cpp \ + CMutex.cpp \ + CCondVar.cpp \ + CThread.cpp \ + CTimerThread.cpp \ + XMT.cpp \ + CCondVar.h \ + CLock.h \ + CMutex.h \ + CThread.h \ + CTimerThread.h \ + XMT.h \ + XThread.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + $(NULL) + +subdir = lib/mt +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libmt_a_AR = $(AR) cru +libmt_a_LIBADD = +am_libmt_a_OBJECTS = CLock.$(OBJEXT) CMutex.$(OBJEXT) CCondVar.$(OBJEXT) \ + CThread.$(OBJEXT) CTimerThread.$(OBJEXT) XMT.$(OBJEXT) +libmt_a_OBJECTS = $(am_libmt_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CCondVar.Po $(DEPDIR)/CLock.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CMutex.Po $(DEPDIR)/CThread.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CTimerThread.Po $(DEPDIR)/XMT.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libmt_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libmt_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/mt/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libmt.a: $(libmt_a_OBJECTS) $(libmt_a_DEPENDENCIES) + -rm -f libmt.a + $(libmt_a_AR) libmt.a $(libmt_a_OBJECTS) $(libmt_a_LIBADD) + $(RANLIB) libmt.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CCondVar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CLock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CMutex.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CThread.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CTimerThread.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XMT.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/mt/XMT.cpp b/lib/mt/XMT.cpp new file mode 100644 index 0000000..b10d2d7 --- /dev/null +++ b/lib/mt/XMT.cpp @@ -0,0 +1,25 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XMT.h" + +// +// XMTThreadUnavailable +// + +CString +XMTThreadUnavailable::getWhat() const throw() +{ + return format("XMTThreadUnavailable", "cannot create thread"); +} diff --git a/lib/mt/XMT.h b/lib/mt/XMT.h new file mode 100644 index 0000000..f66428e --- /dev/null +++ b/lib/mt/XMT.h @@ -0,0 +1,29 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XMT_H +#define XMT_H + +#include "XBase.h" + +//! Generic multithreading exception +XBASE_SUBCLASS(XMT, XBase); + +//! Thread creation exception +/*! +Thrown when a thread cannot be created. +*/ +XBASE_SUBCLASS_WHAT(XMTThreadUnavailable, XMT); + +#endif diff --git a/lib/mt/XThread.h b/lib/mt/XThread.h new file mode 100644 index 0000000..30dff1d --- /dev/null +++ b/lib/mt/XThread.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XTHREAD_H +#define XTHREAD_H + +#include "XArch.h" + +//! Thread exception to exit +/*! +Thrown by CThread::exit() to exit a thread. Clients of CThread +must not throw this type but must rethrow it if caught (by +XThreadExit, XThread, or ...). +*/ +class XThreadExit : public XThread { +public: + //! \c result is the result of the thread + XThreadExit(void* result) : m_result(result) { } + ~XThreadExit() { } + +public: + void* m_result; +}; + +#endif diff --git a/lib/mt/mt.dsp b/lib/mt/mt.dsp new file mode 100644 index 0000000..a11135e --- /dev/null +++ b/lib/mt/mt.dsp @@ -0,0 +1,146 @@ +# Microsoft Developer Studio Project File - Name="mt" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=mt - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mt.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mt.mak" CFG="mt - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mt - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "mt - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mt - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "mt - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "mt - Win32 Release" +# Name "mt - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CCondVar.cpp +# End Source File +# Begin Source File + +SOURCE=.\CLock.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMutex.cpp +# End Source File +# Begin Source File + +SOURCE=.\CThread.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.cpp +# End Source File +# Begin Source File + +SOURCE=.\XMT.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CCondVar.h +# End Source File +# Begin Source File + +SOURCE=.\CLock.h +# End Source File +# Begin Source File + +SOURCE=.\CMutex.h +# End Source File +# Begin Source File + +SOURCE=.\CThread.h +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.h +# End Source File +# Begin Source File + +SOURCE=.\XMT.h +# End Source File +# Begin Source File + +SOURCE=.\XThread.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp new file mode 100644 index 0000000..c60b96a --- /dev/null +++ b/lib/net/CNetworkAddress.cpp @@ -0,0 +1,166 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CNetworkAddress.h" +#include "XSocket.h" +#include "CArch.h" +#include "XArch.h" +#include + +// +// CNetworkAddress +// + +CNetworkAddress::CNetworkAddress() : + m_address(NULL) +{ + // note -- make no calls to CNetwork socket interface here; + // we're often called prior to CNetwork::init(). +} + +CNetworkAddress::CNetworkAddress(int port) +{ + if (port == 0) { + throw XSocketAddress(XSocketAddress::kBadPort, "", port); + } + + m_address = ARCH->newAnyAddr(IArchNetwork::kINET); + ARCH->setAddrPort(m_address, port); +} + +CNetworkAddress::CNetworkAddress(const CNetworkAddress& addr) : + m_address(ARCH->copyAddr(addr.m_address)), + m_hostname(addr.m_hostname) +{ + // do nothing +} + +CNetworkAddress::CNetworkAddress(const CString& hostname_, int port) : + m_hostname(hostname_) +{ + if (port == 0) { + throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, port); + } + + // check for port suffix + CString hostname(m_hostname); + CString::size_type i = hostname.rfind(':'); + if (i != CString::npos && i + 1 < hostname.size()) { + // found a colon. see if it looks like an IPv6 address. + bool colonNotation = false; + bool dotNotation = false; + bool doubleColon = false; + for (CString::size_type j = 0; j < i; ++j) { + if (hostname[j] == ':') { + colonNotation = true; + dotNotation = false; + if (hostname[j + 1] == ':') { + doubleColon = true; + } + } + else if (hostname[j] == '.' && colonNotation) { + dotNotation = true; + } + } + + // port suffix is ambiguous with IPv6 notation if there's + // a double colon and the end of the address is not in dot + // notation. in that case we assume it's not a port suffix. + // the user can replace the double colon with zeros to + // disambiguate. + if ((!doubleColon || dotNotation) || !colonNotation) { + char* end; + long suffixPort = strtol(hostname.c_str() + i + 1, &end, 10); + if (end == hostname.c_str() + i + 1 || *end != '\0' || + suffixPort <= 0 || suffixPort > 65535) { + // bogus port + throw XSocketAddress(XSocketAddress::kBadPort, + m_hostname, port); + } + else { + // good port + port = static_cast(suffixPort); + hostname.erase(i); + } + } + } + + // if hostname is empty then use wildcard address + if (hostname.empty()) { + m_address = ARCH->newAnyAddr(IArchNetwork::kINET); + ARCH->setAddrPort(m_address, port); + } + else { + // look up name + try { + m_address = ARCH->nameToAddr(hostname); + ARCH->setAddrPort(m_address, port); + } + catch (XArchNetworkNameUnknown&) { + throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); + } + catch (XArchNetworkNameNoAddress&) { + throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); + } + catch (XArchNetworkName&) { + throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); + } + } +} + +CNetworkAddress::~CNetworkAddress() +{ + if (m_address != NULL) { + ARCH->closeAddr(m_address); + } +} + +CNetworkAddress& +CNetworkAddress::operator=(const CNetworkAddress& addr) +{ + CArchNetAddress newAddr = NULL; + if (addr.m_address != NULL) { + newAddr = ARCH->copyAddr(addr.m_address); + } + if (m_address != NULL) { + ARCH->closeAddr(m_address); + } + m_hostname = addr.m_hostname; + m_address = newAddr; + return *this; +} + +bool +CNetworkAddress::isValid() const +{ + return (m_address != NULL); +} + +const CArchNetAddress& +CNetworkAddress::getAddress() const +{ + return m_address; +} + +int +CNetworkAddress::getPort() const +{ + return (m_address == NULL) ? 0 : ARCH->getAddrPort(m_address); +} + +CString +CNetworkAddress::getHostname() const +{ + return m_hostname; +} diff --git a/lib/net/CNetworkAddress.h b/lib/net/CNetworkAddress.h new file mode 100644 index 0000000..0289827 --- /dev/null +++ b/lib/net/CNetworkAddress.h @@ -0,0 +1,91 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CNETWORKADDRESS_H +#define CNETWORKADDRESS_H + +#include "CString.h" +#include "BasicTypes.h" +#include "IArchNetwork.h" + +//! Network address type +/*! +This class represents a network address. +*/ +class CNetworkAddress { +public: + /*! + Constructs the invalid address + */ + CNetworkAddress(); + + /*! + Construct the wildcard address with the given port. \c port must + not be zero. + */ + CNetworkAddress(int port); + + /*! + Construct the network address for the given \c hostname and \c port. + If \c hostname can be parsed as a numerical address then that's how + it's used, otherwise the host name is looked up. If the lookup fails + then this throws XSocketAddress. If \c hostname ends in ":[0-9]+" then + that suffix is extracted and used as the port, overridding the port + parameter. Neither the extracted port or \c port may be zero. + */ + CNetworkAddress(const CString& hostname, int port); + + CNetworkAddress(const CNetworkAddress&); + + ~CNetworkAddress(); + + CNetworkAddress& operator=(const CNetworkAddress&); + + //! @name accessors + //@{ + + //! Check address validity + /*! + Returns true if this is not the invalid address. + */ + bool isValid() const; + + //! Get address + /*! + Returns the address in the platform's native network address + structure. + */ + const CArchNetAddress& getAddress() const; + + //! Get port + /*! + Returns the port passed to the c'tor as a suffix to the hostname, + if that existed, otherwise as passed directly to the c'tor. + */ + int getPort() const; + + //! Get hostname + /*! + Returns the hostname passed to the c'tor sans the port suffix. + */ + CString getHostname() const; + + //@} + +private: + CArchNetAddress m_address; + CString m_hostname; +}; + +#endif diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp new file mode 100644 index 0000000..1177516 --- /dev/null +++ b/lib/net/CTCPListenSocket.cpp @@ -0,0 +1,98 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CTCPListenSocket.h" +#include "CTCPSocket.h" +#include "CNetworkAddress.h" +#include "XIO.h" +#include "XSocket.h" +#include "CThread.h" +#include "CArch.h" +#include "XArch.h" + +// +// CTCPListenSocket +// + +CTCPListenSocket::CTCPListenSocket() +{ + try { + m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); + } + catch (XArchNetwork& e) { + throw XSocketCreate(e.what()); + } +} + +CTCPListenSocket::~CTCPListenSocket() +{ + try { + ARCH->closeSocket(m_socket); + } + catch (...) { + // ignore + } +} + +void +CTCPListenSocket::bind(const CNetworkAddress& addr) +{ + try { + ARCH->bindSocket(m_socket, addr.getAddress()); + ARCH->listenOnSocket(m_socket); + } + catch (XArchNetworkAddressInUse& e) { + throw XSocketAddressInUse(e.what()); + } + catch (XArchNetwork& e) { + throw XSocketBind(e.what()); + } +} + +IDataSocket* +CTCPListenSocket::accept() +{ + // accept asynchronously so we can check for cancellation + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; + pfds[0].m_events = IArchNetwork::kPOLLIN; + for (;;) { + ARCH->testCancelThread(); + try { + const int status = ARCH->pollSocket(pfds, 1, 0.01); + if (status > 0 && + (pfds[0].m_revents & IArchNetwork::kPOLLIN) != 0) { + return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); + } + } + catch (XArchNetwork&) { + // ignore and retry + } + } +} + +void +CTCPListenSocket::close() +{ + if (m_socket == NULL) { + throw XIOClosed(); + } + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork& e) { + throw XSocketIOClose(e.what()); + } +} diff --git a/lib/net/CTCPListenSocket.h b/lib/net/CTCPListenSocket.h new file mode 100644 index 0000000..d511637 --- /dev/null +++ b/lib/net/CTCPListenSocket.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CTCPLISTENSOCKET_H +#define CTCPLISTENSOCKET_H + +#include "IListenSocket.h" +#include "IArchNetwork.h" + +//! TCP listen socket +/*! +A listen socket using TCP. +*/ +class CTCPListenSocket : public IListenSocket { +public: + CTCPListenSocket(); + ~CTCPListenSocket(); + + // ISocket overrides + virtual void bind(const CNetworkAddress&); + virtual void close(); + + // IListenSocket overrides + virtual IDataSocket* accept(); + +private: + CArchSocket m_socket; +}; + +#endif diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp new file mode 100644 index 0000000..e74fc4f --- /dev/null +++ b/lib/net/CTCPSocket.cpp @@ -0,0 +1,379 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CTCPSocket.h" +#include "CBufferedInputStream.h" +#include "CBufferedOutputStream.h" +#include "CNetworkAddress.h" +#include "XIO.h" +#include "XSocket.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "TMethodJob.h" +#include "CArch.h" +#include "XArch.h" + +// +// CTCPSocket +// + +CTCPSocket::CTCPSocket() +{ + try { + m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); + } + catch (XArchNetwork& e) { + throw XSocketCreate(e.what()); + } + init(); +} + +CTCPSocket::CTCPSocket(CArchSocket socket) : + m_socket(socket) +{ + assert(m_socket != NULL); + + init(); + + // socket starts in connected state + m_connected = kReadWrite; + + // start handling socket + m_thread = new CThread(new TMethodJob( + this, &CTCPSocket::ioThread)); +} + +CTCPSocket::~CTCPSocket() +{ + try { + close(); + } + catch (...) { + // ignore + } + + // clean up + delete m_input; + delete m_output; + delete m_mutex; +} + +void +CTCPSocket::bind(const CNetworkAddress& addr) +{ + try { + ARCH->bindSocket(m_socket, addr.getAddress()); + } + catch (XArchNetworkAddressInUse& e) { + throw XSocketAddressInUse(e.what()); + } + catch (XArchNetwork& e) { + throw XSocketBind(e.what()); + } +} + +void +CTCPSocket::close() +{ + // see if buffers should be flushed + bool doFlush = false; + { + CLock lock(m_mutex); + doFlush = (m_thread != NULL && (m_connected & kWrite) != 0); + } + + // flush buffers + if (doFlush) { + m_output->flush(); + } + + // cause ioThread to exit + if (m_socket != NULL) { + CLock lock(m_mutex); + try { + ARCH->closeSocketForRead(m_socket); + } + catch (XArchNetwork&) { + // ignore + } + try { + ARCH->closeSocketForWrite(m_socket); + } + catch (XArchNetwork&) { + // ignore + } + m_connected = kClosed; + } + + // wait for thread + if (m_thread != NULL) { + m_thread->wait(); + delete m_thread; + m_thread = NULL; + } + + // close socket + if (m_socket != NULL) { + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork& e) { + throw XSocketIOClose(e.what()); + } + } +} + +void +CTCPSocket::connect(const CNetworkAddress& addr) +{ + do { + // connect asynchronously so we can check for cancellation. + // we can't wrap setting and resetting the blocking flag in + // the c'tor/d'tor of a class (to make resetting automatic) + // because setBlockingOnSocket() can throw and it might be + // called while unwinding the stack due to a throw. + try { + ARCH->setBlockingOnSocket(m_socket, false); + ARCH->connectSocket(m_socket, addr.getAddress()); + ARCH->setBlockingOnSocket(m_socket, true); + + // connected + break; + } + catch (XArchNetworkConnecting&) { + // connection is in progress + ARCH->setBlockingOnSocket(m_socket, true); + } + catch (XArchNetwork& e) { + ARCH->setBlockingOnSocket(m_socket, true); + throw XSocketConnect(e.what()); + } + + // wait for connection or failure + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; + pfds[0].m_events = IArchNetwork::kPOLLOUT; + for (;;) { + ARCH->testCancelThread(); + try { + const int status = ARCH->pollSocket(pfds, 1, 0.01); + if (status > 0) { + if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR | + IArchNetwork::kPOLLNVAL)) != 0) { + // connection failed + ARCH->throwErrorOnSocket(m_socket); + } + if ((pfds[0].m_revents & IArchNetwork::kPOLLOUT) != 0) { + // connection may have failed or succeeded + ARCH->throwErrorOnSocket(m_socket); + + // connected! + break; + } + } + } + catch (XArchNetwork& e) { + throw XSocketConnect(e.what()); + } + } + } while (false); + + // start servicing the socket + m_connected = kReadWrite; + m_thread = new CThread(new TMethodJob( + this, &CTCPSocket::ioThread)); +} + +IInputStream* +CTCPSocket::getInputStream() +{ + return m_input; +} + +IOutputStream* +CTCPSocket::getOutputStream() +{ + return m_output; +} + +void +CTCPSocket::init() +{ + m_mutex = new CMutex; + m_thread = NULL; + m_connected = kClosed; + m_input = new CBufferedInputStream(m_mutex, + new TMethodJob( + this, &CTCPSocket::closeInput)); + m_output = new CBufferedOutputStream(m_mutex, + new TMethodJob( + this, &CTCPSocket::closeOutput)); + + // turn off Nagle algorithm. we send lots of very short messages + // that should be sent without (much) delay. for example, the + // mouse motion messages are much less useful if they're delayed. +// FIXME -- the client should do this + try { + ARCH->setNoDelayOnSocket(m_socket, true); + } + catch (XArchNetwork& e) { + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork&) { + // ignore + } + throw XSocketCreate(e.what()); + } +} + +void +CTCPSocket::ioThread(void*) +{ + try { + ioService(); + ioCleanup(); + } + catch (...) { + ioCleanup(); + throw; + } +} + +void +CTCPSocket::ioCleanup() +{ + try { + m_input->close(); + } + catch (...) { + // ignore + } + try { + m_output->close(); + } + catch (...) { + // ignore + } +} + +void +CTCPSocket::ioService() +{ + assert(m_socket != NULL); + + // now service the connection + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; + for (;;) { + { + // choose events to poll for + CLock lock(m_mutex); + pfds[0].m_events = 0; + if (m_connected == 0) { + return; + } + if ((m_connected & kRead) != 0) { + // still open for reading + pfds[0].m_events |= IArchNetwork::kPOLLIN; + } + if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) { + // data queued for writing + pfds[0].m_events |= IArchNetwork::kPOLLOUT; + } + } + + try { + // check for status + const int status = ARCH->pollSocket(pfds, 1, 0.01); + + // transfer data and handle errors + if (status == 1) { + if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR | + IArchNetwork::kPOLLNVAL)) != 0) { + // stream is no good anymore so bail + CLock lock(m_mutex); + m_input->hangup(); + return; + } + + // read some data + if (pfds[0].m_revents & IArchNetwork::kPOLLIN) { + UInt8 buffer[4096]; + size_t n = ARCH->readSocket(m_socket, + buffer, sizeof(buffer)); + CLock lock(m_mutex); + if (n > 0) { + m_input->write(buffer, n); + } + else { + // stream hungup + m_input->hangup(); + m_connected &= ~kRead; + } + } + + // write some data + if (pfds[0].m_revents & IArchNetwork::kPOLLOUT) { + CLock lock(m_mutex); + + // get amount of data to write + UInt32 n = m_output->getSize(); + + // write data + const void* buffer = m_output->peek(n); + size_t n2 = ARCH->writeSocket(m_socket, buffer, n); + + // discard written data + if (n2 > 0) { + m_output->pop(n2); + } + } + } + } + catch (XArchNetwork&) { + // socket has failed + return; + } + } +} + +void +CTCPSocket::closeInput(void*) +{ + // note -- m_mutex should already be locked + try { + ARCH->closeSocketForRead(m_socket); + m_connected &= ~kRead; + } + catch (XArchNetwork&) { + // ignore + } +} + +void +CTCPSocket::closeOutput(void*) +{ + // note -- m_mutex should already be locked + try { + ARCH->closeSocketForWrite(m_socket); + m_connected &= ~kWrite; + } + catch (XArchNetwork&) { + // ignore + } +} diff --git a/lib/net/CTCPSocket.h b/lib/net/CTCPSocket.h new file mode 100644 index 0000000..bfbade8 --- /dev/null +++ b/lib/net/CTCPSocket.h @@ -0,0 +1,66 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CTCPSOCKET_H +#define CTCPSOCKET_H + +#include "IDataSocket.h" +#include "BasicTypes.h" +#include "IArchNetwork.h" + +class CMutex; +class CThread; +class CBufferedInputStream; +class CBufferedOutputStream; + +//! TCP data socket +/*! +A data socket using TCP. +*/ +class CTCPSocket : public IDataSocket { +public: + CTCPSocket(); + CTCPSocket(CArchSocket); + ~CTCPSocket(); + + // ISocket overrides + virtual void bind(const CNetworkAddress&); + virtual void close(); + + // IDataSocket overrides + virtual void connect(const CNetworkAddress&); + virtual IInputStream* getInputStream(); + virtual IOutputStream* getOutputStream(); + +private: + void init(); + void ioThread(void*); + void ioCleanup(); + void ioService(); + void closeInput(void*); + void closeOutput(void*); + +private: + enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 }; + + CArchSocket m_socket; + CBufferedInputStream* m_input; + CBufferedOutputStream* m_output; + + CMutex* m_mutex; + CThread* m_thread; + UInt32 m_connected; +}; + +#endif diff --git a/lib/net/CTCPSocketFactory.cpp b/lib/net/CTCPSocketFactory.cpp new file mode 100644 index 0000000..f590efa --- /dev/null +++ b/lib/net/CTCPSocketFactory.cpp @@ -0,0 +1,43 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CTCPSocketFactory.h" +#include "CTCPSocket.h" +#include "CTCPListenSocket.h" + +// +// CTCPSocketFactory +// + +CTCPSocketFactory::CTCPSocketFactory() +{ + // do nothing +} + +CTCPSocketFactory::~CTCPSocketFactory() +{ + // do nothing +} + +IDataSocket* +CTCPSocketFactory::create() const +{ + return new CTCPSocket; +} + +IListenSocket* +CTCPSocketFactory::createListen() const +{ + return new CTCPListenSocket; +} diff --git a/lib/net/CTCPSocketFactory.h b/lib/net/CTCPSocketFactory.h new file mode 100644 index 0000000..2b946d1 --- /dev/null +++ b/lib/net/CTCPSocketFactory.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CTCPSOCKETFACTORY_H +#define CTCPSOCKETFACTORY_H + +#include "ISocketFactory.h" + +//! Socket factory for TCP sockets +class CTCPSocketFactory : public ISocketFactory { +public: + CTCPSocketFactory(); + virtual ~CTCPSocketFactory(); + + // ISocketFactory overrides + virtual IDataSocket* create() const; + virtual IListenSocket* createListen() const; +}; + +#endif diff --git a/lib/net/IDataSocket.h b/lib/net/IDataSocket.h new file mode 100644 index 0000000..0193e1d --- /dev/null +++ b/lib/net/IDataSocket.h @@ -0,0 +1,63 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IDATASOCKET_H +#define IDATASOCKET_H + +#include "ISocket.h" + +class IInputStream; +class IOutputStream; + +//! Data stream socket interface +/*! +This interface defines the methods common to all network sockets that +represent a full-duplex data stream. +*/ +class IDataSocket : public ISocket { +public: + //! @name manipulators + //@{ + + //! Connect socket + /*! + Attempt to connect to a remote endpoint. This waits until the + connection is established or fails. If it fails it throws an + XSocketConnect exception. + + (cancellation point) + */ + virtual void connect(const CNetworkAddress&) = 0; + + //! Get input stream + /*! + Returns the input stream for reading from the socket. Closing this + stream will shutdown the socket for reading. + */ + virtual IInputStream* getInputStream() = 0; + //! Get output stream + /*! + Returns the output stream for writing to the socket. Closing this + stream will shutdown the socket for writing. + */ + virtual IOutputStream* getOutputStream() = 0; + + //@} + + // ISocket overrides + virtual void bind(const CNetworkAddress&) = 0; + virtual void close() = 0; +}; + +#endif diff --git a/lib/net/IListenSocket.h b/lib/net/IListenSocket.h new file mode 100644 index 0000000..7a7ddaa --- /dev/null +++ b/lib/net/IListenSocket.h @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ILISTENSOCKET_H +#define ILISTENSOCKET_H + +#include "ISocket.h" + +class IDataSocket; + +//! Listen socket interface +/*! +This interface defines the methods common to all network sockets that +listen for incoming connections. +*/ +class IListenSocket : public ISocket { +public: + //! @name manipulators + //@{ + + //! Accept connection + /*! + Wait for and accept a connection, returning a socket representing + the full-duplex data stream. + + (cancellation point) + */ + virtual IDataSocket* accept() = 0; + + //@} + + // ISocket overrides + virtual void bind(const CNetworkAddress&) = 0; + virtual void close() = 0; +}; + +#endif diff --git a/lib/net/ISocket.h b/lib/net/ISocket.h new file mode 100644 index 0000000..623a25a --- /dev/null +++ b/lib/net/ISocket.h @@ -0,0 +1,46 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISOCKET_H +#define ISOCKET_H + +#include "IInterface.h" + +class CNetworkAddress; + +//! Generic socket interface +/*! +This interface defines the methods common to all network sockets. +*/ +class ISocket : public IInterface { +public: + //! @name manipulators + //@{ + + //! Bind socket to address + /*! + Binds the socket to a particular address. + */ + virtual void bind(const CNetworkAddress&) = 0; + + //! Close socket + /*! + Closes the socket. This should flush the output stream. + */ + virtual void close() = 0; + + //@} +}; + +#endif diff --git a/lib/net/ISocketFactory.h b/lib/net/ISocketFactory.h new file mode 100644 index 0000000..dbf9ae2 --- /dev/null +++ b/lib/net/ISocketFactory.h @@ -0,0 +1,42 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISOCKETFACTORY_H +#define ISOCKETFACTORY_H + +#include "IInterface.h" + +class IDataSocket; +class IListenSocket; + +//! Socket factory +/*! +This interface defines the methods common to all factories used to +create sockets. +*/ +class ISocketFactory : public IInterface { +public: + //! @name accessors + //@{ + + //! Create data socket + virtual IDataSocket* create() const = 0; + + //! Create listen socket + virtual IListenSocket* createListen() const = 0; + + //@} +}; + +#endif diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am new file mode 100644 index 0000000..efeb519 --- /dev/null +++ b/lib/net/Makefile.am @@ -0,0 +1,49 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + net.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libnet.a +libnet_a_SOURCES = \ + CNetworkAddress.cpp \ + CTCPListenSocket.cpp \ + CTCPSocket.cpp \ + CTCPSocketFactory.cpp \ + XSocket.cpp \ + CNetworkAddress.h \ + CTCPListenSocket.h \ + CTCPSocket.h \ + CTCPSocketFactory.h \ + IDataSocket.h \ + IListenSocket.h \ + ISocket.h \ + ISocketFactory.h \ + XSocket.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + $(NULL) diff --git a/lib/net/Makefile.in b/lib/net/Makefile.in new file mode 100644 index 0000000..a2f4eff --- /dev/null +++ b/lib/net/Makefile.in @@ -0,0 +1,357 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + net.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libnet.a +libnet_a_SOURCES = \ + CNetworkAddress.cpp \ + CTCPListenSocket.cpp \ + CTCPSocket.cpp \ + CTCPSocketFactory.cpp \ + XSocket.cpp \ + CNetworkAddress.h \ + CTCPListenSocket.h \ + CTCPSocket.h \ + CTCPSocketFactory.h \ + IDataSocket.h \ + IListenSocket.h \ + ISocket.h \ + ISocketFactory.h \ + XSocket.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + $(NULL) + +subdir = lib/net +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libnet_a_AR = $(AR) cru +libnet_a_LIBADD = +am_libnet_a_OBJECTS = CNetworkAddress.$(OBJEXT) \ + CTCPListenSocket.$(OBJEXT) CTCPSocket.$(OBJEXT) \ + CTCPSocketFactory.$(OBJEXT) XSocket.$(OBJEXT) +libnet_a_OBJECTS = $(am_libnet_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CNetworkAddress.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CTCPListenSocket.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CTCPSocket.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CTCPSocketFactory.Po $(DEPDIR)/XSocket.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libnet_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libnet_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/net/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libnet.a: $(libnet_a_OBJECTS) $(libnet_a_DEPENDENCIES) + -rm -f libnet.a + $(libnet_a_AR) libnet.a $(libnet_a_OBJECTS) $(libnet_a_LIBADD) + $(RANLIB) libnet.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CNetworkAddress.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CTCPListenSocket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CTCPSocket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CTCPSocketFactory.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XSocket.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/net/XSocket.cpp b/lib/net/XSocket.cpp new file mode 100644 index 0000000..cf8b3ca --- /dev/null +++ b/lib/net/XSocket.cpp @@ -0,0 +1,111 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XSocket.h" +#include "CStringUtil.h" + +// +// XSocketAddress +// + +XSocketAddress::XSocketAddress(EError error, + const CString& hostname, int port) throw() : + m_error(error), + m_hostname(hostname), + m_port(port) +{ + // do nothing +} + +XSocketAddress::EError +XSocketAddress::getError() const throw() +{ + return m_error; +} + +CString +XSocketAddress::getHostname() const throw() +{ + return m_hostname; +} + +int +XSocketAddress::getPort() const throw() +{ + return m_port; +} + +CString +XSocketAddress::getWhat() const throw() +{ + static const char* s_errorID[] = { + "XSocketAddressUnknown", + "XSocketAddressNotFound", + "XSocketAddressNoAddress", + "XSocketAddressBadPort" + }; + static const char* s_errorMsg[] = { + "unknown error for: %{1}:%{2}", + "address not found for: %{1}", + "no address for: %{1}", + "invalid port" // m_port may not be set to the bad port + }; + return format(s_errorID[m_error], s_errorMsg[m_error], + m_hostname.c_str(), + CStringUtil::print("%d", m_port).c_str()); +} + + +// +// XSocketIOClose +// + +CString +XSocketIOClose::getWhat() const throw() +{ + return format("XSocketIOClose", "close: %{1}", what()); +} + + +// +// XSocketBind +// + +CString +XSocketBind::getWhat() const throw() +{ + return format("XSocketBind", "cannot bind address: %{1}", what()); +} + + +// +// XSocketConnect +// + +CString +XSocketConnect::getWhat() const throw() +{ + return format("XSocketConnect", "cannot connect socket: %{1}", what()); +} + + +// +// XSocketCreate +// + +CString +XSocketCreate::getWhat() const throw() +{ + return format("XSocketCreate", "cannot create socket: %{1}", what()); +} diff --git a/lib/net/XSocket.h b/lib/net/XSocket.h new file mode 100644 index 0000000..c366303 --- /dev/null +++ b/lib/net/XSocket.h @@ -0,0 +1,95 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XSOCKET_H +#define XSOCKET_H + +#include "XIO.h" +#include "XBase.h" +#include "CString.h" +#include "BasicTypes.h" + +//! Generic socket exception +XBASE_SUBCLASS(XSocket, XBase); + +//! Socket bad address exception +/*! +Thrown when attempting to create an invalid network address. +*/ +class XSocketAddress : public XSocket { +public: + //! Failure codes + enum EError { + kUnknown, //!< Unknown error + kNotFound, //!< The hostname is unknown + kNoAddress, //!< The hostname is valid but has no IP address + kBadPort //!< The port is invalid + }; + + XSocketAddress(EError, const CString& hostname, int port) throw(); + + //! @name accessors + //@{ + + //! Get the error code + EError getError() const throw(); + //! Get the hostname + CString getHostname() const throw(); + //! Get the port + int getPort() const throw(); + + //@} + +protected: + // XBase overrides + virtual CString getWhat() const throw(); + +private: + EError m_error; + CString m_hostname; + int m_port; +}; + +//! I/O closing exception +/*! +Thrown if a stream cannot be closed. +*/ +XBASE_SUBCLASS_FORMAT(XSocketIOClose, XIOClose); + +//! Socket cannot bind address exception +/*! +Thrown when a socket cannot be bound to an address. +*/ +XBASE_SUBCLASS_FORMAT(XSocketBind, XSocket); + +//! Socket address in use exception +/*! +Thrown when a socket cannot be bound to an address because the address +is already in use. +*/ +XBASE_SUBCLASS(XSocketAddressInUse, XSocketBind); + +//! Cannot connect socket exception +/*! +Thrown when a socket cannot connect to a remote endpoint. +*/ +XBASE_SUBCLASS_FORMAT(XSocketConnect, XSocket); + +//! Cannot create socket exception +/*! +Thrown when a socket cannot be created (by the operating system). +*/ +XBASE_SUBCLASS_FORMAT(XSocketCreate, XSocket); + +#endif diff --git a/lib/net/net.dsp b/lib/net/net.dsp new file mode 100644 index 0000000..0a5fdd5 --- /dev/null +++ b/lib/net/net.dsp @@ -0,0 +1,146 @@ +# Microsoft Developer Studio Project File - Name="net" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=net - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "net.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "net.mak" CFG="net - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "net - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "net - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "net - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "net - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "net - Win32 Release" +# Name "net - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CNetworkAddress.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPListenSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.cpp +# End Source File +# Begin Source File + +SOURCE=.\XSocket.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CNetworkAddress.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPListenSocket.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocket.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.h +# End Source File +# Begin Source File + +SOURCE=.\IListenSocket.h +# End Source File +# Begin Source File + +SOURCE=.\ISocket.h +# End Source File +# Begin Source File + +SOURCE=.\ISocketFactory.h +# End Source File +# Begin Source File + +SOURCE=.\XSocket.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/platform/CMSWindowsClipboard.cpp b/lib/platform/CMSWindowsClipboard.cpp new file mode 100644 index 0000000..5f4c767 --- /dev/null +++ b/lib/platform/CMSWindowsClipboard.cpp @@ -0,0 +1,205 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClipboard.h" +#include "CMSWindowsClipboardTextConverter.h" +#include "CMSWindowsClipboardUTF16Converter.h" +#include "CLog.h" +#include "CArchMiscWindows.h" + +// +// CMSWindowsClipboard +// + +UINT CMSWindowsClipboard::s_ownershipFormat = 0; + +CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : + m_window(window), + m_time(0) +{ + // add converters, most desired first + m_converters.push_back(new CMSWindowsClipboardUTF16Converter); + if (CArchMiscWindows::isWindows95Family()) { + // windows nt family converts to/from unicode automatically. + // let it do so to avoid text encoding issues. + m_converters.push_back(new CMSWindowsClipboardTextConverter); + } +} + +CMSWindowsClipboard::~CMSWindowsClipboard() +{ + clearConverters(); +} + +bool +CMSWindowsClipboard::emptyUnowned() +{ + LOG((CLOG_DEBUG "empty clipboard")); + + // empty the clipboard (and take ownership) + if (!EmptyClipboard()) { + LOG((CLOG_DEBUG "failed to grab clipboard")); + return false; + } + + return true; +} + +bool +CMSWindowsClipboard::empty() +{ + if (!emptyUnowned()) { + return false; + } + + // mark clipboard as being owned by synergy + HGLOBAL data = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, 1); + SetClipboardData(getOwnershipFormat(), data); + + return true; +} + +void +CMSWindowsClipboard::add(EFormat format, const CString& data) +{ + LOG((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format)); + + // convert data to win32 form + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IMSWindowsClipboardConverter* converter = *index; + + // skip converters for other formats + if (converter->getFormat() == format) { + HANDLE win32Data = converter->fromIClipboard(data); + if (win32Data != NULL) { + UINT win32Format = converter->getWin32Format(); + if (SetClipboardData(win32Format, win32Data) == NULL) { + // free converted data if we couldn't put it on + // the clipboard + GlobalFree(win32Data); + } + } + } + } +} + +bool +CMSWindowsClipboard::open(Time time) const +{ + LOG((CLOG_DEBUG "open clipboard")); + + if (!OpenClipboard(m_window)) { + LOG((CLOG_WARN "failed to open clipboard")); + return false; + } + + m_time = time; + + return true; +} + +void +CMSWindowsClipboard::close() const +{ + LOG((CLOG_DEBUG "close clipboard")); + CloseClipboard(); +} + +IClipboard::Time +CMSWindowsClipboard::getTime() const +{ + return m_time; +} + +bool +CMSWindowsClipboard::has(EFormat format) const +{ + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IMSWindowsClipboardConverter* converter = *index; + if (converter->getFormat() == format) { + if (IsClipboardFormatAvailable(converter->getWin32Format())) { + return true; + } + } + } + return false; +} + +CString +CMSWindowsClipboard::get(EFormat format) const +{ + // find the converter for the first clipboard format we can handle + IMSWindowsClipboardConverter* converter = NULL; + UINT win32Format = EnumClipboardFormats(0); + while (converter == NULL && win32Format != 0) { + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + converter = *index; + if (converter->getWin32Format() == win32Format && + converter->getFormat() == format) { + break; + } + converter = NULL; + } + win32Format = EnumClipboardFormats(win32Format); + } + + // if no converter then we don't recognize any formats + if (converter == NULL) { + return CString(); + } + + // get a handle to the clipboard data + HANDLE win32Data = GetClipboardData(converter->getWin32Format()); + if (win32Data == NULL) { + return CString(); + } + + // convert + return converter->toIClipboard(win32Data); +} + +void +CMSWindowsClipboard::clearConverters() +{ + for (ConverterList::iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + delete *index; + } + m_converters.clear(); +} + +bool +CMSWindowsClipboard::isOwnedBySynergy() +{ + // create ownership format if we haven't yet + if (s_ownershipFormat == 0) { + s_ownershipFormat = RegisterClipboardFormat(TEXT("SynergyOwnership")); + } + return (IsClipboardFormatAvailable(getOwnershipFormat()) != 0); +} + +UINT +CMSWindowsClipboard::getOwnershipFormat() +{ + // create ownership format if we haven't yet + if (s_ownershipFormat == 0) { + s_ownershipFormat = RegisterClipboardFormat(TEXT("SynergyOwnership")); + } + + // return the format + return s_ownershipFormat; +} diff --git a/lib/platform/CMSWindowsClipboard.h b/lib/platform/CMSWindowsClipboard.h new file mode 100644 index 0000000..e9b59fb --- /dev/null +++ b/lib/platform/CMSWindowsClipboard.h @@ -0,0 +1,104 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIPBOARD_H +#define CMSWINDOWSCLIPBOARD_H + +#include "IClipboard.h" +#include "stdvector.h" +#define WIN32_LEAN_AND_MEAN +#include + +class IMSWindowsClipboardConverter; + +//! Microsoft windows clipboard implementation +class CMSWindowsClipboard : public IClipboard { +public: + CMSWindowsClipboard(HWND window); + virtual ~CMSWindowsClipboard(); + + //! Empty clipboard without ownership + /*! + Take ownership of the clipboard and clear all data from it. + This must be called between a successful open() and close(). + Return false if the clipboard ownership could not be taken; + the clipboard should not be emptied in this case. Unlike + empty(), isOwnedBySynergy() will return false when emptied + this way. This is useful when synergy wants to put data on + clipboard but pretend (to itself) that some other app did it. + When using empty(), synergy assumes the data came from the + server and doesn't need to be sent back. emptyUnowned() + makes synergy send the data to the server. + */ + bool emptyUnowned(); + + //! Test if clipboard is owned by synergy + static bool isOwnedBySynergy(); + + // IClipboard overrides + virtual bool empty(); + virtual void add(EFormat, const CString& data); + virtual bool open(Time) const; + virtual void close() const; + virtual Time getTime() const; + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; + +private: + void clearConverters(); + + UINT convertFormatToWin32(EFormat) const; + HANDLE convertTextToWin32(const CString& data) const; + CString convertTextFromWin32(HANDLE) const; + + static UINT getOwnershipFormat(); + +private: + typedef std::vector ConverterList; + + HWND m_window; + mutable Time m_time; + ConverterList m_converters; + static UINT s_ownershipFormat; +}; + +//! Clipboard format converter interface +/*! +This interface defines the methods common to all win32 clipboard format +converters. +*/ +class IMSWindowsClipboardConverter : public IInterface { +public: + // accessors + + // return the clipboard format this object converts from/to + virtual IClipboard::EFormat + getFormat() const = 0; + + // return the atom representing the win32 clipboard format that + // this object converts from/to + virtual UINT getWin32Format() const = 0; + + // convert from the IClipboard format to the win32 clipboard format. + // the input data must be in the IClipboard format returned by + // getFormat(). the return data will be in the win32 clipboard + // format returned by getWin32Format(), allocated by GlobalAlloc(). + virtual HANDLE fromIClipboard(const CString&) const = 0; + + // convert from the win32 clipboard format to the IClipboard format + // (i.e., the reverse of fromIClipboard()). + virtual CString toIClipboard(HANDLE data) const = 0; +}; + +#endif diff --git a/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp b/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp new file mode 100644 index 0000000..730f342 --- /dev/null +++ b/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp @@ -0,0 +1,145 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClipboardAnyTextConverter.h" + +// +// CMSWindowsClipboardAnyTextConverter +// + +CMSWindowsClipboardAnyTextConverter::CMSWindowsClipboardAnyTextConverter() +{ + // do nothing +} + +CMSWindowsClipboardAnyTextConverter::~CMSWindowsClipboardAnyTextConverter() +{ + // do nothing +} + +IClipboard::EFormat +CMSWindowsClipboardAnyTextConverter::getFormat() const +{ + return IClipboard::kText; +} + +HANDLE +CMSWindowsClipboardAnyTextConverter::fromIClipboard(const CString& data) const +{ + // convert linefeeds and then convert to desired encoding + CString text = doFromIClipboard(convertLinefeedToWin32(data)); + UInt32 size = text.size(); + + // copy to memory handle + HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size); + if (gData != NULL) { + // get a pointer to the allocated memory + char* dst = (char*)GlobalLock(gData); + if (dst != NULL) { + memcpy(dst, text.data(), size); + GlobalUnlock(gData); + } + else { + GlobalFree(gData); + gData = NULL; + } + } + + return gData; +} + +CString +CMSWindowsClipboardAnyTextConverter::toIClipboard(HANDLE data) const +{ + // get datator + const char* src = (const char*)GlobalLock(data); + UInt32 srcSize = (UInt32)GlobalSize(data); + if (src == NULL || srcSize <= 1) { + return CString(); + } + + // convert text + CString text = doToIClipboard(CString(src, srcSize)); + + // release handle + GlobalUnlock(data); + + // convert newlines + return convertLinefeedToUnix(text); +} + +CString +CMSWindowsClipboardAnyTextConverter::convertLinefeedToWin32( + const CString& src) const +{ + // note -- we assume src is a valid UTF-8 string + + // count newlines in string + UInt32 numNewlines = 0; + UInt32 n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (*scan == '\n') { + ++numNewlines; + } + } + if (numNewlines == 0) { + return src; + } + + // allocate new string + CString dst; + dst.reserve(src.size() + numNewlines); + + // copy string, converting newlines + n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (scan[0] == '\n') { + dst += '\r'; + } + dst += scan[0]; + } + + return dst; +} + +CString +CMSWindowsClipboardAnyTextConverter::convertLinefeedToUnix( + const CString& src) const +{ + // count newlines in string + UInt32 numNewlines = 0; + UInt32 n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (scan[0] == '\r' && scan[1] == '\n') { + ++numNewlines; + } + } + if (numNewlines == 0) { + return src; + } + + // allocate new string + CString dst; + dst.reserve(src.size()); + + // copy string, converting newlines + n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (scan[0] != '\r' || scan[1] != '\n') { + dst += scan[0]; + } + } + + return dst; +} diff --git a/lib/platform/CMSWindowsClipboardAnyTextConverter.h b/lib/platform/CMSWindowsClipboardAnyTextConverter.h new file mode 100644 index 0000000..254099f --- /dev/null +++ b/lib/platform/CMSWindowsClipboardAnyTextConverter.h @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIPBOARDANYTEXTCONVERTER_H +#define CMSWINDOWSCLIPBOARDANYTEXTCONVERTER_H + +#include "CMSWindowsClipboard.h" + +//! Convert to/from some text encoding +class CMSWindowsClipboardAnyTextConverter : + public IMSWindowsClipboardConverter { +public: + CMSWindowsClipboardAnyTextConverter(); + virtual ~CMSWindowsClipboardAnyTextConverter(); + + // IMSWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual UINT getWin32Format() const = 0; + virtual HANDLE fromIClipboard(const CString&) const; + virtual CString toIClipboard(HANDLE) const; + +protected: + //! Convert from IClipboard format + /*! + Do UTF-8 conversion only. Memory handle allocation and + linefeed conversion is done by this class. doFromIClipboard() + must include the nul terminator in the returned string (not + including the CString's nul terminator). + */ + virtual CString doFromIClipboard(const CString&) const = 0; + + //! Convert to IClipboard format + /*! + Do UTF-8 conversion only. Memory handle allocation and + linefeed conversion is done by this class. + */ + virtual CString doToIClipboard(const CString&) const = 0; + +private: + CString convertLinefeedToWin32(const CString&) const; + CString convertLinefeedToUnix(const CString&) const; +}; + +#endif diff --git a/lib/platform/CMSWindowsClipboardTextConverter.cpp b/lib/platform/CMSWindowsClipboardTextConverter.cpp new file mode 100644 index 0000000..a735094 --- /dev/null +++ b/lib/platform/CMSWindowsClipboardTextConverter.cpp @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClipboardTextConverter.h" +#include "CUnicode.h" + +// +// CMSWindowsClipboardTextConverter +// + +CMSWindowsClipboardTextConverter::CMSWindowsClipboardTextConverter() +{ + // do nothing +} + +CMSWindowsClipboardTextConverter::~CMSWindowsClipboardTextConverter() +{ + // do nothing +} + +UINT +CMSWindowsClipboardTextConverter::getWin32Format() const +{ + return CF_TEXT; +} + +CString +CMSWindowsClipboardTextConverter::doFromIClipboard(const CString& data) const +{ + // convert and add nul terminator + return CUnicode::UTF8ToText(data) += '\0'; +} + +CString +CMSWindowsClipboardTextConverter::doToIClipboard(const CString& data) const +{ + // convert and truncate at first nul terminator + CString dst = CUnicode::textToUTF8(data); + CString::size_type n = dst.find('\0'); + if (n != CString::npos) { + dst.erase(n); + } + return dst; +} diff --git a/lib/platform/CMSWindowsClipboardTextConverter.h b/lib/platform/CMSWindowsClipboardTextConverter.h new file mode 100644 index 0000000..6f00d47 --- /dev/null +++ b/lib/platform/CMSWindowsClipboardTextConverter.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIPBOARDTEXTCONVERTER_H +#define CMSWINDOWSCLIPBOARDTEXTCONVERTER_H + +#include "CMSWindowsClipboardAnyTextConverter.h" + +//! Convert to/from locale text encoding +class CMSWindowsClipboardTextConverter : + public CMSWindowsClipboardAnyTextConverter { +public: + CMSWindowsClipboardTextConverter(); + virtual ~CMSWindowsClipboardTextConverter(); + + // IMSWindowsClipboardConverter overrides + virtual UINT getWin32Format() const; + +protected: + // CMSWindowsClipboardAnyTextConverter overrides + virtual CString doFromIClipboard(const CString&) const; + virtual CString doToIClipboard(const CString&) const; +}; + +#endif diff --git a/lib/platform/CMSWindowsClipboardUTF16Converter.cpp b/lib/platform/CMSWindowsClipboardUTF16Converter.cpp new file mode 100644 index 0000000..81b85c6 --- /dev/null +++ b/lib/platform/CMSWindowsClipboardUTF16Converter.cpp @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClipboardUTF16Converter.h" +#include "CUnicode.h" + +// +// CMSWindowsClipboardUTF16Converter +// + +CMSWindowsClipboardUTF16Converter::CMSWindowsClipboardUTF16Converter() +{ + // do nothing +} + +CMSWindowsClipboardUTF16Converter::~CMSWindowsClipboardUTF16Converter() +{ + // do nothing +} + +UINT +CMSWindowsClipboardUTF16Converter::getWin32Format() const +{ + return CF_UNICODETEXT; +} + +CString +CMSWindowsClipboardUTF16Converter::doFromIClipboard(const CString& data) const +{ + // convert and add nul terminator + return CUnicode::UTF8ToUTF16(data).append(sizeof(wchar_t), 0); +} + +CString +CMSWindowsClipboardUTF16Converter::doToIClipboard(const CString& data) const +{ + // convert and strip nul terminator + CString dst = CUnicode::UTF16ToUTF8(data); + CString::size_type n = dst.find('\0'); + if (n != CString::npos) { + dst.erase(n); + } + return dst; +} diff --git a/lib/platform/CMSWindowsClipboardUTF16Converter.h b/lib/platform/CMSWindowsClipboardUTF16Converter.h new file mode 100644 index 0000000..51f477f --- /dev/null +++ b/lib/platform/CMSWindowsClipboardUTF16Converter.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIPBOARDUTF16CONVERTER_H +#define CMSWINDOWSCLIPBOARDUTF16CONVERTER_H + +#include "CMSWindowsClipboardAnyTextConverter.h" + +//! Convert to/from UTF-16 encoding +class CMSWindowsClipboardUTF16Converter : + public CMSWindowsClipboardAnyTextConverter { +public: + CMSWindowsClipboardUTF16Converter(); + virtual ~CMSWindowsClipboardUTF16Converter(); + + // IMSWindowsClipboardConverter overrides + virtual UINT getWin32Format() const; + +protected: + // CMSWindowsClipboardAnyTextConverter overrides + virtual CString doFromIClipboard(const CString&) const; + virtual CString doToIClipboard(const CString&) const; +}; + +#endif diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp new file mode 100644 index 0000000..d68612d --- /dev/null +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -0,0 +1,1816 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsPrimaryScreen.h" +#include "CMSWindowsScreen.h" +#include "IPrimaryScreenReceiver.h" +#include "XScreen.h" +#include "CLog.h" +#include "CArch.h" +#include "CArchMiscWindows.h" +#include + +// X button stuff +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_NCXBUTTONDOWN 0x00AB +#define WM_NCXBUTTONUP 0x00AC +#define WM_NCXBUTTONDBLCLK 0x00AD +#define MOUSEEVENTF_XDOWN 0x0100 +#define MOUSEEVENTF_XUP 0x0200 +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + +// +// map virtual key id to a name +// + +static const char* g_buttonToName[] = { + "button 0", + "Left Button", + "Middle Button", + "Right Button", + "X Button 1", + "X Button 2" +}; +static const char* g_vkToName[] = { + "vk 0x00", + "Left Button", + "Right Button", + "VK_CANCEL", + "Middle Button", + "vk 0x05", + "vk 0x06", + "vk 0x07", + "VK_BACK", + "VK_TAB", + "vk 0x0a", + "vk 0x0b", + "VK_CLEAR", + "VK_RETURN", + "vk 0x0e", + "vk 0x0f", + "VK_SHIFT", + "VK_CONTROL", + "VK_MENU", + "VK_PAUSE", + "VK_CAPITAL", + "VK_KANA", + "vk 0x16", + "VK_JUNJA", + "VK_FINAL", + "VK_KANJI", + "vk 0x1a", + "VK_ESCAPE", + "VK_CONVERT", + "VK_NONCONVERT", + "VK_ACCEPT", + "VK_MODECHANGE", + "VK_SPACE", + "VK_PRIOR", + "VK_NEXT", + "VK_END", + "VK_HOME", + "VK_LEFT", + "VK_UP", + "VK_RIGHT", + "VK_DOWN", + "VK_SELECT", + "VK_PRINT", + "VK_EXECUTE", + "VK_SNAPSHOT", + "VK_INSERT", + "VK_DELETE", + "VK_HELP", + "VK_0", + "VK_1", + "VK_2", + "VK_3", + "VK_4", + "VK_5", + "VK_6", + "VK_7", + "VK_8", + "VK_9", + "vk 0x3a", + "vk 0x3b", + "vk 0x3c", + "vk 0x3d", + "vk 0x3e", + "vk 0x3f", + "vk 0x40", + "VK_A", + "VK_B", + "VK_C", + "VK_D", + "VK_E", + "VK_F", + "VK_G", + "VK_H", + "VK_I", + "VK_J", + "VK_K", + "VK_L", + "VK_M", + "VK_N", + "VK_O", + "VK_P", + "VK_Q", + "VK_R", + "VK_S", + "VK_T", + "VK_U", + "VK_V", + "VK_W", + "VK_X", + "VK_Y", + "VK_Z", + "VK_LWIN", + "VK_RWIN", + "VK_APPS", + "vk 0x5e", + "vk 0x5f", + "VK_NUMPAD0", + "VK_NUMPAD1", + "VK_NUMPAD2", + "VK_NUMPAD3", + "VK_NUMPAD4", + "VK_NUMPAD5", + "VK_NUMPAD6", + "VK_NUMPAD7", + "VK_NUMPAD8", + "VK_NUMPAD9", + "VK_MULTIPLY", + "VK_ADD", + "VK_SEPARATOR", + "VK_SUBTRACT", + "VK_DECIMAL", + "VK_DIVIDE", + "VK_F1", + "VK_F2", + "VK_F3", + "VK_F4", + "VK_F5", + "VK_F6", + "VK_F7", + "VK_F8", + "VK_F9", + "VK_F10", + "VK_F11", + "VK_F12", + "VK_F13", + "VK_F14", + "VK_F15", + "VK_F16", + "VK_F17", + "VK_F18", + "VK_F19", + "VK_F20", + "VK_F21", + "VK_F22", + "VK_F23", + "VK_F24", + "vk 0x88", + "vk 0x89", + "vk 0x8a", + "vk 0x8b", + "vk 0x8c", + "vk 0x8d", + "vk 0x8e", + "vk 0x8f", + "VK_NUMLOCK", + "VK_SCROLL", + "vk 0x92", + "vk 0x93", + "vk 0x94", + "vk 0x95", + "vk 0x96", + "vk 0x97", + "vk 0x98", + "vk 0x99", + "vk 0x9a", + "vk 0x9b", + "vk 0x9c", + "vk 0x9d", + "vk 0x9e", + "vk 0x9f", + "VK_LSHIFT", + "VK_RSHIFT", + "VK_LCONTROL", + "VK_RCONTROL", + "VK_LMENU", + "VK_RMENU", + "VK_BROWSER_BACK", + "VK_BROWSER_FORWARD", + "VK_BROWSER_REFRESH", + "VK_BROWSER_STOP", + "VK_BROWSER_SEARCH", + "VK_BROWSER_FAVORITES", + "VK_BROWSER_HOME", + "VK_VOLUME_MUTE", + "VK_VOLUME_DOWN", + "VK_VOLUME_UP", + "VK_MEDIA_NEXT_TRACK", + "VK_MEDIA_PREV_TRACK", + "VK_MEDIA_STOP", + "VK_MEDIA_PLAY_PAUSE", + "VK_LAUNCH_MAIL", + "VK_LAUNCH_MEDIA_SELECT", + "VK_LAUNCH_APP1", + "VK_LAUNCH_APP2", + "vk 0xb8", + "vk 0xb9", + "vk 0xba", + "vk 0xbb", + "vk 0xbc", + "vk 0xbd", + "vk 0xbe", + "vk 0xbf", + "vk 0xc0", + "vk 0xc1", + "vk 0xc2", + "vk 0xc3", + "vk 0xc4", + "vk 0xc5", + "vk 0xc6", + "vk 0xc7", + "vk 0xc8", + "vk 0xc9", + "vk 0xca", + "vk 0xcb", + "vk 0xcc", + "vk 0xcd", + "vk 0xce", + "vk 0xcf", + "vk 0xd0", + "vk 0xd1", + "vk 0xd2", + "vk 0xd3", + "vk 0xd4", + "vk 0xd5", + "vk 0xd6", + "vk 0xd7", + "vk 0xd8", + "vk 0xd9", + "vk 0xda", + "vk 0xdb", + "vk 0xdc", + "vk 0xdd", + "vk 0xde", + "vk 0xdf", + "vk 0xe0", + "vk 0xe1", + "vk 0xe2", + "vk 0xe3", + "vk 0xe4", + "VK_PROCESSKEY", + "vk 0xe6", + "vk 0xe7", + "vk 0xe8", + "vk 0xe9", + "vk 0xea", + "vk 0xeb", + "vk 0xec", + "vk 0xed", + "vk 0xee", + "vk 0xef", + "vk 0xf0", + "vk 0xf1", + "vk 0xf2", + "vk 0xf3", + "vk 0xf4", + "vk 0xf5", + "VK_ATTN", + "VK_CRSEL", + "VK_EXSEL", + "VK_EREOF", + "VK_PLAY", + "VK_ZOOM", + "VK_NONAME", + "VK_PA1", + "VK_OEM_CLEAR", + "vk 0xff" +}; + +// +// CMSWindowsPrimaryScreen +// + +CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( + IScreenReceiver* receiver, + IPrimaryScreenReceiver* primaryReceiver) : + CPrimaryScreen(receiver), + m_receiver(primaryReceiver), + m_is95Family(CArchMiscWindows::isWindows95Family()), + m_threadID(0), + m_mark(0), + m_markReceived(0), + m_lowLevel(false), + m_cursorThread(0) +{ + assert(m_receiver != NULL); + + // load the hook library + m_hookLibrary = LoadLibrary("synrgyhk"); + if (m_hookLibrary == NULL) { + LOG((CLOG_ERR "Failed to load hook library; synrgyhk.dll is missing")); + throw XScreenOpenFailure(); + } + m_setSides = (SetSidesFunc)GetProcAddress(m_hookLibrary, "setSides"); + m_setZone = (SetZoneFunc)GetProcAddress(m_hookLibrary, "setZone"); + m_setRelay = (SetRelayFunc)GetProcAddress(m_hookLibrary, "setRelay"); + m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install"); + m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall"); + m_init = (InitFunc)GetProcAddress(m_hookLibrary, "init"); + m_cleanup = (CleanupFunc)GetProcAddress(m_hookLibrary, "cleanup"); + if (m_setSides == NULL || + m_setZone == NULL || + m_setRelay == NULL || + m_install == NULL || + m_uninstall == NULL || + m_init == NULL || + m_cleanup == NULL) { + LOG((CLOG_ERR "Invalid hook library; use a newer synrgyhk.dll")); + FreeLibrary(m_hookLibrary); + throw XScreenOpenFailure(); + } + + // create screen + m_screen = new CMSWindowsScreen(receiver, this); +} + +CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() +{ + assert(m_hookLibrary != NULL); + + delete m_screen; + FreeLibrary(m_hookLibrary); +} + +void +CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides) +{ + m_setSides(activeSides); +} + +void +CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + // warp mouse + warpCursorNoFlush(x, y); + + // remove all input events before and including warp + MSG msg; + while (PeekMessage(&msg, NULL, SYNERGY_MSG_INPUT_FIRST, + SYNERGY_MSG_INPUT_LAST, PM_REMOVE)) { + // do nothing + } + + // save position as last position + m_x = x; + m_y = y; +} + +void +CMSWindowsPrimaryScreen::resetOptions() +{ + // no options +} + +void +CMSWindowsPrimaryScreen::setOptions(const COptionsList& /*options*/) +{ + // no options +} + +UInt32 +CMSWindowsPrimaryScreen::addOneShotTimer(double timeout) +{ + return m_screen->addOneShotTimer(timeout); +} + +KeyModifierMask +CMSWindowsPrimaryScreen::getToggleMask() const +{ + KeyModifierMask mask = 0; + + // get key state from our shadow state + if ((m_keys[VK_CAPITAL] & 0x01) != 0) + mask |= KeyModifierCapsLock; + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + mask |= KeyModifierNumLock; + if ((m_keys[VK_SCROLL] & 0x01) != 0) + mask |= KeyModifierScrollLock; + + return mask; +} + +bool +CMSWindowsPrimaryScreen::isLockedToScreen() const +{ + // use shadow keyboard state in m_keys and m_buttons + for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { + if ((m_buttons[i] & 0x80) != 0) { + LOG((CLOG_DEBUG "locked by \"%s\"", g_buttonToName[i])); + return true; + } + } + for (UInt32 i = 0; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { + if ((m_keys[i] & 0x80) != 0) { + LOG((CLOG_DEBUG "locked by \"%s\"", g_vkToName[i])); + return true; + } + } + + // not locked + return false; +} + +IScreen* +CMSWindowsPrimaryScreen::getScreen() const +{ + return m_screen; +} + +void +CMSWindowsPrimaryScreen::onScreensaver(bool activated) +{ + m_receiver->onScreensaver(activated); +} + +bool +CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) +{ + assert(event != NULL); + + const MSG* msg = &event->m_msg; + + // check if windows key is up but we think it's down. if so then + // synthesize a key release for it. we have to do this because + // if the user presses and releases a windows key without pressing + // any other key when its down then windows will eat the key + // release. if we don't detect that an synthesize the release + // then the user will be locked to the screen and the client won't + // take the usual windows key release action (which on windows is + // to show the start menu). + // + // we can use GetKeyState() to check the state of the windows keys + // because, event though the key release is not reported to us, + // the event is processed and the keyboard state updated by the + // system. since the key could go up at any time we'll check the + // state on every event. only check on windows 95 family since + // NT family reports the key release as usual. obviously we skip + // this if the event is for the windows key itself. + if (m_is95Family) { + if ((m_keys[VK_LWIN] & 0x80) != 0 && + (GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 && + !(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_LWIN)) { + // compute appropriate parameters for fake event + WPARAM wParam = VK_LWIN; + LPARAM lParam = 0xc1000000; + lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); + + // process as if it were a key up + bool altgr; + KeyModifierMask mask; + KeyButton button = static_cast( + (lParam & 0x00ff0000u) >> 16); + const KeyID key = mapKey(wParam, lParam, &mask, &altgr); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyUp(key, mask, button); + updateKey(wParam, false); + } + if ((m_keys[VK_RWIN] & 0x80) != 0 && + (GetAsyncKeyState(VK_RWIN) & 0x8000) == 0 && + !(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_RWIN)) { + // compute appropriate parameters for fake event + WPARAM wParam = VK_RWIN; + LPARAM lParam = 0xc1000000; + lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); + + // process as if it were a key up + bool altgr; + KeyModifierMask mask; + KeyButton button = static_cast( + (lParam & 0x00ff0000u) >> 16); + const KeyID key = mapKey(wParam, lParam, &mask, &altgr); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyUp(key, mask, button); + updateKey(wParam, false); + } + } + + // handle event + switch (msg->message) { + case SYNERGY_MSG_MARK: + m_markReceived = msg->wParam; + return true; + + case SYNERGY_MSG_KEY: + // ignore message if posted prior to last mark change + if (!ignore()) { + WPARAM wParam = msg->wParam; + LPARAM lParam = msg->lParam; + + // check for ctrl+alt+del emulation + if ((wParam == VK_PAUSE || wParam == VK_CANCEL) && + (m_keys[VK_CONTROL] & 0x80) != 0 && + (m_keys[VK_MENU] & 0x80) != 0) { + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + wParam = VK_DELETE; + lParam &= 0xffff0000; + lParam |= 0x00000001; + } + + // process key normally + bool altgr; + KeyModifierMask mask; + const KeyID key = mapKey(wParam, lParam, &mask, &altgr); + KeyButton button = static_cast( + (lParam & 0x00ff0000u) >> 16); + if (key != kKeyNone && key != kKeyMultiKey) { + if ((lParam & 0x80000000) == 0) { + // key press + + // if AltGr required for this key then make sure + // the ctrl and alt keys are *not* down on the + // client. windows simulates AltGr with ctrl and + // alt for some inexplicable reason and clients + // will get confused if they see mode switch and + // ctrl and alt. we'll also need to put ctrl and + // alt back the way there were after we simulate + // the key. + bool ctrlL = ((m_keys[VK_LCONTROL] & 0x80) != 0); + bool ctrlR = ((m_keys[VK_RCONTROL] & 0x80) != 0); + bool altL = ((m_keys[VK_LMENU] & 0x80) != 0); + bool altR = ((m_keys[VK_RMENU] & 0x80) != 0); + if (altgr) { + KeyID key; + KeyButton button; + KeyModifierMask mask2 = (mask & + ~(KeyModifierControl | + KeyModifierAlt | + KeyModifierModeSwitch)); + if (ctrlL) { + key = kKeyControl_L; + button = mapKeyToScanCode(VK_LCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + if (ctrlR) { + key = kKeyControl_R; + button = mapKeyToScanCode(VK_RCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + if (altL) { + key = kKeyAlt_L; + button = mapKeyToScanCode(VK_LMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + if (altR) { + key = kKeyAlt_R; + button = mapKeyToScanCode(VK_RMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + } + + // send key + const bool wasDown = ((lParam & 0x40000000) != 0); + SInt32 repeat = (SInt32)(lParam & 0xffff); + if (!wasDown) { + LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyDown(key, mask, button); + if (repeat > 0) { + --repeat; + } + } + if (repeat >= 1) { + LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button)); + m_receiver->onKeyRepeat(key, mask, repeat, button); + } + + // restore ctrl and alt state + if (altgr) { + KeyID key; + KeyButton button; + KeyModifierMask mask2 = (mask & + ~(KeyModifierControl | + KeyModifierAlt | + KeyModifierModeSwitch)); + if (ctrlL) { + key = kKeyControl_L; + button = mapKeyToScanCode(VK_LCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierControl; + } + if (ctrlR) { + key = kKeyControl_R; + button = mapKeyToScanCode(VK_RCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierControl; + } + if (altL) { + key = kKeyAlt_L; + button = mapKeyToScanCode(VK_LMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierAlt; + } + if (altR) { + key = kKeyAlt_R; + button = mapKeyToScanCode(VK_RMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierAlt; + } + } + } + else { + // key release. if the key isn't down according to + // our table then we never got the key press event + // for it. if it's not a modifier key then we'll + // synthesize the press first. only do this on + // the windows 95 family, which eats certain special + // keys like alt+tab, ctrl+esc, etc. + if (m_is95Family && !isModifier(msg->wParam) && + (m_keys[msg->wParam] & 0x80) == 0) { + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyDown(key, mask, button); + updateKey(msg->wParam, true); + } + + // do key up + LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyUp(key, mask, button); + } + } + else { + LOG((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", msg->wParam, msg->lParam)); + } + } + + // keep our shadow key state up to date + updateKey(msg->wParam, ((msg->lParam & 0x80000000) == 0)); + + return true; + + case SYNERGY_MSG_MOUSE_BUTTON: { + // get which button + bool pressed = false; + const ButtonID button = mapButton(msg->wParam, msg->lParam); + + // ignore message if posted prior to last mark change + if (!ignore()) { + switch (msg->wParam) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_XBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_XBUTTONDBLCLK: + case WM_NCLBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCXBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCMBUTTONDBLCLK: + case WM_NCRBUTTONDBLCLK: + case WM_NCXBUTTONDBLCLK: + LOG((CLOG_DEBUG1 "event: button press button=%d", button)); + if (button != kButtonNone) { + m_receiver->onMouseDown(button); + } + pressed = true; + break; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_XBUTTONUP: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONUP: + case WM_NCRBUTTONUP: + case WM_NCXBUTTONUP: + LOG((CLOG_DEBUG1 "event: button release button=%d", button)); + if (button != kButtonNone) { + m_receiver->onMouseUp(button); + } + pressed = false; + break; + } + } + + // keep our shadow key state up to date + if (button >= kButtonLeft && button <= kButtonExtra0 + 1) { + if (pressed) { + m_buttons[button] |= 0x80; + } + else { + m_buttons[button] &= ~0x80; + } + } + + return true; + } + + case SYNERGY_MSG_MOUSE_WHEEL: + // ignore message if posted prior to last mark change + if (!ignore()) { + LOG((CLOG_DEBUG1 "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); + m_receiver->onMouseWheel(msg->wParam); + } + return true; + + case SYNERGY_MSG_PRE_WARP: + { + // save position to compute delta of next motion + m_x = static_cast(msg->wParam); + m_y = static_cast(msg->lParam); + + // we warped the mouse. discard events until we find the + // matching post warp event. see warpCursorNoFlush() for + // where the events are sent. we discard the matching + // post warp event and can be sure we've skipped the warp + // event. + MSG msg; + do { + GetMessage(&msg, NULL, SYNERGY_MSG_MOUSE_MOVE, + SYNERGY_MSG_POST_WARP); + } while (msg.message != SYNERGY_MSG_POST_WARP); + + return true; + } + + case SYNERGY_MSG_POST_WARP: + LOG((CLOG_WARN "unmatched post warp")); + return true; + + case SYNERGY_MSG_MOUSE_MOVE: + // ignore message if posted prior to last mark change + if (!ignore()) { + // compute motion delta (relative to the last known + // mouse position) + SInt32 x = static_cast(msg->wParam) - m_x; + SInt32 y = static_cast(msg->lParam) - m_y; + + // save position to compute delta of next motion + m_x = static_cast(msg->wParam); + m_y = static_cast(msg->lParam); + + if (!isActive()) { + // motion on primary screen + if (x != 0 || y != 0) { + m_receiver->onMouseMovePrimary(m_x, m_y); + } + } + else { + // motion on secondary screen. warp mouse back to + // center. + if (x != 0 || y != 0) { + // back to center + warpCursorNoFlush(m_xCenter, m_yCenter); + + // examine the motion. if it's about the distance + // from the center of the screen to an edge then + // it's probably a bogus motion that we want to + // ignore (see warpCursorNoFlush() for a further + // description). + static SInt32 bogusZoneSize = 10; + SInt32 x0, y0, w0, h0; + m_screen->getShape(x0, y0, w0, h0); + if (-x + bogusZoneSize > m_xCenter - x0 || + x + bogusZoneSize > x0 + w0 - m_xCenter || + -y + bogusZoneSize > m_yCenter - y0 || + y + bogusZoneSize > y0 + h0 - m_yCenter) { + LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y)); + } + else { + // send motion + m_receiver->onMouseMoveSecondary(x, y); + } + } + } + } + return true; + } + + return false; +} + +bool +CMSWindowsPrimaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + + const MSG& msg = event->m_msg; + switch (msg.message) { + case WM_DISPLAYCHANGE: + // recompute center pixel of primary screen + m_screen->getCursorCenter(m_xCenter, m_yCenter); + + // warp mouse to center if active + if (isActive()) { + warpCursorToCenter(); + } + + // tell hook about resize if not active + else { + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + m_setZone(x, y, w, h, getJumpZoneSize()); + } + return true; + } + + return false; +} + +void +CMSWindowsPrimaryScreen::onOneShotTimerExpired(UInt32 id) +{ + m_receiver->onOneShotTimerExpired(id); +} + +SInt32 +CMSWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + +void +CMSWindowsPrimaryScreen::postCreateWindow(HWND) +{ + // install hooks + switch (m_install()) { + case kHOOK_FAILED: + // FIXME -- can't install hook so we won't work; report error + m_lowLevel = false; + break; + + case kHOOK_OKAY: + m_lowLevel = false; + break; + + case kHOOK_OKAY_LL: + m_lowLevel = true; + break; + } + + if (!isActive()) { + // watch jump zones + m_setRelay(false); + + // all messages prior to now are invalid + nextMark(); + } +} + +void +CMSWindowsPrimaryScreen::preDestroyWindow(HWND) +{ + // uninstall hooks + m_uninstall(); +} + +void +CMSWindowsPrimaryScreen::onAccessibleDesktop() +{ + // get the current keyboard state + updateKeys(); +} + +void +CMSWindowsPrimaryScreen::onPreMainLoop() +{ + // must call mainLoop() from same thread as open() + assert(m_threadID == GetCurrentThreadId()); +} + +void +CMSWindowsPrimaryScreen::onPreOpen() +{ + // initialize hook library + m_threadID = GetCurrentThreadId(); + if (m_init(m_threadID) == 0) { + LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?")); + throw XScreenOpenFailure(); + } +} + +void +CMSWindowsPrimaryScreen::onPostOpen() +{ + // get cursor info + m_screen->getCursorPos(m_x, m_y); + m_screen->getCursorCenter(m_xCenter, m_yCenter); + + // set jump zones + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + m_setZone(x, y, w, h, getJumpZoneSize()); + + // initialize marks + m_mark = 0; + m_markReceived = 0; + nextMark(); +} + +void +CMSWindowsPrimaryScreen::onPostClose() +{ + m_cleanup(); + m_threadID = 0; +} + +void +CMSWindowsPrimaryScreen::onPreEnter() +{ + // show cursor if we hid it + if (m_cursorThread != 0) { + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, TRUE); + } + ShowCursor(TRUE); + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, FALSE); + } + m_cursorThread = 0; + } + + // enable ctrl+alt+del, alt+tab, etc + if (m_is95Family) { + DWORD dummy = 0; + SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, &dummy, 0); + } + + // watch jump zones + m_setRelay(false); +} + +void +CMSWindowsPrimaryScreen::onPostEnter() +{ + // all messages prior to now are invalid + nextMark(); +} + +void +CMSWindowsPrimaryScreen::onPreLeave() +{ + // all messages prior to now are invalid + nextMark(); +} + +void +CMSWindowsPrimaryScreen::onPostLeave(bool success) +{ + if (success) { + // relay all mouse and keyboard events + m_setRelay(true); + + // disable ctrl+alt+del, alt+tab, etc + if (m_is95Family) { + DWORD dummy = 0; + SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); + } + + // hide the cursor if using low level hooks + if (m_lowLevel) { + HWND hwnd = GetForegroundWindow(); + m_cursorThread = GetWindowThreadProcessId(hwnd, NULL); + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, TRUE); + } + ShowCursor(FALSE); + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, FALSE); + } + } + } +} + +void +CMSWindowsPrimaryScreen::createWindow() +{ + // open the desktop and the window + m_window = m_screen->openDesktop(); + if (m_window == NULL) { + throw XScreenOpenFailure(); + } + + // we don't ever want our window to activate + EnableWindow(m_window, FALSE); +} + +void +CMSWindowsPrimaryScreen::destroyWindow() +{ + // close the desktop and the window + m_screen->closeDesktop(); +} + +bool +CMSWindowsPrimaryScreen::showWindow() +{ + // we don't need a window to capture input but we need a window + // to hide the cursor when using low-level hooks. do not try to + // take the activation; we want the currently active window to + // stay active. + if (m_lowLevel) { + SetWindowPos(m_window, HWND_TOPMOST, m_xCenter, m_yCenter, 1, 1, + SWP_NOACTIVATE); + ShowWindow(m_window, SW_SHOWNA); + } + return true; +} + +void +CMSWindowsPrimaryScreen::hideWindow() +{ + // hide our window + if (m_lowLevel) { + ShowWindow(m_window, SW_HIDE); + } +} + +void +CMSWindowsPrimaryScreen::warpCursorToCenter() +{ + warpCursor(m_xCenter, m_yCenter); +} + +void +CMSWindowsPrimaryScreen::warpCursorNoFlush(SInt32 x, SInt32 y) +{ + // send an event that we can recognize before the mouse warp + PostThreadMessage(m_threadID, SYNERGY_MSG_PRE_WARP, x, y); + + // warp mouse. hopefully this inserts a mouse motion event + // between the previous message and the following message. + SetCursorPos(x, y); + + // yield the CPU. there's a race condition when warping: + // a hardware mouse event occurs + // the mouse hook is not called because that process doesn't have the CPU + // we send PRE_WARP, SetCursorPos(), send POST_WARP + // we process all of those events and update m_x, m_y + // we finish our time slice + // the hook is called + // the hook sends us a mouse event from the pre-warp position + // we get the CPU + // we compute a bogus warp + // we need the hook to process all mouse events that occur + // before we warp before we do the warp but i'm not sure how + // to guarantee that. yielding the CPU here may reduce the + // chance of undesired behavior. we'll also check for very + // large motions that look suspiciously like about half width + // or height of the screen. + ARCH->sleep(0.0); + + // send an event that we can recognize after the mouse warp + PostThreadMessage(m_threadID, SYNERGY_MSG_POST_WARP, 0, 0); +} + +void +CMSWindowsPrimaryScreen::nextMark() +{ + // next mark + ++m_mark; + + // mark point in message queue where the mark was changed + PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); +} + +bool +CMSWindowsPrimaryScreen::ignore() const +{ + return (m_mark != m_markReceived); +} + +// map virtual keys to synergy key enumeration. use extended keyboard +// bit to distinguish some keys. +static const KeyID g_virtualKey[][2] = +{ + /* 0x00 */ kKeyNone, kKeyNone, // reserved + /* 0x01 */ kKeyNone, kKeyNone, // VK_LBUTTON + /* 0x02 */ kKeyNone, kKeyNone, // VK_RBUTTON + /* 0x03 */ kKeyNone, kKeyBreak, // VK_CANCEL + /* 0x04 */ kKeyNone, kKeyNone, // VK_MBUTTON + /* 0x05 */ kKeyNone, kKeyNone, // undefined + /* 0x06 */ kKeyNone, kKeyNone, // undefined + /* 0x07 */ kKeyNone, kKeyNone, // undefined + /* 0x08 */ kKeyBackSpace, kKeyNone, // VK_BACK + /* 0x09 */ kKeyTab, kKeyNone, // VK_TAB + /* 0x0a */ kKeyNone, kKeyNone, // undefined + /* 0x0b */ kKeyNone, kKeyNone, // undefined + /* 0x0c */ kKeyClear, kKeyClear, // VK_CLEAR + /* 0x0d */ kKeyReturn, kKeyKP_Enter, // VK_RETURN + /* 0x0e */ kKeyNone, kKeyNone, // undefined + /* 0x0f */ kKeyNone, kKeyNone, // undefined + /* 0x10 */ kKeyShift_L, kKeyShift_R, // VK_SHIFT + /* 0x11 */ kKeyControl_L, kKeyControl_R, // VK_CONTROL + /* 0x12 */ kKeyAlt_L, kKeyAlt_R, // VK_MENU + /* 0x13 */ kKeyPause, kKeyNone, // VK_PAUSE + /* 0x14 */ kKeyCapsLock, kKeyNone, // VK_CAPITAL + /* 0x15 */ kKeyNone, kKeyNone, // VK_KANA + /* 0x16 */ kKeyNone, kKeyNone, // VK_HANGUL + /* 0x17 */ kKeyNone, kKeyNone, // VK_JUNJA + /* 0x18 */ kKeyNone, kKeyNone, // VK_FINAL + /* 0x19 */ kKeyNone, kKeyNone, // VK_KANJI + /* 0x1a */ kKeyNone, kKeyNone, // undefined + /* 0x1b */ kKeyEscape, kKeyNone, // VK_ESCAPE + /* 0x1c */ kKeyNone, kKeyNone, // VK_CONVERT + /* 0x1d */ kKeyNone, kKeyNone, // VK_NONCONVERT + /* 0x1e */ kKeyNone, kKeyNone, // VK_ACCEPT + /* 0x1f */ kKeyNone, kKeyNone, // VK_MODECHANGE + /* 0x20 */ kKeyNone, kKeyNone, // VK_SPACE + /* 0x21 */ kKeyKP_PageUp, kKeyPageUp, // VK_PRIOR + /* 0x22 */ kKeyKP_PageDown, kKeyPageDown, // VK_NEXT + /* 0x23 */ kKeyKP_End, kKeyEnd, // VK_END + /* 0x24 */ kKeyKP_Home, kKeyHome, // VK_HOME + /* 0x25 */ kKeyKP_Left, kKeyLeft, // VK_LEFT + /* 0x26 */ kKeyKP_Up, kKeyUp, // VK_UP + /* 0x27 */ kKeyKP_Right, kKeyRight, // VK_RIGHT + /* 0x28 */ kKeyKP_Down, kKeyDown, // VK_DOWN + /* 0x29 */ kKeySelect, kKeySelect, // VK_SELECT + /* 0x2a */ kKeyNone, kKeyNone, // VK_PRINT + /* 0x2b */ kKeyExecute, kKeyExecute, // VK_EXECUTE + /* 0x2c */ kKeyPrint, kKeyPrint, // VK_SNAPSHOT + /* 0x2d */ kKeyKP_Insert, kKeyInsert, // VK_INSERT + /* 0x2e */ kKeyKP_Delete, kKeyDelete, // VK_DELETE + /* 0x2f */ kKeyHelp, kKeyHelp, // VK_HELP + /* 0x30 */ kKeyNone, kKeyNone, // VK_0 + /* 0x31 */ kKeyNone, kKeyNone, // VK_1 + /* 0x32 */ kKeyNone, kKeyNone, // VK_2 + /* 0x33 */ kKeyNone, kKeyNone, // VK_3 + /* 0x34 */ kKeyNone, kKeyNone, // VK_4 + /* 0x35 */ kKeyNone, kKeyNone, // VK_5 + /* 0x36 */ kKeyNone, kKeyNone, // VK_6 + /* 0x37 */ kKeyNone, kKeyNone, // VK_7 + /* 0x38 */ kKeyNone, kKeyNone, // VK_8 + /* 0x39 */ kKeyNone, kKeyNone, // VK_9 + /* 0x3a */ kKeyNone, kKeyNone, // undefined + /* 0x3b */ kKeyNone, kKeyNone, // undefined + /* 0x3c */ kKeyNone, kKeyNone, // undefined + /* 0x3d */ kKeyNone, kKeyNone, // undefined + /* 0x3e */ kKeyNone, kKeyNone, // undefined + /* 0x3f */ kKeyNone, kKeyNone, // undefined + /* 0x40 */ kKeyNone, kKeyNone, // undefined + /* 0x41 */ kKeyNone, kKeyNone, // VK_A + /* 0x42 */ kKeyNone, kKeyNone, // VK_B + /* 0x43 */ kKeyNone, kKeyNone, // VK_C + /* 0x44 */ kKeyNone, kKeyNone, // VK_D + /* 0x45 */ kKeyNone, kKeyNone, // VK_E + /* 0x46 */ kKeyNone, kKeyNone, // VK_F + /* 0x47 */ kKeyNone, kKeyNone, // VK_G + /* 0x48 */ kKeyNone, kKeyNone, // VK_H + /* 0x49 */ kKeyNone, kKeyNone, // VK_I + /* 0x4a */ kKeyNone, kKeyNone, // VK_J + /* 0x4b */ kKeyNone, kKeyNone, // VK_K + /* 0x4c */ kKeyNone, kKeyNone, // VK_L + /* 0x4d */ kKeyNone, kKeyNone, // VK_M + /* 0x4e */ kKeyNone, kKeyNone, // VK_N + /* 0x4f */ kKeyNone, kKeyNone, // VK_O + /* 0x50 */ kKeyNone, kKeyNone, // VK_P + /* 0x51 */ kKeyNone, kKeyNone, // VK_Q + /* 0x52 */ kKeyNone, kKeyNone, // VK_R + /* 0x53 */ kKeyNone, kKeyNone, // VK_S + /* 0x54 */ kKeyNone, kKeyNone, // VK_T + /* 0x55 */ kKeyNone, kKeyNone, // VK_U + /* 0x56 */ kKeyNone, kKeyNone, // VK_V + /* 0x57 */ kKeyNone, kKeyNone, // VK_W + /* 0x58 */ kKeyNone, kKeyNone, // VK_X + /* 0x59 */ kKeyNone, kKeyNone, // VK_Y + /* 0x5a */ kKeyNone, kKeyNone, // VK_Z + /* 0x5b */ kKeyNone, kKeySuper_L, // VK_LWIN + /* 0x5c */ kKeyNone, kKeySuper_R, // VK_RWIN + /* 0x5d */ kKeyMenu, kKeyMenu, // VK_APPS + /* 0x5e */ kKeyNone, kKeyNone, // undefined + /* 0x5f */ kKeyNone, kKeyNone, // undefined + /* 0x60 */ kKeyKP_0, kKeyNone, // VK_NUMPAD0 + /* 0x61 */ kKeyKP_1, kKeyNone, // VK_NUMPAD1 + /* 0x62 */ kKeyKP_2, kKeyNone, // VK_NUMPAD2 + /* 0x63 */ kKeyKP_3, kKeyNone, // VK_NUMPAD3 + /* 0x64 */ kKeyKP_4, kKeyNone, // VK_NUMPAD4 + /* 0x65 */ kKeyKP_5, kKeyNone, // VK_NUMPAD5 + /* 0x66 */ kKeyKP_6, kKeyNone, // VK_NUMPAD6 + /* 0x67 */ kKeyKP_7, kKeyNone, // VK_NUMPAD7 + /* 0x68 */ kKeyKP_8, kKeyNone, // VK_NUMPAD8 + /* 0x69 */ kKeyKP_9, kKeyNone, // VK_NUMPAD9 + /* 0x6a */ kKeyKP_Multiply, kKeyNone, // VK_MULTIPLY + /* 0x6b */ kKeyKP_Add, kKeyNone, // VK_ADD + /* 0x6c */ kKeyKP_Separator,kKeyKP_Separator,// VK_SEPARATOR + /* 0x6d */ kKeyKP_Subtract, kKeyNone, // VK_SUBTRACT + /* 0x6e */ kKeyKP_Decimal, kKeyNone, // VK_DECIMAL + /* 0x6f */ kKeyNone, kKeyKP_Divide, // VK_DIVIDE + /* 0x70 */ kKeyF1, kKeyNone, // VK_F1 + /* 0x71 */ kKeyF2, kKeyNone, // VK_F2 + /* 0x72 */ kKeyF3, kKeyNone, // VK_F3 + /* 0x73 */ kKeyF4, kKeyNone, // VK_F4 + /* 0x74 */ kKeyF5, kKeyNone, // VK_F5 + /* 0x75 */ kKeyF6, kKeyNone, // VK_F6 + /* 0x76 */ kKeyF7, kKeyNone, // VK_F7 + /* 0x77 */ kKeyF8, kKeyNone, // VK_F8 + /* 0x78 */ kKeyF9, kKeyNone, // VK_F9 + /* 0x79 */ kKeyF10, kKeyNone, // VK_F10 + /* 0x7a */ kKeyF11, kKeyNone, // VK_F11 + /* 0x7b */ kKeyF12, kKeyNone, // VK_F12 + /* 0x7c */ kKeyF13, kKeyF13, // VK_F13 + /* 0x7d */ kKeyF14, kKeyF14, // VK_F14 + /* 0x7e */ kKeyF15, kKeyF15, // VK_F15 + /* 0x7f */ kKeyF16, kKeyF16, // VK_F16 + /* 0x80 */ kKeyF17, kKeyF17, // VK_F17 + /* 0x81 */ kKeyF18, kKeyF18, // VK_F18 + /* 0x82 */ kKeyF19, kKeyF19, // VK_F19 + /* 0x83 */ kKeyF20, kKeyF20, // VK_F20 + /* 0x84 */ kKeyF21, kKeyF21, // VK_F21 + /* 0x85 */ kKeyF22, kKeyF22, // VK_F22 + /* 0x86 */ kKeyF23, kKeyF23, // VK_F23 + /* 0x87 */ kKeyF24, kKeyF24, // VK_F24 + /* 0x88 */ kKeyNone, kKeyNone, // unassigned + /* 0x89 */ kKeyNone, kKeyNone, // unassigned + /* 0x8a */ kKeyNone, kKeyNone, // unassigned + /* 0x8b */ kKeyNone, kKeyNone, // unassigned + /* 0x8c */ kKeyNone, kKeyNone, // unassigned + /* 0x8d */ kKeyNone, kKeyNone, // unassigned + /* 0x8e */ kKeyNone, kKeyNone, // unassigned + /* 0x8f */ kKeyNone, kKeyNone, // unassigned + /* 0x90 */ kKeyNumLock, kKeyNumLock, // VK_NUMLOCK + /* 0x91 */ kKeyScrollLock, kKeyNone, // VK_SCROLL + /* 0x92 */ kKeyNone, kKeyNone, // unassigned + /* 0x93 */ kKeyNone, kKeyNone, // unassigned + /* 0x94 */ kKeyNone, kKeyNone, // unassigned + /* 0x95 */ kKeyNone, kKeyNone, // unassigned + /* 0x96 */ kKeyNone, kKeyNone, // unassigned + /* 0x97 */ kKeyNone, kKeyNone, // unassigned + /* 0x98 */ kKeyNone, kKeyNone, // unassigned + /* 0x99 */ kKeyNone, kKeyNone, // unassigned + /* 0x9a */ kKeyNone, kKeyNone, // unassigned + /* 0x9b */ kKeyNone, kKeyNone, // unassigned + /* 0x9c */ kKeyNone, kKeyNone, // unassigned + /* 0x9d */ kKeyNone, kKeyNone, // unassigned + /* 0x9e */ kKeyNone, kKeyNone, // unassigned + /* 0x9f */ kKeyNone, kKeyNone, // unassigned + /* 0xa0 */ kKeyShift_L, kKeyShift_L, // VK_LSHIFT + /* 0xa1 */ kKeyShift_R, kKeyShift_R, // VK_RSHIFT + /* 0xa2 */ kKeyControl_L, kKeyControl_L, // VK_LCONTROL + /* 0xa3 */ kKeyControl_R, kKeyControl_R, // VK_RCONTROL + /* 0xa4 */ kKeyAlt_L, kKeyAlt_L, // VK_LMENU + /* 0xa5 */ kKeyAlt_R, kKeyAlt_R, // VK_RMENU + /* 0xa6 */ kKeyNone, kKeyWWWBack, // VK_BROWSER_BACK + /* 0xa7 */ kKeyNone, kKeyWWWForward, // VK_BROWSER_FORWARD + /* 0xa8 */ kKeyNone, kKeyWWWRefresh, // VK_BROWSER_REFRESH + /* 0xa9 */ kKeyNone, kKeyWWWStop, // VK_BROWSER_STOP + /* 0xaa */ kKeyNone, kKeyWWWSearch, // VK_BROWSER_SEARCH + /* 0xab */ kKeyNone, kKeyWWWFavorites, // VK_BROWSER_FAVORITES + /* 0xac */ kKeyNone, kKeyWWWHome, // VK_BROWSER_HOME + /* 0xad */ kKeyNone, kKeyAudioMute, // VK_VOLUME_MUTE + /* 0xae */ kKeyNone, kKeyAudioDown, // VK_VOLUME_DOWN + /* 0xaf */ kKeyNone, kKeyAudioUp, // VK_VOLUME_UP + /* 0xb0 */ kKeyNone, kKeyAudioNext, // VK_MEDIA_NEXT_TRACK + /* 0xb1 */ kKeyNone, kKeyAudioPrev, // VK_MEDIA_PREV_TRACK + /* 0xb2 */ kKeyNone, kKeyAudioStop, // VK_MEDIA_STOP + /* 0xb3 */ kKeyNone, kKeyAudioPlay, // VK_MEDIA_PLAY_PAUSE + /* 0xb4 */ kKeyNone, kKeyAppMail, // VK_LAUNCH_MAIL + /* 0xb5 */ kKeyNone, kKeyAppMedia, // VK_LAUNCH_MEDIA_SELECT + /* 0xb6 */ kKeyNone, kKeyAppUser1, // VK_LAUNCH_APP1 + /* 0xb7 */ kKeyNone, kKeyAppUser2, // VK_LAUNCH_APP2 + /* 0xb8 */ kKeyNone, kKeyNone, // unassigned + /* 0xb9 */ kKeyNone, kKeyNone, // unassigned + /* 0xba */ kKeyNone, kKeyNone, // OEM specific + /* 0xbb */ kKeyNone, kKeyNone, // OEM specific + /* 0xbc */ kKeyNone, kKeyNone, // OEM specific + /* 0xbd */ kKeyNone, kKeyNone, // OEM specific + /* 0xbe */ kKeyNone, kKeyNone, // OEM specific + /* 0xbf */ kKeyNone, kKeyNone, // OEM specific + /* 0xc0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xc1 */ kKeyNone, kKeyNone, // unassigned + /* 0xc2 */ kKeyNone, kKeyNone, // unassigned + /* 0xc3 */ kKeyNone, kKeyNone, // unassigned + /* 0xc4 */ kKeyNone, kKeyNone, // unassigned + /* 0xc5 */ kKeyNone, kKeyNone, // unassigned + /* 0xc6 */ kKeyNone, kKeyNone, // unassigned + /* 0xc7 */ kKeyNone, kKeyNone, // unassigned + /* 0xc8 */ kKeyNone, kKeyNone, // unassigned + /* 0xc9 */ kKeyNone, kKeyNone, // unassigned + /* 0xca */ kKeyNone, kKeyNone, // unassigned + /* 0xcb */ kKeyNone, kKeyNone, // unassigned + /* 0xcc */ kKeyNone, kKeyNone, // unassigned + /* 0xcd */ kKeyNone, kKeyNone, // unassigned + /* 0xce */ kKeyNone, kKeyNone, // unassigned + /* 0xcf */ kKeyNone, kKeyNone, // unassigned + /* 0xd0 */ kKeyNone, kKeyNone, // unassigned + /* 0xd1 */ kKeyNone, kKeyNone, // unassigned + /* 0xd2 */ kKeyNone, kKeyNone, // unassigned + /* 0xd3 */ kKeyNone, kKeyNone, // unassigned + /* 0xd4 */ kKeyNone, kKeyNone, // unassigned + /* 0xd5 */ kKeyNone, kKeyNone, // unassigned + /* 0xd6 */ kKeyNone, kKeyNone, // unassigned + /* 0xd7 */ kKeyNone, kKeyNone, // unassigned + /* 0xd8 */ kKeyNone, kKeyNone, // unassigned + /* 0xd9 */ kKeyNone, kKeyNone, // unassigned + /* 0xda */ kKeyNone, kKeyNone, // unassigned + /* 0xdb */ kKeyNone, kKeyNone, // OEM specific + /* 0xdc */ kKeyNone, kKeyNone, // OEM specific + /* 0xdd */ kKeyNone, kKeyNone, // OEM specific + /* 0xde */ kKeyNone, kKeyNone, // OEM specific + /* 0xdf */ kKeyNone, kKeyNone, // OEM specific + /* 0xe0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe1 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe2 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe3 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe4 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe5 */ kKeyNone, kKeyNone, // unassigned + /* 0xe6 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe7 */ kKeyNone, kKeyNone, // unassigned + /* 0xe8 */ kKeyNone, kKeyNone, // unassigned + /* 0xe9 */ kKeyNone, kKeyNone, // OEM specific + /* 0xea */ kKeyNone, kKeyNone, // OEM specific + /* 0xeb */ kKeyNone, kKeyNone, // OEM specific + /* 0xec */ kKeyNone, kKeyNone, // OEM specific + /* 0xed */ kKeyNone, kKeyNone, // OEM specific + /* 0xee */ kKeyNone, kKeyNone, // OEM specific + /* 0xef */ kKeyNone, kKeyNone, // OEM specific + /* 0xf0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf1 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf2 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf3 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf4 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf5 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf6 */ kKeyNone, kKeyNone, // VK_ATTN + /* 0xf7 */ kKeyNone, kKeyNone, // VK_CRSEL + /* 0xf8 */ kKeyNone, kKeyNone, // VK_EXSEL + /* 0xf9 */ kKeyNone, kKeyNone, // VK_EREOF + /* 0xfa */ kKeyNone, kKeyNone, // VK_PLAY + /* 0xfb */ kKeyNone, kKeyNone, // VK_ZOOM + /* 0xfc */ kKeyNone, kKeyNone, // reserved + /* 0xfd */ kKeyNone, kKeyNone, // VK_PA1 + /* 0xfe */ kKeyNone, kKeyNone, // VK_OEM_CLEAR + /* 0xff */ kKeyNone, kKeyNone // reserved +}; + +KeyID +CMSWindowsPrimaryScreen::mapKey( + WPARAM vkCode, + LPARAM info, + KeyModifierMask* maskOut, + bool* altgr) +{ + // note: known microsoft bugs + // Q72583 -- MapVirtualKey() maps keypad keys incorrectly + // 95,98: num pad vk code -> invalid scan code + // 95,98,NT4: num pad scan code -> bad vk code except + // SEPARATOR, MULTIPLY, SUBTRACT, ADD + + assert(maskOut != NULL); + assert(altgr != NULL); + + // get the scan code and the extended keyboard flag + UINT scanCode = static_cast((info & 0x00ff0000u) >> 16); + int extended = ((info & 0x01000000) == 0) ? 0 : 1; + LOG((CLOG_DEBUG1 "key vk=%d info=0x%08x ext=%d scan=%d", vkCode, info, extended, scanCode)); + + // handle some keys via table lookup + char c = 0; + KeyID id = g_virtualKey[vkCode][extended]; + if (id == kKeyNone) { + // not in table + + // save the control state then clear it. ToAscii() maps ctrl+letter + // to the corresponding control code and ctrl+backspace to delete. + // we don't want that translation so we clear the control modifier + // state. however, if we want to simulate AltGr (which is ctrl+alt) + // then we must not clear it. + BYTE lControl = m_keys[VK_LCONTROL]; + BYTE rControl = m_keys[VK_RCONTROL]; + BYTE control = m_keys[VK_CONTROL]; + BYTE lMenu = m_keys[VK_LMENU]; + BYTE menu = m_keys[VK_MENU]; + if ((control & 0x80) == 0 || (menu & 0x80) == 0) { + m_keys[VK_LCONTROL] = 0; + m_keys[VK_RCONTROL] = 0; + m_keys[VK_CONTROL] = 0; + } + else { + m_keys[VK_LCONTROL] = 0x80; + m_keys[VK_CONTROL] = 0x80; + m_keys[VK_LMENU] = 0x80; + m_keys[VK_MENU] = 0x80; + } + + // convert to ascii + WORD ascii; + int result = ToAscii(vkCode, scanCode, m_keys, &ascii, + ((menu & 0x80) == 0) ? 0 : 1); + + // restore control state + m_keys[VK_LCONTROL] = lControl; + m_keys[VK_RCONTROL] = rControl; + m_keys[VK_CONTROL] = control; + m_keys[VK_LMENU] = lMenu; + m_keys[VK_MENU] = menu; + + // if result is less than zero then it was a dead key. leave it + // there. + if (result < 0) { + id = kKeyMultiKey; + } + + // if result is 1 then the key was succesfully converted + else if (result == 1) { + c = static_cast(ascii & 0xff); + if (ascii >= 0x80) { + // character is not really ASCII. instead it's some + // character in the current ANSI code page. try to + // convert that to a Unicode character. if we fail + // then use the single byte character as is. + char src = c; + wchar_t unicode; + if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, + &src, 1, &unicode, 1) > 0) { + id = static_cast(unicode); + } + else { + id = static_cast(ascii & 0x00ff); + } + } + else { + id = static_cast(ascii & 0x00ff); + } + } + + // if result is 2 then a previous dead key could not be composed. + else if (result == 2) { + // if the two characters are the same and this is a key release + // then this event is the dead key being released. we put the + // dead key back in that case, otherwise we discard both key + // events because we can't compose the character. alternatively + // we could generate key events for both keys. + if (((ascii & 0xff00) >> 8) != (ascii & 0x00ff) || + (info & 0x80000000) == 0) { + // cannot compose key + return kKeyNone; + } + + // get the scan code of the dead key and the shift state + // required to generate it. + vkCode = VkKeyScan(static_cast(ascii & 0x00ff)); + + // set shift state required to generate key + BYTE keys[256]; + memset(keys, 0, sizeof(keys)); + if (vkCode & 0x0100) { + keys[VK_SHIFT] = 0x80; + } + if (vkCode & 0x0200) { + keys[VK_CONTROL] = 0x80; + } + if (vkCode & 0x0400) { + keys[VK_MENU] = 0x80; + } + + // strip shift state off of virtual key code + vkCode &= 0x00ff; + + // get the scan code for the key + scanCode = MapVirtualKey(vkCode, 0); + + // put it back + ToAscii(vkCode, scanCode, keys, &ascii, 0); + id = kKeyMultiKey; + } + } + + // set mask + *altgr = false; + if (id != kKeyNone && id != kKeyMultiKey && c != 0) { + // note if key requires AltGr. VkKeyScan() can have a problem + // with some characters. there are two problems in particular. + // first, typing a dead key then pressing space will cause + // VkKeyScan() to return 0xffff. second, certain characters + // may map to multiple virtual keys and we might get the wrong + // one. if that happens then we might not get the right + // modifier mask. AltGr+9 on the french keyboard layout (^) + // has this problem. in the first case, we'll assume AltGr is + // required (only because it solves the problems we've seen + // so far). in the second, we'll use whatever the keyboard + // state says. + WORD virtualKeyAndModifierState = VkKeyScan(c); + if (virtualKeyAndModifierState == 0xffff) { + // there is no mapping. assume AltGr. + LOG((CLOG_DEBUG1 "no VkKeyScan() mapping")); + *altgr = true; + } + else if (LOBYTE(virtualKeyAndModifierState) != vkCode) { + // we didn't get the key that was actually pressed + LOG((CLOG_DEBUG1 "VkKeyScan() mismatch")); + if ((m_keys[VK_CONTROL] & 0x80) != 0 && + (m_keys[VK_MENU] & 0x80) != 0) { + *altgr = true; + } + } + else { + BYTE modifierState = HIBYTE(virtualKeyAndModifierState); + if ((modifierState & 6) == 6) { + // key requires ctrl and alt == AltGr + *altgr = true; + } + } + } + + // map modifier key + KeyModifierMask mask = 0; + if (((m_keys[VK_LSHIFT] | + m_keys[VK_RSHIFT] | + m_keys[VK_SHIFT]) & 0x80) != 0) { + mask |= KeyModifierShift; + } + if (*altgr) { + mask |= KeyModifierModeSwitch; + } + else { + if (((m_keys[VK_LCONTROL] | + m_keys[VK_RCONTROL] | + m_keys[VK_CONTROL]) & 0x80) != 0) { + mask |= KeyModifierControl; + } + if (((m_keys[VK_LMENU] | + m_keys[VK_RMENU] | + m_keys[VK_MENU]) & 0x80) != 0) { + mask |= KeyModifierAlt; + } + } + if (((m_keys[VK_LWIN] | + m_keys[VK_RWIN]) & 0x80) != 0) { + mask |= KeyModifierSuper; + } + if ((m_keys[VK_CAPITAL] & 0x01) != 0) { + mask |= KeyModifierCapsLock; + } + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { + mask |= KeyModifierNumLock; + } + if ((m_keys[VK_SCROLL] & 0x01) != 0) { + mask |= KeyModifierScrollLock; + } + *maskOut = mask; + + return id; +} + +ButtonID +CMSWindowsPrimaryScreen::mapButton(WPARAM msg, LPARAM button) const +{ + switch (msg) { + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_LBUTTONUP: + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCLBUTTONUP: + return kButtonLeft; + + case WM_MBUTTONDOWN: + case WM_MBUTTONDBLCLK: + case WM_MBUTTONUP: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONDBLCLK: + case WM_NCMBUTTONUP: + return kButtonMiddle; + + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + case WM_RBUTTONUP: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONDBLCLK: + case WM_NCRBUTTONUP: + return kButtonRight; + + case WM_XBUTTONDOWN: + case WM_XBUTTONDBLCLK: + case WM_XBUTTONUP: + case WM_NCXBUTTONDOWN: + case WM_NCXBUTTONDBLCLK: + case WM_NCXBUTTONUP: + switch (button) { + case XBUTTON1: + return kButtonExtra0 + 0; + + case XBUTTON2: + return kButtonExtra0 + 1; + } + return kButtonNone; + + default: + return kButtonNone; + } +} + +void +CMSWindowsPrimaryScreen::updateKeys() +{ + // not using GetKeyboardState() because that doesn't seem to give + // up-to-date results. i don't know why that is or why GetKeyState() + // should give different results. + + // clear key and button state + memset(m_keys, 0, sizeof(m_keys)); + memset(m_buttons, 0, sizeof(m_buttons)); + + // we only care about the modifier key states. other keys and the + // mouse buttons should be up. + // sometimes these seem to be out of date so, to avoid getting + // locked to a screen unexpectedly, just assume the non-toggle + // modifier keys are up. +/* + m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT) & 0x80); + m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT) & 0x80); + m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT) & 0x80); + m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL) & 0x80); + m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL) & 0x80); + m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL) & 0x80); + m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU) & 0x80); + m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU) & 0x80); + m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU) & 0x80); + m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN) & 0x80); + m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN) & 0x80); + m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS) & 0x80); +*/ + m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); + m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); + m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); +} + +void +CMSWindowsPrimaryScreen::updateKey(UINT vkCode, bool press) +{ + if (press) { + switch (vkCode) { + case 0: + case VK_LBUTTON: + case VK_MBUTTON: + case VK_RBUTTON: + // ignore bogus key + break; + + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + m_keys[vkCode] |= 0x80; + m_keys[VK_SHIFT] |= 0x80; + break; + + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + m_keys[vkCode] |= 0x80; + m_keys[VK_CONTROL] |= 0x80; + break; + + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + m_keys[vkCode] |= 0x80; + m_keys[VK_MENU] |= 0x80; + break; + + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // toggle keys + m_keys[vkCode] |= 0x80; + break; + + default: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + m_keys[vkCode] |= 0x80; + break; + } + + // special case: we detect ctrl+alt+del being pressed on some + // systems but we don't detect the release of those keys. so + // if ctrl, alt, and del are down then mark them up. + if ((m_keys[VK_CONTROL] & 0x80) != 0 && + (m_keys[VK_MENU] & 0x80) != 0 && + (m_keys[VK_DELETE] & 0x80) != 0) { + m_keys[VK_LCONTROL] &= ~0x80; + m_keys[VK_RCONTROL] &= ~0x80; + m_keys[VK_CONTROL] &= ~0x80; + m_keys[VK_LMENU] &= ~0x80; + m_keys[VK_RMENU] &= ~0x80; + m_keys[VK_MENU] &= ~0x80; + m_keys[VK_DELETE] &= ~0x80; + } + } + else { + switch (vkCode) { + case 0: + case VK_LBUTTON: + case VK_MBUTTON: + case VK_RBUTTON: + // ignore bogus key + break; + + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + m_keys[vkCode] &= ~0x80; + if (((m_keys[VK_LSHIFT] | m_keys[VK_RSHIFT]) & 0x80) == 0) { + m_keys[VK_SHIFT] &= ~0x80; + } + break; + + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + m_keys[vkCode] &= ~0x80; + if (((m_keys[VK_LCONTROL] | m_keys[VK_RCONTROL]) & 0x80) == 0) { + m_keys[VK_CONTROL] &= ~0x80; + } + break; + + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + m_keys[vkCode] &= ~0x80; + if (((m_keys[VK_LMENU] | m_keys[VK_RMENU]) & 0x80) == 0) { + m_keys[VK_MENU] &= ~0x80; + } + break; + + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // toggle keys + m_keys[vkCode] &= ~0x80; + m_keys[vkCode] ^= 0x01; + break; + + default: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + m_keys[vkCode] &= ~0x80; + break; + } + } +} + +bool +CMSWindowsPrimaryScreen::isModifier(UINT vkCode) const + +{ + switch (vkCode) { + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + case VK_LWIN: + case VK_RWIN: + return true; + + default: + return false; + } +} + +KeyButton +CMSWindowsPrimaryScreen::mapKeyToScanCode(UINT vk1, UINT vk2) const +{ + KeyButton button = static_cast(MapVirtualKey(vk1, 0)); + if (button == 0) { + button = static_cast(MapVirtualKey(vk2, 0)); + } + return button; +} diff --git a/lib/platform/CMSWindowsPrimaryScreen.h b/lib/platform/CMSWindowsPrimaryScreen.h new file mode 100644 index 0000000..c318d12 --- /dev/null +++ b/lib/platform/CMSWindowsPrimaryScreen.h @@ -0,0 +1,140 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSPRIMARYSCREEN_H +#define CMSWINDOWSPRIMARYSCREEN_H + +#include "CPrimaryScreen.h" +#include "IMSWindowsScreenEventHandler.h" +#include "CSynergyHook.h" +#include "MouseTypes.h" +#include "CString.h" + +class CMSWindowsScreen; +class IScreenReceiver; +class IPrimaryScreenReceiver; + +//! Microsoft windows primary screen implementation +class CMSWindowsPrimaryScreen : + public CPrimaryScreen, public IMSWindowsScreenEventHandler { +public: + typedef bool (CMSWindowsPrimaryScreen::*HookMethod)(int, WPARAM, LPARAM); + + CMSWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); + virtual ~CMSWindowsPrimaryScreen(); + + // CPrimaryScreen overrides + virtual void reconfigure(UInt32 activeSides); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual UInt32 addOneShotTimer(double timeout); + virtual KeyModifierMask getToggleMask() const; + virtual bool isLockedToScreen() const; + virtual IScreen* getScreen() const; + + // IMSWindowsScreenEventHandler overrides + virtual void onScreensaver(bool activated); + virtual bool onPreDispatch(const CEvent* event); + virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); + virtual SInt32 getJumpZoneSize() const; + virtual void postCreateWindow(HWND); + virtual void preDestroyWindow(HWND); + virtual void onAccessibleDesktop(); + +protected: + // CPrimaryScreen overrides + virtual void onPreMainLoop(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPostClose(); + virtual void onPreEnter(); + virtual void onPostEnter(); + virtual void onPreLeave(); + virtual void onPostLeave(bool); + + virtual void createWindow(); + virtual void destroyWindow(); + virtual bool showWindow(); + virtual void hideWindow(); + virtual void warpCursorToCenter(); + + virtual void updateKeys(); + +private: + void enterNoWarp(); + + // warp cursor without discarding queued events + void warpCursorNoFlush(SInt32 x, SInt32 y); + + // discard posted messages + void nextMark(); + + // test if event should be ignored + bool ignore() const; + + // key and button queries + KeyID mapKey(WPARAM keycode, LPARAM info, + KeyModifierMask* maskOut, bool* altgr); + ButtonID mapButton(WPARAM msg, LPARAM button) const; + void updateKey(UINT vkCode, bool press); + bool isModifier(UINT vkCode) const; + KeyButton mapKeyToScanCode(UINT vk1, UINT vk2) const; + +private: + IPrimaryScreenReceiver* m_receiver; + CMSWindowsScreen* m_screen; + + // true if windows 95/98/me + bool m_is95Family; + + // the main loop's thread id + DWORD m_threadID; + + // my window + HWND m_window; + + // used to discard queued messages that are no longer needed + UInt32 m_mark; + UInt32 m_markReceived; + + // map of key state + BYTE m_keys[256]; + + // map of button state + BYTE m_buttons[1 + kButtonExtra0 + 1]; + + // last mouse position + SInt32 m_x, m_y; + + // position of center pixel of screen + SInt32 m_xCenter, m_yCenter; + + // hook library stuff + HINSTANCE m_hookLibrary; + InitFunc m_init; + CleanupFunc m_cleanup; + InstallFunc m_install; + UninstallFunc m_uninstall; + SetSidesFunc m_setSides; + SetZoneFunc m_setZone; + SetRelayFunc m_setRelay; + bool m_lowLevel; + + // stuff for hiding the cursor + DWORD m_cursorThread; +}; + +#endif diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp new file mode 100644 index 0000000..0b37d99 --- /dev/null +++ b/lib/platform/CMSWindowsScreen.cpp @@ -0,0 +1,820 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsScreen.h" +#include "CMSWindowsClipboard.h" +#include "CMSWindowsScreenSaver.h" +#include "CClipboard.h" +#include "IMSWindowsScreenEventHandler.h" +#include "IScreenReceiver.h" +#include "CThread.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CLog.h" +#include "CString.h" +#include "CStringUtil.h" +#include "CArchMiscWindows.h" +#include +#include +#include + +// +// add backwards compatible multihead support (and suppress bogus warning) +// +#pragma warning(push) +#pragma warning(disable: 4706) // assignment within conditional +#define COMPILE_MULTIMON_STUBS +#include +#pragma warning(pop) + +// +// CMSWindowsScreen +// + +HINSTANCE CMSWindowsScreen::s_instance = NULL; +CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; + +CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver, + IMSWindowsScreenEventHandler* eventHandler) : + m_receiver(receiver), + m_eventHandler(eventHandler), + m_class(NULL), + m_icon(NULL), + m_cursor(NULL), + m_is95Family(CArchMiscWindows::isWindows95Family()), + m_window(NULL), + m_x(0), m_y(0), + m_w(0), m_h(0), + m_multimon(false), + m_threadID(0), + m_lastThreadID(0), + m_nextClipboardWindow(NULL), + m_ownClipboard(false), + m_timer(0), + m_desk(NULL), + m_deskName(), + m_hookLibrary(NULL), + m_installScreensaver(NULL), + m_uninstallScreensaver(NULL), + m_screensaver(NULL), + m_screensaverNotify(false), + m_inaccessibleDesktop(false) +{ + assert(s_screen == NULL); + assert(m_receiver != NULL); + assert(m_eventHandler != NULL); + + s_screen = this; + + // make sure this thread has a message queue + MSG dummy; + PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); +} + +CMSWindowsScreen::~CMSWindowsScreen() +{ + assert(s_screen != NULL); + assert(m_class == 0); + + s_screen = NULL; +} + +void +CMSWindowsScreen::init(HINSTANCE instance) +{ + s_instance = instance; +} + +HWND +CMSWindowsScreen::openDesktop() +{ + // save thread id + m_threadID = GetCurrentThreadId(); + + // get the input desktop and switch to it + if (!switchDesktop(openInputDesktop())) { + return NULL; + } + + // poll input desktop to see if it changes (onPreDispatch() + // handles WM_TIMER). this is also used for polling other + // stuff. + m_timer = SetTimer(NULL, 0, 200, NULL); + + return m_window; +} + +void +CMSWindowsScreen::closeDesktop() +{ + // remove timer + if (m_timer != 0) { + KillTimer(NULL, m_timer); + m_timer = 0; + } + if (m_oneShotTimer != 0) { + KillTimer(NULL, m_oneShotTimer); + m_oneShotTimer = 0; + } + + // disconnect from desktop + switchDesktop(NULL); + + // clear thread id + m_threadID = 0; + + assert(m_window == NULL); + assert(m_desk == NULL); +} + +UInt32 +CMSWindowsScreen::addOneShotTimer(double timeout) +{ + // FIXME -- support multiple one-shot timers + if (m_oneShotTimer != 0) { + KillTimer(NULL, m_oneShotTimer); + } + m_oneShotTimer = SetTimer(NULL, 0, + static_cast(1000.0 * timeout), NULL); + return 0; +} + +bool +CMSWindowsScreen::isMultimon() const +{ + return m_multimon; +} + +HINSTANCE +CMSWindowsScreen::getInstance() +{ + return s_instance; +} + +void +CMSWindowsScreen::open() +{ + assert(s_instance != NULL); + assert(m_class == 0); + + LOG((CLOG_DEBUG "opening display")); + + // create the transparent cursor + createBlankCursor(); + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_DBLCLKS | CS_NOCLOSE; + classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + classInfo.hInstance = s_instance; + classInfo.hIcon = NULL; + classInfo.hCursor = m_cursor; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = "Synergy"; + classInfo.hIconSm = NULL; + m_class = RegisterClassEx(&classInfo); + + // get screen shape + updateScreenShape(); + + // initialize the screen saver + m_screensaver = new CMSWindowsScreenSaver(); + + // load the hook library and get the screen saver functions + m_hookLibrary = LoadLibrary("synrgyhk"); + if (m_hookLibrary != NULL) { + m_installScreensaver = (InstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "installScreenSaver"); + m_uninstallScreensaver = (UninstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "uninstallScreenSaver"); + if (m_installScreensaver == NULL || m_uninstallScreensaver == NULL) { + // disable if either install or uninstall is unavailable + m_installScreensaver = NULL; + m_uninstallScreensaver = NULL; + } + } +} + +void +CMSWindowsScreen::mainLoop() +{ + // must call mainLoop() from same thread as openDesktop() + assert(m_threadID == GetCurrentThreadId()); + + // event loop + CEvent event; + event.m_result = 0; + for (;;) { + // wait for an event in a cancellable way + if (CThread::getCurrentThread().waitForEvent(-1.0) != CThread::kEvent) { + continue; + } + if (!PeekMessage(&event.m_msg, NULL, 0, 0, PM_REMOVE)) { + continue; + } + + // handle quit message + if (event.m_msg.message == WM_QUIT) { + if (event.m_msg.wParam == 0) { + // force termination + CThread::getCurrentThread().cancel(); + } + else { + // just exit the main loop + break; + } + } + + // dispatch message + if (!onPreDispatch(&event)) { + TranslateMessage(&event.m_msg); + DispatchMessage(&event.m_msg); + } + } +} + +void +CMSWindowsScreen::exitMainLoop() +{ + // close down cleanly + PostThreadMessage(m_threadID, WM_QUIT, 1, 0); +} + +void +CMSWindowsScreen::close() +{ + assert(s_instance != NULL); + + // done with hook library + if (m_hookLibrary != NULL) { + FreeLibrary(m_hookLibrary); + m_installScreensaver = NULL; + m_uninstallScreensaver = NULL; + m_hookLibrary = NULL; + } + + // done with screen saver + delete m_screensaver; + m_screensaver = NULL; + + // unregister the window class + if (m_class != 0) { + UnregisterClass((LPCTSTR)m_class, s_instance); + m_class = 0; + } + + // delete resources + if (m_cursor != NULL) { + DestroyCursor(m_cursor); + m_cursor = NULL; + } + + LOG((CLOG_DEBUG "closed display")); +} + +bool +CMSWindowsScreen::setClipboard(ClipboardID, const IClipboard* src) +{ + CMSWindowsClipboard dst(m_window); + if (src != NULL) { + // save clipboard data + return CClipboard::copy(&dst, src); + } + else { + // assert clipboard ownership + if (!dst.open(0)) { + return false; + } + dst.empty(); + dst.close(); + return true; + } +} + +void +CMSWindowsScreen::checkClipboards() +{ + // if we think we own the clipboard but we don't then somebody + // grabbed the clipboard on this screen without us knowing. + // tell the server that this screen grabbed the clipboard. + // + // this works around bugs in the clipboard viewer chain. + // sometimes NT will simply never send WM_DRAWCLIPBOARD + // messages for no apparent reason and rebooting fixes the + // problem. since we don't want a broken clipboard until the + // next reboot we do this double check. clipboard ownership + // won't be reflected on other screens until we leave but at + // least the clipboard itself will work. + if (m_ownClipboard && !CMSWindowsClipboard::isOwnedBySynergy()) { + LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received")); + m_ownClipboard = false; + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } +} + +void +CMSWindowsScreen::openScreensaver(bool notify) +{ + assert(m_screensaver != NULL); + + m_screensaverNotify = notify; + if (m_screensaverNotify) { + if (m_installScreensaver != NULL) { + m_installScreensaver(); + } + } + else { + m_screensaver->disable(); + } +} + +void +CMSWindowsScreen::closeScreensaver() +{ + if (m_screensaver != NULL) { + if (m_screensaverNotify) { + if (m_uninstallScreensaver != NULL) { + m_uninstallScreensaver(); + } + } + else { + m_screensaver->enable(); + } + } + m_screensaverNotify = false; +} + +void +CMSWindowsScreen::screensaver(bool activate) +{ + assert(m_screensaver != NULL); + if (activate) { + m_screensaver->activate(); + } + else { + m_screensaver->deactivate(); + } +} + +void +CMSWindowsScreen::syncDesktop() +{ + // change calling thread's desktop + if (!m_is95Family) { + if (SetThreadDesktop(m_desk) == 0) { +// LOG((CLOG_WARN "failed to set desktop: %d", GetLastError())); + } + } + + // attach input queues if not already attached. this has a habit + // of sucking up more and more CPU each time it's called (even if + // the threads are already attached). since we only expect one + // thread to call this more than once we can save just the last + // attached thread. + DWORD threadID = GetCurrentThreadId(); + if (threadID != m_lastThreadID && threadID != m_threadID) { + m_lastThreadID = threadID; + AttachThreadInput(threadID, m_threadID, TRUE); + } +} + +bool +CMSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const +{ + CMSWindowsClipboard src(m_window); + CClipboard::copy(dst, &src); + return true; +} + +void +CMSWindowsScreen::getShape(SInt32& x, SInt32& y, + SInt32& w, SInt32& h) const +{ + assert(m_class != 0); + + x = m_x; + y = m_y; + w = m_w; + h = m_h; +} + +void +CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + POINT pos; + if (GetCursorPos(&pos)) { + x = pos.x; + y = pos.y; + } + else { + getCursorCenter(x, y); + } +} + +void +CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + x = GetSystemMetrics(SM_CXSCREEN) >> 1; + y = GetSystemMetrics(SM_CYSCREEN) >> 1; +} + +void +CMSWindowsScreen::updateScreenShape() +{ + // get shape + m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); + m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); + m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); + m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + + // check for multiple monitors + m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || + m_h != GetSystemMetrics(SM_CYSCREEN)); +} + +bool +CMSWindowsScreen::onPreDispatch(const CEvent* event) +{ + // handle event + const MSG* msg = &event->m_msg; + switch (msg->message) { + case SYNERGY_MSG_SCREEN_SAVER: + { + // activating or deactivating? + bool activate = (msg->wParam != 0); + + // ignore this message if there are any other screen saver + // messages already in the queue. this is important because + // our checkStarted() function has a deliberate delay, so it + // can't respond to events at full CPU speed and will fall + // behind if a lot of screen saver events are generated. + // that can easily happen because windows will continually + // send SC_SCREENSAVE until the screen saver starts, even if + // the screen saver is disabled! + MSG msg; + if (!PeekMessage(&msg, NULL, SYNERGY_MSG_SCREEN_SAVER, + SYNERGY_MSG_SCREEN_SAVER, PM_NOREMOVE)) { + if (activate) { + if (m_screensaver->checkStarted( + SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) { + m_eventHandler->onScreensaver(true); + } + } + else { + m_eventHandler->onScreensaver(false); + } + } + return true; + } + + case WM_TIMER: + if (msg->wParam == m_timer) { + // if current desktop is not the input desktop then switch to it. + // windows 95 doesn't support multiple desktops so don't bother + // to check under it. + if (!m_is95Family) { + HDESK desk = openInputDesktop(); + if (desk != NULL) { + if (isCurrentDesktop(desk)) { + CloseDesktop(desk); + } + else if (m_screensaver->isActive()) { + // don't switch desktops when the screensaver is + // active. we'd most likely switch to the + // screensaver desktop which would have the side + // effect of forcing the screensaver to stop. + CloseDesktop(desk); + } + else { + switchDesktop(desk); + } + + // if the desktop was inaccessible then notify the + // event handler of that. + if (m_inaccessibleDesktop) { + LOG((CLOG_DEBUG "desktop is now accessible")); + m_inaccessibleDesktop = false; + m_eventHandler->onAccessibleDesktop(); + } + } + else if (!m_inaccessibleDesktop) { + // the desktop has become inaccessible + m_inaccessibleDesktop = true; + LOG((CLOG_DEBUG "desktop is now inaccessible")); + } + } + + // let client do timer related stuff. ignore the return + // value though since the event has been handled here. + m_eventHandler->onPreDispatch(event); + } + else if (msg->wParam == m_oneShotTimer) { + // one shot timer expired + KillTimer(NULL, m_oneShotTimer); + m_oneShotTimer = 0; + m_eventHandler->onOneShotTimerExpired(0); + } + + return true; + } + + // let client handle the event + return m_eventHandler->onPreDispatch(event); +} + +bool +CMSWindowsScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + + const MSG& msg = event->m_msg; + switch (msg.message) { + case WM_QUERYENDSESSION: + if (m_is95Family) { + event->m_result = TRUE; + return true; + } + break; + + case WM_ENDSESSION: + if (m_is95Family) { + if (msg.wParam == TRUE && msg.lParam == 0) { + exitMainLoop(); + } + return true; + } + break; + + case WM_PAINT: + ValidateRect(msg.hwnd, NULL); + return true; + + case WM_DRAWCLIPBOARD: + LOG((CLOG_DEBUG "clipboard was taken")); + + // first pass it on + if (m_nextClipboardWindow != NULL) { + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); + } + + // now notify client that somebody changed the clipboard (unless + // we're the owner). + if (!CMSWindowsClipboard::isOwnedBySynergy()) { + LOG((CLOG_DEBUG "clipboard changed: foreign owned")); + if (m_ownClipboard) { + LOG((CLOG_DEBUG "clipboard changed: lost ownership")); + m_ownClipboard = false; + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } + } + else { + LOG((CLOG_DEBUG "clipboard changed: synergy owned")); + m_ownClipboard = true; + } + return true; + + case WM_CHANGECBCHAIN: + if (m_nextClipboardWindow == (HWND)msg.wParam) { + m_nextClipboardWindow = (HWND)msg.lParam; + LOG((CLOG_DEBUG "clipboard chain: new next: 0x%08x", m_nextClipboardWindow)); + } + else if (m_nextClipboardWindow != NULL) { + LOG((CLOG_DEBUG "clipboard chain: forward: %d 0x%08x 0x%08x", msg.message, msg.wParam, msg.lParam)); + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); + } + return true; + + case WM_DISPLAYCHANGE: + { + // screen resolution may have changed. get old shape. + SInt32 xOld, yOld, wOld, hOld; + getShape(xOld, yOld, wOld, hOld); + + // update shape + updateScreenShape(); + + // collect new screen info + CClientInfo info; + getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = m_eventHandler->getJumpZoneSize(); + + // do nothing if resolution hasn't changed + if (info.m_x != xOld || info.m_y != yOld || + info.m_w != wOld || info.m_h != hOld) { + // forward event + m_eventHandler->onEvent(event); + + // send new screen info + m_receiver->onInfoChanged(info); + } + + return true; + } + } + + return m_eventHandler->onEvent(event); +} + +void +CMSWindowsScreen::createBlankCursor() +{ + // create a transparent cursor + int cw = GetSystemMetrics(SM_CXCURSOR); + int ch = GetSystemMetrics(SM_CYCURSOR); + UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; + UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; + memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); + memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); + m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); + delete[] cursorXOR; + delete[] cursorAND; +} + +bool +CMSWindowsScreen::switchDesktop(HDESK desk) +{ + // assume we don't own the clipboard until later + m_ownClipboard = false; + + // destroy old window + if (m_window != NULL) { + // first remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + + // let client clean up before we destroy the window + m_eventHandler->preDestroyWindow(m_window); + + // now destroy window + DestroyWindow(m_window); + m_window = NULL; + + // done with desk + if (!m_is95Family) { + CloseDesktop(m_desk); + } + m_desk = NULL; + m_deskName = ""; + } + + // if no new desktop then we're done + if (desk == NULL) { + LOG((CLOG_DEBUG "disconnecting desktop")); + return true; + } + + // uninstall screen saver hooks + if (m_screensaverNotify) { + if (m_uninstallScreensaver != NULL) { + m_uninstallScreensaver(); + } + } + + // set the desktop. can only do this when there are no windows + // and hooks on the current desktop owned by this thread. + if (SetThreadDesktop(desk) == 0) { + LOG((CLOG_ERR "failed to set desktop: %d", GetLastError())); + if (!m_is95Family) { + CloseDesktop(desk); + } + return false; + } + + // create the window + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | + WS_EX_TOOLWINDOW, + (LPCTSTR)m_class, + "Synergy", + WS_POPUP, + 0, 0, 1, 1, + NULL, NULL, + getInstance(), + NULL); + if (m_window == NULL) { + LOG((CLOG_ERR "failed to create window: %d", GetLastError())); + if (!m_is95Family) { + CloseDesktop(desk); + } + return false; + } + + // reinstall screen saver hooks + if (m_screensaverNotify) { + if (m_installScreensaver != NULL) { + m_installScreensaver(); + } + } + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); + + // check if we own the clipboard + m_ownClipboard = CMSWindowsClipboard::isOwnedBySynergy(); + + // save new desktop + m_desk = desk; + m_deskName = getDesktopName(m_desk); + LOG((CLOG_DEBUG "switched to desktop \"%s\"", m_deskName.c_str())); + + // let client prepare the window + m_eventHandler->postCreateWindow(m_window); + + return true; +} + +HDESK +CMSWindowsScreen::openInputDesktop() const +{ + if (m_is95Family) { + // there's only one desktop on windows 95 et al. + return GetThreadDesktop(GetCurrentThreadId()); + } + else { + return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, + DESKTOP_CREATEWINDOW | + DESKTOP_HOOKCONTROL | + GENERIC_WRITE); + } +} + +CString +CMSWindowsScreen::getDesktopName(HDESK desk) const +{ + if (desk == NULL) { + return CString(); + } + else if (m_is95Family) { + return "desktop"; + } + else { + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR)); + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + CString result(name); + return result; + } +} + +bool +CMSWindowsScreen::isCurrentDesktop(HDESK desk) const +{ + // don't allocate space for current desktop name on heap since + // we do this a lot and we never save the name. + TCHAR* name; + if (desk == NULL) { + name = _T(""); + } + else if (m_is95Family) { + name = _T("desktop"); + } + else { + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + name = (TCHAR*)alloca(size + sizeof(TCHAR)); + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + } + return (_tcsicmp(name, m_deskName.c_str()) == 0); +} + +LRESULT CALLBACK +CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + assert(s_screen != NULL); + + CEvent event; + event.m_msg.hwnd = hwnd; + event.m_msg.message = msg; + event.m_msg.wParam = wParam; + event.m_msg.lParam = lParam; + event.m_result = 0; + + if (s_screen->onEvent(&event)) { + return event.m_result; + } + else { + return DefWindowProc(hwnd, msg, wParam, lParam); + } +} diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h new file mode 100644 index 0000000..f4af0ca --- /dev/null +++ b/lib/platform/CMSWindowsScreen.h @@ -0,0 +1,204 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSSCREEN_H +#define CMSWINDOWSSCREEN_H + +#include "IScreen.h" +#include "CSynergyHook.h" +#include "CMutex.h" +#include "CString.h" +#define WIN32_LEAN_AND_MEAN +#include + +class CMSWindowsScreenSaver; +class CThread; + +// Microsoft windows event +class CEvent { +public: + MSG m_msg; + LRESULT m_result; +}; + +class IScreenReceiver; +class IMSWindowsScreenEventHandler; + +//! Implementation of IScreen for Microsoft Windows +class CMSWindowsScreen : public IScreen { +public: + CMSWindowsScreen(IScreenReceiver*, IMSWindowsScreenEventHandler*); + virtual ~CMSWindowsScreen(); + + //! @name manipulators + //@{ + + //! Initialize + /*! + Saves the application's HINSTANCE. This \b must be called by + WinMain with the HINSTANCE it was passed. + */ + static void init(HINSTANCE); + + //! Open desktop + /*! + Open the desktop and create and return the window. Returns NULL + on failure. + */ + HWND openDesktop(); + + //! Close desktop + /*! + Close the window and desktop. + */ + void closeDesktop(); + + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer (which will be passed to the receiver's + \c onTimerExpired()). + */ + UInt32 addOneShotTimer(double timeout); + + //@} + //! @name accessors + //@{ + + //! Test for multiple monitors + /*! + Returns true iff the system appears to have multiple monitors. + */ + bool isMultimon() const; + + //! Get instance + /*! + Returns the application instance handle passed to init(). + */ + static HINSTANCE getInstance(); + + //@} + + // IScreen overrides + // note -- this class expects the hook DLL to have been loaded + // and initialized before open() is called. + void open(); + void mainLoop(); + void exitMainLoop(); + void close(); + bool setClipboard(ClipboardID, const IClipboard*); + void checkClipboards(); + void openScreensaver(bool notify); + void closeScreensaver(); + void screensaver(bool activate); + void syncDesktop(); + bool getClipboard(ClipboardID, IClipboard*) const; + void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; + void getCursorPos(SInt32&, SInt32&) const; + void getCursorCenter(SInt32&, SInt32&) const; + +private: + // update screen size cache + void updateScreenShape(); + + // internal pre-dispatch event processing + bool onPreDispatch(const CEvent* event); + + // internal (post-dispatch) event processing + bool onEvent(CEvent* event); + + // create the transparent cursor + void createBlankCursor(); + + // switch to the given desktop. this destroys the window and unhooks + // all hooks, switches the desktop, then creates the window and rehooks + // all hooks (because you can't switch the thread's desktop if it has + // any windows or hooks). + bool switchDesktop(HDESK desk); + + // get the input desktop. caller must CloseDesktop() the result. + // do not call under windows 95/98/me. + HDESK openInputDesktop() const; + + // get the desktop's name. do not call under windows 95/98/me. + CString getDesktopName(HDESK) const; + + // returns true iff desk is the current desk. do not call under + // windows 95/98/me. + bool isCurrentDesktop(HDESK desk) const; + + // our window proc + static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); + +private: + static HINSTANCE s_instance; + + IScreenReceiver* m_receiver; + IMSWindowsScreenEventHandler* m_eventHandler; + + ATOM m_class; + HICON m_icon; + HCURSOR m_cursor; + + // true if windows 95/98/me + bool m_is95Family; + + // our window + HWND m_window; + + // screen shape + SInt32 m_x, m_y; + SInt32 m_w, m_h; + + // true if system appears to have multiple monitors + bool m_multimon; + + // the main loop's thread id + DWORD m_threadID; + + // the thread id of the last attached thread + DWORD m_lastThreadID; + + // clipboard stuff + HWND m_nextClipboardWindow; + bool m_ownClipboard; + + // the timer used to check for desktop switching + UINT m_timer; + + // the one shot timer + UINT m_oneShotTimer; + + // the current desk and it's name + HDESK m_desk; + CString m_deskName; + + // screen saver stuff + HINSTANCE m_hookLibrary; + InstallScreenSaverFunc m_installScreensaver; + UninstallScreenSaverFunc m_uninstallScreensaver; + CMSWindowsScreenSaver* m_screensaver; + bool m_screensaverNotify; + + // true when the current desktop is inaccessible. while + // the desktop is inaccessible we won't receive user input + // and we'll lose track of the keyboard state. when the + // desktop becomes accessible again we'll notify the event + // handler of that. + bool m_inaccessibleDesktop; + + static CMSWindowsScreen* s_screen; +}; + +#endif diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp new file mode 100644 index 0000000..14e0bac --- /dev/null +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -0,0 +1,377 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsScreenSaver.h" +#include "CThread.h" +#include "CLog.h" +#include "TMethodJob.h" +#include "CArch.h" +#include +#include + +#if !defined(SPI_GETSCREENSAVERRUNNING) +#define SPI_GETSCREENSAVERRUNNING 114 +#endif + +// +// CMSWindowsScreenSaver +// + +CMSWindowsScreenSaver::CMSWindowsScreenSaver() : + m_process(NULL), + m_threadID(0), + m_watch(NULL) +{ + // detect OS + m_is95Family = false; + m_is95 = false; + m_isNT = false; + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(info); + if (GetVersionEx(&info)) { + m_is95Family = (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); + if (info.dwPlatformId == VER_PLATFORM_WIN32_NT && + info.dwMajorVersion <= 4) { + m_isNT = true; + } + else if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && + info.dwMajorVersion == 4 && + info.dwMinorVersion == 0) { + m_is95 = true; + } + } + + // check if screen saver is enabled + SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); +} + +CMSWindowsScreenSaver::~CMSWindowsScreenSaver() +{ + unwatchProcess(); +} + +bool +CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) +{ + // screen saver may have started. look for it and get + // the process. if we can't find it then assume it + // didn't really start. we wait a moment before + // looking to give the screen saver a chance to start. + // this shouldn't be a problem since we only get here + // if the screen saver wants to kick in, meaning that + // the system is idle or the user deliberately started + // the screen saver. + Sleep(250); + + // set parameters common to all screen saver handling + m_threadID = GetCurrentThreadId(); + m_msg = msg; + m_wParam = wParam; + m_lParam = lParam; + + // we handle the screen saver differently for the windows + // 95 and nt families. + if (m_is95Family) { + // on windows 95 we wait for the screen saver process + // to terminate. get the process. + DWORD processID = findScreenSaver(); + HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, processID); + if (process == NULL) { + // didn't start + LOG((CLOG_DEBUG "can't open screen saver process")); + return false; + } + + // watch for the process to exit + watchProcess(process); + } + else { + // on the windows nt family we wait for the desktop to + // change until it's neither the Screen-Saver desktop + // nor a desktop we can't open (the login desktop). + // since windows will send the request-to-start-screen- + // saver message even when the screen saver is disabled + // we first check that the screen saver is indeed active + // before watching for it to stop. + if (!isActive()) { + LOG((CLOG_DEBUG "can't open screen saver desktop")); + return false; + } + + watchDesktop(); + } + + return true; +} + +void +CMSWindowsScreenSaver::enable() +{ + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, m_wasEnabled, 0, 0); +} + +void +CMSWindowsScreenSaver::disable() +{ + SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, 0, 0); +} + +void +CMSWindowsScreenSaver::activate() +{ + // don't activate if already active + if (!isActive()) { + HWND hwnd = GetForegroundWindow(); + if (hwnd != NULL) { + PostMessage(hwnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0); + } + else { + // no foreground window. pretend we got the event instead. + DefWindowProc(NULL, WM_SYSCOMMAND, SC_SCREENSAVE, 0); + } + } +} + +void +CMSWindowsScreenSaver::deactivate() +{ + bool killed = false; + if (!m_is95Family) { + // NT runs screen saver in another desktop + HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, + DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS); + if (desktop != NULL) { + EnumDesktopWindows(desktop, + &CMSWindowsScreenSaver::killScreenSaverFunc, + reinterpret_cast(&killed)); + CloseDesktop(desktop); + } + } + + // if above failed or wasn't tried, try the windows 95 way + if (!killed) { + // find screen saver window and close it + HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL); + if (hwnd == NULL) { + // win2k may use a different class + hwnd = FindWindow("Default Screen Saver", NULL); + } + if (hwnd != NULL) { + PostMessage(hwnd, WM_CLOSE, 0, 0); + } + } + + // force timer to restart + SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, + !m_wasEnabled, 0, SPIF_SENDWININICHANGE); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, + m_wasEnabled, 0, SPIF_SENDWININICHANGE); +} + +bool +CMSWindowsScreenSaver::isActive() const +{ + if (m_is95) { + return (FindWindow("WindowsScreenSaverClass", NULL) != NULL); + } + else if (m_isNT) { + // screen saver runs on a separate desktop + HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, MAXIMUM_ALLOWED); + if (desktop == NULL && GetLastError() != ERROR_ACCESS_DENIED) { + // desktop doesn't exist so screen saver is not running + return false; + } + + // desktop exists. this should indicate that the screen saver + // is running but an OS bug can cause a valid handle to be + // returned even if the screen saver isn't running (Q230117). + // we'll try to enumerate the windows on the desktop and, if + // there are any, we assume the screen saver is running. (note + // that if we don't have permission to enumerate then we'll + // assume that the screen saver is not running.) that'd be + // easy enough except there's another OS bug (Q198590) that can + // cause EnumDesktopWindows() to enumerate the windows of + // another desktop if the requested desktop has no windows. to + // work around that we have to verify that the enumerated + // windows are, in fact, on the expected desktop. + CFindScreenSaverInfo info; + info.m_desktop = desktop; + info.m_window = NULL; + EnumDesktopWindows(desktop, + &CMSWindowsScreenSaver::findScreenSaverFunc, + reinterpret_cast(&info)); + + // done with desktop + CloseDesktop(desktop); + + // screen saver is running if a window was found + return (info.m_window != NULL); + } + else { + BOOL running; + SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, 0); + return (running != FALSE); + } +} + +BOOL CALLBACK +CMSWindowsScreenSaver::findScreenSaverFunc(HWND hwnd, LPARAM arg) +{ + CFindScreenSaverInfo* info = reinterpret_cast(arg); + + if (info->m_desktop != NULL) { + DWORD threadID = GetWindowThreadProcessId(hwnd, NULL); + HDESK desktop = GetThreadDesktop(threadID); + if (desktop != NULL && desktop != info->m_desktop) { + // stop enumerating -- wrong desktop + return FALSE; + } + } + + // found a window + info->m_window = hwnd; + + // don't need to enumerate further + return FALSE; +} + +BOOL CALLBACK +CMSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg) +{ + if (IsWindowVisible(hwnd)) { + PostMessage(hwnd, WM_CLOSE, 0, 0); + *reinterpret_cast(arg) = true; + } + return TRUE; +} + +DWORD +CMSWindowsScreenSaver::findScreenSaver() +{ + // try windows 95 way + HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL); + + // get process ID of process that owns the window, if found + if (hwnd != NULL) { + DWORD processID; + GetWindowThreadProcessId(hwnd, &processID); + return processID; + } + + // not found + return 0; +} + +void +CMSWindowsScreenSaver::watchDesktop() +{ + // stop watching previous process/desktop + unwatchProcess(); + + // watch desktop in another thread + LOG((CLOG_DEBUG "watching screen saver desktop")); + m_watch = new CThread(new TMethodJob(this, + &CMSWindowsScreenSaver::watchDesktopThread)); +} + +void +CMSWindowsScreenSaver::watchProcess(HANDLE process) +{ + // stop watching previous process/desktop + unwatchProcess(); + + // watch new process in another thread + if (process != NULL) { + LOG((CLOG_DEBUG "watching screen saver process")); + m_process = process; + m_watch = new CThread(new TMethodJob(this, + &CMSWindowsScreenSaver::watchProcessThread)); + } +} + +void +CMSWindowsScreenSaver::unwatchProcess() +{ + if (m_watch != NULL) { + LOG((CLOG_DEBUG "stopped watching screen saver process/desktop")); + m_watch->cancel(); + m_watch->wait(); + delete m_watch; + m_watch = NULL; + } + if (m_process != NULL) { + CloseHandle(m_process); + m_process = NULL; + } +} + +void +CMSWindowsScreenSaver::watchDesktopThread(void*) +{ + DWORD reserved = 0; + TCHAR* name = NULL; + + for (;;) { + // wait a bit + ARCH->sleep(0.2); + + // get current desktop + HDESK desk = OpenInputDesktop(0, FALSE, GENERIC_READ); + if (desk == NULL) { + // can't open desktop so keep waiting + continue; + } + + // get current desktop name length + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + + // allocate more space for the name, if necessary + if (size > reserved) { + reserved = size; + name = (TCHAR*)alloca(reserved + sizeof(TCHAR)); + } + + // get current desktop name + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + + // compare name to screen saver desktop name + if (_tcsicmp(name, TEXT("Screen-saver")) == 0) { + // still the screen saver desktop so keep waiting + continue; + } + + // send screen saver deactivation message + PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); + return; + } +} + +void +CMSWindowsScreenSaver::watchProcessThread(void*) +{ + for (;;) { + CThread::testCancel(); + if (WaitForSingleObject(m_process, 50) == WAIT_OBJECT_0) { + // process terminated + LOG((CLOG_DEBUG "screen saver died")); + + // send screen saver deactivation message + PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); + return; + } + } +} diff --git a/lib/platform/CMSWindowsScreenSaver.h b/lib/platform/CMSWindowsScreenSaver.h new file mode 100644 index 0000000..b4c5fea --- /dev/null +++ b/lib/platform/CMSWindowsScreenSaver.h @@ -0,0 +1,82 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSSCREENSAVER_H +#define CMSWINDOWSSCREENSAVER_H + +#include "IScreenSaver.h" +#define WIN32_LEAN_AND_MEAN +#include + +class CThread; + +//! Microsoft windows screen saver implementation +class CMSWindowsScreenSaver : public IScreenSaver { +public: + CMSWindowsScreenSaver(); + virtual ~CMSWindowsScreenSaver(); + + //! @name manipulators + //@{ + + //! Check if screen saver started + /*! + Check if the screen saver really started. Returns false if it + hasn't, true otherwise. When the screen saver stops, \c msg will + be posted to the current thread's message queue with the given + parameters. + */ + bool checkStarted(UINT msg, WPARAM, LPARAM); + + //@} + + // IScreenSaver overrides + virtual void enable(); + virtual void disable(); + virtual void activate(); + virtual void deactivate(); + virtual bool isActive() const; + +private: + class CFindScreenSaverInfo { + public: + HDESK m_desktop; + HWND m_window; + }; + + static BOOL CALLBACK findScreenSaverFunc(HWND hwnd, LPARAM lParam); + static BOOL CALLBACK killScreenSaverFunc(HWND hwnd, LPARAM lParam); + + DWORD findScreenSaver(); + void watchDesktop(); + void watchProcess(HANDLE process); + void unwatchProcess(); + void watchDesktopThread(void*); + void watchProcessThread(void*); + +private: + bool m_is95Family; + bool m_is95; + bool m_isNT; + BOOL m_wasEnabled; + + HANDLE m_process; + CThread* m_watch; + DWORD m_threadID; + UINT m_msg; + WPARAM m_wParam; + LPARAM m_lParam; +}; + +#endif diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp new file mode 100644 index 0000000..cc61733 --- /dev/null +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -0,0 +1,1604 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsSecondaryScreen.h" +#include "CMSWindowsScreen.h" +#include "XScreen.h" +#include "CLock.h" +#include "CThread.h" +#include "CFunctionJob.h" +#include "CLog.h" +#include "CArchMiscWindows.h" +#include + +// these are only defined when WINVER >= 0x0500 +#if !defined(SPI_GETMOUSESPEED) +#define SPI_GETMOUSESPEED 112 +#endif +#if !defined(SPI_SETMOUSESPEED) +#define SPI_SETMOUSESPEED 113 +#endif + +// X button stuff +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_NCXBUTTONDOWN 0x00AB +#define WM_NCXBUTTONUP 0x00AC +#define WM_NCXBUTTONDBLCLK 0x00AD +#define MOUSEEVENTF_XDOWN 0x0100 +#define MOUSEEVENTF_XUP 0x0200 +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + +// multimedia keys +#if !defined(VK_BROWSER_BACK) +#define VK_BROWSER_BACK 0xA6 +#define VK_BROWSER_FORWARD 0xA7 +#define VK_BROWSER_REFRESH 0xA8 +#define VK_BROWSER_STOP 0xA9 +#define VK_BROWSER_SEARCH 0xAA +#define VK_BROWSER_FAVORITES 0xAB +#define VK_BROWSER_HOME 0xAC +#define VK_VOLUME_MUTE 0xAD +#define VK_VOLUME_DOWN 0xAE +#define VK_VOLUME_UP 0xAF +#define VK_MEDIA_NEXT_TRACK 0xB0 +#define VK_MEDIA_PREV_TRACK 0xB1 +#define VK_MEDIA_STOP 0xB2 +#define VK_MEDIA_PLAY_PAUSE 0xB3 +#define VK_LAUNCH_MAIL 0xB4 +#define VK_LAUNCH_MEDIA_SELECT 0xB5 +#define VK_LAUNCH_APP1 0xB6 +#define VK_LAUNCH_APP2 0xB7 +#endif + +// +// CMSWindowsSecondaryScreen +// + +// a list of modifier key info +const CMSWindowsSecondaryScreen::CModifierInfo + CMSWindowsSecondaryScreen::s_modifier[] = { + { KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false }, + { KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false }, + { KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false }, + // note -- no keys for KeyModifierMeta + { KeyModifierSuper, VK_LWIN | 0x100, VK_RWIN | 0x100, false }, + { KeyModifierCapsLock, VK_CAPITAL, 0, true }, + { KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true }, + { KeyModifierScrollLock,VK_SCROLL, 0, true } +}; + +CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen( + IScreenReceiver* receiver) : + m_is95Family(CArchMiscWindows::isWindows95Family()), + m_window(NULL), + m_mask(0) +{ + m_screen = new CMSWindowsScreen(receiver, this); +} + +CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() +{ + assert(m_window == NULL); + + delete m_screen; +} + +void +CMSWindowsSecondaryScreen::keyDown(KeyID key, + KeyModifierMask mask, KeyButton button) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + + // check for ctrl+alt+del emulation + if (key == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + synthesizeCtrlAltDel(); + return; + } + + // get the sequence of keys to simulate key press and the final + // modifier state. + Keystrokes keys; + UINT virtualKey; + m_mask = mapKey(keys, virtualKey, key, mask, kPress); + if (keys.empty()) { + // do nothing if there are no associated keys (i.e. lookup failed) + return; + } + + // generate key events + doKeystrokes(keys, 1); + + // do not record button down if button or virtual key is 0 (invalid) + if (button != 0 && virtualKey != 0) { + // note that key is now down + m_serverKeyMap[button] = virtualKey; + m_keys[virtualKey] |= 0x80; + m_fakeKeys[virtualKey] |= 0x80; + switch (virtualKey) { + case VK_LSHIFT: + case VK_RSHIFT: + m_keys[VK_SHIFT] |= 0x80; + m_fakeKeys[VK_SHIFT] |= 0x80; + break; + + case VK_LCONTROL: + case VK_RCONTROL: + m_keys[VK_CONTROL] |= 0x80; + m_fakeKeys[VK_CONTROL] |= 0x80; + break; + + case VK_LMENU: + case VK_RMENU: + m_keys[VK_MENU] |= 0x80; + m_fakeKeys[VK_MENU] |= 0x80; + break; + } + } +} + +void +CMSWindowsSecondaryScreen::keyRepeat(KeyID key, + KeyModifierMask mask, SInt32 count, KeyButton button) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + + // get the sequence of keys to simulate key repeat and the final + // modifier state. + Keystrokes keys; + UINT virtualKey; + m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); + if (keys.empty()) { + return; + } + + // if the keycode for the auto-repeat is not the same as for the + // initial press then mark the initial key as released and the new + // key as pressed. this can happen when we auto-repeat after a + // dead key. for example, a dead accent followed by 'a' will + // generate an 'a with accent' followed by a repeating 'a'. the + // keycodes for the two keysyms might be different. + if (virtualKey != index->second) { + // replace key up with previous keycode but leave key down + // alone so it uses the new keycode and store that keycode + // in the server key map. + for (Keystrokes::iterator index2 = keys.begin(); + index2 != keys.end(); ++index2) { + if (index2->m_virtualKey == virtualKey) { + index2->m_virtualKey = index->second; + break; + } + } + + // note that old key is now up + m_keys[index->second] = false; + m_fakeKeys[index->second] = false; + + // map server key to new key + index->second = virtualKey; + + // note that new key is now down + m_keys[index->second] = true; + m_fakeKeys[index->second] = true; + } + + // generate key events + doKeystrokes(keys, count); +} + +void +CMSWindowsSecondaryScreen::keyUp(KeyID, KeyModifierMask, KeyButton button) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + UINT virtualKey = index->second; + + // get the sequence of keys to simulate key release and the final + // modifier state. + Keystrokes keys; + m_mask = mapKeyRelease(keys, virtualKey); + + // generate key events + doKeystrokes(keys, 1); + + // note that key is now up + m_serverKeyMap.erase(index); + m_keys[virtualKey] &= ~0x80; + m_fakeKeys[virtualKey] &= ~0x80; + switch (virtualKey) { + case VK_LSHIFT: + if ((m_keys[VK_RSHIFT] & 0x80) == 0) { + m_keys[VK_SHIFT] &= ~0x80; + m_fakeKeys[VK_SHIFT] &= ~0x80; + } + break; + + case VK_RSHIFT: + if ((m_keys[VK_LSHIFT] & 0x80) == 0) { + m_keys[VK_SHIFT] &= ~0x80; + m_fakeKeys[VK_SHIFT] &= ~0x80; + } + break; + + case VK_LCONTROL: + if ((m_keys[VK_RCONTROL] & 0x80) == 0) { + m_keys[VK_CONTROL] &= ~0x80; + m_fakeKeys[VK_CONTROL] &= ~0x80; + } + break; + + case VK_RCONTROL: + if ((m_keys[VK_LCONTROL] & 0x80) == 0) { + m_keys[VK_CONTROL] &= ~0x80; + m_fakeKeys[VK_CONTROL] &= ~0x80; + } + break; + + case VK_LMENU: + if ((m_keys[VK_RMENU] & 0x80) == 0) { + m_keys[VK_MENU] &= ~0x80; + m_fakeKeys[VK_MENU] &= ~0x80; + } + break; + + case VK_RMENU: + if ((m_keys[VK_LMENU] & 0x80) == 0) { + m_keys[VK_MENU] &= ~0x80; + m_fakeKeys[VK_MENU] &= ~0x80; + } + break; + } +} + +void +CMSWindowsSecondaryScreen::mouseDown(ButtonID button) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + + // map button id to button flag + DWORD data; + DWORD flags = mapButton(button, true, &data); + + // send event + if (flags != 0) { + mouse_event(flags, 0, 0, data, 0); + } +} + +void +CMSWindowsSecondaryScreen::mouseUp(ButtonID button) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + + // map button id to button flag + DWORD data; + DWORD flags = mapButton(button, false, &data); + + // send event + if (flags != 0) { + mouse_event(flags, 0, 0, data, 0); + } +} + +void +CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + warpCursor(x, y); +} + +void +CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); +} + +void +CMSWindowsSecondaryScreen::resetOptions() +{ + CSecondaryScreen::resetOptions(); +} + +void +CMSWindowsSecondaryScreen::setOptions(const COptionsList& options) +{ + CSecondaryScreen::setOptions(options); +} + +IScreen* +CMSWindowsSecondaryScreen::getScreen() const +{ + return m_screen; +} + +void +CMSWindowsSecondaryScreen::onScreensaver(bool) +{ + // ignore +} + +bool +CMSWindowsSecondaryScreen::onPreDispatch(const CEvent*) +{ + return false; +} + +bool +CMSWindowsSecondaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + + const MSG& msg = event->m_msg; + switch (msg.message) { + case WM_ACTIVATEAPP: + if (msg.wParam == FALSE) { + // some other app activated. hide the hider window. + ShowWindow(m_window, SW_HIDE); + } + break; + } + + return false; +} + +void +CMSWindowsSecondaryScreen::onOneShotTimerExpired(UInt32) +{ + // ignore +} + +SInt32 +CMSWindowsSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + +void +CMSWindowsSecondaryScreen::postCreateWindow(HWND window) +{ + m_window = window; + + // update key state + updateKeys(); + + // hide cursor if this screen isn't active + if (!isActive()) { + SInt32 x, y; + getScreen()->getCursorCenter(x, y); + showWindow(x, y); + } +} + +void +CMSWindowsSecondaryScreen::preDestroyWindow(HWND) +{ + // do nothing +} + +void +CMSWindowsSecondaryScreen::onAccessibleDesktop() +{ + // get the current keyboard state + updateKeys(); +} + +void +CMSWindowsSecondaryScreen::onPreMainLoop() +{ + assert(m_window != NULL); +} + +void +CMSWindowsSecondaryScreen::onPreOpen() +{ + assert(m_window == NULL); +} + +void +CMSWindowsSecondaryScreen::onPreEnter() +{ + assert(m_window != NULL); +} + +void +CMSWindowsSecondaryScreen::onPreLeave() +{ + assert(m_window != NULL); +} + +void +CMSWindowsSecondaryScreen::createWindow() +{ + // open the desktop and the window + m_window = m_screen->openDesktop(); + if (m_window == NULL) { + throw XScreenOpenFailure(); + } +} + +void +CMSWindowsSecondaryScreen::destroyWindow() +{ + // release keys that are logically pressed + releaseKeys(); + + // close the desktop and the window + m_screen->closeDesktop(); + m_window = NULL; +} + +void +CMSWindowsSecondaryScreen::showWindow(SInt32 x, SInt32 y) +{ + // move hider window under the given position + MoveWindow(m_window, x, y, 1, 1, FALSE); + + // raise and show the hider window + ShowWindow(m_window, SW_SHOWNA); + + // now warp the mouse + warpCursor(x, y); +} + +void +CMSWindowsSecondaryScreen::hideWindow() +{ + ShowWindow(m_window, SW_HIDE); +} + +void +CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + // motion is simple (i.e. it's on the primary monitor) if there + // is only one monitor. + bool simple = !m_screen->isMultimon(); + if (!simple) { + // also simple if motion is within the primary monitor + simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && + y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); + } + + // move the mouse directly to target position if motion is simple + if (simple) { + // when using absolute positioning with mouse_event(), + // the normalized device coordinates range over only + // the primary screen. + SInt32 w = GetSystemMetrics(SM_CXSCREEN); + SInt32 h = GetSystemMetrics(SM_CYSCREEN); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65536.0 * x) / w), + (DWORD)((65536.0 * y) / h), + 0, 0); + } + + // windows 98 (and Me?) is broken. you cannot set the absolute + // position of the mouse except on the primary monitor but you + // can do relative moves onto any monitor. this is, in microsoft's + // words, "by design." apparently the designers of windows 2000 + // we're a little less lazy and did it right. + // + // microsoft recommends in Q193003 to absolute position the cursor + // somewhere on the primary monitor then relative move to the + // desired location. this doesn't work for us because when the + // user drags a scrollbar, a window, etc. it causes the dragged + // item to jump back a forth between the position on the primary + // monitor and the desired position. while it always ends up in + // the right place, the effect is disconcerting. + // + // instead we'll get the cursor's current position and do just a + // relative move from there to the desired position. relative + // moves are subject to cursor acceleration which we don't want. + // so we disable acceleration, do the relative move, then restore + // acceleration. there's a slight chance we'll end up in the + // wrong place if the user moves the cursor using this system's + // mouse while simultaneously moving the mouse on the server + // system. that defeats the purpose of synergy so we'll assume + // that won't happen. even if it does, the next mouse move will + // correct the position. + else { + // save mouse speed & acceleration + int oldSpeed[4]; + bool accelChanged = + SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && + SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); + + // use 1:1 motion + if (accelChanged) { + int newSpeed[4] = { 0, 0, 0, 1 }; + accelChanged = + SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || + SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); + } + + // get current mouse position + POINT pos; + GetCursorPos(&pos); + + // move relative to mouse position + mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); + + // restore mouse speed & acceleration + if (accelChanged) { + SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); + SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); + } + } +} + +void +CMSWindowsSecondaryScreen::updateKeys() +{ + // clear key state + memset(m_keys, 0, sizeof(m_keys)); + memset(m_fakeKeys, 0, sizeof(m_keys)); + + // we only care about the modifier key states + m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); + m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT)); + m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT)); + m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL)); + m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL)); + m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL)); + m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU)); + m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU)); + m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU)); + m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN)); + m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN)); + m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS)); + m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); + m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); + m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); + + // copy over lock states to m_fakeKeys + m_fakeKeys[VK_CAPITAL] = static_cast(m_keys[VK_CAPITAL] & 0x01); + m_fakeKeys[VK_NUMLOCK] = static_cast(m_keys[VK_NUMLOCK] & 0x01); + m_fakeKeys[VK_SCROLL] = static_cast(m_keys[VK_SCROLL] & 0x01); + + // update active modifier mask + m_mask = 0; + if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) { + m_mask |= KeyModifierShift; + } + if ((m_keys[VK_LCONTROL] & 0x80) != 0 || + (m_keys[VK_RCONTROL] & 0x80) != 0) { + m_mask |= KeyModifierControl; + } + if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) { + m_mask |= KeyModifierAlt; + } + // note -- no keys for KeyModifierMeta + if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) { + m_mask |= KeyModifierSuper; + } + if ((m_keys[VK_CAPITAL] & 0x01) != 0) { + m_mask |= KeyModifierCapsLock; + } + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { + m_mask |= KeyModifierNumLock; + } + if ((m_keys[VK_SCROLL] & 0x01) != 0) { + m_mask |= KeyModifierScrollLock; + } + // note -- do not save KeyModifierModeSwitch in m_mask + LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); +} + +void +CMSWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) +{ + // toggle modifiers that don't match the desired state + if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) { + toggleKey(VK_CAPITAL, KeyModifierCapsLock); + } + if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) { + toggleKey(VK_NUMLOCK | 0x100, KeyModifierNumLock); + } + if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) { + toggleKey(VK_SCROLL, KeyModifierScrollLock); + } +} + +KeyModifierMask +CMSWindowsSecondaryScreen::getToggleState() const +{ + return (m_mask & (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock)); +} + +// map special KeyID keys to virtual key codes. if the key is an +// extended key then the entry is the virtual key code | 0x100. +// unmapped keys have a 0 entry. +static const UINT g_mapE000[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, + /* 0xa4 */ 0, 0, VK_BROWSER_BACK|0x100, VK_BROWSER_FORWARD|0x100, + /* 0xa8 */ VK_BROWSER_REFRESH|0x100, VK_BROWSER_STOP|0x100, + /* 0xaa */ VK_BROWSER_SEARCH|0x100, VK_BROWSER_FAVORITES|0x100, + /* 0xac */ VK_BROWSER_HOME|0x100, VK_VOLUME_MUTE|0x100, + /* 0xae */ VK_VOLUME_DOWN|0x100, VK_VOLUME_UP|0x100, + /* 0xb0 */ VK_MEDIA_NEXT_TRACK|0x100, VK_MEDIA_PREV_TRACK|0x100, + /* 0xb2 */ VK_MEDIA_STOP|0x100, VK_MEDIA_PLAY_PAUSE|0x100, + /* 0xb4 */ VK_LAUNCH_MAIL|0x100, VK_LAUNCH_MEDIA_SELECT|0x100, + /* 0xb6 */ VK_LAUNCH_APP1|0x100, VK_LAUNCH_APP2|0x100, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_mapEE00[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ VK_TAB, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_mapEF00[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, + /* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0, + /* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ VK_HOME|0x100, VK_LEFT|0x100, VK_UP|0x100, VK_RIGHT|0x100, + /* 0x54 */ VK_DOWN|0x100, VK_PRIOR|0x100, VK_NEXT|0x100, VK_END|0x100, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ VK_SELECT|0x100, VK_SNAPSHOT|0x100, VK_EXECUTE|0x100, VK_INSERT|0x100, + /* 0x64 */ 0, 0, 0, VK_APPS|0x100, + /* 0x68 */ 0, 0, VK_HELP|0x100, VK_CANCEL|0x100, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK|0x100, + /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN|0x100, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, + /* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT, + /* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD, + /* 0xac */ VK_SEPARATOR, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE|0x100, + /* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, + /* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, + /* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2, + /* 0xc0 */ VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, + /* 0xc8 */ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, + /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, + /* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, 0, + /* 0xe8 */ 0, VK_LMENU, VK_RMENU|0x100, VK_LWIN|0x100, + /* 0xec */ VK_RWIN|0x100, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100 +}; + +DWORD +CMSWindowsSecondaryScreen::mapButton(ButtonID button, + bool press, DWORD* inData) const +{ + DWORD dummy; + DWORD* data = (inData != NULL) ? inData : &dummy; + + // the system will swap the meaning of left/right for us if + // the user has configured a left-handed mouse but we don't + // want it to swap since we want the handedness of the + // server's mouse. so pre-swap for a left-handed mouse. + if (GetSystemMetrics(SM_SWAPBUTTON)) { + switch (button) { + case kButtonLeft: + button = kButtonRight; + break; + + case kButtonRight: + button = kButtonLeft; + break; + } + } + + // map button id to button flag and button data + *data = 0; + switch (button) { + case kButtonLeft: + return press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; + + case kButtonMiddle: + return press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; + + case kButtonRight: + return press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; + + case kButtonExtra0 + 0: + *data = XBUTTON1; + return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; + + case kButtonExtra0 + 1: + *data = XBUTTON2; + return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; + + default: + return 0; + } +} + +KeyModifierMask +CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, + KeyID id, KeyModifierMask mask, EKeyAction action) const +{ + virtualKey = 0; + + // check for special keys + if ((id & 0xfffff000) == 0xe000) { + if ((id & 0xff00) == 0xe000) { + virtualKey = g_mapE000[id & 0xff]; + } + else if ((id & 0xff00) == 0xee00) { + virtualKey = g_mapEE00[id & 0xff]; + } + else if ((id & 0xff00) == 0xef00) { + virtualKey = g_mapEF00[id & 0xff]; + } + if (virtualKey == 0) { + LOG((CLOG_DEBUG2 "unknown special key")); + return m_mask; + } + } + + // special handling of VK_SNAPSHOT + if ((virtualKey & 0xff) == VK_SNAPSHOT) { + // ignore key repeats on print screen + if (action != kRepeat) { + // get event flags + DWORD flags = 0; + if (isExtendedKey(virtualKey)) { + flags |= KEYEVENTF_EXTENDEDKEY; + } + if (action != kPress) { + flags |= KEYEVENTF_KEYUP; + } + + // active window or fullscreen? + BYTE scan = 0; + if ((mask & KeyModifierAlt) == 0) { + scan = 1; + } + + // send event + keybd_event(static_cast(virtualKey & 0xff), scan, flags, 0); + } + return m_mask; + } + + // handle other special keys + if (virtualKey != 0) { + // compute the final desired modifier mask. special keys use + // the desired modifiers as given except we keep the caps lock, + // num lock, and scroll lock as is. + KeyModifierMask outMask = (m_mask & + (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock)); + outMask |= (mask & + (KeyModifierShift | + KeyModifierControl | + KeyModifierAlt | + KeyModifierMeta | + KeyModifierSuper)); + + // strip out extended key flag + UINT virtualKey2 = (virtualKey & ~0x100); + + // check numeric keypad. note that virtual keys do not distinguish + // between the keypad and non-keypad movement keys. however, the + // virtual keys do distinguish between keypad numbers and operators + // (e.g. add, multiply) and their main keyboard counterparts. + // therefore, we can ignore the num-lock state for movement virtual + // keys but not for numeric keys. + if (virtualKey2 >= VK_NUMPAD0 && virtualKey2 <= VK_DIVIDE) { + // set required shift state based on current numlock state + if ((outMask & KeyModifierNumLock) == 0) { + if ((m_mask & KeyModifierNumLock) == 0) { + LOG((CLOG_DEBUG2 "turn on num lock for keypad key")); + outMask |= KeyModifierNumLock; + } + else { + LOG((CLOG_DEBUG2 "turn on shift for keypad key")); + outMask |= KeyModifierShift; + } + } + } + + // check for left tab. that requires the shift key. + if (id == kKeyLeftTab) { + mask |= KeyModifierShift; + } + + // now generate the keystrokes and return the resulting modifier mask + LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d mask 0x%04x", id, virtualKey2, outMask)); + return mapToKeystrokes(keys, virtualKey, m_mask, outMask, action); + } + + // determine the thread that'll receive this event + // FIXME -- we can't be sure we'll get the right thread here + HWND targetWindow = GetForegroundWindow(); + DWORD targetThread = GetWindowThreadProcessId(targetWindow, NULL); + + // figure out the code page for the target thread. i'm just + // guessing here. get the target thread's keyboard layout, + // extract the language id from that, and choose the code page + // based on that language. + HKL hkl = GetKeyboardLayout(targetThread); + LANGID langID = static_cast(LOWORD(hkl)); + UINT codePage = getCodePageFromLangID(langID); + LOG((CLOG_DEBUG2 "using code page %d and language id 0x%04x for thread 0x%08x", codePage, langID, targetThread)); + + // regular characters are complicated by dead keys. it may not be + // possible to generate a desired character directly. we may need + // to generate a dead key first then some other character. the + // app receiving the events will compose these two characters into + // a single precomposed character. + // + // as best as i can tell this is the simplest way to convert a + // character into its uncomposed version. along the way we'll + // discover if the key cannot be handled at all. we convert + // from wide char to multibyte, then from multibyte to wide char + // forcing conversion to composite characters, then from wide + // char back to multibyte without making precomposed characters. + // + // after the first conversion to multibyte we see if we can map + // the key. if so then we don't bother trying to decompose dead + // keys. + BOOL error; + char multiByte[2 * MB_LEN_MAX]; + wchar_t unicode[2]; + unicode[0] = static_cast(id & 0x0000ffff); + int nChars = WideCharToMultiByte(codePage, + WC_COMPOSITECHECK | WC_DEFAULTCHAR, + unicode, 1, + multiByte, sizeof(multiByte), + NULL, &error); + if (nChars == 0 || error) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id)); + return m_mask; + } + virtualKey = mapCharacter(keys, multiByte[0], hkl, m_mask, mask, action); + if (virtualKey != static_cast(-1)) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); + if ((MapVirtualKey(virtualKey, 2) & 0x80000000u) != 0) { + // it looks like this character is a dead key but + // MapVirtualKey() will claim it's a dead key even if it's + // not (though i don't think it ever claims it's not when + // it is). we need a backup test to ensure that this is + // really a dead key. we could use ToAscii() for this but + // that keeps state and it's a hassle to restore that state. + // OemKeyScan() appears to do the trick. if the character + // cannot be generated with a single keystroke then it + // returns 0xffffffff. + if (OemKeyScan(multiByte[0]) != 0xffffffffu) { + // character mapped to a dead key but we want the + // character for real so send a space key afterwards. + LOG((CLOG_DEBUG2 "character mapped to dead key")); + Keystroke keystroke; + keystroke.m_virtualKey = VK_SPACE; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + + // ignore the release of this key since we already + // handled it in mapCharacter(). + virtualKey = 0; + } + } + return m_mask; + } + nChars = MultiByteToWideChar(codePage, + MB_COMPOSITE | MB_ERR_INVALID_CHARS, + multiByte, nChars, + unicode, 2); + if (nChars == 0) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x mb->wc mapping failed", id)); + return m_mask; + } + nChars = WideCharToMultiByte(codePage, + 0, + unicode, nChars, + multiByte, sizeof(multiByte), + NULL, &error); + if (nChars == 0 || error) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x wc->mb mapping failed", id)); + return m_mask; + } + + // we expect one or two characters in multiByte. if there are two + // then the *second* is a dead key. process the dead key if there. + // FIXME -- we assume each character is one byte here + if (nChars > 2) { + LOG((CLOG_DEBUG2 "multibyte characters not supported for character 0x%04x", id)); + return m_mask; + } + if (nChars == 2) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x needs dead key %u", id, (unsigned char)multiByte[1])); + mapCharacter(keys, multiByte[1], hkl, m_mask, mask, action); + } + + // process character + LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); + virtualKey = mapCharacter(keys, multiByte[0], hkl, m_mask, mask, action); + + // non-special key cannot modify the modifier mask + return m_mask; +} + +KeyModifierMask +CMSWindowsSecondaryScreen::mapKeyRelease(Keystrokes& keys, + UINT virtualKey) const +{ + // add key release + Keystroke keystroke; + keystroke.m_virtualKey = virtualKey; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + + // if this is a modifier keycode then update the current modifier mask + const CModifierInfo* modifier = getModifierInfo(virtualKey); + if (modifier != NULL) { + if (modifier->m_isToggle) { + // toggle keys modify the state on release + return (m_mask ^ modifier->m_mask); + } + else { + // can't reset bit until all keys that set it are released. + // scan those keys to see if any (except virtualKey) are + // pressed. + bool down = false; + if (virtualKey != (modifier->m_virtualKey & 0xff) && + (m_keys[modifier->m_virtualKey & 0xff] & 0x80) != 0) { + down = true; + } + if (modifier->m_virtualKey2 != 0 && + virtualKey != (modifier->m_virtualKey2 & 0xff) && + (m_keys[modifier->m_virtualKey2 & 0xff] & 0x80) != 0) { + down = true; + } + if (!down) { + return (m_mask & ~modifier->m_mask); + } + } + } + + return m_mask; +} + +UINT +CMSWindowsSecondaryScreen::mapCharacter(Keystrokes& keys, + char c, HKL hkl, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, EKeyAction action) const +{ + // translate the character into its virtual key and its required + // modifier state. + SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); + + // get virtual key + UINT virtualKey = LOBYTE(virtualKeyAndModifierState); + if (LOBYTE(virtualKeyAndModifierState) == 0xff) { + LOG((CLOG_DEBUG2 "cannot map character %d", static_cast(c))); + return static_cast(-1); + } + + // get the required modifier state + BYTE modifierState = HIBYTE(virtualKeyAndModifierState); + + // compute the final desired modifier mask. this is the + // desired modifier mask except that the system might require + // that certain modifiers be up or down in order to generate + // the character. to start with, we know that we want to keep + // the caps lock, num lock, scroll lock modifiers as is. also, + // the system never requires the meta or super modifiers so we + // can set those however we like. + KeyModifierMask outMask = (currentMask & + (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock)); + outMask |= (desiredMask & + (KeyModifierMeta | + KeyModifierSuper)); + + // win32 does not permit ctrl and alt used together to + // modify a character because ctrl and alt together mean + // AltGr. if the desired mask has both ctrl and alt then + // strip them both out. + if ((desiredMask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + desiredMask &= ~(KeyModifierControl | KeyModifierAlt); + } + + // strip out the desired shift state. we're forced to use + // a particular shift state to generate the desired character. + outMask &= ~KeyModifierShift; + + // use the required modifiers. if AltGr is required then + // modifierState will indicate control and alt. + if ((modifierState & 1) != 0) { + outMask |= KeyModifierShift; + } + if ((modifierState & 2) != 0) { + outMask |= KeyModifierControl; + } + if ((modifierState & 4) != 0) { + outMask |= KeyModifierAlt; + } + + // use desired modifiers + if ((desiredMask & KeyModifierControl) != 0) { + outMask |= KeyModifierControl; + } + if ((desiredMask & KeyModifierAlt) != 0) { + outMask |= KeyModifierAlt; + } + + // handle combination of caps-lock and shift. if caps-lock is + // off locally then use shift as necessary. if caps-lock is on + // locally then it reverses the meaning of shift for keys that + // are subject to case conversion. + if ((outMask & KeyModifierCapsLock) != 0) { + // there doesn't seem to be a simple way to test if a + // character respects the caps lock key. for normal + // characters it's easy enough but CharLower() and + // CharUpper() don't map dead keys even though they + // do respect caps lock for some unfathomable reason. + // first check the easy way. if that doesn't work + // then see if it's a dead key. + unsigned char uc = static_cast(c); + if (CharLower((LPTSTR)uc) != CharUpper((LPTSTR)uc) || + (MapVirtualKey(virtualKey, 2) & 0x80000000lu) != 0) { + LOG((CLOG_DEBUG2 "flip shift")); + outMask ^= KeyModifierShift; + } + } + + // now generate the keystrokes. ignore the resulting modifier + // mask since it can't have changed (because we don't call this + // method for modifier keys). + LOG((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%04x", (unsigned char)c, virtualKey, outMask)); + mapToKeystrokes(keys, virtualKey, currentMask, outMask, action); + + return virtualKey; +} + +KeyModifierMask +CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, + UINT virtualKey, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, EKeyAction action) const +{ + const CModifierInfo* modifier = getModifierInfo(virtualKey); + + // add the key events required to get to the desired modifier state. + // also save the key events required to restore the current state. + // if the key is a modifier key then skip this because modifiers + // should not modify modifiers. + Keystrokes undo; + Keystroke keystroke; + if (desiredMask != currentMask && modifier == NULL) { + const unsigned int s_numModifiers = sizeof(s_modifier) / + sizeof(s_modifier[0]); + for (unsigned int i = 0; i < s_numModifiers; ++i) { + KeyModifierMask bit = s_modifier[i].m_mask; + if ((desiredMask & bit) != (currentMask & bit)) { + if ((desiredMask & bit) != 0) { + // modifier is not active but should be. if the + // modifier is a toggle then toggle it on with a + // press/release, otherwise activate it with a + // press. + keystroke.m_virtualKey = s_modifier[i].m_virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + if (s_modifier[i].m_isToggle) { + keystroke.m_press = false; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + else { + keystroke.m_press = false; + undo.push_back(keystroke); + } + } + + else { + // modifier is active but should not be. if the + // modifier is a toggle then toggle it off with a + // press/release, otherwise deactivate it with a + // release. we must check each keycode for the + // modifier if not a toggle. + if (s_modifier[i].m_isToggle) { + keystroke.m_virtualKey = s_modifier[i].m_virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + else { + UINT key = s_modifier[i].m_virtualKey; + if ((m_keys[key & 0xff] & 0x80) != 0) { + keystroke.m_virtualKey = key; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + key = s_modifier[i].m_virtualKey2; + if (key != 0 && (m_keys[key & 0xff] & 0x80) != 0) { + keystroke.m_virtualKey = key; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + } + } + } + } + } + + // add the key event + keystroke.m_virtualKey = virtualKey; + switch (action) { + case kPress: + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRelease: + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRepeat: + keystroke.m_press = true; + keystroke.m_repeat = true; + keys.push_back(keystroke); + break; + } + + // if this is a dead key press then send a release immediately. + // the dead key may not be processed correctly if its release + // event comes after we release the modifiers. + if (action == kPress && + (MapVirtualKey(virtualKey, 2) & 0x80000000lu) != 0) { + keystroke.m_press = false; + keys.push_back(keystroke); + } + + // add key events to restore the modifier state. apply events in + // the reverse order that they're stored in undo. + while (!undo.empty()) { + keys.push_back(undo.back()); + undo.pop_back(); + } + + // if the key is a modifier key then compute the modifier mask after + // this key is pressed. toggle keys modify the state on release. + // other keys set the modifier bit on press. + KeyModifierMask mask = currentMask; + if (action == kPress) { + if (modifier != NULL && !modifier->m_isToggle) { + mask |= modifier->m_mask; + } + } + + LOG((CLOG_DEBUG2 "previous modifiers 0x%04x, final modifiers 0x%04x", currentMask, mask)); + return mask; +} + +void +CMSWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) +{ + // do nothing if no keys or no repeats + if (count < 1 || keys.empty()) { + return; + } + + // generate key events + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { + if (k->m_repeat) { + // repeat from here up to but not including the next key + // with m_repeat == false count times. + Keystrokes::const_iterator start = k; + for (; count > 0; --count) { + // send repeating events + for (k = start; k != keys.end() && k->m_repeat; ++k) { + sendKeyEvent(k->m_virtualKey, k->m_press); + } + } + + // note -- k is now on the first non-repeat key after the + // repeat keys, exactly where we'd like to continue from. + } + else { + // send event + sendKeyEvent(k->m_virtualKey, k->m_press); + + // next key + ++k; + } + } +} + +const CMSWindowsSecondaryScreen::CModifierInfo* +CMSWindowsSecondaryScreen::getModifierInfo(UINT virtualKey) const +{ + // note if the key is a modifier. strip out extended key flag from + // virtual key before lookup. + switch (virtualKey & ~0x100) { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + return s_modifier + 0; + + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + return s_modifier + 1; + + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + return s_modifier + 2; + + case VK_LWIN: + case VK_RWIN: + return s_modifier + 3; + + case VK_CAPITAL: + return s_modifier + 4; + + case VK_NUMLOCK: + return s_modifier + 5; + + case VK_SCROLL: + return s_modifier + 6; + + default: + return NULL; + } +} + +void +CMSWindowsSecondaryScreen::releaseKeys() +{ + // release keys that we've synthesized a press for and only those + // keys. we don't want to synthesize a release on a key the user + // is still physically pressing. + + CLock lock(&m_mutex); + + m_screen->syncDesktop(); + + // release left/right modifier keys first. if the platform doesn't + // support them then they won't be set and the non-side-distinuishing + // key will retain its state. if the platform does support them then + // the non-side-distinguishing will be reset. + if ((m_fakeKeys[VK_LSHIFT] & 0x80) != 0) { + sendKeyEvent(VK_LSHIFT, false); + m_fakeKeys[VK_SHIFT] = 0; + m_fakeKeys[VK_LSHIFT] = 0; + } + if ((m_fakeKeys[VK_RSHIFT] & 0x80) != 0) { + sendKeyEvent(VK_RSHIFT, false); + m_fakeKeys[VK_SHIFT] = 0; + m_fakeKeys[VK_RSHIFT] = 0; + } + if ((m_fakeKeys[VK_LCONTROL] & 0x80) != 0) { + sendKeyEvent(VK_LCONTROL, false); + m_fakeKeys[VK_CONTROL] = 0; + m_fakeKeys[VK_LCONTROL] = 0; + } + if ((m_fakeKeys[VK_RCONTROL] & 0x80) != 0) { + sendKeyEvent(VK_RCONTROL, false); + m_fakeKeys[VK_CONTROL] = 0; + m_fakeKeys[VK_RCONTROL] = 0; + } + if ((m_fakeKeys[VK_LMENU] & 0x80) != 0) { + sendKeyEvent(VK_LMENU, false); + m_fakeKeys[VK_MENU] = 0; + m_fakeKeys[VK_LMENU] = 0; + } + if ((m_fakeKeys[VK_RMENU] & 0x80) != 0) { + sendKeyEvent(VK_RMENU, false); + m_fakeKeys[VK_MENU] = 0; + m_fakeKeys[VK_RMENU] = 0; + } + + // now check all the other keys + for (UInt32 i = 0; i < sizeof(m_fakeKeys) / sizeof(m_fakeKeys[0]); ++i) { + if ((m_fakeKeys[i] & 0x80) != 0) { + sendKeyEvent(i, false); + m_fakeKeys[i] = 0; + } + } +} + +void +CMSWindowsSecondaryScreen::toggleKey(UINT virtualKey, KeyModifierMask mask) +{ + // send key events to simulate a press and release + sendKeyEvent(virtualKey, true); + sendKeyEvent(virtualKey, false); + + // toggle shadow state + m_mask ^= mask; + m_keys[virtualKey & 0xff] ^= 0x01; + m_fakeKeys[virtualKey & 0xff] ^= 0x01; +} + +UINT +CMSWindowsSecondaryScreen::virtualKeyToScanCode(UINT& virtualKey) const +{ + // try mapping given virtual key + UINT code = MapVirtualKey(virtualKey & 0xff, 0); + if (code != 0) { + return code; + } + + // no dice. if the virtual key distinguishes between left/right + // then try the one that doesn't distinguish sides. windows (or + // keyboard drivers) are inconsistent in their treatment of these + // virtual keys. the following behaviors have been observed: + // + // win2k (gateway desktop): + // MapVirtualKey(vk, 0): + // VK_SHIFT == VK_LSHIFT != VK_RSHIFT + // VK_CONTROL == VK_LCONTROL == VK_RCONTROL + // VK_MENU == VK_LMENU == VK_RMENU + // MapVirtualKey(sc, 3): + // VK_LSHIFT and VK_RSHIFT mapped independently + // VK_LCONTROL is mapped but not VK_RCONTROL + // VK_LMENU is mapped but not VK_RMENU + // + // win me (sony vaio laptop): + // MapVirtualKey(vk, 0): + // VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped + // VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped + // VK_MENU mapped; VK_LMENU, VK_RMENU not mapped + // MapVirtualKey(sc, 3): + // all scan codes unmapped (function apparently unimplemented) + switch (virtualKey & 0xff) { + case VK_LSHIFT: + case VK_RSHIFT: + virtualKey = VK_SHIFT; + return MapVirtualKey(VK_SHIFT, 0); + + case VK_LCONTROL: + case VK_RCONTROL: + virtualKey = VK_CONTROL; + return MapVirtualKey(VK_CONTROL, 0); + + case VK_LMENU: + case VK_RMENU: + virtualKey = VK_MENU; + return MapVirtualKey(VK_MENU, 0); + + default: + return 0; + } +} + +bool +CMSWindowsSecondaryScreen::isExtendedKey(UINT virtualKey) const +{ + // see if we've already encoded the extended flag + if ((virtualKey & 0x100) != 0) { + return true; + } + + // check known virtual keys + switch (virtualKey & 0xff) { + case VK_NUMLOCK: + case VK_RCONTROL: + case VK_RMENU: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + return true; + + default: + return false; + } +} + +void +CMSWindowsSecondaryScreen::sendKeyEvent(UINT virtualKey, bool press) +{ + DWORD flags = 0; + if (isExtendedKey(virtualKey)) { + flags |= KEYEVENTF_EXTENDEDKEY; + } + if (!press) { + flags |= KEYEVENTF_KEYUP; + } + const UINT code = virtualKeyToScanCode(virtualKey); + keybd_event(static_cast(virtualKey & 0xff), + static_cast(code), flags, 0); + LOG((CLOG_DEBUG1 "send key %d, 0x%04x, %s%s", virtualKey & 0xff, code, ((flags & KEYEVENTF_KEYUP) ? "release" : "press"), ((flags & KEYEVENTF_EXTENDEDKEY) ? " extended" : ""))); +} + +UINT +CMSWindowsSecondaryScreen::getCodePageFromLangID(LANGID langid) const +{ + // construct a locale id from the language id + LCID lcid = MAKELCID(langid, SORT_DEFAULT); + + // get the ANSI code page for this locale + char data[6]; + if (GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, data, 6) == 0) { + // can't get code page + LOG((CLOG_DEBUG1 "can't find code page for langid 0x%04x", langid)); + return CP_ACP; + } + + // convert stringified code page into a number + UINT codePage = static_cast(atoi(data)); + if (codePage == 0) { + // parse failed + LOG((CLOG_DEBUG1 "can't parse code page %s for langid 0x%04x", data, langid)); + return CP_ACP; + } + + return codePage; +} + +void +CMSWindowsSecondaryScreen::synthesizeCtrlAltDel() +{ + LOG((CLOG_DEBUG "emulating ctrl+alt+del")); + if (!m_is95Family) { + // to fake ctrl+alt+del on the NT family we broadcast a suitable + // hotkey to all windows on the winlogon desktop. however, we + // the current thread must be on that desktop to do the broadcast + // and we can't switch just any thread because some own windows + // or hooks. so start a new thread to do the real work. + CThread cad(new CFunctionJob( + &CMSWindowsSecondaryScreen::ctrlAltDelThread)); + cad.wait(); + } + else { + Keystrokes keys; + UINT virtualKey; + KeyID key = kKeyDelete; + KeyModifierMask mask = KeyModifierControl | KeyModifierAlt; + + // get the sequence of keys to simulate ctrl+alt+del + mapKey(keys, virtualKey, key, mask, kPress); + if (!keys.empty()) { + // generate key events + doKeystrokes(keys, 1); + } + } +} + +void +CMSWindowsSecondaryScreen::ctrlAltDelThread(void*) +{ + // get the Winlogon desktop at whatever privilege we can + HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED); + if (desk != NULL) { + if (SetThreadDesktop(desk)) { + PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, + MAKELPARAM(MOD_CONTROL | MOD_ALT, VK_DELETE)); + } + else { + LOG((CLOG_DEBUG "can't switch to Winlogon desk: %d", GetLastError())); + } + CloseDesktop(desk); + } + else { + LOG((CLOG_DEBUG "can't open Winlogon desk: %d", GetLastError())); + } +} diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h new file mode 100644 index 0000000..ed1bdf9 --- /dev/null +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -0,0 +1,166 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSSECONDARYSCREEN_H +#define CMSWINDOWSSECONDARYSCREEN_H + +// ensure that we get SendInput() +#if _WIN32_WINNT <= 0x400 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x401 +#endif + +#include "CSecondaryScreen.h" +#include "IMSWindowsScreenEventHandler.h" +#include "CMutex.h" +#include "CString.h" +#include "stdmap.h" +#include "stdvector.h" + +class CMSWindowsScreen; +class IScreenReceiver; + +//! Microsoft windows secondary screen implementation +class CMSWindowsSecondaryScreen : + public CSecondaryScreen, public IMSWindowsScreenEventHandler { +public: + CMSWindowsSecondaryScreen(IScreenReceiver*); + virtual ~CMSWindowsSecondaryScreen(); + + // CSecondaryScreen overrides + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void mouseWheel(SInt32 delta); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual IScreen* getScreen() const; + + // IMSWindowsScreenEventHandler overrides + virtual void onScreensaver(bool activated); + virtual bool onPreDispatch(const CEvent* event); + virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); + virtual SInt32 getJumpZoneSize() const; + virtual void postCreateWindow(HWND); + virtual void preDestroyWindow(HWND); + virtual void onAccessibleDesktop(); + +protected: + // CSecondaryScreen overrides + virtual void onPreMainLoop(); + virtual void onPreOpen(); + virtual void onPreEnter(); + virtual void onPreLeave(); + virtual void createWindow(); + virtual void destroyWindow(); + virtual void showWindow(SInt32 x, SInt32 y); + virtual void hideWindow(); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void updateKeys(); + virtual void releaseKeys(); + virtual void setToggleState(KeyModifierMask); + virtual KeyModifierMask getToggleState() const; + +private: + enum EKeyAction { kPress, kRelease, kRepeat }; + class Keystroke { + public: + UINT m_virtualKey; + bool m_press; + bool m_repeat; + }; + class CModifierInfo { + public: + KeyModifierMask m_mask; + UINT m_virtualKey; + UINT m_virtualKey2; + bool m_isToggle; + }; + typedef std::vector Keystrokes; + typedef std::map ServerKeyMap; + + // open/close desktop (for windows 95/98/me) + bool openDesktop(); + void closeDesktop(); + + // make desk the thread desktop (for windows NT/2000/XP) + bool switchDesktop(HDESK desk); + + // returns true iff there appear to be multiple monitors + bool isMultimon() const; + + // key and button queries and operations + DWORD mapButton(ButtonID button, + bool press, DWORD* data) const; + KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, + KeyModifierMask, EKeyAction) const; + KeyModifierMask mapKeyRelease(Keystrokes& keys, UINT virtualKey) const; + UINT mapCharacter(Keystrokes& keys, + char c, HKL hkl, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, + EKeyAction action) const; + KeyModifierMask mapToKeystrokes(Keystrokes& keys, + UINT virtualKey, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, + EKeyAction action) const; + void doKeystrokes(const Keystrokes&, SInt32 count); + const CModifierInfo* getModifierInfo(UINT virtualKey) const; + + void toggleKey(UINT virtualKey, KeyModifierMask mask); + UINT virtualKeyToScanCode(UINT& virtualKey) const; + bool isExtendedKey(UINT virtualKey) const; + void sendKeyEvent(UINT virtualKey, bool press); + + UINT getCodePageFromLangID(LANGID) const; + + // generate a fake ctrl+alt+del + void synthesizeCtrlAltDel(); + + // thread that generates fake ctrl+alt+del + static void ctrlAltDelThread(void*); + +private: + CMutex m_mutex; + CMSWindowsScreen* m_screen; + + // true if windows 95/98/me + bool m_is95Family; + + // our window + HWND m_window; + + // virtual key states as set by us or the user + BYTE m_keys[256]; + + // virtual key states as set by us + BYTE m_fakeKeys[256]; + + // current active modifiers + KeyModifierMask m_mask; + + // map server key buttons to local virtual keys + ServerKeyMap m_serverKeyMap; + + // modifier table + static const CModifierInfo s_modifier[]; +}; + +#endif diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp new file mode 100644 index 0000000..904d46b --- /dev/null +++ b/lib/platform/CSynergyHook.cpp @@ -0,0 +1,842 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CSynergyHook.h" +#include "ProtocolTypes.h" +#include + +// +// debugging compile flag. when not zero the server doesn't grab +// the keyboard when the mouse leaves the server screen. this +// makes it possible to use the debugger (via the keyboard) when +// all user input would normally be caught by the hook procedures. +// +#define NO_GRAB_KEYBOARD 0 + +// +// extra mouse wheel stuff +// + +enum EWheelSupport { + kWheelNone, + kWheelOld, + kWheelWin2000, + kWheelModern +}; + +// declare extended mouse hook struct. useable on win2k +typedef struct tagMOUSEHOOKSTRUCTWin2000 { + MOUSEHOOKSTRUCT mhs; + DWORD mouseData; +} MOUSEHOOKSTRUCTWin2000; + +#if !defined(SM_MOUSEWHEELPRESENT) +#define SM_MOUSEWHEELPRESENT 75 +#endif + +// X button stuff +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_NCXBUTTONDOWN 0x00AB +#define WM_NCXBUTTONUP 0x00AC +#define WM_NCXBUTTONDBLCLK 0x00AD +#define MOUSEEVENTF_XDOWN 0x0100 +#define MOUSEEVENTF_XUP 0x0200 +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + + +// +// globals +// + +#pragma comment(linker, "-section:shared,rws") +#pragma data_seg("shared") +// all data in this shared section *must* be initialized + +static HINSTANCE g_hinstance = NULL; +static DWORD g_processID = 0; +static EWheelSupport g_wheelSupport = kWheelNone; +static UINT g_wmMouseWheel = 0; +static DWORD g_threadID = 0; +static HHOOK g_keyboard = NULL; +static HHOOK g_mouse = NULL; +static HHOOK g_getMessage = NULL; +static HANDLE g_hookThreadLL = NULL; +static DWORD g_hookThreadIDLL = 0; +static HANDLE g_hookEventLL = NULL; +static HHOOK g_keyboardLL = NULL; +static HHOOK g_mouseLL = NULL; +static bool g_screenSaver = false; +static bool g_relay = false; +static UInt32 g_zoneSides = 0; +static SInt32 g_zoneSize = 0; +static SInt32 g_xScreen = 0; +static SInt32 g_yScreen = 0; +static SInt32 g_wScreen = 0; +static SInt32 g_hScreen = 0; +static HCURSOR g_cursor = NULL; +static DWORD g_cursorThread = 0; + +#pragma data_seg() + +// keep linker quiet about floating point stuff. we don't use any +// floating point operations but our includes may define some +// (unused) floating point values. +#ifndef _DEBUG +extern "C" int _fltused=0; +#endif + + +// +// internal functions +// + +static +void +hideCursor(DWORD thread) +{ + // we should be running the context of the window who's cursor + // we want to hide so we shouldn't have to attach thread input + // but we'll check to make sure. + g_cursorThread = thread; + if (g_cursorThread != 0) { + DWORD myThread = GetCurrentThreadId(); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, TRUE); + g_cursor = SetCursor(NULL); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, FALSE); + } +} + +static +void +restoreCursor() +{ + // restore the show cursor in the window we hid it last + if (g_cursor != NULL && g_cursorThread != 0) { + DWORD myThread = GetCurrentThreadId(); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, TRUE); + SetCursor(g_cursor); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, FALSE); + } + g_cursor = NULL; + g_cursorThread = 0; +} + +static +bool +keyboardHookHandler(WPARAM wParam, LPARAM lParam) +{ + // forward message to our window. do this whether or not we're + // forwarding events to clients because this'll keep our thread's + // key state table up to date. that's important for querying + // the scroll lock toggle state. + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, wParam, lParam); + + if (g_relay) { + // let certain keys pass through + switch (wParam) { + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // pass event on. we want to let these through to + // the window proc because otherwise the keyboard + // lights may not stay synchronized. + break; + + default: + // discard event + return true; + } + } + + return false; +} + +static +bool +mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) +{ + switch (wParam) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_XBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_XBUTTONDBLCLK: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_XBUTTONUP: + case WM_NCLBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCXBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCMBUTTONDBLCLK: + case WM_NCRBUTTONDBLCLK: + case WM_NCXBUTTONDBLCLK: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONUP: + case WM_NCRBUTTONUP: + case WM_NCXBUTTONUP: + // always relay the event. eat it if relaying. + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_BUTTON, wParam, data); + return g_relay; + + case WM_MOUSEWHEEL: + if (g_relay) { + // relay event + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, data, 0); + } + return g_relay; + + case WM_NCMOUSEMOVE: + case WM_MOUSEMOVE: + if (g_relay) { + // we want the cursor to be hidden at all times so we + // hide the cursor on whatever window has it. but then + // we have to show the cursor whenever we leave that + // window (or at some later time before we stop relaying). + // so check the window with the cursor. if it's not the + // same window that had it before then show the cursor + // in the last window and hide it in this window. + DWORD thread = GetCurrentThreadId(); + if (thread != g_cursorThread) { + restoreCursor(); + hideCursor(thread); + } + + // relay and eat event + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); + return true; + } + else { + // check for mouse inside jump zone + bool inside = false; + if (!inside && (g_zoneSides & kLeftMask) != 0) { + inside = (x < g_xScreen + g_zoneSize); + } + if (!inside && (g_zoneSides & kRightMask) != 0) { + inside = (x >= g_xScreen + g_wScreen - g_zoneSize); + } + if (!inside && (g_zoneSides & kTopMask) != 0) { + inside = (y < g_yScreen + g_zoneSize); + } + if (!inside && (g_zoneSides & kBottomMask) != 0) { + inside = (y >= g_yScreen + g_hScreen - g_zoneSize); + } + + // relay the event + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); + + // if inside then eat the event + return inside; + } + } + + // pass the event + return false; +} + +static +LRESULT CALLBACK +keyboardHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // handle the message + if (keyboardHookHandler(wParam, lParam)) { + return 1; + } + } + + return CallNextHookEx(g_keyboard, code, wParam, lParam); +} + +static +LRESULT CALLBACK +mouseHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // decode message + const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + SInt32 w = 0; + if (wParam == WM_MOUSEWHEEL) { + // win2k and other systems supporting WM_MOUSEWHEEL in + // the mouse hook are gratuitously different (and poorly + // documented). if a low-level mouse hook is in place + // it should capture these events so we'll never see + // them. + switch (g_wheelSupport) { + case kWheelModern: + w = static_cast(LOWORD(info->dwExtraInfo)); + break; + + case kWheelWin2000: { + const MOUSEHOOKSTRUCTWin2000* info2k = + (const MOUSEHOOKSTRUCTWin2000*)lParam; + w = static_cast(HIWORD(info2k->mouseData)); + break; + } + } + } + + // handle the message. note that we don't handle X buttons + // here. that's okay because they're only supported on + // win2k and winxp and up and on those platforms we'll get + // get the mouse events through the low level hook. + if (mouseHookHandler(wParam, x, y, w)) { + return 1; + } + } + + return CallNextHookEx(g_mouse, code, wParam, lParam); +} + +static +LRESULT CALLBACK +getMessageHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + if (g_screenSaver) { + MSG* msg = reinterpret_cast(lParam); + if (msg->message == WM_SYSCOMMAND && + msg->wParam == SC_SCREENSAVE) { + // broadcast screen saver started message + PostThreadMessage(g_threadID, + SYNERGY_MSG_SCREEN_SAVER, TRUE, 0); + } + } + if (g_relay) { + MSG* msg = reinterpret_cast(lParam); + if (msg->message == g_wmMouseWheel) { + // post message to our window + PostThreadMessage(g_threadID, + SYNERGY_MSG_MOUSE_WHEEL, msg->wParam, 0); + + // zero out the delta in the message so it's (hopefully) + // ignored + msg->wParam = 0; + } + } + } + + return CallNextHookEx(g_getMessage, code, wParam, lParam); +} + +#if (_WIN32_WINNT >= 0x0400) + +// +// low-level keyboard hook -- this allows us to capture and handle +// alt+tab, alt+esc, ctrl+esc, and windows key hot keys. on the down +// side, key repeats are not reported to us. +// + +static +LRESULT CALLBACK +keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // decode the message + KBDLLHOOKSTRUCT* info = reinterpret_cast(lParam); + WPARAM wParam = info->vkCode; + LPARAM lParam = 1; // repeat code + lParam |= (info->scanCode << 16); // scan code + if (info->flags & LLKHF_EXTENDED) { + lParam |= (1lu << 24); // extended key + } + if (info->flags & LLKHF_ALTDOWN) { + lParam |= (1lu << 29); // context code + } + if (info->flags & LLKHF_UP) { + lParam |= (1lu << 31); // transition + } + // FIXME -- bit 30 should be set if key was already down but + // we don't know that info. as a result we'll never generate + // key repeat events. + + // handle the message + if (keyboardHookHandler(wParam, lParam)) { + return 1; + } + } + + return CallNextHookEx(g_keyboardLL, code, wParam, lParam); +} + +// +// low-level mouse hook -- this allows us to capture and handle mouse +// events very early. the earlier the better. +// + +static +LRESULT CALLBACK +mouseLLHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // decode the message + MSLLHOOKSTRUCT* info = reinterpret_cast(lParam); + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + SInt32 w = (SInt32)HIWORD(info->mouseData); + + // handle the message + if (mouseHookHandler(wParam, x, y, w)) { + return 1; + } + } + + return CallNextHookEx(g_mouseLL, code, wParam, lParam); +} + +static +DWORD WINAPI +getLowLevelProc(void*) +{ + // thread proc for low-level keyboard/mouse hooks. this does + // nothing but install the hook, process events, and uninstall + // the hook. + + // force this thread to have a message queue + MSG msg; + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + +#if !NO_GRAB_KEYBOARD + // install low-level keyboard hook + g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL, + &keyboardLLHook, + g_hinstance, + 0); + if (g_keyboardLL == NULL) { + // indicate failure and exit + g_hookThreadIDLL = 0; + SetEvent(g_hookEventLL); + return 1; + } +#else + // keep compiler quiet + &keyboardLLHook; +#endif + + // install low-level mouse hook + g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL, + &mouseLLHook, + g_hinstance, + 0); + if (g_mouseLL == NULL) { + // indicate failure and exit + if (g_keyboardLL != NULL) { + UnhookWindowsHookEx(g_keyboardLL); + g_keyboardLL = NULL; + } + g_hookThreadIDLL = 0; + SetEvent(g_hookEventLL); + return 1; + } + + // ready + SetEvent(g_hookEventLL); + + // message loop + bool done = false; + while (!done) { + switch (GetMessage(&msg, NULL, 0, 0)) { + case -1: + break; + + case 0: + done = true; + break; + + default: + TranslateMessage(&msg); + DispatchMessage(&msg); + break; + } + } + + // uninstall hook + UnhookWindowsHookEx(g_mouseLL); + UnhookWindowsHookEx(g_keyboardLL); + g_mouseLL = NULL; + g_keyboardLL = NULL; + + return 0; +} + +#else // (_WIN32_WINNT < 0x0400) + +static +DWORD WINAPI +getLowLevelProc(void*) +{ + g_hookThreadIDLL = 0; + SetEvent(g_hookEventLL); + return 1; +} + +#endif + +static +EWheelSupport +getWheelSupport() +{ + // get operating system + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(info); + if (!GetVersionEx(&info)) { + return kWheelNone; + } + + // see if modern wheel is present + if (GetSystemMetrics(SM_MOUSEWHEELPRESENT)) { + // note if running on win2k + if (info.dwPlatformId == VER_PLATFORM_WIN32_NT && + info.dwMajorVersion == 5 && + info.dwMinorVersion == 0) { + return kWheelWin2000; + } + return kWheelModern; + } + + // not modern. see if we've got old-style support. + UINT wheelSupportMsg = RegisterWindowMessage(MSH_WHEELSUPPORT); + HWND wheelSupportWindow = FindWindow(MSH_WHEELMODULE_CLASS, + MSH_WHEELMODULE_TITLE); + if (wheelSupportWindow != NULL && wheelSupportMsg != 0) { + if (SendMessage(wheelSupportWindow, wheelSupportMsg, 0, 0) != 0) { + g_wmMouseWheel = RegisterWindowMessage(MSH_MOUSEWHEEL); + if (g_wmMouseWheel != 0) { + return kWheelOld; + } + } + } + + // assume modern. we don't do anything special in this case + // except respond to WM_MOUSEWHEEL messages. GetSystemMetrics() + // can apparently return FALSE even if a mouse wheel is present + // though i'm not sure exactly when it does that (WinME returns + // FALSE for my logitech USB trackball). + return kWheelModern; +} + + +// +// external functions +// + +BOOL WINAPI +DllMain(HINSTANCE instance, DWORD reason, LPVOID) +{ + if (reason == DLL_PROCESS_ATTACH) { + DisableThreadLibraryCalls(instance); + if (g_processID == 0) { + g_hinstance = instance; + g_processID = GetCurrentProcessId(); + } + } + else if (reason == DLL_PROCESS_DETACH) { + if (g_processID == GetCurrentProcessId()) { + if (g_keyboard != NULL || + g_mouse != NULL || + g_getMessage != NULL) { + uninstall(); + uninstallScreenSaver(); + } + g_processID = 0; + g_hinstance = NULL; + } + } + return TRUE; +} + +extern "C" { + +int +init(DWORD threadID) +{ + assert(g_hinstance != NULL); + + // try to open process that last called init() to see if it's + // still running or if it died without cleaning up. + if (g_processID != 0 && g_processID != GetCurrentProcessId()) { + HANDLE process = OpenProcess(STANDARD_RIGHTS_REQUIRED, + FALSE, g_processID); + if (process != NULL) { + // old process (probably) still exists so refuse to + // reinitialize this DLL (and thus steal it from the + // old process). + CloseHandle(process); + return 0; + } + + // clean up after old process. the system should've already + // removed the hooks so we just need to reset our state. + g_hinstance = GetModuleHandle("synrgyhk"); + g_processID = GetCurrentProcessId(); + g_wheelSupport = kWheelNone; + g_threadID = 0; + g_keyboard = NULL; + g_mouse = NULL; + g_getMessage = NULL; + g_hookThreadLL = NULL; + g_hookThreadIDLL = 0; + g_hookEventLL = NULL; + g_keyboardLL = NULL; + g_mouseLL = NULL; + g_screenSaver = false; + } + + // save thread id. we'll post messages to this thread's + // message queue. + g_threadID = threadID; + + // set defaults + g_relay = false; + g_zoneSides = 0; + g_zoneSize = 0; + g_xScreen = 0; + g_yScreen = 0; + g_wScreen = 0; + g_hScreen = 0; + g_cursor = NULL; + g_cursorThread = 0; + + return 1; +} + +int +cleanup(void) +{ + assert(g_hinstance != NULL); + + if (g_processID == GetCurrentProcessId()) { + g_threadID = 0; + } + + return 1; +} + +EHookResult +install() +{ + assert(g_hinstance != NULL); + assert(g_keyboard == NULL); + assert(g_mouse == NULL); + assert(g_getMessage == NULL || g_screenSaver); + + // must be initialized + if (g_threadID == 0) { + return kHOOK_FAILED; + } + + // check for mouse wheel support + g_wheelSupport = getWheelSupport(); + + // install GetMessage hook (unless already installed) + if (g_wheelSupport == kWheelOld && g_getMessage == NULL) { + g_getMessage = SetWindowsHookEx(WH_GETMESSAGE, + &getMessageHook, + g_hinstance, + 0); + } + + // install low-level keyboard/mouse hooks, if possible. since these + // hooks are called in the context of the installing thread and that + // thread must have a message loop but we don't want the caller's + // message loop to do the work, we'll fire up a separate thread + // just for the hooks. note that low-level hooks are only available + // on windows NT SP3 and above. + g_hookEventLL = CreateEvent(NULL, TRUE, FALSE, NULL); + if (g_hookEventLL != NULL) { + g_hookThreadLL = CreateThread(NULL, 0, &getLowLevelProc, 0, + CREATE_SUSPENDED, &g_hookThreadIDLL); + if (g_hookThreadLL != NULL) { + // start the thread and wait for it to initialize + ResumeThread(g_hookThreadLL); + WaitForSingleObject(g_hookEventLL, INFINITE); + ResetEvent(g_hookEventLL); + + // the thread clears g_hookThreadIDLL if it failed + if (g_hookThreadIDLL == 0) { + CloseHandle(g_hookThreadLL); + g_hookThreadLL = NULL; + } + } + if (g_hookThreadLL == NULL) { + CloseHandle(g_hookEventLL); + g_hookEventLL = NULL; + } + } + + // install non-low-level hooks if the low-level hooks are not installed + if (g_hookThreadLL == NULL) { +#if !NO_GRAB_KEYBOARD + g_keyboard = SetWindowsHookEx(WH_KEYBOARD, + &keyboardHook, + g_hinstance, + 0); +#else + // keep compiler quiet + &keyboardHook; +#endif + g_mouse = SetWindowsHookEx(WH_MOUSE, + &mouseHook, + g_hinstance, + 0); + } + + // check for any failures. uninstall all hooks on failure. + if (g_hookThreadLL == NULL && +#if !NO_GRAB_KEYBOARD + (g_keyboard == NULL || g_mouse == NULL)) { +#else + (g_mouse == NULL)) { +#endif + if (g_keyboard != NULL) { + UnhookWindowsHookEx(g_keyboard); + g_keyboard = NULL; + } + if (g_mouse != NULL) { + UnhookWindowsHookEx(g_mouse); + g_mouse = NULL; + } + if (g_getMessage != NULL && !g_screenSaver) { + UnhookWindowsHookEx(g_getMessage); + g_getMessage = NULL; + } + g_threadID = NULL; + return kHOOK_FAILED; + } + + return (g_hookThreadLL == NULL) ? kHOOK_OKAY : kHOOK_OKAY_LL; +} + +int +uninstall(void) +{ + assert(g_hinstance != NULL); + + // uninstall hooks + if (g_hookThreadLL != NULL) { + PostThreadMessage(g_hookThreadIDLL, WM_QUIT, 0, 0); + WaitForSingleObject(g_hookThreadLL, INFINITE); + CloseHandle(g_hookEventLL); + CloseHandle(g_hookThreadLL); + g_hookEventLL = NULL; + g_hookThreadLL = NULL; + g_hookThreadIDLL = 0; + } + if (g_keyboard != NULL) { + UnhookWindowsHookEx(g_keyboard); + g_keyboard = NULL; + } + if (g_mouse != NULL) { + UnhookWindowsHookEx(g_mouse); + g_mouse = NULL; + } + if (g_getMessage != NULL && !g_screenSaver) { + UnhookWindowsHookEx(g_getMessage); + g_getMessage = NULL; + } + g_wheelSupport = kWheelNone; + + // show the cursor + restoreCursor(); + + return 1; +} + +int +installScreenSaver(void) +{ + assert(g_hinstance != NULL); + + // must be initialized + if (g_threadID == 0) { + return 0; + } + + // generate screen saver messages + g_screenSaver = true; + + // install hook unless it's already installed + if (g_getMessage == NULL) { + g_getMessage = SetWindowsHookEx(WH_GETMESSAGE, + &getMessageHook, + g_hinstance, + 0); + } + + return (g_getMessage != NULL) ? 1 : 0; +} + +int +uninstallScreenSaver(void) +{ + assert(g_hinstance != NULL); + + // uninstall hook unless the mouse wheel hook is installed + if (g_getMessage != NULL && g_wheelSupport != kWheelOld) { + UnhookWindowsHookEx(g_getMessage); + g_getMessage = NULL; + } + + // screen saver hook is no longer installed + g_screenSaver = false; + + return 1; +} + +void +setSides(UInt32 sides) +{ + g_zoneSides = sides; +} + +void +setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize) +{ + g_zoneSize = jumpZoneSize; + g_xScreen = x; + g_yScreen = y; + g_wScreen = w; + g_hScreen = h; +} + +void +setRelay(int enable) +{ + if ((enable != 0) == g_relay) { + // no change + return; + } + g_relay = (enable != 0); + if (!g_relay) { + restoreCursor(); + } +} + +} diff --git a/lib/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h new file mode 100644 index 0000000..03e72fa --- /dev/null +++ b/lib/platform/CSynergyHook.h @@ -0,0 +1,75 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSYNERGYHOOK_H +#define CSYNERGYHOOK_H + +#include "BasicTypes.h" + +#if WINDOWS_LIKE +#define WIN32_LEAN_AND_MEAN +#include +#else +#error CSynergyHook is a win32 specific file +#endif + +#if defined(SYNRGYHK_EXPORTS) +#define CSYNERGYHOOK_API __declspec(dllexport) +#else +#define CSYNERGYHOOK_API __declspec(dllimport) +#endif + +#define SYNERGY_MSG_MARK WM_APP + 0x0011 // mark id; +#define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data +#define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; +#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0014 // delta; +#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0015 // x; y +#define SYNERGY_MSG_POST_WARP WM_APP + 0x0016 // ; +#define SYNERGY_MSG_PRE_WARP WM_APP + 0x0017 // x; y +#define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0018 // activated; +#define SYNERGY_MSG_INPUT_FIRST SYNERGY_MSG_KEY +#define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP + +extern "C" { + +enum EHookResult { + kHOOK_FAILED, + kHOOK_OKAY, + kHOOK_OKAY_LL +}; + +typedef int (*InitFunc)(DWORD targetQueueThreadID); +typedef int (*CleanupFunc)(void); +typedef EHookResult (*InstallFunc)(void); +typedef int (*UninstallFunc)(void); +typedef int (*InstallScreenSaverFunc)(void); +typedef int (*UninstallScreenSaverFunc)(void); +typedef void (*SetSidesFunc)(UInt32); +typedef void (*SetZoneFunc)(SInt32, SInt32, SInt32, SInt32, SInt32); +typedef void (*SetRelayFunc)(int); + +CSYNERGYHOOK_API int init(DWORD); +CSYNERGYHOOK_API int cleanup(void); +CSYNERGYHOOK_API EHookResult install(void); +CSYNERGYHOOK_API int uninstall(void); +CSYNERGYHOOK_API int installScreenSaver(void); +CSYNERGYHOOK_API int uninstallScreenSaver(void); +CSYNERGYHOOK_API void setSides(UInt32 sides); +CSYNERGYHOOK_API void setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, + SInt32 jumpZoneSize); +CSYNERGYHOOK_API void setRelay(int enable); // relay iff enable != 0 + +} + +#endif diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp new file mode 100644 index 0000000..0606c3e --- /dev/null +++ b/lib/platform/CXWindowsClipboard.cpp @@ -0,0 +1,1492 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClipboard.h" +#include "CXWindowsClipboardTextConverter.h" +#include "CXWindowsClipboardUCS2Converter.h" +#include "CXWindowsClipboardUTF8Converter.h" +#include "CXWindowsUtil.h" +#include "CThread.h" +#include "CLog.h" +#include "CStopwatch.h" +#include "CArch.h" +#include "stdvector.h" +#include +#include + +// +// CXWindowsClipboard +// + +CXWindowsClipboard::CXWindowsClipboard(Display* display, + Window window, ClipboardID id) : + m_display(display), + m_window(window), + m_id(id), + m_open(false), + m_time(0), + m_owner(false), + m_timeOwned(0), + m_timeLost(0) +{ + // get some atoms + m_atomTargets = XInternAtom(m_display, "TARGETS", False); + m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False); + m_atomTimestamp = XInternAtom(m_display, "TIMESTAMP", False); + m_atomInteger = XInternAtom(m_display, "INTEGER", False); + m_atomAtom = XInternAtom(m_display, "ATOM", False); + m_atomAtomPair = XInternAtom(m_display, "ATOM_PAIR", False); + m_atomData = XInternAtom(m_display, "CLIP_TEMPORARY", False); + m_atomINCR = XInternAtom(m_display, "INCR", False); + m_atomMotifClipLock = XInternAtom(m_display, "_MOTIF_CLIP_LOCK", False); + m_atomMotifClipHeader = XInternAtom(m_display, "_MOTIF_CLIP_HEADER", False); + m_atomMotifClipAccess = XInternAtom(m_display, + "_MOTIF_CLIP_LOCK_ACCESS_VALID", False); + m_atomGDKSelection = XInternAtom(m_display, "GDK_SELECTION", False); + + // set selection atom based on clipboard id + switch (id) { + case kClipboardClipboard: + m_selection = XInternAtom(m_display, "CLIPBOARD", False); + break; + + case kClipboardSelection: + default: + m_selection = XA_PRIMARY; + break; + } + + // add converters, most desired first + m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, + "text/plain;charset=UTF-8")); + m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, + "UTF8_STRING")); + m_converters.push_back(new CXWindowsClipboardUCS2Converter(m_display, + "text/plain;charset=ISO-10646-UCS-2")); + m_converters.push_back(new CXWindowsClipboardUCS2Converter(m_display, + "text/unicode")); + m_converters.push_back(new CXWindowsClipboardTextConverter(m_display, + "text/plain")); + m_converters.push_back(new CXWindowsClipboardTextConverter(m_display, + "STRING")); + + // we have no data + clearCache(); +} + +CXWindowsClipboard::~CXWindowsClipboard() +{ + clearReplies(); + clearConverters(); +} + +void +CXWindowsClipboard::lost(Time time) +{ + LOG((CLOG_DEBUG "lost clipboard %d ownership at %d", m_id, time)); + if (m_owner) { + m_owner = false; + m_timeLost = time; + clearCache(); + } +} + +void +CXWindowsClipboard::addRequest(Window owner, Window requestor, + Atom target, ::Time time, Atom property) +{ + // must be for our window and we must have owned the selection + // at the given time. + bool success = false; + if (owner == m_window) { + LOG((CLOG_DEBUG1 "request for clipboard %d, target %d by 0x%08x (property=%d)", m_selection, target, requestor, property)); + if (wasOwnedAtTime(time)) { + if (target == m_atomMultiple) { + // add a multiple request. property may not be None + // according to ICCCM. + if (property != None) { + success = insertMultipleReply(requestor, time, property); + } + } + else { + addSimpleRequest(requestor, target, time, property); + + // addSimpleRequest() will have already handled failure + success = true; + } + } + else { + LOG((CLOG_DEBUG1 "failed, not owned at time %d", time)); + } + } + + if (!success) { + // send failure + LOG((CLOG_DEBUG1 "failed")); + insertReply(new CReply(requestor, target, time)); + } + + // send notifications that are pending + pushReplies(); +} + +bool +CXWindowsClipboard::addSimpleRequest(Window requestor, + Atom target, ::Time time, Atom property) +{ + // obsolete requestors may supply a None property. in + // that case we use the target as the property to store + // the conversion. + if (property == None) { + property = target; + } + + // handle targets + CString data; + Atom type = None; + int format = 0; + if (target == m_atomTargets) { + type = getTargetsData(data, &format); + } + else if (target == m_atomTimestamp) { + type = getTimestampData(data, &format); + } + else { + IXWindowsClipboardConverter* converter = getConverter(target); + if (converter != NULL) { + IClipboard::EFormat clipboardFormat = converter->getFormat(); + if (m_added[clipboardFormat]) { + try { + data = converter->fromIClipboard(m_data[clipboardFormat]); + format = converter->getDataSize(); + type = converter->getAtom(); + } + catch (...) { + // ignore -- cannot convert + } + } + } + } + + if (type != None) { + // success + LOG((CLOG_DEBUG1 "success")); + insertReply(new CReply(requestor, target, time, + property, data, type, format)); + return true; + } + else { + // failure + LOG((CLOG_DEBUG1 "failed")); + insertReply(new CReply(requestor, target, time)); + return false; + } +} + +bool +CXWindowsClipboard::processRequest(Window requestor, + ::Time /*time*/, Atom property) +{ + CReplyMap::iterator index = m_replies.find(requestor); + if (index == m_replies.end()) { + // unknown requestor window + return false; + } + LOG((CLOG_DEBUG1 "received property %d delete from 0x08%x", property, requestor)); + + // find the property in the known requests. it should be the + // first property but we'll check 'em all if we have to. + CReplyList& replies = index->second; + for (CReplyList::iterator index2 = replies.begin(); + index2 != replies.end(); ++index2) { + CReply* reply = *index2; + if (reply->m_replied && reply->m_property == property) { + // if reply is complete then remove it and start the + // next one. + pushReplies(index, replies, index2); + return true; + } + } + + return false; +} + +bool +CXWindowsClipboard::destroyRequest(Window requestor) +{ + CReplyMap::iterator index = m_replies.find(requestor); + if (index == m_replies.end()) { + // unknown requestor window + return false; + } + + // destroy all replies for this window + clearReplies(index->second); + m_replies.erase(index); + + // note -- we don't stop watching the window for events because + // we're called in response to the window being destroyed. + + return true; +} + +Window +CXWindowsClipboard::getWindow() const +{ + return m_window; +} + +Atom +CXWindowsClipboard::getSelection() const +{ + return m_selection; +} + +bool +CXWindowsClipboard::empty() +{ + assert(m_open); + + LOG((CLOG_DEBUG "empty clipboard %d", m_id)); + + // assert ownership of clipboard + XSetSelectionOwner(m_display, m_selection, m_window, m_time); + if (XGetSelectionOwner(m_display, m_selection) != m_window) { + LOG((CLOG_DEBUG "failed to grab clipboard %d", m_id)); + return false; + } + + // clear all data. since we own the data now, the cache is up + // to date. + clearCache(); + m_cached = true; + + // FIXME -- actually delete motif clipboard items? + // FIXME -- do anything to motif clipboard properties? + + // save time + m_timeOwned = m_time; + m_timeLost = 0; + + // we're the owner now + m_owner = true; + LOG((CLOG_DEBUG "grabbed clipboard %d", m_id)); + + return true; +} + +void +CXWindowsClipboard::add(EFormat format, const CString& data) +{ + assert(m_open); + assert(m_owner); + + LOG((CLOG_DEBUG "add %d bytes to clipboard %d format: %d", data.size(), m_id, format)); + + m_data[format] = data; + m_added[format] = true; + + // FIXME -- set motif clipboard item? +} + +bool +CXWindowsClipboard::open(Time time) const +{ + assert(!m_open); + + LOG((CLOG_DEBUG "open clipboard %d", m_id)); + + // assume not motif + m_motif = false; + + // lock clipboard + if (m_id == kClipboardClipboard) { + if (!motifLockClipboard()) { + return false; + } + + // check if motif owns the selection. unlock motif clipboard + // if it does not. + m_motif = motifOwnsClipboard(); + LOG((CLOG_DEBUG1 "motif does %sown clipboard", m_motif ? "" : "not ")); + if (!m_motif) { + motifUnlockClipboard(); + } + } + + // now open + m_open = true; + m_time = time; + + // be sure to flush the cache later if it's dirty + m_checkCache = true; + + return true; +} + +void +CXWindowsClipboard::close() const +{ + assert(m_open); + + LOG((CLOG_DEBUG "close clipboard %d", m_id)); + + // unlock clipboard + if (m_motif) { + motifUnlockClipboard(); + } + + m_motif = false; + m_open = false; +} + +IClipboard::Time +CXWindowsClipboard::getTime() const +{ + checkCache(); + return m_timeOwned; +} + +bool +CXWindowsClipboard::has(EFormat format) const +{ + assert(m_open); + + fillCache(); + return m_added[format]; +} + +CString +CXWindowsClipboard::get(EFormat format) const +{ + assert(m_open); + + fillCache(); + return m_data[format]; +} + +void +CXWindowsClipboard::clearConverters() +{ + for (ConverterList::iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + delete *index; + } + m_converters.clear(); +} + +IXWindowsClipboardConverter* +CXWindowsClipboard::getConverter(Atom target, bool onlyIfNotAdded) const +{ + IXWindowsClipboardConverter* converter = NULL; + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + converter = *index; + if (converter->getAtom() == target) { + break; + } + } + if (converter == NULL) { + LOG((CLOG_DEBUG1 " no converter for target %d", target)); + return NULL; + } + + // optionally skip already handled targets + if (onlyIfNotAdded) { + if (m_added[converter->getFormat()]) { + LOG((CLOG_DEBUG1 " skipping handled format %d", converter->getFormat())); + return NULL; + } + } + + return converter; +} + +void +CXWindowsClipboard::checkCache() const +{ + if (!m_checkCache) { + return; + } + m_checkCache = false; + + // get the time the clipboard ownership was taken by the current + // owner. + if (m_motif) { + m_timeOwned = motifGetTime(); + } + else { + m_timeOwned = icccmGetTime(); + } + + // if we can't get the time then use the time passed to us + if (m_timeOwned == 0) { + m_timeOwned = m_time; + } + + // if the cache is dirty then flush it + if (m_timeOwned != m_cacheTime) { + clearCache(); + } +} + +void +CXWindowsClipboard::clearCache() const +{ + const_cast(this)->doClearCache(); +} + +void +CXWindowsClipboard::doClearCache() +{ + m_checkCache = false; + m_cached = false; + for (SInt32 index = 0; index < kNumFormats; ++index) { + m_data[index] = ""; + m_added[index] = false; + } +} + +void +CXWindowsClipboard::fillCache() const +{ + // get the selection data if not already cached + checkCache(); + if (!m_cached) { + const_cast(this)->doFillCache(); + } +} + +void +CXWindowsClipboard::doFillCache() +{ + if (m_motif) { + motifFillCache(); + } + else { + icccmFillCache(); + } + m_checkCache = false; + m_cached = true; + m_cacheTime = m_timeOwned; +} + +void +CXWindowsClipboard::icccmFillCache() +{ + LOG((CLOG_DEBUG "ICCCM fill clipboard %d", m_id)); + + // see if we can get the list of available formats from the selection. + // if not then use a default list of formats. note that some clipboard + // owners are broken and report TARGETS as the type of the TARGETS data + // instead of the correct type ATOM; allow either. + const Atom atomTargets = m_atomTargets; + Atom target; + CString data; + if (!icccmGetSelection(atomTargets, &target, &data) || + (target != m_atomAtom && target != m_atomTargets)) { + LOG((CLOG_DEBUG1 "selection doesn't support TARGETS")); + data = ""; + + target = XA_STRING; + data.append(reinterpret_cast(&target), sizeof(target)); + } + + // try each converter in order (because they're in order of + // preference). + const Atom* targets = reinterpret_cast(data.data()); + const UInt32 numTargets = data.size() / sizeof(Atom); + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IXWindowsClipboardConverter* converter = *index; + + // skip already handled targets + if (m_added[converter->getFormat()]) { + continue; + } + + // see if atom is in target list + Atom target = None; + for (UInt32 i = 0; i < numTargets; ++i) { + if (converter->getAtom() == targets[i]) { + target = targets[i]; + break; + } + } + if (target == None) { + continue; + } + + // get the data + Atom actualTarget; + CString targetData; + if (!icccmGetSelection(target, &actualTarget, &targetData)) { + LOG((CLOG_DEBUG1 " no data for target %d", target)); + continue; + } + + // add to clipboard and note we've done it + IClipboard::EFormat format = converter->getFormat(); + m_data[format] = converter->toIClipboard(targetData); + m_added[format] = true; + LOG((CLOG_DEBUG " added format %d for target %d", format, target)); + } +} + +bool +CXWindowsClipboard::icccmGetSelection(Atom target, + Atom* actualTarget, CString* data) const +{ + assert(actualTarget != NULL); + assert(data != NULL); + + // request data conversion + CICCCMGetClipboard getter(m_window, m_time, m_atomData); + if (!getter.readClipboard(m_display, m_selection, + target, actualTarget, data)) { + LOG((CLOG_DEBUG1 "can't get data for selection target %d", target)); + LOGC(getter.m_error, (CLOG_WARN "ICCCM violation by clipboard owner")); + return false; + } + else if (*actualTarget == None) { + LOG((CLOG_DEBUG1 "selection conversion failed for target %d", target)); + return false; + } + return true; +} + +IClipboard::Time +CXWindowsClipboard::icccmGetTime() const +{ + Atom actualTarget; + CString data; + if (icccmGetSelection(m_atomTimestamp, &actualTarget, &data) && + actualTarget == m_atomInteger) { + Time time = *reinterpret_cast(data.data()); + LOG((CLOG_DEBUG1 "got ICCCM time %d", time)); + return time; + } + else { + // no timestamp + LOG((CLOG_DEBUG1 "can't get ICCCM time")); + return 0; + } +} + +bool +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; + } + + // try to grab the lock + // FIXME -- is this right? there's a race condition here -- + // A grabs successfully, B grabs successfully, A thinks it + // still has the grab until it gets a SelectionClear. + Time time = CXWindowsUtil::getCurrentTime(m_display, m_window); + 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; + } + + LOG((CLOG_DEBUG1 "locked motif clipboard")); + return true; +} + +void +CXWindowsClipboard::motifUnlockClipboard() const +{ + LOG((CLOG_DEBUG1 "unlocked motif clipboard")); + + // fail if we don't own the lock + Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); + if (lockOwner != m_window) { + return; + } + + // release lock + Time time = CXWindowsUtil::getCurrentTime(m_display, m_window); + XSetSelectionOwner(m_display, m_atomMotifClipLock, None, time); +} + +bool +CXWindowsClipboard::motifOwnsClipboard() const +{ + // get the current selection owner + // FIXME -- this can't be right. even if the window is destroyed + // Motif will still have a valid clipboard. how can we tell if + // some other client owns CLIPBOARD? + Window owner = XGetSelectionOwner(m_display, m_selection); + if (owner == None) { + return false; + } + + // get the Motif clipboard header property from the root window + Atom target; + SInt32 format; + CString data; + Window root = RootWindow(m_display, DefaultScreen(m_display)); + if (!CXWindowsUtil::getWindowProperty(m_display, root, + m_atomMotifClipHeader, + &data, &target, &format, False)) { + return false; + } + + // check the owner window against the current clipboard owner + const CMotifClipHeader* header = + reinterpret_cast(data.data()); + if (data.size() >= sizeof(CMotifClipHeader) && + header->m_id == kMotifClipHeader) { + if (header->m_selectionOwner == owner) { + return true; + } + } + + return false; +} + +void +CXWindowsClipboard::motifFillCache() +{ + LOG((CLOG_DEBUG "Motif fill clipboard %d", m_id)); + + // get the Motif clipboard header property from the root window + Atom target; + SInt32 format; + CString data; + Window root = RootWindow(m_display, DefaultScreen(m_display)); + if (!CXWindowsUtil::getWindowProperty(m_display, root, + m_atomMotifClipHeader, + &data, &target, &format, False)) { + return; + } + + // check that the header is okay + const CMotifClipHeader* header = + reinterpret_cast(data.data()); + if (data.size() < sizeof(CMotifClipHeader) || + header->m_id != kMotifClipHeader || + header->m_numItems < 1) { + return; + } + + // get the Motif item property from the root window + char name[18 + 20]; + sprintf(name, "_MOTIF_CLIP_ITEM_%d", header->m_item); + Atom atomItem = XInternAtom(m_display, name, False); + data = ""; + if (!CXWindowsUtil::getWindowProperty(m_display, root, + atomItem, &data, + &target, &format, False)) { + return; + } + + // check that the item is okay + const CMotifClipItem* item = + reinterpret_cast(data.data()); + if (data.size() < sizeof(CMotifClipItem) || + item->m_id != kMotifClipItem || + item->m_numFormats - item->m_numDeletedFormats < 1) { + return; + } + + // format list is after static item structure elements + const SInt32 numFormats = item->m_numFormats - item->m_numDeletedFormats; + const SInt32* formats = reinterpret_cast(item->m_size + + reinterpret_cast(data.data())); + + // get the available formats + typedef std::map CMotifFormatMap; + CMotifFormatMap motifFormats; + for (SInt32 i = 0; i < numFormats; ++i) { + // get Motif format property from the root window + sprintf(name, "_MOTIF_CLIP_ITEM_%d", formats[i]); + Atom atomFormat = XInternAtom(m_display, name, False); + CString data; + if (!CXWindowsUtil::getWindowProperty(m_display, root, + atomFormat, &data, + &target, &format, False)) { + continue; + } + + // check that the format is okay + const CMotifClipFormat* motifFormat = + reinterpret_cast(data.data()); + if (data.size() < sizeof(CMotifClipFormat) || + motifFormat->m_id != kMotifClipFormat || + motifFormat->m_length < 0 || + motifFormat->m_type == None || + motifFormat->m_deleted != 0) { + continue; + } + + // save it + motifFormats.insert(std::make_pair(motifFormat->m_type, data)); + } + const UInt32 numMotifFormats = motifFormats.size(); + + // try each converter in order (because they're in order of + // preference). + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IXWindowsClipboardConverter* converter = *index; + + // skip already handled targets + if (m_added[converter->getFormat()]) { + continue; + } + + // see if atom is in target list + CMotifFormatMap::const_iterator index2 = + motifFormats.find(converter->getAtom()); + if (index2 == motifFormats.end()) { + continue; + } + + // get format + const CMotifClipFormat* motifFormat = + reinterpret_cast( + index2->second.data()); + const Atom target = motifFormat->m_type; + + // get the data (finally) + Atom actualTarget; + CString targetData; + if (!motifGetSelection(motifFormat, &actualTarget, &targetData)) { + LOG((CLOG_DEBUG1 " no data for target %d", target)); + continue; + } + + // add to clipboard and note we've done it + IClipboard::EFormat format = converter->getFormat(); + m_data[format] = converter->toIClipboard(targetData); + m_added[format] = true; + LOG((CLOG_DEBUG " added format %d for target %d", format, target)); + } +} + +bool +CXWindowsClipboard::motifGetSelection(const CMotifClipFormat* format, + Atom* actualTarget, CString* data) const +{ + // if the current clipboard owner and the owner indicated by the + // motif clip header are the same then transfer via a property on + // the root window, otherwise transfer as a normal ICCCM client. + if (!motifOwnsClipboard()) { + return icccmGetSelection(format->m_type, actualTarget, data); + } + + // use motif way + // FIXME -- this isn't right. it'll only work if the data is + // already stored on the root window and only if it fits in a + // property. motif has some scheme for transferring part by + // part that i don't know. + char name[18 + 20]; + sprintf(name, "_MOTIF_CLIP_ITEM_%d", format->m_data); + Atom target = XInternAtom(m_display, name, False); + Window root = RootWindow(m_display, DefaultScreen(m_display)); + return CXWindowsUtil::getWindowProperty(m_display, root, + target, data, + actualTarget, NULL, False); +} + +IClipboard::Time +CXWindowsClipboard::motifGetTime() const +{ + return icccmGetTime(); +} + +bool +CXWindowsClipboard::insertMultipleReply(Window requestor, + ::Time time, Atom property) +{ + // get the requested targets + Atom target; + SInt32 format; + CString data; + if (!CXWindowsUtil::getWindowProperty(m_display, requestor, + property, &data, &target, &format, False)) { + // can't get the requested targets + return false; + } + + // fail if the requested targets isn't of the correct form + if (format != 32 || + target != m_atomAtomPair) { + return false; + } + + // data is a list of atom pairs: target, property + const Atom* targets = reinterpret_cast(data.data()); + const UInt32 numTargets = data.size() / sizeof(Atom); + + // add replies for each target + bool changed = false; + for (UInt32 i = 0; i < numTargets; i += 2) { + const Atom target = targets[i + 0]; + const Atom property = targets[i + 1]; + if (!addSimpleRequest(requestor, target, time, property)) { + // note that we can't perform the requested conversion + static const Atom none = None; + data.replace(i * sizeof(Atom), sizeof(Atom), + reinterpret_cast(&none), + sizeof(Atom)); + changed = true; + } + } + + // update the targets property if we changed it + if (changed) { + CXWindowsUtil::setWindowProperty(m_display, requestor, + property, data.data(), data.size(), + target, format); + } + + // add reply for MULTIPLE request + insertReply(new CReply(requestor, m_atomMultiple, + time, property, CString(), None, 32)); + + return true; +} + +void +CXWindowsClipboard::insertReply(CReply* reply) +{ + assert(reply != NULL); + + // note -- we must respond to requests in order if requestor,target,time + // are the same, otherwise we can use whatever order we like with one + // exception: each reply in a MULTIPLE reply must be handled in order + // as well. those replies will almost certainly not share targets so + // we can't simply use requestor,target,time as map index. + // + // instead we'll use just the requestor. that's more restrictive than + // necessary but we're guaranteed to do things in the right order. + // note that we could also include the time in the map index and still + // ensure the right order. but since that'll just make it harder to + // find the right reply when handling property notify events we stick + // to just the requestor. + + const bool newWindow = (m_replies.count(reply->m_requestor) == 0); + m_replies[reply->m_requestor].push_back(reply); + + // adjust requestor's event mask if we haven't done so already. we + // want events in case the window is destroyed or any of its + // properties change. + if (newWindow) { + // note errors while we adjust event masks + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + + // get and save the current event mask + XWindowAttributes attr; + XGetWindowAttributes(m_display, reply->m_requestor, &attr); + m_eventMasks[reply->m_requestor] = attr.your_event_mask; + + // add the events we want + XSelectInput(m_display, reply->m_requestor, attr.your_event_mask | + StructureNotifyMask | PropertyChangeMask); + + // if we failed then the window has already been destroyed + if (error) { + m_replies.erase(reply->m_requestor); + delete reply; + } + } +} + +void +CXWindowsClipboard::pushReplies() +{ + // send the first reply for each window if that reply hasn't + // been sent yet. + for (CReplyMap::iterator index = m_replies.begin(); + index != m_replies.end(); ++index) { + assert(!index->second.empty()); + if (!index->second.front()->m_replied) { + pushReplies(index, index->second, index->second.begin()); + } + } +} + +void +CXWindowsClipboard::pushReplies(CReplyMap::iterator mapIndex, + CReplyList& replies, CReplyList::iterator index) +{ + CReply* reply = *index; + while (sendReply(reply)) { + // reply is complete. discard it and send the next reply, + // if any. + index = replies.erase(index); + delete reply; + if (index == replies.end()) { + break; + } + reply = *index; + } + + // if there are no more replies in the list then remove the list + // and stop watching the requestor for events. + if (replies.empty()) { + CXWindowsUtil::CErrorLock lock(m_display); + Window requestor = mapIndex->first; + XSelectInput(m_display, requestor, m_eventMasks[requestor]); + m_replies.erase(mapIndex); + m_eventMasks.erase(requestor); + } +} + +bool +CXWindowsClipboard::sendReply(CReply* reply) +{ + assert(reply != NULL); + + // bail out immediately if reply is done + if (reply->m_done) { + LOG((CLOG_DEBUG1 "clipboard: finished reply to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + return true; + } + + // start in failed state if property is None + bool failed = (reply->m_property == None); + if (!failed) { + LOG((CLOG_DEBUG1 "clipboard: setting property on 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + + // send using INCR if already sending incrementally or if reply + // is too large, otherwise just send it. + const UInt32 maxRequestSize = 3 * XMaxRequestSize(m_display); + const bool useINCR = (reply->m_data.size() > maxRequestSize); + + // send INCR reply if incremental and we haven't replied yet + if (useINCR && !reply->m_replied) { + UInt32 size = reply->m_data.size(); + if (!CXWindowsUtil::setWindowProperty(m_display, + reply->m_requestor, reply->m_property, + &size, 4, m_atomINCR, 32)) { + failed = true; + } + } + + // send more INCR reply or entire non-incremental reply + else { + // how much more data should we send? + UInt32 size = reply->m_data.size() - reply->m_ptr; + if (size > maxRequestSize) + size = maxRequestSize; + + // send it + if (!CXWindowsUtil::setWindowProperty(m_display, + reply->m_requestor, reply->m_property, + reply->m_data.data() + reply->m_ptr, + size, + reply->m_type, reply->m_format)) { + failed = true; + } + else { + reply->m_ptr += size; + + // we've finished the reply if we just sent the zero + // size incremental chunk or if we're not incremental. + reply->m_done = (size == 0 || !useINCR); + } + } + } + + // if we've failed then delete the property and say we're done. + // if we haven't replied yet then we can send a failure notify, + // otherwise we've failed in the middle of an incremental + // transfer; i don't know how to cancel that so i'll just send + // the final zero-length property. + // FIXME -- how do you gracefully cancel an incremental transfer? + if (failed) { + LOG((CLOG_DEBUG1 "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + reply->m_done = true; + if (reply->m_property != None) { + CXWindowsUtil::CErrorLock lock(m_display); + XDeleteProperty(m_display, reply->m_requestor, reply->m_property); + } + + if (!reply->m_replied) { + sendNotify(reply->m_requestor, m_selection, + reply->m_target, None, + reply->m_time); + + // don't wait for any reply (because we're not expecting one) + return true; + } + else { + static const char dummy = 0; + CXWindowsUtil::setWindowProperty(m_display, + reply->m_requestor, reply->m_property, + &dummy, + 0, + reply->m_type, reply->m_format); + + // wait for delete notify + return false; + } + } + + // send notification if we haven't yet + if (!reply->m_replied) { + LOG((CLOG_DEBUG1 "clipboard: sending notify to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + reply->m_replied = true; + + // dump every property on the requestor window to the debug2 + // log. we've seen what appears to be a bug in lesstif and + // knowing the properties may help design a workaround, if + // it becomes necessary. + if (CLOG->getFilter() >= CLog::kDEBUG2) { + CXWindowsUtil::CErrorLock lock(m_display); + int n; + Atom* props = XListProperties(m_display, reply->m_requestor, &n); + LOG((CLOG_DEBUG2 "properties of 0x%08x:", reply->m_requestor)); + for (int i = 0; i < n; ++i) { + Atom target; + CString data; + char* name = XGetAtomName(m_display, props[i]); + if (!CXWindowsUtil::getWindowProperty(m_display, + reply->m_requestor, + props[i], &data, &target, NULL, False)) { + LOG((CLOG_DEBUG2 " %s: ", name)); + } + else { + // if there are any non-ascii characters in string + // then print the binary data. + static const char* hex = "0123456789abcdef"; + for (CString::size_type j = 0; j < data.size(); ++j) { + if (data[j] < 32 || data[j] > 126) { + CString tmp; + tmp.reserve(data.size() * 3); + for (j = 0; j < data.size(); ++j) { + unsigned char v = (unsigned char)data[j]; + tmp += hex[v >> 16]; + tmp += hex[v & 15]; + tmp += ' '; + } + data = tmp; + break; + } + } + char* type = XGetAtomName(m_display, target); + LOG((CLOG_DEBUG2 " %s (%s): %s", name, type, data.c_str())); + if (type != NULL) { + XFree(type); + } + } + if (name != NULL) { + XFree(name); + } + } + if (props != NULL) { + XFree(props); + } + } + + sendNotify(reply->m_requestor, m_selection, + reply->m_target, reply->m_property, + reply->m_time); + } + + // wait for delete notify + return false; +} + +void +CXWindowsClipboard::clearReplies() +{ + for (CReplyMap::iterator index = m_replies.begin(); + index != m_replies.end(); ++index) { + clearReplies(index->second); + } + m_replies.clear(); + m_eventMasks.clear(); +} + +void +CXWindowsClipboard::clearReplies(CReplyList& replies) +{ + for (CReplyList::iterator index = replies.begin(); + index != replies.end(); ++index) { + delete *index; + } + replies.clear(); +} + +void +CXWindowsClipboard::sendNotify(Window requestor, + Atom selection, Atom target, Atom property, Time time) +{ + XEvent event; + event.xselection.type = SelectionNotify; + event.xselection.display = m_display; + event.xselection.requestor = requestor; + event.xselection.selection = selection; + event.xselection.target = target; + event.xselection.property = property; + event.xselection.time = time; + CXWindowsUtil::CErrorLock lock(m_display); + XSendEvent(m_display, requestor, False, 0, &event); +} + +bool +CXWindowsClipboard::wasOwnedAtTime(::Time time) const +{ + // not owned if we've never owned the selection + checkCache(); + if (m_timeOwned == 0) { + return false; + } + + // if time is CurrentTime then return true if we still own the + // selection and false if we do not. else if we still own the + // selection then get the current time, otherwise use + // m_timeLost as the end time. + Time lost = m_timeLost; + if (m_timeLost == 0) { + if (time == CurrentTime) { + return true; + } + else { + lost = CXWindowsUtil::getCurrentTime(m_display, m_window); + } + } + else { + if (time == CurrentTime) { + return false; + } + } + + // compare time to range + Time duration = lost - m_timeOwned; + Time when = time - m_timeOwned; + return (/*when >= 0 &&*/ when < duration); +} + +Atom +CXWindowsClipboard::getTargetsData(CString& data, int* format) const +{ + assert(format != NULL); + + // add standard targets + Atom atom; + atom = m_atomTargets; + data.append(reinterpret_cast(&atom), sizeof(Atom)); + atom = m_atomMultiple; + data.append(reinterpret_cast(&atom), sizeof(Atom)); + atom = m_atomTimestamp; + data.append(reinterpret_cast(&atom), sizeof(Atom)); + + // add targets we can convert to + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IXWindowsClipboardConverter* converter = *index; + + // skip formats we don't have + if (m_added[converter->getFormat()]) { + atom = converter->getAtom(); + data.append(reinterpret_cast(&atom), sizeof(Atom)); + } + } + + *format = 32; + return m_atomAtom; +} + +Atom +CXWindowsClipboard::getTimestampData(CString& data, int* format) const +{ + assert(format != NULL); + + assert(sizeof(m_timeOwned) == 4); + checkCache(); + data.append(reinterpret_cast(&m_timeOwned), 4); + *format = 32; + return m_atomInteger; +} + + +// +// CXWindowsClipboard::CICCCMGetClipboard +// + +CXWindowsClipboard::CICCCMGetClipboard::CICCCMGetClipboard( + Window requestor, Time time, Atom property) : + m_requestor(requestor), + m_time(time), + m_property(property), + m_incr(false), + m_failed(false), + m_done(false), + m_reading(false), + m_data(NULL), + m_actualTarget(NULL), + m_error(false) +{ + // do nothing +} + +CXWindowsClipboard::CICCCMGetClipboard::~CICCCMGetClipboard() +{ + // do nothing +} + +bool +CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, + Atom selection, Atom target, Atom* actualTarget, CString* data) +{ + assert(actualTarget != NULL); + assert(data != NULL); + + LOG((CLOG_DEBUG1 "request selection=%d, target=%d, window=%x", selection, target, m_requestor)); + + // save output pointers + m_actualTarget = actualTarget; + m_data = data; + + // assume failure + *m_actualTarget = None; + *m_data = ""; + + // delete target property + XDeleteProperty(display, m_requestor, m_property); + + // select window for property changes + XWindowAttributes attr; + XGetWindowAttributes(display, m_requestor, &attr); + XSelectInput(display, m_requestor, + attr.your_event_mask | PropertyChangeMask); + + // request data conversion + XConvertSelection(display, selection, target, + m_property, m_requestor, m_time); + + // synchronize with server before we start following timeout countdown + XSync(display, False); + + // Xlib inexplicably omits the ability to wait for an event with + // a timeout. (it's inexplicable because there's no portable way + // to do it.) we'll poll until we have what we're looking for or + // a timeout expires. we use a timeout so we don't get locked up + // by badly behaved selection owners. + XEvent xevent; + std::vector events; + CStopwatch timeout(true); + static const double s_timeout = 0.25; // FIXME -- is this too short? + bool noWait = false; + while (!m_done && !m_failed) { + // fail if timeout has expired + if (timeout.getTime() >= s_timeout) { + m_failed = true; + break; + } + + // process events if any otherwise sleep + if (noWait || XPending(display) > 0) { + while (!m_done && !m_failed && (noWait || XPending(display) > 0)) { + XNextEvent(display, &xevent); + if (!processEvent(display, &xevent)) { + // not processed so save it + events.push_back(xevent); + } + else { + // reset timer since we've made some progress + timeout.reset(); + + // don't sleep anymore, just block waiting for events. + // we're assuming here that the clipboard owner will + // complete the protocol correctly. if we continue to + // sleep we'll get very bad performance. + noWait = true; + } + } + } + else { + ARCH->sleep(0.01); + } + } + + // put unprocessed events back + for (UInt32 i = events.size(); i > 0; --i) { + XPutBackEvent(display, &events[i - 1]); + } + + // restore mask + XSelectInput(display, m_requestor, attr.your_event_mask); + + // return success or failure + LOG((CLOG_DEBUG1 "request %s", m_failed ? "failed" : "succeeded")); + return !m_failed; +} + +bool +CXWindowsClipboard::CICCCMGetClipboard::processEvent( + Display* display, XEvent* xevent) +{ + // process event + switch (xevent->type) { + case DestroyNotify: + if (xevent->xdestroywindow.window == m_requestor) { + m_failed = true; + return true; + } + + // not interested + return false; + + case SelectionNotify: + if (xevent->xselection.requestor == m_requestor) { + // done if we can't convert + if (xevent->xselection.property == None) { + m_done = true; + return true; + } + + // proceed if conversion successful + else if (xevent->xselection.property == m_property) { + m_reading = true; + break; + } + } + + // otherwise not interested + return false; + + case PropertyNotify: + // proceed if conversion successful and we're receiving more data + if (xevent->xproperty.window == m_requestor && + xevent->xproperty.atom == m_property && + xevent->xproperty.state == PropertyNewValue) { + if (!m_reading) { + // we haven't gotten the SelectionNotify yet + return true; + } + break; + } + + // otherwise not interested + return false; + + default: + // not interested + return false; + } + + // get the data from the property + Atom target; + const CString::size_type oldSize = m_data->size(); + if (!CXWindowsUtil::getWindowProperty(display, m_requestor, + m_property, m_data, &target, NULL, True)) { + // unable to read property + m_failed = true; + return true; + } + + // note if incremental. if we're already incremental then the + // selection owner is busted. if the INCR property has no size + // then the selection owner is busted. + if (target == XInternAtom(display, "INCR", False)) { + if (m_incr) { + m_failed = true; + m_error = true; + } + else if (m_data->size() == oldSize) { + m_failed = true; + m_error = true; + } + else { + m_incr = true; + + // discard INCR data + *m_data = ""; + } + } + + // handle incremental chunks + else if (m_incr) { + // if first incremental chunk then save target + if (oldSize == 0) { + LOG((CLOG_DEBUG1 " INCR first chunk, target %d", target)); + *m_actualTarget = target; + } + + // secondary chunks must have the same target + else { + if (target != *m_actualTarget) { + LOG((CLOG_WARN " INCR target mismatch")); + m_failed = true; + m_error = true; + } + } + + // note if this is the final chunk + if (m_data->size() == oldSize) { + LOG((CLOG_DEBUG1 " INCR final chunk: %d bytes total", m_data->size())); + m_done = true; + } + } + + // not incremental; save the target. + else { + LOG((CLOG_DEBUG1 " target %d", target)); + *m_actualTarget = target; + m_done = true; + } + + // this event has been processed + LOGC(!m_incr, (CLOG_DEBUG1 " got data, %d bytes", m_data->size())); + return true; +} + + +// +// CXWindowsClipboard::CReply +// + +CXWindowsClipboard::CReply::CReply(Window requestor, Atom target, ::Time time) : + m_requestor(requestor), + m_target(target), + m_time(time), + m_property(None), + m_replied(false), + m_done(false), + m_data(), + m_type(None), + m_format(32), + m_ptr(0) +{ + // do nothing +} + +CXWindowsClipboard::CReply::CReply(Window requestor, Atom target, ::Time time, + Atom property, const CString& data, Atom type, int format) : + m_requestor(requestor), + m_target(target), + m_time(time), + m_property(property), + m_replied(false), + m_done(false), + m_data(data), + m_type(type), + m_format(format), + m_ptr(0) +{ + // do nothing +} diff --git a/lib/platform/CXWindowsClipboard.h b/lib/platform/CXWindowsClipboard.h new file mode 100644 index 0000000..25c221b --- /dev/null +++ b/lib/platform/CXWindowsClipboard.h @@ -0,0 +1,372 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIPBOARD_H +#define CXWINDOWSCLIPBOARD_H + +#include "IClipboard.h" +#include "ClipboardTypes.h" +#include "stdmap.h" +#include "stdlist.h" +#include "stdvector.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +class IXWindowsClipboardConverter; + +//! X11 clipboard implementation +class CXWindowsClipboard : public IClipboard { +public: + /*! + Use \c window as the window that owns or interacts with the + clipboard identified by \c id. + */ + CXWindowsClipboard(Display*, Window window, ClipboardID id); + virtual ~CXWindowsClipboard(); + + //! Notify clipboard was lost + /*! + Tells clipboard it lost ownership at the given time. + */ + void lost(Time); + + //! Add clipboard request + /*! + Adds a selection request to the request list. If the given + owner window isn't this clipboard's window then this simply + sends a failure event to the requestor. + */ + void addRequest(Window owner, + Window requestor, Atom target, + ::Time time, Atom property); + + //! Process clipboard request + /*! + Continues processing a selection request. Returns true if the + request was handled, false if the request was unknown. + */ + bool processRequest(Window requestor, + ::Time time, Atom property); + + //! Cancel clipboard request + /*! + Terminate a selection request. Returns true iff the request + was known and handled. + */ + bool destroyRequest(Window requestor); + + //! Get window + /*! + Returns the clipboard's window (passed the c'tor). + */ + Window getWindow() const; + + //! Get selection atom + /*! + Returns the selection atom that identifies the clipboard to X11 + (e.g. XA_PRIMARY). + */ + Atom getSelection() const; + + // IClipboard overrides + virtual bool empty(); + virtual void add(EFormat, const CString& data); + virtual bool open(Time) const; + virtual void close() const; + virtual Time getTime() const; + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; + +private: + // remove all converters from our list + void clearConverters(); + + // get the converter for a clipboard format. returns NULL if no + // suitable converter. iff onlyIfNotAdded is true then also + // return NULL if a suitable converter was found but we already + // have data of the converter's clipboard format. + IXWindowsClipboardConverter* + getConverter(Atom target, + bool onlyIfNotAdded = false) const; + + // convert target atom to clipboard format + EFormat getFormat(Atom target) const; + + // add a non-MULTIPLE request. does not verify that the selection + // was owned at the given time. returns true if the conversion + // could be performed, false otherwise. in either case, the + // reply is inserted. + bool addSimpleRequest( + Window requestor, Atom target, + ::Time time, Atom property); + + // if not already checked then see if the cache is stale and, if so, + // clear it. this has the side effect of updating m_timeOwned. + void checkCache() const; + + // clear the cache, resetting the cached flag and the added flag for + // each format. + void clearCache() const; + void doClearCache(); + + // cache all formats of the selection + void fillCache() const; + void doFillCache(); + + // + // helper classes + // + + // read an ICCCM conforming selection + class CICCCMGetClipboard { + public: + CICCCMGetClipboard(Window requestor, Time time, Atom property); + ~CICCCMGetClipboard(); + + // convert the given selection to the given type. returns + // true iff the conversion was successful or the conversion + // cannot be performed (in which case *actualTarget == None). + bool readClipboard(Display* display, + Atom selection, Atom target, + Atom* actualTarget, CString* data); + + private: + bool processEvent(Display* display, XEvent* event); + + private: + Window m_requestor; + Time m_time; + Atom m_property; + bool m_incr; + bool m_failed; + bool m_done; + + // true iff we've received the selection notify + bool m_reading; + + // the converted selection data + CString* m_data; + + // the actual type of the data. if this is None then the + // selection owner cannot convert to the requested type. + Atom* m_actualTarget; + + public: + // true iff the selection owner didn't follow ICCCM conventions + bool m_error; + }; + + // Motif structure IDs + enum { kMotifClipFormat = 1, kMotifClipItem, kMotifClipHeader }; + + // _MOTIF_CLIP_HEADER structure + class CMotifClipHeader { + public: + SInt32 m_id; // kMotifClipHeader + SInt32 m_pad1[3]; + SInt32 m_item; + SInt32 m_pad2[4]; + SInt32 m_numItems; + SInt32 m_pad3[3]; + Window m_selectionOwner; + SInt32 m_pad4[2]; + }; + + // Motif clip item structure + class CMotifClipItem { + public: + SInt32 m_id; // kMotifClipItem + SInt32 m_pad1[5]; + SInt32 m_size; + SInt32 m_numFormats; + SInt32 m_numDeletedFormats; + SInt32 m_pad2[6]; + }; + + // Motif clip format structure + class CMotifClipFormat { + public: + SInt32 m_id; // kMotifClipFormat + SInt32 m_pad1[6]; + SInt32 m_length; + SInt32 m_data; + Atom m_type; + SInt32 m_pad2[1]; + int m_deleted; + SInt32 m_pad3[4]; + }; + + // stores data needed to respond to a selection request + class CReply { + public: + CReply(Window, Atom target, ::Time); + CReply(Window, Atom target, ::Time, Atom property, + const CString& data, Atom type, int format); + + public: + // information about the request + Window m_requestor; + Atom m_target; + ::Time m_time; + Atom m_property; + + // true iff we've sent the notification for this reply + bool m_replied; + + // true iff the reply has sent its last message + bool m_done; + + // the data to send and its type and format + CString m_data; + Atom m_type; + int m_format; + + // index of next byte in m_data to send + UInt32 m_ptr; + }; + typedef std::list CReplyList; + typedef std::map CReplyMap; + typedef std::map CReplyEventMask; + + // ICCCM interoperability methods + void icccmFillCache(); + bool icccmGetSelection(Atom target, + Atom* actualTarget, CString* data) const; + Time icccmGetTime() const; + + // motif interoperability methods + bool motifLockClipboard() const; + void motifUnlockClipboard() const; + bool motifOwnsClipboard() const; + void motifFillCache(); + bool motifGetSelection(const CMotifClipFormat*, + Atom* actualTarget, CString* data) const; + Time motifGetTime() const; + + // reply methods + bool insertMultipleReply(Window, ::Time, Atom); + void insertReply(CReply*); + void pushReplies(); + void pushReplies(CReplyMap::iterator, + CReplyList&, CReplyList::iterator); + bool sendReply(CReply*); + void clearReplies(); + void clearReplies(CReplyList&); + void sendNotify(Window requestor, Atom selection, + Atom target, Atom property, Time time); + bool wasOwnedAtTime(::Time) const; + + // data conversion methods + Atom getTargetsData(CString&, int* format) const; + Atom getTimestampData(CString&, int* format) const; + +private: + typedef std::vector ConverterList; + + Display* m_display; + Window m_window; + ClipboardID m_id; + Atom m_selection; + mutable bool m_open; + mutable Time m_time; + bool m_owner; + mutable Time m_timeOwned; + Time m_timeLost; + + // true iff open and clipboard owned by a motif app + mutable bool m_motif; + + // the added/cached clipboard data + mutable bool m_checkCache; + bool m_cached; + Time m_cacheTime; + bool m_added[kNumFormats]; + CString m_data[kNumFormats]; + + // conversion request replies + CReplyMap m_replies; + CReplyEventMask m_eventMasks; + + // clipboard format converters + ConverterList m_converters; + + // atoms we'll need + Atom m_atomTargets; + Atom m_atomMultiple; + Atom m_atomTimestamp; + Atom m_atomInteger; + Atom m_atomAtom; + Atom m_atomAtomPair; + Atom m_atomData; + Atom m_atomINCR; + Atom m_atomMotifClipLock; + Atom m_atomMotifClipHeader; + Atom m_atomMotifClipAccess; + Atom m_atomGDKSelection; +}; + +//! Clipboard format converter interface +/*! +This interface defines the methods common to all X11 clipboard format +converters. +*/ +class IXWindowsClipboardConverter : public IInterface { +public: + //! @name accessors + //@{ + + //! Get clipboard format + /*! + Return the clipboard format this object converts from/to. + */ + virtual IClipboard::EFormat + getFormat() const = 0; + + //! Get X11 format atom + /*! + Return the atom representing the X selection format that + this object converts from/to. + */ + virtual Atom getAtom() const = 0; + + //! Get X11 property datum size + /*! + Return the size (in bits) of data elements returned by + toIClipboard(). + */ + virtual int getDataSize() const = 0; + + //! Convert from IClipboard format + /*! + Convert from the IClipboard format to the X selection format. + The input data must be in the IClipboard format returned by + getFormat(). The return data will be in the X selection + format returned by getAtom(). + */ + virtual CString fromIClipboard(const CString&) const = 0; + + //! Convert to IClipboard format + /*! + Convert from the X selection format to the IClipboard format + (i.e., the reverse of fromIClipboard()). + */ + virtual CString toIClipboard(const CString&) const = 0; + + //@} +}; + +#endif diff --git a/lib/platform/CXWindowsClipboardTextConverter.cpp b/lib/platform/CXWindowsClipboardTextConverter.cpp new file mode 100644 index 0000000..bd1a520 --- /dev/null +++ b/lib/platform/CXWindowsClipboardTextConverter.cpp @@ -0,0 +1,74 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClipboardTextConverter.h" +#include "CUnicode.h" + +// +// CXWindowsClipboardTextConverter +// + +CXWindowsClipboardTextConverter::CXWindowsClipboardTextConverter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardTextConverter::~CXWindowsClipboardTextConverter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardTextConverter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardTextConverter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardTextConverter::getDataSize() const +{ + return 8; +} + +CString +CXWindowsClipboardTextConverter::fromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToText(data); +} + +CString +CXWindowsClipboardTextConverter::toIClipboard(const CString& data) const +{ + // convert to UTF-8 + bool errors; + CString utf8 = CUnicode::textToUTF8(data, &errors); + + // if there were decoding errors then, to support old applications + // that don't understand UTF-8 but can report the exact binary + // UTF-8 representation, see if the data appears to be UTF-8. if + // so then use it as is. + if (errors && CUnicode::isUTF8(data)) { + return data; + } + + return utf8; +} diff --git a/lib/platform/CXWindowsClipboardTextConverter.h b/lib/platform/CXWindowsClipboardTextConverter.h new file mode 100644 index 0000000..3840b7d --- /dev/null +++ b/lib/platform/CXWindowsClipboardTextConverter.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIPBOARDTEXTCONVERTER_H +#define CXWINDOWSCLIPBOARDTEXTCONVERTER_H + +#include "CXWindowsClipboard.h" + +//! Convert to/from locale text encoding +class CXWindowsClipboardTextConverter : public IXWindowsClipboardConverter { +public: + /*! + \c name is converted to an atom and that is reported by getAtom(). + */ + CXWindowsClipboardTextConverter(Display* display, const char* name); + virtual ~CXWindowsClipboardTextConverter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/lib/platform/CXWindowsClipboardUCS2Converter.cpp b/lib/platform/CXWindowsClipboardUCS2Converter.cpp new file mode 100644 index 0000000..86b8d13 --- /dev/null +++ b/lib/platform/CXWindowsClipboardUCS2Converter.cpp @@ -0,0 +1,62 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClipboardUCS2Converter.h" +#include "CUnicode.h" + +// +// CXWindowsClipboardUCS2Converter +// + +CXWindowsClipboardUCS2Converter::CXWindowsClipboardUCS2Converter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardUCS2Converter::~CXWindowsClipboardUCS2Converter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardUCS2Converter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardUCS2Converter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardUCS2Converter::getDataSize() const +{ + return 16; +} + +CString +CXWindowsClipboardUCS2Converter::fromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToUCS2(data); +} + +CString +CXWindowsClipboardUCS2Converter::toIClipboard(const CString& data) const +{ + return CUnicode::UCS2ToUTF8(data); +} diff --git a/lib/platform/CXWindowsClipboardUCS2Converter.h b/lib/platform/CXWindowsClipboardUCS2Converter.h new file mode 100644 index 0000000..e848e30 --- /dev/null +++ b/lib/platform/CXWindowsClipboardUCS2Converter.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIPBOARDUCS2CONVERTER_H +#define CXWINDOWSCLIPBOARDUCS2CONVERTER_H + +#include "CXWindowsClipboard.h" + +//! Convert to/from UCS-2 encoding +class CXWindowsClipboardUCS2Converter : public IXWindowsClipboardConverter { +public: + /*! + \c name is converted to an atom and that is reported by getAtom(). + */ + CXWindowsClipboardUCS2Converter(Display* display, const char* name); + virtual ~CXWindowsClipboardUCS2Converter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/lib/platform/CXWindowsClipboardUTF8Converter.cpp b/lib/platform/CXWindowsClipboardUTF8Converter.cpp new file mode 100644 index 0000000..7edc850 --- /dev/null +++ b/lib/platform/CXWindowsClipboardUTF8Converter.cpp @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClipboardUTF8Converter.h" + +// +// CXWindowsClipboardUTF8Converter +// + +CXWindowsClipboardUTF8Converter::CXWindowsClipboardUTF8Converter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardUTF8Converter::~CXWindowsClipboardUTF8Converter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardUTF8Converter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardUTF8Converter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardUTF8Converter::getDataSize() const +{ + return 8; +} + +CString +CXWindowsClipboardUTF8Converter::fromIClipboard(const CString& data) const +{ + return data; +} + +CString +CXWindowsClipboardUTF8Converter::toIClipboard(const CString& data) const +{ + return data; +} diff --git a/lib/platform/CXWindowsClipboardUTF8Converter.h b/lib/platform/CXWindowsClipboardUTF8Converter.h new file mode 100644 index 0000000..5ac8b15 --- /dev/null +++ b/lib/platform/CXWindowsClipboardUTF8Converter.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIPBOARDUTF8CONVERTER_H +#define CXWINDOWSCLIPBOARDUTF8CONVERTER_H + +#include "CXWindowsClipboard.h" + +//! Convert to/from UTF-8 encoding +class CXWindowsClipboardUTF8Converter : public IXWindowsClipboardConverter { +public: + /*! + \c name is converted to an atom and that is reported by getAtom(). + */ + CXWindowsClipboardUTF8Converter(Display* display, const char* name); + virtual ~CXWindowsClipboardUTF8Converter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp new file mode 100644 index 0000000..360bea8 --- /dev/null +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -0,0 +1,1044 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsPrimaryScreen.h" +#include "CXWindowsScreen.h" +#include "CXWindowsUtil.h" +#include "IPrimaryScreenReceiver.h" +#include "XScreen.h" +#include "CThread.h" +#include "CLog.h" +#include "CStopwatch.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +# include +# define XK_MISCELLANY +# define XK_XKB_KEYS +# include +#endif +#include "CArch.h" + +// +// CXWindowsPrimaryScreen +// + +CXWindowsPrimaryScreen::CXWindowsPrimaryScreen( + IScreenReceiver* receiver, + IPrimaryScreenReceiver* primaryReceiver) : + CPrimaryScreen(receiver), + m_receiver(primaryReceiver), + m_window(None), + m_im(NULL), + m_ic(NULL), + m_lastKeycode(0) +{ + m_screen = new CXWindowsScreen(receiver, this); +} + +CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() +{ + assert(m_window == None); + delete m_screen; +} + +void +CXWindowsPrimaryScreen::reconfigure(UInt32) +{ + // do nothing +} + +void +CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + CDisplayLock display(m_screen); + + // warp mouse + warpCursorNoFlush(display, x, y); + + // remove all input events before and including warp + XEvent event; + while (XCheckMaskEvent(display, PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask, + &event)) { + // do nothing + } + + // save position as last position + m_x = x; + m_y = y; +} + +void +CXWindowsPrimaryScreen::resetOptions() +{ + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; +} + +void +CXWindowsPrimaryScreen::setOptions(const COptionsList& options) +{ + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionHalfDuplexCapsLock) { + m_capsLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); + } + else if (options[i] == kOptionHalfDuplexNumLock) { + m_numLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); + } + } +} + +UInt32 +CXWindowsPrimaryScreen::addOneShotTimer(double timeout) +{ + return m_screen->addOneShotTimer(timeout); +} + +KeyModifierMask +CXWindowsPrimaryScreen::getToggleMask() const +{ + CDisplayLock display(m_screen); + + // query the pointer to get the keyboard state + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (!XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + return 0; + } + + // convert to KeyModifierMask + KeyModifierMask mask = 0; + if (state & m_numLockMask) { + mask |= KeyModifierNumLock; + } + if (state & m_capsLockMask) { + mask |= KeyModifierCapsLock; + } + if (state & m_scrollLockMask) { + mask |= KeyModifierScrollLock; + } + + return mask; +} + +bool +CXWindowsPrimaryScreen::isLockedToScreen() const +{ + CDisplayLock display(m_screen); + + // query the pointer to get the button state + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + if ((state & (Button1Mask | Button2Mask | Button3Mask | + Button4Mask | Button5Mask)) != 0) { + LOG((CLOG_DEBUG "locked by mouse button")); + return true; + } + } + + // get logical keyboard state + char keyMap[32]; + memset(keyMap, 0, sizeof(keyMap)); + XQueryKeymap(display, keyMap); + + // locked if any key is down + for (unsigned int i = 0; i < sizeof(keyMap); ++i) { + if (keyMap[i] != 0) { + for (unsigned int j = 0; j < 8; ++j) { + if ((keyMap[i] & (1 << j)) != 0) { + const KeyCode keycode = 8 * i + j; + const KeySym keysym = XKeycodeToKeysym(display, + keycode, 0); + // if any key is half-duplex then it'll be down when + // toggled on but shouldn't count as a reason to lock + // to the screen. + if (m_numLockHalfDuplex && keysym == XK_Num_Lock) { + continue; + } + if (m_capsLockHalfDuplex && keysym == XK_Caps_Lock) { + continue; + } + + // non-half-duplex key down + char* name = XKeysymToString(keysym); + if (name == NULL) { + LOG((CLOG_DEBUG "locked by keycode %d", keycode)); + } + else { + LOG((CLOG_DEBUG "locked by \"%s\"", name)); + } + return true; + } + } + } + } + + // not locked + return false; +} + +IScreen* +CXWindowsPrimaryScreen::getScreen() const +{ + return m_screen; +} + +void +CXWindowsPrimaryScreen::onScreensaver(bool activated) +{ + m_receiver->onScreensaver(activated); +} + +bool +CXWindowsPrimaryScreen::onPreDispatch(const CEvent*) +{ + return false; +} + +bool +CXWindowsPrimaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + XEvent& xevent = event->m_event; + + // let input methods try to handle event first + if (m_ic != NULL) { + // XFilterEvent() may eat the event and generate a new KeyPress + // event with a keycode of 0 because there isn't an actual key + // associated with the keysym. but the KeyRelease may pass + // through XFilterEvent() and keep its keycode. this means + // there's a mismatch between KeyPress and KeyRelease keycodes. + // since we use the keycode on the client to detect when a key + // is released this won't do. so we remember the keycode on + // the most recent KeyPress (and clear it on a matching + // KeyRelease) so we have a keycode for a synthesized KeyPress. + if (xevent.type == KeyPress && xevent.xkey.keycode != 0) { + m_lastKeycode = xevent.xkey.keycode; + } + else if (xevent.type == KeyRelease && + xevent.xkey.keycode == m_lastKeycode) { + m_lastKeycode = 0; + } + + // now filter the event + if (XFilterEvent(&xevent, None)) { + return true; + } + } + + // handle event + switch (xevent.type) { + case CreateNotify: + { + // select events on new window + CDisplayLock display(m_screen); + selectEvents(display, xevent.xcreatewindow.window); + } + return true; + + case MappingNotify: + // keyboard mapping changed + updateKeys(); + return true; + + case KeyPress: + { + LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + KeyID key = mapKey(&xevent.xkey); + if (key != kKeyNone) { + // check for ctrl+alt+del emulation + if ((key == kKeyPause || key == kKeyBreak) && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + // pretend it's ctrl+alt+del + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + key = kKeyDelete; + } + + // get which button. see call to XFilterEvent() above + // for more info. + KeyCode keycode = xevent.xkey.keycode; + if (keycode == 0) { + keycode = m_lastKeycode; + } + + // handle key + m_receiver->onKeyDown(key, mask, + static_cast(keycode)); + if (key == kKeyCapsLock && m_capsLockHalfDuplex) { + m_receiver->onKeyUp(key, mask | KeyModifierCapsLock, + static_cast(keycode)); + } + else if (key == kKeyNumLock && m_numLockHalfDuplex) { + m_receiver->onKeyUp(key, mask | KeyModifierNumLock, + static_cast(keycode)); + } + } + } + return true; + + case KeyRelease: + { + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + KeyID key = mapKey(&xevent.xkey); + if (key != kKeyNone) { + // check if this is a key repeat by getting the next + // KeyPress event that has the same key and time as + // this release event, if any. first prepare the + // filter info. + CKeyEventInfo filter; + filter.m_event = KeyPress; + filter.m_window = xevent.xkey.window; + filter.m_time = xevent.xkey.time; + filter.m_keycode = xevent.xkey.keycode; + + // now check for event + bool hasPress; + { + XEvent xevent2; + CDisplayLock display(m_screen); + hasPress = (XCheckIfEvent(display, &xevent2, + &CXWindowsPrimaryScreen::findKeyEvent, + (XPointer)&filter) == True); + } + + // check for ctrl+alt+del emulation + if ((key == kKeyPause || key == kKeyBreak) && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + // pretend it's ctrl+alt+del and ignore autorepeat + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + key = kKeyDelete; + hasPress = false; + } + + + if (!hasPress) { + // 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 == kKeyCapsLock && m_capsLockHalfDuplex) { + m_receiver->onKeyDown(key, mask, + static_cast(xevent.xkey.keycode)); + } + else if (key == kKeyNumLock && m_numLockHalfDuplex) { + m_receiver->onKeyDown(key, mask, + static_cast(xevent.xkey.keycode)); + } + m_receiver->onKeyUp(key, mask, + static_cast(xevent.xkey.keycode)); + } + else { + // found a press event following so it's a repeat. + // we could attempt to count the already queued + // 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_receiver->onKeyRepeat(key, mask, 1, + static_cast(xevent.xkey.keycode)); + } + } + } + return true; + + case ButtonPress: + { + LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_receiver->onMouseDown(button); + } + } + return true; + + case ButtonRelease: + { + LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_receiver->onMouseUp(button); + } + else if (xevent.xbutton.button == 4) { + // wheel forward (away from user) + m_receiver->onMouseWheel(120); + } + else if (xevent.xbutton.button == 5) { + // wheel backward (toward user) + m_receiver->onMouseWheel(-120); + } + } + return true; + + case MotionNotify: + { + LOG((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); + + // compute motion delta (relative to the last known + // mouse position) + SInt32 x = xevent.xmotion.x_root - m_x; + SInt32 y = xevent.xmotion.y_root - m_y; + + // save position to compute delta of next motion + m_x = xevent.xmotion.x_root; + m_y = xevent.xmotion.y_root; + + if (xevent.xmotion.send_event) { + // we warped the mouse. discard events until we + // find the matching sent event. see + // warpCursorNoFlush() for where the events are + // sent. we discard the matching sent event and + // can be sure we've skipped the warp event. + CDisplayLock display(m_screen); + do { + XMaskEvent(display, PointerMotionMask, &xevent); + } while (!xevent.xmotion.send_event); + } + else if (!isActive()) { + // motion on primary screen + m_receiver->onMouseMovePrimary(m_x, m_y); + } + else { + // motion on secondary screen. warp mouse back to + // center. + // + // my lombard (powerbook g3) running linux and + // using the adbmouse driver has two problems: + // first, the driver only sends motions of +/-2 + // pixels and, second, it seems to discard some + // physical input after a warp. the former isn't a + // big deal (we're just limited to every other + // pixel) but the latter is a PITA. to work around + // it we only warp when the mouse has moved more + // than s_size pixels from the center. + static const SInt32 s_size = 32; + if (xevent.xmotion.x_root - m_xCenter < -s_size || + xevent.xmotion.x_root - m_xCenter > s_size || + xevent.xmotion.y_root - m_yCenter < -s_size || + xevent.xmotion.y_root - m_yCenter > s_size) { + CDisplayLock display(m_screen); + warpCursorNoFlush(display, m_xCenter, m_yCenter); + } + + // send event if mouse moved. do this after warping + // back to center in case the motion takes us onto + // the primary screen. if we sent the event first + // in that case then the warp would happen after + // warping to the primary screen's enter position, + // effectively overriding it. + if (x != 0 || y != 0) { + m_receiver->onMouseMoveSecondary(x, y); + } + } + } + return true; + } + + return false; +} + +void +CXWindowsPrimaryScreen::onOneShotTimerExpired(UInt32 id) +{ + m_receiver->onOneShotTimerExpired(id); +} + +SInt32 +CXWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + +void +CXWindowsPrimaryScreen::onPreMainLoop() +{ + assert(m_window != None); +} + +void +CXWindowsPrimaryScreen::onPreOpen() +{ + assert(m_window == None); +} + +void +CXWindowsPrimaryScreen::onPostOpen() +{ + assert(m_window != None); + + // get cursor info + m_screen->getCursorPos(m_x, m_y); + m_screen->getCursorCenter(m_xCenter, m_yCenter); + + // get the input method + CDisplayLock display(m_screen); + m_im = XOpenIM(display, NULL, NULL, NULL); + if (m_im == NULL) { + return; + } + + // find the appropriate style. synergy supports XIMPreeditNothing + // only at the moment. + XIMStyles* styles; + if (XGetIMValues(m_im, XNQueryInputStyle, &styles, NULL) != NULL || + styles == NULL) { + LOG((CLOG_WARN "cannot get IM styles")); + return; + } + XIMStyle style = 0; + for (unsigned short i = 0; i < styles->count_styles; ++i) { + style = styles->supported_styles[i]; + if ((style & XIMPreeditNothing) != 0) { + if ((style & (XIMStatusNothing | XIMStatusNone)) != 0) { + break; + } + } + } + XFree(styles); + if (style == 0) { + LOG((CLOG_WARN "no supported IM styles")); + return; + } + + // create an input context for the style and tell it about our window + m_ic = XCreateIC(m_im, XNInputStyle, style, XNClientWindow, m_window, NULL); + if (m_ic == NULL) { + LOG((CLOG_WARN "cannot create IC")); + return; + } + + // find out the events we must select for and do so + unsigned long mask; + if (XGetICValues(m_ic, XNFilterEvents, &mask, NULL) != NULL) { + LOG((CLOG_WARN "cannot get IC filter events")); + return; + } + XWindowAttributes attr; + XGetWindowAttributes(display, m_window, &attr); + XSelectInput(display, m_window, attr.your_event_mask | mask); + + // no previous keycode + m_lastKeycode = 0; +} + +void +CXWindowsPrimaryScreen::onPreClose() +{ + CDisplayLock display(m_screen); + if (m_ic != NULL) { + XDestroyIC(m_ic); + m_ic = NULL; + } + if (m_im != NULL) { + XCloseIM(m_im); + m_im = NULL; + } + m_lastKeycode = 0; +} + +void +CXWindowsPrimaryScreen::onPreEnter() +{ + assert(m_window != None); + + if (m_ic != NULL) { + XUnsetICFocus(m_ic); + } +} + +void +CXWindowsPrimaryScreen::onPreLeave() +{ + assert(m_window != None); + + if (m_ic != NULL) { + XmbResetIC(m_ic); + XSetICFocus(m_ic); + } +} + +void +CXWindowsPrimaryScreen::onEnterScreenSaver() +{ + CDisplayLock display(m_screen); + + // set keyboard focus to root window. the screensaver should then + // pick up key events for when the user enters a password to unlock. + XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); +} + +void +CXWindowsPrimaryScreen::createWindow() +{ + assert(m_window == None); + + // get size of screen + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + + // grab window attributes. this window is used to capture user + // input when the user is focused on another client. don't let + // the window manager mess with it. + XSetWindowAttributes attr; + attr.event_mask = PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask | PropertyChangeMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = m_screen->getBlankCursor(); + + { + // create the grab window + CDisplayLock display(m_screen); + m_window = XCreateWindow(display, m_screen->getRoot(), + x, y, w, h, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + if (m_window == None) { + throw XScreenOpenFailure(); + } + LOG((CLOG_DEBUG "window is 0x%08x", m_window)); + + // start watching for events on other windows + selectEvents(display, m_screen->getRoot()); + } + + // tell generic screen about the window + m_screen->setWindow(m_window); +} + +void +CXWindowsPrimaryScreen::destroyWindow() +{ + // display can be NULL if the server unexpectedly disconnected + if (m_window != None) { + m_screen->setWindow(None); + CDisplayLock display(m_screen); + if (display != NULL) { + XDestroyWindow(display, m_window); + } + m_window = None; + } +} + +bool +CXWindowsPrimaryScreen::showWindow() +{ + assert(m_window != None); + + CDisplayLock display(m_screen); + + // raise and show the input window + XMapRaised(display, m_window); + + // grab the mouse and keyboard. keep trying until we get them. + // if we can't grab one after grabbing the other then ungrab + // and wait before retrying. give up after s_timeout seconds. + static const double s_timeout = 1.0; + int result; + CStopwatch timer; + do { + // keyboard first + do { + result = XGrabKeyboard(display, m_window, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) { + LOG((CLOG_DEBUG2 "waiting to grab keyboard")); + ARCH->sleep(0.05); + if (timer.getTime() >= s_timeout) { + LOG((CLOG_DEBUG2 "grab keyboard timed out")); + XUnmapWindow(display, m_window); + return false; + } + } + } while (result != GrabSuccess); + LOG((CLOG_DEBUG2 "grabbed keyboard")); + + // now the mouse + result = XGrabPointer(display, m_window, True, 0, + GrabModeAsync, GrabModeAsync, + m_window, None, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) { + // back off to avoid grab deadlock + XUngrabKeyboard(display, CurrentTime); + LOG((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer")); + ARCH->sleep(0.05); + if (timer.getTime() >= s_timeout) { + LOG((CLOG_DEBUG2 "grab pointer timed out")); + XUnmapWindow(display, m_window); + return false; + } + } + } while (result != GrabSuccess); + LOG((CLOG_DEBUG1 "grabbed pointer and keyboard")); + + return true; +} + +void +CXWindowsPrimaryScreen::hideWindow() +{ + CDisplayLock display(m_screen); + + // unmap the grab window. this also ungrabs the mouse and keyboard. + XUnmapWindow(display, m_window); +} + +void +CXWindowsPrimaryScreen::warpCursorToCenter() +{ + warpCursor(m_xCenter, m_yCenter); +} + +void +CXWindowsPrimaryScreen::warpCursorNoFlush( + Display* display, SInt32 x, SInt32 y) +{ + assert(display != NULL); + assert(m_window != None); + + // send an event that we can recognize before the mouse warp + XEvent eventBefore; + eventBefore.type = MotionNotify; + eventBefore.xmotion.display = display; + eventBefore.xmotion.window = m_window; + eventBefore.xmotion.root = m_screen->getRoot(); + eventBefore.xmotion.subwindow = m_window; + eventBefore.xmotion.time = CurrentTime; + eventBefore.xmotion.x = x; + eventBefore.xmotion.y = y; + eventBefore.xmotion.x_root = x; + eventBefore.xmotion.y_root = y; + eventBefore.xmotion.state = 0; + eventBefore.xmotion.is_hint = NotifyNormal; + eventBefore.xmotion.same_screen = True; + XEvent eventAfter = eventBefore; + XSendEvent(display, m_window, False, 0, &eventBefore); + + // warp mouse + XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y); + + // send an event that we can recognize after the mouse warp + XSendEvent(display, m_window, False, 0, &eventAfter); + XSync(display, False); + + LOG((CLOG_DEBUG2 "warped to %d,%d", x, y)); +} + +void +CXWindowsPrimaryScreen::selectEvents(Display* display, Window w) const +{ + // ignore errors while we adjust event masks. windows could be + // destroyed at any time after the XQueryTree() in doSelectEvents() + // so we must ignore BadWindow errors. + CXWindowsUtil::CErrorLock lock(display); + + // adjust event masks + doSelectEvents(display, w); +} + +void +CXWindowsPrimaryScreen::doSelectEvents(Display* display, Window w) const +{ + // we want to track the mouse everywhere on the display. to achieve + // that we select PointerMotionMask on every window. we also select + // SubstructureNotifyMask in order to get CreateNotify events so we + // select events on new windows too. + // + // note that this can break certain clients due a design flaw of X. + // X will deliver a PointerMotion event to the deepest window in the + // hierarchy that contains the pointer and has PointerMotionMask + // selected by *any* client. if another client doesn't select + // motion events in a subwindow so the parent window will get them + // then by selecting for motion events on the subwindow we break + // that client because the parent will no longer get the events. + + // FIXME -- should provide some workaround for event selection + // design flaw. perhaps only select for motion events on windows + // that already do or are top-level windows or don't propagate + // pointer events. or maybe an option to simply poll the mouse. + + // we don't want to adjust our grab window + if (w == m_window) { + return; + } + + // select events of interest. do this before querying the tree so + // we'll get notifications of children created after the XQueryTree() + // so we won't miss them. + XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask); + + // recurse on child windows + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(display, w, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) { + doSelectEvents(display, cw[i]); + } + XFree(cw); + } +} + +KeyModifierMask +CXWindowsPrimaryScreen::mapModifier(unsigned int state) const +{ + KeyModifierMask mask = 0; + if (state & ShiftMask) + mask |= KeyModifierShift; + if (state & LockMask) + mask |= KeyModifierCapsLock; + if (state & ControlMask) + mask |= KeyModifierControl; + if (state & m_altMask) + mask |= KeyModifierAlt; + if (state & m_metaMask) + mask |= KeyModifierMeta; + if (state & m_superMask) + mask |= KeyModifierSuper; + if (state & m_modeSwitchMask) + mask |= KeyModifierModeSwitch; + if (state & m_numLockMask) + mask |= KeyModifierNumLock; + if (state & m_scrollLockMask) + mask |= KeyModifierScrollLock; + return mask; +} + +// map "Internet" keys to KeyIDs +static const KeySym g_map1008FF[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, kKeyAudioDown, kKeyAudioMute, kKeyAudioUp, + /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext, + /* 0x18 */ kKeyWWWHome, kKeyAppMail, 0, kKeyWWWSearch, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, kKeyWWWBack, kKeyWWWForward, + /* 0x28 */ kKeyWWWStop, kKeyWWWRefresh, 0, 0, 0, 0, 0, 0, + /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; + +KeyID +CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const +{ + CDisplayLock display(m_screen); + + // convert to a keysym + KeySym keysym; + if (event->type == KeyPress && m_ic != NULL) { + // do multibyte lookup. can only call XmbLookupString with a + // key press event and a valid XIC so we checked those above. + char scratch[32]; + int n = sizeof(scratch) / sizeof(scratch[0]); + char* buffer = scratch; + int status; + n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status); + if (status == XBufferOverflow) { + // not enough space. grow buffer and try again. + buffer = new char[n]; + n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status); + delete[] buffer; + } + + // see what we got. since we don't care about the string + // we'll just look for a keysym. + switch (status) { + default: + case XLookupNone: + case XLookupChars: + keysym = 0; + break; + + case XLookupKeySym: + case XLookupBoth: + break; + } + } + else { + // plain old lookup + char dummy[1]; + XLookupString(event, dummy, 0, &keysym, NULL); + } + + // convert key + switch (keysym & 0xffffff00) { + case 0x0000: + // Latin-1 + return static_cast(keysym); + + case 0xfe00: + // ISO 9995 Function and Modifier Keys + if (keysym == XK_ISO_Left_Tab) { + return kKeyLeftTab; + } + return kKeyNone; + + case 0xff00: + // MISCELLANY + return static_cast(keysym - 0xff00 + 0xef00); + + case 0x1008ff00: + // "Internet" keys + return g_map1008FF[keysym & 0xff]; + + default: { + // lookup character in table + UInt32 key = CXWindowsUtil::mapKeySymToUCS4(keysym); + if (key != 0x0000ffff) { + return static_cast(key); + } + + // unknown character + return kKeyNone; + } + } +} + +ButtonID +CXWindowsPrimaryScreen::mapButton(unsigned int button) const +{ + // first three buttons map to 1, 2, 3 (kButtonLeft, Middle, Right) + if (button >= 1 && button <= 3) { + return static_cast(button); + } + + // buttons 4 and 5 are ignored here. they're used for the wheel. + // buttons 6, 7, etc and up map to 4, 5, etc. + else if (button >= 6) { + return static_cast(button - 2); + } + + // unknown button + else { + return kButtonNone; + } +} + +void +CXWindowsPrimaryScreen::updateKeys() +{ + CDisplayLock display(m_screen); + + // get modifier map from server + XModifierKeymap* keymap = XGetModifierMapping(display); + + // initialize + m_altMask = 0; + m_metaMask = 0; + m_superMask = 0; + m_modeSwitchMask = 0; + m_numLockMask = 0; + m_capsLockMask = 0; + m_scrollLockMask = 0; + + // work around for my system, which reports this state bit when + // mode switch is down, instead of the appropriate modifier bit. + // should have no effect on other systems. -crs 9/02. + m_modeSwitchMask |= (1 << 13); + + // set keycodes and masks + for (unsigned int i = 0; i < 8; ++i) { + const unsigned int bit = (1 << i); + for (int j = 0; j < keymap->max_keypermod; ++j) { + KeyCode keycode = keymap->modifiermap[i * + keymap->max_keypermod + j]; + + // note mask for particular modifiers + const KeySym keysym = XKeycodeToKeysym(display, keycode, 0); + switch (keysym) { + case XK_Alt_L: + case XK_Alt_R: + m_altMask |= bit; + break; + + case XK_Meta_L: + case XK_Meta_R: + m_metaMask |= bit; + break; + + case XK_Super_L: + case XK_Super_R: + m_superMask |= bit; + break; + + case XK_Mode_switch: + m_modeSwitchMask |= bit; + break; + + case XK_Num_Lock: + m_numLockMask |= bit; + break; + + case XK_Caps_Lock: + m_capsLockMask |= bit; + break; + + case XK_Scroll_Lock: + m_scrollLockMask |= bit; + break; + } + } + } + + XFreeModifiermap(keymap); +} + +Bool +CXWindowsPrimaryScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) +{ + CKeyEventInfo* filter = reinterpret_cast(arg); + return (xevent->type == filter->m_event && + xevent->xkey.window == filter->m_window && + xevent->xkey.time == filter->m_time && + xevent->xkey.keycode == filter->m_keycode) ? True : False; +} diff --git a/lib/platform/CXWindowsPrimaryScreen.h b/lib/platform/CXWindowsPrimaryScreen.h new file mode 100644 index 0000000..2343ca0 --- /dev/null +++ b/lib/platform/CXWindowsPrimaryScreen.h @@ -0,0 +1,126 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSPRIMARYSCREEN_H +#define CXWINDOWSPRIMARYSCREEN_H + +#include "CPrimaryScreen.h" +#include "IScreenEventHandler.h" +#include "MouseTypes.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +class CXWindowsScreen; +class IScreenReceiver; +class IPrimaryScreenReceiver; + +//! X11 primary screen implementation +class CXWindowsPrimaryScreen : + public CPrimaryScreen, public IScreenEventHandler { +public: + CXWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); + virtual ~CXWindowsPrimaryScreen(); + + // CPrimaryScreen overrides + virtual void reconfigure(UInt32 activeSides); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual UInt32 addOneShotTimer(double timeout); + virtual KeyModifierMask getToggleMask() const; + virtual bool isLockedToScreen() const; + virtual IScreen* getScreen() const; + + // IScreenEventHandler overrides + virtual void onScreensaver(bool activated); + virtual bool onPreDispatch(const CEvent* event); + virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); + virtual SInt32 getJumpZoneSize() const; + +protected: + // CPrimaryScreen overrides + virtual void onPreMainLoop(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreClose(); + virtual void onPreEnter(); + virtual void onPreLeave(); + virtual void onEnterScreenSaver(); + + virtual void createWindow(); + virtual void destroyWindow(); + virtual bool showWindow(); + virtual void hideWindow(); + virtual void warpCursorToCenter(); + + virtual void updateKeys(); + +private: + void warpCursorNoFlush(Display*, + SInt32 xAbsolute, SInt32 yAbsolute); + + void selectEvents(Display*, Window) const; + void doSelectEvents(Display*, Window) const; + + KeyModifierMask mapModifier(unsigned int state) const; + KeyID mapKey(XKeyEvent*) const; + ButtonID mapButton(unsigned int button) const; + + class CKeyEventInfo { + public: + int m_event; + Window m_window; + Time m_time; + KeyCode m_keycode; + }; + static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); + +private: + CXWindowsScreen* m_screen; + IPrimaryScreenReceiver* m_receiver; + + // our window + Window m_window; + + // note toggle keys that toggle on up/down (false) or on + // transition (true) + bool m_numLockHalfDuplex; + bool m_capsLockHalfDuplex; + + // modifier masks + unsigned int m_altMask; + unsigned int m_metaMask; + unsigned int m_superMask; + unsigned int m_modeSwitchMask; + unsigned int m_numLockMask; + unsigned int m_capsLockMask; + unsigned int m_scrollLockMask; + + // last mouse position + SInt32 m_x, m_y; + + // position of center pixel of screen + SInt32 m_xCenter, m_yCenter; + + // input method stuff + XIM m_im; + XIC m_ic; + KeyCode m_lastKeycode; +}; + +#endif diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp new file mode 100644 index 0000000..870da6d --- /dev/null +++ b/lib/platform/CXWindowsScreen.cpp @@ -0,0 +1,922 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsScreen.h" +#include "CXWindowsClipboard.h" +#include "CXWindowsScreenSaver.h" +#include "CXWindowsUtil.h" +#include "CClipboard.h" +#include "IScreenEventHandler.h" +#include "IScreenReceiver.h" +#include "XScreen.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" +#include "IJob.h" +#include +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +# include +# if HAVE_X11_EXTENSIONS_XINERAMA_H + // Xinerama.h may lack extern "C" for inclusion by C++ + extern "C" { +# include + } +# endif +#endif +#if UNIX_LIKE +# if HAVE_POLL +# include +# else +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TIME_H +# include +# endif +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_UNISTD_H +# include +# endif +# endif +#endif + +// +// CXWindowsScreen::CTimer +// + +CXWindowsScreen::CTimer::CTimer(IJob* job, double startTime, double resetTime) : + m_job(job), + m_timeout(resetTime), + m_time(resetTime), + m_startTime(startTime) +{ + assert(m_timeout > 0.0); +} + +CXWindowsScreen::CTimer::~CTimer() +{ + // do nothing +} + +void +CXWindowsScreen::CTimer::run() +{ + if (m_job != NULL) { + m_job->run(); + } +} + +void +CXWindowsScreen::CTimer::reset() +{ + m_time = m_timeout; + m_startTime = 0.0; +} + +CXWindowsScreen::CTimer::CTimer& +CXWindowsScreen::CTimer::operator-=(double dt) +{ + m_time -= dt - m_startTime; + m_startTime = 0.0; + return *this; +} + +CXWindowsScreen::CTimer::operator double() const +{ + return m_time; +} + +bool +CXWindowsScreen::CTimer::operator<(const CTimer& t) const +{ + return m_time < t.m_time; +} + + +// +// CXWindowsScreen +// + +CXWindowsScreen* CXWindowsScreen::s_screen = NULL; + +CXWindowsScreen::CXWindowsScreen(IScreenReceiver* receiver, + IScreenEventHandler* eventHandler) : + m_display(NULL), + m_root(None), + m_stop(false), + m_receiver(receiver), + m_eventHandler(eventHandler), + m_window(None), + m_x(0), m_y(0), + m_w(0), m_h(0), + m_xCenter(0), m_yCenter(0), + m_screensaver(NULL), + m_screensaverNotify(false), + m_atomScreensaver(None), + m_oneShotTimer(NULL) +{ + assert(s_screen == NULL); + assert(m_receiver != NULL); + assert(m_eventHandler != NULL); + + s_screen = this; + + // no clipboards to start with + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = NULL; + } +} + +CXWindowsScreen::~CXWindowsScreen() +{ + assert(s_screen != NULL); + assert(m_display == NULL); + + delete m_oneShotTimer; + s_screen = NULL; +} + +void +CXWindowsScreen::addTimer(IJob* job, double timeout) +{ + CLock lock(&m_timersMutex); + removeTimerNoLock(job); + m_timers.push(CTimer(job, m_time.getTime(), timeout)); +} + +void +CXWindowsScreen::removeTimer(IJob* job) +{ + CLock lock(&m_timersMutex); + removeTimerNoLock(job); +} + +void +CXWindowsScreen::removeTimerNoLock(IJob* job) +{ + // do it the hard way. first collect all jobs that are not + // the removed job. + CTimerPriorityQueue::container_type tmp; + for (CTimerPriorityQueue::iterator index = m_timers.begin(); + index != m_timers.end(); ++index) { + if (index->getJob() != job) { + tmp.push_back(*index); + } + } + + // now swap in the new list + m_timers.swap(tmp); +} + +UInt32 +CXWindowsScreen::addOneShotTimer(double timeout) +{ + CLock lock(&m_timersMutex); + // FIXME -- support multiple one-shot timers + m_oneShotTimer = new CTimer(NULL, m_time.getTime(), timeout); + return 0; +} + +void +CXWindowsScreen::setWindow(Window window) +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + // destroy the clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + delete m_clipboard[id]; + m_clipboard[id] = NULL; + } + + // save the new window + m_window = window; + + // initialize the clipboards + if (m_window != None) { + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = new CXWindowsClipboard(m_display, m_window, id); + } + } +} + +Window +CXWindowsScreen::getRoot() const +{ + assert(m_display != NULL); + return m_root; +} + +Cursor +CXWindowsScreen::getBlankCursor() const +{ + return m_cursor; +} + +void +CXWindowsScreen::open() +{ + assert(m_display == NULL); + + // set the X I/O error handler so we catch the display disconnecting + XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); + + // get the DISPLAY + const char* display = getenv("DISPLAY"); + if (display == NULL) { + display = ":0.0"; + } + + // open the display + LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); + m_display = XOpenDisplay(display); + if (m_display == NULL) { + throw XScreenUnavailable(60.0); + } + + // get root window + m_root = DefaultRootWindow(m_display); + + // create the transparent cursor + createBlankCursor(); + + // get screen shape + updateScreenShape(); + + // initialize the screen saver + m_atomScreensaver = XInternAtom(m_display, "SYNERGY_SCREENSAVER", False); + m_screensaver = new CXWindowsScreenSaver(this, m_display); +} + +void +CXWindowsScreen::mainLoop() +{ + // wait for an event in a cancellable way and don't lock the + // display while we're waiting. + CEvent event; + m_mutex.lock(); + +#if UNIX_LIKE + + // use poll() to wait for a message from the X server or for timeout. + // this is a good deal more efficient than polling and sleeping. +#if HAVE_POLL + struct pollfd pfds[1]; + pfds[0].fd = ConnectionNumber(m_display); + pfds[0].events = POLLIN; +#endif + while (!m_stop) { + // compute timeout to next timer + double dtimeout; + { + CLock timersLock(&m_timersMutex); + dtimeout = (m_timers.empty() ? -1.0 : m_timers.top()); + if (m_oneShotTimer != NULL && + (dtimeout == -1.0 || *m_oneShotTimer < dtimeout)) { + dtimeout = *m_oneShotTimer; + } + } +#if HAVE_POLL + int timeout = static_cast(1000.0 * dtimeout); +#else + struct timeval timeout; + struct timeval* timeoutPtr; + if (dtimeout < 0.0) { + timeoutPtr = NULL; + } + else { + timeout.tv_sec = static_cast(dtimeout); + timeout.tv_usec = static_cast(1.0e+6 * + (dtimeout - timeout.tv_sec)); + timeoutPtr = &timeout; + } + + // initialize file descriptor sets + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(ConnectionNumber(m_display), &rfds); +#endif + + // wait for message from X server or for timeout. also check + // if the thread has been cancelled. poll() should return -1 + // with EINTR when the thread is cancelled. + m_mutex.unlock(); + CThread::testCancel(); +#if HAVE_POLL + poll(pfds, 1, timeout); +#else + select(ConnectionNumber(m_display) + 1, + SELECT_TYPE_ARG234 &rfds, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 timeoutPtr); +#endif + CThread::testCancel(); + m_mutex.lock(); + + // process timers + processTimers(); + + // handle pending events + while (!m_stop && XPending(m_display) > 0) { + // get the event + XNextEvent(m_display, &event.m_event); + + // process the event. if unhandled then let the subclass + // have a go at it. + m_mutex.unlock(); + if (!onPreDispatch(&event)) { + m_eventHandler->onEvent(&event); + } + m_mutex.lock(); + } + } + +#else // !UNIX_LIKE + + // poll and sleep + while (!m_stop) { + // poll for pending events and process timers + while (!m_stop && XPending(m_display) == 0) { + // check timers + if (processTimers()) { + continue; + } + + // wait + m_mutex.unlock(); + CThread::sleep(0.01); + m_mutex.lock(); + } + + // process events + while (!m_stop && XPending(m_display) > 0) { + // get the event + XNextEvent(m_display, &event.m_event); + + // process the event. if unhandled then let the subclass + // have a go at it. + m_mutex.unlock(); + if (!onPreDispatch(&event)) { + m_eventHandler->onEvent(&event); + } + m_mutex.lock(); + } + } + +#endif // !UNIX_LIKE + + m_mutex.unlock(); +} + +void +CXWindowsScreen::exitMainLoop() +{ + // m_stop should be a condition variable that we signal here + // but we can't wait on both the condition variable and the + // X connection so there's no point. however, we do need + // to wake up the X connection so send ourself some event. + CLock lock(&m_mutex); + m_stop = true; + + if (m_display != NULL && m_window != None) { + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.display = m_display; + event.xclient.window = m_window; + event.xclient.message_type = XInternAtom(m_display, "ATOM", False); + event.xclient.format = 32; + event.xclient.data.l[0] = 0; + event.xclient.data.l[1] = 0; + event.xclient.data.l[2] = 0; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + CXWindowsUtil::CErrorLock lock(m_display); + XSendEvent(m_display, m_window, False, 0, &event); + } +} + +void +CXWindowsScreen::close() +{ + CLock lock(&m_mutex); + + // done with screen saver + delete m_screensaver; + m_screensaver = NULL; + + // destroy clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + delete m_clipboard[id]; + m_clipboard[id] = NULL; + } + + // close the display + if (m_display != NULL) { + XCloseDisplay(m_display); + m_display = NULL; + LOG((CLOG_DEBUG "closed display")); + } + XSetIOErrorHandler(NULL); +} + +bool +CXWindowsScreen::setClipboard(ClipboardID id, const IClipboard* clipboard) +{ + CLock lock(&m_mutex); + + // fail if we don't have the requested clipboard + if (m_clipboard[id] == NULL) { + return false; + } + + // get the actual time. ICCCM does not allow CurrentTime. + Time timestamp = CXWindowsUtil::getCurrentTime( + m_display, m_clipboard[id]->getWindow()); + + if (clipboard != NULL) { + // save clipboard data + return CClipboard::copy(m_clipboard[id], clipboard, timestamp); + } + else { + // assert clipboard ownership + if (!m_clipboard[id]->open(timestamp)) { + return false; + } + m_clipboard[id]->empty(); + m_clipboard[id]->close(); + return true; + } +} + +void +CXWindowsScreen::checkClipboards() +{ + // do nothing, we're always up to date +} + +void +CXWindowsScreen::openScreensaver(bool notify) +{ + CLock lock(&m_mutex); + assert(m_screensaver != NULL); + + m_screensaverNotify = notify; + if (m_screensaverNotify) { + m_screensaver->setNotify(m_window); + } + else { + m_screensaver->disable(); + } +} + +void +CXWindowsScreen::closeScreensaver() +{ + CLock lock(&m_mutex); + if (m_screensaver != NULL) { + if (m_screensaverNotify) { + m_screensaver->setNotify(None); + } + else { + m_screensaver->enable(); + } + } +} + +void +CXWindowsScreen::screensaver(bool activate) +{ + CLock lock(&m_mutex); + assert(m_screensaver != NULL); + + if (activate) { + m_screensaver->activate(); + } + else { + m_screensaver->deactivate(); + } +} + +void +CXWindowsScreen::syncDesktop() +{ + // do nothing; X doesn't suffer from this bogosity +} + +bool +CXWindowsScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const +{ + assert(clipboard != NULL); + + // block others from using the display while we get the clipboard + CLock lock(&m_mutex); + + // fail if we don't have the requested clipboard + if (m_clipboard[id] == NULL) { + return false; + } + + // get the actual time. ICCCM does not allow CurrentTime. + Time timestamp = CXWindowsUtil::getCurrentTime( + m_display, m_clipboard[id]->getWindow()); + + // copy the clipboard + return CClipboard::copy(clipboard, m_clipboard[id], timestamp); +} + +void +CXWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + x = m_x; + y = m_y; + w = m_w; + h = m_h; +} + +void +CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + Window root, window; + int mx, my, xWindow, yWindow; + unsigned int mask; + if (XQueryPointer(m_display, getRoot(), &root, &window, + &mx, &my, &xWindow, &yWindow, &mask)) { + x = mx; + y = my; + } + else { + x = m_xCenter; + y = m_yCenter; + } +} + +void +CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + CLock lock(&m_mutex); + + x = m_xCenter; + y = m_yCenter; +} + +void +CXWindowsScreen::updateScreenShape() +{ + // get shape of default screen + m_x = 0; + m_y = 0; + m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); + m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + + // get center of default screen + m_xCenter = m_x + (m_w >> 1); + m_yCenter = m_y + (m_h >> 1); + +#if HAVE_X11_EXTENSIONS_XINERAMA_H + // get center of first Xinerama screen. Xinerama appears to have + // a bug when XWarpPointer() is used in combination with + // XGrabPointer(). in that case, the warp is successful but the + // next pointer motion warps the pointer again, apparently to + // constrain it to some unknown region, possibly the region from + // 0,0 to Wm,Hm where Wm (Hm) is the minimum width (height) over + // all physical screens. this warp only seems to happen if the + // pointer wasn't in that region before the XWarpPointer(). the + // second (unexpected) warp causes synergy to think the pointer + // has been moved when it hasn't. to work around the problem, + // we warp the pointer to the center of the first physical + // screen instead of the logical screen. + int eventBase, errorBase; + if (XineramaQueryExtension(m_display, &eventBase, &errorBase)) { + if (XineramaIsActive(m_display)) { + int numScreens; + XineramaScreenInfo* screens; + screens = XineramaQueryScreens(m_display, &numScreens); + if (screens != NULL) { + if (numScreens > 1) { + m_xCenter = screens[0].x_org + (screens[0].width >> 1); + m_yCenter = screens[0].y_org + (screens[0].height >> 1); + } + XFree(screens); + } + } + } +#endif +} + +bool +CXWindowsScreen::onPreDispatch(CEvent* event) +{ + assert(event != NULL); + XEvent* xevent = &event->m_event; + + switch (xevent->type) { + case MappingNotify: + { + CLock lock(&m_mutex); + if (XPending(m_display) > 0) { + XEvent tmpEvent; + XPeekEvent(m_display, &tmpEvent); + if (tmpEvent.type == MappingNotify) { + // discard this MappingNotify since another follows. + // we tend to get a bunch of these in a row. + return true; + } + } + } + + // keyboard mapping changed + XRefreshKeyboardMapping(&xevent->xmapping); + + // pass event on + break; + + case SelectionClear: + { + // we just lost the selection. that means someone else + // grabbed the selection so this screen is now the + // selection owner. report that to the receiver. + ClipboardID id = getClipboardID(xevent->xselectionclear.selection); + if (id != kClipboardEnd) { + LOG((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); + m_clipboard[id]->lost(xevent->xselectionclear.time); + m_receiver->onGrabClipboard(id); + return true; + } + } + break; + + case SelectionNotify: + // notification of selection transferred. we shouldn't + // get this here because we handle them in the selection + // retrieval methods. we'll just delete the property + // with the data (satisfying the usual ICCCM protocol). + if (xevent->xselection.property != None) { + CLock lock(&m_mutex); + XDeleteProperty(m_display, + xevent->xselection.requestor, + xevent->xselection.property); + } + return true; + + case SelectionRequest: + { + // somebody is asking for clipboard data + ClipboardID id = getClipboardID( + xevent->xselectionrequest.selection); + if (id != kClipboardEnd) { + CLock lock(&m_mutex); + m_clipboard[id]->addRequest( + xevent->xselectionrequest.owner, + xevent->xselectionrequest.requestor, + xevent->xselectionrequest.target, + xevent->xselectionrequest.time, + xevent->xselectionrequest.property); + return true; + } + } + break; + + case PropertyNotify: + // property delete may be part of a selection conversion + if (xevent->xproperty.state == PropertyDelete) { + processClipboardRequest(xevent->xproperty.window, + xevent->xproperty.time, + xevent->xproperty.atom); + return true; + } + break; + + case ClientMessage: + if (xevent->xclient.message_type == m_atomScreensaver && + xevent->xclient.format == 32) { + // screen saver activation/deactivation event + m_eventHandler->onScreensaver(xevent->xclient.data.l[0] != 0); + return true; + } + break; + + case DestroyNotify: + // looks like one of the windows that requested a clipboard + // transfer has gone bye-bye. + destroyClipboardRequest(xevent->xdestroywindow.window); + + // we don't know if the event was handled or not so continue + break; + } + + // let screen saver have a go + { + CLock lock(&m_mutex); + m_screensaver->onPreDispatch(xevent); + } + + return m_eventHandler->onPreDispatch(event); +} + +void +CXWindowsScreen::createBlankCursor() +{ + // this seems just a bit more complicated than really necessary + + // get the closet cursor size to 1x1 + unsigned int w, h; + XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); + + // make bitmap data for cursor of closet size. since the cursor + // is blank we can use the same bitmap for shape and mask: all + // zeros. + const int size = ((w + 7) >> 3) * h; + char* data = new char[size]; + memset(data, 0, size); + + // make bitmap + Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h); + + // need an arbitrary color for the cursor + XColor color; + color.pixel = 0; + color.red = color.green = color.blue = 0; + color.flags = DoRed | DoGreen | DoBlue; + + // make cursor from bitmap + m_cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, + &color, &color, 0, 0); + + // don't need bitmap or the data anymore + delete[] data; + XFreePixmap(m_display, bitmap); +} + +bool +CXWindowsScreen::processTimers() +{ + bool oneShot = false; + std::vector jobs; + { + CLock lock(&m_timersMutex); + + // get current time + const double time = m_time.getTime(); + + // done if no timers have expired + if ((m_oneShotTimer == NULL || *m_oneShotTimer > time) && + (m_timers.empty() || m_timers.top() > time)) { + return false; + } + + // handle one shot timers + if (m_oneShotTimer != NULL) { + *m_oneShotTimer -= time; + if (*m_oneShotTimer <= 0.0) { + delete m_oneShotTimer; + m_oneShotTimer = NULL; + oneShot = true; + } + } + + // subtract current time from all timers. note that this won't + // change the order of elements in the priority queue (except + // for floating point round off which we'll ignore). + for (CTimerPriorityQueue::iterator index = m_timers.begin(); + index != m_timers.end(); ++index) { + (*index) -= time; + } + + // process all timers at or below zero, saving the jobs + if (!m_timers.empty()) { + while (m_timers.top() <= 0.0) { + CTimer timer = m_timers.top(); + jobs.push_back(timer.getJob()); + timer.reset(); + m_timers.pop(); + m_timers.push(timer); + } + } + + // reset the clock + m_time.reset(); + } + + // now notify of the one shot timers + if (oneShot) { + m_mutex.unlock(); + m_eventHandler->onOneShotTimerExpired(0); + m_mutex.lock(); + } + + // now run the jobs. note that if one of these jobs removes + // a timer later in the jobs list and deletes that job pointer + // then this will crash when it tries to run that job. + for (std::vector::iterator index = jobs.begin(); + index != jobs.end(); ++index) { + (*index)->run(); + } +} + +ClipboardID +CXWindowsScreen::getClipboardID(Atom selection) const +{ + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_clipboard[id] != NULL && + m_clipboard[id]->getSelection() == selection) { + return id; + } + } + return kClipboardEnd; +} + +void +CXWindowsScreen::processClipboardRequest(Window requestor, + Time time, Atom property) +{ + CLock lock(&m_mutex); + + // check every clipboard until one returns success + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_clipboard[id] != NULL && + m_clipboard[id]->processRequest(requestor, time, property)) { + break; + } + } +} + +void +CXWindowsScreen::destroyClipboardRequest(Window requestor) +{ + CLock lock(&m_mutex); + + // check every clipboard until one returns success + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_clipboard[id] != NULL && + m_clipboard[id]->destroyRequest(requestor)) { + break; + } + } +} + +int +CXWindowsScreen::ioErrorHandler(Display*) +{ + // the display has disconnected, probably because X is shutting + // down. X forces us to exit at this point. that's arguably + // a flaw in X but, realistically, it's difficult to gracefully + // handle not having a Display* anymore. we'll simply log the + // error, notify the subclass (which must not use the display + // so we set it to NULL), and exit. + LOG((CLOG_WARN "X display has unexpectedly disconnected")); + s_screen->m_display = NULL; + s_screen->m_receiver->onError(); + LOG((CLOG_CRIT "quiting due to X display disconnection")); + exit(17); +} + + +// +// CDisplayLock +// + +CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : + m_mutex(&screen->m_mutex), + m_display(screen->m_display) +{ + // note -- it's permitted for m_display to be NULL. that might + // happen if we couldn't connect to the display or if the + // display unexpectedly disconnected. the caller is expected + // to check for NULL as necessary. + + m_mutex->lock(); +} + +CDisplayLock::~CDisplayLock() +{ + m_mutex->unlock(); +} + +CDisplayLock::operator Display*() const +{ + return m_display; +} diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h new file mode 100644 index 0000000..ce37a00 --- /dev/null +++ b/lib/platform/CXWindowsScreen.h @@ -0,0 +1,311 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSSCREEN_H +#define CXWINDOWSSCREEN_H + +#include "IScreen.h" +#include "CMutex.h" +#include "CStopwatch.h" +#include "stdvector.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif +#include +#include + +class IJob; +class IScreenEventHandler; +class IScreenReceiver; +class CXWindowsClipboard; +class CXWindowsScreenSaver; + +/*! +\class CEvent +\brief User event data +An architecture dependent type holding user event data. +*/ +// X11 event +class CEvent { +public: + XEvent m_event; + SInt32 m_result; +}; + +//! Implementation of IScreen for X11 +class CXWindowsScreen : public IScreen { +public: + CXWindowsScreen(IScreenReceiver*, IScreenEventHandler*); + virtual ~CXWindowsScreen(); + + //! @name manipulators + //@{ + + //! Add timer + /*! + Add a job to invoke every timeout seconds. The job is + called with the display locked. If a job timeout expires twice + or more before the job can be called then the job is called + just once. The caller retains ownership of the job. + */ + void addTimer(IJob*, double timeout); + + //! Remove timer + /*! + Remove a job. The caller retains ownership of the job. + */ + void removeTimer(IJob*); + + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer (which will be passed to the receiver's + \c onTimerExpired()). + */ + UInt32 addOneShotTimer(double timeout); + + //! Set window + /*! + Set the window (created by the subclass). This performs some + initialization and saves the window in case it's needed later. + */ + void setWindow(Window); + + //@} + //! @name accessors + //@{ + + //! Get window + /*! + Returns the root window of the screen. + */ + Window getRoot() const; + + //! Get transparent cursor + /*! + Returns a cursor that is transparent everywhere. + */ + Cursor getBlankCursor() const; + + //@} + + // IScreen overrides + void open(); + void mainLoop(); + void exitMainLoop(); + void close(); + bool setClipboard(ClipboardID, const IClipboard*); + void checkClipboards(); + void openScreensaver(bool notify); + void closeScreensaver(); + void screensaver(bool activate); + void syncDesktop(); + bool getClipboard(ClipboardID, IClipboard*) const; + void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; + void getCursorPos(SInt32&, SInt32&) const; + void getCursorCenter(SInt32&, SInt32&) const; + +private: + // update screen size cache + void updateScreenShape(); + + // process events before dispatching to receiver + bool onPreDispatch(CEvent* event); + + // create the transparent cursor + void createBlankCursor(); + + // remove a timer without locking + void removeTimerNoLock(IJob*); + + // process timers + bool processTimers(); + + // determine the clipboard from the X selection. returns + // kClipboardEnd if no such clipboard. + ClipboardID getClipboardID(Atom selection) const; + + // continue processing a selection request + void processClipboardRequest(Window window, + Time time, Atom property); + + // terminate a selection request + void destroyClipboardRequest(Window window); + + // X I/O error handler + static int ioErrorHandler(Display*); + +private: + // a priority queue will direct access to the elements + template , + class Compare = std::greater > + class CPriorityQueue { + public: + typedef typename Container::value_type value_type; + typedef typename Container::size_type size_type; + typedef typename Container::iterator iterator; + typedef Container container_type; + + CPriorityQueue() { } + CPriorityQueue(Container& swappedIn); + ~CPriorityQueue() { } + + // manipulators + + void push(const value_type& v) + { + c.push_back(v); + std::push_heap(c.begin(), c.end(), comp); + } + + void pop() + { + std::pop_heap(c.begin(), c.end(), comp); + c.pop_back(); + } + + iterator begin() + { + return c.begin(); + } + + iterator end() + { + return c.end(); + } + + void swap(CPriorityQueue& q) + { + c.swap(q.c); + } + + void swap(Container& c2) + { + c.swap(c2); + std::make_heap(c.begin(), c.end(), comp); + } + + // accessors + + bool empty() const + { + return c.empty(); + } + + size_type size() const + { + return c.size(); + } + + const value_type& + top() const + { + return c.front(); + } + + private: + Container c; + Compare comp; + }; + + // a timer priority queue element + class CTimer { + public: + CTimer(IJob* job, double startTime, double resetTime); + ~CTimer(); + + // manipulators + + void run(); + + void reset(); + + CTimer& operator-=(double); + + // accessors + + IJob* getJob() const + { + return m_job; + } + + operator double() const; + + bool operator<(const CTimer&) const; + + private: + IJob* m_job; + double m_timeout; + double m_time; + double m_startTime; + }; + +private: + friend class CDisplayLock; + + typedef CPriorityQueue CTimerPriorityQueue; + + // X is not thread safe + CMutex m_mutex; + + Display* m_display; + Window m_root; + bool m_stop; + + IScreenReceiver* m_receiver; + IScreenEventHandler* m_eventHandler; + Window m_window; + + SInt32 m_x, m_y; + SInt32 m_w, m_h; + SInt32 m_xCenter, m_yCenter; + + // clipboards + CXWindowsClipboard* m_clipboard[kClipboardEnd]; + + // the transparent cursor + Cursor m_cursor; + + // screen saver stuff + CXWindowsScreenSaver* m_screensaver; + bool m_screensaverNotify; + Atom m_atomScreensaver; + + // timers, the stopwatch used to time, and a mutex for the timers + CTimerPriorityQueue m_timers; + CStopwatch m_time; + CMutex m_timersMutex; + CTimer* m_oneShotTimer; + + // pointer to (singleton) screen. this is only needed by + // ioErrorHandler(). + static CXWindowsScreen* s_screen; +}; + +//! Convenience object to lock/unlock a CXWindowsScreen +class CDisplayLock { +public: + CDisplayLock(const CXWindowsScreen*); + ~CDisplayLock(); + + operator Display*() const; + +private: + const CMutex* m_mutex; + Display* m_display; +}; + +#endif diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp new file mode 100644 index 0000000..c652c3d --- /dev/null +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -0,0 +1,482 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsScreenSaver.h" +#include "CXWindowsScreen.h" +#include "CXWindowsUtil.h" +#include "CLog.h" +#include "TMethodJob.h" +#include +#if defined(HAVE_X11_EXTENSIONS_XTEST_H) +# include +#else +# error The XTest extension is required to build synergy +#endif + +// +// CXWindowsScreenSaver +// + +CXWindowsScreenSaver::CXWindowsScreenSaver( + CXWindowsScreen* screen, Display* display) : + m_screen(screen), + m_display(display), + m_notify(None), + m_xscreensaver(None), + m_xscreensaverActive(false), + m_disabled(false), + m_suppressDisable(false), + m_disableJobInstalled(false) +{ + // screen saver disable callback + m_disableJob = new TMethodJob(this, + &CXWindowsScreenSaver::disableCallback); + + // get atoms + m_atomScreenSaver = XInternAtom(m_display, + "SCREENSAVER", False); + m_atomScreenSaverVersion = XInternAtom(m_display, + "_SCREENSAVER_VERSION", False); + m_atomScreenSaverActivate = XInternAtom(m_display, + "ACTIVATE", False); + m_atomScreenSaverDeactivate = XInternAtom(m_display, + "DEACTIVATE", False); + m_atomSynergyScreenSaver = XInternAtom(m_display, + "SYNERGY_SCREENSAVER", False); + + // create dummy window to receive xscreensaver responses. this + // shouldn't be necessary (we should be able to send responses + // to None) but it doesn't hurt. + XSetWindowAttributes attr; + attr.event_mask = 0;//PropertyChangeMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + m_xscreensaverSink = XCreateWindow(m_display, + DefaultRootWindow(m_display), + 0, 0, 1, 1, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect, + &attr); + LOG((CLOG_DEBUG "xscreensaver sink window is 0x%08x", m_xscreensaverSink)); + + // watch top-level windows for changes + { + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + Window root = DefaultRootWindow(m_display); + XWindowAttributes attr; + XGetWindowAttributes(m_display, root, &attr); + m_rootEventMask = attr.your_event_mask; + XSelectInput(m_display, root, m_rootEventMask | SubstructureNotifyMask); + if (error) { + LOG((CLOG_DEBUG "didn't set root event mask")); + m_rootEventMask = 0; + } + } + + // get the xscreensaver window, if any + if (!findXScreenSaver()) { + setXScreenSaver(None); + } + + // get the built-in settings + XGetScreenSaver(m_display, &m_timeout, &m_interval, + &m_preferBlanking, &m_allowExposures); +} + +CXWindowsScreenSaver::~CXWindowsScreenSaver() +{ + // clear watch list + clearWatchForXScreenSaver(); + + // stop watching root for events + CXWindowsUtil::CErrorLock lock(m_display); + Window root = DefaultRootWindow(m_display); + XSelectInput(m_display, root, m_rootEventMask); + + // destroy dummy sink window + XDestroyWindow(m_display, m_xscreensaverSink); + + // done with disable job + m_screen->removeTimer(m_disableJob); + delete m_disableJob; +} + +bool +CXWindowsScreenSaver::onPreDispatch(const XEvent* xevent) +{ + switch (xevent->type) { + case CreateNotify: + if (m_xscreensaver == None) { + if (isXScreenSaver(xevent->xcreatewindow.window)) { + // found the xscreensaver + setXScreenSaver(xevent->xcreatewindow.window); + } + else { + // another window to watch. to detect the xscreensaver + // window we look for a property but that property may + // not yet exist by the time we get this event so we + // have to watch the window for property changes. + // this would be so much easier if xscreensaver did the + // smart thing and stored its window in a property on + // the root window. + addWatchXScreenSaver(xevent->xcreatewindow.window); + } + } + break; + + case DestroyNotify: + if (xevent->xdestroywindow.window == m_xscreensaver) { + // xscreensaver is gone + LOG((CLOG_DEBUG "xscreensaver died")); + setXScreenSaver(None); + return true; + } + break; + + case PropertyNotify: + if (xevent->xproperty.state == PropertyNewValue) { + if (isXScreenSaver(xevent->xproperty.window)) { + // found the xscreensaver + setXScreenSaver(xevent->xcreatewindow.window); + } + } + break; + + case MapNotify: + if (xevent->xmap.window == m_xscreensaver) { + // xscreensaver has activated + setXScreenSaverActive(true); + return true; + } + break; + + case UnmapNotify: + if (xevent->xunmap.window == m_xscreensaver) { + // xscreensaver has deactivated + setXScreenSaverActive(false); + return true; + } + break; + } + + return false; +} + +void +CXWindowsScreenSaver::setNotify(Window notify) +{ + m_notify = notify; +} + +void +CXWindowsScreenSaver::enable() +{ + // for xscreensaver + m_disabled = false; + updateDisableJob(); + + // for built-in X screen saver + XSetScreenSaver(m_display, m_timeout, m_interval, + m_preferBlanking, m_allowExposures); +} + +void +CXWindowsScreenSaver::disable() +{ + // for xscreensaver + m_disabled = true; + updateDisableJob(); + + // use built-in X screen saver + XGetScreenSaver(m_display, &m_timeout, &m_interval, + &m_preferBlanking, &m_allowExposures); + XSetScreenSaver(m_display, 0, m_interval, + m_preferBlanking, m_allowExposures); + // FIXME -- now deactivate? +} + +void +CXWindowsScreenSaver::activate() +{ + // remove disable job timer + m_suppressDisable = true; + updateDisableJob(); + + // try xscreensaver + findXScreenSaver(); + if (m_xscreensaver != None) { + sendXScreenSaverCommand(m_atomScreenSaverActivate); + return; + } + + // use built-in X screen saver + XForceScreenSaver(m_display, ScreenSaverActive); +} + +void +CXWindowsScreenSaver::deactivate() +{ + // reinstall disable job timer + m_suppressDisable = false; + updateDisableJob(); + + // try xscreensaver + findXScreenSaver(); + if (m_xscreensaver != None) { + sendXScreenSaverCommand(m_atomScreenSaverDeactivate); + return; + } + + // use built-in X screen saver + XForceScreenSaver(m_display, ScreenSaverReset); +} + +bool +CXWindowsScreenSaver::isActive() const +{ + // check xscreensaver + if (m_xscreensaver != None) { + return m_xscreensaverActive; + } + + // can't check built-in X screen saver activity + return false; +} + +void +CXWindowsScreenSaver::sendNotify(bool activated) +{ + if (m_notify != None) { + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.display = m_display; + event.xclient.window = m_notify; + event.xclient.message_type = m_atomSynergyScreenSaver; + event.xclient.format = 32; + event.xclient.data.l[0] = activated ? 1 : 0; + event.xclient.data.l[1] = 0; + event.xclient.data.l[2] = 0; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + CXWindowsUtil::CErrorLock lock(m_display); + XSendEvent(m_display, m_notify, False, 0, &event); + } +} + +bool +CXWindowsScreenSaver::findXScreenSaver() +{ + // do nothing if we've already got the xscreensaver window + if (m_xscreensaver == None) { + // find top-level window xscreensaver window + Window root = DefaultRootWindow(m_display); + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) { + if (isXScreenSaver(cw[i])) { + setXScreenSaver(cw[i]); + break; + } + } + XFree(cw); + } + } + + return (m_xscreensaver != None); +} + +void +CXWindowsScreenSaver::setXScreenSaver(Window window) +{ + LOG((CLOG_DEBUG "xscreensaver window: 0x%08x", window)); + + // save window + m_xscreensaver = window; + + if (m_xscreensaver != None) { + // clear old watch list + clearWatchForXScreenSaver(); + + // see if xscreensaver is active + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + XWindowAttributes attr; + XGetWindowAttributes(m_display, m_xscreensaver, &attr); + setXScreenSaverActive(!error && attr.map_state != IsUnmapped); + } + else { + // screen saver can't be active if it doesn't exist + setXScreenSaverActive(false); + + // start watching for xscreensaver + watchForXScreenSaver(); + } +} + +bool +CXWindowsScreenSaver::isXScreenSaver(Window w) const +{ + // check for m_atomScreenSaverVersion string property + Atom type; + return (CXWindowsUtil::getWindowProperty(m_display, w, + m_atomScreenSaverVersion, + NULL, &type, NULL, False) && + type == XA_STRING); +} + +void +CXWindowsScreenSaver::setXScreenSaverActive(bool activated) +{ + if (m_xscreensaverActive != activated) { + LOG((CLOG_DEBUG "xscreensaver %s on window 0x%08x", activated ? "activated" : "deactivated", m_xscreensaver)); + m_xscreensaverActive = activated; + + // if screen saver was activated forcefully (i.e. against + // our will) then just accept it. don't try to keep it + // from activating since that'll just pop up the password + // dialog if locking is enabled. + m_suppressDisable = activated; + updateDisableJob(); + + sendNotify(activated); + } +} + +void +CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2) +{ + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.display = m_display; + event.xclient.window = m_xscreensaverSink; + event.xclient.message_type = m_atomScreenSaver; + event.xclient.format = 32; + event.xclient.data.l[0] = static_cast(cmd); + event.xclient.data.l[1] = arg1; + event.xclient.data.l[2] = arg2; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + LOG((CLOG_DEBUG "send xscreensaver command: %d %d %d", (long)cmd, arg1, arg2)); + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + XSendEvent(m_display, m_xscreensaver, False, 0, &event); + if (error) { + findXScreenSaver(); + } +} + +void +CXWindowsScreenSaver::watchForXScreenSaver() +{ + // clear old watch list + clearWatchForXScreenSaver(); + + // add every child of the root to the list of windows to watch + Window root = DefaultRootWindow(m_display); + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) { + addWatchXScreenSaver(cw[i]); + } + XFree(cw); + } + + // now check for xscreensaver window in case it set the property + // before we could request property change events. + if (findXScreenSaver()) { + // found it so clear out our watch list + clearWatchForXScreenSaver(); + } +} + +void +CXWindowsScreenSaver::clearWatchForXScreenSaver() +{ + // stop watching all windows + CXWindowsUtil::CErrorLock lock(m_display); + for (CWatchList::iterator index = m_watchWindows.begin(); + index != m_watchWindows.end(); ++index) { + XSelectInput(m_display, index->first, index->second); + } + m_watchWindows.clear(); +} + +void +CXWindowsScreenSaver::addWatchXScreenSaver(Window window) +{ + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + + // get window attributes + XWindowAttributes attr; + XGetWindowAttributes(m_display, window, &attr); + + // if successful and window uses override_redirect (like xscreensaver + // does) then watch it for property changes. + if (!error && attr.override_redirect == True) { + XSelectInput(m_display, window, + attr.your_event_mask | PropertyChangeMask); + if (!error) { + // if successful then add the window to our list + m_watchWindows.insert(std::make_pair(window, attr.your_event_mask)); + } + } +} + +void +CXWindowsScreenSaver::updateDisableJob() +{ + assert(m_disableJob != NULL); + + if (m_disabled && !m_suppressDisable && !m_disableJobInstalled) { + // 5 seconds should be plenty often to suppress the screen saver + m_disableJobInstalled = true; + m_screen->addTimer(m_disableJob, 5.0); + } + else if ((!m_disabled || m_suppressDisable) && m_disableJobInstalled) { + m_disableJobInstalled = false; + m_screen->removeTimer(m_disableJob); + } +} + +void +CXWindowsScreenSaver::disableCallback(void*) +{ + // send fake mouse motion directly to xscreensaver + if (m_xscreensaver != None) { + XEvent event; + event.xmotion.type = MotionNotify; + event.xmotion.display = m_display; + event.xmotion.window = m_xscreensaver; + event.xmotion.root = DefaultRootWindow(m_display); + event.xmotion.subwindow = None; + event.xmotion.time = CurrentTime; + event.xmotion.x = 0; + event.xmotion.y = 0; + event.xmotion.x_root = 0; + event.xmotion.y_root = 0; + event.xmotion.state = 0; + event.xmotion.is_hint = NotifyNormal; + event.xmotion.same_screen = True; + + CXWindowsUtil::CErrorLock lock(m_display); + XSendEvent(m_display, m_xscreensaver, False, 0, &event); + } +} diff --git a/lib/platform/CXWindowsScreenSaver.h b/lib/platform/CXWindowsScreenSaver.h new file mode 100644 index 0000000..3285a1b --- /dev/null +++ b/lib/platform/CXWindowsScreenSaver.h @@ -0,0 +1,161 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSSCREENSAVER_H +#define CXWINDOWSSCREENSAVER_H + +#include "IScreenSaver.h" +#include "stdmap.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +class IJob; +class CXWindowsScreen; + +//! X11 screen saver implementation +class CXWindowsScreenSaver : public IScreenSaver { +public: + // note -- the caller must ensure that Display* passed to c'tor isn't + // being used in another call to Xlib when calling any method on this + // object (including during the c'tor and d'tor). + CXWindowsScreenSaver(CXWindowsScreen*, Display*); + virtual ~CXWindowsScreenSaver(); + + //! @name manipulators + //@{ + + //! Event filtering + /*! + Called for each event before event translation and dispatch. Return + true to skip translation and dispatch. Subclasses should call the + superclass's version first and return true if it returns true. + */ + bool onPreDispatch(const XEvent*); + + //! Set notify target + /*! + Tells this object to send a ClientMessage to the given window + when the screen saver activates or deactivates. Only one window + can be notified at a time. The message type is the "SCREENSAVER" + atom, the format is 32, and the data.l[0] member is non-zero + if activated, zero if deactivated. Pass None to disable + notification. + */ + void setNotify(Window); + + //@} + + // IScreenSaver overrides + virtual void enable(); + virtual void disable(); + virtual void activate(); + virtual void deactivate(); + virtual bool isActive() const; + +private: + // send a notification + void sendNotify(bool activated); + + // find and set the running xscreensaver's window. returns true iff + // found. + bool findXScreenSaver(); + + // set the xscreensaver's window, updating the activation state flag + void setXScreenSaver(Window); + + // returns true if the window appears to be the xscreensaver window + bool isXScreenSaver(Window) const; + + // set xscreensaver's activation state flag. sends notification + // if the state has changed. + void setXScreenSaverActive(bool activated); + + // send a command to xscreensaver + void sendXScreenSaverCommand(Atom, long = 0, long = 0); + + // watch all windows that could potentially be the xscreensaver for + // the events that will confirm it. + void watchForXScreenSaver(); + + // stop watching all watched windows + void clearWatchForXScreenSaver(); + + // add window to the watch list + void addWatchXScreenSaver(Window window); + + // install/uninstall the job used to suppress the screensaver + void updateDisableJob(); + + // called periodically to prevent the screen saver from starting + void disableCallback(void*); + +private: + typedef std::map CWatchList; + + // the event loop object + CXWindowsScreen* m_screen; + + // the X display + Display* m_display; + + // old event mask on root window + long m_rootEventMask; + + // window to notify on screen saver activation/deactivation + Window m_notify; + + // xscreensaver's window + Window m_xscreensaver; + + // xscreensaver activation state + bool m_xscreensaverActive; + + // dummy window to receive xscreensaver repsonses + Window m_xscreensaverSink; + + // potential xscreensaver windows being watched + CWatchList m_watchWindows; + + // atoms used to communicate with xscreensaver's window + Atom m_atomScreenSaver; + Atom m_atomScreenSaverVersion; + Atom m_atomScreenSaverActivate; + Atom m_atomScreenSaverDeactivate; + Atom m_atomSynergyScreenSaver; + + // built-in screen saver settings + int m_timeout; + int m_interval; + int m_preferBlanking; + int m_allowExposures; + + // true iff the client wants the screen saver suppressed + bool m_disabled; + + // true iff we're ignoring m_disabled. this is true, for example, + // when the client has called activate() and so presumably wants + // to activate the screen saver even if disabled. + bool m_suppressDisable; + + // true iff the disabled job timer is installed + bool m_disableJobInstalled; + + // the job used to invoke disableCallback + IJob* m_disableJob; +}; + +#endif diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp new file mode 100644 index 0000000..b69369e --- /dev/null +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -0,0 +1,2070 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsSecondaryScreen.h" +#include "CXWindowsClipboard.h" +#include "CXWindowsScreen.h" +#include "CXWindowsScreenSaver.h" +#include "CXWindowsUtil.h" +#include "IScreenReceiver.h" +#include "XScreen.h" +#include "CThread.h" +#include "CLog.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +# include +# define XK_MISCELLANY +# define XK_XKB_KEYS +# define XK_LATIN1 +# define XK_LATIN2 +# define XK_LATIN3 +# define XK_LATIN4 +# define XK_LATIN8 +# define XK_LATIN9 +# include +# if defined(HAVE_X11_EXTENSIONS_XTEST_H) +# include +# else +# error The XTest extension is required to build synergy +# endif +# if HAVE_X11_EXTENSIONS_XINERAMA_H + // Xinerama.h may lack extern "C" for inclusion by C++ + extern "C" { +# include + } +# endif +# if defined(HAVE_X11_XF86KEYSYM_H) +# include +# endif +# if !defined(XF86XK_Launch0) +# define XF86XK_Launch0 0x1008FF40 +# endif +# if !defined(XF86XK_Launch1) +# define XF86XK_Launch1 0x1008FF41 +# endif +#endif + +// +// utility functions +// + +inline +static +unsigned int getBits(unsigned int src, unsigned int mask) +{ + return src & mask; +} + +inline +static +unsigned int setBits(unsigned int src, unsigned int mask) +{ + return src | mask; +} + +inline +static +unsigned int clearBits(unsigned int src, unsigned int mask) +{ + return src & ~mask; +} + +inline +static +unsigned int flipBits(unsigned int src, unsigned int mask) +{ + return src ^ mask; +} + +inline +static +unsigned int assignBits(unsigned int src, + unsigned int mask, unsigned int value) +{ + return setBits(clearBits(src, mask), clearBits(value, ~mask)); +} + + +// +// CXWindowsSecondaryScreen +// + +CXWindowsSecondaryScreen::KeySymsMap + CXWindowsSecondaryScreen::s_decomposedKeySyms; + +CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) : + CSecondaryScreen(), + m_window(None), + m_xtestIsXineramaUnaware(true) +{ + m_screen = new CXWindowsScreen(receiver, this); + + // make sure decomposed keysym table is prepared + getDecomposedKeySymTable(); +} + +CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() +{ + assert(m_window == None); + delete m_screen; +} + +void +CXWindowsSecondaryScreen::keyDown(KeyID key, + KeyModifierMask mask, KeyButton button) +{ + // check for ctrl+alt+del emulation + if (key == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + LOG((CLOG_DEBUG "ctrl+alt+del emulation")); + // just pass the key through + } + + // get the sequence of keys to simulate key press and the final + // modifier state. + Keystrokes keys; + KeyCode keycode; + m_mask = mapKey(keys, keycode, key, mask, kPress); + if (keys.empty()) { + // do nothing if there are no associated keys (i.e. lookup failed) + return; + } + + // generate key events + doKeystrokes(keys, 1); + + // do not record button down if button is 0 (invalid) + if (button != 0) { + // note that key is now down + m_serverKeyMap[button] = keycode; + m_keys[keycode] = true; + m_fakeKeys[keycode] = true; + } +} + +void +CXWindowsSecondaryScreen::keyRepeat(KeyID key, + KeyModifierMask mask, SInt32 count, KeyButton button) +{ + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + + // get the sequence of keys to simulate key repeat and the final + // modifier state. + Keystrokes keys; + KeyCode keycode; + m_mask = mapKey(keys, keycode, key, mask, kRepeat); + if (keys.empty()) { + return; + } + + // if this keycode shouldn't auto-repeat then ignore + if ((m_keyControl.auto_repeats[keycode >> 3] & (1 << (keycode & 7))) == 0) { + return; + } + + // if the keycode for the auto-repeat is not the same as for the + // initial press then mark the initial key as released and the new + // key as pressed. this can happen when we auto-repeat after a + // dead key. for example, a dead accent followed by 'a' will + // generate an 'a with accent' followed by a repeating 'a'. the + // keycodes for the two keysyms might be different. + if (keycode != index->second) { + // replace key up with previous keycode but leave key down + // alone so it uses the new keycode and store that keycode + // in the server key map. the key up is the first keystroke + // with the keycode returned by mapKey(). + for (Keystrokes::iterator index2 = keys.begin(); + index2 != keys.end(); ++index2) { + if (index2->m_keycode == keycode) { + index2->m_keycode = index->second; + break; + } + } + + // note that old key is now up + m_keys[index->second] = false; + m_fakeKeys[index->second] = false; + + // map server key to new key + index->second = keycode; + + // note that new key is now down + m_keys[index->second] = true; + m_fakeKeys[index->second] = true; + } + + // generate key events + doKeystrokes(keys, count); +} + +void +CXWindowsSecondaryScreen::keyUp(KeyID key, + KeyModifierMask mask, KeyButton button) +{ + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + KeyCode keycode = index->second; + + // check for ctrl+alt+del emulation + if (key == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + LOG((CLOG_DEBUG "ctrl+alt+del emulation")); + // just pass the key through + } + + // get the sequence of keys to simulate key release and the final + // modifier state. + Keystrokes keys; + if (!((key == kKeyCapsLock && m_capsLockHalfDuplex) || + (key == kKeyNumLock && m_numLockHalfDuplex))) { + m_mask = mapKeyRelease(keys, keycode); + } + + // generate key events + doKeystrokes(keys, 1); + + // note that key is now up + m_serverKeyMap.erase(index); + m_keys[keycode] = false; + m_fakeKeys[keycode] = false; +} + +void +CXWindowsSecondaryScreen::flush(Display* display) const +{ + XFlush(display); +} + +void +CXWindowsSecondaryScreen::mouseDown(ButtonID button) +{ + const unsigned int xButton = mapButton(button); + if (xButton != 0) { + CDisplayLock display(m_screen); + XTestFakeButtonEvent(display, xButton, True, CurrentTime); + flush(display); + } +} + +void +CXWindowsSecondaryScreen::mouseUp(ButtonID button) +{ + const unsigned int xButton = mapButton(button); + if (xButton != 0) { + CDisplayLock display(m_screen); + XTestFakeButtonEvent(display, xButton, False, CurrentTime); + flush(display); + } +} + +void +CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) +{ + warpCursor(x, y); +} + +void +CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) +{ + // choose button depending on rotation direction + const unsigned int xButton = mapButton(static_cast( + (delta >= 0) ? -1 : -2)); + if (xButton == 0) { + return; + } + + // now use absolute value of delta + if (delta < 0) { + delta = -delta; + } + + // send as many clicks as necessary + CDisplayLock display(m_screen); + for (; delta >= 120; delta -= 120) { + XTestFakeButtonEvent(display, xButton, True, CurrentTime); + XTestFakeButtonEvent(display, xButton, False, CurrentTime); + } + flush(display); +} + +void +CXWindowsSecondaryScreen::resetOptions() +{ + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; + m_xtestIsXineramaUnaware = true; + CSecondaryScreen::resetOptions(); +} + +void +CXWindowsSecondaryScreen::setOptions(const COptionsList& options) +{ + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionHalfDuplexCapsLock) { + m_capsLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); + } + else if (options[i] == kOptionHalfDuplexNumLock) { + m_numLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); + } + else if (options[i] == kOptionXTestXineramaUnaware) { + m_xtestIsXineramaUnaware = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "XTest is Xinerama unaware %s", m_xtestIsXineramaUnaware ? "true" : "false")); + } + } + CSecondaryScreen::setOptions(options); +} + +IScreen* +CXWindowsSecondaryScreen::getScreen() const +{ + return m_screen; +} + +void +CXWindowsSecondaryScreen::onScreensaver(bool) +{ + // ignore +} + +bool +CXWindowsSecondaryScreen::onPreDispatch(const CEvent*) +{ + return false; +} + +bool +CXWindowsSecondaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + XEvent& xevent = event->m_event; + + // handle event + switch (xevent.type) { + case MappingNotify: { + // keyboard mapping changed + CDisplayLock display(m_screen); + doUpdateKeys(display); + return true; + } + + case LeaveNotify: + // mouse moved out of hider window somehow. hide the window. + hideWindow(); + return true; + } +} + +void +CXWindowsSecondaryScreen::onOneShotTimerExpired(UInt32) +{ + // ignore +} + +SInt32 +CXWindowsSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + +void +CXWindowsSecondaryScreen::onPreMainLoop() +{ + assert(m_window != None); +} + +void +CXWindowsSecondaryScreen::onPreOpen() +{ + assert(m_window == None); +} + +void +CXWindowsSecondaryScreen::onPostOpen() +{ + assert(m_window != None); + + // get the keyboard control state + CDisplayLock display(m_screen); + XGetKeyboardControl(display, &m_keyControl); + + // check if xinerama is enabled and there is more than one screen + m_xinerama = false; +#if HAVE_X11_EXTENSIONS_XINERAMA_H + int eventBase, errorBase; + if (XineramaQueryExtension(display, &eventBase, &errorBase)) { + if (XineramaIsActive(display)) { + int numScreens; + XineramaScreenInfo* screens; + screens = XineramaQueryScreens(display, &numScreens); + if (screens != NULL) { + m_xinerama = (numScreens > 1); + XFree(screens); + } + } + } +#endif +} + +void +CXWindowsSecondaryScreen::onPreClose() +{ + if (m_keyControl.global_auto_repeat == AutoRepeatModeOn) { + CDisplayLock display(m_screen); + XAutoRepeatOn(display); + } +} + +void +CXWindowsSecondaryScreen::onPreEnter() +{ + assert(m_window != None); +} + +void +CXWindowsSecondaryScreen::onPostEnter() +{ + assert(m_window != None); + + // get the keyboard control state + CDisplayLock display(m_screen); + XGetKeyboardControl(display, &m_keyControl); + + // turn off auto-repeat. we do this so fake key press events don't + // cause the local server to generate their own auto-repeats of + // those keys. + XAutoRepeatOff(display); +} + +void +CXWindowsSecondaryScreen::onPreLeave() +{ + assert(m_window != None); + + // restore the previous keyboard auto-repeat state. if the user + // changed the auto-repeat configuration while on the client then + // that state is lost. that's because we can't get notified by + // the X server when the auto-repeat configuration is changed so + // we can't track the desired configuration. + if (m_keyControl.global_auto_repeat == AutoRepeatModeOn) { + CDisplayLock display(m_screen); + XAutoRepeatOn(display); + } +} + +void +CXWindowsSecondaryScreen::createWindow() +{ + { + CDisplayLock display(m_screen); + + // verify the availability of the XTest extension + int majorOpcode, firstEvent, firstError; + if (!XQueryExtension(display, XTestExtensionName, + &majorOpcode, &firstEvent, &firstError)) { + LOG((CLOG_ERR "XTEST extension not available")); + throw XScreenOpenFailure(); + } + + // cursor hider window attributes. this window is used to hide the + // cursor when it's not on the screen. the window is hidden as soon + // as the cursor enters the screen or the display's real cursor is + // moved. + XSetWindowAttributes attr; + attr.event_mask = LeaveWindowMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = m_screen->getBlankCursor(); + + // create the cursor hider window + m_window = XCreateWindow(display, m_screen->getRoot(), + 0, 0, 1, 1, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + if (m_window == None) { + throw XScreenOpenFailure(); + } + LOG((CLOG_DEBUG "window is 0x%08x", m_window)); + + // become impervious to server grabs + XTestGrabControl(display, True); + } + + // tell generic screen about the window + m_screen->setWindow(m_window); +} + +void +CXWindowsSecondaryScreen::destroyWindow() +{ + { + CDisplayLock display(m_screen); + if (display != NULL) { + // release keys that are still pressed + doReleaseKeys(display); + + // no longer impervious to server grabs + XTestGrabControl(display, False); + + // update + flush(display); + } + } + + // destroy window + if (m_window != None) { + m_screen->setWindow(None); + CDisplayLock display(m_screen); + if (display != NULL) { + XDestroyWindow(display, m_window); + } + m_window = None; + } +} + +void +CXWindowsSecondaryScreen::showWindow(SInt32 x, SInt32 y) +{ + { + CDisplayLock display(m_screen); + + // move hider window under the given position + XMoveWindow(display, m_window, x, y); + + // raise and show the hider window. take activation. + // FIXME -- take focus? + XMapRaised(display, m_window); + } + + // now warp the mouse. we warp after showing the window so we're + // guaranteed to get the mouse leave event and to prevent the + // keyboard focus from changing under point-to-focus policies. + warpCursor(x, y); +} + +void +CXWindowsSecondaryScreen::hideWindow() +{ + assert(m_window != None); + + CDisplayLock display(m_screen); + XUnmapWindow(display, m_window); +} + +void +CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + CDisplayLock display(m_screen); + Display* pDisplay = display; + + if (m_xinerama && m_xtestIsXineramaUnaware) { + XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y); + } + else { + XTestFakeMotionEvent(display, DefaultScreen(pDisplay), + x, y, CurrentTime); + } + flush(display); +} + +void +CXWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) +{ + CDisplayLock display(m_screen); + + // toggle modifiers that don't match the desired state + ModifierMask xMask = maskToX(mask); + if ((xMask & m_capsLockMask) != (m_mask & m_capsLockMask)) { + toggleKey(display, m_capsLockKeysym, m_capsLockMask); + } + if ((xMask & m_numLockMask) != (m_mask & m_numLockMask)) { + toggleKey(display, m_numLockKeysym, m_numLockMask); + } + if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) { + toggleKey(display, m_scrollLockKeysym, m_scrollLockMask); + } +} + +KeyModifierMask +CXWindowsSecondaryScreen::getToggleState() const +{ + KeyModifierMask mask = 0; + if ((m_mask & m_capsLockMask) != 0) { + mask |= KeyModifierCapsLock; + } + if ((m_mask & m_numLockMask) != 0) { + mask |= KeyModifierNumLock; + } + if ((m_mask & m_scrollLockMask) != 0) { + mask |= KeyModifierScrollLock; + } + return mask; +} + +unsigned int +CXWindowsSecondaryScreen::mapButton(ButtonID id) const +{ + // map button -1 to button 4 (+wheel) + if (id == static_cast(-1)) { + id = 4; + } + + // map button -2 to button 5 (-wheel) + else if (id == static_cast(-2)) { + id = 5; + } + + // map buttons 4, 5, etc. to 6, 7, etc. to make room for buttons + // 4 and 5 used to simulate the mouse wheel. + else if (id >= 4) { + id += 2; + } + + // check button is in legal range + if (id < 1 || id > m_buttons.size()) { + // out of range + return 0; + } + + // map button + return static_cast(m_buttons[id - 1]); +} + +CXWindowsSecondaryScreen::ModifierMask +CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, + KeyID id, KeyModifierMask mask, EKeyAction action) const +{ + // note -- must have display locked on entry + + // the system translates key events into characters depending + // on the modifier key state at the time of the event. to + // generate the right keysym we need to set the modifier key + // states appropriately. + // + // the mask passed by the caller is the desired mask. however, + // there may not be a keycode mapping to generate the desired + // keysym with that mask. we override the bits in the mask + // that cannot be accomodated. + + // ignore releases and repeats for half-duplex keys + const bool isHalfDuplex = ((id == kKeyCapsLock && m_capsLockHalfDuplex) || + (id == kKeyNumLock && m_numLockHalfDuplex)); + if (isHalfDuplex && action != kPress) { + return m_mask; + } + + // requested notes the modifiers requested by the server. + ModifierMask requested = maskToX(mask); + + // convert KeyID to a KeySym + KeySym keysym = keyIDToKeySym(id, requested); + if (keysym == NoSymbol) { + // unknown key + LOG((CLOG_DEBUG2 "no keysym for id 0x%08x", id)); + return m_mask; + } + + // get the mapping for this keysym + KeySymIndex keyIndex = m_keysymMap.find(keysym); + + // if the mapping isn't found and keysym is caps lock sensitive + // then convert the case of the keysym and try again. + if (keyIndex == m_keysymMap.end()) { + KeySym lKey, uKey; + XConvertCase(keysym, &lKey, &uKey); + if (lKey != uKey) { + if (lKey == keysym) { + keyIndex = m_keysymMap.find(uKey); + } + else { + keyIndex = m_keysymMap.find(lKey); + } + } + } + + if (keyIndex != m_keysymMap.end()) { + // the keysym is mapped to some keycode. if it's a modifier + // and that modifier is already in the desired state then + // ignore the request since there's nothing to do. never + // ignore a toggle modifier on press or release, though. + const KeyMapping& keyMapping = keyIndex->second; + const ModifierMask modifierBit = keyMapping.m_modifierMask; + if (modifierBit != 0) { + if (action == kRepeat) { + LOG((CLOG_DEBUG2 "ignore repeating modifier")); + return m_mask; + } + if (getBits(m_toggleModifierMask, modifierBit) == 0) { + if ((action == kPress && (m_mask & modifierBit) != 0) || + (action == kRelease && (m_mask & modifierBit) == 0)) { + LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", m_mask)); + return m_mask; + } + } + } + + // create the keystrokes for this keysym + ModifierMask mask; + if (!mapToKeystrokes(keys, keycode, mask, keyIndex, m_mask, action)) { + // failed to generate keystrokes + keys.clear(); + return m_mask; + } + else { + // success + LOG((CLOG_DEBUG2 "new mask: 0x%04x", mask)); + return mask; + } + } + + // we can't find the keysym mapped to any keycode. this doesn't + // necessarily mean we can't generate the keysym, though. if the + // keysym can be created by combining keysyms then we may still + // be okay. + KeySyms decomposition; + if (decomposeKeySym(keysym, decomposition)) { + LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms", keysym, decomposition.size())); + + // map each decomposed keysym to keystrokes. we want the mask + // and the keycode from the last keysym (which should be the + // only non-dead key). the dead keys are not sensitive to + // anything but shift and mode switch. + ModifierMask mask; + for (KeySyms::const_iterator i = decomposition.begin(); + i != decomposition.end();) { + // increment the iterator + KeySyms::const_iterator next = i; + ++next; + + // lookup the key + keysym = *i; + keyIndex = m_keysymMap.find(keysym); + if (keyIndex == m_keysymMap.end()) { + // missing a required keysym + LOG((CLOG_DEBUG2 "no keycode for decomposed keysym 0x%08x", keysym)); + keys.clear(); + return m_mask; + } + + // the keysym is mapped to some keycode + if (!mapToKeystrokes(keys, keycode, mask, + keyIndex, m_mask, action)) { + // failed to generate keystrokes + keys.clear(); + return m_mask; + } + + // on to the next keysym + i = next; + } + LOG((CLOG_DEBUG2 "new mask: 0x%04x", mask)); + return mask; + } + + LOG((CLOG_DEBUG2 "no keycode for keysym")); + return m_mask; +} + +CXWindowsSecondaryScreen::ModifierMask +CXWindowsSecondaryScreen::mapKeyRelease(Keystrokes& keys, KeyCode keycode) const +{ + // add key release + Keystroke keystroke; + keystroke.m_keycode = keycode; + keystroke.m_press = False; + keystroke.m_repeat = false; + keys.push_back(keystroke); + + // if this is a modifier keycode then update the current modifier mask + KeyCodeToModifierMap::const_iterator i = m_keycodeToModifier.find(keycode); + if (i != m_keycodeToModifier.end()) { + ModifierMask bit = (1 << i->second); + if (getBits(m_toggleModifierMask, bit) != 0) { + // toggle keys modify the state on release + return flipBits(m_mask, bit); + } + else { + // can't reset bit until all keys that set it are released. + // scan those keys to see if any (except keycode) are pressed. + KeyCodes::const_iterator j; + const KeyCodes& keycodes = m_modifierKeycodes[i->second]; + for (j = keycodes.begin(); j != keycodes.end(); ++j) { + KeyCode modKeycode = *j; + if (modKeycode != keycode && m_keys[modKeycode]) { + break; + } + } + if (j == keycodes.end()) { + return clearBits(m_mask, bit); + } + } + } + + return m_mask; +} + +unsigned int +CXWindowsSecondaryScreen::findBestKeyIndex(KeySymIndex keyIndex, + ModifierMask /*currentMask*/) const +{ + // there are up to 4 keycodes per keysym to choose from. the + // best choice is the one that requires the fewest adjustments + // to the modifier state. for example, the letter A normally + // requires shift + a. if shift isn't already down we'd have + // to synthesize a shift press before the a press. however, + // if A could also be created with some other keycode without + // shift then we'd prefer that when shift wasn't down. + // + // if the action is kRepeat or kRelease then we don't call this + // method since we just need to synthesize a key repeat/release + // on the same keycode that we pressed. + // XXX -- do this right + for (unsigned int i = 0; i < 4; ++i) { + if (keyIndex->second.m_keycode[i] != 0) { + return i; + } + } + + assert(0 && "no keycode found for keysym"); + return 0; +} + +bool +CXWindowsSecondaryScreen::isShiftInverted(KeySymIndex keyIndex, + ModifierMask currentMask) const +{ + // each keycode has up to 4 keysym associated with it, one each for: + // no modifiers, shift, mode switch, and shift and mode switch. if + // a keysym is modified by num lock and num lock is active then you + // get the shifted keysym when shift is not down and the unshifted + // keysym when it is. that is, num lock inverts the sense of the + // shift modifier when active. similarly for caps lock. this + // method returns true iff the sense of shift should be inverted + // for this key given a modifier state. + if (keyIndex->second.m_numLockSensitive) { + if (getBits(currentMask, m_numLockMask) != 0) { + return true; + } + } + + // if a keysym is num lock sensitive it is never caps lock + // sensitive, thus the else here. + else if (keyIndex->second.m_capsLockSensitive) { + if (getBits(currentMask, m_capsLockMask) != 0) { + return true; + } + } + + return false; +} + +CXWindowsSecondaryScreen::ModifierMask +CXWindowsSecondaryScreen::getModifierMask(KeySym keysym) const +{ + // find the keysym mapping. if it exists and there's a keycode + // for index 0 (the index we use for modifiers) then return the + // modifierMask, which might be 0. otherwise return 0. + KeySymIndex keyIndex = m_keysymMap.find(keysym); + if (keyIndex != m_keysymMap.end() && keyIndex->second.m_keycode[0] != 0) { + return keyIndex->second.m_modifierMask; + } + else { + return 0; + } +} + +bool +CXWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, + KeyCode& keycode, + ModifierMask& finalMask, + KeySymIndex keyIndex, + ModifierMask currentMask, + EKeyAction action) const +{ + // keyIndex must be valid + assert(keyIndex != m_keysymMap.end()); + + // get the keysym we're trying to generate and possible keycodes + const KeySym keysym = keyIndex->first; + const KeyMapping& mapping = keyIndex->second; + LOG((CLOG_DEBUG2 "keysym = 0x%08x", keysym)); + + // get the best keycode index for the keysym and modifiers. note + // that (bestIndex & 1) == 0 if the keycode is a shift modifier + // and (bestIndex & 2) == 0 if the keycode is a mode switch + // modifier. this is important later because we don't want + // adjustModifiers() to adjust a modifier if that's the key we're + // mapping. + unsigned int bestIndex = findBestKeyIndex(keyIndex, currentMask); + + // get the keycode + keycode = mapping.m_keycode[bestIndex]; + + // flip low bit of bestIndex if shift is inverted. if there's a + // keycode for this new index then use it. otherwise use the old + // keycode. you'd think we should fail if there isn't a keycode + // for the new index but some keymaps only include the upper case + // keysyms (notably those on Sun Solaris) so to handle the missing + // lower case keysyms we just use the old keycode. note that + // isShiftInverted() will always return false for a shift modifier. + if (isShiftInverted(keyIndex, currentMask)) { + LOG((CLOG_DEBUG2 "shift is inverted")); + bestIndex ^= 1; + if (mapping.m_keycode[bestIndex] != 0) { + keycode = mapping.m_keycode[bestIndex]; + } + } + LOG((CLOG_DEBUG2 "bestIndex = %d, keycode = %d", bestIndex, keycode)); + + // compute desired mask. the desired mask is the one that matches + // bestIndex, except if the key being synthesized is a shift key + // where we desire what we already have or if it's the mode switch + // key where we only desire to adjust shift. also, if the keycode + // is not sensitive to shift then don't adjust it, otherwise + // something like shift+home would become just home. similiarly + // for mode switch. + ModifierMask desiredMask = currentMask; + if (keyIndex->second.m_modifierMask != m_shiftMask) { + if (keyIndex->second.m_shiftSensitive[bestIndex]) { + if ((bestIndex & 1) != 0) { + desiredMask = setBits(desiredMask, m_shiftMask); + } + else { + desiredMask = clearBits(desiredMask, m_shiftMask); + } + } + if (keyIndex->second.m_modifierMask != m_modeSwitchMask) { + if (keyIndex->second.m_modeSwitchSensitive[bestIndex]) { + if ((bestIndex & 2) != 0) { + desiredMask = setBits(desiredMask, m_modeSwitchMask); + } + else { + desiredMask = clearBits(desiredMask, m_modeSwitchMask); + } + } + } + } + + // adjust the modifiers to match the desired modifiers + Keystrokes undo; + ModifierMask tmpMask = currentMask; + if (!adjustModifiers(keys, undo, tmpMask, desiredMask)) { + LOG((CLOG_DEBUG2 "failed to adjust modifiers")); + return false; + } + + // note if the press of a half-duplex key should be treated as a release + const bool isHalfDuplex = + ((keysym == m_capsLockKeysym && m_capsLockHalfDuplex) || + (keysym == m_numLockKeysym && m_numLockHalfDuplex)); + if (isHalfDuplex && getBits(currentMask, mapping.m_modifierMask) != 0) { + action = kRelease; + } + + // add the key event + Keystroke keystroke; + keystroke.m_keycode = keycode; + switch (action) { + case kPress: + keystroke.m_press = True; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRelease: + keystroke.m_press = False; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRepeat: + keystroke.m_press = False; + keystroke.m_repeat = true; + keys.push_back(keystroke); + keystroke.m_press = True; + keys.push_back(keystroke); + break; + } + + // put undo keystrokes at end of keystrokes in reverse order + while (!undo.empty()) { + keys.push_back(undo.back()); + undo.pop_back(); + } + + // if the key is a modifier key then compute the modifier map after + // this key is pressed or released. + finalMask = currentMask; + if (mapping.m_modifierMask != 0) { + // can't be repeating if we've gotten here + assert(action != kRepeat); + + // toggle keys modify the state on release. other keys set the + // bit on press and clear the bit on release. if half-duplex + // then toggle each time we get here. + if (getBits(m_toggleModifierMask, mapping.m_modifierMask) != 0) { + if (isHalfDuplex) { + finalMask = flipBits(finalMask, mapping.m_modifierMask); + } + } + else if (action == kPress) { + finalMask = setBits(finalMask, mapping.m_modifierMask); + } + } + + return true; +} + +bool +CXWindowsSecondaryScreen::adjustModifiers(Keystrokes& keys, + Keystrokes& undo, + ModifierMask& inOutMask, + ModifierMask desiredMask) const +{ + // get mode switch set correctly. do this before shift because + // mode switch may be sensitive to the shift modifier and will + // set/reset it as necessary. + const bool wantModeSwitch = ((desiredMask & m_modeSwitchMask) != 0); + const bool haveModeSwitch = ((inOutMask & m_modeSwitchMask) != 0); + if (wantModeSwitch != haveModeSwitch) { + LOG((CLOG_DEBUG2 "fix mode switch")); + + // adjust shift if necessary + KeySymIndex modeSwitchIndex = m_keysymMap.find(m_modeSwitchKeysym); + assert(modeSwitchIndex != m_keysymMap.end()); + if (modeSwitchIndex->second.m_shiftSensitive[0]) { + const bool wantShift = false; + const bool haveShift = ((inOutMask & m_shiftMask) != 0); + if (wantShift != haveShift) { + // add shift keystrokes + LOG((CLOG_DEBUG2 "fix shift for mode switch")); + if (!adjustModifier(keys, undo, m_shiftKeysym, wantShift)) { + return false; + } + inOutMask ^= m_shiftMask; + } + } + + // add mode switch keystrokes + if (!adjustModifier(keys, undo, m_modeSwitchKeysym, wantModeSwitch)) { + return false; + } + inOutMask ^= m_modeSwitchMask; + } + + // get shift set correctly + const bool wantShift = ((desiredMask & m_shiftMask) != 0); + const bool haveShift = ((inOutMask & m_shiftMask) != 0); + if (wantShift != haveShift) { + // add shift keystrokes + LOG((CLOG_DEBUG2 "fix shift")); + if (!adjustModifier(keys, undo, m_shiftKeysym, wantShift)) { + return false; + } + inOutMask ^= m_shiftMask; + } + + return true; +} + +bool +CXWindowsSecondaryScreen::adjustModifier(Keystrokes& keys, + Keystrokes& undo, KeySym keysym, bool desireActive) const +{ + // this method generates keystrokes to change a modifier into the + // desired state. under X11, we only expect to adjust the shift + // and mode switch states. other modifiers don't affect keysym + // generation, except num lock and caps lock and we don't change + // those but instead just invert the handling of the shift key. + // we don't check here if the modifier is already in the desired + // state; the caller should do that. + + // get the key mapping for keysym + KeySymIndex keyIndex = m_keysymMap.find(keysym); + if (keyIndex == m_keysymMap.end() || keyIndex->second.m_keycode[0] == 0) { + // no keycode for keysym or keycode is not a modifier + LOG((CLOG_DEBUG2 "no modifier for 0x%08x", keysym)); + return false; + } + + // this had better be a modifier + assert(keyIndex->second.m_modifierMask != 0); + + // we do not handle toggle modifiers here. they never need to be + // adjusted + assert((keyIndex->second.m_modifierMask & m_toggleModifierMask) == 0); + + // initialize keystroke + Keystroke keystroke; + keystroke.m_repeat = false; + + // releasing a modifier is quite different from pressing one. + // when we release a modifier we have to release every keycode that + // is assigned to the modifier since the modifier is active if any + // one of them is down. when we press a modifier we just have to + // press one of those keycodes. + if (desireActive) { + // press + keystroke.m_keycode = keyIndex->second.m_keycode[0]; + keystroke.m_press = True; + keys.push_back(keystroke); + keystroke.m_press = False; + undo.push_back(keystroke); + } + else { + // release + KeyCodeToModifierMap::const_iterator index = + m_keycodeToModifier.find(keyIndex->second.m_keycode[0]); + if (index != m_keycodeToModifier.end()) { + const KeyCodes& keycodes = m_modifierKeycodes[index->second]; + for (KeyCodes::const_iterator j = keycodes.begin(); + j != keycodes.end(); ++j) { + if (m_keys[*j]) { + keystroke.m_keycode = *j; + keystroke.m_press = False; + keys.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); + } + } + } + } + + return true; +} + +void +CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) +{ + // do nothing if no keys or no repeats + if (count < 1 || keys.empty()) { + return; + } + + // lock display + CDisplayLock display(m_screen); + + // generate key events + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { + if (k->m_repeat) { + // repeat from here up to but not including the next key + // with m_repeat == false count times. + Keystrokes::const_iterator start = k; + for (; count > 0; --count) { + // send repeating events + for (k = start; k != keys.end() && k->m_repeat; ++k) { + XTestFakeKeyEvent(display, + k->m_keycode, k->m_press, CurrentTime); + } + } + + // note -- k is now on the first non-repeat key after the + // repeat keys, exactly where we'd like to continue from. + } + else { + // send event + LOG((CLOG_DEBUG2 "keystrokes:")); + LOG((CLOG_DEBUG2 " %d %s", k->m_keycode, k->m_press ? "down" : "up")); + XTestFakeKeyEvent(display, k->m_keycode, k->m_press, CurrentTime); + + // next key + ++k; + } + } + + // update + flush(display); +} + +CXWindowsSecondaryScreen::ModifierMask +CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const +{ + ModifierMask outMask = 0; + if (inMask & KeyModifierShift) { + outMask |= m_shiftMask; + } + if (inMask & KeyModifierControl) { + outMask |= m_ctrlMask; + } + if (inMask & KeyModifierAlt) { + outMask |= m_altMask; + } + if (inMask & KeyModifierMeta) { + outMask |= m_metaMask; + } + if (inMask & KeyModifierSuper) { + outMask |= m_superMask; + } + if (inMask & KeyModifierModeSwitch) { + outMask |= m_modeSwitchMask; + } + if (inMask & KeyModifierCapsLock) { + outMask |= m_capsLockMask; + } + if (inMask & KeyModifierNumLock) { + outMask |= m_numLockMask; + } + if (inMask & KeyModifierScrollLock) { + outMask |= m_scrollLockMask; + } + return outMask; +} + +void +CXWindowsSecondaryScreen::doReleaseKeys(Display* display) +{ + assert(display != NULL); + + // key release for each key that we faked a press for + for (UInt32 i = 0; i < 256; ++i) { + if (m_fakeKeys[i]) { + XTestFakeKeyEvent(display, i, False, CurrentTime); + m_fakeKeys[i] = false; + m_keys[i] = false; + } + } +} + +void +CXWindowsSecondaryScreen::doUpdateKeys(Display* display) +{ + // query the button mapping + UInt32 numButtons = XGetPointerMapping(display, NULL, 0); + unsigned char* tmpButtons = new unsigned char[numButtons]; + XGetPointerMapping(display, tmpButtons, numButtons); + + // find the largest logical button id + unsigned char maxButton = 0; + for (UInt32 i = 0; i < numButtons; ++i) { + if (tmpButtons[i] > maxButton) { + maxButton = tmpButtons[i]; + } + } + + // allocate button array + m_buttons.resize(maxButton); + + // fill in button array values. m_buttons[i] is the physical + // button number for logical button i+1. + for (UInt32 i = 0; i < numButtons; ++i) { + m_buttons[i] = 0; + } + for (UInt32 i = 0; i < numButtons; ++i) { + m_buttons[tmpButtons[i] - 1] = i + 1; + } + + // clean up + delete[] tmpButtons; + + // update mappings and current modifiers + updateKeysymMap(display); + updateModifiers(display); +} + +void +CXWindowsSecondaryScreen::updateKeys() +{ + CDisplayLock display(m_screen); + + // ask server which keys are pressed + char keys[32]; + XQueryKeymap(display, keys); + + // transfer to our state + for (UInt32 i = 0, j = 0; i < 32; j += 8, ++i) { + m_keys[j + 0] = ((keys[i] & 0x01) != 0); + m_keys[j + 1] = ((keys[i] & 0x02) != 0); + m_keys[j + 2] = ((keys[i] & 0x04) != 0); + m_keys[j + 3] = ((keys[i] & 0x08) != 0); + m_keys[j + 4] = ((keys[i] & 0x10) != 0); + m_keys[j + 5] = ((keys[i] & 0x20) != 0); + m_keys[j + 6] = ((keys[i] & 0x40) != 0); + m_keys[j + 7] = ((keys[i] & 0x80) != 0); + } + + // we've fake pressed no keys + m_fakeKeys.reset(); + + // update mappings and current modifiers and mouse buttons + doUpdateKeys(display); +} + +void +CXWindowsSecondaryScreen::releaseKeys() +{ + CDisplayLock display(m_screen); + if (display != NULL) { + doReleaseKeys(display); + } +} + +void +CXWindowsSecondaryScreen::updateKeysymMap(Display* display) +{ + // there are up to 4 keysyms per keycode + static const unsigned int maxKeysyms = 4; + + // get the number of keycodes + int minKeycode, maxKeycode; + XDisplayKeycodes(display, &minKeycode, &maxKeycode); + const int numKeycodes = maxKeycode - minKeycode + 1; + + // get the keyboard mapping for all keys + int keysymsPerKeycode; + KeySym* keysyms = XGetKeyboardMapping(display, + minKeycode, numKeycodes, + &keysymsPerKeycode); + + // we only understand up to maxKeysyms keysyms per keycodes + unsigned int numKeysyms = keysymsPerKeycode; + if (numKeysyms > maxKeysyms) { + numKeysyms = maxKeysyms; + } + + // get modifier map from server + XModifierKeymap* modifiers = XGetModifierMapping(display); + + // determine shift and mode switch sensitivity. a keysym is shift + // or mode switch sensitive if its keycode is. a keycode is mode + // mode switch sensitive if it has keysyms for indices 2 or 3. + // it's shift sensitive if the keysym for index 1 (if any) is + // different from the keysym for index 0 and, if the keysym for + // for index 3 (if any) is different from the keysym for index 2. + // that is, if shift changes the generated keysym for the keycode. + std::vector usesShift(numKeycodes); + std::vector usesModeSwitch(numKeycodes); + for (int i = 0; i < numKeycodes; ++i) { + // check mode switch first + if (numKeysyms > 2 && + keysyms[i * keysymsPerKeycode + 2] != NoSymbol || + keysyms[i * keysymsPerKeycode + 3] != NoSymbol) { + usesModeSwitch[i] = true; + } + + // check index 0 with index 1 keysyms + if (keysyms[i * keysymsPerKeycode + 0] != NoSymbol && + keysyms[i * keysymsPerKeycode + 1] != NoSymbol && + keysyms[i * keysymsPerKeycode + 1] != + keysyms[i * keysymsPerKeycode + 0]) { + usesShift[i] = true; + } + + else if (numKeysyms >= 4 && + keysyms[i * keysymsPerKeycode + 2] != NoSymbol && + keysyms[i * keysymsPerKeycode + 3] != NoSymbol && + keysyms[i * keysymsPerKeycode + 3] != + keysyms[i * keysymsPerKeycode + 2]) { + usesShift[i] = true; + } + } + + // initialize + m_keysymMap.clear(); + int keysPerModifier = modifiers->max_keypermod; + + // for each modifier keycode, get the index 0 keycode and add it to + // the keysym map. also collect all keycodes for each modifier. + m_keycodeToModifier.clear(); + for (ModifierIndex i = 0; i < 8; ++i) { + // start with no keycodes for this modifier + m_modifierKeycodes[i].clear(); + + // add each keycode for modifier + for (unsigned int j = 0; j < keysPerModifier; ++j) { + // get keycode and ignore unset keycodes + KeyCode keycode = modifiers->modifiermap[i * keysPerModifier + j]; + if (keycode == 0) { + continue; + } + + // save keycode for modifier and modifier for keycode + m_modifierKeycodes[i].push_back(keycode); + m_keycodeToModifier[keycode] = i; + + // get keysym and get/create key mapping + const int keycodeIndex = keycode - minKeycode; + const KeySym keysym = keysyms[keycodeIndex * + keysymsPerKeycode + 0]; + KeyMapping& mapping = m_keysymMap[keysym]; + + // skip if we already have a keycode for this index + if (mapping.m_keycode[0] != 0) { + continue; + } + + // fill in keysym info + mapping.m_keycode[0] = keycode; + mapping.m_shiftSensitive[0] = usesShift[keycodeIndex]; + mapping.m_modeSwitchSensitive[0] = usesModeSwitch[keycodeIndex]; + mapping.m_modifierMask = (1 << i); + mapping.m_capsLockSensitive = false; + mapping.m_numLockSensitive = false; + } + } + + // create a convenient NoSymbol entry (if it doesn't exist yet). + // sometimes it's useful to handle NoSymbol like a normal keysym. + // remove any entry for NoSymbol. that keysym doesn't count. + { + KeyMapping& mapping = m_keysymMap[NoSymbol]; + for (unsigned int i = 0; i < numKeysyms; ++i) { + mapping.m_keycode[i] = 0; + mapping.m_shiftSensitive[i] = false; + mapping.m_modeSwitchSensitive[i] = false; + } + mapping.m_modifierMask = 0; + mapping.m_capsLockSensitive = false; + mapping.m_numLockSensitive = false; + } + + // add each keysym to the map, unless we've already inserted a key + // for that keysym index. + for (int i = 0; i < numKeycodes; ++i) { + for (unsigned int j = 0; j < numKeysyms; ++j) { + // lookup keysym + const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; + if (keysym == NoSymbol) { + continue; + } + KeyMapping& mapping = m_keysymMap[keysym]; + + // skip if we already have a keycode for this index + if (mapping.m_keycode[j] != 0) { + continue; + } + + // fill in keysym info + if (mapping.m_keycode[0] == 0) { + mapping.m_modifierMask = 0; + } + mapping.m_keycode[j] = static_cast( + minKeycode + i); + mapping.m_shiftSensitive[j] = usesShift[i]; + mapping.m_modeSwitchSensitive[j] = usesModeSwitch[i]; + mapping.m_numLockSensitive = adjustForNumLock(keysym); + mapping.m_capsLockSensitive = adjustForCapsLock(keysym); + } + } + + // choose the keysym to use for each modifier. if the modifier + // isn't mapped then use NoSymbol. if a modifier has both left + // and right versions then (arbitrarily) prefer the left. also + // collect the available modifier bits. + struct CModifierBitInfo { + public: + KeySym CXWindowsSecondaryScreen::*m_keysym; + KeySym m_left; + KeySym m_right; + }; + static const CModifierBitInfo s_modifierBitTable[] = { + { &CXWindowsSecondaryScreen::m_shiftKeysym, XK_Shift_L, XK_Shift_R }, + { &CXWindowsSecondaryScreen::m_ctrlKeysym, XK_Control_L, XK_Control_R }, + { &CXWindowsSecondaryScreen::m_altKeysym, XK_Alt_L, XK_Alt_R }, + { &CXWindowsSecondaryScreen::m_metaKeysym, XK_Meta_L, XK_Meta_R }, + { &CXWindowsSecondaryScreen::m_superKeysym, XK_Super_L, XK_Super_R }, + { &CXWindowsSecondaryScreen::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol }, + { &CXWindowsSecondaryScreen::m_numLockKeysym, XK_Num_Lock, NoSymbol }, + { &CXWindowsSecondaryScreen::m_capsLockKeysym, XK_Caps_Lock, NoSymbol }, + { &CXWindowsSecondaryScreen::m_scrollLockKeysym, XK_Scroll_Lock, NoSymbol } + }; + m_modifierMask = 0; + m_toggleModifierMask = 0; + for (size_t i = 0; i < sizeof(s_modifierBitTable) / + sizeof(s_modifierBitTable[0]); ++i) { + const CModifierBitInfo& info = s_modifierBitTable[i]; + + // find available keysym + KeySymIndex keyIndex = m_keysymMap.find(info.m_left); + if (keyIndex == m_keysymMap.end() && info.m_right != NoSymbol) { + keyIndex = m_keysymMap.find(info.m_right); + } + if (keyIndex != m_keysymMap.end() && + keyIndex->second.m_modifierMask != 0) { + this->*(info.m_keysym) = keyIndex->first; + } + else { + this->*(info.m_keysym) = NoSymbol; + continue; + } + + // add modifier bit + m_modifierMask |= keyIndex->second.m_modifierMask; + if (isToggleKeysym(this->*(info.m_keysym))) { + m_toggleModifierMask |= keyIndex->second.m_modifierMask; + } + } + + // if there's no mode switch key mapped then remove all keycodes + // that depend on it and no keycode can be mode switch sensitive. + if (m_modeSwitchKeysym == NoSymbol) { + LOG((CLOG_DEBUG2 "no mode switch in keymap")); + for (KeySymMap::iterator i = m_keysymMap.begin(); + i != m_keysymMap.end(); ) { + i->second.m_keycode[2] = 0; + i->second.m_keycode[3] = 0; + i->second.m_modeSwitchSensitive[0] = false; + i->second.m_modeSwitchSensitive[1] = false; + i->second.m_modeSwitchSensitive[2] = false; + i->second.m_modeSwitchSensitive[3] = false; + + // if this keysym no has no keycodes then remove it + // except for the NoSymbol keysym mapping. + if (i->second.m_keycode[0] == 0 && i->second.m_keycode[1] == 0) { + m_keysymMap.erase(i++); + } + else { + ++i; + } + } + } + + // cache the bits for the modifier + m_shiftMask = getModifierMask(m_shiftKeysym); + m_ctrlMask = getModifierMask(m_ctrlKeysym); + m_altMask = getModifierMask(m_altKeysym); + m_metaMask = getModifierMask(m_metaKeysym); + m_superMask = getModifierMask(m_superKeysym); + m_capsLockMask = getModifierMask(m_capsLockKeysym); + m_numLockMask = getModifierMask(m_numLockKeysym); + m_modeSwitchMask = getModifierMask(m_modeSwitchKeysym); + m_scrollLockMask = getModifierMask(m_scrollLockKeysym); + + // clean up + XFree(keysyms); + XFreeModifiermap(modifiers); +} + +void +CXWindowsSecondaryScreen::updateModifiers(Display* display) +{ + // query the pointer to get the keyboard state + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (!XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + state = 0; + } + + // update active modifier mask + m_mask = 0; + for (ModifierIndex i = 0; i < 8; ++i) { + const ModifierMask bit = (1 << i); + if ((bit & m_toggleModifierMask) == 0) { + for (KeyCodes::const_iterator j = m_modifierKeycodes[i].begin(); + j != m_modifierKeycodes[i].end(); ++j) { + if (m_keys[*j]) { + m_mask |= bit; + break; + } + } + } + else if ((bit & state) != 0) { + // toggle is on + m_mask |= bit; + } + } +} + +void +CXWindowsSecondaryScreen::toggleKey(Display* display, + KeySym keysym, ModifierMask mask) +{ + // lookup the key mapping + KeySymIndex index = m_keysymMap.find(keysym); + if (index == m_keysymMap.end()) { + return; + } + KeyCode keycode = index->second.m_keycode[0]; + + // toggle the key + if ((keysym == m_capsLockKeysym && m_capsLockHalfDuplex) || + (keysym == m_numLockKeysym && m_numLockHalfDuplex)) { + // "half-duplex" toggle + XTestFakeKeyEvent(display, keycode, (m_mask & mask) == 0, CurrentTime); + } + else { + // normal toggle + XTestFakeKeyEvent(display, keycode, True, CurrentTime); + XTestFakeKeyEvent(display, keycode, False, CurrentTime); + } + + // toggle shadow state + m_mask ^= mask; +} + +bool +CXWindowsSecondaryScreen::isToggleKeysym(KeySym key) +{ + switch (key) { + case XK_Caps_Lock: + case XK_Shift_Lock: + case XK_Num_Lock: + case XK_Scroll_Lock: + return true; + + default: + return false; + } +} + +// map special KeyID keys to KeySyms +#if defined(HAVE_X11_XF86KEYSYM_H) +static const KeySym g_mapE000[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, + /* 0xa4 */ 0, 0, + /* 0xa6 */ XF86XK_Back, XF86XK_Forward, + /* 0xa8 */ XF86XK_Refresh, XF86XK_Stop, + /* 0xaa */ XF86XK_Search, XF86XK_Favorites, + /* 0xac */ XF86XK_HomePage, XF86XK_AudioMute, + /* 0xae */ XF86XK_AudioLowerVolume, XF86XK_AudioRaiseVolume, + /* 0xb0 */ XF86XK_AudioNext, XF86XK_AudioPrev, + /* 0xb2 */ XF86XK_AudioStop, XF86XK_AudioPlay, + /* 0xb4 */ XF86XK_Mail, XF86XK_AudioMedia, + /* 0xb6 */ XF86XK_Launch0, XF86XK_Launch1, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif + +KeySym +CXWindowsSecondaryScreen::keyIDToKeySym(KeyID id, ModifierMask mask) const +{ + // convert id to keysym + KeySym keysym = NoSymbol; + if ((id & 0xfffff000) == 0xe000) { + // special character + switch (id & 0x0000ff00) { +#if defined(HAVE_X11_XF86KEYSYM_H) + case 0xe000: + return g_mapE000[id & 0xff]; +#endif + + case 0xee00: + // ISO 9995 Function and Modifier Keys + if (id == kKeyLeftTab) { + keysym = XK_ISO_Left_Tab; + } + break; + + case 0xef00: + // MISCELLANY + keysym = static_cast(id - 0xef00 + 0xff00); + break; + } + } + else if ((id >= 0x0020 && id <= 0x007e) || + (id >= 0x00a0 && id <= 0x00ff)) { + // Latin-1 maps directly + return static_cast(id); + } + else { + // lookup keysym in table + return CXWindowsUtil::mapUCS4ToKeySym(id); + } + + // fail if unknown key + if (keysym == NoSymbol) { + return keysym; + } + + // if kKeyTab is requested with shift active then try XK_ISO_Left_Tab + // instead. if that doesn't work, we'll fall back to XK_Tab with + // shift active. this is to handle primary screens that don't map + // XK_ISO_Left_Tab sending events to secondary screens that do. + if (keysym == XK_Tab && (mask & ShiftMask) != 0) { + keysym = XK_ISO_Left_Tab; + } + + // some keysyms have emergency backups (particularly the numpad + // keys since most laptops don't have a separate numpad and the + // numpad overlaying the main keyboard may not have movement + // key bindings). figure out the emergency backup. + KeySym backupKeysym; + switch (keysym) { + case XK_KP_Home: + backupKeysym = XK_Home; + break; + + case XK_KP_Left: + backupKeysym = XK_Left; + break; + + case XK_KP_Up: + backupKeysym = XK_Up; + break; + + case XK_KP_Right: + backupKeysym = XK_Right; + break; + + case XK_KP_Down: + backupKeysym = XK_Down; + break; + + case XK_KP_Prior: + backupKeysym = XK_Prior; + break; + + case XK_KP_Next: + backupKeysym = XK_Next; + break; + + case XK_KP_End: + backupKeysym = XK_End; + break; + + case XK_KP_Insert: + backupKeysym = XK_Insert; + break; + + case XK_KP_Delete: + backupKeysym = XK_Delete; + break; + + case XK_ISO_Left_Tab: + backupKeysym = XK_Tab; + break; + + default: + backupKeysym = keysym; + break; + } + + // see if the keysym is assigned to any keycode. if not and the + // backup keysym is then use the backup keysym. + if (backupKeysym != keysym && + m_keysymMap.find(keysym) == m_keysymMap.end() && + m_keysymMap.find(backupKeysym) != m_keysymMap.end()) { + keysym = backupKeysym; + } + + return keysym; +} + +bool +CXWindowsSecondaryScreen::decomposeKeySym(KeySym keysym, + KeySyms& decomposed) const +{ + // unfortunately, X11 doesn't appear to have any way of + // decomposing a keysym into its component keysyms. we'll + // use a lookup table for certain character sets. + const KeySymsMap& table = getDecomposedKeySymTable(); + KeySymsMap::const_iterator i = table.find(keysym); + if (i == table.end()) { + return false; + } + decomposed = i->second; + return true; +} + +bool +CXWindowsSecondaryScreen::adjustForNumLock(KeySym keysym) const +{ + return (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)); +} + +bool +CXWindowsSecondaryScreen::adjustForCapsLock(KeySym keysym) const +{ + KeySym lKey, uKey; + XConvertCase(keysym, &lKey, &uKey); + return (lKey != uKey); +} + +const CXWindowsSecondaryScreen::KeySymsMap& +CXWindowsSecondaryScreen::getDecomposedKeySymTable() +{ + static const KeySym s_rawTable[] = { + // non-dead version of dead keys + XK_grave, XK_dead_grave, XK_space, 0, + XK_acute, XK_dead_acute, XK_space, 0, + XK_asciicircum, XK_dead_circumflex, XK_space, 0, + XK_asciitilde, XK_dead_tilde, XK_space, 0, + XK_cedilla, XK_dead_cedilla, XK_space, 0, + XK_ogonek, XK_dead_ogonek, XK_space, 0, + XK_caron, XK_dead_caron, XK_space, 0, + XK_abovedot, XK_dead_abovedot, XK_space, 0, + XK_doubleacute, XK_dead_doubleacute, XK_space, 0, + XK_breve, XK_dead_breve, XK_space, 0, + XK_macron, XK_dead_macron, XK_space, 0, + + // Latin-1 (ISO 8859-1) + XK_Agrave, XK_dead_grave, XK_A, 0, + XK_Aacute, XK_dead_acute, XK_A, 0, + XK_Acircumflex, XK_dead_circumflex, XK_A, 0, + XK_Atilde, XK_dead_tilde, XK_A, 0, + XK_Adiaeresis, XK_dead_diaeresis, XK_A, 0, + XK_Aring, XK_dead_abovering, XK_A, 0, + XK_Ccedilla, XK_dead_cedilla, XK_C, 0, + XK_Egrave, XK_dead_grave, XK_E, 0, + XK_Eacute, XK_dead_acute, XK_E, 0, + XK_Ecircumflex, XK_dead_circumflex, XK_E, 0, + XK_Ediaeresis, XK_dead_diaeresis, XK_E, 0, + XK_Igrave, XK_dead_grave, XK_I, 0, + XK_Iacute, XK_dead_acute, XK_I, 0, + XK_Icircumflex, XK_dead_circumflex, XK_I, 0, + XK_Idiaeresis, XK_dead_diaeresis, XK_I, 0, + XK_Ntilde, XK_dead_tilde, XK_N, 0, + XK_Ograve, XK_dead_grave, XK_O, 0, + XK_Oacute, XK_dead_acute, XK_O, 0, + XK_Ocircumflex, XK_dead_circumflex, XK_O, 0, + XK_Otilde, XK_dead_tilde, XK_O, 0, + XK_Odiaeresis, XK_dead_diaeresis, XK_O, 0, + XK_Ugrave, XK_dead_grave, XK_U, 0, + XK_Uacute, XK_dead_acute, XK_U, 0, + XK_Ucircumflex, XK_dead_circumflex, XK_U, 0, + XK_Udiaeresis, XK_dead_diaeresis, XK_U, 0, + XK_Yacute, XK_dead_acute, XK_Y, 0, + XK_agrave, XK_dead_grave, XK_a, 0, + XK_aacute, XK_dead_acute, XK_a, 0, + XK_acircumflex, XK_dead_circumflex, XK_a, 0, + XK_atilde, XK_dead_tilde, XK_a, 0, + XK_adiaeresis, XK_dead_diaeresis, XK_a, 0, + XK_aring, XK_dead_abovering, XK_a, 0, + XK_ccedilla, XK_dead_cedilla, XK_c, 0, + XK_egrave, XK_dead_grave, XK_e, 0, + XK_eacute, XK_dead_acute, XK_e, 0, + XK_ecircumflex, XK_dead_circumflex, XK_e, 0, + XK_ediaeresis, XK_dead_diaeresis, XK_e, 0, + XK_igrave, XK_dead_grave, XK_i, 0, + XK_iacute, XK_dead_acute, XK_i, 0, + XK_icircumflex, XK_dead_circumflex, XK_i, 0, + XK_idiaeresis, XK_dead_diaeresis, XK_i, 0, + XK_ntilde, XK_dead_tilde, XK_n, 0, + XK_ograve, XK_dead_grave, XK_o, 0, + XK_oacute, XK_dead_acute, XK_o, 0, + XK_ocircumflex, XK_dead_circumflex, XK_o, 0, + XK_otilde, XK_dead_tilde, XK_o, 0, + XK_odiaeresis, XK_dead_diaeresis, XK_o, 0, + XK_ugrave, XK_dead_grave, XK_u, 0, + XK_uacute, XK_dead_acute, XK_u, 0, + XK_ucircumflex, XK_dead_circumflex, XK_u, 0, + XK_udiaeresis, XK_dead_diaeresis, XK_u, 0, + XK_yacute, XK_dead_acute, XK_y, 0, + XK_ydiaeresis, XK_dead_diaeresis, XK_y, 0, + + // Latin-2 (ISO 8859-2) + XK_Aogonek, XK_dead_ogonek, XK_A, 0, + XK_Lcaron, XK_dead_caron, XK_L, 0, + XK_Sacute, XK_dead_acute, XK_S, 0, + XK_Scaron, XK_dead_caron, XK_S, 0, + XK_Scedilla, XK_dead_cedilla, XK_S, 0, + XK_Tcaron, XK_dead_caron, XK_T, 0, + XK_Zacute, XK_dead_acute, XK_Z, 0, + XK_Zcaron, XK_dead_caron, XK_Z, 0, + XK_Zabovedot, XK_dead_abovedot, XK_Z, 0, + XK_aogonek, XK_dead_ogonek, XK_a, 0, + XK_lcaron, XK_dead_caron, XK_l, 0, + XK_sacute, XK_dead_acute, XK_s, 0, + XK_scaron, XK_dead_caron, XK_s, 0, + XK_scedilla, XK_dead_cedilla, XK_s, 0, + XK_tcaron, XK_dead_caron, XK_t, 0, + XK_zacute, XK_dead_acute, XK_z, 0, + XK_zcaron, XK_dead_caron, XK_z, 0, + XK_zabovedot, XK_dead_abovedot, XK_z, 0, + XK_Racute, XK_dead_acute, XK_R, 0, + XK_Abreve, XK_dead_breve, XK_A, 0, + XK_Lacute, XK_dead_acute, XK_L, 0, + XK_Cacute, XK_dead_acute, XK_C, 0, + XK_Ccaron, XK_dead_caron, XK_C, 0, + XK_Eogonek, XK_dead_ogonek, XK_E, 0, + XK_Ecaron, XK_dead_caron, XK_E, 0, + XK_Dcaron, XK_dead_caron, XK_D, 0, + XK_Nacute, XK_dead_acute, XK_N, 0, + XK_Ncaron, XK_dead_caron, XK_N, 0, + XK_Odoubleacute, XK_dead_doubleacute, XK_O, 0, + XK_Rcaron, XK_dead_caron, XK_R, 0, + XK_Uring, XK_dead_abovering, XK_U, 0, + XK_Udoubleacute, XK_dead_doubleacute, XK_U, 0, + XK_Tcedilla, XK_dead_cedilla, XK_T, 0, + XK_racute, XK_dead_acute, XK_r, 0, + XK_abreve, XK_dead_breve, XK_a, 0, + XK_lacute, XK_dead_acute, XK_l, 0, + XK_cacute, XK_dead_acute, XK_c, 0, + XK_ccaron, XK_dead_caron, XK_c, 0, + XK_eogonek, XK_dead_ogonek, XK_e, 0, + XK_ecaron, XK_dead_caron, XK_e, 0, + XK_dcaron, XK_dead_caron, XK_d, 0, + XK_nacute, XK_dead_acute, XK_n, 0, + XK_ncaron, XK_dead_caron, XK_n, 0, + XK_odoubleacute, XK_dead_doubleacute, XK_o, 0, + XK_rcaron, XK_dead_caron, XK_r, 0, + XK_uring, XK_dead_abovering, XK_u, 0, + XK_udoubleacute, XK_dead_doubleacute, XK_u, 0, + XK_tcedilla, XK_dead_cedilla, XK_t, 0, + + // Latin-3 (ISO 8859-3) + XK_Hcircumflex, XK_dead_circumflex, XK_H, 0, + XK_Iabovedot, XK_dead_abovedot, XK_I, 0, + XK_Gbreve, XK_dead_breve, XK_G, 0, + XK_Jcircumflex, XK_dead_circumflex, XK_J, 0, + XK_hcircumflex, XK_dead_circumflex, XK_h, 0, + XK_gbreve, XK_dead_breve, XK_g, 0, + XK_jcircumflex, XK_dead_circumflex, XK_j, 0, + XK_Cabovedot, XK_dead_abovedot, XK_C, 0, + XK_Ccircumflex, XK_dead_circumflex, XK_C, 0, + XK_Gabovedot, XK_dead_abovedot, XK_G, 0, + XK_Gcircumflex, XK_dead_circumflex, XK_G, 0, + XK_Ubreve, XK_dead_breve, XK_U, 0, + XK_Scircumflex, XK_dead_circumflex, XK_S, 0, + XK_cabovedot, XK_dead_abovedot, XK_c, 0, + XK_ccircumflex, XK_dead_circumflex, XK_c, 0, + XK_gabovedot, XK_dead_abovedot, XK_g, 0, + XK_gcircumflex, XK_dead_circumflex, XK_g, 0, + XK_ubreve, XK_dead_breve, XK_u, 0, + XK_scircumflex, XK_dead_circumflex, XK_s, 0, + + // Latin-4 (ISO 8859-4) + XK_scircumflex, XK_dead_circumflex, XK_s, 0, + XK_Rcedilla, XK_dead_cedilla, XK_R, 0, + XK_Itilde, XK_dead_tilde, XK_I, 0, + XK_Lcedilla, XK_dead_cedilla, XK_L, 0, + XK_Emacron, XK_dead_macron, XK_E, 0, + XK_Gcedilla, XK_dead_cedilla, XK_G, 0, + XK_rcedilla, XK_dead_cedilla, XK_r, 0, + XK_itilde, XK_dead_tilde, XK_i, 0, + XK_lcedilla, XK_dead_cedilla, XK_l, 0, + XK_emacron, XK_dead_macron, XK_e, 0, + XK_gcedilla, XK_dead_cedilla, XK_g, 0, + XK_Amacron, XK_dead_macron, XK_A, 0, + XK_Iogonek, XK_dead_ogonek, XK_I, 0, + XK_Eabovedot, XK_dead_abovedot, XK_E, 0, + XK_Imacron, XK_dead_macron, XK_I, 0, + XK_Ncedilla, XK_dead_cedilla, XK_N, 0, + XK_Omacron, XK_dead_macron, XK_O, 0, + XK_Kcedilla, XK_dead_cedilla, XK_K, 0, + XK_Uogonek, XK_dead_ogonek, XK_U, 0, + XK_Utilde, XK_dead_tilde, XK_U, 0, + XK_Umacron, XK_dead_macron, XK_U, 0, + XK_amacron, XK_dead_macron, XK_a, 0, + XK_iogonek, XK_dead_ogonek, XK_i, 0, + XK_eabovedot, XK_dead_abovedot, XK_e, 0, + XK_imacron, XK_dead_macron, XK_i, 0, + XK_ncedilla, XK_dead_cedilla, XK_n, 0, + XK_omacron, XK_dead_macron, XK_o, 0, + XK_kcedilla, XK_dead_cedilla, XK_k, 0, + XK_uogonek, XK_dead_ogonek, XK_u, 0, + XK_utilde, XK_dead_tilde, XK_u, 0, + XK_umacron, XK_dead_macron, XK_u, 0, + + // Latin-8 (ISO 8859-14) + XK_Babovedot, XK_dead_abovedot, XK_B, 0, + XK_babovedot, XK_dead_abovedot, XK_b, 0, + XK_Dabovedot, XK_dead_abovedot, XK_D, 0, + XK_Wgrave, XK_dead_grave, XK_W, 0, + XK_Wacute, XK_dead_acute, XK_W, 0, + XK_dabovedot, XK_dead_abovedot, XK_d, 0, + XK_Ygrave, XK_dead_grave, XK_Y, 0, + XK_Fabovedot, XK_dead_abovedot, XK_F, 0, + XK_fabovedot, XK_dead_abovedot, XK_f, 0, + XK_Mabovedot, XK_dead_abovedot, XK_M, 0, + XK_mabovedot, XK_dead_abovedot, XK_m, 0, + XK_Pabovedot, XK_dead_abovedot, XK_P, 0, + XK_wgrave, XK_dead_grave, XK_w, 0, + XK_pabovedot, XK_dead_abovedot, XK_p, 0, + XK_wacute, XK_dead_acute, XK_w, 0, + XK_Sabovedot, XK_dead_abovedot, XK_S, 0, + XK_ygrave, XK_dead_grave, XK_y, 0, + XK_Wdiaeresis, XK_dead_diaeresis, XK_W, 0, + XK_wdiaeresis, XK_dead_diaeresis, XK_w, 0, + XK_sabovedot, XK_dead_abovedot, XK_s, 0, + XK_Wcircumflex, XK_dead_circumflex, XK_W, 0, + XK_Tabovedot, XK_dead_abovedot, XK_T, 0, + XK_Ycircumflex, XK_dead_circumflex, XK_Y, 0, + XK_wcircumflex, XK_dead_circumflex, XK_w, 0, + XK_tabovedot, XK_dead_abovedot, XK_t, 0, + XK_ycircumflex, XK_dead_circumflex, XK_y, 0, + + // Latin-9 (ISO 8859-15) + XK_Ydiaeresis, XK_dead_diaeresis, XK_Y, 0, + + // end of table + 0 + }; + + // fill table if not yet initialized + if (s_decomposedKeySyms.empty()) { + const KeySym* scan = s_rawTable; + while (*scan != 0) { + // add an entry for this keysym + KeySyms& entry = s_decomposedKeySyms[*scan]; + + // add the decomposed keysyms for the keysym + while (*++scan != 0) { + entry.push_back(*scan); + } + + // skip end of entry marker + ++scan; + } + } + + return s_decomposedKeySyms; +} + + +// +// CXWindowsSecondaryScreen::KeyMapping +// + +CXWindowsSecondaryScreen::KeyMapping::KeyMapping() +{ + m_keycode[0] = 0; + m_keycode[1] = 0; + m_keycode[2] = 0; + m_keycode[3] = 0; +} diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h new file mode 100644 index 0000000..c2de53e --- /dev/null +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -0,0 +1,240 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSSECONDARYSCREEN_H +#define CXWINDOWSSECONDARYSCREEN_H + +#include "CSecondaryScreen.h" +#include "IScreenEventHandler.h" +#include "stdbitset.h" +#include "stdmap.h" +#include "stdvector.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +class CXWindowsScreen; +class IScreenReceiver; + +//! X11 secondary screen implementation +class CXWindowsSecondaryScreen : + public CSecondaryScreen, public IScreenEventHandler { +public: + CXWindowsSecondaryScreen(IScreenReceiver*); + virtual ~CXWindowsSecondaryScreen(); + + // CSecondaryScreen overrides + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 x, SInt32 y); + virtual void mouseWheel(SInt32 delta); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual IScreen* getScreen() const; + + // IScreenEventHandler overrides + virtual void onScreensaver(bool activated); + virtual bool onPreDispatch(const CEvent* event); + virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); + virtual SInt32 getJumpZoneSize() const; + +protected: + // CSecondaryScreen overrides + virtual void onPreMainLoop(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreClose(); + virtual void onPreEnter(); + virtual void onPostEnter(); + virtual void onPreLeave(); + virtual void createWindow(); + virtual void destroyWindow(); + virtual void showWindow(SInt32 x, SInt32 y); + virtual void hideWindow(); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void updateKeys(); + virtual void releaseKeys(); + virtual void setToggleState(KeyModifierMask); + virtual KeyModifierMask getToggleState() const; + +private: + enum EKeyAction { kPress, kRelease, kRepeat }; + typedef unsigned int ModifierIndex; + typedef unsigned int ModifierMask; + class Keystroke { + public: + KeyCode m_keycode; + Bool m_press; + bool m_repeat; + }; + class KeyMapping { + public: + KeyMapping(); + + public: + // KeyCode to generate keysym and whether keycode[i] is + // sensitive to shift and mode switch. + KeyCode m_keycode[4]; + bool m_shiftSensitive[4]; + bool m_modeSwitchSensitive[4]; + + // the modifier mask of keysym or 0 if not a modifier + ModifierMask m_modifierMask; + + // whether keysym is sensitive to caps and num lock + bool m_numLockSensitive; + bool m_capsLockSensitive; + }; + + typedef std::vector KeyCodes; + typedef std::map KeyCodeToModifierMap; + typedef std::map KeySymMap; + typedef KeySymMap::const_iterator KeySymIndex; + typedef std::vector Keystrokes; + typedef std::vector KeySyms; + typedef std::map KeySymsMap; + typedef std::map ServerKeyMap; + + void flush(Display*) const; + + unsigned int mapButton(ButtonID button) const; + + ModifierMask mapKey(Keystrokes&, KeyCode&, KeyID, + KeyModifierMask, EKeyAction) const; + ModifierMask mapKeyRelease(Keystrokes&, KeyCode) const; + bool mapToKeystrokes(Keystrokes& keys, + KeyCode& keycode, + ModifierMask& finalMask, + KeySymIndex keyIndex, + ModifierMask currentMask, + EKeyAction action) const; + bool adjustModifiers(Keystrokes& keys, + Keystrokes& undo, + ModifierMask& inOutMask, + ModifierMask desiredMask) const; + bool adjustModifier(Keystrokes& keys, + Keystrokes& undo, + KeySym keysym, + bool desireActive) const; + void doKeystrokes(const Keystrokes&, SInt32 count); + ModifierMask maskToX(KeyModifierMask) const; + + unsigned int findBestKeyIndex(KeySymIndex keyIndex, + ModifierMask currentMask) const; + bool isShiftInverted(KeySymIndex keyIndex, + ModifierMask currentMask) const; + ModifierMask getModifierMask(KeySym) const; + + void doUpdateKeys(Display*); + void doReleaseKeys(Display*); + void updateKeysymMap(Display* display); + void updateModifiers(Display* display); + ModifierIndex keySymToModifierIndex(KeySym) const; + void toggleKey(Display*, KeySym, ModifierMask mask); + static bool isToggleKeysym(KeySym); + + KeySym keyIDToKeySym(KeyID id, ModifierMask mask) const; + bool adjustForNumLock(KeySym) const; + bool adjustForCapsLock(KeySym) const; + + bool decomposeKeySym(KeySym keysym, + KeySyms& decomposed) const; + static const KeySymsMap& getDecomposedKeySymTable(); + +private: + CXWindowsScreen* m_screen; + Window m_window; + + // note toggle keys that toggles on up/down (false) or on + // transition (true) + bool m_numLockHalfDuplex; + bool m_capsLockHalfDuplex; + + // set entries indicate keys that are pressed (by us or by the user). + // indexed by keycode. + std::bitset<256> m_keys; + + // set entries indicate keys that are synthetically pressed by us. + // this is normally the same as m_keys. + std::bitset<256> m_fakeKeys; + + // logical to physical button mapping. m_buttons[i] gives the + // physical button for logical button i+1. + std::vector m_buttons; + + // current active modifiers (X key masks) + ModifierMask m_mask; + + // the modifiers that have keys bound to them + ModifierMask m_modifierMask; + + // set bits indicate modifiers that toggle (e.g. caps-lock) + ModifierMask m_toggleModifierMask; + + // keysym to keycode mapping + KeySymMap m_keysymMap; + + // modifier index to keycodes + KeyCodes m_modifierKeycodes[8]; + + // keycode to modifier index + KeyCodeToModifierMap m_keycodeToModifier; + + // modifier keysyms + KeySym m_shiftKeysym; + KeySym m_ctrlKeysym; + KeySym m_altKeysym; + KeySym m_metaKeysym; + KeySym m_superKeysym; + KeySym m_modeSwitchKeysym; + KeySym m_numLockKeysym; + KeySym m_capsLockKeysym; + KeySym m_scrollLockKeysym; + + // modifier masks + ModifierMask m_shiftMask; + ModifierMask m_ctrlMask; + ModifierMask m_altMask; + ModifierMask m_metaMask; + ModifierMask m_superMask; + ModifierMask m_modeSwitchMask; + ModifierMask m_numLockMask; + ModifierMask m_capsLockMask; + ModifierMask m_scrollLockMask; + + // map server key buttons to local keycodes + ServerKeyMap m_serverKeyMap; + + // the keyboard control state the last time this screen was entered + XKeyboardState m_keyControl; + + // stuff to workaround xtest being xinerama unaware. attempting + // to fake a mouse motion under xinerama may behave strangely, + // especially if screen 0 is not at 0,0 or if faking a motion on + // a screen other than screen 0. + bool m_xtestIsXineramaUnaware; + bool m_xinerama; + + // a table of keysym decompositions + static KeySymsMap s_decomposedKeySyms; +}; + +#endif diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp new file mode 100644 index 0000000..05415dd --- /dev/null +++ b/lib/platform/CXWindowsUtil.cpp @@ -0,0 +1,1111 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsUtil.h" +#include "CThread.h" +#include "CLog.h" +#include + +/* + * This table maps keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. + * + * Author: Markus G. Kuhn , + * University of Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven for preparing + * an initial draft of the mapping table. + * + * This software is in the public domain. Share and enjoy! + */ + +struct codepair { + KeySym keysym; + UInt32 ucs4; +} s_keymap[] = { +{ 0x01a1, 0x0104 }, /* Aogonek LATIN CAPITAL LETTER A WITH OGONEK */ +{ 0x01a2, 0x02d8 }, /* breve BREVE */ +{ 0x01a3, 0x0141 }, /* Lstroke LATIN CAPITAL LETTER L WITH STROKE */ +{ 0x01a5, 0x013d }, /* Lcaron LATIN CAPITAL LETTER L WITH CARON */ +{ 0x01a6, 0x015a }, /* Sacute LATIN CAPITAL LETTER S WITH ACUTE */ +{ 0x01a9, 0x0160 }, /* Scaron LATIN CAPITAL LETTER S WITH CARON */ +{ 0x01aa, 0x015e }, /* Scedilla LATIN CAPITAL LETTER S WITH CEDILLA */ +{ 0x01ab, 0x0164 }, /* Tcaron LATIN CAPITAL LETTER T WITH CARON */ +{ 0x01ac, 0x0179 }, /* Zacute LATIN CAPITAL LETTER Z WITH ACUTE */ +{ 0x01ae, 0x017d }, /* Zcaron LATIN CAPITAL LETTER Z WITH CARON */ +{ 0x01af, 0x017b }, /* Zabovedot LATIN CAPITAL LETTER Z WITH DOT ABOVE */ +{ 0x01b1, 0x0105 }, /* aogonek LATIN SMALL LETTER A WITH OGONEK */ +{ 0x01b2, 0x02db }, /* ogonek OGONEK */ +{ 0x01b3, 0x0142 }, /* lstroke LATIN SMALL LETTER L WITH STROKE */ +{ 0x01b5, 0x013e }, /* lcaron LATIN SMALL LETTER L WITH CARON */ +{ 0x01b6, 0x015b }, /* sacute LATIN SMALL LETTER S WITH ACUTE */ +{ 0x01b7, 0x02c7 }, /* caron CARON */ +{ 0x01b9, 0x0161 }, /* scaron LATIN SMALL LETTER S WITH CARON */ +{ 0x01ba, 0x015f }, /* scedilla LATIN SMALL LETTER S WITH CEDILLA */ +{ 0x01bb, 0x0165 }, /* tcaron LATIN SMALL LETTER T WITH CARON */ +{ 0x01bc, 0x017a }, /* zacute LATIN SMALL LETTER Z WITH ACUTE */ +{ 0x01bd, 0x02dd }, /* doubleacute DOUBLE ACUTE ACCENT */ +{ 0x01be, 0x017e }, /* zcaron LATIN SMALL LETTER Z WITH CARON */ +{ 0x01bf, 0x017c }, /* zabovedot LATIN SMALL LETTER Z WITH DOT ABOVE */ +{ 0x01c0, 0x0154 }, /* Racute LATIN CAPITAL LETTER R WITH ACUTE */ +{ 0x01c3, 0x0102 }, /* Abreve LATIN CAPITAL LETTER A WITH BREVE */ +{ 0x01c5, 0x0139 }, /* Lacute LATIN CAPITAL LETTER L WITH ACUTE */ +{ 0x01c6, 0x0106 }, /* Cacute LATIN CAPITAL LETTER C WITH ACUTE */ +{ 0x01c8, 0x010c }, /* Ccaron LATIN CAPITAL LETTER C WITH CARON */ +{ 0x01ca, 0x0118 }, /* Eogonek LATIN CAPITAL LETTER E WITH OGONEK */ +{ 0x01cc, 0x011a }, /* Ecaron LATIN CAPITAL LETTER E WITH CARON */ +{ 0x01cf, 0x010e }, /* Dcaron LATIN CAPITAL LETTER D WITH CARON */ +{ 0x01d0, 0x0110 }, /* Dstroke LATIN CAPITAL LETTER D WITH STROKE */ +{ 0x01d1, 0x0143 }, /* Nacute LATIN CAPITAL LETTER N WITH ACUTE */ +{ 0x01d2, 0x0147 }, /* Ncaron LATIN CAPITAL LETTER N WITH CARON */ +{ 0x01d5, 0x0150 }, /* Odoubleacute LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ +{ 0x01d8, 0x0158 }, /* Rcaron LATIN CAPITAL LETTER R WITH CARON */ +{ 0x01d9, 0x016e }, /* Uring LATIN CAPITAL LETTER U WITH RING ABOVE */ +{ 0x01db, 0x0170 }, /* Udoubleacute LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ +{ 0x01de, 0x0162 }, /* Tcedilla LATIN CAPITAL LETTER T WITH CEDILLA */ +{ 0x01e0, 0x0155 }, /* racute LATIN SMALL LETTER R WITH ACUTE */ +{ 0x01e3, 0x0103 }, /* abreve LATIN SMALL LETTER A WITH BREVE */ +{ 0x01e5, 0x013a }, /* lacute LATIN SMALL LETTER L WITH ACUTE */ +{ 0x01e6, 0x0107 }, /* cacute LATIN SMALL LETTER C WITH ACUTE */ +{ 0x01e8, 0x010d }, /* ccaron LATIN SMALL LETTER C WITH CARON */ +{ 0x01ea, 0x0119 }, /* eogonek LATIN SMALL LETTER E WITH OGONEK */ +{ 0x01ec, 0x011b }, /* ecaron LATIN SMALL LETTER E WITH CARON */ +{ 0x01ef, 0x010f }, /* dcaron LATIN SMALL LETTER D WITH CARON */ +{ 0x01f0, 0x0111 }, /* dstroke LATIN SMALL LETTER D WITH STROKE */ +{ 0x01f1, 0x0144 }, /* nacute LATIN SMALL LETTER N WITH ACUTE */ +{ 0x01f2, 0x0148 }, /* ncaron LATIN SMALL LETTER N WITH CARON */ +{ 0x01f5, 0x0151 }, /* odoubleacute LATIN SMALL LETTER O WITH DOUBLE ACUTE */ +{ 0x01f8, 0x0159 }, /* rcaron LATIN SMALL LETTER R WITH CARON */ +{ 0x01f9, 0x016f }, /* uring LATIN SMALL LETTER U WITH RING ABOVE */ +{ 0x01fb, 0x0171 }, /* udoubleacute LATIN SMALL LETTER U WITH DOUBLE ACUTE */ +{ 0x01fe, 0x0163 }, /* tcedilla LATIN SMALL LETTER T WITH CEDILLA */ +{ 0x01ff, 0x02d9 }, /* abovedot DOT ABOVE */ +{ 0x02a1, 0x0126 }, /* Hstroke LATIN CAPITAL LETTER H WITH STROKE */ +{ 0x02a6, 0x0124 }, /* Hcircumflex LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ +{ 0x02a9, 0x0130 }, /* Iabovedot LATIN CAPITAL LETTER I WITH DOT ABOVE */ +{ 0x02ab, 0x011e }, /* Gbreve LATIN CAPITAL LETTER G WITH BREVE */ +{ 0x02ac, 0x0134 }, /* Jcircumflex LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ +{ 0x02b1, 0x0127 }, /* hstroke LATIN SMALL LETTER H WITH STROKE */ +{ 0x02b6, 0x0125 }, /* hcircumflex LATIN SMALL LETTER H WITH CIRCUMFLEX */ +{ 0x02b9, 0x0131 }, /* idotless LATIN SMALL LETTER DOTLESS I */ +{ 0x02bb, 0x011f }, /* gbreve LATIN SMALL LETTER G WITH BREVE */ +{ 0x02bc, 0x0135 }, /* jcircumflex LATIN SMALL LETTER J WITH CIRCUMFLEX */ +{ 0x02c5, 0x010a }, /* Cabovedot LATIN CAPITAL LETTER C WITH DOT ABOVE */ +{ 0x02c6, 0x0108 }, /* Ccircumflex LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ +{ 0x02d5, 0x0120 }, /* Gabovedot LATIN CAPITAL LETTER G WITH DOT ABOVE */ +{ 0x02d8, 0x011c }, /* Gcircumflex LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ +{ 0x02dd, 0x016c }, /* Ubreve LATIN CAPITAL LETTER U WITH BREVE */ +{ 0x02de, 0x015c }, /* Scircumflex LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ +{ 0x02e5, 0x010b }, /* cabovedot LATIN SMALL LETTER C WITH DOT ABOVE */ +{ 0x02e6, 0x0109 }, /* ccircumflex LATIN SMALL LETTER C WITH CIRCUMFLEX */ +{ 0x02f5, 0x0121 }, /* gabovedot LATIN SMALL LETTER G WITH DOT ABOVE */ +{ 0x02f8, 0x011d }, /* gcircumflex LATIN SMALL LETTER G WITH CIRCUMFLEX */ +{ 0x02fd, 0x016d }, /* ubreve LATIN SMALL LETTER U WITH BREVE */ +{ 0x02fe, 0x015d }, /* scircumflex LATIN SMALL LETTER S WITH CIRCUMFLEX */ +{ 0x03a2, 0x0138 }, /* kra LATIN SMALL LETTER KRA */ +{ 0x03a3, 0x0156 }, /* Rcedilla LATIN CAPITAL LETTER R WITH CEDILLA */ +{ 0x03a5, 0x0128 }, /* Itilde LATIN CAPITAL LETTER I WITH TILDE */ +{ 0x03a6, 0x013b }, /* Lcedilla LATIN CAPITAL LETTER L WITH CEDILLA */ +{ 0x03aa, 0x0112 }, /* Emacron LATIN CAPITAL LETTER E WITH MACRON */ +{ 0x03ab, 0x0122 }, /* Gcedilla LATIN CAPITAL LETTER G WITH CEDILLA */ +{ 0x03ac, 0x0166 }, /* Tslash LATIN CAPITAL LETTER T WITH STROKE */ +{ 0x03b3, 0x0157 }, /* rcedilla LATIN SMALL LETTER R WITH CEDILLA */ +{ 0x03b5, 0x0129 }, /* itilde LATIN SMALL LETTER I WITH TILDE */ +{ 0x03b6, 0x013c }, /* lcedilla LATIN SMALL LETTER L WITH CEDILLA */ +{ 0x03ba, 0x0113 }, /* emacron LATIN SMALL LETTER E WITH MACRON */ +{ 0x03bb, 0x0123 }, /* gcedilla LATIN SMALL LETTER G WITH CEDILLA */ +{ 0x03bc, 0x0167 }, /* tslash LATIN SMALL LETTER T WITH STROKE */ +{ 0x03bd, 0x014a }, /* ENG LATIN CAPITAL LETTER ENG */ +{ 0x03bf, 0x014b }, /* eng LATIN SMALL LETTER ENG */ +{ 0x03c0, 0x0100 }, /* Amacron LATIN CAPITAL LETTER A WITH MACRON */ +{ 0x03c7, 0x012e }, /* Iogonek LATIN CAPITAL LETTER I WITH OGONEK */ +{ 0x03cc, 0x0116 }, /* Eabovedot LATIN CAPITAL LETTER E WITH DOT ABOVE */ +{ 0x03cf, 0x012a }, /* Imacron LATIN CAPITAL LETTER I WITH MACRON */ +{ 0x03d1, 0x0145 }, /* Ncedilla LATIN CAPITAL LETTER N WITH CEDILLA */ +{ 0x03d2, 0x014c }, /* Omacron LATIN CAPITAL LETTER O WITH MACRON */ +{ 0x03d3, 0x0136 }, /* Kcedilla LATIN CAPITAL LETTER K WITH CEDILLA */ +{ 0x03d9, 0x0172 }, /* Uogonek LATIN CAPITAL LETTER U WITH OGONEK */ +{ 0x03dd, 0x0168 }, /* Utilde LATIN CAPITAL LETTER U WITH TILDE */ +{ 0x03de, 0x016a }, /* Umacron LATIN CAPITAL LETTER U WITH MACRON */ +{ 0x03e0, 0x0101 }, /* amacron LATIN SMALL LETTER A WITH MACRON */ +{ 0x03e7, 0x012f }, /* iogonek LATIN SMALL LETTER I WITH OGONEK */ +{ 0x03ec, 0x0117 }, /* eabovedot LATIN SMALL LETTER E WITH DOT ABOVE */ +{ 0x03ef, 0x012b }, /* imacron LATIN SMALL LETTER I WITH MACRON */ +{ 0x03f1, 0x0146 }, /* ncedilla LATIN SMALL LETTER N WITH CEDILLA */ +{ 0x03f2, 0x014d }, /* omacron LATIN SMALL LETTER O WITH MACRON */ +{ 0x03f3, 0x0137 }, /* kcedilla LATIN SMALL LETTER K WITH CEDILLA */ +{ 0x03f9, 0x0173 }, /* uogonek LATIN SMALL LETTER U WITH OGONEK */ +{ 0x03fd, 0x0169 }, /* utilde LATIN SMALL LETTER U WITH TILDE */ +{ 0x03fe, 0x016b }, /* umacron LATIN SMALL LETTER U WITH MACRON */ +{ 0x047e, 0x203e }, /* overline OVERLINE */ +{ 0x04a1, 0x3002 }, /* kana_fullstop IDEOGRAPHIC FULL STOP */ +{ 0x04a2, 0x300c }, /* kana_openingbracket LEFT CORNER BRACKET */ +{ 0x04a3, 0x300d }, /* kana_closingbracket RIGHT CORNER BRACKET */ +{ 0x04a4, 0x3001 }, /* kana_comma IDEOGRAPHIC COMMA */ +{ 0x04a5, 0x30fb }, /* kana_conjunctive KATAKANA MIDDLE DOT */ +{ 0x04a6, 0x30f2 }, /* kana_WO KATAKANA LETTER WO */ +{ 0x04a7, 0x30a1 }, /* kana_a KATAKANA LETTER SMALL A */ +{ 0x04a8, 0x30a3 }, /* kana_i KATAKANA LETTER SMALL I */ +{ 0x04a9, 0x30a5 }, /* kana_u KATAKANA LETTER SMALL U */ +{ 0x04aa, 0x30a7 }, /* kana_e KATAKANA LETTER SMALL E */ +{ 0x04ab, 0x30a9 }, /* kana_o KATAKANA LETTER SMALL O */ +{ 0x04ac, 0x30e3 }, /* kana_ya KATAKANA LETTER SMALL YA */ +{ 0x04ad, 0x30e5 }, /* kana_yu KATAKANA LETTER SMALL YU */ +{ 0x04ae, 0x30e7 }, /* kana_yo KATAKANA LETTER SMALL YO */ +{ 0x04af, 0x30c3 }, /* kana_tsu KATAKANA LETTER SMALL TU */ +{ 0x04b0, 0x30fc }, /* prolongedsound KATAKANA-HIRAGANA PROLONGED SOUND MARK */ +{ 0x04b1, 0x30a2 }, /* kana_A KATAKANA LETTER A */ +{ 0x04b2, 0x30a4 }, /* kana_I KATAKANA LETTER I */ +{ 0x04b3, 0x30a6 }, /* kana_U KATAKANA LETTER U */ +{ 0x04b4, 0x30a8 }, /* kana_E KATAKANA LETTER E */ +{ 0x04b5, 0x30aa }, /* kana_O KATAKANA LETTER O */ +{ 0x04b6, 0x30ab }, /* kana_KA KATAKANA LETTER KA */ +{ 0x04b7, 0x30ad }, /* kana_KI KATAKANA LETTER KI */ +{ 0x04b8, 0x30af }, /* kana_KU KATAKANA LETTER KU */ +{ 0x04b9, 0x30b1 }, /* kana_KE KATAKANA LETTER KE */ +{ 0x04ba, 0x30b3 }, /* kana_KO KATAKANA LETTER KO */ +{ 0x04bb, 0x30b5 }, /* kana_SA KATAKANA LETTER SA */ +{ 0x04bc, 0x30b7 }, /* kana_SHI KATAKANA LETTER SI */ +{ 0x04bd, 0x30b9 }, /* kana_SU KATAKANA LETTER SU */ +{ 0x04be, 0x30bb }, /* kana_SE KATAKANA LETTER SE */ +{ 0x04bf, 0x30bd }, /* kana_SO KATAKANA LETTER SO */ +{ 0x04c0, 0x30bf }, /* kana_TA KATAKANA LETTER TA */ +{ 0x04c1, 0x30c1 }, /* kana_CHI KATAKANA LETTER TI */ +{ 0x04c2, 0x30c4 }, /* kana_TSU KATAKANA LETTER TU */ +{ 0x04c3, 0x30c6 }, /* kana_TE KATAKANA LETTER TE */ +{ 0x04c4, 0x30c8 }, /* kana_TO KATAKANA LETTER TO */ +{ 0x04c5, 0x30ca }, /* kana_NA KATAKANA LETTER NA */ +{ 0x04c6, 0x30cb }, /* kana_NI KATAKANA LETTER NI */ +{ 0x04c7, 0x30cc }, /* kana_NU KATAKANA LETTER NU */ +{ 0x04c8, 0x30cd }, /* kana_NE KATAKANA LETTER NE */ +{ 0x04c9, 0x30ce }, /* kana_NO KATAKANA LETTER NO */ +{ 0x04ca, 0x30cf }, /* kana_HA KATAKANA LETTER HA */ +{ 0x04cb, 0x30d2 }, /* kana_HI KATAKANA LETTER HI */ +{ 0x04cc, 0x30d5 }, /* kana_FU KATAKANA LETTER HU */ +{ 0x04cd, 0x30d8 }, /* kana_HE KATAKANA LETTER HE */ +{ 0x04ce, 0x30db }, /* kana_HO KATAKANA LETTER HO */ +{ 0x04cf, 0x30de }, /* kana_MA KATAKANA LETTER MA */ +{ 0x04d0, 0x30df }, /* kana_MI KATAKANA LETTER MI */ +{ 0x04d1, 0x30e0 }, /* kana_MU KATAKANA LETTER MU */ +{ 0x04d2, 0x30e1 }, /* kana_ME KATAKANA LETTER ME */ +{ 0x04d3, 0x30e2 }, /* kana_MO KATAKANA LETTER MO */ +{ 0x04d4, 0x30e4 }, /* kana_YA KATAKANA LETTER YA */ +{ 0x04d5, 0x30e6 }, /* kana_YU KATAKANA LETTER YU */ +{ 0x04d6, 0x30e8 }, /* kana_YO KATAKANA LETTER YO */ +{ 0x04d7, 0x30e9 }, /* kana_RA KATAKANA LETTER RA */ +{ 0x04d8, 0x30ea }, /* kana_RI KATAKANA LETTER RI */ +{ 0x04d9, 0x30eb }, /* kana_RU KATAKANA LETTER RU */ +{ 0x04da, 0x30ec }, /* kana_RE KATAKANA LETTER RE */ +{ 0x04db, 0x30ed }, /* kana_RO KATAKANA LETTER RO */ +{ 0x04dc, 0x30ef }, /* kana_WA KATAKANA LETTER WA */ +{ 0x04dd, 0x30f3 }, /* kana_N KATAKANA LETTER N */ +{ 0x04de, 0x309b }, /* voicedsound KATAKANA-HIRAGANA VOICED SOUND MARK */ +{ 0x04df, 0x309c }, /* semivoicedsound KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ +{ 0x05ac, 0x060c }, /* Arabic_comma ARABIC COMMA */ +{ 0x05bb, 0x061b }, /* Arabic_semicolon ARABIC SEMICOLON */ +{ 0x05bf, 0x061f }, /* Arabic_question_mark ARABIC QUESTION MARK */ +{ 0x05c1, 0x0621 }, /* Arabic_hamza ARABIC LETTER HAMZA */ +{ 0x05c2, 0x0622 }, /* Arabic_maddaonalef ARABIC LETTER ALEF WITH MADDA ABOVE */ +{ 0x05c3, 0x0623 }, /* Arabic_hamzaonalef ARABIC LETTER ALEF WITH HAMZA ABOVE */ +{ 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ARABIC LETTER WAW WITH HAMZA ABOVE */ +{ 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef ARABIC LETTER ALEF WITH HAMZA BELOW */ +{ 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ARABIC LETTER YEH WITH HAMZA ABOVE */ +{ 0x05c7, 0x0627 }, /* Arabic_alef ARABIC LETTER ALEF */ +{ 0x05c8, 0x0628 }, /* Arabic_beh ARABIC LETTER BEH */ +{ 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ARABIC LETTER TEH MARBUTA */ +{ 0x05ca, 0x062a }, /* Arabic_teh ARABIC LETTER TEH */ +{ 0x05cb, 0x062b }, /* Arabic_theh ARABIC LETTER THEH */ +{ 0x05cc, 0x062c }, /* Arabic_jeem ARABIC LETTER JEEM */ +{ 0x05cd, 0x062d }, /* Arabic_hah ARABIC LETTER HAH */ +{ 0x05ce, 0x062e }, /* Arabic_khah ARABIC LETTER KHAH */ +{ 0x05cf, 0x062f }, /* Arabic_dal ARABIC LETTER DAL */ +{ 0x05d0, 0x0630 }, /* Arabic_thal ARABIC LETTER THAL */ +{ 0x05d1, 0x0631 }, /* Arabic_ra ARABIC LETTER REH */ +{ 0x05d2, 0x0632 }, /* Arabic_zain ARABIC LETTER ZAIN */ +{ 0x05d3, 0x0633 }, /* Arabic_seen ARABIC LETTER SEEN */ +{ 0x05d4, 0x0634 }, /* Arabic_sheen ARABIC LETTER SHEEN */ +{ 0x05d5, 0x0635 }, /* Arabic_sad ARABIC LETTER SAD */ +{ 0x05d6, 0x0636 }, /* Arabic_dad ARABIC LETTER DAD */ +{ 0x05d7, 0x0637 }, /* Arabic_tah ARABIC LETTER TAH */ +{ 0x05d8, 0x0638 }, /* Arabic_zah ARABIC LETTER ZAH */ +{ 0x05d9, 0x0639 }, /* Arabic_ain ARABIC LETTER AIN */ +{ 0x05da, 0x063a }, /* Arabic_ghain ARABIC LETTER GHAIN */ +{ 0x05e0, 0x0640 }, /* Arabic_tatweel ARABIC TATWEEL */ +{ 0x05e1, 0x0641 }, /* Arabic_feh ARABIC LETTER FEH */ +{ 0x05e2, 0x0642 }, /* Arabic_qaf ARABIC LETTER QAF */ +{ 0x05e3, 0x0643 }, /* Arabic_kaf ARABIC LETTER KAF */ +{ 0x05e4, 0x0644 }, /* Arabic_lam ARABIC LETTER LAM */ +{ 0x05e5, 0x0645 }, /* Arabic_meem ARABIC LETTER MEEM */ +{ 0x05e6, 0x0646 }, /* Arabic_noon ARABIC LETTER NOON */ +{ 0x05e7, 0x0647 }, /* Arabic_ha ARABIC LETTER HEH */ +{ 0x05e8, 0x0648 }, /* Arabic_waw ARABIC LETTER WAW */ +{ 0x05e9, 0x0649 }, /* Arabic_alefmaksura ARABIC LETTER ALEF MAKSURA */ +{ 0x05ea, 0x064a }, /* Arabic_yeh ARABIC LETTER YEH */ +{ 0x05eb, 0x064b }, /* Arabic_fathatan ARABIC FATHATAN */ +{ 0x05ec, 0x064c }, /* Arabic_dammatan ARABIC DAMMATAN */ +{ 0x05ed, 0x064d }, /* Arabic_kasratan ARABIC KASRATAN */ +{ 0x05ee, 0x064e }, /* Arabic_fatha ARABIC FATHA */ +{ 0x05ef, 0x064f }, /* Arabic_damma ARABIC DAMMA */ +{ 0x05f0, 0x0650 }, /* Arabic_kasra ARABIC KASRA */ +{ 0x05f1, 0x0651 }, /* Arabic_shadda ARABIC SHADDA */ +{ 0x05f2, 0x0652 }, /* Arabic_sukun ARABIC SUKUN */ +{ 0x06a1, 0x0452 }, /* Serbian_dje CYRILLIC SMALL LETTER DJE */ +{ 0x06a2, 0x0453 }, /* Macedonia_gje CYRILLIC SMALL LETTER GJE */ +{ 0x06a3, 0x0451 }, /* Cyrillic_io CYRILLIC SMALL LETTER IO */ +{ 0x06a4, 0x0454 }, /* Ukrainian_ie CYRILLIC SMALL LETTER UKRAINIAN IE */ +{ 0x06a5, 0x0455 }, /* Macedonia_dse CYRILLIC SMALL LETTER DZE */ +{ 0x06a6, 0x0456 }, /* Ukrainian_i CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ +{ 0x06a7, 0x0457 }, /* Ukrainian_yi CYRILLIC SMALL LETTER YI */ +{ 0x06a8, 0x0458 }, /* Cyrillic_je CYRILLIC SMALL LETTER JE */ +{ 0x06a9, 0x0459 }, /* Cyrillic_lje CYRILLIC SMALL LETTER LJE */ +{ 0x06aa, 0x045a }, /* Cyrillic_nje CYRILLIC SMALL LETTER NJE */ +{ 0x06ab, 0x045b }, /* Serbian_tshe CYRILLIC SMALL LETTER TSHE */ +{ 0x06ac, 0x045c }, /* Macedonia_kje CYRILLIC SMALL LETTER KJE */ +{ 0x06ae, 0x045e }, /* Byelorussian_shortu CYRILLIC SMALL LETTER SHORT U */ +{ 0x06af, 0x045f }, /* Cyrillic_dzhe CYRILLIC SMALL LETTER DZHE */ +{ 0x06b0, 0x2116 }, /* numerosign NUMERO SIGN */ +{ 0x06b1, 0x0402 }, /* Serbian_DJE CYRILLIC CAPITAL LETTER DJE */ +{ 0x06b2, 0x0403 }, /* Macedonia_GJE CYRILLIC CAPITAL LETTER GJE */ +{ 0x06b3, 0x0401 }, /* Cyrillic_IO CYRILLIC CAPITAL LETTER IO */ +{ 0x06b4, 0x0404 }, /* Ukrainian_IE CYRILLIC CAPITAL LETTER UKRAINIAN IE */ +{ 0x06b5, 0x0405 }, /* Macedonia_DSE CYRILLIC CAPITAL LETTER DZE */ +{ 0x06b6, 0x0406 }, /* Ukrainian_I CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ +{ 0x06b7, 0x0407 }, /* Ukrainian_YI CYRILLIC CAPITAL LETTER YI */ +{ 0x06b8, 0x0408 }, /* Cyrillic_JE CYRILLIC CAPITAL LETTER JE */ +{ 0x06b9, 0x0409 }, /* Cyrillic_LJE CYRILLIC CAPITAL LETTER LJE */ +{ 0x06ba, 0x040a }, /* Cyrillic_NJE CYRILLIC CAPITAL LETTER NJE */ +{ 0x06bb, 0x040b }, /* Serbian_TSHE CYRILLIC CAPITAL LETTER TSHE */ +{ 0x06bc, 0x040c }, /* Macedonia_KJE CYRILLIC CAPITAL LETTER KJE */ +{ 0x06be, 0x040e }, /* Byelorussian_SHORTU CYRILLIC CAPITAL LETTER SHORT U */ +{ 0x06bf, 0x040f }, /* Cyrillic_DZHE CYRILLIC CAPITAL LETTER DZHE */ +{ 0x06c0, 0x044e }, /* Cyrillic_yu CYRILLIC SMALL LETTER YU */ +{ 0x06c1, 0x0430 }, /* Cyrillic_a CYRILLIC SMALL LETTER A */ +{ 0x06c2, 0x0431 }, /* Cyrillic_be CYRILLIC SMALL LETTER BE */ +{ 0x06c3, 0x0446 }, /* Cyrillic_tse CYRILLIC SMALL LETTER TSE */ +{ 0x06c4, 0x0434 }, /* Cyrillic_de CYRILLIC SMALL LETTER DE */ +{ 0x06c5, 0x0435 }, /* Cyrillic_ie CYRILLIC SMALL LETTER IE */ +{ 0x06c6, 0x0444 }, /* Cyrillic_ef CYRILLIC SMALL LETTER EF */ +{ 0x06c7, 0x0433 }, /* Cyrillic_ghe CYRILLIC SMALL LETTER GHE */ +{ 0x06c8, 0x0445 }, /* Cyrillic_ha CYRILLIC SMALL LETTER HA */ +{ 0x06c9, 0x0438 }, /* Cyrillic_i CYRILLIC SMALL LETTER I */ +{ 0x06ca, 0x0439 }, /* Cyrillic_shorti CYRILLIC SMALL LETTER SHORT I */ +{ 0x06cb, 0x043a }, /* Cyrillic_ka CYRILLIC SMALL LETTER KA */ +{ 0x06cc, 0x043b }, /* Cyrillic_el CYRILLIC SMALL LETTER EL */ +{ 0x06cd, 0x043c }, /* Cyrillic_em CYRILLIC SMALL LETTER EM */ +{ 0x06ce, 0x043d }, /* Cyrillic_en CYRILLIC SMALL LETTER EN */ +{ 0x06cf, 0x043e }, /* Cyrillic_o CYRILLIC SMALL LETTER O */ +{ 0x06d0, 0x043f }, /* Cyrillic_pe CYRILLIC SMALL LETTER PE */ +{ 0x06d1, 0x044f }, /* Cyrillic_ya CYRILLIC SMALL LETTER YA */ +{ 0x06d2, 0x0440 }, /* Cyrillic_er CYRILLIC SMALL LETTER ER */ +{ 0x06d3, 0x0441 }, /* Cyrillic_es CYRILLIC SMALL LETTER ES */ +{ 0x06d4, 0x0442 }, /* Cyrillic_te CYRILLIC SMALL LETTER TE */ +{ 0x06d5, 0x0443 }, /* Cyrillic_u CYRILLIC SMALL LETTER U */ +{ 0x06d6, 0x0436 }, /* Cyrillic_zhe CYRILLIC SMALL LETTER ZHE */ +{ 0x06d7, 0x0432 }, /* Cyrillic_ve CYRILLIC SMALL LETTER VE */ +{ 0x06d8, 0x044c }, /* Cyrillic_softsign CYRILLIC SMALL LETTER SOFT SIGN */ +{ 0x06d9, 0x044b }, /* Cyrillic_yeru CYRILLIC SMALL LETTER YERU */ +{ 0x06da, 0x0437 }, /* Cyrillic_ze CYRILLIC SMALL LETTER ZE */ +{ 0x06db, 0x0448 }, /* Cyrillic_sha CYRILLIC SMALL LETTER SHA */ +{ 0x06dc, 0x044d }, /* Cyrillic_e CYRILLIC SMALL LETTER E */ +{ 0x06dd, 0x0449 }, /* Cyrillic_shcha CYRILLIC SMALL LETTER SHCHA */ +{ 0x06de, 0x0447 }, /* Cyrillic_che CYRILLIC SMALL LETTER CHE */ +{ 0x06df, 0x044a }, /* Cyrillic_hardsign CYRILLIC SMALL LETTER HARD SIGN */ +{ 0x06e0, 0x042e }, /* Cyrillic_YU CYRILLIC CAPITAL LETTER YU */ +{ 0x06e1, 0x0410 }, /* Cyrillic_A CYRILLIC CAPITAL LETTER A */ +{ 0x06e2, 0x0411 }, /* Cyrillic_BE CYRILLIC CAPITAL LETTER BE */ +{ 0x06e3, 0x0426 }, /* Cyrillic_TSE CYRILLIC CAPITAL LETTER TSE */ +{ 0x06e4, 0x0414 }, /* Cyrillic_DE CYRILLIC CAPITAL LETTER DE */ +{ 0x06e5, 0x0415 }, /* Cyrillic_IE CYRILLIC CAPITAL LETTER IE */ +{ 0x06e6, 0x0424 }, /* Cyrillic_EF CYRILLIC CAPITAL LETTER EF */ +{ 0x06e7, 0x0413 }, /* Cyrillic_GHE CYRILLIC CAPITAL LETTER GHE */ +{ 0x06e8, 0x0425 }, /* Cyrillic_HA CYRILLIC CAPITAL LETTER HA */ +{ 0x06e9, 0x0418 }, /* Cyrillic_I CYRILLIC CAPITAL LETTER I */ +{ 0x06ea, 0x0419 }, /* Cyrillic_SHORTI CYRILLIC CAPITAL LETTER SHORT I */ +{ 0x06eb, 0x041a }, /* Cyrillic_KA CYRILLIC CAPITAL LETTER KA */ +{ 0x06ec, 0x041b }, /* Cyrillic_EL CYRILLIC CAPITAL LETTER EL */ +{ 0x06ed, 0x041c }, /* Cyrillic_EM CYRILLIC CAPITAL LETTER EM */ +{ 0x06ee, 0x041d }, /* Cyrillic_EN CYRILLIC CAPITAL LETTER EN */ +{ 0x06ef, 0x041e }, /* Cyrillic_O CYRILLIC CAPITAL LETTER O */ +{ 0x06f0, 0x041f }, /* Cyrillic_PE CYRILLIC CAPITAL LETTER PE */ +{ 0x06f1, 0x042f }, /* Cyrillic_YA CYRILLIC CAPITAL LETTER YA */ +{ 0x06f2, 0x0420 }, /* Cyrillic_ER CYRILLIC CAPITAL LETTER ER */ +{ 0x06f3, 0x0421 }, /* Cyrillic_ES CYRILLIC CAPITAL LETTER ES */ +{ 0x06f4, 0x0422 }, /* Cyrillic_TE CYRILLIC CAPITAL LETTER TE */ +{ 0x06f5, 0x0423 }, /* Cyrillic_U CYRILLIC CAPITAL LETTER U */ +{ 0x06f6, 0x0416 }, /* Cyrillic_ZHE CYRILLIC CAPITAL LETTER ZHE */ +{ 0x06f7, 0x0412 }, /* Cyrillic_VE CYRILLIC CAPITAL LETTER VE */ +{ 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN CYRILLIC CAPITAL LETTER SOFT SIGN */ +{ 0x06f9, 0x042b }, /* Cyrillic_YERU CYRILLIC CAPITAL LETTER YERU */ +{ 0x06fa, 0x0417 }, /* Cyrillic_ZE CYRILLIC CAPITAL LETTER ZE */ +{ 0x06fb, 0x0428 }, /* Cyrillic_SHA CYRILLIC CAPITAL LETTER SHA */ +{ 0x06fc, 0x042d }, /* Cyrillic_E CYRILLIC CAPITAL LETTER E */ +{ 0x06fd, 0x0429 }, /* Cyrillic_SHCHA CYRILLIC CAPITAL LETTER SHCHA */ +{ 0x06fe, 0x0427 }, /* Cyrillic_CHE CYRILLIC CAPITAL LETTER CHE */ +{ 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN CYRILLIC CAPITAL LETTER HARD SIGN */ +{ 0x07a1, 0x0386 }, /* Greek_ALPHAaccent GREEK CAPITAL LETTER ALPHA WITH TONOS */ +{ 0x07a2, 0x0388 }, /* Greek_EPSILONaccent GREEK CAPITAL LETTER EPSILON WITH TONOS */ +{ 0x07a3, 0x0389 }, /* Greek_ETAaccent GREEK CAPITAL LETTER ETA WITH TONOS */ +{ 0x07a4, 0x038a }, /* Greek_IOTAaccent GREEK CAPITAL LETTER IOTA WITH TONOS */ +{ 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ +{ 0x07a7, 0x038c }, /* Greek_OMICRONaccent GREEK CAPITAL LETTER OMICRON WITH TONOS */ +{ 0x07a8, 0x038e }, /* Greek_UPSILONaccent GREEK CAPITAL LETTER UPSILON WITH TONOS */ +{ 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ +{ 0x07ab, 0x038f }, /* Greek_OMEGAaccent GREEK CAPITAL LETTER OMEGA WITH TONOS */ +{ 0x07ae, 0x0385 }, /* Greek_accentdieresis GREEK DIALYTIKA TONOS */ +{ 0x07af, 0x2015 }, /* Greek_horizbar HORIZONTAL BAR */ +{ 0x07b1, 0x03ac }, /* Greek_alphaaccent GREEK SMALL LETTER ALPHA WITH TONOS */ +{ 0x07b2, 0x03ad }, /* Greek_epsilonaccent GREEK SMALL LETTER EPSILON WITH TONOS */ +{ 0x07b3, 0x03ae }, /* Greek_etaaccent GREEK SMALL LETTER ETA WITH TONOS */ +{ 0x07b4, 0x03af }, /* Greek_iotaaccent GREEK SMALL LETTER IOTA WITH TONOS */ +{ 0x07b5, 0x03ca }, /* Greek_iotadieresis GREEK SMALL LETTER IOTA WITH DIALYTIKA */ +{ 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ +{ 0x07b7, 0x03cc }, /* Greek_omicronaccent GREEK SMALL LETTER OMICRON WITH TONOS */ +{ 0x07b8, 0x03cd }, /* Greek_upsilonaccent GREEK SMALL LETTER UPSILON WITH TONOS */ +{ 0x07b9, 0x03cb }, /* Greek_upsilondieresis GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ +{ 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ +{ 0x07bb, 0x03ce }, /* Greek_omegaaccent GREEK SMALL LETTER OMEGA WITH TONOS */ +{ 0x07c1, 0x0391 }, /* Greek_ALPHA GREEK CAPITAL LETTER ALPHA */ +{ 0x07c2, 0x0392 }, /* Greek_BETA GREEK CAPITAL LETTER BETA */ +{ 0x07c3, 0x0393 }, /* Greek_GAMMA GREEK CAPITAL LETTER GAMMA */ +{ 0x07c4, 0x0394 }, /* Greek_DELTA GREEK CAPITAL LETTER DELTA */ +{ 0x07c5, 0x0395 }, /* Greek_EPSILON GREEK CAPITAL LETTER EPSILON */ +{ 0x07c6, 0x0396 }, /* Greek_ZETA GREEK CAPITAL LETTER ZETA */ +{ 0x07c7, 0x0397 }, /* Greek_ETA GREEK CAPITAL LETTER ETA */ +{ 0x07c8, 0x0398 }, /* Greek_THETA GREEK CAPITAL LETTER THETA */ +{ 0x07c9, 0x0399 }, /* Greek_IOTA GREEK CAPITAL LETTER IOTA */ +{ 0x07ca, 0x039a }, /* Greek_KAPPA GREEK CAPITAL LETTER KAPPA */ +{ 0x07cb, 0x039b }, /* Greek_LAMBDA GREEK CAPITAL LETTER LAMDA */ +{ 0x07cc, 0x039c }, /* Greek_MU GREEK CAPITAL LETTER MU */ +{ 0x07cd, 0x039d }, /* Greek_NU GREEK CAPITAL LETTER NU */ +{ 0x07ce, 0x039e }, /* Greek_XI GREEK CAPITAL LETTER XI */ +{ 0x07cf, 0x039f }, /* Greek_OMICRON GREEK CAPITAL LETTER OMICRON */ +{ 0x07d0, 0x03a0 }, /* Greek_PI GREEK CAPITAL LETTER PI */ +{ 0x07d1, 0x03a1 }, /* Greek_RHO GREEK CAPITAL LETTER RHO */ +{ 0x07d2, 0x03a3 }, /* Greek_SIGMA GREEK CAPITAL LETTER SIGMA */ +{ 0x07d4, 0x03a4 }, /* Greek_TAU GREEK CAPITAL LETTER TAU */ +{ 0x07d5, 0x03a5 }, /* Greek_UPSILON GREEK CAPITAL LETTER UPSILON */ +{ 0x07d6, 0x03a6 }, /* Greek_PHI GREEK CAPITAL LETTER PHI */ +{ 0x07d7, 0x03a7 }, /* Greek_CHI GREEK CAPITAL LETTER CHI */ +{ 0x07d8, 0x03a8 }, /* Greek_PSI GREEK CAPITAL LETTER PSI */ +{ 0x07d9, 0x03a9 }, /* Greek_OMEGA GREEK CAPITAL LETTER OMEGA */ +{ 0x07e1, 0x03b1 }, /* Greek_alpha GREEK SMALL LETTER ALPHA */ +{ 0x07e2, 0x03b2 }, /* Greek_beta GREEK SMALL LETTER BETA */ +{ 0x07e3, 0x03b3 }, /* Greek_gamma GREEK SMALL LETTER GAMMA */ +{ 0x07e4, 0x03b4 }, /* Greek_delta GREEK SMALL LETTER DELTA */ +{ 0x07e5, 0x03b5 }, /* Greek_epsilon GREEK SMALL LETTER EPSILON */ +{ 0x07e6, 0x03b6 }, /* Greek_zeta GREEK SMALL LETTER ZETA */ +{ 0x07e7, 0x03b7 }, /* Greek_eta GREEK SMALL LETTER ETA */ +{ 0x07e8, 0x03b8 }, /* Greek_theta GREEK SMALL LETTER THETA */ +{ 0x07e9, 0x03b9 }, /* Greek_iota GREEK SMALL LETTER IOTA */ +{ 0x07ea, 0x03ba }, /* Greek_kappa GREEK SMALL LETTER KAPPA */ +{ 0x07eb, 0x03bb }, /* Greek_lambda GREEK SMALL LETTER LAMDA */ +{ 0x07ec, 0x03bc }, /* Greek_mu GREEK SMALL LETTER MU */ +{ 0x07ed, 0x03bd }, /* Greek_nu GREEK SMALL LETTER NU */ +{ 0x07ee, 0x03be }, /* Greek_xi GREEK SMALL LETTER XI */ +{ 0x07ef, 0x03bf }, /* Greek_omicron GREEK SMALL LETTER OMICRON */ +{ 0x07f0, 0x03c0 }, /* Greek_pi GREEK SMALL LETTER PI */ +{ 0x07f1, 0x03c1 }, /* Greek_rho GREEK SMALL LETTER RHO */ +{ 0x07f2, 0x03c3 }, /* Greek_sigma GREEK SMALL LETTER SIGMA */ +{ 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma GREEK SMALL LETTER FINAL SIGMA */ +{ 0x07f4, 0x03c4 }, /* Greek_tau GREEK SMALL LETTER TAU */ +{ 0x07f5, 0x03c5 }, /* Greek_upsilon GREEK SMALL LETTER UPSILON */ +{ 0x07f6, 0x03c6 }, /* Greek_phi GREEK SMALL LETTER PHI */ +{ 0x07f7, 0x03c7 }, /* Greek_chi GREEK SMALL LETTER CHI */ +{ 0x07f8, 0x03c8 }, /* Greek_psi GREEK SMALL LETTER PSI */ +{ 0x07f9, 0x03c9 }, /* Greek_omega GREEK SMALL LETTER OMEGA */ +{ 0x08a1, 0x23b7 }, /* leftradical ??? */ +{ 0x08a2, 0x250c }, /* topleftradical BOX DRAWINGS LIGHT DOWN AND RIGHT */ +{ 0x08a3, 0x2500 }, /* horizconnector BOX DRAWINGS LIGHT HORIZONTAL */ +{ 0x08a4, 0x2320 }, /* topintegral TOP HALF INTEGRAL */ +{ 0x08a5, 0x2321 }, /* botintegral BOTTOM HALF INTEGRAL */ +{ 0x08a6, 0x2502 }, /* vertconnector BOX DRAWINGS LIGHT VERTICAL */ +{ 0x08a7, 0x23a1 }, /* topleftsqbracket ??? */ +{ 0x08a8, 0x23a3 }, /* botleftsqbracket ??? */ +{ 0x08a9, 0x23a4 }, /* toprightsqbracket ??? */ +{ 0x08aa, 0x23a6 }, /* botrightsqbracket ??? */ +{ 0x08ab, 0x239b }, /* topleftparens ??? */ +{ 0x08ac, 0x239d }, /* botleftparens ??? */ +{ 0x08ad, 0x239e }, /* toprightparens ??? */ +{ 0x08ae, 0x23a0 }, /* botrightparens ??? */ +{ 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ??? */ +{ 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ??? */ +{ 0x08bc, 0x2264 }, /* lessthanequal LESS-THAN OR EQUAL TO */ +{ 0x08bd, 0x2260 }, /* notequal NOT EQUAL TO */ +{ 0x08be, 0x2265 }, /* greaterthanequal GREATER-THAN OR EQUAL TO */ +{ 0x08bf, 0x222b }, /* integral INTEGRAL */ +{ 0x08c0, 0x2234 }, /* therefore THEREFORE */ +{ 0x08c1, 0x221d }, /* variation PROPORTIONAL TO */ +{ 0x08c2, 0x221e }, /* infinity INFINITY */ +{ 0x08c5, 0x2207 }, /* nabla NABLA */ +{ 0x08c8, 0x223c }, /* approximate TILDE OPERATOR */ +{ 0x08c9, 0x2243 }, /* similarequal ASYMPTOTICALLY EQUAL TO */ +{ 0x08cd, 0x21d4 }, /* ifonlyif LEFT RIGHT DOUBLE ARROW */ +{ 0x08ce, 0x21d2 }, /* implies RIGHTWARDS DOUBLE ARROW */ +{ 0x08cf, 0x2261 }, /* identical IDENTICAL TO */ +{ 0x08d6, 0x221a }, /* radical SQUARE ROOT */ +{ 0x08da, 0x2282 }, /* includedin SUBSET OF */ +{ 0x08db, 0x2283 }, /* includes SUPERSET OF */ +{ 0x08dc, 0x2229 }, /* intersection INTERSECTION */ +{ 0x08dd, 0x222a }, /* union UNION */ +{ 0x08de, 0x2227 }, /* logicaland LOGICAL AND */ +{ 0x08df, 0x2228 }, /* logicalor LOGICAL OR */ +{ 0x08ef, 0x2202 }, /* partialderivative PARTIAL DIFFERENTIAL */ +{ 0x08f6, 0x0192 }, /* function LATIN SMALL LETTER F WITH HOOK */ +{ 0x08fb, 0x2190 }, /* leftarrow LEFTWARDS ARROW */ +{ 0x08fc, 0x2191 }, /* uparrow UPWARDS ARROW */ +{ 0x08fd, 0x2192 }, /* rightarrow RIGHTWARDS ARROW */ +{ 0x08fe, 0x2193 }, /* downarrow DOWNWARDS ARROW */ +/* 0x09df blank ??? */ +{ 0x09e0, 0x25c6 }, /* soliddiamond BLACK DIAMOND */ +{ 0x09e1, 0x2592 }, /* checkerboard MEDIUM SHADE */ +{ 0x09e2, 0x2409 }, /* ht SYMBOL FOR HORIZONTAL TABULATION */ +{ 0x09e3, 0x240c }, /* ff SYMBOL FOR FORM FEED */ +{ 0x09e4, 0x240d }, /* cr SYMBOL FOR CARRIAGE RETURN */ +{ 0x09e5, 0x240a }, /* lf SYMBOL FOR LINE FEED */ +{ 0x09e8, 0x2424 }, /* nl SYMBOL FOR NEWLINE */ +{ 0x09e9, 0x240b }, /* vt SYMBOL FOR VERTICAL TABULATION */ +{ 0x09ea, 0x2518 }, /* lowrightcorner BOX DRAWINGS LIGHT UP AND LEFT */ +{ 0x09eb, 0x2510 }, /* uprightcorner BOX DRAWINGS LIGHT DOWN AND LEFT */ +{ 0x09ec, 0x250c }, /* upleftcorner BOX DRAWINGS LIGHT DOWN AND RIGHT */ +{ 0x09ed, 0x2514 }, /* lowleftcorner BOX DRAWINGS LIGHT UP AND RIGHT */ +{ 0x09ee, 0x253c }, /* crossinglines BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ +{ 0x09ef, 0x23ba }, /* horizlinescan1 HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ +{ 0x09f0, 0x23bb }, /* horizlinescan3 HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ +{ 0x09f1, 0x2500 }, /* horizlinescan5 BOX DRAWINGS LIGHT HORIZONTAL */ +{ 0x09f2, 0x23bc }, /* horizlinescan7 HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ +{ 0x09f3, 0x23bd }, /* horizlinescan9 HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ +{ 0x09f4, 0x251c }, /* leftt BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ +{ 0x09f5, 0x2524 }, /* rightt BOX DRAWINGS LIGHT VERTICAL AND LEFT */ +{ 0x09f6, 0x2534 }, /* bott BOX DRAWINGS LIGHT UP AND HORIZONTAL */ +{ 0x09f7, 0x252c }, /* topt BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ +{ 0x09f8, 0x2502 }, /* vertbar BOX DRAWINGS LIGHT VERTICAL */ +{ 0x0aa1, 0x2003 }, /* emspace EM SPACE */ +{ 0x0aa2, 0x2002 }, /* enspace EN SPACE */ +{ 0x0aa3, 0x2004 }, /* em3space THREE-PER-EM SPACE */ +{ 0x0aa4, 0x2005 }, /* em4space FOUR-PER-EM SPACE */ +{ 0x0aa5, 0x2007 }, /* digitspace FIGURE SPACE */ +{ 0x0aa6, 0x2008 }, /* punctspace PUNCTUATION SPACE */ +{ 0x0aa7, 0x2009 }, /* thinspace THIN SPACE */ +{ 0x0aa8, 0x200a }, /* hairspace HAIR SPACE */ +{ 0x0aa9, 0x2014 }, /* emdash EM DASH */ +{ 0x0aaa, 0x2013 }, /* endash EN DASH */ +/* 0x0aac signifblank ??? */ +{ 0x0aae, 0x2026 }, /* ellipsis HORIZONTAL ELLIPSIS */ +{ 0x0aaf, 0x2025 }, /* doubbaselinedot TWO DOT LEADER */ +{ 0x0ab0, 0x2153 }, /* onethird VULGAR FRACTION ONE THIRD */ +{ 0x0ab1, 0x2154 }, /* twothirds VULGAR FRACTION TWO THIRDS */ +{ 0x0ab2, 0x2155 }, /* onefifth VULGAR FRACTION ONE FIFTH */ +{ 0x0ab3, 0x2156 }, /* twofifths VULGAR FRACTION TWO FIFTHS */ +{ 0x0ab4, 0x2157 }, /* threefifths VULGAR FRACTION THREE FIFTHS */ +{ 0x0ab5, 0x2158 }, /* fourfifths VULGAR FRACTION FOUR FIFTHS */ +{ 0x0ab6, 0x2159 }, /* onesixth VULGAR FRACTION ONE SIXTH */ +{ 0x0ab7, 0x215a }, /* fivesixths VULGAR FRACTION FIVE SIXTHS */ +{ 0x0ab8, 0x2105 }, /* careof CARE OF */ +{ 0x0abb, 0x2012 }, /* figdash FIGURE DASH */ +{ 0x0abc, 0x2329 }, /* leftanglebracket LEFT-POINTING ANGLE BRACKET */ +/* 0x0abd decimalpoint ??? */ +{ 0x0abe, 0x232a }, /* rightanglebracket RIGHT-POINTING ANGLE BRACKET */ +/* 0x0abf marker ??? */ +{ 0x0ac3, 0x215b }, /* oneeighth VULGAR FRACTION ONE EIGHTH */ +{ 0x0ac4, 0x215c }, /* threeeighths VULGAR FRACTION THREE EIGHTHS */ +{ 0x0ac5, 0x215d }, /* fiveeighths VULGAR FRACTION FIVE EIGHTHS */ +{ 0x0ac6, 0x215e }, /* seveneighths VULGAR FRACTION SEVEN EIGHTHS */ +{ 0x0ac9, 0x2122 }, /* trademark TRADE MARK SIGN */ +{ 0x0aca, 0x2613 }, /* signaturemark SALTIRE */ +/* 0x0acb trademarkincircle ??? */ +{ 0x0acc, 0x25c1 }, /* leftopentriangle WHITE LEFT-POINTING TRIANGLE */ +{ 0x0acd, 0x25b7 }, /* rightopentriangle WHITE RIGHT-POINTING TRIANGLE */ +{ 0x0ace, 0x25cb }, /* emopencircle WHITE CIRCLE */ +{ 0x0acf, 0x25af }, /* emopenrectangle WHITE VERTICAL RECTANGLE */ +{ 0x0ad0, 0x2018 }, /* leftsinglequotemark LEFT SINGLE QUOTATION MARK */ +{ 0x0ad1, 0x2019 }, /* rightsinglequotemark RIGHT SINGLE QUOTATION MARK */ +{ 0x0ad2, 0x201c }, /* leftdoublequotemark LEFT DOUBLE QUOTATION MARK */ +{ 0x0ad3, 0x201d }, /* rightdoublequotemark RIGHT DOUBLE QUOTATION MARK */ +{ 0x0ad4, 0x211e }, /* prescription PRESCRIPTION TAKE */ +{ 0x0ad6, 0x2032 }, /* minutes PRIME */ +{ 0x0ad7, 0x2033 }, /* seconds DOUBLE PRIME */ +{ 0x0ad9, 0x271d }, /* latincross LATIN CROSS */ +/* 0x0ada hexagram ??? */ +{ 0x0adb, 0x25ac }, /* filledrectbullet BLACK RECTANGLE */ +{ 0x0adc, 0x25c0 }, /* filledlefttribullet BLACK LEFT-POINTING TRIANGLE */ +{ 0x0add, 0x25b6 }, /* filledrighttribullet BLACK RIGHT-POINTING TRIANGLE */ +{ 0x0ade, 0x25cf }, /* emfilledcircle BLACK CIRCLE */ +{ 0x0adf, 0x25ae }, /* emfilledrect BLACK VERTICAL RECTANGLE */ +{ 0x0ae0, 0x25e6 }, /* enopencircbullet WHITE BULLET */ +{ 0x0ae1, 0x25ab }, /* enopensquarebullet WHITE SMALL SQUARE */ +{ 0x0ae2, 0x25ad }, /* openrectbullet WHITE RECTANGLE */ +{ 0x0ae3, 0x25b3 }, /* opentribulletup WHITE UP-POINTING TRIANGLE */ +{ 0x0ae4, 0x25bd }, /* opentribulletdown WHITE DOWN-POINTING TRIANGLE */ +{ 0x0ae5, 0x2606 }, /* openstar WHITE STAR */ +{ 0x0ae6, 0x2022 }, /* enfilledcircbullet BULLET */ +{ 0x0ae7, 0x25aa }, /* enfilledsqbullet BLACK SMALL SQUARE */ +{ 0x0ae8, 0x25b2 }, /* filledtribulletup BLACK UP-POINTING TRIANGLE */ +{ 0x0ae9, 0x25bc }, /* filledtribulletdown BLACK DOWN-POINTING TRIANGLE */ +{ 0x0aea, 0x261c }, /* leftpointer WHITE LEFT POINTING INDEX */ +{ 0x0aeb, 0x261e }, /* rightpointer WHITE RIGHT POINTING INDEX */ +{ 0x0aec, 0x2663 }, /* club BLACK CLUB SUIT */ +{ 0x0aed, 0x2666 }, /* diamond BLACK DIAMOND SUIT */ +{ 0x0aee, 0x2665 }, /* heart BLACK HEART SUIT */ +{ 0x0af0, 0x2720 }, /* maltesecross MALTESE CROSS */ +{ 0x0af1, 0x2020 }, /* dagger DAGGER */ +{ 0x0af2, 0x2021 }, /* doubledagger DOUBLE DAGGER */ +{ 0x0af3, 0x2713 }, /* checkmark CHECK MARK */ +{ 0x0af4, 0x2717 }, /* ballotcross BALLOT X */ +{ 0x0af5, 0x266f }, /* musicalsharp MUSIC SHARP SIGN */ +{ 0x0af6, 0x266d }, /* musicalflat MUSIC FLAT SIGN */ +{ 0x0af7, 0x2642 }, /* malesymbol MALE SIGN */ +{ 0x0af8, 0x2640 }, /* femalesymbol FEMALE SIGN */ +{ 0x0af9, 0x260e }, /* telephone BLACK TELEPHONE */ +{ 0x0afa, 0x2315 }, /* telephonerecorder TELEPHONE RECORDER */ +{ 0x0afb, 0x2117 }, /* phonographcopyright SOUND RECORDING COPYRIGHT */ +{ 0x0afc, 0x2038 }, /* caret CARET */ +{ 0x0afd, 0x201a }, /* singlelowquotemark SINGLE LOW-9 QUOTATION MARK */ +{ 0x0afe, 0x201e }, /* doublelowquotemark DOUBLE LOW-9 QUOTATION MARK */ +/* 0x0aff cursor ??? */ +{ 0x0ba3, 0x003c }, /* leftcaret LESS-THAN SIGN */ +{ 0x0ba6, 0x003e }, /* rightcaret GREATER-THAN SIGN */ +{ 0x0ba8, 0x2228 }, /* downcaret LOGICAL OR */ +{ 0x0ba9, 0x2227 }, /* upcaret LOGICAL AND */ +{ 0x0bc0, 0x00af }, /* overbar MACRON */ +{ 0x0bc2, 0x22a5 }, /* downtack UP TACK */ +{ 0x0bc3, 0x2229 }, /* upshoe INTERSECTION */ +{ 0x0bc4, 0x230a }, /* downstile LEFT FLOOR */ +{ 0x0bc6, 0x005f }, /* underbar LOW LINE */ +{ 0x0bca, 0x2218 }, /* jot RING OPERATOR */ +{ 0x0bcc, 0x2395 }, /* quad APL FUNCTIONAL SYMBOL QUAD */ +{ 0x0bce, 0x22a4 }, /* uptack DOWN TACK */ +{ 0x0bcf, 0x25cb }, /* circle WHITE CIRCLE */ +{ 0x0bd3, 0x2308 }, /* upstile LEFT CEILING */ +{ 0x0bd6, 0x222a }, /* downshoe UNION */ +{ 0x0bd8, 0x2283 }, /* rightshoe SUPERSET OF */ +{ 0x0bda, 0x2282 }, /* leftshoe SUBSET OF */ +{ 0x0bdc, 0x22a2 }, /* lefttack RIGHT TACK */ +{ 0x0bfc, 0x22a3 }, /* righttack LEFT TACK */ +{ 0x0cdf, 0x2017 }, /* hebrew_doublelowline DOUBLE LOW LINE */ +{ 0x0ce0, 0x05d0 }, /* hebrew_aleph HEBREW LETTER ALEF */ +{ 0x0ce1, 0x05d1 }, /* hebrew_bet HEBREW LETTER BET */ +{ 0x0ce2, 0x05d2 }, /* hebrew_gimel HEBREW LETTER GIMEL */ +{ 0x0ce3, 0x05d3 }, /* hebrew_dalet HEBREW LETTER DALET */ +{ 0x0ce4, 0x05d4 }, /* hebrew_he HEBREW LETTER HE */ +{ 0x0ce5, 0x05d5 }, /* hebrew_waw HEBREW LETTER VAV */ +{ 0x0ce6, 0x05d6 }, /* hebrew_zain HEBREW LETTER ZAYIN */ +{ 0x0ce7, 0x05d7 }, /* hebrew_chet HEBREW LETTER HET */ +{ 0x0ce8, 0x05d8 }, /* hebrew_tet HEBREW LETTER TET */ +{ 0x0ce9, 0x05d9 }, /* hebrew_yod HEBREW LETTER YOD */ +{ 0x0cea, 0x05da }, /* hebrew_finalkaph HEBREW LETTER FINAL KAF */ +{ 0x0ceb, 0x05db }, /* hebrew_kaph HEBREW LETTER KAF */ +{ 0x0cec, 0x05dc }, /* hebrew_lamed HEBREW LETTER LAMED */ +{ 0x0ced, 0x05dd }, /* hebrew_finalmem HEBREW LETTER FINAL MEM */ +{ 0x0cee, 0x05de }, /* hebrew_mem HEBREW LETTER MEM */ +{ 0x0cef, 0x05df }, /* hebrew_finalnun HEBREW LETTER FINAL NUN */ +{ 0x0cf0, 0x05e0 }, /* hebrew_nun HEBREW LETTER NUN */ +{ 0x0cf1, 0x05e1 }, /* hebrew_samech HEBREW LETTER SAMEKH */ +{ 0x0cf2, 0x05e2 }, /* hebrew_ayin HEBREW LETTER AYIN */ +{ 0x0cf3, 0x05e3 }, /* hebrew_finalpe HEBREW LETTER FINAL PE */ +{ 0x0cf4, 0x05e4 }, /* hebrew_pe HEBREW LETTER PE */ +{ 0x0cf5, 0x05e5 }, /* hebrew_finalzade HEBREW LETTER FINAL TSADI */ +{ 0x0cf6, 0x05e6 }, /* hebrew_zade HEBREW LETTER TSADI */ +{ 0x0cf7, 0x05e7 }, /* hebrew_qoph HEBREW LETTER QOF */ +{ 0x0cf8, 0x05e8 }, /* hebrew_resh HEBREW LETTER RESH */ +{ 0x0cf9, 0x05e9 }, /* hebrew_shin HEBREW LETTER SHIN */ +{ 0x0cfa, 0x05ea }, /* hebrew_taw HEBREW LETTER TAV */ +{ 0x0da1, 0x0e01 }, /* Thai_kokai THAI CHARACTER KO KAI */ +{ 0x0da2, 0x0e02 }, /* Thai_khokhai THAI CHARACTER KHO KHAI */ +{ 0x0da3, 0x0e03 }, /* Thai_khokhuat THAI CHARACTER KHO KHUAT */ +{ 0x0da4, 0x0e04 }, /* Thai_khokhwai THAI CHARACTER KHO KHWAI */ +{ 0x0da5, 0x0e05 }, /* Thai_khokhon THAI CHARACTER KHO KHON */ +{ 0x0da6, 0x0e06 }, /* Thai_khorakhang THAI CHARACTER KHO RAKHANG */ +{ 0x0da7, 0x0e07 }, /* Thai_ngongu THAI CHARACTER NGO NGU */ +{ 0x0da8, 0x0e08 }, /* Thai_chochan THAI CHARACTER CHO CHAN */ +{ 0x0da9, 0x0e09 }, /* Thai_choching THAI CHARACTER CHO CHING */ +{ 0x0daa, 0x0e0a }, /* Thai_chochang THAI CHARACTER CHO CHANG */ +{ 0x0dab, 0x0e0b }, /* Thai_soso THAI CHARACTER SO SO */ +{ 0x0dac, 0x0e0c }, /* Thai_chochoe THAI CHARACTER CHO CHOE */ +{ 0x0dad, 0x0e0d }, /* Thai_yoying THAI CHARACTER YO YING */ +{ 0x0dae, 0x0e0e }, /* Thai_dochada THAI CHARACTER DO CHADA */ +{ 0x0daf, 0x0e0f }, /* Thai_topatak THAI CHARACTER TO PATAK */ +{ 0x0db0, 0x0e10 }, /* Thai_thothan THAI CHARACTER THO THAN */ +{ 0x0db1, 0x0e11 }, /* Thai_thonangmontho THAI CHARACTER THO NANGMONTHO */ +{ 0x0db2, 0x0e12 }, /* Thai_thophuthao THAI CHARACTER THO PHUTHAO */ +{ 0x0db3, 0x0e13 }, /* Thai_nonen THAI CHARACTER NO NEN */ +{ 0x0db4, 0x0e14 }, /* Thai_dodek THAI CHARACTER DO DEK */ +{ 0x0db5, 0x0e15 }, /* Thai_totao THAI CHARACTER TO TAO */ +{ 0x0db6, 0x0e16 }, /* Thai_thothung THAI CHARACTER THO THUNG */ +{ 0x0db7, 0x0e17 }, /* Thai_thothahan THAI CHARACTER THO THAHAN */ +{ 0x0db8, 0x0e18 }, /* Thai_thothong THAI CHARACTER THO THONG */ +{ 0x0db9, 0x0e19 }, /* Thai_nonu THAI CHARACTER NO NU */ +{ 0x0dba, 0x0e1a }, /* Thai_bobaimai THAI CHARACTER BO BAIMAI */ +{ 0x0dbb, 0x0e1b }, /* Thai_popla THAI CHARACTER PO PLA */ +{ 0x0dbc, 0x0e1c }, /* Thai_phophung THAI CHARACTER PHO PHUNG */ +{ 0x0dbd, 0x0e1d }, /* Thai_fofa THAI CHARACTER FO FA */ +{ 0x0dbe, 0x0e1e }, /* Thai_phophan THAI CHARACTER PHO PHAN */ +{ 0x0dbf, 0x0e1f }, /* Thai_fofan THAI CHARACTER FO FAN */ +{ 0x0dc0, 0x0e20 }, /* Thai_phosamphao THAI CHARACTER PHO SAMPHAO */ +{ 0x0dc1, 0x0e21 }, /* Thai_moma THAI CHARACTER MO MA */ +{ 0x0dc2, 0x0e22 }, /* Thai_yoyak THAI CHARACTER YO YAK */ +{ 0x0dc3, 0x0e23 }, /* Thai_rorua THAI CHARACTER RO RUA */ +{ 0x0dc4, 0x0e24 }, /* Thai_ru THAI CHARACTER RU */ +{ 0x0dc5, 0x0e25 }, /* Thai_loling THAI CHARACTER LO LING */ +{ 0x0dc6, 0x0e26 }, /* Thai_lu THAI CHARACTER LU */ +{ 0x0dc7, 0x0e27 }, /* Thai_wowaen THAI CHARACTER WO WAEN */ +{ 0x0dc8, 0x0e28 }, /* Thai_sosala THAI CHARACTER SO SALA */ +{ 0x0dc9, 0x0e29 }, /* Thai_sorusi THAI CHARACTER SO RUSI */ +{ 0x0dca, 0x0e2a }, /* Thai_sosua THAI CHARACTER SO SUA */ +{ 0x0dcb, 0x0e2b }, /* Thai_hohip THAI CHARACTER HO HIP */ +{ 0x0dcc, 0x0e2c }, /* Thai_lochula THAI CHARACTER LO CHULA */ +{ 0x0dcd, 0x0e2d }, /* Thai_oang THAI CHARACTER O ANG */ +{ 0x0dce, 0x0e2e }, /* Thai_honokhuk THAI CHARACTER HO NOKHUK */ +{ 0x0dcf, 0x0e2f }, /* Thai_paiyannoi THAI CHARACTER PAIYANNOI */ +{ 0x0dd0, 0x0e30 }, /* Thai_saraa THAI CHARACTER SARA A */ +{ 0x0dd1, 0x0e31 }, /* Thai_maihanakat THAI CHARACTER MAI HAN-AKAT */ +{ 0x0dd2, 0x0e32 }, /* Thai_saraaa THAI CHARACTER SARA AA */ +{ 0x0dd3, 0x0e33 }, /* Thai_saraam THAI CHARACTER SARA AM */ +{ 0x0dd4, 0x0e34 }, /* Thai_sarai THAI CHARACTER SARA I */ +{ 0x0dd5, 0x0e35 }, /* Thai_saraii THAI CHARACTER SARA II */ +{ 0x0dd6, 0x0e36 }, /* Thai_saraue THAI CHARACTER SARA UE */ +{ 0x0dd7, 0x0e37 }, /* Thai_sarauee THAI CHARACTER SARA UEE */ +{ 0x0dd8, 0x0e38 }, /* Thai_sarau THAI CHARACTER SARA U */ +{ 0x0dd9, 0x0e39 }, /* Thai_sarauu THAI CHARACTER SARA UU */ +{ 0x0dda, 0x0e3a }, /* Thai_phinthu THAI CHARACTER PHINTHU */ +/* 0x0dde Thai_maihanakat_maitho ??? */ +{ 0x0ddf, 0x0e3f }, /* Thai_baht THAI CURRENCY SYMBOL BAHT */ +{ 0x0de0, 0x0e40 }, /* Thai_sarae THAI CHARACTER SARA E */ +{ 0x0de1, 0x0e41 }, /* Thai_saraae THAI CHARACTER SARA AE */ +{ 0x0de2, 0x0e42 }, /* Thai_sarao THAI CHARACTER SARA O */ +{ 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan THAI CHARACTER SARA AI MAIMUAN */ +{ 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai THAI CHARACTER SARA AI MAIMALAI */ +{ 0x0de5, 0x0e45 }, /* Thai_lakkhangyao THAI CHARACTER LAKKHANGYAO */ +{ 0x0de6, 0x0e46 }, /* Thai_maiyamok THAI CHARACTER MAIYAMOK */ +{ 0x0de7, 0x0e47 }, /* Thai_maitaikhu THAI CHARACTER MAITAIKHU */ +{ 0x0de8, 0x0e48 }, /* Thai_maiek THAI CHARACTER MAI EK */ +{ 0x0de9, 0x0e49 }, /* Thai_maitho THAI CHARACTER MAI THO */ +{ 0x0dea, 0x0e4a }, /* Thai_maitri THAI CHARACTER MAI TRI */ +{ 0x0deb, 0x0e4b }, /* Thai_maichattawa THAI CHARACTER MAI CHATTAWA */ +{ 0x0dec, 0x0e4c }, /* Thai_thanthakhat THAI CHARACTER THANTHAKHAT */ +{ 0x0ded, 0x0e4d }, /* Thai_nikhahit THAI CHARACTER NIKHAHIT */ +{ 0x0df0, 0x0e50 }, /* Thai_leksun THAI DIGIT ZERO */ +{ 0x0df1, 0x0e51 }, /* Thai_leknung THAI DIGIT ONE */ +{ 0x0df2, 0x0e52 }, /* Thai_leksong THAI DIGIT TWO */ +{ 0x0df3, 0x0e53 }, /* Thai_leksam THAI DIGIT THREE */ +{ 0x0df4, 0x0e54 }, /* Thai_leksi THAI DIGIT FOUR */ +{ 0x0df5, 0x0e55 }, /* Thai_lekha THAI DIGIT FIVE */ +{ 0x0df6, 0x0e56 }, /* Thai_lekhok THAI DIGIT SIX */ +{ 0x0df7, 0x0e57 }, /* Thai_lekchet THAI DIGIT SEVEN */ +{ 0x0df8, 0x0e58 }, /* Thai_lekpaet THAI DIGIT EIGHT */ +{ 0x0df9, 0x0e59 }, /* Thai_lekkao THAI DIGIT NINE */ +{ 0x0ea1, 0x3131 }, /* Hangul_Kiyeog HANGUL LETTER KIYEOK */ +{ 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog HANGUL LETTER SSANGKIYEOK */ +{ 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios HANGUL LETTER KIYEOK-SIOS */ +{ 0x0ea4, 0x3134 }, /* Hangul_Nieun HANGUL LETTER NIEUN */ +{ 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj HANGUL LETTER NIEUN-CIEUC */ +{ 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh HANGUL LETTER NIEUN-HIEUH */ +{ 0x0ea7, 0x3137 }, /* Hangul_Dikeud HANGUL LETTER TIKEUT */ +{ 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud HANGUL LETTER SSANGTIKEUT */ +{ 0x0ea9, 0x3139 }, /* Hangul_Rieul HANGUL LETTER RIEUL */ +{ 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog HANGUL LETTER RIEUL-KIYEOK */ +{ 0x0eab, 0x313b }, /* Hangul_RieulMieum HANGUL LETTER RIEUL-MIEUM */ +{ 0x0eac, 0x313c }, /* Hangul_RieulPieub HANGUL LETTER RIEUL-PIEUP */ +{ 0x0ead, 0x313d }, /* Hangul_RieulSios HANGUL LETTER RIEUL-SIOS */ +{ 0x0eae, 0x313e }, /* Hangul_RieulTieut HANGUL LETTER RIEUL-THIEUTH */ +{ 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf HANGUL LETTER RIEUL-PHIEUPH */ +{ 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh HANGUL LETTER RIEUL-HIEUH */ +{ 0x0eb1, 0x3141 }, /* Hangul_Mieum HANGUL LETTER MIEUM */ +{ 0x0eb2, 0x3142 }, /* Hangul_Pieub HANGUL LETTER PIEUP */ +{ 0x0eb3, 0x3143 }, /* Hangul_SsangPieub HANGUL LETTER SSANGPIEUP */ +{ 0x0eb4, 0x3144 }, /* Hangul_PieubSios HANGUL LETTER PIEUP-SIOS */ +{ 0x0eb5, 0x3145 }, /* Hangul_Sios HANGUL LETTER SIOS */ +{ 0x0eb6, 0x3146 }, /* Hangul_SsangSios HANGUL LETTER SSANGSIOS */ +{ 0x0eb7, 0x3147 }, /* Hangul_Ieung HANGUL LETTER IEUNG */ +{ 0x0eb8, 0x3148 }, /* Hangul_Jieuj HANGUL LETTER CIEUC */ +{ 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj HANGUL LETTER SSANGCIEUC */ +{ 0x0eba, 0x314a }, /* Hangul_Cieuc HANGUL LETTER CHIEUCH */ +{ 0x0ebb, 0x314b }, /* Hangul_Khieuq HANGUL LETTER KHIEUKH */ +{ 0x0ebc, 0x314c }, /* Hangul_Tieut HANGUL LETTER THIEUTH */ +{ 0x0ebd, 0x314d }, /* Hangul_Phieuf HANGUL LETTER PHIEUPH */ +{ 0x0ebe, 0x314e }, /* Hangul_Hieuh HANGUL LETTER HIEUH */ +{ 0x0ebf, 0x314f }, /* Hangul_A HANGUL LETTER A */ +{ 0x0ec0, 0x3150 }, /* Hangul_AE HANGUL LETTER AE */ +{ 0x0ec1, 0x3151 }, /* Hangul_YA HANGUL LETTER YA */ +{ 0x0ec2, 0x3152 }, /* Hangul_YAE HANGUL LETTER YAE */ +{ 0x0ec3, 0x3153 }, /* Hangul_EO HANGUL LETTER EO */ +{ 0x0ec4, 0x3154 }, /* Hangul_E HANGUL LETTER E */ +{ 0x0ec5, 0x3155 }, /* Hangul_YEO HANGUL LETTER YEO */ +{ 0x0ec6, 0x3156 }, /* Hangul_YE HANGUL LETTER YE */ +{ 0x0ec7, 0x3157 }, /* Hangul_O HANGUL LETTER O */ +{ 0x0ec8, 0x3158 }, /* Hangul_WA HANGUL LETTER WA */ +{ 0x0ec9, 0x3159 }, /* Hangul_WAE HANGUL LETTER WAE */ +{ 0x0eca, 0x315a }, /* Hangul_OE HANGUL LETTER OE */ +{ 0x0ecb, 0x315b }, /* Hangul_YO HANGUL LETTER YO */ +{ 0x0ecc, 0x315c }, /* Hangul_U HANGUL LETTER U */ +{ 0x0ecd, 0x315d }, /* Hangul_WEO HANGUL LETTER WEO */ +{ 0x0ece, 0x315e }, /* Hangul_WE HANGUL LETTER WE */ +{ 0x0ecf, 0x315f }, /* Hangul_WI HANGUL LETTER WI */ +{ 0x0ed0, 0x3160 }, /* Hangul_YU HANGUL LETTER YU */ +{ 0x0ed1, 0x3161 }, /* Hangul_EU HANGUL LETTER EU */ +{ 0x0ed2, 0x3162 }, /* Hangul_YI HANGUL LETTER YI */ +{ 0x0ed3, 0x3163 }, /* Hangul_I HANGUL LETTER I */ +{ 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog HANGUL JONGSEONG KIYEOK */ +{ 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog HANGUL JONGSEONG SSANGKIYEOK */ +{ 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios HANGUL JONGSEONG KIYEOK-SIOS */ +{ 0x0ed7, 0x11ab }, /* Hangul_J_Nieun HANGUL JONGSEONG NIEUN */ +{ 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj HANGUL JONGSEONG NIEUN-CIEUC */ +{ 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh HANGUL JONGSEONG NIEUN-HIEUH */ +{ 0x0eda, 0x11ae }, /* Hangul_J_Dikeud HANGUL JONGSEONG TIKEUT */ +{ 0x0edb, 0x11af }, /* Hangul_J_Rieul HANGUL JONGSEONG RIEUL */ +{ 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog HANGUL JONGSEONG RIEUL-KIYEOK */ +{ 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum HANGUL JONGSEONG RIEUL-MIEUM */ +{ 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub HANGUL JONGSEONG RIEUL-PIEUP */ +{ 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios HANGUL JONGSEONG RIEUL-SIOS */ +{ 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut HANGUL JONGSEONG RIEUL-THIEUTH */ +{ 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf HANGUL JONGSEONG RIEUL-PHIEUPH */ +{ 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh HANGUL JONGSEONG RIEUL-HIEUH */ +{ 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum HANGUL JONGSEONG MIEUM */ +{ 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub HANGUL JONGSEONG PIEUP */ +{ 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios HANGUL JONGSEONG PIEUP-SIOS */ +{ 0x0ee6, 0x11ba }, /* Hangul_J_Sios HANGUL JONGSEONG SIOS */ +{ 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios HANGUL JONGSEONG SSANGSIOS */ +{ 0x0ee8, 0x11bc }, /* Hangul_J_Ieung HANGUL JONGSEONG IEUNG */ +{ 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj HANGUL JONGSEONG CIEUC */ +{ 0x0eea, 0x11be }, /* Hangul_J_Cieuc HANGUL JONGSEONG CHIEUCH */ +{ 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq HANGUL JONGSEONG KHIEUKH */ +{ 0x0eec, 0x11c0 }, /* Hangul_J_Tieut HANGUL JONGSEONG THIEUTH */ +{ 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf HANGUL JONGSEONG PHIEUPH */ +{ 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh HANGUL JONGSEONG HIEUH */ +{ 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh HANGUL LETTER RIEUL-YEORINHIEUH */ +{ 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum HANGUL LETTER KAPYEOUNMIEUM */ +{ 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub HANGUL LETTER KAPYEOUNPIEUP */ +{ 0x0ef2, 0x317f }, /* Hangul_PanSios HANGUL LETTER PANSIOS */ +{ 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung HANGUL LETTER YESIEUNG */ +{ 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf HANGUL LETTER KAPYEOUNPHIEUPH */ +{ 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh HANGUL LETTER YEORINHIEUH */ +{ 0x0ef6, 0x318d }, /* Hangul_AraeA HANGUL LETTER ARAEA */ +{ 0x0ef7, 0x318e }, /* Hangul_AraeAE HANGUL LETTER ARAEAE */ +{ 0x0ef8, 0x11eb }, /* Hangul_J_PanSios HANGUL JONGSEONG PANSIOS */ +{ 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung HANGUL JONGSEONG YESIEUNG */ +{ 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh HANGUL JONGSEONG YEORINHIEUH */ +{ 0x0eff, 0x20a9 }, /* Korean_Won WON SIGN */ +{ 0x13a4, 0x20ac }, /* Euro EURO SIGN */ +{ 0x13bc, 0x0152 }, /* OE LATIN CAPITAL LIGATURE OE */ +{ 0x13bd, 0x0153 }, /* oe LATIN SMALL LIGATURE OE */ +{ 0x13be, 0x0178 }, /* Ydiaeresis LATIN CAPITAL LETTER Y WITH DIAERESIS */ +{ 0x20ac, 0x20ac } /* EuroSign EURO SIGN */ +}; + + +// +// CXWindowsUtil +// + +CXWindowsUtil::CKeySymMap CXWindowsUtil::s_keySymToUCS4; +CXWindowsUtil::CUCS4Map CXWindowsUtil::s_UCS4ToKeySym; + +bool +CXWindowsUtil::getWindowProperty(Display* display, Window window, + Atom property, CString* data, Atom* type, + SInt32* format, bool deleteProperty) +{ + assert(display != NULL); + + Atom actualType; + int actualDatumSize; + + // ignore errors. XGetWindowProperty() will report failure. + CXWindowsUtil::CErrorLock lock(display); + + // read the property + bool okay = true; + const long length = XMaxRequestSize(display); + long offset = 0; + unsigned long bytesLeft = 1; + while (bytesLeft != 0) { + // get more data + unsigned long numItems; + unsigned char* rawData; + if (XGetWindowProperty(display, window, property, + offset, length, False, AnyPropertyType, + &actualType, &actualDatumSize, + &numItems, &bytesLeft, &rawData) != Success || + actualType == None || actualDatumSize == 0) { + // failed + okay = false; + break; + } + + // compute bytes read and advance offset + unsigned long numBytes; + switch (actualDatumSize) { + case 8: + default: + numBytes = numItems; + offset += numItems / 4; + break; + + case 16: + numBytes = 2 * numItems; + offset += numItems / 2; + break; + + case 32: + numBytes = 4 * numItems; + offset += numItems; + break; + } + + // append data + if (data != NULL) { + data->append((char*)rawData, numBytes); + } + else { + // data is not required so don't try to get any more + bytesLeft = 0; + } + + // done with returned data + XFree(rawData); + } + + // delete the property if requested + if (deleteProperty) { + XDeleteProperty(display, window, property); + } + + // save property info + if (type != NULL) { + *type = actualType; + } + if (format != NULL) { + *format = static_cast(actualDatumSize); + } + + if (okay) { + LOG((CLOG_DEBUG2 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size())); + return true; + } + else { + LOG((CLOG_DEBUG2 "can't read property %d on window 0x%08x", property, window)); + return false; + } +} + +bool +CXWindowsUtil::setWindowProperty(Display* display, Window window, + Atom property, const void* vdata, UInt32 size, + Atom type, SInt32 format) +{ + const UInt32 length = 4 * XMaxRequestSize(display); + const unsigned char* data = reinterpret_cast(vdata); + const UInt32 datumSize = static_cast(format / 8); + + // save errors + bool error = false; + CXWindowsUtil::CErrorLock lock(display, &error); + + // how much data to send in first chunk? + UInt32 chunkSize = size; + if (chunkSize > length) { + chunkSize = length; + } + + // send first chunk + XChangeProperty(display, window, property, + type, format, PropModeReplace, + data, chunkSize / datumSize); + + // append remaining chunks + data += chunkSize; + size -= chunkSize; + while (!error && size > 0) { + chunkSize = size; + if (chunkSize > length) { + chunkSize = length; + } + XChangeProperty(display, window, property, + type, format, PropModeAppend, + data, chunkSize / datumSize); + data += chunkSize; + size -= chunkSize; + } + + return !error; +} + +Time +CXWindowsUtil::getCurrentTime(Display* display, Window window) +{ + // select property events on window + XWindowAttributes attr; + XGetWindowAttributes(display, window, &attr); + XSelectInput(display, window, attr.your_event_mask | PropertyChangeMask); + + // make a property name to receive dummy change + Atom atom = XInternAtom(display, "TIMESTAMP", False); + + // do a zero-length append to get the current time + unsigned char dummy; + XChangeProperty(display, window, atom, + XA_INTEGER, 8, + PropModeAppend, + &dummy, 0); + + // look for property notify events with the following + CPropertyNotifyPredicateInfo filter; + filter.m_window = window; + filter.m_property = atom; + + // wait for reply + XEvent xevent; + XIfEvent(display, &xevent, &CXWindowsUtil::propertyNotifyPredicate, + (XPointer)&filter); + assert(xevent.type == PropertyNotify); + assert(xevent.xproperty.window == window); + assert(xevent.xproperty.atom == atom); + + // restore event mask + XSelectInput(display, window, attr.your_event_mask); + + return xevent.xproperty.time; +} + +UInt32 +CXWindowsUtil::mapKeySymToUCS4(KeySym k) +{ + initKeyMaps(); + + CKeySymMap::const_iterator index = s_keySymToUCS4.find(k); + if (index != s_keySymToUCS4.end()) { + return index->second; + } + else { + return 0x0000ffff; + } +} + +KeySym +CXWindowsUtil::mapUCS4ToKeySym(UInt32 c) +{ + initKeyMaps(); + + CUCS4Map::const_iterator index = s_UCS4ToKeySym.find(c); + if (index != s_UCS4ToKeySym.end()) { + return index->second; + } + else { + return NoSymbol; + } +} + +Bool +CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) +{ + CPropertyNotifyPredicateInfo* filter = + reinterpret_cast(arg); + return (xevent->type == PropertyNotify && + xevent->xproperty.window == filter->m_window && + xevent->xproperty.atom == filter->m_property && + xevent->xproperty.state == PropertyNewValue) ? True : False; +} + +void +CXWindowsUtil::initKeyMaps() +{ + // note that keysyms 0x13a4 and 0x20ac both map to 0x20ac, which + // means ambiguity when converting unicode 0x20ac to a keysym. + // as written, the m_UCS4ToKeySym will map to XK_EuroSign. + if (s_keySymToUCS4.empty()) { + for (size_t i =0; i < sizeof(s_keymap) / sizeof(s_keymap[0]); ++i) { + s_keySymToUCS4[s_keymap[i].keysym] = s_keymap[i].ucs4; + s_UCS4ToKeySym[s_keymap[i].ucs4] = s_keymap[i].keysym; + } + } +} + + +// +// CXWindowsUtil::CErrorLock +// + +CXWindowsUtil::CErrorLock* CXWindowsUtil::CErrorLock::s_top = NULL; + +CXWindowsUtil::CErrorLock::CErrorLock(Display* display) : + m_display(display) +{ + install(&CXWindowsUtil::CErrorLock::ignoreHandler, NULL); +} + +CXWindowsUtil::CErrorLock::CErrorLock(Display* display, bool* flag) : + m_display(display) +{ + install(&CXWindowsUtil::CErrorLock::saveHandler, flag); +} + +CXWindowsUtil::CErrorLock::CErrorLock(Display* display, + ErrorHandler handler, void* data) : + m_display(display) +{ + install(handler, data); +} + +CXWindowsUtil::CErrorLock::~CErrorLock() +{ + // make sure everything finishes before uninstalling handler + if (m_display != NULL) { + XSync(m_display, False); + } + + // restore old handler + XSetErrorHandler(m_oldXHandler); + s_top = m_next; +} + +void +CXWindowsUtil::CErrorLock::install(ErrorHandler handler, void* data) +{ + // make sure everything finishes before installing handler + if (m_display != NULL) { + XSync(m_display, False); + } + + // install handler + m_handler = handler; + m_userData = data; + m_oldXHandler = XSetErrorHandler( + &CXWindowsUtil::CErrorLock::internalHandler); + m_next = s_top; + s_top = this; +} + +int +CXWindowsUtil::CErrorLock::internalHandler(Display* display, XErrorEvent* event) +{ + if (s_top != NULL && s_top->m_handler != NULL) { + s_top->m_handler(display, event, s_top->m_userData); + } + return 0; +} + +void +CXWindowsUtil::CErrorLock::ignoreHandler(Display*, XErrorEvent* e, void*) +{ + LOG((CLOG_DEBUG1 "ignoring X error: %d", e->error_code)); +} + +void +CXWindowsUtil::CErrorLock::saveHandler(Display*, XErrorEvent* e, void* flag) +{ + LOG((CLOG_DEBUG1 "flagging X error: %d", e->error_code)); + *reinterpret_cast(flag) = true; +} diff --git a/lib/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h new file mode 100644 index 0000000..7136b73 --- /dev/null +++ b/lib/platform/CXWindowsUtil.h @@ -0,0 +1,141 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSUTIL_H +#define CXWINDOWSUTIL_H + +#include "CString.h" +#include "BasicTypes.h" +#include "stdmap.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +//! X11 utility functions +class CXWindowsUtil { +public: + //! Get property + /*! + Gets property \c property on \c window. \b Appends the data to + \c *data if \c data is not NULL, saves the property type in \c *type + if \c type is not NULL, and saves the property format in \c *format + if \c format is not NULL. If \c deleteProperty is true then the + property is deleted after being read. + */ + static bool getWindowProperty(Display*, + Window window, Atom property, + CString* data, Atom* type, + SInt32* format, bool deleteProperty); + + //! Set property + /*! + Sets property \c property on \c window to \c size bytes of data from + \c data. + */ + static bool setWindowProperty(Display*, + Window window, Atom property, + const void* data, UInt32 size, + Atom type, SInt32 format); + + //! Get X server time + /*! + Returns the current X server time. + */ + static Time getCurrentTime(Display*, Window); + + //! Convert KeySym to UCS-4 + /*! + Converts a KeySym to the equivalent UCS-4 character. Returns + 0x0000ffff if the KeySym cannot be mapped. + */ + static UInt32 mapKeySymToUCS4(KeySym); + + //! Convert UCS-4 to KeySym + /*! + Converts a UCS-4 character to the equivalent KeySym. Returns + NoSymbol (0) if the character cannot be mapped. + */ + static KeySym mapUCS4ToKeySym(UInt32); + + //! X11 error handler + /*! + This class sets an X error handler in the c'tor and restores the + previous error handler in the d'tor. A lock should only be + installed while the display is locked by the thread. + + CErrorLock() ignores errors + CErrorLock(bool* flag) sets *flag to true if any error occurs + */ + class CErrorLock { + public: + //! Error handler type + typedef void (*ErrorHandler)(Display*, XErrorEvent*, void* userData); + + /*! + Ignore X11 errors. + */ + CErrorLock(Display*); + + /*! + Set \c *errorFlag if any error occurs. + */ + CErrorLock(Display*, bool* errorFlag); + + /*! + Call \c handler on each error. + */ + CErrorLock(Display*, ErrorHandler handler, void* userData); + + ~CErrorLock(); + + private: + void install(ErrorHandler, void*); + static int internalHandler(Display*, XErrorEvent*); + static void ignoreHandler(Display*, XErrorEvent*, void*); + static void saveHandler(Display*, XErrorEvent*, void*); + + private: + typedef int (*XErrorHandler)(Display*, XErrorEvent*); + + Display* m_display; + ErrorHandler m_handler; + void* m_userData; + XErrorHandler m_oldXHandler; + CErrorLock* m_next; + static CErrorLock* s_top; + }; + +private: + class CPropertyNotifyPredicateInfo { + public: + Window m_window; + Atom m_property; + }; + + static Bool propertyNotifyPredicate(Display*, + XEvent* xevent, XPointer arg); + + static void initKeyMaps(); + +private: + typedef std::map CKeySymMap; + typedef std::map CUCS4Map; + + static CKeySymMap s_keySymToUCS4; + static CUCS4Map s_UCS4ToKeySym; +}; + +#endif diff --git a/lib/platform/IMSWindowsScreenEventHandler.h b/lib/platform/IMSWindowsScreenEventHandler.h new file mode 100644 index 0000000..ee24ea8 --- /dev/null +++ b/lib/platform/IMSWindowsScreenEventHandler.h @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IMSWINDOWSSCREENEVENTHANDLER_H +#define IMSWINDOWSSCREENEVENTHANDLER_H + +#include "IScreenEventHandler.h" +#define WIN32_LEAN_AND_MEAN +#include + +//! MS Windows screen event handler interface +class IMSWindowsScreenEventHandler : public IScreenEventHandler { +public: + //! @name manipulators + //@{ + + //! Notify of window creation + /*! + This is called after the window is created. + */ + virtual void postCreateWindow(HWND) = 0; + + //! Notify of window destruction + /*! + This is called before the window is destroyed. + */ + virtual void preDestroyWindow(HWND) = 0; + + //! Notify of newly accessible desktop + /*! + This is called when the user switched from an inaccessible desktop + to an accessible desktop. + */ + virtual void onAccessibleDesktop() = 0; + + //@} + + // IScreenEventHandler overrides + virtual void onScreensaver(bool activated) = 0; + virtual bool onPreDispatch(const CEvent* event) = 0; + virtual bool onEvent(CEvent* event) = 0; + virtual SInt32 getJumpZoneSize() const = 0; +}; + +#endif diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am new file mode 100644 index 0000000..ea082e1 --- /dev/null +++ b/lib/platform/Makefile.am @@ -0,0 +1,75 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + makehook.dsp \ + platform.dsp \ + synrgyhk.dsp \ + CMSWindowsClipboard.cpp \ + CMSWindowsClipboardAnyTextConverter.cpp \ + CMSWindowsClipboardTextConverter.cpp \ + CMSWindowsClipboardUTF16Converter.cpp \ + CMSWindowsPrimaryScreen.cpp \ + CMSWindowsScreen.cpp \ + CMSWindowsScreenSaver.cpp \ + CMSWindowsSecondaryScreen.cpp \ + CSynergyHook.cpp \ + CMSWindowsClipboard.h \ + CMSWindowsClipboardAnyTextConverter.h \ + CMSWindowsClipboardTextConverter.h \ + CMSWindowsClipboardUTF16Converter.h \ + CMSWindowsPrimaryScreen.h \ + CMSWindowsScreen.h \ + CMSWindowsScreenSaver.h \ + CMSWindowsSecondaryScreen.h \ + CSynergyHook.h \ + IMSWindowsScreenEventHandler.h \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libplatform.a +libplatform_a_SOURCES = \ + CXWindowsClipboard.cpp \ + CXWindowsClipboardTextConverter.cpp \ + CXWindowsClipboardUCS2Converter.cpp \ + CXWindowsClipboardUTF8Converter.cpp \ + CXWindowsPrimaryScreen.cpp \ + CXWindowsScreen.cpp \ + CXWindowsScreenSaver.cpp \ + CXWindowsSecondaryScreen.cpp \ + CXWindowsUtil.cpp \ + CXWindowsClipboard.h \ + CXWindowsClipboardTextConverter.h \ + CXWindowsClipboardUCS2Converter.h \ + CXWindowsClipboardUTF8Converter.h \ + CXWindowsPrimaryScreen.h \ + CXWindowsScreen.h \ + CXWindowsScreenSaver.h \ + CXWindowsSecondaryScreen.h \ + CXWindowsUtil.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/synergy \ + $(NULL) diff --git a/lib/platform/Makefile.in b/lib/platform/Makefile.in new file mode 100644 index 0000000..f2f4d45 --- /dev/null +++ b/lib/platform/Makefile.in @@ -0,0 +1,396 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + makehook.dsp \ + platform.dsp \ + synrgyhk.dsp \ + CMSWindowsClipboard.cpp \ + CMSWindowsClipboardAnyTextConverter.cpp \ + CMSWindowsClipboardTextConverter.cpp \ + CMSWindowsClipboardUTF16Converter.cpp \ + CMSWindowsPrimaryScreen.cpp \ + CMSWindowsScreen.cpp \ + CMSWindowsScreenSaver.cpp \ + CMSWindowsSecondaryScreen.cpp \ + CSynergyHook.cpp \ + CMSWindowsClipboard.h \ + CMSWindowsClipboardAnyTextConverter.h \ + CMSWindowsClipboardTextConverter.h \ + CMSWindowsClipboardUTF16Converter.h \ + CMSWindowsPrimaryScreen.h \ + CMSWindowsScreen.h \ + CMSWindowsScreenSaver.h \ + CMSWindowsSecondaryScreen.h \ + CSynergyHook.h \ + IMSWindowsScreenEventHandler.h \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libplatform.a +libplatform_a_SOURCES = \ + CXWindowsClipboard.cpp \ + CXWindowsClipboardTextConverter.cpp \ + CXWindowsClipboardUCS2Converter.cpp \ + CXWindowsClipboardUTF8Converter.cpp \ + CXWindowsPrimaryScreen.cpp \ + CXWindowsScreen.cpp \ + CXWindowsScreenSaver.cpp \ + CXWindowsSecondaryScreen.cpp \ + CXWindowsUtil.cpp \ + CXWindowsClipboard.h \ + CXWindowsClipboardTextConverter.h \ + CXWindowsClipboardUCS2Converter.h \ + CXWindowsClipboardUTF8Converter.h \ + CXWindowsPrimaryScreen.h \ + CXWindowsScreen.h \ + CXWindowsScreenSaver.h \ + CXWindowsSecondaryScreen.h \ + CXWindowsUtil.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/synergy \ + $(NULL) + +subdir = lib/platform +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libplatform_a_AR = $(AR) cru +libplatform_a_LIBADD = +am_libplatform_a_OBJECTS = CXWindowsClipboard.$(OBJEXT) \ + CXWindowsClipboardTextConverter.$(OBJEXT) \ + CXWindowsClipboardUCS2Converter.$(OBJEXT) \ + CXWindowsClipboardUTF8Converter.$(OBJEXT) \ + CXWindowsPrimaryScreen.$(OBJEXT) CXWindowsScreen.$(OBJEXT) \ + CXWindowsScreenSaver.$(OBJEXT) \ + CXWindowsSecondaryScreen.$(OBJEXT) CXWindowsUtil.$(OBJEXT) +libplatform_a_OBJECTS = $(am_libplatform_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CXWindowsClipboard.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsClipboardTextConverter.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsClipboardUCS2Converter.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsClipboardUTF8Converter.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsPrimaryScreen.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsScreen.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsScreenSaver.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsSecondaryScreen.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsUtil.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libplatform_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libplatform_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/platform/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libplatform.a: $(libplatform_a_OBJECTS) $(libplatform_a_DEPENDENCIES) + -rm -f libplatform.a + $(libplatform_a_AR) libplatform.a $(libplatform_a_OBJECTS) $(libplatform_a_LIBADD) + $(RANLIB) libplatform.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsClipboard.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsClipboardTextConverter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsClipboardUCS2Converter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsClipboardUTF8Converter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsPrimaryScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsScreenSaver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsSecondaryScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsUtil.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/platform/makehook.dsp b/lib/platform/makehook.dsp new file mode 100644 index 0000000..2f66320 --- /dev/null +++ b/lib/platform/makehook.dsp @@ -0,0 +1,63 @@ +# Microsoft Developer Studio Project File - Name="makehook" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=makehook - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "makehook.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makehook.mak" CFG="makehook - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "makehook - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "makehook - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "makehook - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "makehook - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "makehook - Win32 Release" +# Name "makehook - Win32 Debug" +# End Target +# End Project diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp new file mode 100644 index 0000000..ed478c6 --- /dev/null +++ b/lib/platform/platform.dsp @@ -0,0 +1,162 @@ +# Microsoft Developer Studio Project File - Name="platform" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=platform - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "platform.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "platform.mak" CFG="platform - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "platform - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "platform - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "platform - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "platform - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "platform - Win32 Release" +# Name "platform - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardAnyTextConverter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardTextConverter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreenSaver.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardAnyTextConverter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardTextConverter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardUTF16Converter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreenSaver.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IMSWindowsScreenEventHandler.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp new file mode 100644 index 0000000..2e2210f --- /dev/null +++ b/lib/platform/synrgyhk.dsp @@ -0,0 +1,116 @@ +# Microsoft Developer Studio Project File - Name="synrgyhk" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=synrgyhk - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "synrgyhk.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "synrgyhk.mak" CFG="synrgyhk - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synrgyhk - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "synrgyhk - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synrgyhk - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../Release" +# PROP Intermediate_Dir "ReleaseHook" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"DllMain" /dll /machine:I386 /nodefaultlib +# SUBTRACT LINK32 /verbose + +!ELSEIF "$(CFG)" == "synrgyhk - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../Debug" +# PROP Intermediate_Dir "DebugHook" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "synrgyhk - Win32 Release" +# Name "synrgyhk - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CSynergyHook.cpp +# ADD CPP /D _WIN32_WINNT=0x0400 +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CSynergyHook.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/server/CClientProxy.cpp b/lib/server/CClientProxy.cpp new file mode 100644 index 0000000..55e940f --- /dev/null +++ b/lib/server/CClientProxy.cpp @@ -0,0 +1,67 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "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; +} + +const CMutex* +CClientProxy::getMutex() const +{ + return &m_mutex; +} diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h new file mode 100644 index 0000000..2ab41c3 --- /dev/null +++ b/lib/server/CClientProxy.h @@ -0,0 +1,105 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTPROXY_H +#define CCLIENTPROXY_H + +#include "IClient.h" +#include "CMutex.h" +#include "CString.h" + +class IInputStream; +class IOutputStream; +class IServer; + +//! Generic proxy for client +class CClientProxy : public IClient { +public: + /*! + \c name is the name of the client. + */ + CClientProxy(IServer* server, const CString& name, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CClientProxy(); + + //! @name accessors + //@{ + + //! Get server + /*! + Returns the server passed to the c'tor. + */ + IServer* getServer() const; + + //! Get input stream + /*! + Returns the input stream passed to the c'tor. + */ + IInputStream* getInputStream() const; + + //! Get output stream + /*! + Returns the output stream passed to the c'tor. + */ + IOutputStream* getOutputStream() const; + + //@} + + // IClient overrides + virtual void open() = 0; + virtual void mainLoop() = 0; + virtual void close() = 0; + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver) = 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, KeyButton) = 0; + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton) = 0; + virtual void keyUp(KeyID, KeyModifierMask, KeyButton) = 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 void resetOptions() = 0; + virtual void setOptions(const COptionsList& options) = 0; + virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const = 0; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; + +protected: + //! Get mutex + /*! + Returns the mutex for this object. Subclasses should use this + mutex to protect their data. + */ + const CMutex* getMutex() const; + +private: + CMutex m_mutex; + IServer* m_server; + CString m_name; + IInputStream* m_input; + IOutputStream* m_output; +}; + +#endif diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp new file mode 100644 index 0000000..7a9e734 --- /dev/null +++ b/lib/server/CClientProxy1_0.cpp @@ -0,0 +1,398 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientProxy1_0.h" +#include "CServer.h" +#include "CClipboard.h" +#include "CProtocolUtil.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), + m_heartRate(kHeartRate), + m_heartDeath(kHeartRate * kHeartBeatsUntilDeath) +{ + 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::mainLoop() +{ + // 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, m_heartRate); + 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) { + if (m_heartDeath >= 0.0 && heartTimer.getTime() > m_heartDeath) { + 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 + LOG((CLOG_DEBUG2 "no-op from", getName().c_str())); + 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(getMutex()); + 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(getMutex()); + m_clipboardDirty[id] = true; +} + +void +CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty) +{ + CLock lock(getMutex()); + m_clipboardDirty[id] = dirty; +} + +void +CClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask, KeyButton) +{ + LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown1_0, key, mask); +} + +void +CClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton) +{ + LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getName().c_str(), key, mask, count)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat1_0, key, mask, count); +} + +void +CClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask, KeyButton) +{ + LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp1_0, 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::resetOptions() +{ + LOG((CLOG_DEBUG1 "send reset options to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCResetOptions); + + // reset heart rate and death + CLock lock(getMutex()); + m_heartRate = kHeartRate; + m_heartDeath = kHeartRate * kHeartBeatsUntilDeath; +} + +void +CClientProxy1_0::setOptions(const COptionsList& options) +{ + LOG((CLOG_DEBUG1 "send set options to \"%s\" size=%d", getName().c_str(), options.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDSetOptions, &options); + + // check options + CLock lock(getMutex()); + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionHeartbeat) { + m_heartRate = 1.0e-3 * static_cast(options[i + 1]); + if (m_heartRate <= 0.0) { + m_heartRate = -1.0; + } + m_heartDeath = m_heartRate * kHeartBeatsUntilDeath; + } + } +} + +SInt32 +CClientProxy1_0::getJumpZoneSize() const +{ + CLock lock(getMutex()); + return m_info.m_zoneSize; +} + +void +CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + CLock lock(getMutex()); + x = m_info.m_x; + y = m_info.m_y; + w = m_info.m_w; + h = m_info.m_h; +} + +void +CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + +void +CClientProxy1_0::getCursorCenter(SInt32& x, SInt32& y) const +{ + CLock lock(getMutex()); + x = m_info.m_mx; + y = m_info.m_my; +} + +void +CClientProxy1_0::recvInfo(bool notify) +{ + { + CLock lock(getMutex()); + + // parse the message + SInt16 x, y, w, h, zoneSize, mx, my; + CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, + &x, &y, &w, &h, &zoneSize, &mx, &my); + LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my)); + + // validate + if (w <= 0 || h <= 0 || zoneSize < 0) { + throw XBadClient(); + } + if (mx < x || my < y || mx >= x + w || my >= y + h) { + throw XBadClient(); + } + + // save + m_info.m_x = x; + m_info.m_y = y; + m_info.m_w = w; + m_info.m_h = h; + m_info.m_zoneSize = zoneSize; + m_info.m_mx = mx; + m_info.m_my = my; + } + + // tell server of change + if (notify) { + getServer()->onInfoChanged(getName(), m_info); + } + + // 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(getName(), id, seqNum); +} diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h new file mode 100644 index 0000000..0a86e1a --- /dev/null +++ b/lib/server/CClientProxy1_0.h @@ -0,0 +1,69 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTPROXY1_0_H +#define CCLIENTPROXY1_0_H + +#include "CClientProxy.h" +#include "ProtocolTypes.h" + +//! Proxy for client implementing protocol version 1.0 +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 mainLoop(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool); + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); + 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 resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual SInt32 getJumpZoneSize() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; + +private: + void recvInfo(bool notify); + void recvClipboard(); + void recvGrabClipboard(); + +private: + CClientInfo m_info; + bool m_clipboardDirty[kClipboardEnd]; + double m_heartRate; + double m_heartDeath; +}; + +#endif diff --git a/lib/server/CClientProxy1_1.cpp b/lib/server/CClientProxy1_1.cpp new file mode 100644 index 0000000..a09b27b --- /dev/null +++ b/lib/server/CClientProxy1_1.cpp @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientProxy1_1.h" +#include "CProtocolUtil.h" +#include "CLog.h" +#include + +// +// CClientProxy1_1 +// + +CClientProxy1_1::CClientProxy1_1(IServer* server, const CString& name, + IInputStream* input, IOutputStream* output) : + CClientProxy1_0(server, name, input, output) +{ + // do nothing +} + +CClientProxy1_1::~CClientProxy1_1() +{ + // do nothing +} + +void +CClientProxy1_1::keyDown(KeyID key, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask, button); +} + +void +CClientProxy1_1::keyRepeat(KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d, button=0x%04x", getName().c_str(), key, mask, count, button)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count, button); +} + +void +CClientProxy1_1::keyUp(KeyID key, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask, button); +} diff --git a/lib/server/CClientProxy1_1.h b/lib/server/CClientProxy1_1.h new file mode 100644 index 0000000..dbae783 --- /dev/null +++ b/lib/server/CClientProxy1_1.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTPROXY1_1_H +#define CCLIENTPROXY1_1_H + +#include "CClientProxy1_0.h" + +//! Proxy for client implementing protocol version 1.1 +class CClientProxy1_1 : public CClientProxy1_0 { +public: + CClientProxy1_1(IServer* server, const CString& name, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CClientProxy1_1(); + + // IClient overrides + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); +}; + +#endif diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp new file mode 100644 index 0000000..89417b2 --- /dev/null +++ b/lib/server/CConfig.cpp @@ -0,0 +1,1174 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "KeyTypes.h" +#include "XSocket.h" +#include "stdistream.h" +#include "stdostream.h" +#include + +// +// CConfig +// + +CConfig::CConfig() +{ + // do nothing +} + +CConfig::~CConfig() +{ + // do nothing +} + +bool +CConfig::addScreen(const CString& name) +{ + // alias name must not exist + if (m_nameToCanonicalName.find(name) != m_nameToCanonicalName.end()) { + return false; + } + + // add cell + m_map.insert(std::make_pair(name, CCell())); + + // add name + m_nameToCanonicalName.insert(std::make_pair(name, name)); + + return true; +} + +bool +CConfig::renameScreen(const CString& oldName, + const CString& newName) +{ + // get canonical name and find cell + CString oldCanonical = getCanonicalName(oldName); + CCellMap::iterator index = m_map.find(oldCanonical); + if (index == m_map.end()) { + return false; + } + + // accept if names are equal but replace with new name to maintain + // case. otherwise, the new name must not exist. + if (!CStringUtil::CaselessCmp::equal(oldName, newName) && + m_nameToCanonicalName.find(newName) != m_nameToCanonicalName.end()) { + return false; + } + + // update cell + CCell tmpCell = index->second; + m_map.erase(index); + m_map.insert(std::make_pair(newName, tmpCell)); + + // update name + m_nameToCanonicalName.erase(oldCanonical); + m_nameToCanonicalName.insert(std::make_pair(newName, newName)); + + // update connections + for (index = m_map.begin(); index != m_map.end(); ++index) { + for (UInt32 i = 0; i < kNumDirections; ++i) { + if (CStringUtil::CaselessCmp::equal(getCanonicalName( + index->second.m_neighbor[i]), oldCanonical)) { + index->second.m_neighbor[i] = newName; + } + } + } + + // update alias targets + if (CStringUtil::CaselessCmp::equal(oldName, oldCanonical)) { + for (CNameMap::iterator index = m_nameToCanonicalName.begin(); + index != m_nameToCanonicalName.end(); ++index) { + if (CStringUtil::CaselessCmp::equal( + index->second, oldCanonical)) { + index->second = newName; + } + } + } + + return true; +} + +void +CConfig::removeScreen(const CString& name) +{ + // get canonical name and find cell + CString canonical = getCanonicalName(name); + CCellMap::iterator index = m_map.find(canonical); + if (index == m_map.end()) { + return; + } + + // remove from map + m_map.erase(index); + + // disconnect + for (index = m_map.begin(); index != m_map.end(); ++index) { + CCell& cell = index->second; + for (UInt32 i = 0; i < kNumDirections; ++i) { + if (getCanonicalName(cell.m_neighbor[i]) == canonical) { + cell.m_neighbor[i].erase(); + } + } + } + + // remove aliases (and canonical name) + for (CNameMap::iterator index = m_nameToCanonicalName.begin(); + index != m_nameToCanonicalName.end(); ) { + if (index->second == canonical) { + m_nameToCanonicalName.erase(index++); + } + else { + ++index; + } + } +} + +void +CConfig::removeAllScreens() +{ + m_map.clear(); + m_nameToCanonicalName.clear(); +} + +bool +CConfig::addAlias(const CString& canonical, const CString& alias) +{ + // alias name must not exist + if (m_nameToCanonicalName.find(alias) != m_nameToCanonicalName.end()) { + return false; + } + + // canonical name must be known + if (m_map.find(canonical) == m_map.end()) { + return false; + } + + // insert alias + m_nameToCanonicalName.insert(std::make_pair(alias, canonical)); + + return true; +} + +bool +CConfig::removeAlias(const CString& alias) +{ + // must not be a canonical name + if (m_map.find(alias) != m_map.end()) { + return false; + } + + // find alias + CNameMap::iterator index = m_nameToCanonicalName.find(alias); + if (index == m_nameToCanonicalName.end()) { + return false; + } + + // remove alias + m_nameToCanonicalName.erase(index); + + return true; +} + +void +CConfig::removeAllAliases() +{ + // remove all names + m_nameToCanonicalName.clear(); + + // put the canonical names back in + for (CCellMap::iterator index = m_map.begin(); + index != m_map.end(); ++index) { + m_nameToCanonicalName.insert( + std::make_pair(index->first, index->first)); + } +} + +bool +CConfig::connect(const CString& srcName, + EDirection srcSide, const CString& dstName) +{ + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // find source cell + CCellMap::iterator index = m_map.find(getCanonicalName(srcName)); + if (index == m_map.end()) { + return false; + } + + // connect side (overriding any previous connection). we + // canonicalize in getNeighbor() instead of here because the + // destination name doesn't have to exist yet. + index->second.m_neighbor[srcSide - kFirstDirection] = dstName; + + return true; +} + +bool +CConfig::disconnect(const CString& srcName, EDirection srcSide) +{ + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // find source cell + CCellMap::iterator index = m_map.find(srcName); + if (index == m_map.end()) { + return false; + } + + // disconnect side + index->second.m_neighbor[srcSide - kFirstDirection].erase(); + + return true; +} + +void +CConfig::setSynergyAddress(const CNetworkAddress& addr) +{ + m_synergyAddress = addr; +} + +void +CConfig::setHTTPAddress(const CNetworkAddress& addr) +{ + m_httpAddress = addr; +} + +bool +CConfig::addOption(const CString& name, OptionID option, OptionValue value) +{ + // find options + CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } + } + if (options == NULL) { + return false; + } + + // add option + options->insert(std::make_pair(option, value)); + return true; +} + +bool +CConfig::removeOption(const CString& name, OptionID option) +{ + // find options + CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } + } + if (options == NULL) { + return false; + } + + // remove option + options->erase(option); + return true; +} + +bool +CConfig::removeOptions(const CString& name) +{ + // find options + CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } + } + if (options == NULL) { + return false; + } + + // remove options + options->clear(); + return true; +} + +bool +CConfig::isValidScreenName(const CString& name) const +{ + // name is valid if matches validname + // name ::= [_A-Za-z0-9] | [_A-Za-z0-9][-_A-Za-z0-9]*[_A-Za-z0-9] + // domain ::= . name + // validname ::= name domain* + + // check each dot separated part + CString::size_type b = 0; + for (;;) { + // find end of part + CString::size_type e = name.find('.', b); + if (e == CString::npos) { + e = name.size(); + } + + // part may not be empty + if (e - b < 1) { + return false; + } + + // check first and last characters + if (!(isalnum(name[b]) || name[b] == '_') || + !(isalnum(name[e - 1]) || name[e - 1] == '_')) { + return false; + } + + // check interior characters + for (CString::size_type i = b; i < e; ++i) { + if (!isalnum(name[i]) && name[i] != '_' && name[i] != '-') { + return false; + } + } + + // next part + if (e == name.size()) { + // no more parts + break; + } + b = e + 1; + } + + return true; +} + +CConfig::const_iterator +CConfig::begin() const +{ + return const_iterator(m_map.begin()); +} + +CConfig::const_iterator +CConfig::end() const +{ + return const_iterator(m_map.end()); +} + +CConfig::all_const_iterator +CConfig::beginAll() const +{ + return m_nameToCanonicalName.begin(); +} + +CConfig::all_const_iterator +CConfig::endAll() const +{ + return m_nameToCanonicalName.end(); +} + +bool +CConfig::isScreen(const CString& name) const +{ + return (m_nameToCanonicalName.count(name) > 0); +} + +bool +CConfig::isCanonicalName(const CString& name) const +{ + return CStringUtil::CaselessCmp::equal(getCanonicalName(name), name); +} + +CString +CConfig::getCanonicalName(const CString& name) const +{ + CNameMap::const_iterator index = m_nameToCanonicalName.find(name); + if (index == m_nameToCanonicalName.end()) { + return CString(); + } + else { + return index->second; + } +} + +CString +CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const +{ + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // find source cell + CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); + if (index == m_map.end()) { + return CString(); + } + + // return connection + return getCanonicalName(index->second.m_neighbor[ + srcSide - kFirstDirection]); +} + +const CNetworkAddress& +CConfig::getSynergyAddress() const +{ + return m_synergyAddress; +} + +const CNetworkAddress& +CConfig::getHTTPAddress() const +{ + return m_httpAddress; +} + +const CConfig::CScreenOptions* +CConfig::getOptions(const CString& name) const +{ + // find options + const CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::const_iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } + } + + // return options + return options; +} + +bool +CConfig::operator==(const CConfig& x) const +{ +/* FIXME -- no compare available for CNetworkAddress + if (m_synergyAddress != x.m_synergyAddress) { + return false; + } + if (m_httpAddress != x.m_httpAddress) { + return false; + } +*/ + if (m_map.size() != x.m_map.size()) { + return false; + } + if (m_nameToCanonicalName.size() != x.m_nameToCanonicalName.size()) { + return false; + } + + // compare global options + if (m_globalOptions != x.m_globalOptions) { + return false; + } + + for (CCellMap::const_iterator index1 = m_map.begin(), + index2 = x.m_map.begin(); + index1 != m_map.end(); ++index1, ++index2) { + // compare names + if (!CStringUtil::CaselessCmp::equal(index1->first, index2->first)) { + return false; + } + + // compare options + if (index1->second.m_options != index2->second.m_options) { + return false; + } + + // compare neighbors + for (UInt32 i = 0; i < kNumDirections; ++i) { + if (!CStringUtil::CaselessCmp::equal(index1->second.m_neighbor[i], + index2->second.m_neighbor[i])) { + return false; + } + } + } + + for (CNameMap::const_iterator index1 = m_nameToCanonicalName.begin(), + index2 = x.m_nameToCanonicalName.begin(); + index1 != m_nameToCanonicalName.end(); + ++index1, ++index2) { + if (!CStringUtil::CaselessCmp::equal(index1->first, index2->first) || + !CStringUtil::CaselessCmp::equal(index1->second, index2->second)) { + return false; + } + } + + return true; +} + +bool +CConfig::operator!=(const CConfig& x) const +{ + return !operator==(x); +} + +const char* +CConfig::dirName(EDirection dir) +{ + static const char* s_name[] = { "left", "right", "top", "bottom" }; + + assert(dir >= kFirstDirection && dir <= kLastDirection); + + return s_name[dir - kFirstDirection]; +} + +bool +CConfig::readLine(std::istream& s, CString& line) +{ + s >> std::ws; + while (std::getline(s, line)) { + // strip comments and then trailing whitespace + CString::size_type i = line.find('#'); + if (i != CString::npos) { + line.erase(i); + } + i = line.find_last_not_of(" \r\t"); + if (i != CString::npos) { + line.erase(i + 1); + } + + // return non empty line + if (!line.empty()) { + return true; + } + s >> std::ws; + } + return false; +} + +OptionValue +CConfig::parseBoolean(const CString& arg) +{ + if (CStringUtil::CaselessCmp::equal(arg, "true")) { + return static_cast(true); + } + if (CStringUtil::CaselessCmp::equal(arg, "false")) { + return static_cast(false); + } + throw XConfigRead("invalid argument"); +} + +OptionValue +CConfig::parseInt(const CString& arg) +{ + const char* s = arg.c_str(); + char* end; + long tmp = strtol(s, &end, 10); + if (*end != '\0') { + // invalid characters + throw XConfigRead("invalid argument"); + } + OptionValue value = static_cast(tmp); + if (value != tmp) { + // out of range + throw XConfigRead("argument out of range"); + } + return value; +} + +OptionValue +CConfig::parseModifierKey(const CString& arg) +{ + if (CStringUtil::CaselessCmp::equal(arg, "shift")) { + return static_cast(kKeyModifierIDShift); + } + if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) { + return static_cast(kKeyModifierIDControl); + } + if (CStringUtil::CaselessCmp::equal(arg, "alt")) { + return static_cast(kKeyModifierIDAlt); + } + if (CStringUtil::CaselessCmp::equal(arg, "meta")) { + return static_cast(kKeyModifierIDMeta); + } + if (CStringUtil::CaselessCmp::equal(arg, "super")) { + return static_cast(kKeyModifierIDSuper); + } + if (CStringUtil::CaselessCmp::equal(arg, "none")) { + return static_cast(kKeyModifierIDNull); + } + throw XConfigRead("invalid argument"); +} + +const char* +CConfig::getOptionName(OptionID id) +{ + if (id == kOptionHalfDuplexCapsLock) { + return "halfDuplexCapsLock"; + } + if (id == kOptionHalfDuplexNumLock) { + return "halfDuplexNumLock"; + } + if (id == kOptionModifierMapForShift) { + return "shift"; + } + if (id == kOptionModifierMapForControl) { + return "ctrl"; + } + if (id == kOptionModifierMapForAlt) { + return "alt"; + } + if (id == kOptionModifierMapForMeta) { + return "meta"; + } + if (id == kOptionModifierMapForSuper) { + return "super"; + } + if (id == kOptionHeartbeat) { + return "heartbeat"; + } + if (id == kOptionScreenSwitchDelay) { + return "switchDelay"; + } + if (id == kOptionScreenSwitchTwoTap) { + return "switchDoubleTap"; + } + if (id == kOptionScreenSaverSync) { + return "screenSaverSync"; + } + if (id == kOptionXTestXineramaUnaware) { + return "xtestIsXineramaUnaware"; + } + return NULL; +} + +CString +CConfig::getOptionValue(OptionID id, OptionValue value) +{ + if (id == kOptionHalfDuplexCapsLock || + id == kOptionHalfDuplexNumLock || + id == kOptionScreenSaverSync || + id == kOptionXTestXineramaUnaware) { + return (value != 0) ? "true" : "false"; + } + if (id == kOptionModifierMapForShift || + id == kOptionModifierMapForControl || + id == kOptionModifierMapForAlt || + id == kOptionModifierMapForMeta || + id == kOptionModifierMapForSuper) { + switch (value) { + case kKeyModifierIDShift: + return "shift"; + + case kKeyModifierIDControl: + return "ctrl"; + + case kKeyModifierIDAlt: + return "alt"; + + case kKeyModifierIDMeta: + return "meta"; + + case kKeyModifierIDSuper: + return "super"; + + default: + return "none"; + } + } + if (id == kOptionHeartbeat || + id == kOptionScreenSwitchDelay || + id == kOptionScreenSwitchTwoTap) { + return CStringUtil::print("%d", value); + } + + return ""; +} + +void +CConfig::readSection(std::istream& s) +{ + static const char s_section[] = "section:"; + static const char s_options[] = "options"; + static const char s_screens[] = "screens"; + static const char s_links[] = "links"; + static const char s_aliases[] = "aliases"; + + CString line; + if (!readLine(s, line)) { + // no more sections + return; + } + + // should be a section header + if (line.find(s_section) != 0) { + throw XConfigRead("found data outside section"); + } + + // get section name + CString::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1); + if (i == CString::npos) { + throw XConfigRead("section name is missing"); + } + CString name = line.substr(i); + i = name.find_first_of(" \t"); + if (i != CString::npos) { + throw XConfigRead("unexpected data after section name"); + } + + // read section + if (name == s_options) { + readSectionOptions(s); + } + else if (name == s_screens) { + readSectionScreens(s); + } + else if (name == s_links) { + readSectionLinks(s); + } + else if (name == s_aliases) { + readSectionAliases(s); + } + else { + throw XConfigRead("unknown section name"); + } +} + +void +CConfig::readSectionOptions(std::istream& s) +{ + CString line; + CString name; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // parse argument: `=' + CString::size_type i = line.find_first_of(" \t="); + if (i == 0) { + throw XConfigRead("missing argument name"); + } + if (i == CString::npos) { + throw XConfigRead("missing = in argument"); + } + CString name = line.substr(0, i); + i = line.find_first_not_of(" \t", i); + if (i == CString::npos || line[i] != '=') { + throw XConfigRead("missing = in argument"); + } + i = line.find_first_not_of(" \t", i + 1); + CString value; + if (i != CString::npos) { + value = line.substr(i); + } + if (value.empty()) { + throw XConfigRead("missing value after ="); + } + + if (name == "address") { + try { + m_synergyAddress = CNetworkAddress(value, kDefaultPort); + } + catch (XSocketAddress&) { + throw XConfigRead("invalid address argument"); + } + } + else if (name == "http") { + try { + m_httpAddress = CNetworkAddress(value, kDefaultPort + 1); + } + catch (XSocketAddress&) { + throw XConfigRead("invalid http argument"); + } + } + else if (name == "heartbeat") { + addOption("", kOptionHeartbeat, parseInt(value)); + } + else if (name == "switchDelay") { + addOption("", kOptionScreenSwitchDelay, parseInt(value)); + } + else if (name == "switchDoubleTap") { + addOption("", kOptionScreenSwitchTwoTap, parseInt(value)); + } + else if (name == "screenSaverSync") { + addOption("", kOptionScreenSaverSync, parseBoolean(value)); + } + else { + throw XConfigRead("unknown argument"); + } + } + throw XConfigRead("unexpected end of screens section"); +} + +void +CConfig::readSectionScreens(std::istream& s) +{ + CString line; + CString screen; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify validity of screen name + if (!isValidScreenName(screen)) { + throw XConfigRead("invalid screen name"); + } + + // add the screen to the configuration + if (!addScreen(screen)) { + throw XConfigRead("duplicate screen name"); + } + } + else if (screen.empty()) { + throw XConfigRead("argument before first screen"); + } + else { + // parse argument: `=' + CString::size_type i = line.find_first_of(" \t="); + if (i == 0) { + throw XConfigRead("missing argument name"); + } + if (i == CString::npos) { + throw XConfigRead("missing = in argument"); + } + CString name = line.substr(0, i); + i = line.find_first_not_of(" \t", i); + if (i == CString::npos || line[i] != '=') { + throw XConfigRead("missing = in argument"); + } + i = line.find_first_not_of(" \t", i + 1); + CString value; + if (i != CString::npos) { + value = line.substr(i); + } + + // handle argument + if (name == "halfDuplexCapsLock") { + addOption(screen, kOptionHalfDuplexCapsLock, + parseBoolean(value)); + } + else if (name == "halfDuplexNumLock") { + addOption(screen, kOptionHalfDuplexNumLock, + parseBoolean(value)); + } + else if (name == "shift") { + addOption(screen, kOptionModifierMapForShift, + parseModifierKey(value)); + } + else if (name == "ctrl") { + addOption(screen, kOptionModifierMapForControl, + parseModifierKey(value)); + } + else if (name == "alt") { + addOption(screen, kOptionModifierMapForAlt, + parseModifierKey(value)); + } + else if (name == "meta") { + addOption(screen, kOptionModifierMapForMeta, + parseModifierKey(value)); + } + else if (name == "super") { + addOption(screen, kOptionModifierMapForSuper, + parseModifierKey(value)); + } + else if (name == "xtestIsXineramaUnaware") { + addOption(screen, kOptionXTestXineramaUnaware, + parseBoolean(value)); + } + else { + // unknown argument + throw XConfigRead("unknown argument"); + } + } + } + throw XConfigRead("unexpected end of screens section"); +} + +void +CConfig::readSectionLinks(std::istream& s) +{ + CString line; + CString screen; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify we know about the screen + if (!isScreen(screen)) { + throw XConfigRead("unknown screen name"); + } + if (!isCanonicalName(screen)) { + throw XConfigRead("cannot use screen name alias here"); + } + } + else if (screen.empty()) { + throw XConfigRead("argument before first screen"); + } + else { + // parse argument: `=' + CString::size_type i = line.find_first_of(" \t="); + if (i == 0) { + throw XConfigRead("missing argument name"); + } + if (i == CString::npos) { + throw XConfigRead("missing = in argument"); + } + CString name = line.substr(0, i); + i = line.find_first_not_of(" \t", i); + if (i == CString::npos || line[i] != '=') { + throw XConfigRead("missing = in argument"); + } + i = line.find_first_not_of(" \t", i + 1); + CString value; + if (i != CString::npos) { + value = line.substr(i); + } + + // handle argument + if (name == "left") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kLeft, value); + } + else if (name == "right") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kRight, value); + } + else if (name == "up") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kTop, value); + } + else if (name == "down") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kBottom, value); + } + else { + // unknown argument + throw XConfigRead("unknown argument"); + } + } + } + throw XConfigRead("unexpected end of links section"); +} + +void +CConfig::readSectionAliases(std::istream& s) +{ + CString line; + CString screen; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify we know about the screen + if (!isScreen(screen)) { + throw XConfigRead("unknown screen name"); + } + if (!isCanonicalName(screen)) { + throw XConfigRead("cannot use screen name alias here"); + } + } + else if (screen.empty()) { + throw XConfigRead("argument before first screen"); + } + else { + // verify validity of screen name + if (!isValidScreenName(line)) { + throw XConfigRead("invalid screen alias"); + } + + // add alias + if (!addAlias(screen, line)) { + throw XConfigRead("alias is duplicate screen name"); + } + } + } + throw XConfigRead("unexpected end of aliases section"); +} + + +// +// CConfig I/O +// + +std::istream& +operator>>(std::istream& s, CConfig& config) +{ + // FIXME -- should track line and column to improve error reporting + + CConfig tmp; + while (s) { + tmp.readSection(s); + } + config = tmp; + return s; +} + +std::ostream& +operator<<(std::ostream& s, const CConfig& config) +{ + // options section + s << "section: options" << std::endl; + const CConfig::CScreenOptions* options = config.getOptions(""); + if (options != NULL && options->size() > 0) { + for (CConfig::CScreenOptions::const_iterator + option = options->begin(); + option != options->end(); ++option) { + const char* name = CConfig::getOptionName(option->first); + CString value = CConfig::getOptionValue(option->first, + option->second); + if (name != NULL && !value.empty()) { + s << "\t" << name << " = " << value << std::endl; + } + } + } + if (config.m_synergyAddress.isValid()) { + s << "\taddress = " << + config.m_synergyAddress.getHostname().c_str() << std::endl; + } + if (config.m_httpAddress.isValid()) { + s << "\thttp = " << + config.m_httpAddress.getHostname().c_str() << std::endl; + } + s << "end" << std::endl; + + // screens section + s << "section: screens" << std::endl; + for (CConfig::const_iterator screen = config.begin(); + screen != config.end(); ++screen) { + s << "\t" << screen->c_str() << ":" << std::endl; + const CConfig::CScreenOptions* options = config.getOptions(*screen); + if (options != NULL && options->size() > 0) { + for (CConfig::CScreenOptions::const_iterator + option = options->begin(); + option != options->end(); ++option) { + const char* name = CConfig::getOptionName(option->first); + CString value = CConfig::getOptionValue(option->first, + option->second); + if (name != NULL && !value.empty()) { + s << "\t\t" << name << " = " << value << std::endl; + } + } + } + } + s << "end" << std::endl; + + // links section + CString neighbor; + s << "section: links" << std::endl; + for (CConfig::const_iterator screen = config.begin(); + screen != config.end(); ++screen) { + s << "\t" << screen->c_str() << ":" << std::endl; + + neighbor = config.getNeighbor(*screen, kLeft); + if (!neighbor.empty()) { + s << "\t\tleft=" << neighbor.c_str() << std::endl; + } + + neighbor = config.getNeighbor(*screen, kRight); + if (!neighbor.empty()) { + s << "\t\tright=" << neighbor.c_str() << std::endl; + } + + neighbor = config.getNeighbor(*screen, kTop); + if (!neighbor.empty()) { + s << "\t\tup=" << neighbor.c_str() << std::endl; + } + + neighbor = config.getNeighbor(*screen, kBottom); + if (!neighbor.empty()) { + s << "\t\tdown=" << neighbor.c_str() << std::endl; + } + } + s << "end" << std::endl; + + // aliases section (if there are any) + if (config.m_map.size() != config.m_nameToCanonicalName.size()) { + // map canonical to alias + typedef std::multimap CMNameMap; + CMNameMap aliases; + for (CConfig::CNameMap::const_iterator + index = config.m_nameToCanonicalName.begin(); + index != config.m_nameToCanonicalName.end(); + ++index) { + if (index->first != index->second) { + aliases.insert(std::make_pair(index->second, index->first)); + } + } + + // dump it + CString screen; + s << "section: aliases" << std::endl; + for (CMNameMap::const_iterator index = aliases.begin(); + index != aliases.end(); ++index) { + if (index->first != screen) { + screen = index->first; + s << "\t" << screen.c_str() << ":" << std::endl; + } + s << "\t\t" << index->second.c_str() << std::endl; + } + s << "end" << std::endl; + } + + return s; +} + + +// +// CConfig I/O exceptions +// + +XConfigRead::XConfigRead(const CString& error) : + m_error(error) +{ + // do nothing +} + +XConfigRead::~XConfigRead() +{ + // do nothing +} + +CString +XConfigRead::getWhat() const throw() +{ + return format("XConfigRead", "read error: %{1}", m_error.c_str()); +} diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h new file mode 100644 index 0000000..28e2c5b --- /dev/null +++ b/lib/server/CConfig.h @@ -0,0 +1,332 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCONFIG_H +#define CCONFIG_H + +#include "OptionTypes.h" +#include "ProtocolTypes.h" +#include "CNetworkAddress.h" +#include "CStringUtil.h" +#include "XBase.h" +#include "stdmap.h" +#include "stdset.h" +#include + +class CConfig; + +namespace std { +template <> +struct iterator_traits { + typedef CString value_type; + typedef ptrdiff_t difference_type; + typedef bidirectional_iterator_tag iterator_category; + typedef CString* pointer; + typedef CString& reference; +}; +}; + +//! Server configuration +/*! +This class holds server configuration information. That includes +the names of screens and their aliases, the links between them, +and network addresses. + +Note that case is preserved in screen names but is ignored when +comparing names. Screen names and their aliases share a +namespace and must be unique. +*/ +class CConfig { +public: + typedef std::map CScreenOptions; + +private: + class CCell { + public: + CString m_neighbor[kLastDirection - kFirstDirection + 1]; + CScreenOptions m_options; + }; + typedef std::map CCellMap; + typedef std::map CNameMap; + +public: + typedef CCellMap::const_iterator internal_const_iterator; + typedef CNameMap::const_iterator all_const_iterator; + class const_iterator : std::iterator_traits { + public: + explicit const_iterator() : m_i() { } + explicit const_iterator(const internal_const_iterator& i) : m_i(i) { } + + const_iterator& operator=(const const_iterator& i) { + m_i = i.m_i; + return *this; + } + CString operator*() { return m_i->first; } + const CString* operator->() { return &(m_i->first); } + const_iterator& operator++() { ++m_i; return *this; } + const_iterator operator++(int) { return const_iterator(m_i++); } + const_iterator& operator--() { --m_i; return *this; } + const_iterator operator--(int) { return const_iterator(m_i--); } + bool operator==(const const_iterator& i) const { + return (m_i == i.m_i); + } + bool operator!=(const const_iterator& i) const { + return (m_i != i.m_i); + } + + private: + internal_const_iterator m_i; + }; + + CConfig(); + virtual ~CConfig(); + + //! @name manipulators + //@{ + + //! Add screen + /*! + Adds a screen, returning true iff successful. If a screen or + alias with the given name exists then it fails. + */ + bool addScreen(const CString& name); + + //! Rename screen + /*! + Renames a screen. All references to the name are updated. + Returns true iff successful. + */ + bool renameScreen(const CString& oldName, + const CString& newName); + + //! Remove screen + /*! + Removes a screen. This also removes aliases for the screen and + disconnects any connections to the screen. \c name may be an + alias. + */ + void removeScreen(const CString& name); + + //! Remove all screens + /*! + Removes all screens, aliases, and connections. + */ + void removeAllScreens(); + + //! Add alias + /*! + Adds an alias for a screen name. An alias can be used + any place the canonical screen name can (except addScreen()). + Returns false if the alias name already exists or the canonical + name is unknown, otherwise returns true. + */ + bool addAlias(const CString& canonical, + const CString& alias); + + //! Remove alias + /*! + Removes an alias for a screen name. It returns false if the + alias is unknown or a canonical name, otherwise returns true. + */ + bool removeAlias(const CString& alias); + + //! Remove all aliases + /*! + This removes all aliases but not the screens. + */ + void removeAllAliases(); + + //! Connect screens + /*! + Establishes a one-way connection between opposite edges of two + screens. The user will be able to jump from the \c srcSide of + screen \c srcName to the opposite side of screen \c dstName + when both screens are connected to the server and the user + isn't locked to a screen. Returns false if \c srcName is + unknown. + */ + bool connect(const CString& srcName, + EDirection srcSide, + const CString& dstName); + + //! Disconnect screens + /*! + Removes a connection created by connect(). Returns false if + \c srcName is unknown. + */ + bool disconnect(const CString& srcName, + EDirection srcSide); + + //! Set server address + /*! + Set the synergy listen addresses. There is no default address so + this must be called to run a server using this configuration. + */ + void setSynergyAddress(const CNetworkAddress&); + + //! Set HTTP server address + /*! + Set the HTTP listen addresses. There is no default address so + this must be called to run an HTTP server using this configuration. + */ + void setHTTPAddress(const CNetworkAddress&); + + //! Add a screen option + /*! + Adds an option and its value to the named screen. Replaces the + existing option's value if there is one. Returns true iff \c name + is a known screen. + */ + bool addOption(const CString& name, + OptionID option, OptionValue value); + + //! Remove a screen option + /*! + Removes an option and its value from the named screen. Does + nothing if the option doesn't exist on the screen. Returns true + iff \c name is a known screen. + */ + bool removeOption(const CString& name, OptionID option); + + //! Remove a screen options + /*! + Removes all options and values from the named screen. Returns true + iff \c name is a known screen. + */ + bool removeOptions(const CString& name); + + //@} + //! @name accessors + //@{ + + //! Test screen name validity + /*! + Returns true iff \c name is a valid screen name. + */ + bool isValidScreenName(const CString& name) const; + + //! Get beginning (canonical) screen name iterator + const_iterator begin() const; + //! Get ending (canonical) screen name iterator + const_iterator end() const; + + //! Get beginning screen name iterator + all_const_iterator beginAll() const; + //! Get ending screen name iterator + all_const_iterator endAll() const; + + //! Test for screen name + /*! + Returns true iff \c name names a screen. + */ + bool isScreen(const CString& name) const; + + //! Test for canonical screen name + /*! + Returns true iff \c name is the canonical name of a screen. + */ + bool isCanonicalName(const CString& name) const; + + //! Get canonical name + /*! + Returns the canonical name of a screen or the empty string if + the name is unknown. Returns the canonical name if one is given. + */ + CString getCanonicalName(const CString& name) const; + + //! Get neighbor + /*! + Returns the canonical screen name of the neighbor in the given + direction (set through connect()). Returns the empty string + if there is no neighbor in that direction. + */ + CString getNeighbor(const CString&, EDirection) const; + + //! Get the server address + const CNetworkAddress& getSynergyAddress() const; + //! Get the HTTP server address + const CNetworkAddress& getHTTPAddress() const; + + //! Get the screen options + /*! + Returns all the added options for the named screen. Returns NULL + if the screen is unknown and an empty collection if there are no + options. + */ + const CScreenOptions* getOptions(const CString& name) const; + + //! Compare configurations + bool operator==(const CConfig&) const; + //! Compare configurations + bool operator!=(const CConfig&) const; + + //! Read configuration + /*! + Reads a configuration from a stream. Throws XConfigRead on error. + */ + friend std::istream& operator>>(std::istream&, CConfig&); + + //! Write configuration + /*! + Writes a configuration to a stream. + */ + friend std::ostream& operator<<(std::ostream&, const CConfig&); + + //! Get direction name + /*! + Returns the name of a direction (for debugging). + */ + static const char* dirName(EDirection); + + //@} + +private: + static bool readLine(std::istream&, CString&); + static OptionValue parseBoolean(const CString&); + static OptionValue parseInt(const CString&); + static OptionValue parseModifierKey(const CString&); + static const char* getOptionName(OptionID); + static CString getOptionValue(OptionID, OptionValue); + void readSection(std::istream&); + void readSectionOptions(std::istream&); + void readSectionScreens(std::istream&); + void readSectionLinks(std::istream&); + void readSectionAliases(std::istream&); + +private: + CCellMap m_map; + CNameMap m_nameToCanonicalName; + CNetworkAddress m_synergyAddress; + CNetworkAddress m_httpAddress; + CScreenOptions m_globalOptions; +}; + +//! Configuration stream read exception +/*! +Thrown when a configuration stream cannot be parsed. +*/ +class XConfigRead : public XBase { +public: + XConfigRead(const CString&); + ~XConfigRead(); + +protected: + // XBase overrides + virtual CString getWhat() const throw(); + +private: + CString m_error; +}; + +#endif diff --git a/lib/server/CHTTPServer.cpp b/lib/server/CHTTPServer.cpp new file mode 100644 index 0000000..d3582ac --- /dev/null +++ b/lib/server/CHTTPServer.cpp @@ -0,0 +1,799 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CHTTPServer.h" +#include "CConfig.h" +#include "CHTTPProtocol.h" +#include "CServer.h" +#include "XHTTP.h" +#include "IDataSocket.h" +#include "XThread.h" +#include "CLog.h" +#include "stdset.h" +#include "stdsstream.h" + +// +// CHTTPServer +// + +// maximum size of an HTTP request. this should be large enough to +// handle any reasonable request but small enough to prevent a +// malicious client from causing us to use too much memory. +const UInt32 CHTTPServer::s_maxRequestSize = 32768; + +CHTTPServer::CHTTPServer( + CServer* server) : + m_server(server) +{ + // do nothing +} + +CHTTPServer::~CHTTPServer() +{ + // do nothing +} + +void +CHTTPServer::processRequest(IDataSocket* socket) +{ + assert(socket != NULL); + + CHTTPRequest* request = NULL; + try { + // parse request + request = CHTTPProtocol::readRequest( + socket->getInputStream(), s_maxRequestSize); + if (request == NULL) { + throw XHTTP(400); + } + + // if absolute uri then strip off scheme and host + if (request->m_uri[0] != '/') { + CString::size_type n = request->m_uri.find('/'); + if (n == CString::npos) { + throw XHTTP(404); + } + request->m_uri = request->m_uri.substr(n); + } + + // prepare reply + CHTTPReply reply; + reply.m_majorVersion = request->m_majorVersion; + reply.m_minorVersion = request->m_minorVersion; + reply.m_status = 200; + reply.m_reason = "OK"; + reply.m_method = request->m_method; + + // process + doProcessRequest(*request, reply); + + // send reply + CHTTPProtocol::reply(socket->getOutputStream(), reply); + LOG((CLOG_INFO "HTTP reply %d for %s %s", reply.m_status, request->m_method.c_str(), request->m_uri.c_str())); + + // clean up + delete request; + } + catch (XHTTP& e) { + LOG((CLOG_WARN "returning HTTP error %d %s for %s", e.getStatus(), e.getReason().c_str(), (request != NULL) ? request->m_uri.c_str() : "")); + + // clean up + delete request; + + // return error + CHTTPReply reply; + reply.m_majorVersion = 1; + reply.m_minorVersion = 0; + reply.m_status = e.getStatus(); + reply.m_reason = e.getReason(); + reply.m_method = "GET"; +// FIXME -- use a nicer error page + reply.m_headers.push_back(std::make_pair(CString("Content-Type"), + CString("text/plain"))); + reply.m_body = e.getReason(); + e.addHeaders(reply); + CHTTPProtocol::reply(socket->getOutputStream(), reply); + } + catch (...) { + // ignore other exceptions + RETHROW_XTHREAD + } +} + +void +CHTTPServer::doProcessRequest(CHTTPRequest& request, CHTTPReply& reply) +{ + // switch based on uri + if (request.m_uri == "/editmap") { + if (request.m_method == "GET" || request.m_method == "HEAD") { + doProcessGetEditMap(request, reply); + reply.m_headers.push_back(std::make_pair( + CString("Content-Type"), + CString("text/html"))); + } + else if (request.m_method == "POST") { + doProcessPostEditMap(request, reply); + reply.m_headers.push_back(std::make_pair( + CString("Content-Type"), + CString("text/html"))); + } + else { + throw XHTTPAllow("GET, HEAD, POST"); + } + } + else { + // unknown page + throw XHTTP(404); + } +} + +void +CHTTPServer::doProcessGetEditMap(CHTTPRequest& /*request*/, CHTTPReply& reply) +{ + static const char* s_editMapProlog1 = + "\r\n" + "\r\n" + " Synergy -- Edit Screens\r\n" + "\r\n" + "\r\n" + "
\r\n" + " \r\n"; + static const char* s_editMapEpilog = + " \r\n" + " \r\n" + "
\r\n" + "
\r\n" + "\r\n" + "\r\n"; + static const char* s_editMapTableProlog = + "
" + " \r\n"; + static const char* s_editMapTableEpilog = + "
" + "
\r\n"; + static const char* s_editMapRowProlog = + "\r\n"; + static const char* s_editMapRowEpilog = + "\r\n"; + static const char* s_editMapScreenDummy = + ""; + static const char* s_editMapScreenPrimary = + ""; + static const char* s_editMapScreenEnd = + "\r\n"; + + std::ostringstream s; + + // convert screen map into a temporary screen map + CScreenArray screens; + { + CConfig config; + m_server->getConfig(&config); + screens.convertFrom(config); + // FIXME -- note to user if config couldn't be exactly represented + } + + // insert blank columns and rows around array (to allow the user + // to insert new screens) + screens.insertColumn(0); + screens.insertColumn(screens.getWidth()); + screens.insertRow(0); + screens.insertRow(screens.getHeight()); + + // get array size + const SInt32 w = screens.getWidth(); + const SInt32 h = screens.getHeight(); + + // construct reply + reply.m_body += s_editMapProlog1; + s << w << "x" << h; + reply.m_body += s.str(); + reply.m_body += s_editMapProlog2; + + // add screen map for editing + const CString primaryName = m_server->getPrimaryScreenName(); + reply.m_body += s_editMapTableProlog; + for (SInt32 y = 0; y < h; ++y) { + reply.m_body += s_editMapRowProlog; + for (SInt32 x = 0; x < w; ++x) { + s.str(""); + if (!screens.isAllowed(x, y) && screens.get(x, y) != primaryName) { + s << s_editMapScreenDummy; + } + else { + if (!screens.isSet(x, y)) { + s << s_editMapScreenDead; + } + else if (screens.get(x, y) == primaryName) { + s << s_editMapScreenPrimary; + } + else { + s << s_editMapScreenLive; + } + s << screens.get(x, y) << + s_editMapScreenLiveDead1 << + "n" << x << "x" << y << + s_editMapScreenLiveDead2; + } + s << s_editMapScreenEnd; + reply.m_body += s.str(); + } + reply.m_body += s_editMapRowEpilog; + } + reply.m_body += s_editMapTableEpilog; + + reply.m_body += s_editMapEpilog; +} + +void +CHTTPServer::doProcessPostEditMap(CHTTPRequest& request, CHTTPReply& reply) +{ + typedef std::vector ScreenArray; + typedef std::set ScreenSet; + + // parse the result + CHTTPProtocol::CFormParts parts; + if (!CHTTPProtocol::parseFormData(request, parts)) { + LOG((CLOG_WARN "editmap: cannot parse form data")); + throw XHTTP(400); + } + + try { + std::ostringstream s; + + // convert post data into a temporary screen map. also check + // that no screen name is invalid or used more than once. + SInt32 w, h; + CHTTPProtocol::CFormParts::iterator index = parts.find("size"); + if (index == parts.end() || + !parseXY(index->second, w, h) || + w <= 0 || h <= 0) { + LOG((CLOG_WARN "editmap: cannot parse size or size is invalid")); + throw XHTTP(400); + } + ScreenSet screenNames; + CScreenArray screens; + screens.resize(w, h); + for (SInt32 y = 0; y < h; ++y) { + for (SInt32 x = 0; x < w; ++x) { + // find part + s.str(""); + s << "n" << x << "x" << y; + index = parts.find(s.str()); + if (index == parts.end()) { + // FIXME -- screen is missing. error? + continue; + } + + // skip blank names + const CString& name = index->second; + if (name.empty()) { + continue; + } + + // check name. name must be legal and must not have + // already been seen. + if (screenNames.count(name)) { + // FIXME -- better error message + LOG((CLOG_WARN "editmap: duplicate name %s", name.c_str())); + throw XHTTP(400); + } + // FIXME -- check that name is legal + + // save name. if we've already seen the name then + // report an error. + screens.set(x, y, name); + screenNames.insert(name); + } + } + + // if new map is invalid then return error. map is invalid if: + // there are no screens, or + // the screens are not 4-connected. + if (screenNames.empty()) { + // no screens + // FIXME -- need better no screens + LOG((CLOG_WARN "editmap: no screens")); + throw XHTTP(400); + } + if (!screens.isValid()) { + // FIXME -- need better unconnected screens error + LOG((CLOG_WARN "editmap: unconnected screens")); + throw XHTTP(400); + } + + // convert temporary screen map into a regular map + CConfig config; + m_server->getConfig(&config); + screens.convertTo(config); + + // set new screen map on server + m_server->setConfig(config); + + // now reply with current map + doProcessGetEditMap(request, reply); + } + catch (XHTTP&) { + // FIXME -- construct a more meaningful error? + throw; + } +} + +bool +CHTTPServer::parseXY(const CString& xy, SInt32& x, SInt32& y) +{ + std::istringstream s(xy); + char delimiter; + s >> x; + s.get(delimiter); + s >> y; + return (!!s && delimiter == 'x'); +} + + +// +// CHTTPServer::CScreenArray +// + +CHTTPServer::CScreenArray::CScreenArray() : + m_w(0), + m_h(0) +{ + // do nothing +} + +CHTTPServer::CScreenArray::~CScreenArray() +{ + // do nothing +} + +void +CHTTPServer::CScreenArray::resize(SInt32 w, SInt32 h) +{ + m_screens.clear(); + m_screens.resize(w * h); + m_w = w; + m_h = h; +} + +void +CHTTPServer::CScreenArray::insertRow(SInt32 i) +{ + assert(i >= 0 && i <= m_h); + + CNames newScreens; + newScreens.resize(m_w * (m_h + 1)); + + for (SInt32 y = 0; y < i; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + y * m_w] = m_screens[x + y * m_w]; + } + } + for (SInt32 y = i; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + (y + 1) * m_w] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + ++m_h; +} + +void +CHTTPServer::CScreenArray::insertColumn(SInt32 i) +{ + assert(i >= 0 && i <= m_w); + + CNames newScreens; + newScreens.resize((m_w + 1) * m_h); + + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < i; ++x) { + newScreens[x + y * (m_w + 1)] = m_screens[x + y * m_w]; + } + for (SInt32 x = i; x < m_w; ++x) { + newScreens[(x + 1) + y * (m_w + 1)] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + ++m_w; +} + +void +CHTTPServer::CScreenArray::eraseRow(SInt32 i) +{ + assert(i >= 0 && i < m_h); + + CNames newScreens; + newScreens.resize(m_w * (m_h - 1)); + + for (SInt32 y = 0; y < i; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + y * m_w] = m_screens[x + y * m_w]; + } + } + for (SInt32 y = i + 1; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + (y - 1) * m_w] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + --m_h; +} + +void +CHTTPServer::CScreenArray::eraseColumn(SInt32 i) +{ + assert(i >= 0 && i < m_w); + + CNames newScreens; + newScreens.resize((m_w - 1) * m_h); + + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + y * (m_w - 1)] = m_screens[x + y * m_w]; + } + for (SInt32 x = i + 1; x < m_w; ++x) { + newScreens[(x - 1) + y * (m_w - 1)] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + --m_w; +} + +void +CHTTPServer::CScreenArray::rotateRows(SInt32 i) +{ + // nothing to do if no rows + if (m_h == 0) { + return; + } + + // convert to canonical form + if (i < 0) { + i = m_h - ((-i) % m_h); + } + else { + i %= m_h; + } + if (i == 0 || i == m_h) { + return; + } + + while (i > 0) { + // rotate one row + for (SInt32 x = 0; x < m_w; ++x) { + CString tmp = m_screens[x]; + for (SInt32 y = 1; y < m_h; ++y) { + m_screens[x + (y - 1) * m_w] = m_screens[x + y * m_w]; + } + m_screens[x + (m_h - 1) * m_w] = tmp; + } + } +} + +void +CHTTPServer::CScreenArray::rotateColumns(SInt32 i) +{ + // nothing to do if no columns + if (m_h == 0) { + return; + } + + // convert to canonical form + if (i < 0) { + i = m_w - ((-i) % m_w); + } + else { + i %= m_w; + } + if (i == 0 || i == m_w) { + return; + } + + while (i > 0) { + // rotate one column + for (SInt32 y = 0; y < m_h; ++y) { + CString tmp = m_screens[0 + y * m_w]; + for (SInt32 x = 1; x < m_w; ++x) { + m_screens[x - 1 + y * m_w] = m_screens[x + y * m_w]; + } + m_screens[m_w - 1 + y * m_w] = tmp; + } + } +} + +void +CHTTPServer::CScreenArray::remove(SInt32 x, SInt32 y) +{ + set(x, y, CString()); +} + +void +CHTTPServer::CScreenArray::set(SInt32 x, SInt32 y, const CString& name) +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + m_screens[x + y * m_w] = name; +} + +bool +CHTTPServer::CScreenArray::isAllowed(SInt32 x, SInt32 y) const +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + if (x > 0 && !m_screens[(x - 1) + y * m_w].empty()) { + return true; + } + if (x < m_w - 1 && !m_screens[(x + 1) + y * m_w].empty()) { + return true; + } + if (y > 0 && !m_screens[x + (y - 1) * m_w].empty()) { + return true; + } + if (y < m_h - 1 && !m_screens[x + (y + 1) * m_w].empty()) { + return true; + } + return false; +} + +bool +CHTTPServer::CScreenArray::isSet(SInt32 x, SInt32 y) const +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + return !m_screens[x + y * m_w].empty(); +} + +CString +CHTTPServer::CScreenArray::get(SInt32 x, SInt32 y) const +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + return m_screens[x + y * m_w]; +} + +bool +CHTTPServer::CScreenArray::find(const CString& name, + SInt32& xOut, SInt32& yOut) const +{ + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (m_screens[x + y * m_w] == name) { + xOut = x; + yOut = y; + return true; + } + } + } + return false; +} + +bool +CHTTPServer::CScreenArray::isValid() const +{ + SInt32 count = 0, isolated = 0; + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (isSet(x, y)) { + ++count; + if (!isAllowed(x, y)) { + ++isolated; + } + } + } + } + return (count <= 1 || isolated == 0); +} + +bool +CHTTPServer::CScreenArray::convertFrom(const CConfig& config) +{ + typedef std::set ScreenSet; + + // insert the first screen + CConfig::const_iterator index = config.begin(); + if (index == config.end()) { + // no screens + resize(0, 0); + return true; + } + CString name = *index; + resize(1, 1); + set(0, 0, name); + + // flood fill state + CNames screenStack; + ScreenSet doneSet; + + // put all but the first screen on the stack + // note -- if all screens are 4-connected then we can skip this + while (++index != config.end()) { + screenStack.push_back(*index); + } + + // put the first screen on the stack last so we process it first + screenStack.push_back(name); + + // perform a flood fill using the stack as the seeds + while (!screenStack.empty()) { + // get next screen from stack + CString name = screenStack.back(); + screenStack.pop_back(); + + // skip screen if we've seen it before + if (doneSet.count(name) > 0) { + continue; + } + + // add this screen to doneSet so we don't process it again + doneSet.insert(name); + + // find the screen. if it's not found then not all of the + // screens are 4-connected. discard disconnected screens. + SInt32 x, y; + if (!find(name, x, y)) { + continue; + } + + // insert the screen's neighbors + // FIXME -- handle edge wrapping + CString neighbor; + neighbor = config.getNeighbor(name, kLeft); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert left neighbor, adding a column if necessary + if (x == 0 || get(x - 1, y) != neighbor) { + ++x; + insertColumn(x - 1); + set(x - 1, y, neighbor); + } + screenStack.push_back(neighbor); + } + neighbor = config.getNeighbor(name, kRight); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert right neighbor, adding a column if necessary + if (x == m_w - 1 || get(x + 1, y) != neighbor) { + insertColumn(x + 1); + set(x + 1, y, neighbor); + } + screenStack.push_back(neighbor); + } + neighbor = config.getNeighbor(name, kTop); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert top neighbor, adding a row if necessary + if (y == 0 || get(x, y - 1) != neighbor) { + ++y; + insertRow(y - 1); + set(x, y - 1, neighbor); + } + screenStack.push_back(neighbor); + } + neighbor = config.getNeighbor(name, kBottom); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert bottom neighbor, adding a row if necessary + if (y == m_h - 1 || get(x, y + 1) != neighbor) { + insertRow(y + 1); + set(x, y + 1, neighbor); + } + screenStack.push_back(neighbor); + } + } + + // check symmetry + // FIXME -- handle edge wrapping + for (index = config.begin(); index != config.end(); ++index) { + const CString& name = *index; + SInt32 x, y; + if (!find(name, x, y)) { + return false; + } + + CString neighbor; + neighbor = config.getNeighbor(name, kLeft); + if ((x == 0 && !neighbor.empty()) || + (x > 0 && get(x - 1, y) != neighbor)) { + return false; + } + + neighbor = config.getNeighbor(name, kRight); + if ((x == m_w - 1 && !neighbor.empty()) || + (x < m_w - 1 && get(x + 1, y) != neighbor)) { + return false; + } + + neighbor = config.getNeighbor(name, kTop); + if ((y == 0 && !neighbor.empty()) || + (y > 0 && get(x, y - 1) != neighbor)) { + return false; + } + + neighbor = config.getNeighbor(name, kBottom); + if ((y == m_h - 1 && !neighbor.empty()) || + (y < m_h - 1 && get(x, y + 1) != neighbor)) { + return false; + } + } + + return true; +} + +void +CHTTPServer::CScreenArray::convertTo(CConfig& config) const +{ + config.removeAllScreens(); + + // add screens and find smallest box containing all screens + SInt32 x0 = m_w, x1 = 0, y0 = m_h, y1 = 0; + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (isSet(x, y)) { + config.addScreen(get(x, y)); + if (x < x0) { + x0 = x; + } + if (x > x1) { + x1 = x; + } + if (y < y0) { + y0 = y; + } + if (y > y1) { + y1 = y; + } + } + + } + } + + // make connections between screens + // FIXME -- add support for wrapping + // FIXME -- mark topmost and leftmost screens + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (!isSet(x, y)) { + continue; + } + if (x > x0 && isSet(x - 1, y)) { + config.connect(get(x, y), kLeft, get(x - 1, y)); + } + if (x < x1 && isSet(x + 1, y)) { + config.connect(get(x, y), kRight, get(x + 1, y)); + } + if (y > y0 && isSet(x, y - 1)) { + config.connect(get(x, y), kTop, get(x, y - 1)); + } + if (y < y1 && isSet(x, y + 1)) { + config.connect(get(x, y), kBottom, get(x, y + 1)); + } + } + } +} diff --git a/lib/server/CHTTPServer.h b/lib/server/CHTTPServer.h new file mode 100644 index 0000000..18f8044 --- /dev/null +++ b/lib/server/CHTTPServer.h @@ -0,0 +1,142 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CHTTPSERVER_H +#define CHTTPSERVER_H + +#include "CString.h" +#include "BasicTypes.h" +#include "stdvector.h" + +class CServer; +class CConfig; +class CHTTPRequest; +class CHTTPReply; +class IDataSocket; + +//! Simple HTTP server +/*! +This class implements a simple HTTP server for interacting with the +synergy server. +*/ +class CHTTPServer { +public: + CHTTPServer(CServer*); + virtual ~CHTTPServer(); + + //! @name manipulators + //@{ + + //! Process HTTP request + /*! + Synchronously processes an HTTP request on the given socket. + */ + void processRequest(IDataSocket*); + + //@} + +protected: + //! Process HTTP request + /*! + Processes a successfully read HTTP request. The reply is partially + filled in (version, method, status (200) and reason (OK)). This + method checks the URI and handles the request, filling in the rest + of the reply. If the request cannot be satisfied it throws an + appropriate XHTTP exception. + */ + virtual void doProcessRequest(CHTTPRequest&, CHTTPReply&); + + //! Process request for map + virtual void doProcessGetEditMap(CHTTPRequest&, CHTTPReply&); + + //! Process request for changing map + virtual void doProcessPostEditMap(CHTTPRequest&, CHTTPReply&); + + //! Parse coordinate string + static bool parseXY(const CString&, SInt32& x, SInt32& y); + + //! Screen map helper + /*! + This class represents the screen map as a resizable array. It's + used to handle map requests. + */ + class CScreenArray { + public: + CScreenArray(); + ~CScreenArray(); + + // resize the array. this also clears all the elements. + void resize(SInt32 w, SInt32 h); + + // insert/remove a row/column. all elements in a new row/column + // are unset. + void insertRow(SInt32 insertedBeforeRow); + void insertColumn(SInt32 insertedBeforeColumn); + void eraseRow(SInt32 row); + void eraseColumn(SInt32 column); + + // rotate rows or columns + void rotateRows(SInt32 rowsDown); + void rotateColumns(SInt32 columnsDown); + + // remove/set a screen name. setting an empty name is the + // same as removing a name. names are not checked for + // validity. + void remove(SInt32 x, SInt32 y); + void set(SInt32 x, SInt32 y, const CString&); + + // convert a CConfig to a CScreenArray. returns true iff + // all connections are symmetric and therefore exactly + // representable by a CScreenArray. + bool convertFrom(const CConfig&); + + // accessors + + // get the array size + SInt32 getWidth() const { return m_w; } + SInt32 getHeight() const { return m_h; } + + // returns true iff the cell has a 4-connected neighbor + bool isAllowed(SInt32 x, SInt32 y) const; + + // returns true iff the cell has a (non-empty) name + bool isSet(SInt32 x, SInt32 y) const; + + // get a screen name + CString get(SInt32 x, SInt32 y) const; + + // find a screen by name. returns true iff found. + bool find(const CString&, SInt32& x, SInt32& y) const; + + // return true iff the overall array is valid. that means + // just zero or one screen or all screens are 4-connected + // to other screens. + bool isValid() const; + + // convert this to a CConfig + void convertTo(CConfig&) const; + + private: + typedef std::vector CNames; + + SInt32 m_w, m_h; + CNames m_screens; + }; + +private: + CServer* m_server; + static const UInt32 s_maxRequestSize; +}; + +#endif diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp new file mode 100644 index 0000000..b425a68 --- /dev/null +++ b/lib/server/CPrimaryClient.cpp @@ -0,0 +1,295 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CPrimaryClient.h" +#include "IPrimaryScreenFactory.h" +#include "IServer.h" +#include "XScreen.h" +#include "XSynergy.h" +#include "CPrimaryScreen.h" +#include "CClipboard.h" +#include "CLog.h" + +// +// CPrimaryClient +// + +CPrimaryClient::CPrimaryClient(IPrimaryScreenFactory* screenFactory, + IServer* server, + IPrimaryScreenReceiver* receiver, + 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 (screenFactory != NULL) { + m_screen = screenFactory->create(this, receiver); + } + if (m_screen == NULL) { + throw XScreenOpenFailure(); + } +} + +CPrimaryClient::~CPrimaryClient() +{ + LOG((CLOG_DEBUG1 "destroying primary screen")); + delete m_screen; +} + +void +CPrimaryClient::exitMainLoop() +{ + m_screen->exitMainLoop(); +} + +void +CPrimaryClient::reconfigure(UInt32 activeSides) +{ + m_screen->reconfigure(activeSides); +} + +UInt32 +CPrimaryClient::addOneShotTimer(double timeout) +{ + return m_screen->addOneShotTimer(timeout); +} + +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() +{ + // forward to server + m_server->onError(); +} + +void +CPrimaryClient::onInfoChanged(const CClientInfo& info) +{ + m_info = info; + try { + m_server->onInfoChanged(getName(), m_info); + } + catch (XBadClient&) { + // ignore + } +} + +bool +CPrimaryClient::onGrabClipboard(ClipboardID id) +{ + try { + return m_server->onGrabClipboard(getName(), id, m_seqNum); + } + catch (XBadClient&) { + return false; + } +} + +void +CPrimaryClient::onClipboardChanged(ClipboardID id, const CString& data) +{ + m_server->onClipboardChanged(id, m_seqNum, data); +} + +void +CPrimaryClient::open() +{ + // all clipboards are clean + for (UInt32 i = 0; i < kClipboardEnd; ++i) { + m_clipboardDirty[i] = false; + } + + // now open the screen + m_screen->open(); +} + +void +CPrimaryClient::mainLoop() +{ + m_screen->mainLoop(); +} + +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_clipboardDirty[id] = true; +} + +void +CPrimaryClient::setClipboardDirty(ClipboardID id, bool dirty) +{ + m_clipboardDirty[id] = dirty; +} + +void +CPrimaryClient::keyDown(KeyID, KeyModifierMask, KeyButton) +{ + // ignore +} + +void +CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton) +{ + // ignore +} + +void +CPrimaryClient::keyUp(KeyID, KeyModifierMask, KeyButton) +{ + // 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 +} + +void +CPrimaryClient::resetOptions() +{ + m_screen->resetOptions(); +} + +void +CPrimaryClient::setOptions(const COptionsList& options) +{ + m_screen->setOptions(options); +} + +CString +CPrimaryClient::getName() const +{ + return m_name; +} + +SInt32 +CPrimaryClient::getJumpZoneSize() const +{ + return m_info.m_zoneSize; +} + +void +CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + x = m_info.m_x; + y = m_info.m_y; + w = m_info.m_w; + h = m_info.m_h; +} + +void +CPrimaryClient::getCursorPos(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + +void +CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const +{ + x = m_info.m_mx; + y = m_info.m_my; +} diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h new file mode 100644 index 0000000..d0202cf --- /dev/null +++ b/lib/server/CPrimaryClient.h @@ -0,0 +1,137 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CPRIMARYCLIENT_H +#define CPRIMARYCLIENT_H + +#include "IClient.h" +#include "IScreenReceiver.h" +#include "ProtocolTypes.h" + +class IClipboard; +class CPrimaryScreen; +class IPrimaryScreenFactory; +class IPrimaryScreenReceiver; +class IServer; + +//! Primary screen as pseudo-client +/*! +The primary screen does not have a client associated with it. This +class provides a pseudo-client to allow the primary screen to be +treated as if it was on a client. +*/ +class CPrimaryClient : public IScreenReceiver, public IClient { +public: + /*! + \c name is the name of the server. The caller retains ownership of + \c factory. Throws XScreenOpenFailure or whatever the factory can + throw if the screen cannot be created. + */ + CPrimaryClient(IPrimaryScreenFactory* factory, IServer*, + IPrimaryScreenReceiver*, const CString& name); + ~CPrimaryClient(); + + //! @name manipulators + //@{ + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). This may only be + called between a successful open() and close(). + */ + void exitMainLoop(); + + //! Update configuration + /*! + Handles reconfiguration of jump zones. + */ + void reconfigure(UInt32 activeSides); + + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer (which will be passed to \c onTimerExpired()). + */ + UInt32 addOneShotTimer(double timeout); + + //@} + //! @name accessors + //@{ + + //! Get clipboard + /*! + Save the marshalled contents of the clipboard indicated by \c id. + */ + void getClipboard(ClipboardID, CString&) const; + + //! Get toggle key state + /*! + Returns the primary screen's current toggle modifier key state. + */ + KeyModifierMask getToggleMask() const; + + //! Get screen lock state + /*! + Returns true if the user is locked to the screen. + */ + bool isLockedToScreen() const; + + //@} + + // IScreenReceiver overrides + virtual void onError(); + virtual void onInfoChanged(const CClientInfo&); + virtual bool onGrabClipboard(ClipboardID); + virtual void onClipboardChanged(ClipboardID, const CString&); + + // IClient overrides + virtual void open(); + virtual void mainLoop(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool); + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); + 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 resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; + +private: + IServer* m_server; + CPrimaryScreen* m_screen; + CString m_name; + UInt32 m_seqNum; + CClientInfo m_info; + bool m_clipboardDirty[kClipboardEnd]; +}; + +#endif diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp new file mode 100644 index 0000000..fa2487d --- /dev/null +++ b/lib/server/CServer.cpp @@ -0,0 +1,2141 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CServer.h" +#include "CHTTPServer.h" +#include "CPrimaryClient.h" +#include "IPrimaryScreenFactory.h" +#include "CInputPacketStream.h" +#include "COutputPacketStream.h" +#include "CProtocolUtil.h" +#include "CClientProxy1_0.h" +#include "CClientProxy1_1.h" +#include "OptionTypes.h" +#include "ProtocolTypes.h" +#include "XScreen.h" +#include "XSynergy.h" +#include "CTCPListenSocket.h" +#include "IDataSocket.h" +#include "ISocketFactory.h" +#include "XSocket.h" +#include "IStreamFilterFactory.h" +#include "CLock.h" +#include "CThread.h" +#include "CTimerThread.h" +#include "XMT.h" +#include "XThread.h" +#include "CFunctionJob.h" +#include "CLog.h" +#include "CStopwatch.h" +#include "TMethodJob.h" +#include "CArch.h" + +// +// CServer +// + +const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; + +CServer::CServer(const CString& serverName) : + m_name(serverName), + m_error(false), + m_bindTimeout(5.0 * 60.0), + m_screenFactory(NULL), + m_socketFactory(NULL), + m_streamFilterFactory(NULL), + m_acceptClientThread(NULL), + m_active(NULL), + m_primaryClient(NULL), + m_seqNum(0), + m_activeSaver(NULL), + m_httpServer(NULL), + m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests), + m_switchDir(kNoDirection), + m_switchScreen(NULL), + m_switchWaitDelay(0.0), + m_switchWaitEngaged(false), + m_switchTwoTapDelay(0.0), + m_switchTwoTapEngaged(false), + m_switchTwoTapArmed(false), + m_switchTwoTapZone(3), + m_status(kNotRunning) +{ + // do nothing +} + +CServer::~CServer() +{ + delete m_screenFactory; + delete m_socketFactory; + delete m_streamFilterFactory; +} + +void +CServer::open() +{ + // open the screen + try { + LOG((CLOG_INFO "opening screen")); + openPrimaryScreen(); + setStatus(kNotRunning); + } + catch (XScreen& e) { + // can't open screen + setStatus(kError, e.what()); + LOG((CLOG_INFO "failed to open screen")); + throw; + } + catch (XUnknownClient& e) { + // can't open screen + setStatus(kServerNameUnknown); + LOG((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); + throw; + } +} + +void +CServer::mainLoop() +{ + // check preconditions + { + CLock lock(&m_mutex); + assert(m_primaryClient != NULL); + } + + try { + setStatus(kNotRunning); + LOG((CLOG_NOTE "starting server")); + + // start listening for new clients + m_acceptClientThread = new CThread(startThread( + new TMethodJob(this, + &CServer::acceptClients))); + + // start listening for HTTP requests + if (m_config.getHTTPAddress().isValid()) { + m_httpServer = new CHTTPServer(this); + startThread(new TMethodJob(this, + &CServer::acceptHTTPClients)); + } + + // handle events + m_primaryClient->mainLoop(); + + // clean up + LOG((CLOG_NOTE "stopping server")); + + // 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; \ + runStatusJobs(); \ + } while (false) + FINALLY; + } + catch (XMT& e) { + LOG((CLOG_ERR "server error: %s", e.what())); + setStatus(kError, e.what()); + + // clean up + LOG((CLOG_NOTE "stopping server")); + FINALLY; + throw; + } + catch (XBase& e) { + LOG((CLOG_ERR "server error: %s", e.what())); + setStatus(kError, e.what()); + + // clean up + LOG((CLOG_NOTE "stopping server")); + FINALLY; + } + catch (XThread&) { + setStatus(kNotRunning); + + // clean up + LOG((CLOG_NOTE "stopping server")); + FINALLY; + throw; + } + catch (...) { + LOG((CLOG_DEBUG "unknown server error")); + setStatus(kError); + + // clean up + LOG((CLOG_NOTE "stopping server")); + FINALLY; + throw; + } +#undef FINALLY + + // throw if there was an error + if (m_error) { + LOG((CLOG_DEBUG "forwarding child thread exception")); + throw XServerRethrow(); + } +} + +void +CServer::exitMainLoop() +{ + m_primaryClient->exitMainLoop(); +} + +void +CServer::exitMainLoopWithError() +{ + { + CLock lock(&m_mutex); + m_error = true; + } + exitMainLoop(); +} + +void +CServer::close() +{ + if (m_primaryClient != NULL) { + closePrimaryScreen(); + } + LOG((CLOG_INFO "closed screen")); +} + +bool +CServer::setConfig(const CConfig& config) +{ + // refuse configuration if it doesn't include the primary screen + { + CLock lock(&m_mutex); + if (m_primaryClient != NULL && + !config.isScreen(m_primaryClient->getName())) { + return false; + } + } + + // close clients that are connected but being dropped from the + // configuration. + closeClients(config); + + // cut over + CLock lock(&m_mutex); + m_config = config; + + // process global options + const CConfig::CScreenOptions* options = m_config.getOptions(""); + if (options != NULL && options->size() > 0) { + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + const OptionID id = index->first; + const OptionValue value = index->second; + if (id == kOptionScreenSwitchDelay) { + m_switchWaitDelay = 1.0e-3 * static_cast(value); + if (m_switchWaitDelay < 0.0) { + m_switchWaitDelay = 0.0; + } + m_switchWaitEngaged = false; + } + else if (id == kOptionScreenSwitchTwoTap) { + m_switchTwoTapDelay = 1.0e-3 * static_cast(value); + if (m_switchTwoTapDelay < 0.0) { + m_switchTwoTapDelay = 0.0; + } + m_switchTwoTapEngaged = false; + } + } + } + + // tell primary screen about reconfiguration + if (m_primaryClient != NULL) { + m_primaryClient->reconfigure(getActivePrimarySides()); + } + + // tell all (connected) clients about current options + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + sendOptions(client); + } + + // notify of status + runStatusJobs(); + + return true; +} + +void +CServer::setScreenFactory(IPrimaryScreenFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_screenFactory; + m_screenFactory = adopted; +} + +void +CServer::setSocketFactory(ISocketFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_socketFactory; + m_socketFactory = adopted; +} + +void +CServer::setStreamFilterFactory(IStreamFilterFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_streamFilterFactory; + m_streamFilterFactory = adopted; +} + +void +CServer::addStatusJob(IJob* job) +{ + m_statusJobs.addJob(job); +} + +void +CServer::removeStatusJob(IJob* job) +{ + m_statusJobs.removeJob(job); +} + +CString +CServer::getPrimaryScreenName() const +{ + return m_name; +} + +UInt32 +CServer::getNumClients() const +{ + CLock lock(&m_mutex); + return m_clients.size(); +} + +void +CServer::getClients(std::vector& list) const +{ + CLock lock(&m_mutex); + list.clear(); + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + list.push_back(index->first); + } +} + +CServer::EStatus +CServer::getStatus(CString* msg) const +{ + CLock lock(&m_mutex); + if (msg != NULL) { + *msg = m_statusMessage; + } + return m_status; +} + +void +CServer::getConfig(CConfig* config) const +{ + assert(config != NULL); + + CLock lock(&m_mutex); + *config = m_config; +} + +void +CServer::runStatusJobs() const +{ + m_statusJobs.runJobs(); +} + +void +CServer::setStatus(EStatus status, const char* msg) +{ + { + CLock lock(&m_mutex); + m_status = status; + if (m_status == kError) { + m_statusMessage = (msg == NULL) ? "Error" : msg; + } + else { + m_statusMessage = (msg == NULL) ? "" : msg; + } + } + runStatusJobs(); +} + +UInt32 +CServer::getActivePrimarySides() const +{ + // note -- m_mutex must be locked on entry + UInt32 sides = 0; + if (!m_config.getNeighbor(getPrimaryScreenName(), kLeft).empty()) { + sides |= kLeftMask; + } + if (!m_config.getNeighbor(getPrimaryScreenName(), kRight).empty()) { + sides |= kRightMask; + } + if (!m_config.getNeighbor(getPrimaryScreenName(), kTop).empty()) { + sides |= kTopMask; + } + if (!m_config.getNeighbor(getPrimaryScreenName(), kBottom).empty()) { + sides |= kBottomMask; + } + return sides; +} + +void +CServer::onError() +{ + setStatus(kError); + + // 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 + CLock lock(&m_mutex); + delete m_httpServer; + m_httpServer = NULL; + + // note -- we do not attempt to close down the primary screen +} + +void +CServer::onInfoChanged(const CString& name, const CClientInfo& info) +{ + CLock lock(&m_mutex); + + // look up client + CClientList::iterator index = m_clients.find(name); + if (index == m_clients.end()) { + throw XBadClient(); + } + IClient* client = index->second; + assert(client != NULL); + + // update the remote mouse coordinates + if (client == m_active) { + m_x = info.m_mx; + m_y = info.m_my; + } + LOG((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", name.c_str(), info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); + + // handle resolution change to primary screen + if (client == m_primaryClient) { + if (client == m_active) { + onMouseMovePrimaryNoLock(m_x, m_y); + } + else { + onMouseMoveSecondaryNoLock(0, 0); + } + } +} + +bool +CServer::onGrabClipboard(const CString& name, ClipboardID id, UInt32 seqNum) +{ + CLock lock(&m_mutex); + + // screen must be connected + 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. + CClipboardInfo& clipboard = m_clipboards[id]; + if (name != m_primaryClient->getName() && + seqNum < clipboard.m_clipboardSeqNum) { + 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\"", name.c_str(), id, clipboard.m_clipboardOwner.c_str())); + clipboard.m_clipboardOwner = name; + clipboard.m_clipboardSeqNum = seqNum; + + // 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. 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); + } + } + + return true; +} + +void +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 (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_clipboardData = data; + clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); + + // 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 + m_active->setClipboard(id, m_clipboards[id].m_clipboardData); +} + +void +CServer::onScreensaver(bool activated) +{ + LOG((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); + CLock lock(&m_mutex); + + if (activated) { + // save current screen and position + m_activeSaver = m_active; + m_xSaver = m_x; + m_ySaver = m_y; + + // jump to primary screen + 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_primaryClient) { + // check position + 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 >= x + w - zoneSize) { + m_xSaver = x + w - zoneSize - 1; + } + if (m_ySaver < y + zoneSize) { + m_ySaver = y + zoneSize; + } + else if (m_ySaver >= y + h - zoneSize) { + m_ySaver = y + h - zoneSize - 1; + } + + // jump + switchScreen(screen, m_xSaver, m_ySaver, false); + } + + // reset state + m_activeSaver = NULL; + } + + // 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); + } +} + +void +CServer::onOneShotTimerExpired(UInt32 id) +{ + CLock lock(&m_mutex); + + // ignore if it's an old timer or if switch wait isn't engaged anymore + if (!m_switchWaitEngaged || id != m_switchWaitTimer) { + return; + } + + // ignore if mouse is locked to screen + if (isLockedToScreenNoLock()) { + LOG((CLOG_DEBUG1 "locked to screen")); + clearSwitchState(); + return; + } + + // switch screen + switchScreen(m_switchScreen, m_switchWaitX, m_switchWaitY, false); +} + +void +CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, true)) { + return; + } + + // relay + m_active->keyDown(id, mask, button); +} + +void +CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, false)) { + return; + } + + // relay + m_active->keyUp(id, mask, button); +} + +void +CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, false)) { + onCommandKey(id, mask, true); + return; + } + + // relay + m_active->keyRepeat(id, mask, count, button); +} + +void +CServer::onMouseDown(ButtonID id) +{ + LOG((CLOG_DEBUG1 "onMouseDown id=%d", id)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // relay + m_active->mouseDown(id); +} + +void +CServer::onMouseUp(ButtonID id) +{ + LOG((CLOG_DEBUG1 "onMouseUp id=%d", id)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // relay + m_active->mouseUp(id); +} + +bool +CServer::onMouseMovePrimary(SInt32 x, SInt32 y) +{ + LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); + CLock lock(&m_mutex); + return onMouseMovePrimaryNoLock(x, y); +} + +bool +CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) +{ + // mouse move on primary (server's) screen + assert(m_primaryClient != NULL); + assert(m_active == m_primaryClient); + + // 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 + EDirection dir; + if (x < ax + zoneSize) { + x -= zoneSize; + dir = kLeft; + } + else if (x >= ax + aw - zoneSize) { + x += zoneSize; + dir = kRight; + } + else if (y < ay + zoneSize) { + y -= zoneSize; + dir = kTop; + } + else if (y >= ay + ah - zoneSize) { + y += zoneSize; + dir = kBottom; + } + else { + // still on local screen. check if we're inside the tap region. + SInt32 tapZone = (zoneSize < m_switchTwoTapZone) ? + m_switchTwoTapZone : zoneSize; + bool inTapZone = (x < ax + tapZone || + x >= ax + aw - tapZone || + y < ay + tapZone || + y >= ay + ah - tapZone); + + // failed to switch + onNoSwitch(inTapZone); + return false; + } + + // get jump destination + IClient* newScreen = getNeighbor(m_active, dir, x, y); + + // should we switch or not? + if (isSwitchOkay(newScreen, dir, x, y)) { + // switch screen + switchScreen(newScreen, x, y, false); + return true; + } + else { + return false; + } +} + +void +CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) +{ + LOG((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); + CLock lock(&m_mutex); + onMouseMoveSecondaryNoLock(dx, dy); +} + +void +CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) +{ + // mouse move on secondary (client's) screen + assert(m_active != 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) + // screen disconnects causing us to jump to the primary + // screen, and finally the primary screen finishes + // processing the mouse move, still thinking it's for + // a secondary screen. we just ignore the motion. + return; + } + + // save old position + const SInt32 xOld = m_x; + const SInt32 yOld = m_y; + + // accumulate motion + m_x += dx; + m_y += dy; + + // get screen shape + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + + // find direction of neighbor and get the neighbor + bool jump = true; + IClient* newScreen; + do { + EDirection dir; + if (m_x < ax) { + dir = kLeft; + } + else if (m_x > ax + aw - 1) { + dir = kRight; + } + else if (m_y < ay) { + dir = kTop; + } + else if (m_y > ay + ah - 1) { + dir = kBottom; + } + else { + // we haven't left the screen + newScreen = m_active; + jump = false; + + // if waiting and mouse is not on the border we're waiting + // on then stop waiting. also if it's not on the border + // then arm the double tap. + if (m_switchScreen != NULL) { + bool clearWait; + SInt32 zoneSize = m_primaryClient->getJumpZoneSize(); + switch (m_switchDir) { + case kLeft: + clearWait = (m_x >= ax + zoneSize); + break; + + case kRight: + clearWait = (m_x <= ax + aw - 1 - zoneSize); + break; + + case kTop: + clearWait = (m_y >= ay + zoneSize); + break; + + case kBottom: + clearWait = (m_y <= ay + ah - 1 + zoneSize); + break; + + default: + clearWait = false; + break; + } + if (clearWait) { + // still on local screen. check if we're inside the + // tap region. + SInt32 tapZone = (zoneSize < m_switchTwoTapZone) ? + m_switchTwoTapZone : zoneSize; + bool inTapZone = (m_x < ax + tapZone || + m_x >= ax + aw - tapZone || + m_y < ay + tapZone || + m_y >= ay + ah - tapZone); + + // failed to switch + onNoSwitch(inTapZone); + } + } + + // skip rest of block + break; + } + + // try to switch screen. get the neighbor. + newScreen = getNeighbor(m_active, dir, m_x, m_y); + + // see if we should switch + if (!isSwitchOkay(newScreen, dir, m_x, m_y)) { + newScreen = m_active; + jump = false; + } + } while (false); + + if (jump) { + // switch screens + switchScreen(newScreen, m_x, m_y, false); + } + else { + // same screen. clamp mouse to edge. + m_x = xOld + dx; + m_y = yOld + dy; + if (m_x < ax) { + m_x = ax; + LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", m_active->getName().c_str())); + } + else if (m_x > ax + aw - 1) { + m_x = ax + aw - 1; + LOG((CLOG_DEBUG2 "clamp to right of \"%s\"", m_active->getName().c_str())); + } + if (m_y < ay) { + m_y = ay; + LOG((CLOG_DEBUG2 "clamp to top of \"%s\"", m_active->getName().c_str())); + } + else if (m_y > ay + ah - 1) { + m_y = ay + ah - 1; + LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", m_active->getName().c_str())); + } + + // warp cursor if it moved. + if (m_x != xOld || m_y != yOld) { + 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); + } + } +} + +void +CServer::onMouseWheel(SInt32 delta) +{ + LOG((CLOG_DEBUG1 "onMouseWheel %+d", delta)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // relay + m_active->mouseWheel(delta); +} + +bool +CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) +{ + return false; +} + +bool +CServer::isLockedToScreenNoLock() const +{ + // locked if scroll-lock is toggled on + if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) { + LOG((CLOG_DEBUG "locked by ScrollLock")); + return true; + } + + // locked if primary says we're locked + if (m_primaryClient->isLockedToScreen()) { + return true; + } + + // not locked + return false; +} + +void +CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) +{ + // note -- must be locked on entry + + assert(dst != NULL); +#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->getName().c_str(), dst->getName().c_str(), x, y)); + + // stop waiting to switch + clearSwitchState(); + + // record new position + m_x = x; + m_y = y; + + // wrapping means leaving the active screen and entering it again. + // since that's a waste of time we skip that and just warp the + // mouse. + if (m_active != dst) { + // leave active screen + if (!m_active->leave()) { + // cannot leave screen + LOG((CLOG_WARN "can't leave screen")); + return; + } + + // update the primary client's clipboards if we're leaving the + // primary screen. + if (m_active == m_primaryClient) { + for (ClipboardID 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, + clipboard.m_clipboardSeqNum, clipboardData); + } + } + } + + // cut over + m_active = dst; + + // increment enter sequence number + ++m_seqNum; + + // enter new screen + m_active->enter(x, y, m_seqNum, + m_primaryClient->getToggleMask(), + forScreensaver); + + // send the clipboard data to new active screen + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_active->setClipboard(id, m_clipboards[id].m_clipboardData); + } + } + else { + m_active->mouseMove(x, y); + } +} + +IClient* +CServer::getNeighbor(IClient* src, EDirection dir) const +{ + // note -- must be locked on entry + + assert(src != NULL); + + // get source screen name + CString srcName = src->getName(); + assert(!srcName.empty()); + LOG((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); + + // get first neighbor. if it's the source then the source jumps + // to itself and we return the source. + CString dstName(m_config.getNeighbor(srcName, dir)); + if (dstName == srcName) { + LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); + return src; + } + + // keep checking + for (;;) { + // if nothing in that direction then return NULL. if the + // destination is the source then we can make no more + // progress in this direction. since we haven't found a + // connected neighbor we return NULL. + if (dstName.empty() || dstName == srcName) { + LOG((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); + return NULL; + } + + // look up neighbor cell. if the screen is connected and + // ready then we can stop. + 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; + } + + // skip over unconnected screen + LOG((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); + srcName = dstName; + + // look up name of neighbor of skipped screen + dstName = m_config.getNeighbor(srcName, dir); + } +} + +IClient* +CServer::getNeighbor(IClient* src, + EDirection srcSide, SInt32& x, SInt32& y) const +{ + // note -- must be locked on entry + + assert(src != NULL); + + // get the first neighbor + IClient* dst = getNeighbor(src, srcSide); + if (dst == NULL) { + return NULL; + } + + // get the source screen's size (needed for kRight and kBottom) + 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. + switch (srcSide) { + case kLeft: + x -= dx; + while (dst != NULL) { + lastGoodScreen = dst; + lastGoodScreen->getShape(dx, dy, dw, dh); + x += dw; + if (x >= 0) { + break; + } + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + assert(lastGoodScreen != NULL); + x += dx; + break; + + case kRight: + x -= dx; + while (dst != NULL) { + x -= dw; + lastGoodScreen = dst; + lastGoodScreen->getShape(dx, dy, dw, dh); + if (x < dw) { + break; + } + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + assert(lastGoodScreen != NULL); + x += dx; + break; + + case kTop: + y -= dy; + while (dst != NULL) { + lastGoodScreen = dst; + lastGoodScreen->getShape(dx, dy, dw, dh); + y += dh; + if (y >= 0) { + break; + } + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + assert(lastGoodScreen != NULL); + y += dy; + break; + + case kBottom: + y -= dy; + while (dst != NULL) { + y -= dh; + lastGoodScreen = dst; + lastGoodScreen->getShape(dx, dy, dw, dh); + if (y < sh) { + break; + } + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + assert(lastGoodScreen != NULL); + y += dy; + break; + } + + // save destination screen + assert(lastGoodScreen != NULL); + dst = lastGoodScreen; + + // if entering primary screen then be sure to move in far enough + // 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_primaryClient) { + const CString dstName(dst->getName()); + switch (srcSide) { + case kLeft: + if (!m_config.getNeighbor(dstName, kRight).empty() && + x > dx + dw - 1 - dst->getJumpZoneSize()) + x = dx + dw - 1 - dst->getJumpZoneSize(); + break; + + case kRight: + if (!m_config.getNeighbor(dstName, kLeft).empty() && + x < dx + dst->getJumpZoneSize()) + x = dx + dst->getJumpZoneSize(); + break; + + case kTop: + if (!m_config.getNeighbor(dstName, kBottom).empty() && + y > dy + dh - 1 - dst->getJumpZoneSize()) + y = dy + dh - 1 - dst->getJumpZoneSize(); + break; + + case kBottom: + if (!m_config.getNeighbor(dstName, kTop).empty() && + y < dy + dst->getJumpZoneSize()) + y = dy + dst->getJumpZoneSize(); + break; + } + } + + // adjust the coordinate orthogonal to srcSide to account for + // resolution differences. for example, if y is 200 pixels from + // the top on a screen 1000 pixels high (20% from the top) when + // we cross the left edge onto a screen 600 pixels high then y + // should be set 120 pixels from the top (again 20% from the + // top). + switch (srcSide) { + case kLeft: + case kRight: + y -= sy; + if (y < 0) { + y = 0; + } + else if (y >= sh) { + y = dh - 1; + } + else { + y = static_cast(0.5 + y * + static_cast(dh - 1) / (sh - 1)); + } + y += dy; + break; + + case kTop: + case kBottom: + x -= sx; + if (x < 0) { + x = 0; + } + else if (x >= sw) { + x = dw - 1; + } + else { + x = static_cast(0.5 + x * + static_cast(dw - 1) / (sw - 1)); + } + x += dx; + break; + } + + return dst; +} + +bool +CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y) +{ + LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir))); + + // is there a neighbor? + if (newScreen == NULL) { + // there's no neighbor. we don't want to switch and we don't + // want to try to switch later. + LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir))); + clearSwitchState(); + return false; + } + + // should we switch or not? + bool preventSwitch = false; + bool allowSwitch = false; + + // note if the switch direction has changed. save the new + // direction and screen if so. + bool isNewDirection = (dir != m_switchDir); + if (isNewDirection || m_switchScreen == NULL) { + m_switchDir = dir; + m_switchScreen = newScreen; + } + + // is this a double tap and do we care? + if (!allowSwitch && m_switchTwoTapDelay > 0.0) { + if (isNewDirection || !m_switchTwoTapEngaged) { + // tapping a different or new edge. prepare for second tap. + preventSwitch = true; + m_switchTwoTapEngaged = true; + m_switchTwoTapArmed = false; + m_switchTwoTapTimer.reset(); + LOG((CLOG_DEBUG1 "waiting for second tap")); + } + else { + // second tap if we were armed. if soon enough then switch. + if (m_switchTwoTapArmed && + m_switchTwoTapTimer.getTime() <= m_switchTwoTapDelay) { + allowSwitch = true; + } + else { + // not fast enough. reset the clock. + preventSwitch = true; + m_switchTwoTapEngaged = true; + m_switchTwoTapArmed = false; + m_switchTwoTapTimer.reset(); + LOG((CLOG_DEBUG1 "waiting for second tap")); + } + } + } + + // if waiting before a switch then prepare to switch later + if (!allowSwitch && m_switchWaitDelay > 0.0) { + if (isNewDirection || !m_switchWaitEngaged) { + m_switchWaitEngaged = true; + m_switchWaitX = x; + m_switchWaitY = y; + m_switchWaitTimer = m_primaryClient->addOneShotTimer( + m_switchWaitDelay); + LOG((CLOG_DEBUG1 "waiting to switch")); + } + preventSwitch = true; + } + + // ignore if mouse is locked to screen + if (!preventSwitch && isLockedToScreenNoLock()) { + LOG((CLOG_DEBUG1 "locked to screen")); + preventSwitch = true; + + // don't try to switch later. it's possible that we might + // not be locked to the screen when the wait delay expires + // and could switch then but we'll base the decision on + // when the user first attempts the switch. this also + // ensures that all switch tests are using the same + clearSwitchState(); + } + + return !preventSwitch; +} + +void +CServer::onNoSwitch(bool inTapZone) +{ + if (m_switchTwoTapEngaged) { + if (m_switchTwoTapTimer.getTime() > m_switchTwoTapDelay) { + // second tap took too long. disengage. + m_switchTwoTapEngaged = false; + m_switchTwoTapArmed = false; + } + else if (!inTapZone) { + // we've moved away from the edge and there's still + // time to get back for a double tap. + m_switchTwoTapArmed = true; + } + } + + // once the mouse moves away from the edge we no longer want to + // switch after a delay. + m_switchWaitEngaged = false; +} + +void +CServer::clearSwitchState() +{ + if (m_switchScreen != NULL) { + m_switchDir = kNoDirection; + m_switchScreen = NULL; + m_switchWaitEngaged = false; + m_switchTwoTapEngaged = false; + } +} + +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)) { + // lookup IClient with name + CClientList::const_iterator index2 = m_clients.find(name); + assert(index2 != m_clients.end()); + + // save the thread and remove it from m_clientThreads + threads.push_back(index->second); + m_clientThreads.erase(index++); + + // close that client + assert(index2->second != m_primaryClient); + index2->second->close(); + + // don't switch to it if we planned to + if (index2->second == m_switchScreen) { + clearSwitchState(); + } + } + 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) { + ARCH->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(); +} + +CThread +CServer::startThread(IJob* job) +{ + CLock lock(&m_mutex); + + // reap completed threads + doReapThreads(m_threads); + + // add new thread to list + CThread thread(job); + m_threads.push_back(thread); + LOG((CLOG_DEBUG1 "started thread 0x%08x", thread.getID())); + return thread; +} + +void +CServer::stopThreads(double timeout) +{ + LOG((CLOG_DEBUG1 "stopping threads")); + + // cancel the accept client thread to prevent more clients from + // connecting while we're shutting down. + CThread* acceptClientThread; + { + CLock lock(&m_mutex); + acceptClientThread = m_acceptClientThread; + m_acceptClientThread = NULL; + } + if (acceptClientThread != NULL) { + acceptClientThread->cancel(); + acceptClientThread->wait(timeout); + delete acceptClientThread; + } + + // close all clients (except the primary) + { + CConfig emptyConfig; + closeClients(emptyConfig); + } + + // swap thread list so nobody can mess with it + CThreadList threads; + { + CLock lock(&m_mutex); + threads.swap(m_threads); + } + + // cancel every thread + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + index->cancel(); + } + + // now wait for the threads + CStopwatch timer(true); + while (threads.size() > 0 && (timeout < 0.0 || timer.getTime() < timeout)) { + doReapThreads(threads); + ARCH->sleep(0.01); + } + + // delete remaining threads + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + LOG((CLOG_DEBUG1 "reaped running thread 0x%08x", index->getID())); + } + + LOG((CLOG_DEBUG1 "stopped threads")); +} + +void +CServer::reapThreads() +{ + CLock lock(&m_mutex); + doReapThreads(m_threads); +} + +void +CServer::doReapThreads(CThreadList& threads) +{ + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ) { + if (index->wait(0.0)) { + // thread terminated + LOG((CLOG_DEBUG1 "reaped thread 0x%08x", index->getID())); + index = threads.erase(index); + } + else { + // thread is running + ++index; + } + } +} + +void +CServer::acceptClients(void*) +{ + LOG((CLOG_DEBUG1 "starting to wait for clients")); + + IListenSocket* listen = NULL; + try { + // create socket listener + if (m_socketFactory != NULL) { + listen = m_socketFactory->createListen(); + } + assert(listen != NULL); + + // bind to the desired port. keep retrying if we can't bind + // the address immediately. + CStopwatch timer; + for (;;) { + try { + LOG((CLOG_DEBUG1 "binding listen socket")); + listen->bind(m_config.getSynergyAddress()); + break; + } + catch (XSocketAddressInUse& e) { + setStatus(kError, e.what()); + LOG((CLOG_WARN "bind failed: %s", e.what())); + + // give up if we've waited too long + if (timer.getTime() >= m_bindTimeout) { + LOG((CLOG_ERR "waited too long to bind, giving up")); + throw; + } + + // wait a bit before retrying + ARCH->sleep(5.0); + } + } + + // accept connections and begin processing them + setStatus(kRunning); + LOG((CLOG_DEBUG1 "waiting for client connections")); + for (;;) { + // accept connection + CThread::testCancel(); + IDataSocket* socket = listen->accept(); + LOG((CLOG_NOTE "accepted client connection")); + CThread::testCancel(); + + // start handshake thread + startThread(new TMethodJob( + this, &CServer::runClient, socket)); + } + } + catch (XBase& e) { + setStatus(kError, e.what()); + LOG((CLOG_ERR "cannot listen for clients: %s", e.what())); + delete listen; + exitMainLoopWithError(); + } + catch (...) { + setStatus(kNotRunning); + delete listen; + throw; + } +} + +void +CServer::runClient(void* vsocket) +{ + // get the socket pointer from the argument + assert(vsocket != NULL); + IDataSocket* socket = reinterpret_cast(vsocket); + + // create proxy + CClientProxy* proxy = NULL; + try { + proxy = handshakeClient(socket); + if (proxy == NULL) { + delete socket; + return; + } + } + catch (...) { + delete socket; + throw; + } + + // 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())); + + // send configuration options + sendOptions(proxy); + } + catch (XDuplicateClient& e) { + // client has duplicate name + LOG((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); + try { + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy); + } + catch (XSocket&) { + // ignore + } + delete proxy; + delete socket; + 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())); + try { + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown); + } + catch (XSocket&) { + // ignore + } + delete proxy; + delete socket; + return; + } + catch (...) { + delete proxy; + delete socket; + 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->mainLoop(); + } + catch (XBadClient&) { + // client not behaving + LOG((CLOG_WARN "protocol error from client \"%s\"", proxy->getName().c_str())); + try { + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad); + } + catch (XSocket&) { + // ignore. client probably aborted the connection. + } + } + catch (XBase& e) { + // misc error + LOG((CLOG_WARN "error communicating with client \"%s\": %s", proxy->getName().c_str(), e.what())); + } + catch (...) { + // mainLoop() was probably cancelled + removeConnection(proxy->getName()); + delete socket; + throw; + } + + // clean up + removeConnection(proxy->getName()); + delete socket; +} + +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 filters + if (m_streamFilterFactory != NULL) { + input = m_streamFilterFactory->createInput(input, own); + output = m_streamFilterFactory->createOutput(output, own); + own = true; + } + + // attach the packetizing filters + input = new CInputPacketStream(input, own); + output = new COutputPacketStream(output, own); + own = true; + + CClientProxy* proxy = NULL; + CString name(""); + try { + // give the client a limited time to complete the handshake + CTimerThread timer(30.0); + + // say hello + LOG((CLOG_DEBUG1 "saying hello")); + CProtocolUtil::writef(output, kMsgHello, + kProtocolMajorVersion, + kProtocolMinorVersion); + output->flush(); + + // wait for the reply + LOG((CLOG_DEBUG1 "waiting for hello reply")); + UInt32 n = input->getSize(); + + // limit the maximum length of the hello + if (n > kMaxHelloLength) { + throw XBadClient(); + } + + // get and parse the reply to hello + SInt16 major, minor; + try { + LOG((CLOG_DEBUG1 "parsing hello reply")); + CProtocolUtil::readf(input, kMsgHelloBack, + &major, &minor, &name); + } + catch (XIO&) { + throw XBadClient(); + } + + // disallow invalid version numbers + if (major <= 0 || minor < 0) { + 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)); + if (major == 1) { + switch (minor) { + case 0: + proxy = new CClientProxy1_0(this, name, input, output); + break; + + case 1: + proxy = new CClientProxy1_1(this, name, input, output); + break; + } + } + + // hangup (with error) if version isn't supported + if (proxy == NULL) { + throw XIncompatibleClient(major, minor); + } + + // 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 + LOG((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); + try { + CProtocolUtil::writef(output, kMsgEIncompatible, + kProtocolMajorVersion, kProtocolMinorVersion); + } + catch (XSocket&) { + // ignore + } + } + catch (XBadClient&) { + // client not behaving + LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); + try { + CProtocolUtil::writef(output, kMsgEBad); + } + catch (XSocket&) { + // ignore. client probably aborted the connection. + } + } + catch (XBase& e) { + // misc error + LOG((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); + } + catch (...) { + // probably timed out + if (proxy != NULL) { + delete proxy; + } + else if (own) { + delete input; + delete output; + } + throw; + } + + // failed + if (proxy != NULL) { + delete proxy; + } + else if (own) { + delete input; + delete output; + } + + return NULL; +} + +void +CServer::acceptHTTPClients(void*) +{ + LOG((CLOG_DEBUG1 "starting to wait for HTTP clients")); + + IListenSocket* listen = NULL; + try { + // create socket listener + listen = new CTCPListenSocket; + + // bind to the desired port. keep retrying if we can't bind + // the address immediately. + CStopwatch timer; + for (;;) { + try { + LOG((CLOG_DEBUG1 "binding HTTP listen socket")); + listen->bind(m_config.getHTTPAddress()); + break; + } + catch (XSocketBind& e) { + LOG((CLOG_DEBUG1 "bind HTTP failed: %s", e.what())); + + // give up if we've waited too long + if (timer.getTime() >= m_bindTimeout) { + LOG((CLOG_DEBUG1 "waited too long to bind HTTP, giving up")); + throw; + } + + // wait a bit before retrying + ARCH->sleep(5.0); + } + } + + // accept connections and begin processing them + LOG((CLOG_DEBUG1 "waiting for HTTP connections")); + for (;;) { + // limit the number of HTTP requests being handled at once + { + CLock lock(&m_httpAvailable); + while (m_httpAvailable == 0) { + m_httpAvailable.wait(); + } + assert(m_httpAvailable > 0); + m_httpAvailable = m_httpAvailable - 1; + } + + // accept connection + CThread::testCancel(); + IDataSocket* socket = listen->accept(); + LOG((CLOG_NOTE "accepted HTTP connection")); + CThread::testCancel(); + + // handle HTTP request + startThread(new TMethodJob( + this, &CServer::processHTTPRequest, socket)); + } + + // clean up + delete listen; + } + catch (XBase& e) { + LOG((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); + delete listen; + exitMainLoopWithError(); + } + catch (...) { + delete listen; + throw; + } +} + +void +CServer::processHTTPRequest(void* vsocket) +{ + IDataSocket* socket = reinterpret_cast(vsocket); + try { + // process the request and force delivery + m_httpServer->processRequest(socket); + socket->getOutputStream()->flush(); + + // wait a moment to give the client a chance to hangup first + ARCH->sleep(3.0); + + // clean up + socket->close(); + delete socket; + + // increment available HTTP handlers + { + CLock lock(&m_httpAvailable); + m_httpAvailable = m_httpAvailable + 1; + m_httpAvailable.signal(); + } + } + catch (...) { + delete socket; + { + CLock lock(&m_httpAvailable); + m_httpAvailable = m_httpAvailable + 1; + m_httpAvailable.signal(); + } + throw; + } +} + +void +CServer::sendOptions(IClient* client) const +{ + // note -- must be locked on entry + + COptionsList optionsList; + + // look up options for client + const CConfig::CScreenOptions* options = + m_config.getOptions(client->getName()); + if (options != NULL && options->size() > 0) { + // convert options to a more convenient form for sending + optionsList.reserve(2 * options->size()); + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + optionsList.push_back(index->first); + optionsList.push_back(static_cast(index->second)); + } + } + + // look up global options + options = m_config.getOptions(""); + if (options != NULL && options->size() > 0) { + // convert options to a more convenient form for sending + optionsList.reserve(optionsList.size() + 2 * options->size()); + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + optionsList.push_back(index->first); + optionsList.push_back(static_cast(index->second)); + } + } + + // send the options + client->setOptions(optionsList); +} + +void +CServer::openPrimaryScreen() +{ + assert(m_primaryClient == NULL); + + // reset sequence number + m_seqNum = 0; + + // canonicalize the primary screen name + CString primaryName = m_config.getCanonicalName(getPrimaryScreenName()); + if (primaryName.empty()) { + throw XUnknownClient(getPrimaryScreenName()); + } + + // clear clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + CClipboardInfo& clipboard = m_clipboards[id]; + clipboard.m_clipboardOwner = primaryName; + 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(); + } + + try { + // create the primary client + m_primaryClient = new CPrimaryClient(m_screenFactory, + this, this, primaryName); + + // add connection + addConnection(m_primaryClient); + m_active = m_primaryClient; + + // open the screen + LOG((CLOG_DEBUG1 "opening primary screen")); + m_primaryClient->open(); + + // tell it about the active sides + m_primaryClient->reconfigure(getActivePrimarySides()); + + // tell primary client about its options + sendOptions(m_primaryClient); + } + catch (...) { + // if m_active is NULL then we haven't added the connection + // for the primary client so we don't try to remove it. + if (m_active != NULL) { + removeConnection(primaryName); + } + else { + delete m_primaryClient; + } + m_active = NULL; + m_primaryClient = NULL; + throw; + } +} + +void +CServer::closePrimaryScreen() +{ + assert(m_primaryClient != NULL); + + // close the primary screen + try { + LOG((CLOG_DEBUG1 "closing primary screen")); + m_primaryClient->close(); + } + catch (...) { + // ignore + } + + // remove connection + removeConnection(m_primaryClient->getName()); + m_primaryClient = NULL; +} + +void +CServer::addConnection(IClient* client) +{ + 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(client->getName())) { + throw XUnknownClient(client->getName()); + } + + // can only have one screen with a given name at any given time + if (m_clients.count(client->getName()) != 0) { + throw XDuplicateClient(client->getName()); + } + + // save screen info + m_clients.insert(std::make_pair(client->getName(), client)); + LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str())); + } + runStatusJobs(); +} + +void +CServer::removeConnection(const CString& name) +{ + LOG((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); + bool updateStatus; + { + CLock lock(&m_mutex); + + // 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 + IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; + if (active == index->second && active != m_primaryClient) { + // record new position (center of primary screen) + m_primaryClient->getCursorCenter(m_x, m_y); + + // stop waiting to switch if we were + if (active == m_switchScreen) { + clearSwitchState(); + } + + // don't notify active screen since it probably already + // disconnected. + 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_primaryClient; + + // enter new screen (unless we already have because of the + // screen saver) + if (m_activeSaver == NULL) { + m_primaryClient->enter(m_x, m_y, m_seqNum, + m_primaryClient->getToggleMask(), false); + } + } + + // if this screen had the cursor when the screen saver activated + // then we can't switch back to it when the screen saver + // deactivates. + if (m_activeSaver == index->second) { + m_activeSaver = NULL; + } + + // done with client + delete index->second; + m_clients.erase(index); + + // remove any thread for this client + m_clientThreads.erase(name); + + updateStatus = (m_clients.size() <= 1); + } + + if (updateStatus) { + runStatusJobs(); + } +} + + +// +// CServer::CClipboardInfo +// + +CString +CServer::XServerRethrow::getWhat() const throw() +{ + return format("XServerRethrow", "child thread failed"); +} + + +// +// CServer::CClipboardInfo +// + +CServer::CClipboardInfo::CClipboardInfo() : + m_clipboard(), + m_clipboardData(), + m_clipboardOwner(), + m_clipboardSeqNum(0) +{ + // do nothing +} diff --git a/lib/server/CServer.h b/lib/server/CServer.h new file mode 100644 index 0000000..89a52bd --- /dev/null +++ b/lib/server/CServer.h @@ -0,0 +1,414 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSERVER_H +#define CSERVER_H + +#include "IServer.h" +#include "IPrimaryScreenReceiver.h" +#include "CConfig.h" +#include "CClipboard.h" +#include "CCondVar.h" +#include "CMutex.h" +#include "CThread.h" +#include "CJobList.h" +#include "CStopwatch.h" +#include "stdlist.h" +#include "stdmap.h" +#include "stdvector.h" + +class CClientProxy; +class CHTTPServer; +class CPrimaryClient; +class IClient; +class IDataSocket; +class IPrimaryScreenFactory; +class IServerProtocol; +class ISocketFactory; +class IStreamFilterFactory; + +//! Synergy server +/*! +This class implements the top-level server algorithms for synergy. +*/ +class CServer : public IServer, public IPrimaryScreenReceiver { +public: + enum EStatus { + kNotRunning, + kRunning, + kServerNameUnknown, + kError, + kMaxStatus + }; + + /*! + The server will look itself up in the configuration using \c serverName + as its name. + */ + CServer(const CString& serverName); + ~CServer(); + + //! @name manipulators + //@{ + + //! Open server + /*! + Open the server. Throws XScreenUnavailable if the server's + screen cannot be opened but might be available after some time. + Otherwise throws some other exception if the server's screen or + the server cannot be opened and retrying won't help. + */ + void open(); + + //! Server main loop + /*! + Run server's event loop and return when exitMainLoop() is called. + This must be called between a successful open() and close(). + + (cancellation point) + */ + void mainLoop(); + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). This may only be + called between a successful open() and close(). + */ + void exitMainLoop(); + + //! Close server + /*! + Close the server. + */ + void close(); + + //! Set configuration + /*! + Change the server's configuration. Returns true iff the new + configuration was accepted (it must include the server's name). + This will disconnect any clients no longer in the configuration. + */ + bool setConfig(const CConfig&); + + //! Set primary screen factory + /*! + Sets the factory for creating primary screens. This must be + set before calling open(). This object takes ownership of the + factory. + */ + void setScreenFactory(IPrimaryScreenFactory*); + + //! Set socket factory + /*! + Sets the factory used to create a socket to connect to the server. + This must be set before calling mainLoop(). This object takes + ownership of the factory. + */ + void setSocketFactory(ISocketFactory*); + + //! Set stream filter factory + /*! + Sets the factory used to filter the socket streams used to + communicate with the server. This object takes ownership + of the factory. + */ + void setStreamFilterFactory(IStreamFilterFactory*); + + //! Add a job to notify of status changes + /*! + The added job is run whenever the server's status changes in + certain externally visible ways. The client keeps ownership + of the job. + */ + void addStatusJob(IJob*); + + //! Remove a job to notify of status changes + /*! + Removes a previously added status notification job. A job can + remove itself when called but must not remove any other jobs. + The client keeps ownership of the job. + */ + void removeStatusJob(IJob*); + + //@} + //! @name accessors + //@{ + + //! Get configuration + /*! + Returns the current configuration. + */ + void getConfig(CConfig*) const; + + //! Get name + /*! + Returns the server's name passed to the c'tor + */ + CString getPrimaryScreenName() const; + + //! Get number of connected clients + /*! + Returns the number of connected clients, including the server itself. + */ + UInt32 getNumClients() const; + + //! Get the list of connected clients + /*! + Set the \c list to the names of the currently connected clients. + */ + void getClients(std::vector& list) const; + + //! Get the status + /*! + Returns the current status and status message. + */ + EStatus getStatus(CString* = NULL) const; + + //@} + + // IServer overrides + virtual void onError(); + virtual void onInfoChanged(const CString&, const CClientInfo&); + virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); + virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); + + // IPrimaryScreenReceiver overrides + virtual void onScreensaver(bool activated); + virtual void onOneShotTimerExpired(UInt32 id); + virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton); + virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton); + virtual void onKeyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + 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); + +protected: + //! Handle special keys + /*! + Handles keys with special meaning. + */ + bool onCommandKey(KeyID, KeyModifierMask, bool down); + + //! Exit event loop and note an error condition + /*! + Force mainLoop() to return by throwing an exception. This call + can return before mainLoop() does (i.e. asynchronously). This + may only be called between a successful open() and close(). + */ + void exitMainLoopWithError(); + +private: + typedef std::list CThreadList; + + // notify status jobs of a change + void runStatusJobs() const; + + // set new status + void setStatus(EStatus, const char* msg = NULL); + + // get the sides of the primary screen that have neighbors + UInt32 getActivePrimarySides() const; + + // handle mouse motion + bool onMouseMovePrimaryNoLock(SInt32 x, SInt32 y); + void onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy); + + // 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(IClient*, + SInt32 x, SInt32 y, bool forScreenSaver); + + // lookup neighboring screen + IClient* getNeighbor(IClient*, 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. + IClient* getNeighbor(IClient*, EDirection, + SInt32& x, SInt32& y) const; + + // test if a switch is permitted. this includes testing user + // options like switch delay and tracking any state required to + // implement them. returns true iff a switch is permitted. + bool isSwitchOkay(IClient* dst, EDirection, + SInt32 x, SInt32 y); + + // update switch state due to a mouse move that doesn't try to + // switch screens. + void onNoSwitch(bool inTapZone); + + // reset switch wait state + void clearSwitchState(); + + // send screen options to \c client + void sendOptions(IClient* client) const; + + // open/close the primary screen + void openPrimaryScreen(); + void closePrimaryScreen(); + + // 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 + CThread startThread(IJob* adopted); + + // cancel running threads, waiting at most timeout seconds for + // them to finish. + void stopThreads(double timeout = -1.0); + + // reap threads, clearing finished threads from the thread list. + // doReapThreads does the work on the given thread list. + void reapThreads(); + void doReapThreads(CThreadList&); + + // thread method to accept incoming client connections + void acceptClients(void*); + + // thread method to do client interaction + void runClient(void*); + CClientProxy* handshakeClient(IDataSocket*); + + // thread method to accept incoming HTTP connections + void acceptHTTPClients(void*); + + // thread method to process HTTP requests + void processHTTPRequest(void*); + + // connection list maintenance + void addConnection(IClient*); + void removeConnection(const CString& name); + +private: + class XServerRethrow : public XBase { + protected: + // XBase overrides + virtual CString getWhat() const throw(); + }; + + class CClipboardInfo { + public: + CClipboardInfo(); + + public: + CClipboard m_clipboard; + CString m_clipboardData; + CString m_clipboardOwner; + UInt32 m_clipboardSeqNum; + }; + + CMutex m_mutex; + + // the name of the primary screen + CString m_name; + + // true if we should exit the main loop by throwing an exception. + // this is used to propagate an exception from one of our threads + // to the mainLoop() thread. but, since we can't make a copy of + // the original exception, we return an arbitrary, unique + // exception type. the caller of mainLoop() cannot catch this + // exception except through XBase or .... + bool m_error; + + // how long to wait to bind our socket until we give up + double m_bindTimeout; + + // factories + IPrimaryScreenFactory* m_screenFactory; + ISocketFactory* m_socketFactory; + IStreamFilterFactory* m_streamFilterFactory; + + // running threads + CThreadList m_threads; + CThread* m_acceptClientThread; + + // the screens + 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; + + // current mouse position (in absolute secondary screen coordinates) + SInt32 m_x, m_y; + + // current configuration + CConfig m_config; + + // clipboard cache + CClipboardInfo m_clipboards[kClipboardEnd]; + + // state saved when screen saver activates + IClient* m_activeSaver; + SInt32 m_xSaver, m_ySaver; + + // HTTP request processing stuff + CHTTPServer* m_httpServer; + CCondVar m_httpAvailable; + static const SInt32 s_httpMaxSimultaneousRequests; + + // common state for screen switch tests. all tests are always + // trying to reach the same screen in the same direction. + EDirection m_switchDir; + IClient* m_switchScreen; + + // state for delayed screen switching + double m_switchWaitDelay; + UInt32 m_switchWaitTimer; + bool m_switchWaitEngaged; + SInt32 m_switchWaitX, m_switchWaitY; + + // state for double-tap screen switching + double m_switchTwoTapDelay; + CStopwatch m_switchTwoTapTimer; + bool m_switchTwoTapEngaged; + bool m_switchTwoTapArmed; + SInt32 m_switchTwoTapZone; + + // the status change jobs and status + CJobList m_statusJobs; + EStatus m_status; + CString m_statusMessage; +}; + +#endif diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am new file mode 100644 index 0000000..a414bc9 --- /dev/null +++ b/lib/server/Makefile.am @@ -0,0 +1,53 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + server.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libserver.a +libserver_a_SOURCES = \ + CClientProxy.cpp \ + CClientProxy1_0.cpp \ + CClientProxy1_1.cpp \ + CConfig.cpp \ + CHTTPServer.cpp \ + CPrimaryClient.cpp \ + CServer.cpp \ + CClientProxy.h \ + CClientProxy1_0.h \ + CClientProxy1_1.h \ + CConfig.h \ + CHTTPServer.h \ + CPrimaryClient.h \ + CServer.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/http \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + $(NULL) diff --git a/lib/server/Makefile.in b/lib/server/Makefile.in new file mode 100644 index 0000000..e7c12ad --- /dev/null +++ b/lib/server/Makefile.in @@ -0,0 +1,365 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + server.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libserver.a +libserver_a_SOURCES = \ + CClientProxy.cpp \ + CClientProxy1_0.cpp \ + CClientProxy1_1.cpp \ + CConfig.cpp \ + CHTTPServer.cpp \ + CPrimaryClient.cpp \ + CServer.cpp \ + CClientProxy.h \ + CClientProxy1_0.h \ + CClientProxy1_1.h \ + CConfig.h \ + CHTTPServer.h \ + CPrimaryClient.h \ + CServer.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/http \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + $(NULL) + +subdir = lib/server +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libserver_a_AR = $(AR) cru +libserver_a_LIBADD = +am_libserver_a_OBJECTS = CClientProxy.$(OBJEXT) \ + CClientProxy1_0.$(OBJEXT) CClientProxy1_1.$(OBJEXT) \ + CConfig.$(OBJEXT) CHTTPServer.$(OBJEXT) \ + CPrimaryClient.$(OBJEXT) CServer.$(OBJEXT) +libserver_a_OBJECTS = $(am_libserver_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CClientProxy.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CClientProxy1_0.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CClientProxy1_1.Po $(DEPDIR)/CConfig.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CHTTPServer.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CPrimaryClient.Po $(DEPDIR)/CServer.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libserver_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libserver_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/server/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libserver.a: $(libserver_a_OBJECTS) $(libserver_a_DEPENDENCIES) + -rm -f libserver.a + $(libserver_a_AR) libserver.a $(libserver_a_OBJECTS) $(libserver_a_LIBADD) + $(RANLIB) libserver.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClientProxy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClientProxy1_0.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClientProxy1_1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CConfig.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CHTTPServer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CPrimaryClient.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CServer.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/server/server.dsp b/lib/server/server.dsp new file mode 100644 index 0000000..bc3dbb5 --- /dev/null +++ b/lib/server/server.dsp @@ -0,0 +1,154 @@ +# Microsoft Developer Studio Project File - Name="server" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=server - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "server.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "server.mak" CFG="server - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "server - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "server - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "server - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "server - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "server - Win32 Release" +# Name "server - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClientProxy.cpp +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.cpp +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_1.cpp +# End Source File +# Begin Source File + +SOURCE=.\CConfig.cpp +# End Source File +# Begin Source File + +SOURCE=.\CHTTPServer.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryClient.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServer.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClientProxy.h +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.h +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_1.h +# End Source File +# Begin Source File + +SOURCE=.\CConfig.h +# End Source File +# Begin Source File + +SOURCE=.\CHTTPServer.h +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryClient.h +# End Source File +# Begin Source File + +SOURCE=.\CServer.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/synergy/CClipboard.cpp b/lib/synergy/CClipboard.cpp new file mode 100644 index 0000000..d82448a --- /dev/null +++ b/lib/synergy/CClipboard.cpp @@ -0,0 +1,226 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClipboard.h" + +// +// CClipboard +// + +CClipboard::CClipboard() : + m_open(false), + m_owner(false) +{ + open(0); + empty(); + close(); +} + +CClipboard::~CClipboard() +{ + // do nothing +} + +bool +CClipboard::empty() +{ + assert(m_open); + + // clear all data + for (SInt32 index = 0; index < kNumFormats; ++index) { + m_data[index] = ""; + m_added[index] = false; + } + + // save time + m_timeOwned = m_time; + + // we're the owner now + m_owner = true; + + return true; +} + +void +CClipboard::add(EFormat format, const CString& data) +{ + assert(m_open); + assert(m_owner); + + m_data[format] = data; + m_added[format] = true; +} + +bool +CClipboard::open(Time time) const +{ + assert(!m_open); + + m_open = true; + m_time = time; + + return true; +} + +void +CClipboard::close() const +{ + assert(m_open); + + m_open = false; +} + +CClipboard::Time +CClipboard::getTime() const +{ + return m_timeOwned; +} + +bool +CClipboard::has(EFormat format) const +{ + assert(m_open); + return m_added[format]; +} + +CString +CClipboard::get(EFormat format) const +{ + assert(m_open); + return m_data[format]; +} + +bool +CClipboard::copy(IClipboard* dst, const IClipboard* src) +{ + assert(dst != NULL); + assert(src != NULL); + + return copy(dst, src, src->getTime()); +} + +bool +CClipboard::copy(IClipboard* dst, const IClipboard* src, Time time) +{ + assert(dst != NULL); + assert(src != NULL); + + bool success = false; + if (src->open(time)) { + if (dst->open(time)) { + if (dst->empty()) { + for (SInt32 format = 0; + format != IClipboard::kNumFormats; ++format) { + IClipboard::EFormat eFormat = (IClipboard::EFormat)format; + if (src->has(eFormat)) { + dst->add(eFormat, src->get(eFormat)); + } + } + success = true; + } + dst->close(); + } + src->close(); + } + + return success; +} + +void +CClipboard::unmarshall(const CString& data, Time time) +{ + const char* index = data.data(); + + // clear existing data + open(time); + empty(); + + // read the number of formats + const UInt32 numFormats = readUInt32(index); + index += 4; + + // read each format + for (UInt32 i = 0; i < numFormats; ++i) { + // get the format id + UInt32 format = readUInt32(index); + index += 4; + + // get the size of the format data + UInt32 size = readUInt32(index); + index += 4; + + // save the data if it's a known format. if either the client + // or server supports more clipboard formats than the other + // then one of them will get a format >= kNumFormats here. + if (format < static_cast(IClipboard::kNumFormats)) { + m_added[format] = true; + m_data[format] = CString(index, size); + } + index += size; + } + + // done + close(); +} + +CString +CClipboard::marshall() const +{ + CString data; + + // compute size of marshalled data + UInt32 size = 4; + UInt32 numFormats = 0; + UInt32 format; + for (format = 0; format != IClipboard::kNumFormats; ++format) { + if (m_added[format]) { + ++numFormats; + size += 4 + 4 + m_data[format].size(); + } + } + + // allocate space + data.reserve(size); + + // marshall the data + writeUInt32(&data, numFormats); + for (format = 0; format != IClipboard::kNumFormats; ++format) { + if (m_added[format]) { + writeUInt32(&data, format); + writeUInt32(&data, m_data[format].size()); + data += m_data[format]; + } + } + + return data; +} + +UInt32 +CClipboard::readUInt32(const char* buf) const +{ + const unsigned char* ubuf = reinterpret_cast(buf); + return (static_cast(ubuf[0]) << 24) | + (static_cast(ubuf[1]) << 16) | + (static_cast(ubuf[2]) << 8) | + static_cast(ubuf[3]); +} + +void +CClipboard::writeUInt32(CString* buf, UInt32 v) const +{ + *buf += static_cast((v >> 24) & 0xff); + *buf += static_cast((v >> 16) & 0xff); + *buf += static_cast((v >> 8) & 0xff); + *buf += static_cast( v & 0xff); +} diff --git a/lib/synergy/CClipboard.h b/lib/synergy/CClipboard.h new file mode 100644 index 0000000..55d1293 --- /dev/null +++ b/lib/synergy/CClipboard.h @@ -0,0 +1,93 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIPBOARD_H +#define CCLIPBOARD_H + +#include "IClipboard.h" + +//! Memory buffer clipboard +/*! +This class implements a clipboard that stores data in memory. +*/ +class CClipboard : public IClipboard { +public: + CClipboard(); + virtual ~CClipboard(); + + //! @name manipulators + //@{ + + //! Unmarshall clipboard data + /*! + Extract marshalled clipboard data and store it in this clipboard. + Sets the clipboard time to \c time. + */ + void unmarshall(const CString& data, Time time); + + //@} + //! @name accessors + //@{ + + //! Marshall clipboard data + /*! + Merge this clipboard's data into a single buffer that can be later + unmarshalled to restore the clipboard and return the buffer. + */ + CString marshall() const; + + //! Copy clipboard + /*! + Transfers all the data in one clipboard to another. The + clipboards can be of any concrete clipboard type (and + they don't have to be the same type). This also sets + the destination clipboard's timestamp to source clipboard's + timestamp. Returns true iff the copy succeeded. + */ + static bool copy(IClipboard* dst, const IClipboard* src); + + //! Copy clipboard + /*! + Transfers all the data in one clipboard to another. The + clipboards can be of any concrete clipboard type (and they + don't have to be the same type). This also sets the + timestamp to \c time. Returns true iff the copy succeeded. + */ + static bool copy(IClipboard* dst, const IClipboard* src, Time); + + //@} + + // IClipboard overrides + virtual bool empty(); + virtual void add(EFormat, const CString& data); + virtual bool open(Time) const; + virtual void close() const; + virtual Time getTime() const; + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; + +private: + UInt32 readUInt32(const char*) const; + void writeUInt32(CString*, UInt32) const; + +private: + mutable bool m_open; + mutable Time m_time; + bool m_owner; + Time m_timeOwned; + bool m_added[kNumFormats]; + CString m_data[kNumFormats]; +}; + +#endif diff --git a/lib/synergy/CInputPacketStream.cpp b/lib/synergy/CInputPacketStream.cpp new file mode 100644 index 0000000..8b0096f --- /dev/null +++ b/lib/synergy/CInputPacketStream.cpp @@ -0,0 +1,181 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CInputPacketStream.h" +#include "CLock.h" +#include "CStopwatch.h" + +// +// CInputPacketStream +// + +CInputPacketStream::CInputPacketStream(IInputStream* stream, bool adopt) : + CInputStreamFilter(stream, adopt), + m_mutex(), + m_size(0), + m_buffer(&m_mutex, NULL) +{ + // do nothing +} + +CInputPacketStream::~CInputPacketStream() +{ + // do nothing +} + +void +CInputPacketStream::close() +{ + getStream()->close(); +} + +UInt32 +CInputPacketStream::read(void* buffer, UInt32 n, double timeout) +{ + CLock lock(&m_mutex); + + // wait for entire message to be read. return if stream + // hungup or timeout. + switch (waitForFullMessage(timeout)) { + case kData: + break; + + case kHungup: + return 0; + + case kTimedout: + return (UInt32)-1; + } + + // limit number of bytes to read to the number of bytes left in the + // current message. + if (n > m_size) { + n = m_size; + } + + // now read from our buffer + n = m_buffer.readNoLock(buffer, n, -1.0); + assert(n <= m_size); + m_size -= n; + + return n; +} + +UInt32 +CInputPacketStream::getSize() const +{ + CLock lock(&m_mutex); + return getSizeNoLock(); +} + +UInt32 +CInputPacketStream::getSizeNoLock() const +{ + CStopwatch timer(true); + while (!hasFullMessage() && getStream()->getSize() > 0) { + // read more data + if (getMoreMessage(-1.0) != kData) { + // stream hungup + return 0; + } + } + + return m_size; +} + +CInputPacketStream::EResult +CInputPacketStream::waitForFullMessage(double timeout) const +{ + CStopwatch timer(true); + while (!hasFullMessage()) { + // compute remaining timeout + double t = timeout - timer.getTime(); + if (timeout >= 0.0 && t <= 0.0) { + // timeout + return kTimedout; + } + + // read more data + switch (getMoreMessage(t)) { + case kData: + break; + + case kHungup: + // stream hungup + return kHungup; + + case kTimedout: + // stream timed out + return kTimedout; + } + } + + return kData; +} + +CInputPacketStream::EResult +CInputPacketStream::getMoreMessage(double timeout) const +{ + // read more data + char buffer[4096]; + UInt32 n = getStream()->read(buffer, sizeof(buffer), timeout); + + // return if stream timed out + if (n == (UInt32)-1) { + return kTimedout; + } + + // return if stream hungup + if (n == 0) { + m_buffer.hangup(); + return kHungup; + } + + // append to our buffer + m_buffer.write(buffer, n); + + return kData; +} + +bool +CInputPacketStream::hasFullMessage() const +{ + // get payload length if we don't have it yet + if (m_size == 0) { + // check if length field has been read yet + if (m_buffer.getSizeNoLock() < 4) { + // not enough data for payload length + return false; + } + + // save payload length + UInt8 buffer[4]; + UInt32 n = m_buffer.readNoLock(buffer, sizeof(buffer), -1.0); + assert(n == 4); + m_size = ((UInt32)buffer[0] << 24) | + ((UInt32)buffer[1] << 16) | + ((UInt32)buffer[2] << 8) | + (UInt32)buffer[3]; + + // if payload length is zero then discard null message + if (m_size == 0) { + return false; + } + } + assert(m_size > 0); + + // we have the full message when we have at least m_size bytes in + // the buffer + return (m_buffer.getSizeNoLock() >= m_size); +} diff --git a/lib/synergy/CInputPacketStream.h b/lib/synergy/CInputPacketStream.h new file mode 100644 index 0000000..a4122f3 --- /dev/null +++ b/lib/synergy/CInputPacketStream.h @@ -0,0 +1,51 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CINPUTPACKETSTREAM_H +#define CINPUTPACKETSTREAM_H + +#include "CInputStreamFilter.h" +#include "CBufferedInputStream.h" +#include "CMutex.h" + +//! Packetizing input stream filter +/*! +Filters an input stream to extract packet by packet. +*/ +class CInputPacketStream : public CInputStreamFilter { +public: + CInputPacketStream(IInputStream*, bool adoptStream = true); + ~CInputPacketStream(); + + // IInputStream overrides + virtual void close(); + virtual UInt32 read(void*, UInt32 maxCount, double timeout); + virtual UInt32 getSize() const; + +private: + enum EResult { kData, kHungup, kTimedout }; + + UInt32 getSizeNoLock() const; + EResult waitForFullMessage(double timeout) const; + EResult getMoreMessage(double timeout) const; + bool hasFullMessage() const; + +private: + CMutex m_mutex; + mutable UInt32 m_size; + mutable CBufferedInputStream m_buffer; +}; + +#endif + diff --git a/lib/synergy/COutputPacketStream.cpp b/lib/synergy/COutputPacketStream.cpp new file mode 100644 index 0000000..7fbf1ad --- /dev/null +++ b/lib/synergy/COutputPacketStream.cpp @@ -0,0 +1,72 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COutputPacketStream.h" + +// +// COuputPacketStream +// + +COutputPacketStream::COutputPacketStream(IOutputStream* stream, bool adopt) : + COutputStreamFilter(stream, adopt) +{ + // do nothing +} + +COutputPacketStream::~COutputPacketStream() +{ + // do nothing +} + +void +COutputPacketStream::close() +{ + getStream()->close(); +} + +UInt32 +COutputPacketStream::write(const void* buffer, UInt32 count) +{ + // write the length of the payload + UInt8 length[4]; + length[0] = (UInt8)((count >> 24) & 0xff); + length[1] = (UInt8)((count >> 16) & 0xff); + length[2] = (UInt8)((count >> 8) & 0xff); + length[3] = (UInt8)( count & 0xff); + UInt32 count2 = sizeof(length); + const UInt8* cbuffer = length; + while (count2 > 0) { + UInt32 n = getStream()->write(cbuffer, count2); + cbuffer += n; + count2 -= n; + } + + // write the payload + count2 = count; + cbuffer = reinterpret_cast(buffer); + while (count2 > 0) { + UInt32 n = getStream()->write(cbuffer, count2); + cbuffer += n; + count2 -= n; + } + + return count; +} + +void +COutputPacketStream::flush() +{ + getStream()->flush(); +} + diff --git a/lib/synergy/COutputPacketStream.h b/lib/synergy/COutputPacketStream.h new file mode 100644 index 0000000..7e25d47 --- /dev/null +++ b/lib/synergy/COutputPacketStream.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COUTPUTPACKETSTREAM_H +#define COUTPUTPACKETSTREAM_H + +#include "COutputStreamFilter.h" + +//! Packetizing output stream filter +/*! +Filters an output stream to create packets that include message +boundaries. Each write() is considered a single packet. +*/ +class COutputPacketStream : public COutputStreamFilter { +public: + COutputPacketStream(IOutputStream*, bool adoptStream = true); + ~COutputPacketStream(); + + // IOutputStream overrides + virtual void close(); + virtual UInt32 write(const void*, UInt32 count); + virtual void flush(); +}; + +#endif diff --git a/lib/synergy/CPrimaryScreen.cpp b/lib/synergy/CPrimaryScreen.cpp new file mode 100644 index 0000000..320733f --- /dev/null +++ b/lib/synergy/CPrimaryScreen.cpp @@ -0,0 +1,281 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CPrimaryScreen.h" +#include "IScreen.h" +#include "IScreenReceiver.h" +#include "ProtocolTypes.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" + +// +// CPrimaryScreen +// + +CPrimaryScreen::CPrimaryScreen(IScreenReceiver* receiver) : + m_receiver(receiver), + m_active(false) +{ + // do nothing +} + +CPrimaryScreen::~CPrimaryScreen() +{ + // do nothing +} + +void +CPrimaryScreen::mainLoop() +{ + // change our priority + CThread::getCurrentThread().setPriority(-14); + + // run event loop + try { + LOG((CLOG_DEBUG "entering event loop")); + onPreMainLoop(); + getScreen()->mainLoop(); + onPostMainLoop(); + LOG((CLOG_DEBUG "exiting event loop")); + } + catch (...) { + onPostMainLoop(); + LOG((CLOG_DEBUG "exiting event loop")); + throw; + } +} + +void +CPrimaryScreen::exitMainLoop() +{ + getScreen()->exitMainLoop(); +} + +void +CPrimaryScreen::open() +{ + CClientInfo info; + try { + // subclass hook + onPreOpen(); + + // open the screen + getScreen()->open(); + + // create and prepare our window + createWindow(); + + // collect screen info + getScreen()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getScreen()->getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = getJumpZoneSize(); + + // update keyboard state + updateKeys(); + + // get notified of screen saver activation/deactivation + getScreen()->openScreensaver(true); + + // subclass hook + onPostOpen(); + + // reset options + resetOptions(); + } + catch (...) { + close(); + throw; + } + + // enter the screen + { + CLock lock(&m_mutex); + enterNoWarp(); + } + + // send screen info + m_receiver->onInfoChanged(info); +} + +void +CPrimaryScreen::close() +{ + onPreClose(); + getScreen()->closeScreensaver(); + destroyWindow(); + getScreen()->close(); + onPostClose(); +} + +void +CPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreensaver) +{ + LOG((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreensaver ? " for screen saver" : "")); + CLock lock(&m_mutex); + assert(m_active == true); + + if (!forScreensaver) { + warpCursor(x, y); + } + else { + onEnterScreensaver(); + } + enterNoWarp(); +} + +void +CPrimaryScreen::enterNoWarp() +{ + // note -- must be locked on entry + + // not active anymore + m_active = false; + + // subclass hook + onPreEnter(); + + // restore active window and hide our window + hideWindow(); + + // subclass hook + onPostEnter(); +} + +bool +CPrimaryScreen::leave() +{ + LOG((CLOG_INFO "leaving primary")); + CLock lock(&m_mutex); + assert(m_active == false); + + // subclass hook + onPreLeave(); + + // show our window + if (!showWindow()) { + onPostLeave(false); + return false; + } + + // get keyboard state as we leave + updateKeys(); + + // warp mouse to center + warpCursorToCenter(); + + // subclass hook + onPostLeave(true); + + // local client now active + m_active = true; + + // make sure our idea of clipboard ownership is correct + getScreen()->checkClipboards(); + + return true; +} + +void +CPrimaryScreen::setClipboard(ClipboardID id, + const IClipboard* clipboard) +{ + getScreen()->setClipboard(id, clipboard); +} + +void +CPrimaryScreen::grabClipboard(ClipboardID id) +{ + getScreen()->setClipboard(id, NULL); +} + +bool +CPrimaryScreen::isActive() const +{ + CLock lock(&m_mutex); + return m_active; +} + +void +CPrimaryScreen::getClipboard(ClipboardID id, + IClipboard* clipboard) const +{ + getScreen()->getClipboard(id, clipboard); +} + +void +CPrimaryScreen::onPreMainLoop() +{ + // do nothing +} + +void +CPrimaryScreen::onPostMainLoop() +{ + // do nothing +} + +void +CPrimaryScreen::onPreOpen() +{ + // do nothing +} + +void +CPrimaryScreen::onPostOpen() +{ + // do nothing +} + +void +CPrimaryScreen::onPreClose() +{ + // do nothing +} + +void +CPrimaryScreen::onPostClose() +{ + // do nothing +} + +void +CPrimaryScreen::onPreEnter() +{ + // do nothing +} + +void +CPrimaryScreen::onPostEnter() +{ + // do nothing +} + +void +CPrimaryScreen::onEnterScreensaver() +{ + // do nothing +} + +void +CPrimaryScreen::onPreLeave() +{ + // do nothing +} + +void +CPrimaryScreen::onPostLeave(bool) +{ + // do nothing +} diff --git a/lib/synergy/CPrimaryScreen.h b/lib/synergy/CPrimaryScreen.h new file mode 100644 index 0000000..c370d19 --- /dev/null +++ b/lib/synergy/CPrimaryScreen.h @@ -0,0 +1,349 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CPRIMARYSCREEN_H +#define CPRIMARYSCREEN_H + +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "OptionTypes.h" +#include "CMutex.h" + +class IClipboard; +class IScreen; +class IScreenReceiver; + +//! Generic server-side screen +/*! +This is a platform independent base class for primary screen +implementations. A primary screen is a server-side screen. +Each platform will derive a class from CPrimaryScreen to handle +platform dependent operations. +*/ +class CPrimaryScreen { +public: + CPrimaryScreen(IScreenReceiver*); + virtual ~CPrimaryScreen(); + + //! @name manipulators + //@{ + + //! Open screen + /*! + Opens the screen. This includes initializing the screen, opening + the screen saver, synchronizing keyboard state, and causing events + to be reported to an IPrimaryScreenReceiver (set through another + interface). Calls close() before returning (rethrowing) if it + fails for any reason. + */ + void open(); + + //! Run event loop + /*! + Run the screen's event loop. This returns when it detects + the application should terminate or when exitMainLoop() is called. + mainLoop() may only be called between open() and close(). + */ + void mainLoop(); + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). + */ + void exitMainLoop(); + + //! Close screen + /*! + Closes the screen. This close the screen saver and the screen. + */ + void close(); + + //! Enter screen + /*! + Called when the user navigates to the primary screen. Warps + the cursor to the absolute coordinates \c x,y and unhides + it. If \c forScreensaver is true then we're entering because + the screen saver started and the cursor is not warped. + */ + void enter(SInt32 x, SInt32 y, bool forScreensaver); + + //! Leave screen + /*! + Called when the user navigates off the primary screen. Returns + true iff successful. + */ + bool leave(); + + //! Update configuration + /*! + This is called when the configuration has changed. \c activeSides + is a bitmask of EDirectionMask indicating which sides of the + primary screen are linked to clients. Override to handle the + possible change in jump zones. + */ + virtual void reconfigure(UInt32 activeSides) = 0; + + //! Warp cursor + /*! + Warp the cursor to the absolute coordinates \c x,y. Also + discard input events up to and including the warp before + returning. + */ + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + + //! Set clipboard + /*! + Sets the system's clipboard contents. This is usually called + soon after an enter(). + */ + void setClipboard(ClipboardID, const IClipboard*); + + //! Grab clipboard + /*! + Grabs (i.e. take ownership of) the system clipboard. + */ + void grabClipboard(ClipboardID); + + //! Notify of options changes + /*! + Reset all options to their default values. + */ + virtual void resetOptions() = 0; + + //! Notify of options changes + /*! + Set options to given values. Ignore unknown options and don't + modify our options that aren't given in \c options. + */ + virtual void setOptions(const COptionsList& options) = 0; + + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer. + */ + virtual UInt32 addOneShotTimer(double timeout) = 0; + + //@} + //! @name accessors + //@{ + + //! Test if active + /*! + Returns true iff the screen is active (i.e. the user has left + the screen). Note this is the reverse of a secdonary screen. + */ + bool isActive() const; + + //! Get clipboard + /*! + Saves the contents of the system clipboard indicated by \c id. + */ + void getClipboard(ClipboardID, IClipboard*) const; + + //! Get jump zone size + /*! + Return the jump zone size, the size of the regions on the edges of + the screen that cause the cursor to jump to another screen. + */ + virtual SInt32 getJumpZoneSize() const = 0; + + //! Get toggle key state + /*! + Return the primary screen's current toggle modifier key state. + The returned mask should have the corresponding bit set for + each toggle key that is active. For example, if caps lock is + on then the returned mask should have \c KeyModifierCapsLock set. + */ + virtual KeyModifierMask getToggleMask() const = 0; + + //! Get screen lock state + /*! + Return true if any key or button is being pressed or if there's + any other reason that the user should not be allowed to switch + screens. Active toggle keys (including the scroll lock key) + should not be counted as reasons to lock to the screen. + If this method returns true it should log a message on why at + the CLOG_DEBUG level. + */ + virtual bool isLockedToScreen() const = 0; + + //! Get screen + /*! + Return the platform dependent screen. + */ + virtual IScreen* getScreen() const = 0; + + //@} + +protected: + //! Pre-mainLoop() hook + /*! + Called on entry to mainLoop(). Override to perform platform specific + operations. Default does nothing. May throw. + */ + virtual void onPreMainLoop(); + + //! Post-mainLoop() hook + /*! + Called on exit from mainLoop(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostMainLoop(); + + //! Pre-open() hook + /*! + Called on entry to open(). Override to perform platform specific + operations. Default does nothing. May throw. + */ + virtual void onPreOpen(); + + //! Post-open() hook + /*! + Called on exit from open() iff the open was successful. Default + does nothing. May throw. + */ + virtual void onPostOpen(); + + //! Pre-close() hook + /*! + Called on entry to close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPreClose(); + + //! Post-close() hook + /*! + Called on exit from close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostClose(); + + //! Pre-enter() hook + /*! + Called from enter() after the cursor has been warped or, if + \c forScreensaver is true, onEnterScreensaver() was called. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ + virtual void onPreEnter(); + + //! Post-enter() hook + /*! + Called on exit from enter(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostEnter(); + + //! Pre-enter() for screen saver hook + /*! + Called on entry to enter() if the \c forScreensaver passed to it was + true. Override to perform platform specific operations. Default + does nothing. May \b not throw. + */ + virtual void onEnterScreensaver(); + + //! Pre-leave() hook + /*! + Called on entry to leave() after desktop synchronization. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ + virtual void onPreLeave(); + + //! Post-leave() hook + /*! + Called on exit from leave(). \c success is the value returned by + showWindow(). Override to perform platform specific operations. + Default does nothing. May \b not throw. + */ + virtual void onPostLeave(bool success); + + //! Create window + /*! + Called to create the window. This window is generally used to + receive events, hide the cursor, and to capture keyboard and mouse + input. + */ + virtual void createWindow() = 0; + + //! Destroy window + /*! + Called to destroy the window created by createWindow(). + */ + virtual void destroyWindow() = 0; + + //! Show window + /*! + Called when the user navigates off the primary screen. Hide the + cursor and grab exclusive access to the input devices. Every call + to showWindow() has a matching call to hideWindow() which preceeds + it. Return true iff successful (in particular, iff the input + devices were grabbed). + + After a successful showWindow(), user input events and + screensaver activation/deactivation should be reported to an + IPrimaryScreenReceiver (set through another interface) until + hideWindow() is called. Report mouse motion to its + onMouseMoveSecondary(). User input should not be delivered to + any application except this one. + */ + virtual bool showWindow() = 0; + + //! Hide window + /*! + Called when the user navigates back to the primary screen. Show + the cursor and ungrab the input devices. + + After hideWindow(), user input events should be delivered normally + to other applications. Mouse motion over (at least) the jump zones + must be reported to an IPrimaryScreenReceiver's onMouseMovePrimary(). + */ + virtual void hideWindow() = 0; + + //! Warp cursor for relative motion + /*! + Prepare the cursor to report relative motion. When the user has + navigated to another screen, synergy requires the cursor motion + deltas, not the absolute coordinates. Typically this is done by + warping the cursor to the center of the primary screen and then + every time it moves compute the motion and warp back to the + center (but without reporting that warp as motion). This is + only called after a successful showWindow(). + */ + virtual void warpCursorToCenter() = 0; + + //! Synchronize key state + /*! + Check the current keyboard state. Normally a screen will save + the keyboard state in this method and use this shadow state + when handling user input and in methods like isLockedToScreen(). + */ + virtual void updateKeys() = 0; + +private: + void enterNoWarp(); + +private: + CMutex m_mutex; + + // object to notify of changes + IScreenReceiver* m_receiver; + + // m_active is true if this screen has been left + bool m_active; +}; + +#endif diff --git a/lib/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp new file mode 100644 index 0000000..7ef9de6 --- /dev/null +++ b/lib/synergy/CProtocolUtil.cpp @@ -0,0 +1,519 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CProtocolUtil.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CLog.h" +#include "stdvector.h" +#include +#include + +// +// CProtocolUtil +// + +void +CProtocolUtil::writef(IOutputStream* stream, const char* fmt, ...) +{ + assert(stream != NULL); + assert(fmt != NULL); + LOG((CLOG_DEBUG2 "writef(%s)", fmt)); + + va_list args; + + // determine total size to write + va_start(args, fmt); + UInt32 count = getLength(fmt, args); + va_end(args); + + // done if nothing to write + if (count == 0) { + return; + } + + // fill buffer + UInt8* buffer = new UInt8[count]; + va_start(args, fmt); + writef(buffer, fmt, args); + va_end(args); + + // write buffer + UInt8* scan = buffer; + while (count > 0) { + const UInt32 n = stream->write(scan, count); + LOG((CLOG_DEBUG2 "wrote %d of %d bytes", n, count)); + count -= n; + scan += n; + } + + delete[] buffer; +} + +void +CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) +{ + assert(stream != NULL); + assert(fmt != NULL); + LOG((CLOG_DEBUG2 "readf(%s)", fmt)); + + va_list args; + va_start(args, fmt); + + // begin scanning + while (*fmt) { + if (*fmt == '%') { + // format specifier. determine argument size. + ++fmt; + UInt32 len = eatLength(&fmt); + switch (*fmt) { + case 'i': { + // check for valid length + assert(len == 1 || len == 2 || len == 4); + + // read the data + UInt8 buffer[4]; + read(stream, buffer, len); + + // convert it + void* v = va_arg(args, void*); + switch (len) { + case 1: + // 1 byte integer + *reinterpret_cast(v) = buffer[0]; + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + break; + + case 2: + // 2 byte integer + *reinterpret_cast(v) = + static_cast( + (static_cast(buffer[0]) << 8) | + static_cast(buffer[1])); + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + break; + + case 4: + // 4 byte integer + *reinterpret_cast(v) = + (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3]); + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + break; + } + break; + } + + case 'I': { + // check for valid length + assert(len == 1 || len == 2 || len == 4); + + // read the vector length + UInt8 buffer[4]; + read(stream, buffer, 4); + UInt32 n = (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3]); + + // convert it + void* v = va_arg(args, void*); + switch (len) { + case 1: + // 1 byte integer + for (UInt32 i = 0; i < n; ++i) { + read(stream, buffer, 1); + reinterpret_cast*>(v)->push_back( + buffer[0]); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + } + break; + + case 2: + // 2 byte integer + for (UInt32 i = 0; i < n; ++i) { + read(stream, buffer, 2); + reinterpret_cast*>(v)->push_back( + static_cast( + (static_cast(buffer[0]) << 8) | + static_cast(buffer[1]))); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + } + break; + + case 4: + // 4 byte integer + for (UInt32 i = 0; i < n; ++i) { + read(stream, buffer, 4); + reinterpret_cast*>(v)->push_back( + (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3])); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + } + break; + } + break; + } + + case 's': { + assert(len == 0); + + // read the string length + UInt8 buffer[128]; + read(stream, buffer, 4); + UInt32 len = (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3]); + + // use a fixed size buffer if its big enough + const bool useFixed = (len <= sizeof(buffer)); + + // allocate a buffer to read the data + UInt8* sBuffer; + if (useFixed) { + sBuffer = buffer; + } + else { + sBuffer = new UInt8[len]; + } + + // read the data + try { + read(stream, sBuffer, len); + } + catch (...) { + if (!useFixed) { + delete[] sBuffer; + } + throw; + } + LOG((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer)); + + // save the data + CString* dst = va_arg(args, CString*); + dst->assign((const char*)sBuffer, len); + + // release the buffer + if (!useFixed) { + delete[] sBuffer; + } + break; + } + + case '%': + assert(len == 0); + break; + + default: + assert(0 && "invalid format specifier"); + } + + // next format character + ++fmt; + } + else { + // read next character + char buffer[1]; + read(stream, buffer, 1); + + // verify match + if (buffer[0] != *fmt) { + LOG((CLOG_DEBUG2 "readf: format mismatch: %c vs %c", *fmt, buffer[0])); + throw XIOReadMismatch(); + } + + // next format character + ++fmt; + } + } + + va_end(args); +} + +UInt32 +CProtocolUtil::getLength(const char* fmt, va_list args) +{ + UInt32 n = 0; + while (*fmt) { + if (*fmt == '%') { + // format specifier. determine argument size. + ++fmt; + UInt32 len = eatLength(&fmt); + switch (*fmt) { + case 'i': + assert(len == 1 || len == 2 || len == 4); + (void)va_arg(args, UInt32); + break; + + case 'I': + assert(len == 1 || len == 2 || len == 4); + switch (len) { + case 1: + len = (va_arg(args, std::vector*))->size() + 4; + break; + + case 2: + len = 2 * (va_arg(args, std::vector*))->size() + 4; + break; + + case 4: + len = 4 * (va_arg(args, std::vector*))->size() + 4; + break; + } + break; + + case 's': + assert(len == 0); + len = (va_arg(args, CString*))->size() + 4; + (void)va_arg(args, UInt8*); + break; + + case 'S': + assert(len == 0); + len = va_arg(args, UInt32) + 4; + (void)va_arg(args, UInt8*); + break; + + case '%': + assert(len == 0); + len = 1; + break; + + default: + assert(0 && "invalid format specifier"); + } + + // accumulate size + n += len; + ++fmt; + } + else { + // regular character + ++n; + ++fmt; + } + } + return n; +} + +void +CProtocolUtil::writef(void* buffer, const char* fmt, va_list args) +{ + UInt8* dst = reinterpret_cast(buffer); + + while (*fmt) { + if (*fmt == '%') { + // format specifier. determine argument size. + ++fmt; + UInt32 len = eatLength(&fmt); + switch (*fmt) { + case 'i': { + const UInt32 v = va_arg(args, UInt32); + switch (len) { + case 1: + // 1 byte integer + *dst++ = static_cast(v & 0xff); + break; + + case 2: + // 2 byte integer + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + break; + + case 4: + // 4 byte integer + *dst++ = static_cast((v >> 24) & 0xff); + *dst++ = static_cast((v >> 16) & 0xff); + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + break; + + default: + assert(0 && "invalid integer format length"); + return; + } + break; + } + + case 'I': { + switch (len) { + case 1: { + // 1 byte integers + const std::vector* list = + va_arg(args, const std::vector*); + const UInt32 n = list->size(); + *dst++ = static_cast((n >> 24) & 0xff); + *dst++ = static_cast((n >> 16) & 0xff); + *dst++ = static_cast((n >> 8) & 0xff); + *dst++ = static_cast( n & 0xff); + for (UInt32 i = 0; i < n; ++i) { + *dst++ = (*list)[i]; + } + break; + } + + case 2: { + // 2 byte integers + const std::vector* list = + va_arg(args, const std::vector*); + const UInt32 n = list->size(); + *dst++ = static_cast((n >> 24) & 0xff); + *dst++ = static_cast((n >> 16) & 0xff); + *dst++ = static_cast((n >> 8) & 0xff); + *dst++ = static_cast( n & 0xff); + for (UInt32 i = 0; i < n; ++i) { + const UInt16 v = (*list)[i]; + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + } + break; + } + + case 4: { + // 4 byte integers + const std::vector* list = + va_arg(args, const std::vector*); + const UInt32 n = list->size(); + *dst++ = static_cast((n >> 24) & 0xff); + *dst++ = static_cast((n >> 16) & 0xff); + *dst++ = static_cast((n >> 8) & 0xff); + *dst++ = static_cast( n & 0xff); + for (UInt32 i = 0; i < n; ++i) { + const UInt32 v = (*list)[i]; + *dst++ = static_cast((v >> 24) & 0xff); + *dst++ = static_cast((v >> 16) & 0xff); + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + } + break; + } + + default: + assert(0 && "invalid integer vector format length"); + return; + } + break; + } + + case 's': { + assert(len == 0); + const CString* src = va_arg(args, CString*); + const UInt32 len = (src != NULL) ? src->size() : 0; + *dst++ = static_cast((len >> 24) & 0xff); + *dst++ = static_cast((len >> 16) & 0xff); + *dst++ = static_cast((len >> 8) & 0xff); + *dst++ = static_cast( len & 0xff); + if (len != 0) { + memcpy(dst, src->data(), len); + dst += len; + } + break; + } + + case 'S': { + assert(len == 0); + const UInt32 len = va_arg(args, UInt32); + const UInt8* src = va_arg(args, UInt8*); + *dst++ = static_cast((len >> 24) & 0xff); + *dst++ = static_cast((len >> 16) & 0xff); + *dst++ = static_cast((len >> 8) & 0xff); + *dst++ = static_cast( len & 0xff); + memcpy(dst, src, len); + dst += len; + break; + } + + case '%': + assert(len == 0); + *dst++ = '%'; + break; + + default: + assert(0 && "invalid format specifier"); + } + + // next format character + ++fmt; + } + else { + // copy regular character + *dst++ = *fmt++; + } + } +} + +UInt32 +CProtocolUtil::eatLength(const char** pfmt) +{ + const char* fmt = *pfmt; + UInt32 n = 0; + for (;;) { + UInt32 d; + switch (*fmt) { + case '0': d = 0; break; + case '1': d = 1; break; + case '2': d = 2; break; + case '3': d = 3; break; + case '4': d = 4; break; + case '5': d = 5; break; + case '6': d = 6; break; + case '7': d = 7; break; + case '8': d = 8; break; + case '9': d = 9; break; + default: *pfmt = fmt; return n; + } + n = 10 * n + d; + ++fmt; + } +} + +void +CProtocolUtil::read(IInputStream* stream, void* vbuffer, UInt32 count) +{ + assert(stream != NULL); + assert(vbuffer != NULL); + + UInt8* buffer = reinterpret_cast(vbuffer); + while (count > 0) { + // read more + UInt32 n = stream->read(buffer, count, -1.0); + + // bail if stream has hungup + if (n == 0) { + LOG((CLOG_DEBUG2 "unexpected disconnect in readf(), %d bytes left", count)); + throw XIOEndOfStream(); + } + + // prepare for next read + buffer += n; + count -= n; + } +} + + +// +// XIOReadMismatch +// + +CString +XIOReadMismatch::getWhat() const throw() +{ + return format("XIOReadMismatch", "CProtocolUtil::readf() mismatch"); +} diff --git a/lib/synergy/CProtocolUtil.h b/lib/synergy/CProtocolUtil.h new file mode 100644 index 0000000..1e0db39 --- /dev/null +++ b/lib/synergy/CProtocolUtil.h @@ -0,0 +1,90 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CPROTOCOLUTIL_H +#define CPROTOCOLUTIL_H + +#include "BasicTypes.h" +#include "XIO.h" +#include + +class IInputStream; +class IOutputStream; + +//! Synergy protocol utilities +/*! +This class provides various functions for implementing the synergy +protocol. +*/ +class CProtocolUtil { +public: + //! Write formatted data + /*! + Write formatted binary data to a stream. \c fmt consists of + regular characters and format specifiers. Format specifiers + begin with \%. All characters not part of a format specifier + are regular and are transmitted unchanged. + + Format specifiers are: + - \%\% -- literal `\%' + - \%1i -- converts integer argument to 1 byte integer + - \%2i -- converts integer argument to 2 byte integer in NBO + - \%4i -- converts integer argument to 4 byte integer in NBO + - \%1I -- converts std::vector* to 1 byte integers + - \%2I -- converts std::vector* to 2 byte integers in NBO + - \%4I -- converts std::vector* to 4 byte integers in NBO + - \%s -- converts CString* to stream of bytes + - \%S -- converts integer N and const UInt8* to stream of N bytes + */ + static void writef(IOutputStream*, + const char* fmt, ...); + + //! Read formatted data + /*! + Read formatted binary data from a buffer. This performs the + reverse operation of writef(). + + Format specifiers are: + - \%\% -- read (and discard) a literal `\%' + - \%1i -- reads a 1 byte integer; argument is a SInt32* or UInt32* + - \%2i -- reads an NBO 2 byte integer; arg is SInt32* or UInt32* + - \%4i -- reads an NBO 4 byte integer; arg is SInt32* or UInt32* + - \%1I -- reads 1 byte integers; arg is std::vector* + - \%2I -- reads NBO 2 byte integers; arg is std::vector* + - \%4I -- reads NBO 4 byte integers; arg is std::vector* + - \%s -- reads bytes; argument must be a CString*, \b not a char* + */ + static void readf(IInputStream*, + const char* fmt, ...); + +private: + static UInt32 getLength(const char* fmt, va_list); + static void writef(void*, const char* fmt, va_list); + static UInt32 eatLength(const char** fmt); + static void read(IInputStream*, void*, UInt32); +}; + +//! Mismatched read exception +/*! +Thrown by CProtocolUtil::readf() when the data being read does not +match the format. +*/ +class XIOReadMismatch : public XIO { +public: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +#endif + diff --git a/lib/synergy/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp new file mode 100644 index 0000000..3bf40d4 --- /dev/null +++ b/lib/synergy/CSecondaryScreen.cpp @@ -0,0 +1,381 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CSecondaryScreen.h" +#include "IScreen.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" + +// +// CSecondaryScreen +// + +CSecondaryScreen::CSecondaryScreen() : + m_remoteReady(false), + m_active(false), + m_toggleKeys(0), + m_screenSaverSync(true) +{ + // do nothing +} + +CSecondaryScreen::~CSecondaryScreen() +{ + // do nothing +} + +void +CSecondaryScreen::mainLoop() +{ + // change our priority + CThread::getCurrentThread().setPriority(-14); + + // run event loop + try { + LOG((CLOG_DEBUG "entering event loop")); + onPreMainLoop(); + getScreen()->mainLoop(); + onPostMainLoop(); + LOG((CLOG_DEBUG "exiting event loop")); + } + catch (...) { + onPostMainLoop(); + LOG((CLOG_DEBUG "exiting event loop")); + throw; + } +} + +void +CSecondaryScreen::exitMainLoop() +{ + getScreen()->exitMainLoop(); +} + +void +CSecondaryScreen::open() +{ + try { + // subclass hook + onPreOpen(); + + // open the screen + getScreen()->open(); + + // create and prepare our window. pretend we're active so + // we don't try to show our window until later. + { + CLock lock(&m_mutex); + assert(m_active == false); + m_active = true; + } + createWindow(); + { + CLock lock(&m_mutex); + m_active = false; + } + + // subclass hook + onPostOpen(); + + // reset options + resetOptions(); + } + catch (...) { + close(); + throw; + } +} + +void +CSecondaryScreen::close() +{ + onPreClose(); + destroyWindow(); + getScreen()->close(); + onPostClose(); +} + +void +CSecondaryScreen::remoteControl() +{ + // assume primary has all clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + grabClipboard(id); + } + + // update keyboard state + updateKeys(); + + // now remote ready. fake being active for call to leave(). + bool screenSaverSync; + { + CLock lock(&m_mutex); + m_remoteReady = true; + m_active = true; + + // copy screen saver synchronization state + screenSaverSync = m_screenSaverSync; + } + + // disable the screen saver if synchronization is enabled + if (screenSaverSync) { + getScreen()->openScreensaver(false); + } + + // hide the cursor + leave(); +} + +void +CSecondaryScreen::localControl() +{ + getScreen()->closeScreensaver(); + + // not remote ready anymore + CLock lock(&m_mutex); + m_remoteReady = false; +} + +void +CSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) +{ + CLock lock(&m_mutex); + assert(m_active == false); + + LOG((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); + + getScreen()->syncDesktop(); + + // now active + m_active = true; + + // subclass hook + onPreEnter(); + + // update our keyboard state to reflect the local state + updateKeys(); + + // remember toggle key state + m_toggleKeys = getToggleState(); + + // toggle modifiers that don't match the desired state + setToggleState(mask); + + // warp to requested location + warpCursor(x, y); + + // show mouse + hideWindow(); + + // subclass hook + onPostEnter(); +} + +void +CSecondaryScreen::leave() +{ + LOG((CLOG_INFO "leaving screen")); + CLock lock(&m_mutex); + assert(m_active == true); + + getScreen()->syncDesktop(); + + // subclass hook + onPreLeave(); + + // restore toggle key state + setToggleState(m_toggleKeys); + + // warp and hide mouse + SInt32 x, y; + getScreen()->getCursorCenter(x, y); + showWindow(x, y); + + // subclass hook + onPostLeave(); + + // not active anymore + m_active = false; + + // make sure our idea of clipboard ownership is correct + getScreen()->checkClipboards(); +} + +void +CSecondaryScreen::setClipboard(ClipboardID id, + const IClipboard* clipboard) +{ + getScreen()->setClipboard(id, clipboard); +} + +void +CSecondaryScreen::grabClipboard(ClipboardID id) +{ + getScreen()->setClipboard(id, NULL); +} + +void +CSecondaryScreen::screensaver(bool activate) +{ + // get screen saver synchronization flag + bool screenSaverSync; + { + CLock lock(&m_mutex); + screenSaverSync = m_screenSaverSync; + } + + // activate/deactivation screen saver iff synchronization enabled + if (screenSaverSync) { + getScreen()->screensaver(activate); + } +} + +void +CSecondaryScreen::resetOptions() +{ + // set screen saver synchronization flag and see if we need to + // update the screen saver synchronization. + bool screenSaverSyncOn; + { + CLock lock(&m_mutex); + screenSaverSyncOn = (!m_screenSaverSync && m_remoteReady); + m_screenSaverSync = true; + } + + // update screen saver synchronization + if (screenSaverSyncOn) { + getScreen()->openScreensaver(false); + } +} + +void +CSecondaryScreen::setOptions(const COptionsList& options) +{ + // update options + bool updateScreenSaverSync = false; + bool oldScreenSaverSync; + { + CLock lock(&m_mutex); + oldScreenSaverSync = m_screenSaverSync; + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionScreenSaverSync) { + updateScreenSaverSync = true; + m_screenSaverSync = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "screen saver synchronization %s", m_screenSaverSync ? "on" : "off")); + } + } + if (!m_remoteReady || oldScreenSaverSync == m_screenSaverSync) { + updateScreenSaverSync = false; + } + } + + // update screen saver synchronization + if (updateScreenSaverSync) { + if (oldScreenSaverSync) { + getScreen()->closeScreensaver(); + } + else { + getScreen()->openScreensaver(false); + } + } +} + +bool +CSecondaryScreen::isActive() const +{ + CLock lock(&m_mutex); + return m_active; +} + +void +CSecondaryScreen::getClipboard(ClipboardID id, + IClipboard* clipboard) const +{ + getScreen()->getClipboard(id, clipboard); +} + +void +CSecondaryScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + getScreen()->syncDesktop(); + getScreen()->getShape(x, y, w, h); +} + +void +CSecondaryScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + getScreen()->syncDesktop(); + getScreen()->getCursorPos(x, y); +} + +void +CSecondaryScreen::onPreMainLoop() +{ + // do nothing +} + +void +CSecondaryScreen::onPostMainLoop() +{ + // do nothing +} + +void +CSecondaryScreen::onPreOpen() +{ + // do nothing +} + +void +CSecondaryScreen::onPostOpen() +{ + // do nothing +} + +void +CSecondaryScreen::onPreClose() +{ + // do nothing +} + +void +CSecondaryScreen::onPostClose() +{ + // do nothing +} + +void +CSecondaryScreen::onPreEnter() +{ + // do nothing +} + +void +CSecondaryScreen::onPostEnter() +{ + // do nothing +} + +void +CSecondaryScreen::onPreLeave() +{ + // do nothing +} + +void +CSecondaryScreen::onPostLeave() +{ + // do nothing +} diff --git a/lib/synergy/CSecondaryScreen.h b/lib/synergy/CSecondaryScreen.h new file mode 100644 index 0000000..f1790a8 --- /dev/null +++ b/lib/synergy/CSecondaryScreen.h @@ -0,0 +1,390 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSECONDARYSCREEN_H +#define CSECONDARYSCREEN_H + +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "OptionTypes.h" +#include "CMutex.h" + +class IClipboard; +class IScreen; + +//! Generic client-side screen +/*! +This is a platform independent base class for secondary screen +implementations. A secondary screen is a client-side screen. +Each platform will derive a class from CSecondaryScreen to handle +platform dependent operations. +*/ +class CSecondaryScreen { +public: + CSecondaryScreen(); + virtual ~CSecondaryScreen(); + + //! @name manipulators + //@{ + + //! Open screen + /*! + Opens the screen. It also causes events to the reported to an + IScreenReceiver (which is set through some other interface). + Calls close() before returning (rethrowing) if it fails for any + reason. + */ + void open(); + + //! Run event loop + /*! + Run the screen's event loop. This returns when it detects + the application should terminate or when exitMainLoop() is called. + mainLoop() may only be called between open() and close(). + */ + void mainLoop(); + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). + */ + void exitMainLoop(); + + //! Prepare for remote control + /*! + Prepares the screen for remote control by the server. In + particular, it disables the screen saver. + */ + void remoteControl(); + + //! Release from remote control + /*! + Cleans up the screen from remote control by the server. In + particular, it enables the screen saver. It also synthesizes + key up events for any keys that are logically down; without + this the client will leave its keyboard in the wrong logical + state. + */ + void localControl(); + + //! Close screen + /*! + Closes the screen. + */ + void close(); + + //! Enter screen + /*! + Called when the user navigates to this secondary screen. Warps + the cursor to the absolute coordinates \c x,y and unhides + it. Also prepares to synthesize input events. + */ + void enter(SInt32 x, SInt32 y, KeyModifierMask mask); + + //! Leave screen + /*! + Called when the user navigates off the secondary screen. Cleans + up input event synthesis and hides the cursor. + */ + void leave(); + + //! Set clipboard + /*! + Sets the system's clipboard contents. This is usually called + soon after an enter(). + */ + void setClipboard(ClipboardID, const IClipboard*); + + //! Grab clipboard + /*! + Grabs (i.e. take ownership of) the system clipboard. + */ + void grabClipboard(ClipboardID); + + //! Activate/deactivate screen saver + /*! + Forcibly activates the screen saver if \c activate is true otherwise + forcibly deactivates it. + */ + void screensaver(bool activate); + + //! Notify of key press + /*! + Synthesize key events to generate a press of key \c id. If possible + match the given modifier mask. The KeyButton identifies the physical + key on the server that generated this key down. The client must + ensure that a key up or key repeat that uses the same KeyButton will + synthesize an up or repeat for the same client key synthesized by + keyDown(). + */ + virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0; + + //! Notify of key repeat + /*! + Synthesize key events to generate a press and release of key \c id + \c count times. If possible match the given modifier mask. + */ + virtual void keyRepeat(KeyID id, KeyModifierMask, + SInt32 count, KeyButton) = 0; + + //! Notify of key release + /*! + Synthesize key events to generate a release of key \c id. If possible + match the given modifier mask. + */ + virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0; + + //! Notify of mouse press + /*! + Synthesize mouse events to generate a press of mouse button \c id. + */ + virtual void mouseDown(ButtonID id) = 0; + + //! Notify of mouse release + /*! + Synthesize mouse events to generate a release of mouse button \c id. + */ + virtual void mouseUp(ButtonID id) = 0; + + //! Notify of mouse motion + /*! + Synthesize mouse events to generate mouse motion to the absolute + screen position \c xAbs,yAbs. + */ + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + + //! Notify of mouse wheel motion + /*! + Synthesize mouse events to generate mouse wheel motion of \c delta. + \c delta is positive for motion away from the user and negative for + motion towards the user. Each wheel click should generate a delta + of +/-120. + */ + virtual void mouseWheel(SInt32 delta) = 0; + + //! Notify of options changes + /*! + Reset all options to their default values. Overrides should call + the superclass's method. + */ + virtual void resetOptions(); + + //! Notify of options changes + /*! + Set options to given values. Ignore unknown options and don't + modify our options that aren't given in \c options. Overrides + should call the superclass's method. + */ + virtual void setOptions(const COptionsList& options); + + //@} + //! @name accessors + //@{ + + //! Test if active + /*! + Returns true iff the screen is active (i.e. the user has entered + the screen). Note this is the reverse of a primary screen. + */ + bool isActive() const; + + //! Get clipboard + /*! + Saves the contents of the system clipboard indicated by \c id. + */ + void getClipboard(ClipboardID id, IClipboard*) const; + + //! Get jump zone size + /*! + Return the jump zone size, the size of the regions on the edges of + the screen that cause the cursor to jump to another screen. + */ + virtual SInt32 getJumpZoneSize() const = 0; + + //! Get screen shape + /*! + Return the position of the upper-left corner of the screen in \c x and + \c y and the size of the screen in \c width and \c height. + */ + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + + //! Get cursor position + /*! + Return the current position of the cursor in \c x,y. + */ + virtual void getCursorPos(SInt32& x, SInt32& y) const; + + //! Get screen + /*! + Return the platform dependent screen. + */ + virtual IScreen* getScreen() const = 0; + + //@} + +protected: + //! Pre-mainLoop() hook + /*! + Called on entry to mainLoop(). Override to perform platform specific + operations. Default does nothing. May throw. + */ + virtual void onPreMainLoop(); + + //! Post-mainLoop() hook + /*! + Called on exit from mainLoop(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostMainLoop(); + + //! Pre-open() hook + /*! + Called on entry to open(). Override to perform platform specific + operations. Default does nothing. May throw. + */ + virtual void onPreOpen(); + + //! Post-open() hook + /*! + Called on exit from open() iff the open was successful. Default + does nothing. May throw. + */ + virtual void onPostOpen(); + + //! Pre-close() hook + /*! + Called on entry to close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPreClose(); + + //! Post-close() hook + /*! + Called on exit from close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostClose(); + + //! Pre-enter() hook + /*! + Called on entry to enter() after desktop synchronization. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ + virtual void onPreEnter(); + + //! Post-enter() hook + /*! + Called on exit from enter(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostEnter(); + + //! Pre-leave() hook + /*! + Called on entry to leave() after desktop synchronization. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ + virtual void onPreLeave(); + + //! Post-leave() hook + /*! + Called on exit from leave(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostLeave(); + + //! Create window + /*! + Called to create the window. This window is generally used to + receive events and hide the cursor. + */ + virtual void createWindow() = 0; + + //! Destroy window + /*! + Called to destroy the window created by createWindow(). + */ + virtual void destroyWindow() = 0; + + //! Show window + /*! + Called when the user navigates off this secondary screen. It needn't + actually show the window created by createWindow() but it must move + the cursor to x,y, hide it, and clean up event synthesis. + */ + virtual void showWindow(SInt32 x, SInt32 y) = 0; + + //! Hide window + /*! + Called when the user navigates to this secondary screen. It should + hide the window (if shown), show the cursor, and prepare to synthesize + input events. + */ + virtual void hideWindow() = 0; + + //! Warp cursor + /*! + Warp the cursor to the absolute coordinates \c x,y. + */ + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + + //! Synchronize key state + /*! + Check the current keyboard state. Normally a screen will save + the keyboard state in this method and use this shadow state + when synthesizing events. + */ + virtual void updateKeys() = 0; + + //! Release keys + /*! + Synthesizes key release event for any key that our key state + says is down. + */ + virtual void releaseKeys() = 0; + + //! Synchronize toggle key state + /*! + Toggle modifiers that don't match the given state so that they do. + */ + virtual void setToggleState(KeyModifierMask) = 0; + + //! Get the toggle key state + /*! + Returns the current state of the toggle keys. + */ + virtual KeyModifierMask getToggleState() const = 0; + +private: + CMutex m_mutex; + + // true if ready for remote control + bool m_remoteReady; + + // m_active is true if this screen has been entered + bool m_active; + + // the toggle key state when this screen was last entered + KeyModifierMask m_toggleKeys; + + // true if screen saver should be synchronized to server + bool m_screenSaverSync; +}; + +#endif diff --git a/lib/synergy/ClipboardTypes.h b/lib/synergy/ClipboardTypes.h new file mode 100644 index 0000000..a638e82 --- /dev/null +++ b/lib/synergy/ClipboardTypes.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CLIPBOARDTYPES_H +#define CLIPBOARDTYPES_H + +#include "BasicTypes.h" + +//! Clipboard ID +/*! +Type to hold a clipboard identifier. +*/ +typedef UInt8 ClipboardID; + +//! @name Clipboard identifiers +//@{ +// clipboard identifiers. kClipboardClipboard is what is normally +// considered the clipboard (e.g. the cut/copy/paste menu items +// affect it). kClipboardSelection is the selection on those +// platforms that can treat the selection as a clipboard (e.g. X +// windows). clipboard identifiers must be sequential starting +// at zero. +static const ClipboardID kClipboardClipboard = 0; +static const ClipboardID kClipboardSelection = 1; + +// the number of clipboards (i.e. one greater than the last clipboard id) +static const ClipboardID kClipboardEnd = 2; +//@} + +#endif diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h new file mode 100644 index 0000000..01ab441 --- /dev/null +++ b/lib/synergy/IClient.h @@ -0,0 +1,214 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ICLIENT_H +#define ICLIENT_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "OptionTypes.h" +#include "CString.h" + +//! Client interface +/*! +This interface defines the methods necessary for the server to +communicate with a client. +*/ +class IClient : public IInterface { +public: + //! @name manipulators + //@{ + + //! Open client + /*! + Open the client. Throw if the client cannot be opened. If the + screen cannot be opened but retrying later may succeed then throw + XScreenUnavailable. + */ + virtual void open() = 0; + + //! Client main loop + /*! + Run client's event loop. This method is typically called in a + separate thread and is exited by cancelling the thread. This + must be called between a successful open() and close(). + + (cancellation point) + */ + virtual void mainLoop() = 0; + + //! Close client + /*! + Close the client. + */ + virtual void close() = 0; + + //! Enter screen + /*! + Enter the screen. The cursor should be warped to \c xAbs,yAbs. + The client should record seqNum for future reporting of + clipboard changes. \c mask is the expected toggle button state + and the client should update its state to match. \c forScreensaver + is true iff the screen is being entered because the screen saver is + starting. + */ + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver) = 0; + + //! Leave screen + /*! + Leave the screen. Return false iff the user may not leave the + client's screen (because, for example, a button is down). + */ + virtual bool leave() = 0; + + //! Set clipboard + /*! + Update the client's clipboard. This implies that the client's + clipboard is now up to date. If the client's clipboard was + already known to be up to date then this may do nothing. \c data + has marshalled clipboard data. + */ + virtual void setClipboard(ClipboardID, const CString& data) = 0; + + //! Grab clipboard + /*! + Grab (i.e. take ownership of) the client's clipboard. Since this + is called when another client takes ownership of the clipboard it + implies that the client's clipboard is out of date. + */ + virtual void grabClipboard(ClipboardID) = 0; + + //! Mark clipboard dirty + /*! + Mark the client's clipboard as dirty (out of date) or clean (up to + date). + */ + virtual void setClipboardDirty(ClipboardID, bool dirty) = 0; + + //! Notify of key press + /*! + Synthesize key events to generate a press of key \c id. If possible + match the given modifier mask. The KeyButton identifies the physical + key on the server that generated this key down. The client must + ensure that a key up or key repeat that uses the same KeyButton will + synthesize an up or repeat for the same client key synthesized by + keyDown(). + */ + virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0; + + //! Notify of key repeat + /*! + Synthesize key events to generate a press and release of key \c id + \c count times. If possible match the given modifier mask. + */ + virtual void keyRepeat(KeyID id, KeyModifierMask, + SInt32 count, KeyButton) = 0; + + //! Notify of key release + /*! + Synthesize key events to generate a release of key \c id. If possible + match the given modifier mask. + */ + virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0; + + //! Notify of mouse press + /*! + Synthesize mouse events to generate a press of mouse button \c id. + */ + virtual void mouseDown(ButtonID id) = 0; + + //! Notify of mouse release + /*! + Synthesize mouse events to generate a release of mouse button \c id. + */ + virtual void mouseUp(ButtonID id) = 0; + + //! Notify of mouse motion + /*! + Synthesize mouse events to generate mouse motion to the absolute + screen position \c xAbs,yAbs. + */ + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + + //! Notify of mouse wheel motion + /*! + Synthesize mouse events to generate mouse wheel motion of \c delta. + \c delta is positive for motion away from the user and negative for + motion towards the user. Each wheel click should generate a delta + of +/-120. + */ + virtual void mouseWheel(SInt32 delta) = 0; + + //! Notify of screen saver change + virtual void screensaver(bool activate) = 0; + + //! Notify of options changes + /*! + Reset all options to their default values. + */ + virtual void resetOptions() = 0; + + //! Notify of options changes + /*! + Set options to given values. Ignore unknown options and don't + modify our options that aren't given in \c options. + */ + virtual void setOptions(const COptionsList& options) = 0; + + //@} + //! @name accessors + //@{ + + //! Get client name + /*! + Return the client's name. + */ + virtual CString getName() const = 0; + + //! Get jump zone size + /*! + Called to get the jump zone size. + */ + virtual SInt32 getJumpZoneSize() const = 0; + + //! Get screen shape + /*! + Return the position of the upper-left corner of the screen in \c x and + \c y and the size of the screen in \c width and \c height. + */ + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + + //! Get cursor position + /*! + Return the current position of the cursor in \c x and \c y. + */ + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + + //! Get cursor center position + /*! + Return the cursor center position which is where we park the + cursor to compute cursor motion deltas and should be far from + the edges of the screen, typically the center. + */ + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/IClipboard.h b/lib/synergy/IClipboard.h new file mode 100644 index 0000000..25defae --- /dev/null +++ b/lib/synergy/IClipboard.h @@ -0,0 +1,117 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ICLIPBOARD_H +#define ICLIPBOARD_H + +#include "IInterface.h" +#include "CString.h" +#include "BasicTypes.h" + +//! Clipboard interface +/*! +This interface defines the methods common to all clipboards. +*/ +class IClipboard : public IInterface { +public: + //! Timestamp type + /*! + Timestamp type. Timestamps are in milliseconds from some + arbitrary starting time. Timestamps will wrap around to 0 + after about 49 3/4 days. + */ + typedef UInt32 Time; + + //! Clipboard formats + /*! + The list of known clipboard formats. kNumFormats must be last and + formats must be sequential starting from zero. Clipboard data set + via add() and retrieved via get() must be in one of these formats. + Platform dependent clipboard subclasses can and should present any + suitable formats derivable from these formats. + */ + enum EFormat { + kText, //!< Text format, UTF-8, newline is LF + kNumFormats //!< The number of clipboard formats + }; + + //! @name manipulators + //@{ + + //! Empty clipboard + /*! + Take ownership of the clipboard and clear all data from it. + This must be called between a successful open() and close(). + Return false if the clipboard ownership could not be taken; + the clipboard should not be emptied in this case. + */ + virtual bool empty() = 0; + + //! Add data + /*! + Add data in the given format to the clipboard. May only be + called after a successful empty(). + */ + virtual void add(EFormat, const CString& data) = 0; + + //@} + //! @name accessors + //@{ + + //! Open clipboard + /*! + Open the clipboard. Return true iff the clipboard could be + opened. If open() returns true then the client must call + close() at some later time; if it returns false then close() + must not be called. \c time should be the current time or + a time in the past when the open should effectively have taken + place. + */ + virtual bool open(Time time) const = 0; + + //! Close clipboard + /*! + Close the clipboard. close() must match a preceding successful + open(). This signals that the clipboard has been filled with + all the necessary data or all data has been read. It does not + mean the clipboard ownership should be released (if it was + taken). + */ + virtual void close() const = 0; + + //! Get time + /*! + Return the timestamp passed to the last successful open(). + */ + virtual Time getTime() const = 0; + + //! Check for data + /*! + Return true iff the clipboard contains data in the given + format. Must be called between a successful open() and close(). + */ + virtual bool has(EFormat) const = 0; + + //! Get data + /*! + Return the data in the given format. Returns the empty string + if there is no data in that format. Must be called between + a successful open() and close(). + */ + virtual CString get(EFormat) const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/IPrimaryScreenFactory.h b/lib/synergy/IPrimaryScreenFactory.h new file mode 100644 index 0000000..630449a --- /dev/null +++ b/lib/synergy/IPrimaryScreenFactory.h @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IPRIMARYSCREENFACTORY_H +#define IPRIMARYSCREENFACTORY_H + +#include "IInterface.h" + +class CPrimaryScreen; +class IPrimaryScreenReceiver; +class IScreenReceiver; + +//! Primary screen factory interface +/*! +This interface provides factory methods to create primary screens. +*/ +class IPrimaryScreenFactory : public IInterface { +public: + //! Create screen + /*! + Create and return a primary screen. The caller must delete the + returned object. + */ + virtual CPrimaryScreen* + create(IScreenReceiver*, IPrimaryScreenReceiver*) = 0; +}; + +#endif diff --git a/lib/synergy/IPrimaryScreenReceiver.h b/lib/synergy/IPrimaryScreenReceiver.h new file mode 100644 index 0000000..44b1bee --- /dev/null +++ b/lib/synergy/IPrimaryScreenReceiver.h @@ -0,0 +1,73 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IPRIMARYSCREENRECEIVER_H +#define IPRIMARYSCREENRECEIVER_H + +#include "IInterface.h" +#include "KeyTypes.h" +#include "MouseTypes.h" + +//! Primary screen event receiver interface +/*! +The interface for receiving notification of events on the primary +screen. The server implements this interface to handle user input. +Platform dependent primary screen implementation will need to take +an IPrimaryScreenReceiver* and notify it of events. +*/ +class IPrimaryScreenReceiver : public IInterface { +public: + //! Notify of screen saver change + /*! + Called when the screensaver is activated or deactivated. + */ + virtual void onScreensaver(bool activated) = 0; + + //! Notify of one-shot timer expiration + /*! + Called when a one-shot timer expires. + */ + virtual void onOneShotTimerExpired(UInt32 id) = 0; + + // call to notify of events. onMouseMovePrimary() returns + // true iff the mouse enters a jump zone and jumps. + //! Notify of key press + virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton) = 0; + //! Notify of key release + virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton) = 0; + //! Notify of key repeat + virtual void onKeyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton) = 0; + //! Notify of mouse button press + virtual void onMouseDown(ButtonID) = 0; + //! Notify of mouse button release + virtual void onMouseUp(ButtonID) = 0; + //! Notify of mouse motion + /*! + Called when the mouse has moved while on the primary screen. \c x + and \c y are the absolute screen position of the mouse. Return + true iff the mouse enters a jump zone and jumps. + */ + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; + //! Notify of mouse motion + /*! + Called when the mouse has moved while on the secondary screen. + \c dx and \c dy are the relative motion from the last position. + */ + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; + //! Notify of mouse wheen motion + virtual void onMouseWheel(SInt32 delta) = 0; +}; + +#endif diff --git a/lib/synergy/IScreen.h b/lib/synergy/IScreen.h new file mode 100644 index 0000000..1676982 --- /dev/null +++ b/lib/synergy/IScreen.h @@ -0,0 +1,150 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISCREEN_H +#define ISCREEN_H + +#include "IInterface.h" +#include "ClipboardTypes.h" + +class IClipboard; + +//! Screen interface +/*! +This interface defines the methods common to all platform dependent +screen implementations that are use by both primary and secondary +screens. +*/ +class IScreen : public IInterface { +public: + //! @name manipulators + //@{ + + //! Open screen + /*! + Called to open and initialize the screen. Throw XScreenUnavailable + if the screen cannot be opened but retrying later may succeed. + Otherwise throw some other XScreenOpenFailure exception. + */ + virtual void open() = 0; + + //! Run event loop + /*! + Run the event loop and return when exitMainLoop() is called. + This must be called between a successful open() and close(). + */ + virtual void mainLoop() = 0; + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). This may only be + called between a successful open() and close(). + */ + virtual void exitMainLoop() = 0; + + //! Close screen + /*! + Called to close the screen. close() should quietly ignore calls + that don't have a matching successful call to open(). + */ + virtual void close() = 0; + + //! Set clipboard + /*! + Set the contents of the system clipboard indicated by \c id. + */ + virtual bool setClipboard(ClipboardID id, const IClipboard*) = 0; + + //! Check clipboard owner + /*! + Check ownership of all clipboards and notify an IScreenReceiver (set + through some other interface) if any changed. This is used as a + backup in case the system doesn't reliably report clipboard ownership + changes. + */ + virtual void checkClipboards() = 0; + + //! Open screen saver + /*! + Open the screen saver. If \c notify is true then this object must + call an IScreenEventHandler's (set through some other interface) + onScreenSaver() when the screensaver activates or deactivates until + it's closed. If \c notify is false then the screen saver is + disabled on open and restored on close. + */ + virtual void openScreensaver(bool notify) = 0; + + //! Close screen saver + /*! + // Close the screen saver. Stop reporting screen saver activation + and deactivation and, if the screen saver was disabled by + openScreensaver(), enable the screen saver. + */ + virtual void closeScreensaver() = 0; + + //! Activate/deactivate screen saver + /*! + Forcibly activate the screen saver if \c activate is true otherwise + forcibly deactivate it. + */ + virtual void screensaver(bool activate) = 0; + + //! Attach to desktop + /*! + Called to ensure that this thread is attached to the visible desktop. + This is mainly intended for microsoft windows which has an artificial + distinction between desktops where a thread cannot interact with the + visible desktop unless the thread is attached to that desktop. Since + it doesn't report when the visible desktop changes we must poll. + */ + virtual void syncDesktop() = 0; + + //@} + //! @name accessors + //@{ + + //! Get clipboard + /*! + Save the contents of the clipboard indicated by \c id and return + true iff successful. + */ + virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; + + //! Get screen shape + /*! + Return the position of the upper-left corner of the screen in \c x and + \c y and the size of the screen in \c w (width) and \c h (height). + */ + virtual void getShape(SInt32& x, SInt32& y, + SInt32& w, SInt32& h) const = 0; + + //! Get cursor position + /*! + Return the current position of the cursor in \c x and \c y. + */ + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + + //! Get cursor center position + /*! + Return the cursor center position which is where we park the + cursor to compute cursor motion deltas and should be far from + the edges of the screen, typically the center. + */ + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/IScreenEventHandler.h b/lib/synergy/IScreenEventHandler.h new file mode 100644 index 0000000..a68858e --- /dev/null +++ b/lib/synergy/IScreenEventHandler.h @@ -0,0 +1,79 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISCREENEVENTHANDLER_H +#define ISCREENEVENTHANDLER_H + +#include "IInterface.h" + +// the platform screen should define this +class CEvent; + +class IScreen; + +//! Screen event handler interface +/*! +This is the interface through which IScreen sends notification of events. +Each platform will derive two types from IScreenEventHandler, one +for handling events on the primary screen and one for the +secondary screen. The header file with the IScreen subclass for +each platform should define the CEvent type, which depends on the +type of native events for that platform. +*/ +class IScreenEventHandler : public IInterface { +public: + //! @name manipulators + //@{ + + //! Notify of screen saver change + /*! + Called when the screensaver is activated or deactivated. + */ + virtual void onScreensaver(bool activated) = 0; + + //! Event filtering + /*! + Called for each event before event translation and dispatch. Return + true to skip translation and dispatch. Subclasses should call the + superclass's version first and return true if it returns true. + */ + virtual bool onPreDispatch(const CEvent* event) = 0; + + //! Event handling + /*! + Called to handle an event. Iff the event was handled return true and + store the result, if any, in event->m_result, which defaults to zero. + */ + virtual bool onEvent(CEvent* event) = 0; + + //! Notify of one-shot timer expiration + /*! + Called when a one-shot timer expires. + */ + virtual void onOneShotTimerExpired(UInt32 id) = 0; + + //@} + //! @name accessors + //@{ + + //! Get jump zone size + /*! + Called to get the jump zone size. + */ + virtual SInt32 getJumpZoneSize() const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/IScreenReceiver.h b/lib/synergy/IScreenReceiver.h new file mode 100644 index 0000000..0dd5507 --- /dev/null +++ b/lib/synergy/IScreenReceiver.h @@ -0,0 +1,65 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISCREENRECEIVER_H +#define ISCREENRECEIVER_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "ProtocolTypes.h" +#include "CString.h" + +//! Screen event receiver interface +/*! +This interface defines the methods common to most types that receive +events for changes to a screen. Note that the methods in this +interface are similar to the methods in IServer but have different +parameters. This interface is suitable for client-side types. +*/ +class IScreenReceiver : public IInterface { +public: + //! Notify of error + /*! + Called when the screen is unexpectedly closing. This implies that + the screen is no longer usable and that the program should close + the screen and probably terminate. + */ + virtual void onError() = 0; + + //! Notify of client screen change + /*! + Called when the client's info has changed. For example, when the + screen resolution has changed. + */ + virtual void onInfoChanged(const CClientInfo&) = 0; + + //! Notify of clipboad grab + /*! + Called when the clipboard was grabbed by another program and, + therefore, we no longer own it. Returns true if the grab was + honored, false otherwise. + */ + virtual bool onGrabClipboard(ClipboardID) = 0; + + //! Notify of new clipboard data + /*! + Called when the data on the clipboard has changed because some + other program has changed it. \c data will have marshalled + clipboard data. + */ + virtual void onClipboardChanged(ClipboardID, + const CString& data) = 0; +}; + +#endif diff --git a/lib/synergy/IScreenSaver.h b/lib/synergy/IScreenSaver.h new file mode 100644 index 0000000..f2da811 --- /dev/null +++ b/lib/synergy/IScreenSaver.h @@ -0,0 +1,73 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISCREENSAVER_H +#define ISCREENSAVER_H + +#include "IInterface.h" + +//! Screen saver interface +/*! +This interface defines the methods common to all screen savers. +*/ +class IScreenSaver : public IInterface { +public: + // note -- the c'tor/d'tor must *not* enable/disable the screen saver + + //! @name manipulators + //@{ + + //! Enable screen saver + /*! + Enable the screen saver, restoring the screen saver settings to + what they were when disable() was previously called. If disable() + wasn't previously called then it should keep the current settings + or use reasonable defaults. + */ + virtual void enable() = 0; + + //! Disable screen saver + /*! + Disable the screen saver, saving the old settings for the next + call to enable(). + */ + virtual void disable() = 0; + + //! Activate screen saver + /*! + Activate (i.e. show) the screen saver. + */ + virtual void activate() = 0; + + //! Deactivate screen saver + /*! + Deactivate (i.e. hide) the screen saver, reseting the screen saver + timer. + */ + virtual void deactivate() = 0; + + //@} + //! @name accessors + //@{ + + //! Test if screen saver on + /*! + Returns true iff the screen saver is currently active (showing). + */ + virtual bool isActive() const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/ISecondaryScreenFactory.h b/lib/synergy/ISecondaryScreenFactory.h new file mode 100644 index 0000000..03667ec --- /dev/null +++ b/lib/synergy/ISecondaryScreenFactory.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISECONDARYSCREENFACTORY_H +#define ISECONDARYSCREENFACTORY_H + +#include "IInterface.h" + +class CSecondaryScreen; +class IScreenReceiver; + +//! Secondary screen factory interface +/*! +This interface provides factory methods to create secondary screens. +*/ +class ISecondaryScreenFactory : public IInterface { +public: + //! Create screen + /*! + Create and return a secondary screen. The caller must delete the + returned object. + */ + virtual CSecondaryScreen* + create(IScreenReceiver*) = 0; +}; + +#endif diff --git a/lib/synergy/IServer.h b/lib/synergy/IServer.h new file mode 100644 index 0000000..42b7a67 --- /dev/null +++ b/lib/synergy/IServer.h @@ -0,0 +1,75 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISERVER_H +#define ISERVER_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "CString.h" + +class CClientInfo; + +//! Server interface +/*! +This interface defines the methods necessary for clients to +communicate with the server. Note that the methods in this +interface are similar to the methods in IScreenReceiver but +include extra parameters. This interface is suitable for +server-side client proxies. Client-side objects should use +the IScreenReceiver interface since the extra parameters are +meaningless on the client-side. +*/ +class IServer : public IInterface { +public: + //! @name manipulators + //@{ + + //! Notify of error + /*! + Called when the screen is unexpectedly closing. This implies that + the screen is no longer usable and that the program should close + the screen and probably terminate. + */ + virtual void onError() = 0; + + //! Notify of client screen change + /*! + Called when the client's info has changed. + */ + virtual void onInfoChanged(const CString& clientName, + const CClientInfo&) = 0; + + //! Notify of clipboad grab + /*! + Called when the clipboard was grabbed by another program and, + therefore, we no longer own it. Returns true if the grab was + honored, false otherwise. + */ + virtual bool onGrabClipboard(const CString& clientName, + ClipboardID, UInt32 seqNum) = 0; + + //! Notify of new clipboard data + /*! + Called when the data on the clipboard has changed because some + other program has changed it. \c data has the marshalled clipboard + data. + */ + virtual void onClipboardChanged(ClipboardID, + UInt32 seqNum, const CString& data) = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h new file mode 100644 index 0000000..9af4dd0 --- /dev/null +++ b/lib/synergy/KeyTypes.h @@ -0,0 +1,237 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef KEYTYPES_H +#define KEYTYPES_H + +#include "BasicTypes.h" + +//! Key ID +/*! +Type to hold a key symbol identifier. The encoding is UTF-32, using +U+E000 through U+EFFF for the various control keys (e.g. arrow +keys, function keys, modifier keys, etc). +*/ +typedef UInt32 KeyID; + +//! Key Code +/*! +Type to hold a physical key identifier. That is, it identifies a +physical key on the keyboard. +*/ +typedef UInt16 KeyButton; + +//! Modifier key mask +/*! +Type to hold a bitmask of key modifiers (e.g. shift keys). +*/ +typedef UInt32 KeyModifierMask; + +//! Modifier key ID +/*! +Type to hold the id of a key modifier (e.g. a shift key). +*/ +typedef UInt32 KeyModifierID; + +//! @name Modifier key masks +//@{ +static const KeyModifierMask KeyModifierShift = 0x0001; +static const KeyModifierMask KeyModifierControl = 0x0002; +static const KeyModifierMask KeyModifierAlt = 0x0004; +static const KeyModifierMask KeyModifierMeta = 0x0008; +static const KeyModifierMask KeyModifierSuper = 0x0010; +static const KeyModifierMask KeyModifierModeSwitch = 0x0020; +static const KeyModifierMask KeyModifierCapsLock = 0x1000; +static const KeyModifierMask KeyModifierNumLock = 0x2000; +static const KeyModifierMask KeyModifierScrollLock = 0x4000; +//@} + +//! @name Modifier key identifiers +//@{ +static const KeyModifierID kKeyModifierIDNull = 0; +static const KeyModifierID kKeyModifierIDShift = 1; +static const KeyModifierID kKeyModifierIDControl = 2; +static const KeyModifierID kKeyModifierIDAlt = 3; +static const KeyModifierID kKeyModifierIDMeta = 4; +static const KeyModifierID kKeyModifierIDSuper = 5; +static const KeyModifierID kKeyModifierIDLast = 6; +//@} + +//! @name Key identifiers +//@{ +// all identifiers except kKeyNone and those in 0xE000 to 0xE0FF +// inclusive are equal to the corresponding X11 keysym - 0x1000. + +// no key +static const KeyID kKeyNone = 0x0000; + +// TTY functions +static const KeyID kKeyBackSpace = 0xEF08; /* back space, back char */ +static const KeyID kKeyTab = 0xEF09; +static const KeyID kKeyLinefeed = 0xEF0A; /* Linefeed, LF */ +static const KeyID kKeyClear = 0xEF0B; +static const KeyID kKeyReturn = 0xEF0D; /* Return, enter */ +static const KeyID kKeyPause = 0xEF13; /* Pause, hold */ +static const KeyID kKeyScrollLock = 0xEF14; +static const KeyID kKeySysReq = 0xEF15; +static const KeyID kKeyEscape = 0xEF1B; +static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */ + +// multi-key character composition +static const KeyID kKeyMultiKey = 0xEF20; /* Multi-key character compose */ + +// cursor control +static const KeyID kKeyHome = 0xEF50; +static const KeyID kKeyLeft = 0xEF51; /* Move left, left arrow */ +static const KeyID kKeyUp = 0xEF52; /* Move up, up arrow */ +static const KeyID kKeyRight = 0xEF53; /* Move right, right arrow */ +static const KeyID kKeyDown = 0xEF54; /* Move down, down arrow */ +static const KeyID kKeyPageUp = 0xEF55; +static const KeyID kKeyPageDown = 0xEF56; +static const KeyID kKeyEnd = 0xEF57; /* EOL */ +static const KeyID kKeyBegin = 0xEF58; /* BOL */ + +// misc functions +static const KeyID kKeySelect = 0xEF60; /* Select, mark */ +static const KeyID kKeyPrint = 0xEF61; +static const KeyID kKeyExecute = 0xEF62; /* Execute, run, do */ +static const KeyID kKeyInsert = 0xEF63; /* Insert, insert here */ +static const KeyID kKeyUndo = 0xEF65; /* Undo, oops */ +static const KeyID kKeyRedo = 0xEF66; /* redo, again */ +static const KeyID kKeyMenu = 0xEF67; +static const KeyID kKeyFind = 0xEF68; /* Find, search */ +static const KeyID kKeyCancel = 0xEF69; /* Cancel, stop, abort, exit */ +static const KeyID kKeyHelp = 0xEF6A; /* Help */ +static const KeyID kKeyBreak = 0xEF6B; +static const KeyID kKeyModeSwitch = 0xEF7E; /* Character set switch */ +static const KeyID kKeyNumLock = 0xEF7F; + +// keypad +static const KeyID kKeyKP_Space = 0xEF80; /* space */ +static const KeyID kKeyKP_Tab = 0xEF89; +static const KeyID kKeyKP_Enter = 0xEF8D; /* enter */ +static const KeyID kKeyKP_F1 = 0xEF91; /* PF1, KP_A, ... */ +static const KeyID kKeyKP_F2 = 0xEF92; +static const KeyID kKeyKP_F3 = 0xEF93; +static const KeyID kKeyKP_F4 = 0xEF94; +static const KeyID kKeyKP_Home = 0xEF95; +static const KeyID kKeyKP_Left = 0xEF96; +static const KeyID kKeyKP_Up = 0xEF97; +static const KeyID kKeyKP_Right = 0xEF98; +static const KeyID kKeyKP_Down = 0xEF99; +static const KeyID kKeyKP_Prior = 0xEF9A; +static const KeyID kKeyKP_PageUp = 0xEF9A; +static const KeyID kKeyKP_Next = 0xEF9B; +static const KeyID kKeyKP_PageDown = 0xEF9B; +static const KeyID kKeyKP_End = 0xEF9C; +static const KeyID kKeyKP_Begin = 0xEF9D; +static const KeyID kKeyKP_Insert = 0xEF9E; +static const KeyID kKeyKP_Delete = 0xEF9F; +static const KeyID kKeyKP_Equal = 0xEFBD; /* equals */ +static const KeyID kKeyKP_Multiply = 0xEFAA; +static const KeyID kKeyKP_Add = 0xEFAB; +static const KeyID kKeyKP_Separator= 0xEFAC; /* separator, often comma */ +static const KeyID kKeyKP_Subtract = 0xEFAD; +static const KeyID kKeyKP_Decimal = 0xEFAE; +static const KeyID kKeyKP_Divide = 0xEFAF; +static const KeyID kKeyKP_0 = 0xEFB0; +static const KeyID kKeyKP_1 = 0xEFB1; +static const KeyID kKeyKP_2 = 0xEFB2; +static const KeyID kKeyKP_3 = 0xEFB3; +static const KeyID kKeyKP_4 = 0xEFB4; +static const KeyID kKeyKP_5 = 0xEFB5; +static const KeyID kKeyKP_6 = 0xEFB6; +static const KeyID kKeyKP_7 = 0xEFB7; +static const KeyID kKeyKP_8 = 0xEFB8; +static const KeyID kKeyKP_9 = 0xEFB9; + +// function keys +static const KeyID kKeyF1 = 0xEFBE; +static const KeyID kKeyF2 = 0xEFBF; +static const KeyID kKeyF3 = 0xEFC0; +static const KeyID kKeyF4 = 0xEFC1; +static const KeyID kKeyF5 = 0xEFC2; +static const KeyID kKeyF6 = 0xEFC3; +static const KeyID kKeyF7 = 0xEFC4; +static const KeyID kKeyF8 = 0xEFC5; +static const KeyID kKeyF9 = 0xEFC6; +static const KeyID kKeyF10 = 0xEFC7; +static const KeyID kKeyF11 = 0xEFC8; +static const KeyID kKeyF12 = 0xEFC9; +static const KeyID kKeyF13 = 0xEFCA; +static const KeyID kKeyF14 = 0xEFCB; +static const KeyID kKeyF15 = 0xEFCC; +static const KeyID kKeyF16 = 0xEFCD; +static const KeyID kKeyF17 = 0xEFCE; +static const KeyID kKeyF18 = 0xEFCF; +static const KeyID kKeyF19 = 0xEFD0; +static const KeyID kKeyF20 = 0xEFD1; +static const KeyID kKeyF21 = 0xEFD2; +static const KeyID kKeyF22 = 0xEFD3; +static const KeyID kKeyF23 = 0xEFD4; +static const KeyID kKeyF24 = 0xEFD5; +static const KeyID kKeyF25 = 0xEFD6; +static const KeyID kKeyF26 = 0xEFD7; +static const KeyID kKeyF27 = 0xEFD8; +static const KeyID kKeyF28 = 0xEFD9; +static const KeyID kKeyF29 = 0xEFDA; +static const KeyID kKeyF30 = 0xEFDB; +static const KeyID kKeyF31 = 0xEFDC; +static const KeyID kKeyF32 = 0xEFDD; +static const KeyID kKeyF33 = 0xEFDE; +static const KeyID kKeyF34 = 0xEFDF; +static const KeyID kKeyF35 = 0xEFE0; + +// modifiers +static const KeyID kKeyShift_L = 0xEFE1; /* Left shift */ +static const KeyID kKeyShift_R = 0xEFE2; /* Right shift */ +static const KeyID kKeyControl_L = 0xEFE3; /* Left control */ +static const KeyID kKeyControl_R = 0xEFE4; /* Right control */ +static const KeyID kKeyCapsLock = 0xEFE5; /* Caps lock */ +static const KeyID kKeyShiftLock = 0xEFE6; /* Shift lock */ +static const KeyID kKeyMeta_L = 0xEFE7; /* Left meta */ +static const KeyID kKeyMeta_R = 0xEFE8; /* Right meta */ +static const KeyID kKeyAlt_L = 0xEFE9; /* Left alt */ +static const KeyID kKeyAlt_R = 0xEFEA; /* Right alt */ +static const KeyID kKeySuper_L = 0xEFEB; /* Left super */ +static const KeyID kKeySuper_R = 0xEFEC; /* Right super */ +static const KeyID kKeyHyper_L = 0xEFED; /* Left hyper */ +static const KeyID kKeyHyper_R = 0xEFEE; /* Right hyper */ + +// more function and modifier keys +static const KeyID kKeyLeftTab = 0xEE20; + +// extended keys +static const KeyID kKeyWWWBack = 0xE0A6; +static const KeyID kKeyWWWForward = 0xE0A7; +static const KeyID kKeyWWWRefresh = 0xE0A8; +static const KeyID kKeyWWWStop = 0xE0A9; +static const KeyID kKeyWWWSearch = 0xE0AA; +static const KeyID kKeyWWWFavorites = 0xE0AB; +static const KeyID kKeyWWWHome = 0xE0AC; +static const KeyID kKeyAudioMute = 0xE0AD; +static const KeyID kKeyAudioDown = 0xE0AE; +static const KeyID kKeyAudioUp = 0xE0AF; +static const KeyID kKeyAudioNext = 0xE0B0; +static const KeyID kKeyAudioPrev = 0xE0B1; +static const KeyID kKeyAudioStop = 0xE0B2; +static const KeyID kKeyAudioPlay = 0xE0B3; +static const KeyID kKeyAppMail = 0xE0B4; +static const KeyID kKeyAppMedia = 0xE0B5; +static const KeyID kKeyAppUser1 = 0xE0B6; +static const KeyID kKeyAppUser2 = 0xE0B7; + +//@} + +#endif diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am new file mode 100644 index 0000000..4f1f135 --- /dev/null +++ b/lib/synergy/Makefile.am @@ -0,0 +1,67 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + libsynergy.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libsynergy.a +libsynergy_a_SOURCES = \ + CClipboard.cpp \ + CInputPacketStream.cpp \ + COutputPacketStream.cpp \ + CPrimaryScreen.cpp \ + CProtocolUtil.cpp \ + CSecondaryScreen.cpp \ + XScreen.cpp \ + XSynergy.cpp \ + CClipboard.h \ + CInputPacketStream.h \ + COutputPacketStream.h \ + CPrimaryScreen.h \ + CProtocolUtil.h \ + CSecondaryScreen.h \ + ClipboardTypes.h \ + IClient.h \ + IClipboard.h \ + IPrimaryScreenFactory.h \ + IPrimaryScreenReceiver.h \ + IScreen.h \ + IScreenEventHandler.h \ + IScreenReceiver.h \ + IScreenSaver.h \ + ISecondaryScreenFactory.h \ + IServer.h \ + KeyTypes.h \ + MouseTypes.h \ + OptionTypes.h \ + ProtocolTypes.h \ + XScreen.h \ + XSynergy.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + $(NULL) diff --git a/lib/synergy/Makefile.in b/lib/synergy/Makefile.in new file mode 100644 index 0000000..9af27e8 --- /dev/null +++ b/lib/synergy/Makefile.in @@ -0,0 +1,382 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + libsynergy.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libsynergy.a +libsynergy_a_SOURCES = \ + CClipboard.cpp \ + CInputPacketStream.cpp \ + COutputPacketStream.cpp \ + CPrimaryScreen.cpp \ + CProtocolUtil.cpp \ + CSecondaryScreen.cpp \ + XScreen.cpp \ + XSynergy.cpp \ + CClipboard.h \ + CInputPacketStream.h \ + COutputPacketStream.h \ + CPrimaryScreen.h \ + CProtocolUtil.h \ + CSecondaryScreen.h \ + ClipboardTypes.h \ + IClient.h \ + IClipboard.h \ + IPrimaryScreenFactory.h \ + IPrimaryScreenReceiver.h \ + IScreen.h \ + IScreenEventHandler.h \ + IScreenReceiver.h \ + IScreenSaver.h \ + ISecondaryScreenFactory.h \ + IServer.h \ + KeyTypes.h \ + MouseTypes.h \ + OptionTypes.h \ + ProtocolTypes.h \ + XScreen.h \ + XSynergy.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + $(NULL) + +subdir = lib/synergy +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libsynergy_a_AR = $(AR) cru +libsynergy_a_LIBADD = +am_libsynergy_a_OBJECTS = CClipboard.$(OBJEXT) \ + CInputPacketStream.$(OBJEXT) COutputPacketStream.$(OBJEXT) \ + CPrimaryScreen.$(OBJEXT) CProtocolUtil.$(OBJEXT) \ + CSecondaryScreen.$(OBJEXT) XScreen.$(OBJEXT) XSynergy.$(OBJEXT) +libsynergy_a_OBJECTS = $(am_libsynergy_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CClipboard.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CInputPacketStream.Po \ +@AMDEP_TRUE@ $(DEPDIR)/COutputPacketStream.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CPrimaryScreen.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CProtocolUtil.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CSecondaryScreen.Po $(DEPDIR)/XScreen.Po \ +@AMDEP_TRUE@ $(DEPDIR)/XSynergy.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libsynergy_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libsynergy_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/synergy/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libsynergy.a: $(libsynergy_a_OBJECTS) $(libsynergy_a_DEPENDENCIES) + -rm -f libsynergy.a + $(libsynergy_a_AR) libsynergy.a $(libsynergy_a_OBJECTS) $(libsynergy_a_LIBADD) + $(RANLIB) libsynergy.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClipboard.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CInputPacketStream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/COutputPacketStream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CPrimaryScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CProtocolUtil.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CSecondaryScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XSynergy.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/synergy/MouseTypes.h b/lib/synergy/MouseTypes.h new file mode 100644 index 0000000..e498855 --- /dev/null +++ b/lib/synergy/MouseTypes.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MOUSETYPES_H +#define MOUSETYPES_H + +#include "BasicTypes.h" + +//! Mouse button ID +/*! +Type to hold a mouse button identifier. +*/ +typedef UInt8 ButtonID; + +//! @name Mouse button identifiers +//@{ +static const ButtonID kButtonNone = 0; +static const ButtonID kButtonLeft = 1; +static const ButtonID kButtonMiddle = 2; +static const ButtonID kButtonRight = 3; +static const ButtonID kButtonExtra0 = 4; +//@} + +#endif diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h new file mode 100644 index 0000000..aaefe7f --- /dev/null +++ b/lib/synergy/OptionTypes.h @@ -0,0 +1,62 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef OPTIONTYPES_H +#define OPTIONTYPES_H + +#include "BasicTypes.h" +#include "stdvector.h" + +//! Option ID +/*! +Type to hold an option identifier. +*/ +typedef UInt32 OptionID; + +//! Option Value +/*! +Type to hold an option value. +*/ +typedef SInt32 OptionValue; + +// for now, options are just pairs of integers +typedef std::vector COptionsList; + +// macro for packing 4 character strings into 4 byte integers +#define OPTION_CODE(_s) \ + (static_cast(static_cast(_s[0]) << 24) | \ + static_cast(static_cast(_s[1]) << 16) | \ + static_cast(static_cast(_s[2]) << 8) | \ + static_cast(static_cast(_s[3]) )) + +//! @name Option identifiers +//@{ +static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL"); +static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL"); +static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL"); +static const OptionID kOptionModifierMapForShift = OPTION_CODE("MMFS"); +static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC"); +static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA"); +static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); +static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); +static const OptionID kOptionHeartbeat = OPTION_CODE("HART"); +static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); +static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); +static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); +static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); +//@} + +#undef OPTION_CODE + +#endif diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h new file mode 100644 index 0000000..539ddaf --- /dev/null +++ b/lib/synergy/ProtocolTypes.h @@ -0,0 +1,286 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef PROTOCOLTYPES_H +#define PROTOCOLTYPES_H + +#include "BasicTypes.h" + +// protocol version number +// 1.0: initial protocol +// 1.1: adds KeyCode to key press, release, and repeat +static const SInt16 kProtocolMajorVersion = 1; +static const SInt16 kProtocolMinorVersion = 1; + +// default contact port number +static const UInt16 kDefaultPort = 24800; + +// maximum total length for greeting returned by client +static const UInt32 kMaxHelloLength = 1024; + +// time between heartbeats (in seconds). negative value disables +// heartbeat. +static const double kHeartRate = -1.0; + +// number of skipped heartbeats that constitutes death +static const double kHeartBeatsUntilDeath = 3.0; + +// direction constants +enum EDirection { + kNoDirection, + kLeft, + kRight, + kTop, + kBottom, + kFirstDirection = kLeft, + kLastDirection = kBottom, + kNumDirections = kLastDirection - kFirstDirection + 1 +}; +enum EDirectionMask { + kNoDirMask = 0, + kLeftMask = 1 << kLeft, + kRightMask = 1 << kRight, + kTopMask = 1 << kTop, + kBottomMask = 1 << kBottom +}; + + +// +// message codes (trailing NUL is not part of code). in comments, $n +// refers to the n'th argument (counting from one). message codes are +// always 4 bytes optionally followed by message specific parameters +// except those for the greeting handshake. +// + +// +// positions and sizes are signed 16 bit integers. +// + +// +// greeting handshake messages +// + +// say hello to client; primary -> secondary +// $1 = protocol major version number supported by server. $2 = +// protocol minor version number supported by server. +static const char kMsgHello[] = "Synergy%2i%2i"; + +// respond to hello from server; secondary -> primary +// $1 = protocol major version number supported by client. $2 = +// protocol minor version number supported by client. $3 = client +// name. +static const char kMsgHelloBack[] = "Synergy%2i%2i%s"; + + +// +// command codes +// + +// no operation; secondary -> primary +static const char kMsgCNoop[] = "CNOP"; + +// close connection; primary -> secondary +static const char kMsgCClose[] = "CBYE"; + +// enter screen: primary -> secondary +// entering screen at screen position $1 = x, $2 = y. x,y are +// absolute screen coordinates. $3 = sequence number, which is +// used to order messages between screens. the secondary screen +// must return this number with some messages. $4 = modifier key +// mask. this will have bits set for each toggle modifier key +// that is activated on entry to the screen. the secondary screen +// should adjust its toggle modifiers to reflect that state. +static const char kMsgCEnter[] = "CINN%2i%2i%4i%2i"; + +// leave screen: primary -> secondary +// leaving screen. the secondary screen should send clipboard +// data in response to this message for those clipboards that +// it has grabbed (i.e. has sent a kMsgCClipboard for and has +// not received a kMsgCClipboard for with a greater sequence +// number) and that were grabbed or have changed since the +// last leave. +static const char kMsgCLeave[] = "COUT"; + +// grab clipboard: primary <-> secondary +// sent by screen when some other app on that screen grabs a +// clipboard. $1 = the clipboard identifier, $2 = sequence number. +// secondary screens must use the sequence number passed in the +// most recent kMsgCEnter. the primary always sends 0. +static const char kMsgCClipboard[] = "CCLP%1i%4i"; + +// screensaver change: primary -> secondary +// screensaver on primary has started ($1 == 1) or closed ($1 == 0) +static const char kMsgCScreenSaver[] = "CSEC%1i"; + +// reset options: primary -> secondary +// client should reset all of its options to their defaults. +static const char kMsgCResetOptions[] = "CROP"; + +// resolution change acknowledgment: primary -> secondary +// sent by primary in response to a secondary screen's kMsgDInfo. +// this is sent for every kMsgDInfo, whether or not the primary +// had sent a kMsgQInfo. +static const char kMsgCInfoAck[] = "CIAK"; + + +// +// data codes +// + +// key pressed: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton +// the KeyButton identifies the physical key on the primary used to +// generate this key. the secondary should note the KeyButton along +// with the physical key it uses to generate the key press. on +// release, the secondary can then use the primary's KeyButton to +// find its corresponding physical key and release it. this is +// necessary because the KeyID on release may not be the KeyID of +// the press. this can happen with combining (dead) keys or if +// the keyboard layouts are not identical and the user releases +// a modifier key before releasing the modified key. +static const char kMsgDKeyDown[] = "DKDN%2i%2i%2i"; + +// key pressed 1.0: same as above but without KeyButton +static const char kMsgDKeyDown1_0[] = "DKDN%2i%2i"; + +// key auto-repeat: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats, $4 = KeyButton +static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i%2i"; + +// key auto-repeat 1.0: same as above but without KeyButton +static const char kMsgDKeyRepeat1_0[] = "DKRP%2i%2i%2i"; + +// key released: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton +static const char kMsgDKeyUp[] = "DKUP%2i%2i%2i"; + +// key released 1.0: same as above but without KeyButton +static const char kMsgDKeyUp1_0[] = "DKUP%2i%2i"; + +// mouse button pressed: primary -> secondary +// $1 = ButtonID +static const char kMsgDMouseDown[] = "DMDN%1i"; + +// mouse button released: primary -> secondary +// $1 = ButtonID +static const char kMsgDMouseUp[] = "DMUP%1i"; + +// mouse moved: primary -> secondary +// $1 = x, $2 = y. x,y are absolute screen coordinates. +static const char kMsgDMouseMove[] = "DMMV%2i%2i"; + +// mouse button pressed: primary -> secondary +// $1 = delta. the delta should be +120 for one tick forward (away +// from the user) and -120 for one tick backward (toward the user). +static const char kMsgDMouseWheel[] = "DMWM%2i"; + +// clipboard data: primary <-> secondary +// $2 = sequence number, $3 = clipboard data. the sequence number +// is 0 when sent by the primary. secondary screens should use the +// sequence number from the most recent kMsgCEnter. $1 = clipboard +// identifier. +static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; + +// client data: secondary -> primary +// $1 = coordinate of leftmost pixel on secondary screen, +// $2 = coordinate of topmost pixel on secondary screen, +// $3 = width of secondary screen in pixels, +// $4 = height of secondary screen in pixels, +// $5 = size of warp zone, +// $6, $7 = the x,y position of the mouse on the secondary screen. +// +// the secondary screen must send this message in response to the +// kMsgQInfo message. it must also send this message when the +// screen's resolution changes. in this case, the secondary screen +// should ignore any kMsgDMouseMove messages until it receives a +// kMsgCInfoAck in order to prevent attempts to move the mouse off +// the new screen area. +static const char kMsgDInfo[] = "DINF%2i%2i%2i%2i%2i%2i%2i"; + +// set options: primary -> secondary +// client should set the given option/value pairs. $1 = option/value +// pairs. +static const char kMsgDSetOptions[] = "DSOP%4I"; + + +// +// query codes +// + +// query screen info: primary -> secondary +// client should reply with a kMsgDInfo. +static const char kMsgQInfo[] = "QINF"; + + +// +// error codes +// + +// incompatible versions: primary -> secondary +// $1 = major version of primary, $2 = minor version of primary. +static const char kMsgEIncompatible[] = "EICV%2i%2i"; + +// name provided when connecting is already in use: primary -> secondary +static const char kMsgEBusy[] = "EBSY"; + +// unknown client: primary -> secondary +// name provided when connecting is not in primary's screen +// configuration map. +static const char kMsgEUnknown[] = "EUNK"; + +// protocol violation: primary -> secondary +// primary should disconnect after sending this message. +static const char kMsgEBad[] = "EBAD"; + + +// +// structures +// + +//! Screen information +/*! +This class contains information about a screen. +*/ +class CClientInfo { +public: + //! Screen position + /*! + The position of the upper-left corner of the screen. This is + typically 0,0. + */ + SInt32 m_x, m_y; + + //! Screen size + /*! + The size of the screen in pixels. + */ + SInt32 m_w, m_h; + + //! Jump zone size + /*! + This is the size of the jump zone. The cursor jumps to the adjacent + screen when it comes within this many pixels of the edge of the screen. + */ + SInt32 m_zoneSize; + + //! Mouse position + /*! + The position of the cursor. This is not kept up-to-date so it's + only meaningful when receiving an update. + */ + SInt32 m_mx, m_my; +}; + +#endif + diff --git a/lib/synergy/XScreen.cpp b/lib/synergy/XScreen.cpp new file mode 100644 index 0000000..8a694bf --- /dev/null +++ b/lib/synergy/XScreen.cpp @@ -0,0 +1,53 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XScreen.h" + +// +// XScreenOpenFailure +// + +CString +XScreenOpenFailure::getWhat() const throw() +{ + return format("XScreenOpenFailure", "unable to open screen"); +} + + +// +// XScreenUnavailable +// + +XScreenUnavailable::XScreenUnavailable(double timeUntilRetry) : + m_timeUntilRetry(timeUntilRetry) +{ + // do nothing +} + +XScreenUnavailable::~XScreenUnavailable() +{ + // do nothing +} + +double +XScreenUnavailable::getRetryTime() const +{ + return m_timeUntilRetry; +} + +CString +XScreenUnavailable::getWhat() const throw() +{ + return format("XScreenUnavailable", "unable to open screen"); +} diff --git a/lib/synergy/XScreen.h b/lib/synergy/XScreen.h new file mode 100644 index 0000000..9966e6c --- /dev/null +++ b/lib/synergy/XScreen.h @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XSCREEN_H +#define XSCREEN_H + +#include "XBase.h" + +//! Generic screen exception +XBASE_SUBCLASS(XScreen, XBase); + +//! Cannot open screen exception +/*! +Thrown when a screen cannot be opened or initialized. +*/ +XBASE_SUBCLASS_WHAT(XScreenOpenFailure, XScreen); + +//! Screen unavailable exception +/*! +Thrown when a screen cannot be opened or initialized but retrying later +may be successful. +*/ +class XScreenUnavailable : public XScreenOpenFailure { +public: + /*! + \c timeUntilRetry is the suggested time the caller should wait until + trying to open the screen again. + */ + XScreenUnavailable(double timeUntilRetry); + virtual ~XScreenUnavailable(); + + //! @name manipulators + //@{ + + //! Get retry time + /*! + Returns the suggested time to wait until retrying to open the screen. + */ + double getRetryTime() const; + + //@} + +protected: + virtual CString getWhat() const throw(); + +private: + double m_timeUntilRetry; +}; + +#endif diff --git a/lib/synergy/XSynergy.cpp b/lib/synergy/XSynergy.cpp new file mode 100644 index 0000000..1e19945 --- /dev/null +++ b/lib/synergy/XSynergy.cpp @@ -0,0 +1,104 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XSynergy.h" +#include "CStringUtil.h" + +// +// XBadClient +// + +CString +XBadClient::getWhat() const throw() +{ + return "XBadClient"; +} + + +// +// XIncompatibleClient +// + +XIncompatibleClient::XIncompatibleClient(int major, int minor) : + m_major(major), + m_minor(minor) +{ + // do nothing +} + +int +XIncompatibleClient::getMajor() const throw() +{ + return m_major; +} + +int +XIncompatibleClient::getMinor() const throw() +{ + return m_minor; +} + +CString +XIncompatibleClient::getWhat() const throw() +{ + return format("XIncompatibleClient", "incompatible client %{1}.%{2}", + CStringUtil::print("%d", m_major).c_str(), + CStringUtil::print("%d", m_minor).c_str()); +} + + +// +// XDuplicateClient +// + +XDuplicateClient::XDuplicateClient(const CString& name) : + m_name(name) +{ + // do nothing +} + +const CString& +XDuplicateClient::getName() const throw() +{ + return m_name; +} + +CString +XDuplicateClient::getWhat() const throw() +{ + return format("XDuplicateClient", "duplicate client %{1}", m_name.c_str()); +} + + +// +// XUnknownClient +// + +XUnknownClient::XUnknownClient(const CString& name) : + m_name(name) +{ + // do nothing +} + +const CString& +XUnknownClient::getName() const throw() +{ + return m_name; +} + +CString +XUnknownClient::getWhat() const throw() +{ + return format("XUnknownClient", "unknown client %{1}", m_name.c_str()); +} diff --git a/lib/synergy/XSynergy.h b/lib/synergy/XSynergy.h new file mode 100644 index 0000000..2313119 --- /dev/null +++ b/lib/synergy/XSynergy.h @@ -0,0 +1,105 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XSYNERGY_H +#define XSYNERGY_H + +#include "XBase.h" + +//! Generic synergy exception +XBASE_SUBCLASS(XSynergy, XBase); + +//! Client error exception +/*! +Thrown when the client fails to follow the protocol. +*/ +XBASE_SUBCLASS_WHAT(XBadClient, XSynergy); + +//! Incompatible client exception +/*! +Thrown when a client attempting to connect has an incompatible version. +*/ +class XIncompatibleClient : public XSynergy { +public: + XIncompatibleClient(int major, int minor); + + //! @name accessors + //@{ + + //! Get client's major version number + int getMajor() const throw(); + //! Get client's minor version number + int getMinor() const throw(); + + //@} + +protected: + virtual CString getWhat() const throw(); + +private: + int m_major; + int m_minor; +}; + +//! Client already connected exception +/*! +Thrown when a client attempting to connect is using the same name as +a client that is already connected. +*/ +class XDuplicateClient : public XSynergy { +public: + XDuplicateClient(const CString& name); + + //! @name accessors + //@{ + + //! Get client's name + virtual const CString& + getName() const throw(); + + //@} + +protected: + virtual CString getWhat() const throw(); + +private: + CString m_name; +}; + +//! Client not in map exception +/*! +Thrown when a client attempting to connect is using a name that is +unknown to the server. +*/ +class XUnknownClient : public XSynergy { +public: + XUnknownClient(const CString& name); + + //! @name accessors + //@{ + + //! Get the client's name + virtual const CString& + getName() const throw(); + + //@} + +protected: + virtual CString getWhat() const throw(); + +private: + CString m_name; +}; + +#endif diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp new file mode 100644 index 0000000..08a3256 --- /dev/null +++ b/lib/synergy/libsynergy.dsp @@ -0,0 +1,222 @@ +# Microsoft Developer Studio Project File - Name="libsynergy" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=libsynergy - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libsynergy.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libsynergy.mak" CFG="libsynergy - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libsynergy - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libsynergy - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libsynergy - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "libsynergy - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "libsynergy - Win32 Release" +# Name "libsynergy - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\CInputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.cpp +# End Source File +# Begin Source File + +SOURCE=.\CSecondaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\XScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\XSynergy.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CInputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\ClipboardTypes.h +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.h +# End Source File +# Begin Source File + +SOURCE=.\CSecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IClient.h +# End Source File +# Begin Source File + +SOURCE=.\IClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreenFactory.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreenReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\IScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenEventHandler.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenSaver.h +# End Source File +# Begin Source File + +SOURCE=.\ISecondaryScreenFactory.h +# End Source File +# Begin Source File + +SOURCE=.\IServer.h +# End Source File +# Begin Source File + +SOURCE=.\KeyTypes.h +# End Source File +# Begin Source File + +SOURCE=.\MouseTypes.h +# End Source File +# Begin Source File + +SOURCE=.\OptionTypes.h +# End Source File +# Begin Source File + +SOURCE=.\ProtocolTypes.h +# End Source File +# Begin Source File + +SOURCE=.\XScreen.h +# End Source File +# Begin Source File + +SOURCE=.\XSynergy.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/stamp-h.in b/stamp-h.in new file mode 100644 index 0000000..e69de29 diff --git a/synergy.dsw b/synergy.dsw new file mode 100644 index 0000000..660e164 --- /dev/null +++ b/synergy.dsw @@ -0,0 +1,335 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "all"=".\all.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name launcher + End Project Dependency +}}} + +############################################################################### + +Project: "arch"=".\lib\arch\arch.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "base"=".\lib\base\base.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "client"=".\lib\client\client.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "common"=".\lib\common\common.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "exec"=".\cmd\exec.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name synergyc + End Project Dependency + Begin Project Dependency + Project_Dep_Name synergys + End Project Dependency +}}} + +############################################################################### + +Project: "http"=".\lib\HTTP\http.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "io"=".\lib\io\io.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "launcher"=".\cmd\launcher\launcher.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency + Begin Project Dependency + Project_Dep_Name base + End Project Dependency + Begin Project Dependency + Project_Dep_Name http + End Project Dependency + Begin Project Dependency + Project_Dep_Name io + End Project Dependency + Begin Project Dependency + Project_Dep_Name mt + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name platform + End Project Dependency + Begin Project Dependency + Project_Dep_Name server + End Project Dependency + Begin Project Dependency + Project_Dep_Name exec + End Project Dependency +}}} + +############################################################################### + +Project: "libsynergy"=".\lib\synergy\libsynergy.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "makehook"=".\lib\platform\makehook.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name synrgyhk + End Project Dependency +}}} + +############################################################################### + +Project: "mt"=".\lib\mt\mt.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "net"=".\lib\net\net.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "platform"=".\lib\platform\platform.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "server"=".\lib\server\server.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "synergyc"=".\cmd\synergyc\synergyc.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency + Begin Project Dependency + Project_Dep_Name base + End Project Dependency + Begin Project Dependency + Project_Dep_Name io + End Project Dependency + Begin Project Dependency + Project_Dep_Name mt + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name libsynergy + End Project Dependency + Begin Project Dependency + Project_Dep_Name platform + End Project Dependency + Begin Project Dependency + Project_Dep_Name client + End Project Dependency +}}} + +############################################################################### + +Project: "synergys"=".\cmd\synergys\synergys.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency + Begin Project Dependency + Project_Dep_Name base + End Project Dependency + Begin Project Dependency + Project_Dep_Name http + End Project Dependency + Begin Project Dependency + Project_Dep_Name io + End Project Dependency + Begin Project Dependency + Project_Dep_Name mt + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name libsynergy + End Project Dependency + Begin Project Dependency + Project_Dep_Name platform + End Project Dependency + Begin Project Dependency + Project_Dep_Name makehook + End Project Dependency + Begin Project Dependency + Project_Dep_Name server + End Project Dependency +}}} + +############################################################################### + +Project: "synrgyhk"=".\lib\platform\synrgyhk.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### +