diff --git a/client/CClient.cpp b/client/CClient.cpp index c3d44cfd..3f8ec103 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -32,7 +32,8 @@ CClient::CClient(const CString& clientName) : m_output(NULL), m_screen(NULL), m_active(false), - m_seqNum(0) + m_seqNum(0), + m_ignoreMove(false) { // do nothing } @@ -106,7 +107,7 @@ void CClient::onClipboardChanged(ClipboardID id) m_timeClipboard[id] = 0; // if we're not the active screen then send the clipboard now, - // otherwise we'll until we leave. + // otherwise we'll wait until we leave. if (!m_active) { // get clipboard CClipboard clipboard; @@ -127,6 +128,19 @@ void CClient::onClipboardChanged(ClipboardID id) } } +void CClient::onResolutionChanged() +{ + log((CLOG_DEBUG "resolution changed")); + + CLock lock(&m_mutex); + + // start ignoring mouse movement until we get an acknowledgment + m_ignoreMove = true; + + // send notification of resolution change + onQueryInfoNoLock(); +} + #include "CTCPSocket.h" // FIXME void CClient::runSession(void*) { @@ -261,6 +275,9 @@ void CClient::runSession(void*) else if (memcmp(code, kMsgQInfo, 4) == 0) { onQueryInfo(); } + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + onInfoAcknowledgment(); + } else if (memcmp(code, kMsgDClipboard, 4) == 0) { onSetClipboard(); } @@ -399,10 +416,13 @@ void CClient::onLeave() // marshall the data CString data = clipboard.marshall(); - // send data - log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); - CProtocolUtil::writef(m_output, + // save and send data if different + if (data != m_dataClipboard[id]) { + log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); + m_dataClipboard[id] = data; + CProtocolUtil::writef(m_output, kMsgDClipboard, id, m_seqNum, &data); + } } } } @@ -441,13 +461,26 @@ void CClient::onScreenSaver() void CClient::onQueryInfo() { - SInt32 w, h; + CLock lock(&m_mutex); + onQueryInfoNoLock(); +} + +void CClient::onQueryInfoNoLock() +{ + SInt32 x, y, w, h; + m_screen->getMousePos(&x, &y); m_screen->getSize(&w, &h); SInt32 zoneSize = m_screen->getJumpZoneSize(); - log((CLOG_DEBUG1 "sending info size=%d,%d zone=%d", w, h, zoneSize)); + log((CLOG_DEBUG1 "sending info size=%d,%d zone=%d pos=%d,%d", w, h, zoneSize, x, y)); + CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize, x, y); +} + +void CClient::onInfoAcknowledgment() +{ + log((CLOG_DEBUG1 "recv info acknowledgment")); CLock lock(&m_mutex); - CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); + m_ignoreMove = false; } void CClient::onSetClipboard() @@ -536,13 +569,17 @@ void CClient::onMouseUp() void CClient::onMouseMove() { + bool ignore; SInt16 x, y; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); + ignore = m_ignoreMove; } log((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); - m_screen->mouseMove(x, y); + if (!ignore) { + m_screen->mouseMove(x, y); + } } void CClient::onMouseWheel() diff --git a/client/CClient.h b/client/CClient.h index 498a9d5b..a6c0cea1 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -23,6 +23,7 @@ public: // handle events on client's screen void onClipboardChanged(ClipboardID); + void onResolutionChanged(); // accessors @@ -40,6 +41,8 @@ private: void onGrabClipboard(); void onScreenSaver(); void onQueryInfo(); + void onQueryInfoNoLock(); + void onInfoAcknowledgment(); void onSetClipboard(); void onKeyDown(); void onKeyRepeat(); @@ -61,8 +64,10 @@ private: const CNetworkAddress* m_serverAddress; bool m_active; UInt32 m_seqNum; + bool m_ignoreMove; bool m_ownClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd]; + CString m_dataClipboard[kClipboardEnd]; }; #endif diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index cdca03c4..26a97fa5 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -315,6 +315,23 @@ void CMSWindowsSecondaryScreen::grabClipboard(ClipboardID id) } } +void CMSWindowsSecondaryScreen::getMousePos( + SInt32* x, SInt32* y) const +{ + assert(x != NULL); + assert(y != NULL); + + POINT pos; + if (GetCursorPos(&pos)) { + *x = pos.x; + *y = pos.y; + } + else { + *x = 0; + *y = 0; + } +} + void CMSWindowsSecondaryScreen::getSize( SInt32* width, SInt32* height) const { @@ -420,6 +437,12 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( else SendMessage(m_nextClipboardWindow, msg, wParam, lParam); return 0; + + case WM_DISPLAYCHANGE: + // screen resolution has changed + updateScreenSize(); + m_client->onResolutionChanged(); + return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 8149ea92..3ca03338 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -28,6 +28,7 @@ public: virtual void mouseWheel(SInt32 delta); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); + virtual void getMousePos(SInt32* x, SInt32* y) const; virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 010c4475..1a58a7bd 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -323,6 +323,19 @@ void CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) setDisplayClipboard(id, NULL, m_window, getCurrentTime(m_window)); } +void CXWindowsSecondaryScreen::getMousePos( + SInt32* x, SInt32* y) const +{ + CDisplayLock display(this); + int xTmp, yTmp, dummy; + unsigned int dummyMask; + Window dummyWindow; + XQueryPointer(display, getRoot(), &dummyWindow, &dummyWindow, + &xTmp, &yTmp, &dummy, &dummy, &dummyMask); + *x = xTmp; + *y = yTmp; +} + void CXWindowsSecondaryScreen::getSize( SInt32* width, SInt32* height) const { diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 6aa1cb8b..44f63182 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -27,6 +27,7 @@ public: virtual void mouseWheel(SInt32 delta); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); + virtual void getMousePos(SInt32* x, SInt32* y) const; virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 546536d0..fa6d7e5c 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -61,6 +61,11 @@ void CMSWindowsPrimaryScreen::open(CServer* server) // get keyboard state updateKeys(); + // send screen info + SInt32 w, h; + getScreenSize(&w, &h); + m_server->setInfo(w, h, getJumpZoneSize(), 0, 0); + // enter the screen doEnter(); } @@ -246,18 +251,35 @@ void CMSWindowsPrimaryScreen::getClipboard( KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { KeyModifierMask mask = 0; - if ((m_keys[VK_CAPITAL] & 0x01) != 0) + if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) mask |= KeyModifierCapsLock; - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) mask |= KeyModifierNumLock; - if ((m_keys[VK_SCROLL] & 0x01) != 0) + if ((GetKeyState(VK_SCROLL) & 0x01) != 0) mask |= KeyModifierScrollLock; return mask; } bool CMSWindowsPrimaryScreen::isLockedToScreen() const { - // FIXME + // check buttons + if (GetAsyncKeyState(VK_LBUTTON) < 0 || + GetAsyncKeyState(VK_MBUTTON) < 0 || + GetAsyncKeyState(VK_RBUTTON) < 0) { + return true; + } + + // check keys + BYTE keys[256]; + if (GetKeyboardState(keys)) { + for (unsigned int i = 0; i < sizeof(keys); ++i) { + if ((keys[i] & 0x80) != 0) { + return true; + } + } + } + + // not locked return false; } @@ -433,13 +455,17 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) // get mouse deltas x -= m_xCenter; y -= m_yCenter; - log((CLOG_DEBUG2 "event: active move %d,%d", x, y)); - // warp mouse back to center - warpCursor(m_xCenter, m_yCenter); + // ignore if the mouse didn't move + if (x != 0 && y != 0) { + log((CLOG_DEBUG2 "event: active move %d,%d", x, y)); - // send motion - m_server->onMouseMoveSecondary(x, y); + // warp mouse back to center + warpCursor(m_xCenter, m_yCenter); + + // send motion + m_server->onMouseMoveSecondary(x, y); + } } } return true; @@ -485,6 +511,36 @@ LRESULT CMSWindowsPrimaryScreen::onEvent( else SendMessage(m_nextClipboardWindow, msg, wParam, lParam); return 0; + + case WM_DISPLAYCHANGE: { + // screen resolution has changed + SInt32 w, h; + updateScreenSize(); + getScreenSize(&w, &h); + + // recompute center pixel of screen + m_xCenter = w >> 1; + m_yCenter = h >> 1; + + // warp mouse to center if active + if (m_active) { + warpCursor(m_xCenter, m_yCenter); + } + + // tell hook about resize if not active + else { + SetZoneFunc setZone = (SetZoneFunc)GetProcAddress( + m_hookLibrary, "setZone"); + setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize()); + } + + // send new screen info + POINT pos; + GetCursorPos(&pos); + m_server->setInfo(w, h, getJumpZoneSize(), pos.x, pos.y); + + return 0; + } } return DefWindowProc(hwnd, msg, wParam, lParam); diff --git a/server/CServer.cpp b/server/CServer.cpp index 09739198..6c8001a3 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -132,8 +132,16 @@ UInt32 CServer::getActivePrimarySides() const return sides; } +void CServer::setInfo( + SInt32 w, SInt32 h, SInt32 zoneSize, + SInt32 x, SInt32 y) +{ + setInfo(m_primaryInfo->m_name, w, h, zoneSize, x, y); +} + void CServer::setInfo(const CString& client, - SInt32 w, SInt32 h, SInt32 zoneSize) + SInt32 w, SInt32 h, SInt32 zoneSize, + SInt32 x, SInt32 y) { assert(!client.empty()); assert(w > 0); @@ -151,12 +159,29 @@ void CServer::setInfo(const CString& client, // update client info CScreenInfo* info = index->second; if (info == m_active) { - // FIXME -- ensure mouse is still on screen. warp it if necessary. + // update the remote mouse coordinates + m_x = x; + m_y = y; } info->m_width = w; info->m_height = h; info->m_zoneSize = zoneSize; - log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d", client.c_str(), w, h, zoneSize)); + log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d pos=%d,%d", client.c_str(), w, h, zoneSize, x, y)); + + // send acknowledgement (if client isn't the primary) + if (info->m_protocol != NULL) { + info->m_protocol->sendInfoAcknowledgment(); + } + + // handle resolution change to primary screen + else { + if (info == m_active) { + onMouseMovePrimary(x, y); + } + else { + onMouseMoveSecondary(0, 0); + } + } } void CServer::grabClipboard(ClipboardID id) @@ -1011,6 +1036,10 @@ void CServer::openPrimaryScreen() // reset sequence number m_seqNum = 0; + // add connection + m_active = addConnection(CString("primary"/* FIXME */), NULL); + m_primaryInfo = m_active; + // open screen log((CLOG_DEBUG1 "creating primary screen")); #if defined(CONFIG_PLATFORM_WIN32) @@ -1021,16 +1050,6 @@ void CServer::openPrimaryScreen() log((CLOG_DEBUG1 "opening primary screen")); m_primary->open(this); - // add connection - m_active = addConnection(CString("primary"/* FIXME */), NULL); - m_primaryInfo = m_active; - - // update info - m_primary->getSize(&m_active->m_width, &m_active->m_height); - 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. for (ClipboardID id = 0; id < kClipboardEnd; ++id) { diff --git a/server/CServer.h b/server/CServer.h index 321017ce..5cdadfe5 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -46,9 +46,16 @@ public: void onMouseWheel(SInt32 delta); void grabClipboard(ClipboardID); + // handle updates from primary + void setInfo(SInt32 wScreen, SInt32 hScreen, + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse); + // handle messages from clients void setInfo(const CString& clientName, - SInt32 w, SInt32 h, SInt32 zoneSize); + SInt32 wScreen, SInt32 hScreen, + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse); void grabClipboard(ClipboardID, UInt32 seqNum, const CString& clientName); void setClipboard(ClipboardID, @@ -187,6 +194,7 @@ private: // the sequence number of enter messages UInt32 m_seqNum; + // current mouse position (in absolute secondary screen coordinates) SInt32 m_x, m_y; CScreenMap m_screenMap; diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index c64fa6cf..47ae856d 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -37,6 +37,7 @@ public: virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; virtual void sendScreenSaver(bool on) = 0; + virtual void sendInfoAcknowledgment() = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index e9ed0519..1d4240f0 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -124,6 +124,12 @@ void CServerProtocol1_0::sendScreenSaver(bool on) CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); } +void CServerProtocol1_0::sendInfoAcknowledgment() +{ + log((CLOG_DEBUG1 "send info ack to \"%s\"", getClient().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCInfoAck); +} + void CServerProtocol1_0::sendKeyDown( KeyID key, KeyModifierMask mask) { @@ -176,17 +182,21 @@ void CServerProtocol1_0::sendMouseWheel( void CServerProtocol1_0::recvInfo() { // parse the message - SInt16 w, h, zoneInfo; - CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, &w, &h, &zoneInfo); - log((CLOG_DEBUG "received client \"%s\" info size=%dx%d, zone=%d", getClient().c_str(), w, h, zoneInfo)); + SInt16 x, y, w, h, zoneInfo; + CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, + &w, &h, &zoneInfo, &x, &y); + log((CLOG_DEBUG "received client \"%s\" info size=%dx%d, zone=%d, pos=%d,%d", getClient().c_str(), w, h, zoneInfo, x, y)); // validate if (w <= 0 || h <= 0 || zoneInfo < 0) { throw XBadClient(); } + if (x < 0 || y < 0 || x >= w || y >= h) { + throw XBadClient(); + } // tell server of change - getServer()->setInfo(getClient(), w, h, zoneInfo); + getServer()->setInfo(getClient(), w, h, zoneInfo, x, y); } void CServerProtocol1_0::recvClipboard() diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index e7fbcc58..4114643e 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -22,6 +22,7 @@ public: virtual void sendClipboard(ClipboardID, const CString&); virtual void sendGrabClipboard(ClipboardID); virtual void sendScreenSaver(bool on); + virtual void sendInfoAcknowledgment(); virtual void sendKeyDown(KeyID, KeyModifierMask); virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count); virtual void sendKeyUp(KeyID, KeyModifierMask); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 26e374c0..db112338 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -268,6 +268,11 @@ void CXWindowsPrimaryScreen::open(CServer* server) CDisplayLock display(this); updateModifierMap(display); } + + // send screen info + SInt32 w, h; + getScreenSize(&w, &h); + m_server->setInfo(w, h, getJumpZoneSize(), 0, 0); } void CXWindowsPrimaryScreen::close() diff --git a/synergy/CMSWindowsScreen.cpp b/synergy/CMSWindowsScreen.cpp index 49860496..ff2c2e39 100644 --- a/synergy/CMSWindowsScreen.cpp +++ b/synergy/CMSWindowsScreen.cpp @@ -135,6 +135,13 @@ ATOM CMSWindowsScreen::getClass() const return m_class; } +void CMSWindowsScreen::updateScreenSize() +{ + m_w = GetSystemMetrics(SM_CXSCREEN); + m_h = GetSystemMetrics(SM_CYSCREEN); + log((CLOG_INFO "display resize: %dx%d", m_w, m_h)); +} + void CMSWindowsScreen::getScreenSize( SInt32* w, SInt32* h) const { diff --git a/synergy/CMSWindowsScreen.h b/synergy/CMSWindowsScreen.h index a916beb3..510ed3c6 100644 --- a/synergy/CMSWindowsScreen.h +++ b/synergy/CMSWindowsScreen.h @@ -40,6 +40,9 @@ protected: static HINSTANCE getInstance(); ATOM getClass() const; + // update screen size cache + void updateScreenSize(); + // get the size of the screen void getScreenSize(SInt32* w, SInt32* h) const; diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 02535ac9..4390addc 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -28,7 +28,11 @@ public: // can't be detected then this object should disable the system's // screen saver timer and should start the screen saver after // idling for an appropriate time. - virtual void open(CServer*) = 0; + // + // open() must call server->setInfo() to notify the server of the + // primary screen's resolution and jump zone size. the mouse + // position is ignored and may be 0,0. + virtual void open(CServer* server) = 0; // close the screen. should restore the screen saver timer if it // was disabled. diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index c7f1ba3a..c5bd736e 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -66,6 +66,9 @@ public: // accessors + // get the position of the mouse on the screen + virtual void getMousePos(SInt32* x, SInt32* y) const = 0; + // get the size of the screen virtual void getSize(SInt32* width, SInt32* height) const = 0; diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index c8d40f2a..ecddeeda 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -30,6 +30,7 @@ public: virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; virtual void sendScreenSaver(bool on) = 0; + virtual void sendInfoAcknowledgment() = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index 66b45c93..c8194961 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -50,6 +50,12 @@ static const char kMsgCClipboard[] = "CCLP%1i%4i"; // screensaver on primary has started ($1 == 1) or closed ($1 == 0) static const char kMsgCScreenSaver[] = "CSEC%1i"; +// resolution change acknowledgment: primary -> secondary +// sent by primary in response to a secondary screen's kMsgDInfo. +// this is sent for every kMsgDInfo, whether or not the primary +// had sent a kMsgQInfo. +static const char kMsgCInfoAck[] = "CIAK"; + // // data codes @@ -80,7 +86,8 @@ static const char kMsgDMouseUp[] = "DMUP%1i"; static const char kMsgDMouseMove[] = "DMMV%2i%2i"; // mouse button pressed: primary -> secondary -// $1 = delta +// $1 = delta. the delta should be +120 for one tick forward (away +// from the user) and -120 for one tick backward (toward the user). static const char kMsgDMouseWheel[] = "DMWM%2i"; // clipboard data: primary <-> secondary @@ -92,8 +99,16 @@ static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; // client data: secondary -> primary // $1 = seconary screen width in pixels, $2 = screen height, $3 = -// size of warp zone. -static const char kMsgDInfo[] = "DINF%2i%2i%2i"; +// size of warp zone. $4 and $5 are the x,y position of the mouse +// on the secondary screen. +// +// the secondary screen must send this message in response to the +// kMsgQInfo message. it must also send this message when the +// screen's resolution changes. in this case, the secondary screen +// should ignore any kMsgDMouseMove messages until it receives a +// kMsgCInfoAck in order to prevent attempts to move the mouse off +// the new screen area. +static const char kMsgDInfo[] = "DINF%2i%2i%2i%2i%2i"; //