joy2key

ジョイパッドの入力をキーボードのキーコードに変換するjoy2keyを試していたけど、window名の指定が面倒なのでフォーカスがあたったところ限定にしたオリジナル版を作ってみました。Shift, Control, Metaなどのmodifierがない簡易的なものです。

joy2keyのソースコードを見ればわかりますが、はっきりいっていまいちです。sleep(0.2)とか使い方間違ってるし。

はまっている方もいらっしゃるようです。
http://d.hatena.ne.jp/n9d/20080210/1202648836

$ ./myjoy2key -b "d c x s NoSymbol NoSymbol NoSymbol NoSymbol space Return a z" \
-a "Left Right Up Down"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>

#include <unistd.h>
#include <fcntl.h>

#if !defined(MYJOY2KEY_DEBUG)
#include <linux/joystick.h>

#include <X11/Xlib.h>
#include <X11/keysymdef.h>
#else
#include "myjoy2key_debug.h"
#endif

typedef struct {
    int pos_on;
    int neg_on;
    KeySym pos;
    KeySym neg;
} Axis;

typedef struct {
    char name[128];
    int fd;
    int naxes;
    int nbuttons;
    Axis *axis;
    KeySym *button;
} Joypad;

typedef struct {
    Display *display;
} Client;


void usage(const char *);
int joypad_load(Joypad *, const char *);
int joypad_exec(Client *, Joypad *);
int bind_axes(Joypad *, const char *);
int bind_buttons(Joypad *, const char *);
int gettok(const char *, char *, int, const char **);
int sendkey(Client *, KeySym, int);
void error(const char *, ...);


int main(int argc, char **argv)
{
    const char *progname;
    const char *devname;
    const char *axesstr;
    const char *buttonsstr;
    Joypad joypad;
    Client client;
    int opt;

    progname = argv[0];
    devname = "/dev/input/js0";
    axesstr = NULL;
    buttonsstr = NULL;
    while ((opt = getopt(argc, argv, "a:b:d:")) != -1) {
        switch (opt) {
        case 'a':
            axesstr = optarg;
            break;
        case 'b':
            buttonsstr = optarg;
            break;
        case 'd':
            devname = optarg;
            break;
        default:
            usage(progname);
        }
    }

    if ((client.display = XOpenDisplay(NULL)) == NULL)
        return EXIT_FAILURE;

    if (joypad_load(&joypad, devname) != 0)
        return EXIT_FAILURE;

    if (axesstr)
        if (bind_axes(&joypad, axesstr) != 0)
            return EXIT_FAILURE;

    if (buttonsstr)
        if (bind_buttons(&joypad, buttonsstr) != 0)
            return EXIT_FAILURE;

    while (joypad_exec(&client, &joypad) == 0)
        ;

    return EXIT_SUCCESS;
}

void usage(const char *progname)
{
    printf("%s [-a axes] [-b buttons] [-d devname]\n", progname);

    exit(EXIT_FAILURE);
}

int joypad_load(Joypad *joy, const char *devname)
{
    unsigned char v;
    size_t size;
    int i;

    if ((joy->fd = open(devname, O_RDONLY)) == -1) {
        error("open(`%s')", devname);
        return -1;
    }

    if (ioctl(joy->fd, JSIOCGNAME(sizeof(joy->name)), joy->name) < 0) {
        error("ioctl(name)");
        close(joy->fd);
        return -1;
    }

    if (ioctl(joy->fd, JSIOCGAXES, &v) < 0) {
        error("ioctl(axes)");
        close(joy->fd);
        return -1;
    }
    joy->naxes = v;

    if (ioctl(joy->fd, JSIOCGBUTTONS, &v) < 0) {
        error("ioctl(buttons)");
        close(joy->fd);
        return -1;
    }
    joy->nbuttons = v;

    printf("NAME    : %s\n", joy->name);
    printf("AXES    : %d\n", joy->naxes);
    printf("BUTTONS : %d\n", joy->nbuttons);

    size = joy->naxes * sizeof(*joy->axis);
    if ((joy->axis = malloc(size)) == NULL) {
        error("malloc(axis: %zu)", size);
        close(joy->fd);
        return -1;
    }

    for (i = 0; i < joy->naxes; i++) {
        joy->axis[i].pos = NoSymbol;
        joy->axis[i].neg = NoSymbol;
        joy->axis[i].pos_on = 0;
        joy->axis[i].neg_on = 0;
    }

    size = joy->nbuttons * sizeof(*joy->button);
    if ((joy->button = malloc(size)) == NULL) {
        error("malloc(button: %zu)", size);
        close(joy->fd);
        free(joy->axis);
        return -1;
    }

    for (i = 0; i < joy->nbuttons; i++)
        joy->button[i] = NoSymbol;

    return 0;
}

