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