combjs

view 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 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 <sys/select.h>
28 #include <sys/ioctl.h>
29 #include <linux/joystick.h>
30 #include <linux/input.h>
31 #include <linux/uinput.h>
33 #define DBG_MAX_BN 32
35 struct joystick {
36 char *name;
37 int fd;
38 int num_bn, num_axes;
39 int bn_offset, axis_offset;
41 struct joystick *next;
42 };
44 void cleanup(void);
45 int create_virt_js(const char *uinput_dev, const char *devname);
46 int handle_event(struct joystick *js, struct js_event *ev);
47 int add_joystick(const char *devfile);
48 void sig_handler(int sig);
49 int proc_cmdline(int argc, char **argv);
50 void print_usage(const char *argv0);
53 int vfd = -1;
54 const char *virt_dev_name = "combjs - virtual multi-joystick combiner";
55 const char *uinput_dev_name = "/dev/uinput";
56 struct joystick *jslist, *jstail;
57 int num_vaxes, num_vbn;
60 int main(int argc, char **argv)
61 {
62 signal(SIGINT, sig_handler);
63 signal(SIGQUIT, sig_handler);
64 signal(SIGTERM, sig_handler);
66 if(proc_cmdline(argc, argv) == -1) {
67 return 1;
68 }
70 if((vfd = create_virt_js(uinput_dev_name, virt_dev_name)) == -1) {
71 cleanup();
72 return 1;
73 }
75 for(;;) {
76 fd_set rdset;
77 struct joystick *js = jslist;
78 int maxfd = jstail->fd; /* XXX precalc */
80 FD_ZERO(&rdset);
81 while(js) {
82 FD_SET(js->fd, &rdset);
83 if(js->fd > maxfd) {
84 maxfd = js->fd;
85 }
86 js = js->next;
87 }
89 while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
91 js = jslist;
92 while(js) {
93 if(FD_ISSET(js->fd, &rdset)) {
94 struct js_event ev;
95 if(read(js->fd, &ev, sizeof ev) > 0) {
96 handle_event(js, &ev);
97 }
98 }
99 js = js->next;
100 }
101 }
103 return 0;
104 }
106 void cleanup(void)
107 {
108 while(jslist) {
109 struct joystick *js = jslist;
110 jslist = jslist->next;
112 close(js->fd);
113 free(js->name);
114 free(js);
115 }
117 if(vfd >= 0) {
118 ioctl(vfd, UI_DEV_DESTROY);
119 close(vfd);
120 }
121 }
123 int create_virt_js(const char *uinput_dev, const char *devname)
124 {
125 int i, fd;
126 struct uinput_user_dev uidev;
128 if((fd = open(uinput_dev, O_WRONLY | O_NONBLOCK)) == -1) {
129 perror("failed to open uinput device");
130 return -1;
131 }
133 if(ioctl(fd, UI_SET_EVBIT, EV_ABS) == -1) {
134 close(fd);
135 return -1;
136 }
137 if(ioctl(fd, UI_SET_EVBIT, EV_KEY) == -1) {
138 close(fd);
139 return -1;
140 }
141 if(ioctl(fd, UI_SET_EVBIT, EV_SYN) == -1) {
142 close(fd);
143 return -1;
144 }
146 memset(&uidev, 0, sizeof uidev);
147 strncpy(uidev.name, devname, UINPUT_MAX_NAME_SIZE);
148 uidev.id.bustype = BUS_USB;
149 uidev.id.vendor = 0x1234;
150 uidev.id.product = 0xfedc;
151 uidev.id.version = 1;
153 for(i=0; i<num_vbn; i++) {
154 if(i >= DBG_MAX_BN) break;
155 ioctl(fd, UI_SET_KEYBIT, i + BTN_0);
156 }
158 for(i=0; i<num_vaxes; i++) {
159 ioctl(fd, UI_SET_ABSBIT, i);
160 uidev.absmin[i] = SHRT_MIN;
161 uidev.absmax[i] = SHRT_MAX;
162 }
164 if(write(fd, &uidev, sizeof uidev) == -1) {
165 perror("failed to set virtual device parameters");
166 close(fd);
167 return -1;
168 }
170 if(ioctl(fd, UI_DEV_CREATE) == -1) {
171 perror("failed to create the virtual device");
172 close(fd);
173 return -1;
174 }
175 return fd;
176 }
178 int handle_event(struct joystick *js, struct js_event *evin)
179 {
180 struct input_event evout;
182 memset(&evout, 0, sizeof evout);
184 if(evin->type & JS_EVENT_AXIS) {
185 evout.type = EV_ABS;
186 evout.code = evin->number + js->axis_offset;
187 evout.value = evin->value;
189 } else if(evin->type & JS_EVENT_BUTTON) {
190 evout.type = EV_KEY;
191 evout.code = evin->number + js->bn_offset + BTN_0;
192 evout.value = evin->value;
194 if(evout.code >= BTN_0 + DBG_MAX_BN) return 0;
196 } else {
197 return 0;
198 }
200 if(write(vfd, &evout, sizeof evout) == -1) {
201 perror("failed to generate virtual joystick event");
202 return -1;
203 }
204 return 0;
205 }
207 int add_joystick(const char *devfile)
208 {
209 struct joystick *js;
210 char buf[512];
212 if(!(js = calloc(1, sizeof *js))) {
213 perror("failed to allocate memory");
214 goto err;
215 }
217 if((js->fd = open(devfile, O_RDONLY | O_NONBLOCK)) == -1) {
218 fprintf(stderr, "failed to open %s: %s\n", devfile, strerror(errno));
219 goto err;
220 }
222 if(ioctl(js->fd, JSIOCGNAME(sizeof buf), buf) == -1) {
223 fprintf(stderr, "failed to get joystick name for %s: %s\n", devfile, strerror(errno));
224 strcpy(buf, "<unknown>");
225 }
226 if(!(js->name = malloc(strlen(buf) + 1))) {
227 perror("failed to allocate memory");
228 goto err;
229 }
230 strcpy(js->name, buf);
232 if(ioctl(js->fd, JSIOCGBUTTONS, &js->num_bn) == -1) {
233 fprintf(stderr, "failed to get joystick %s's number of buttons: %s\n", devfile, strerror(errno));
234 goto err;
235 }
236 if(ioctl(js->fd, JSIOCGAXES, &js->num_axes) == -1) {
237 fprintf(stderr, "failed to get joystick %s's number of axes: %s\n", devfile, strerror(errno));
238 goto err;
239 }
241 if(!jstail) {
242 jslist = jstail = js;
243 } else {
244 js->axis_offset = jstail->axis_offset + jstail->num_axes;
245 js->bn_offset = jstail->bn_offset + jstail->num_bn;
246 jstail->next = js;
247 jstail = js;
248 }
250 num_vaxes = js->axis_offset + js->num_axes;
251 num_vbn = js->bn_offset + js->num_bn;
253 printf("added joystick %s: %s\n", devfile, js->name);
254 printf(" - %d axes (offset: %d)\n", js->num_axes, js->axis_offset);
255 printf(" - %d buttons (offset: %d)\n", js->num_bn, js->bn_offset);
256 return 0;
258 err:
259 if(js) {
260 close(js->fd);
261 free(js->name);
262 free(js);
263 }
264 return -1;
265 }
267 void sig_handler(int sig)
268 {
269 cleanup();
270 exit(0);
271 }
273 int proc_cmdline(int argc, char **argv)
274 {
275 int i;
277 for(i=1; i<argc; i++) {
278 if(argv[i][0] == '-') {
279 if(argv[i][2] == 0) {
280 switch(argv[i][1]) {
281 case 'h':
282 print_usage(argv[0]);
283 exit(0);
285 default:
286 fprintf(stderr, "invalid argument: %s\n", argv[i]);
287 print_usage(argv[0]);
288 return -1;
289 }
290 } else {
291 fprintf(stderr, "invalid argument: %s\n", argv[i]);
292 print_usage(argv[0]);
293 }
294 } else {
295 if(add_joystick(argv[i]) == -1) {
296 return -1;
297 }
298 }
299 }
301 if(!jslist) {
302 fprintf(stderr, "no joystick devices specified\n");
303 print_usage(argv[0]);
304 return -1;
305 }
307 return 0;
308 }
310 void print_usage(const char *argv0)
311 {
312 printf("usage: %s <dev 0> [dev1] ... [dev n]\n", argv0);
313 }