combjs
diff combjs.c @ 0:15293518d92b
first version, combined joystick seems to be working, max 32 buttons.
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Wed, 13 Jul 2011 07:12:57 +0300 |
parents | |
children | dd02002227a2 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/combjs.c Wed Jul 13 07:12:57 2011 +0300 1.3 @@ -0,0 +1,313 @@ 1.4 +/* 1.5 +combjs - combine multiple joysticks in one virtual joystick 1.6 +Copyright (C) 2011 John Tsiombikas <nuclear@member.fsf.org> 1.7 + 1.8 +This program is free software: you can redistribute it and/or modify 1.9 +it under the terms of the GNU General Public License as published by 1.10 +the Free Software Foundation, either version 3 of the License, or 1.11 +(at your option) any later version. 1.12 + 1.13 +This program is distributed in the hope that it will be useful, 1.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 1.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.16 +GNU General Public License for more details. 1.17 + 1.18 +You should have received a copy of the GNU General Public License 1.19 +along with this program. If not, see <http://www.gnu.org/licenses/>. 1.20 +*/ 1.21 + 1.22 +#include <stdio.h> 1.23 +#include <stdlib.h> 1.24 +#include <string.h> 1.25 +#include <errno.h> 1.26 +#include <limits.h> 1.27 +#include <signal.h> 1.28 +#include <unistd.h> 1.29 +#include <fcntl.h> 1.30 +#include <sys/select.h> 1.31 +#include <sys/ioctl.h> 1.32 +#include <linux/joystick.h> 1.33 +#include <linux/input.h> 1.34 +#include <linux/uinput.h> 1.35 + 1.36 +#define DBG_MAX_BN 32 1.37 + 1.38 +struct joystick { 1.39 + char *name; 1.40 + int fd; 1.41 + int num_bn, num_axes; 1.42 + int bn_offset, axis_offset; 1.43 + 1.44 + struct joystick *next; 1.45 +}; 1.46 + 1.47 +void cleanup(void); 1.48 +int create_virt_js(const char *uinput_dev, const char *devname); 1.49 +int handle_event(struct joystick *js, struct js_event *ev); 1.50 +int add_joystick(const char *devfile); 1.51 +void sig_handler(int sig); 1.52 +int proc_cmdline(int argc, char **argv); 1.53 +void print_usage(const char *argv0); 1.54 + 1.55 + 1.56 +int vfd = -1; 1.57 +const char *virt_dev_name = "combjs - virtual multi-joystick combiner"; 1.58 +const char *uinput_dev_name = "/dev/uinput"; 1.59 +struct joystick *jslist, *jstail; 1.60 +int num_vaxes, num_vbn; 1.61 + 1.62 + 1.63 +int main(int argc, char **argv) 1.64 +{ 1.65 + signal(SIGINT, sig_handler); 1.66 + signal(SIGQUIT, sig_handler); 1.67 + signal(SIGTERM, sig_handler); 1.68 + 1.69 + if(proc_cmdline(argc, argv) == -1) { 1.70 + return 1; 1.71 + } 1.72 + 1.73 + if((vfd = create_virt_js(uinput_dev_name, virt_dev_name)) == -1) { 1.74 + cleanup(); 1.75 + return 1; 1.76 + } 1.77 + 1.78 + for(;;) { 1.79 + fd_set rdset; 1.80 + struct joystick *js = jslist; 1.81 + int maxfd = jstail->fd; /* XXX precalc */ 1.82 + 1.83 + FD_ZERO(&rdset); 1.84 + while(js) { 1.85 + FD_SET(js->fd, &rdset); 1.86 + if(js->fd > maxfd) { 1.87 + maxfd = js->fd; 1.88 + } 1.89 + js = js->next; 1.90 + } 1.91 + 1.92 + while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR); 1.93 + 1.94 + js = jslist; 1.95 + while(js) { 1.96 + if(FD_ISSET(js->fd, &rdset)) { 1.97 + struct js_event ev; 1.98 + if(read(js->fd, &ev, sizeof ev) > 0) { 1.99 + handle_event(js, &ev); 1.100 + } 1.101 + } 1.102 + js = js->next; 1.103 + } 1.104 + } 1.105 + 1.106 + return 0; 1.107 +} 1.108 + 1.109 +void cleanup(void) 1.110 +{ 1.111 + while(jslist) { 1.112 + struct joystick *js = jslist; 1.113 + jslist = jslist->next; 1.114 + 1.115 + close(js->fd); 1.116 + free(js->name); 1.117 + free(js); 1.118 + } 1.119 + 1.120 + if(vfd >= 0) { 1.121 + ioctl(vfd, UI_DEV_DESTROY); 1.122 + close(vfd); 1.123 + } 1.124 +} 1.125 + 1.126 +int create_virt_js(const char *uinput_dev, const char *devname) 1.127 +{ 1.128 + int i, fd; 1.129 + struct uinput_user_dev uidev; 1.130 + 1.131 + if((fd = open(uinput_dev, O_WRONLY | O_NONBLOCK)) == -1) { 1.132 + perror("failed to open uinput device"); 1.133 + return -1; 1.134 + } 1.135 + 1.136 + if(ioctl(fd, UI_SET_EVBIT, EV_ABS) == -1) { 1.137 + close(fd); 1.138 + return -1; 1.139 + } 1.140 + if(ioctl(fd, UI_SET_EVBIT, EV_KEY) == -1) { 1.141 + close(fd); 1.142 + return -1; 1.143 + } 1.144 + if(ioctl(fd, UI_SET_EVBIT, EV_SYN) == -1) { 1.145 + close(fd); 1.146 + return -1; 1.147 + } 1.148 + 1.149 + memset(&uidev, 0, sizeof uidev); 1.150 + strncpy(uidev.name, devname, UINPUT_MAX_NAME_SIZE); 1.151 + uidev.id.bustype = BUS_USB; 1.152 + uidev.id.vendor = 0x1234; 1.153 + uidev.id.product = 0xfedc; 1.154 + uidev.id.version = 1; 1.155 + 1.156 + for(i=0; i<num_vbn; i++) { 1.157 + if(i >= DBG_MAX_BN) break; 1.158 + ioctl(fd, UI_SET_KEYBIT, i + BTN_0); 1.159 + } 1.160 + 1.161 + for(i=0; i<num_vaxes; i++) { 1.162 + ioctl(fd, UI_SET_ABSBIT, i); 1.163 + uidev.absmin[i] = SHRT_MIN; 1.164 + uidev.absmax[i] = SHRT_MAX; 1.165 + } 1.166 + 1.167 + if(write(fd, &uidev, sizeof uidev) == -1) { 1.168 + perror("failed to set virtual device parameters"); 1.169 + close(fd); 1.170 + return -1; 1.171 + } 1.172 + 1.173 + if(ioctl(fd, UI_DEV_CREATE) == -1) { 1.174 + perror("failed to create the virtual device"); 1.175 + close(fd); 1.176 + return -1; 1.177 + } 1.178 + return fd; 1.179 +} 1.180 + 1.181 +int handle_event(struct joystick *js, struct js_event *evin) 1.182 +{ 1.183 + struct input_event evout; 1.184 + 1.185 + memset(&evout, 0, sizeof evout); 1.186 + 1.187 + if(evin->type & JS_EVENT_AXIS) { 1.188 + evout.type = EV_ABS; 1.189 + evout.code = evin->number + js->axis_offset; 1.190 + evout.value = evin->value; 1.191 + 1.192 + } else if(evin->type & JS_EVENT_BUTTON) { 1.193 + evout.type = EV_KEY; 1.194 + evout.code = evin->number + js->bn_offset + BTN_0; 1.195 + evout.value = evin->value; 1.196 + 1.197 + if(evout.code >= BTN_0 + DBG_MAX_BN) return 0; 1.198 + 1.199 + } else { 1.200 + return 0; 1.201 + } 1.202 + 1.203 + if(write(vfd, &evout, sizeof evout) == -1) { 1.204 + perror("failed to generate virtual joystick event"); 1.205 + return -1; 1.206 + } 1.207 + return 0; 1.208 +} 1.209 + 1.210 +int add_joystick(const char *devfile) 1.211 +{ 1.212 + struct joystick *js; 1.213 + char buf[512]; 1.214 + 1.215 + if(!(js = calloc(1, sizeof *js))) { 1.216 + perror("failed to allocate memory"); 1.217 + goto err; 1.218 + } 1.219 + 1.220 + if((js->fd = open(devfile, O_RDONLY | O_NONBLOCK)) == -1) { 1.221 + fprintf(stderr, "failed to open %s: %s\n", devfile, strerror(errno)); 1.222 + goto err; 1.223 + } 1.224 + 1.225 + if(ioctl(js->fd, JSIOCGNAME(sizeof buf), buf) == -1) { 1.226 + fprintf(stderr, "failed to get joystick name for %s: %s\n", devfile, strerror(errno)); 1.227 + strcpy(buf, "<unknown>"); 1.228 + } 1.229 + if(!(js->name = malloc(strlen(buf) + 1))) { 1.230 + perror("failed to allocate memory"); 1.231 + goto err; 1.232 + } 1.233 + strcpy(js->name, buf); 1.234 + 1.235 + if(ioctl(js->fd, JSIOCGBUTTONS, &js->num_bn) == -1) { 1.236 + fprintf(stderr, "failed to get joystick %s's number of buttons: %s\n", devfile, strerror(errno)); 1.237 + goto err; 1.238 + } 1.239 + if(ioctl(js->fd, JSIOCGAXES, &js->num_axes) == -1) { 1.240 + fprintf(stderr, "failed to get joystick %s's number of axes: %s\n", devfile, strerror(errno)); 1.241 + goto err; 1.242 + } 1.243 + 1.244 + if(!jstail) { 1.245 + jslist = jstail = js; 1.246 + } else { 1.247 + js->axis_offset = jstail->axis_offset + jstail->num_axes; 1.248 + js->bn_offset = jstail->bn_offset + jstail->num_bn; 1.249 + jstail->next = js; 1.250 + jstail = js; 1.251 + } 1.252 + 1.253 + num_vaxes = js->axis_offset + js->num_axes; 1.254 + num_vbn = js->bn_offset + js->num_bn; 1.255 + 1.256 + printf("added joystick %s: %s\n", devfile, js->name); 1.257 + printf(" - %d axes (offset: %d)\n", js->num_axes, js->axis_offset); 1.258 + printf(" - %d buttons (offset: %d)\n", js->num_bn, js->bn_offset); 1.259 + return 0; 1.260 + 1.261 +err: 1.262 + if(js) { 1.263 + close(js->fd); 1.264 + free(js->name); 1.265 + free(js); 1.266 + } 1.267 + return -1; 1.268 +} 1.269 + 1.270 +void sig_handler(int sig) 1.271 +{ 1.272 + cleanup(); 1.273 + exit(0); 1.274 +} 1.275 + 1.276 +int proc_cmdline(int argc, char **argv) 1.277 +{ 1.278 + int i; 1.279 + 1.280 + for(i=1; i<argc; i++) { 1.281 + if(argv[i][0] == '-') { 1.282 + if(argv[i][2] == 0) { 1.283 + switch(argv[i][1]) { 1.284 + case 'h': 1.285 + print_usage(argv[0]); 1.286 + exit(0); 1.287 + 1.288 + default: 1.289 + fprintf(stderr, "invalid argument: %s\n", argv[i]); 1.290 + print_usage(argv[0]); 1.291 + return -1; 1.292 + } 1.293 + } else { 1.294 + fprintf(stderr, "invalid argument: %s\n", argv[i]); 1.295 + print_usage(argv[0]); 1.296 + } 1.297 + } else { 1.298 + if(add_joystick(argv[i]) == -1) { 1.299 + return -1; 1.300 + } 1.301 + } 1.302 + } 1.303 + 1.304 + if(!jslist) { 1.305 + fprintf(stderr, "no joystick devices specified\n"); 1.306 + print_usage(argv[0]); 1.307 + return -1; 1.308 + } 1.309 + 1.310 + return 0; 1.311 +} 1.312 + 1.313 +void print_usage(const char *argv0) 1.314 +{ 1.315 + printf("usage: %s <dev 0> [dev1] ... [dev n]\n", argv0); 1.316 +}