1 /*
2    Copyright (C) 2002-2010 Karl J. Runge <runge@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 
check_uinput(void)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 
init_key_tracker(void)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 
mod_is_down(void)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 
key_is_down(void)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 
shutdown_uinput(void)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  */
get_bustype(char * s)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 
load_tslib_cal(void)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 
initialize_uinput(void)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 
set_uinput_accel_xy(double fx,double fy)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 
set_uinput_accel(char * str)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 
set_uinput_thresh(char * str)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 
set_uinput_reset(int ms)558 void set_uinput_reset(int ms) {
559 	zero_delay = (double) ms/1000.;
560 	rfbLog("set_uinput_reset: %d\n", ms);
561 }
562 
set_uinput_always(int a)563 void set_uinput_always(int a) {
564 	uinput_always = a;
565 }
566 
set_uinput_touchscreen(int b)567 void set_uinput_touchscreen(int b) {
568 	uinput_touchscreen = b;
569 }
570 
set_uinput_abs(int b)571 void set_uinput_abs(int b) {
572 	uinput_abs = b;
573 }
574 
get_uinput_accel(void)575 char *get_uinput_accel(void) {
576 	return uinput_accel_str;
577 }
get_uinput_thresh(void)578 char *get_uinput_thresh(void) {
579 	return uinput_thresh_str;
580 }
get_uinput_reset(void)581 int get_uinput_reset(void) {
582 	return (int) (1000 * zero_delay);
583 }
584 
get_uinput_always(void)585 int get_uinput_always(void) {
586 	return uinput_always;
587 }
588 
get_uinput_touchscreen(void)589 int get_uinput_touchscreen(void) {
590 	return uinput_touchscreen;
591 }
592 
get_uinput_abs(void)593 int get_uinput_abs(void) {
594 	return uinput_abs;
595 }
596 
parse_uinput_str(char * in)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 
ptr_move(int dx,int dy)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 
apply_tslib(int * x,int * y)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 
ptr_abs(int x,int y,int p)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 
inside_thresh(int dx,int dy,int thr)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 
ptr_rel(int dx,int dy)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 
button_click(int down,int btn)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 
uinput_pointer_command(int mask,int x,int y,rfbClientPtr client)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 
uinput_key_command(int down,int keysym,rfbClientPtr client)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 
lookup_code(int keysym)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