From 3f6146b15f5f4f2943bac060ee0cfca08ef6acc4 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 19 Nov 2001 00:33:36 +0000 Subject: [PATCH] checkpoint. merging win32 code. server on X is currently broken and client probably is. --- Make-linux | 2 +- all.dsp | 63 ++ base/BasicTypes.h | 24 +- base/CLog.cpp | 94 ++- base/CLog.h | 8 +- base/XBase.cpp | 2 +- base/base.dsp | 150 +++++ base/common.h | 6 +- client/CClient.cpp | 150 +++-- client/CClient.h | 14 +- client/CMSWindowsSecondaryScreen.cpp | 839 +++++++++++++++++++++++++++ client/CMSWindowsSecondaryScreen.h | 43 ++ client/CXWindowsSecondaryScreen.cpp | 83 +-- client/CXWindowsSecondaryScreen.h | 3 +- client/Makefile | 6 +- client/client.cpp | 62 +- client/client.dsp | 127 ++++ client/client.rc | 97 ++++ client/resource.h | 17 + io/io.dsp | 150 +++++ mt/CCondVar.cpp | 2 +- mt/CThread.cpp | 20 - mt/CThreadRep.cpp | 21 +- mt/CThreadRep.h | 20 + mt/mt.dsp | 146 +++++ net/CNetwork.cpp | 339 +++++++++++ net/CNetwork.h | 143 +++++ net/CNetworkAddress.cpp | 24 +- net/CNetworkAddress.h | 8 +- net/CTCPListenSocket.cpp | 41 +- net/CTCPListenSocket.h | 3 +- net/CTCPSocket.cpp | 55 +- net/CTCPSocket.h | 5 +- net/Makefile | 3 +- net/XNetwork.cpp | 72 +++ net/XNetwork.h | 52 ++ net/net.dsp | 174 ++++++ notes | 20 + server/CMSWindowsPrimaryScreen.cpp | 509 ++++++++++++++++ server/CMSWindowsPrimaryScreen.h | 56 ++ server/CScreenMap.h | 2 + server/CServer.cpp | 94 ++- server/CServer.h | 15 +- server/CSynergyHook.cpp | 242 ++++++++ server/CSynergyHook.h | 37 ++ server/CXWindowsPrimaryScreen.cpp | 229 ++++---- server/CXWindowsPrimaryScreen.h | 3 +- server/Makefile | 6 +- server/makehook.dsp | 63 ++ server/resource.h | 17 + server/server.cpp | 67 ++- server/server.dsp | 155 +++++ server/server.rc | 97 ++++ server/synrgyhk.dsp | 111 ++++ synergy.dsw | 215 +++++++ synergy/CMSWindowsClipboard.cpp | 41 ++ synergy/CMSWindowsClipboard.h | 19 + synergy/CMSWindowsScreen.cpp | 248 ++++++++ synergy/CMSWindowsScreen.h | 73 +++ synergy/CXWindowsClipboard.h | 1 - synergy/CXWindowsScreen.cpp | 37 +- synergy/CXWindowsScreen.h | 11 +- synergy/IClipboard.h | 1 - synergy/IPrimaryScreen.h | 14 +- synergy/ISecondaryScreen.h | 9 + synergy/synergy.dsp | 186 ++++++ 66 files changed, 5222 insertions(+), 424 deletions(-) create mode 100644 all.dsp create mode 100644 base/base.dsp create mode 100644 client/CMSWindowsSecondaryScreen.cpp create mode 100644 client/CMSWindowsSecondaryScreen.h create mode 100644 client/client.dsp create mode 100644 client/client.rc create mode 100644 client/resource.h create mode 100644 io/io.dsp create mode 100644 mt/mt.dsp create mode 100644 net/CNetwork.cpp create mode 100644 net/CNetwork.h create mode 100644 net/XNetwork.cpp create mode 100644 net/XNetwork.h create mode 100644 net/net.dsp create mode 100644 server/CMSWindowsPrimaryScreen.cpp create mode 100644 server/CMSWindowsPrimaryScreen.h create mode 100644 server/CSynergyHook.cpp create mode 100644 server/CSynergyHook.h create mode 100644 server/makehook.dsp create mode 100644 server/resource.h create mode 100644 server/server.dsp create mode 100644 server/server.rc create mode 100644 server/synrgyhk.dsp create mode 100644 synergy.dsw create mode 100644 synergy/CMSWindowsClipboard.cpp create mode 100644 synergy/CMSWindowsClipboard.h create mode 100644 synergy/CMSWindowsScreen.cpp create mode 100644 synergy/CMSWindowsScreen.h create mode 100644 synergy/synergy.dsp diff --git a/Make-linux b/Make-linux index f5ce7843..033ca228 100644 --- a/Make-linux +++ b/Make-linux @@ -13,7 +13,7 @@ RMR = /bin/rm -rf # # compiler options # -GCXXDEFS = -D_XOPEN_SOURCE=500 +GCXXDEFS = -D_XOPEN_SOURCE=600 GCXXINCS = -I$(DEPTH)/include -I/usr/X11R6/include GCXXOPTS = -Wall -W -fexceptions CXXOPTIMIZER = -g diff --git a/all.dsp b/all.dsp new file mode 100644 index 00000000..902d872b --- /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 "millpond" +# 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 "all___Win32_Debug" +# PROP BASE Intermediate_Dir "all___Win32_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/base/BasicTypes.h b/base/BasicTypes.h index 1a88d3a0..b21eba8f 100644 --- a/base/BasicTypes.h +++ b/base/BasicTypes.h @@ -37,7 +37,29 @@ typedef uint64_t UInt64; #if defined(CONFIG_PLATFORM_WIN32) -// FIXME +// use VC++ extensions if available +#if defined(_MSC_VER) +typedef signed __int8 SInt8; +typedef signed __int16 SInt16; +typedef signed __int32 SInt32; +typedef signed __int64 SInt64; + +typedef unsigned __int8 UInt8; +typedef unsigned __int16 UInt16; +typedef unsigned __int32 UInt32; +typedef unsigned __int64 UInt64; +#else +typedef signed char SInt8; +typedef short SInt16; +typedef int SInt32; +typedef long long SInt64; + +typedef unsigned char UInt8; +typedef unsigned short UInt16; +typedef unsigned int UInt32; +typedef unsigned long long UInt64; + +#endif #endif // CONFIG_PLATFORM_WIN32 diff --git a/base/CLog.cpp b/base/CLog.cpp index cb98cbbd..f8fd3479 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -1,14 +1,37 @@ #include "CLog.h" +#include "BasicTypes.h" #include #include #include #include +#if defined(CONFIG_PLATFORM_WIN32) +#include +#define vsnprintf _vsnprintf +#endif + +static int g_maxPriority = -1; +static const char* g_priority[] = { + "FATAL", + "ERROR", + "WARNING", + "NOTE", + "INFO", + "DEBUG", + }; +static const int g_numPriority = (int)(sizeof(g_priority) / + sizeof(g_priority[0])); +static const int g_maxPriorityLength = 7; // length of longest string +static const int g_prioritySuffixLength = 2; +static const int g_priorityPad = g_maxPriorityLength + + g_prioritySuffixLength; +static const int g_newlineLength = 2; + // // CLog // -static int g_maxPriority = -1; +CLog::Outputter CLog::s_outputter = NULL; void CLog::print(const char* fmt, ...) { @@ -19,11 +42,14 @@ void CLog::print(const char* fmt, ...) fmt += 3; } + // compute prefix padding length + int pad = g_priorityPad; + // print to buffer char stack[1024]; va_list args; va_start(args, fmt); - char* buffer = vsprint(0, stack, + char* buffer = vsprint(pad, stack, sizeof(stack) / sizeof(stack[0]), fmt, args); va_end(args); @@ -48,17 +74,19 @@ void CLog::printt(const char* file, int line, // compute prefix padding length char stack[1024]; sprintf(stack, "%d", line); - int pad = strlen(file) + 1 + strlen(stack) + 1 + 1; + int pad = strlen(file) + 1 /* comma */ + + strlen(stack) + 1 /* colon */ + 1 /* space */ + + g_priorityPad; - // print to buffer + // print to buffer, leaving space for a newline at the end va_list args; va_start(args, fmt); char* buffer = vsprint(pad, stack, sizeof(stack) / sizeof(stack[0]), fmt, args); va_end(args); - // print the prefix to the buffer - sprintf(buffer, "%s,%d:", file, line); + // print the prefix to the buffer. leave space for priority label. + sprintf(buffer + g_priorityPad, "%s,%d:", file, line); buffer[pad - 1] = ' '; // output buffer @@ -69,35 +97,51 @@ void CLog::printt(const char* file, int line, delete[] buffer; } -void CLog::output(int priority, const char* msg) +void CLog::setOutputter(Outputter outputter) { - static const char* s_priority[] = { - "FATAL", - "ERROR", - "WARNING", - "NOTE", - "INFO", - "DEBUG", - }; - static const int s_numPriority = (int)(sizeof(s_priority) / - sizeof(s_priority[0])); - assert(priority >= 0 && priority < s_numPriority); + s_outputter = outputter; +} + +void CLog::output(int priority, char* msg) +{ + assert(priority >= 0 && priority < g_numPriority); assert(msg != 0); if (g_maxPriority == -1) { - g_maxPriority = s_numPriority - 1; + g_maxPriority = g_numPriority - 1; const char* priEnv = getenv("SYN_LOG_PRI"); if (priEnv != NULL) { - for (int i = 0; i < s_numPriority; ++i) - if (strcmp(priEnv, s_priority[i]) == 0) { + for (int i = 0; i < g_numPriority; ++i) + if (strcmp(priEnv, g_priority[i]) == 0) { g_maxPriority = i; break; } } } - if (priority <= g_maxPriority) - fprintf(stderr, "%s: %s\n", s_priority[priority], msg); + if (priority <= g_maxPriority) { + // insert priority label + int n = strlen(g_priority[priority]); + sprintf(msg + g_maxPriorityLength - n, "%s:", g_priority[priority]); + msg[g_maxPriorityLength + 1] = ' '; + + // put a newline at the end +#if defined(CONFIG_PLATFORM_WIN32) + strcat(msg + g_priorityPad, "\r\n"); +#else + strcat(msg + g_priorityPad, "\n"); +#endif + + // print it + if (s_outputter) + s_outputter(msg + g_maxPriorityLength - n); + else +#if defined(CONFIG_PLATFORM_WIN32) + OutputDebugString(msg + g_maxPriorityLength - n); +#else + fprintf(stderr, "%s", msg + g_maxPriorityLength - n); +#endif + } } char* CLog::vsprint(int pad, char* buffer, int len, @@ -109,7 +153,7 @@ char* CLog::vsprint(int pad, char* buffer, int len, int n; if (len >= pad) { n = vsnprintf(buffer + pad, len - pad, fmt, args); - if (n != -1 && n <= len - pad) + if (n != -1 && n <= len - pad + g_newlineLength) return buffer; } @@ -120,7 +164,7 @@ char* CLog::vsprint(int pad, char* buffer, int len, len *= 2; buffer = new char[len + pad]; n = vsnprintf(buffer + pad, len - pad, fmt, args); - } while (n == -1 || n > len - pad); + } while (n == -1 || n > len - pad + g_newlineLength); return buffer; } diff --git a/base/CLog.h b/base/CLog.h index 3116142b..908072e9 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -5,13 +5,19 @@ class CLog { public: + typedef void (*Outputter)(const char*); + static void print(const char*, ...); static void printt(const char* file, int line, const char*, ...); + static void setOutputter(Outputter); private: - static void output(int priority, const char* msg); + static void output(int priority, char* msg); static char* vsprint(int pad, char*, int len, const char*, va_list); static int nprint(const char*, va_list); + + private: + static Outputter s_outputter; }; #if defined(NOLOGGING) diff --git a/base/XBase.cpp b/base/XBase.cpp index 6558d5e3..bc2db608 100644 --- a/base/XBase.cpp +++ b/base/XBase.cpp @@ -2,7 +2,7 @@ #include // win32 wants a const char* argument to std::exception c'tor -#if CONFIG_PLATFORM_WIN32 +#if defined(CONFIG_PLATFORM_WIN32) #define STDEXCEPTARG "" #endif diff --git a/base/base.dsp b/base/base.dsp new file mode 100644 index 00000000..c73c0e44 --- /dev/null +++ b/base/base.dsp @@ -0,0 +1,150 @@ +# 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 "millpond" +# 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 /W3 /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)" == "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 /W3 /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 "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=.\CLog.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStopwatch.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=.\BasicTypes.h +# End Source File +# Begin Source File + +SOURCE=.\CFunctionJob.h +# End Source File +# Begin Source File + +SOURCE=.\CLog.h +# End Source File +# Begin Source File + +SOURCE=.\common.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=.\IInterface.h +# End Source File +# Begin Source File + +SOURCE=.\IJob.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/base/common.h b/base/common.h index ca1224ca..e95b14ea 100644 --- a/base/common.h +++ b/base/common.h @@ -15,10 +15,14 @@ #define CONFIG_TYPES_X11 #define CONFIG_PTHREADS -#elif defined(_WINDOWS) && defined(WIN32) +#elif defined(_WIN32) #define CONFIG_PLATFORM_WIN32 +#if (_MSC_VER >= 1200) +#pragma warning(disable: 4786) // identifier truncated in debug info +#endif + #else #error unsupported platform diff --git a/client/CClient.cpp b/client/CClient.cpp index bb4579e7..33447115 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -9,8 +9,17 @@ #include "XSynergy.h" #include "TMethodJob.h" #include "CLog.h" +#include #include +// hack to work around operator=() bug in STL in g++ prior to v3 +#if defined(__GNUC__) && (__GNUC__ < 3) +#define assign(_dst, _src, _type) _dst.reset(_src) +#else +#define assign(_dst, _src, _type) _dst = std::auto_ptr<_type >(_src) +#endif + + // // CClient // @@ -31,13 +40,50 @@ CClient::~CClient() void CClient::run(const CNetworkAddress& serverAddress) { - m_serverAddress = &serverAddress; - CThread thread(new TMethodJob(this, &CClient::runSession)); - thread.wait(); + CThread* thread; + try { + log((CLOG_NOTE "starting client")); + + // connect to secondary screen + openSecondaryScreen(); + + // start server interactions + m_serverAddress = &serverAddress; + thread = new CThread(new TMethodJob(this, &CClient::runSession)); + + // handle events + log((CLOG_DEBUG "starting event handling")); + m_screen->run(); + + // clean up + log((CLOG_DEBUG "stopping client")); + thread->cancel(); + thread->wait(); + delete thread; + closeSecondaryScreen(); + } + catch (XBase& e) { + log((CLOG_ERR "client error: %s\n", e.what())); + + // clean up + thread->cancel(); + thread->wait(); + delete thread; + closeSecondaryScreen(); + } + catch (...) { + log((CLOG_DEBUG "unknown client error")); + + // clean up + thread->cancel(); + thread->wait(); + delete thread; + closeSecondaryScreen(); + throw; + } } -#include "CTCPSocket.h" -#include "CXWindowsSecondaryScreen.h" +#include "CTCPSocket.h" // FIXME void CClient::runSession(void*) { log((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); @@ -51,7 +97,7 @@ void CClient::runSession(void*) // create socket and attempt to connect to server log((CLOG_DEBUG "connecting to server")); - socket.reset(new CTCPSocket()); // FIXME -- use factory + assign(socket, new CTCPSocket(), ISocket); // FIXME -- use factory socket->connect(*m_serverAddress); log((CLOG_INFO "connected to server")); @@ -72,8 +118,8 @@ void CClient::runSession(void*) */ // attach the packetizing filters - input.reset(new CInputPacketStream(srcInput, own)); - output.reset(new COutputPacketStream(srcOutput, own)); + assign(input, new CInputPacketStream(srcInput, own), IInputStream); + assign(output, new COutputPacketStream(srcOutput, own), IOutputStream); // wait for hello from server log((CLOG_DEBUG "wait for hello")); @@ -99,26 +145,17 @@ void CClient::runSession(void*) } catch (XIncompatibleClient& e) { log((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); + m_screen->stop(); return; } catch (XThread&) { log((CLOG_ERR "connection timed out")); + m_screen->stop(); throw; } catch (XBase& e) { log((CLOG_ERR "connection failed: %s", e.what())); - return; - } - - // connect to screen - std::auto_ptr screenCleaner; - try { - log((CLOG_DEBUG "creating secondary screen")); - m_screen = new CXWindowsSecondaryScreen; - screenCleaner.reset(new CScreenCleaner(this, m_screen)); - } - catch (XBase& e) { - log((CLOG_ERR "cannot open screen: %s", e.what())); + m_screen->stop(); return; } @@ -199,16 +236,56 @@ void CClient::runSession(void*) } catch (XBase& e) { log((CLOG_ERR "error: %s", e.what())); + m_screen->stop(); return; } - // done with screen - log((CLOG_DEBUG "destroying secondary screen")); - screenCleaner.reset(); - // done with socket log((CLOG_DEBUG "disconnecting from server")); socket->close(); + + // exit event loop + m_screen->stop(); +} + +// FIXME -- use factory to create screen +#if defined(CONFIG_PLATFORM_WIN32) +#include "CMSWindowsSecondaryScreen.h" +#elif defined(CONFIG_PLATFORM_UNIX) +#include "CXWindowsSecondaryScreen.h" +#endif +void CClient::openSecondaryScreen() +{ + assert(m_screen == NULL); + + // open screen + log((CLOG_DEBUG "creating secondary screen")); +#if defined(CONFIG_PLATFORM_WIN32) + m_screen = new CMSWindowsSecondaryScreen; +#elif defined(CONFIG_PLATFORM_UNIX) + m_screen = new CXWindowsSecondaryScreen; +#endif + log((CLOG_DEBUG "opening secondary screen")); + m_screen->open(this); +} + +void CClient::closeSecondaryScreen() +{ + assert(m_screen != NULL); + + // close the secondary screen + try { + log((CLOG_DEBUG "closing secondary screen")); + m_screen->close(); + } + catch (...) { + // ignore + } + + // clean up + log((CLOG_DEBUG "destroying secondary screen")); + delete m_screen; + m_screen = NULL; } void CClient::onEnter() @@ -306,28 +383,3 @@ void CClient::onMouseWheel() CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); m_screen->mouseWheel(delta); } - - -// -// CClient::CScreenCleaner -// - -CClient::CScreenCleaner::CScreenCleaner(CClient* client, - ISecondaryScreen* screen) : - m_screen(screen) -{ - assert(m_screen != NULL); - try { - m_screen->open(client); - } - catch (...) { - delete m_screen; - throw; - } -} - -CClient::CScreenCleaner::~CScreenCleaner() -{ - m_screen->close(); - delete m_screen; -} diff --git a/client/CClient.h b/client/CClient.h index ddcd3a09..c332ae7c 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -24,6 +24,10 @@ class CClient { private: void runSession(void*); + // open/close the primary screen + void openSecondaryScreen(); + void closeSecondaryScreen(); + // message handlers void onEnter(); void onLeave(); @@ -40,16 +44,6 @@ class CClient { void onMouseMove(); void onMouseWheel(); - private: - class CScreenCleaner { - public: - CScreenCleaner(CClient*, ISecondaryScreen*); - ~CScreenCleaner(); - - private: - ISecondaryScreen* m_screen; - }; - private: CString m_name; IInputStream* m_input; diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp new file mode 100644 index 00000000..439b5876 --- /dev/null +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -0,0 +1,839 @@ +#include "CMSWindowsSecondaryScreen.h" +#include "CClient.h" +#include "CThread.h" +#include "CLog.h" +#include + +// +// CMSWindowsSecondaryScreen +// + +CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : + m_client(NULL), + m_window(NULL) +{ + // do nothing +} + +CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() +{ + assert(m_window == NULL); +} + +static HWND s_debug = NULL; +static HWND s_debugLog = NULL; +static DWORD s_thread = 0; +static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: + return TRUE; + + case WM_CLOSE: + PostQuitMessage(0); + return TRUE; + } + return FALSE; +} +static void debugOutput(const char* msg) +{ + if (s_thread != 0) { + const DWORD threadID = ::GetCurrentThreadId(); + if (threadID != s_thread) { + GetDesktopWindow(); + AttachThreadInput(threadID, s_thread, TRUE); + } + } + DWORD len = SendMessage(s_debugLog, WM_GETTEXTLENGTH, 0, 0); + if (len > 20000) { + SendMessage(s_debugLog, EM_SETSEL, -1, 0); + SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)msg); + } + else { + SendMessage(s_debugLog, EM_SETSEL, -1, len); + SendMessage(s_debugLog, EM_REPLACESEL, FALSE, (LPARAM)(LPCTSTR)msg); + } + SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0); +} + +void CMSWindowsSecondaryScreen::run() +{ +CLog::setOutputter(&debugOutput); + doRun(); +CLog::setOutputter(NULL); +} + +void CMSWindowsSecondaryScreen::stop() +{ + doStop(); +} + +void CMSWindowsSecondaryScreen::open(CClient* client) +{ + assert(m_client == NULL); + assert(client != NULL); + + // set the client + m_client = client; + + // open the display + openDisplay(); +} + +void CMSWindowsSecondaryScreen::close() +{ + assert(m_client != NULL); + + // close the display + closeDisplay(); + + // done with client + m_client = NULL; +} + +void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) +{ + assert(m_window != NULL); + + // warp to requested location + SInt32 w, h; + getScreenSize(&w, &h); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65535.99 * x) / (w - 1)), + (DWORD)((65535.99 * y) / (h - 1)), + 0, 0); + + // show cursor + ShowWindow(m_window, SW_HIDE); +} + +void CMSWindowsSecondaryScreen::leave() +{ + assert(m_window != NULL); + + // move hider window under the mouse (rather than moving the mouse + // somewhere else on the screen) + POINT point; + GetCursorPos(&point); + MoveWindow(m_window, point.x, point.y, 1, 1, FALSE); + + // raise and show the hider window. take activation. + ShowWindow(m_window, SW_SHOWNORMAL); + + // hide cursor by moving it into the hider window + SetCursorPos(point.x, point.y); +} + +void CMSWindowsSecondaryScreen::keyDown( + KeyID key, KeyModifierMask mask) +{ + const UINT vkey = mapKey(key, mask); + if (vkey != 0) { + const UINT code = MapVirtualKey(vkey, 0); + keybd_event(vkey, code, 0, 0); + } +} + +void CMSWindowsSecondaryScreen::keyRepeat( + KeyID key, KeyModifierMask mask, SInt32 count) +{ + const UINT vkey = mapKey(key, mask); + if (vkey != 0) { + const UINT code = MapVirtualKey(vkey, 0); + for (SInt32 i = 0; i < count; ++i) { + keybd_event(vkey, code, KEYEVENTF_KEYUP, 0); + keybd_event(vkey, code, 0, 0); + } + } +} + +void CMSWindowsSecondaryScreen::keyUp( + KeyID key, KeyModifierMask mask) +{ + const UINT vkey = mapKey(key, mask); + if (vkey != 0) { + const UINT code = MapVirtualKey(vkey, 0); + keybd_event(vkey, code, KEYEVENTF_KEYUP, 0); + } +} + +void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) +{ + // map button id to button flag + DWORD flags; + switch (button) { + case kButtonLeft: + flags = MOUSEEVENTF_LEFTDOWN; + break; + + case kButtonMiddle: + flags = MOUSEEVENTF_MIDDLEDOWN; + break; + + case kButtonRight: + flags = MOUSEEVENTF_RIGHTDOWN; + break; + + default: + return; + } + + // send event + mouse_event(flags, 0, 0, 0, 0); +} + +void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) +{ + // map button id to button flag + DWORD flags; + switch (button) { + case kButtonLeft: + flags = MOUSEEVENTF_LEFTUP; + break; + + case kButtonMiddle: + flags = MOUSEEVENTF_MIDDLEUP; + break; + + case kButtonRight: + flags = MOUSEEVENTF_RIGHTUP; + break; + + default: + return; + } + + // send event + mouse_event(flags, 0, 0, 0, 0); +} + +void CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) +{ + SInt32 w, h; + getScreenSize(&w, &h); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (SInt32)(65535.99 * x / (w - 1)), + (SInt32)(65535.99 * y / (h - 1)), + 0, 0); +} + +void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) +{ + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); +} + +void CMSWindowsSecondaryScreen::getSize( + SInt32* width, SInt32* height) const +{ + getScreenSize(width, height); +} + +SInt32 CMSWindowsSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + +#include "resource.h" // FIXME + +void CMSWindowsSecondaryScreen::onOpenDisplay() +{ + assert(m_window == NULL); + +// create debug dialog +s_thread = GetCurrentThreadId();; +s_debug = CreateDialog(getInstance(), MAKEINTRESOURCE(IDD_SYNERGY), NULL, &debugProc); +s_debugLog = ::GetDlgItem(s_debug, IDC_LOG); +CLog::setOutputter(&debugOutput); +ShowWindow(s_debug, SW_SHOWNORMAL); + + // create the cursor hiding window. 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. + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, + (LPCTSTR)getClass(), "Synergy", + WS_POPUP | WS_DISABLED, + 0, 0, 1, 1, NULL, NULL, + getInstance(), + NULL); + + // hide the cursor + leave(); +} + +void CMSWindowsSecondaryScreen::onCloseDisplay() +{ + assert(m_window != NULL); + + // destroy window + DestroyWindow(m_window); + m_window = NULL; + +CLog::setOutputter(NULL); +DestroyWindow(s_debug); +s_debug = NULL; +s_thread = 0; +} + +bool CMSWindowsSecondaryScreen::onEvent(MSG* msg) +{ +if (IsDialogMessage(s_debug, msg)) { + return true; +} + + // handle event + switch (msg->message) { + // FIXME -- handle display changes + case WM_PAINT: + ValidateRect(m_window, NULL); + return true; + + case WM_MOUSEMOVE: + // mouse was moved. hide the hider window. + ShowWindow(m_window, SW_HIDE); + break; + + case WM_ACTIVATEAPP: + if (msg->wParam == FALSE) { + // some other app activated. hide the hider window. + ShowWindow(m_window, SW_HIDE); + } + break; + +/* + // FIXME -- handle screen resolution changes + + case SelectionClear: + target->XXX(xevent.xselectionclear.); + break; + + case SelectionNotify: + target->XXX(xevent.xselection.); + break; + + case SelectionRequest: + target->XXX(xevent.xselectionrequest.); + break; +*/ + } + + return false; +} + +static const UINT g_latin1[] = +{ + /* 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, 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_latin2[] = +{ + /* 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, 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_latin3[] = +{ + /* 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, 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_latin4[] = +{ + /* 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, 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_latin5[] = +{ + /* 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, 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_latin6[] = +{ + /* 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, 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_latin7[] = +{ + /* 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, 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_latin8[] = +{ + /* 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, 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_latin9[] = +{ + /* 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, 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_terminal[] = +{ + /* 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, 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_function[] = +{ + /* 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, 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 +}; +// FIXME -- will this work? +// 0x100 + = shift +// 0x200 + = ctrl +// 0x400 + = alt +/* XK_KP_Space to XK_KP_Equal */ +static const UINT g_miscellany[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ VK_BACK, VK_TAB, /*0x100 +*/ VK_RETURN, 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, VK_LEFT, VK_UP, VK_RIGHT, + /* 0x54 */ VK_DOWN, VK_PRIOR, VK_NEXT, VK_END, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT, 0, 0, 0, VK_APPS, + /* 0x68 */ 0, 0, VK_HELP, VK_CANCEL, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, VK_MODECHANGE, VK_NUMLOCK, + /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN, 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, + /* 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, VK_CAPITAL, 0, VK_LWIN, + /* 0xe8 */ VK_RWIN, VK_LMENU, VK_RMENU, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE +}; +static const UINT* g_katakana = NULL; +static const UINT* g_arabic = NULL; +static const UINT* g_cyrillic = NULL; +static const UINT* g_greek = NULL; +static const UINT* g_technical = NULL; +static const UINT* g_special = NULL; +static const UINT* g_publishing = NULL; +static const UINT* g_apl = NULL; +static const UINT* g_hebrew = NULL; +static const UINT* g_thai = NULL; +static const UINT* g_korean = NULL; +static const UINT* g_armenian = NULL; +static const UINT* g_georgian = NULL; +static const UINT* g_azeri = NULL; +static const UINT* g_vietnamese = NULL; +static const UINT* g_currency = NULL; +static const UINT* g_mapTable[] = +{ + /* 0x00 */ g_latin1, g_latin2, g_latin3, g_latin4, + /* 0x04 */ g_katakana, g_arabic, g_cyrillic, g_greek, + /* 0x08 */ g_technical, g_special, g_publishing, g_apl, + /* 0x0c */ g_hebrew, g_thai, g_korean, NULL, + /* 0x10 */ NULL, NULL, g_latin8, g_latin9, + /* 0x14 */ g_armenian, g_georgian, g_azeri, NULL, + /* 0x18 */ NULL, NULL, NULL, NULL, NULL, NULL, g_vietnamese, NULL, + /* 0x20 */ g_currency, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x28 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x30 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x38 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x40 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x48 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x50 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x58 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x60 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x68 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x70 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x78 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x80 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x88 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x90 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x98 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xa0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xa8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xb0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xb8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xc0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xc8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xd0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xd8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xe0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xe8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xf0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xf8 */ NULL, NULL, NULL, NULL, + /* 0xfc */ NULL, g_terminal, g_function, g_miscellany +}; + +UINT CMSWindowsSecondaryScreen::mapKey( + KeyID id, KeyModifierMask /*mask*/) const +{ + const UInt32 mapID = ((id >> 8) & 0xff); + const UInt32 code = (id & 0xff); + + // lookup the key table + const UINT* map = g_mapTable[mapID]; + if (map == NULL) { + return 0; + } + + if (mapID == 0) { + SHORT scan = VkKeyScan(code); + if (scan != 0xffff) { + // FIXME -- must ensure shift state is correct. that means + // tracking the shift state from the moment we enter until + // the moment we leave (and probably disallowing leave if + // any shift keys are down). if current shift state is + // correct then do nothing extra, otherwise must surround + // injected key event with injected shift key events to + // get shift key in correct state then back to the previous + // state. + return (UINT)LOBYTE(scan); + } + } + + // lookup the key in the table + return map[code]; +} diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h new file mode 100644 index 00000000..4362d400 --- /dev/null +++ b/client/CMSWindowsSecondaryScreen.h @@ -0,0 +1,43 @@ +#ifndef CMSWINDOWSSECONDARYSCREEN_H +#define CMSWINDOWSSECONDARYSCREEN_H + +#include "CMSWindowsScreen.h" +#include "ISecondaryScreen.h" + +class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScreen { + public: + CMSWindowsSecondaryScreen(); + virtual ~CMSWindowsSecondaryScreen(); + + // ISecondaryScreen overrides + virtual void run(); + virtual void stop(); + virtual void open(CClient*); + virtual void close(); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void leave(); + virtual void keyDown(KeyID, KeyModifierMask); + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void keyUp(KeyID, KeyModifierMask); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void mouseWheel(SInt32 delta); + virtual void getSize(SInt32* width, SInt32* height) const; + virtual SInt32 getJumpZoneSize() const; + + protected: + // CMSWindowsScreen overrides + virtual bool onEvent(MSG*); + virtual void onOpenDisplay(); + virtual void onCloseDisplay(); + + private: + UINT mapKey(KeyID, KeyModifierMask) const; + + private: + CClient* m_client; + HWND m_window; +}; + +#endif diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 853855a6..d4b99bd9 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -22,6 +22,51 @@ CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() assert(m_window == None); } +void CXWindowsSecondaryScreen::run() +{ + assert(m_window != None); + + for (;;) { + // wait for and get the next event + XEvent xevent; + if (!getEvent(&xevent)) { + break; + } + + // handle event + switch (xevent.type) { + case LeaveNotify: { + // mouse moved out of hider window somehow. hide the window. + assert(m_window != None); + CDisplayLock display(this); + XUnmapWindow(display, m_window); + break; + } + +/* + // FIXME -- handle screen resolution changes + + case SelectionClear: + target->XXX(xevent.xselectionclear.); + break; + + case SelectionNotify: + target->XXX(xevent.xselection.); + break; + + case SelectionRequest: + target->XXX(xevent.xselectionrequest.); + break; +*/ + } + } +} + +void CXWindowsSecondaryScreen::stop() +{ + doStop(); +} + void CXWindowsSecondaryScreen::open(CClient* client) { assert(m_client == NULL); @@ -174,44 +219,6 @@ void CXWindowsSecondaryScreen::onCloseDisplay() m_window = None; } -void CXWindowsSecondaryScreen::eventThread(void*) -{ - assert(m_window != None); - - for (;;) { - // wait for and get the next event - XEvent xevent; - getEvent(&xevent); - - // handle event - switch (xevent.type) { - case LeaveNotify: { - // mouse moved out of hider window somehow. hide the window. - assert(m_window != None); - CDisplayLock display(this); - XUnmapWindow(display, m_window); - break; - } - -/* - // FIXME -- handle screen resolution changes - - case SelectionClear: - target->XXX(xevent.xselectionclear.); - break; - - case SelectionNotify: - target->XXX(xevent.xselection.); - break; - - case SelectionRequest: - target->XXX(xevent.xselectionrequest.); - break; -*/ - } - } -} - void CXWindowsSecondaryScreen::leaveNoLock(Display* display) { assert(display != NULL); diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index ef180537..2ee6989b 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -10,6 +10,8 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen virtual ~CXWindowsSecondaryScreen(); // ISecondaryScreen overrides + virtual void run(); + virtual void stop(); virtual void open(CClient*); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); @@ -28,7 +30,6 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen // CXWindowsScreen overrides virtual void onOpenDisplay(); virtual void onCloseDisplay(); - virtual void eventThread(void*); private: void leaveNoLock(Display*); diff --git a/client/Makefile b/client/Makefile index 6c0b49cd..e7a6573a 100644 --- a/client/Makefile +++ b/client/Makefile @@ -4,7 +4,7 @@ include $(DEPTH)/Makecommon # # target file # -TARGET = client +TARGETS = client # # source files @@ -37,8 +37,8 @@ LLDLIBS = \ -lpthread \ $(NULL) -targets: $(TARGET) +targets: $(TARGETS) -$(TARGET): $(OBJECTS) $(DEPLIBS) +$(TARGETS): $(OBJECTS) $(DEPLIBS) $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) diff --git a/client/client.cpp b/client/client.cpp index 4ada77d9..abca2c8f 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -1,24 +1,76 @@ #include "CClient.h" +#include "CString.h" +#include "CNetwork.h" #include "CNetworkAddress.h" #include "CThread.h" + +void realMain(const CString& name, + const CString& hostname, + SInt32 port) +{ + CThread::init(); + CNetwork::init(); + + CClient* client = NULL; + try { + CNetworkAddress addr(hostname, port); + client = new CClient(name); + client->run(addr); + delete client; + CNetwork::cleanup(); + } + catch (...) { + delete client; + CNetwork::cleanup(); + throw; + } +} + +#if defined(CONFIG_PLATFORM_WIN32) + +#include "CMSWindowsScreen.h" + +int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) +{ + CMSWindowsScreen::init(instance); + + if (__argc != 2) { + CString msg = "hostname required. exiting."; + MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); + return 1; + } + + try { + realMain("ingrid", __argv[1], 50001); + return 0; + } + catch (XBase& e) { + CString msg = "failed: "; + msg += e.what(); + MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); + return 1; + } +} + +#else + #include int main(int argc, char** argv) { - CThread::init(); - if (argc != 2) { fprintf(stderr, "usage: %s \n", argv[0]); return 1; } try { - CClient* client = new CClient("ingrid"); - client->run(CNetworkAddress(argv[1], 50001)); + realMain("ingrid", argv[1], 50001); + return 0; } catch (XBase& e) { fprintf(stderr, "failed: %s\n", e.what()); return 1; } - return 0; } + +#endif diff --git a/client/client.dsp b/client/client.dsp new file mode 100644 index 00000000..905b486a --- /dev/null +++ b/client/client.dsp @@ -0,0 +1,127 @@ +# 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) Application" 0x0101 + +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) Application") +!MESSAGE "client - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.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 "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /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)" == "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 "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /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 "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=.\client.cpp +# End Source File +# Begin Source File + +SOURCE=.\client.rc +# 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=.\CClient.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.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/client/client.rc b/client/client.rc new file mode 100644 index 00000000..60c3a920 --- /dev/null +++ b/client/client.rc @@ -0,0 +1,97 @@ +//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_SYNERGY DIALOG DISCARDABLE 0, 0, 329, 158 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Synergy" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_LOG,7,7,315,144,ES_MULTILINE | ES_AUTOHSCROLL | + ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_SYNERGY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 322 + TOPMARGIN, 7 + BOTTOMMARGIN, 151 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/client/resource.h b/client/resource.h new file mode 100644 index 00000000..993c9edc --- /dev/null +++ b/client/resource.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by client.rc +// +#define IDD_SYNERGY 101 +#define IDC_LOG 1000 + +// 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/io/io.dsp b/io/io.dsp new file mode 100644 index 00000000..b06989a9 --- /dev/null +++ b/io/io.dsp @@ -0,0 +1,150 @@ +# 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 "millpond" +# 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 /W3 /GX /O2 /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 /W3 /Gm /GX /ZI /Od /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=.\XIO.h +# End Source File +# End Group +# End Target +# End Project diff --git a/mt/CCondVar.cpp b/mt/CCondVar.cpp index f6072058..7e39e7f4 100644 --- a/mt/CCondVar.cpp +++ b/mt/CCondVar.cpp @@ -238,7 +238,7 @@ bool CCondVarBase::wait( } // prepare to wait - CRefCountedPtr currentRep(CThreadRep::getCurrentThreadRep()); + CThreadPtr currentRep = CThreadRep::getCurrentThreadRep(); const DWORD winTimeout = (timeout < 0.0) ? INFINITE : static_cast(1000.0 * timeout); HANDLE* events = reinterpret_cast(m_cond); diff --git a/mt/CThread.cpp b/mt/CThread.cpp index 6e40060b..c9a07fa4 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -5,26 +5,6 @@ #include "CStopwatch.h" #include "CLog.h" -// -// CThreadPtr -// - -class CThreadPtr { - public: - CThreadPtr(CThreadRep* rep) : m_rep(rep) { } - ~CThreadPtr() { m_rep->unref(); } - - CThreadRep* operator->() const { return m_rep; } - - private: - // not implemented - CThreadPtr(const CThreadPtr&); - CThreadPtr& operator=(const CThreadPtr&); - - private: - CThreadRep* m_rep; -}; - // // CThread // diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index dcba9e9f..313b33b4 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -12,10 +12,17 @@ #define SIGWAKEUP SIGUSR1 #endif +#if defined(CONFIG_PLATFORM_WIN32) +# if !defined(_MT) +# error multithreading compile option is required +# endif +#include +#endif + // FIXME -- temporary exception type class XThreadUnavailable { }; -#ifndef NDEBUG +#if defined(CONFIG_PLATFORM_UNIX) && !defined(NDEBUG) #include #include #include @@ -146,19 +153,19 @@ void CThreadRep::initThreads() act.sa_handler = &threadDebug; sigaction(SIGSEGV, &act, NULL); # endif -#endif // set signal mask sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGWAKEUP); -#ifndef NDEBUG +# ifndef NDEBUG sigaddset(&sigset, SIGSEGV); -#endif +# endif pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); sigemptyset(&sigset); sigaddset(&sigset, SIGPIPE); pthread_sigmask(SIG_BLOCK, &sigset, NULL); +#endif } } @@ -255,7 +262,7 @@ void CThreadRep::doThreadFunc() m_job->run(); } - catch (XThreadCancel& e) { + catch (XThreadCancel&) { // client called cancel() log((CLOG_DEBUG "caught cancel on thread %p", this)); } @@ -421,8 +428,6 @@ void CThreadRep::threadCancel(int) #elif defined(CONFIG_PLATFORM_WIN32) -#include - void CThreadRep::init() { m_result = NULL; @@ -485,7 +490,7 @@ bool CThreadRep::wait(CThreadRep* target, double timeout) { // get the current thread. if it's the same as the target thread // then the thread is waiting on itself. - CRefCountedPtr currentRep(CThreadRep::getCurrentThreadRep()); + CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); if (target == this) return false; diff --git a/mt/CThreadRep.h b/mt/CThreadRep.h index 3cfce4b9..3a14ba09 100644 --- a/mt/CThreadRep.h +++ b/mt/CThreadRep.h @@ -122,4 +122,24 @@ class CThreadRep { #endif }; +// +// CThreadPtr -- auto unref'ing pointer to thread rep +// + +class CThreadPtr { + public: + CThreadPtr(CThreadRep* rep) : m_rep(rep) { } + ~CThreadPtr() { m_rep->unref(); } + + CThreadRep* operator->() const { return m_rep; } + + private: + // not implemented + CThreadPtr(const CThreadPtr&); + CThreadPtr& operator=(const CThreadPtr&); + + private: + CThreadRep* m_rep; +}; + #endif diff --git a/mt/mt.dsp b/mt/mt.dsp new file mode 100644 index 00000000..8067b7d7 --- /dev/null +++ b/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 "millpond" +# 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 /W3 /GX /O2 /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 /W3 /Gm /GX /ZI /Od /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=.\CThreadRep.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.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=.\CThreadRep.h +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.h +# End Source File +# Begin Source File + +SOURCE=.\XThread.h +# End Source File +# End Group +# End Target +# End Project diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp new file mode 100644 index 00000000..4eac18f5 --- /dev/null +++ b/net/CNetwork.cpp @@ -0,0 +1,339 @@ +#include "CNetwork.h" +#include "XNetwork.h" +#include "CLog.h" +#include + +// +// CNetwork +// + +CNetwork::Socket (PASCAL FAR *CNetwork::accept)(CNetwork::Socket s, CNetwork::Address FAR *addr, CNetwork::AddressLength FAR *addrlen); +int (PASCAL FAR *CNetwork::bind)(CNetwork::Socket s, const CNetwork::Address FAR *addr, CNetwork::AddressLength namelen); +int (PASCAL FAR *CNetwork::close)(CNetwork::Socket s); +int (PASCAL FAR *CNetwork::connect)(CNetwork::Socket s, const CNetwork::Address FAR *name, CNetwork::AddressLength namelen); +int (PASCAL FAR *CNetwork::ioctl)(CNetwork::Socket s, int cmd, ...); +int (PASCAL FAR *CNetwork::getpeername)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); +int (PASCAL FAR *CNetwork::getsockname)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); +int (PASCAL FAR *CNetwork::getsockopt)(CNetwork::Socket s, int level, int optname, void FAR * optval, CNetwork::AddressLength FAR *optlen); +UInt32 (PASCAL FAR *CNetwork::swaphtonl)(UInt32 hostlong); +UInt16 (PASCAL FAR *CNetwork::swaphtons)(UInt16 hostshort); +unsigned long (PASCAL FAR *CNetwork::inet_addr)(const char FAR * cp); +char FAR * (PASCAL FAR *CNetwork::inet_ntoa)(struct in_addr in); +int (PASCAL FAR *CNetwork::listen)(CNetwork::Socket s, int backlog); +UInt32 (PASCAL FAR *CNetwork::swapntohl)(UInt32 netlong); +UInt16 (PASCAL FAR *CNetwork::swapntohs)(UInt16 netshort); +ssize_t (PASCAL FAR *CNetwork::read)(CNetwork::Socket s, void FAR * buf, size_t len); +ssize_t (PASCAL FAR *CNetwork::recv)(CNetwork::Socket s, void FAR * buf, size_t len, int flags); +ssize_t (PASCAL FAR *CNetwork::recvfrom)(CNetwork::Socket s, void FAR * buf, size_t len, int flags, CNetwork::Address FAR *from, CNetwork::AddressLength FAR * fromlen); +int (PASCAL FAR *CNetwork::poll)(CNetwork::PollEntry fds[], int nfds, int timeout); +ssize_t (PASCAL FAR *CNetwork::send)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags); +ssize_t (PASCAL FAR *CNetwork::sendto)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags, const CNetwork::Address FAR *to, CNetwork::AddressLength tolen); +int (PASCAL FAR *CNetwork::setsockopt)(CNetwork::Socket s, int level, int optname, const void FAR * optval, CNetwork::AddressLength optlen); +int (PASCAL FAR *CNetwork::shutdown)(CNetwork::Socket s, int how); +CNetwork::Socket (PASCAL FAR *CNetwork::socket)(int af, int type, int protocol); +ssize_t (PASCAL FAR *CNetwork::write)(CNetwork::Socket s, const void FAR * buf, size_t len); +struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyaddr)(const char FAR * addr, int len, int type); +struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyname)(const char FAR * name); +int (PASCAL FAR *CNetwork::gethostname)(char FAR * name, int namelen); +struct servent FAR * (PASCAL FAR *CNetwork::getservbyport)(int port, const char FAR * proto); +struct servent FAR * (PASCAL FAR *CNetwork::getservbyname)(const char FAR * name, const char FAR * proto); +struct protoent FAR * (PASCAL FAR *CNetwork::getprotobynumber)(int proto); +struct protoent FAR * (PASCAL FAR *CNetwork::getprotobyname)(const char FAR * name); +int (PASCAL FAR *CNetwork::getsockerror)(void); +int (PASCAL FAR *CNetwork::gethosterror)(void); + +#if defined(CONFIG_PLATFORM_WIN32) + +int (PASCAL FAR *CNetwork::WSACleanup)(void); +int (PASCAL FAR *CNetwork::__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); +const int CNetwork::Error = SOCKET_ERROR; +const CNetwork::Socket CNetwork::Null = INVALID_SOCKET; + +#undef FD_ISSET +#define FD_ISSET(fd, set) CNetwork::__WSAFDIsSet((SOCKET)(fd), (fd_set FAR *)(set)) + +static HMODULE s_networkModule = NULL; + +static FARPROC netGetProcAddress(HMODULE module, LPCSTR name) +{ + FARPROC func = ::GetProcAddress(module, name); + if (!func) + throw XNetworkFunctionUnavailable(name); + return func; +} + +void CNetwork::init() +{ + assert(WSACleanup == NULL); + assert(s_networkModule == NULL); + + // try winsock 2 + HMODULE module = (HMODULE)::LoadLibrary("ws2_32.dll"); + if (module == NULL) { + log((CLOG_DEBUG "ws2_32.dll not found")); + } + else { + try { + init2(module); + return; + } + catch (XNetwork& e) { + log((CLOG_DEBUG "ws2_32.dll error: %s", e.what())); + } + } + + // try winsock 1 + module = (HMODULE)::LoadLibrary("wsock32.dll"); + if (module == NULL) { + log((CLOG_DEBUG "wsock32.dll not found")); + } + else { + try { + init2(module); + return; + } + catch (XNetwork& e) { + log((CLOG_DEBUG "wsock32.dll error: %s", e.what())); + } + } + + // no networking + throw XNetworkUnavailable(); +} + +void CNetwork::cleanup() +{ + if (s_networkModule != NULL) { + WSACleanup(); + ::FreeLibrary(s_networkModule); + + WSACleanup = NULL; + s_networkModule = NULL; + } +} + +#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name) + +void CNetwork::init2(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 XNetworkVersion(LOBYTE(data.wVersion), HIBYTE(data.wVersion)); + if (err != 0) + throw XNetworkFailed(); + + // get function addresses + setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); + setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); + setfunc(close, closesocket, int (PASCAL FAR *)(Socket s)); + setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); + setfunc(ioctl, ioctlsocket, int (PASCAL FAR *)(Socket s, int cmd, ...)); + setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); + setfunc(swaphtonl, htonl, UInt32 (PASCAL FAR *)(UInt32 hostlong)); + setfunc(swaphtons, htons, UInt16 (PASCAL FAR *)(UInt16 hostshort)); + setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); + setfunc(swapntohl, ntohl, UInt32 (PASCAL FAR *)(UInt32 netlong)); + setfunc(swapntohs, ntohs, UInt16 (PASCAL FAR *)(UInt16 netshort)); + setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); + setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); + setfunc(send, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); + setfunc(sendto, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); + setfunc(setsockopt, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); + setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); + setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(gethostbyaddr, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(gethostname, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); + setfunc(getservbyport, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); + setfunc(getservbyname, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); + setfunc(getprotobynumber, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); + setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(getsockerror, WSAGetLastError, int (PASCAL FAR *)(void)); + setfunc(gethosterror, WSAGetLastError, int (PASCAL FAR *)(void)); + setfunc(WSACleanup, WSACleanup, int (PASCAL FAR *)(void)); + setfunc(__WSAFDIsSet, __WSAFDIsSet, int (PASCAL FAR *)(CNetwork::Socket, fd_set FAR *)); + setfunc(select, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); + poll = poll2; + read = read2; + write = write2; + + s_networkModule = module; +} + +int PASCAL FAR CNetwork::poll2(PollEntry fd[], int nfds, int timeout) +{ + int i; + + // prepare sets for select + 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 < nfds; ++i) { + if (fd[i].events & kPOLLIN) { + FD_SET(fd[i].fd, &readSet); + readSetP = &readSet; + } + if (fd[i].events & kPOLLOUT) { + FD_SET(fd[i].fd, &writeSet); + writeSetP = &writeSet; + } + if (true) { + FD_SET(fd[i].fd, &errSet); + errSetP = &errSet; + } + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = timeout / 1000; + timeout2.tv_usec = 1000 * (timeout % 1000); + } + + // do the select. note that winsock ignores the first argument. + int n = select(0, readSetP, writeSetP, errSetP, timeout2P); + + // handle results + if (n == Error) + return Error; + if (n == 0) + return 0; + for (i = 0; i < nfds; ++i) { + fd[i].revents = 0; + if (FD_ISSET(fd[i].fd, &readSet)) + fd[i].revents |= kPOLLIN; + if (FD_ISSET(fd[i].fd, &writeSet)) + fd[i].revents |= kPOLLOUT; + if (FD_ISSET(fd[i].fd, &errSet)) + fd[i].revents |= kPOLLERR; + } + return n; +} + +ssize_t PASCAL FAR CNetwork::read2(Socket s, void FAR * buf, size_t len) +{ + return recv(s, buf, len, 0); +} + +ssize_t PASCAL FAR CNetwork::write2(Socket s, + const void FAR * buf, size_t len) +{ + return send(s, buf, len, 0); +} + +#endif + +#if defined(CONFIG_PLATFORM_UNIX) + +#include +#include +#include + +// FIXME -- use reentrant versions of non-reentrant functions + +#define setfunc(var, name, type) var = (type)::name + +static UInt32 myhtonl(UInt32 v) +{ + return htonl(v); +} + +static UInt16 myhtons(UInt16 v) +{ + return htons(v); +} + +static UInt32 myntohl(UInt32 v) +{ + return ntohl(v); +} + +static UInt16 myntohs(UInt16 v) +{ + return ntohs(v); +} + +static int myerrno() +{ + return errno; +} + +static int myherrno() +{ + return h_errno; +} + +static int mygethostname(char* name, int namelen) +{ + return gethostname(name, namelen); +} + +const int CNetwork::Error = -1; +const CNetwork::Socket CNetwork::Null = -1; + +void CNetwork::init() +{ + setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); + setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); + setfunc(close, close, int (PASCAL FAR *)(Socket s)); + setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); + setfunc(ioctl, ioctl, int (PASCAL FAR *)(Socket s, int cmd, ...)); + setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); + setfunc(swaphtonl, myhtonl, UInt32 (PASCAL FAR *)(UInt32 hostlong)); + setfunc(swaphtons, myhtons, UInt16 (PASCAL FAR *)(UInt16 hostshort)); + setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); + setfunc(swapntohl, myntohl, UInt32 (PASCAL FAR *)(UInt32 netlong)); + setfunc(swapntohs, myntohs, UInt16 (PASCAL FAR *)(UInt16 netshort)); + setfunc(poll, poll, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); + setfunc(read, read, ssize_t (PASCAL FAR *)(CNetwork::Socket s, void FAR * buf, size_t len)); + setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); + setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); + setfunc(send, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); + setfunc(sendto, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); + setfunc(setsockopt, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); + setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); + setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(write, write, ssize_t (PASCAL FAR *)(CNetwork::Socket s, const void FAR * buf, size_t len)); + setfunc(gethostbyaddr, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(gethostname, mygethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); + setfunc(getservbyport, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); + setfunc(getservbyname, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); + setfunc(getprotobynumber, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); + setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(getsockerror, myerrno, int (PASCAL FAR *)(void)); + setfunc(gethosterror, myherrno, int (PASCAL FAR *)(void)); +} + +void CNetwork::cleanup() +{ + // do nothing +} + +#endif diff --git a/net/CNetwork.h b/net/CNetwork.h new file mode 100644 index 00000000..fdbb2ae2 --- /dev/null +++ b/net/CNetwork.h @@ -0,0 +1,143 @@ +#ifndef CNETWORK_H +#define CNETWORK_H + +#include "BasicTypes.h" + +#if defined(CONFIG_PLATFORM_WIN32) +// declare no functions in winsock2 +# define INCL_WINSOCK_API_PROTOTYPES 0 +# define INCL_WINSOCK_API_TYPEDEFS 0 +# include +typedef int ssize_t; +#else +# define FAR +# define PASCAL +#endif + +#if defined(CONFIG_PLATFORM_UNIX) +# include +# include +# include +# include +# include +# include +#endif + +// FIXME -- must handle htonl and ilk when defined as macros + +class CNetwork { + public: +#if defined(CONFIG_PLATFORM_WIN32) + typedef SOCKET Socket; + typedef struct sockaddr Address; + typedef int AddressLength; + struct PollEntry { + Socket fd; + short events; + short revents; + }; + enum { + kPOLLIN = 1, + kPOLLOUT = 2, + kPOLLERR = 4, + kPOLLNVAL = 8 + }; +#elif defined(CONFIG_PLATFORM_UNIX) + typedef int Socket; + typedef struct sockaddr Address; + typedef socklen_t AddressLength; + typedef struct pollfd PollEntry; + enum { + kPOLLIN = POLLIN, + kPOLLOUT = POLLOUT, + kPOLLERR = POLLERR, + kPOLLNVAL = POLLNVAL + }; +#endif + + // manipulators + + static void init(); + static void cleanup(); + + // constants + + static const int Error; + static const Socket Null; + + // getsockerror() constants + enum { +#if defined(CONFIG_PLATFORM_WIN32) + kEADDRINUSE = WSAEADDRINUSE, +#elif defined(CONFIG_PLATFORM_UNIX) + kEADDRINUSE = EADDRINUSE, +#endif + kNone = 0 + }; + + // gethosterror() constants + enum { +#if defined(CONFIG_PLATFORM_WIN32) + kHOST_NOT_FOUND = WSAHOST_NOT_FOUND, + kNO_DATA = WSANO_DATA, + kNO_RECOVERY = WSANO_RECOVERY, + kTRY_AGAIN = WSATRY_AGAIN, +#elif defined(CONFIG_PLATFORM_UNIX) + kHOST_NOT_FOUND = HOST_NOT_FOUND, + kNO_DATA = NO_DATA, + kNO_RECOVERY = NO_RECOVERY, + kTRY_AGAIN = TRY_AGAIN, +#endif + kHNone = 0 + }; + + // socket interface + + static Socket (PASCAL FAR *accept)(Socket s, Address FAR *addr, AddressLength FAR *addrlen); + static int (PASCAL FAR *bind)(Socket s, const Address FAR *addr, AddressLength namelen); + static int (PASCAL FAR *close)(Socket s); + static int (PASCAL FAR *connect)(Socket s, const Address FAR *name, AddressLength namelen); + static int (PASCAL FAR *ioctl)(Socket s, int cmd, ...); + static int (PASCAL FAR *getpeername)(Socket s, Address FAR *name, AddressLength FAR * namelen); + static int (PASCAL FAR *getsockname)(Socket s, Address FAR *name, AddressLength FAR * namelen); + static int (PASCAL FAR *getsockopt)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen); + static UInt32 (PASCAL FAR *swaphtonl)(UInt32 hostlong); + static UInt16 (PASCAL FAR *swaphtons)(UInt16 hostshort); + static unsigned long (PASCAL FAR *inet_addr)(const char FAR * cp); + static char FAR * (PASCAL FAR *inet_ntoa)(struct in_addr in); + static int (PASCAL FAR *listen)(Socket s, int backlog); + static UInt32 (PASCAL FAR *swapntohl)(UInt32 netlong); + static UInt16 (PASCAL FAR *swapntohs)(UInt16 netshort); + static ssize_t (PASCAL FAR *read)(Socket s, void FAR * buf, size_t len); + static ssize_t (PASCAL FAR *recv)(Socket s, void FAR * buf, size_t len, int flags); + static ssize_t (PASCAL FAR *recvfrom)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen); + static int (PASCAL FAR *poll)(PollEntry[], int nfds, int timeout); + static ssize_t (PASCAL FAR *send)(Socket s, const void FAR * buf, size_t len, int flags); + static ssize_t (PASCAL FAR *sendto)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen); + static int (PASCAL FAR *setsockopt)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen); + static int (PASCAL FAR *shutdown)(Socket s, int how); + static Socket (PASCAL FAR *socket)(int af, int type, int protocol); + static ssize_t (PASCAL FAR *write)(Socket s, const void FAR * buf, size_t len); + static struct hostent FAR * (PASCAL FAR *gethostbyaddr)(const char FAR * addr, int len, int type); + static struct hostent FAR * (PASCAL FAR *gethostbyname)(const char FAR * name); + static int (PASCAL FAR *gethostname)(char FAR * name, int namelen); + static struct servent FAR * (PASCAL FAR *getservbyport)(int port, const char FAR * proto); + static struct servent FAR * (PASCAL FAR *getservbyname)(const char FAR * name, const char FAR * proto); + static struct protoent FAR * (PASCAL FAR *getprotobynumber)(int proto); + static struct protoent FAR * (PASCAL FAR *getprotobyname)(const char FAR * name); + static int (PASCAL FAR *getsockerror)(void); + static int (PASCAL FAR *gethosterror)(void); + +#if defined(CONFIG_PLATFORM_WIN32) + private: + static void init2(HMODULE); + static int PASCAL FAR poll2(PollEntry[], int nfds, int timeout); + static ssize_t PASCAL FAR read2(Socket s, void FAR * buf, size_t len); + static ssize_t PASCAL FAR write2(Socket s, const void FAR * buf, size_t len); + static int (PASCAL FAR *WSACleanup)(void); + static int (PASCAL FAR *__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); + static int (PASCAL FAR *select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); +#endif +}; + +#endif diff --git a/net/CNetworkAddress.cpp b/net/CNetworkAddress.cpp index 84c7f3ec..a07d5ac7 100644 --- a/net/CNetworkAddress.cpp +++ b/net/CNetworkAddress.cpp @@ -1,8 +1,4 @@ #include "CNetworkAddress.h" -#include -#include -#include -#include // // CNetworkAddress @@ -15,7 +11,7 @@ CNetworkAddress::CNetworkAddress(UInt16 port) struct sockaddr_in* inetAddress = reinterpret_cast(&m_address); inetAddress->sin_family = AF_INET; - inetAddress->sin_port = htons(port); + inetAddress->sin_port = CNetwork::swaphtons(port); inetAddress->sin_addr.s_addr = INADDR_ANY; memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } @@ -25,17 +21,17 @@ CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port) if (port == 0) throw XSocketAddress(XSocketAddress::kBadPort, hostname, port); - struct hostent* hent = gethostbyname(hostname.c_str()); + struct hostent* hent = CNetwork::gethostbyname(hostname.c_str()); if (hent == NULL) { - switch (h_errno) { - case HOST_NOT_FOUND: + switch (CNetwork::gethosterror()) { + case CNetwork::kHOST_NOT_FOUND: throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); - case NO_DATA: + case CNetwork::kNO_DATA: throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); - case NO_RECOVERY: - case TRY_AGAIN: + case CNetwork::kNO_RECOVERY: + case CNetwork::kTRY_AGAIN: default: throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); } @@ -43,7 +39,7 @@ CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port) struct sockaddr_in* inetAddress = reinterpret_cast(&m_address); inetAddress->sin_family = hent->h_addrtype; - inetAddress->sin_port = htons(port); + inetAddress->sin_port = CNetwork::swaphtons(port); memcpy(&inetAddress->sin_addr, hent->h_addr_list[0], hent->h_length); memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } @@ -53,12 +49,12 @@ CNetworkAddress::~CNetworkAddress() // do nothing } -const struct sockaddr* CNetworkAddress::getAddress() const +const CNetwork::Address* CNetworkAddress::getAddress() const { return &m_address; } -int CNetworkAddress::getAddressLength() const +CNetwork::AddressLength CNetworkAddress::getAddressLength() const { return sizeof(m_address); } diff --git a/net/CNetworkAddress.h b/net/CNetworkAddress.h index 95e377ce..18d69812 100644 --- a/net/CNetworkAddress.h +++ b/net/CNetworkAddress.h @@ -2,8 +2,8 @@ #define CNETWORKADDRESS_H #include "BasicTypes.h" +#include "CNetwork.h" #include "XSocket.h" -#include class CString; @@ -17,11 +17,11 @@ class CNetworkAddress { // accessors - const struct sockaddr* getAddress() const; - int getAddressLength() const; + const CNetwork::Address* getAddress() const; + CNetwork::AddressLength getAddressLength() const; private: - struct sockaddr m_address; + CNetwork::Address m_address; }; #endif diff --git a/net/CTCPListenSocket.cpp b/net/CTCPListenSocket.cpp index d7fdaa93..cb95baf7 100644 --- a/net/CTCPListenSocket.cpp +++ b/net/CTCPListenSocket.cpp @@ -2,11 +2,6 @@ #include "CTCPSocket.h" #include "CNetworkAddress.h" #include "CThread.h" -#include -#include -#include -#include -#include // // CTCPListenSocket @@ -14,8 +9,8 @@ CTCPListenSocket::CTCPListenSocket() { - m_fd = socket(PF_INET, SOCK_STREAM, 0); - if (m_fd == -1) { + m_fd = CNetwork::socket(PF_INET, SOCK_STREAM, 0); + if (m_fd == CNetwork::Null) { throw XSocketCreate(); } } @@ -33,40 +28,44 @@ CTCPListenSocket::~CTCPListenSocket() void CTCPListenSocket::bind( const CNetworkAddress& addr) { - if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { - if (errno == EADDRINUSE) { + if (CNetwork::bind(m_fd, addr.getAddress(), + addr.getAddressLength()) == CNetwork::Error) { + if (CNetwork::getsockerror() == CNetwork::kEADDRINUSE) { throw XSocketAddressInUse(); } throw XSocketBind(); } - if (listen(m_fd, 3) == -1) { + if (CNetwork::listen(m_fd, 3) == CNetwork::Error) { throw XSocketBind(); } } ISocket* CTCPListenSocket::accept() { + CNetwork::PollEntry pfds[1]; + pfds[0].fd = m_fd; + pfds[0].events = CNetwork::kPOLLIN; for (;;) { - struct sockaddr addr; - socklen_t addrlen = sizeof(addr); CThread::testCancel(); - int fd = ::accept(m_fd, &addr, &addrlen); - if (fd == -1) { - CThread::testCancel(); - } - else { - return new CTCPSocket(fd); + const int status = CNetwork::poll(pfds, 1, 50); + if (status > 0 && (pfds[0].revents & CNetwork::kPOLLIN) != 0) { + CNetwork::Address addr; + CNetwork::AddressLength addrlen = sizeof(addr); + int fd = CNetwork::accept(m_fd, &addr, &addrlen); + if (fd != CNetwork::Null) { + return new CTCPSocket(fd); + } } } } void CTCPListenSocket::close() { - if (m_fd == -1) { + if (m_fd == CNetwork::Null) { throw XIOClosed(); } - if (::close(m_fd) == -1) { + if (CNetwork::close(m_fd) == CNetwork::Error) { throw XIOClose(); } - m_fd = -1; + m_fd = CNetwork::Null; } diff --git a/net/CTCPListenSocket.h b/net/CTCPListenSocket.h index 30310b52..20031d17 100644 --- a/net/CTCPListenSocket.h +++ b/net/CTCPListenSocket.h @@ -2,6 +2,7 @@ #define CTCPLISTENSOCKET_H #include "IListenSocket.h" +#include "CNetwork.h" class CTCPListenSocket : public IListenSocket { public: @@ -18,7 +19,7 @@ class CTCPListenSocket : public IListenSocket { virtual void close(); private: - int m_fd; + CNetwork::Socket m_fd; }; #endif diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index 39defc3d..ce32c846 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -8,12 +8,6 @@ #include "CThread.h" #include "TMethodJob.h" #include "CStopwatch.h" -#include -#include -#include -#include -#include -#include #include // @@ -22,16 +16,16 @@ CTCPSocket::CTCPSocket() { - m_fd = socket(PF_INET, SOCK_STREAM, 0); - if (m_fd == -1) { + m_fd = CNetwork::socket(PF_INET, SOCK_STREAM, 0); + if (m_fd == CNetwork::Null) { throw XSocketCreate(); } init(); } -CTCPSocket::CTCPSocket(int fd) : m_fd(fd) +CTCPSocket::CTCPSocket(CNetwork::Socket fd) : m_fd(fd) { - assert(m_fd != -1); + assert(m_fd != CNetwork::Null); init(); @@ -60,8 +54,9 @@ CTCPSocket::~CTCPSocket() void CTCPSocket::bind(const CNetworkAddress& addr) { - if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { - if (errno == EADDRINUSE) { + if (CNetwork::bind(m_fd, addr.getAddress(), + addr.getAddressLength()) == CNetwork::Error) { + if (errno == CNetwork::kEADDRINUSE) { throw XSocketAddressInUse(); } throw XSocketBind(); @@ -71,7 +66,8 @@ void CTCPSocket::bind(const CNetworkAddress& addr) void CTCPSocket::connect(const CNetworkAddress& addr) { CThread::testCancel(); - if (::connect(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { + if (CNetwork::connect(m_fd, addr.getAddress(), + addr.getAddressLength()) == CNetwork::Error) { CThread::testCancel(); throw XSocketConnect(); } @@ -103,11 +99,11 @@ void CTCPSocket::close() } CLock lock(m_mutex); - if (m_fd != -1) { - if (::close(m_fd) == -1) { + if (m_fd != CNetwork::Null) { + if (CNetwork::close(m_fd) == CNetwork::Error) { throw XIOClose(); } - m_fd = -1; + m_fd = CNetwork::Null; } } @@ -150,10 +146,10 @@ void CTCPSocket::ioThread(void*) void CTCPSocket::ioService() { - assert(m_fd != -1); + assert(m_fd != CNetwork::Null); // now service the connection - struct pollfd pfds[1]; + CNetwork::PollEntry pfds[1]; pfds[0].fd = m_fd; for (;;) { { @@ -162,31 +158,32 @@ void CTCPSocket::ioService() pfds[0].events = 0; if ((m_connected & kRead) != 0) { // still open for reading - pfds[0].events |= POLLIN; + pfds[0].events |= CNetwork::kPOLLIN; } if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) { // data queued for writing - pfds[0].events |= POLLOUT; + pfds[0].events |= CNetwork::kPOLLOUT; } } // check for status CThread::testCancel(); - const int status = poll(pfds, 1, 50); + const int status = CNetwork::poll(pfds, 1, 50); CThread::testCancel(); // transfer data and handle errors if (status == 1) { - if ((pfds[0].revents & (POLLERR | POLLNVAL)) != 0) { + if ((pfds[0].revents & (CNetwork::kPOLLERR | + CNetwork::kPOLLNVAL)) != 0) { // stream is no good anymore so bail m_input->hangup(); return; } // read some data - if (pfds[0].revents & POLLIN) { + if (pfds[0].revents & CNetwork::kPOLLIN) { UInt8 buffer[4096]; - ssize_t n = read(m_fd, buffer, sizeof(buffer)); + ssize_t n = CNetwork::read(m_fd, buffer, sizeof(buffer)); if (n > 0) { CLock lock(m_mutex); m_input->write(buffer, n); @@ -199,7 +196,7 @@ void CTCPSocket::ioService() } // write some data - if (pfds[0].revents & POLLOUT) { + if (pfds[0].revents & CNetwork::kPOLLOUT) { CLock lock(m_mutex); // get amount of data to write @@ -211,13 +208,13 @@ void CTCPSocket::ioService() // write data const void* buffer = m_output->peek(n); - n = (UInt32)write(m_fd, buffer, n); + n = (UInt32)CNetwork::write(m_fd, buffer, n); // discard written data if (n > 0) { m_output->pop(n); } - else if (n == (UInt32)-1 && errno == EPIPE) { + else if (n == (UInt32)-1 && CNetwork::getsockerror() == EPIPE) { return; } } @@ -228,13 +225,13 @@ void CTCPSocket::ioService() void CTCPSocket::closeInput(void*) { // note -- m_mutex should already be locked - shutdown(m_fd, 0); + CNetwork::shutdown(m_fd, 0); m_connected &= ~kRead; } void CTCPSocket::closeOutput(void*) { // note -- m_mutex should already be locked - shutdown(m_fd, 1); + CNetwork::shutdown(m_fd, 1); m_connected &= ~kWrite; } diff --git a/net/CTCPSocket.h b/net/CTCPSocket.h index c7b9ca73..56da71b4 100644 --- a/net/CTCPSocket.h +++ b/net/CTCPSocket.h @@ -2,6 +2,7 @@ #define CTCPSOCKET_H #include "ISocket.h" +#include "CNetwork.h" #include "XThread.h" class CMutex; @@ -14,7 +15,7 @@ class CBufferedOutputStream; class CTCPSocket : public ISocket { public: CTCPSocket(); - CTCPSocket(int fd); + CTCPSocket(CNetwork::Socket); ~CTCPSocket(); // manipulators @@ -38,7 +39,7 @@ class CTCPSocket : public ISocket { private: enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 }; - int m_fd; + CNetwork::Socket m_fd; CBufferedInputStream* m_input; CBufferedOutputStream* m_output; diff --git a/net/Makefile b/net/Makefile index 5b9534f1..38110334 100644 --- a/net/Makefile +++ b/net/Makefile @@ -15,10 +15,11 @@ LCXXINCS = \ -I$(DEPTH)/io \ $(NULL) CXXFILES = \ - XSocket.cpp \ + CNetwork.cpp \ CNetworkAddress.cpp \ CTCPSocket.cpp \ CTCPListenSocket.cpp \ + XSocket.cpp \ $(NULL) targets: $(LIBTARGET) diff --git a/net/XNetwork.cpp b/net/XNetwork.cpp new file mode 100644 index 00000000..a745fc6a --- /dev/null +++ b/net/XNetwork.cpp @@ -0,0 +1,72 @@ +#include "XNetwork.h" + +// +// XNetworkUnavailable +// + +CString XNetworkUnavailable::getWhat() const throw() +{ + return format("XNetworkUnavailable", "network library is not available"); +} + + +// +// XNetworkFailed +// + +CString XNetworkFailed::getWhat() const throw() +{ + return format("XNetworkFailed", "cannot initialize network library"); +} + + +// +// XNetworkVersion +// + +XNetworkVersion::XNetworkVersion(int major, int minor) throw() : + m_major(major), + m_minor(minor) +{ + // do nothing +} + +int XNetworkVersion::getMajor() const throw() +{ + return m_major; +} + +int XNetworkVersion::getMinor() const throw() +{ + return m_minor; +} + +CString XNetworkVersion::getWhat() const throw() +{ + return format("XNetworkVersion", + "unsupported network version %d.%d", + m_major, m_minor); +} + + +// +// XNetworkFunctionUnavailable +// + +XNetworkFunctionUnavailable::XNetworkFunctionUnavailable( + const char* name) throw() +{ + try { + m_name = name; + } + catch (...) { + // ignore + } +} + +CString XNetworkFunctionUnavailable::getWhat() const throw() +{ + return format("XNetworkFunctionUnavailable", + "missing network function %s", + m_name.c_str()); +} diff --git a/net/XNetwork.h b/net/XNetwork.h new file mode 100644 index 00000000..21e91574 --- /dev/null +++ b/net/XNetwork.h @@ -0,0 +1,52 @@ +#ifndef XNETWORK_H +#define XNETWORK_H + +#include "CString.h" +#include "XBase.h" +#include "BasicTypes.h" + +class XNetwork : public XBase { }; + +class XNetworkUnavailable : public XNetwork { + protected: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +class XNetworkFailed : public XNetwork { + protected: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +class XNetworkVersion : public XNetwork { + public: + XNetworkVersion(int major, int minor) throw(); + + // accessors + + int getMajor() const throw(); + int getMinor() const throw(); + + protected: + // XBase overrides + virtual CString getWhat() const throw(); + + private: + int m_major; + int m_minor; +}; + +class XNetworkFunctionUnavailable : public XNetwork { + public: + XNetworkFunctionUnavailable(const char* name) throw(); + + protected: + // XBase overrides + virtual CString getWhat() const throw(); + + private: + CString m_name; +}; + +#endif diff --git a/net/net.dsp b/net/net.dsp new file mode 100644 index 00000000..58d0ee13 --- /dev/null +++ b/net/net.dsp @@ -0,0 +1,174 @@ +# 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 "millpond" +# 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 /W3 /GX /O2 /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 /W3 /Gm /GX /ZI /Od /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=.\CNetwork.cpp +# End Source File +# Begin Source File + +SOURCE=.\CNetworkAddress.cpp +# End Source File +# Begin Source File + +SOURCE=.\CSocketInputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CSocketOutputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CSocketStreamBuffer.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=.\XNetwork.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=.\CNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\CNetworkAddress.h +# End Source File +# Begin Source File + +SOURCE=.\CSocketInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CSocketOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CSocketStreamBuffer.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=.\IListenSocket.h +# End Source File +# Begin Source File + +SOURCE=.\ISocket.h +# End Source File +# Begin Source File + +SOURCE=.\XNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\XSocket.h +# End Source File +# End Group +# End Target +# End Project diff --git a/notes b/notes index b6340818..14edcc1b 100644 --- a/notes +++ b/notes @@ -78,3 +78,23 @@ server: asynchronously handle primary screen events comm send/recv messages to/from clients + +--- +win32: + double click support + need to support window stations + login screen on NT is a separate window station + handle display changes + +--- +win32 key translation (client) + get character for key (with appropriate shift) + keypad enter -> VK_??? + sys req -> VK_??? + compose -> VK_??? + +X11 key translation (server) + handle compose key? + * don't send compose, don't send dead keys, send composed key? + * send all keys, let client do compose + diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp new file mode 100644 index 00000000..1580de71 --- /dev/null +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -0,0 +1,509 @@ +#include "CMSWindowsPrimaryScreen.h" +#include "CServer.h" +#include "CSynergyHook.h" +#include "CThread.h" +#include "CLog.h" +#include + +// +// CMSWindowsPrimaryScreen +// + +CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() : + m_server(NULL), + m_active(false), + m_window(NULL), + m_hookLibrary(NULL), + m_mark(0), + m_markReceived(0) +{ + // do nothing +} + +CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() +{ + assert(m_window == NULL); + assert(m_hookLibrary == NULL); +} + +static HWND s_debug = NULL; +static HWND s_debugLog = NULL; +static DWORD s_thread = 0; +static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: + return TRUE; + + case WM_CLOSE: + PostQuitMessage(0); + return TRUE; + } + return FALSE; +} +static void debugOutput(const char* msg) +{ + if (s_thread != 0) { + const DWORD threadID = ::GetCurrentThreadId(); + if (threadID != s_thread) { + GetDesktopWindow(); + AttachThreadInput(threadID, s_thread, TRUE); + } + } + DWORD len = SendMessage(s_debugLog, WM_GETTEXTLENGTH, 0, 0); + if (len > 20000) { + SendMessage(s_debugLog, EM_SETSEL, -1, 0); + SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)msg); + } + else { + SendMessage(s_debugLog, EM_SETSEL, -1, len); + SendMessage(s_debugLog, EM_REPLACESEL, FALSE, (LPARAM)(LPCTSTR)msg); + } + SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0); +} + +void CMSWindowsPrimaryScreen::run() +{ +CLog::setOutputter(&debugOutput); + doRun(); +CLog::setOutputter(NULL); +} + +void CMSWindowsPrimaryScreen::stop() +{ + doStop(); +} + +void CMSWindowsPrimaryScreen::open(CServer* server) +{ + assert(m_server == NULL); + assert(server != NULL); + + // set the server + m_server = server; + + // open the display + openDisplay(); + + // enter the screen + doEnter(); +} + +void CMSWindowsPrimaryScreen::close() +{ + assert(m_server != NULL); + + // close the display + closeDisplay(); + + // done with server + m_server = NULL; +} + +void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) +{ + log((CLOG_INFO "entering primary at %d,%d", x, y)); + assert(m_active == true); + + // do non-warp enter stuff + doEnter(); + + // warp to requested location + warpCursor(x, y); +} + +void CMSWindowsPrimaryScreen::doEnter() +{ + // set the zones that should cause a jump + SInt32 w, h; + getScreenSize(&w, &h); + SetZoneFunc setZone = (SetZoneFunc)GetProcAddress( + m_hookLibrary, "setZone"); + setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize()); + + // all messages prior to now are invalid + nextMark(); + + // not active anymore + m_active = false; +} + +void CMSWindowsPrimaryScreen::leave() +{ + log((CLOG_INFO "leaving primary")); + assert(m_active == false); + + // all messages prior to now are invalid + nextMark(); + + // relay all mouse and keyboard events + SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress( + m_hookLibrary, "setRelay"); + setRelay(); + + // warp the mouse to the center of the screen + SInt32 w, h; + getScreenSize(&w, &h); + warpCursor(w >> 1, h >> 1); + + // warp is also invalid + nextMark(); + + // local client now active + m_active = true; +} + +void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + SInt32 w, h; + getScreenSize(&w, &h); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65535.99 * x) / (w - 1)), + (DWORD)((65535.99 * y) / (h - 1)), + 0, 0); +} + +void CMSWindowsPrimaryScreen::setClipboard( + const IClipboard* /*clipboard*/) +{ + assert(m_window != NULL); + + // FIXME -- should we retry until we get it? + if (!OpenClipboard(m_window)) + return; + if (EmptyClipboard()) { + log((CLOG_DEBUG "grabbed clipboard")); + // FIXME -- set the clipboard data + } + CloseClipboard(); +} + +void CMSWindowsPrimaryScreen::getSize( + SInt32* width, SInt32* height) const +{ + getScreenSize(width, height); +} + +SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + +void CMSWindowsPrimaryScreen::getClipboard( + IClipboard* clipboard) const +{ + // FIXME -- put this in superclass? + // FIXME -- don't use CurrentTime +// getDisplayClipboard(clipboard, m_window, CurrentTime); +} + +#include "resource.h" // FIXME + +void CMSWindowsPrimaryScreen::onOpenDisplay() +{ + assert(m_window == NULL); + assert(m_server != NULL); + +// create debug dialog +s_thread = GetCurrentThreadId();; +s_debug = CreateDialog(getInstance(), MAKEINTRESOURCE(IDD_SYNERGY), NULL, &debugProc); +s_debugLog = ::GetDlgItem(s_debug, IDC_LOG); +CLog::setOutputter(&debugOutput); +ShowWindow(s_debug, SW_SHOWNORMAL); + + // create the window + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, + (LPCTSTR)getClass(), "Synergy", + WS_POPUP, + 0, 0, 1, 1, NULL, NULL, + getInstance(), + NULL); + + // load the hook library + bool hooked = false; + m_hookLibrary = LoadLibrary("synrgyhk"); + if (m_hookLibrary != NULL) { + // install input hooks + InstallFunc install = (InstallFunc)GetProcAddress( + m_hookLibrary, "install"); + if (install != NULL) { + hooked = (install(m_window) != 0); + } + } + if (!hooked) { + DestroyWindow(m_window); + m_window = NULL; + // FIXME -- throw + } + + // initialize marks + m_mark = 0; + m_markReceived = 0; + nextMark(); +} + +void CMSWindowsPrimaryScreen::onCloseDisplay() +{ + assert(m_window != NULL); + + // uninstall input hooks + UninstallFunc uninstall = (UninstallFunc)GetProcAddress( + m_hookLibrary, "uninstall"); + if (uninstall != NULL) { + uninstall(); + } + + // done with hook library + FreeLibrary(m_hookLibrary); + m_hookLibrary = NULL; + + // destroy window + DestroyWindow(m_window); + m_window = NULL; + +CLog::setOutputter(NULL); +DestroyWindow(s_debug); +s_debug = NULL; +s_thread = 0; +} + +bool CMSWindowsPrimaryScreen::onEvent(MSG* msg) +{ +if (IsDialogMessage(s_debug, msg)) { + return true; +} + + // handle event + switch (msg->message) { + // FIXME -- handle display changes + case WM_PAINT: + ValidateRect(m_window, NULL); + return true; + + case SYNERGY_MSG_MARK: + m_markReceived = msg->wParam; + return true; + + case SYNERGY_MSG_KEY: + // ignore if not at current mark + if (m_mark == m_markReceived) { + // FIXME -- vk code; key data + } + return true; + + case SYNERGY_MSG_MOUSE_BUTTON: + // ignore if not at current mark + if (m_mark == m_markReceived) { + const ButtonID button = mapButton(msg->wParam); + switch (msg->wParam) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + log((CLOG_DEBUG "event: button press button=%d", button)); + if (button != kButtonNone) { + m_server->onMouseDown(button); + } + break; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + log((CLOG_DEBUG "event: button release button=%d", button)); + if (button != kButtonNone) { + m_server->onMouseUp(button); + } + break; + } + } + return true; + + case SYNERGY_MSG_MOUSE_MOVE: + // ignore if not at current mark + if (m_mark == m_markReceived) { + SInt32 x = (SInt32)msg->wParam; + SInt32 y = (SInt32)msg->lParam; + if (!m_active) { + log((CLOG_DEBUG "event: inactive move %d,%d", x, y)); + m_server->onMouseMovePrimary(x, y); + } + else { + log((CLOG_DEBUG "event: active move %d,%d", x, y)); + + // get screen size + SInt32 w, h; + getScreenSize(&w, &h); + + // get center pixel + w >>= 1; + h >>= 1; + + // ignore and discard message if motion is to center of + // screen. those are caused by us warping the mouse. + if (x != w || y != h) { + // get mouse deltas + x -= w; + y -= h; + + // warp mouse back to center + warpCursor(w, h); + + // send motion + m_server->onMouseMoveSecondary(x, y); + } + } + } + return true; + } + + return false; +/* + case WM_MOUSEMOVE: { + if (!m_active) { + // mouse entered a jump zone window + POINT p; + p.x = (short)LOWORD(msg.lParam); + p.y = (short)HIWORD(msg.lParam); + ClientToScreen(msg.hwnd, &p); + log((CLOG_DEBUG "event: WM_MOUSEMOVE %d,%d", p.x, p.y)); + m_server->onMouseMovePrimary((SInt32)p.x, (SInt32)p.y); + } + break; + } + + case KeyPress: { + log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(xevent.xkey.keycode, mask); + if (key != kKeyNULL) { + m_server->onKeyDown(key, mask); + } + break; + } + + // FIXME -- simulate key repeat. X sends press/release for + // repeat. must detect auto repeat and use kKeyRepeat. + case KeyRelease: { + log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(xevent.xkey.keycode, mask); + if (key != kKeyNULL) { + m_server->onKeyUp(key, mask); + } + break; + } + + case ButtonPress: { + log((CLOG_DEBUG "event: ButtonPress button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNULL) { + m_server->onMouseDown(button); + } + break; + } + + case ButtonRelease: { + log((CLOG_DEBUG "event: ButtonRelease button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNULL) { + m_server->onMouseUp(button); + } + break; + } + + case SelectionClear: + target->XXX(xevent.xselectionclear.); + break; + + case SelectionNotify: + target->XXX(xevent.xselection.); + break; + + case SelectionRequest: + target->XXX(xevent.xselectionrequest.); + break; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +*/ +} + +void CMSWindowsPrimaryScreen::nextMark() +{ + assert(m_window != NULL); + + PostMessage(m_window, SYNERGY_MSG_MARK, ++m_mark, 0); +} + +#if 0 +bool CMSWindowsPrimaryScreen::keyboardHook( + int /*code*/, WPARAM wParam, LPARAM lParam) +{ + if (m_active) { + // handle keyboard events + const KeyID key = mapKey(wParam, lParam); + if (key != kKeyNone) { + const KeyModifierMask modifiers = mapModifier(wParam, lParam); + if ((lParam & KF_UP) == 0) { + log((CLOG_DEBUG "event: key press key=%d", key)); + m_server->onKeyDown(key, modifiers); + } + else { + log((CLOG_DEBUG "event: key release key=%d", key)); + m_server->onKeyUp(key, modifiers); + } + return true; + } + } + return false; +} +#endif + +KeyModifierMask CMSWindowsPrimaryScreen::mapModifier( + WPARAM keycode, LPARAM info) const +{ + // FIXME -- should be configurable + KeyModifierMask mask = 0; + if (GetKeyState(VK_SHIFT) < 0) + mask |= KeyModifierShift; + if ((GetKeyState(VK_CAPITAL) & 1) != 0) + mask |= KeyModifierCapsLock; + if (GetKeyState(VK_CONTROL) < 0) + mask |= KeyModifierControl; + if (GetKeyState(VK_MENU) < 0) + mask |= KeyModifierAlt; + if ((GetKeyState(VK_NUMLOCK) & 1) != 0) + mask |= KeyModifierNumLock; + if (GetKeyState(VK_LWIN) < 0 || GetKeyState(VK_RWIN) < 0) + mask |= KeyModifierMeta; + if ((GetKeyState(VK_SCROLL) & 1) != 0) + mask |= KeyModifierScrollLock; + return mask; +} + +KeyID CMSWindowsPrimaryScreen::mapKey( + WPARAM keycode, LPARAM info) const +{ + // FIXME -- must convert to X keysyms + return keycode; +} + +ButtonID CMSWindowsPrimaryScreen::mapButton( + WPARAM button) const +{ + switch (button) { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + return kButtonLeft; + + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + return kButtonMiddle; + + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + return kButtonRight; + + default: + return kButtonNone; + } +} diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h new file mode 100644 index 00000000..d34b44bf --- /dev/null +++ b/server/CMSWindowsPrimaryScreen.h @@ -0,0 +1,56 @@ +#ifndef CMSWINDOWSPRIMARYSCREEN_H +#define CMSWINDOWSPRIMARYSCREEN_H + +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CMSWindowsScreen.h" +#include "IPrimaryScreen.h" + +class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { + public: + typedef bool (CMSWindowsPrimaryScreen::*HookMethod)(int, WPARAM, LPARAM); + + CMSWindowsPrimaryScreen(); + virtual ~CMSWindowsPrimaryScreen(); + + // IPrimaryScreen overrides + virtual void run(); + virtual void stop(); + virtual void open(CServer*); + virtual void close(); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void leave(); + virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void setClipboard(const IClipboard*); + virtual void getSize(SInt32* width, SInt32* height) const; + virtual SInt32 getJumpZoneSize() const; + virtual void getClipboard(IClipboard*) const; + + protected: + // CMSWindowsScreen overrides + virtual bool onEvent(MSG*); + virtual void onOpenDisplay(); + virtual void onCloseDisplay(); + + private: + void doEnter(); + + void nextMark(); + +// bool keyboardHook(int, WPARAM, LPARAM); +// bool mouseHook(int, WPARAM, LPARAM); + + KeyModifierMask mapModifier(WPARAM keycode, LPARAM info) const; + KeyID mapKey(WPARAM keycode, LPARAM info) const; + ButtonID mapButton(WPARAM button) const; + + private: + CServer* m_server; + bool m_active; + HWND m_window; + HINSTANCE m_hookLibrary; + UInt32 m_mark; + UInt32 m_markReceived; +}; + +#endif diff --git a/server/CScreenMap.h b/server/CScreenMap.h index 80c571f6..e86a61d8 100644 --- a/server/CScreenMap.h +++ b/server/CScreenMap.h @@ -9,6 +9,8 @@ class CScreenMap { public: enum EDirection { kLeft, kRight, kTop, kBottom, kFirstDirection = kLeft, kLastDirection = kBottom }; + enum EDirectionMask { kLeftMask = 1, kRightMask = 2, + kTopMask = 4, kBottomMask = 8 }; CScreenMap(); virtual ~CScreenMap(); diff --git a/server/CServer.cpp b/server/CServer.cpp index c60d515c..42df94f8 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -20,6 +20,14 @@ #include #include +// hack to work around operator=() bug in STL in g++ prior to v3 +#if defined(__GNUC__) && (__GNUC__ < 3) +#define assign(_dst, _src, _type) _dst.reset(_src) +#else +#define assign(_dst, _src, _type) _dst = std::auto_ptr<_type >(_src) +#endif + + /* XXX #include #include @@ -33,8 +41,7 @@ else { wait(0); exit(1); } // CServer // -CServer::CServer() : m_done(&m_mutex, false), - m_primary(NULL), +CServer::CServer() : m_primary(NULL), m_active(NULL), m_primaryInfo(NULL) { @@ -61,31 +68,28 @@ void CServer::run() // start listening for configuration connections // FIXME - // wait until done - log((CLOG_DEBUG "waiting for quit")); - CLock lock(&m_mutex); - while (m_done == false) { - m_done.wait(); - } + // handle events + log((CLOG_DEBUG "starting event handling")); + m_primary->run(); // clean up log((CLOG_DEBUG "stopping server")); - closePrimaryScreen(); cleanupThreads(); + closePrimaryScreen(); } catch (XBase& e) { log((CLOG_ERR "server error: %s\n", e.what())); // clean up - closePrimaryScreen(); cleanupThreads(); + closePrimaryScreen(); } catch (...) { - log((CLOG_DEBUG "server shutdown")); + log((CLOG_DEBUG "unknown server error")); // clean up - closePrimaryScreen(); cleanupThreads(); + closePrimaryScreen(); throw; } } @@ -107,6 +111,21 @@ void CServer::getScreenMap(CScreenMap* screenMap) const *screenMap = m_screenMap; } +UInt32 CServer::getActivePrimarySides() const +{ + UInt32 sides = 0; + CLock lock(&m_mutex); + if (!m_screenMap.getNeighbor("primary", CScreenMap::kLeft).empty()) + sides |= CScreenMap::kLeftMask; + if (!m_screenMap.getNeighbor("primary", CScreenMap::kRight).empty()) + sides |= CScreenMap::kRightMask; + if (!m_screenMap.getNeighbor("primary", CScreenMap::kTop).empty()) + sides |= CScreenMap::kTopMask; + if (!m_screenMap.getNeighbor("primary", CScreenMap::kBottom).empty()) + sides |= CScreenMap::kBottomMask; + return sides; +} + void CServer::setInfo(const CString& client, SInt32 w, SInt32 h, SInt32 zoneSize) { @@ -206,7 +225,7 @@ void CServer::onMouseUp(ButtonID id) } } -void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) +bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) { log((CLOG_DEBUG "onMouseMovePrimary %d,%d", x, y)); @@ -216,7 +235,7 @@ void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) // ignore if mouse is locked to screen if (isLockedToScreen()) { - return; + return false; } // see if we should change screens @@ -243,7 +262,7 @@ void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) } else { // still on local screen - return; + return false; } // get jump destination @@ -251,7 +270,7 @@ void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) // if no screen in jump direction then ignore the move if (newScreen == NULL) { - return; + return false; } // remap position to account for resolution differences @@ -259,6 +278,7 @@ void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) // switch screen switchScreen(newScreen, x, y); + return true; } void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) @@ -365,7 +385,11 @@ bool CServer::isLockedToScreen() const return false; } +#if defined(CONFIG_PLATFORM_WIN32) +#include "CMSWindowsClipboard.h" // FIXME +#elif defined(CONFIG_PLATFORM_UNIX) #include "CXWindowsClipboard.h" // FIXME +#endif void CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y) { @@ -384,7 +408,11 @@ void CServer::switchScreen(CScreenInfo* dst, m_primary->leave(); // FIXME -- testing +#if defined(CONFIG_PLATFORM_WIN32) + CMSWindowsClipboard clipboard; +#elif defined(CONFIG_PLATFORM_UNIX) CXWindowsClipboard clipboard; +#endif m_primary->getClipboard(&clipboard); } else { @@ -610,8 +638,8 @@ void CServer::acceptClients(void*) std::auto_ptr listen; try { // create socket listener -// listen.reset(m_socketFactory->createListen()); - listen.reset(new CTCPListenSocket); // FIXME +// listen = std::auto_ptr(m_socketFactory->createListen()); + assign(listen, new CTCPListenSocket, IListenSocket); // FIXME // bind to the desired port. keep retrying if we can't bind // the address immediately. @@ -689,8 +717,8 @@ void CServer::handshakeClient(void* vsocket) } // attach the packetizing filters - input.reset(new CInputPacketStream(srcInput, own)); - output.reset(new COutputPacketStream(srcOutput, own)); + assign(input, new CInputPacketStream(srcInput, own), IInputStream); + assign(output, new COutputPacketStream(srcOutput, own), IOutputStream); std::auto_ptr protocol; std::auto_ptr connectedNote; @@ -730,12 +758,13 @@ void CServer::handshakeClient(void* vsocket) // create a protocol interpreter for the version log((CLOG_DEBUG "creating interpreter for client %s version %d.%d", name.c_str(), major, minor)); - protocol.reset(CServerProtocol::create(major, minor, - this, name, input.get(), output.get())); + assign(protocol, CServerProtocol::create(major, minor, + this, name, input.get(), output.get()), + IServerProtocol); // client is now pending - connectedNote.reset(new CConnectionNote(this, - name, protocol.get())); + assign(connectedNote, new CConnectionNote(this, + name, protocol.get()), CConnectionNote); // ask and wait for the client's info log((CLOG_DEBUG "waiting for info for client %s", name.c_str())); @@ -766,20 +795,26 @@ void CServer::handshakeClient(void* vsocket) void CServer::quit() { - CLock lock(&m_mutex); - m_done = true; - m_done.broadcast(); + m_primary->stop(); } // FIXME -- use factory to create screen +#if defined(CONFIG_PLATFORM_WIN32) +#include "CMSWindowsPrimaryScreen.h" +#elif defined(CONFIG_PLATFORM_UNIX) #include "CXWindowsPrimaryScreen.h" +#endif void CServer::openPrimaryScreen() { assert(m_primary == NULL); // open screen log((CLOG_DEBUG "creating primary screen")); +#if defined(CONFIG_PLATFORM_WIN32) + m_primary = new CMSWindowsPrimaryScreen; +#elif defined(CONFIG_PLATFORM_UNIX) m_primary = new CXWindowsPrimaryScreen; +#endif log((CLOG_DEBUG "opening primary screen")); m_primary->open(this); @@ -828,8 +863,9 @@ void CServer::removeCleanupThread(const CThread& thread) for (CThreadList::iterator index = m_cleanupList.begin(); index != m_cleanupList.end(); ++index) { if (**index == thread) { + CThread* thread = *index; m_cleanupList.erase(index); - delete *index; + delete thread; return; } } @@ -877,7 +913,7 @@ void CServer::removeConnection(const CString& name) assert(index != m_screens.end()); // if this is active screen then we have to jump off of it - if (m_active == index->second) { + if (m_active == index->second && m_active != m_primaryInfo) { // record new position (center of primary screen) m_x = m_primaryInfo->m_width >> 1; m_y = m_primaryInfo->m_height >> 1; diff --git a/server/CServer.h b/server/CServer.h index 83fa337f..18391aad 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -4,7 +4,6 @@ #include "KeyTypes.h" #include "MouseTypes.h" #include "CScreenMap.h" -#include "CCondVar.h" #include "CMutex.h" #include "CString.h" #include "XBase.h" @@ -24,18 +23,23 @@ class CServer { // manipulators + // start the server. does not return until quit() is called. void run(); + // tell server to exit gracefully + void quit(); + // update screen map void setScreenMap(const CScreenMap&); - // handle events on server's screen + // handle events on server's screen. onMouseMovePrimary() returns + // true iff the mouse enters a jump zone and jumps. void onKeyDown(KeyID, KeyModifierMask); void onKeyUp(KeyID, KeyModifierMask); void onKeyRepeat(KeyID, KeyModifierMask); void onMouseDown(ButtonID); void onMouseUp(ButtonID); - void onMouseMovePrimary(SInt32 x, SInt32 y); + bool onMouseMovePrimary(SInt32 x, SInt32 y); void onMouseMoveSecondary(SInt32 dx, SInt32 dy); void onMouseWheel(SInt32 delta); @@ -50,9 +54,11 @@ class CServer { // get the current screen map void getScreenMap(CScreenMap*) const; + // get the sides of the primary screen that have neighbors + UInt32 getActivePrimarySides() const; + protected: bool onCommandKey(KeyID, KeyModifierMask, bool down); - void quit(); private: class CCleanupNote { @@ -137,7 +143,6 @@ class CServer { typedef std::map CScreenList; CMutex m_mutex; - CCondVar m_done; double m_bindTimeout; diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp new file mode 100644 index 00000000..2cdf876d --- /dev/null +++ b/server/CSynergyHook.cpp @@ -0,0 +1,242 @@ +#include "CSynergyHook.h" +#include "CScreenMap.h" +#include + +// +// 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_process = NULL; +static HWND g_hwnd = NULL; +static HHOOK g_keyboard = NULL; +static HHOOK g_mouse = NULL; +static bool g_relay = false; +static SInt32 g_zoneSize = 0; +static UInt32 g_zoneSides = 0; +static SInt32 g_wScreen = 0; +static SInt32 g_hScreen = 0; +static HCURSOR g_cursor = NULL; +static DWORD g_cursorThread = 0; + +#pragma data_seg() + +// +// 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. + g_cursor = GetCursor(); + g_cursorThread = thread; + SetCursor(NULL); +} + +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 LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // FIXME + } + + return CallNextHookEx(g_keyboard, code, wParam, lParam); +} + +static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + if (g_relay) { + switch (wParam) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_BUTTON, wParam, 0); + return 1; + + case WM_MOUSEMOVE: { + const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + + // 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 = GetWindowThreadProcessId(info->hwnd, NULL); + if (thread != g_cursorThread) { + restoreCursor(); + hideCursor(thread); + } + + // relay the motion + PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_MOVE, x, y); + return 1; + } + } + } + else { + // check for mouse inside jump zone + bool inside = false; + const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + if (!inside && (g_zoneSides & CScreenMap::kLeftMask) != 0) { + inside = (x < g_zoneSize); + } + if (!inside && (g_zoneSides & CScreenMap::kRightMask) != 0) { + inside = (x >= g_wScreen - g_zoneSize); + } + if (!inside && (g_zoneSides & CScreenMap::kTopMask) != 0) { + inside = (y < g_zoneSize); + } + if (!inside && (g_zoneSides & CScreenMap::kBottomMask) != 0) { + inside = (y >= g_hScreen - g_zoneSize); + } + + // if inside then eat event and notify our window + if (inside) { + restoreCursor(); + PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_MOVE, x, y); + return 1; + } + } + } + + return CallNextHookEx(g_mouse, code, wParam, lParam); +} + + +// +// external functions +// + +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID) +{ + if (reason == DLL_PROCESS_ATTACH) { + if (g_hinstance == NULL) { + g_hinstance = instance; + g_process = GetCurrentProcessId(); + } + } + else if (reason == DLL_PROCESS_DETACH) { + if (g_process == GetCurrentProcessId()) { + if (g_keyboard != NULL || g_mouse != NULL) { + uninstall(); + } + g_process = NULL; + } + } + return TRUE; +} + +extern "C" { + +int install(HWND hwnd) +{ + assert(g_hinstance != NULL); + assert(g_keyboard == NULL); + assert(g_mouse == NULL); + + // save window + g_hwnd = hwnd; + + // set defaults + g_relay = false; + g_zoneSize = 0; + g_zoneSides = 0; + g_wScreen = 0; + g_hScreen = 0; + g_cursor = NULL; + g_cursorThread = 0; + + // install keyboard hook + g_keyboard = SetWindowsHookEx(WH_KEYBOARD, + &keyboardHook, + g_hinstance, + 0); + if (g_keyboard == NULL) { + g_hwnd = NULL; + return 0; + } + + // install mouse hook + g_mouse = SetWindowsHookEx(WH_MOUSE, + &mouseHook, + g_hinstance, + 0); + if (g_mouse == NULL) { + // uninstall keyboard hook before failing + UnhookWindowsHookEx(g_keyboard); + g_keyboard = NULL; + g_hwnd = NULL; + return 0; + } + + return 1; +} + +int uninstall(void) +{ + assert(g_keyboard != NULL); + assert(g_mouse != NULL); + + // uninstall hooks + UnhookWindowsHookEx(g_keyboard); + UnhookWindowsHookEx(g_mouse); + g_keyboard = NULL; + g_mouse = NULL; + g_hwnd = NULL; + + // show the cursor + restoreCursor(); + + return 1; +} + +void setZone(UInt32 sides, + SInt32 w, SInt32 h, SInt32 jumpZoneSize) +{ + g_zoneSize = jumpZoneSize; + g_zoneSides = sides; + g_wScreen = w; + g_hScreen = h; + g_relay = false; + restoreCursor(); +} + +void setRelay(void) +{ + g_relay = true; + g_zoneSize = 0; + g_zoneSides = 0; + g_wScreen = 0; + g_hScreen = 0; +} + +} diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h new file mode 100644 index 00000000..b3b30c24 --- /dev/null +++ b/server/CSynergyHook.h @@ -0,0 +1,37 @@ +#ifndef CSYNERGYHOOK_H +#define CSYNERGYHOOK_H + +#include "BasicTypes.h" +#include + +#if defined(CONFIG_PLATFORM_WIN32) +#if defined(SYNRGYHK_EXPORTS) +#define CSYNERGYHOOK_API __declspec(dllexport) +#else +#define CSYNERGYHOOK_API __declspec(dllimport) +#endif +#else +#define CSYNERGYHOOK_API +#endif + +#define SYNERGY_MSG_MARK WM_APP + 0x0001 // mark id; +#define SYNERGY_MSG_KEY WM_APP + 0x0002 // vk code; key data +#define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0003 // button msg; +#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0004 // x; y + +typedef int (*InstallFunc)(HWND); +typedef int (*UninstallFunc)(void); +typedef void (*SetZoneFunc)(UInt32, SInt32, SInt32, SInt32); +typedef void (*SetRelayFunc)(void); + +extern "C" { + +CSYNERGYHOOK_API int install(HWND); +CSYNERGYHOOK_API int uninstall(void); +CSYNERGYHOOK_API void setZone(UInt32 sides, + SInt32 w, SInt32 h, SInt32 jumpZoneSize); +CSYNERGYHOOK_API void setRelay(void); + +} + +#endif diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 6e208869..a36b07ec 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -19,7 +19,124 @@ CXWindowsPrimaryScreen::CXWindowsPrimaryScreen() : CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() { - assert(m_window == None); + assert(m_window == None); +} + +void CXWindowsPrimaryScreen::run() +{ + for (;;) { + // wait for and get the next event + XEvent xevent; + if (!getEvent(&xevent)) { + break; + } + + // handle event + switch (xevent.type) { + case CreateNotify: { + // select events on new window + CDisplayLock display(this); + selectEvents(display, xevent.xcreatewindow.window); + break; + } + + case KeyPress: { + log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(xevent.xkey.keycode, mask); + if (key != kKeyNone) { + m_server->onKeyDown(key, mask); + } + break; + } + + // FIXME -- simulate key repeat. X sends press/release for + // repeat. must detect auto repeat and use kKeyRepeat. + case KeyRelease: { + log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(xevent.xkey.keycode, mask); + if (key != kKeyNone) { + m_server->onKeyUp(key, mask); + } + break; + } + + case ButtonPress: { + log((CLOG_DEBUG "event: ButtonPress button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_server->onMouseDown(button); + } + break; + } + + case ButtonRelease: { + log((CLOG_DEBUG "event: ButtonRelease button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_server->onMouseUp(button); + } + break; + } + + case MotionNotify: { + log((CLOG_DEBUG "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); + SInt32 x, y; + if (!m_active) { + x = xevent.xmotion.x_root; + y = xevent.xmotion.y_root; + m_server->onMouseMovePrimary(x, y); + } + else { + // FIXME -- slurp up all remaining motion events? + // probably not since key strokes may go to wrong place. + + // get mouse deltas + { + CDisplayLock display(this); + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int mask; + if (!XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &mask)) + break; + + // compute position of center of window + SInt32 w, h; + getScreenSize(&w, &h); + x = xRoot - (w >> 1); + y = yRoot - (h >> 1); + + // warp mouse back to center + warpCursorNoLock(display, w >> 1, h >> 1); + } + + m_server->onMouseMoveSecondary(x, y); + } + break; + } + +/* + case SelectionClear: + target->XXX(xevent.xselectionclear.); + break; + + case SelectionNotify: + target->XXX(xevent.xselection.); + break; + + case SelectionRequest: + target->XXX(xevent.xselectionrequest.); + break; +*/ + } + } +} + +void CXWindowsPrimaryScreen::stop() +{ + doStop(); } void CXWindowsPrimaryScreen::open(CServer* server) @@ -251,116 +368,6 @@ void CXWindowsPrimaryScreen::selectEvents( } } -void CXWindowsPrimaryScreen::eventThread(void*) -{ - for (;;) { - // wait for and get the next event - XEvent xevent; - getEvent(&xevent); - - // handle event - switch (xevent.type) { - case CreateNotify: { - // select events on new window - CDisplayLock display(this); - selectEvents(display, xevent.xcreatewindow.window); - break; - } - - case KeyPress: { - log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(xevent.xkey.keycode, mask); - if (key != kKeyNone) { - m_server->onKeyDown(key, mask); - } - break; - } - - // FIXME -- simulate key repeat. X sends press/release for - // repeat. must detect auto repeat and use kKeyRepeat. - case KeyRelease: { - log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(xevent.xkey.keycode, mask); - if (key != kKeyNone) { - m_server->onKeyUp(key, mask); - } - break; - } - - case ButtonPress: { - log((CLOG_DEBUG "event: ButtonPress button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNone) { - m_server->onMouseDown(button); - } - break; - } - - case ButtonRelease: { - log((CLOG_DEBUG "event: ButtonRelease button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNone) { - m_server->onMouseUp(button); - } - break; - } - - case MotionNotify: { - log((CLOG_DEBUG "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); - SInt32 x, y; - if (!m_active) { - x = xevent.xmotion.x_root; - y = xevent.xmotion.y_root; - m_server->onMouseMovePrimary(x, y); - } - else { - // FIXME -- slurp up all remaining motion events? - // probably not since key strokes may go to wrong place. - - // get mouse deltas - { - CDisplayLock display(this); - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int mask; - if (!XQueryPointer(display, m_window, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &mask)) - break; - - // compute position of center of window - SInt32 w, h; - getScreenSize(&w, &h); - x = xRoot - (w >> 1); - y = yRoot - (h >> 1); - - // warp mouse back to center - warpCursorNoLock(display, w >> 1, h >> 1); - } - - m_server->onMouseMoveSecondary(x, y); - } - break; - } - -/* - case SelectionClear: - target->XXX(xevent.xselectionclear.); - break; - - case SelectionNotify: - target->XXX(xevent.xselection.); - break; - - case SelectionRequest: - target->XXX(xevent.xselectionrequest.); - break; -*/ - } - } -} - KeyModifierMask CXWindowsPrimaryScreen::mapModifier( unsigned int state) const { diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 3e056154..66b84fb5 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -12,6 +12,8 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { virtual ~CXWindowsPrimaryScreen(); // IPrimaryScreen overrides + virtual void run(); + virtual void stop(); virtual void open(CServer*); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); @@ -26,7 +28,6 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { // CXWindowsScreen overrides virtual void onOpenDisplay(); virtual void onCloseDisplay(); - virtual void eventThread(void*); private: void selectEvents(Display*, Window) const; diff --git a/server/Makefile b/server/Makefile index 2ff1150f..b8dd68a3 100644 --- a/server/Makefile +++ b/server/Makefile @@ -4,7 +4,7 @@ include $(DEPTH)/Makecommon # # target file # -TARGET = server +TARGETS = server # # source files @@ -40,8 +40,8 @@ LLDLIBS = \ -lpthread \ $(NULL) -targets: $(TARGET) +targets: $(TARGETS) -$(TARGET): $(OBJECTS) $(DEPLIBS) +$(TARGETS): $(OBJECTS) $(DEPLIBS) $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) diff --git a/server/makehook.dsp b/server/makehook.dsp new file mode 100644 index 00000000..507157ef --- /dev/null +++ b/server/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 "makehook___Win32_Release" +# PROP BASE Intermediate_Dir "makehook___Win32_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 "makehook___Win32_Debug" +# PROP BASE Intermediate_Dir "makehook___Win32_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/server/resource.h b/server/resource.h new file mode 100644 index 00000000..2b751aa3 --- /dev/null +++ b/server/resource.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by server.rc +// +#define IDD_SYNERGY 101 +#define IDC_LOG 1000 + +// 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 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/server/server.cpp b/server/server.cpp index ef1b578f..809c53d6 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -1,16 +1,12 @@ #include "CServer.h" #include "CScreenMap.h" #include "CThread.h" -#include +#include "CNetwork.h" -int main(int argc, char** argv) +void realMain() { CThread::init(); - - if (argc != 1) { - fprintf(stderr, "usage: %s\n", argv[0]); - return 1; - } + CNetwork::init(); CScreenMap screenMap; screenMap.addScreen("primary"); @@ -18,15 +14,66 @@ int main(int argc, char** argv) screenMap.connect("primary", CScreenMap::kRight, "ingrid"); screenMap.connect("ingrid", CScreenMap::kLeft, "primary"); + CServer* server = NULL; try { - CServer* server = new CServer(); + server = new CServer(); server->setScreenMap(screenMap); server->run(); + delete server; + CNetwork::cleanup(); + } + catch (...) { + delete server; + CNetwork::cleanup(); + throw; + } +} + +#if defined(CONFIG_PLATFORM_WIN32) + +#include "CMSWindowsScreen.h" + +int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) +{ + CMSWindowsScreen::init(instance); + + if (__argc != 1) { + CString msg = "no arguments allowed. exiting."; + MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); + return 1; + } + + try { + realMain(); + return 0; + } + catch (XBase& e) { + CString msg = "failed: "; + msg += e.what(); + MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); + return 1; + } +} + +#else + +#include + +int main(int argc, char** argv) +{ + if (argc != 1) { + fprintf(stderr, "usage: %s\n", argv[0]); + return 1; + } + + try { + realMain(); + return 0; } catch (XBase& e) { fprintf(stderr, "failed: %s\n", e.what()); return 1; } - - return 0; } + +#endif diff --git a/server/server.dsp b/server/server.dsp new file mode 100644 index 00000000..b65f6dec --- /dev/null +++ b/server/server.dsp @@ -0,0 +1,155 @@ +# 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) Application" 0x0101 + +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) Application") +!MESSAGE "server - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.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 "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /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)" == "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 "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /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 "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=.\CMSWindowsPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CScreenMap.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServer.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerProtocol.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerProtocol1_0.cpp +# End Source File +# Begin Source File + +SOURCE=.\server.cpp +# End Source File +# Begin Source File + +SOURCE=.\server.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CScreenMap.h +# End Source File +# Begin Source File + +SOURCE=.\CServer.h +# End Source File +# Begin Source File + +SOURCE=.\CServerProtocol.h +# End Source File +# Begin Source File + +SOURCE=.\CServerProtocol1_0.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" +# End Group +# End Target +# End Project diff --git a/server/server.rc b/server/server.rc new file mode 100644 index 00000000..60c3a920 --- /dev/null +++ b/server/server.rc @@ -0,0 +1,97 @@ +//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_SYNERGY DIALOG DISCARDABLE 0, 0, 329, 158 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Synergy" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_LOG,7,7,315,144,ES_MULTILINE | ES_AUTOHSCROLL | + ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_SYNERGY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 322 + TOPMARGIN, 7 + BOTTOMMARGIN, 151 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/server/synrgyhk.dsp b/server/synrgyhk.dsp new file mode 100644 index 00000000..979b6172 --- /dev/null +++ b/server/synrgyhk.dsp @@ -0,0 +1,111 @@ +# 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 "millpond" +# 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 "synrgyhk___Win32_Release" +# PROP BASE Intermediate_Dir "synrgyhk___Win32_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 /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\base" /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 /dll /machine:I386 + +!ELSEIF "$(CFG)" == "synrgyhk - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "synrgyhk___Win32_Debug" +# PROP BASE Intermediate_Dir "synrgyhk___Win32_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 /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 /W3 /Gm /GX /ZI /Od /I "..\base" /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 + +!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 +# 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/synergy.dsw b/synergy.dsw new file mode 100644 index 00000000..e441f5fc --- /dev/null +++ b/synergy.dsw @@ -0,0 +1,215 @@ +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> +{{{ + begin source code control + millpond + . + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name client + End Project Dependency + Begin Project Dependency + Project_Dep_Name server + End Project Dependency +}}} + +############################################################################### + +Project: "base"=.\base\base.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\base + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "client"=.\client\client.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\client + end source code control +}}} + +Package=<4> +{{{ + 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 synergy + End Project Dependency +}}} + +############################################################################### + +Project: "io"=.\io\io.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\io + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "makehook"=.\server\makehook.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name synrgyhk + End Project Dependency +}}} + +############################################################################### + +Project: "mt"=.\mt\mt.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\mt + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "net"=.\net\net.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\net + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "server"=.\server\server.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\server + end source code control +}}} + +Package=<4> +{{{ + 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 synergy + End Project Dependency + Begin Project Dependency + Project_Dep_Name makehook + End Project Dependency +}}} + +############################################################################### + +Project: "synergy"=.\synergy\synergy.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\synergy + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "synrgyhk"=.\server\synrgyhk.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\server + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/synergy/CMSWindowsClipboard.cpp b/synergy/CMSWindowsClipboard.cpp new file mode 100644 index 00000000..bdef4024 --- /dev/null +++ b/synergy/CMSWindowsClipboard.cpp @@ -0,0 +1,41 @@ +#include "CMSWindowsClipboard.h" +#include "CString.h" +#include "CLog.h" + +// +// CMSWindowsClipboard +// + +CMSWindowsClipboard::CMSWindowsClipboard() +{ +} + +CMSWindowsClipboard::~CMSWindowsClipboard() +{ +} + +void CMSWindowsClipboard::open() +{ + log((CLOG_INFO "open clipboard")); +} + +void CMSWindowsClipboard::close() +{ + log((CLOG_INFO "close clipboard")); +} + +void CMSWindowsClipboard::add( + EFormat format, const CString& data) +{ + log((CLOG_INFO "add clipboard format: %d\n%s", format, data.c_str())); +} + +bool CMSWindowsClipboard::has(EFormat /*format*/) const +{ + return false; +} + +CString CMSWindowsClipboard::get(EFormat /*format*/) const +{ + return CString(); +} diff --git a/synergy/CMSWindowsClipboard.h b/synergy/CMSWindowsClipboard.h new file mode 100644 index 00000000..c13b11d3 --- /dev/null +++ b/synergy/CMSWindowsClipboard.h @@ -0,0 +1,19 @@ +#ifndef CMSWINDOWSCLIPBOARD_H +#define CMSWINDOWSCLIPBOARD_H + +#include "IClipboard.h" + +class CMSWindowsClipboard : public IClipboard { + public: + CMSWindowsClipboard(); + virtual ~CMSWindowsClipboard(); + + // IClipboard overrides + virtual void open(); + virtual void close(); + virtual void add(EFormat, const CString& data); + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; +}; + +#endif diff --git a/synergy/CMSWindowsScreen.cpp b/synergy/CMSWindowsScreen.cpp new file mode 100644 index 00000000..63640388 --- /dev/null +++ b/synergy/CMSWindowsScreen.cpp @@ -0,0 +1,248 @@ +#include "CMSWindowsScreen.h" +#include "CThread.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CLog.h" +#include "CString.h" +#include +#include + +// +// CMSWindowsScreen +// + +HINSTANCE CMSWindowsScreen::s_instance = NULL; + +CMSWindowsScreen::CMSWindowsScreen() : + m_class(0), + m_cursor(NULL), + m_w(0), m_h(0), + m_thread(0) +{ + // do nothing +} + +CMSWindowsScreen::~CMSWindowsScreen() +{ + assert(m_class == 0); +} + +void CMSWindowsScreen::init(HINSTANCE instance) +{ + s_instance = instance; +} + +void CMSWindowsScreen::doRun() +{ + m_thread = GetCurrentThreadId(); + for (;;) { + // wait for and get the next event + MSG msg; + getEvent(&msg); + + // handle quit message + if (msg.message == WM_QUIT) { + break; + } + + // dispatch message + if (!onEvent(&msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +void CMSWindowsScreen::doStop() +{ + PostThreadMessage(m_thread, WM_QUIT, 0, 0); +} + +void CMSWindowsScreen::openDisplay() +{ + assert(s_instance != NULL); + assert(m_class == 0); + + // create a transparent cursor + int cw = GetSystemMetrics(SM_CXCURSOR); + int ch = GetSystemMetrics(SM_CYCURSOR); + UInt8* cursorBits = new UInt8[ch * ((cw + 31) >> 5)]; + memset(cursorBits, 0, ch * ((cw + 31) >> 5)); + m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorBits, cursorBits); + delete[] cursorBits; + + // 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 size + // FIXME -- should handle multiple screens + m_w = GetSystemMetrics(SM_CXSCREEN); + m_h = GetSystemMetrics(SM_CYSCREEN); + log((CLOG_INFO "display size: %dx%d", m_w, m_h)); + + // let subclass prep display + onOpenDisplay(); +} + +void CMSWindowsScreen::closeDisplay() +{ + assert(s_instance != NULL); + assert(m_class != 0); + + // let subclass close down display + onCloseDisplay(); + + // unregister the window class + UnregisterClass((LPCTSTR)m_class, s_instance); + m_class = 0; + + // delete resources + DestroyCursor(m_cursor); + m_cursor = NULL; + + log((CLOG_DEBUG "closed display")); +} + +HINSTANCE CMSWindowsScreen::getInstance() +{ + return s_instance; +} + +ATOM CMSWindowsScreen::getClass() const +{ + return m_class; +} + +void CMSWindowsScreen::getScreenSize( + SInt32* w, SInt32* h) const +{ + assert(m_class != 0); + assert(w != NULL && h != NULL); + + *w = m_w; + *h = m_h; +} + +void CMSWindowsScreen::getEvent(MSG* msg) const +{ + // wait for an event in a cancellable way + while (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0) { + CThread::sleep(0.05); + } + GetMessage(msg, NULL, 0, 0); +} + +void CMSWindowsScreen::getDisplayClipboard( + IClipboard* clipboard, + HWND requestor) const +{ +/* FIXME + assert(clipboard != NULL); + assert(requestor != None); + + // clear the clipboard object + clipboard->open(); + + // block others from using the display while we get the clipboard. + // in particular, this prevents the event thread from stealing the + // selection notify event we're expecting. + CLock lock(&m_mutex); + + // use PRIMARY selection as the "clipboard" + Atom selection = XA_PRIMARY; + + // ask the selection for all the formats it has. some owners return + // the TARGETS atom and some the ATOM atom when TARGETS is requested. + Atom format; + CString targets; + if (getDisplayClipboard(selection, m_atomTargets, + requestor, timestamp, &format, &targets) && + (format == m_atomTargets || format == XA_ATOM)) { + // get each target (that we can interpret). some owners return + // some targets multiple times in the list so don't try to get + // those multiple times. + const Atom* targetAtoms = reinterpret_cast(targets.data()); + const SInt32 numTargets = targets.size() / sizeof(Atom); + std::set clipboardFormats; + std::set targets; + log((CLOG_DEBUG "selection has %d targets", numTargets)); + for (SInt32 i = 0; i < numTargets; ++i) { + Atom format = targetAtoms[i]; + log((CLOG_DEBUG " source target %d", format)); + + // skip already handled targets + if (targets.count(format) > 0) { + log((CLOG_DEBUG " skipping handled target %d", format)); + continue; + } + + // mark this target as done + targets.insert(format); + + // determine the expected clipboard format + IClipboard::EFormat expectedFormat = getFormat(format); + + // if we can use the format and we haven't already retrieved + // it then get it + if (expectedFormat == IClipboard::kNum) { + log((CLOG_DEBUG " no format for target", format)); + continue; + } + if (clipboardFormats.count(expectedFormat) > 0) { + log((CLOG_DEBUG " skipping handled format %d", expectedFormat)); + continue; + } + + CString data; + if (!getDisplayClipboard(selection, format, + requestor, timestamp, &format, &data)) { + log((CLOG_DEBUG " no data for target", format)); + continue; + } + + // use the actual format, not the expected + IClipboard::EFormat actualFormat = getFormat(format); + if (actualFormat == IClipboard::kNum) { + log((CLOG_DEBUG " no format for target", format)); + continue; + } + if (clipboardFormats.count(actualFormat) > 0) { + log((CLOG_DEBUG " skipping handled format %d", actualFormat)); + continue; + } + + // add to clipboard and note we've done it + clipboard->add(actualFormat, data); + clipboardFormats.insert(actualFormat); + } + } + else { + // non-ICCCM conforming selection owner. try TEXT format. + // FIXME + log((CLOG_DEBUG "selection doesn't support TARGETS, format is %d", format)); + } + + // done with clipboard + clipboard->close(); +*/ +} + +LRESULT CALLBACK CMSWindowsScreen::wndProc( + HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hwnd, msg, wParam, lParam); +} diff --git a/synergy/CMSWindowsScreen.h b/synergy/CMSWindowsScreen.h new file mode 100644 index 00000000..4c51be00 --- /dev/null +++ b/synergy/CMSWindowsScreen.h @@ -0,0 +1,73 @@ +#ifndef CMSWINDOWSSCREEN_H +#define CMSWINDOWSSCREEN_H + +#include "CMutex.h" +#include "IClipboard.h" +#include "BasicTypes.h" +#include + +class CString; +class CThread; + +class CMSWindowsScreen { + public: + CMSWindowsScreen(); + virtual ~CMSWindowsScreen(); + + // manipulators + + static void init(HINSTANCE); + + protected: + // runs an event loop and returns when WM_QUIT is received + void doRun(); + + // sends WM_QUIT to force doRun() to return + void doStop(); + + // open the X display. calls onOpenDisplay() after opening the display, + // getting the screen, its size, and root window. then it starts the + // event thread. + void openDisplay(); + + // destroy the window and close the display. calls onCloseDisplay() + // after the event thread has been shut down but before the display + // is closed. + void closeDisplay(); + + // get the application instance handle and the registered window + // class atom + static HINSTANCE getInstance(); + ATOM getClass() const; + + // get the size of the screen + void getScreenSize(SInt32* w, SInt32* h) const; + + // wait for and get the next message. cancellable. + void getEvent(MSG*) const; + + // copy the clipboard contents to clipboard + void getDisplayClipboard(IClipboard* clipboard, HWND) const; + + // called by doRun() to handle an event + virtual bool onEvent(MSG*) = 0; + + // called by openDisplay() to allow subclasses to prepare the display + virtual void onOpenDisplay() = 0; + + // called by closeDisplay() to + virtual void onCloseDisplay() = 0; + + private: + static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); + + private: + static HINSTANCE s_instance; + ATOM m_class; + HICON m_icon; + HCURSOR m_cursor; + SInt32 m_w, m_h; + DWORD m_thread; +}; + +#endif diff --git a/synergy/CXWindowsClipboard.h b/synergy/CXWindowsClipboard.h index a67aeed8..334af77c 100644 --- a/synergy/CXWindowsClipboard.h +++ b/synergy/CXWindowsClipboard.h @@ -17,4 +17,3 @@ class CXWindowsClipboard : public IClipboard { }; #endif - diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 3559de52..dafbb648 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -17,7 +17,8 @@ CXWindowsScreen::CXWindowsScreen() : m_display(NULL), m_root(None), - m_w(0), m_h(0) + m_w(0), m_h(0), + m_stop(false) { // do nothing } @@ -58,24 +59,11 @@ void CXWindowsScreen::openDisplay() // let subclass prep display onOpenDisplay(); - - // start processing events - m_eventThread = new CThread(new TMethodJob( - this, &CXWindowsScreen::eventThread)); } void CXWindowsScreen::closeDisplay() { assert(m_display != NULL); - assert(m_eventThread != NULL); - - // stop event thread - log((CLOG_DEBUG "stopping event thread")); - m_eventThread->cancel(); - m_eventThread->wait(); - delete m_eventThread; - m_eventThread = NULL; - log((CLOG_DEBUG "stopped event thread")); // let subclass close down display onCloseDisplay(); @@ -143,18 +131,31 @@ Cursor CXWindowsScreen::createBlankCursor() const return cursor; } -void CXWindowsScreen::getEvent(XEvent* xevent) const +bool CXWindowsScreen::getEvent(XEvent* xevent) const { // wait for an event in a cancellable way and don't lock the // display while we're waiting. m_mutex.lock(); - while (XPending(m_display) == 0) { + while (!m_stop && XPending(m_display) == 0) { m_mutex.unlock(); CThread::sleep(0.05); m_mutex.lock(); } - XNextEvent(m_display, xevent); - m_mutex.unlock(); + if (m_stop) { + m_mutex.unlock(); + return true; + } + else { + XNextEvent(m_display, xevent); + m_mutex.unlock(); + return false; + } +} + +void CXWindowsScreen::doStop() +{ + CLock lock(&m_mutex); + m_stop = true; } void CXWindowsScreen::getDisplayClipboard( diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 66cac76e..98088925 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -7,7 +7,6 @@ #include class CString; -class CThread; class CXWindowsScreen { public: @@ -50,7 +49,10 @@ class CXWindowsScreen { Cursor createBlankCursor() const; // wait for and get the next X event. cancellable. - void getEvent(XEvent*) const; + bool getEvent(XEvent*) const; + + // cause getEvent() to return false immediately and forever after + void doStop(); // copy the clipboard contents to clipboard. requestor must be a // valid window; it will be used to receive the transfer. timestamp @@ -64,9 +66,6 @@ class CXWindowsScreen { // called by closeDisplay() to virtual void onCloseDisplay() = 0; - // override to process X events - virtual void eventThread(void*) = 0; - private: struct PropertyNotifyInfo { public: @@ -87,11 +86,11 @@ class CXWindowsScreen { XEvent* xevent, XPointer arg); private: - CThread* m_eventThread; Display* m_display; int m_screen; Window m_root; SInt32 m_w, m_h; + bool m_stop; // atoms we'll need Atom m_atomTargets; diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index e42b2706..9eaae81e 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -41,4 +41,3 @@ class IClipboard : public IInterface { }; #endif - diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 284728b4..17341b63 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -11,6 +11,15 @@ class IPrimaryScreen : public IInterface { public: // manipulators + // enter the screen's message loop. this returns when it detects + // the application should terminate or when stop() is called. + // the screen must be open()'d before run() and must not be + // close()'d until run() returns. + virtual void run() = 0; + + // cause run() to return + virtual void stop() = 0; + // initialize the screen and start reporting events to the server. // events should be reported no matter where on the screen they // occur but do not interfere with normal event dispatch. the @@ -26,8 +35,9 @@ class IPrimaryScreen : public IInterface { // called when the user navigates back to the primary screen. // warp the cursor to the given coordinates, unhide it, and - // ungrab the input devices. every call to method has a matching - // call to leave() which preceeds it. + // ungrab the input devices. every call to enter has a matching + // call to leave() which preceeds it, however the screen can + // assume an implicit call to enter() in the call to open(). virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute) = 0; // called when the user navigates off the primary screen. hide diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 29db0066..88893da6 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -13,6 +13,15 @@ class ISecondaryScreen : public IInterface { public: // manipulators + // enter the screen's message loop. this returns when it detects + // the application should terminate or when stop() is called. + // the screen must be open()'d before run() and must not be + // close()'d until run() returns. + virtual void run() = 0; + + // cause run() to return + virtual void stop() = 0; + // initialize the screen, hide the cursor, and disable the screen // saver. start reporting certain events to the client (clipboard // stolen and screen size changed). diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp new file mode 100644 index 00000000..9bff2e3d --- /dev/null +++ b/synergy/synergy.dsp @@ -0,0 +1,186 @@ +# Microsoft Developer Studio Project File - Name="synergy" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=synergy - 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 "synergy.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 "synergy.mak" CFG="synergy - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synergy - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "synergy - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synergy - 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 /W3 /GX /O2 /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)" == "synergy - 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 /W3 /Gm /GX /ZI /Od /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 "synergy - Win32 Release" +# Name "synergy - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CInputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.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=.\CInputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.h +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.h +# End Source File +# Begin Source File + +SOURCE=.\IClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\ISecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IServerProtocol.h +# End Source File +# Begin Source File + +SOURCE=.\ISocketFactory.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=.\ProtocolTypes.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