Code:
#define _POSIX_C_SOURCE 200809L
/*
* Compile using
*
* gcc -Wall -Wextra -O2 example.c -lX11 -o example
*
* and run
*
* ./example
*/
#include <stdlib.h>
#include <sys/select.h>
#include <stdio.h>
#include <time.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
/* Main loop at 10 ms = 10,000,000 ns intervals */
#define LOOP_NS (10*1000*1000)
#define CONTROLS 4
struct {
Bool pressed;
KeyCode keycode;
KeySym keysym;
} control[CONTROLS] = {
{ /* [0]: */ False, 0, XK_Up },
{ /* [1]: */ False, 0, XK_Down },
{ /* [2]: */ False, 0, XK_Left },
{ /* [3]: */ False, 0, XK_Right },
};
int main(void)
{
Display *display;
int screen;
Window window;
Atom wm_delete_window;
XEvent ev;
int connfd;
long elapsed_us;
struct timespec curr, prev, next;
display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "No X11 available.\n");
return EXIT_FAILURE;
}
screen = DefaultScreen(display);
window = XCreateSimpleWindow(display, RootWindow(display, screen),
10, 10, 640, 360, 1,
BlackPixel(display, screen),
BlackPixel(display, screen));
XStoreName(display, window, "X11 Input Example");
XSelectInput(display, window, ExposureMask | KeyPressMask | KeyReleaseMask);
XMapWindow(display, window);
while (1) {
XNextEvent(display, &ev);
if (ev.type == Expose)
break;
else
if (ev.type == ClientMessage &&
ev.xclient.format == 32 &&
(Atom)ev.xclient.data.l[0] == wm_delete_window) {
XDestroyWindow(display, window);
XCloseDisplay(display);
return EXIT_SUCCESS;
}
}
wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, &wm_delete_window, 1);
{
size_t i = CONTROLS;
while (i-->0)
control[i].keycode = XKeysymToKeycode(display, control[i].keysym);
}
connfd = ConnectionNumber(display);
clock_gettime(CLOCK_MONOTONIC, &curr);
while (1) {
prev = curr;
next = curr;
next.tv_nsec += (long)LOOP_NS;
if (next.tv_nsec >= 1000000000L) {
next.tv_nsec -= 1000000000L;
next.tv_sec++;
}
while (1)
if (XPending(display)) {
XNextEvent(display, &ev);
if (ev.type == KeyPress) {
size_t i = CONTROLS;
while (i-->0)
if (ev.xkey.keycode == control[i].keycode) {
control[i].pressed = True;
break;
}
} else
if (ev.type == KeyRelease) {
size_t i = CONTROLS;
while (i-->0)
if (ev.xkey.keycode == control[i].keycode) {
control[i].pressed = False;
break;
}
} else
if (ev.type == ClientMessage &&
ev.xclient.format == 32 &&
(Atom)ev.xclient.data.l[0] == wm_delete_window) {
XDestroyWindow(display, window);
XCloseDisplay(display);
return EXIT_SUCCESS;
}
} else {
struct timeval timeout;
long timeout_us;
fd_set set;
clock_gettime(CLOCK_MONOTONIC, &curr);
if (curr.tv_sec > next.tv_sec ||
(curr.tv_sec == next.tv_sec && curr.tv_nsec >= next.tv_nsec))
break;
timeout_us = (next.tv_nsec - curr.tv_nsec) / 1000L;
if (timeout_us < 0L) {
timeout.tv_sec = next.tv_sec - curr.tv_sec - 1;
timeout.tv_usec = timeout_us + 1000000L;
} else
if (timeout_us < 1000000L) {
timeout.tv_sec = next.tv_sec - curr.tv_sec;
timeout.tv_usec = timeout_us;
} else {
timeout.tv_sec = next.tv_sec - curr.tv_sec + 1;
timeout.tv_usec = timeout_us - 1000000L;
}
FD_ZERO(&set);
FD_SET(connfd, &set);
select(connfd + 1, &set, NULL, NULL, &timeout);
}
elapsed_us = (long)(curr.tv_sec - prev.tv_sec) * 1000000L
+ (curr.tv_nsec - prev.tv_nsec) / 1000L;
/* One iteration of the game loop follows.
*
* elapsed_us is the time in microseconds (1,000,000 us = 1 second)
* elapsed since the last iteration.
* control[0].pressed is True if and only if Up key is pressed.
* control[1].pressed is True if and only if Down key is pressed.
* control[2].pressed is True if and only if Left key is pressed.
* control[3].pressed is True if and only if Right key is pressed.
*/
}
}
Questions?