Keyboard fixes on win32.

This commit is contained in:
crs 2003-07-05 17:04:26 +00:00
parent b9193ae1bb
commit 28427a0e9b
2 changed files with 155 additions and 166 deletions

View File

@ -70,6 +70,19 @@
// CMSWindowsSecondaryScreen // CMSWindowsSecondaryScreen
// //
// a list of modifier key info
const CMSWindowsSecondaryScreen::CModifierInfo
CMSWindowsSecondaryScreen::s_modifier[] = {
{ KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false },
{ KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false },
{ KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false },
// note -- no keys for KeyModifierMeta
{ KeyModifierSuper, VK_LWIN | 0x100, VK_RWIN | 0x100, false },
{ KeyModifierCapsLock, VK_CAPITAL, 0, true },
{ KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true },
{ KeyModifierScrollLock,VK_SCROLL, 0, true }
};
CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen( CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen(
IScreenReceiver* receiver) : IScreenReceiver* receiver) :
m_is95Family(CArchMiscWindows::isWindows95Family()), m_is95Family(CArchMiscWindows::isWindows95Family()),
@ -90,9 +103,6 @@ void
CMSWindowsSecondaryScreen::keyDown(KeyID key, CMSWindowsSecondaryScreen::keyDown(KeyID key,
KeyModifierMask mask, KeyButton button) KeyModifierMask mask, KeyButton button)
{ {
Keystrokes keys;
UINT virtualKey;
CLock lock(&m_mutex); CLock lock(&m_mutex);
m_screen->syncDesktop(); m_screen->syncDesktop();
@ -106,15 +116,21 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key,
// get the sequence of keys to simulate key press and the final // get the sequence of keys to simulate key press and the final
// modifier state. // modifier state.
Keystrokes keys;
UINT virtualKey;
m_mask = mapKey(keys, virtualKey, key, mask, kPress); m_mask = mapKey(keys, virtualKey, key, mask, kPress);
if (keys.empty()) { if (keys.empty()) {
// do nothing if there are no associated keys (i.e. lookup failed)
return; return;
} }
// generate key events // generate key events
doKeystrokes(keys, 1); doKeystrokes(keys, 1);
// do not record button down if button is 0 (invalid)
if (button != 0) {
// note that key is now down // note that key is now down
m_serverKeyMap[button] = virtualKey;
m_keys[virtualKey] |= 0x80; m_keys[virtualKey] |= 0x80;
m_fakeKeys[virtualKey] |= 0x80; m_fakeKeys[virtualKey] |= 0x80;
switch (virtualKey) { switch (virtualKey) {
@ -136,18 +152,13 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key,
m_fakeKeys[VK_MENU] |= 0x80; m_fakeKeys[VK_MENU] |= 0x80;
break; break;
} }
}
// note which server key generated this key
m_serverKeyMap[button] = virtualKey;
} }
void void
CMSWindowsSecondaryScreen::keyRepeat(KeyID key, CMSWindowsSecondaryScreen::keyRepeat(KeyID key,
KeyModifierMask mask, SInt32 count, KeyButton button) KeyModifierMask mask, SInt32 count, KeyButton button)
{ {
Keystrokes keys;
UINT virtualKey;
CLock lock(&m_mutex); CLock lock(&m_mutex);
m_screen->syncDesktop(); m_screen->syncDesktop();
@ -159,20 +170,26 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key,
// get the sequence of keys to simulate key repeat and the final // get the sequence of keys to simulate key repeat and the final
// modifier state. // modifier state.
Keystrokes keys;
UINT virtualKey;
m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); m_mask = mapKey(keys, virtualKey, key, mask, kRepeat);
if (keys.empty()) { if (keys.empty()) {
return; return;
} }
// if we've seen this button (and we should have) then make sure // if the keycode for the auto-repeat is not the same as for the
// we release the same key we pressed when we saw it. // initial press then mark the initial key as released and the new
if (index != m_serverKeyMap.end() && virtualKey != index->second) { // key as pressed. this can happen when we auto-repeat after a
// 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.
if (virtualKey != index->second) {
// replace key up with previous keycode but leave key down // replace key up with previous keycode but leave key down
// alone so it uses the new keycode and store that keycode // alone so it uses the new keycode and store that keycode
// in the server key map. // in the server key map.
for (Keystrokes::iterator index2 = keys.begin(); for (Keystrokes::iterator index2 = keys.begin();
index2 != keys.end(); ++index2) { index2 != keys.end(); ++index2) {
if (index2->m_virtualKey == index->second) { if (index2->m_virtualKey == virtualKey) {
index2->m_virtualKey = index->second; index2->m_virtualKey = index->second;
break; break;
} }
@ -195,12 +212,8 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key,
} }
void void
CMSWindowsSecondaryScreen::keyUp(KeyID key, CMSWindowsSecondaryScreen::keyUp(KeyID, KeyModifierMask, KeyButton button)
KeyModifierMask mask, KeyButton button)
{ {
Keystrokes keys;
UINT virtualKey;
CLock lock(&m_mutex); CLock lock(&m_mutex);
m_screen->syncDesktop(); m_screen->syncDesktop();
@ -209,42 +222,18 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key,
if (index == m_serverKeyMap.end()) { if (index == m_serverKeyMap.end()) {
return; return;
} }
UINT virtualKey = index->second;
// get the sequence of keys to simulate key release and the final // get the sequence of keys to simulate key release and the final
// modifier state. // modifier state.
m_mask = mapKey(keys, virtualKey, key, mask, kRelease); Keystrokes keys;
m_mask = mapKeyRelease(keys, virtualKey);
// if there are no keys to generate then we should at least generate
// a key release for the key we pressed.
if (keys.empty()) {
Keystroke keystroke;
virtualKey = index->second;
keystroke.m_virtualKey = virtualKey;
keystroke.m_press = false;
keystroke.m_repeat = false;
keys.push_back(keystroke);
}
// if we've seen this button (and we should have) then make sure
// we release the same key we pressed when we saw it.
if (index != m_serverKeyMap.end() && virtualKey != index->second) {
// replace key up with previous virtual key
for (Keystrokes::iterator index2 = keys.begin();
index2 != keys.end(); ++index2) {
if (index2->m_virtualKey == virtualKey) {
index2->m_virtualKey = index->second;
break;
}
}
// use old virtual key
virtualKey = index->second;
}
// generate key events // generate key events
doKeystrokes(keys, 1); doKeystrokes(keys, 1);
// note that key is now up // note that key is now up
m_serverKeyMap.erase(index);
m_keys[virtualKey] &= ~0x80; m_keys[virtualKey] &= ~0x80;
m_fakeKeys[virtualKey] &= ~0x80; m_fakeKeys[virtualKey] &= ~0x80;
switch (virtualKey) { switch (virtualKey) {
@ -290,11 +279,6 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key,
} }
break; break;
} }
// remove server key from map
if (index != m_serverKeyMap.end()) {
m_serverKeyMap.erase(index);
}
} }
void void
@ -1003,6 +987,47 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey,
return m_mask; return m_mask;
} }
KeyModifierMask
CMSWindowsSecondaryScreen::mapKeyRelease(Keystrokes& keys,
UINT virtualKey) const
{
// add key release
Keystroke keystroke;
keystroke.m_virtualKey = virtualKey;
keystroke.m_press = false;
keystroke.m_repeat = false;
keys.push_back(keystroke);
// if this is a modifier keycode then update the current modifier mask
const CModifierInfo* modifier = getModifierInfo(virtualKey);
if (modifier != NULL) {
if (modifier->m_isToggle) {
// toggle keys modify the state on release
return (m_mask ^ modifier->m_mask);
}
else {
// can't reset bit until all keys that set it are released.
// scan those keys to see if any (except virtualKey) are
// pressed.
bool down = false;
if (virtualKey != (modifier->m_virtualKey & 0xff) &&
(m_keys[modifier->m_virtualKey & 0xff] & 0x80) != 0) {
down = true;
}
if (modifier->m_virtualKey2 != 0 &&
virtualKey != (modifier->m_virtualKey2 & 0xff) &&
(m_keys[modifier->m_virtualKey2 & 0xff] & 0x80) != 0) {
down = true;
}
if (!down) {
return (m_mask & ~modifier->m_mask);
}
}
}
return m_mask;
}
UINT UINT
CMSWindowsSecondaryScreen::mapCharacter(Keystrokes& keys, CMSWindowsSecondaryScreen::mapCharacter(Keystrokes& keys,
char c, HKL hkl, char c, HKL hkl,
@ -1102,73 +1127,7 @@ CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys,
KeyModifierMask currentMask, KeyModifierMask currentMask,
KeyModifierMask desiredMask, EKeyAction action) const KeyModifierMask desiredMask, EKeyAction action) const
{ {
// a list of modifier key info const CModifierInfo* modifier = getModifierInfo(virtualKey);
class CModifierInfo {
public:
KeyModifierMask m_mask;
UINT m_virtualKey;
UINT m_virtualKey2;
bool m_isToggle;
};
static const CModifierInfo s_modifier[] = {
{ KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false },
{ KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false },
{ KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false },
// note -- no keys for KeyModifierMeta
{ KeyModifierSuper, VK_LWIN | 0x100, VK_RWIN | 0x100, false },
{ KeyModifierCapsLock, VK_CAPITAL, 0, true },
{ KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true },
{ KeyModifierScrollLock,VK_SCROLL, 0, true }
};
static const unsigned int s_numModifiers =
sizeof(s_modifier) / sizeof(s_modifier[0]);
// strip out extended key flag
UINT virtualKey2 = (virtualKey & ~0x100);
// note if the key is a modifier
unsigned int modifierIndex;
switch (virtualKey2) {
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT:
modifierIndex = 0;
break;
case VK_CONTROL:
case VK_LCONTROL:
case VK_RCONTROL:
modifierIndex = 1;
break;
case VK_MENU:
case VK_LMENU:
case VK_RMENU:
modifierIndex = 2;
break;
case VK_LWIN:
case VK_RWIN:
modifierIndex = 3;
break;
case VK_CAPITAL:
modifierIndex = 4;
break;
case VK_NUMLOCK:
modifierIndex = 5;
break;
case VK_SCROLL:
modifierIndex = 6;
break;
default:
modifierIndex = s_numModifiers;
break;
}
const bool isModifier = (modifierIndex != s_numModifiers);
// add the key events required to get to the desired modifier state. // add the key events required to get to the desired modifier state.
// also save the key events required to restore the current state. // also save the key events required to restore the current state.
@ -1176,7 +1135,9 @@ CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys,
// should not modify modifiers. // should not modify modifiers.
Keystrokes undo; Keystrokes undo;
Keystroke keystroke; Keystroke keystroke;
if (desiredMask != currentMask && !isModifier) { if (desiredMask != currentMask && modifier == NULL) {
const unsigned int s_numModifiers = sizeof(s_modifier) /
sizeof(s_modifier[0]);
for (unsigned int i = 0; i < s_numModifiers; ++i) { for (unsigned int i = 0; i < s_numModifiers; ++i) {
KeyModifierMask bit = s_modifier[i].m_mask; KeyModifierMask bit = s_modifier[i].m_mask;
if ((desiredMask & bit) != (currentMask & bit)) { if ((desiredMask & bit) != (currentMask & bit)) {
@ -1274,35 +1235,12 @@ CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys,
} }
// if the key is a modifier key then compute the modifier mask after // if the key is a modifier key then compute the modifier mask after
// this key is pressed. // this key is pressed. toggle keys modify the state on release.
// other keys set the modifier bit on press.
KeyModifierMask mask = currentMask; KeyModifierMask mask = currentMask;
if (isModifier && action != kRepeat) { if (action == kPress) {
// toggle keys modify the state on release. other keys set if (modifier != NULL && !modifier->m_isToggle) {
// the bit on press and clear the bit on release. mask |= modifier->m_mask;
const CModifierInfo& modifier = s_modifier[modifierIndex];
if (modifier.m_isToggle) {
if (action == kRelease) {
mask ^= modifier.m_mask;
}
}
else if (action == kPress) {
mask |= modifier.m_mask;
}
else {
// can't reset bit until all keys that set it are released.
// scan those keys to see if any are pressed.
bool down = false;
if (virtualKey2 != (modifier.m_virtualKey & 0xff) &&
(m_keys[modifier.m_virtualKey & 0xff] & 0x80) != 0) {
down = true;
}
if (modifier.m_virtualKey2 != 0 &&
virtualKey2 != (modifier.m_virtualKey2 & 0xff) &&
(m_keys[modifier.m_virtualKey2 & 0xff] & 0x80) != 0) {
down = true;
}
if (!down)
mask &= ~modifier.m_mask;
} }
} }
@ -1344,6 +1282,45 @@ CMSWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count)
} }
} }
const CMSWindowsSecondaryScreen::CModifierInfo*
CMSWindowsSecondaryScreen::getModifierInfo(UINT virtualKey) const
{
// note if the key is a modifier. strip out extended key flag from
// virtual key before lookup.
switch (virtualKey & ~0x100) {
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT:
return s_modifier + 0;
case VK_CONTROL:
case VK_LCONTROL:
case VK_RCONTROL:
return s_modifier + 1;
case VK_MENU:
case VK_LMENU:
case VK_RMENU:
return s_modifier + 2;
case VK_LWIN:
case VK_RWIN:
return s_modifier + 3;
case VK_CAPITAL:
return s_modifier + 4;
case VK_NUMLOCK:
return s_modifier + 5;
case VK_SCROLL:
return s_modifier + 6;
default:
return NULL;
}
}
void void
CMSWindowsSecondaryScreen::releaseKeys() CMSWindowsSecondaryScreen::releaseKeys()
{ {

View File

@ -85,6 +85,13 @@ private:
bool m_press; bool m_press;
bool m_repeat; bool m_repeat;
}; };
class CModifierInfo {
public:
KeyModifierMask m_mask;
UINT m_virtualKey;
UINT m_virtualKey2;
bool m_isToggle;
};
typedef std::vector<Keystroke> Keystrokes; typedef std::vector<Keystroke> Keystrokes;
typedef std::map<KeyButton, UINT> ServerKeyMap; typedef std::map<KeyButton, UINT> ServerKeyMap;
@ -103,6 +110,7 @@ private:
bool press, DWORD* data) const; bool press, DWORD* data) const;
KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID,
KeyModifierMask, EKeyAction) const; KeyModifierMask, EKeyAction) const;
KeyModifierMask mapKeyRelease(Keystrokes& keys, UINT virtualKey) const;
UINT mapCharacter(Keystrokes& keys, UINT mapCharacter(Keystrokes& keys,
char c, HKL hkl, char c, HKL hkl,
KeyModifierMask currentMask, KeyModifierMask currentMask,
@ -114,6 +122,7 @@ private:
KeyModifierMask desiredMask, KeyModifierMask desiredMask,
EKeyAction action) const; EKeyAction action) const;
void doKeystrokes(const Keystrokes&, SInt32 count); void doKeystrokes(const Keystrokes&, SInt32 count);
const CModifierInfo* getModifierInfo(UINT virtualKey) const;
void toggleKey(UINT virtualKey, KeyModifierMask mask); void toggleKey(UINT virtualKey, KeyModifierMask mask);
UINT virtualKeyToScanCode(UINT& virtualKey) const; UINT virtualKeyToScanCode(UINT& virtualKey) const;
@ -149,6 +158,9 @@ private:
// map server key buttons to local virtual keys // map server key buttons to local virtual keys
ServerKeyMap m_serverKeyMap; ServerKeyMap m_serverKeyMap;
// modifier table
static const CModifierInfo s_modifier[];
}; };
#endif #endif