diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index f1f4ae22..59bbc4fc 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -257,6 +257,12 @@ CClient::mouseMove(SInt32 x, SInt32 y) m_screen->mouseMove(x, y); } +void +CClient::mouseRelativeMove(SInt32 dx, SInt32 dy) +{ + m_screen->mouseRelativeMove(dx, dy); +} + void CClient::mouseWheel(SInt32 delta) { diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 9c49d3cd..5087759b 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -135,6 +135,7 @@ public: virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); virtual void mouseWheel(SInt32 delta); virtual void screensaver(bool activate); virtual void resetOptions(); diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index ffe5a64e..7aee2341 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -550,6 +550,19 @@ CXWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const XFlush(m_display); } +void +CXWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const +{ + // FIXME -- ignore xinerama for now + if (false && m_xinerama && m_xtestIsXineramaUnaware) { +// XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); + } + else { + XTestFakeRelativeMotionEvent(m_display, dx, dy, CurrentTime); + } + XFlush(m_display); +} + void CXWindowsScreen::fakeMouseWheel(SInt32 delta) const { diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index c7be5bb4..d1289dca 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -55,6 +55,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; virtual void fakeMouseWheel(SInt32 delta) const; // IPlatformScreen overrides diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index 9453a203..b8e38fa8 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -96,6 +96,7 @@ public: virtual void mouseDown(ButtonID) = 0; virtual void mouseUp(ButtonID) = 0; virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0; virtual void mouseWheel(SInt32 delta) = 0; virtual void screensaver(bool activate) = 0; virtual void resetOptions() = 0; diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index 617e12a2..2bb412ce 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -316,6 +316,12 @@ CClientProxy1_0::mouseMove(SInt32 xAbs, SInt32 yAbs) CProtocolUtil::writef(getStream(), kMsgDMouseMove, xAbs, yAbs); } +void +CClientProxy1_0::mouseRelativeMove(SInt32, SInt32) +{ + // ignore -- not supported in protocol 1.0 +} + void CClientProxy1_0::mouseWheel(SInt32 delta) { diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index 503dcba9..4f752085 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -49,6 +49,7 @@ public: virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); virtual void mouseWheel(SInt32 delta); virtual void screensaver(bool activate); virtual void resetOptions(); diff --git a/lib/server/CClientProxyUnknown.cpp b/lib/server/CClientProxyUnknown.cpp index 4c45ba07..806f23b8 100644 --- a/lib/server/CClientProxyUnknown.cpp +++ b/lib/server/CClientProxyUnknown.cpp @@ -15,6 +15,7 @@ #include "CClientProxyUnknown.h" #include "CClientProxy1_0.h" #include "CClientProxy1_1.h" +#include "CClientProxy1_2.h" #include "ProtocolTypes.h" #include "CProtocolUtil.h" #include "XSynergy.h" @@ -214,6 +215,10 @@ CClientProxyUnknown::handleData(const CEvent&, void*) case 1: m_proxy = new CClientProxy1_1(name, m_stream); break; + + case 2: + m_proxy = new CClientProxy1_2(name, m_stream); + break; } } diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index a4893cc8..03e0eb2d 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -629,6 +629,9 @@ CConfig::getOptionName(OptionID id) if (id == kOptionXTestXineramaUnaware) { return "xtestIsXineramaUnaware"; } + if (id == kOptionRelativeMouseMoves) { + return "relativeMouseMoves"; + } return NULL; } @@ -638,7 +641,8 @@ CConfig::getOptionValue(OptionID id, OptionValue value) if (id == kOptionHalfDuplexCapsLock || id == kOptionHalfDuplexNumLock || id == kOptionScreenSaverSync || - id == kOptionXTestXineramaUnaware) { + id == kOptionXTestXineramaUnaware || + id == kOptionRelativeMouseMoves) { return (value != 0) ? "true" : "false"; } if (id == kOptionModifierMapForShift || @@ -778,6 +782,9 @@ CConfig::readSectionOptions(std::istream& s) else if (name == "screenSaverSync") { addOption("", kOptionScreenSaverSync, parseBoolean(value)); } + else if (name == "relativeMouseMoves") { + addOption("", kOptionRelativeMouseMoves, parseBoolean(value)); + } else { throw XConfigRead("unknown argument"); } diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index a8e21c3c..7ec681bc 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -185,6 +185,12 @@ CPrimaryClient::mouseMove(SInt32 x, SInt32 y) m_screen->warpCursor(x, y); } +void +CPrimaryClient::mouseRelativeMove(SInt32, SInt32) +{ + // ignore +} + void CPrimaryClient::mouseWheel(SInt32) { diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 985fc4a3..2006508c 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -102,6 +102,7 @@ public: virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); virtual void mouseWheel(SInt32 delta); virtual void screensaver(bool activate); virtual void resetOptions(); diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 27a8651a..fce2584a 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -53,7 +53,8 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_switchTwoTapDelay(0.0), m_switchTwoTapEngaged(false), m_switchTwoTapArmed(false), - m_switchTwoTapZone(3) + m_switchTwoTapZone(3), + m_relativeMoves(false) { // must have a primary client and it must have a canonical name assert(m_primaryClient != NULL); @@ -283,6 +284,9 @@ CServer::onCommandKey(KeyID id, KeyModifierMask /*mask*/, bool /*down*/) { if (id == kKeyScrollLock) { m_primaryClient->reconfigure(getActivePrimarySides()); + if (!isLockedToScreenServer()) { + stopRelativeMoves(); + } } return false; } @@ -322,13 +326,7 @@ bool CServer::isLockedToScreenServer() const { // locked if scroll-lock is toggled on - if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) { - LOG((CLOG_DEBUG "locked by ScrollLock")); - return true; - } - - // not locked - return false; + return ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0); } bool @@ -336,6 +334,7 @@ CServer::isLockedToScreen() const { // locked if we say we're locked if (isLockedToScreenServer()) { + LOG((CLOG_DEBUG "locked by ScrollLock")); return true; } @@ -841,6 +840,24 @@ CServer::isSwitchWaitStarted() const return (m_switchWaitTimer != NULL); } +void +CServer::stopRelativeMoves() +{ + if (m_relativeMoves && m_active != m_primaryClient) { + // warp to the center of the active client so we know where we are + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + m_x = ax + (aw >> 1); + m_y = ay + (ah >> 1); + m_xDelta = 0; + m_yDelta = 0; + m_xDelta2 = 0; + m_yDelta2 = 0; + LOG((CLOG_DEBUG2 "synchronize move on %s by %d,%d", getName(m_active).c_str(), m_x, m_y)); + m_active->mouseMove(m_x, m_y); + } +} + void CServer::sendOptions(IClient* client) const { @@ -883,6 +900,7 @@ CServer::processOptions() return; } + bool newRelativeMoves = m_relativeMoves; for (CConfig::CScreenOptions::const_iterator index = options->begin(); index != options->end(); ++index) { const OptionID id = index->first; @@ -901,7 +919,15 @@ CServer::processOptions() } stopSwitchTwoTap(); } + else if (id == kOptionRelativeMouseMoves) { + newRelativeMoves = (value != 0); + } } + + if (m_relativeMoves && !newRelativeMoves) { + stopRelativeMoves(); + } + m_relativeMoves = newRelativeMoves; } void @@ -1339,6 +1365,18 @@ CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) return; } + // if doing relative motion on secondary screens and we're locked + // to the screen (which activates relative moves) then send a + // relative mouse motion. when we're doing this we pretend as if + // the mouse isn't actually moving because we're expecting some + // program on the secondary screen to warp the mouse on us, so we + // have no idea where it really is. + if (m_relativeMoves && isLockedToScreenServer()) { + LOG((CLOG_DEBUG2 "relative move on %s by %d,%d", getName(m_active).c_str(), dx, dy)); + m_active->mouseRelativeMove(dx, dy); + return; + } + // save old position const SInt32 xOld = m_x; const SInt32 yOld = m_y; diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 0e28eee7..3272d837 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -26,8 +26,6 @@ #include "stdset.h" #include "stdvector.h" -class CClientProxy; -class CClientProxyUnknown; class CEventQueueTimer; class CPrimaryClient; class IClient; @@ -183,6 +181,9 @@ private: // returns true iff the delay switch timer is started bool isSwitchWaitStarted() const; + // stop relative mouse moves + void stopRelativeMoves(); + // send screen options to \c client void sendOptions(IClient* client) const; @@ -312,6 +313,9 @@ private: bool m_switchTwoTapArmed; SInt32 m_switchTwoTapZone; + // relative mouse move option + bool m_relativeMoves; + static CEvent::Type s_errorEvent; static CEvent::Type s_disconnectedEvent; }; diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index 7ffb76b4..90f3d6e1 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -27,6 +27,7 @@ libserver_a_SOURCES = \ CClientProxy.cpp \ CClientProxy1_0.cpp \ CClientProxy1_1.cpp \ + CClientProxy1_2.cpp \ CClientProxyUnknown.cpp \ CConfig.cpp \ CPrimaryClient.cpp \ @@ -35,6 +36,7 @@ libserver_a_SOURCES = \ CClientProxy.h \ CClientProxy1_0.h \ CClientProxy1_1.h \ + CClientProxy1_2.h \ CClientProxyUnknown.h \ CConfig.h \ CPrimaryClient.h \ diff --git a/lib/synergy/CPlatformScreen.h b/lib/synergy/CPlatformScreen.h index b5fcde01..d5bb7fbb 100644 --- a/lib/synergy/CPlatformScreen.h +++ b/lib/synergy/CPlatformScreen.h @@ -45,6 +45,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press) const = 0; virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; + virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; virtual void fakeMouseWheel(SInt32 delta) const = 0; // IKeyState overrides diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index 787e72ba..67140498 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -222,6 +222,13 @@ CScreen::mouseMove(SInt32 x, SInt32 y) m_screen->fakeMouseMove(x, y); } +void +CScreen::mouseRelativeMove(SInt32 dx, SInt32 dy) +{ + assert(!m_isPrimary); + m_screen->fakeMouseRelativeMove(dx, dy); +} + void CScreen::mouseWheel(SInt32 delta) { diff --git a/lib/synergy/CScreen.h b/lib/synergy/CScreen.h index ae605d9b..01b10417 100644 --- a/lib/synergy/CScreen.h +++ b/lib/synergy/CScreen.h @@ -146,6 +146,13 @@ public: */ void mouseMove(SInt32 xAbs, SInt32 yAbs); + //! Notify of mouse motion + /*! + Synthesize mouse events to generate mouse motion by the relative + amount \c xRel,yRel. + */ + void mouseRelativeMove(SInt32 xRel, SInt32 yRel); + //! Notify of mouse wheel motion /*! Synthesize mouse events to generate mouse wheel motion of \c delta. diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h index b4ef2eca..7679d104 100644 --- a/lib/synergy/IClient.h +++ b/lib/synergy/IClient.h @@ -120,6 +120,13 @@ public: */ virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + //! Notify of mouse motion + /*! + Synthesize mouse events to generate mouse motion by the relative + amount \c xRel,yRel. + */ + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0; + //! Notify of mouse wheel motion /*! Synthesize mouse events to generate mouse wheel motion of \c delta. diff --git a/lib/synergy/IPlatformScreen.h b/lib/synergy/IPlatformScreen.h index 13bd0a31..8a5da677 100644 --- a/lib/synergy/IPlatformScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -153,6 +153,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press) const = 0; virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; + virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; virtual void fakeMouseWheel(SInt32 delta) const = 0; // IKeyState overrides diff --git a/lib/synergy/ISecondaryScreen.h b/lib/synergy/ISecondaryScreen.h index 1311e7af..092cd805 100644 --- a/lib/synergy/ISecondaryScreen.h +++ b/lib/synergy/ISecondaryScreen.h @@ -40,6 +40,12 @@ public: */ virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; + //! Fake mouse move + /*! + Synthesize a mouse move to the relative coordinates \c dx,dy. + */ + virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; + //! Fake mouse wheel /*! Synthesize a mouse wheel event of amount \c delta. diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index aaefe7fa..31504993 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -55,6 +55,7 @@ static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); +static const OptionID kOptionRelativeMouseMoves = OPTION_CODE("MDLT"); //@} #undef OPTION_CODE diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index c1162c2d..4fd6d475 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -21,7 +21,7 @@ // 1.0: initial protocol // 1.1: adds KeyCode to key press, release, and repeat static const SInt16 kProtocolMajorVersion = 1; -static const SInt16 kProtocolMinorVersion = 1; +static const SInt16 kProtocolMinorVersion = 2; // default contact port number static const UInt16 kDefaultPort = 24800; @@ -180,6 +180,10 @@ static const char kMsgDMouseUp[] = "DMUP%1i"; // $1 = x, $2 = y. x,y are absolute screen coordinates. static const char kMsgDMouseMove[] = "DMMV%2i%2i"; +// relative mouse move: primary -> secondary +// $1 = dx, $2 = dy. dx,dy are motion deltas. +static const char kMsgDMouseRelMove[] = "DMRM%2i%2i"; + // mouse button pressed: primary -> secondary // $1 = delta. the delta should be +120 for one tick forward (away // from the user) and -120 for one tick backward (toward the user).