diff --git a/lib/platform/CMSWindowsClipboard.cpp b/lib/platform/CMSWindowsClipboard.cpp index 32aad8e1..7df5d61f 100644 --- a/lib/platform/CMSWindowsClipboard.cpp +++ b/lib/platform/CMSWindowsClipboard.cpp @@ -21,6 +21,8 @@ // CMSWindowsClipboard // +UINT CMSWindowsClipboard::s_ownershipFormat = 0; + CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : m_window(window), m_time(0) @@ -40,11 +42,16 @@ CMSWindowsClipboard::empty() { LOG((CLOG_DEBUG "empty clipboard")); + // empty the clipboard (and take ownership) if (!EmptyClipboard()) { LOG((CLOG_DEBUG "failed to grab clipboard")); return false; } + // mark clipboard as being owned by synergy + HGLOBAL data = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, 1); + SetClipboardData(getOwnershipFormat(), data); + return true; } @@ -159,3 +166,25 @@ CMSWindowsClipboard::clearConverters() } m_converters.clear(); } + +bool +CMSWindowsClipboard::isOwnedBySynergy() +{ + // create ownership format if we haven't yet + if (s_ownershipFormat == 0) { + s_ownershipFormat = RegisterClipboardFormat(TEXT("SynergyOwnership")); + } + return (IsClipboardFormatAvailable(getOwnershipFormat()) != 0); +} + +UINT +CMSWindowsClipboard::getOwnershipFormat() +{ + // create ownership format if we haven't yet + if (s_ownershipFormat == 0) { + s_ownershipFormat = RegisterClipboardFormat(TEXT("SynergyOwnership")); + } + + // return the format + return s_ownershipFormat; +} diff --git a/lib/platform/CMSWindowsClipboard.h b/lib/platform/CMSWindowsClipboard.h index a2a65b57..ce7ba724 100644 --- a/lib/platform/CMSWindowsClipboard.h +++ b/lib/platform/CMSWindowsClipboard.h @@ -28,6 +28,9 @@ public: CMSWindowsClipboard(HWND window); virtual ~CMSWindowsClipboard(); + //! Test if clipboard is owned by synergy + static bool isOwnedBySynergy(); + // IClipboard overrides virtual bool empty(); virtual void add(EFormat, const CString& data); @@ -44,12 +47,15 @@ private: HANDLE convertTextToWin32(const CString& data) const; CString convertTextFromWin32(HANDLE) const; + static UINT getOwnershipFormat(); + private: typedef std::vector ConverterList; HWND m_window; mutable Time m_time; ConverterList m_converters; + static UINT s_ownershipFormat; }; //! Clipboard format converter interface diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 98785b08..bee3ceb8 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -385,12 +385,45 @@ KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { KeyModifierMask mask = 0; - if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) - mask |= KeyModifierCapsLock; - if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) - mask |= KeyModifierNumLock; - if ((GetKeyState(VK_SCROLL) & 0x01) != 0) - mask |= KeyModifierScrollLock; + if (isActive()) { + // get key state + 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; + } + else { + // show the window, but make it very small. we must do this + // because GetKeyState() reports the key state according to + // processed messages and until the window is visible the + // system won't update the state of the toggle keys reported + // by that function. unfortunately, this slows this method + // down significantly and, for some reason i don't understand, + // causes everything on the screen to redraw. + if (m_window != NULL) { + MoveWindow(m_window, 1, 1, 1, 1, FALSE); + const_cast(this)->showWindow(); + } + + // get key state + if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) + mask |= KeyModifierCapsLock; + if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) + mask |= KeyModifierNumLock; + if ((GetKeyState(VK_SCROLL) & 0x01) != 0) + mask |= KeyModifierScrollLock; + + // make the window hidden again and restore its size + if (m_window != NULL) { + const_cast(this)->hideWindow(); + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + MoveWindow(m_window, x, y, w, h, FALSE); + } + } + return mask; } @@ -459,6 +492,57 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) { assert(event != NULL); + const MSG* msg = &event->m_msg; + + // check if windows key is up but we think it's down. if so then + // synthesize a key release for it. we have to do this because + // if the user presses and releases a windows key without pressing + // any other key when its down then windows will eat the key + // release. if we don't detect that an synthesize the release + // then the user will be locked to the screen and the client won't + // take the usual windows key release action (which on windows is + // to show the start menu). + // + // we can use GetKeyState() to check the state of the windows keys + // because, event though the key release is not reported to us, + // the event is processed and the keyboard state updated by the + // system. since the key could go up at any time we'll check the + // state on every event. only check on windows 95 family since + // NT family reports the key release as usual. obviously we skip + // this if the event is for the windows key itself. + if (m_is95Family) { + if ((m_keys[VK_LWIN] & 0x80) != 0 && + (GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 && + !(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_LWIN)) { + // compute appropriate parameters for fake event + WPARAM wParam = VK_LWIN; + LPARAM lParam = 0xc1000000; + lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); + + // process as if it were a key up + KeyModifierMask mask; + const KeyID key = mapKey(wParam, lParam, &mask); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x", key, mask)); + m_receiver->onKeyUp(key, mask); + updateKey(wParam, false); + } + if ((m_keys[VK_RWIN] & 0x80) != 0 && + (GetAsyncKeyState(VK_RWIN) & 0x8000) == 0 && + !(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_RWIN)) { + // compute appropriate parameters for fake event + WPARAM wParam = VK_RWIN; + LPARAM lParam = 0xc1000000; + lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); + + // process as if it were a key up + KeyModifierMask mask; + const KeyID key = mapKey(wParam, lParam, &mask); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x", key, mask)); + m_receiver->onKeyUp(key, mask); + updateKey(wParam, false); + } + } + // handle event const MSG* msg = &event->m_msg; switch (msg->message) { @@ -474,8 +558,9 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) if (key != kKeyNone) { if ((msg->lParam & 0x80000000) == 0) { // key press + const bool wasDown = ((msg->lParam & 0x40000000) != 0); const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); - if (repeat >= 2) { + if (repeat >= 2 || wasDown) { LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); m_receiver->onKeyRepeat(key, mask, repeat); } @@ -488,7 +573,20 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) updateKey(msg->wParam, true); } else { - // key release + // key release. if the key isn't down according to + // our table then we never got the key press event + // for it. if it's not a modifier key then we'll + // synthesize the press first. only do this on + // the windows 95 family, which eats certain special + // keys like alt+tab, ctrl+esc, etc. + if (m_is95Family && !isModifier(msg->wParam) && + (m_keys[msg->wParam] & 0x80) == 0) { + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x", key, mask)); + m_receiver->onKeyDown(key, mask); + updateKey(msg->wParam, true); + } + + // do key up LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask)); m_receiver->onKeyUp(key, mask); @@ -583,7 +681,9 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) if (!isActive()) { // motion on primary screen - m_receiver->onMouseMovePrimary(m_x, m_y); + if (x != 0 || y != 0) { + m_receiver->onMouseMovePrimary(m_x, m_y); + } } else { // motion on secondary screen. warp mouse back to @@ -1271,7 +1371,7 @@ CMSWindowsPrimaryScreen::mapKey( } if (((m_keys[VK_LWIN] | m_keys[VK_RWIN]) & 0x80) != 0) { - mask |= KeyModifierMeta; + mask |= KeyModifierSuper; } if ((m_keys[VK_CAPITAL] & 0x01) != 0) { mask |= KeyModifierCapsLock; @@ -1523,3 +1623,29 @@ CMSWindowsPrimaryScreen::updateKey(UINT vkCode, bool press) } } } + +bool +CMSWindowsPrimaryScreen::isModifier(UINT vkCode) const + +{ + switch (vkCode) { + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + case VK_LWIN: + case VK_RWIN: + return true; + + default: + return false; + } +} diff --git a/lib/platform/CMSWindowsPrimaryScreen.h b/lib/platform/CMSWindowsPrimaryScreen.h index c274de40..f179a90c 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.h +++ b/lib/platform/CMSWindowsPrimaryScreen.h @@ -89,6 +89,7 @@ private: KeyModifierMask* maskOut); ButtonID mapButton(WPARAM button) const; void updateKey(UINT vkCode, bool press); + bool isModifier(UINT vkCode) const; private: IPrimaryScreenReceiver* m_receiver; diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 188c070c..1e848b1c 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -60,7 +60,7 @@ CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver, m_threadID(0), m_lastThreadID(0), m_nextClipboardWindow(NULL), - m_clipboardOwner(NULL), + m_ownClipboard(false), m_timer(0), m_desk(NULL), m_deskName(), @@ -107,11 +107,9 @@ CMSWindowsScreen::openDesktop() } // poll input desktop to see if it changes (onPreDispatch() - // handles WM_TIMER) - m_timer = 0; - if (!m_is95Family) { - m_timer = SetTimer(NULL, 0, 200, NULL); - } + // handles WM_TIMER). this is also used for polling other + // stuff. + m_timer = SetTimer(NULL, 0, 200, NULL); return m_window; } @@ -321,13 +319,10 @@ CMSWindowsScreen::checkClipboards() // 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_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } + if (m_ownClipboard && !CMSWindowsClipboard::isOwnedBySynergy()) { + m_ownClipboard = false; + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); } } @@ -510,6 +505,10 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) } } } + + // let client do timer related stuff. ignore the return + // value though since the event has been handled here. + m_eventHandler->onPreDispatch(event); } else if (msg->wParam == m_oneShotTimer) { // one shot timer expired @@ -517,9 +516,11 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) m_oneShotTimer = 0; m_eventHandler->onOneShotTimerExpired(0); } + return true; } + // let client handle the event return m_eventHandler->onPreDispatch(event); } @@ -560,14 +561,16 @@ CMSWindowsScreen::onEvent(CEvent* event) } // now notify client that somebody changed the clipboard (unless - // we're now the owner, in which case it's because we took - // ownership, or now it's owned by nobody, which will happen if - // we owned it and switched desktops because we destroy our - // window to do that). - m_clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); + // we're the owner). + if (!CMSWindowsClipboard::isOwnedBySynergy()) { + if (m_ownClipboard) { + m_ownClipboard = false; + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } + } + else { + m_ownClipboard = true; } return true; @@ -631,8 +634,8 @@ CMSWindowsScreen::createBlankCursor() bool CMSWindowsScreen::switchDesktop(HDESK desk) { - // did we own the clipboard? - bool ownClipboard = (m_clipboardOwner == m_window && m_window != NULL); + // assume we don't own the clipboard until later + m_ownClipboard = false; // destroy old window if (m_window != NULL) { @@ -640,11 +643,6 @@ CMSWindowsScreen::switchDesktop(HDESK desk) ChangeClipboardChain(m_window, m_nextClipboardWindow); m_nextClipboardWindow = NULL; - // we no longer own the clipboard - if (ownClipboard) { - m_clipboardOwner = NULL; - } - // let client clean up before we destroy the window m_eventHandler->preDestroyWindow(m_window); @@ -712,12 +710,8 @@ CMSWindowsScreen::switchDesktop(HDESK desk) // install our clipboard snooper m_nextClipboardWindow = SetClipboardViewer(m_window); - // reassert clipboard ownership - if (ownClipboard) { - // FIXME -- take clipboard ownership, but we should also set - // the clipboard data. - } - m_clipboardOwner = GetClipboardOwner(); + // check if we own the clipboard + m_ownClipboard = CMSWindowsClipboard::isOwnedBySynergy(); // save new desktop m_desk = desk; diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index d11f5ef1..b7b97156 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -172,7 +172,7 @@ private: // clipboard stuff HWND m_nextClipboardWindow; - HWND m_clipboardOwner; + bool m_ownClipboard; // the timer used to check for desktop switching UINT m_timer; diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 079b8e30..885ec8d0 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -254,7 +254,6 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) } else { PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); - return 0; } } }