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