e14743d1 |
1 | /* |
2 | SDL - Simple DirectMedia Layer |
3 | Copyright (C) 1997-2009 Sam Lantinga |
4 | |
5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | This library 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 GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with this library; if not, write to the Free Software |
17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
18 | |
19 | Sam Lantinga |
20 | slouken@libsdl.org |
21 | */ |
22 | #include "SDL_config.h" |
23 | |
24 | #ifdef SDL_JOYSTICK_LINUX |
25 | |
26 | /* This is the system specific header for the SDL joystick API */ |
27 | |
28 | #include <sys/stat.h> |
29 | #include <unistd.h> |
30 | #include <fcntl.h> |
31 | #include <sys/ioctl.h> |
32 | #include <limits.h> /* For the definition of PATH_MAX */ |
33 | #include <linux/joystick.h> |
34 | #if SDL_INPUT_LINUXEV |
35 | #include <linux/input.h> |
36 | #endif |
37 | |
38 | #include "SDL_joystick.h" |
39 | #include "../SDL_sysjoystick.h" |
40 | #include "../SDL_joystick_c.h" |
41 | |
42 | /* Special joystick configurations */ |
43 | static struct { |
44 | const char *name; |
45 | int naxes; |
46 | int nhats; |
47 | int nballs; |
48 | } special_joysticks[] = { |
49 | { "MadCatz Panther XL", 3, 2, 1 }, /* We don't handle rudder (axis 8) */ |
50 | { "SideWinder Precision Pro", 4, 1, 0 }, |
51 | { "SideWinder 3D Pro", 4, 1, 0 }, |
52 | { "Microsoft SideWinder 3D Pro", 4, 1, 0 }, |
53 | { "Microsoft SideWinder Precision Pro", 4, 1, 0 }, |
54 | { "Microsoft SideWinder Dual Strike USB version 1.0", 2, 1, 0 }, |
55 | { "WingMan Interceptor", 3, 3, 0 }, |
56 | { "WingMan Extreme Digital 3D", 4, 1, 0 }, |
57 | { "Microsoft SideWinder Precision 2 Joystick", 4, 1, 0 }, |
58 | { "Logitech Inc. WingMan Extreme Digital 3D", 4, 1, 0 }, |
59 | { "Saitek Saitek X45", 6, 1, 0 } |
60 | }; |
61 | |
62 | /* It looks like newer kernels have the logical mapping at the driver level */ |
63 | #define NO_LOGICAL_JOYSTICKS |
64 | |
65 | #ifndef NO_LOGICAL_JOYSTICKS |
66 | |
67 | /* |
68 | Some USB HIDs show up as a single joystick even though they actually |
69 | control 2 or more joysticks. |
70 | */ |
71 | /* |
72 | This code handles the MP-8800 (Quad) and MP-8866 (Dual), which can |
73 | be identified by their transparent blue design. It's quite trivial |
74 | to add other joysticks with similar quirky behavior. |
75 | -id |
76 | */ |
77 | |
78 | struct joystick_logical_mapping { |
79 | int njoy; |
80 | int nthing; |
81 | }; |
82 | |
83 | /* |
84 | {logical joy, logical axis}, |
85 | {logical joy, logical hat}, |
86 | {logical joy, logical ball}, |
87 | {logical joy, logical button} |
88 | */ |
89 | |
90 | static struct joystick_logical_mapping mp88xx_1_logical_axismap[] = { |
91 | {0,0},{0,1},{0,2},{0,3},{0,4},{0,5} |
92 | }; |
93 | static struct joystick_logical_mapping mp88xx_1_logical_buttonmap[] = { |
94 | {0,0},{0,1},{0,2},{0,3},{0,4},{0,5},{0,6},{0,7},{0,8},{0,9},{0,10},{0,11} |
95 | }; |
96 | |
97 | static struct joystick_logical_mapping mp88xx_2_logical_axismap[] = { |
98 | {0,0},{0,1},{0,2},{1,0},{1,1},{0,3}, |
99 | {1,2},{1,3},{0,4},{0,5},{1,4},{1,5} |
100 | }; |
101 | static struct joystick_logical_mapping mp88xx_2_logical_buttonmap[] = { |
102 | {0,0},{0,1},{0,2},{0,3},{0,4},{0,5},{0,6},{0,7},{0,8},{0,9},{0,10},{0,11}, |
103 | {1,0},{1,1},{1,2},{1,3},{1,4},{1,5},{1,6},{1,7},{1,8},{1,9},{1,10},{1,11} |
104 | }; |
105 | |
106 | static struct joystick_logical_mapping mp88xx_3_logical_axismap[] = { |
107 | {0,0},{0,1},{0,2},{1,0},{1,1},{0,3}, |
108 | {1,2},{1,3},{2,0},{2,1},{2,2},{2,3}, |
109 | {0,4},{0,5},{1,4},{1,5},{2,4},{2,5} |
110 | }; |
111 | static struct joystick_logical_mapping mp88xx_3_logical_buttonmap[] = { |
112 | {0,0},{0,1},{0,2},{0,3},{0,4},{0,5},{0,6},{0,7},{0,8},{0,9},{0,10},{0,11}, |
113 | {1,0},{1,1},{1,2},{1,3},{1,4},{1,5},{1,6},{1,7},{1,8},{1,9},{1,10},{1,11}, |
114 | {2,0},{2,1},{2,2},{2,3},{2,4},{2,5},{2,6},{2,7},{2,8},{2,9},{2,10},{2,11} |
115 | }; |
116 | |
117 | static struct joystick_logical_mapping mp88xx_4_logical_axismap[] = { |
118 | {0,0},{0,1},{0,2},{1,0},{1,1},{0,3}, |
119 | {1,2},{1,3},{2,0},{2,1},{2,2},{2,3}, |
120 | {3,0},{3,1},{3,2},{3,3},{0,4},{0,5}, |
121 | {1,4},{1,5},{2,4},{2,5},{3,4},{3,5} |
122 | }; |
123 | static struct joystick_logical_mapping mp88xx_4_logical_buttonmap[] = { |
124 | {0,0},{0,1},{0,2},{0,3},{0,4},{0,5},{0,6},{0,7},{0,8},{0,9},{0,10},{0,11}, |
125 | {1,0},{1,1},{1,2},{1,3},{1,4},{1,5},{1,6},{1,7},{1,8},{1,9},{1,10},{1,11}, |
126 | {2,0},{2,1},{2,2},{2,3},{2,4},{2,5},{2,6},{2,7},{2,8},{2,9},{2,10},{2,11}, |
127 | {3,0},{3,1},{3,2},{3,3},{3,4},{3,5},{3,6},{3,7},{3,8},{3,9},{3,10},{3,11} |
128 | }; |
129 | |
130 | struct joystick_logical_layout { |
131 | int naxes; |
132 | int nhats; |
133 | int nballs; |
134 | int nbuttons; |
135 | }; |
136 | |
137 | static struct joystick_logical_layout mp88xx_1_logical_layout[] = { |
138 | {6, 0, 0, 12} |
139 | }; |
140 | static struct joystick_logical_layout mp88xx_2_logical_layout[] = { |
141 | {6, 0, 0, 12}, |
142 | {6, 0, 0, 12} |
143 | }; |
144 | static struct joystick_logical_layout mp88xx_3_logical_layout[] = { |
145 | {6, 0, 0, 12}, |
146 | {6, 0, 0, 12}, |
147 | {6, 0, 0, 12} |
148 | }; |
149 | static struct joystick_logical_layout mp88xx_4_logical_layout[] = { |
150 | {6, 0, 0, 12}, |
151 | {6, 0, 0, 12}, |
152 | {6, 0, 0, 12}, |
153 | {6, 0, 0, 12} |
154 | }; |
155 | |
156 | /* |
157 | This array sets up a means of mapping a single physical joystick to |
158 | multiple logical joysticks. (djm) |
159 | |
160 | njoys |
161 | the number of logical joysticks |
162 | |
163 | layouts |
164 | an array of layout structures, one to describe each logical joystick |
165 | |
166 | axes, hats, balls, buttons |
167 | arrays that map a physical thingy to a logical thingy |
168 | */ |
169 | struct joystick_logicalmap { |
170 | const char *name; |
171 | int nbuttons; |
172 | int njoys; |
173 | struct joystick_logical_layout *layout; |
174 | struct joystick_logical_mapping *axismap; |
175 | struct joystick_logical_mapping *hatmap; |
176 | struct joystick_logical_mapping *ballmap; |
177 | struct joystick_logical_mapping *buttonmap; |
178 | }; |
179 | |
180 | static struct joystick_logicalmap joystick_logicalmap[] = { |
181 | { |
182 | "WiseGroup.,Ltd MP-8866 Dual USB Joypad", |
183 | 12, |
184 | 1, |
185 | mp88xx_1_logical_layout, |
186 | mp88xx_1_logical_axismap, |
187 | NULL, |
188 | NULL, |
189 | mp88xx_1_logical_buttonmap |
190 | }, |
191 | { |
192 | "WiseGroup.,Ltd MP-8866 Dual USB Joypad", |
193 | 24, |
194 | 2, |
195 | mp88xx_2_logical_layout, |
196 | mp88xx_2_logical_axismap, |
197 | NULL, |
198 | NULL, |
199 | mp88xx_2_logical_buttonmap |
200 | }, |
201 | { |
202 | "WiseGroup.,Ltd MP-8800 Quad USB Joypad", |
203 | 12, |
204 | 1, |
205 | mp88xx_1_logical_layout, |
206 | mp88xx_1_logical_axismap, |
207 | NULL, |
208 | NULL, |
209 | mp88xx_1_logical_buttonmap |
210 | }, |
211 | { |
212 | "WiseGroup.,Ltd MP-8800 Quad USB Joypad", |
213 | 24, |
214 | 2, |
215 | mp88xx_2_logical_layout, |
216 | mp88xx_2_logical_axismap, |
217 | NULL, |
218 | NULL, |
219 | mp88xx_2_logical_buttonmap |
220 | }, |
221 | { |
222 | "WiseGroup.,Ltd MP-8800 Quad USB Joypad", |
223 | 36, |
224 | 3, |
225 | mp88xx_3_logical_layout, |
226 | mp88xx_3_logical_axismap, |
227 | NULL, |
228 | NULL, |
229 | mp88xx_3_logical_buttonmap |
230 | }, |
231 | { |
232 | "WiseGroup.,Ltd MP-8800 Quad USB Joypad", |
233 | 48, |
234 | 4, |
235 | mp88xx_4_logical_layout, |
236 | mp88xx_4_logical_axismap, |
237 | NULL, |
238 | NULL, |
239 | mp88xx_4_logical_buttonmap |
240 | } |
241 | }; |
242 | |
243 | /* find the head of a linked list, given a point in it |
244 | */ |
245 | #define SDL_joylist_head(i, start)\ |
246 | for(i = start; SDL_joylist[i].fname == NULL;) i = SDL_joylist[i].prev; |
247 | |
248 | #define SDL_logical_joydecl(d) d |
249 | |
250 | |
251 | #else |
252 | |
253 | #define SDL_logical_joydecl(d) |
254 | |
255 | #endif /* USE_LOGICAL_JOYSTICKS */ |
256 | |
257 | /* The maximum number of joysticks we'll detect */ |
258 | #define MAX_JOYSTICKS 32 |
259 | |
260 | /* A list of available joysticks */ |
261 | static struct |
262 | { |
263 | char* fname; |
264 | #ifndef NO_LOGICAL_JOYSTICKS |
265 | SDL_Joystick* joy; |
266 | struct joystick_logicalmap* map; |
267 | int prev; |
268 | int next; |
269 | int logicalno; |
270 | #endif /* USE_LOGICAL_JOYSTICKS */ |
271 | } SDL_joylist[MAX_JOYSTICKS]; |
272 | |
273 | |
274 | /* The private structure used to keep track of a joystick */ |
275 | struct joystick_hwdata { |
276 | int fd; |
277 | /* The current linux joystick driver maps hats to two axes */ |
278 | struct hwdata_hat { |
279 | int axis[2]; |
280 | } *hats; |
281 | /* The current linux joystick driver maps balls to two axes */ |
282 | struct hwdata_ball { |
283 | int axis[2]; |
284 | } *balls; |
285 | |
286 | /* Support for the Linux 2.4 unified input interface */ |
287 | #if SDL_INPUT_LINUXEV |
288 | SDL_bool is_hid; |
289 | Uint8 key_map[KEY_MAX-BTN_MISC]; |
290 | Uint8 abs_map[ABS_MAX]; |
291 | struct axis_correct { |
292 | int used; |
293 | int coef[3]; |
294 | } abs_correct[ABS_MAX]; |
295 | #endif |
296 | }; |
297 | |
298 | |
299 | #ifndef NO_LOGICAL_JOYSTICKS |
300 | |
301 | static int CountLogicalJoysticks(int max) |
302 | { |
303 | register int i, j, k, ret, prev; |
304 | const char* name; |
305 | int nbuttons, fd; |
306 | unsigned char n; |
307 | |
308 | ret = 0; |
309 | |
310 | for(i = 0; i < max; i++) { |
311 | name = SDL_SYS_JoystickName(i); |
312 | |
313 | fd = open(SDL_joylist[i].fname, O_RDONLY, 0); |
314 | if ( fd >= 0 ) { |
315 | if ( ioctl(fd, JSIOCGBUTTONS, &n) < 0 ) { |
316 | nbuttons = -1; |
317 | } else { |
318 | nbuttons = n; |
319 | } |
320 | close(fd); |
321 | } |
322 | else { |
323 | nbuttons=-1; |
324 | } |
325 | |
326 | if (name) { |
327 | for(j = 0; j < SDL_arraysize(joystick_logicalmap); j++) { |
328 | if (!SDL_strcmp(name, joystick_logicalmap[j].name) && (nbuttons==-1 || nbuttons==joystick_logicalmap[j].nbuttons)) { |
329 | prev = i; |
330 | SDL_joylist[prev].map = &(joystick_logicalmap[j]); |
331 | |
332 | for(k = 1; k < joystick_logicalmap[j].njoys; k++) { |
333 | SDL_joylist[prev].next = max + ret; |
334 | SDL_joylist[max+ret].prev = prev; |
335 | |
336 | prev = max + ret; |
337 | SDL_joylist[prev].logicalno = k; |
338 | SDL_joylist[prev].map = &(joystick_logicalmap[j]); |
339 | ret++; |
340 | } |
341 | |
342 | break; |
343 | } |
344 | } |
345 | } |
346 | } |
347 | |
348 | return ret; |
349 | } |
350 | |
351 | static void LogicalSuffix(int logicalno, char* namebuf, int len) |
352 | { |
353 | register int slen; |
354 | const static char suffixs[] = |
355 | "01020304050607080910111213141516171819" |
356 | "20212223242526272829303132"; |
357 | const char* suffix; |
358 | slen = SDL_strlen(namebuf); |
359 | suffix = NULL; |
360 | |
361 | if (logicalno*2<sizeof(suffixs)) |
362 | suffix = suffixs + (logicalno*2); |
363 | |
364 | if (slen + 4 < len && suffix) { |
365 | namebuf[slen++] = ' '; |
366 | namebuf[slen++] = '#'; |
367 | namebuf[slen++] = suffix[0]; |
368 | namebuf[slen++] = suffix[1]; |
369 | namebuf[slen++] = 0; |
370 | } |
371 | } |
372 | |
373 | #endif /* USE_LOGICAL_JOYSTICKS */ |
374 | |
375 | #if SDL_INPUT_LINUXEV |
376 | #define test_bit(nr, addr) \ |
377 | (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0) |
378 | #define NBITS(x) ((((x)-1)/(sizeof(long) * 8))+1) |
379 | |
380 | static int EV_IsJoystick(int fd) |
381 | { |
382 | unsigned long evbit[NBITS(EV_MAX)] = { 0 }; |
383 | unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; |
384 | unsigned long absbit[NBITS(ABS_MAX)] = { 0 }; |
385 | |
386 | if ( (ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) || |
387 | (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || |
388 | (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0) ) { |
389 | return(0); |
390 | } |
391 | if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) && |
392 | test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit) && |
393 | (test_bit(BTN_TRIGGER, keybit) || test_bit(BTN_A, keybit) || test_bit(BTN_1, keybit)))) return 0; |
394 | return(1); |
395 | } |
396 | |
397 | #endif /* SDL_INPUT_LINUXEV */ |
398 | |
399 | /* Function to scan the system for joysticks */ |
400 | int SDL_SYS_JoystickInit(void) |
401 | { |
402 | /* The base path of the joystick devices */ |
403 | const char *joydev_pattern[] = { |
404 | #if SDL_INPUT_LINUXEV |
405 | "/dev/input/event%d", |
406 | #endif |
407 | "/dev/input/js%d", |
408 | "/dev/js%d" |
409 | }; |
410 | int numjoysticks; |
411 | int i, j; |
412 | int fd; |
413 | char path[PATH_MAX]; |
414 | dev_t dev_nums[MAX_JOYSTICKS]; /* major/minor device numbers */ |
415 | struct stat sb; |
416 | int n, duplicate; |
417 | |
418 | numjoysticks = 0; |
419 | |
420 | /* First see if the user specified a joystick to use */ |
421 | if ( SDL_getenv("SDL_JOYSTICK_DEVICE") != NULL ) { |
422 | SDL_strlcpy(path, SDL_getenv("SDL_JOYSTICK_DEVICE"), sizeof(path)); |
423 | if ( stat(path, &sb) == 0 ) { |
424 | fd = open(path, O_RDONLY, 0); |
425 | if ( fd >= 0 ) { |
426 | /* Assume the user knows what they're doing. */ |
427 | SDL_joylist[numjoysticks].fname = SDL_strdup(path); |
428 | if ( SDL_joylist[numjoysticks].fname ) { |
429 | dev_nums[numjoysticks] = sb.st_rdev; |
430 | ++numjoysticks; |
431 | } |
432 | close(fd); |
433 | } |
434 | } |
435 | } |
436 | |
437 | for ( i=0; i<SDL_arraysize(joydev_pattern); ++i ) { |
438 | for ( j=0; j < MAX_JOYSTICKS; ++j ) { |
439 | SDL_snprintf(path, SDL_arraysize(path), joydev_pattern[i], j); |
440 | |
441 | /* rcg06302000 replaced access(F_OK) call with stat(). |
442 | * stat() will fail if the file doesn't exist, so it's |
443 | * equivalent behaviour. |
444 | */ |
445 | if ( stat(path, &sb) == 0 ) { |
446 | /* Check to make sure it's not already in list. |
447 | * This happens when we see a stick via symlink. |
448 | */ |
449 | duplicate = 0; |
450 | for (n=0; (n<numjoysticks) && !duplicate; ++n) { |
451 | if ( sb.st_rdev == dev_nums[n] ) { |
452 | duplicate = 1; |
453 | } |
454 | } |
455 | if (duplicate) { |
456 | continue; |
457 | } |
458 | |
459 | fd = open(path, O_RDONLY, 0); |
460 | if ( fd < 0 ) { |
461 | continue; |
462 | } |
463 | #if SDL_INPUT_LINUXEV |
464 | #ifdef DEBUG_INPUT_EVENTS |
465 | printf("Checking %s\n", path); |
466 | #endif |
467 | if ( (i == 0) && ! EV_IsJoystick(fd) ) { |
468 | close(fd); |
469 | continue; |
470 | } |
471 | #endif |
472 | close(fd); |
473 | |
474 | /* We're fine, add this joystick */ |
475 | SDL_joylist[numjoysticks].fname = SDL_strdup(path); |
476 | if ( SDL_joylist[numjoysticks].fname ) { |
477 | dev_nums[numjoysticks] = sb.st_rdev; |
478 | ++numjoysticks; |
479 | } |
480 | } |
481 | } |
482 | |
483 | #if SDL_INPUT_LINUXEV |
484 | /* This is a special case... |
485 | If the event devices are valid then the joystick devices |
486 | will be duplicates but without extra information about their |
487 | hats or balls. Unfortunately, the event devices can't |
488 | currently be calibrated, so it's a win-lose situation. |
489 | So : /dev/input/eventX = /dev/input/jsY = /dev/jsY |
490 | */ |
491 | if ( (i == 0) && (numjoysticks > 0) ) |
492 | break; |
493 | #endif |
494 | } |
495 | #ifndef NO_LOGICAL_JOYSTICKS |
496 | numjoysticks += CountLogicalJoysticks(numjoysticks); |
497 | #endif |
498 | |
499 | return(numjoysticks); |
500 | } |
501 | |
502 | /* Function to get the device-dependent name of a joystick */ |
503 | const char *SDL_SYS_JoystickName(int index) |
504 | { |
505 | int fd; |
506 | static char namebuf[128]; |
507 | char *name; |
508 | SDL_logical_joydecl(int oindex = index); |
509 | |
510 | #ifndef NO_LOGICAL_JOYSTICKS |
511 | SDL_joylist_head(index, index); |
512 | #endif |
513 | name = NULL; |
514 | fd = open(SDL_joylist[index].fname, O_RDONLY, 0); |
515 | if ( fd >= 0 ) { |
516 | if ( |
517 | #if SDL_INPUT_LINUXEV |
518 | (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) && |
519 | #endif |
520 | (ioctl(fd, JSIOCGNAME(sizeof(namebuf)), namebuf) <= 0) ) { |
521 | name = SDL_joylist[index].fname; |
522 | } else { |
523 | name = namebuf; |
524 | } |
525 | close(fd); |
526 | |
527 | |
528 | #ifndef NO_LOGICAL_JOYSTICKS |
529 | if (SDL_joylist[oindex].prev || SDL_joylist[oindex].next || index!=oindex) |
530 | { |
531 | LogicalSuffix(SDL_joylist[oindex].logicalno, namebuf, 128); |
532 | } |
533 | #endif |
534 | } |
535 | return name; |
536 | } |
537 | |
538 | static int allocate_hatdata(SDL_Joystick *joystick) |
539 | { |
540 | int i; |
541 | |
542 | joystick->hwdata->hats = (struct hwdata_hat *)SDL_malloc( |
543 | joystick->nhats * sizeof(struct hwdata_hat)); |
544 | if ( joystick->hwdata->hats == NULL ) { |
545 | return(-1); |
546 | } |
547 | for ( i=0; i<joystick->nhats; ++i ) { |
548 | joystick->hwdata->hats[i].axis[0] = 1; |
549 | joystick->hwdata->hats[i].axis[1] = 1; |
550 | } |
551 | return(0); |
552 | } |
553 | |
554 | static int allocate_balldata(SDL_Joystick *joystick) |
555 | { |
556 | int i; |
557 | |
558 | joystick->hwdata->balls = (struct hwdata_ball *)SDL_malloc( |
559 | joystick->nballs * sizeof(struct hwdata_ball)); |
560 | if ( joystick->hwdata->balls == NULL ) { |
561 | return(-1); |
562 | } |
563 | for ( i=0; i<joystick->nballs; ++i ) { |
564 | joystick->hwdata->balls[i].axis[0] = 0; |
565 | joystick->hwdata->balls[i].axis[1] = 0; |
566 | } |
567 | return(0); |
568 | } |
569 | |
570 | static SDL_bool JS_ConfigJoystick(SDL_Joystick *joystick, int fd) |
571 | { |
572 | SDL_bool handled; |
573 | unsigned char n; |
574 | int old_axes, tmp_naxes, tmp_nhats, tmp_nballs; |
575 | const char *name; |
576 | char *env, env_name[128]; |
577 | int i; |
578 | |
579 | handled = SDL_FALSE; |
580 | |
581 | /* Default joystick device settings */ |
582 | if ( ioctl(fd, JSIOCGAXES, &n) < 0 ) { |
583 | joystick->naxes = 2; |
584 | } else { |
585 | joystick->naxes = n; |
586 | } |
587 | if ( ioctl(fd, JSIOCGBUTTONS, &n) < 0 ) { |
588 | joystick->nbuttons = 2; |
589 | } else { |
590 | joystick->nbuttons = n; |
591 | } |
592 | |
593 | name = SDL_SYS_JoystickName(joystick->index); |
594 | old_axes = joystick->naxes; |
595 | |
596 | /* Generic analog joystick support */ |
597 | if ( SDL_strstr(name, "Analog") == name && SDL_strstr(name, "-hat") ) { |
598 | if ( SDL_sscanf(name,"Analog %d-axis %*d-button %d-hat", |
599 | &tmp_naxes, &tmp_nhats) == 2 ) { |
600 | |
601 | joystick->naxes = tmp_naxes; |
602 | joystick->nhats = tmp_nhats; |
603 | |
604 | handled = SDL_TRUE; |
605 | } |
606 | } |
607 | |
608 | /* Special joystick support */ |
609 | for ( i=0; i < SDL_arraysize(special_joysticks); ++i ) { |
610 | if ( SDL_strcmp(name, special_joysticks[i].name) == 0 ) { |
611 | |
612 | joystick->naxes = special_joysticks[i].naxes; |
613 | joystick->nhats = special_joysticks[i].nhats; |
614 | joystick->nballs = special_joysticks[i].nballs; |
615 | |
616 | handled = SDL_TRUE; |
617 | break; |
618 | } |
619 | } |
620 | |
621 | /* User environment joystick support */ |
622 | if ( (env = SDL_getenv("SDL_LINUX_JOYSTICK")) ) { |
623 | *env_name = '\0'; |
624 | if ( *env == '\'' && SDL_sscanf(env, "'%[^']s'", env_name) == 1 ) |
625 | env += SDL_strlen(env_name)+2; |
626 | else if ( SDL_sscanf(env, "%s", env_name) == 1 ) |
627 | env += SDL_strlen(env_name); |
628 | |
629 | if ( SDL_strcmp(name, env_name) == 0 ) { |
630 | |
631 | if ( SDL_sscanf(env, "%d %d %d", &tmp_naxes, &tmp_nhats, |
632 | &tmp_nballs) == 3 ) { |
633 | |
634 | joystick->naxes = tmp_naxes; |
635 | joystick->nhats = tmp_nhats; |
636 | joystick->nballs = tmp_nballs; |
637 | |
638 | handled = SDL_TRUE; |
639 | } |
640 | } |
641 | } |
642 | |
643 | /* Remap hats and balls */ |
644 | if (handled) { |
645 | if ( joystick->nhats > 0 ) { |
646 | if ( allocate_hatdata(joystick) < 0 ) { |
647 | joystick->nhats = 0; |
648 | } |
649 | } |
650 | if ( joystick->nballs > 0 ) { |
651 | if ( allocate_balldata(joystick) < 0 ) { |
652 | joystick->nballs = 0; |
653 | } |
654 | } |
655 | } |
656 | |
657 | return(handled); |
658 | } |
659 | |
660 | #if SDL_INPUT_LINUXEV |
661 | |
662 | static SDL_bool EV_ConfigJoystick(SDL_Joystick *joystick, int fd) |
663 | { |
664 | int i, t; |
665 | unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; |
666 | unsigned long absbit[NBITS(ABS_MAX)] = { 0 }; |
667 | unsigned long relbit[NBITS(REL_MAX)] = { 0 }; |
668 | |
669 | /* See if this device uses the new unified event API */ |
670 | if ( (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) && |
671 | (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) && |
672 | (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0) ) { |
673 | joystick->hwdata->is_hid = SDL_TRUE; |
674 | |
675 | /* Get the number of buttons, axes, and other thingamajigs */ |
676 | for ( i=BTN_JOYSTICK; i < KEY_MAX; ++i ) { |
677 | if ( test_bit(i, keybit) ) { |
678 | #ifdef DEBUG_INPUT_EVENTS |
679 | printf("Joystick has button: 0x%x\n", i); |
680 | #endif |
681 | joystick->hwdata->key_map[i-BTN_MISC] = |
682 | joystick->nbuttons; |
683 | ++joystick->nbuttons; |
684 | } |
685 | } |
686 | for ( i=BTN_MISC; i < BTN_JOYSTICK; ++i ) { |
687 | if ( test_bit(i, keybit) ) { |
688 | #ifdef DEBUG_INPUT_EVENTS |
689 | printf("Joystick has button: 0x%x\n", i); |
690 | #endif |
691 | joystick->hwdata->key_map[i-BTN_MISC] = |
692 | joystick->nbuttons; |
693 | ++joystick->nbuttons; |
694 | } |
695 | } |
696 | for ( i=0; i<ABS_MAX; ++i ) { |
697 | /* Skip hats */ |
698 | if ( i == ABS_HAT0X ) { |
699 | i = ABS_HAT3Y; |
700 | continue; |
701 | } |
702 | if ( test_bit(i, absbit) ) { |
703 | int values[5]; |
704 | |
705 | if ( ioctl(fd, EVIOCGABS(i), values) < 0 ) |
706 | continue; |
707 | #ifdef DEBUG_INPUT_EVENTS |
708 | printf("Joystick has absolute axis: %x\n", i); |
709 | printf("Values = { %d, %d, %d, %d, %d }\n", |
710 | values[0], values[1], |
711 | values[2], values[3], values[4]); |
712 | #endif /* DEBUG_INPUT_EVENTS */ |
713 | joystick->hwdata->abs_map[i] = joystick->naxes; |
714 | if ( values[1] == values[2] ) { |
715 | joystick->hwdata->abs_correct[i].used = 0; |
716 | } else { |
717 | joystick->hwdata->abs_correct[i].used = 1; |
718 | joystick->hwdata->abs_correct[i].coef[0] = |
719 | (values[2] + values[1]) / 2 - values[4]; |
720 | joystick->hwdata->abs_correct[i].coef[1] = |
721 | (values[2] + values[1]) / 2 + values[4]; |
722 | t = ((values[2] - values[1]) / 2 - 2 * values[4]); |
723 | if ( t != 0 ) { |
724 | joystick->hwdata->abs_correct[i].coef[2] = (1 << 29) / t; |
725 | } else { |
726 | joystick->hwdata->abs_correct[i].coef[2] = 0; |
727 | } |
728 | } |
729 | ++joystick->naxes; |
730 | } |
731 | } |
732 | for ( i=ABS_HAT0X; i <= ABS_HAT3Y; i += 2 ) { |
733 | if ( test_bit(i, absbit) || test_bit(i+1, absbit) ) { |
734 | #ifdef DEBUG_INPUT_EVENTS |
735 | printf("Joystick has hat %d\n",(i-ABS_HAT0X)/2); |
736 | #endif |
737 | ++joystick->nhats; |
738 | } |
739 | } |
740 | if ( test_bit(REL_X, relbit) || test_bit(REL_Y, relbit) ) { |
741 | ++joystick->nballs; |
742 | } |
743 | |
744 | /* Allocate data to keep track of these thingamajigs */ |
745 | if ( joystick->nhats > 0 ) { |
746 | if ( allocate_hatdata(joystick) < 0 ) { |
747 | joystick->nhats = 0; |
748 | } |
749 | } |
750 | if ( joystick->nballs > 0 ) { |
751 | if ( allocate_balldata(joystick) < 0 ) { |
752 | joystick->nballs = 0; |
753 | } |
754 | } |
755 | } |
756 | return(joystick->hwdata->is_hid); |
757 | } |
758 | |
759 | #endif /* SDL_INPUT_LINUXEV */ |
760 | |
761 | #ifndef NO_LOGICAL_JOYSTICKS |
762 | static void ConfigLogicalJoystick(SDL_Joystick *joystick) |
763 | { |
764 | struct joystick_logical_layout* layout; |
765 | |
766 | layout = SDL_joylist[joystick->index].map->layout + |
767 | SDL_joylist[joystick->index].logicalno; |
768 | |
769 | joystick->nbuttons = layout->nbuttons; |
770 | joystick->nhats = layout->nhats; |
771 | joystick->naxes = layout->naxes; |
772 | joystick->nballs = layout->nballs; |
773 | } |
774 | #endif |
775 | |
776 | |
777 | /* Function to open a joystick for use. |
778 | The joystick to open is specified by the index field of the joystick. |
779 | This should fill the nbuttons and naxes fields of the joystick structure. |
780 | It returns 0, or -1 if there is an error. |
781 | */ |
782 | int SDL_SYS_JoystickOpen(SDL_Joystick *joystick) |
783 | { |
784 | int fd; |
785 | SDL_logical_joydecl(int realindex); |
786 | SDL_logical_joydecl(SDL_Joystick *realjoy = NULL); |
787 | |
788 | /* Open the joystick and set the joystick file descriptor */ |
789 | #ifndef NO_LOGICAL_JOYSTICKS |
790 | if (SDL_joylist[joystick->index].fname == NULL) { |
791 | SDL_joylist_head(realindex, joystick->index); |
792 | realjoy = SDL_JoystickOpen(realindex); |
793 | |
794 | if (realjoy == NULL) |
795 | return(-1); |
796 | |
797 | fd = realjoy->hwdata->fd; |
798 | |
799 | } else { |
800 | fd = open(SDL_joylist[joystick->index].fname, O_RDONLY, 0); |
801 | } |
802 | SDL_joylist[joystick->index].joy = joystick; |
803 | #else |
804 | fd = open(SDL_joylist[joystick->index].fname, O_RDONLY, 0); |
805 | #endif |
806 | |
807 | if ( fd < 0 ) { |
808 | SDL_SetError("Unable to open %s\n", |
809 | SDL_joylist[joystick->index]); |
810 | return(-1); |
811 | } |
812 | joystick->hwdata = (struct joystick_hwdata *) |
813 | SDL_malloc(sizeof(*joystick->hwdata)); |
814 | if ( joystick->hwdata == NULL ) { |
815 | SDL_OutOfMemory(); |
816 | close(fd); |
817 | return(-1); |
818 | } |
819 | SDL_memset(joystick->hwdata, 0, sizeof(*joystick->hwdata)); |
820 | joystick->hwdata->fd = fd; |
821 | |
822 | /* Set the joystick to non-blocking read mode */ |
823 | fcntl(fd, F_SETFL, O_NONBLOCK); |
824 | |
825 | /* Get the number of buttons and axes on the joystick */ |
826 | #ifndef NO_LOGICAL_JOYSTICKS |
827 | if (realjoy) |
828 | ConfigLogicalJoystick(joystick); |
829 | else |
830 | #endif |
831 | #if SDL_INPUT_LINUXEV |
832 | if ( ! EV_ConfigJoystick(joystick, fd) ) |
833 | #endif |
834 | JS_ConfigJoystick(joystick, fd); |
835 | |
836 | return(0); |
837 | } |
838 | |
839 | #ifndef NO_LOGICAL_JOYSTICKS |
840 | |
841 | static SDL_Joystick* FindLogicalJoystick( |
842 | SDL_Joystick *joystick, struct joystick_logical_mapping* v) |
843 | { |
844 | SDL_Joystick *logicaljoy; |
845 | register int i; |
846 | |
847 | i = joystick->index; |
848 | logicaljoy = NULL; |
849 | |
850 | /* get the fake joystick that will receive the event |
851 | */ |
852 | for(;;) { |
853 | |
854 | if (SDL_joylist[i].logicalno == v->njoy) { |
855 | logicaljoy = SDL_joylist[i].joy; |
856 | break; |
857 | } |
858 | |
859 | if (SDL_joylist[i].next == 0) |
860 | break; |
861 | |
862 | i = SDL_joylist[i].next; |
863 | |
864 | } |
865 | |
866 | return logicaljoy; |
867 | } |
868 | |
869 | static int LogicalJoystickButton( |
870 | SDL_Joystick *joystick, Uint8 button, Uint8 state){ |
871 | struct joystick_logical_mapping* buttons; |
872 | SDL_Joystick *logicaljoy = NULL; |
873 | |
874 | /* if there's no map then this is just a regular joystick |
875 | */ |
876 | if (SDL_joylist[joystick->index].map == NULL) |
877 | return 0; |
878 | |
879 | /* get the logical joystick that will receive the event |
880 | */ |
881 | buttons = SDL_joylist[joystick->index].map->buttonmap+button; |
882 | logicaljoy = FindLogicalJoystick(joystick, buttons); |
883 | |
884 | if (logicaljoy == NULL) |
885 | return 1; |
886 | |
887 | SDL_PrivateJoystickButton(logicaljoy, buttons->nthing, state); |
888 | |
889 | return 1; |
890 | } |
891 | |
892 | static int LogicalJoystickAxis( |
893 | SDL_Joystick *joystick, Uint8 axis, Sint16 value) |
894 | { |
895 | struct joystick_logical_mapping* axes; |
896 | SDL_Joystick *logicaljoy = NULL; |
897 | |
898 | /* if there's no map then this is just a regular joystick |
899 | */ |
900 | if (SDL_joylist[joystick->index].map == NULL) |
901 | return 0; |
902 | |
903 | /* get the logical joystick that will receive the event |
904 | */ |
905 | axes = SDL_joylist[joystick->index].map->axismap+axis; |
906 | logicaljoy = FindLogicalJoystick(joystick, axes); |
907 | |
908 | if (logicaljoy == NULL) |
909 | return 1; |
910 | |
911 | SDL_PrivateJoystickAxis(logicaljoy, axes->nthing, value); |
912 | |
913 | return 1; |
914 | } |
915 | #endif /* USE_LOGICAL_JOYSTICKS */ |
916 | |
917 | static __inline__ |
918 | void HandleHat(SDL_Joystick *stick, Uint8 hat, int axis, int value) |
919 | { |
920 | struct hwdata_hat *the_hat; |
921 | const Uint8 position_map[3][3] = { |
922 | { SDL_HAT_LEFTUP, SDL_HAT_UP, SDL_HAT_RIGHTUP }, |
923 | { SDL_HAT_LEFT, SDL_HAT_CENTERED, SDL_HAT_RIGHT }, |
924 | { SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN } |
925 | }; |
926 | SDL_logical_joydecl(SDL_Joystick *logicaljoy = NULL); |
927 | SDL_logical_joydecl(struct joystick_logical_mapping* hats = NULL); |
928 | |
929 | the_hat = &stick->hwdata->hats[hat]; |
930 | if ( value < 0 ) { |
931 | value = 0; |
932 | } else |
933 | if ( value == 0 ) { |
934 | value = 1; |
935 | } else |
936 | if ( value > 0 ) { |
937 | value = 2; |
938 | } |
939 | if ( value != the_hat->axis[axis] ) { |
940 | the_hat->axis[axis] = value; |
941 | |
942 | #ifndef NO_LOGICAL_JOYSTICKS |
943 | /* if there's no map then this is just a regular joystick |
944 | */ |
945 | if (SDL_joylist[stick->index].map != NULL) { |
946 | |
947 | /* get the fake joystick that will receive the event |
948 | */ |
949 | hats = SDL_joylist[stick->index].map->hatmap+hat; |
950 | logicaljoy = FindLogicalJoystick(stick, hats); |
951 | } |
952 | |
953 | if (logicaljoy) { |
954 | stick = logicaljoy; |
955 | hat = hats->nthing; |
956 | } |
957 | #endif /* USE_LOGICAL_JOYSTICKS */ |
958 | |
959 | SDL_PrivateJoystickHat(stick, hat, |
960 | position_map[the_hat->axis[1]][the_hat->axis[0]]); |
961 | } |
962 | } |
963 | |
964 | static __inline__ |
965 | void HandleBall(SDL_Joystick *stick, Uint8 ball, int axis, int value) |
966 | { |
967 | stick->hwdata->balls[ball].axis[axis] += value; |
968 | } |
969 | |
970 | /* Function to update the state of a joystick - called as a device poll. |
971 | * This function shouldn't update the joystick structure directly, |
972 | * but instead should call SDL_PrivateJoystick*() to deliver events |
973 | * and update joystick device state. |
974 | */ |
975 | static __inline__ void JS_HandleEvents(SDL_Joystick *joystick) |
976 | { |
977 | struct js_event events[32]; |
978 | int i, len; |
979 | Uint8 other_axis; |
980 | |
981 | #ifndef NO_LOGICAL_JOYSTICKS |
982 | if (SDL_joylist[joystick->index].fname == NULL) { |
983 | SDL_joylist_head(i, joystick->index); |
984 | JS_HandleEvents(SDL_joylist[i].joy); |
985 | return; |
986 | } |
987 | #endif |
988 | |
989 | while ((len=read(joystick->hwdata->fd, events, (sizeof events))) > 0) { |
990 | len /= sizeof(events[0]); |
991 | for ( i=0; i<len; ++i ) { |
992 | switch (events[i].type & ~JS_EVENT_INIT) { |
993 | case JS_EVENT_AXIS: |
994 | if ( events[i].number < joystick->naxes ) { |
995 | #ifndef NO_LOGICAL_JOYSTICKS |
996 | if (!LogicalJoystickAxis(joystick, |
997 | events[i].number, events[i].value)) |
998 | #endif |
999 | SDL_PrivateJoystickAxis(joystick, |
1000 | events[i].number, events[i].value); |
1001 | break; |
1002 | } |
1003 | events[i].number -= joystick->naxes; |
1004 | other_axis = (events[i].number / 2); |
1005 | if ( other_axis < joystick->nhats ) { |
1006 | HandleHat(joystick, other_axis, |
1007 | events[i].number%2, |
1008 | events[i].value); |
1009 | break; |
1010 | } |
1011 | events[i].number -= joystick->nhats*2; |
1012 | other_axis = (events[i].number / 2); |
1013 | if ( other_axis < joystick->nballs ) { |
1014 | HandleBall(joystick, other_axis, |
1015 | events[i].number%2, |
1016 | events[i].value); |
1017 | break; |
1018 | } |
1019 | break; |
1020 | case JS_EVENT_BUTTON: |
1021 | #ifndef NO_LOGICAL_JOYSTICKS |
1022 | if (!LogicalJoystickButton(joystick, |
1023 | events[i].number, events[i].value)) |
1024 | #endif |
1025 | SDL_PrivateJoystickButton(joystick, |
1026 | events[i].number, events[i].value); |
1027 | break; |
1028 | default: |
1029 | /* ?? */ |
1030 | break; |
1031 | } |
1032 | } |
1033 | } |
1034 | } |
1035 | #if SDL_INPUT_LINUXEV |
1036 | static __inline__ int EV_AxisCorrect(SDL_Joystick *joystick, int which, int value) |
1037 | { |
1038 | struct axis_correct *correct; |
1039 | |
1040 | correct = &joystick->hwdata->abs_correct[which]; |
1041 | if ( correct->used ) { |
1042 | if ( value > correct->coef[0] ) { |
1043 | if ( value < correct->coef[1] ) { |
1044 | return 0; |
1045 | } |
1046 | value -= correct->coef[1]; |
1047 | } else { |
1048 | value -= correct->coef[0]; |
1049 | } |
1050 | value *= correct->coef[2]; |
1051 | value >>= 14; |
1052 | } |
1053 | |
1054 | /* Clamp and return */ |
1055 | if ( value < -32768 ) return -32768; |
1056 | if ( value > 32767 ) return 32767; |
1057 | |
1058 | return value; |
1059 | } |
1060 | |
1061 | static __inline__ void EV_HandleEvents(SDL_Joystick *joystick) |
1062 | { |
1063 | struct input_event events[32]; |
1064 | int i, len; |
1065 | int code; |
1066 | |
1067 | #ifndef NO_LOGICAL_JOYSTICKS |
1068 | if (SDL_joylist[joystick->index].fname == NULL) { |
1069 | SDL_joylist_head(i, joystick->index); |
1070 | return EV_HandleEvents(SDL_joylist[i].joy); |
1071 | } |
1072 | #endif |
1073 | |
1074 | while ((len=read(joystick->hwdata->fd, events, (sizeof events))) > 0) { |
1075 | len /= sizeof(events[0]); |
1076 | for ( i=0; i<len; ++i ) { |
1077 | code = events[i].code; |
1078 | switch (events[i].type) { |
1079 | case EV_KEY: |
1080 | if ( code >= BTN_MISC ) { |
1081 | code -= BTN_MISC; |
1082 | #ifndef NO_LOGICAL_JOYSTICKS |
1083 | if (!LogicalJoystickButton(joystick, |
1084 | joystick->hwdata->key_map[code], |
1085 | events[i].value)) |
1086 | #endif |
1087 | SDL_PrivateJoystickButton(joystick, |
1088 | joystick->hwdata->key_map[code], |
1089 | events[i].value); |
1090 | } |
1091 | break; |
1092 | case EV_ABS: |
1093 | switch (code) { |
1094 | case ABS_HAT0X: |
1095 | case ABS_HAT0Y: |
1096 | case ABS_HAT1X: |
1097 | case ABS_HAT1Y: |
1098 | case ABS_HAT2X: |
1099 | case ABS_HAT2Y: |
1100 | case ABS_HAT3X: |
1101 | case ABS_HAT3Y: |
1102 | code -= ABS_HAT0X; |
1103 | HandleHat(joystick, code/2, code%2, |
1104 | events[i].value); |
1105 | break; |
1106 | default: |
1107 | events[i].value = EV_AxisCorrect(joystick, code, events[i].value); |
1108 | #ifndef NO_LOGICAL_JOYSTICKS |
1109 | if (!LogicalJoystickAxis(joystick, |
1110 | joystick->hwdata->abs_map[code], |
1111 | events[i].value)) |
1112 | #endif |
1113 | SDL_PrivateJoystickAxis(joystick, |
1114 | joystick->hwdata->abs_map[code], |
1115 | events[i].value); |
1116 | break; |
1117 | } |
1118 | break; |
1119 | case EV_REL: |
1120 | switch (code) { |
1121 | case REL_X: |
1122 | case REL_Y: |
1123 | code -= REL_X; |
1124 | HandleBall(joystick, code/2, code%2, |
1125 | events[i].value); |
1126 | break; |
1127 | default: |
1128 | break; |
1129 | } |
1130 | break; |
1131 | default: |
1132 | break; |
1133 | } |
1134 | } |
1135 | } |
1136 | } |
1137 | #endif /* SDL_INPUT_LINUXEV */ |
1138 | |
1139 | void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick) |
1140 | { |
1141 | int i; |
1142 | |
1143 | #if SDL_INPUT_LINUXEV |
1144 | if ( joystick->hwdata->is_hid ) |
1145 | EV_HandleEvents(joystick); |
1146 | else |
1147 | #endif |
1148 | JS_HandleEvents(joystick); |
1149 | |
1150 | /* Deliver ball motion updates */ |
1151 | for ( i=0; i<joystick->nballs; ++i ) { |
1152 | int xrel, yrel; |
1153 | |
1154 | xrel = joystick->hwdata->balls[i].axis[0]; |
1155 | yrel = joystick->hwdata->balls[i].axis[1]; |
1156 | if ( xrel || yrel ) { |
1157 | joystick->hwdata->balls[i].axis[0] = 0; |
1158 | joystick->hwdata->balls[i].axis[1] = 0; |
1159 | SDL_PrivateJoystickBall(joystick, (Uint8)i, xrel, yrel); |
1160 | } |
1161 | } |
1162 | } |
1163 | |
1164 | /* Function to close a joystick after use */ |
1165 | void SDL_SYS_JoystickClose(SDL_Joystick *joystick) |
1166 | { |
1167 | #ifndef NO_LOGICAL_JOYSTICKS |
1168 | register int i; |
1169 | if (SDL_joylist[joystick->index].fname == NULL) { |
1170 | SDL_joylist_head(i, joystick->index); |
1171 | SDL_JoystickClose(SDL_joylist[i].joy); |
1172 | } |
1173 | #endif |
1174 | |
1175 | if ( joystick->hwdata ) { |
1176 | #ifndef NO_LOGICAL_JOYSTICKS |
1177 | if (SDL_joylist[joystick->index].fname != NULL) |
1178 | #endif |
1179 | close(joystick->hwdata->fd); |
1180 | if ( joystick->hwdata->hats ) { |
1181 | SDL_free(joystick->hwdata->hats); |
1182 | } |
1183 | if ( joystick->hwdata->balls ) { |
1184 | SDL_free(joystick->hwdata->balls); |
1185 | } |
1186 | SDL_free(joystick->hwdata); |
1187 | joystick->hwdata = NULL; |
1188 | } |
1189 | } |
1190 | |
1191 | /* Function to perform any system-specific joystick related cleanup */ |
1192 | void SDL_SYS_JoystickQuit(void) |
1193 | { |
1194 | int i; |
1195 | |
1196 | for ( i=0; SDL_joylist[i].fname; ++i ) { |
1197 | SDL_free(SDL_joylist[i].fname); |
1198 | SDL_joylist[i].fname = NULL; |
1199 | } |
1200 | } |
1201 | |
1202 | #endif /* SDL_JOYSTICK_LINUX */ |