combjs

annotate combjs.c @ 2:4e8f2bbe8426

doing the gui
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 15 Jul 2011 06:54:32 +0300
parents 15293518d92b
children
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 }