xinput.c revision 41667cea
1/*
2 * Copyright 1996 by Frederic Lepied, France. <Frederic.Lepied@sugix.frmug.org>
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is  hereby granted without fee, provided that
6 * the  above copyright   notice appear  in   all  copies and  that both  that
7 * copyright  notice   and   this  permission   notice  appear  in  supporting
8 * documentation, and that   the  name of  the authors  not  be  used  in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific,  written      prior  permission.     The authors  make  no
11 * representations about the suitability of this software for any purpose.  It
12 * is provided "as is" without express or implied warranty.
13 *
14 * THE AUTHORS DISCLAIM ALL   WARRANTIES WITH REGARD  TO  THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED   WARRANTIES OF MERCHANTABILITY  AND   FITNESS, IN NO
16 * EVENT  SHALL THE AUTHORS  BE   LIABLE   FOR ANY  SPECIAL, INDIRECT   OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA  OR PROFITS, WHETHER  IN  AN ACTION OF  CONTRACT,  NEGLIGENCE OR OTHER
19 * TORTIOUS  ACTION, ARISING    OUT OF OR   IN  CONNECTION  WITH THE USE    OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 *
22 */
23
24#include "xinput.h"
25#include <ctype.h>
26#include <string.h>
27
28int xi_opcode;
29
30typedef int (*prog)(Display* display, int argc, char *argv[],
31		    char *prog_name, char *prog_desc);
32
33typedef struct
34{
35    char	*func_name;
36    char	*arg_desc;
37    prog	func;
38} entry;
39
40static entry drivers[] =
41{
42    {"get-feedbacks",
43     "<device name>",
44     get_feedbacks
45    },
46    {"set-ptr-feedback",
47     "<device name> <threshold> <num> <denom>",
48     set_ptr_feedback
49    },
50    {"set-integer-feedback",
51     "<device name> <feedback id> <value>",
52     set_integer_feedback
53    },
54    {"get-button-map",
55     "<device name>",
56     get_button_map
57    },
58    {"set-button-map",
59     "<device name> <map button 1> [<map button 2> [...]]",
60     set_button_map
61    },
62    {"set-pointer",
63     "<device name> [<x index> <y index>]",
64     set_pointer
65    },
66    {"set-mode",
67     "<device name> ABSOLUTE|RELATIVE",
68     set_mode
69    },
70    {"list",
71     "[--short || --long || --name-only || --id-only] [<device name>...]",
72     list
73    },
74    {"query-state",
75     "<device name>",
76     query_state
77    },
78    {"test",
79     "[-proximity] <device name>",
80     test
81    },
82#if HAVE_XI2
83    { "create-master",
84      "<id> [<sendCore (dflt:1)>] [<enable (dflt:1)>]",
85      create_master
86    },
87    { "remove-master",
88      "<id> [Floating|AttachToMaster (dflt:Floating)] [<returnPointer>] [<returnKeyboard>]",
89      remove_master
90    },
91    { "reattach",
92      "<id> <master>",
93      change_attachment
94    },
95    { "float",
96      "<id>",
97      float_device
98    },
99    { "set-cp",
100      "<window> <device>",
101      set_clientpointer
102    },
103    { "test-xi2",
104      "[--root] <device>",
105      test_xi2,
106    },
107    { "map-to-output",
108      "<device> <output name>",
109      map_to_output,
110    },
111#endif
112    { "list-props",
113      "<device> [<device> ...]",
114      list_props
115    },
116    { "set-int-prop",
117      "<device> <property> <format (8, 16, 32)> <val> [<val> ...]",
118      set_int_prop
119    },
120    { "set-float-prop",
121      "<device> <property> <val> [<val> ...]",
122      set_float_prop
123    },
124    { "set-atom-prop",
125      "<device> <property> <val> [<val> ...]",
126      set_atom_prop
127    },
128    { "watch-props",
129      "<device>",
130      watch_props
131    },
132    { "delete-prop",
133      "<device> <property>",
134      delete_prop
135    },
136    { "set-prop",
137      "<device> [--type=atom|float|int] [--format=8|16|32] <property> <val> [<val> ...]",
138      set_prop
139    },
140    {
141      "disable",
142      "<device>",
143      disable,
144    },
145    {
146      "enable",
147      "<device>",
148      enable,
149    },
150    {NULL, NULL, NULL
151    }
152};
153
154static const char version_id[] = VERSION;
155
156static int
157print_version(void)
158{
159    XExtensionVersion	*version;
160    Display *display;
161
162    printf("xinput version %s\n", version_id);
163
164    display = XOpenDisplay(NULL);
165
166    printf("XI version on server: ");
167
168    if (display == NULL)
169        printf("Failed to open display.\n");
170    else {
171        version = XGetExtensionVersion(display, INAME);
172        if (!version || (version == (XExtensionVersion*) NoSuchExtension))
173            printf(" Extension not supported.\n");
174        else {
175            printf("%d.%d\n", version->major_version,
176                    version->minor_version);
177            XFree(version);
178            return 0;
179        }
180    }
181
182    return 1;
183}
184
185int
186xinput_version(Display	*display)
187{
188    XExtensionVersion	*version;
189    static int vers = -1;
190
191    if (vers != -1)
192        return vers;
193
194    version = XGetExtensionVersion(display, INAME);
195
196    if (version && (version != (XExtensionVersion*) NoSuchExtension)) {
197	vers = version->major_version;
198	XFree(version);
199    }
200
201#if HAVE_XI2
202    /* Announce our supported version so the server treats us correctly. */
203    if (vers >= XI_2_Major)
204    {
205        const char *forced_version;
206        int maj = 2,
207            min = 0;
208
209#if HAVE_XI22
210        min = 2;
211#elif HAVE_XI21
212        min = 1;
213#endif
214
215        forced_version = getenv("XINPUT_XI2_VERSION");
216        if (forced_version) {
217            if (sscanf(forced_version, "%d.%d", &maj, &min) != 2) {
218                fprintf(stderr, "Invalid format of XINPUT_XI2_VERSION "
219                                "environment variable. Need major.minor\n");
220                exit(1);
221            }
222            printf("Overriding XI2 version to: %d.%d\n", maj, min);
223        }
224
225        XIQueryVersion(display, &maj, &min);
226    }
227#endif
228
229    return vers;
230}
231
232XDeviceInfo*
233find_device_info(Display	*display,
234		 char		*name,
235		 Bool		only_extended)
236{
237    XDeviceInfo	*devices;
238    XDeviceInfo *found = NULL;
239    int		loop;
240    int		num_devices;
241    int		len = strlen(name);
242    Bool	is_id = True;
243    XID		id = (XID)-1;
244
245    for(loop=0; loop<len; loop++) {
246	if (!isdigit(name[loop])) {
247	    is_id = False;
248	    break;
249	}
250    }
251
252    if (is_id) {
253	id = atoi(name);
254    }
255
256    devices = XListInputDevices(display, &num_devices);
257
258    for(loop=0; loop<num_devices; loop++) {
259	if ((!only_extended || (devices[loop].use >= IsXExtensionDevice)) &&
260	    ((!is_id && strcmp(devices[loop].name, name) == 0) ||
261	     (is_id && devices[loop].id == id))) {
262	    if (found) {
263	        fprintf(stderr,
264	                "Warning: There are multiple devices named '%s'.\n"
265	                "To ensure the correct one is selected, please use "
266	                "the device ID instead.\n\n", name);
267		return NULL;
268	    } else {
269		found = &devices[loop];
270	    }
271	}
272    }
273    return found;
274}
275
276#ifdef HAVE_XI2
277Bool is_pointer(int use)
278{
279    return use == XIMasterPointer || use == XISlavePointer;
280}
281
282Bool is_keyboard(int use)
283{
284    return use == XIMasterKeyboard || use == XISlaveKeyboard;
285}
286
287Bool device_matches(XIDeviceInfo *info, char *name)
288{
289    if (strcmp(info->name, name) == 0) {
290        return True;
291    }
292
293    if (strncmp(name, "pointer:", strlen("pointer:")) == 0 &&
294        strcmp(info->name, name + strlen("pointer:")) == 0 &&
295        is_pointer(info->use)) {
296        return True;
297    }
298
299    if (strncmp(name, "keyboard:", strlen("keyboard:")) == 0 &&
300        strcmp(info->name, name + strlen("keyboard:")) == 0 &&
301        is_keyboard(info->use)) {
302        return True;
303    }
304
305    return False;
306}
307
308XIDeviceInfo*
309xi2_find_device_info(Display *display, char *name)
310{
311    XIDeviceInfo *info;
312    XIDeviceInfo *found = NULL;
313    int ndevices;
314    Bool is_id = True;
315    int i, id = -1;
316
317    for(i = 0; i < strlen(name); i++) {
318	if (!isdigit(name[i])) {
319	    is_id = False;
320	    break;
321	}
322    }
323
324    if (is_id) {
325	id = atoi(name);
326    }
327
328    info = XIQueryDevice(display, XIAllDevices, &ndevices);
329    for(i = 0; i < ndevices; i++)
330    {
331        if (is_id ? info[i].deviceid == id : device_matches (&info[i], name)) {
332            if (found) {
333                fprintf(stderr,
334                        "Warning: There are multiple devices matching '%s'.\n"
335                        "To ensure the correct one is selected, please use "
336                        "the device ID, or prefix the\ndevice name with "
337                        "'pointer:' or 'keyboard:' as appropriate.\n\n", name);
338                XIFreeDeviceInfo(info);
339                return NULL;
340            } else {
341                found = &info[i];
342            }
343        }
344    }
345
346    return found;
347}
348#endif
349
350static void
351usage(void)
352{
353    entry	*pdriver = drivers;
354
355    fprintf(stderr, "usage :\n");
356
357    while(pdriver->func_name) {
358	fprintf(stderr, "\txinput %s %s\n", pdriver->func_name,
359		pdriver->arg_desc);
360	pdriver++;
361    }
362}
363
364int
365main(int argc, char * argv[])
366{
367    Display	*display;
368    entry	*driver = drivers;
369    char        *func;
370    int event, error;
371
372    if (argc > 1) {
373	func = argv[1];
374	while(func[0] == '-') func++;
375    } else {
376	func = "list";
377    }
378
379    if (strcmp("version", func) == 0) {
380        return print_version();
381    }
382
383    if (strcmp("help", func) == 0) {
384        usage();
385        return 0;
386    }
387
388    display = XOpenDisplay(NULL);
389
390    if (display == NULL) {
391	fprintf(stderr, "Unable to connect to X server\n");
392	goto out;
393    }
394
395    if (!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error)) {
396        printf("X Input extension not available.\n");
397        goto out;
398    }
399
400    if (!xinput_version(display)) {
401	fprintf(stderr, "%s extension not available\n", INAME);
402	goto out;
403    }
404
405    while(driver->func_name) {
406	if (strcmp(driver->func_name, func) == 0) {
407	    int	r = (*driver->func)(display, argc-2, argv+2,
408				    driver->func_name, driver->arg_desc);
409	    XSync(display, False);
410	    XCloseDisplay(display);
411	    return r;
412	}
413	driver++;
414    }
415
416    usage();
417
418out:
419    if (display)
420        XCloseDisplay(display);
421    return EXIT_FAILURE;
422}
423
424/* end of xinput.c */
425