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:
parent
47b480c0bc
commit
8f9cc6e476
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue