diff --git a/client/CClient.cpp b/client/CClient.cpp index 33447115..5721ac5c 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -2,8 +2,10 @@ #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CProtocolUtil.h" +#include "CClipboard.h" #include "ISecondaryScreen.h" #include "ProtocolTypes.h" +#include "CLock.h" #include "CThread.h" #include "CTimerThread.h" #include "XSynergy.h" @@ -63,7 +65,7 @@ void CClient::run(const CNetworkAddress& serverAddress) closeSecondaryScreen(); } catch (XBase& e) { - log((CLOG_ERR "client error: %s\n", e.what())); + log((CLOG_ERR "client error: %s", e.what())); // clean up thread->cancel(); @@ -83,6 +85,17 @@ void CClient::run(const CNetworkAddress& serverAddress) } } +void CClient::onClipboardChanged() +{ + log((CLOG_DEBUG "sending clipboard changed")); + CLock lock(&m_mutex); + if (m_output != NULL) { + // m_output can be NULL if the screen calls this method + // before we've gotten around to connecting to the server. + CProtocolUtil::writef(m_output, kMsgCClipboard); + } +} + #include "CTCPSocket.h" // FIXME void CClient::runSession(void*) { @@ -136,10 +149,10 @@ void CClient::runSession(void*) // say hello back log((CLOG_DEBUG "say hello version %d.%d", kMajorVersion, kMinorVersion)); CProtocolUtil::writef(output.get(), "Synergy%2i%2i%s", - kMajorVersion, kMinorVersion, - m_name.size(), m_name.data()); + kMajorVersion, kMinorVersion, &m_name); // record streams in a more useful place + CLock lock(&m_mutex); m_input = input.get(); m_output = output.get(); } @@ -291,7 +304,10 @@ void CClient::closeSecondaryScreen() void CClient::onEnter() { SInt32 x, y; - CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y); + } m_screen->enter(x, y); } @@ -302,13 +318,16 @@ void CClient::onLeave() void CClient::onGrabClipboard() { - // FIXME + m_screen->grabClipboard(); } void CClient::onScreenSaver() { SInt32 on; - CProtocolUtil::readf(m_input, kMsgCScreenSaver + 4, &on); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgCScreenSaver + 4, &on); + } // FIXME } @@ -317,24 +336,63 @@ void CClient::onQueryInfo() SInt32 w, h; m_screen->getSize(&w, &h); SInt32 zoneSize = m_screen->getJumpZoneSize(); + log((CLOG_DEBUG "sending info size=%d,%d zone=%d", w, h, zoneSize)); + CLock lock(&m_mutex); CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); } void CClient::onQueryClipboard() { - // FIXME + // parse message + UInt32 seqNum; + CClipboard clipboard; + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgQClipboard + 4, &seqNum); + } + log((CLOG_DEBUG "received query clipboard seqnum=%d", seqNum)); + + // get screen's clipboard data + m_screen->getClipboard(&clipboard); + + // marshall the data + CString data = clipboard.marshall(); + + // send it + log((CLOG_DEBUG "sending clipboard seqnum=%d, size=%d", seqNum, data.size())); + { + CLock lock(&m_mutex); + CProtocolUtil::writef(m_output, kMsgDClipboard, seqNum, &data); + } } void CClient::onSetClipboard() { - // FIXME + CString data; + { + // parse message + UInt32 seqNum; + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDClipboard + 4, &seqNum, &data); + } + log((CLOG_DEBUG "received clipboard size=%d", data.size())); + + // unmarshall + CClipboard clipboard; + clipboard.unmarshall(data); + + // set screen's clipboard + m_screen->setClipboard(&clipboard); } void CClient::onKeyDown() { SInt32 id, mask; - CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); + } m_screen->keyDown(static_cast(id), static_cast(mask)); } @@ -342,7 +400,10 @@ void CClient::onKeyDown() void CClient::onKeyRepeat() { SInt32 id, mask, count; - CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); + } m_screen->keyRepeat(static_cast(id), static_cast(mask), count); @@ -351,7 +412,10 @@ void CClient::onKeyRepeat() void CClient::onKeyUp() { SInt32 id, mask; - CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); + } m_screen->keyUp(static_cast(id), static_cast(mask)); } @@ -359,27 +423,39 @@ void CClient::onKeyUp() void CClient::onMouseDown() { SInt32 id; - CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); + } m_screen->mouseDown(static_cast(id)); } void CClient::onMouseUp() { SInt32 id; - CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); + } m_screen->mouseUp(static_cast(id)); } void CClient::onMouseMove() { SInt32 x, y; - CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); + } m_screen->mouseMove(x, y); } void CClient::onMouseWheel() { SInt32 delta; - CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); + } m_screen->mouseWheel(delta); } diff --git a/client/CClient.h b/client/CClient.h index c332ae7c..b214dfc3 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -1,6 +1,7 @@ #ifndef CCLIENT_H #define CCLIENT_H +#include "CMutex.h" #include "CString.h" #include "BasicTypes.h" @@ -18,6 +19,9 @@ class CClient { void run(const CNetworkAddress& serverAddress); + // handle events on client's screen + void onClipboardChanged(); + // accessors @@ -45,6 +49,7 @@ class CClient { void onMouseWheel(); private: + CMutex m_mutex; CString m_name; IInputStream* m_input; IOutputStream* m_output; diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 439b5876..94507e4e 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -1,5 +1,7 @@ #include "CMSWindowsSecondaryScreen.h" +#include "CMSWindowsClipboard.h" #include "CClient.h" +#include "CClipboard.h" #include "CThread.h" #include "CLog.h" #include @@ -10,7 +12,8 @@ CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : m_client(NULL), - m_window(NULL) + m_window(NULL), + m_nextClipboardWindow(NULL) { // do nothing } @@ -20,6 +23,8 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() assert(m_window == NULL); } +static CString s_log; +static CString s_logMore; static HWND s_debug = NULL; static HWND s_debugLog = NULL; static DWORD s_thread = 0; @@ -32,39 +37,40 @@ static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lPar case WM_CLOSE: PostQuitMessage(0); return TRUE; + + case WM_APP: + if (!s_logMore.empty()) { + if (s_log.size() > 20000) + s_log = s_logMore; + else + s_log += s_logMore; + s_logMore = ""; + SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)s_log.c_str()); + SendMessage(s_debugLog, EM_SETSEL, s_log.size(), s_log.size()); + SendMessage(s_debugLog, EM_SCROLLCARET, 0, 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); + s_logMore += msg; + PostMessage(s_debug, WM_APP, 0, 0); } void CMSWindowsSecondaryScreen::run() { CLog::setOutputter(&debugOutput); + log((CLOG_INFO "entering event loop")); doRun(); + log((CLOG_INFO "exiting event loop")); CLog::setOutputter(NULL); } void CMSWindowsSecondaryScreen::stop() { + log((CLOG_INFO "requesting event loop stop")); doStop(); } @@ -73,6 +79,8 @@ void CMSWindowsSecondaryScreen::open(CClient* client) assert(m_client == NULL); assert(client != NULL); + log((CLOG_INFO "opening screen")); + // set the client m_client = client; @@ -84,6 +92,8 @@ void CMSWindowsSecondaryScreen::close() { assert(m_client != NULL); + log((CLOG_INFO "closing screen")); + // close the display closeDisplay(); @@ -95,6 +105,8 @@ void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) { assert(m_window != NULL); + log((CLOG_INFO "entering screen at %d,%d", x, y)); + // warp to requested location SInt32 w, h; getScreenSize(&w, &h); @@ -104,6 +116,7 @@ void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) 0, 0); // show cursor + log((CLOG_INFO "show cursor")); ShowWindow(m_window, SW_HIDE); } @@ -111,6 +124,8 @@ void CMSWindowsSecondaryScreen::leave() { assert(m_window != NULL); + log((CLOG_INFO "leaving screen")); + // move hider window under the mouse (rather than moving the mouse // somewhere else on the screen) POINT point; @@ -118,10 +133,27 @@ void CMSWindowsSecondaryScreen::leave() MoveWindow(m_window, point.x, point.y, 1, 1, FALSE); // raise and show the hider window. take activation. + log((CLOG_INFO "hide cursor")); ShowWindow(m_window, SW_SHOWNORMAL); - // hide cursor by moving it into the hider window - SetCursorPos(point.x, point.y); + // if we think we own the clipboard but we don't then somebody + // grabbed the clipboard on this screen without us knowing. + // tell the server that this screen grabbed the clipboard. + // + // this works around bugs in the clipboard viewer chain. + // sometimes NT will simply never send WM_DRAWCLIPBOARD + // messages for no apparent reason and rebooting fixes the + // problem. since we don't want a broken clipboard until the + // next reboot we do this double check. clipboard ownership + // won't be reflected on other screens until we leave but at + // least the clipboard itself will work. + HWND clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != clipboardOwner) { + m_clipboardOwner = clipboardOwner; + if (m_clipboardOwner != m_window) { + m_client->onClipboardChanged(); + } + } } void CMSWindowsSecondaryScreen::keyDown( @@ -222,6 +254,25 @@ void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); } +void CMSWindowsSecondaryScreen::setClipboard( + const IClipboard* src) +{ + assert(m_window != NULL); + + CMSWindowsClipboard dst(m_window); + CClipboard::copy(&dst, src); +} + +void CMSWindowsSecondaryScreen::grabClipboard() +{ + assert(m_window != NULL); + + CMSWindowsClipboard clipboard(m_window); + if (clipboard.open()) { + clipboard.close(); + } +} + void CMSWindowsSecondaryScreen::getSize( SInt32* width, SInt32* height) const { @@ -233,6 +284,15 @@ SInt32 CMSWindowsSecondaryScreen::getJumpZoneSize() const return 0; } +void CMSWindowsSecondaryScreen::getClipboard( + IClipboard* dst) const +{ + assert(m_window != NULL); + + CMSWindowsClipboard src(m_window); + CClipboard::copy(dst, &src); +} + #include "resource.h" // FIXME void CMSWindowsSecondaryScreen::onOpenDisplay() @@ -246,6 +306,10 @@ s_debugLog = ::GetDlgItem(s_debug, IDC_LOG); CLog::setOutputter(&debugOutput); ShowWindow(s_debug, SW_SHOWNORMAL); + // initialize clipboard owner to current owner. we don't want + // to take ownership of the clipboard just by starting up. + m_clipboardOwner = GetClipboardOwner(); + // 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 @@ -253,19 +317,26 @@ ShowWindow(s_debug, SW_SHOWNORMAL); m_window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, (LPCTSTR)getClass(), "Synergy", - WS_POPUP | WS_DISABLED, + WS_POPUP, 0, 0, 1, 1, NULL, NULL, getInstance(), NULL); // hide the cursor leave(); + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); } void CMSWindowsSecondaryScreen::onCloseDisplay() { assert(m_window != NULL); + // remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + // destroy window DestroyWindow(m_window); m_window = NULL; @@ -276,49 +347,56 @@ s_debug = NULL; s_thread = 0; } -bool CMSWindowsSecondaryScreen::onEvent(MSG* msg) +bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg) { if (IsDialogMessage(s_debug, msg)) { return true; } + return false; +} - // handle event - switch (msg->message) { + +LRESULT CMSWindowsSecondaryScreen::onEvent( + HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + switch (msg) { // 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; + ValidateRect(hwnd, NULL); + return 0; case WM_ACTIVATEAPP: - if (msg->wParam == FALSE) { + if (wParam == FALSE) { // some other app activated. hide the hider window. + log((CLOG_INFO "show cursor")); ShowWindow(m_window, SW_HIDE); } break; -/* - // FIXME -- handle screen resolution changes + case WM_DRAWCLIPBOARD: + log((CLOG_DEBUG "clipboard was taken")); - case SelectionClear: - target->XXX(xevent.xselectionclear.); - break; + // first pass it on + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); - case SelectionNotify: - target->XXX(xevent.xselection.); - break; + // now notify client that somebody changed the clipboard (unless + // we're now the owner, in which case it's because we took + // ownership). + m_clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != m_window) { + m_client->onClipboardChanged(); + } + return 0; - case SelectionRequest: - target->XXX(xevent.xselectionrequest.); - break; -*/ + case WM_CHANGECBCHAIN: + if (m_nextClipboardWindow == (HWND)wParam) + m_nextClipboardWindow = (HWND)lParam; + else + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + return 0; } - - return false; + return DefWindowProc(hwnd, msg, wParam, lParam); } static const UINT g_latin1[] = @@ -745,9 +823,11 @@ static const UINT g_miscellany[] = /* 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, +/* FIXME -- want to use LSHIFT, LCONTROL, and LMENU but those don't seem + * to affect the shift state for VkKeyScan. */ + /* 0xe0 */ 0, VK_SHIFT, VK_RSHIFT, VK_CONTROL, /* 0xe4 */ VK_RCONTROL, VK_CAPITAL, 0, VK_LWIN, - /* 0xe8 */ VK_RWIN, VK_LMENU, VK_RMENU, 0, 0, 0, 0, 0, + /* 0xe8 */ VK_RWIN, VK_MENU, 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 }; diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 4362d400..12763e60 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -23,12 +23,16 @@ class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScre virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); virtual void mouseWheel(SInt32 delta); + virtual void setClipboard(const IClipboard*); + virtual void grabClipboard(); 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 bool onPreTranslate(MSG*); + virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); virtual void onOpenDisplay(); virtual void onCloseDisplay(); @@ -38,6 +42,8 @@ class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScre private: CClient* m_client; HWND m_window; + HWND m_nextClipboardWindow; + HWND m_clipboardOwner; }; #endif diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index d4b99bd9..beb143df 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -19,7 +19,7 @@ CXWindowsSecondaryScreen::CXWindowsSecondaryScreen() : CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() { - assert(m_window == None); + assert(m_window == None); } void CXWindowsSecondaryScreen::run() @@ -43,21 +43,46 @@ void CXWindowsSecondaryScreen::run() break; } -/* - // FIXME -- handle screen resolution changes - case SelectionClear: - target->XXX(xevent.xselectionclear.); + // we just lost the selection. that means someone else + // grabbed the selection so this screen is now the + // selection owner. report that to the server. + m_client->onClipboardChanged(); break; case SelectionNotify: - target->XXX(xevent.xselection.); + // notification of selection transferred. we shouldn't + // get this here because we handle them in the selection + // retrieval methods. we'll just delete the property + // with the data (satisfying the usual ICCCM protocol). + if (xevent.xselection.property != None) { + CDisplayLock display(this); + XDeleteProperty(display, m_window, xevent.xselection.property); + } break; case SelectionRequest: - target->XXX(xevent.xselectionrequest.); + // somebody is asking for clipboard data + if (xevent.xselectionrequest.owner == m_window) { + addClipboardRequest(m_window, + xevent.xselectionrequest.requestor, + xevent.xselectionrequest.selection, + xevent.xselectionrequest.target, + xevent.xselectionrequest.property, + xevent.xselectionrequest.time); + } + break; + + case PropertyNotify: + // clipboard transfers involve property changes so forward + // the event to the superclass. we only care about the + // deletion of properties. + if (xevent.xproperty.state == PropertyDelete) { + processClipboardRequest(xevent.xproperty.window, + xevent.xproperty.atom, + xevent.xproperty.time); + } break; -*/ } } } @@ -167,6 +192,19 @@ void CXWindowsSecondaryScreen::mouseWheel(SInt32) // FIXME } +void CXWindowsSecondaryScreen::setClipboard( + const IClipboard* clipboard) +{ + // FIXME -- don't use CurrentTime + setDisplayClipboard(clipboard, m_window, CurrentTime); +} + +void CXWindowsSecondaryScreen::grabClipboard() +{ + // FIXME -- don't use CurrentTime + setDisplayClipboard(NULL, m_window, CurrentTime); +} + void CXWindowsSecondaryScreen::getSize( SInt32* width, SInt32* height) const { @@ -178,6 +216,13 @@ SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const return 0; } +void CXWindowsSecondaryScreen::getClipboard( + IClipboard* clipboard) const +{ + // FIXME -- don't use CurrentTime + getDisplayClipboard(clipboard, m_window, CurrentTime); +} + void CXWindowsSecondaryScreen::onOpenDisplay() { assert(m_window == None); diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 2ee6989b..11586f26 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -23,8 +23,11 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); virtual void mouseWheel(SInt32 delta); + virtual void setClipboard(const IClipboard*); + virtual void grabClipboard(); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; + virtual void getClipboard(IClipboard*) const; protected: // CXWindowsScreen overrides diff --git a/client/client.cpp b/client/client.cpp index abca2c8f..6cbf991c 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -41,7 +41,7 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) } try { - realMain("ingrid", __argv[1], 50001); + realMain("secondary", __argv[1], 50001); return 0; } catch (XBase& e) { @@ -64,7 +64,7 @@ int main(int argc, char** argv) } try { - realMain("ingrid", argv[1], 50001); + realMain("secondary", argv[1], 50001); return 0; } catch (XBase& e) { diff --git a/client/client.dsp b/client/client.dsp index 905b486a..b7ffa272 100644 --- a/client/client.dsp +++ b/client/client.dsp @@ -38,7 +38,7 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" +# 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 @@ -64,7 +64,7 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" +# 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 diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp index 4eac18f5..7093ce3e 100644 --- a/net/CNetwork.cpp +++ b/net/CNetwork.cpp @@ -44,6 +44,7 @@ int (PASCAL FAR *CNetwork::gethosterror)(void); #if defined(CONFIG_PLATFORM_WIN32) +int (PASCAL FAR *CNetwork::select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); int (PASCAL FAR *CNetwork::WSACleanup)(void); int (PASCAL FAR *CNetwork::__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); const int CNetwork::Error = SOCKET_ERROR; @@ -220,6 +221,7 @@ int PASCAL FAR CNetwork::poll2(PollEntry fd[], int nfds, int timeout) return Error; if (n == 0) return 0; + n = 0; for (i = 0; i < nfds; ++i) { fd[i].revents = 0; if (FD_ISSET(fd[i].fd, &readSet)) @@ -228,6 +230,8 @@ int PASCAL FAR CNetwork::poll2(PollEntry fd[], int nfds, int timeout) fd[i].revents |= kPOLLOUT; if (FD_ISSET(fd[i].fd, &errSet)) fd[i].revents |= kPOLLERR; + if (fd[i].revents != 0) + ++n; } return n; } diff --git a/notes b/notes index 14edcc1b..f4933c81 100644 --- a/notes +++ b/notes @@ -87,11 +87,12 @@ win32: handle display changes --- -win32 key translation (client) - get character for key (with appropriate shift) - keypad enter -> VK_??? - sys req -> VK_??? - compose -> VK_??? +not sending VK_?WIN and VK_APPS. possibly hotkeys being stolen. + +VkKeyScan() doesn't get proper shift state unless we map shift +(etc?) to VK_SHIFT not VK_LSHIFT or VK_RSHIFT. + +not handling international characters X11 key translation (server) handle compose key? diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 1580de71..d2775577 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -1,6 +1,8 @@ #include "CMSWindowsPrimaryScreen.h" +#include "CMSWindowsClipboard.h" #include "CServer.h" #include "CSynergyHook.h" +#include "XSynergy.h" #include "CThread.h" #include "CLog.h" #include @@ -13,6 +15,8 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() : m_server(NULL), m_active(false), m_window(NULL), + m_nextClipboardWindow(NULL), + m_clipboardOwner(NULL), m_hookLibrary(NULL), m_mark(0), m_markReceived(0) @@ -26,6 +30,8 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() assert(m_hookLibrary == NULL); } +static CString s_log; +static CString s_logMore; static HWND s_debug = NULL; static HWND s_debugLog = NULL; static DWORD s_thread = 0; @@ -38,28 +44,26 @@ static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lPar case WM_CLOSE: PostQuitMessage(0); return TRUE; + + case WM_APP: + if (!s_logMore.empty()) { + if (s_log.size() > 20000) + s_log = s_logMore; + else + s_log += s_logMore; + s_logMore = ""; + SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)s_log.c_str()); + SendMessage(s_debugLog, EM_SETSEL, s_log.size(), s_log.size()); + SendMessage(s_debugLog, EM_SCROLLCARET, 0, 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); + s_logMore += msg; + PostMessage(s_debug, WM_APP, 0, 0); } void CMSWindowsPrimaryScreen::run() @@ -114,6 +118,13 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) void CMSWindowsPrimaryScreen::doEnter() { + // release the capture + ReleaseCapture(); + + // hide our window and restore the foreground window + SetForegroundWindow(m_lastActive); + ShowWindow(m_window, SW_HIDE); + // set the zones that should cause a jump SInt32 w, h; getScreenSize(&w, &h); @@ -136,6 +147,16 @@ void CMSWindowsPrimaryScreen::leave() // all messages prior to now are invalid nextMark(); + // remember the active window before we leave + m_lastActive = GetForegroundWindow(); + + // show our window and put it in the foreground + ShowWindow(m_window, SW_SHOW); + SetForegroundWindow(m_window); + + // capture the cursor so we don't lose keyboard input + SetCapture(m_window); + // relay all mouse and keyboard events SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress( m_hookLibrary, "setRelay"); @@ -151,6 +172,30 @@ void CMSWindowsPrimaryScreen::leave() // local client now active m_active = true; + + // if we think we own the clipboard but we don't then somebody + // grabbed the clipboard on this screen without us knowing. + // tell the server that this screen grabbed the clipboard. + // + // this works around bugs in the clipboard viewer chain. + // sometimes NT will simply never send WM_DRAWCLIPBOARD + // messages for no apparent reason and rebooting fixes the + // problem. since we don't want a broken clipboard until the + // next reboot we do this double check. clipboard ownership + // won't be reflected on other screens until we leave but at + // least the clipboard itself will work. + HWND clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != clipboardOwner) { + try { + m_clipboardOwner = clipboardOwner; + if (m_clipboardOwner != m_window) { + m_server->grabClipboard(); + } + } + catch (XBadClient&) { + // ignore + } + } } void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) @@ -164,18 +209,22 @@ void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) } void CMSWindowsPrimaryScreen::setClipboard( - const IClipboard* /*clipboard*/) + const IClipboard* src) { 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 + CMSWindowsClipboard dst(m_window); + CClipboard::copy(&dst, src); +} + +void CMSWindowsPrimaryScreen::grabClipboard() +{ + assert(m_window != NULL); + + CMSWindowsClipboard clipboard(m_window); + if (clipboard.open()) { + clipboard.close(); } - CloseClipboard(); } void CMSWindowsPrimaryScreen::getSize( @@ -190,11 +239,12 @@ SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const } void CMSWindowsPrimaryScreen::getClipboard( - IClipboard* clipboard) const + IClipboard* dst) const { - // FIXME -- put this in superclass? - // FIXME -- don't use CurrentTime -// getDisplayClipboard(clipboard, m_window, CurrentTime); + assert(m_window != NULL); + + CMSWindowsClipboard src(m_window); + CClipboard::copy(dst, &src); } #include "resource.h" // FIXME @@ -211,6 +261,10 @@ s_debugLog = ::GetDlgItem(s_debug, IDC_LOG); CLog::setOutputter(&debugOutput); ShowWindow(s_debug, SW_SHOWNORMAL); + // initialize clipboard owner to current owner. we don't want + // to take ownership of the clipboard just by starting up. + m_clipboardOwner = GetClipboardOwner(); + // create the window m_window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, @@ -219,6 +273,10 @@ ShowWindow(s_debug, SW_SHOWNORMAL); 0, 0, 1, 1, NULL, NULL, getInstance(), NULL); + assert(m_window != NULL); + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); // load the hook library bool hooked = false; @@ -232,6 +290,8 @@ ShowWindow(s_debug, SW_SHOWNORMAL); } } if (!hooked) { + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; DestroyWindow(m_window); m_window = NULL; // FIXME -- throw @@ -258,6 +318,10 @@ void CMSWindowsPrimaryScreen::onCloseDisplay() FreeLibrary(m_hookLibrary); m_hookLibrary = NULL; + // remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + // destroy window DestroyWindow(m_window); m_window = NULL; @@ -268,7 +332,7 @@ s_debug = NULL; s_thread = 0; } -bool CMSWindowsPrimaryScreen::onEvent(MSG* msg) +bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) { if (IsDialogMessage(s_debug, msg)) { return true; @@ -276,11 +340,6 @@ if (IsDialogMessage(s_debug, msg)) { // 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; @@ -288,7 +347,29 @@ if (IsDialogMessage(s_debug, msg)) { case SYNERGY_MSG_KEY: // ignore if not at current mark if (m_mark == m_markReceived) { - // FIXME -- vk code; key data + KeyModifierMask mask; + const KeyID key = mapKey(msg->wParam, msg->lParam, &mask); +log((CLOG_DEBUG "event: key event vk=%d info=0x%08x", msg->wParam, msg->lParam)); + if (key != kKeyNone) { + if ((msg->lParam & 0x80000000) == 0) { + // key press + const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); + if (repeat >= 2) { + log((CLOG_DEBUG "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); + m_server->onKeyRepeat(key, mask, repeat); + } + else { + log((CLOG_DEBUG "event: key press key=%d mask=0x%04x", key, mask)); + m_server->onKeyDown(key, mask); + } + } + else { + // key release + log((CLOG_DEBUG "event: key release key=%d mask=0x%04x", key, mask)); + m_server->onKeyUp(key, mask); + } + } + } return true; @@ -357,74 +438,46 @@ if (IsDialogMessage(s_debug, msg)) { } 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); +} + +LRESULT CMSWindowsPrimaryScreen::onEvent( + HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + // FIXME -- handle display changes + case WM_PAINT: + ValidateRect(hwnd, NULL); + return 0; + + case WM_DRAWCLIPBOARD: + log((CLOG_DEBUG "clipboard was taken")); + + // first pass it on + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + + // now notify server that somebody changed the clipboard. + // skip that if we're the new owner. + try { + m_clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != m_window) { + m_server->grabClipboard(); + } } - 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); + catch (XBadClient&) { + // ignore. this can happen if we receive this event + // before we've fully started up. } - break; - } + return 0; - // 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; + case WM_CHANGECBCHAIN: + if (m_nextClipboardWindow == (HWND)wParam) + m_nextClipboardWindow = (HWND)lParam; + else + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); -*/ } void CMSWindowsPrimaryScreen::nextMark() @@ -434,34 +487,274 @@ void CMSWindowsPrimaryScreen::nextMark() PostMessage(m_window, SYNERGY_MSG_MARK, ++m_mark, 0); } -#if 0 -bool CMSWindowsPrimaryScreen::keyboardHook( - int /*code*/, WPARAM wParam, LPARAM lParam) +static const KeyID g_virtualKey[] = { - 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 + /* 0x00 */ kKeyNone, // reserved + /* 0x01 */ kKeyNone, // VK_LBUTTON + /* 0x02 */ kKeyNone, // VK_RBUTTON + /* 0x03 */ 0xff6b, // VK_CANCEL XK_Break + /* 0x04 */ kKeyNone, // VK_MBUTTON + /* 0x05 */ kKeyNone, // undefined + /* 0x06 */ kKeyNone, // undefined + /* 0x07 */ kKeyNone, // undefined + /* 0x08 */ 0xff08, // VK_BACK XK_Backspace + /* 0x09 */ 0xff09, // VK_TAB VK_Tab + /* 0x0a */ kKeyNone, // undefined + /* 0x0b */ kKeyNone, // undefined + /* 0x0c */ 0xff0b, // VK_CLEAR XK_Clear + /* 0x0d */ 0xff0d, // VK_RETURN XK_Return + /* 0x0e */ kKeyNone, // undefined + /* 0x0f */ kKeyNone, // undefined + /* 0x10 */ 0xffe1, // VK_SHIFT XK_Shift_L + /* 0x11 */ 0xffe3, // VK_CONTROL XK_Control_L + /* 0x12 */ 0xffe9, // VK_MENU XK_Alt_L + /* 0x13 */ 0xff13, // VK_PAUSE XK_Pause + /* 0x14 */ 0xffe5, // VK_CAPITAL XK_Caps_Lock + /* 0x15 */ kKeyNone, // VK_KANA + /* 0x16 */ kKeyNone, // VK_HANGUL + /* 0x17 */ kKeyNone, // VK_JUNJA + /* 0x18 */ kKeyNone, // VK_FINAL + /* 0x19 */ kKeyNone, // VK_KANJI + /* 0x1a */ kKeyNone, // undefined + /* 0x1b */ 0xff1b, // VK_ESCAPE XK_Escape + /* 0x1c */ kKeyNone, // VK_CONVERT + /* 0x1d */ kKeyNone, // VK_NONCONVERT + /* 0x1e */ kKeyNone, // VK_ACCEPT + /* 0x1f */ kKeyNone, // VK_MODECHANGE + /* 0x20 */ 0xff20, // VK_SPACE XK_space + /* 0x21 */ 0xff55, // VK_PRIOR XK_Prior + /* 0x22 */ 0xff56, // VK_NEXT XK_Next + /* 0x23 */ 0xff57, // VK_END XK_End + /* 0x24 */ 0xff50, // VK_HOME XK_Home + /* 0x25 */ 0xff51, // VK_LEFT XK_Left + /* 0x26 */ 0xff52, // VK_UP XK_Up + /* 0x27 */ 0xff53, // VK_RIGHT XK_Right + /* 0x28 */ 0xff54, // VK_DOWN XK_Down + /* 0x29 */ 0xff60, // VK_SELECT XK_Select + /* 0x2a */ kKeyNone, // VK_PRINT + /* 0x2b */ 0xff62, // VK_EXECUTE XK_Execute + /* 0x2c */ 0xff61, // VK_SNAPSHOT XK_Print + /* 0x2d */ 0xff63, // VK_INSERT XK_Insert + /* 0x2e */ 0xffff, // VK_DELETE XK_Delete + /* 0x2f */ 0xff6a, // VK_HELP XK_Help + /* 0x30 */ 0x0030, // VK_0 XK_0 + /* 0x31 */ 0x0031, // VK_1 XK_1 + /* 0x32 */ 0x0032, // VK_2 XK_2 + /* 0x33 */ 0x0033, // VK_3 XK_3 + /* 0x34 */ 0x0034, // VK_4 XK_4 + /* 0x35 */ 0x0035, // VK_5 XK_5 + /* 0x36 */ 0x0036, // VK_6 XK_6 + /* 0x37 */ 0x0037, // VK_7 XK_7 + /* 0x38 */ 0x0038, // VK_8 XK_8 + /* 0x39 */ 0x0039, // VK_9 XK_9 + /* 0x3a */ kKeyNone, // undefined + /* 0x3b */ kKeyNone, // undefined + /* 0x3c */ kKeyNone, // undefined + /* 0x3d */ kKeyNone, // undefined + /* 0x3e */ kKeyNone, // undefined + /* 0x3f */ kKeyNone, // undefined + /* 0x40 */ kKeyNone, // undefined + /* 0x41 */ 0x0041, // VK_A XK_A + /* 0x42 */ 0x0042, // VK_B XK_B + /* 0x43 */ 0x0043, // VK_C XK_C + /* 0x44 */ 0x0044, // VK_D XK_D + /* 0x45 */ 0x0045, // VK_E XK_E + /* 0x46 */ 0x0046, // VK_F XK_F + /* 0x47 */ 0x0047, // VK_G XK_G + /* 0x48 */ 0x0048, // VK_H XK_H + /* 0x49 */ 0x0049, // VK_I XK_I + /* 0x4a */ 0x004a, // VK_J XK_J + /* 0x4b */ 0x004b, // VK_K XK_K + /* 0x4c */ 0x004c, // VK_L XK_L + /* 0x4d */ 0x004d, // VK_M XK_M + /* 0x4e */ 0x004e, // VK_N XK_N + /* 0x4f */ 0x004f, // VK_O XK_O + /* 0x50 */ 0x0050, // VK_P XK_P + /* 0x51 */ 0x0051, // VK_Q XK_Q + /* 0x52 */ 0x0052, // VK_R XK_R + /* 0x53 */ 0x0053, // VK_S XK_S + /* 0x54 */ 0x0054, // VK_T XK_T + /* 0x55 */ 0x0055, // VK_U XK_U + /* 0x56 */ 0x0056, // VK_V XK_V + /* 0x57 */ 0x0057, // VK_W XK_W + /* 0x58 */ 0x0058, // VK_X XK_X + /* 0x59 */ 0x0059, // VK_Y XK_Y + /* 0x5a */ 0x005a, // VK_Z XK_Z + /* 0x5b */ 0xffe7, // VK_LWIN XK_Meta_L + /* 0x5c */ 0xffe8, // VK_RWIN XK_Meta_R + /* 0x5d */ 0xff67, // VK_APPS XK_Menu + /* 0x5e */ kKeyNone, // undefined + /* 0x5f */ kKeyNone, // undefined + /* 0x60 */ 0xffb0, // VK_NUMPAD0 XK_KP_0 + /* 0x61 */ 0xffb1, // VK_NUMPAD1 XK_KP_1 + /* 0x62 */ 0xffb2, // VK_NUMPAD2 XK_KP_2 + /* 0x63 */ 0xffb3, // VK_NUMPAD3 XK_KP_3 + /* 0x64 */ 0xffb4, // VK_NUMPAD4 XK_KP_4 + /* 0x65 */ 0xffb5, // VK_NUMPAD5 XK_KP_5 + /* 0x66 */ 0xffb6, // VK_NUMPAD6 XK_KP_6 + /* 0x67 */ 0xffb7, // VK_NUMPAD7 XK_KP_7 + /* 0x68 */ 0xffb8, // VK_NUMPAD8 XK_KP_8 + /* 0x69 */ 0xffb9, // VK_NUMPAD9 XK_KP_9 + /* 0x6a */ 0xffaa, // VK_MULTIPLY XK_KP_Multiply + /* 0x6b */ 0xffab, // VK_ADD XK_KP_Add + /* 0x6c */ 0xffac, // VK_SEPARATOR XK_KP_Separator + /* 0x6d */ 0xffad, // VK_SUBTRACT XK_KP_Subtract + /* 0x6e */ 0xffae, // VK_DECIMAL XK_KP_Decimal + /* 0x6f */ 0xffaf, // VK_DIVIDE XK_KP_Divide + /* 0x70 */ 0xffbe, // VK_F1 XK_F1 + /* 0x71 */ 0xffbf, // VK_F2 XK_F2 + /* 0x72 */ 0xffc0, // VK_F3 XK_F3 + /* 0x73 */ 0xffc1, // VK_F4 XK_F4 + /* 0x74 */ 0xffc2, // VK_F5 XK_F5 + /* 0x75 */ 0xffc3, // VK_F6 XK_F6 + /* 0x76 */ 0xffc4, // VK_F7 XK_F7 + /* 0x77 */ 0xffc5, // VK_F8 XK_F8 + /* 0x78 */ 0xffc6, // VK_F9 XK_F9 + /* 0x79 */ 0xffc7, // VK_F10 XK_F10 + /* 0x7a */ 0xffc8, // VK_F11 XK_F11 + /* 0x7b */ 0xffc9, // VK_F12 XK_F12 + /* 0x7c */ 0xffca, // VK_F13 XK_F13 + /* 0x7d */ 0xffcb, // VK_F14 XK_F14 + /* 0x7e */ 0xffcc, // VK_F15 XK_F15 + /* 0x7f */ 0xffcd, // VK_F16 XK_F16 + /* 0x80 */ 0xffce, // VK_F17 XK_F17 + /* 0x81 */ 0xffcf, // VK_F18 XK_F18 + /* 0x82 */ 0xffd0, // VK_F19 XK_F19 + /* 0x83 */ 0xffd1, // VK_F20 XK_F20 + /* 0x84 */ 0xffd2, // VK_F21 XK_F21 + /* 0x85 */ 0xffd3, // VK_F22 XK_F22 + /* 0x86 */ 0xffd4, // VK_F23 XK_F23 + /* 0x87 */ 0xffd5, // VK_F24 XK_F24 + /* 0x88 */ kKeyNone, // unassigned + /* 0x89 */ kKeyNone, // unassigned + /* 0x8a */ kKeyNone, // unassigned + /* 0x8b */ kKeyNone, // unassigned + /* 0x8c */ kKeyNone, // unassigned + /* 0x8d */ kKeyNone, // unassigned + /* 0x8e */ kKeyNone, // unassigned + /* 0x8f */ kKeyNone, // unassigned + /* 0x90 */ 0xff7f, // VK_NUMLOCK XK_Num_Lock + /* 0x91 */ 0xff14, // VK_SCROLL XK_Scroll_Lock + /* 0x92 */ kKeyNone, // unassigned + /* 0x93 */ kKeyNone, // unassigned + /* 0x94 */ kKeyNone, // unassigned + /* 0x95 */ kKeyNone, // unassigned + /* 0x96 */ kKeyNone, // unassigned + /* 0x97 */ kKeyNone, // unassigned + /* 0x98 */ kKeyNone, // unassigned + /* 0x99 */ kKeyNone, // unassigned + /* 0x9a */ kKeyNone, // unassigned + /* 0x9b */ kKeyNone, // unassigned + /* 0x9c */ kKeyNone, // unassigned + /* 0x9d */ kKeyNone, // unassigned + /* 0x9e */ kKeyNone, // unassigned + /* 0x9f */ kKeyNone, // unassigned + /* 0xa0 */ kKeyNone, // unassigned + /* 0xa1 */ kKeyNone, // unassigned + /* 0xa2 */ kKeyNone, // unassigned + /* 0xa3 */ kKeyNone, // unassigned + /* 0xa4 */ kKeyNone, // unassigned + /* 0xa5 */ kKeyNone, // unassigned + /* 0xa6 */ kKeyNone, // unassigned + /* 0xa7 */ kKeyNone, // unassigned + /* 0xa8 */ kKeyNone, // unassigned + /* 0xa9 */ kKeyNone, // unassigned + /* 0xaa */ kKeyNone, // unassigned + /* 0xab */ kKeyNone, // unassigned + /* 0xac */ kKeyNone, // unassigned + /* 0xad */ kKeyNone, // unassigned + /* 0xae */ kKeyNone, // unassigned + /* 0xaf */ kKeyNone, // unassigned + /* 0xb0 */ kKeyNone, // unassigned + /* 0xb1 */ kKeyNone, // unassigned + /* 0xb2 */ kKeyNone, // unassigned + /* 0xb3 */ kKeyNone, // unassigned + /* 0xb4 */ kKeyNone, // unassigned + /* 0xb5 */ kKeyNone, // unassigned + /* 0xb6 */ kKeyNone, // unassigned + /* 0xb7 */ kKeyNone, // unassigned + /* 0xb8 */ kKeyNone, // unassigned + /* 0xb9 */ kKeyNone, // unassigned + /* 0xba */ kKeyNone, // OEM specific + /* 0xbb */ kKeyNone, // OEM specific + /* 0xbc */ kKeyNone, // OEM specific + /* 0xbd */ kKeyNone, // OEM specific + /* 0xbe */ kKeyNone, // OEM specific + /* 0xbf */ kKeyNone, // OEM specific + /* 0xc0 */ kKeyNone, // OEM specific + /* 0xc1 */ kKeyNone, // unassigned + /* 0xc2 */ kKeyNone, // unassigned + /* 0xc3 */ kKeyNone, // unassigned + /* 0xc4 */ kKeyNone, // unassigned + /* 0xc5 */ kKeyNone, // unassigned + /* 0xc6 */ kKeyNone, // unassigned + /* 0xc7 */ kKeyNone, // unassigned + /* 0xc8 */ kKeyNone, // unassigned + /* 0xc9 */ kKeyNone, // unassigned + /* 0xca */ kKeyNone, // unassigned + /* 0xcb */ kKeyNone, // unassigned + /* 0xcc */ kKeyNone, // unassigned + /* 0xcd */ kKeyNone, // unassigned + /* 0xce */ kKeyNone, // unassigned + /* 0xcf */ kKeyNone, // unassigned + /* 0xd0 */ kKeyNone, // unassigned + /* 0xd1 */ kKeyNone, // unassigned + /* 0xd2 */ kKeyNone, // unassigned + /* 0xd3 */ kKeyNone, // unassigned + /* 0xd4 */ kKeyNone, // unassigned + /* 0xd5 */ kKeyNone, // unassigned + /* 0xd6 */ kKeyNone, // unassigned + /* 0xd7 */ kKeyNone, // unassigned + /* 0xd8 */ kKeyNone, // unassigned + /* 0xd9 */ kKeyNone, // unassigned + /* 0xda */ kKeyNone, // unassigned + /* 0xdb */ kKeyNone, // OEM specific + /* 0xdc */ kKeyNone, // OEM specific + /* 0xdd */ kKeyNone, // OEM specific + /* 0xde */ kKeyNone, // OEM specific + /* 0xdf */ kKeyNone, // OEM specific + /* 0xe0 */ kKeyNone, // OEM specific + /* 0xe1 */ kKeyNone, // OEM specific + /* 0xe2 */ kKeyNone, // OEM specific + /* 0xe3 */ kKeyNone, // OEM specific + /* 0xe4 */ kKeyNone, // OEM specific + /* 0xe5 */ kKeyNone, // unassigned + /* 0xe6 */ kKeyNone, // OEM specific + /* 0xe7 */ kKeyNone, // unassigned + /* 0xe8 */ kKeyNone, // unassigned + /* 0xe9 */ kKeyNone, // OEM specific + /* 0xea */ kKeyNone, // OEM specific + /* 0xeb */ kKeyNone, // OEM specific + /* 0xec */ kKeyNone, // OEM specific + /* 0xed */ kKeyNone, // OEM specific + /* 0xee */ kKeyNone, // OEM specific + /* 0xef */ kKeyNone, // OEM specific + /* 0xf0 */ kKeyNone, // OEM specific + /* 0xf1 */ kKeyNone, // OEM specific + /* 0xf2 */ kKeyNone, // OEM specific + /* 0xf3 */ kKeyNone, // OEM specific + /* 0xf4 */ kKeyNone, // OEM specific + /* 0xf5 */ kKeyNone, // OEM specific + /* 0xf6 */ kKeyNone, // VK_ATTN + /* 0xf7 */ kKeyNone, // VK_CRSEL + /* 0xf8 */ kKeyNone, // VK_EXSEL + /* 0xf9 */ kKeyNone, // VK_EREOF + /* 0xfa */ kKeyNone, // VK_PLAY + /* 0xfb */ kKeyNone, // VK_ZOOM + /* 0xfc */ kKeyNone, // reserved + /* 0xfd */ kKeyNone, // VK_PA1 + /* 0xfe */ kKeyNone, // VK_OEM_CLEAR + /* 0xff */ kKeyNone // reserved +}; -KeyModifierMask CMSWindowsPrimaryScreen::mapModifier( - WPARAM keycode, LPARAM info) const +KeyID CMSWindowsPrimaryScreen::mapKey( + WPARAM vkCode, LPARAM info, + KeyModifierMask* maskOut) const { - // FIXME -- should be configurable + assert(maskOut != NULL); + + // map modifier key + // FIXME -- should be configurable? KeyModifierMask mask = 0; if (GetKeyState(VK_SHIFT) < 0) mask |= KeyModifierShift; @@ -477,14 +770,48 @@ KeyModifierMask CMSWindowsPrimaryScreen::mapModifier( mask |= KeyModifierMeta; if ((GetKeyState(VK_SCROLL) & 1) != 0) mask |= KeyModifierScrollLock; - return mask; -} + *maskOut = mask; -KeyID CMSWindowsPrimaryScreen::mapKey( - WPARAM keycode, LPARAM info) const -{ - // FIXME -- must convert to X keysyms - return keycode; + KeyID id = g_virtualKey[vkCode]; + if (id != kKeyNone) { + return id; + } + + BYTE state[256]; + GetKeyboardState(state); + WORD ascii; + int result = ToAscii(vkCode, MapVirtualKey(0, vkCode), state, &ascii, 0); + if (result > 0) { + // FIXME -- handle dead keys + return (KeyID)(ascii & 0x00ff); + } + + return kKeyNone; + +/* + UINT character = MapVirtualKey(2, vkCode); + if (character != 0) { + if ((character & ~0xff) != 0) { + // dead key (i.e. a key to compose with the next) + // FIXME + return kKeyNone; + } + else { + // map character + KeyID id = g_virtualKey[character & 0xff]; + + // uppercase to lowercase conversion + if ((mask & KeyModifierShift) == 0 && id >= 'A' && id <= 'Z') + id += 0x20; + + return id; + } + } + else { + // non-ascii key + return g_virtualKey[vkCode]; + } +*/ } ButtonID CMSWindowsPrimaryScreen::mapButton( diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index d34b44bf..fd96d5d5 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -22,13 +22,15 @@ class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { virtual void leave(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(const IClipboard*); + virtual void grabClipboard(); 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 bool onPreTranslate(MSG*); + virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); virtual void onOpenDisplay(); virtual void onCloseDisplay(); @@ -37,17 +39,17 @@ class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { 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; + KeyID mapKey(WPARAM keycode, LPARAM info, + KeyModifierMask* maskOut) const; ButtonID mapButton(WPARAM button) const; private: CServer* m_server; bool m_active; HWND m_window; + HWND m_nextClipboardWindow; + HWND m_clipboardOwner; + HWND m_lastActive; HINSTANCE m_hookLibrary; UInt32 m_mark; UInt32 m_markReceived; diff --git a/server/CServer.cpp b/server/CServer.cpp index 42df94f8..10158e26 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -43,7 +43,9 @@ else { wait(0); exit(1); } CServer::CServer() : m_primary(NULL), m_active(NULL), - m_primaryInfo(NULL) + m_primaryInfo(NULL), + m_clipboardSeqNum(0), + m_clipboardReady(false) { m_socketFactory = NULL; m_securityFactory = NULL; @@ -78,7 +80,7 @@ void CServer::run() closePrimaryScreen(); } catch (XBase& e) { - log((CLOG_ERR "server error: %s\n", e.what())); + log((CLOG_ERR "server error: %s", e.what())); // clean up cleanupThreads(); @@ -148,6 +150,93 @@ void CServer::setInfo(const CString& client, log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d", client.c_str(), w, h, zoneSize)); } +void CServer::grabClipboard() +{ + grabClipboard(m_primaryInfo->m_name); +} + +void CServer::grabClipboard(const CString& client) +{ + CLock lock(&m_mutex); + + // client must be connected + CScreenList::iterator index = m_screens.find(client); + if (index == m_screens.end()) { + throw XBadClient(); + } + + log((CLOG_NOTE "client \"%s\" grabbed clipboard from \"%s\"", client.c_str(), m_clipboardOwner.c_str())); + + // save the clipboard owner + m_clipboardOwner = client; + + // mark client as having the clipboard data + index->second->m_gotClipboard = true; + + // tell all other clients to take ownership of clipboard and mark + // them as not having the data yet. + for (index = m_screens.begin(); index != m_screens.end(); ++index) { + if (index->first != client) { + CScreenInfo* info = index->second; + info->m_gotClipboard = false; + if (info->m_protocol == NULL) { + m_primary->grabClipboard(); + } + else { + info->m_protocol->sendGrabClipboard(); + } + } + } + + // increment the clipboard sequence number so we can identify the + // clipboard query's response. + ++m_clipboardSeqNum; + + // begin getting the clipboard data + if (m_active->m_protocol == NULL) { + // get clipboard immediately from primary screen + m_primary->getClipboard(&m_clipboard); + m_clipboardData = m_clipboard.marshall(); + m_clipboardReady = true; + } + else { + // clear out the clipboard since existing data is now out of date. + if (m_clipboard.open()) { + m_clipboard.close(); + } + m_clipboardReady = false; + + // send request but don't wait for reply + m_active->m_protocol->sendQueryClipboard(m_clipboardSeqNum); + } +} + +void CServer::setClipboard( + UInt32 seqNum, const CString& data) +{ + // update the clipboard if the sequence number matches + CLock lock(&m_mutex); + if (seqNum == m_clipboardSeqNum) { + // unmarshall into our clipboard buffer + m_clipboardData = data; + m_clipboard.unmarshall(m_clipboardData); + m_clipboardReady = true; + + // if the active client doesn't have the clipboard data + // (and it won't unless the client is the one sending us + // the data) then send the data now. + if (!m_active->m_gotClipboard) { + if (m_active->m_protocol == NULL) { + m_primary->setClipboard(&m_clipboard); + } + else { + m_active->m_protocol->sendClipboard(m_clipboardData); + } + m_active->m_gotClipboard = true; + } + } +} + bool CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) { @@ -186,9 +275,10 @@ void CServer::onKeyUp(KeyID id, KeyModifierMask mask) } } -void CServer::onKeyRepeat(KeyID id, KeyModifierMask mask) +void CServer::onKeyRepeat( + KeyID id, KeyModifierMask mask, SInt32 count) { - log((CLOG_DEBUG "onKeyRepeat id=%d mask=0x%04x", id, mask)); + log((CLOG_DEBUG "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count)); assert(m_active != NULL); // handle command keys @@ -199,7 +289,7 @@ void CServer::onKeyRepeat(KeyID id, KeyModifierMask mask) // relay if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendKeyRepeat(id, mask); + m_active->m_protocol->sendKeyRepeat(id, mask, count); } } @@ -385,11 +475,6 @@ 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) { @@ -398,6 +483,7 @@ void CServer::switchScreen(CScreenInfo* dst, assert(m_active != NULL); log((CLOG_NOTE "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y)); + // FIXME -- we're not locked here but we probably should be // wrapping means leaving the active screen and entering it again. // since that's a waste of time we skip that and just warp the @@ -406,14 +492,6 @@ void CServer::switchScreen(CScreenInfo* dst, // leave active screen if (m_active->m_protocol == NULL) { 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 { m_active->m_protocol->sendLeave(); @@ -429,6 +507,17 @@ void CServer::switchScreen(CScreenInfo* dst, else { m_active->m_protocol->sendEnter(x, y); } + + // send the clipboard data if we haven't done so yet + if (m_clipboardReady && !m_active->m_gotClipboard) { + if (m_active->m_protocol == NULL) { + m_primary->setClipboard(&m_clipboard); + } + else { + m_active->m_protocol->sendClipboard(m_clipboardData); + } + m_active->m_gotClipboard = true; + } } else { if (m_active->m_protocol == NULL) { @@ -827,6 +916,13 @@ void CServer::openPrimaryScreen() m_active->m_zoneSize = m_primary->getJumpZoneSize(); log((CLOG_NOTE "server size=%dx%d zone=%d", m_active->m_width, m_active->m_height, m_active->m_zoneSize)); // FIXME -- need way for primary screen to call us back + + // set the clipboard owner to the primary screen and then get the + // current clipboard data. + m_primary->getClipboard(&m_clipboard); + m_clipboardData = m_clipboard.marshall(); + m_clipboardReady = true; + m_clipboardOwner = m_active->m_name; } void CServer::closePrimaryScreen() @@ -979,7 +1075,8 @@ CServer::CScreenInfo::CScreenInfo(const CString& name, m_name(name), m_protocol(protocol), m_width(0), m_height(0), - m_zoneSize(0) + m_zoneSize(0), + m_gotClipboard(false) { // do nothing } diff --git a/server/CServer.h b/server/CServer.h index 18391aad..3ca443d2 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -4,6 +4,7 @@ #include "KeyTypes.h" #include "MouseTypes.h" #include "CScreenMap.h" +#include "CClipboard.h" #include "CMutex.h" #include "CString.h" #include "XBase.h" @@ -36,16 +37,19 @@ class CServer { // true iff the mouse enters a jump zone and jumps. void onKeyDown(KeyID, KeyModifierMask); void onKeyUp(KeyID, KeyModifierMask); - void onKeyRepeat(KeyID, KeyModifierMask); + void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); void onMouseDown(ButtonID); void onMouseUp(ButtonID); bool onMouseMovePrimary(SInt32 x, SInt32 y); void onMouseMoveSecondary(SInt32 dx, SInt32 dy); void onMouseWheel(SInt32 delta); + void grabClipboard(); // handle messages from clients void setInfo(const CString& clientName, SInt32 w, SInt32 h, SInt32 zoneSize); + void grabClipboard(const CString& clientName); + void setClipboard(UInt32 seqNum, const CString& data); // accessors @@ -91,6 +95,7 @@ class CServer { IServerProtocol* m_protocol; SInt32 m_width, m_height; SInt32 m_zoneSize; + bool m_gotClipboard; }; // change the active screen @@ -159,6 +164,12 @@ class CServer { SInt32 m_x, m_y; CScreenMap m_screenMap; + + CClipboard m_clipboard; + CString m_clipboardData; + CString m_clipboardOwner; + UInt32 m_clipboardSeqNum; + bool m_clipboardReady; }; #endif diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index 5fb552a7..d5a69a4d 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -33,11 +33,12 @@ class CServerProtocol : public IServerProtocol { virtual void sendClose() = 0; virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; virtual void sendLeave() = 0; + virtual void sendClipboard(const CString&) = 0; virtual void sendGrabClipboard() = 0; - virtual void sendQueryClipboard() = 0; + virtual void sendQueryClipboard(UInt32 seqNum) = 0; virtual void sendScreenSaver(bool on) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; - virtual void sendKeyRepeat(KeyID, KeyModifierMask) = 0; + virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; virtual void sendMouseDown(ButtonID) = 0; virtual void sendMouseUp(ButtonID) = 0; @@ -47,6 +48,8 @@ class CServerProtocol : public IServerProtocol { protected: //IServerProtocol overrides virtual void recvInfo() = 0; + virtual void recvClipboard() = 0; + virtual void recvGrabClipboard() = 0; private: CServer* m_server; diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index de28aa81..a2e02811 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -1,5 +1,6 @@ #include "CServerProtocol1_0.h" #include "CServer.h" +#include "CClipboard.h" #include "CProtocolUtil.h" #include "ProtocolTypes.h" #include "IInputStream.h" @@ -49,6 +50,12 @@ void CServerProtocol1_0::run() if (memcmp(code, kMsgDInfo, 4) == 0) { recvInfo(); } + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + recvGrabClipboard(); + } + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + recvClipboard(); + } // FIXME -- more message here else { log((CLOG_ERR "unknown message from client \"%s\"", getClient().c_str())); @@ -96,16 +103,22 @@ void CServerProtocol1_0::sendLeave() CProtocolUtil::writef(getOutputStream(), kMsgCLeave); } +void CServerProtocol1_0::sendClipboard(const CString& data) +{ + log((CLOG_INFO "send clipboard to \"%s\" size=%d", getClient().c_str(), data.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, 0, &data); +} + void CServerProtocol1_0::sendGrabClipboard() { log((CLOG_INFO "send grab clipboard to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard); } -void CServerProtocol1_0::sendQueryClipboard() +void CServerProtocol1_0::sendQueryClipboard(UInt32 seqNum) { log((CLOG_INFO "query clipboard to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgQClipboard); + CProtocolUtil::writef(getOutputStream(), kMsgQClipboard, seqNum); } void CServerProtocol1_0::sendScreenSaver(bool on) @@ -122,10 +135,10 @@ void CServerProtocol1_0::sendKeyDown( } void CServerProtocol1_0::sendKeyRepeat( - KeyID key, KeyModifierMask mask) + KeyID key, KeyModifierMask mask, SInt32 count) { log((CLOG_INFO "send key repeat to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); } void CServerProtocol1_0::sendKeyUp( @@ -179,3 +192,17 @@ void CServerProtocol1_0::recvInfo() getServer()->setInfo(getClient(), w, h, zoneInfo); } +void CServerProtocol1_0::recvClipboard() +{ + UInt32 seqNum; + CString data; + CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &seqNum, &data); + log((CLOG_INFO "received client \"%s\" clipboard seqnum=%d, size=%d", getClient().c_str(), seqNum, data.size())); + getServer()->setClipboard(seqNum, data); +} + +void CServerProtocol1_0::recvGrabClipboard() +{ + log((CLOG_INFO "received client \"%s\" grabbed clipboard", getClient().c_str())); + getServer()->grabClipboard(getClient()); +} diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index 00cc58cb..97745298 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -18,11 +18,12 @@ class CServerProtocol1_0 : public CServerProtocol { virtual void sendClose(); virtual void sendEnter(SInt32 xAbs, SInt32 yAbs); virtual void sendLeave(); + virtual void sendClipboard(const CString&); virtual void sendGrabClipboard(); - virtual void sendQueryClipboard(); + virtual void sendQueryClipboard(UInt32 seqNum); virtual void sendScreenSaver(bool on); virtual void sendKeyDown(KeyID, KeyModifierMask); - virtual void sendKeyRepeat(KeyID, KeyModifierMask); + virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count); virtual void sendKeyUp(KeyID, KeyModifierMask); virtual void sendMouseDown(ButtonID); virtual void sendMouseUp(ButtonID); @@ -32,6 +33,8 @@ class CServerProtocol1_0 : public CServerProtocol { protected: // IServerProtocol overrides virtual void recvInfo(); + virtual void recvClipboard(); + virtual void recvGrabClipboard(); }; #endif diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 2cdf876d..92eb5803 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -56,7 +56,10 @@ static void restoreCursor() static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { - // FIXME + if (g_relay) { + PostMessage(g_hwnd, SYNERGY_MSG_KEY, wParam, lParam); + return 1; + } } return CallNextHookEx(g_keyboard, code, wParam, lParam); diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index b3b30c24..5cdf9c12 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -14,10 +14,10 @@ #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 +#define SYNERGY_MSG_MARK WM_APP + 0x0011 // mark id; +#define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data +#define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; +#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y typedef int (*InstallFunc)(HWND); typedef int (*UninstallFunc)(void); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index a36b07ec..db5cac08 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -117,19 +117,46 @@ void CXWindowsPrimaryScreen::run() break; } -/* case SelectionClear: - target->XXX(xevent.xselectionclear.); + // we just lost the selection. that means someone else + // grabbed the selection so this screen is now the + // selection owner. report that to the server. + m_server->grabClipboard(); break; case SelectionNotify: - target->XXX(xevent.xselection.); + // notification of selection transferred. we shouldn't + // get this here because we handle them in the selection + // retrieval methods. we'll just delete the property + // with the data (satisfying the usual ICCCM protocol). + if (xevent.xselection.property != None) { + CDisplayLock display(this); + XDeleteProperty(display, m_window, xevent.xselection.property); + } break; case SelectionRequest: - target->XXX(xevent.xselectionrequest.); + // somebody is asking for clipboard data + if (xevent.xselectionrequest.owner == m_window) { + addClipboardRequest(m_window, + xevent.xselectionrequest.requestor, + xevent.xselectionrequest.selection, + xevent.xselectionrequest.target, + xevent.xselectionrequest.property, + xevent.xselectionrequest.time); + } + break; + + case PropertyNotify: + // clipboard transfers involve property changes so forward + // the event to the superclass. we only care about the + // deletion of properties. + if (xevent.xproperty.state == PropertyDelete) { + processClipboardRequest(xevent.xproperty.window, + xevent.xproperty.atom, + xevent.xproperty.time); + } break; -*/ } } } @@ -267,19 +294,17 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( } } -#include // FIXME void CXWindowsPrimaryScreen::setClipboard( - const IClipboard* /*clipboard*/) + const IClipboard* clipboard) { - // FIXME -- put this in superclass? // FIXME -- don't use CurrentTime - CDisplayLock display(this); - XSetSelectionOwner(display, XA_PRIMARY, m_window, CurrentTime); - if (XGetSelectionOwner(display, XA_PRIMARY) == m_window) { - // we got the selection - log((CLOG_DEBUG "grabbed clipboard")); - } - // FIXME -- need to copy or adopt the clipboard to serve future requests + setDisplayClipboard(clipboard, m_window, CurrentTime); +} + +void CXWindowsPrimaryScreen::grabClipboard() +{ + // FIXME -- don't use CurrentTime + setDisplayClipboard(NULL, m_window, CurrentTime); } void CXWindowsPrimaryScreen::getSize( @@ -296,7 +321,6 @@ SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const void CXWindowsPrimaryScreen::getClipboard( IClipboard* clipboard) const { - // FIXME -- put this in superclass? // FIXME -- don't use CurrentTime getDisplayClipboard(clipboard, m_window, CurrentTime); } diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 66b84fb5..74f7456d 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -20,6 +20,7 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { virtual void leave(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(const IClipboard*); + virtual void grabClipboard(); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(IClipboard*) const; diff --git a/server/makehook.dsp b/server/makehook.dsp index 507157ef..7a9aac98 100644 --- a/server/makehook.dsp +++ b/server/makehook.dsp @@ -23,8 +23,8 @@ CFG=makehook - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." MTL=midl.exe !IF "$(CFG)" == "makehook - Win32 Release" diff --git a/server/server.cpp b/server/server.cpp index 809c53d6..b01f75c3 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -10,9 +10,9 @@ void realMain() CScreenMap screenMap; screenMap.addScreen("primary"); - screenMap.addScreen("ingrid"); - screenMap.connect("primary", CScreenMap::kRight, "ingrid"); - screenMap.connect("ingrid", CScreenMap::kLeft, "primary"); + screenMap.addScreen("secondary"); + screenMap.connect("primary", CScreenMap::kRight, "secondary"); + screenMap.connect("secondary", CScreenMap::kLeft, "primary"); CServer* server = NULL; try { diff --git a/server/server.dsp b/server/server.dsp index b65f6dec..cdfb0802 100644 --- a/server/server.dsp +++ b/server/server.dsp @@ -38,7 +38,7 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" +# 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 @@ -64,7 +64,7 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" +# 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 diff --git a/server/synrgyhk.dsp b/server/synrgyhk.dsp index 979b6172..d3c6c9b5 100644 --- a/server/synrgyhk.dsp +++ b/server/synrgyhk.dsp @@ -38,8 +38,8 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "../Release" +# PROP Intermediate_Dir "ReleaseHook" # 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 @@ -64,8 +64,8 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "../Debug" +# PROP Intermediate_Dir "DebugHook" # 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 diff --git a/synergy.dsw b/synergy.dsw index e441f5fc..4e67cce8 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -92,6 +92,10 @@ Project: "makehook"=.\server\makehook.dsp - Package Owner=<4> Package=<5> {{{ + begin source code control + millpond + .\server + end source code control }}} Package=<4> diff --git a/synergy/CClipboard.cpp b/synergy/CClipboard.cpp new file mode 100644 index 00000000..1e572af1 --- /dev/null +++ b/synergy/CClipboard.cpp @@ -0,0 +1,137 @@ +#include "CClipboard.h" + +// +// CClipboard +// + +CClipboard::CClipboard() +{ + // do nothing +} + +CClipboard::~CClipboard() +{ + // do nothing +} + +bool CClipboard::open() +{ + // clear all data + for (SInt32 index = 0; index < kNumFormats; ++index) { + m_data[index] = ""; + m_added[index] = false; + } + return true; +} + +void CClipboard::close() +{ + // do nothing +} + +void CClipboard::add(EFormat format, const CString& data) +{ + m_data[format] = data; + m_added[format] = true; +} + +bool CClipboard::has(EFormat format) const +{ + return m_added[format]; +} + +CString CClipboard::get(EFormat format) const +{ + return m_data[format]; +} + +void CClipboard::copy(IClipboard* dst, const IClipboard* src) +{ + if (dst->open()) { + for (SInt32 format = 0; format != IClipboard::kNumFormats; ++format) { + IClipboard::EFormat eFormat = (IClipboard::EFormat)format; + if (src->has(eFormat)) { + dst->add(eFormat, src->get(eFormat)); + } + } + dst->close(); + } +} + +void CClipboard::unmarshall(const CString& data) +{ + const char* index = data.data(); + + // clear existing data + open(); + + // read the number of formats + const UInt32 numFormats = readUInt32(index); + index += 4; + + // read each format + for (UInt32 format = 0; format < numFormats; ++format) { + // get the format id + UInt32 format = readUInt32(index); + index += 4; + + // get the size of the format data + UInt32 size = readUInt32(index); + index += 4; + + // save the data + m_added[format] = true; + m_data[format] = CString(index, size); + index += size; + } + + // done + close(); +} + +CString CClipboard::marshall() const +{ + CString data; + + // compute size of marshalled data + UInt32 size = 4; + UInt32 numFormats = 0; + UInt32 format; + for (format = 0; format != IClipboard::kNumFormats; ++format) { + if (m_added[format]) { + ++numFormats; + size += 4 + 4 + m_data[format].size(); + } + } + + // allocate space + data.reserve(size); + + // marshall the data + writeUInt32(&data, numFormats); + for (format = 0; format != IClipboard::kNumFormats; ++format) { + if (m_added[format]) { + writeUInt32(&data, format); + writeUInt32(&data, m_data[format].size()); + data += m_data[format]; + } + } + + return data; +} + +UInt32 CClipboard::readUInt32(const char* buf) const +{ + return (static_cast(buf[0]) << 24) | + (static_cast(buf[1]) << 16) | + (static_cast(buf[2]) << 8) | + static_cast(buf[3]); +} + +void CClipboard::writeUInt32(CString* buf, UInt32 v) const +{ + *buf += static_cast((v >> 24) & 0xff); + *buf += static_cast((v >> 16) & 0xff); + *buf += static_cast((v >> 8) & 0xff); + *buf += static_cast( v & 0xff); +} diff --git a/synergy/CClipboard.h b/synergy/CClipboard.h new file mode 100644 index 00000000..e5a63ccc --- /dev/null +++ b/synergy/CClipboard.h @@ -0,0 +1,49 @@ +#ifndef CCLIPBOARD_H +#define CCLIPBOARD_H + +// +// CClipboard -- stores clipboard data in a memory buffer +// + +#include "IClipboard.h" +#include "CString.h" + +class CClipboard : public IClipboard { + public: + CClipboard(); + virtual ~CClipboard(); + + // manipulators + + // unmarshall clipboard data + void unmarshall(const CString& data); + + // accessors + + // marshall clipboard data + CString marshall() const; + + // IClipboard overrides + virtual bool open(); + virtual void close(); + virtual void add(EFormat, const CString& data); + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; + + // accessors + + // transfer all the data in one clipboard to another. the + // clipboards can be of any concrete clipboard type (and + // they don't have to be the same type). + static void copy(IClipboard* dst, const IClipboard* src); + + private: + UInt32 readUInt32(const char*) const; + void writeUInt32(CString*, UInt32) const; + + private: + bool m_added[kNumFormats]; + CString m_data[kNumFormats]; +}; + +#endif diff --git a/synergy/CMSWindowsClipboard.cpp b/synergy/CMSWindowsClipboard.cpp index bdef4024..ecf8ae65 100644 --- a/synergy/CMSWindowsClipboard.cpp +++ b/synergy/CMSWindowsClipboard.cpp @@ -6,36 +6,190 @@ // CMSWindowsClipboard // -CMSWindowsClipboard::CMSWindowsClipboard() +CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : m_window(window) { + // do nothing } CMSWindowsClipboard::~CMSWindowsClipboard() { + // do nothing } -void CMSWindowsClipboard::open() +bool CMSWindowsClipboard::open() { log((CLOG_INFO "open clipboard")); + + if (!OpenClipboard(m_window)) { + log((CLOG_WARN "failed to open clipboard")); + return false; + } + if (EmptyClipboard()) { + log((CLOG_DEBUG "grabbed clipboard")); + } + else { + log((CLOG_WARN "failed to grab clipboard")); + } + return true; } void CMSWindowsClipboard::close() { log((CLOG_INFO "close clipboard")); + CloseClipboard(); } void CMSWindowsClipboard::add( EFormat format, const CString& data) { - log((CLOG_INFO "add clipboard format: %d\n%s", format, data.c_str())); + log((CLOG_INFO "add %d bytes to clipboard format: %d", data.size(), format)); + + if (!OpenClipboard(m_window)) { + log((CLOG_WARN "failed to open clipboard")); + return; + } + + // convert data to win32 required form + const UINT win32Format = convertFormatToWin32(format); + HANDLE win32Data; + switch (win32Format) { + case CF_TEXT: + win32Data = convertTextToWin32(data); + break; + + default: + win32Data = NULL; + break; + } + + // put the data on the clipboard + if (win32Data != NULL) { + SetClipboardData(win32Format, win32Data); + } + + // done with clipboard + CloseClipboard(); } -bool CMSWindowsClipboard::has(EFormat /*format*/) const +bool CMSWindowsClipboard::has(EFormat format) const { - return false; + const UINT win32Format = convertFormatToWin32(format); + return (win32Format != 0 && IsClipboardFormatAvailable(win32Format) != 0); } -CString CMSWindowsClipboard::get(EFormat /*format*/) const +CString CMSWindowsClipboard::get(EFormat format) const { - return CString(); + // get the win32 format. return empty data if unknown format. + const UINT win32Format = convertFormatToWin32(format); + if (win32Format == 0) + return CString(); + + if (!OpenClipboard(m_window)) { + log((CLOG_WARN "failed to open clipboard")); + return CString(); + } + + // get a handle to the clipboard data and convert it + HANDLE win32Data = GetClipboardData(win32Format); + CString data; + if (win32Data != NULL) { + // convert the data + switch (win32Format) { + case CF_TEXT: + data = convertTextFromWin32(win32Data); + } + } + + // close the clipboard + CloseClipboard(); + + return data; +} + +UINT CMSWindowsClipboard::convertFormatToWin32( + EFormat format) const +{ + switch (format) { + case kText: + return CF_TEXT; + + default: + return 0; + } +} + +HANDLE CMSWindowsClipboard::convertTextToWin32( + const CString& data) const +{ + // compute size of converted text + UInt32 dstSize = 0; + const UInt32 srcSize = data.size(); + const char* src = data.c_str(); + for (UInt32 index = 0; index < srcSize; ++index) { + if (src[index] == '\n') { + // add \r + ++dstSize; + } + ++dstSize; + } + + // allocate + HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dstSize); + if (gData != NULL) { + // get a pointer to the allocated memory + char* dst = (char*)GlobalLock(gData); + if (dst != NULL) { + // convert text. we change LF to CRLF. + dstSize = 0; + for (UInt32 index = 0; index < srcSize; ++index) { + if (src[index] == '\n') { + // add \r + dst[dstSize++] = '\r'; + } + dst[dstSize++] = src[index]; + } + + // done converting + GlobalUnlock(gData); + } + } + return gData; +} + +CString CMSWindowsClipboard::convertTextFromWin32( + HANDLE handle) const +{ + // get source data and it's size + const char* src = (const char*)GlobalLock(handle); + UInt32 srcSize = (SInt32)GlobalSize(handle); + if (src == NULL || srcSize == 0) + return CString(); + + // compute size of converted text + UInt32 dstSize = 0; + UInt32 index; + for (index = 0; index < srcSize; ++index) { + if (src[index] == '\r') { + // skip \r + if (index + 1 < srcSize && src[index + 1] == '\n') + ++index; + } + ++dstSize; + } + + // allocate + CString data; + data.reserve(dstSize); + + // convert text. we change CRLF to LF. + for (index = 0; index < srcSize; ++index) { + if (src[index] == '\r') { + // skip \r + if (index + 1 < srcSize && src[index + 1] == '\n') + ++index; + } + data += src[index]; + } + + return data; } diff --git a/synergy/CMSWindowsClipboard.h b/synergy/CMSWindowsClipboard.h index c13b11d3..afc8f52c 100644 --- a/synergy/CMSWindowsClipboard.h +++ b/synergy/CMSWindowsClipboard.h @@ -2,18 +2,27 @@ #define CMSWINDOWSCLIPBOARD_H #include "IClipboard.h" +#include class CMSWindowsClipboard : public IClipboard { public: - CMSWindowsClipboard(); + CMSWindowsClipboard(HWND window); virtual ~CMSWindowsClipboard(); // IClipboard overrides - virtual void open(); + virtual bool open(); virtual void close(); virtual void add(EFormat, const CString& data); virtual bool has(EFormat) const; virtual CString get(EFormat) const; + + private: + UINT convertFormatToWin32(EFormat) const; + HANDLE convertTextToWin32(const CString& data) const; + CString convertTextFromWin32(HANDLE) const; + + private: + HWND m_window; }; #endif diff --git a/synergy/CMSWindowsScreen.cpp b/synergy/CMSWindowsScreen.cpp index 63640388..e0fbbdd6 100644 --- a/synergy/CMSWindowsScreen.cpp +++ b/synergy/CMSWindowsScreen.cpp @@ -12,6 +12,7 @@ // HINSTANCE CMSWindowsScreen::s_instance = NULL; +CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; CMSWindowsScreen::CMSWindowsScreen() : m_class(0), @@ -19,12 +20,14 @@ CMSWindowsScreen::CMSWindowsScreen() : m_w(0), m_h(0), m_thread(0) { - // do nothing + assert(s_screen == NULL); + s_screen = this; } CMSWindowsScreen::~CMSWindowsScreen() { assert(m_class == 0); + s_screen = NULL; } void CMSWindowsScreen::init(HINSTANCE instance) @@ -46,7 +49,7 @@ void CMSWindowsScreen::doRun() } // dispatch message - if (!onEvent(&msg)) { + if (!onPreTranslate(&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } @@ -66,10 +69,13 @@ void CMSWindowsScreen::openDisplay() // 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; + UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; + UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; + memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); + memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); + m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); + delete[] cursorXOR; + delete[] cursorAND; // register a window class WNDCLASSEX classInfo; @@ -197,7 +203,7 @@ void CMSWindowsScreen::getDisplayClipboard( // if we can use the format and we haven't already retrieved // it then get it - if (expectedFormat == IClipboard::kNum) { + if (expectedFormat == IClipboard::kNumFormats) { log((CLOG_DEBUG " no format for target", format)); continue; } @@ -215,7 +221,7 @@ void CMSWindowsScreen::getDisplayClipboard( // use the actual format, not the expected IClipboard::EFormat actualFormat = getFormat(format); - if (actualFormat == IClipboard::kNum) { + if (actualFormat == IClipboard::kNumFormats) { log((CLOG_DEBUG " no format for target", format)); continue; } @@ -244,5 +250,6 @@ LRESULT CALLBACK CMSWindowsScreen::wndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - return DefWindowProc(hwnd, msg, wParam, lParam); + assert(s_screen != NULL); + return s_screen->onEvent(hwnd, msg, wParam, lParam); } diff --git a/synergy/CMSWindowsScreen.h b/synergy/CMSWindowsScreen.h index 4c51be00..2cfa9964 100644 --- a/synergy/CMSWindowsScreen.h +++ b/synergy/CMSWindowsScreen.h @@ -49,8 +49,12 @@ class CMSWindowsScreen { // 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 doRun() to handle an event. return true to skip + // event translation and dispatch. + virtual bool onPreTranslate(MSG*) = 0; + + // called by window proc. subclass must call DefWindowProc() if necessary + virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM) = 0; // called by openDisplay() to allow subclasses to prepare the display virtual void onOpenDisplay() = 0; @@ -68,6 +72,7 @@ class CMSWindowsScreen { HCURSOR m_cursor; SInt32 m_w, m_h; DWORD m_thread; + static CMSWindowsScreen* s_screen; }; #endif diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index e22d24c4..c15cfb2a 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -190,6 +190,12 @@ UInt32 CProtocolUtil::getLength( break; case 's': + assert(len == 0); + len = (va_arg(args, CString*))->size() + 4; + (void)va_arg(args, UInt8*); + break; + + case 'S': assert(len == 0); len = va_arg(args, UInt32) + 4; (void)va_arg(args, UInt8*); @@ -258,6 +264,21 @@ void CProtocolUtil::writef(void* buffer, } case 's': { + assert(len == 0); + const CString* src = va_arg(args, CString*); + const UInt32 len = (src != NULL) ? src->size() : 0; + *dst++ = static_cast((len >> 24) & 0xff); + *dst++ = static_cast((len >> 16) & 0xff); + *dst++ = static_cast((len >> 8) & 0xff); + *dst++ = static_cast( len & 0xff); + if (len != 0) { + memcpy(dst, src->data(), len); + dst += len; + } + break; + } + + case 'S': { assert(len == 0); const UInt32 len = va_arg(args, UInt32); const UInt8* src = va_arg(args, UInt8*); diff --git a/synergy/CProtocolUtil.h b/synergy/CProtocolUtil.h index 70bd5851..d85d26f1 100644 --- a/synergy/CProtocolUtil.h +++ b/synergy/CProtocolUtil.h @@ -20,7 +20,8 @@ class CProtocolUtil { // %1i -- converts integer argument to 1 byte integer // %2i -- converts integer argument to 2 byte integer in NBO // %4i -- converts integer argument to 4 byte integer in NBO - // %s -- converts integer N and const UInt8* to stream of N bytes + // %s -- converts CString* to stream of bytes + // %S -- converts integer N and const UInt8* to stream of N bytes static void writef(IOutputStream*, const char* fmt, ...); diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index dafbb648..a1a6aa74 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -52,6 +52,7 @@ void CXWindowsScreen::openDisplay() // get some atoms m_atomTargets = XInternAtom(m_display, "TARGETS", False); + m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False); m_atomData = XInternAtom(m_display, "DESTINATION", False); m_atomINCR = XInternAtom(m_display, "INCR", False); m_atomText = XInternAtom(m_display, "TEXT", False); @@ -143,12 +144,12 @@ bool CXWindowsScreen::getEvent(XEvent* xevent) const } if (m_stop) { m_mutex.unlock(); - return true; + return false; } else { XNextEvent(m_display, xevent); m_mutex.unlock(); - return false; + return true; } } @@ -158,6 +159,34 @@ void CXWindowsScreen::doStop() m_stop = true; } +bool CXWindowsScreen::setDisplayClipboard( + const IClipboard* clipboard, + Window requestor, Time timestamp) +{ + CLock lock(&m_mutex); + + XSetSelectionOwner(m_display, XA_PRIMARY, requestor, timestamp); + if (XGetSelectionOwner(m_display, XA_PRIMARY) == requestor) { + // we got the selection + log((CLOG_DEBUG "grabbed clipboard")); + + if (clipboard != NULL) { + // save clipboard to serve requests + CClipboard::copy(&m_clipboard, clipboard); + } + else { + // clear clipboard + if (m_clipboard.open()) { + m_clipboard.close(); + } + } + + return true; + } + + return false; +} + void CXWindowsScreen::getDisplayClipboard( IClipboard* clipboard, Window requestor, Time timestamp) const @@ -166,7 +195,8 @@ void CXWindowsScreen::getDisplayClipboard( assert(requestor != None); // clear the clipboard object - clipboard->open(); + if (!clipboard->open()) + return; // block others from using the display while we get the clipboard. // in particular, this prevents the event thread from stealing the @@ -209,7 +239,7 @@ void CXWindowsScreen::getDisplayClipboard( // if we can use the format and we haven't already retrieved // it then get it - if (expectedFormat == IClipboard::kNum) { + if (expectedFormat == IClipboard::kNumFormats) { log((CLOG_DEBUG " no format for target", format)); continue; } @@ -227,7 +257,7 @@ void CXWindowsScreen::getDisplayClipboard( // use the actual format, not the expected IClipboard::EFormat actualFormat = getFormat(format); - if (actualFormat == IClipboard::kNum) { + if (actualFormat == IClipboard::kNumFormats) { log((CLOG_DEBUG " no format for target", format)); continue; } @@ -452,7 +482,7 @@ IClipboard::EFormat CXWindowsScreen::getFormat(Atom src) const src == m_atomText || src == m_atomCompoundText) return IClipboard::kText; - return IClipboard::kNum; + return IClipboard::kNumFormats; } Bool CXWindowsScreen::findSelectionNotify( @@ -473,6 +503,98 @@ Bool CXWindowsScreen::findPropertyNotify( xevent->xproperty.state == PropertyNewValue) ? True : False; } +void CXWindowsScreen::addClipboardRequest( + Window /*owner*/, Window requestor, + Atom selection, Atom target, + Atom property, Time time) +{ + // mutex the display + CLock lock(&m_mutex); + + // check the target + IClipboard::EFormat format = IClipboard::kNumFormats; + if (selection == XA_PRIMARY) { + if (target == m_atomTargets) { + // send the list of available targets + sendClipboardTargetsNotify(requestor, property, time); + return; + } + else if (target == m_atomMultiple) { + // add a multiple request + if (property != None) { + addClipboardMultipleRequest(requestor, property, time); + return; + } + } + else { + format = getFormat(target); + } + } + + // if we can't handle the format then send a failure notification + if (format == IClipboard::kNumFormats) { + XEvent event; + event.xselection.type = SelectionNotify; + event.xselection.display = m_display; + event.xselection.requestor = requestor; + event.xselection.selection = selection; + event.xselection.target = target; + event.xselection.property = None; + event.xselection.time = time; + XSendEvent(m_display, requestor, False, 0, &event); + return; + } + + // we could process short requests without adding to the request + // queue but we'll keep things simple by doing all requests the + // same way. + // FIXME +} + +void CXWindowsScreen::sendClipboardTargetsNotify( + Window requestor, + Atom property, Time time) +{ + // construct response + // FIXME + + // set property + // FIXME + + // send notification + XEvent event; + event.xselection.type = SelectionNotify; + event.xselection.display = m_display; + event.xselection.requestor = requestor; + event.xselection.selection = XA_PRIMARY; + event.xselection.target = m_atomTargets; + event.xselection.property = property; + event.xselection.time = time; + XSendEvent(m_display, requestor, False, 0, &event); +} + +void CXWindowsScreen::processClipboardRequest( + Window requestor, + Atom property, Time time) +{ + // FIXME +} + +void CXWindowsScreen::addClipboardRequest( + Window requestor, + Atom property, Time time, + IClipboard::EFormat format) +{ + // FIXME +} + +void CXWindowsScreen::addClipboardMultipleRequest( + Window requestor, + Atom property, Time time) +{ + // FIXME +} + // // CXWindowsScreen::CDisplayLock diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 98088925..d7e983b9 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -1,8 +1,8 @@ #ifndef CXWINDOWSSCREEN_H #define CXWINDOWSSCREEN_H +#include "CClipboard.h" #include "CMutex.h" -#include "IClipboard.h" #include "BasicTypes.h" #include @@ -54,12 +54,25 @@ class CXWindowsScreen { // cause getEvent() to return false immediately and forever after void doStop(); + bool setDisplayClipboard(const IClipboard* clipboard, + Window requestor, Time timestamp); + // copy the clipboard contents to clipboard. requestor must be a // valid window; it will be used to receive the transfer. timestamp // should be the timestamp of the provoking event and not CurrentTime. void getDisplayClipboard(IClipboard* clipboard, Window requestor, Time timestamp) const; + // add a selection request to the request list + void addClipboardRequest( + Window owner, Window requestor, + Atom selection, Atom target, + Atom property, Time time); + + // continue processing a selection request + void processClipboardRequest(Window window, + Atom property, Time time); + // called by openDisplay() to allow subclasses to prepare the display virtual void onOpenDisplay() = 0; @@ -85,6 +98,14 @@ class CXWindowsScreen { static Bool findPropertyNotify(Display*, XEvent* xevent, XPointer arg); + void sendClipboardTargetsNotify(Window requestor, + Atom property, Time time); + void addClipboardRequest(Window requestor, + Atom property, Time time, + IClipboard::EFormat); + void addClipboardMultipleRequest(Window requestor, + Atom property, Time time); + private: Display* m_display; int m_screen; @@ -94,11 +115,15 @@ class CXWindowsScreen { // atoms we'll need Atom m_atomTargets; + Atom m_atomMultiple; Atom m_atomData; Atom m_atomINCR; Atom m_atomText; Atom m_atomCompoundText; + // the contents of our selection + CClipboard m_clipboard; + // X is not thread safe CMutex m_mutex; }; diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index 9eaae81e..08551e37 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -8,14 +8,16 @@ class CString; class IClipboard : public IInterface { public: - enum EFormat { kText, kNum }; + enum EFormat { kText, kNumFormats }; // manipulators // grab ownership of and clear the clipboard of all data. // only add() may be called between an open() and its - // corresponding close(). - virtual void open() = 0; + // corresponding close(). if open() returns false then + // the clipboard could not be opened or grabbed; do not + // call close() in that case. + virtual bool open() = 0; // close the clipboard. close() must match a preceding open(). // this signals that the clipboard has been filled with all the diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 17341b63..92572e33 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -54,11 +54,11 @@ class IPrimaryScreen : public IInterface { /* // show or hide the screen saver virtual void onScreenSaver(bool show) = 0; - - // clipboard input - virtual void onClipboardChanged() = 0; */ + // synergy should own the clipboard + virtual void grabClipboard() = 0; + // accessors /* diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 88893da6..2e886fd5 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -7,7 +7,7 @@ #include "MouseTypes.h" class CClient; -//class IClipboard; +class IClipboard; class ISecondaryScreen : public IInterface { public: @@ -50,18 +50,18 @@ class ISecondaryScreen : public IInterface { virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; virtual void mouseWheel(SInt32 delta) = 0; -/* // set the screen's clipboard contents. this is usually called // soon after an enter(). virtual void setClipboard(const IClipboard*) = 0; +/* // show or hide the screen saver virtual void screenSaver(bool show) = 0; - - // clipboard input - virtual void clipboardChanged() = 0; */ + // take ownership of clipboard + virtual void grabClipboard() = 0; + // accessors // get the size of the screen @@ -70,10 +70,8 @@ class ISecondaryScreen : public IInterface { // get the size of jump zone virtual SInt32 getJumpZoneSize() const = 0; -/* // get the screen's clipboard contents virtual void getClipboard(IClipboard*) const = 0; -*/ }; #endif diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index 360c9d7e..ecd396be 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -7,6 +7,8 @@ #include "XSynergy.h" #include "XIO.h" +class IClipboard; + class IServerProtocol : public IInterface { public: // manipulators @@ -23,11 +25,12 @@ class IServerProtocol : public IInterface { virtual void sendClose() = 0; virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; virtual void sendLeave() = 0; + virtual void sendClipboard(const CString&) = 0; virtual void sendGrabClipboard() = 0; - virtual void sendQueryClipboard() = 0; + virtual void sendQueryClipboard(UInt32 seqNum) = 0; virtual void sendScreenSaver(bool on) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; - virtual void sendKeyRepeat(KeyID, KeyModifierMask) = 0; + virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; virtual void sendMouseDown(ButtonID) = 0; virtual void sendMouseUp(ButtonID) = 0; @@ -40,6 +43,8 @@ class IServerProtocol : public IInterface { // manipulators virtual void recvInfo() = 0; + virtual void recvClipboard() = 0; + virtual void recvGrabClipboard() = 0; // accessors }; diff --git a/synergy/Makefile b/synergy/Makefile index bb557bf8..d1cef559 100644 --- a/synergy/Makefile +++ b/synergy/Makefile @@ -19,6 +19,7 @@ CXXFILES = \ CInputPacketStream.cpp \ COutputPacketStream.cpp \ CProtocolUtil.cpp \ + CClipboard.cpp \ CTCPSocketFactory.cpp \ CXWindowsClipboard.cpp \ CXWindowsScreen.cpp \ diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index c41935c0..c806ae3a 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -16,7 +16,7 @@ static const SInt32 kMinorVersion = 1; static const char kMsgCClose[] = "CBYE"; // server static const char kMsgCEnter[] = "CINN%2i%2i"; // server static const char kMsgCLeave[] = "COUT"; // server -static const char kMsgCClipboard[] = "CCLP"; // server +static const char kMsgCClipboard[] = "CCLP"; // server/client static const char kMsgCScreenSaver[] = "CSEC%1i"; // server static const char kMsgDKeyDown[] = "DKDN%2i%2i"; // server @@ -26,10 +26,10 @@ static const char kMsgDMouseDown[] = "DMDN%1i"; // server static const char kMsgDMouseUp[] = "DMUP%1i"; // server static const char kMsgDMouseMove[] = "DMMV%2i%2i"; // server static const char kMsgDMouseWheel[] = "DMWM%2i"; // server -static const char kMsgDClipboard[] = "DCLP%s"; // server +static const char kMsgDClipboard[] = "DCLP%4i%s"; // server/client static const char kMsgDInfo[] = "DINF%2i%2i%2i"; // client -static const char kMsgQClipboard[] = "QCLP"; // server +static const char kMsgQClipboard[] = "QCLP%4i"; // server static const char kMsgQInfo[] = "QINF"; // server static const char kMsgEIncompatible[] = "EICV"; diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp index 9bff2e3d..6b95ec97 100644 --- a/synergy/synergy.dsp +++ b/synergy/synergy.dsp @@ -87,6 +87,10 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CClipboard.cpp +# End Source File +# Begin Source File + SOURCE=.\CInputPacketStream.cpp # End Source File # Begin Source File @@ -119,6 +123,10 @@ SOURCE=.\XSynergy.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CClipboard.h +# End Source File +# Begin Source File + SOURCE=.\CInputPacketStream.h # End Source File # Begin Source File