🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Handle Multiple Keypress Events With Xwindows

Started by
3 comments, last by Null and Void 13 years, 5 months ago
I am using XCheckMaskEvent to pull key presses. I am not having any problem with individual keys being held down, but the system is not reading multiple key downs. For example I will be holding the up arrow, then press left arrow, then release left arrow and up arrow is no longer registering.
"It's like naming him Asskicker Monstertrucktits O'Ninja" -Khaiy

Advertisement
Are you relying on the key to auto-repeat their press and release events? You may want to disable auto-repeat on at least the arrow keys, store their pressed/released state on your own, and generate your own repeat behavior based on your stored state.

By the way, if you're curious to see what events are being generated quickly and easily, run a program named "xev". It's a window that reports the events it's receiving into the terminal.

Are you relying on the key to auto-repeat their press and release events? You may want to disable auto-repeat on at least the arrow keys, store their pressed/released state on your own, and generate your own repeat behavior based on your stored state.

By the way, if you're curious to see what events are being generated quickly and easily, run a program named "xev". It's a window that reports the events it's receiving into the terminal.


Is there a way to do this besides xkbsetdetectableautorepeat? I would much rather have a keydown/keyup system but I couldn't find a decent workaround for the false keyup that linux likes to generate when a key is held down. I know I am not reading the keys near low level enough because things like the keyboard delay are showing up in my simulation, which is undesirable.
"It's like naming him Asskicker Monstertrucktits O'Ninja" -Khaiy

Oh and thanks for hipping me to Xev it is extremely useful.
"It's like naming him Asskicker Monstertrucktits O'Ninja" -Khaiy

If you just care about which keys are currently pressed and released, an auto-repeat release will be immediately followed by a press. So, we'll just store which keys are pressed and released:

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>

int keysym_to_arrow_key(KeySym keysym) {
switch(keysym) {
case XK_Up:
return 0;
case XK_Down:
return 1;
case XK_Left:
return 2;
case XK_Right:
return 3;
}

return -1;
}

int main(void) {
Display *display;
Window window;
int arrow_keys[4] = { 0, 0, 0, 0 };

display = XOpenDisplay(NULL);
window = XCreateSimpleWindow(display, RootWindow(display, 0),
100, 100, 100, 100,
0, BlackPixel(display, 0), BlackPixel(display, 0));

XSelectInput(display, window, KeyPressMask | KeyReleaseMask);
XMapWindow(display, window);
XFlush(display);

for(;;) {
/* Event loop */
while(XPending(display) > 0) {
XEvent event;

XNextEvent(display, &event);

switch(event.type) {
case KeyPress: {
KeySym keysym = XLookupKeysym(&event.xkey, 0);
int arrow_key = keysym_to_arrow_key(keysym);

/* Is this a key we care about? All we're caring about now are the arrow keys. */
if(arrow_key != -1)
arrow_keys[arrow_key] = 1;
} break;
case KeyRelease: {
KeySym keysym = XLookupKeysym(&event.xkey, 0);
int arrow_key = keysym_to_arrow_key(keysym);

/* Is this a key we care about? All we're caring about now are the arrow keys. */
if(arrow_key != -1)
arrow_keys[arrow_key] = 0;
} break;
}
}

/* Do stuff */
printf("%d%d%d%d\n", arrow_keys[0], arrow_keys[1], arrow_keys[2], arrow_keys[3]);
}

return 0;
}

If you do need auto-repeating keys, it gets a little more complicated:

#include <stdio.h>
#include <time.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>

unsigned long get_time_milliseconds(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec*1000 + ts.tv_nsec/1000000;
}
int keysym_to_arrow_key(KeySym keysym) {
switch(keysym) {
case XK_Up:
return 0;
case XK_Down:
return 1;
case XK_Left:
return 2;
case XK_Right:
return 3;
}

return -1;
}

