/* -- 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 #include #include #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