You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libtdevnc/x11vnc/uinput.c

1528 lines
31 KiB

/* -- uinput.c -- */
#include "x11vnc.h"
#include "cleanup.h"
#include "scan.h"
#include "xinerama.h"
#include "screen.h"
#include "pointer.h"
#include "keyboard.h"
#include "allowed_input_t.h"
#if LIBVNCSERVER_HAVE_SYS_IOCTL_H
#if LIBVNCSERVER_HAVE_LINUX_INPUT_H
#if LIBVNCSERVER_HAVE_LINUX_UINPUT_H
#define UINPUT_OK
#endif
#endif
#endif
#ifdef UINPUT_OK
#include <sys/ioctl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#if !defined(EV_SYN) || !defined(SYN_REPORT)
#undef UINPUT_OK
#endif
#endif
int check_uinput(void);
int initialize_uinput(void);
int set_uinput_accel(char *str);
int set_uinput_thresh(char *str);
void set_uinput_reset(int ms);
void set_uinput_always(int);
void set_uinput_touchscreen(int);
void set_uinput_abs(int);
char *get_uinput_accel();
char *get_uinput_thresh();
int get_uinput_reset();
int get_uinput_always();
int get_uinput_touchscreen();
int get_uinput_abs();
void parse_uinput_str(char *str);
void uinput_pointer_command(int mask, int x, int y, rfbClientPtr client);
void uinput_key_command(int down, int keysym, rfbClientPtr client);
static void init_key_tracker(void);
static int mod_is_down(void);
static int key_is_down(void);
static void set_uinput_accel_xy(double fx, double fy);
static void shutdown_uinput(void);
static void ptr_move(int dx, int dy);
static void ptr_rel(int dx, int dy);
static void button_click(int down, int btn);
static int lookup_code(int keysym);
static int fd = -1;
static int db = 0;
static int bmask = 0;
static char *injectable = NULL;
static char *uinput_dev = NULL;
static int uinput_touchscreen = 0;
static int uinput_abs = 0;
static int abs_x = 0, abs_y = 0;
static char *devs[] = {
"/dev/misc/uinput",
"/dev/input/uinput",
"/dev/uinput",
NULL
};
/*
* User may need to do:
modprode uinput
mknod /dev/input/uinput c 10 223
*/
int check_uinput(void) {
#ifndef UINPUT_OK
return 0;
#else
int i;
if (UT.release) {
int maj, min;
/* guard against linux 2.4 */
if (sscanf(UT.release, "%d.%d.", &maj, &min) == 2) {
if (maj < 2) {
return 0;
} else if (maj == 2) {
/* hmmm IPAQ 2.4.19-rmk6-pxa1-hh37 works... */
#if 0
if (min < 6) {
return 0;
}
#endif
}
}
}
fd = -1;
i = 0;
while (devs[i] != NULL) {
if ( (fd = open(devs[i++], O_RDWR)) >= 0) {
break;
}
}
if (fd < 0) {
return 0;
}
close(fd);
fd = -1;
return 1;
#endif
}
static int key_pressed[256];
static int key_ismod[256];
static void init_key_tracker(void) {
int i;
for (i = 0; i < 256; i++) {
key_pressed[i] = 0;
key_ismod[i] = 0;
}
i = lookup_code(XK_Shift_L); if (0<=i && i<256) key_ismod[i] = 1;
i = lookup_code(XK_Shift_R); if (0<=i && i<256) key_ismod[i] = 1;
i = lookup_code(XK_Control_L); if (0<=i && i<256) key_ismod[i] = 1;
i = lookup_code(XK_Control_R); if (0<=i && i<256) key_ismod[i] = 1;
i = lookup_code(XK_Alt_L); if (0<=i && i<256) key_ismod[i] = 1;
i = lookup_code(XK_Alt_R); if (0<=i && i<256) key_ismod[i] = 1;
i = lookup_code(XK_Meta_L); if (0<=i && i<256) key_ismod[i] = 1;
i = lookup_code(XK_Meta_R); if (0<=i && i<256) key_ismod[i] = 1;
}
static int mod_is_down(void) {
int i;
if (0) {key_is_down();}
for (i = 0; i < 256; i++) {
if (key_pressed[i] && key_ismod[i]) {
return 1;
}
}
return 0;
}
static int key_is_down(void) {
int i;
for (i = 0; i < 256; i++) {
if (key_pressed[i]) {
return 1;
}
}
return 0;
}
static void shutdown_uinput(void) {
#ifdef UINPUT_OK
ioctl(fd, UI_DEV_DESTROY);
#endif
}
int initialize_uinput(void) {
#ifndef UINPUT_OK
return 0;
#else
int i;
struct uinput_user_dev udev;
if (fd >= 0) {
shutdown_uinput();
close(fd);
fd = -1;
}
if (getenv("X11VNC_UINPUT_DEBUG")) {
db = atoi(getenv("X11VNC_UINPUT_DEBUG"));
rfbLog("set uinput debug to: %d\n", db);
}
init_key_tracker();
if (uinput_dev) {
fd = open(uinput_dev, O_RDWR);
rfbLog("initialize_uinput: using: %s %d\n", uinput_dev, fd);
} else {
i = 0;
while (devs[i] != NULL) {
if ( (fd = open(devs[i], O_RDWR)) >= 0) {
rfbLog("initialize_uinput: using: %s %d\n",
devs[i], fd);
break;
}
i++;
}
}
if (fd < 0) {
rfbLog("initialize_uinput: could not open an uinput device.\n");
rfbLogPerror("open");
clean_up_exit(1);
}
memset(&udev, 0, sizeof(udev));
strncpy(udev.name, "x11vnc injector", UINPUT_MAX_NAME_SIZE);
udev.id.bustype = BUS_USB; /* Matters? */
udev.id.version = 4;
ioctl(fd, UI_SET_EVBIT, EV_REL);
ioctl(fd, UI_SET_RELBIT, REL_X);
ioctl(fd, UI_SET_RELBIT, REL_Y);
ioctl(fd, UI_SET_EVBIT, EV_KEY);
for (i=0; i < 256; i++) {
ioctl(fd, UI_SET_KEYBIT, i);
}
ioctl(fd, UI_SET_KEYBIT, BTN_MOUSE);
ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
if (uinput_touchscreen) {
ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
rfbLog("uinput: touchscreen enabled.\n");
}
if (uinput_touchscreen || uinput_abs) {
int gw = abs_x, gh = abs_y;
if (! gw || ! gh) {
gw = fb_x; gh = fb_y;
}
if (! gw || ! gh) {
gw = dpy_x; gh = dpy_y;
}
abs_x = gw;
abs_y = gh;
ioctl(fd, UI_SET_EVBIT, EV_ABS);
ioctl(fd, UI_SET_ABSBIT, ABS_X);
ioctl(fd, UI_SET_ABSBIT, ABS_Y);
udev.absmin[ABS_X] = 0;
udev.absmax[ABS_X] = gw;
udev.absfuzz[ABS_X] = 0;
udev.absflat[ABS_X] = 0;
udev.absmin[ABS_Y] = 0;
udev.absmax[ABS_Y] = gh;
udev.absfuzz[ABS_Y] = 0;
udev.absflat[ABS_Y] = 0;
rfbLog("uinput: absolute pointer enabled at %dx%d.\n", abs_x, abs_y);
set_uinput_accel_xy(1.0, 1.0);
}
write(fd, &udev, sizeof(udev));
if (ioctl(fd, UI_DEV_CREATE) != 0) {
rfbLog("ioctl(fd, UI_DEV_CREATE) failed.\n");
rfbLogPerror("ioctl");
close(fd);
clean_up_exit(1);
}
return 1;
#endif
}
/* these defaults are based on qt-embedded 7/2006 */
static double fudge_x = 0.5; /* accel=2.0 */
static double fudge_y = 0.5;
static int thresh = 5;
static int thresh_or = 1;
static double resid_x = 0.0;
static double resid_y = 0.0;
static double zero_delay = 0.15;
static double last_button_click = 0.0;
static int uinput_always = 0;
static void set_uinput_accel_xy(double fx, double fy) {
fudge_x = 1.0/fx;
fudge_y = 1.0/fy;
rfbLog("set_uinput_accel: fx=%.5f fy=%.5f\n", fx, fy);
rfbLog("set_uinput_accel: ix=%.5f iy=%.5f\n", fudge_x, fudge_y);
}
static char *uinput_accel_str = NULL;
static char *uinput_thresh_str = NULL;
int set_uinput_accel(char *str) {
double fx, fy;
rfbLog("set_uinput_accel: str=%s\n", str);
if (sscanf(str, "%lf+%lf", &fx, &fy) == 2) {
set_uinput_accel_xy(fx, fy);
} else if (sscanf(str, "%lf", &fx) == 1) {
set_uinput_accel_xy(fx, fx);
} else {
rfbLog("invalid UINPUT accel= option: %s\n", str);
return 0;
}
if (uinput_accel_str) {
free(uinput_accel_str);
}
uinput_accel_str = strdup(str);
return 1;
}
int set_uinput_thresh(char *str) {
rfbLog("set_uinput_thresh: str=%s\n", str);
if (str[0] == '+') {
thresh_or = 0;
}
thresh = atoi(str);
if (uinput_thresh_str) {
free(uinput_thresh_str);
}
uinput_thresh_str = strdup(str);
return 1;
}
void set_uinput_reset(int ms) {
zero_delay = (double) ms/1000.;
rfbLog("set_uinput_reset: %d\n", ms);
}
void set_uinput_always(int a) {
uinput_always = a;
}
void set_uinput_touchscreen(int b) {
uinput_touchscreen = b;
}
void set_uinput_abs(int b) {
uinput_abs = b;
}
char *get_uinput_accel(void) {
return uinput_accel_str;
}
char *get_uinput_thresh(void) {
return uinput_thresh_str;
}
int get_uinput_reset(void) {
return (int) (1000 * zero_delay);
}
int get_uinput_always(void) {
return uinput_always;
}
int get_uinput_touchscreen(void) {
return uinput_touchscreen;
}
int get_uinput_abs(void) {
return uinput_abs;
}
void parse_uinput_str(char *in) {
char *p, *q, *str = strdup(in);
if (injectable) {
free(injectable);
injectable = strdup("KMB");
}
uinput_touchscreen = 0;
uinput_abs = 0;
abs_x = abs_y = 0;
p = strtok(str, ",");
while (p) {
if (p[0] == '/') {
if (uinput_dev) {
free(uinput_dev);
}
uinput_dev = strdup(p);
} else if (strstr(p, "accel=") == p) {
q = p + strlen("accel=");
if (! set_uinput_accel(q)) {
clean_up_exit(1);
}
} else if (strstr(p, "thresh=") == p) {
q = p + strlen("thresh=");
set_uinput_thresh(q);
} else if (strstr(p, "reset=") == p) {
int n = atoi(p + strlen("reset="));
set_uinput_reset(n);
} else if (strstr(p, "always=") == p) {
int n = atoi(p + strlen("always="));
set_uinput_always(n);
} else if (strpbrk(p, "KMB") == p) {
if (injectable) {
free(injectable);
}
injectable = strdup(p);
} else if (strstr(p, "touch") == p) {
int gw, gh;
q = strchr(p, '=');
set_uinput_touchscreen(1);
set_uinput_abs(1);
if (q && sscanf(q+1, "%dx%d", &gw, &gh) == 2) {
abs_x = gw;
abs_y = gh;
}
} else if (strstr(p, "abs") == p) {
int gw, gh;
q = strchr(p, '=');
set_uinput_abs(1);
if (q && sscanf(q+1, "%dx%d", &gw, &gh) == 2) {
abs_x = gw;
abs_y = gh;
}
} else {
rfbLog("invalid UINPUT option: %s\n", p);
clean_up_exit(1);
}
p = strtok(NULL, ",");
}
free(str);
}
static void ptr_move(int dx, int dy) {
#ifdef UINPUT_OK
struct input_event ev;
if (injectable && strchr(injectable, 'M') == NULL) {
return;
}
memset(&ev, 0, sizeof(ev));
gettimeofday(&ev.time, NULL);
ev.type = EV_REL;
ev.code = REL_Y;
ev.value = dy;
write(fd, &ev, sizeof(ev));
ev.type = EV_REL;
ev.code = REL_X;
ev.value = dx;
write(fd, &ev, sizeof(ev));
ev.type = EV_SYN;
ev.code = SYN_REPORT;
ev.value = 0;
write(fd, &ev, sizeof(ev));
#else
if (!dx || !dy) {}
#endif
}
static void ptr_abs(int x, int y) {
#ifdef UINPUT_OK
struct input_event ev;
if (injectable && strchr(injectable, 'M') == NULL) {
return;
}
memset(&ev, 0, sizeof(ev));
if (db) fprintf(stderr, "ptr_abs(%d, %d)\n", x, y);
gettimeofday(&ev.time, NULL);
ev.type = EV_ABS;
ev.code = ABS_Y;
ev.value = y;
write(fd, &ev, sizeof(ev));
ev.type = EV_ABS;
ev.code = ABS_X;
ev.value = x;
write(fd, &ev, sizeof(ev));
ev.type = EV_SYN;
ev.code = SYN_REPORT;
ev.value = 0;
write(fd, &ev, sizeof(ev));
#else
if (!x || !y) {}
#endif
}
static int inside_thresh(int dx, int dy, int thr) {
if (thresh_or) {
/* this is peeking at qt-embedded qmouse_qws.cpp */
if (nabs(dx) <= thresh && nabs(dy) <= thr) {
return 1;
}
} else {
/* this is peeking at xfree/xorg xf86Xinput.c */
if (nabs(dx) + nabs(dy) < thr) {
return 1;
}
}
return 0;
}
static void ptr_rel(int dx, int dy) {
int dxf, dyf, nx, ny, k;
int accel, thresh_high, thresh_mid;
double fx, fy;
static int try_threshes = -1;
if (try_threshes < 0) {
if (getenv("X11VNC_UINPUT_THRESHOLDS")) {
try_threshes = 1;
} else {
try_threshes = 0;
}
}
if (try_threshes) {
thresh_high = (int) ( (double) thresh/fudge_x );
thresh_mid = (int) ( (double) (thresh + thresh_high) / 2.0 );
if (thresh_mid <= thresh) {
thresh_mid = thresh + 1;
}
if (thresh_high <= thresh_mid) {
thresh_high = thresh_mid + 1;
}
if (inside_thresh(dx, dy, thresh)) {
accel = 0;
} else {
accel = 1;
}
nx = nabs(dx);
ny = nabs(dy);
} else {
accel = 1;
thresh_high = 0;
nx = ny = 1;
}
if (accel && nx + ny > 0 ) {
if (thresh_high > 0 && inside_thresh(dx, dy, thresh_high)) {
double alpha, t;
/* XXX */
if (1 || inside_thresh(dx, dy, thresh_mid)) {
t = thresh;
accel = 2;
} else {
accel = 3;
t = thresh_high;
}
if (thresh_or) {
if (nx > ny) {
fx = t;
fy = ((double) ny / (double) nx) * t;
} else {
fx = ((double) nx / (double) ny) * t;
fy = t;
}
dxf = (int) fx;
dyf = (int) fy;
fx = dx;
fy = dy;
} else {
if (t > 1) {
/* XXX */
t = t - 1.0;
}
alpha = t/(nx + ny);
fx = alpha * dx;
fy = alpha * dy;
dxf = (int) fx;
dyf = (int) fy;
fx = dx;
fy = dy;
}
} else {
fx = fudge_x * (double) dx;
fy = fudge_y * (double) dy;
dxf = (int) fx;
dyf = (int) fy;
}
} else {
fx = dx;
fy = dy;
dxf = dx;
dyf = dy;
}
if (db > 1) fprintf(stderr, "old dx dy: %d %d\n", dx, dy);
if (db > 1) fprintf(stderr, "new dx dy: %d %d accel: %d\n", dxf, dyf, accel);
ptr_move(dxf, dyf);
resid_x += fx - dxf;
resid_y += fy - dyf;
for (k = 0; k < 4; k++) {
if (resid_x <= -1.0 || resid_x >= 1.0 || resid_y <= -1.0 || resid_y >= 1.0) {
dxf = 0;
dyf = 0;
if (resid_x >= 1.0) {
dxf = (int) resid_x;
dxf = 1;
} else if (resid_x <= -1.0) {
dxf = -((int) (-resid_x));
dxf = -1;
}
resid_x -= dxf;
if (resid_y >= 1.0) {
dyf = (int) resid_y;
dyf = 1;
} else if (resid_y <= -1.0) {
dyf = -((int) (-resid_y));
dyf = -1;
}
resid_y -= dyf;
if (db > 1) fprintf(stderr, "*%s resid: dx dy: %d %d %f %f\n", accel > 1 ? "*" : " ", dxf, dyf, resid_x, resid_y);
if (0) {usleep(100*1000)};
ptr_move(dxf, dyf);
}
}
}
static void button_click(int down, int btn) {
#ifdef UINPUT_OK
struct input_event ev;
if (injectable && strchr(injectable, 'B') == NULL) {
return;
}
if (db) fprintf(stderr, "button_click: btn %d %s\n", btn, down ? "down" : "up");
memset(&ev, 0, sizeof(ev));
gettimeofday(&ev.time, NULL);
ev.type = EV_KEY;
ev.value = down;
if (uinput_touchscreen) {
ev.code = BTN_TOUCH;
if (db) fprintf(stderr, "set code to BTN_TOUCH\n");
} else if (btn == 1) {
ev.code = BTN_LEFT;
} else if (btn == 2) {
ev.code = BTN_MIDDLE;
} else if (btn == 3) {
ev.code = BTN_RIGHT;
} else if (btn == 4) {
ev.code = BTN_FORWARD;
} else if (btn == 5) {
ev.code = BTN_BACK;
} else {
return;
}
write(fd, &ev, sizeof(ev));
ev.type = EV_SYN;
ev.code = SYN_REPORT;
ev.value = 0;
write(fd, &ev, sizeof(ev));
last_button_click = dnow();
#else
if (!down || !btn) {}
#endif
}
void uinput_pointer_command(int mask, int x, int y, rfbClientPtr client) {
static int last_x = -1, last_y = -1, last_mask = -1;
static double last_zero = 0.0;
allowed_input_t input;
int do_reset, reset_lower_right = 1;
double now;
static int first = 1;
if (first) {
if (getenv("RESET_ALWAYS")) {
set_uinput_always(1);
} else {
set_uinput_always(0);
}
}
first = 0;
if (db) fprintf(stderr, "uinput_pointer_command: %d %d - %d\n", x, y, mask);
if (view_only) {
return;
}
get_allowed_input(client, &input);
now = dnow();
do_reset = 1;
if (mask || bmask) {
do_reset = 0; /* do not do reset if mouse button down */
} else if (! input.motion) {
do_reset = 0;
} else if (now < last_zero + zero_delay) {
do_reset = 0;
}
if (do_reset) {
if (mod_is_down()) {
do_reset = 0;
} else if (now < last_button_click + 0.25) {
do_reset = 0;
}
}
if (uinput_always && !mask && !bmask && input.motion) {
do_reset = 1;
}
if (uinput_abs) {
#if 0
/* this is a bad idea... need to do something else */
if (do_reset) {
ptr_abs(dpy_x, dpy_y);
usleep(10*1000);
ptr_abs(x, y);
usleep(10*1000);
}
#endif
do_reset = 0;
}
if (do_reset) {
static int first = 1;
if (zero_delay > 0.0 || first) {
/* try to push it to 0,0 */
int tx, ty, bigjump = 1;
if (reset_lower_right) {
tx = fudge_x * (dpy_x - last_x);
ty = fudge_y * (dpy_y - last_y);
} else {
tx = fudge_x * last_x;
ty = fudge_y * last_y;
}
tx += 50;
ty += 50;
if (bigjump) {
if (reset_lower_right) {
ptr_move(0, +ty);
usleep(2*1000);
ptr_move(+tx, +ty);
ptr_move(+tx, +ty);
} else {
ptr_move(0, -ty);
usleep(2*1000);
ptr_move(-tx, -ty);
ptr_move(-tx, -ty);
}
} else {
int i, step, n = 20;
step = dpy_x / n;
if (step < 100) step = 100;
for (i=0; i < n; i++) {
if (reset_lower_right) {
ptr_move(+step, +step);
} else {
ptr_move(-step, -step);
}
}
for (i=0; i < n; i++) {
if (reset_lower_right) {
ptr_move(+1, +1);
} else {
ptr_move(-1, -1);
}
}
}
if (db) {
if (reset_lower_right) {
fprintf(stderr, "uinput_pointer_command: reset -> (W,H) (%d,%d) [%d,%d]\n", x, y, tx, ty);
} else {
fprintf(stderr, "uinput_pointer_command: reset -> (0,0) (%d,%d) [%d,%d]\n", x, y, tx, ty);
}
}
/* rest a bit for system to absorb the change */
if (uinput_always) {
static double last_sleep = 0.0;
double nw = dnow(), delay = zero_delay;
if (delay <= 0.0) delay = 0.1;
if (nw > last_sleep + delay) {
usleep(10*1000);
last_sleep = nw;
} else {
usleep(1*1000);
}
} else {
usleep(30*1000);
}
/* now jump back out */
if (reset_lower_right) {
ptr_rel(x - dpy_x, y - dpy_y);
} else {
ptr_rel(x, y);
}
if (1) {usleep(10*1000)};
last_x = x;
last_y = y;
resid_x = 0.0;
resid_y = 0.0;
first = 0;
}
last_zero = dnow();
}
if (input.motion) {
if (x != last_x || y != last_y) {
if (uinput_abs) {
ptr_abs(x, y);
} else {
ptr_rel(x - last_x, y - last_y);
}
last_x = x;
last_y = y;
}
}
if (! input.button) {
return;
}
if (last_mask < 0) {
last_mask = mask;
}
if (db > 2) {
fprintf(stderr, "mask: %s\n", bitprint(mask, 16));
fprintf(stderr, "bmask: %s\n", bitprint(bmask, 16));
fprintf(stderr, "last_mask: %s\n", bitprint(last_mask, 16));
fprintf(stderr, "button_mask: %s\n", bitprint(button_mask, 16));
}
if (mask != last_mask) {
int i;
for (i=1; i <= MAX_BUTTONS; i++) {
int down, b = 1 << (i-1);
if ( (last_mask & b) == (mask & b)) {
continue;
}
if (mask & b) {
down = 1;
} else {
down = 0;
}
button_click(down, i);
}
last_mask = mask;
}
bmask = mask;
}
void uinput_key_command(int down, int keysym, rfbClientPtr client) {
#ifdef UINPUT_OK
struct input_event ev;
int scancode;
allowed_input_t input;
if (injectable && strchr(injectable, 'K') == NULL) {
return;
}
if (view_only) {
return;
}
get_allowed_input(client, &input);
if (! input.keystroke) {
return;
}
scancode = lookup_code(keysym);
if (scancode < 0) {
return;
}
if (db) fprintf(stderr, "uinput_key_command: %d -> %d %s\n", keysym, scancode, down ? "down" : "up");
memset(&ev, 0, sizeof(ev));
gettimeofday(&ev.time, NULL);
ev.type = EV_KEY;
ev.code = (unsigned char) scancode;
ev.value = down;
write(fd, &ev, sizeof(ev));
ev.type = EV_SYN;
ev.code = SYN_REPORT;
ev.value = 0;
write(fd, &ev, sizeof(ev));
if (0 <= scancode && scancode < 256) {
key_pressed[scancode] = down ? 1 : 0;
}
#else
if (!down || !keysym || !client) {}
#endif
}
static int lookup_code(int keysym) {
if (keysym == NoSymbol) {
return -1;
}
switch(keysym) {
#ifdef UINPUT_OK
case XK_Escape: return KEY_ESC;
case XK_1: return KEY_1;
case XK_2: return KEY_2;
case XK_3: return KEY_3;
case XK_4: return KEY_4;
case XK_5: return KEY_5;
case XK_6: return KEY_6;
case XK_7: return KEY_7;
case XK_8: return KEY_8;
case XK_9: return KEY_9;
case XK_0: return KEY_0;
case XK_exclam: return KEY_1;
case XK_at: return KEY_2;
case XK_numbersign: return KEY_3;
case XK_dollar: return KEY_4;
case XK_percent: return KEY_5;
case XK_asciicircum: return KEY_6;
case XK_ampersand: return KEY_7;
case XK_asterisk: return KEY_8;
case XK_parenleft: return KEY_9;
case XK_parenright: return KEY_0;
case XK_minus: return KEY_MINUS;
case XK_underscore: return KEY_MINUS;
case XK_equal: return KEY_EQUAL;
case XK_plus: return KEY_EQUAL;
case XK_BackSpace: return KEY_BACKSPACE;
case XK_Tab: return KEY_TAB;
case XK_q: return KEY_Q;
case XK_Q: return KEY_Q;
case XK_w: return KEY_W;
case XK_W: return KEY_W;
case XK_e: return KEY_E;
case XK_E: return KEY_E;
case XK_r: return KEY_R;
case XK_R: return KEY_R;
case XK_t: return KEY_T;
case XK_T: return KEY_T;
case XK_y: return KEY_Y;
case XK_Y: return KEY_Y;
case XK_u: return KEY_U;
case XK_U: return KEY_U;
case XK_i: return KEY_I;
case XK_I: return KEY_I;
case XK_o: return KEY_O;
case XK_O: return KEY_O;
case XK_p: return KEY_P;
case XK_P: return KEY_P;
case XK_braceleft: return KEY_LEFTBRACE;
case XK_braceright: return KEY_RIGHTBRACE;
case XK_bracketleft: return KEY_LEFTBRACE;
case XK_bracketright: return KEY_RIGHTBRACE;
case XK_Return: return KEY_ENTER;
case XK_Control_L: return KEY_LEFTCTRL;
case XK_a: return KEY_A;
case XK_A: return KEY_A;
case XK_s: return KEY_S;
case XK_S: return KEY_S;
case XK_d: return KEY_D;
case XK_D: return KEY_D;
case XK_f: return KEY_F;
case XK_F: return KEY_F;
case XK_g: return KEY_G;
case XK_G: return KEY_G;
case XK_h: return KEY_H;
case XK_H: return KEY_H;
case XK_j: return KEY_J;
case XK_J: return KEY_J;
case XK_k: return KEY_K;
case XK_K: return KEY_K;
case XK_l: return KEY_L;
case XK_L: return KEY_L;
case XK_semicolon: return KEY_SEMICOLON;
case XK_colon: return KEY_SEMICOLON;
case XK_apostrophe: return KEY_APOSTROPHE;
case XK_quotedbl: return KEY_APOSTROPHE;
case XK_grave: return KEY_GRAVE;
case XK_asciitilde: return KEY_GRAVE;
case XK_Shift_L: return KEY_LEFTSHIFT;
case XK_backslash: return KEY_BACKSLASH;
case XK_bar: return KEY_BACKSLASH;
case XK_z: return KEY_Z;
case XK_Z: return KEY_Z;
case XK_x: return KEY_X;
case XK_X: return KEY_X;
case XK_c: return KEY_C;
case XK_C: return KEY_C;
case XK_v: return KEY_V;
case XK_V: return KEY_V;
case XK_b: return KEY_B;
case XK_B: return KEY_B;
case XK_n: return KEY_N;
case XK_N: return KEY_N;
case XK_m: return KEY_M;
case XK_M: return KEY_M;
case XK_comma: return KEY_COMMA;
case XK_less: return KEY_COMMA;
case XK_period: return KEY_DOT;
case XK_greater: return KEY_DOT;
case XK_slash: return KEY_SLASH;
case XK_question: return KEY_SLASH;
case XK_Shift_R: return KEY_RIGHTSHIFT;
case XK_KP_Multiply: return KEY_KPASTERISK;
case XK_Alt_L: return KEY_LEFTALT;
case XK_space: return KEY_SPACE;
case XK_Caps_Lock: return KEY_CAPSLOCK;
case XK_F1: return KEY_F1;
case XK_F2: return KEY_F2;
case XK_F3: return KEY_F3;
case XK_F4: return KEY_F4;
case XK_F5: return KEY_F5;
case XK_F6: return KEY_F6;
case XK_F7: return KEY_F7;
case XK_F8: return KEY_F8;
case XK_F9: return KEY_F9;
case XK_F10: return KEY_F10;
case XK_Num_Lock: return KEY_NUMLOCK;
case XK_Scroll_Lock: return KEY_SCROLLLOCK;
case XK_KP_7: return KEY_KP7;
case XK_KP_8: return KEY_KP8;
case XK_KP_9: return KEY_KP9;
case XK_KP_Subtract: return KEY_KPMINUS;
case XK_KP_4: return KEY_KP4;
case XK_KP_5: return KEY_KP5;
case XK_KP_6: return KEY_KP6;
case XK_KP_Add: return KEY_KPPLUS;
case XK_KP_1: return KEY_KP1;
case XK_KP_2: return KEY_KP2;
case XK_KP_3: return KEY_KP3;
case XK_KP_0: return KEY_KP0;
case XK_KP_Decimal: return KEY_KPDOT;
case XK_F13: return KEY_F13;
case XK_F11: return KEY_F11;
case XK_F12: return KEY_F12;
case XK_F14: return KEY_F14;
case XK_F15: return KEY_F15;
case XK_F16: return KEY_F16;
case XK_F17: return KEY_F17;
case XK_F18: return KEY_F18;
case XK_F19: return KEY_F19;
case XK_F20: return KEY_F20;
case XK_KP_Enter: return KEY_KPENTER;
case XK_Control_R: return KEY_RIGHTCTRL;
case XK_KP_Divide: return KEY_KPSLASH;
case XK_Sys_Req: return KEY_SYSRQ;
case XK_Alt_R: return KEY_RIGHTALT;
case XK_Linefeed: return KEY_LINEFEED;
case XK_Home: return KEY_HOME;
case XK_Up: return KEY_UP;
case XK_Page_Up: return KEY_PAGEUP;
case XK_Left: return KEY_LEFT;
case XK_Right: return KEY_RIGHT;
case XK_End: return KEY_END;
case XK_Down: return KEY_DOWN;
case XK_Page_Down: return KEY_PAGEDOWN;
case XK_Insert: return KEY_INSERT;
case XK_Delete: return KEY_DELETE;
case XK_KP_Equal: return KEY_KPEQUAL;
case XK_Pause: return KEY_PAUSE;
case XK_F21: return KEY_F21;
case XK_F22: return KEY_F22;
case XK_F23: return KEY_F23;
case XK_F24: return KEY_F24;
case XK_KP_Separator: return KEY_KPCOMMA;
case XK_Meta_L: return KEY_LEFTMETA;
case XK_Meta_R: return KEY_RIGHTMETA;
case XK_Multi_key: return KEY_COMPOSE;
#endif
default: return -1;
}
}
#if 0
From /usr/include/linux/input.h
We maintain it here since it is such a painful mess.
Here is a little script to make it easier:
#!/usr/bin/perl
while (<>) {
$_ =~ s/-XK_/XK_/;
next unless /^XK_/;
chomp;
if (/^(\S+)(\s+)(\S+)/) {
$a = $1;
$t = $2;
$b = $3;
print "\tcase $a:${t}return $b;\n";
if ($a =~ /XK_[a-z]$/) {
$a = uc($a);
print "\tcase $a:${t}return $b;\n";
}
}
}
This only handles US kbd, we would need a kbd database in general...
Ugh: parse dumpkeys(1) or -fookeys /usr/share/keymaps/i386/qwerty/dk.kmap.gz
XK_Escape KEY_ESC
XK_1 KEY_1
XK_2 KEY_2
XK_3 KEY_3
XK_4 KEY_4
XK_5 KEY_5
XK_6 KEY_6
XK_7 KEY_7
XK_8 KEY_8
XK_9 KEY_9
XK_0 KEY_0
-XK_exclam KEY_1
-XK_at KEY_2
-XK_numbersign KEY_3
-XK_dollar KEY_4
-XK_percent KEY_5
-XK_asciicircum KEY_6
-XK_ampersand KEY_7
-XK_asterisk KEY_8
-XK_parenleft KEY_9
-XK_parenright KEY_0
XK_minus KEY_MINUS
-XK_underscore KEY_MINUS
XK_equal KEY_EQUAL
-XK_plus KEY_EQUAL
XK_BackSpace KEY_BACKSPACE
XK_Tab KEY_TAB
XK_q KEY_Q
XK_w KEY_W
XK_e KEY_E
XK_r KEY_R
XK_t KEY_T
XK_y KEY_Y
XK_u KEY_U
XK_i KEY_I
XK_o KEY_O
XK_p KEY_P
XK_braceleft KEY_LEFTBRACE
XK_braceright KEY_RIGHTBRACE
-XK_bracketleft KEY_LEFTBRACE
-XK_bracketright KEY_RIGHTBRACE
XK_Return KEY_ENTER
XK_Control_L KEY_LEFTCTRL
XK_a KEY_A
XK_s KEY_S
XK_d KEY_D
XK_f KEY_F
XK_g KEY_G
XK_h KEY_H
XK_j KEY_J
XK_k KEY_K
XK_l KEY_L
XK_semicolon KEY_SEMICOLON
-XK_colon KEY_SEMICOLON
XK_apostrophe KEY_APOSTROPHE
-XK_quotedbl KEY_APOSTROPHE
XK_grave KEY_GRAVE
-XK_asciitilde KEY_GRAVE
XK_Shift_L KEY_LEFTSHIFT
XK_backslash KEY_BACKSLASH
-XK_bar KEY_BACKSLASH
XK_z KEY_Z
XK_x KEY_X
XK_c KEY_C
XK_v KEY_V
XK_b KEY_B
XK_n KEY_N
XK_m KEY_M
XK_comma KEY_COMMA
-XK_less KEY_COMMA
XK_period KEY_DOT
-XK_greater KEY_DOT
XK_slash KEY_SLASH
-XK_question KEY_SLASH
XK_Shift_R KEY_RIGHTSHIFT
XK_KP_Multiply KEY_KPASTERISK
XK_Alt_L KEY_LEFTALT
XK_space KEY_SPACE
XK_Caps_Lock KEY_CAPSLOCK
XK_F1 KEY_F1
XK_F2 KEY_F2
XK_F3 KEY_F3
XK_F4 KEY_F4
XK_F5 KEY_F5
XK_F6 KEY_F6
XK_F7 KEY_F7
XK_F8 KEY_F8
XK_F9 KEY_F9
XK_F10 KEY_F10
XK_Num_Lock KEY_NUMLOCK
XK_Scroll_Lock KEY_SCROLLLOCK
XK_KP_7 KEY_KP7
XK_KP_8 KEY_KP8
XK_KP_9 KEY_KP9
XK_KP_Subtract KEY_KPMINUS
XK_KP_4 KEY_KP4
XK_KP_5 KEY_KP5
XK_KP_6 KEY_KP6
XK_KP_Add KEY_KPPLUS
XK_KP_1 KEY_KP1
XK_KP_2 KEY_KP2
XK_KP_3 KEY_KP3
XK_KP_0 KEY_KP0
XK_KP_Decimal KEY_KPDOT
NoSymbol KEY_103RD
XK_F13 KEY_F13
NoSymbol KEY_102ND
XK_F11 KEY_F11
XK_F12 KEY_F12
XK_F14 KEY_F14
XK_F15 KEY_F15
XK_F16 KEY_F16
XK_F17 KEY_F17
XK_F18 KEY_F18
XK_F19 KEY_F19
XK_F20 KEY_F20
XK_KP_Enter KEY_KPENTER
XK_Control_R KEY_RIGHTCTRL
XK_KP_Divide KEY_KPSLASH
XK_Sys_Req KEY_SYSRQ
XK_Alt_R KEY_RIGHTALT
XK_Linefeed KEY_LINEFEED
XK_Home KEY_HOME
XK_Up KEY_UP
XK_Page_Up KEY_PAGEUP
XK_Left KEY_LEFT
XK_Right KEY_RIGHT
XK_End KEY_END
XK_Down KEY_DOWN
XK_Page_Down KEY_PAGEDOWN
XK_Insert KEY_INSERT
XK_Delete KEY_DELETE
NoSymbol KEY_MACRO
NoSymbol KEY_MUTE
NoSymbol KEY_VOLUMEDOWN
NoSymbol KEY_VOLUMEUP
NoSymbol KEY_POWER
XK_KP_Equal KEY_KPEQUAL
NoSymbol KEY_KPPLUSMINUS
XK_Pause KEY_PAUSE
XK_F21 KEY_F21
XK_F22 KEY_F22
XK_F23 KEY_F23
XK_F24 KEY_F24
XK_KP_Separator KEY_KPCOMMA
XK_Meta_L KEY_LEFTMETA
XK_Meta_R KEY_RIGHTMETA
XK_Multi_key KEY_COMPOSE
NoSymbol KEY_STOP
NoSymbol KEY_AGAIN
NoSymbol KEY_PROPS
NoSymbol KEY_UNDO
NoSymbol KEY_FRONT
NoSymbol KEY_COPY
NoSymbol KEY_OPEN
NoSymbol KEY_PASTE
NoSymbol KEY_FIND
NoSymbol KEY_CUT
NoSymbol KEY_HELP
NoSymbol KEY_MENU
NoSymbol KEY_CALC
NoSymbol KEY_SETUP
NoSymbol KEY_SLEEP
NoSymbol KEY_WAKEUP
NoSymbol KEY_FILE
NoSymbol KEY_SENDFILE
NoSymbol KEY_DELETEFILE
NoSymbol KEY_XFER
NoSymbol KEY_PROG1
NoSymbol KEY_PROG2
NoSymbol KEY_WWW
NoSymbol KEY_MSDOS
NoSymbol KEY_COFFEE
NoSymbol KEY_DIRECTION
NoSymbol KEY_CYCLEWINDOWS
NoSymbol KEY_MAIL
NoSymbol KEY_BOOKMARKS
NoSymbol KEY_COMPUTER
NoSymbol KEY_BACK
NoSymbol KEY_FORWARD
NoSymbol KEY_CLOSECD
NoSymbol KEY_EJECTCD
NoSymbol KEY_EJECTCLOSECD
NoSymbol KEY_NEXTSONG
NoSymbol KEY_PLAYPAUSE
NoSymbol KEY_PREVIOUSSONG
NoSymbol KEY_STOPCD
NoSymbol KEY_RECORD
NoSymbol KEY_REWIND
NoSymbol KEY_PHONE
NoSymbol KEY_ISO
NoSymbol KEY_CONFIG
NoSymbol KEY_HOMEPAGE
NoSymbol KEY_REFRESH
NoSymbol KEY_EXIT
NoSymbol KEY_MOVE
NoSymbol KEY_EDIT
NoSymbol KEY_SCROLLUP
NoSymbol KEY_SCROLLDOWN
NoSymbol KEY_KPLEFTPAREN
NoSymbol KEY_KPRIGHTPAREN
NoSymbol KEY_INTL1
NoSymbol KEY_INTL2
NoSymbol KEY_INTL3
NoSymbol KEY_INTL4
NoSymbol KEY_INTL5
NoSymbol KEY_INTL6
NoSymbol KEY_INTL7
NoSymbol KEY_INTL8
NoSymbol KEY_INTL9
NoSymbol KEY_LANG1
NoSymbol KEY_LANG2
NoSymbol KEY_LANG3
NoSymbol KEY_LANG4
NoSymbol KEY_LANG5
NoSymbol KEY_LANG6
NoSymbol KEY_LANG7
NoSymbol KEY_LANG8
NoSymbol KEY_LANG9
NoSymbol KEY_PLAYCD
NoSymbol KEY_PAUSECD
NoSymbol KEY_PROG3
NoSymbol KEY_PROG4
NoSymbol KEY_SUSPEND
NoSymbol KEY_CLOSE
NoSymbol KEY_PLAY
NoSymbol KEY_FASTFORWARD
NoSymbol KEY_BASSBOOST
NoSymbol KEY_PRINT
NoSymbol KEY_HP
NoSymbol KEY_CAMERA
NoSymbol KEY_SOUND
NoSymbol KEY_QUESTION
NoSymbol KEY_EMAIL
NoSymbol KEY_CHAT
NoSymbol KEY_SEARCH
NoSymbol KEY_CONNECT
NoSymbol KEY_FINANCE
NoSymbol KEY_SPORT
NoSymbol KEY_SHOP
NoSymbol KEY_ALTERASE
NoSymbol KEY_CANCEL
NoSymbol KEY_BRIGHTNESSDOWN
NoSymbol KEY_BRIGHTNESSUP
NoSymbol KEY_MEDIA
NoSymbol KEY_UNKNOWN
NoSymbol
NoSymbol BTN_MISC
NoSymbol BTN_0
NoSymbol BTN_1
NoSymbol BTN_2
NoSymbol BTN_3
NoSymbol BTN_4
NoSymbol BTN_5
NoSymbol BTN_6
NoSymbol BTN_7
NoSymbol BTN_8
NoSymbol BTN_9
NoSymbol
NoSymbol BTN_MOUSE
NoSymbol BTN_LEFT
NoSymbol BTN_RIGHT
NoSymbol BTN_MIDDLE
NoSymbol BTN_SIDE
NoSymbol BTN_EXTRA
NoSymbol BTN_FORWARD
NoSymbol BTN_BACK
NoSymbol BTN_TASK
NoSymbol
NoSymbol BTN_JOYSTICK
NoSymbol BTN_TRIGGER
NoSymbol BTN_THUMB
NoSymbol BTN_THUMB2
NoSymbol BTN_TOP
NoSymbol BTN_TOP2
NoSymbol BTN_PINKIE
NoSymbol BTN_BASE
NoSymbol BTN_BASE2
NoSymbol BTN_BASE3
NoSymbol BTN_BASE4
NoSymbol BTN_BASE5
NoSymbol BTN_BASE6
NoSymbol BTN_DEAD
NoSymbol BTN_GAMEPAD
NoSymbol BTN_A
NoSymbol BTN_B
NoSymbol BTN_C
NoSymbol BTN_X
NoSymbol BTN_Y
NoSymbol BTN_Z
NoSymbol BTN_TL
NoSymbol BTN_TR
NoSymbol BTN_TL2
NoSymbol BTN_TR2
NoSymbol BTN_SELECT
NoSymbol BTN_START
NoSymbol BTN_MODE
NoSymbol BTN_THUMBL
NoSymbol BTN_THUMBR
NoSymbol BTN_DIGI
NoSymbol BTN_TOOL_PEN
NoSymbol BTN_TOOL_RUBBER
NoSymbol BTN_TOOL_BRUSH
NoSymbol BTN_TOOL_PENCIL
NoSymbol BTN_TOOL_AIRBRUSH
NoSymbol BTN_TOOL_FINGER
NoSymbol BTN_TOOL_MOUSE
NoSymbol BTN_TOOL_LENS
NoSymbol BTN_TOUCH
NoSymbol BTN_STYLUS
NoSymbol BTN_STYLUS2
NoSymbol BTN_TOOL_DOUBLETAP
NoSymbol BTN_TOOL_TRIPLETAP
NoSymbol BTN_WHEEL
NoSymbol BTN_GEAR_DOWN
NoSymbol BTN_GEAR_UP
NoSymbol KEY_OK
NoSymbol KEY_SELECT
NoSymbol KEY_GOTO
NoSymbol KEY_CLEAR
NoSymbol KEY_POWER2
NoSymbol KEY_OPTION
NoSymbol KEY_INFO
NoSymbol KEY_TIME
NoSymbol KEY_VENDOR
NoSymbol KEY_ARCHIVE
NoSymbol KEY_PROGRAM
NoSymbol KEY_CHANNEL
NoSymbol KEY_FAVORITES
NoSymbol KEY_EPG
NoSymbol KEY_PVR
NoSymbol KEY_MHP
NoSymbol KEY_LANGUAGE
NoSymbol KEY_TITLE
NoSymbol KEY_SUBTITLE
NoSymbol KEY_ANGLE
NoSymbol KEY_ZOOM
NoSymbol KEY_MODE
NoSymbol KEY_KEYBOARD
NoSymbol KEY_SCREEN
NoSymbol KEY_PC
NoSymbol KEY_TV
NoSymbol KEY_TV2
NoSymbol KEY_VCR
NoSymbol KEY_VCR2
NoSymbol KEY_SAT
NoSymbol KEY_SAT2
NoSymbol KEY_CD
NoSymbol KEY_TAPE
NoSymbol KEY_RADIO
NoSymbol KEY_TUNER
NoSymbol KEY_PLAYER
NoSymbol KEY_TEXT
NoSymbol KEY_DVD
NoSymbol KEY_AUX
NoSymbol KEY_MP3
NoSymbol KEY_AUDIO
NoSymbol KEY_VIDEO
NoSymbol KEY_DIRECTORY
NoSymbol KEY_LIST
NoSymbol KEY_MEMO
NoSymbol KEY_CALENDAR
NoSymbol KEY_RED
NoSymbol KEY_GREEN
NoSymbol KEY_YELLOW
NoSymbol KEY_BLUE
NoSymbol KEY_CHANNELUP
NoSymbol KEY_CHANNELDOWN
NoSymbol KEY_FIRST
NoSymbol KEY_LAST
NoSymbol KEY_AB
NoSymbol KEY_NEXT
NoSymbol KEY_RESTART
NoSymbol KEY_SLOW
NoSymbol KEY_SHUFFLE
NoSymbol KEY_BREAK
NoSymbol KEY_PREVIOUS
NoSymbol KEY_DIGITS
NoSymbol KEY_TEEN
NoSymbol KEY_TWEN
NoSymbol KEY_DEL_EOL
NoSymbol KEY_DEL_EOS
NoSymbol KEY_INS_LINE
NoSymbol KEY_DEL_LINE
NoSymbol KEY_MAX
#endif