int main(void) {
unsigned int a;
Display *display;
Window window;
struct {
int pressed;
int pressed_count;
unsigned long press_time;
unsigned long repeat_interval;
} arrow_keys[4] = { { 0, 0, 0, 0 }, };

display = XOpenDisplay(NULL);
window = XCreateSimpleWindow(display, RootWindow(display, 0),
100, 100, 100, 100,
0, BlackPixel(display, 0), BlackPixel(display, 0));

XSelectInput(display, window, KeyPressMask | KeyReleaseMask);
XMapWindow(display, window);
XFlush(display);

/* Setup an automatic repeat interval */
arrow_keys[0].repeat_interval =
arrow_keys[1].repeat_interval =
arrow_keys[2].repeat_interval =
arrow_keys[3].repeat_interval = 250; /* milliseconds */

for(;;) {
unsigned long currenttime = get_time_milliseconds();

/* Event loop */
while(XPending(display) > 0) {
XEvent event;

XNextEvent(display, &event);

switch(event.type) {
case KeyPress: {
KeySym keysym = XLookupKeysym(&event.xkey, 0);
int arrow_key = keysym_to_arrow_key(keysym);

/* Is this a key we care about? All we're caring about now are the arrow keys. */
if(arrow_key != -1) {
arrow_keys[arrow_key].pressed = 1;
arrow_keys[arrow_key].pressed_count += 1;
arrow_keys[arrow_key].press_time = currenttime;
}
} break;
case KeyRelease: {
KeySym keysym = XLookupKeysym(&event.xkey, 0);
int arrow_key = keysym_to_arrow_key(keysym);

/* Is this a key we care about? All we're caring about now are the arrow keys. */
if(arrow_key != -1) {
XEvent next_event;

/* Check if this is an auto-repeat by seeing if the
next event is a KeyPress at the exact same time. */
if(XPending(display) > 0
&& XPeekEvent(display, &next_event)
&& next_event.type == KeyPress
&& next_event.xkey.time == event.xkey.time
&& next_event.xkey.keycode == event.xkey.keycode) {
/* It's an auto-repeat, eat the event */
XNextEvent(display, &next_event);
} else if(arrow_keys[arrow_key].pressed) {
/* Okay, it's not auto-repeat. */
unsigned int presses = (currenttime - arrow_keys[arrow_key].press_time)/arrow_keys[arrow_key].repeat_interval;
arrow_keys[arrow_key].pressed = 0;
arrow_keys[arrow_key].pressed_count += presses;
}
}
} break;
}
}
/* Manual event loop checking for auto-repeat */
{
for(a = 0; a < sizeof(arrow_keys)/sizeof(arrow_keys[0]); ++a) {
if(arrow_keys[a].pressed) {
unsigned int presses = (currenttime - arrow_keys[a].press_time)/arrow_keys[a].repeat_interval;

arrow_keys[a].pressed_count += presses;
arrow_keys[a].press_time += presses * arrow_keys[a].repeat_interval;
}
}
}

/* Do stuff */
{
const char *state[] = { "pressed", "released" };
const char *names[] = { "up", "down", "left", "right" };

/* Report and deregister key presses */
for(a = 0; a < sizeof(arrow_keys)/sizeof(arrow_keys[0]); ++a) {
while(arrow_keys[a].pressed_count > 0) {
printf("%s pressed.\n", names[a]);
arrow_keys[a].pressed_count -= 1;
}
}

/* Disabled due to flood of text without any sort of delay */
if(0) {
/* Report key state */
for(a = 0; a < sizeof(arrow_keys)/sizeof(arrow_keys[0]); ++a)
printf("%5s is %8s. ", names[a], state[!arrow_keys[a].pressed]);
putc('\n', stdout);
}
}
}

return 0;
}

I'm hardly Mr. Xlib-expert, so there may be easier ways to accomplish this. At least it will keep you from having to disable auto-repeat for the entire display.

This topic is closed to new replies.

Advertisement