If you press and hold a key in X11 while AutoRepeat is enabled, you continuously receive KeyPress and KeyRelease events. I know that AutoRepeat can be disabled using the function XAutoRepeatOff(), but this changes the setting for the whole X server. Is there a way to either disable AutoRepeat for a single application or to ignore repeated keystrokes?
What I'm looking for is a single KeyPress event when a key is pressed and a single KeyRelease event when a key is released, without interfering with the X server's AutoRepeat setting.
Here's a minimal example to get you going (mostly from the Beginner Xlib Tutorial):
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
Display *dis;
Window win;
XEvent report;
int main ()
{
dis = XOpenDisplay (NULL);
// XAutoRepeatOn(dis);
win = XCreateSimpleWindow (dis, RootWindow (dis, 0), 1, 1, 500, 500,
0, BlackPixel (dis, 0), BlackPixel (dis, 0));
XSelectInput (dis, win, KeyPressMask | KeyReleaseMask);
XMapWindow (dis, win);
XFlush (dis);
while (1)
{
XNextEvent (dis, &report);
switch (report.type)
{
case KeyPress:
fprintf (stdout, "key #%ld was pressed.\n",
(long) XLookupKeysym (&report.xkey, 0));
break;
case KeyRelease:
fprintf (stdout, "key #%ld was released.\n",
(long) XLookupKeysym (&report.xkey, 0));
break;
}
}
return (0);
}
You can use the XkbSetDetectableAutorepeat function to tell the X server to only send KeyRelease events when the user actually releases the key - when you don't want autorepeat events, then you discard any KeyPress without matching KeyRelease.
another approach. it works for me.
When you receive a key release and the next event is a key press of the same key combination, then it's auto-repeat and the key wasn't acutally released. You can use code like this to peek next event
For your reference, here's a working minimal example that deletes auto-repeated KeyPress events. Thank you, kralyk!
You could set a timer when a key is pressed or released and ignore KeyPress and KeyRelease events that occur within the repetition interval.
Here's the solution I came up with.
So, everytime I get a KeyRelease event, I use
XQueryKeymap
which will copy tokeys
the bits of pressed keys (8 different keys bychar
). For the ones who are not used to work with bitwise operatos and shift operator, here's a simple explanation:keys[event.xkey.keycode>>3]
search for indexevent.xkey.keycode / 8
using "right shift operator" (that allows for "integer division" by 2, 4, 8, 16 and so on, without type cast to float or double and back to integer).0x1 << (event.xkey.keycode % 8)
does the oposite. It multiplies the value of0x1
(== 1
) by 2 raised to(event.xkey.keycode % 8)
The
&
bitwise operator betweenkeys[event.xkey.keycode>>3]
and0x1 << (event.xkey.keycode % 8)
will compare if the only bit set in the right side operand is set to 1 inside this array index. If so, the key is being pressed.Finally you just enclose it in
()
, with a!
right before and if the result becomes true, you're not pressing that key anymore.One final Note: To use this method, you need to keep feeding the XServer with events. If not, XQueryKeymap will freze until it does (better use with threads).