Minor X11 keyboard code cleanup. Also now handling KeyPress with

keycode == 0 generated by XFilterEvent() by using the keycode from
the previous KeyPress.
This commit is contained in:
crs 2003-07-05 14:49:08 +00:00
parent 47b480c0bc
commit 8f9cc6e476
3 changed files with 70 additions and 34 deletions

View File

@ -42,7 +42,8 @@ CXWindowsPrimaryScreen::CXWindowsPrimaryScreen(
m_receiver(primaryReceiver), m_receiver(primaryReceiver),
m_window(None), m_window(None),
m_im(NULL), m_im(NULL),
m_ic(NULL) m_ic(NULL),
m_lastKeycode(0)
{ {
m_screen = new CXWindowsScreen(receiver, this); m_screen = new CXWindowsScreen(receiver, this);
} }
@ -223,9 +224,29 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event)
XEvent& xevent = event->m_event; XEvent& xevent = event->m_event;
// let input methods try to handle event first // let input methods try to handle event first
if (m_ic != NULL && XFilterEvent(&xevent, None)) { if (m_ic != NULL) {
// XFilterEvent() may eat the event and generate a new KeyPress
// event with a keycode of 0 because there isn't an actual key
// associated with the keysym. but the KeyRelease may pass
// through XFilterEvent() and keep its keycode. this means
// there's a mismatch between KeyPress and KeyRelease keycodes.
// since we use the keycode on the client to detect when a key
// is released this won't do. so we remember the keycode on
// the most recent KeyPress (and clear it on a matching
// KeyRelease) so we have a keycode for a synthesized KeyPress.
if (xevent.type == KeyPress && xevent.xkey.keycode != 0) {
m_lastKeycode = xevent.xkey.keycode;
}
else if (xevent.type == KeyRelease &&
xevent.xkey.keycode == m_lastKeycode) {
m_lastKeycode = 0;
}
// now filter the event
if (XFilterEvent(&xevent, None)) {
return true; return true;
} }
}
// handle event // handle event
switch (xevent.type) { switch (xevent.type) {
@ -257,16 +278,23 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event)
key = kKeyDelete; key = kKeyDelete;
} }
// get which button. see call to XFilterEvent() above
// for more info.
KeyCode keycode = xevent.xkey.keycode;
if (keycode == 0) {
keycode = m_lastKeycode;
}
// handle key // handle key
m_receiver->onKeyDown(key, mask, m_receiver->onKeyDown(key, mask,
static_cast<KeyButton>(xevent.xkey.keycode)); static_cast<KeyButton>(keycode));
if (key == kKeyCapsLock && m_capsLockHalfDuplex) { if (key == kKeyCapsLock && m_capsLockHalfDuplex) {
m_receiver->onKeyUp(key, mask | KeyModifierCapsLock, m_receiver->onKeyUp(key, mask | KeyModifierCapsLock,
static_cast<KeyButton>(xevent.xkey.keycode)); static_cast<KeyButton>(keycode));
} }
else if (key == kKeyNumLock && m_numLockHalfDuplex) { else if (key == kKeyNumLock && m_numLockHalfDuplex) {
m_receiver->onKeyUp(key, mask | KeyModifierNumLock, m_receiver->onKeyUp(key, mask | KeyModifierNumLock,
static_cast<KeyButton>(xevent.xkey.keycode)); static_cast<KeyButton>(keycode));
} }
} }
} }
@ -509,6 +537,9 @@ CXWindowsPrimaryScreen::onPostOpen()
XWindowAttributes attr; XWindowAttributes attr;
XGetWindowAttributes(display, m_window, &attr); XGetWindowAttributes(display, m_window, &attr);
XSelectInput(display, m_window, attr.your_event_mask | mask); XSelectInput(display, m_window, attr.your_event_mask | mask);
// no previous keycode
m_lastKeycode = 0;
} }
void void
@ -523,6 +554,7 @@ CXWindowsPrimaryScreen::onPreClose()
XCloseIM(m_im); XCloseIM(m_im);
m_im = NULL; m_im = NULL;
} }
m_lastKeycode = 0;
} }
void void

View File

@ -117,8 +117,10 @@ private:
// position of center pixel of screen // position of center pixel of screen
SInt32 m_xCenter, m_yCenter; SInt32 m_xCenter, m_yCenter;
// input method stuff
XIM m_im; XIM m_im;
XIC m_ic; XIC m_ic;
KeyCode m_lastKeycode;
}; };
#endif #endif

View File

