rev |
line source |
nuclear@0
|
1 /*
|
nuclear@0
|
2 combjs - combine multiple joysticks in one virtual joystick
|
nuclear@0
|
3 Copyright (C) 2011 John Tsiombikas <nuclear@member.fsf.org>
|
nuclear@0
|
4
|
nuclear@0
|
5 This program is free software: you can redistribute it and/or modify
|
nuclear@0
|
6 it under the terms of the GNU General Public License as published by
|
nuclear@0
|
7 the Free Software Foundation, either version 3 of the License, or
|
nuclear@0
|
8 (at your option) any later version.
|
nuclear@0
|
9
|
nuclear@0
|
10 This program is distributed in the hope that it will be useful,
|
nuclear@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nuclear@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nuclear@0
|
13 GNU General Public License for more details.
|
nuclear@0
|
14
|
nuclear@0
|
15 You should have received a copy of the GNU General Public License
|
nuclear@0
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
nuclear@0
|
17 */
|
nuclear@0
|
18
|
nuclear@0
|
19 #include <stdio.h>
|
nuclear@0
|
20 #include <stdlib.h>
|
nuclear@0
|
21 #include <string.h>
|
nuclear@0
|
22 #include <errno.h>
|
nuclear@0
|
23 #include <limits.h>
|
nuclear@0
|
24 #include <signal.h>
|
nuclear@0
|
25 #include <unistd.h>
|
nuclear@0
|
26 #include <fcntl.h>
|
nuclear@1
|
27 #include <dirent.h>
|
nuclear@0
|
28 #include <sys/select.h>
|
nuclear@0
|
29 #include <sys/ioctl.h>
|
nuclear@0
|
30 #include <linux/joystick.h>
|
nuclear@0
|
31 #include <linux/input.h>
|
nuclear@0
|
32 #include <linux/uinput.h>
|
nuclear@0
|
33
|
nuclear@0
|
34 #define DBG_MAX_BN 32
|
nuclear@0
|
35
|
nuclear@0
|
36 struct joystick {
|
nuclear@1
|
37 char *devfile;
|
nuclear@0
|
38 char *name;
|
nuclear@0
|
39 int fd;
|
nuclear@0
|
40 int num_bn, num_axes;
|
nuclear@0
|
41 int bn_offset, axis_offset;
|
nuclear@0
|
42
|
nuclear@0
|
43 struct joystick *next;
|
nuclear@0
|
44 };
|
nuclear@0
|
45
|
nuclear@0
|
46 void cleanup(void);
|
nuclear@0
|
47 int create_virt_js(const char *uinput_dev, const char *devname);
|
nuclear@0
|
48 int handle_event(struct joystick *js, struct js_event *ev);
|
nuclear@0
|
49 int add_joystick(const char *devfile);
|
nuclear@1
|
50 struct joystick *open_joystick(const char *devfile);
|
nuclear@1
|
51 void close_joystick(struct joystick *js);
|
nuclear@1
|
52 void print_joystick(struct joystick *js, int verbose);
|
nuclear@1
|
53 int list_devices(void);
|
nuclear@1
|
54 int cmp_jsp(const void *a, const void *b);
|
nuclear@0
|
55 void sig_handler(int sig);
|
nuclear@0
|
56 int proc_cmdline(int argc, char **argv);
|
nuclear@0
|
57 void print_usage(const char *argv0);
|
nuclear@0
|
58
|
nuclear@0
|
59
|
nuclear@0
|
60 int vfd = -1;
|
nuclear@1
|
61 const char *virt_name = "combjs - virtual multi-joystick combiner";
|
nuclear@1
|
62 const char *uinput_dev = "/dev/uinput";
|
nuclear@0
|
63 struct joystick *jslist, *jstail;
|
nuclear@0
|
64 int num_vaxes, num_vbn;
|
nuclear@1
|
65 int verbose = 1;
|
nuclear@0
|
66
|
nuclear@0
|
67
|
nuclear@0
|
68 int main(int argc, char **argv)
|
nuclear@0
|
69 {
|
nuclear@0
|
70 signal(SIGINT, sig_handler);
|
nuclear@0
|
71 signal(SIGQUIT, sig_handler);
|
nuclear@0
|
72 signal(SIGTERM, sig_handler);
|
nuclear@0
|
73
|
nuclear@0
|
74 if(proc_cmdline(argc, argv) == -1) {
|
nuclear@0
|
75 return 1;
|
nuclear@0
|
76 }
|
nuclear@0
|
77
|
nuclear@1
|
78 if((vfd = create_virt_js(uinput_dev, virt_name)) == -1) {
|
nuclear@0
|
79 cleanup();
|
nuclear@0
|
80 return 1;
|
nuclear@0
|
81 }
|
nuclear@0
|
82
|
nuclear@1
|
83 /* if we where running suid-root, drop root priviledges */
|
nuclear@1
|
84 if(geteuid() == 0) {
|
nuclear@1
|
85 setuid(getuid());
|
nuclear@1
|
86 }
|
nuclear@1
|
87
|
nuclear@0
|
88 for(;;) {
|
nuclear@0
|
89 fd_set rdset;
|
nuclear@0
|
90 struct joystick *js = jslist;
|
nuclear@0
|
91 int maxfd = jstail->fd; /* XXX precalc */
|
nuclear@0
|
92
|
nuclear@0
|
93 FD_ZERO(&rdset);
|
nuclear@0
|
94 while(js) {
|
nuclear@0
|
95 FD_SET(js->fd, &rdset);
|
nuclear@0
|
96 if(js->fd > maxfd) {
|
nuclear@0
|
97 maxfd = js->fd;
|
nuclear@0
|
98 }
|
nuclear@0
|
99 js = js->next;
|
nuclear@0
|
100 }
|
nuclear@0
|
101
|
nuclear@0
|
102 while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
|
nuclear@0
|
103
|
nuclear@0
|
104 js = jslist;
|
nuclear@0
|
105 while(js) {
|
nuclear@0
|
106 if(FD_ISSET(js->fd, &rdset)) {
|
nuclear@0
|
107 struct js_event ev;
|
nuclear@0
|
108 if(read(js->fd, &ev, sizeof ev) > 0) {
|
nuclear@0
|
109 handle_event(js, &ev);
|
nuclear@0
|
110 }
|
nuclear@0
|
111 }
|
nuclear@0
|
112 js = js->next;
|
nuclear@0
|
113 }
|
nuclear@0
|
114 }
|
nuclear@0
|
115
|
nuclear@0
|
116 return 0;
|
nuclear@0
|
117 }
|
nuclear@0
|
118
|
nuclear@0
|
119 void cleanup(void)
|
nuclear@0
|
120 {
|
nuclear@0
|
121 while(jslist) {
|
nuclear@0
|
122 struct joystick *js = jslist;
|
nuclear@0
|
123 jslist = jslist->next;
|
nuclear@0
|
124
|
nuclear@0
|
125 close(js->fd);
|
nuclear@0
|
126 free(js->name);
|
nuclear@0
|
127 free(js);
|
nuclear@0
|
128 }
|
nuclear@0
|
129
|
nuclear@0
|
130 if(vfd >= 0) {
|
nuclear@0
|
131 ioctl(vfd, UI_DEV_DESTROY);
|
nuclear@0
|
132 close(vfd);
|
nuclear@0
|
133 }
|
nuclear@0
|
134 }
|
nuclear@0
|
135
|
nuclear@0
|
136 int create_virt_js(const char *uinput_dev, const char *devname)
|
nuclear@0
|
137 {
|
nuclear@0
|
138 int i, fd;
|
nuclear@0
|
139 struct uinput_user_dev uidev;
|
nuclear@0
|
140
|
nuclear@0
|
141 if((fd = open(uinput_dev, O_WRONLY | O_NONBLOCK)) == -1) {
|
nuclear@0
|
142 perror("failed to open uinput device");
|
nuclear@0
|
143 return -1;
|
nuclear@0
|
144 }
|
nuclear@0
|
145
|
nuclear@0
|
146 if(ioctl(fd, UI_SET_EVBIT, EV_ABS) == -1) {
|
nuclear@0
|
147 close(fd);
|
nuclear@0
|
148 return -1;
|
nuclear@0
|
149 }
|
nuclear@0
|
150 if(ioctl(fd, UI_SET_EVBIT, EV_KEY) == -1) {
|
nuclear@0
|
151 close(fd);
|
nuclear@0
|
152 return -1;
|
nuclear@0
|
153 }
|
nuclear@0
|
154 if(ioctl(fd, UI_SET_EVBIT, EV_SYN) == -1) {
|
nuclear@0
|
155 close(fd);
|
nuclear@0
|
156 return -1;
|
nuclear@0
|
157 }
|
nuclear@0
|
158
|
nuclear@0
|
159 memset(&uidev, 0, sizeof uidev);
|
nuclear@0
|
160 strncpy(uidev.name, devname, UINPUT_MAX_NAME_SIZE);
|
nuclear@0
|
161 uidev.id.bustype = BUS_USB;
|
nuclear@0
|
162 uidev.id.vendor = 0x1234;
|
nuclear@0
|
163 uidev.id.product = 0xfedc;
|
nuclear@0
|
164 uidev.id.version = 1;
|
nuclear@0
|
165
|
nuclear@0
|
166 for(i=0; i<num_vbn; i++) {
|
nuclear@0
|
167 if(i >= DBG_MAX_BN) break;
|
nuclear@0
|
168 ioctl(fd, UI_SET_KEYBIT, i + BTN_0);
|
nuclear@0
|
169 }
|
nuclear@0
|
170
|
nuclear@0
|
171 for(i=0; i<num_vaxes; i++) {
|
nuclear@0
|
172 ioctl(fd, UI_SET_ABSBIT, i);
|
nuclear@0
|
173 uidev.absmin[i] = SHRT_MIN;
|
nuclear@0
|
174 uidev.absmax[i] = SHRT_MAX;
|
nuclear@0
|
175 }
|
nuclear@0
|
176
|
nuclear@0
|
177 if(write(fd, &uidev, sizeof uidev) == -1) {
|
nuclear@0
|
178 perror("failed to set virtual device parameters");
|
nuclear@0
|
179 close(fd);
|
nuclear@0
|
180 return -1;
|
nuclear@0
|
181 }
|
nuclear@0
|
182
|
nuclear@0
|
183 if(ioctl(fd, UI_DEV_CREATE) == -1) {
|
nuclear@0
|
184 perror("failed to create the virtual device");
|
nuclear@0
|
185 close(fd);
|
nuclear@0
|
186 return -1;
|
nuclear@0
|
187 }
|
nuclear@0
|
188 return fd;
|
nuclear@0
|
189 }
|
nuclear@0
|
190
|
nuclear@0
|
191 int handle_event(struct joystick *js, struct js_event *evin)
|
nuclear@0
|
192 {
|
nuclear@0
|
193 struct input_event evout;
|
nuclear@0
|
194
|
nuclear@0
|
195 memset(&evout, 0, sizeof evout);
|
nuclear@0
|
196
|
nuclear@0
|
197 if(evin->type & JS_EVENT_AXIS) {
|
nuclear@0
|
198 evout.type = EV_ABS;
|
nuclear@0
|
199 evout.code = evin->number + js->axis_offset;
|
nuclear@0
|
200 evout.value = evin->value;
|
nuclear@0
|
201
|
nuclear@0
|
202 } else if(evin->type & JS_EVENT_BUTTON) {
|
nuclear@0
|
203 evout.type = EV_KEY;
|
nuclear@0
|
204 evout.code = evin->number + js->bn_offset + BTN_0;
|
nuclear@0
|
205 evout.value = evin->value;
|
nuclear@0
|
206
|
nuclear@0
|
207 if(evout.code >= BTN_0 + DBG_MAX_BN) return 0;
|
nuclear@0
|
208
|
nuclear@0
|
209 } else {
|
nuclear@0
|
210 return 0;
|
nuclear@0
|
211 }
|
nuclear@0
|
212
|
nuclear@0
|
213 if(write(vfd, &evout, sizeof evout) == -1) {
|
nuclear@0
|
214 perror("failed to generate virtual joystick event");
|
nuclear@0
|
215 return -1;
|
nuclear@0
|
216 }
|
nuclear@0
|
217 return 0;
|
nuclear@0
|
218 }
|
nuclear@0
|
219
|
nuclear@0
|
220 int add_joystick(const char *devfile)
|
nuclear@0
|
221 {
|
nuclear@0
|
222 struct joystick *js;
|
nuclear@0
|
223
|
nuclear@1
|
224 if(!(js = open_joystick(devfile))) {
|
nuclear@1
|
225 return -1;
|
nuclear@0
|
226 }
|
nuclear@0
|
227
|
nuclear@0
|
228 if(!jstail) {
|
nuclear@0
|
229 jslist = jstail = js;
|
nuclear@0
|
230 } else {
|
nuclear@0
|
231 js->axis_offset = jstail->axis_offset + jstail->num_axes;
|
nuclear@0
|
232 js->bn_offset = jstail->bn_offset + jstail->num_bn;
|
nuclear@0
|
233 jstail->next = js;
|
nuclear@0
|
234 jstail = js;
|
nuclear@0
|
235 }
|
nuclear@0
|
236
|
nuclear@0
|
237 num_vaxes = js->axis_offset + js->num_axes;
|
nuclear@0
|
238 num_vbn = js->bn_offset + js->num_bn;
|
nuclear@0
|
239
|
nuclear@1
|
240 if(verbose) {
|
nuclear@1
|
241 printf("added ");
|
nuclear@1
|
242 print_joystick(js, 1);
|
nuclear@1
|
243 }
|
nuclear@0
|
244 return 0;
|
nuclear@1
|
245 }
|
nuclear@0
|
246
|
nuclear@1
|
247 struct joystick *open_joystick(const char *devfile)
|
nuclear@1
|
248 {
|
nuclear@1
|
249 struct joystick *js;
|
nuclear@1
|
250 char buf[512];
|
nuclear@1
|
251
|
nuclear@1
|
252 if(!(js = calloc(1, sizeof *js))) {
|
nuclear@1
|
253 perror("failed to allocate memory");
|
nuclear@1
|
254 close_joystick(js);
|
nuclear@1
|
255 return 0;
|
nuclear@1
|
256 }
|
nuclear@1
|
257
|
nuclear@1
|
258 if(!(js->devfile = malloc(strlen(devfile) + 1))) {
|
nuclear@1
|
259 perror("failed to allocate memory");
|
nuclear@1
|
260 close_joystick(js);
|
nuclear@1
|
261 return 0;
|
nuclear@1
|
262 }
|
nuclear@1
|
263 strcpy(js->devfile, devfile);
|
nuclear@1
|
264
|
nuclear@1
|
265 if((js->fd = open(devfile, O_RDONLY | O_NONBLOCK)) == -1) {
|
nuclear@1
|
266 fprintf(stderr, "failed to open %s: %s\n", devfile, strerror(errno));
|
nuclear@1
|
267 close_joystick(js);
|
nuclear@1
|
268 return 0;
|
nuclear@1
|
269 }
|
nuclear@1
|
270
|
nuclear@1
|
271 if(ioctl(js->fd, JSIOCGNAME(sizeof buf), buf) == -1) {
|
nuclear@1
|
272 fprintf(stderr, "failed to get joystick name for %s: %s\n", devfile, strerror(errno));
|
nuclear@1
|
273 strcpy(buf, "<unknown>");
|
nuclear@1
|
274 }
|
nuclear@1
|
275 if(!(js->name = malloc(strlen(buf) + 1))) {
|
nuclear@1
|
276 perror("failed to allocate memory");
|
nuclear@1
|
277 close_joystick(js);
|
nuclear@1
|
278 return 0;
|
nuclear@1
|
279 }
|
nuclear@1
|
280 strcpy(js->name, buf);
|
nuclear@1
|
281
|
nuclear@1
|
282 if(ioctl(js->fd, JSIOCGBUTTONS, &js->num_bn) == -1) {
|
nuclear@1
|
283 fprintf(stderr, "failed to get joystick %s's number of buttons: %s\n", devfile, strerror(errno));
|
nuclear@1
|
284 close_joystick(js);
|
nuclear@1
|
285 return 0;
|
nuclear@1
|
286 }
|
nuclear@1
|
287 if(ioctl(js->fd, JSIOCGAXES, &js->num_axes) == -1) {
|
nuclear@1
|
288 fprintf(stderr, "failed to get joystick %s's number of axes: %s\n", devfile, strerror(errno));
|
nuclear@1
|
289 close_joystick(js);
|
nuclear@1
|
290 return 0;
|
nuclear@1
|
291 }
|
nuclear@1
|
292 return js;
|
nuclear@1
|
293 }
|
nuclear@1
|
294
|
nuclear@1
|
295 void close_joystick(struct joystick *js)
|
nuclear@1
|
296 {
|
nuclear@0
|
297 if(js) {
|
nuclear@0
|
298 close(js->fd);
|
nuclear@0
|
299 free(js->name);
|
nuclear@0
|
300 free(js);
|
nuclear@0
|
301 }
|
nuclear@1
|
302 }
|
nuclear@1
|
303
|
nuclear@1
|
304 void print_joystick(struct joystick *js, int verbose)
|
nuclear@1
|
305 {
|
nuclear@1
|
306 if(verbose) {
|
nuclear@1
|
307 printf("joystick %s: %s\n", js->devfile, js->name);
|
nuclear@1
|
308 printf(" - %d axes (offset: %d)\n", js->num_axes, js->axis_offset);
|
nuclear@1
|
309 printf(" - %d buttons (offset: %d)\n", js->num_bn, js->bn_offset);
|
nuclear@1
|
310 } else {
|
nuclear@1
|
311 printf("%s: \"%s\", axes: %d, buttons: %d\n", js->devfile, js->name, js->num_axes, js->num_bn);
|
nuclear@1
|
312 }
|
nuclear@1
|
313 }
|
nuclear@1
|
314
|
nuclear@1
|
315 int list_devices(void)
|
nuclear@1
|
316 {
|
nuclear@1
|
317 DIR *dir;
|
nuclear@1
|
318 struct dirent *dent;
|
nuclear@1
|
319 char buf[PATH_MAX];
|
nuclear@1
|
320 struct joystick **jsp_arr, *js;
|
nuclear@1
|
321 int i, num_js = 0;
|
nuclear@1
|
322
|
nuclear@1
|
323 /* we don't need root priviledges for this */
|
nuclear@1
|
324 if(geteuid() == 0) {
|
nuclear@1
|
325 setuid(getuid());
|
nuclear@1
|
326 }
|
nuclear@1
|
327
|
nuclear@1
|
328 if(!(dir = opendir("/dev/input"))) {
|
nuclear@1
|
329 perror("failed to open /dev/input");
|
nuclear@1
|
330 return -1;
|
nuclear@1
|
331 }
|
nuclear@1
|
332
|
nuclear@1
|
333 while((dent = readdir(dir))) {
|
nuclear@1
|
334 if(strstr(dent->d_name, "js") == dent->d_name) {
|
nuclear@1
|
335 sprintf(buf, "/dev/input/%s", dent->d_name);
|
nuclear@1
|
336
|
nuclear@1
|
337 if(!(js = open_joystick(buf))) {
|
nuclear@1
|
338 continue;
|
nuclear@1
|
339 }
|
nuclear@1
|
340
|
nuclear@1
|
341 if(jslist) {
|
nuclear@1
|
342 js->next = jslist;
|
nuclear@1
|
343 }
|
nuclear@1
|
344 jslist = js;
|
nuclear@1
|
345 num_js++;
|
nuclear@1
|
346 }
|
nuclear@1
|
347 }
|
nuclear@1
|
348 closedir(dir);
|
nuclear@1
|
349
|
nuclear@1
|
350 if(!(jsp_arr = malloc(num_js * sizeof *jsp_arr))) {
|
nuclear@1
|
351 perror("failed to allocate memory");
|
nuclear@1
|
352 return -1;
|
nuclear@1
|
353 }
|
nuclear@1
|
354
|
nuclear@1
|
355 js = jslist;
|
nuclear@1
|
356 for(i=0; i<num_js; i++) {
|
nuclear@1
|
357 jsp_arr[i] = js;
|
nuclear@1
|
358 js = js->next;
|
nuclear@1
|
359 }
|
nuclear@1
|
360
|
nuclear@1
|
361 qsort(jsp_arr, num_js, sizeof *jsp_arr, cmp_jsp);
|
nuclear@1
|
362
|
nuclear@1
|
363 for(i=0; i<num_js; i++) {
|
nuclear@1
|
364 print_joystick(jsp_arr[i], 0);
|
nuclear@1
|
365 close_joystick(jsp_arr[i]);
|
nuclear@1
|
366 }
|
nuclear@1
|
367 free(jsp_arr);
|
nuclear@1
|
368
|
nuclear@1
|
369 return 0;
|
nuclear@1
|
370 }
|
nuclear@1
|
371
|
nuclear@1
|
372 int cmp_jsp(const void *a, const void *b)
|
nuclear@1
|
373 {
|
nuclear@1
|
374 return strcmp((*(struct joystick**)a)->devfile, (*(struct joystick**)b)->devfile);
|
nuclear@0
|
375 }
|
nuclear@0
|
376
|
nuclear@0
|
377 void sig_handler(int sig)
|
nuclear@0
|
378 {
|
nuclear@0
|
379 cleanup();
|
nuclear@0
|
380 exit(0);
|
nuclear@0
|
381 }
|
nuclear@0
|
382
|
nuclear@0
|
383 int proc_cmdline(int argc, char **argv)
|
nuclear@0
|
384 {
|
nuclear@0
|
385 int i;
|
nuclear@0
|
386
|
nuclear@0
|
387 for(i=1; i<argc; i++) {
|
nuclear@0
|
388 if(argv[i][0] == '-') {
|
nuclear@0
|
389 if(argv[i][2] == 0) {
|
nuclear@0
|
390 switch(argv[i][1]) {
|
nuclear@1
|
391 case 'd':
|
nuclear@1
|
392 uinput_dev = argv[++i];
|
nuclear@1
|
393 break;
|
nuclear@1
|
394
|
nuclear@1
|
395 case 'n':
|
nuclear@1
|
396 virt_name = argv[++i];
|
nuclear@1
|
397 break;
|
nuclear@1
|
398
|
nuclear@1
|
399 case 'l':
|
nuclear@1
|
400 if(list_devices() == -1) {
|
nuclear@1
|
401 exit(1);
|
nuclear@1
|
402 }
|
nuclear@1
|
403 exit(0);
|
nuclear@1
|
404
|
nuclear@1
|
405 case 's':
|
nuclear@1
|
406 verbose = 0;
|
nuclear@1
|
407 break;
|
nuclear@1
|
408
|
nuclear@0
|
409 case 'h':
|
nuclear@0
|
410 print_usage(argv[0]);
|
nuclear@0
|
411 exit(0);
|
nuclear@0
|
412
|
nuclear@0
|
413 default:
|
nuclear@0
|
414 fprintf(stderr, "invalid argument: %s\n", argv[i]);
|
nuclear@0
|
415 print_usage(argv[0]);
|
nuclear@0
|
416 return -1;
|
nuclear@0
|
417 }
|
nuclear@0
|
418 } else {
|
nuclear@0
|
419 fprintf(stderr, "invalid argument: %s\n", argv[i]);
|
nuclear@0
|
420 print_usage(argv[0]);
|
nuclear@0
|
421 }
|
nuclear@0
|
422 } else {
|
nuclear@0
|
423 if(add_joystick(argv[i]) == -1) {
|
nuclear@0
|
424 return -1;
|
nuclear@0
|
425 }
|
nuclear@0
|
426 }
|
nuclear@0
|
427 }
|
nuclear@0
|
428
|
nuclear@0
|
429 if(!jslist) {
|
nuclear@0
|
430 fprintf(stderr, "no joystick devices specified\n");
|
nuclear@0
|
431 print_usage(argv[0]);
|
nuclear@0
|
432 return -1;
|
nuclear@0
|
433 }
|
nuclear@0
|
434
|
nuclear@0
|
435 return 0;
|
nuclear@0
|
436 }
|
nuclear@0
|
437
|
nuclear@0
|
438 void print_usage(const char *argv0)
|
nuclear@0
|
439 {
|
nuclear@1
|
440 printf("usage: %s [options] <dev 0> [dev1] ... [dev n]\n", argv0);
|
nuclear@1
|
441 printf("options:\n");
|
nuclear@1
|
442 printf(" -d: uinput device file (default: /dev/uinput)\n");
|
nuclear@1
|
443 printf(" -n: name for the virtual combined joystick\n");
|
nuclear@1
|
444 printf(" -s: silent (reduces verbosity)\n");
|
nuclear@1
|
445 printf(" -l: print a list of all the joystick devices and exit\n");
|
nuclear@1
|
446 printf(" -h: print usage information and exit\n");
|
nuclear@0
|
447 }
|