int bind_axes(Joypad *joy, const char *s)
{
    char token[80];
    const char *brkp;
    int n;

    brkp = NULL;
    for (n = 0; gettok(s, token, sizeof(token), &brkp); n++) {
        if (n >= joy->naxes * 2) {
            error("Too many AXES %d > %d * 2.\n", n + 1,
                  joy->naxes);
            return -1;
        }
        if (n & 1)
            joy->axis[n / 2].pos = XStringToKeysym(token);
        else
            joy->axis[n / 2].neg = XStringToKeysym(token);
    }
    if (n < joy->naxes * 2) {
        error("Too few AXES %d < %d * 2.\n", n, joy->naxes);
        return -1;
    }

    return 0;
}

int bind_buttons(Joypad *joy, const char *s)
{
    char token[80];
    const char *brkp;
    int n;

    brkp = NULL;
    for (n = 0; gettok(s, token, sizeof(token), &brkp); n++) {
        if (n >= joy->nbuttons) {
            error("Too many BUTTONS %d > %d.\n", n + 1,
                  joy->nbuttons);
            return -1;
        }
        joy->button[n] = XStringToKeysym(token);
    }
    if (n < joy->nbuttons) {
        error("Too few BUTTONS %d < %d.\n", n, joy->nbuttons);
        return -1;
    }

    return 0;
}

int gettok(const char *s, char *buf, int len, const char **brkp)
{
    const char *p;

    if (*brkp == NULL)
        p = s;
    else
        p = *brkp;

    if (*p == '\0')
        return 0;

    while (*p == ' ')
        p++;

    do {
        if (len <= 1) {
            error("Internal buffer too small.\n");
            return 0;
        }
        *buf++ = *p++;
        len--;
    } while (*p != ' ' && *p != '\0');

    *buf = '\0';

    *brkp = p;

    return 1;
}

int joypad_exec(Client *client, Joypad *joy)
{
    struct js_event e;
    KeySym key;
    Axis *axis;

    if (read(joy->fd, &e, sizeof(e)) != sizeof(e))
        return -1;

    switch (e.type & ~JS_EVENT_INIT) {
    case JS_EVENT_BUTTON:
        if ((key = joy->button[e.number]) != NoSymbol)
            sendkey(client, key, e.value ? KeyPress : KeyRelease);
        break;
    case JS_EVENT_AXIS:
        axis = &joy->axis[e.number];
        if (axis->neg_on && e.value >= 0) {
            sendkey(client, axis->neg, KeyRelease);
            axis->neg_on = 0;
        }
        if (axis->pos_on && e.value <= 0) {
            sendkey(client, axis->pos, KeyRelease);
            axis->pos_on = 0;
        }
        if (axis->neg != NoSymbol && e.value < 0) {
            sendkey(client, axis->neg, KeyPress);
            axis->neg_on = 1;
        }
        if (axis->pos != NoSymbol && e.value > 0) {
            sendkey(client, axis->pos, KeyPress);
            axis->pos_on = 1;
        }
        break;
    default:
        break;
    }

    return 0;
}

int sendkey(Client *client, KeySym key, int type)
{
    Display *display = client->display;
    XKeyEvent event;
    Window window;
    int r;

    XGetInputFocus(display, &window, &r);

    event.type = type;
    event.serial = 0; /* undef */
    event.send_event = 0; /* undef */
    event.display = display;
    event.window = window;
    event.root = DefaultRootWindow(display);
    event.subwindow = None;
    event.time = CurrentTime;
    event.x = 1; /* undef */
    event.y = 1; /* undef */
    event.x_root = 1; /* undef */
    event.y_root = 1; /* undef */
    event.state = 0;
    event.keycode = XKeysymToKeycode(display, key);
    event.same_screen = True; /* undef */

    XSendEvent(display, window, True, KeyPressMask, (XEvent *)&event);

    XFlush(display);

    return 0;
}

void error(const char *fmt, ...)
{
    int sverror;
    va_list ap;

    sverror = errno;

    fprintf(stderr, "Error: ");

    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);

    if (strchr(fmt, '\n') == NULL)
        fprintf(stderr, " %s\n", strerror(sverror));

    errno = sverror;
}