From b279c80608c997907fed015c0b17e96f53fdb699 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Apr 2002 17:48:11 +0000 Subject: [PATCH] checkpoint. now sending toggle modifier state when entering a screen. this allows the secondary screen to set it's modifier state to match the primary screen's state. this is not strictly necessary since each keystroke should adjust the modifier state as needed to get the right result. --- client/CClient.cpp | 7 ++- client/CMSWindowsSecondaryScreen.cpp | 29 +++++++++- client/CMSWindowsSecondaryScreen.h | 4 +- client/CXWindowsSecondaryScreen.cpp | 84 +++++++++++++++++++++++----- client/CXWindowsSecondaryScreen.h | 7 ++- server/CMSWindowsPrimaryScreen.cpp | 12 ++++ server/CMSWindowsPrimaryScreen.h | 1 + server/CServer.cpp | 3 +- server/CServerProtocol.h | 3 +- server/CServerProtocol1_0.cpp | 8 ++- server/CServerProtocol1_0.h | 3 +- server/CXWindowsPrimaryScreen.cpp | 67 ++++++++++++++++++++++ server/CXWindowsPrimaryScreen.h | 10 ++++ synergy/IPrimaryScreen.h | 5 ++ synergy/ISecondaryScreen.h | 3 +- synergy/IServerProtocol.h | 3 +- synergy/ProtocolTypes.h | 7 ++- 17 files changed, 224 insertions(+), 32 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 2e278cb7..d1ef18a6 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -341,13 +341,14 @@ void CClient::closeSecondaryScreen() void CClient::onEnter() { SInt16 x, y; + UInt16 mask; { CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum); + CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum, &mask); m_active = true; } - log((CLOG_DEBUG1 "recv enter, %d,%d %d", x, y, m_seqNum)); - m_screen->enter(x, y); + log((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, m_seqNum, mask)); + m_screen->enter(x, y, static_cast(mask)); } void CClient::onLeave() diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index e1dc6ab4..6d2fd886 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -109,11 +109,12 @@ void CMSWindowsSecondaryScreen::close() m_client = NULL; } -void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) +void CMSWindowsSecondaryScreen::enter( + SInt32 x, SInt32 y, KeyModifierMask mask) { assert(m_window != NULL); - log((CLOG_INFO "entering screen at %d,%d", x, y)); + log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); // warp to requested location SInt32 w, h; @@ -130,6 +131,17 @@ void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) // update our keyboard state to reflect the local state updateKeys(); updateModifiers(); + + // toggle modifiers that don't match the desired state + if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) { + toggleKey(VK_CAPITAL, KeyModifierCapsLock); + } + if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) { + toggleKey(VK_NUMLOCK, KeyModifierNumLock); + } + if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) { + toggleKey(VK_SCROLL, KeyModifierScrollLock); + } } void CMSWindowsSecondaryScreen::leave() @@ -1184,3 +1196,16 @@ void CMSWindowsSecondaryScreen::updateModifiers() if ((m_keys[VK_SCROLL] & 0x01) != 0) m_mask |= KeyModifierScrollLock; } + +void CMSWindowsSecondaryScreen::toggleKey( + UINT virtualKey, KeyModifierMask mask) +{ + // send key events to simulate a press and release + const UINT code = MapVirtualKey(virtualKey, 0); + keybd_event(virtualKey, code, 0, 0); + keybd_event(virtualKey, code, KEYEVENTF_KEYUP, 0); + + // toggle shadow state + m_mask ^= mask; + m_keys[virtualKey] ^= 0x01; +} diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index dbd9b5a8..56235c65 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -16,7 +16,8 @@ public: virtual void stop(); virtual void open(CClient*); virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, + KeyModifierMask mask); virtual void leave(); virtual void keyDown(KeyID, KeyModifierMask); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -48,6 +49,7 @@ private: void updateKeys(); void updateModifiers(); + void toggleKey(UINT virtualKey, KeyModifierMask mask); private: CClient* m_client; diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index d1ea46ab..8ec64d2d 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -177,7 +177,8 @@ void CXWindowsSecondaryScreen::close() m_client = NULL; } -void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) +void CXWindowsSecondaryScreen::enter( + SInt32 x, SInt32 y, KeyModifierMask mask) { assert(m_window != None); @@ -193,6 +194,19 @@ void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) // update our keyboard state to reflect the local state updateKeys(display); updateModifiers(display); + + // toggle modifiers that don't match the desired state + unsigned int xMask = maskToX(mask); + if ((xMask & m_capsLockMask) != (m_mask & m_capsLockMask)) { + toggleKey(display, XK_Caps_Lock, m_capsLockMask); + } + if ((xMask & m_numLockMask) != (m_mask & m_numLockMask)) { + toggleKey(display, XK_Num_Lock, m_numLockMask); + } + if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) { + toggleKey(display, XK_Scroll_Lock, m_scrollLockMask); + } + XSync(display, False); } void CXWindowsSecondaryScreen::leave() @@ -461,9 +475,14 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( const KeyCode modifierKey = modifierKeys[0]; keys.push_back(std::make_pair(modifierKey, True)); if ((bit & m_toggleModifierMask) != 0) { - keys.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, True)); + if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { + keys.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, True)); + } + else { + undo.push_back(std::make_pair(modifierKey, False)); + } } else { undo.push_back(std::make_pair(modifierKey, False)); @@ -476,12 +495,18 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // press/release, otherwise deactivate it with a // release. we must check each keycode for the // modifier if not a toggle. - if (bit & m_toggleModifierMask) { + if ((bit & m_toggleModifierMask) != 0) { const KeyCode modifierKey = modifierKeys[0]; - keys.push_back(std::make_pair(modifierKey, True)); - keys.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, True)); + if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { + keys.push_back(std::make_pair(modifierKey, True)); + keys.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, True)); + } + else { + keys.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, True)); + } } else { for (unsigned int j = 0; j < m_keysPerModifier; ++j) { @@ -601,11 +626,11 @@ unsigned int CXWindowsSecondaryScreen::maskToX( if (inMask & KeyModifierMeta) outMask |= Mod4Mask; if (inMask & KeyModifierCapsLock) - outMask |= LockMask; + outMask |= m_capsLockMask; if (inMask & KeyModifierNumLock) - outMask |= Mod2Mask; + outMask |= m_numLockMask; if (inMask & KeyModifierScrollLock) - outMask |= Mod5Mask; + outMask |= m_scrollLockMask; return outMask; } @@ -712,6 +737,7 @@ void CXWindowsSecondaryScreen::updateModifierMap( m_toggleModifierMask = 0; m_numLockMask = 0; m_capsLockMask = 0; + m_scrollLockMask = 0; m_keysPerModifier = keymap->max_keypermod; m_modifierToKeycode.clear(); m_modifierToKeycode.resize(8 * m_keysPerModifier); @@ -738,10 +764,15 @@ void CXWindowsSecondaryScreen::updateModifierMap( m_toggleModifierMask |= bit; // note num/caps-lock - if (keysym == XK_Num_Lock) + if (keysym == XK_Num_Lock) { m_numLockMask |= bit; - if (keysym == XK_Caps_Lock) + } + else if (keysym == XK_Caps_Lock) { m_capsLockMask |= bit; + } + else if (keysym == XK_Scroll_Lock) { + m_scrollLockMask |= bit; + } } } } @@ -749,6 +780,31 @@ void CXWindowsSecondaryScreen::updateModifierMap( XFreeModifiermap(keymap); } +void CXWindowsSecondaryScreen::toggleKey( + Display* display, + KeySym keysym, unsigned int mask) +{ + // lookup the keycode + KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym); + if (index == m_keycodeMap.end()) + return; + KeyCode keycode = index->second.keycode; + + // toggle the key + if (keysym == XK_Caps_Lock && m_capsLockHalfDuplex) { + // "half-duplex" toggle + XTestFakeKeyEvent(display, keycode, (m_mask & mask) == 0, CurrentTime); + } + else { + // normal toggle + XTestFakeKeyEvent(display, keycode, True, CurrentTime); + XTestFakeKeyEvent(display, keycode, False, CurrentTime); + } + + // toggle shadow state + m_mask ^= mask; +} + bool CXWindowsSecondaryScreen::isToggleKeysym(KeySym key) { switch (key) { diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 66fe2c55..15388442 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -15,7 +15,8 @@ public: virtual void stop(); virtual void open(CClient*); virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, + KeyModifierMask mask); virtual void leave(); virtual void keyDown(KeyID, KeyModifierMask); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -62,6 +63,7 @@ private: void updateKeycodeMap(Display* display); void updateModifiers(Display* display); void updateModifierMap(Display* display); + void toggleKey(Display*, KeySym, unsigned int mask); static bool isToggleKeysym(KeySym); private: @@ -88,9 +90,10 @@ private: // set bits indicate modifiers that toggle (e.g. caps-lock) unsigned int m_toggleModifierMask; - // masks that indicate which modifier bits are num-lock and caps-lock + // masks that indicate which modifier bits are for toggle keys unsigned int m_numLockMask; unsigned int m_capsLockMask; + unsigned int m_scrollLockMask; // map X modifier key indices to the key codes bound to them unsigned int m_keysPerModifier; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 9c4f3169..51196271 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -252,6 +252,18 @@ void CMSWindowsPrimaryScreen::getClipboard( CClipboard::copy(dst, &src); } +KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const +{ + KeyModifierMask mask; + if ((m_keys[VK_CAPITAL] & 0x01) != 0) + mask |= KeyModifierCapsLock; + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + mask |= KeyModifierNumLock; + if ((m_keys[VK_SCROLL] & 0x01) != 0) + mask |= KeyModifierScrollLock; + return mask; +} + #include "resource.h" // FIXME void CMSWindowsPrimaryScreen::onOpenDisplay() diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 9e19a4b5..f24d51a2 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -26,6 +26,7 @@ public: virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; + virtual KeyModifierMask getToggleMask() const; protected: // CMSWindowsScreen overrides diff --git a/server/CServer.cpp b/server/CServer.cpp index 4e82f043..aaea5f20 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -537,7 +537,8 @@ void CServer::switchScreen(CScreenInfo* dst, m_primary->enter(x, y); } else { - m_active->m_protocol->sendEnter(x, y, m_seqNum); + m_active->m_protocol->sendEnter(x, y, m_seqNum, + m_primary->getToggleMask()); } // send the clipboard data to new active screen diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index d2cb2d80..c64fa6cf 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -31,7 +31,8 @@ public: virtual void run() = 0; virtual void queryInfo() = 0; virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask) = 0; virtual void sendLeave() = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 33159fc2..7e9a9802 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -91,10 +91,12 @@ void CServerProtocol1_0::sendClose() } void CServerProtocol1_0::sendEnter( - SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) + SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask) { - log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d", getClient().c_str(), xAbs, yAbs, seqNum)); - CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs, seqNum); + log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getClient().c_str(), xAbs, yAbs, seqNum, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgCEnter, + xAbs, yAbs, seqNum, mask); } void CServerProtocol1_0::sendLeave() diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index f992bec8..e7fbcc58 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -16,7 +16,8 @@ public: virtual void run(); virtual void queryInfo(); virtual void sendClose(); - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum); + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask); virtual void sendLeave(); virtual void sendClipboard(ClipboardID, const CString&); virtual void sendGrabClipboard(ClipboardID); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index bafcbd02..bff3596b 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -47,6 +47,7 @@ void CXWindowsPrimaryScreen::run() // keyboard mapping changed CDisplayLock display(this); XRefreshKeyboardMapping(&xevent.xmapping); + updateModifierMap(display); break; } @@ -219,6 +220,12 @@ void CXWindowsPrimaryScreen::open(CServer* server) // FIXME -- may have to get these from some database m_capsLockHalfDuplex = false; // m_capsLockHalfDuplex = true; + + // update key state + { + CDisplayLock display(this); + updateModifierMap(display); + } } void CXWindowsPrimaryScreen::close() @@ -365,6 +372,31 @@ void CXWindowsPrimaryScreen::getClipboard( getDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window)); } +KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const +{ + CDisplayLock display(this); + + // query the pointer to get the keyboard state + // FIXME -- is there a better way to do this? + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (!XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) + return 0; + + // convert to KeyModifierMask + KeyModifierMask mask; + if (state & m_numLockMask) + mask |= KeyModifierNumLock; + if (state & m_capsLockMask) + mask |= KeyModifierCapsLock; + if (state & m_scrollLockMask) + mask |= KeyModifierScrollLock; + + return mask; +} + void CXWindowsPrimaryScreen::onOpenDisplay() { assert(m_window == None); @@ -483,3 +515,38 @@ ButtonID CXWindowsPrimaryScreen::mapButton( else return kButtonNone; } + +void CXWindowsPrimaryScreen::updateModifierMap( + Display* display) +{ + // get modifier map from server + XModifierKeymap* keymap = XGetModifierMapping(display); + + // initialize + m_numLockMask = 0; + m_capsLockMask = 0; + m_scrollLockMask = 0; + + // set keycodes and masks + for (unsigned int i = 0; i < 8; ++i) { + const unsigned int bit = (1 << i); + for (int j = 0; j < keymap->max_keypermod; ++j) { + KeyCode keycode = keymap->modifiermap[i * + keymap->max_keypermod + j]; + + // note toggle modifier bits + const KeySym keysym = XKeycodeToKeysym(display, keycode, 0); + if (keysym == XK_Num_Lock) { + m_numLockMask |= bit; + } + else if (keysym == XK_Caps_Lock) { + m_capsLockMask |= bit; + } + else if (keysym == XK_Scroll_Lock) { + m_scrollLockMask |= bit; + } + } + } + + XFreeModifiermap(keymap); +} diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index ba1b4584..5df90544 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -24,6 +24,7 @@ public: virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; + virtual KeyModifierMask getToggleMask() const; protected: // CXWindowsScreen overrides @@ -40,12 +41,21 @@ private: KeyID mapKey(XKeyEvent*) const; ButtonID mapButton(unsigned int button) const; + void updateModifierMap(Display* display); + private: CServer* m_server; bool m_active; Window m_window; + // note if caps lock key toggles on up/down (false) or on + // transition (true) bool m_capsLockHalfDuplex; + + // masks that indicate which modifier bits are for toggle keys + unsigned int m_numLockMask; + unsigned int m_capsLockMask; + unsigned int m_scrollLockMask; }; #endif diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 198c917a..64c44bec 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -79,6 +79,11 @@ public: // and should avoid setting the clipboard object if the screen's // clipboard hasn't changed. virtual void getClipboard(ClipboardID, IClipboard*) const = 0; + + // get the primary screen's current toggle modifier key state. + // the returned mask should have the corresponding bit set for + // each toggle key that is active. + virtual KeyModifierMask getToggleMask() const = 0; }; #endif diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 11752c2b..c7f1ba3a 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -34,7 +34,8 @@ public: // called when the user navigates to the secondary screen. warp // the cursor to the given coordinates and unhide it. prepare to // simulate input events. - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, + KeyModifierMask mask) = 0; // called when the user navigates off the secondary screen. clean // up input event simulation and hide the cursor. diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index 1bf4a9df..c8d40f2a 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -24,7 +24,8 @@ public: // send various messages to client virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask) = 0; virtual void sendLeave() = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index f9b5edf7..13588df9 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -24,8 +24,11 @@ static const char kMsgCClose[] = "CBYE"; // entering screen at screen position $1 = x, $2 = y. x,y are // absolute screen coordinates. $3 = sequence number, which is // used to order messages between screens. the secondary screen -// must return this number with some messages. -static const char kMsgCEnter[] = "CINN%2i%2i%4i"; +// must return this number with some messages. $4 = modifier key +// mask. this will have bits set for each toggle modifier key +// that is activated on entry to the screen. the secondary screen +// should adjust its toggle modifiers to reflect that state. +static const char kMsgCEnter[] = "CINN%2i%2i%4i%2i"; // leave screen: primary -> secondary // leaving screen. the secondary screen should send clipboard