diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 6d2fd886..973bb004 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -5,6 +5,7 @@ #include "CThread.h" #include "CLog.h" #include +#include // // CMSWindowsSecondaryScreen @@ -40,7 +41,7 @@ static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lPar case WM_APP: if (!s_logMore.empty()) { - if (s_log.size() > 20000) + if (s_log.size() > 40000) s_log = s_logMore; else s_log += s_logMore; @@ -189,15 +190,12 @@ void CMSWindowsSecondaryScreen::keyDown( // get the sequence of keys to simulate key press and the final // modifier state. - m_mask = mapKey(keys, virtualKey, key, mask, true); + m_mask = mapKey(keys, virtualKey, key, mask, kPress); if (keys.empty()) return; // generate key events - for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) { - const UINT code = MapVirtualKey(k->first, 0); - keybd_event(k->first, code, k->second ? 0 : KEYEVENTF_KEYUP, 0); - } + doKeystrokes(keys, 1); // note that key is now down m_keys[virtualKey] |= 0x80; @@ -209,22 +207,14 @@ void CMSWindowsSecondaryScreen::keyRepeat( Keystrokes keys; UINT virtualKey; - // get the sequence of keys to simulate key release and the final + // get the sequence of keys to simulate key repeat and the final // modifier state. - m_mask = mapKey(keys, virtualKey, key, mask, true); + m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); if (keys.empty()) return; // generate key events -// YYY -- need to know which code in Keystrokes should be repeated; -// then repeat only that key count times - for (SInt32 i = 0; i < count; ++i) { - for (Keystrokes::const_iterator k = keys.begin(); - k != keys.end(); ++k) { - const UINT code = MapVirtualKey(k->first, 0); - keybd_event(k->first, code, k->second ? 0 : KEYEVENTF_KEYUP, 0); - } - } + doKeystrokes(keys, count); } void CMSWindowsSecondaryScreen::keyUp( @@ -235,15 +225,12 @@ void CMSWindowsSecondaryScreen::keyUp( // get the sequence of keys to simulate key release and the final // modifier state. - m_mask = mapKey(keys, virtualKey, key, mask, false); + m_mask = mapKey(keys, virtualKey, key, mask, kRelease); if (keys.empty()) return; // generate key events - for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) { - const UINT code = MapVirtualKey(k->first, 0); - keybd_event(k->first, code, k->second ? 0 : KEYEVENTF_KEYUP, 0); - } + doKeystrokes(keys, 1); // note that key is now up m_keys[virtualKey] &= ~0x80; @@ -252,7 +239,7 @@ void CMSWindowsSecondaryScreen::keyUp( void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) { // map button id to button flag - DWORD flags = mapButton(button); + DWORD flags = mapButton(button, true); // send event if (flags != 0) @@ -262,7 +249,7 @@ void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) { // map button id to button flag - DWORD flags = mapButton(button); + DWORD flags = mapButton(button, false); // send event if (flags != 0) @@ -786,7 +773,7 @@ static const UINT g_function[] = /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ VK_TAB, 0, 0, 0, 0, 0, 0, 0, /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -815,10 +802,6 @@ static const UINT g_function[] = /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 }; -// FIXME -- will this work? -// 0x100 + = shift -// 0x200 + = ctrl -// 0x400 + = alt /* XK_KP_Space to XK_KP_Equal */ static const UINT g_miscellany[] = { @@ -919,18 +902,18 @@ static const UINT* g_mapTable[] = }; DWORD CMSWindowsSecondaryScreen::mapButton( - ButtonID button) const + ButtonID button, bool press) const { // map button id to button flag switch (button) { case kButtonLeft: - return MOUSEEVENTF_LEFTDOWN; + return press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; case kButtonMiddle: - return MOUSEEVENTF_MIDDLEDOWN; + return press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; case kButtonRight: - return MOUSEEVENTF_RIGHTDOWN; + return press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; default: return 0; @@ -941,7 +924,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( Keystrokes& keys, UINT& virtualKey, KeyID id, KeyModifierMask mask, - bool press) const + EKeyAction action) const { // lookup the key table const UInt32 mapID = ((id >> 8) & 0xff); @@ -959,17 +942,20 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( (KeyModifierCapsLock | KeyModifierNumLock | KeyModifierScrollLock)); + log((CLOG_DEBUG2 "key id %d -> virtual key %d", id, virtualKey)); // if not in map then ask system to convert ascii character if (virtualKey == 0) { if (mapID != 0) { // not ascii + log((CLOG_DEBUG2 "not ascii")); return m_mask; } // translate. return no keys if unknown key. SHORT vk = VkKeyScan(code); if (vk == 0xffff) { + log((CLOG_DEBUG2 "no virtual key for character %d", code)); return m_mask; } @@ -980,11 +966,30 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( outMask |= KeyModifierControl; if (HIBYTE(vk) & 4) outMask |= KeyModifierAlt; + log((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%04x", code, LOBYTE(vk), outMask)); - // if caps-lock is on and so is shift then turn off caps-lock - if (outMask & (KeyModifierShift | KeyModifierCapsLock) == - (KeyModifierShift | KeyModifierCapsLock)) - outMask &= ~KeyModifierCapsLock; + // handle combination of caps-lock and shift. if caps-lock is + // off locally then use shift as necessary. if caps-lock is on + // locally then shift reverses its meaning (for keys that are + // subject to case conversion). + if ((m_mask & KeyModifierCapsLock) != 0) { + // caps-lock is on. note if shift is required. + log((CLOG_DEBUG2 "caps-lock is on")); + const bool needShift = ((outMask & KeyModifierShift) != 0); + + // if needShift is true then see if the key is subject to + // case conversion. if it is then caps-lock and shift + // cancel out. if not then caps-lock is ignored and shift + // is required. + if (needShift) { + // FIXME -- there should be some system call to test + // if a key is subject to case conversion. + if (tolower(code) != toupper(code)) { + log((CLOG_DEBUG2 "turn off shift")); + outMask &= ~KeyModifierShift; + } + } + } // get virtual key virtualKey = LOBYTE(vk); @@ -1001,11 +1006,22 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // virtual keys but not for numeric keys. if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) { // set required shift state based on current numlock state - if ((outMask & KeyModifierNumLock) == 0) - outMask |= KeyModifierShift; + if ((outMask & KeyModifierNumLock) == 0) { + if ((m_mask & KeyModifierNumLock) == 0) { + log((CLOG_DEBUG2 "turn on num lock for keypad key")); + outMask |= KeyModifierNumLock; + } + else { + log((CLOG_DEBUG2 "turn on shift for keypad key")); + outMask |= KeyModifierShift; + } + } } - // FIXME -- should check for LeftTab KeySym + // check for ISO_Left_Tab + else if (id == 0xfe20) { + outMask |= KeyModifierShift; + } } // a list of modifier key info @@ -1078,6 +1094,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // a modifier key then skip this because modifiers should not // modify modifiers. Keystrokes undo; + Keystroke keystroke; if (outMask != m_mask && !isModifier) { for (unsigned int i = 0; i < s_numModifiers; ++i) { KeyModifierMask bit = s_modifier[i].mask; @@ -1087,15 +1104,20 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // modifier is a toggle then toggle it on with a // press/release, otherwise activate it with a // press. - const UINT modifierKey = s_modifier[i].virtualKey; - keys.push_back(std::make_pair(modifierKey, true)); + keystroke.m_virtualKey = s_modifier[i].virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); if (s_modifier[i].isToggle) { - keys.push_back(std::make_pair(modifierKey, false)); - undo.push_back(std::make_pair(modifierKey, false)); - undo.push_back(std::make_pair(modifierKey, true)); + keystroke.m_press = false; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); } else { - undo.push_back(std::make_pair(modifierKey, false)); + keystroke.m_press = false; + undo.push_back(keystroke); } } @@ -1106,22 +1128,34 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // release. we must check each keycode for the // modifier if not a toggle. if (s_modifier[i].isToggle) { - const UINT modifierKey = s_modifier[i].virtualKey; - 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)); + keystroke.m_virtualKey = s_modifier[i].virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); } else { UINT key = s_modifier[i].virtualKey; if ((m_keys[key] & 0x80) != 0) { - keys.push_back(std::make_pair(key, false)); - undo.push_back(std::make_pair(key, true)); + keystroke.m_virtualKey = key; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); } key = s_modifier[i].virtualKey2; - if ((m_keys[key] & 0x80) != 0) { - keys.push_back(std::make_pair(key, false)); - undo.push_back(std::make_pair(key, true)); + if (key != 0 && (m_keys[key] & 0x80) != 0) { + keystroke.m_virtualKey = key; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); } } } @@ -1130,7 +1164,26 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( } // add the key event - keys.push_back(std::make_pair(virtualKey, press)); + keystroke.m_virtualKey = virtualKey; + switch (action) { + case kPress: + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRelease: + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRepeat: + keystroke.m_press = true; + keystroke.m_repeat = true; + keys.push_back(keystroke); + break; + } // add key events to restore the modifier state. apply events in // the reverse order that they're stored in undo. @@ -1142,16 +1195,15 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // if the key is a modifier key then compute the modifier mask after // this key is pressed. mask = m_mask; - if (isModifier) { - // toggle keys modify the state on press if toggling on and on - // release if toggling off. other keys set the bit on press - // and clear the bit on release. - // FIXME -- verify if that's true on win32 + if (isModifier && action != kRepeat) { + // toggle keys modify the state on release. other keys set + // the bit on press and clear the bit on release. if (s_modifier[modifierIndex].isToggle) { - if (((mask & s_modifier[modifierIndex].mask) == 0) == press) + if (action == kRelease) { mask ^= s_modifier[modifierIndex].mask; + } } - else if (press) { + else if (action == kPress) { mask |= s_modifier[modifierIndex].mask; } else { @@ -1161,7 +1213,8 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( if ((m_keys[s_modifier[modifierIndex].virtualKey] & 0x80) != 0) { down = true; } - if ((m_keys[s_modifier[modifierIndex].virtualKey2] & 0x80) != 0) { + if (s_modifier[modifierIndex].virtualKey2 != 0 && + (m_keys[s_modifier[modifierIndex].virtualKey2] & 0x80) != 0) { down = true; } if (!down) @@ -1169,12 +1222,65 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( } } + log((CLOG_DEBUG2 "previous modifiers 0x%04x, final modifiers 0x%04x", m_mask, mask)); return mask; } +void CMSWindowsSecondaryScreen::doKeystrokes( + const Keystrokes& keys, SInt32 count) +{ + // do nothing if no keys or no repeats + if (count < 1 || keys.empty()) + return; + + // generate key events + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { + if (k->m_repeat) { + // repeat from here up to but not including the next key + // with m_repeat == false count times. + Keystrokes::const_iterator start = k; + for (; count > 0; --count) { + // send repeating events + for (k = start; k != keys.end() && k->m_repeat; ++k) { + const UINT code = MapVirtualKey(k->m_virtualKey, 0); + keybd_event(k->m_virtualKey, code, + k->m_press ? 0 : KEYEVENTF_KEYUP, 0); + } + } + + // note -- k is now on the first non-repeat key after the + // repeat keys, exactly where we'd like to continue from. + } + else { + // send event + const UINT code = MapVirtualKey(k->m_virtualKey, 0); + keybd_event(k->m_virtualKey, code, + k->m_press ? 0 : KEYEVENTF_KEYUP, 0); + + // next key + ++k; + } + } +} + void CMSWindowsSecondaryScreen::updateKeys() { - GetKeyboardState(m_keys); + // GetKeyboardKeys() doesn't seem to do the expected thing + memset(m_keys, 0, sizeof(m_keys)); + m_keys[VK_LSHIFT] = GetKeyState(VK_LSHIFT); + m_keys[VK_RSHIFT] = GetKeyState(VK_RSHIFT); + m_keys[VK_SHIFT] = GetKeyState(VK_SHIFT); + m_keys[VK_LCONTROL] = GetKeyState(VK_LCONTROL); + m_keys[VK_RCONTROL] = GetKeyState(VK_RCONTROL); + m_keys[VK_CONTROL] = GetKeyState(VK_CONTROL); + m_keys[VK_LMENU] = GetKeyState(VK_LMENU); + m_keys[VK_RMENU] = GetKeyState(VK_RMENU); + m_keys[VK_MENU] = GetKeyState(VK_MENU); + m_keys[VK_LWIN] = GetKeyState(VK_LWIN); + m_keys[VK_RWIN] = GetKeyState(VK_RWIN); + m_keys[VK_CAPITAL] = GetKeyState(VK_CAPITAL); + m_keys[VK_NUMLOCK] = GetKeyState(VK_NUMLOCK); + m_keys[VK_SCROLL] = GetKeyState(VK_SCROLL); } void CMSWindowsSecondaryScreen::updateModifiers() @@ -1195,6 +1301,7 @@ void CMSWindowsSecondaryScreen::updateModifiers() m_mask |= KeyModifierNumLock; if ((m_keys[VK_SCROLL] & 0x01) != 0) m_mask |= KeyModifierScrollLock; + log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); } void CMSWindowsSecondaryScreen::toggleKey( diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 56235c65..d05f44ab 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -40,12 +40,19 @@ protected: virtual void onCloseDisplay(); private: - typedef std::pair Keystroke; + enum EKeyAction { kPress, kRelease, kRepeat }; + class Keystroke { + public: + UINT m_virtualKey; + bool m_press; + bool m_repeat; + }; typedef std::vector Keystrokes; - DWORD mapButton(ButtonID button) const; + DWORD mapButton(ButtonID button, bool press) const; KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, - KeyModifierMask, bool press) const; + KeyModifierMask, EKeyAction) const; + void doKeystrokes(const Keystrokes&, SInt32 count); void updateKeys(); void updateModifiers(); diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index dc631862..be55c3e7 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -135,9 +135,11 @@ void CTCPSocket::init() CNetwork::TCPNoDelayType flag = 1; CNetwork::setsockopt(m_fd, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); +#if !defined(CONFIG_PLATFORM_WIN32) // don't buffer sends, we merge messages ourself int data = 0; CNetwork::setsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &data, sizeof(data)); +#endif } void CTCPSocket::ioThread(void*) diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 51196271..8ffb036a 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -252,7 +252,7 @@ void CMSWindowsPrimaryScreen::getClipboard( CClipboard::copy(dst, &src); } -KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const +KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { KeyModifierMask mask; if ((m_keys[VK_CAPITAL] & 0x01) != 0)