A generic touchscreen calibration program for X.Org
Revision | 416e33f611f6a9e459dddd18f544bb6c8d79bac0 (tree) |
---|---|
Time | 2009-11-29 23:08:19 |
Author | Tias <tias@kata...> |
Commiter | Tias |
Add support for dynamic evdev re-calibration
@@ -66,6 +66,18 @@ | ||
66 | 66 | #include <X11/Xlib.h> |
67 | 67 | #include <X11/extensions/XInput.h> |
68 | 68 | |
69 | +// begin EvDev includes | |
70 | +#include <X11/Xatom.h> | |
71 | +//#include <X11/Xutil.h> | |
72 | + | |
73 | +#ifndef EXIT_SUCCESS | |
74 | +#define EXIT_SUCCESS 1 | |
75 | +#endif | |
76 | +#ifndef EXIT_FAILURE | |
77 | +#define EXIT_FAILURE 0 | |
78 | +#endif | |
79 | +// end EvDev includes | |
80 | + | |
69 | 81 | /* Number of points the user should click */ |
70 | 82 | const int num_points = 4; |
71 | 83 |
@@ -219,7 +231,6 @@ void Calibrator::finish () | ||
219 | 231 | std::swap (clicked_y [LL], clicked_y [UR]); |
220 | 232 | } |
221 | 233 | |
222 | - | |
223 | 234 | /* Compute min/max coordinates. |
224 | 235 | * These are scaled to [oldcalib_min, oldcalib_max]] */ |
225 | 236 | const float scale_x = (oldcalib_max_x - oldcalib_min_x)/(float)width; |
@@ -271,7 +282,7 @@ public: | ||
271 | 282 | CalibratorXorgPrint::CalibratorXorgPrint (const char* drivername0, int min_x, int max_x, int min_y, int max_y) |
272 | 283 | : Calibrator (drivername0, min_x, max_x, min_y, max_y) |
273 | 284 | { |
274 | - printf ("Calibrating driver %s with min_x=%d, max_x=%d and min_y=%d, max_y=%d\n", | |
285 | + printf ("Calibrating unknown driver \"%s\" (having min_x=%d, max_x=%d and min_y=%d, max_y=%d)\n", | |
275 | 286 | drivername, oldcalib_min_x, oldcalib_max_x, oldcalib_min_y, oldcalib_max_y); |
276 | 287 | } |
277 | 288 |
@@ -294,6 +305,375 @@ void CalibratorXorgPrint::finish_data ( | ||
294 | 305 | } |
295 | 306 | |
296 | 307 | |
308 | + | |
309 | +/*************************** | |
310 | + * Class for dynamic evdev calibration | |
311 | + * uses xinput "Evdev Axis Calibration" | |
312 | + ***************************/ | |
313 | +class CalibratorEvdev: public Calibrator | |
314 | +{ | |
315 | +private: | |
316 | + Display *display; | |
317 | + XDeviceInfo *info; | |
318 | + XDevice *dev; | |
319 | +public: | |
320 | + CalibratorEvdev (const char*, int, int, int, int); | |
321 | + ~CalibratorEvdev (); | |
322 | + | |
323 | + virtual void finish_data (int, int, int, int, int, int, int); | |
324 | + | |
325 | + static Bool check_driver(const char *name); | |
326 | + | |
327 | + // xinput functions | |
328 | + static Atom parse_atom(Display *display, const char *name); | |
329 | + static XDeviceInfo* find_device_info(Display *display, const char *name, Bool only_extended); | |
330 | + int do_set_prop(Display *display, Atom type, int format, int argc, char *argv[]); | |
331 | +}; | |
332 | + | |
333 | +CalibratorEvdev::CalibratorEvdev (const char* drivername0, int min_x, int max_x, int min_y, int max_y) | |
334 | + : Calibrator (drivername0, min_x, max_x, min_y, max_y) | |
335 | +{ | |
336 | + Atom property; | |
337 | + Atom act_type; | |
338 | + int act_format; | |
339 | + unsigned long nitems, bytes_after; | |
340 | + unsigned char *data, *ptr; | |
341 | + | |
342 | + printf ("Calibrating EVDEV driver for \"%s\"\n", drivername); | |
343 | + | |
344 | + // init | |
345 | + display = XOpenDisplay(NULL); | |
346 | + if (display == NULL) { | |
347 | + fprintf(stderr, "Unable to connect to X server\n"); | |
348 | + return; | |
349 | + } | |
350 | + | |
351 | + | |
352 | + info = find_device_info(display, drivername, False); | |
353 | + if (!info) | |
354 | + { | |
355 | + fprintf(stderr, "unable to find device %s\n", drivername); | |
356 | + return; | |
357 | + } | |
358 | + | |
359 | + dev = XOpenDevice(display, info->id); | |
360 | + if (!dev) | |
361 | + { | |
362 | + fprintf(stderr, "unable to open device '%s'\n", info->name); | |
363 | + return; | |
364 | + } | |
365 | + | |
366 | + // get "Evdev Axis Calibration" property | |
367 | + property = parse_atom(display, "Evdev Axis Calibration"); | |
368 | + if (XGetDeviceProperty(display, dev, property, 0, 1000, False, | |
369 | + AnyPropertyType, &act_type, &act_format, | |
370 | + &nitems, &bytes_after, &data) == Success) | |
371 | + { | |
372 | + if (act_format != 32 || act_type != XA_INTEGER) { | |
373 | + fprintf(stderr, "Error: unexpected format or type from \"Evdev Axis Calibration\" property.\n"); | |
374 | + } | |
375 | + | |
376 | + if (nitems != 0) { | |
377 | + ptr = data; | |
378 | + | |
379 | + oldcalib_min_x = *((long*)ptr); | |
380 | + ptr += sizeof(long); | |
381 | + oldcalib_max_x = *((long*)ptr); | |
382 | + ptr += sizeof(long); | |
383 | + oldcalib_min_y = *((long*)ptr); | |
384 | + ptr += sizeof(long); | |
385 | + oldcalib_max_y = *((long*)ptr); | |
386 | + ptr += sizeof(long); | |
387 | + | |
388 | + printf("Read current calibration data from XInput: min_x=%d, max_x=%d and min_y=%d, max_y=%d\n", | |
389 | + oldcalib_min_x, oldcalib_max_x, oldcalib_min_y, oldcalib_max_y); | |
390 | + } | |
391 | + | |
392 | + XFree(data); | |
393 | + } else | |
394 | + printf("\tFetch failure\n"); | |
395 | + | |
396 | +} | |
397 | + | |
398 | +CalibratorEvdev::~CalibratorEvdev () { | |
399 | + XCloseDevice(display, dev); | |
400 | + XCloseDisplay(display); | |
401 | +} | |
402 | + | |
403 | +void CalibratorEvdev::finish_data ( | |
404 | + int min_x, int max_x, int min_y, int max_y, int swap_xy, int flip_x, int flip_y) | |
405 | +{ | |
406 | + // Evdev Axes Swap | |
407 | + if (swap_xy) { | |
408 | + printf("Swapping X and Y axis...\n"); | |
409 | + // xinput set-int-prop 4 224 8 0 | |
410 | + char str_prop[50]; | |
411 | + sprintf(str_prop, "Evdev Axes Swap"); | |
412 | + char str_swap_xy[20]; | |
413 | + sprintf(str_swap_xy, "%d", swap_xy); | |
414 | + | |
415 | + int argc = 3; | |
416 | + char* argv[argc]; | |
417 | + //argv[0] = ""; | |
418 | + argv[1] = str_prop; | |
419 | + argv[2] = str_swap_xy; | |
420 | + do_set_prop(display, XA_INTEGER, 8, argc, argv); | |
421 | + } | |
422 | + | |
423 | + // Evdev Axis Inversion | |
424 | + /* Ignored, X server can handle it by flipping min/max values. */ | |
425 | + | |
426 | + // Evdev Axis Calibration | |
427 | + // xinput set-int-prop 4 223 5 500 8 300 | |
428 | + printf("Setting new calibration data: %d, %d, %d, %d\n", min_x, max_x, min_y, max_y); | |
429 | + char str_prop[50]; | |
430 | + sprintf(str_prop, "Evdev Axis Calibration"); | |
431 | + char str_min_x[20]; | |
432 | + sprintf(str_min_x, "%d", min_x); | |
433 | + char str_max_x[20]; | |
434 | + sprintf(str_max_x, "%d", max_x); | |
435 | + char str_min_y[20]; | |
436 | + sprintf(str_min_y, "%d", min_y); | |
437 | + char str_max_y[20]; | |
438 | + sprintf(str_max_y, "%d", max_y); | |
439 | + | |
440 | + int argc = 6; | |
441 | + char* argv[argc]; | |
442 | + //argv[0] = ""; | |
443 | + argv[1] = str_prop; | |
444 | + argv[2] = str_min_x; | |
445 | + argv[3] = str_max_x; | |
446 | + argv[4] = str_min_y; | |
447 | + argv[5] = str_max_y; | |
448 | + do_set_prop(display, XA_INTEGER, 32, argc, argv); | |
449 | + | |
450 | + // close | |
451 | + XSync(display, False); | |
452 | +} | |
453 | + | |
454 | +Bool CalibratorEvdev::check_driver(const char *name) { | |
455 | + Display *display; | |
456 | + XDeviceInfo *info; | |
457 | + XDevice *dev; | |
458 | + int nprops; | |
459 | + Atom *props; | |
460 | + Bool found = False; | |
461 | + | |
462 | + display = XOpenDisplay(NULL); | |
463 | + if (display == NULL) { | |
464 | + fprintf(stderr, "Unable to connect to X server\n"); | |
465 | + quit(1); | |
466 | + } | |
467 | + | |
468 | + info = find_device_info(display, name, False); | |
469 | + if (!info) | |
470 | + { | |
471 | + fprintf(stderr, "unable to find device %s\n", name); | |
472 | + return NULL; | |
473 | + } | |
474 | + | |
475 | + dev = XOpenDevice(display, info->id); | |
476 | + if (!dev) | |
477 | + { | |
478 | + fprintf(stderr, "unable to open device '%s'\n", info->name); | |
479 | + return NULL; | |
480 | + } | |
481 | + | |
482 | + props = XListDeviceProperties(display, dev, &nprops); | |
483 | + if (!nprops) | |
484 | + { | |
485 | + printf("Device '%s' does not report any properties.\n", info->name); | |
486 | + return NULL; | |
487 | + } | |
488 | + | |
489 | + // check if "Evdev Axis Calibration" exists, then its an evdev driver | |
490 | + while(nprops--) | |
491 | + { | |
492 | + if (strcmp(XGetAtomName(display, props[nprops]), | |
493 | + "Evdev Axis Calibration") == 0) { | |
494 | + found = True; | |
495 | + break; | |
496 | + } | |
497 | + } | |
498 | + | |
499 | + XFree(props); | |
500 | + XCloseDevice(display, dev); | |
501 | + XCloseDisplay(display); | |
502 | + | |
503 | + return found; | |
504 | +} | |
505 | + | |
506 | +Atom CalibratorEvdev::parse_atom(Display *display, const char *name) { | |
507 | + Bool is_atom = True; | |
508 | + int i; | |
509 | + | |
510 | + for (i = 0; name[i] != '\0'; i++) { | |
511 | + if (!isdigit(name[i])) { | |
512 | + is_atom = False; | |
513 | + break; | |
514 | + } | |
515 | + } | |
516 | + | |
517 | + if (is_atom) | |
518 | + return atoi(name); | |
519 | + else | |
520 | + return XInternAtom(display, name, False); | |
521 | +} | |
522 | + | |
523 | +XDeviceInfo* CalibratorEvdev::find_device_info( | |
524 | +Display *display, const char *name, Bool only_extended) | |
525 | +{ | |
526 | + XDeviceInfo *devices; | |
527 | + XDeviceInfo *found = NULL; | |
528 | + int loop; | |
529 | + int num_devices; | |
530 | + int len = strlen(name); | |
531 | + Bool is_id = True; | |
532 | + XID id = (XID)-1; | |
533 | + | |
534 | + for (loop=0; loop<len; loop++) { | |
535 | + if (!isdigit(name[loop])) { | |
536 | + is_id = False; | |
537 | + break; | |
538 | + } | |
539 | + } | |
540 | + | |
541 | + if (is_id) { | |
542 | + id = atoi(name); | |
543 | + } | |
544 | + | |
545 | + devices = XListInputDevices(display, &num_devices); | |
546 | + | |
547 | + for (loop=0; loop<num_devices; loop++) { | |
548 | + if ((!only_extended || (devices[loop].use >= IsXExtensionDevice)) && | |
549 | + ((!is_id && strcmp(devices[loop].name, name) == 0) || | |
550 | + (is_id && devices[loop].id == id))) { | |
551 | + if (found) { | |
552 | + fprintf(stderr, | |
553 | + "Warning: There are multiple devices named \"%s\".\n" | |
554 | + "To ensure the correct one is selected, please use " | |
555 | + "the device ID instead.\n\n", name); | |
556 | + return NULL; | |
557 | + } else { | |
558 | + found = &devices[loop]; | |
559 | + } | |
560 | + } | |
561 | + } | |
562 | + | |
563 | + return found; | |
564 | +} | |
565 | + | |
566 | +int CalibratorEvdev::do_set_prop( | |
567 | +Display *display, Atom type, int format, int argc, char **argv) | |
568 | +{ | |
569 | + Atom prop; | |
570 | + Atom old_type; | |
571 | + char *name; | |
572 | + int i; | |
573 | + Atom float_atom; | |
574 | + int old_format, nelements = 0; | |
575 | + unsigned long act_nitems, bytes_after; | |
576 | + char *endptr; | |
577 | + union { | |
578 | + unsigned char *c; | |
579 | + short *s; | |
580 | + long *l; | |
581 | + Atom *a; | |
582 | + } data; | |
583 | + | |
584 | + if (argc < 3) | |
585 | + { | |
586 | + fprintf(stderr, "Wrong usage of do_set_prop, need at least 3 arguments\n"); | |
587 | + return EXIT_FAILURE; | |
588 | + } | |
589 | + | |
590 | + name = argv[1]; | |
591 | + | |
592 | + prop = parse_atom(display, name); | |
593 | + | |
594 | + if (prop == None) { | |
595 | + fprintf(stderr, "invalid property %s\n", name); | |
596 | + return EXIT_FAILURE; | |
597 | + } | |
598 | + | |
599 | + float_atom = XInternAtom(display, "FLOAT", False); | |
600 | + | |
601 | + nelements = argc - 2; | |
602 | + if (type == None || format == 0) { | |
603 | + if (XGetDeviceProperty(display, dev, prop, 0, 0, False, AnyPropertyType, | |
604 | + &old_type, &old_format, &act_nitems, | |
605 | + &bytes_after, &data.c) != Success) { | |
606 | + fprintf(stderr, "failed to get property type and format for %s\n", | |
607 | + name); | |
608 | + return EXIT_FAILURE; | |
609 | + } else { | |
610 | + if (type == None) | |
611 | + type = old_type; | |
612 | + if (format == 0) | |
613 | + format = old_format; | |
614 | + } | |
615 | + | |
616 | + XFree(data.c); | |
617 | + } | |
618 | + | |
619 | + if (type == None) { | |
620 | + fprintf(stderr, "property %s doesn't exist, you need to specify " | |
621 | + "its type and format\n", name); | |
622 | + return EXIT_FAILURE; | |
623 | + } | |
624 | + | |
625 | + data.c = (unsigned char*)calloc(nelements, sizeof(long)); | |
626 | + | |
627 | + for (i = 0; i < nelements; i++) | |
628 | + { | |
629 | + if (type == XA_INTEGER) { | |
630 | + switch (format) | |
631 | + { | |
632 | + case 8: | |
633 | + data.c[i] = atoi(argv[2 + i]); | |
634 | + break; | |
635 | + case 16: | |
636 | + data.s[i] = atoi(argv[2 + i]); | |
637 | + break; | |
638 | + case 32: | |
639 | + data.l[i] = atoi(argv[2 + i]); | |
640 | + break; | |
641 | + default: | |
642 | + fprintf(stderr, "unexpected size for property %s", name); | |
643 | + return EXIT_FAILURE; | |
644 | + } | |
645 | + } else if (type == float_atom) { | |
646 | + if (format != 32) { | |
647 | + fprintf(stderr, "unexpected format %d for property %s\n", | |
648 | + format, name); | |
649 | + return EXIT_FAILURE; | |
650 | + } | |
651 | + *(float *)(data.l + i) = strtod(argv[2 + i], &endptr); | |
652 | + if (endptr == argv[2 + i]) { | |
653 | + fprintf(stderr, "argument %s could not be parsed\n", argv[2 + i]); | |
654 | + return EXIT_FAILURE; | |
655 | + } | |
656 | + } else if (type == XA_ATOM) { | |
657 | + if (format != 32) { | |
658 | + fprintf(stderr, "unexpected format %d for property %s\n", | |
659 | + format, name); | |
660 | + return EXIT_FAILURE; | |
661 | + } | |
662 | + data.a[i] = parse_atom(display, argv[2 + i]); | |
663 | + } else { | |
664 | + fprintf(stderr, "unexpected type for property %s\n", name); | |
665 | + return EXIT_FAILURE; | |
666 | + } | |
667 | + } | |
668 | + | |
669 | + XChangeDeviceProperty(display, dev, prop, type, format, PropModeReplace, | |
670 | + data.c, nelements); | |
671 | + free(data.c); | |
672 | + XCloseDevice(display, dev); | |
673 | + return EXIT_SUCCESS; | |
674 | +} | |
675 | + | |
676 | + | |
297 | 677 | /********************************** |
298 | 678 | * Class for usbtouchscreen driver, |
299 | 679 | * writes output parameters to running kernel and to modprobe.conf |
@@ -499,19 +879,26 @@ CalibrationArea::CalibrationArea () | ||
499 | 879 | /* Not sure this is the right place for this, but here we go |
500 | 880 | * Get driver name and axis information from XInput */ |
501 | 881 | { |
882 | + int ndevices; | |
883 | + Display *display; | |
884 | + XDeviceInfoPtr list, slist; | |
885 | + XAnyClassPtr any; | |
886 | + int xi_opcode, event, error; | |
887 | + | |
502 | 888 | int found = 0; |
503 | 889 | const char* drivername = ""; |
504 | 890 | int min_x = 0, max_x = 0; |
505 | 891 | int min_y = 0, max_y = 0; |
506 | 892 | |
507 | - int ndevices; | |
508 | - Display *display; | |
509 | - XDeviceInfoPtr list, slist; | |
510 | - XAnyClassPtr any; | |
893 | + display = XOpenDisplay(NULL); | |
511 | 894 | |
512 | - if ((display = XOpenDisplay (0)) == NULL) | |
513 | - { | |
514 | - fprintf (stderr, "Error: No connection to Xserver - Terminating.\n"); | |
895 | + if (display == NULL) { | |
896 | + fprintf(stderr, "Unable to connect to X server\n"); | |
897 | + quit(1); | |
898 | + } | |
899 | + | |
900 | + if (!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error)) { | |
901 | + printf("X Input extension not available.\n"); | |
515 | 902 | quit(1); |
516 | 903 | } |
517 | 904 |
@@ -553,7 +940,8 @@ CalibrationArea::CalibrationArea () | ||
553 | 940 | } |
554 | 941 | |
555 | 942 | } |
556 | - XFreeDeviceList (slist); | |
943 | + XFreeDeviceList(slist); | |
944 | + XCloseDisplay(display); | |
557 | 945 | |
558 | 946 | if (found == 0) { |
559 | 947 | fprintf (stderr, "Error: No calibratable devices found.\n"); |
@@ -565,9 +953,16 @@ CalibrationArea::CalibrationArea () | ||
565 | 953 | /* Different driver, different backend behaviour (case sensitive ?) */ |
566 | 954 | if (strcmp(drivername, "Usbtouchscreen") == 0) |
567 | 955 | W = new CalibratorUsbts(); |
568 | - else | |
569 | - W = new CalibratorXorgPrint(drivername, | |
570 | - min_x, max_x, min_y, max_y); | |
956 | + else { | |
957 | + // unable to know device driver from its name alone | |
958 | + // either its EVDEV or an unknown driver | |
959 | + if (CalibratorEvdev::check_driver(drivername)) | |
960 | + W = new CalibratorEvdev(drivername, | |
961 | + min_x, max_x, min_y, max_y); | |
962 | + else | |
963 | + W = new CalibratorXorgPrint(drivername, | |
964 | + min_x, max_x, min_y, max_y); | |
965 | + } | |
571 | 966 | } |
572 | 967 | |
573 | 968 | /* Listen for mouse events */ |