combjs

view combjs.c @ 1:dd02002227a2

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