property.c revision 0309d3b3
1/* 2 * Copyright © 2007 Peter Hutterer 3 * Copyright © 2009 Red Hat, Inc. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 */ 24 25#include <ctype.h> 26#include <string.h> 27#include <stdlib.h> 28#include <stdint.h> 29#include <X11/Xatom.h> 30#include <X11/extensions/XIproto.h> 31 32#include "xinput.h" 33 34static Atom parse_atom(Display *dpy, char *name) { 35 Bool is_atom = True; 36 int i; 37 38 for (i = 0; name[i] != '\0'; i++) { 39 if (!isdigit(name[i])) { 40 is_atom = False; 41 break; 42 } 43 } 44 45 if (is_atom) 46 return atoi(name); 47 else 48 return XInternAtom(dpy, name, False); 49} 50 51static void 52print_property(Display *dpy, XDevice* dev, Atom property) 53{ 54 Atom act_type; 55 char *name; 56 int act_format; 57 unsigned long nitems, bytes_after; 58 unsigned char *data, *ptr; 59 int j, done = False, size = 0; 60 61 name = XGetAtomName(dpy, property); 62 printf("\t%s (%ld):\t", name, property); 63 64 if (XGetDeviceProperty(dpy, dev, property, 0, 1000, False, 65 AnyPropertyType, &act_type, &act_format, 66 &nitems, &bytes_after, &data) == Success) 67 { 68 Atom float_atom = XInternAtom(dpy, "FLOAT", True); 69 70 ptr = data; 71 72 if (nitems == 0) 73 printf("<no items>"); 74 75 switch(act_format) 76 { 77 case 8: size = sizeof(char); break; 78 case 16: size = sizeof(short); break; 79 case 32: size = sizeof(long); break; 80 } 81 82 for (j = 0; j < nitems; j++) 83 { 84 switch(act_type) 85 { 86 case XA_INTEGER: 87 switch(act_format) 88 { 89 case 8: 90 printf("%d", *((char*)ptr)); 91 break; 92 case 16: 93 printf("%d", *((short*)ptr)); 94 break; 95 case 32: 96 printf("%ld", *((long*)ptr)); 97 break; 98 } 99 break; 100 case XA_CARDINAL: 101 switch(act_format) 102 { 103 case 8: 104 printf("%u", *((unsigned char*)ptr)); 105 break; 106 case 16: 107 printf("%u", *((unsigned short*)ptr)); 108 break; 109 case 32: 110 printf("%lu", *((unsigned long*)ptr)); 111 break; 112 } 113 break; 114 case XA_STRING: 115 if (act_format != 8) 116 { 117 printf("Unknown string format.\n"); 118 done = True; 119 break; 120 } 121 printf("\"%s\"", ptr); 122 j += strlen((char*)ptr); /* The loop's j++ jumps over the 123 terminating 0 */ 124 ptr += strlen((char*)ptr); /* ptr += size below jumps over 125 the terminating 0 */ 126 break; 127 case XA_ATOM: 128 { 129 Atom a = *(Atom*)ptr; 130 printf("\"%s\" (%d)", 131 (a) ? XGetAtomName(dpy, a) : "None", 132 (int)a); 133 break; 134 } 135 default: 136 if (float_atom != None && act_type == float_atom) 137 { 138 printf("%f", *((float*)ptr)); 139 break; 140 } 141 142 printf("\t... of unknown type '%s'\n", 143 XGetAtomName(dpy, act_type)); 144 done = True; 145 break; 146 } 147 148 ptr += size; 149 150 if (done == True) 151 break; 152 if (j < nitems - 1) 153 printf(", "); 154 } 155 printf("\n"); 156 XFree(data); 157 } else 158 printf("\tFetch failure\n"); 159 160} 161 162static int 163list_props_xi1(Display *dpy, int argc, char** argv, char* name, char *desc) 164{ 165 XDeviceInfo *info; 166 XDevice *dev; 167 int i; 168 int nprops; 169 Atom *props; 170 171 if (argc == 0) 172 { 173 fprintf(stderr, "Usage: xinput %s %s\n", name, desc); 174 return EXIT_FAILURE; 175 } 176 177 for (i = 0; i < argc; i++) 178 { 179 info = find_device_info(dpy, argv[i], False); 180 if (!info) 181 { 182 fprintf(stderr, "unable to find device '%s'\n", argv[i]); 183 continue; 184 } 185 186 dev = XOpenDevice(dpy, info->id); 187 if (!dev) 188 { 189 fprintf(stderr, "unable to open device '%s'\n", info->name); 190 continue; 191 } 192 193 props = XListDeviceProperties(dpy, dev, &nprops); 194 if (!nprops) 195 { 196 printf("Device '%s' does not report any properties.\n", info->name); 197 continue; 198 } 199 200 printf("Device '%s':\n", info->name); 201 while(nprops--) 202 { 203 print_property(dpy, dev, props[nprops]); 204 } 205 206 XFree(props); 207 XCloseDevice(dpy, dev); 208 } 209 return EXIT_SUCCESS; 210} 211 212 213int watch_props(Display *dpy, int argc, char** argv, char* n, char *desc) 214{ 215 XDevice *dev; 216 XDeviceInfo *info; 217 XEvent ev; 218 XDevicePropertyNotifyEvent *dpev; 219 char *name; 220 int type_prop; 221 XEventClass cls_prop; 222 223 if (list_props(dpy, argc, argv, n, desc) != EXIT_SUCCESS) 224 return EXIT_FAILURE; 225 226 info = find_device_info(dpy, argv[0], False); 227 if (!info) 228 { 229 fprintf(stderr, "unable to find device '%s'\n", argv[0]); 230 return EXIT_FAILURE; 231 } 232 233 dev = XOpenDevice(dpy, info->id); 234 if (!dev) 235 { 236 fprintf(stderr, "unable to open device '%s'\n", info->name); 237 return EXIT_FAILURE; 238 } 239 240 DevicePropertyNotify(dev, type_prop, cls_prop); 241 XSelectExtensionEvent(dpy, DefaultRootWindow(dpy), &cls_prop, 1); 242 243 while(1) 244 { 245 XNextEvent(dpy, &ev); 246 247 dpev = (XDevicePropertyNotifyEvent*)&ev; 248 if (dpev->type != type_prop) 249 continue; 250 251 name = XGetAtomName(dpy, dpev->atom); 252 printf("Property '%s' changed.\n", name); 253 print_property(dpy, dev, dpev->atom); 254 } 255 256 XCloseDevice(dpy, dev); 257} 258 259static int 260delete_prop_xi1(Display *dpy, int argc, char** argv, char* n, char *desc) 261{ 262 XDevice *dev; 263 XDeviceInfo *info; 264 char *name; 265 Atom prop; 266 267 info = find_device_info(dpy, argv[0], False); 268 if (!info) 269 { 270 fprintf(stderr, "unable to find device '%s'\n", argv[0]); 271 return EXIT_FAILURE; 272 } 273 274 dev = XOpenDevice(dpy, info->id); 275 if (!dev) 276 { 277 fprintf(stderr, "unable to open device '%s'\n", info->name); 278 return EXIT_FAILURE; 279 } 280 281 name = argv[1]; 282 283 prop = parse_atom(dpy, name); 284 285 XDeleteDeviceProperty(dpy, dev, prop); 286 287 XCloseDevice(dpy, dev); 288 return EXIT_SUCCESS; 289} 290 291static int 292do_set_prop_xi1(Display *dpy, Atom type, int format, int argc, char **argv, char *n, char *desc) 293{ 294 XDeviceInfo *info; 295 XDevice *dev; 296 Atom prop; 297 Atom old_type; 298 char *name; 299 int i; 300 Atom float_atom; 301 int old_format, nelements = 0; 302 unsigned long act_nitems, bytes_after; 303 char *endptr; 304 union { 305 unsigned char *c; 306 short *s; 307 long *l; 308 Atom *a; 309 } data; 310 311 if (argc < 3) 312 { 313 fprintf(stderr, "Usage: xinput %s %s\n", n, desc); 314 return EXIT_FAILURE; 315 } 316 317 info = find_device_info(dpy, argv[0], False); 318 if (!info) 319 { 320 fprintf(stderr, "unable to find device '%s'\n", argv[0]); 321 return EXIT_FAILURE; 322 } 323 324 dev = XOpenDevice(dpy, info->id); 325 if (!dev) 326 { 327 fprintf(stderr, "unable to open device '%s'\n", argv[0]); 328 return EXIT_FAILURE; 329 } 330 331 name = argv[1]; 332 333 prop = parse_atom(dpy, name); 334 335 if (prop == None) { 336 fprintf(stderr, "invalid property '%s'\n", name); 337 return EXIT_FAILURE; 338 } 339 340 float_atom = XInternAtom(dpy, "FLOAT", False); 341 342 nelements = argc - 2; 343 if (type == None || format == 0) { 344 if (XGetDeviceProperty(dpy, dev, prop, 0, 0, False, AnyPropertyType, 345 &old_type, &old_format, &act_nitems, 346 &bytes_after, &data.c) != Success) { 347 fprintf(stderr, "failed to get property type and format for '%s'\n", 348 name); 349 return EXIT_FAILURE; 350 } else { 351 if (type == None) 352 type = old_type; 353 if (format == 0) 354 format = old_format; 355 } 356 357 XFree(data.c); 358 } 359 360 if (type == None) { 361 fprintf(stderr, "property '%s' doesn't exist, you need to specify " 362 "its type and format\n", name); 363 return EXIT_FAILURE; 364 } 365 366 data.c = calloc(nelements, sizeof(long)); 367 368 for (i = 0; i < nelements; i++) 369 { 370 if (type == XA_INTEGER || type == XA_CARDINAL) { 371 switch (format) 372 { 373 case 8: 374 data.c[i] = atoi(argv[2 + i]); 375 break; 376 case 16: 377 data.s[i] = atoi(argv[2 + i]); 378 break; 379 case 32: 380 data.l[i] = atoi(argv[2 + i]); 381 break; 382 default: 383 fprintf(stderr, "unexpected size for property '%s'", name); 384 return EXIT_FAILURE; 385 } 386 } else if (type == float_atom) { 387 if (format != 32) { 388 fprintf(stderr, "unexpected format %d for property '%s'\n", 389 format, name); 390 return EXIT_FAILURE; 391 } 392 *(float *)(data.l + i) = strtod(argv[2 + i], &endptr); 393 if (endptr == argv[2 + i]) { 394 fprintf(stderr, "argument '%s' could not be parsed\n", argv[2 + i]); 395 return EXIT_FAILURE; 396 } 397 } else if (type == XA_ATOM) { 398 if (format != 32) { 399 fprintf(stderr, "unexpected format %d for property '%s'\n", 400 format, name); 401 return EXIT_FAILURE; 402 } 403 data.a[i] = parse_atom(dpy, argv[2 + i]); 404 } else { 405 fprintf(stderr, "unexpected type for property '%s'\n", name); 406 return EXIT_FAILURE; 407 } 408 } 409 410 XChangeDeviceProperty(dpy, dev, prop, type, format, PropModeReplace, 411 data.c, nelements); 412 free(data.c); 413 XCloseDevice(dpy, dev); 414 return EXIT_SUCCESS; 415} 416 417#if HAVE_XI2 418static void 419print_property_xi2(Display *dpy, int deviceid, Atom property) 420{ 421 Atom act_type; 422 char *name; 423 int act_format; 424 unsigned long nitems, bytes_after; 425 unsigned char *data, *ptr; 426 int j, done = False; 427 428 name = XGetAtomName(dpy, property); 429 printf("\t%s (%ld):\t", name, property); 430 431 if (XIGetProperty(dpy, deviceid, property, 0, 1000, False, 432 AnyPropertyType, &act_type, &act_format, 433 &nitems, &bytes_after, &data) == Success) 434 { 435 Atom float_atom = XInternAtom(dpy, "FLOAT", True); 436 437 ptr = data; 438 439 if (nitems == 0) 440 printf("<no items>"); 441 442 for (j = 0; j < nitems; j++) 443 { 444 switch(act_type) 445 { 446 case XA_INTEGER: 447 switch(act_format) 448 { 449 case 8: 450 printf("%d", *((int8_t*)ptr)); 451 break; 452 case 16: 453 printf("%d", *((int16_t*)ptr)); 454 break; 455 case 32: 456 printf("%d", *((int32_t*)ptr)); 457 break; 458 } 459 break; 460 case XA_CARDINAL: 461 switch(act_format) 462 { 463 case 8: 464 printf("%u", *((uint8_t*)ptr)); 465 break; 466 case 16: 467 printf("%u", *((uint16_t*)ptr)); 468 break; 469 case 32: 470 printf("%u", *((uint32_t*)ptr)); 471 break; 472 } 473 break; 474 case XA_STRING: 475 if (act_format != 8) 476 { 477 printf("Unknown string format.\n"); 478 done = True; 479 break; 480 } 481 printf("\"%s\"", ptr); 482 j += strlen((char*)ptr); /* The loop's j++ jumps over the 483 terminating 0 */ 484 ptr += strlen((char*)ptr); /* ptr += size below jumps over 485 the terminating 0 */ 486 break; 487 case XA_ATOM: 488 { 489 Atom a = *(uint32_t*)ptr; 490 printf("\"%s\" (%ld)", 491 (a) ? XGetAtomName(dpy, a) : "None", 492 a); 493 break; 494 } 495 break; 496 default: 497 if (float_atom != None && act_type == float_atom) 498 { 499 printf("%f", *((float*)ptr)); 500 break; 501 } 502 503 printf("\t... of unknown type %s\n", 504 XGetAtomName(dpy, act_type)); 505 done = True; 506 break; 507 } 508 509 ptr += act_format/8; 510 511 if (done == True) 512 break; 513 if (j < nitems - 1) 514 printf(", "); 515 } 516 printf("\n"); 517 XFree(data); 518 } else 519 printf("\tFetch failure\n"); 520 521} 522 523static int 524list_props_xi2(Display *dpy, int argc, char** argv, char* name, char *desc) 525{ 526 XIDeviceInfo *info; 527 int i; 528 int nprops; 529 Atom *props; 530 531 if (argc == 0) 532 { 533 fprintf(stderr, "Usage: xinput %s %s\n", name, desc); 534 return EXIT_FAILURE; 535 } 536 537 for (i = 0; i < argc; i++) 538 { 539 info = xi2_find_device_info(dpy, argv[i]); 540 if (!info) 541 { 542 fprintf(stderr, "unable to find device %s\n", argv[i]); 543 continue; 544 } 545 546 props = XIListProperties(dpy, info->deviceid, &nprops); 547 if (!nprops) 548 { 549 printf("Device '%s' does not report any properties.\n", info->name); 550 continue; 551 } 552 553 printf("Device '%s':\n", info->name); 554 while(nprops--) 555 { 556 print_property_xi2(dpy, info->deviceid, props[nprops]); 557 } 558 559 XFree(props); 560 } 561 return EXIT_SUCCESS; 562} 563 564static int 565delete_prop_xi2(Display *dpy, int argc, char** argv, char* n, char *desc) 566{ 567 XIDeviceInfo *info; 568 char *name; 569 Atom prop; 570 571 info = xi2_find_device_info(dpy, argv[0]); 572 if (!info) 573 { 574 fprintf(stderr, "unable to find device %s\n", argv[0]); 575 return EXIT_FAILURE; 576 } 577 578 name = argv[1]; 579 580 prop = parse_atom(dpy, name); 581 582 XIDeleteProperty(dpy, info->deviceid, prop); 583 584 return EXIT_SUCCESS; 585} 586 587static int 588do_set_prop_xi2(Display *dpy, Atom type, int format, int argc, char **argv, char *n, char *desc) 589{ 590 XIDeviceInfo *info; 591 Atom prop; 592 Atom old_type; 593 char *name; 594 int i; 595 Atom float_atom; 596 int old_format, nelements = 0; 597 unsigned long act_nitems, bytes_after; 598 char *endptr; 599 union { 600 unsigned char *c; 601 int16_t *s; 602 int32_t *l; 603 } data; 604 605 if (argc < 3) 606 { 607 fprintf(stderr, "Usage: xinput %s %s\n", n, desc); 608 return EXIT_FAILURE; 609 } 610 611 info = xi2_find_device_info(dpy, argv[0]); 612 if (!info) 613 { 614 fprintf(stderr, "unable to find device %s\n", argv[0]); 615 return EXIT_FAILURE; 616 } 617 618 name = argv[1]; 619 620 prop = parse_atom(dpy, name); 621 622 if (prop == None) { 623 fprintf(stderr, "invalid property '%s'\n", name); 624 return EXIT_FAILURE; 625 } 626 627 float_atom = XInternAtom(dpy, "FLOAT", False); 628 629 nelements = argc - 2; 630 if (type == None || format == 0) { 631 if (XIGetProperty(dpy, info->deviceid, prop, 0, 0, False, 632 AnyPropertyType, &old_type, &old_format, &act_nitems, 633 &bytes_after, &data.c) != Success) { 634 fprintf(stderr, "failed to get property type and format for '%s'\n", 635 name); 636 return EXIT_FAILURE; 637 } else { 638 if (type == None) 639 type = old_type; 640 if (format == 0) 641 format = old_format; 642 } 643 644 XFree(data.c); 645 } 646 647 if (type == None) { 648 fprintf(stderr, "property '%s' doesn't exist, you need to specify " 649 "its type and format\n", name); 650 return EXIT_FAILURE; 651 } 652 653 data.c = calloc(nelements, sizeof(int32_t)); 654 655 for (i = 0; i < nelements; i++) 656 { 657 if (type == XA_INTEGER || type == XA_CARDINAL) { 658 switch (format) 659 { 660 case 8: 661 data.c[i] = atoi(argv[2 + i]); 662 break; 663 case 16: 664 data.s[i] = atoi(argv[2 + i]); 665 break; 666 case 32: 667 data.l[i] = atoi(argv[2 + i]); 668 break; 669 default: 670 fprintf(stderr, "unexpected size for property %s", name); 671 return EXIT_FAILURE; 672 } 673 } else if (type == float_atom) { 674 if (format != 32) { 675 fprintf(stderr, "unexpected format %d for property '%s'\n", 676 format, name); 677 return EXIT_FAILURE; 678 } 679 *(float *)(data.l + i) = strtod(argv[2 + i], &endptr); 680 if (endptr == argv[2 + i]) { 681 fprintf(stderr, "argument %s could not be parsed\n", argv[2 + i]); 682 return EXIT_FAILURE; 683 } 684 } else if (type == XA_ATOM) { 685 if (format != 32) { 686 fprintf(stderr, "unexpected format %d for property '%s'\n", 687 format, name); 688 return EXIT_FAILURE; 689 } 690 data.l[i] = parse_atom(dpy, argv[2 + i]); 691 } else { 692 fprintf(stderr, "unexpected type for property '%s'\n", name); 693 return EXIT_FAILURE; 694 } 695 } 696 697 XIChangeProperty(dpy, info->deviceid, prop, type, format, PropModeReplace, 698 data.c, nelements); 699 free(data.c); 700 return EXIT_SUCCESS; 701} 702#endif 703 704int list_props(Display *display, int argc, char *argv[], char *name, 705 char *desc) 706{ 707#ifdef HAVE_XI2 708 if (xinput_version(display) == XI_2_Major) 709 return list_props_xi2(display, argc, argv, name, desc); 710#endif 711 return list_props_xi1(display, argc, argv, name, desc); 712 713} 714 715int delete_prop(Display *display, int argc, char *argv[], char *name, 716 char *desc) 717{ 718#ifdef HAVE_XI2 719 if (xinput_version(display) == XI_2_Major) 720 return delete_prop_xi2(display, argc, argv, name, desc); 721#endif 722 return delete_prop_xi1(display, argc, argv, name, desc); 723 724} 725 726static int 727do_set_prop(Display *display, Atom type, int format, int argc, char *argv[], char *name, char *desc) 728{ 729#ifdef HAVE_XI2 730 if (xinput_version(display) == XI_2_Major) 731 return do_set_prop_xi2(display, type, format, argc, argv, name, desc); 732#endif 733 return do_set_prop_xi1(display, type, format, argc, argv, name, desc); 734} 735 736int 737set_atom_prop(Display *dpy, int argc, char** argv, char* n, char *desc) 738{ 739 return do_set_prop(dpy, XA_ATOM, 32, argc, argv, n, desc); 740} 741 742int 743set_int_prop(Display *dpy, int argc, char** argv, char* n, char *desc) 744{ 745 int i; 746 int format; 747 748 if (argc < 3) 749 { 750 fprintf(stderr, "Usage: xinput %s %s\n", n, desc); 751 return EXIT_FAILURE; 752 } 753 754 format = atoi(argv[2]); 755 if (format != 8 && format != 16 && format != 32) 756 { 757 fprintf(stderr, "Invalid format %d\n", format); 758 return EXIT_FAILURE; 759 } 760 761 for (i = 3; i < argc; i++) 762 argv[i - 1] = argv[i]; 763 764 return do_set_prop(dpy, XA_INTEGER, format, argc - 1, argv, n, desc); 765} 766 767int 768set_float_prop(Display *dpy, int argc, char** argv, char* n, char *desc) 769{ 770 Atom float_atom = XInternAtom(dpy, "FLOAT", False); 771 772 if (sizeof(float) != 4) 773 { 774 fprintf(stderr, "sane FP required\n"); 775 return EXIT_FAILURE; 776 } 777 778 return do_set_prop(dpy, float_atom, 32, argc, argv, n, desc); 779} 780 781int set_prop(Display *display, int argc, char *argv[], char *name, 782 char *desc) 783{ 784 Atom type = None; 785 int format = 0; 786 int i = 0, j; 787 788 while (i < argc) { 789 char *option = strchr(argv[i], '='); 790 /* skip non-option arguments */ 791 if (strncmp(argv[i], "--", 2) || !option) { 792 i++; 793 continue; 794 } 795 796 if (!strncmp(argv[i], "--type=", strlen("--type="))) { 797 if (!strcmp(option + 1, "int")) { 798 type = XA_INTEGER; 799 } else if (!strcmp(option + 1, "float")) { 800 type = XInternAtom(display, "FLOAT", False); 801 format = 32; 802 } else if (!strcmp(option + 1, "atom")) { 803 type = XA_ATOM; 804 format = 32; 805 } else { 806 fprintf(stderr, "unknown property type %s\n", option + 1); 807 return EXIT_FAILURE; 808 } 809 } else if (!strncmp(argv[i], "--format=", strlen("--format="))) { 810 format = atoi(option + 1); 811 if (format != 8 && format != 16 && format != 32) { 812 fprintf(stderr, "invalid property format '%s'\n", option + 1); 813 return EXIT_FAILURE; 814 } 815 } else { 816 fprintf(stderr, "invalid option '%s'\n", argv[i]); 817 return EXIT_FAILURE; 818 } 819 820 for (j = i; j + 1 < argc; j++) 821 argv[j] = argv[j + 1]; 822 argc--; 823 } 824 825 return do_set_prop(display, type, format, argc, argv, name, desc); 826} 827 828int disable(Display *display, int argc, char *argv[], char *name, char *desc) 829{ 830 char *new_argv[3] = { argv[0], "Device Enabled", "0" }; 831 return set_prop(display, 3, new_argv, name, desc); 832} 833 834int enable(Display *display, int argc, char *argv[], char *name, char *desc) 835{ 836 char *new_argv[3] = { argv[0], "Device Enabled", "1" }; 837 return set_prop(display, 3, new_argv, name, desc); 838} 839