remove warning
[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
35 void printHelp(FILE* out, int argc, char** argv)
36 {
37 (void) argc;
38
39 fprintf(out, "Usage:\n"
40 " %s -h\n"
41 " %s --help\n"
42 " %s {commands} --dev <device>\n"
43 "\n"
44 "Commands:\n"
45 " --list-axismap\n"
46 " --list-buttonmap\n"
47 " --set-axismap 'id, id, id...'\n"
48 " --set-buttonmap 'id, id, id...'\n",
49 argv[0], argv[0], argv[0]);
50 }
51
52
53 __u8 getNumberOfAxes(int fd)
54 {
55 __u8 cnt=0;
56 if (ioctl(fd, JSIOCGAXES, &cnt) )
57 {
58 perror(PACKAGE_STRING ": error getting number of axes");
59 exit(EXIT_IOCTL);
60 }
61 return cnt;
62 }
63
64
65 __u8 getNumberOfButtons(int fd)
66 {
67 __u8 cnt=0;
68 if (ioctl(fd, JSIOCGBUTTONS, &cnt) )
69 {
70 perror(PACKAGE_STRING ": error getting number of buttons");
71 exit(EXIT_IOCTL);
72 }
73 return cnt;
74 }
75
76
77 void listAxismap(int fd)
78 {
79 __u8 map[ABS_CNT] = {-1};
80 const __u8 cnt = getNumberOfAxes(fd);
81 printf("Got %d axes:\n", cnt);
82 assert( cnt <= ABS_CNT);
83
84 if( ioctl(fd, JSIOCGAXMAP, &map) < 0 )
85 {
86 perror(PACKAGE_STRING ": error getting axis map");
87 exit(1);
88 }
89
90 for(int i=0; i<cnt; i++)
91 printf(" %d => %d\n", i, map[i] );
92
93 printf("\n");
94 printf("Set this mapping with:\n");
95 printf(" " PACKAGE_STRING " --set-axismap '");
96 for(int i=0; i<cnt; i++)
97 {
98 if( i> 0)
99 printf(", ");
100 printf("%d", map[i] );
101 }
102 printf("'\n");
103 }
104
105
106 void listButtonmap(int fd)
107 {
108 __u16 map[BTN_CNT] = {-1};
109 const __u8 cnt = getNumberOfButtons(fd);
110 printf("Got %d buttons:\n", cnt);
111
112 if( ioctl(fd, JSIOCGBTNMAP, &map) < 0 )
113 {
114 perror(PACKAGE_STRING ": error getting button map");
115 exit(1);
116 }
117
118 for(int i=0; i<cnt; i++)
119 printf(" %d => %d\n", i, map[i] );
120
121 printf("\n");
122 printf("Set this mapping with:\n");
123 printf(" " PACKAGE_STRING " --set-buttonmap '");
124 for(int i=0; i<cnt; i++)
125 {
126 if( i> 0)
127 printf(", ");
128 printf("%d", map[i] );
129 }
130 printf("'\n");
131 }
132
133
134 /** Fill an integer array of given size by comma-separated values in str.
135 * Return the number of values contained in str. */
136 unsigned parseIntArray(int* array, unsigned size, char* str)
137 {
138 unsigned i;
139 for(i=0; i<size; ++i)
140 {
141 sscanf(str, "%d", &array[i]);
142
143 // Move up to next comma, and one step further
144 str = strstr(str, ",");
145 if( !str )
146 break;
147 str++;
148 }
149
150 // Skip further commas
151 while( str )
152 {
153 i++;
154 str = strstr(str, ",");
155 if( !str )
156 break;
157 str++;
158 }
159
160 return i+1;
161 }
162
163
164 /** Take an array of size that contains cnt values and fill the rest of array
165 * such that a permutation on {0, ..., size-1} results.
166 * Returns 1 if resulting array is a permutation */
167 int fillPermutation(int* array, unsigned cnt, unsigned size)
168 {
169 assert( cnt <= size );
170
171 int* contains = malloc(sizeof(int)*size );
172 memset(contains, 0, sizeof(int)*size );
173
174
175 // Check which value is already there
176 for(unsigned i=0; i<cnt; ++i)
177 {
178 const unsigned v = array[i];
179 if( v>=size || contains[v] )
180 return 0;
181 contains[v] = 1;
182 }
183
184 // Do it the hard way on array[cnt, max]
185 unsigned min=0;
186 for(unsigned i=cnt; i<size; ++i)
187 {
188 // Get minimum free value
189 while( contains[min] )
190 {
191 min++;
192 assert( min<size );
193 }
194
195 array[i] = min;
196 contains[min] = 1;
197 }
198
199 return 1;
200 }
201
202
203 void setAxismap(int fd, char* mapstr)
204 {
205 __u8 map[ABS_CNT] = {-1};
206 int imap[ABS_CNT] = {-1};
207
208
209 const __u8 cnt = getNumberOfAxes(fd);
210 printf("Setting %d axes.\n", cnt);
211 assert( cnt <= ABS_CNT);
212
213
214 const unsigned readcnt = parseIntArray(imap, cnt, mapstr);
215 if( readcnt != cnt )
216 {
217 fprintf(stderr, "Invalid number of values given: %d\n", readcnt);
218 exit(EXIT_VALUE);
219 }
220
221 // Nothing to do
222 if( cnt<=0 )
223 return;
224
225 // Fill the remaining array such that we obtain a permutation
226 if( !fillPermutation(imap, cnt, ABS_CNT) )
227 {
228 fprintf(stderr, "Mapping needs to be a permutation.\n");
229 exit(EXIT_VALUE);
230 }
231
232 // Copy array and check bounds
233 for(unsigned i=0; i<ABS_CNT; ++i)
234 {
235 if( imap[i]<0 || imap[i]>=ABS_CNT )
236 {
237 fprintf(stderr, "Value out of bounds: %d\n", imap[i]);
238 exit(EXIT_VALUE);
239 }
240
241 map[i] = imap[i];
242 }
243
244
245 printf("Setting the following map:\n");
246 for(int i=0; i<cnt; i++)
247 printf(" %d => %d\n", i, map[i] );
248
249
250 if( ioctl(fd, JSIOCSAXMAP, &map) )
251 {
252 perror( PACKAGE_STRING ": error setting axis map");
253 exit(EXIT_IOCTL);
254 }
255 }
256
257
258 void setButtonmap(int fd, char* mapstr)
259 {
260 __u16 map[BTN_CNT] = {-1};
261 int imap[BTN_CNT] = {-1};
262
263 const __u8 cnt = getNumberOfButtons(fd);
264 printf("Setting %d buttons.\n", cnt);
265 assert( cnt <= ABS_CNT);
266
267
268 const unsigned readcnt = parseIntArray(imap, cnt, mapstr);
269 if( readcnt != cnt )
270 {
271 fprintf(stderr, "Invalid number of values given: %d\n", readcnt);
272 exit(EXIT_VALUE);
273 }
274
275 // Nothing to do
276 if( cnt<=0 )
277 return;
278
279 // Fill the remaining array such that we obtain a permutation
280 if( !fillPermutation(imap, cnt, BTN_CNT) )
281 {
282 fprintf(stderr, "Mapping needs to be a permutation.\n");
283 exit(EXIT_VALUE);
284 }
285
286 // Copy array and check bounds
287 for(unsigned i=0; i<ABS_CNT; ++i)
288 {
289 if( imap[i]<0 || imap[i]>=BTN_CNT )
290 {
291 fprintf(stderr, "Value out of bounds: %d\n", imap[i]);
292 exit(EXIT_VALUE);
293 }
294
295 map[i] = imap[i];
296 }
297
298
299 printf("Setting the following map:\n");
300 for(int i=0; i<cnt; i++)
301 printf(" %d => %d\n", i, map[i] );
302
303
304 if( ioctl(fd, JSIOCSBTNMAP, &map) )
305 {
306 perror( PACKAGE_STRING ": error setting axis map");
307 exit(EXIT_IOCTL);
308 }
309 }
310
311
312 int main(int argc, char** argv)
313 {
314 int argListAxismap=0;
315 int argListButtonmap=0;
316 char* argSetAxismap=0;
317 char* argSetButtonmap=0;
318 char* dev=0;
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 }