diff --git a/lib/platform/CMSWindowsKeyMapper.cpp b/lib/platform/CMSWindowsKeyMapper.cpp index 8cc2641c..b07da1fd 100644 --- a/lib/platform/CMSWindowsKeyMapper.cpp +++ b/lib/platform/CMSWindowsKeyMapper.cpp @@ -14,6 +14,7 @@ #include "CMSWindowsKeyMapper.h" #include "CLog.h" +#include "CStringUtil.h" // multimedia keys #if !defined(VK_BROWSER_BACK) @@ -41,22 +42,6 @@ // CMSWindowsKeyMapper // -// table of modifier keys. note that VK_RMENU shows up under the Alt -// key and ModeSwitch. when simulating AltGr we need to use the right -// alt key so we use KeyModifierModeSwitch to get it. -const CMSWindowsKeyMapper::CModifierKeys - CMSWindowsKeyMapper::s_modifiers[] = -{ - KeyModifierShift, { VK_LSHIFT, VK_RSHIFT }, - KeyModifierControl, { VK_LCONTROL, VK_RCONTROL | 0x100 }, - KeyModifierAlt, { VK_LMENU, VK_RMENU | 0x100 }, - KeyModifierSuper, { VK_LWIN | 0x100, VK_RWIN | 0x100 }, - KeyModifierModeSwitch, { VK_RMENU | 0x100, 0 }, - KeyModifierCapsLock, { VK_CAPITAL, 0 }, - KeyModifierNumLock, { VK_NUMLOCK | 0x100, 0 }, - KeyModifierScrollLock, { VK_SCROLL, 0 } -}; - const char* CMSWindowsKeyMapper::s_vkToName[] = { "vk 0x00", @@ -578,10 +563,8 @@ const KeyID CMSWindowsKeyMapper::s_virtualKey[][2] = /* 0xff */ kKeyNone, kKeyNone // reserved }; -// map special KeyID keys to virtual key codes. if the key is an -// extended key then the entry is the virtual key code | 0x100. -// unmapped keys have a 0 entry. -const KeyButton CMSWindowsKeyMapper::s_mapE000[] = +// map special KeyID keys to virtual key codes +const UINT CMSWindowsKeyMapper::s_mapE000[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -604,15 +587,15 @@ const KeyButton CMSWindowsKeyMapper::s_mapE000[] = /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 */ 0, 0, 0, 0, - /* 0xa4 */ 0, 0, VK_BROWSER_BACK|0x100, VK_BROWSER_FORWARD|0x100, - /* 0xa8 */ VK_BROWSER_REFRESH|0x100, VK_BROWSER_STOP|0x100, - /* 0xaa */ VK_BROWSER_SEARCH|0x100, VK_BROWSER_FAVORITES|0x100, - /* 0xac */ VK_BROWSER_HOME|0x100, VK_VOLUME_MUTE|0x100, - /* 0xae */ VK_VOLUME_DOWN|0x100, VK_VOLUME_UP|0x100, - /* 0xb0 */ VK_MEDIA_NEXT_TRACK|0x100, VK_MEDIA_PREV_TRACK|0x100, - /* 0xb2 */ VK_MEDIA_STOP|0x100, VK_MEDIA_PLAY_PAUSE|0x100, - /* 0xb4 */ VK_LAUNCH_MAIL|0x100, VK_LAUNCH_MEDIA_SELECT|0x100, - /* 0xb6 */ VK_LAUNCH_APP1|0x100, VK_LAUNCH_APP2|0x100, + /* 0xa4 */ 0, 0, VK_BROWSER_BACK, VK_BROWSER_FORWARD, + /* 0xa8 */ VK_BROWSER_REFRESH, VK_BROWSER_STOP, + /* 0xaa */ VK_BROWSER_SEARCH, VK_BROWSER_FAVORITES, + /* 0xac */ VK_BROWSER_HOME, VK_VOLUME_MUTE, + /* 0xae */ VK_VOLUME_DOWN, VK_VOLUME_UP, + /* 0xb0 */ VK_MEDIA_NEXT_TRACK, VK_MEDIA_PREV_TRACK, + /* 0xb2 */ VK_MEDIA_STOP, VK_MEDIA_PLAY_PAUSE, + /* 0xb4 */ VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, + /* 0xb6 */ VK_LAUNCH_APP1, VK_LAUNCH_APP2, /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -623,7 +606,7 @@ const KeyButton CMSWindowsKeyMapper::s_mapE000[] = /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 }; -const KeyButton CMSWindowsKeyMapper::s_mapEE00[] = +const UINT CMSWindowsKeyMapper::s_mapEE00[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -661,7 +644,7 @@ const KeyButton CMSWindowsKeyMapper::s_mapEE00[] = /* in g_mapEF00, 0xac is VK_DECIMAL not VK_SEPARATOR because win32 * doesn't seem to use VK_SEPARATOR but instead maps VK_DECIMAL to * the same meaning. */ -const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = +const UINT CMSWindowsKeyMapper::s_mapEF00[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, @@ -673,22 +656,22 @@ const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ VK_HOME|0x100, VK_LEFT|0x100, VK_UP|0x100, VK_RIGHT|0x100, - /* 0x54 */ VK_DOWN|0x100, VK_PRIOR|0x100, VK_NEXT|0x100, VK_END|0x100, + /* 0x50 */ VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, + /* 0x54 */ VK_DOWN, VK_PRIOR, VK_NEXT, VK_END, /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ VK_SELECT|0x100, VK_SNAPSHOT|0x100, VK_EXECUTE|0x100, VK_INSERT|0x100, - /* 0x64 */ 0, 0, 0, VK_APPS|0x100, - /* 0x68 */ 0, 0, VK_HELP|0x100, VK_CANCEL|0x100, 0, 0, 0, 0, + /* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT, + /* 0x64 */ 0, 0, 0, VK_APPS, + /* 0x68 */ 0, 0, VK_HELP, VK_CANCEL, 0, 0, 0, 0, /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK|0x100, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK, /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN|0x100, 0, 0, + /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN, 0, 0, /* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, /* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT, /* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE, /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD, - /* 0xac */ VK_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE|0x100, + /* 0xac */ VK_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE, /* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, /* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, /* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2, @@ -697,11 +680,11 @@ const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, - /* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, 0, - /* 0xe8 */ 0, VK_LMENU, VK_RMENU|0x100, VK_LWIN|0x100, - /* 0xec */ VK_RWIN|0x100, 0, 0, 0, + /* 0xe4 */ VK_RCONTROL, VK_CAPITAL, 0, 0, + /* 0xe8 */ 0, VK_LMENU, VK_RMENU, VK_LWIN, + /* 0xec */ VK_RWIN, 0, 0, 0, /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100 + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE }; CMSWindowsKeyMapper::CMSWindowsKeyMapper() : m_deadKey(0) @@ -717,156 +700,169 @@ CMSWindowsKeyMapper::~CMSWindowsKeyMapper() void CMSWindowsKeyMapper::update(IKeyState* keyState) { - static const size_t numModifiers = sizeof(s_modifiers) / - sizeof(s_modifiers[0]); - // clear shadow state memset(m_keys, 0, sizeof(m_keys)); - // add modifiers + // clear scan code to/from virtual key mapping + memset(m_scanCodeToVirtKey, 0, sizeof(m_scanCodeToVirtKey)); + memset(m_virtKeyToScanCode, 0, sizeof(m_virtKeyToScanCode)); + + // add modifiers. note that VK_RMENU shows up under the Alt key + // and ModeSwitch. when simulating AltGr we need to use the right + // alt key so we use KeyModifierModeSwitch to get it. if (keyState != NULL) { - for (size_t i = 0; i < numModifiers; ++i) { - IKeyState::KeyButtons keys; - for (size_t j = 0; j < CModifierKeys::s_maxKeys; ++j) { - if (s_modifiers[i].m_keys[j] != 0) { - keys.push_back(s_modifiers[i].m_keys[j]); - } - } - keyState->addModifier(s_modifiers[i].m_mask, keys); - } + IKeyState::KeyButtons keys; + keys.push_back((KeyButton)MapVirtualKey(VK_LSHIFT, 0)); + keys.push_back((KeyButton)MapVirtualKey(VK_RSHIFT, 0)); + keyState->addModifier(KeyModifierShift, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_LCONTROL, 0)); + keys.push_back((KeyButton)(MapVirtualKey(VK_RCONTROL, 0) | 0x100)); + keyState->addModifier(KeyModifierControl, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_LMENU, 0)); + keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100)); + keyState->addModifier(KeyModifierAlt, keys); + keys.clear(); + keys.push_back((KeyButton)(MapVirtualKey(VK_LWIN, 0) | 0x100)); + keys.push_back((KeyButton)(MapVirtualKey(VK_RWIN, 0) | 0x100)); + keyState->addModifier(KeyModifierSuper, keys); + keys.clear(); + keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100)); + keyState->addModifier(KeyModifierModeSwitch, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_CAPITAL, 0)); + keyState->addModifier(KeyModifierCapsLock, keys); + keys.clear(); + keys.push_back((KeyButton)(MapVirtualKey(VK_NUMLOCK, 0) | 0x100)); + keyState->addModifier(KeyModifierNumLock, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_SCROLL, 0)); + keyState->addModifier(KeyModifierScrollLock, keys); + keys.clear(); } - // save current state of modifiers - for (size_t i = 0; i < numModifiers; ++i) { - for (size_t j = 0; j < CModifierKeys::s_maxKeys; ++j) { - if (s_modifiers[i].m_keys[j] != 0) { - SHORT s = GetKeyState(s_modifiers[i].m_keys[j]); - m_keys[s_modifiers[i].m_keys[j] & 0xffu] = static_cast(s); +/* FIXME -- potential problem here on win me + // win me (sony vaio laptop): + // MapVirtualKey(vk, 0): + // VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped + // VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped + // VK_MENU mapped; VK_LMENU, VK_RMENU not mapped + // MapVirtualKey(sc, 3): + // all scan codes unmapped (function apparently unimplemented) +*/ + BYTE keys[256]; + GetKeyboardState(keys); + for (UINT i = 1; i < 256; ++i) { + // skip certain virtual keys (the ones for the mouse buttons) + if (i < VK_BACK && i != VK_CANCEL) { + continue; + } + + // map to a scancode and back to a virtual key + UINT scancode = MapVirtualKey(i, 0); + UINT virtKey = MapVirtualKey(scancode, 3); + if (scancode == 0 || virtKey == 0) { + // the VK_PAUSE virtual key doesn't map properly + if (i == VK_PAUSE) { + // i hope this works on all keyboards + scancode = 0x45; + virtKey = i; + } + else { + continue; + } + } + + // we need some adjustments due to inadequacies in the API. + // if the mapped virtual key doesn't match the starting + // point then there's a really good chance that that virtual + // key is mapped to an extended key. however, this is not + // the case for modifiers that don't distinguish between left + // and right. also VK_NUMLOCK gets mapped to a non-extended + // key but it should be. + if (virtKey != i || i == VK_NUMLOCK) { + if (i != VK_SHIFT && i != VK_CONTROL && i != VK_MENU) { + scancode |= 0x100; + } + } + + // okay, now we have the scan code for the virtual key. + // save the key state. + m_scanCodeToVirtKey[scancode] = i; + m_virtKeyToScanCode[i] = (KeyButton)scancode; + m_keys[scancode] = (BYTE)(keys[i] & 0x80); + if (keyState != NULL) { + keyState->setKeyDown((KeyButton)scancode, (keys[i] & 0x80) != 0); + } + // toggle state applies to all keys but we only want it for + // the modifier keys with corresponding lights. + if ((keys[i] & 0x01) != 0) { + switch (i) { + case VK_CAPITAL: + m_keys[scancode] |= 0x01; if (keyState != NULL) { - if ((s & 0x01) != 0) { - keyState->setToggled(s_modifiers[i].m_mask); - } - if ((s & 0x80) != 0) { - keyState->setKeyDown(s_modifiers[i].m_keys[j]); - } + keyState->setToggled(KeyModifierCapsLock); } + break; + + case VK_NUMLOCK: + m_keys[scancode] |= 0x01; + if (keyState != NULL) { + keyState->setToggled(KeyModifierNumLock); + } + break; + + case VK_SCROLL: + m_keys[scancode] |= 0x01; + if (keyState != NULL) { + keyState->setToggled(KeyModifierScrollLock); + } + break; } } } } void -CMSWindowsKeyMapper::updateKey(KeyButton key, bool pressed) +CMSWindowsKeyMapper::updateKey(LPARAM eventLParam) { + bool pressed = ((eventLParam & 0x80000000u) == 0); + UINT scanCode = ((eventLParam & 0x01ff0000u) >> 16); + UINT virtKey = m_scanCodeToVirtKey[scanCode]; + if (virtKey == 0) { + // unmapped key + return; + } + if (pressed) { - switch (key) { - case 0: - case VK_LBUTTON: - case VK_MBUTTON: - case VK_RBUTTON: - // ignore bogus key - break; - - case VK_LSHIFT: - case VK_RSHIFT: - case VK_SHIFT: - m_keys[key] |= 0x80; - m_keys[VK_SHIFT] |= 0x80; - break; - - case VK_LCONTROL: - case VK_RCONTROL: - case VK_CONTROL: - m_keys[key] |= 0x80; - m_keys[VK_CONTROL] |= 0x80; - break; - - case VK_LMENU: - case VK_RMENU: - case VK_MENU: - m_keys[key] |= 0x80; - m_keys[VK_MENU] |= 0x80; - break; - - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - // toggle keys - m_keys[key] |= 0x80; - break; - - default: - case VK_LWIN: - case VK_RWIN: - case VK_APPS: - m_keys[key] |= 0x80; - break; - } + m_keys[scanCode] |= 0x80; // special case: we detect ctrl+alt+del being pressed on some // systems but we don't detect the release of those keys. so // if ctrl, alt, and del are down then mark them up. - if ((m_keys[VK_CONTROL] & 0x80) != 0 && - (m_keys[VK_MENU] & 0x80) != 0 && - (m_keys[VK_DELETE] & 0x80) != 0) { - m_keys[VK_LCONTROL] &= ~0x80; - m_keys[VK_RCONTROL] &= ~0x80; - m_keys[VK_CONTROL] &= ~0x80; - m_keys[VK_LMENU] &= ~0x80; - m_keys[VK_RMENU] &= ~0x80; - m_keys[VK_MENU] &= ~0x80; - m_keys[VK_DELETE] &= ~0x80; + if (isPressed(VK_CONTROL) && + isPressed(VK_MENU) && + isPressed(VK_DELETE)) { + m_keys[m_virtKeyToScanCode[VK_LCONTROL]] &= ~0x80; + m_keys[m_virtKeyToScanCode[VK_RCONTROL]] &= ~0x80; + m_keys[m_virtKeyToScanCode[VK_LMENU]] &= ~0x80; + m_keys[m_virtKeyToScanCode[VK_RMENU]] &= ~0x80; + m_keys[m_virtKeyToScanCode[VK_DELETE]] &= ~0x80; } } else { - switch (key) { - case 0: - case VK_LBUTTON: - case VK_MBUTTON: - case VK_RBUTTON: - // ignore bogus key - break; - - case VK_LSHIFT: - case VK_RSHIFT: - case VK_SHIFT: - m_keys[key] &= ~0x80; - if (((m_keys[VK_LSHIFT] | m_keys[VK_RSHIFT]) & 0x80) == 0) { - m_keys[VK_SHIFT] &= ~0x80; - } - break; - - case VK_LCONTROL: - case VK_RCONTROL: - case VK_CONTROL: - m_keys[key] &= ~0x80; - if (((m_keys[VK_LCONTROL] | m_keys[VK_RCONTROL]) & 0x80) == 0) { - m_keys[VK_CONTROL] &= ~0x80; - } - break; - - case VK_LMENU: - case VK_RMENU: - case VK_MENU: - m_keys[key] &= ~0x80; - if (((m_keys[VK_LMENU] | m_keys[VK_RMENU]) & 0x80) == 0) { - m_keys[VK_MENU] &= ~0x80; - } - break; + m_keys[scanCode] &= ~0x80; + // handle toggle keys + switch (virtKey) { case VK_CAPITAL: case VK_NUMLOCK: case VK_SCROLL: - // toggle keys - m_keys[key] &= ~0x80; - m_keys[key] ^= 0x01; + m_keys[scanCode] ^= 0x01; break; default: - case VK_LWIN: - case VK_RWIN: - case VK_APPS: - m_keys[key] &= ~0x80; break; } } @@ -883,7 +879,7 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, const IKeyState& keyState, KeyID id, KeyModifierMask mask, bool isAutoRepeat) const { - KeyButton virtualKey = 0; + UINT virtualKey = 0; // check for special keys if ((id & 0xfffff000u) == 0xe000u) { @@ -898,20 +894,14 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, } if (virtualKey == 0) { LOG((CLOG_DEBUG2 "unknown special key")); - return virtualKey; + return 0; } } // special handling of VK_SNAPSHOT - if ((virtualKey & 0xffu) == VK_SNAPSHOT) { + if (virtualKey == VK_SNAPSHOT) { // ignore key repeats on print screen if (!isAutoRepeat) { - // get event flags - DWORD flags = 0; - if (isExtendedKey(virtualKey)) { - flags |= KEYEVENTF_EXTENDEDKEY; - } - // active window (with alt) or fullscreen (without alt)? BYTE scan = 0; if ((mask & KeyModifierAlt) != 0) { @@ -919,9 +909,8 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, } // send events - keybd_event(static_cast(virtualKey & 0xffu), scan, flags, 0); - flags |= KEYEVENTF_KEYUP; - keybd_event(static_cast(virtualKey & 0xffu), scan, flags, 0); + keybd_event(VK_SNAPSHOT, scan, 0, 0); + keybd_event(VK_SNAPSHOT, scan, KEYEVENTF_KEYUP, 0); } return 0; } @@ -932,16 +921,13 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, KeyModifierMask requiredMask = 0; KeyModifierMask outMask = 0; - // strip out extended key flag - UINT virtualKey2 = (virtualKey & 0xffu); - // check numeric keypad. note that virtual keys do not distinguish // between the keypad and non-keypad movement keys. however, the // virtual keys do distinguish between keypad numbers and operators // (e.g. add, multiply) and their main keyboard counterparts. // therefore, we can ignore the num-lock state for movement virtual // keys but not for numeric keys. - if (virtualKey2 >= VK_NUMPAD0 && virtualKey2 <= VK_DIVIDE) { + if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) { requiredMask |= KeyModifierNumLock; if (!keyState.isModifierActive(KeyModifierNumLock)) { LOG((CLOG_DEBUG2 "turn on num lock for keypad key")); @@ -956,8 +942,9 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, } // now generate the keystrokes and return the resulting modifier mask - LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d mask 0x%04x", id, virtualKey2, outMask)); - return mapToKeystrokes(keys, keyState, virtualKey, + KeyButton scanCode = m_virtKeyToScanCode[virtualKey]; + LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d scan code 0x%04x mask 0x%04x", id, virtualKey, scanCode, outMask)); + return mapToKeystrokes(keys, keyState, scanCode, outMask, requiredMask, isAutoRepeat); } @@ -1004,15 +991,16 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id)); return 0; } - virtualKey = mapCharacter(keys, keyState, multiByte[0], hkl, isAutoRepeat); - if (virtualKey != 0) { + KeyButton button = mapCharacter(keys, keyState, + multiByte[0], hkl, isAutoRepeat); + if (button != 0) { LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); if (isDeadChar(multiByte[0], hkl, false)) { // character mapped to a dead key but we want the // character for real so send a space key afterwards. LOG((CLOG_DEBUG2 "character mapped to dead key")); IKeyState::Keystroke keystroke; - keystroke.m_key = VK_SPACE; + keystroke.m_key = m_virtKeyToScanCode[VK_SPACE]; keystroke.m_press = true; keystroke.m_repeat = false; keys.push_back(keystroke); @@ -1021,9 +1009,9 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, // ignore the release of this key since we already // handled it. - virtualKey = 0; + button = 0; } - return virtualKey; + return button; } nChars = MultiByteToWideChar(codePage, MB_COMPOSITE | MB_ERR_INVALID_CHARS, @@ -1057,9 +1045,7 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, // process character LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); - virtualKey = mapCharacter(keys, keyState, multiByte[0], hkl, isAutoRepeat); - - return virtualKey; + return mapCharacter(keys, keyState, multiByte[0], hkl, isAutoRepeat); } KeyID @@ -1129,8 +1115,7 @@ CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM charAndVirtKey, else if (LOBYTE(virtualKeyAndModifierState) != vkCode) { // we didn't get the key that was actually pressed LOG((CLOG_DEBUG1 "VkKeyScan() mismatch")); - if ((m_keys[VK_CONTROL] & 0x80) != 0 && - (m_keys[VK_MENU] & 0x80) != 0) { + if (isPressed(VK_CONTROL) && isPressed(VK_MENU)) { needAltGr = true; } } @@ -1147,121 +1132,31 @@ CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM charAndVirtKey, } // map modifier key - KeyModifierMask mask = 0; - if (((m_keys[VK_LSHIFT] | - m_keys[VK_RSHIFT] | - m_keys[VK_SHIFT]) & 0x80) != 0) { - mask |= KeyModifierShift; - } - if (needAltGr) { - mask |= KeyModifierModeSwitch; - } - else { - if (((m_keys[VK_LCONTROL] | - m_keys[VK_RCONTROL] | - m_keys[VK_CONTROL]) & 0x80) != 0) { - mask |= KeyModifierControl; - } - if (((m_keys[VK_LMENU] | - m_keys[VK_RMENU] | - m_keys[VK_MENU]) & 0x80) != 0) { - mask |= KeyModifierAlt; - } - } - if (((m_keys[VK_LWIN] | - m_keys[VK_RWIN]) & 0x80) != 0) { - mask |= KeyModifierSuper; - } - 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; - } if (maskOut != NULL) { - *maskOut = mask; + *maskOut = getShadowModifiers(needAltGr); } return id; } bool -CMSWindowsKeyMapper::isPressed(KeyButton key) const +CMSWindowsKeyMapper::isModifier(UINT virtKey) const { - return ((m_keys[key & 0xffu] & 0x80) != 0); -} - -UINT -CMSWindowsKeyMapper::keyToScanCode(KeyButton* virtualKey) const -{ - // try mapping given virtual key - UINT code = MapVirtualKeyEx((*virtualKey) & 0xffu, 0, m_keyLayout); - if (code != 0) { - return code; - } - - // no dice. if the virtual key distinguishes between left/right - // then try the one that doesn't distinguish sides. windows (or - // keyboard drivers) are inconsistent in their treatment of these - // virtual keys. the following behaviors have been observed: - // - // win2k (gateway desktop): - // MapVirtualKey(vk, 0): - // VK_SHIFT == VK_LSHIFT != VK_RSHIFT - // VK_CONTROL == VK_LCONTROL == VK_RCONTROL - // VK_MENU == VK_LMENU == VK_RMENU - // MapVirtualKey(sc, 3): - // VK_LSHIFT and VK_RSHIFT mapped independently - // VK_LCONTROL is mapped but not VK_RCONTROL - // VK_LMENU is mapped but not VK_RMENU - // - // win me (sony vaio laptop): - // MapVirtualKey(vk, 0): - // VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped - // VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped - // VK_MENU mapped; VK_LMENU, VK_RMENU not mapped - // MapVirtualKey(sc, 3): - // all scan codes unmapped (function apparently unimplemented) - switch ((*virtualKey) & 0xffu) { + switch (virtKey) { case VK_LSHIFT: case VK_RSHIFT: - *virtualKey = VK_SHIFT; - return MapVirtualKeyEx(VK_SHIFT, 0, m_keyLayout); - + case VK_SHIFT: case VK_LCONTROL: case VK_RCONTROL: - *virtualKey = VK_CONTROL; - return MapVirtualKeyEx(VK_CONTROL, 0, m_keyLayout); - + case VK_CONTROL: case VK_LMENU: case VK_RMENU: - *virtualKey = VK_MENU; - return MapVirtualKeyEx(VK_MENU, 0, m_keyLayout); - - default: - return 0; - } -} - -bool -CMSWindowsKeyMapper::isExtendedKey(KeyButton virtualKey) const -{ - // see if we've already encoded the extended flag - if ((virtualKey & 0x100u) != 0) { - return true; - } - - // check known virtual keys - switch (virtualKey & 0xffu) { + case VK_MENU: + case VK_CAPITAL: case VK_NUMLOCK: - case VK_RCONTROL: - case VK_RMENU: + case VK_SCROLL: case VK_LWIN: case VK_RWIN: - case VK_APPS: return true; default: @@ -1269,10 +1164,134 @@ CMSWindowsKeyMapper::isExtendedKey(KeyButton virtualKey) const } } +bool +CMSWindowsKeyMapper::isPressed(UINT virtKey) const +{ + switch (virtKey) { + case VK_SHIFT: + return ((m_keys[m_virtKeyToScanCode[VK_LSHIFT]] & 0x80) != 0 || + (m_keys[m_virtKeyToScanCode[VK_RSHIFT]] & 0x80) != 0); + + case VK_CONTROL: + return ((m_keys[m_virtKeyToScanCode[VK_LCONTROL]] & 0x80) != 0 || + (m_keys[m_virtKeyToScanCode[VK_RCONTROL]] & 0x80) != 0); + + case VK_MENU: + return ((m_keys[m_virtKeyToScanCode[VK_LMENU]] & 0x80) != 0 || + (m_keys[m_virtKeyToScanCode[VK_RMENU]] & 0x80) != 0); + + default: + return ((m_keys[m_virtKeyToScanCode[virtKey & 0xffu]] & 0x80) != 0); + } +} + +bool +CMSWindowsKeyMapper::isToggled(UINT virtKey) const +{ + return ((m_keys[m_virtKeyToScanCode[virtKey & 0xffu]] & 0x01) != 0); +} + +UINT +CMSWindowsKeyMapper::buttonToVirtualKey(KeyButton button) const +{ + return m_scanCodeToVirtKey[button & 0x1ffu]; +} + +KeyButton +CMSWindowsKeyMapper::virtualKeyToButton(UINT virtKey) const +{ + return m_virtKeyToScanCode[virtKey & 0xffu]; +} + +bool +CMSWindowsKeyMapper::isExtendedKey(KeyButton button) const +{ + return ((button & 0x100u) != 0); +} + +KeyModifierMask +CMSWindowsKeyMapper::getActiveModifiers() const +{ + KeyModifierMask mask = 0; + if (GetKeyState(VK_SHIFT) < 0 || + GetKeyState(VK_LSHIFT) < 0 || + GetKeyState(VK_RSHIFT) < 0) { + mask |= KeyModifierShift; + } + if (GetKeyState(VK_CONTROL) < 0 || + GetKeyState(VK_LCONTROL) < 0 || + GetKeyState(VK_RCONTROL) < 0) { + mask |= KeyModifierControl; + } + if (GetKeyState(VK_MENU) < 0 || + GetKeyState(VK_LMENU) < 0 || + GetKeyState(VK_RMENU) < 0) { + mask |= KeyModifierAlt; + } + if (GetKeyState(VK_LWIN) < 0 || + GetKeyState(VK_RWIN) < 0) { + mask |= KeyModifierSuper; + } + if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) { + mask |= KeyModifierCapsLock; + } + if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) { + mask |= KeyModifierNumLock; + } + if ((GetKeyState(VK_SCROLL) & 0x01) != 0) { + mask |= KeyModifierScrollLock; + } + return mask; +} + +KeyModifierMask +CMSWindowsKeyMapper::getShadowModifiers(bool needAltGr) const +{ + KeyModifierMask mask = 0; + if (isPressed(VK_SHIFT)) { + mask |= KeyModifierShift; + } + if (needAltGr) { + mask |= KeyModifierModeSwitch; + } + else { + if (isPressed(VK_CONTROL)) { + mask |= KeyModifierControl; + } + if (isPressed(VK_MENU)) { + mask |= KeyModifierAlt; + } + } + if (isPressed(VK_LWIN) || isPressed(VK_RWIN)) { + mask |= KeyModifierSuper; + } + if (isToggled(VK_CAPITAL)) { + mask |= KeyModifierCapsLock; + } + if (isToggled(VK_NUMLOCK)) { + mask |= KeyModifierNumLock; + } + if (isToggled(VK_SCROLL)) { + mask |= KeyModifierScrollLock; + } + return mask; +} + const char* CMSWindowsKeyMapper::getKeyName(KeyButton key) const { - return s_vkToName[key & 0xffu]; + char keyName[100]; + CMSWindowsKeyMapper* self = const_cast(this); + if (GetKeyNameText((key & 0x01ffu) << 16, keyName, sizeof(keyName)) != 0) { + self->m_keyName = keyName; + } + else if (m_scanCodeToVirtKey[key] != 0) { + self->m_keyName = s_vkToName[m_scanCodeToVirtKey[key]]; + } + else { + self->m_keyName = CStringUtil::print("scan code 0x%03x", key & 0x01ffu); + } + return m_keyName.c_str(); } UINT @@ -1310,7 +1329,7 @@ CMSWindowsKeyMapper::mapCharacter(IKeyState::Keystrokes& keys, SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); // get virtual key - KeyButton virtualKey = LOBYTE(virtualKeyAndModifierState); + UINT virtualKey = LOBYTE(virtualKeyAndModifierState); if (virtualKey == 0xffu) { LOG((CLOG_DEBUG2 "cannot map character %d", static_cast(c))); return 0; @@ -1370,16 +1389,15 @@ CMSWindowsKeyMapper::mapCharacter(IKeyState::Keystrokes& keys, // now generate the keystrokes. ignore the resulting modifier // mask since it can't have changed (because we don't call this // method for modifier keys). - LOG((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%08x", (unsigned char)c, virtualKey, desiredMask)); - mapToKeystrokes(keys, keyState, virtualKey, + KeyButton scanCode = m_virtKeyToScanCode[virtualKey]; + LOG((CLOG_DEBUG2 "character %d to virtual key %d scan code 0x%04x mask 0x%08x", (unsigned char)c, virtualKey, scanCode, desiredMask)); + return mapToKeystrokes(keys, keyState, scanCode, desiredMask, requiredMask, isAutoRepeat); - - return virtualKey; } KeyButton CMSWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, - const IKeyState& keyState, KeyButton virtualKey, + const IKeyState& keyState, KeyButton button, KeyModifierMask desiredMask, KeyModifierMask requiredMask, bool isAutoRepeat) const { @@ -1393,7 +1411,7 @@ CMSWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, // add the key event IKeyState::Keystroke keystroke; - keystroke.m_key = virtualKey; + keystroke.m_key = button; keystroke.m_press = true; keystroke.m_repeat = isAutoRepeat; keys.push_back(keystroke); @@ -1404,7 +1422,7 @@ CMSWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, undo.pop_back(); } - return virtualKey; + return button; } bool @@ -1425,7 +1443,6 @@ CMSWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, requiredMask ^= mask; } } - return true; } diff --git a/lib/platform/CMSWindowsKeyMapper.h b/lib/platform/CMSWindowsKeyMapper.h index 7292e675..883c5c60 100644 --- a/lib/platform/CMSWindowsKeyMapper.h +++ b/lib/platform/CMSWindowsKeyMapper.h @@ -16,6 +16,7 @@ #define CMSWINDOWSKEYMAPPER_H #include "IKeyState.h" +#include "CString.h" #define WIN32_LEAN_AND_MEAN #include @@ -42,7 +43,7 @@ public: /*! Updates the shadow keyboard state. */ - void updateKey(KeyButton key, bool pressed); + void updateKey(LPARAM eventLParam); //! Set the active keyboard layout /*! @@ -77,23 +78,41 @@ public: KeyID mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info, KeyModifierMask* maskOut, bool* altgr) const; + //! Check if virtual key is a modifier + /*! + Returns true iff \p virtKey refers to a modifier key. + */ + bool isModifier(UINT virtKey) const; + //! Test shadow key state /*! Returns true iff the shadow state indicates the key is pressed. */ - bool isPressed(KeyButton key) const; + bool isPressed(UINT virtKey) const; - //! Map key to a scan code + //! Map button to a virtual key /*! - Returns the scan code for \c key and possibly adjusts \c key. + Returns the virtual key for \c button. */ - UINT keyToScanCode(KeyButton* key) const; + UINT buttonToVirtualKey(KeyButton button) const; + + //! Map virtual key to a button + /*! + Returns the button for virtual key \c virtKey. + */ + KeyButton virtualKeyToButton(UINT virtKey) const; //! Check for extended key /*! Returns true iff \c key is an extended key */ - bool isExtendedKey(KeyButton key) const; + bool isExtendedKey(KeyButton button) const; + + //! Get current modifier key state + /*! + Returns the current modifier key state. + */ + KeyModifierMask getActiveModifiers() const; //! Get name of key /*! @@ -116,7 +135,7 @@ private: // map \c virtualKey to the keystrokes to generate it, along with // keystrokes to update and restore the modifier state. KeyButton mapToKeystrokes(IKeyState::Keystrokes& keys, - const IKeyState& keyState, KeyButton virtualKey, + const IKeyState& keyState, KeyButton button, KeyModifierMask desiredMask, KeyModifierMask requiredMask, bool isAutoRepeat) const; @@ -128,6 +147,18 @@ private: KeyModifierMask desiredMask, KeyModifierMask requiredMask) const; + //! Test shadow key toggle state + /*! + Returns true iff the shadow state indicates the key is toggled on. + */ + bool isToggled(UINT virtKey) const; + + //! Get shadow modifier key state + /*! + Returns the shadow modifier key state. + */ + KeyModifierMask getShadowModifiers(bool needAltGr) const; + // pass character to ToAsciiEx(), returning what it returns int toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const; @@ -142,16 +173,24 @@ private: KeyButton m_keys[s_maxKeys]; }; - BYTE m_keys[256]; + // map of key state for each scan code. this would be 8 bits + // except windows reuses some scan codes for "extended" keys + // we actually need 9 bits. an example is the left and right + // alt keys; they share the same scan code but the right key + // is "extended". + BYTE m_keys[512]; + UINT m_scanCodeToVirtKey[512]; + KeyButton m_virtKeyToScanCode[256]; mutable TCHAR m_deadKey; HKL m_keyLayout; + CString m_keyName; static const CModifierKeys s_modifiers[]; - static const char* s_vkToName[]; - static const KeyID s_virtualKey[][2]; - static const KeyButton s_mapE000[]; - static const KeyButton s_mapEE00[]; - static const KeyButton s_mapEF00[]; + static const char* s_vkToName[]; + static const KeyID s_virtualKey[][2]; + static const UINT s_mapE000[]; + static const UINT s_mapEE00[]; + static const UINT s_mapEF00[]; }; #endif diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 14409450..34ba1865 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -84,28 +84,6 @@ #define XBUTTON2 0x0002 #endif -// multimedia keys -#if !defined(VK_BROWSER_BACK) -#define VK_BROWSER_BACK 0xA6 -#define VK_BROWSER_FORWARD 0xA7 -#define VK_BROWSER_REFRESH 0xA8 -#define VK_BROWSER_STOP 0xA9 -#define VK_BROWSER_SEARCH 0xAA -#define VK_BROWSER_FAVORITES 0xAB -#define VK_BROWSER_HOME 0xAC -#define VK_VOLUME_MUTE 0xAD -#define VK_VOLUME_DOWN 0xAE -#define VK_VOLUME_UP 0xAF -#define VK_MEDIA_NEXT_TRACK 0xB0 -#define VK_MEDIA_PREV_TRACK 0xB1 -#define VK_MEDIA_STOP 0xB2 -#define VK_MEDIA_PLAY_PAUSE 0xB3 -#define VK_LAUNCH_MAIL 0xB4 -#define VK_LAUNCH_MEDIA_SELECT 0xB5 -#define VK_LAUNCH_APP1 0xB6 -#define VK_LAUNCH_APP2 0xB7 -#endif - // // CMSWindowsScreen // @@ -231,8 +209,7 @@ void CMSWindowsScreen::setKeyState(IKeyState* keyState) { m_keyState = keyState; - m_keyMapper.update(m_keyState); - memset(m_buttons, 0, sizeof(m_buttons)); + updateKeys(); } void @@ -336,6 +313,7 @@ CMSWindowsScreen::leave() // tell the key mapper about the keyboard layout m_keyMapper.setKeyLayout(m_keyLayout); + sendDeskMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); // tell desk that we're leaving and tell it the keyboard layout sendDeskMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0); @@ -464,6 +442,7 @@ CMSWindowsScreen::updateKeys() { sendDeskMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); memset(m_buttons, 0, sizeof(m_buttons)); + // FIXME -- get the button state } void @@ -568,6 +547,12 @@ CMSWindowsScreen::isAnyMouseButtonDown() const return false; } +KeyModifierMask +CMSWindowsScreen::getActiveModifiers() const +{ + return m_keyMapper.getActiveModifiers(); +} + void CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const { @@ -582,19 +567,19 @@ CMSWindowsScreen::getKeyName(KeyButton virtualKey) const } void -CMSWindowsScreen::fakeKeyEvent(KeyButton virtualKey, bool press) const +CMSWindowsScreen::fakeKeyEvent(KeyButton id, bool press) const { DWORD flags = 0; - if (m_keyMapper.isExtendedKey(virtualKey)) { + if (m_keyMapper.isExtendedKey(id)) { flags |= KEYEVENTF_EXTENDEDKEY; } if (!press) { flags |= KEYEVENTF_KEYUP; } - UINT code = m_keyMapper.keyToScanCode(&virtualKey); + UINT vk = m_keyMapper.buttonToVirtualKey(id); sendDeskMessage(SYNERGY_MSG_FAKE_KEY, flags, - MAKEWORD(static_cast(code), - static_cast(virtualKey & 0xffu))); + MAKEWORD(static_cast(id & 0xffu), + static_cast(vk & 0xffu))); } bool @@ -870,9 +855,8 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, // if the user presses and releases a windows key without pressing // any other key while it's down then windows will eat the key // release. if we don't detect that and 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). + // then the cclient 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, @@ -881,42 +865,12 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, // 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_keyMapper.isPressed(VK_LWIN) && - (GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 && - !(message == SYNERGY_MSG_KEY && 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; - KeyButton button = static_cast( - (lParam & 0x00ff0000u) >> 16); - KeyID key = m_keyMapper.mapKeyFromEvent(wParam, - lParam, &mask, NULL); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, button, 1)); - m_keyMapper.updateKey(static_cast(wParam), false); + if (m_is95Family && message != SYNERGY_MSG_KEY) { + if (wParam != VK_LWIN) { + fixKey(VK_LWIN); } - if (m_keyMapper.isPressed(VK_RWIN) && - (GetAsyncKeyState(VK_RWIN) & 0x8000) == 0 && - !(message == SYNERGY_MSG_KEY && 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; - KeyButton button = static_cast( - (lParam & 0x00ff0000u) >> 16); - KeyID key = m_keyMapper.mapKeyFromEvent(wParam, - lParam, &mask, NULL); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, button, 1)); - m_keyMapper.updateKey(static_cast(wParam), false); + if (wParam != VK_RWIN) { + fixKey(VK_RWIN); } } @@ -1047,6 +1001,16 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) WPARAM charAndVirtKey = wParam; wParam &= 0xffu; + // update key state. ignore key repeats. + if ((lParam & 0xc0000000u) == 0x00000000) { + KeyButton scancode = (KeyButton)((lParam & 0x01ff0000) >> 16); + m_keyState->setKeyDown(scancode, true); + } + else if ((lParam & 0xc0000000u) == 0xc0000000) { + KeyButton scancode = (KeyButton)((lParam & 0x01ff0000) >> 16); + m_keyState->setKeyDown(scancode, false); + } + // ignore message if posted prior to last mark change if (!ignore()) { // check for ctrl+alt+del emulation @@ -1054,9 +1018,11 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) (m_keyMapper.isPressed(VK_CONTROL) && m_keyMapper.isPressed(VK_MENU))) { LOG((CLOG_DEBUG "emulate ctrl+alt+del")); - wParam = VK_DELETE; - lParam &= 0xffff0000; - lParam |= 0x00000001; + wParam = VK_DELETE; + lParam &= 0xfffe0000; + lParam |= m_keyMapper.virtualKeyToButton(wParam) << 16; + lParam |= 0x00000001; + charAndVirtKey = wParam; } // process key normally @@ -1065,7 +1031,7 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) const KeyID key = m_keyMapper.mapKeyFromEvent( charAndVirtKey, lParam, &mask, &altgr); KeyButton button = static_cast( - (lParam & 0x00ff0000u) >> 16); + (lParam & 0x01ff0000u) >> 16); if (key != kKeyNone && key != kKeyMultiKey) { if ((lParam & 0x80000000) == 0) { // key press @@ -1085,43 +1051,34 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) if (altgr) { KeyID key; KeyButton button; - UINT scanCode; KeyModifierMask mask2 = (mask & ~(KeyModifierControl | KeyModifierAlt | KeyModifierModeSwitch)); if (ctrlL) { - key = kKeyControl_L; - button = VK_LCONTROL; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + key = kKeyControl_L; + button = m_keyMapper.virtualKeyToButton(VK_LCONTROL); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask2, button, 1)); } if (ctrlR) { - key = kKeyControl_R; - button = VK_RCONTROL; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + key = kKeyControl_R; + button = m_keyMapper.virtualKeyToButton(VK_RCONTROL); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask2, button, 1)); } if (altL) { - key = kKeyAlt_L; - button = VK_LMENU; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + key = kKeyAlt_L; + button = m_keyMapper.virtualKeyToButton(VK_LMENU); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask2, button, 1)); } if (altR) { - key = kKeyAlt_R; - button = VK_RMENU; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + key = kKeyAlt_R; + button = m_keyMapper.virtualKeyToButton(VK_RMENU); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask2, button, 1)); @@ -1149,16 +1106,13 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) if (altgr) { KeyID key; KeyButton button; - UINT scanCode; KeyModifierMask mask2 = (mask & ~(KeyModifierControl | KeyModifierAlt | KeyModifierModeSwitch)); if (ctrlL) { key = kKeyControl_L; - button = VK_LCONTROL; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + button = m_keyMapper.virtualKeyToButton(VK_LCONTROL); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask2, button, 1)); @@ -1166,9 +1120,7 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) } if (ctrlR) { key = kKeyControl_R; - button = VK_RCONTROL; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + button = m_keyMapper.virtualKeyToButton(VK_RCONTROL); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask2, button, 1)); @@ -1176,9 +1128,7 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) } if (altL) { key = kKeyAlt_L; - button = VK_LMENU; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + button = m_keyMapper.virtualKeyToButton(VK_LMENU); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask2, button, 1)); @@ -1186,9 +1136,7 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) } if (altR) { key = kKeyAlt_R; - button = VK_RMENU; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + button = m_keyMapper.virtualKeyToButton(VK_RMENU); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask2, button, 1)); @@ -1203,12 +1151,13 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) // 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(wParam) && - m_keyMapper.isPressed(static_cast(wParam))) { + if (m_is95Family && + !m_keyMapper.isModifier(wParam) && + m_keyMapper.isPressed(wParam)) { LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask, button, 1)); - m_keyMapper.updateKey(static_cast(wParam), true); + m_keyMapper.updateKey(lParam & 0x3fffffffu); } // do key up @@ -1223,8 +1172,7 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) } // keep our shadow key state up to date - m_keyMapper.updateKey(static_cast(wParam), - ((lParam & 0x80000000) == 0)); + m_keyMapper.updateKey(lParam); return true; } @@ -1520,6 +1468,25 @@ CMSWindowsScreen::enableSpecialKeys(bool enable) const } } +void +CMSWindowsScreen::fixKey(UINT virtualKey) +{ + if (m_keyMapper.isPressed(virtualKey) && + (GetAsyncKeyState(virtualKey) & 0x8000) == 0) { + // compute appropriate parameters for fake event + KeyButton button = m_keyMapper.virtualKeyToButton(virtualKey); + LPARAM lParam = 0xc0000000 | ((LPARAM)button << 16); + + // process as if it were a key up + KeyModifierMask mask; + KeyID key = m_keyMapper.mapKeyFromEvent(virtualKey, + lParam, &mask, NULL); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, button, 1)); + m_keyMapper.updateKey(lParam); + } +} + DWORD CMSWindowsScreen::mapButtonToEvent(ButtonID button, bool press, DWORD* inData) const @@ -1616,31 +1583,6 @@ CMSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const } } -bool -CMSWindowsScreen::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; - } -} - void CMSWindowsScreen::ctrlAltDelThread(void*) { diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index cd7727bf..f9e0b9b7 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -86,6 +86,7 @@ public: virtual void warpCursor(SInt32 x, SInt32 y); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; + virtual KeyModifierMask getActiveModifiers() const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; virtual const char* getKeyName(KeyButton) const; @@ -168,6 +169,10 @@ private: // enable/disable special key combinations so we can catch/pass them void enableSpecialKeys(bool) const; + // send fake key up if shadow state says virtualKey is down but + // system says it isn't. + void fixKey(UINT virtualKey); + // map a button ID and action to a mouse event DWORD mapButtonToEvent(ButtonID button, bool press, DWORD* inData) const; diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index dde44703..ada14d8f 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -235,14 +235,14 @@ CScreen::keyRepeat(KeyID id, // dead key. for example, a dead accent followed by 'a' will // generate an 'a with accent' followed by a repeating 'a'. the // keycodes for the two keysyms might be different. - key &= 0xffu; + key &= 0x1ffu; if (key != index->second) { // replace key up with previous key id but leave key down // alone so it uses the new keycode and store that keycode // in the server key map. for (Keystrokes::iterator index2 = keys.begin(); index2 != keys.end(); ++index2) { - if ((index2->m_key & 0xffu) == key) { + if ((index2->m_key & 0x1ffu) == key) { index2->m_key = index->second; break; } @@ -477,11 +477,10 @@ CScreen::updateKeys() void CScreen::releaseKeys() { -LOG((CLOG_INFO "releaseKeys")); // FIXME // release keys that we've synthesized a press for and only those // keys. we don't want to synthesize a release on a key the user // is still physically pressing. - for (KeyButton i = 1; i < 256; ++i) { + for (KeyButton i = 1; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { if ((m_fakeKeys[i] & kDown) != 0) { fakeKeyEvent(i, false, false); m_keys[i] &= ~kDown; @@ -495,10 +494,10 @@ CScreen::setKeyDown(KeyButton key, bool down) { if (!isHalfDuplex(getMaskForKey(key))) { if (down) { - m_keys[key & 0xffu] |= kDown; + m_keys[key & 0x1ffu] |= kDown; } else { - m_keys[key & 0xffu] &= ~kDown; + m_keys[key & 0x1ffu] &= ~kDown; } } } @@ -515,7 +514,7 @@ CScreen::setToggled(KeyModifierMask mask) } for (KeyButtons::const_iterator j = i->second.begin(); j != i->second.end(); ++j) { - m_keys[(*j) & 0xffu] |= kToggled; + m_keys[(*j) & 0x1ffu] |= kToggled; } } @@ -536,8 +535,9 @@ CScreen::addModifier(KeyModifierMask mask, KeyButtons& keys) // index mask by keycodes for (KeyButtons::iterator j = keys.begin(); j != keys.end(); ++j) { // key must be valid - assert(((*j) & 0xffu) != 0); - m_keyToMask[static_cast((*j) & 0xffu)] = mask; + if (((*j) & 0x1ffu) != 0) { + m_keyToMask[static_cast((*j) & 0x1ffu)] = mask; + } } // index keys by mask @@ -563,7 +563,7 @@ CScreen::setToggleState(KeyModifierMask mask) KeyButton CScreen::isAnyKeyDown() const { - for (UInt32 i = 1; i < 256; ++i) { + for (UInt32 i = 1; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { if ((m_keys[i] & kDown) != 0) { return static_cast(i); } @@ -574,7 +574,7 @@ CScreen::isAnyKeyDown() const bool CScreen::isKeyDown(KeyButton key) const { - key &= 0xffu; + key &= 0x1ffu; return (key != 0 && ((m_keys[key] & kDown) != 0)); } @@ -867,7 +867,7 @@ void CScreen::updateKeyState(KeyButton button, KeyButton key, bool press) { // ignore bogus keys - key &= 0xffu; + key &= 0x1ffu; if (button == 0 || key == 0) { return; } @@ -931,13 +931,13 @@ CScreen::toggleKey(KeyModifierMask mask) // toggle shadow state m_mask ^= mask; - key &= 0xffu; + key &= 0x1ffu; m_keys[key] ^= kToggled; } bool CScreen::isKeyToggled(KeyButton key) const { - key &= 0xffu; + key &= 0x1ffu; return (key != 0 && ((m_keys[key] & kToggled) != 0)); } diff --git a/lib/synergy/CScreen.h b/lib/synergy/CScreen.h index 524bb0bd..ce2e43d3 100644 --- a/lib/synergy/CScreen.h +++ b/lib/synergy/CScreen.h @@ -298,10 +298,10 @@ private: ServerKeyMap m_serverKeyMap; // system key states as set by us or the user - KeyState m_keys[256]; + KeyState m_keys[512]; // system key states as set by us - KeyState m_fakeKeys[256]; + KeyState m_fakeKeys[512]; // modifier info MaskToKeys m_maskToKeys; diff --git a/lib/synergy/IKeyState.h b/lib/synergy/IKeyState.h index f8dec884..d54d29f1 100644 --- a/lib/synergy/IKeyState.h +++ b/lib/synergy/IKeyState.h @@ -68,7 +68,7 @@ public: example, if buttons 5 and 23 were mapped to KeyModifierShift (perhaps as left and right shift keys) then the mask would be KeyModifierShift and \c keys would contain 5 and 23. A modifier with no keys is - ignored. All keys must be valid (not zero). \c keys may be modified + ignored. Keys that are zero are ignored. \c keys may be modified by the call. */ virtual void addModifier(KeyModifierMask, KeyButtons& keys) = 0; diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index 72deabcd..a0f80876 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -107,7 +107,7 @@ SOURCE=.\IClipboard.cpp # End Source File # Begin Source File -SOURCE=.\IPlatformScreen.cpp +SOURCE=.\IPrimaryScreen.cpp # End Source File # Begin Source File