checkpoint. made changes to support key autorepeats on X.

This commit is contained in:
crs 2002-05-03 11:26:44 +00:00
parent 570d85c842
commit 5641a875c1
4 changed files with 197 additions and 66 deletions

View File

@ -221,30 +221,33 @@ void CXWindowsSecondaryScreen::keyDown(
Keystrokes keys; Keystrokes keys;
KeyCode keycode; KeyCode keycode;
CDisplayLock display(this);
// 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.
m_mask = mapKey(keys, keycode, key, mask, True); m_mask = mapKey(keys, keycode, key, mask, kPress);
if (keys.empty()) if (keys.empty())
return; return;
// generate key events // generate key events
for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) doKeystrokes(keys, 1);
XTestFakeKeyEvent(display, k->first, k->second, CurrentTime);
// note that key is now down // note that key is now down
m_keys[keycode] = true; m_keys[keycode] = true;
// update
XSync(display, False);
} }
void CXWindowsSecondaryScreen::keyRepeat( void CXWindowsSecondaryScreen::keyRepeat(
KeyID, KeyModifierMask, SInt32) KeyID key, KeyModifierMask mask, SInt32 count)
{ {
CDisplayLock display(this); Keystrokes keys;
// FIXME KeyCode keycode;
// get the sequence of keys to simulate key repeat and the final
// modifier state.
m_mask = mapKey(keys, keycode, key, mask, kRepeat);
if (keys.empty())
return;
// generate key events
doKeystrokes(keys, count);
} }
void CXWindowsSecondaryScreen::keyUp( void CXWindowsSecondaryScreen::keyUp(
@ -253,23 +256,17 @@ void CXWindowsSecondaryScreen::keyUp(
Keystrokes keys; Keystrokes keys;
KeyCode keycode; KeyCode keycode;
CDisplayLock display(this);
// 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, keycode, key, mask, False); m_mask = mapKey(keys, keycode, key, mask, kRelease);
if (keys.empty()) if (keys.empty())
return; return;
// generate key events // generate key events
for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) doKeystrokes(keys, 1);
XTestFakeKeyEvent(display, k->first, k->second, CurrentTime);
// note that key is now up // note that key is now up
m_keys[keycode] = false; m_keys[keycode] = false;
// update
XSync(display, False);
} }
void CXWindowsSecondaryScreen::mouseDown(ButtonID button) void CXWindowsSecondaryScreen::mouseDown(ButtonID button)
@ -408,7 +405,7 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey(
Keystrokes& keys, Keystrokes& keys,
KeyCode& keycode, KeyCode& keycode,
KeyID id, KeyModifierMask mask, KeyID id, KeyModifierMask mask,
Bool press) const EKeyAction action) const
{ {
// note -- must have display locked on entry // note -- must have display locked on entry
@ -425,8 +422,8 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey(
// note if the key is the caps lock and it's "half-duplex" // note if the key is the caps lock and it's "half-duplex"
const bool isHalfDuplex = (id == XK_Caps_Lock && m_capsLockHalfDuplex); const bool isHalfDuplex = (id == XK_Caps_Lock && m_capsLockHalfDuplex);
// ignore releases for half-duplex keys // ignore releases and repeats for half-duplex keys
if (isHalfDuplex && !press) { if (isHalfDuplex && action != kPress) {
return m_mask; return m_mask;
} }
@ -457,6 +454,7 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey(
// a modifier key then skip this because modifiers should not // a modifier key then skip this because modifiers should not
// modify modifiers. // modify modifiers.
Keystrokes undo; Keystrokes undo;
Keystroke keystroke;
if (outMask != m_mask && !isModifier) { if (outMask != m_mask && !isModifier) {
for (unsigned int i = 0; i < 8; ++i) { for (unsigned int i = 0; i < 8; ++i) {
unsigned int bit = (1 << i); unsigned int bit = (1 << i);
@ -472,20 +470,26 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey(
// modifier is a toggle then toggle it on with a // modifier is a toggle then toggle it on with a
// press/release, otherwise activate it with a // press/release, otherwise activate it with a
// press. use the first keycode for the modifier. // press. use the first keycode for the modifier.
const KeyCode modifierKey = modifierKeys[0]; keystroke.m_keycode = modifierKeys[0];
keys.push_back(std::make_pair(modifierKey, True)); keystroke.m_press = True;
keystroke.m_repeat = False;
keys.push_back(keystroke);
if ((bit & m_toggleModifierMask) != 0) { if ((bit & m_toggleModifierMask) != 0) {
if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { if (bit != m_capsLockMask || !m_capsLockHalfDuplex) {
keys.push_back(std::make_pair(modifierKey, False)); keystroke.m_press = False;
undo.push_back(std::make_pair(modifierKey, False)); keys.push_back(keystroke);
undo.push_back(std::make_pair(modifierKey, True)); undo.push_back(keystroke);
keystroke.m_press = True;
undo.push_back(keystroke);
} }
else { else {
undo.push_back(std::make_pair(modifierKey, False)); keystroke.m_press = False;
undo.push_back(keystroke);
} }
} }
else { else {
undo.push_back(std::make_pair(modifierKey, False)); keystroke.m_press = False;
undo.push_back(keystroke);
} }
} }
@ -496,24 +500,34 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey(
// release. we must check each keycode for the // release. we must check each keycode for the
// modifier if not a toggle. // modifier if not a toggle.
if ((bit & m_toggleModifierMask) != 0) { if ((bit & m_toggleModifierMask) != 0) {
const KeyCode modifierKey = modifierKeys[0]; keystroke.m_keycode = modifierKeys[0];
keystroke.m_repeat = False;
if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { if (bit != m_capsLockMask || !m_capsLockHalfDuplex) {
keys.push_back(std::make_pair(modifierKey, True)); keystroke.m_press = True;
keys.push_back(std::make_pair(modifierKey, False)); keys.push_back(keystroke);
undo.push_back(std::make_pair(modifierKey, False)); keystroke.m_press = False;
undo.push_back(std::make_pair(modifierKey, True)); keys.push_back(keystroke);
undo.push_back(keystroke);
keystroke.m_press = True;
undo.push_back(keystroke);
} }
else { else {
keys.push_back(std::make_pair(modifierKey, False)); keystroke.m_press = False;
undo.push_back(std::make_pair(modifierKey, True)); keys.push_back(keystroke);
keystroke.m_press = True;
undo.push_back(keystroke);
} }
} }
else { else {
for (unsigned int j = 0; j < m_keysPerModifier; ++j) { for (unsigned int j = 0; j < m_keysPerModifier; ++j) {
const KeyCode key = modifierKeys[j]; const KeyCode key = modifierKeys[j];
if (m_keys[key]) { if (m_keys[key]) {
keys.push_back(std::make_pair(key, False)); keystroke.m_keycode = key;
undo.push_back(std::make_pair(key, True)); keystroke.m_press = False;
keystroke.m_repeat = False;
keys.push_back(keystroke);
keystroke.m_press = True;
undo.push_back(keystroke);
} }
} }
} }
@ -524,11 +538,32 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey(
// note if the press of a half-duplex key should be treated as a release // note if the press of a half-duplex key should be treated as a release
if (isHalfDuplex && (m_mask & (1 << index->second)) != 0) { if (isHalfDuplex && (m_mask & (1 << index->second)) != 0) {
press = false; action = kRelease;
} }
// add the key event // add the key event
keys.push_back(std::make_pair(keycode, press)); keystroke.m_keycode = keycode;
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 = False;
keystroke.m_repeat = True;
keys.push_back(keystroke);
keystroke.m_press = True;
keys.push_back(keystroke);
break;
}
// add key events to restore the modifier state. apply events in // add key events to restore the modifier state. apply events in
// the reverse order that they're stored in undo. // the reverse order that they're stored in undo.
@ -538,9 +573,9 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey(
} }
// if the key is a modifier key then compute the modifier map after // if the key is a modifier key then compute the modifier map after
// this key is pressed. // this key is pressed or released. if repeating then ignore.
mask = m_mask; mask = m_mask;
if (isModifier) { if (isModifier && action != kRepeat) {
// get modifier // get modifier
const unsigned int modifierBit = (1 << index->second); const unsigned int modifierBit = (1 << index->second);
@ -549,10 +584,10 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey(
// and clear the bit on release. if half-duplex then toggle // and clear the bit on release. if half-duplex then toggle
// each time we get here. // each time we get here.
if ((modifierBit & m_toggleModifierMask) != 0) { if ((modifierBit & m_toggleModifierMask) != 0) {
if (((mask & modifierBit) == 0) == press) if (((mask & modifierBit) == 0) == (action == kPress))
mask ^= modifierBit; mask ^= modifierBit;
} }
else if (press) { else if (action == kPress) {
mask |= modifierBit; mask |= modifierBit;
} }
else { else {
@ -640,7 +675,7 @@ bool CXWindowsSecondaryScreen::findKeyCode(
} }
// save the keycode // save the keycode
keycode = index->second.keycode; keycode = index->second.m_keycode;
// compute output mask. that's the set of modifiers that need to // compute output mask. that's the set of modifiers that need to
// be enabled when the keycode event is encountered in order to // be enabled when the keycode event is encountered in order to
@ -649,22 +684,68 @@ bool CXWindowsSecondaryScreen::findKeyCode(
// it impossible to generate the keysym. in that case we must // it impossible to generate the keysym. in that case we must
// override maskIn. this is complicated by caps/shift-lock and // override maskIn. this is complicated by caps/shift-lock and
// num-lock. // num-lock.
maskOut = (maskIn & ~index->second.keyMaskMask); maskOut = (maskIn & ~index->second.m_keyMaskMask);
if (IsKeypadKey(id) || IsPrivateKeypadKey(id)) { if (IsKeypadKey(id) || IsPrivateKeypadKey(id)) {
maskOut |= index->second.keyMask; maskOut |= index->second.m_keyMask;
maskOut &= ~m_numLockMask; maskOut &= ~m_numLockMask;
} }
else { else {
unsigned int maskShift = (index->second.keyMask & ShiftMask); unsigned int maskShift = (index->second.m_keyMask & ShiftMask);
if (index->second.keyMaskMask != 0 && (m_mask & m_capsLockMask) != 0) if (index->second.m_keyMaskMask != 0 && (m_mask & m_capsLockMask) != 0)
maskShift ^= ShiftMask; maskShift ^= ShiftMask;
maskOut |= maskShift | (m_mask & m_capsLockMask); maskOut |= maskShift | (m_mask & m_capsLockMask);
maskOut |= (index->second.keyMask & ~(ShiftMask | LockMask)); maskOut |= (index->second.m_keyMask & ~(ShiftMask | LockMask));
} }
return true; return true;
} }
void CXWindowsSecondaryScreen::doKeystrokes(
const Keystrokes& keys, SInt32 count)
{
// do nothing if no keys or no repeats
if (count < 1 || keys.empty())
return;
// lock display
CDisplayLock display(this);
// 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) {
// we generally want repeating keys to use the exact
// same event time for each release/press pair so we
// don't want to use CurrentTime which can't ensure
// that.
Time time = getCurrentTime(m_window);
// send repeating events
for (k = start; k != keys.end() && k->m_repeat; ++k) {
XTestFakeKeyEvent(display,
k->m_keycode, k->m_press, time);
}
}
// 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
XTestFakeKeyEvent(display, k->m_keycode, k->m_press, CurrentTime);
// next key
++k;
}
}
// update
XSync(display, False);
}
unsigned int CXWindowsSecondaryScreen::maskToX( unsigned int CXWindowsSecondaryScreen::maskToX(
KeyModifierMask inMask) const KeyModifierMask inMask) const
{ {
@ -764,12 +845,12 @@ void CXWindowsSecondaryScreen::updateKeycodeMap(
} }
// set the mask of modifiers that this keycode uses // set the mask of modifiers that this keycode uses
entry.keyMaskMask = (n == 1) ? 0 : (ShiftMask | LockMask); entry.m_keyMaskMask = (n == 1) ? 0 : (ShiftMask | LockMask);
// add entries for this keycode // add entries for this keycode
entry.keycode = static_cast<KeyCode>(minKeycode + i); entry.m_keycode = static_cast<KeyCode>(minKeycode + i);
for (int j = 0; j < numKeysyms; ++j) { for (int j = 0; j < numKeysyms; ++j) {
entry.keyMask = (j == 0) ? 0 : ShiftMask; entry.m_keyMask = (j == 0) ? 0 : ShiftMask;
m_keycodeMap.insert(std::make_pair(keysyms[i * m_keycodeMap.insert(std::make_pair(keysyms[i *
keysymsPerKeycode + j], entry)); keysymsPerKeycode + j], entry));
} }
@ -841,7 +922,7 @@ void CXWindowsSecondaryScreen::toggleKey(
KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym); KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym);
if (index == m_keycodeMap.end()) if (index == m_keycodeMap.end())
return; return;
KeyCode keycode = index->second.keycode; KeyCode keycode = index->second.m_keycode;
// toggle the key // toggle the key
if (keysym == XK_Caps_Lock && m_capsLockHalfDuplex) { if (keysym == XK_Caps_Lock && m_capsLockHalfDuplex) {

View File

@ -38,13 +38,19 @@ protected:
virtual long getEventMask(Window) const; virtual long getEventMask(Window) const;
private: private:
struct KeyCodeMask { enum EKeyAction { kPress, kRelease, kRepeat };
class KeyCodeMask {
public: public:
KeyCode keycode; KeyCode m_keycode;
unsigned int keyMask; unsigned int m_keyMask;
unsigned int keyMaskMask; unsigned int m_keyMaskMask;
};
class Keystroke {
public:
KeyCode m_keycode;
Bool m_press;
bool m_repeat;
}; };
typedef std::pair<KeyCode, Bool> Keystroke;
typedef std::vector<Keystroke> Keystrokes; typedef std::vector<Keystroke> Keystrokes;
typedef std::vector<KeyCode> KeyCodes; typedef std::vector<KeyCode> KeyCodes;
typedef std::map<KeyID, KeyCodeMask> KeyCodeMap; typedef std::map<KeyID, KeyCodeMask> KeyCodeMap;
@ -54,9 +60,10 @@ private:
unsigned int mapButton(ButtonID button) const; unsigned int mapButton(ButtonID button) const;
unsigned int mapKey(Keystrokes&, KeyCode&, KeyID, unsigned int mapKey(Keystrokes&, KeyCode&, KeyID,
KeyModifierMask, Bool press) const; KeyModifierMask, EKeyAction) const;
bool findKeyCode(KeyCode&, unsigned int&, bool findKeyCode(KeyCode&, unsigned int&,
KeyID id, unsigned int) const; KeyID id, unsigned int) const;
void doKeystrokes(const Keystrokes&, SInt32 count);
unsigned int maskToX(KeyModifierMask) const; unsigned int maskToX(KeyModifierMask) const;
void updateKeys(Display* display); void updateKeys(Display* display);

View File

@ -63,17 +63,41 @@ void CXWindowsPrimaryScreen::run()
break; break;
} }
// FIXME -- simulate key repeat. X sends press/release for
// repeat. must detect auto repeat and use kKeyRepeat.
case KeyRelease: { case KeyRelease: {
log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyModifierMask mask = mapModifier(xevent.xkey.state);
const KeyID key = mapKey(&xevent.xkey); const KeyID key = mapKey(&xevent.xkey);
if (key != kKeyNone) { if (key != kKeyNone) {
// check if this is a key repeat by getting the next
// KeyPress event that has the same key and time as
// this release event, if any. first prepare the
// filter info.
CKeyEventInfo filter;
filter.m_event = KeyPress;
filter.m_window = xevent.xkey.window;
filter.m_time = xevent.xkey.time;
filter.m_keycode = xevent.xkey.keycode;
// now check for event
XEvent xevent2;
CDisplayLock display(this);
if (XCheckIfEvent(display, &xevent2,
&CXWindowsPrimaryScreen::findKeyEvent,
(XPointer)&filter) != True) {
// no press event follows so it's a plain release
log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
if (key == XK_Caps_Lock && m_capsLockHalfDuplex) if (key == XK_Caps_Lock && m_capsLockHalfDuplex)
m_server->onKeyDown(key, mask); m_server->onKeyDown(key, mask);
m_server->onKeyUp(key, mask); m_server->onKeyUp(key, mask);
} }
else {
// found a press event following so it's a repeat.
// we could attempt to count the already queued
// repeats but we'll just send a repeat of 1.
// note that we discard the press event.
log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
m_server->onKeyRepeat(key, mask, 1);
}
}
break; break;
} }
@ -550,3 +574,13 @@ void CXWindowsPrimaryScreen::updateModifierMap(
XFreeModifiermap(keymap); XFreeModifiermap(keymap);
} }
Bool CXWindowsPrimaryScreen::findKeyEvent(
Display*, XEvent* xevent, XPointer arg)
{
CKeyEventInfo* filter = reinterpret_cast<CKeyEventInfo*>(arg);
return (xevent->type == filter->m_event &&
xevent->xkey.window == filter->m_window &&
xevent->xkey.time == filter->m_time &&
xevent->xkey.keycode == filter->m_keycode) ? True : False;
}

View File

@ -43,6 +43,15 @@ private:
void updateModifierMap(Display* display); void updateModifierMap(Display* display);
class CKeyEventInfo {
public:
int m_event;
Window m_window;
Time m_time;
KeyCode m_keycode;
};
static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg);
private: private:
CServer* m_server; CServer* m_server;
bool m_active; bool m_active;