Jump to content
  • Advertisement
Sign in to follow this  
mrchrismnh

Handle Multiple Keypress Events With Xwindows

This topic is 2712 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!