Home | History | Annotate | Download | only in x11vnc
      1 /*
      2    Copyright (C) 2002-2010 Karl J. Runge <runge (at) karlrunge.com>
      3    All rights reserved.
      4 
      5 This file is part of x11vnc.
      6 
      7 x11vnc is free software; you can redistribute it and/or modify
      8 it under the terms of the GNU General Public License as published by
      9 the Free Software Foundation; either version 2 of the License, or (at
     10 your option) any later version.
     11 
     12 x11vnc is distributed in the hope that it will be useful,
     13 but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with x11vnc; if not, write to the Free Software
     19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
     20 or see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, as a special exception, Karl J. Runge
     23 gives permission to link the code of its release of x11vnc with the
     24 OpenSSL project's "OpenSSL" library (or with modified versions of it
     25 that use the same license as the "OpenSSL" library), and distribute
     26 the linked executables.  You must obey the GNU General Public License
     27 in all respects for all of the code used other than "OpenSSL".  If you
     28 modify this file, you may extend this exception to your version of the
     29 file, but you are not obligated to do so.  If you do not wish to do
     30 so, delete this exception statement from your version.
     31 */
     32 
     33 /* -- uinput.c -- */
     34 
     35 #include "x11vnc.h"
     36 #include "cleanup.h"
     37 #include "scan.h"
     38 #include "xinerama.h"
     39 #include "screen.h"
     40 #include "pointer.h"
     41 #include "keyboard.h"
     42 #include "allowed_input_t.h"
     43 
     44 #if LIBVNCSERVER_HAVE_SYS_IOCTL_H
     45 #if LIBVNCSERVER_HAVE_LINUX_INPUT_H
     46 #if LIBVNCSERVER_HAVE_LINUX_UINPUT_H
     47 #define UINPUT_OK
     48 #endif
     49 #endif
     50 #endif
     51 
     52 #ifdef UINPUT_OK
     53 #include <sys/ioctl.h>
     54 #include <linux/input.h>
     55 #include <linux/uinput.h>
     56 
     57 #if !defined(EV_SYN) || !defined(SYN_REPORT)
     58 #undef UINPUT_OK
     59 #endif
     60 
     61 #endif
     62 
     63 
     64 int check_uinput(void);
     65 int initialize_uinput(void);
     66 void shutdown_uinput(void);
     67 int set_uinput_accel(char *str);
     68 int set_uinput_thresh(char *str);
     69 void set_uinput_reset(int ms);
     70 void set_uinput_always(int);
     71 void set_uinput_touchscreen(int);
     72 void set_uinput_abs(int);
     73 char *get_uinput_accel();
     74 char *get_uinput_thresh();
     75 int get_uinput_reset();
     76 int get_uinput_always();
     77 int get_uinput_touchscreen();
     78 int get_uinput_abs();
     79 void parse_uinput_str(char *str);
     80 void uinput_pointer_command(int mask, int x, int y, rfbClientPtr client);
     81 void uinput_key_command(int down, int keysym, rfbClientPtr client);
     82 
     83 static void init_key_tracker(void);
     84 static int mod_is_down(void);
     85 static int key_is_down(void);
     86 static void set_uinput_accel_xy(double fx, double fy);
     87 static void ptr_move(int dx, int dy);
     88 static void ptr_rel(int dx, int dy);
     89 static void button_click(int down, int btn);
     90 static int lookup_code(int keysym);
     91 
     92 static int fd = -1;
     93 static int direct_rel_fd = -1;
     94 static int direct_abs_fd = -1;
     95 static int direct_btn_fd = -1;
     96 static int direct_key_fd = -1;
     97 static int bmask = 0;
     98 static int db = 0;
     99 
    100 static char *injectable = NULL;
    101 static char *uinput_dev = NULL;
    102 static char *tslib_cal = NULL;
    103 static double a[7];
    104 static int uinput_touchscreen = 0;
    105 static int uinput_abs = 0;
    106 static int btn_touch = 0;
    107 static int dragskip = 0;
    108 static int touch_always = 0;
    109 static int touch_pressure = 1;
    110 static int abs_x = 0, abs_y = 0;
    111 
    112 static char *devs[] = {
    113 	"/dev/misc/uinput",
    114 	"/dev/input/uinput",
    115 	"/dev/uinput",
    116 	NULL
    117 };
    118 
    119 #ifndef O_NDELAY
    120 #ifdef  O_NONBLOCK
    121 #define O_NDELAY O_NONBLOCK
    122 #else
    123 #define O_NDELAY 0
    124 #endif
    125 #endif
    126 
    127 /*
    128  * User may need to do:
    129  	modprode uinput
    130 	mknod /dev/input/uinput c 10 223
    131  */
    132 
    133 int check_uinput(void) {
    134 #ifndef UINPUT_OK
    135 	return 0;
    136 #else
    137 	int i;
    138 	if (UT.release) {
    139 		int maj, min;
    140 		/* guard against linux 2.4 */
    141 		if (sscanf(UT.release, "%d.%d.", &maj, &min) == 2) {
    142 			if (maj < 2) {
    143 				return 0;
    144 			} else if (maj == 2) {
    145 				/* hmmm IPAQ 2.4.19-rmk6-pxa1-hh37 works... */
    146 #if 0
    147 				if (min < 6) {
    148 					return 0;
    149 				}
    150 #endif
    151 			}
    152 		}
    153 	}
    154 	fd = -1;
    155 	i = 0;
    156 	while (devs[i] != NULL) {
    157 		if ( (fd = open(devs[i++], O_WRONLY | O_NDELAY)) >= 0) {
    158 			break;
    159 		}
    160 	}
    161 	if (fd < 0) {
    162 		return 0;
    163 	}
    164 	close(fd);
    165 	fd = -1;
    166 	return 1;
    167 #endif
    168 }
    169 
    170 static int key_pressed[256];
    171 static int key_ismod[256];
    172 
    173 static void init_key_tracker(void) {
    174 	int i;
    175 	for (i = 0; i < 256; i++) {
    176 		key_pressed[i] = 0;
    177 		key_ismod[i] = 0;
    178 	}
    179 	i = lookup_code(XK_Shift_L);	if (0<=i && i<256) key_ismod[i] = 1;
    180 	i = lookup_code(XK_Shift_R);	if (0<=i && i<256) key_ismod[i] = 1;
    181 	i = lookup_code(XK_Control_L);	if (0<=i && i<256) key_ismod[i] = 1;
    182 	i = lookup_code(XK_Control_R);	if (0<=i && i<256) key_ismod[i] = 1;
    183 	i = lookup_code(XK_Alt_L);	if (0<=i && i<256) key_ismod[i] = 1;
    184 	i = lookup_code(XK_Alt_R);	if (0<=i && i<256) key_ismod[i] = 1;
    185 	i = lookup_code(XK_Meta_L);	if (0<=i && i<256) key_ismod[i] = 1;
    186 	i = lookup_code(XK_Meta_R);	if (0<=i && i<256) key_ismod[i] = 1;
    187 }
    188 
    189 static int mod_is_down(void) {
    190 	int i;
    191 	if (0) {key_is_down();}
    192 	for (i = 0; i < 256; i++) {
    193 		if (key_pressed[i] && key_ismod[i]) {
    194 			return 1;
    195 		}
    196 	}
    197 	return 0;
    198 }
    199 
    200 static int key_is_down(void) {
    201 	int i;
    202 	for (i = 0; i < 256; i++) {
    203 		if (key_pressed[i]) {
    204 			return 1;
    205 		}
    206 	}
    207 	return 0;
    208 }
    209 
    210 void shutdown_uinput(void) {
    211 #ifdef UINPUT_OK
    212 	if (fd >= 0) {
    213 		if (db) {
    214 			rfbLog("shutdown_uinput called on fd=%d\n", fd);
    215 		}
    216 		ioctl(fd, UI_DEV_DESTROY);
    217 		close(fd);
    218 		fd = -1;
    219 	}
    220 
    221 	/* close direct injection files too: */
    222 	if (direct_rel_fd >= 0) close(direct_rel_fd);
    223 	if (direct_abs_fd >= 0) close(direct_abs_fd);
    224 	if (direct_btn_fd >= 0) close(direct_btn_fd);
    225 	if (direct_key_fd >= 0) close(direct_key_fd);
    226 	direct_rel_fd = -1;
    227 	direct_abs_fd = -1;
    228 	direct_btn_fd = -1;
    229 	direct_key_fd = -1;
    230 #endif
    231 }
    232 
    233 /*
    234 grep BUS_ /usr/include/linux/input.h | awk '{print $2}' | perl -e 'while (<>) {chomp; print "#ifdef $_\n\t\tif(!strcmp(s, \"$_\"))\tudev.id.bustype = $_\n#endif\n"}'
    235  */
    236 static int get_bustype(char *s) {
    237 #ifdef UINPUT_OK
    238 
    239 	if (!s) return 0;
    240 
    241 #ifdef BUS_PCI
    242 	if(!strcmp(s, "BUS_PCI"))	return BUS_PCI;
    243 #endif
    244 #ifdef BUS_ISAPNP
    245 	if(!strcmp(s, "BUS_ISAPNP"))	return BUS_ISAPNP;
    246 #endif
    247 #ifdef BUS_USB
    248 	if(!strcmp(s, "BUS_USB"))	return BUS_USB;
    249 #endif
    250 #ifdef BUS_HIL
    251 	if(!strcmp(s, "BUS_HIL"))	return BUS_HIL;
    252 #endif
    253 #ifdef BUS_BLUETOOTH
    254 	if(!strcmp(s, "BUS_BLUETOOTH"))	return BUS_BLUETOOTH;
    255 #endif
    256 #ifdef BUS_VIRTUAL
    257 	if(!strcmp(s, "BUS_VIRTUAL"))	return BUS_VIRTUAL;
    258 #endif
    259 #ifdef BUS_ISA
    260 	if(!strcmp(s, "BUS_ISA"))	return BUS_ISA;
    261 #endif
    262 #ifdef BUS_I8042
    263 	if(!strcmp(s, "BUS_I8042"))	return BUS_I8042;
    264 #endif
    265 #ifdef BUS_XTKBD
    266 	if(!strcmp(s, "BUS_XTKBD"))	return BUS_XTKBD;
    267 #endif
    268 #ifdef BUS_RS232
    269 	if(!strcmp(s, "BUS_RS232"))	return BUS_RS232;
    270 #endif
    271 #ifdef BUS_GAMEPORT
    272 	if(!strcmp(s, "BUS_GAMEPORT"))	return BUS_GAMEPORT;
    273 #endif
    274 #ifdef BUS_PARPORT
    275 	if(!strcmp(s, "BUS_PARPORT"))	return BUS_PARPORT;
    276 #endif
    277 #ifdef BUS_AMIGA
    278 	if(!strcmp(s, "BUS_AMIGA"))	return BUS_AMIGA;
    279 #endif
    280 #ifdef BUS_ADB
    281 	if(!strcmp(s, "BUS_ADB"))	return BUS_ADB;
    282 #endif
    283 #ifdef BUS_I2C
    284 	if(!strcmp(s, "BUS_I2C"))	return BUS_I2C;
    285 #endif
    286 #ifdef BUS_HOST
    287 	if(!strcmp(s, "BUS_HOST"))	return BUS_HOST;
    288 #endif
    289 #ifdef BUS_GSC
    290 	if(!strcmp(s, "BUS_GSC"))	return BUS_GSC;
    291 #endif
    292 #ifdef BUS_ATARI
    293 	if(!strcmp(s, "BUS_ATARI"))	return BUS_ATARI;
    294 #endif
    295 	if (atoi(s) > 0) {
    296 		return atoi(s);
    297 	}
    298 
    299 #endif
    300 	return 0;
    301 }
    302 
    303 static void load_tslib_cal(void) {
    304 	FILE *f;
    305 	char line[1024], *p;
    306 	int i;
    307 
    308 	/* /etc/pointercal -528 33408 -3417516 -44200 408 40292028 56541 */
    309 
    310 	/* this is the identity transformation: */
    311 	a[0] = 1.0;
    312 	a[1] = 0.0;
    313 	a[2] = 0.0;
    314 	a[3] = 0.0;
    315 	a[4] = 1.0;
    316 	a[5] = 0.0;
    317 	a[6] = 1.0;
    318 
    319 	if (tslib_cal == NULL) {
    320 		return;
    321 	}
    322 
    323 	rfbLog("load_tslib_cal: reading %s\n", tslib_cal);
    324 	f = fopen(tslib_cal, "r");
    325 	if (f == NULL) {
    326 		rfbLogPerror("load_tslib_cal: fopen");
    327 		clean_up_exit(1);
    328 	}
    329 
    330 	if (fgets(line, sizeof(line), f) == NULL) {
    331 		rfbLogPerror("load_tslib_cal: fgets");
    332 		clean_up_exit(1);
    333 	}
    334 	fclose(f);
    335 
    336 	p = strtok(line, " \t");
    337 	i = 0;
    338 	while (p) {
    339 		a[i] = (double) atoi(p);
    340 		rfbLog("load_tslib_cal: a[%d] %.3f\n", i, a[i]);
    341 		p = strtok(NULL, " \t");
    342 		i++;
    343 		if (i >= 7) {
    344 			break;
    345 		}
    346 	}
    347 	if (i != 7) {
    348 		rfbLog("load_tslib_cal: invalid tslib file format: i=%d %s\n",
    349 		    i, tslib_cal);
    350 		clean_up_exit(1);
    351 	}
    352 }
    353 
    354 
    355 int initialize_uinput(void) {
    356 #ifndef UINPUT_OK
    357 	return 0;
    358 #else
    359 	int i;
    360 	char *s;
    361 	struct uinput_user_dev udev;
    362 
    363 	if (fd >= 0) {
    364 		shutdown_uinput();
    365 	}
    366 	fd = -1;
    367 
    368 	if (getenv("X11VNC_UINPUT_DEBUG")) {
    369 		db = atoi(getenv("X11VNC_UINPUT_DEBUG"));
    370 		rfbLog("set uinput debug to: %d\n", db);
    371 	}
    372 
    373 	if (tslib_cal) {
    374 		load_tslib_cal();
    375 	}
    376 
    377 	init_key_tracker();
    378 
    379 	if (uinput_dev) {
    380 		if (!strcmp(uinput_dev, "nouinput")) {
    381 			rfbLog("initialize_uinput: not creating uinput device.\n");
    382 			return 1;
    383 		} else {
    384 			fd = open(uinput_dev, O_WRONLY | O_NDELAY);
    385 			rfbLog("initialize_uinput: using: %s %d\n", uinput_dev, fd);
    386 		}
    387 	} else {
    388 		i = 0;
    389 		while (devs[i] != NULL) {
    390 			if ( (fd = open(devs[i], O_WRONLY | O_NDELAY)) >= 0) {
    391 				rfbLog("initialize_uinput: using: %s %d\n",
    392 				    devs[i], fd);
    393 				break;
    394 			}
    395 			i++;
    396 		}
    397 	}
    398 	if (fd < 0) {
    399 		rfbLog("initialize_uinput: could not open an uinput device.\n");
    400 		rfbLogPerror("open");
    401 		if (direct_rel_fd < 0 && direct_abs_fd < 0 && direct_btn_fd < 0 && direct_key_fd < 0) {
    402 			clean_up_exit(1);
    403 		}
    404 		return 1;
    405 	}
    406 
    407 	memset(&udev, 0, sizeof(udev));
    408 
    409 	strncpy(udev.name, "x11vnc injector", UINPUT_MAX_NAME_SIZE);
    410 
    411 	s = getenv("X11VNC_UINPUT_BUS");
    412 	if (s) {
    413 		udev.id.bustype = get_bustype(s);
    414 	} else if (0) {
    415 		udev.id.bustype = BUS_USB;
    416 	}
    417 
    418 	s = getenv("X11VNC_UINPUT_VERSION");
    419 	if (s) {
    420 		udev.id.version = atoi(s);
    421 	} else if (0) {
    422 		udev.id.version = 4;
    423 	}
    424 
    425 	ioctl(fd, UI_SET_EVBIT, EV_REL);
    426 	ioctl(fd, UI_SET_RELBIT, REL_X);
    427 	ioctl(fd, UI_SET_RELBIT, REL_Y);
    428 
    429 	ioctl(fd, UI_SET_EVBIT, EV_KEY);
    430 
    431 	ioctl(fd, UI_SET_EVBIT, EV_SYN);
    432 
    433 	for (i=0; i < 256; i++) {
    434 		ioctl(fd, UI_SET_KEYBIT, i);
    435 	}
    436 
    437 	ioctl(fd, UI_SET_KEYBIT, BTN_MOUSE);
    438 	ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
    439 	ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
    440 	ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
    441 	ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
    442 	ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
    443 
    444 	if (uinput_touchscreen) {
    445 		ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
    446 		rfbLog("uinput: touchscreen enabled.\n");
    447 	}
    448 	if (uinput_touchscreen || uinput_abs) {
    449 		int gw = abs_x, gh = abs_y;
    450 		if (! gw || ! gh) {
    451 			gw = fb_x; gh = fb_y;
    452 		}
    453 		if (! gw || ! gh) {
    454 			gw = dpy_x; gh = dpy_y;
    455 		}
    456 		abs_x = gw;
    457 		abs_y = gh;
    458 		ioctl(fd, UI_SET_EVBIT, EV_ABS);
    459 		ioctl(fd, UI_SET_ABSBIT, ABS_X);
    460 		ioctl(fd, UI_SET_ABSBIT, ABS_Y);
    461 		udev.absmin[ABS_X] = 0;
    462 		udev.absmax[ABS_X] = gw;
    463 		udev.absfuzz[ABS_X] = 0;
    464 		udev.absflat[ABS_X] = 0;
    465 		udev.absmin[ABS_Y] = 0;
    466 		udev.absmax[ABS_Y] = gh;
    467 		udev.absfuzz[ABS_Y] = 0;
    468 		udev.absflat[ABS_Y] = 0;
    469 		rfbLog("uinput: absolute pointer enabled at %dx%d.\n", abs_x, abs_y);
    470 		set_uinput_accel_xy(1.0, 1.0);
    471 	}
    472 
    473 	if (db) {
    474 		rfbLog("   udev.name:             %s\n", udev.name);
    475 		rfbLog("   udev.id.bustype:       %d\n", udev.id.bustype);
    476 		rfbLog("   udev.id.vendor:        %d\n", udev.id.vendor);
    477 		rfbLog("   udev.id.product:       %d\n", udev.id.product);
    478 		rfbLog("   udev.id.version:       %d\n", udev.id.version);
    479 		rfbLog("   udev.ff_effects_max:   %d\n", udev.ff_effects_max);
    480 		rfbLog("   udev.absmin[ABS_X]:    %d\n", udev.absmin[ABS_X]);
    481 		rfbLog("   udev.absmax[ABS_X]:    %d\n", udev.absmax[ABS_X]);
    482 		rfbLog("   udev.absfuzz[ABS_X]:   %d\n", udev.absfuzz[ABS_X]);
    483 		rfbLog("   udev.absflat[ABS_X]:   %d\n", udev.absflat[ABS_X]);
    484 		rfbLog("   udev.absmin[ABS_Y]:    %d\n", udev.absmin[ABS_Y]);
    485 		rfbLog("   udev.absmax[ABS_Y]:    %d\n", udev.absmax[ABS_Y]);
    486 		rfbLog("   udev.absfuzz[ABS_Y]:   %d\n", udev.absfuzz[ABS_Y]);
    487 		rfbLog("   udev.absflat[ABS_Y]:   %d\n", udev.absflat[ABS_Y]);
    488 	}
    489 
    490 	write(fd, &udev, sizeof(udev));
    491 
    492 	if (ioctl(fd, UI_DEV_CREATE) != 0) {
    493 		rfbLog("ioctl(fd, UI_DEV_CREATE) failed.\n");
    494 		rfbLogPerror("ioctl");
    495 		close(fd);
    496 		clean_up_exit(1);
    497 	}
    498 	return 1;
    499 #endif
    500 }
    501 
    502 /* these defaults are based on qt-embedded 7/2006 */
    503 static double fudge_x = 0.5;	/* accel=2.0 */
    504 static double fudge_y = 0.5;
    505 
    506 static int thresh = 5;
    507 static int thresh_or = 1;
    508 
    509 static double resid_x = 0.0;
    510 static double resid_y = 0.0;
    511 
    512 static double zero_delay = 0.15;
    513 static double last_button_click = 0.0;
    514 
    515 static int uinput_always = 0;
    516 
    517 static void set_uinput_accel_xy(double fx, double fy) {
    518 	fudge_x = 1.0/fx;
    519 	fudge_y = 1.0/fy;
    520 	rfbLog("set_uinput_accel:  fx=%.5f fy=%.5f\n", fx, fy);
    521 	rfbLog("set_uinput_accel:  ix=%.5f iy=%.5f\n", fudge_x, fudge_y);
    522 }
    523 
    524 static char *uinput_accel_str = NULL;
    525 static char *uinput_thresh_str = NULL;
    526 
    527 int set_uinput_accel(char *str) {
    528 	double fx, fy;
    529 	rfbLog("set_uinput_accel: str=%s\n", str);
    530 	if (sscanf(str, "%lf+%lf", &fx, &fy) == 2) {
    531 		set_uinput_accel_xy(fx, fy);
    532 	} else if (sscanf(str, "%lf", &fx) == 1) {
    533 		set_uinput_accel_xy(fx, fx);
    534 	} else {
    535 		rfbLog("invalid UINPUT accel= option: %s\n", str);
    536 		return 0;
    537 	}
    538 	if (uinput_accel_str) {
    539 		free(uinput_accel_str);
    540 	}
    541 	uinput_accel_str = strdup(str);
    542 	return 1;
    543 }
    544 
    545 int set_uinput_thresh(char *str) {
    546 	rfbLog("set_uinput_thresh: str=%s\n", str);
    547 	if (str[0] == '+') {
    548 		thresh_or = 0;
    549 	}
    550 	thresh = atoi(str);
    551 	if (uinput_thresh_str) {
    552 		free(uinput_thresh_str);
    553 	}
    554 	uinput_thresh_str = strdup(str);
    555 	return 1;
    556 }
    557 
    558 void set_uinput_reset(int ms) {
    559 	zero_delay = (double) ms/1000.;
    560 	rfbLog("set_uinput_reset: %d\n", ms);
    561 }
    562 
    563 void set_uinput_always(int a) {
    564 	uinput_always = a;
    565 }
    566 
    567 void set_uinput_touchscreen(int b) {
    568 	uinput_touchscreen = b;
    569 }
    570 
    571 void set_uinput_abs(int b) {
    572 	uinput_abs = b;
    573 }
    574 
    575 char *get_uinput_accel(void) {
    576 	return uinput_accel_str;
    577 }
    578 char *get_uinput_thresh(void) {
    579 	return uinput_thresh_str;
    580 }
    581 int get_uinput_reset(void) {
    582 	return (int) (1000 * zero_delay);
    583 }
    584 
    585 int get_uinput_always(void) {
    586 	return uinput_always;
    587 }
    588 
    589 int get_uinput_touchscreen(void) {
    590 	return uinput_touchscreen;
    591 }
    592 
    593 int get_uinput_abs(void) {
    594 	return uinput_abs;
    595 }
    596 
    597 void parse_uinput_str(char *in) {
    598 	char *p, *q, *str = strdup(in);
    599 
    600 	if (injectable) {
    601 		free(injectable);
    602 		injectable = strdup("KMB");
    603 	}
    604 
    605 	uinput_touchscreen = 0;
    606 	uinput_abs = 0;
    607 	abs_x = abs_y = 0;
    608 
    609 	if (tslib_cal) {
    610 		free(tslib_cal);
    611 		tslib_cal = NULL;
    612 	}
    613 
    614 	p = strtok(str, ",");
    615 	while (p) {
    616 		if (p[0] == '/') {
    617 			if (uinput_dev) {
    618 				free(uinput_dev);
    619 			}
    620 			uinput_dev = strdup(p);
    621 		} else if (strstr(p, "nouinput") == p) {
    622 			if (uinput_dev) {
    623 				free(uinput_dev);
    624 			}
    625 			uinput_dev = strdup(p);
    626 		} else if (strstr(p, "accel=") == p) {
    627 			q = p + strlen("accel=");
    628 			if (! set_uinput_accel(q)) {
    629 				clean_up_exit(1);
    630 			}
    631 		} else if (strstr(p, "thresh=") == p) {
    632 			q = p + strlen("thresh=");
    633 			set_uinput_thresh(q);
    634 
    635 		} else if (strstr(p, "reset=") == p) {
    636 			int n = atoi(p + strlen("reset="));
    637 			set_uinput_reset(n);
    638 		} else if (strstr(p, "always=") == p) {
    639 			int n = atoi(p + strlen("always="));
    640 			set_uinput_always(n);
    641 		} else if (strpbrk(p, "KMB") == p) {
    642 			if (injectable) {
    643 				free(injectable);
    644 			}
    645 			injectable = strdup(p);
    646 		} else if (strstr(p, "touch_always=") == p) {
    647 			touch_always = atoi(p + strlen("touch_always="));
    648 		} else if (strstr(p, "btn_touch=") == p) {
    649 			btn_touch = atoi(p + strlen("btn_touch="));
    650 		} else if (strstr(p, "dragskip=") == p) {
    651 			dragskip = atoi(p + strlen("dragskip="));
    652 		} else if (strstr(p, "touch") == p) {
    653 			int gw, gh;
    654 			q = strchr(p, '=');
    655 			set_uinput_touchscreen(1);
    656 			set_uinput_abs(1);
    657 			if (q && sscanf(q+1, "%dx%d", &gw, &gh) == 2) {
    658 				abs_x = gw;
    659 				abs_y = gh;
    660 			}
    661 		} else if (strstr(p, "abs") == p) {
    662 			int gw, gh;
    663 			q = strchr(p, '=');
    664 			set_uinput_abs(1);
    665 			if (q && sscanf(q+1, "%dx%d", &gw, &gh) == 2) {
    666 				abs_x = gw;
    667 				abs_y = gh;
    668 			}
    669 		} else if (strstr(p, "pressure=") == p) {
    670 			touch_pressure = atoi(p + strlen("pressure="));
    671 		} else if (strstr(p, "direct_rel=") == p) {
    672 			direct_rel_fd = open(p+strlen("direct_rel="), O_WRONLY);
    673 			if (direct_rel_fd < 0) {
    674 				rfbLogPerror("uinput: direct_rel open");
    675 			} else {
    676 				rfbLog("uinput: opened: %s fd=%d\n", p, direct_rel_fd);
    677 			}
    678 		} else if (strstr(p, "direct_abs=") == p) {
    679 			direct_abs_fd = open(p+strlen("direct_abs="), O_WRONLY);
    680 			if (direct_abs_fd < 0) {
    681 				rfbLogPerror("uinput: direct_abs open");
    682 			} else {
    683 				rfbLog("uinput: opened: %s fd=%d\n", p, direct_abs_fd);
    684 			}
    685 		} else if (strstr(p, "direct_btn=") == p) {
    686 			direct_btn_fd = open(p+strlen("direct_btn="), O_WRONLY);
    687 			if (direct_btn_fd < 0) {
    688 				rfbLogPerror("uinput: direct_btn open");
    689 			} else {
    690 				rfbLog("uinput: opened: %s fd=%d\n", p, direct_btn_fd);
    691 			}
    692 		} else if (strstr(p, "direct_key=") == p) {
    693 			direct_key_fd = open(p+strlen("direct_key="), O_WRONLY);
    694 			if (direct_key_fd < 0) {
    695 				rfbLogPerror("uinput: direct_key open");
    696 			} else {
    697 				rfbLog("uinput: opened: %s fd=%d\n", p, direct_key_fd);
    698 			}
    699 		} else if (strstr(p, "tslib_cal=") == p) {
    700 			tslib_cal = strdup(p+strlen("tslib_cal="));
    701 		} else {
    702 			rfbLog("invalid UINPUT option: %s\n", p);
    703 			clean_up_exit(1);
    704 		}
    705 		p = strtok(NULL, ",");
    706 	}
    707 	free(str);
    708 }
    709 
    710 static void ptr_move(int dx, int dy) {
    711 #ifdef UINPUT_OK
    712 	struct input_event ev;
    713 	int d = direct_rel_fd < 0 ? fd : direct_rel_fd;
    714 
    715 	if (injectable && strchr(injectable, 'M') == NULL) {
    716 		return;
    717 	}
    718 
    719 	memset(&ev, 0, sizeof(ev));
    720 
    721 	if (db) fprintf(stderr, "ptr_move(%d, %d) fd=%d\n", dx, dy, d);
    722 
    723 	gettimeofday(&ev.time, NULL);
    724 	ev.type = EV_REL;
    725 	ev.code = REL_Y;
    726 	ev.value = dy;
    727 	write(d, &ev, sizeof(ev));
    728 
    729 	ev.type = EV_REL;
    730 	ev.code = REL_X;
    731 	ev.value = dx;
    732 	write(d, &ev, sizeof(ev));
    733 
    734 	ev.type = EV_SYN;
    735 	ev.code = SYN_REPORT;
    736 	ev.value = 0;
    737 	write(d, &ev, sizeof(ev));
    738 #else
    739 	if (!dx || !dy) {}
    740 #endif
    741 }
    742 
    743 static void apply_tslib(int *x, int *y) {
    744 	double x1 = *x, y1 = *y, x2, y2;
    745 
    746 	/* this is the inverse of the tslib linear transform: */
    747 	x2 = (a[4] * (a[6] * x1 - a[2]) - a[1] * (a[6] * y1 - a[5]))/(a[4]*a[0] - a[1]*a[3]);
    748 	y2 = (a[0] * (a[6] * y1 - a[5]) - a[3] * (a[6] * x1 - a[2]))/(a[4]*a[0] - a[1]*a[3]);
    749 
    750 	*x = (int) x2;
    751 	*y = (int) y2;
    752 }
    753 
    754 
    755 static void ptr_abs(int x, int y, int p) {
    756 #ifdef UINPUT_OK
    757 	struct input_event ev;
    758 	int x0, y0;
    759 	int d = direct_abs_fd < 0 ? fd : direct_abs_fd;
    760 
    761 	if (injectable && strchr(injectable, 'M') == NULL) {
    762 		return;
    763 	}
    764 
    765 	memset(&ev, 0, sizeof(ev));
    766 
    767 	x0 = x;
    768 	y0 = y;
    769 
    770 	if (tslib_cal) {
    771 		apply_tslib(&x, &y);
    772 	}
    773 
    774 	if (db) fprintf(stderr, "ptr_abs(%d, %d => %d %d, p=%d) fd=%d\n", x0, y0, x, y, p, d);
    775 
    776 	gettimeofday(&ev.time, NULL);
    777 	ev.type = EV_ABS;
    778 	ev.code = ABS_Y;
    779 	ev.value = y;
    780 	write(d, &ev, sizeof(ev));
    781 
    782 	ev.type = EV_ABS;
    783 	ev.code = ABS_X;
    784 	ev.value = x;
    785 	write(d, &ev, sizeof(ev));
    786 
    787 	if (p >= 0) {
    788 		ev.type = EV_ABS;
    789 		ev.code = ABS_PRESSURE;
    790 		ev.value = p;
    791 		write(d, &ev, sizeof(ev));
    792 	}
    793 
    794 	ev.type = EV_SYN;
    795 	ev.code = SYN_REPORT;
    796 	ev.value = 0;
    797 	write(d, &ev, sizeof(ev));
    798 #else
    799 	if (!x || !y) {}
    800 #endif
    801 }
    802 
    803 static int inside_thresh(int dx, int dy, int thr) {
    804 	if (thresh_or) {
    805 		/* this is peeking at qt-embedded qmouse_qws.cpp */
    806 		if (nabs(dx) <= thresh && nabs(dy) <= thr) {
    807 			return 1;
    808 		}
    809 	} else {
    810 		/* this is peeking at xfree/xorg xf86Xinput.c */
    811 		if (nabs(dx) + nabs(dy) < thr) {
    812 			return 1;
    813 		}
    814 	}
    815 	return 0;
    816 }
    817 
    818 static void ptr_rel(int dx, int dy) {
    819 	int dxf, dyf, nx, ny, k;
    820 	int accel, thresh_high, thresh_mid;
    821 	double fx, fy;
    822 	static int try_threshes = -1;
    823 
    824 	if (try_threshes < 0) {
    825 		if (getenv("X11VNC_UINPUT_THRESHOLDS")) {
    826 			try_threshes = 1;
    827 		} else {
    828 			try_threshes = 0;
    829 		}
    830 	}
    831 
    832 	if (try_threshes) {
    833 		thresh_high = (int) ( (double) thresh/fudge_x );
    834 		thresh_mid =  (int) ( (double) (thresh + thresh_high) / 2.0 );
    835 
    836 		if (thresh_mid <= thresh) {
    837 			thresh_mid = thresh + 1;
    838 		}
    839 		if (thresh_high <= thresh_mid) {
    840 			thresh_high = thresh_mid + 1;
    841 		}
    842 
    843 		if (inside_thresh(dx, dy, thresh)) {
    844 			accel = 0;
    845 		} else {
    846 			accel = 1;
    847 		}
    848 		nx = nabs(dx);
    849 		ny = nabs(dy);
    850 
    851 	} else {
    852 		accel = 1;
    853 		thresh_high = 0;
    854 		nx = ny = 1;
    855 	}
    856 
    857 	if (accel && nx + ny > 0 ) {
    858 		if (thresh_high > 0 && inside_thresh(dx, dy, thresh_high)) {
    859 			double alpha, t;
    860 			/* XXX */
    861 			if (1 || inside_thresh(dx, dy, thresh_mid)) {
    862 				t = thresh;
    863 				accel = 2;
    864 			} else {
    865 				accel = 3;
    866 				t = thresh_high;
    867 			}
    868 			if (thresh_or) {
    869 				if (nx > ny) {
    870 					fx = t;
    871 					fy =  ((double) ny / (double) nx) * t;
    872 				} else {
    873 					fx =  ((double) nx / (double) ny) * t;
    874 					fy = t;
    875 				}
    876 				dxf = (int) fx;
    877 				dyf = (int) fy;
    878 				fx = dx;
    879 				fy = dy;
    880 
    881 			} else {
    882 				if (t > 1) {
    883 					/* XXX */
    884 					t = t - 1.0;
    885 				}
    886 				alpha = t/(nx + ny);
    887 				fx = alpha * dx;
    888 				fy = alpha * dy;
    889 				dxf = (int) fx;
    890 				dyf = (int) fy;
    891 				fx = dx;
    892 				fy = dy;
    893 			}
    894 		} else {
    895 			fx = fudge_x * (double) dx;
    896 			fy = fudge_y * (double) dy;
    897 			dxf = (int) fx;
    898 			dyf = (int) fy;
    899 		}
    900 	} else {
    901 		fx = dx;
    902 		fy = dy;
    903 		dxf = dx;
    904 		dyf = dy;
    905 	}
    906 
    907 	if (db > 1) fprintf(stderr, "old dx dy: %d %d\n", dx, dy);
    908 	if (db > 1) fprintf(stderr, "new dx dy: %d %d  accel: %d\n", dxf, dyf, accel);
    909 
    910 	ptr_move(dxf, dyf);
    911 
    912 	resid_x += fx - dxf;
    913 	resid_y += fy - dyf;
    914 
    915 	for (k = 0; k < 4; k++) {
    916 		if (resid_x <= -1.0 || resid_x >= 1.0 || resid_y <= -1.0 || resid_y >= 1.0) {
    917 			dxf = 0;
    918 			dyf = 0;
    919 			if (resid_x >= 1.0) {
    920 				dxf = (int) resid_x;
    921 				dxf = 1;
    922 			} else if (resid_x <= -1.0)  {
    923 				dxf = -((int) (-resid_x));
    924 				dxf = -1;
    925 			}
    926 			resid_x -= dxf;
    927 			if (resid_y >= 1.0) {
    928 				dyf = (int) resid_y;
    929 				dyf = 1;
    930 			} else if (resid_y <= -1.0)  {
    931 				dyf = -((int) (-resid_y));
    932 				dyf = -1;
    933 			}
    934 			resid_y -= dyf;
    935 
    936 			if (db > 1) fprintf(stderr, "*%s resid: dx dy: %d %d  %f %f\n", accel > 1 ? "*" : " ", dxf, dyf, resid_x, resid_y);
    937 if (0) {usleep(100*1000) ;}
    938 			ptr_move(dxf, dyf);
    939 		}
    940 	}
    941 }
    942 
    943 static void button_click(int down, int btn) {
    944 #ifdef UINPUT_OK
    945 	struct input_event ev;
    946 	int d = direct_btn_fd < 0 ? fd : direct_btn_fd;
    947 
    948 	if (injectable && strchr(injectable, 'B') == NULL) {
    949 		return;
    950 	}
    951 
    952 	if (db) fprintf(stderr, "button_click: btn %d %s fd=%d\n", btn, down ? "down" : "up", d);
    953 
    954 	memset(&ev, 0, sizeof(ev));
    955 	gettimeofday(&ev.time, NULL);
    956 	ev.type = EV_KEY;
    957 	ev.value = down;
    958 
    959 	if (uinput_touchscreen) {
    960 		ev.code = BTN_TOUCH;
    961 		if (db) fprintf(stderr, "set code to BTN_TOUCH\n");
    962 	} else if (btn == 1) {
    963 		ev.code = BTN_LEFT;
    964 	} else if (btn == 2) {
    965 		ev.code = BTN_MIDDLE;
    966 	} else if (btn == 3) {
    967 		ev.code = BTN_RIGHT;
    968 	} else if (btn == 4) {
    969 		ev.code = BTN_FORWARD;
    970 	} else if (btn == 5) {
    971 		ev.code = BTN_BACK;
    972 	} else {
    973 		return;
    974 	}
    975 
    976 	write(d, &ev, sizeof(ev));
    977 
    978 	ev.type = EV_SYN;
    979 	ev.code = SYN_REPORT;
    980 	ev.value = 0;
    981 	write(d, &ev, sizeof(ev));
    982 
    983 	last_button_click = dnow();
    984 #else
    985 	if (!down || !btn) {}
    986 #endif
    987 }
    988 
    989 
    990 void uinput_pointer_command(int mask, int x, int y, rfbClientPtr client) {
    991 	static int last_x = -1, last_y = -1, last_mask = -1;
    992 	static double last_zero = 0.0;
    993 	allowed_input_t input;
    994 	int do_reset, reset_lower_right = 1;
    995 	double now;
    996 	static int first = 1;
    997 
    998 	if (first) {
    999 		if (getenv("RESET_ALWAYS")) {
   1000 			set_uinput_always(1);
   1001 		} else {
   1002 			set_uinput_always(0);
   1003 		}
   1004 	}
   1005 	first = 0;
   1006 
   1007 	if (db) fprintf(stderr, "uinput_pointer_command: %d %d - %d\n", x, y, mask);
   1008 
   1009 	if (view_only) {
   1010 		return;
   1011 	}
   1012 	get_allowed_input(client, &input);
   1013 
   1014 	now = dnow();
   1015 
   1016 	do_reset = 1;
   1017 	if (mask || bmask) {
   1018 		do_reset = 0;	/* do not do reset if mouse button down */
   1019 	} else if (! input.motion) {
   1020 		do_reset = 0;
   1021 	} else if (now < last_zero + zero_delay) {
   1022 		do_reset = 0;
   1023 	}
   1024 	if (do_reset) {
   1025 		if (mod_is_down()) {
   1026 			do_reset = 0;
   1027 		} else if (now < last_button_click + 0.25) {
   1028 			do_reset = 0;
   1029 		}
   1030 	}
   1031 
   1032 	if (uinput_always && !mask && !bmask && input.motion) {
   1033 		do_reset = 1;
   1034 	}
   1035 	if (uinput_abs) {
   1036 		do_reset = 0;
   1037 	}
   1038 
   1039 	if (do_reset) {
   1040 		static int first = 1;
   1041 
   1042 		if (zero_delay > 0.0 || first) {
   1043 			/* try to push it to 0,0 */
   1044 			int tx, ty, bigjump = 1;
   1045 
   1046 			if (reset_lower_right) {
   1047 				tx = fudge_x * (dpy_x - last_x);
   1048 				ty = fudge_y * (dpy_y - last_y);
   1049 			} else {
   1050 				tx = fudge_x * last_x;
   1051 				ty = fudge_y * last_y;
   1052 			}
   1053 
   1054 			tx += 50;
   1055 			ty += 50;
   1056 
   1057 			if (bigjump) {
   1058 				if (reset_lower_right) {
   1059 					ptr_move(0, +ty);
   1060 					usleep(2*1000);
   1061 					ptr_move(+tx, +ty);
   1062 					ptr_move(+tx, +ty);
   1063 				} else {
   1064 					ptr_move(0, -ty);
   1065 					usleep(2*1000);
   1066 					ptr_move(-tx, -ty);
   1067 					ptr_move(-tx, -ty);
   1068 				}
   1069 			} else {
   1070 				int i, step, n = 20;
   1071 				step = dpy_x / n;
   1072 
   1073 				if (step < 100) step = 100;
   1074 
   1075 				for (i=0; i < n; i++)  {
   1076 					if (reset_lower_right) {
   1077 						ptr_move(+step, +step);
   1078 					} else {
   1079 						ptr_move(-step, -step);
   1080 					}
   1081 				}
   1082 				for (i=0; i < n; i++)  {
   1083 					if (reset_lower_right) {
   1084 						ptr_move(+1, +1);
   1085 					} else {
   1086 						ptr_move(-1, -1);
   1087 					}
   1088 				}
   1089 			}
   1090 			if (db) {
   1091 				if (reset_lower_right) {
   1092 					fprintf(stderr, "uinput_pointer_command: reset -> (W,H) (%d,%d)  [%d,%d]\n", x, y, tx, ty);
   1093 				} else {
   1094 					fprintf(stderr, "uinput_pointer_command: reset -> (0,0) (%d,%d)  [%d,%d]\n", x, y, tx, ty);
   1095 				}
   1096 			}
   1097 
   1098 			/* rest a bit for system to absorb the change */
   1099 			if (uinput_always) {
   1100 				static double last_sleep = 0.0;
   1101 				double nw = dnow(), delay = zero_delay;
   1102 				if (delay <= 0.0) delay = 0.1;
   1103 				if (nw > last_sleep + delay) {
   1104 					usleep(10*1000);
   1105 					last_sleep = nw;
   1106 				} else {
   1107 					usleep(1*1000);
   1108 				}
   1109 
   1110 			} else {
   1111 				usleep(30*1000);
   1112 			}
   1113 
   1114 			/* now jump back out */
   1115 			if (reset_lower_right) {
   1116 				ptr_rel(x - dpy_x, y - dpy_y);
   1117 			} else {
   1118 				ptr_rel(x, y);
   1119 			}
   1120 			if (1) {usleep(10*1000) ;}
   1121 
   1122 			last_x = x;
   1123 			last_y = y;
   1124 			resid_x = 0.0;
   1125 			resid_y = 0.0;
   1126 
   1127 			first = 0;
   1128 		}
   1129 		last_zero = dnow();
   1130 	}
   1131 
   1132 	if (input.motion) {
   1133 		if (x != last_x || y != last_y) {
   1134 			if (uinput_touchscreen) {
   1135 				;
   1136 			} else if (uinput_abs) {
   1137 				ptr_abs(x, y, -1);
   1138 			} else {
   1139 				ptr_rel(x - last_x, y - last_y);
   1140 			}
   1141 			last_x = x;
   1142 			last_y = y;
   1143 		}
   1144 	}
   1145 
   1146 	if (! input.button) {
   1147 		return;
   1148 	}
   1149 
   1150 	if (last_mask < 0) {
   1151 		last_mask = mask;
   1152 	}
   1153 
   1154 	if (db > 2) {
   1155 		fprintf(stderr, "mask:        %s\n", bitprint(mask, 16));
   1156 		fprintf(stderr, "bmask:       %s\n", bitprint(bmask, 16));
   1157 		fprintf(stderr, "last_mask:   %s\n", bitprint(last_mask, 16));
   1158 		fprintf(stderr, "button_mask: %s\n", bitprint(button_mask, 16));
   1159 	}
   1160 
   1161 	if (uinput_touchscreen) {
   1162 		if (!btn_touch) {
   1163 			static int down_count = 0;
   1164 			int p = touch_pressure >=0 ? touch_pressure : 0;
   1165 			if (!last_mask && !mask) {
   1166 				if (touch_always) {
   1167 					ptr_abs(last_x, last_y, 0);
   1168 				}
   1169 			} else if (!last_mask && mask) {
   1170 				ptr_abs(last_x, last_y, p);
   1171 				down_count = 0;
   1172 			} else if (last_mask && !mask) {
   1173 				ptr_abs(last_x, last_y, 0);
   1174 			} else if (last_mask && mask) {
   1175 				down_count++;
   1176 				if (dragskip > 0) {
   1177 					if (down_count % dragskip == 0) {
   1178 						ptr_abs(last_x, last_y, p);
   1179 					}
   1180 				} else {
   1181 					ptr_abs(last_x, last_y, p);
   1182 				}
   1183 			}
   1184 		} else {
   1185 			if (!last_mask && !mask) {
   1186 				if (touch_always) {
   1187 					ptr_abs(last_x, last_y, 0);
   1188 				}
   1189 			} else if (!last_mask && mask) {
   1190 				ptr_abs(last_x, last_y, 0);
   1191 				button_click(1, 0);
   1192 			} else if (last_mask && !mask) {
   1193 				ptr_abs(last_x, last_y, 0);
   1194 				button_click(0, 0);
   1195 			} else if (last_mask && mask) {
   1196 				;
   1197 			}
   1198 		}
   1199 		last_mask = mask;
   1200 	} else if (mask != last_mask) {
   1201 		int i;
   1202 		for (i=1; i <= MAX_BUTTONS; i++) {
   1203 			int down, b = 1 << (i-1);
   1204 			if ( (last_mask & b) == (mask & b)) {
   1205 				continue;
   1206 			}
   1207 			if (mask & b) {
   1208 				down = 1;
   1209 			} else {
   1210 				down = 0;
   1211 			}
   1212 			button_click(down, i);
   1213 		}
   1214 		if (mask && uinput_abs && touch_pressure >= 0) {
   1215 			ptr_abs(last_x, last_y, touch_pressure);
   1216 		}
   1217 		last_mask = mask;
   1218 	}
   1219 	bmask = mask;
   1220 }
   1221 
   1222 void uinput_key_command(int down, int keysym, rfbClientPtr client) {
   1223 #ifdef UINPUT_OK
   1224 	struct input_event ev;
   1225 	int scancode;
   1226 	allowed_input_t input;
   1227 	int d = direct_key_fd < 0 ? fd : direct_key_fd;
   1228 
   1229 	if (injectable && strchr(injectable, 'K') == NULL) {
   1230 		return;
   1231 	}
   1232 	if (view_only) {
   1233 		return;
   1234 	}
   1235 	get_allowed_input(client, &input);
   1236 	if (! input.keystroke) {
   1237 		return;
   1238 	}
   1239 
   1240 	scancode = lookup_code(keysym);
   1241 
   1242 	if (scancode < 0) {
   1243 		return;
   1244 	}
   1245 	if (db) fprintf(stderr, "uinput_key_command: %d -> %d %s fd=%d\n", keysym, scancode, down ? "down" : "up", d);
   1246 
   1247 	memset(&ev, 0, sizeof(ev));
   1248 	gettimeofday(&ev.time, NULL);
   1249 	ev.type = EV_KEY;
   1250 	ev.code = (unsigned char) scancode;
   1251 	ev.value = down;
   1252 
   1253 	write(d, &ev, sizeof(ev));
   1254 
   1255 	ev.type = EV_SYN;
   1256 	ev.code = SYN_REPORT;
   1257 	ev.value = 0;
   1258 	write(d, &ev, sizeof(ev));
   1259 
   1260 	if (0 <= scancode && scancode < 256) {
   1261 		key_pressed[scancode] = down ? 1 : 0;
   1262 	}
   1263 #else
   1264 	if (!down || !keysym || !client) {}
   1265 #endif
   1266 }
   1267 
   1268 #if 0
   1269   grep 'case XK_' x0vnc.c | sed -e 's/case /$key_lookup{/' -e 's/:/}/' -e 's/return /= $/'
   1270 #endif
   1271 
   1272 static int lookup_code(int keysym) {
   1273 
   1274 	if (keysym == NoSymbol) {
   1275 		return -1;
   1276 	}
   1277 
   1278 	switch(keysym) {
   1279 #ifdef UINPUT_OK
   1280 	case XK_Escape:	return KEY_ESC;
   1281 	case XK_1:		return KEY_1;
   1282 	case XK_2:		return KEY_2;
   1283 	case XK_3:		return KEY_3;
   1284 	case XK_4:		return KEY_4;
   1285 	case XK_5:		return KEY_5;
   1286 	case XK_6:		return KEY_6;
   1287 	case XK_7:		return KEY_7;
   1288 	case XK_8:		return KEY_8;
   1289 	case XK_9:		return KEY_9;
   1290 	case XK_0:		return KEY_0;
   1291 	case XK_exclam:	return KEY_1;
   1292 	case XK_at:		return KEY_2;
   1293 	case XK_numbersign:	return KEY_3;
   1294 	case XK_dollar:	return KEY_4;
   1295 	case XK_percent:	return KEY_5;
   1296 	case XK_asciicircum:	return KEY_6;
   1297 	case XK_ampersand:	return KEY_7;
   1298 	case XK_asterisk:	return KEY_8;
   1299 	case XK_parenleft:	return KEY_9;
   1300 	case XK_parenright:	return KEY_0;
   1301 	case XK_minus:	return KEY_MINUS;
   1302 	case XK_underscore:	return KEY_MINUS;
   1303 	case XK_equal:	return KEY_EQUAL;
   1304 	case XK_plus:	return KEY_EQUAL;
   1305 	case XK_BackSpace:	return KEY_BACKSPACE;
   1306 	case XK_Tab:		return KEY_TAB;
   1307 	case XK_q:		return KEY_Q;
   1308 	case XK_Q:		return KEY_Q;
   1309 	case XK_w:		return KEY_W;
   1310 	case XK_W:		return KEY_W;
   1311 	case XK_e:		return KEY_E;
   1312 	case XK_E:		return KEY_E;
   1313 	case XK_r:		return KEY_R;
   1314 	case XK_R:		return KEY_R;
   1315 	case XK_t:		return KEY_T;
   1316 	case XK_T:		return KEY_T;
   1317 	case XK_y:		return KEY_Y;
   1318 	case XK_Y:		return KEY_Y;
   1319 	case XK_u:		return KEY_U;
   1320 	case XK_U:		return KEY_U;
   1321 	case XK_i:		return KEY_I;
   1322 	case XK_I:		return KEY_I;
   1323 	case XK_o:		return KEY_O;
   1324 	case XK_O:		return KEY_O;
   1325 	case XK_p:		return KEY_P;
   1326 	case XK_P:		return KEY_P;
   1327 	case XK_braceleft:	return KEY_LEFTBRACE;
   1328 	case XK_braceright:	return KEY_RIGHTBRACE;
   1329 	case XK_bracketleft:	return KEY_LEFTBRACE;
   1330 	case XK_bracketright:	return KEY_RIGHTBRACE;
   1331 	case XK_Return:	return KEY_ENTER;
   1332 	case XK_Control_L:	return KEY_LEFTCTRL;
   1333 	case XK_a:		return KEY_A;
   1334 	case XK_A:		return KEY_A;
   1335 	case XK_s:		return KEY_S;
   1336 	case XK_S:		return KEY_S;
   1337 	case XK_d:		return KEY_D;
   1338 	case XK_D:		return KEY_D;
   1339 	case XK_f:		return KEY_F;
   1340 	case XK_F:		return KEY_F;
   1341 	case XK_g:		return KEY_G;
   1342 	case XK_G:		return KEY_G;
   1343 	case XK_h:		return KEY_H;
   1344 	case XK_H:		return KEY_H;
   1345 	case XK_j:		return KEY_J;
   1346 	case XK_J:		return KEY_J;
   1347 	case XK_k:		return KEY_K;
   1348 	case XK_K:		return KEY_K;
   1349 	case XK_l:		return KEY_L;
   1350 	case XK_L:		return KEY_L;
   1351 	case XK_semicolon:	return KEY_SEMICOLON;
   1352 	case XK_colon:	return KEY_SEMICOLON;
   1353 	case XK_apostrophe:	return KEY_APOSTROPHE;
   1354 	case XK_quotedbl:	return KEY_APOSTROPHE;
   1355 	case XK_grave:	return KEY_GRAVE;
   1356 	case XK_asciitilde:	return KEY_GRAVE;
   1357 	case XK_Shift_L:	return KEY_LEFTSHIFT;
   1358 	case XK_backslash:	return KEY_BACKSLASH;
   1359 	case XK_bar:		return KEY_BACKSLASH;
   1360 	case XK_z:		return KEY_Z;
   1361 	case XK_Z:		return KEY_Z;
   1362 	case XK_x:		return KEY_X;
   1363 	case XK_X:		return KEY_X;
   1364 	case XK_c:		return KEY_C;
   1365 	case XK_C:		return KEY_C;
   1366 	case XK_v:		return KEY_V;
   1367 	case XK_V:		return KEY_V;
   1368 	case XK_b:		return KEY_B;
   1369 	case XK_B:		return KEY_B;
   1370 	case XK_n:		return KEY_N;
   1371 	case XK_N:		return KEY_N;
   1372 	case XK_m:		return KEY_M;
   1373 	case XK_M:		return KEY_M;
   1374 	case XK_comma:	return KEY_COMMA;
   1375 	case XK_less:	return KEY_COMMA;
   1376 	case XK_period:	return KEY_DOT;
   1377 	case XK_greater:	return KEY_DOT;
   1378 	case XK_slash:	return KEY_SLASH;
   1379 	case XK_question:	return KEY_SLASH;
   1380 	case XK_Shift_R:	return KEY_RIGHTSHIFT;
   1381 	case XK_KP_Multiply:	return KEY_KPASTERISK;
   1382 	case XK_Alt_L:	return KEY_LEFTALT;
   1383 	case XK_space:	return KEY_SPACE;
   1384 	case XK_Caps_Lock:	return KEY_CAPSLOCK;
   1385 	case XK_F1:		return KEY_F1;
   1386 	case XK_F2:		return KEY_F2;
   1387 	case XK_F3:		return KEY_F3;
   1388 	case XK_F4:		return KEY_F4;
   1389 	case XK_F5:		return KEY_F5;
   1390 	case XK_F6:		return KEY_F6;
   1391 	case XK_F7:		return KEY_F7;
   1392 	case XK_F8:		return KEY_F8;
   1393 	case XK_F9:		return KEY_F9;
   1394 	case XK_F10:		return KEY_F10;
   1395 	case XK_Num_Lock:	return KEY_NUMLOCK;
   1396 	case XK_Scroll_Lock:	return KEY_SCROLLLOCK;
   1397 	case XK_KP_7:		return KEY_KP7;
   1398 	case XK_KP_8:		return KEY_KP8;
   1399 	case XK_KP_9:		return KEY_KP9;
   1400 	case XK_KP_Subtract:	return KEY_KPMINUS;
   1401 	case XK_KP_4:		return KEY_KP4;
   1402 	case XK_KP_5:		return KEY_KP5;
   1403 	case XK_KP_6:		return KEY_KP6;
   1404 	case XK_KP_Add:	return KEY_KPPLUS;
   1405 	case XK_KP_1:		return KEY_KP1;
   1406 	case XK_KP_2:		return KEY_KP2;
   1407 	case XK_KP_3:		return KEY_KP3;
   1408 	case XK_KP_0:		return KEY_KP0;
   1409 	case XK_KP_Decimal:	return KEY_KPDOT;
   1410 	case XK_F13:		return KEY_F13;
   1411 	case XK_F11:		return KEY_F11;
   1412 	case XK_F12:		return KEY_F12;
   1413 	case XK_F14:		return KEY_F14;
   1414 	case XK_F15:		return KEY_F15;
   1415 	case XK_F16:		return KEY_F16;
   1416 	case XK_F17:		return KEY_F17;
   1417 	case XK_F18:		return KEY_F18;
   1418 	case XK_F19:		return KEY_F19;
   1419 	case XK_F20:		return KEY_F20;
   1420 	case XK_KP_Enter:	return KEY_KPENTER;
   1421 	case XK_Control_R:	return KEY_RIGHTCTRL;
   1422 	case XK_KP_Divide:	return KEY_KPSLASH;
   1423 	case XK_Sys_Req:	return KEY_SYSRQ;
   1424 	case XK_Alt_R:	return KEY_RIGHTALT;
   1425 	case XK_Linefeed:	return KEY_LINEFEED;
   1426 	case XK_Home:		return KEY_HOME;
   1427 	case XK_Up:		return KEY_UP;
   1428 	case XK_Page_Up:	return KEY_PAGEUP;
   1429 	case XK_Left:		return KEY_LEFT;
   1430 	case XK_Right:	return KEY_RIGHT;
   1431 	case XK_End:		return KEY_END;
   1432 	case XK_Down:		return KEY_DOWN;
   1433 	case XK_Page_Down:	return KEY_PAGEDOWN;
   1434 	case XK_Insert:	return KEY_INSERT;
   1435 	case XK_Delete:	return KEY_DELETE;
   1436 	case XK_KP_Equal:	return KEY_KPEQUAL;
   1437 	case XK_Pause:	return KEY_PAUSE;
   1438 	case XK_F21:		return KEY_F21;
   1439 	case XK_F22:		return KEY_F22;
   1440 	case XK_F23:		return KEY_F23;
   1441 	case XK_F24:		return KEY_F24;
   1442 	case XK_KP_Separator:	return KEY_KPCOMMA;
   1443 	case XK_Meta_L:	return KEY_LEFTMETA;
   1444 	case XK_Meta_R:	return KEY_RIGHTMETA;
   1445 	case XK_Multi_key:	return KEY_COMPOSE;
   1446 #endif
   1447 	default:		return -1;
   1448 	}
   1449 }
   1450 
   1451 #if 0
   1452 
   1453 From /usr/include/linux/input.h
   1454 
   1455 We maintain it here since it is such a painful mess.
   1456 
   1457 Here is a little script to make it easier:
   1458 
   1459 #!/usr/bin/perl
   1460 while (<>) {
   1461 	$_ =~ s/-XK_/XK_/;
   1462 	next unless /^XK_/;
   1463 	chomp;
   1464 	if (/^(\S+)(\s+)(\S+)/) {
   1465 		$a = $1;
   1466 		$t = $2;
   1467 		$b = $3;
   1468 		print "\tcase $a:${t}return $b;\n";
   1469 		if ($a =~ /XK_[a-z]$/) {
   1470 			$a = uc($a);
   1471 			print "\tcase $a:${t}return $b;\n";
   1472 		}
   1473 	}
   1474 }
   1475 
   1476 This only handles US kbd, we would need a kbd database in general...
   1477 Ugh: parse dumpkeys(1) or -fookeys /usr/share/keymaps/i386/qwerty/dk.kmap.gz
   1478 
   1479 XK_Escape	KEY_ESC
   1480 XK_1		KEY_1
   1481 XK_2		KEY_2
   1482 XK_3		KEY_3
   1483 XK_4		KEY_4
   1484 XK_5		KEY_5
   1485 XK_6		KEY_6
   1486 XK_7		KEY_7
   1487 XK_8		KEY_8
   1488 XK_9		KEY_9
   1489 XK_0		KEY_0
   1490 -XK_exclam	KEY_1
   1491 -XK_at		KEY_2
   1492 -XK_numbersign	KEY_3
   1493 -XK_dollar	KEY_4
   1494 -XK_percent	KEY_5
   1495 -XK_asciicircum	KEY_6
   1496 -XK_ampersand	KEY_7
   1497 -XK_asterisk	KEY_8
   1498 -XK_parenleft	KEY_9
   1499 -XK_parenright	KEY_0
   1500 XK_minus	KEY_MINUS
   1501 -XK_underscore	KEY_MINUS
   1502 XK_equal	KEY_EQUAL
   1503 -XK_plus	KEY_EQUAL
   1504 XK_BackSpace	KEY_BACKSPACE
   1505 XK_Tab		KEY_TAB
   1506 XK_q		KEY_Q
   1507 XK_w		KEY_W
   1508 XK_e		KEY_E
   1509 XK_r		KEY_R
   1510 XK_t		KEY_T
   1511 XK_y		KEY_Y
   1512 XK_u		KEY_U
   1513 XK_i		KEY_I
   1514 XK_o		KEY_O
   1515 XK_p		KEY_P
   1516 XK_braceleft	KEY_LEFTBRACE
   1517 XK_braceright	KEY_RIGHTBRACE
   1518 -XK_bracketleft	KEY_LEFTBRACE
   1519 -XK_bracketright	KEY_RIGHTBRACE
   1520 XK_Return	KEY_ENTER
   1521 XK_Control_L	KEY_LEFTCTRL
   1522 XK_a		KEY_A
   1523 XK_s		KEY_S
   1524 XK_d		KEY_D
   1525 XK_f		KEY_F
   1526 XK_g		KEY_G
   1527 XK_h		KEY_H
   1528 XK_j		KEY_J
   1529 XK_k		KEY_K
   1530 XK_l		KEY_L
   1531 XK_semicolon	KEY_SEMICOLON
   1532 -XK_colon	KEY_SEMICOLON
   1533 XK_apostrophe	KEY_APOSTROPHE
   1534 -XK_quotedbl	KEY_APOSTROPHE
   1535 XK_grave	KEY_GRAVE
   1536 -XK_asciitilde	KEY_GRAVE
   1537 XK_Shift_L	KEY_LEFTSHIFT
   1538 XK_backslash	KEY_BACKSLASH
   1539 -XK_bar		KEY_BACKSLASH
   1540 XK_z		KEY_Z
   1541 XK_x		KEY_X
   1542 XK_c		KEY_C
   1543 XK_v		KEY_V
   1544 XK_b		KEY_B
   1545 XK_n		KEY_N
   1546 XK_m		KEY_M
   1547 XK_comma	KEY_COMMA
   1548 -XK_less	KEY_COMMA
   1549 XK_period	KEY_DOT
   1550 -XK_greater	KEY_DOT
   1551 XK_slash	KEY_SLASH
   1552 -XK_question	KEY_SLASH
   1553 XK_Shift_R	KEY_RIGHTSHIFT
   1554 XK_KP_Multiply	KEY_KPASTERISK
   1555 XK_Alt_L	KEY_LEFTALT
   1556 XK_space	KEY_SPACE
   1557 XK_Caps_Lock	KEY_CAPSLOCK
   1558 XK_F1		KEY_F1
   1559 XK_F2		KEY_F2
   1560 XK_F3		KEY_F3
   1561 XK_F4		KEY_F4
   1562 XK_F5		KEY_F5
   1563 XK_F6		KEY_F6
   1564 XK_F7		KEY_F7
   1565 XK_F8		KEY_F8
   1566 XK_F9		KEY_F9
   1567 XK_F10		KEY_F10
   1568 XK_Num_Lock	KEY_NUMLOCK
   1569 XK_Scroll_Lock	KEY_SCROLLLOCK
   1570 XK_KP_7		KEY_KP7
   1571 XK_KP_8		KEY_KP8
   1572 XK_KP_9		KEY_KP9
   1573 XK_KP_Subtract	KEY_KPMINUS
   1574 XK_KP_4		KEY_KP4
   1575 XK_KP_5		KEY_KP5
   1576 XK_KP_6		KEY_KP6
   1577 XK_KP_Add	KEY_KPPLUS
   1578 XK_KP_1		KEY_KP1
   1579 XK_KP_2		KEY_KP2
   1580 XK_KP_3		KEY_KP3
   1581 XK_KP_0		KEY_KP0
   1582 XK_KP_Decimal	KEY_KPDOT
   1583 NoSymbol	KEY_103RD
   1584 XK_F13		KEY_F13
   1585 NoSymbol	KEY_102ND
   1586 XK_F11		KEY_F11
   1587 XK_F12		KEY_F12
   1588 XK_F14		KEY_F14
   1589 XK_F15		KEY_F15
   1590 XK_F16		KEY_F16
   1591 XK_F17		KEY_F17
   1592 XK_F18		KEY_F18
   1593 XK_F19		KEY_F19
   1594 XK_F20		KEY_F20
   1595 XK_KP_Enter	KEY_KPENTER
   1596 XK_Control_R	KEY_RIGHTCTRL
   1597 XK_KP_Divide	KEY_KPSLASH
   1598 XK_Sys_Req	KEY_SYSRQ
   1599 XK_Alt_R	KEY_RIGHTALT
   1600 XK_Linefeed	KEY_LINEFEED
   1601 XK_Home		KEY_HOME
   1602 XK_Up		KEY_UP
   1603 XK_Page_Up	KEY_PAGEUP
   1604 XK_Left		KEY_LEFT
   1605 XK_Right	KEY_RIGHT
   1606 XK_End		KEY_END
   1607 XK_Down		KEY_DOWN
   1608 XK_Page_Down	KEY_PAGEDOWN
   1609 XK_Insert	KEY_INSERT
   1610 XK_Delete	KEY_DELETE
   1611 NoSymbol	KEY_MACRO
   1612 NoSymbol	KEY_MUTE
   1613 NoSymbol	KEY_VOLUMEDOWN
   1614 NoSymbol	KEY_VOLUMEUP
   1615 NoSymbol	KEY_POWER
   1616 XK_KP_Equal	KEY_KPEQUAL
   1617 NoSymbol	KEY_KPPLUSMINUS
   1618 XK_Pause	KEY_PAUSE
   1619 XK_F21		KEY_F21
   1620 XK_F22		KEY_F22
   1621 XK_F23		KEY_F23
   1622 XK_F24		KEY_F24
   1623 XK_KP_Separator	KEY_KPCOMMA
   1624 XK_Meta_L	KEY_LEFTMETA
   1625 XK_Meta_R	KEY_RIGHTMETA
   1626 XK_Multi_key	KEY_COMPOSE
   1627 
   1628 NoSymbol	KEY_STOP
   1629 NoSymbol	KEY_AGAIN
   1630 NoSymbol	KEY_PROPS
   1631 NoSymbol	KEY_UNDO
   1632 NoSymbol	KEY_FRONT
   1633 NoSymbol	KEY_COPY
   1634 NoSymbol	KEY_OPEN
   1635 NoSymbol	KEY_PASTE
   1636 NoSymbol	KEY_FIND
   1637 NoSymbol	KEY_CUT
   1638 NoSymbol	KEY_HELP
   1639 NoSymbol	KEY_MENU
   1640 NoSymbol	KEY_CALC
   1641 NoSymbol	KEY_SETUP
   1642 NoSymbol	KEY_SLEEP
   1643 NoSymbol	KEY_WAKEUP
   1644 NoSymbol	KEY_FILE
   1645 NoSymbol	KEY_SENDFILE
   1646 NoSymbol	KEY_DELETEFILE
   1647 NoSymbol	KEY_XFER
   1648 NoSymbol	KEY_PROG1
   1649 NoSymbol	KEY_PROG2
   1650 NoSymbol	KEY_WWW
   1651 NoSymbol	KEY_MSDOS
   1652 NoSymbol	KEY_COFFEE
   1653 NoSymbol	KEY_DIRECTION
   1654 NoSymbol	KEY_CYCLEWINDOWS
   1655 NoSymbol	KEY_MAIL
   1656 NoSymbol	KEY_BOOKMARKS
   1657 NoSymbol	KEY_COMPUTER
   1658 NoSymbol	KEY_BACK
   1659 NoSymbol	KEY_FORWARD
   1660 NoSymbol	KEY_CLOSECD
   1661 NoSymbol	KEY_EJECTCD
   1662 NoSymbol	KEY_EJECTCLOSECD
   1663 NoSymbol	KEY_NEXTSONG
   1664 NoSymbol	KEY_PLAYPAUSE
   1665 NoSymbol	KEY_PREVIOUSSONG
   1666 NoSymbol	KEY_STOPCD
   1667 NoSymbol	KEY_RECORD
   1668 NoSymbol	KEY_REWIND
   1669 NoSymbol	KEY_PHONE
   1670 NoSymbol	KEY_ISO
   1671 NoSymbol	KEY_CONFIG
   1672 NoSymbol	KEY_HOMEPAGE
   1673 NoSymbol	KEY_REFRESH
   1674 NoSymbol	KEY_EXIT
   1675 NoSymbol	KEY_MOVE
   1676 NoSymbol	KEY_EDIT
   1677 NoSymbol	KEY_SCROLLUP
   1678 NoSymbol	KEY_SCROLLDOWN
   1679 NoSymbol	KEY_KPLEFTPAREN
   1680 NoSymbol	KEY_KPRIGHTPAREN
   1681 
   1682 NoSymbol	KEY_INTL1
   1683 NoSymbol	KEY_INTL2
   1684 NoSymbol	KEY_INTL3
   1685 NoSymbol	KEY_INTL4
   1686 NoSymbol	KEY_INTL5
   1687 NoSymbol	KEY_INTL6
   1688 NoSymbol	KEY_INTL7
   1689 NoSymbol	KEY_INTL8
   1690 NoSymbol	KEY_INTL9
   1691 NoSymbol	KEY_LANG1
   1692 NoSymbol	KEY_LANG2
   1693 NoSymbol	KEY_LANG3
   1694 NoSymbol	KEY_LANG4
   1695 NoSymbol	KEY_LANG5
   1696 NoSymbol	KEY_LANG6
   1697 NoSymbol	KEY_LANG7
   1698 NoSymbol	KEY_LANG8
   1699 NoSymbol	KEY_LANG9
   1700 
   1701 NoSymbol	KEY_PLAYCD
   1702 NoSymbol	KEY_PAUSECD
   1703 NoSymbol	KEY_PROG3
   1704 NoSymbol	KEY_PROG4
   1705 NoSymbol	KEY_SUSPEND
   1706 NoSymbol	KEY_CLOSE
   1707 NoSymbol	KEY_PLAY
   1708 NoSymbol	KEY_FASTFORWARD
   1709 NoSymbol	KEY_BASSBOOST
   1710 NoSymbol	KEY_PRINT
   1711 NoSymbol	KEY_HP
   1712 NoSymbol	KEY_CAMERA
   1713 NoSymbol	KEY_SOUND
   1714 NoSymbol	KEY_QUESTION
   1715 NoSymbol	KEY_EMAIL
   1716 NoSymbol	KEY_CHAT
   1717 NoSymbol	KEY_SEARCH
   1718 NoSymbol	KEY_CONNECT
   1719 NoSymbol	KEY_FINANCE
   1720 NoSymbol	KEY_SPORT
   1721 NoSymbol	KEY_SHOP
   1722 NoSymbol	KEY_ALTERASE
   1723 NoSymbol	KEY_CANCEL
   1724 NoSymbol	KEY_BRIGHTNESSDOWN
   1725 NoSymbol	KEY_BRIGHTNESSUP
   1726 NoSymbol	KEY_MEDIA
   1727 
   1728 NoSymbol	KEY_UNKNOWN
   1729 NoSymbol
   1730 NoSymbol	BTN_MISC
   1731 NoSymbol	BTN_0
   1732 NoSymbol	BTN_1
   1733 NoSymbol	BTN_2
   1734 NoSymbol	BTN_3
   1735 NoSymbol	BTN_4
   1736 NoSymbol	BTN_5
   1737 NoSymbol	BTN_6
   1738 NoSymbol	BTN_7
   1739 NoSymbol	BTN_8
   1740 NoSymbol	BTN_9
   1741 NoSymbol
   1742 NoSymbol	BTN_MOUSE
   1743 NoSymbol	BTN_LEFT
   1744 NoSymbol	BTN_RIGHT
   1745 NoSymbol	BTN_MIDDLE
   1746 NoSymbol	BTN_SIDE
   1747 NoSymbol	BTN_EXTRA
   1748 NoSymbol	BTN_FORWARD
   1749 NoSymbol	BTN_BACK
   1750 NoSymbol	BTN_TASK
   1751 NoSymbol
   1752 NoSymbol	BTN_JOYSTICK
   1753 NoSymbol	BTN_TRIGGER
   1754 NoSymbol	BTN_THUMB
   1755 NoSymbol	BTN_THUMB2
   1756 NoSymbol	BTN_TOP
   1757 NoSymbol	BTN_TOP2
   1758 NoSymbol	BTN_PINKIE
   1759 NoSymbol	BTN_BASE
   1760 NoSymbol	BTN_BASE2
   1761 NoSymbol	BTN_BASE3
   1762 NoSymbol	BTN_BASE4
   1763 NoSymbol	BTN_BASE5
   1764 NoSymbol	BTN_BASE6
   1765 NoSymbol	BTN_DEAD
   1766 
   1767 NoSymbol	BTN_GAMEPAD
   1768 NoSymbol	BTN_A
   1769 NoSymbol	BTN_B
   1770 NoSymbol	BTN_C
   1771 NoSymbol	BTN_X
   1772 NoSymbol	BTN_Y
   1773 NoSymbol	BTN_Z
   1774 NoSymbol	BTN_TL
   1775 NoSymbol	BTN_TR
   1776 NoSymbol	BTN_TL2
   1777 NoSymbol	BTN_TR2
   1778 NoSymbol	BTN_SELECT
   1779 NoSymbol	BTN_START
   1780 NoSymbol	BTN_MODE
   1781 NoSymbol	BTN_THUMBL
   1782 NoSymbol	BTN_THUMBR
   1783 
   1784 NoSymbol	BTN_DIGI
   1785 NoSymbol	BTN_TOOL_PEN
   1786 NoSymbol	BTN_TOOL_RUBBER
   1787 NoSymbol	BTN_TOOL_BRUSH
   1788 NoSymbol	BTN_TOOL_PENCIL
   1789 NoSymbol	BTN_TOOL_AIRBRUSH
   1790 NoSymbol	BTN_TOOL_FINGER
   1791 NoSymbol	BTN_TOOL_MOUSE
   1792 NoSymbol	BTN_TOOL_LENS
   1793 NoSymbol	BTN_TOUCH
   1794 NoSymbol	BTN_STYLUS
   1795 NoSymbol	BTN_STYLUS2
   1796 NoSymbol	BTN_TOOL_DOUBLETAP
   1797 NoSymbol	BTN_TOOL_TRIPLETAP
   1798 
   1799 NoSymbol	BTN_WHEEL
   1800 NoSymbol	BTN_GEAR_DOWN
   1801 NoSymbol	BTN_GEAR_UP
   1802 
   1803 NoSymbol	KEY_OK
   1804 NoSymbol	KEY_SELECT
   1805 NoSymbol	KEY_GOTO
   1806 NoSymbol	KEY_CLEAR
   1807 NoSymbol	KEY_POWER2
   1808 NoSymbol	KEY_OPTION
   1809 NoSymbol	KEY_INFO
   1810 NoSymbol	KEY_TIME
   1811 NoSymbol	KEY_VENDOR
   1812 NoSymbol	KEY_ARCHIVE
   1813 NoSymbol	KEY_PROGRAM
   1814 NoSymbol	KEY_CHANNEL
   1815 NoSymbol	KEY_FAVORITES
   1816 NoSymbol	KEY_EPG
   1817 NoSymbol	KEY_PVR
   1818 NoSymbol	KEY_MHP
   1819 NoSymbol	KEY_LANGUAGE
   1820 NoSymbol	KEY_TITLE
   1821 NoSymbol	KEY_SUBTITLE
   1822 NoSymbol	KEY_ANGLE
   1823 NoSymbol	KEY_ZOOM
   1824 NoSymbol	KEY_MODE
   1825 NoSymbol	KEY_KEYBOARD
   1826 NoSymbol	KEY_SCREEN
   1827 NoSymbol	KEY_PC
   1828 NoSymbol	KEY_TV
   1829 NoSymbol	KEY_TV2
   1830 NoSymbol	KEY_VCR
   1831 NoSymbol	KEY_VCR2
   1832 NoSymbol	KEY_SAT
   1833 NoSymbol	KEY_SAT2
   1834 NoSymbol	KEY_CD
   1835 NoSymbol	KEY_TAPE
   1836 NoSymbol	KEY_RADIO
   1837 NoSymbol	KEY_TUNER
   1838 NoSymbol	KEY_PLAYER
   1839 NoSymbol	KEY_TEXT
   1840 NoSymbol	KEY_DVD
   1841 NoSymbol	KEY_AUX
   1842 NoSymbol	KEY_MP3
   1843 NoSymbol	KEY_AUDIO
   1844 NoSymbol	KEY_VIDEO
   1845 NoSymbol	KEY_DIRECTORY
   1846 NoSymbol	KEY_LIST
   1847 NoSymbol	KEY_MEMO
   1848 NoSymbol	KEY_CALENDAR
   1849 NoSymbol	KEY_RED
   1850 NoSymbol	KEY_GREEN
   1851 NoSymbol	KEY_YELLOW
   1852 NoSymbol	KEY_BLUE
   1853 NoSymbol	KEY_CHANNELUP
   1854 NoSymbol	KEY_CHANNELDOWN
   1855 NoSymbol	KEY_FIRST
   1856 NoSymbol	KEY_LAST
   1857 NoSymbol	KEY_AB
   1858 NoSymbol	KEY_NEXT
   1859 NoSymbol	KEY_RESTART
   1860 NoSymbol	KEY_SLOW
   1861 NoSymbol	KEY_SHUFFLE
   1862 NoSymbol	KEY_BREAK
   1863 NoSymbol	KEY_PREVIOUS
   1864 NoSymbol	KEY_DIGITS
   1865 NoSymbol	KEY_TEEN
   1866 NoSymbol	KEY_TWEN
   1867 
   1868 NoSymbol	KEY_DEL_EOL
   1869 NoSymbol	KEY_DEL_EOS
   1870 NoSymbol	KEY_INS_LINE
   1871 NoSymbol	KEY_DEL_LINE
   1872 NoSymbol	KEY_MAX
   1873 
   1874 #endif
   1875 
   1876 
   1877