aa794619044128c35ba7cfffaff824d53e774c0d
[joydevmap.git] / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <errno.h>
6
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11
12 #include <sys/ioctl.h>
13 #include <linux/joystick.h>
14
15 #include "config.h"
16
17
18 #define BTN_CNT (KEY_MAX - BTN_MISC + 1)
19
20 #define EXIT_INVARG 1
21 #define EXIT_OPEN 2
22 #define EXIT_IOCTL 3
23 #define EXIT_VERSION 4
24 #define EXIT_VALUE 5
25
26
27
28 void reportMissingArgument(const char* opt)
29 {
30 fprintf(stderr, "Missing argument for option '%s'.\n", opt);
31 exit(EXIT_INVARG);
32 }
33
34 void printHelp(FILE* out, int argc, char** argv)
35 {
36 (void) argc;
37
38 fprintf(out, "Usage:\n"
39 " %s -h\n"
40 " %s --help\n"
41 " %s {commands} --dev <device>\n"
42 "\n"
43 "Commands:\n"
44 " --list-axismap\n"
45 " --list-buttonmap\n"
46 " --set-axismap 'id, id, id...'\n"
47 " --set-buttonmap 'id, id, id...'\n",
48 argv[0], argv[0], argv[0]);
49 }
50
51
52 __u8 getNumberOfAxes(int fd)
53 {
54 __u8 cnt=0;
55 if (ioctl(fd, JSIOCGAXES, &cnt) )
56 {
57 perror(PACKAGE_STRING ": error getting number of axes");
58 exit(EXIT_IOCTL);
59 }
60 return cnt;
61 }
62
63
64 __u8 getNumberOfButtons(int fd)
65 {
66 __u8 cnt=0;
67 if (ioctl(fd, JSIOCGBUTTONS, &cnt) )
68 {
69 perror(PACKAGE_STRING ": error getting number of buttons");
70 exit(EXIT_IOCTL);
71 }
72 return cnt;
73 }
74
75
76 void listAxismap(int fd)
77 {
78 __u8 map[ABS_CNT] = {-1};
79 const __u8 cnt = getNumberOfAxes(fd);
80 printf("Got %d axes:\n", cnt);
81 assert( cnt <= ABS_CNT);
82
83 if( ioctl(fd, JSIOCGAXMAP, &map) < 0 )
84 {
85 perror(PACKAGE_STRING ": error getting axis map");
86 exit(1);
87 }
88
89 for(int i=0; i<cnt; i++)
90 printf(" %d => %d\n", i, map[i] );
91
92 printf("\n");
93 printf("Set this mapping with:\n");
94 printf(" " PACKAGE_STRING " --set-axismap '");
95 for(int i=0; i<cnt; i++)
96 {
97 if( i> 0)
98 printf(", ");
99 printf("%d", map[i] );
100 }
101 printf("'\n");
102 }
103
104
105 void listButtonmap(int fd)
106 {
107 __u16 map[BTN_CNT] = {-1};
108 const __u8 cnt = getNumberOfButtons(fd);
109 printf("Got %d buttons:\n", cnt);
110
111 if( ioctl(fd, JSIOCGBTNMAP, &map) < 0 )
112 {
113 perror(PACKAGE_STRING ": error getting button map");
114 exit(1);
115 }
116
117 for(int i=0; i<cnt; i++)
118 printf(" %d => %d\n", i, map[i] );
119
120 printf("\n");
121 printf("Set this mapping with:\n");
122 printf(" " PACKAGE_STRING " --set-buttonmap '");
123 for(int i=0; i<cnt; i++)
124 {
125 if( i> 0)
126 printf(", ");
127 printf("%d", map[i] );
128 }
129 printf("'\n");
130 }
131
132
133 /** Fill an integer array of given size by comma-separated values in str.
134 * Return the number of values contained in str. */
135 unsigned parseIntArray(int* array, unsigned size, char* str)
136 {
137 unsigned i;
138 for(i=0; i<size; ++i)
139 {
140 sscanf(str, "%d", &array[i]);
141
142 // Move up to next comma, and one step further
143 str = strstr(str, ",");
144 if( !str )
145 break;
146 str++;
147 }
148
149 // Skip further commas
150 while( str )
151 {
152 i++;
153 str = strstr(str, ",");
154 if( !str )
155 break;
156 str++;
157 }
158
159 return i+1;
160 }
161
162
163 /** Take an array of size that contains cnt values and fill the rest of array
164 * such that a permutation on {0, ..., size-1} results.
165 * Returns 1 if resulting array is a permutation */
166 int fillPermutation(int* array, unsigned cnt, unsigned size)
167 {
168 assert( cnt <= size );
169
170 int* contains = malloc(sizeof(int)*size );
171 memset(contains, 0, sizeof(int)*size );
172
173
174 // Check which value is already there
175 for(unsigned i=0; i<cnt; ++i)
176 {
177 const unsigned v = array[i];
178 if( v>=size || contains[v] )
179 return 0;
180 contains[v] = 1;
181 }
182
183 // Do it the hard way on array[cnt, max]
184 unsigned min=0;
185 for(unsigned i=cnt; i<size; ++i)
186 {
187 // Get minimum free value
188 while( contains[min] )
189 {
190 min++;
191 assert( min<size );
192 }
193
194 array[i] = min;
195 contains[min] = 1;
196 }
197
198 return 1;
199 }
200
201
202 void setAxismap(int fd, char* mapstr)
203 {
204 __u8 map[ABS_CNT] = {-1};
205 int imap[ABS_CNT] = {-1};
206
207
208 const __u8 cnt = getNumberOfAxes(fd);
209 printf("Setting %d axes.\n", cnt);
210 assert( cnt <= ABS_CNT);
211
212
213 const unsigned readcnt = parseIntArray(imap, cnt, mapstr);
214 if( readcnt != cnt )
215 {
216 fprintf(stderr, "Invalid number of values given: %d\n", readcnt);
217 exit(EXIT_VALUE);
218 }
219
220 // Nothing to do
221 if( cnt<=0 )
222 return;
223
224 // Fill the remaining array such that we obtain a permutation
225 if( !fillPermutation(imap, cnt, ABS_CNT) )
226 {
227 fprintf(stderr, "Mapping needs to be a permutation.\n");
228 exit(EXIT_VALUE);
229 }
230
231 // Copy array and check bounds
232 for(unsigned i=0; i<ABS_CNT; ++i)
233 {
234 if( imap[i]<0 || imap[i]>=ABS_CNT )
235 {
236 fprintf(stderr, "Value out of bounds: %d\n", imap[i]);
237 exit(EXIT_VALUE);
238 }
239
240 map[i] = imap[i];
241 }
242
243
244 printf("Setting the following map:\n");
245 for(int i=0; i<cnt; i++)
246 printf(" %d => %d\n", i, map[i] );
247
248
249 if( ioctl(fd, JSIOCSAXMAP, &map) )
250 {
251 perror( PACKAGE_STRING ": error setting axis map");
252 exit(EXIT_IOCTL);
253 }
254 }
255
256
257 void setButtonmap(int fd, char* mapstr)
258 {
259 __u16 map[BTN_CNT] = {-1};
260 int imap[BTN_CNT] = {-1};
261
262 const __u8 cnt = getNumberOfButtons(fd);
263 printf("Setting %d buttons.\n", cnt);
264 assert( cnt <= ABS_CNT);
265
266
267 const unsigned readcnt = parseIntArray(imap, cnt, mapstr);
268 if( readcnt != cnt )
269 {
270 fprintf(stderr, "Invalid number of values given: %d\n", readcnt);
271 exit(EXIT_VALUE);
272 }
273
274 // Nothing to do
275 if( cnt<=0 )
276 return;
277
278 // Fill the remaining array such that we obtain a permutation
279 if( !fillPermutation(imap, cnt, BTN_CNT) )
280 {
281 fprintf(stderr, "Mapping needs to be a permutation.\n");
282 exit(EXIT_VALUE);
283 }
284
285 // Copy array and check bounds
286 for(unsigned i=0; i<ABS_CNT; ++i)
287 {
288 if( imap[i]<0 || imap[i]>=BTN_CNT )
289 {
290 fprintf(stderr, "Value out of bounds: %d\n", imap[i]);
291 exit(EXIT_VALUE);
292 }
293
294 map[i] = imap[i];
295 }
296
297
298 printf("Setting the following map:\n");
299 for(int i=0; i<cnt; i++)
300 printf(" %d => %d\n", i, map[i] );
301
302
303 if( ioctl(fd, JSIOCSBTNMAP, &map) )
304 {
305 perror( PACKAGE_STRING ": error setting axis map");
306 exit(EXIT_IOCTL);
307 }
308 }
309
310
311 int main(int argc, char** argv)
312 {
313 int argListAxismap=0;
314 int argListButtonmap=0;
315 char* argSetAxismap=0;
316 char* argSetButtonmap=0;
317 char* dev;
318
319
320
321 for(int i=1; i<argc; ++i)
322 {
323 const char* opt = argv[i];
324
325 if( !strcmp(opt,"--help") || !strcmp(opt,"-h") )
326 {
327 printHelp(stdout, argc, argv);
328 return EXIT_SUCCESS;
329 }
330 else if( !strcmp(opt, "--dev") || !strcmp(opt,"-d") )
331 {
332 i++;
333 if( i >= argc )
334 reportMissingArgument(opt);
335 dev = argv[i];
336 }
337 else if( !strcmp(opt, "--list-axismap") )
338 {
339 argListAxismap = 1;
340 }
341 else if( !strcmp(opt, "--list-buttonmap") )
342 {
343 argListButtonmap = 1;
344 }
345 else if( !strcmp(opt, "--set-axismap") )
346 {
347 i++;
348 if( i >= argc )
349 reportMissingArgument(opt);
350 argSetAxismap = argv[i];
351 }
352 else if( !strcmp(opt, "--set-buttonmap") )
353 {
354 i++;
355 if( i >= argc )
356 reportMissingArgument(opt);
357 argSetButtonmap = argv[i];
358 }
359 else
360 {
361 fprintf(stderr, "Invalid option '%s'.\n", opt);
362 return EXIT_INVARG;
363 }
364 }
365
366
367 if( !dev )
368 {
369 fprintf(stderr, "You need to specify a device.\n");
370 return EXIT_INVARG;
371 }
372
373
374 int fd=-1;
375 if( (fd = open(dev, O_RDONLY)) < 0 )
376 {
377 perror("Cannot open device.");
378 return EXIT_OPEN;
379 }
380
381
382 int version;
383 if( ioctl(fd, JSIOCGVERSION, &version) )
384 {
385 perror( PACKAGE_STRING ": error getting version");
386 exit(EXIT_IOCTL);
387 }
388
389 if( version != JS_VERSION )
390 {
391 fprintf(stderr, PACKAGE_STRING ": compiled with different version %d\n", JS_VERSION);
392 exit(EXIT_VERSION);
393 }
394
395
396 if( argListAxismap )
397 listAxismap(fd);
398
399 if( argListButtonmap )
400 listButtonmap(fd);
401
402 if( argSetAxismap )
403 setAxismap(fd, argSetAxismap);
404
405 if( argSetButtonmap )
406 setButtonmap(fd, argSetButtonmap);
407
408
409 close(fd);
410 return 0;
411 }