@ -126,9 +126,6 @@ void
CXWindowsSecondaryScreen::keyDown(KeyID key, CXWindowsSecondaryScreen::keyDown(KeyID key,
KeyModifierMask mask, KeyButton button) KeyModifierMask mask, KeyButton button)
{ {
Keystrokes keys;
KeyCode keycode;
// check for ctrl+alt+del emulation // check for ctrl+alt+del emulation
if (key == kKeyDelete && if (key == kKeyDelete &&
(mask & (KeyModifierControl | KeyModifierAlt)) == (mask & (KeyModifierControl | KeyModifierAlt)) ==
@ -139,29 +136,30 @@ CXWindowsSecondaryScreen::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;
KeyCode keycode;
m_mask = mapKey(keys, keycode, key, mask, kPress); m_mask = mapKey(keys, keycode, 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] = keycode;
m_keys[keycode] = true; m_keys[keycode] = true;
m_fakeKeys[keycode] = true; m_fakeKeys[keycode] = true;
}
// note which server key generated this key
m_serverKeyMap[button] = keycode;
} }
void void
CXWindowsSecondaryScreen::keyRepeat(KeyID key, CXWindowsSecondaryScreen::keyRepeat(KeyID key,
KeyModifierMask mask, SInt32 count, KeyButton button) KeyModifierMask mask, SInt32 count, KeyButton button)
{ {
Keystrokes keys;
KeyCode keycode;
// if we haven't seen this button go down then ignore it // if we haven't seen this button go down then ignore it
ServerKeyMap::iterator index = m_serverKeyMap.find(button); ServerKeyMap::iterator index = m_serverKeyMap.find(button);
if (index == m_serverKeyMap.end()) { if (index == m_serverKeyMap.end()) {
@ -170,6 +168,8 @@ CXWindowsSecondaryScreen::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;
KeyCode keycode;
m_mask = mapKey(keys, keycode, key, mask, kRepeat); m_mask = mapKey(keys, keycode, key, mask, kRepeat);
if (keys.empty()) { if (keys.empty()) {
return; return;
@ -180,15 +180,20 @@ CXWindowsSecondaryScreen::keyRepeat(KeyID key,
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() && keycode != 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 (keycode != 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. the key up is the first keystroke
// with the keycode returned by mapKey().
for (Keystrokes::iterator index2 = keys.begin(); for (Keystrokes::iterator index2 = keys.begin();
index2 != keys.end(); ++index2) { index2 != keys.end(); ++index2) {
if (index2->m_keycode == index->second) { if (index2->m_keycode == keycode) {
index2->m_keycode = index->second; index2->m_keycode = index->second;
break; break;
} }
@ -214,15 +219,12 @@ void
CXWindowsSecondaryScreen::keyUp(KeyID key, CXWindowsSecondaryScreen::keyUp(KeyID key,
KeyModifierMask mask, KeyButton button) KeyModifierMask mask, KeyButton button)
{ {
Keystrokes keys;
KeyCode keycode;
// if we haven't seen this button go down then ignore it // if we haven't seen this button go down then ignore it
ServerKeyMap::iterator index = m_serverKeyMap.find(button); ServerKeyMap::iterator index = m_serverKeyMap.find(button);
if (index == m_serverKeyMap.end()) { if (index == m_serverKeyMap.end()) {
return; return;
} }
keycode = index->second; KeyCode keycode = index->second;
// check for ctrl+alt+del emulation // check for ctrl+alt+del emulation
if (key == kKeyDelete && if (key == kKeyDelete &&
@ -232,6 +234,9 @@ CXWindowsSecondaryScreen::keyUp(KeyID key,
// just pass the key through // just pass the key through
} }
// get the sequence of keys to simulate key release and the final
// modifier state.
Keystrokes keys;
if (!((key == kKeyCapsLock && m_capsLockHalfDuplex) || if (!((key == kKeyCapsLock && m_capsLockHalfDuplex) ||
(key == kKeyNumLock && m_numLockHalfDuplex))) { (key == kKeyNumLock && m_numLockHalfDuplex))) {
m_mask = mapKeyRelease(keys, keycode); m_mask = mapKeyRelease(keys, keycode);
@ -241,13 +246,9 @@ CXWindowsSecondaryScreen::keyUp(KeyID key,
doKeystrokes(keys, 1); doKeystrokes(keys, 1);
// note that key is now up // note that key is now up
m_serverKeyMap.erase(index);
m_keys[keycode] = false; m_keys[keycode] = false;
m_fakeKeys[keycode] = false; m_fakeKeys[keycode] = false;
// remove server key from map
if (index != m_serverKeyMap.end()) {
m_serverKeyMap.erase(index);
}
} }
void void
@ -798,6 +799,7 @@ CXWindowsSecondaryScreen::mapKeyRelease(Keystrokes& keys, KeyCode keycode) const
if (i != m_keycodeToModifier.end()) { if (i != m_keycodeToModifier.end()) {
ModifierMask bit = (1 << i->second); ModifierMask bit = (1 << i->second);
if (getBits(m_toggleModifierMask, bit) != 0) { if (getBits(m_toggleModifierMask, bit) != 0) {
// toggle keys modify the state on release
return flipBits(m_mask, bit); return flipBits(m_mask, bit);
} }
else { else {