X11-Autorepeating creates a sequence of KeyPress, KeyRelease, KeyPress, KeyRelease etc. events. This makes it hard for clients to reliably determine whether a (physical) key is up or down at any given moment. That's why applications, which need this information, are supposed to turn auto-repeating off. But if such an application aborts unexpectedly (segfault, assert, abort, ...), then nothing turns auto-repeating on again afterwards. This is quite annoying! One has to run "xset r on" manually, or let a wrapper script do that. Not exactly an elegant solution. RigsOfRods suffers from this.
But why do SDL and osgViewer (-> OpenSceneGraph) not have this problem at all? They both use the same trick: they drop all KeyRelease events that are immediately followed by a KeyPress for the same key, as these events must be coming from an auto-repetition. This sounds dangerous, but it is a proven solution since many years. That way an auto-repetition sequence becomes: KeyPress, KeyPress, KeyPress, KeyRelease. This leaves it to the client software, whether it wants to evaluate the repetition, or wants to consider only the first KeyPress (and ignore those for an already pressed key). And that on a per-key basis! There's no need to turn auto-repeat on/off at all for all of X11. And I've been told that this is also in line with MS Windows' behaviour. (Haven't checked this.)
The following patch would do this. It leaves the settings for XAutoRepeat intact, although these could then probably be dropped. The code is inspired by code in OSG, which has been borrowed from SDL.
Index: src/linux/LinuxKeyboard.cpp
===================================================================
--- src/linux/LinuxKeyboard.cpp (revision 11)
+++ src/linux/LinuxKeyboard.cpp (working copy)
@@ -341,14 +341,28 @@
else if( KeyRelease == event.type )
{
- //Mask out the modifier states X sets.. or we will get improper values
- event.xkey.state &= ~ShiftMask;
- event.xkey.state &= ~LockMask;
+ //Drop KeyRelease if it's immediately followed by a KeyPress for the same
+ //key, in which case we assume it's auto-repeating (SDL and OSG do the same)
+ XEvent next;
+ bool is_autorepeating = false;
+ if( XPending(display) ) {
+ XPeekEvent(display, &next);
+ if( next.type == KeyPress && next.xkey.keycode == event.xkey.keycode
+ && (next.xmotion.time - event.xmotion.time < 2 ))
+ is_autorepeating = true;
+ }
- //Else, it is a valid key release
- XLookupString(&event.xkey,NULL,0,&key,NULL);
+ if( !is_autorepeating )
+ {
+ //Mask out the modifier states X sets.. or we will get improper values
+ event.xkey.state &= ~ShiftMask;
+ event.xkey.state &= ~LockMask;
+ //Else, it is a valid key release
+ XLookupString(&event.xkey,NULL,0,&key,NULL);
+ _injectKeyUp(key);
+ }
}
}
EDIT: diff against svn, rather than cvs.
EDIT2: fix explanation: drop KeyRelease followed immediately by a KeyPress, not following one