1 /** \file   joy-osx-hid.c
2  * \brief   Mac OS X joystick support using USB HID devices
3  *
4  * \author  Christian Vogelgsang <chris@vogelgsang.org>
5  */
6 
7 /*
8  * This file is part of VICE, the Versatile Commodore Emulator.
9  * See README for copyright notice.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24  *  02111-1307  USA.
25  *
26  */
27 
28 #define JOY_INTERNAL
29 
30 #include "vice.h"
31 
32 #ifdef MACOSX_SUPPORT
33 #include "joy.h"
34 #include "log.h"
35 #include "lib.h"
36 #include "joy-osx-hid.h"
37 
38 #ifdef HAS_JOYSTICK
39 
40 /* ----- Static Data ----- */
41 
42 static joy_hid_device_array_t *device_array;
43 
44 joy_hid_axis_info_t joy_hid_axis_infos[] = {
45     { "X", kHIDUsage_GD_X },
46     { "Y", kHIDUsage_GD_Y },
47     { "Z", kHIDUsage_GD_Z },
48     { "Rx", kHIDUsage_GD_Rx },
49     { "Ry", kHIDUsage_GD_Ry },
50     { "Rz", kHIDUsage_GD_Rz },
51     { "Slider", kHIDUsage_GD_Slider },
52     { NULL, 0 }
53 };
54 
55 /* ----- Tools ----- */
56 
57 /* count devices with same vid:pid */
get_device_serial(int size,joy_hid_device_t * devs,int vid,int pid)58 static int get_device_serial(int size, joy_hid_device_t *devs, int vid, int pid)
59 {
60     int i;
61     int serial = 0;
62 
63     for(i=0;i<size;i++) {
64         if((devs->vendor_id == vid) && (devs->product_id == pid)) {
65             serial++;
66         }
67         devs++;
68     }
69     return serial;
70 }
71 
assign_device_serials(joy_hid_device_array_t * devices)72 static void assign_device_serials(joy_hid_device_array_t *devices)
73 {
74     int num_devices = devices->num_devices;
75     joy_hid_device_t *d = devices->devices;
76     int i;
77     for(i=0;i<num_devices;i++) {
78         int serial = get_device_serial(i, devices->devices, d->vendor_id, d->product_id);
79         d->serial = serial;
80         d++;
81     }
82 }
83 
detect_elements(struct joystick_descriptor * joy)84 static int detect_elements(struct joystick_descriptor *joy)
85 {
86     joy->num_hid_axis = 0;
87     joy->num_hid_buttons = 0;
88     joy->num_hid_hat_switches = 0;
89 
90     int i;
91     joy_hid_device_t *device = joy->hid->device;
92     int num_elements = device->num_elements;
93     int undetected = 0;
94     for(i=0;i<num_elements;i++) {
95         joy_hid_element_t *element = &device->elements[i];
96 
97         int usage_page = element->usage_page;
98         int usage = element->usage;
99 
100         if(usage_page == kHIDPage_GenericDesktop) {
101             switch(usage) {
102                 case kHIDUsage_GD_Pointer:
103                 case kHIDUsage_GD_GamePad:
104                 case kHIDUsage_GD_Joystick:
105                     /* ignore */
106                     break;
107                 case kHIDUsage_GD_X:
108                 case kHIDUsage_GD_Y:
109                 case kHIDUsage_GD_Z:
110                 case kHIDUsage_GD_Rx:
111                 case kHIDUsage_GD_Ry:
112                 case kHIDUsage_GD_Rz:
113                 case kHIDUsage_GD_Slider:
114                     /* axis found */
115                     if (joy->num_hid_axis == JOYSTICK_DESCRIPTOR_MAX_AXIS) {
116                         undetected++;
117                     } else {
118                         /* check for valid axis */
119                         if(element->min_pvalue != element->max_pvalue) {
120 
121                             /* check if axis already occured ?
122                                this works around broken HID devices
123                                that register multiple times e.g. an x axis
124                                but only the last one works actually...
125                             */
126                             int j;
127                             for(j=0;j<joy->num_hid_axis;j++) {
128                                 if(joy->hid->all_axis[j]->usage == usage)
129                                     break;
130                             }
131 
132                             /* create new or overwrite old axis */
133                             joy->hid->all_axis[j] = element;
134                             if(j==joy->num_hid_axis) {
135                                 joy->num_hid_axis++;
136                             } else {
137                                 log_message(LOG_DEFAULT, "joy-hid: ignoring multiple occurrence of axis element (0x%x)! (broken HID device?)", usage);
138                             }
139 
140                         } else {
141                             log_message(LOG_DEFAULT, "joy-hid: ignoring element (0x%x) with invalid range! (broken HID device?)", usage);
142                         }
143                     }
144                     break;
145                 case kHIDUsage_GD_Hatswitch:
146                     if(joy->num_hid_hat_switches == JOYSTICK_DESCRIPTOR_MAX_HAT_SWITCHES) {
147                         undetected++;
148                     } else {
149                         joy->hid->all_hat_switches[joy->num_hid_hat_switches] = element;
150                         joy->num_hid_hat_switches++;
151                     }
152                     break;
153                 case kHIDUsage_GD_Wheel:
154                 case kHIDUsage_GD_Dial:
155                     undetected++;
156                     break;
157                 default:
158                     undetected++;
159                     break;
160             }
161         }
162         else if(usage_page == kHIDPage_Button) {
163             /* buttons found */
164             if (joy->num_hid_buttons == JOYSTICK_DESCRIPTOR_MAX_BUTTONS) {
165                 undetected++;
166             } else {
167                 joy->hid->all_buttons[joy->num_hid_buttons] = element;
168                 joy->num_hid_buttons++;
169             }
170         }
171     }
172     return undetected;
173 }
174 
175 /* ----- API ----- */
176 
joy_hid_init(void)177 int  joy_hid_init(void)
178 {
179     /* first initialize lib */
180     int result = joy_hidlib_init();
181     if(result != 0) {
182         return result;
183     }
184 
185     /* build device array */
186     device_array = joy_hidlib_enumerate_devices();
187     if(device_array != NULL) {
188         assign_device_serials(device_array);
189         return device_array->num_devices;
190     } else {
191         return -1;
192     }
193 }
194 
joy_hid_exit(void)195 void joy_hid_exit(void)
196 {
197     /* free device array */
198     if(device_array) {
199         joy_hidlib_free_devices(device_array);
200         device_array = NULL;
201     }
202 
203     /* shutdown lib*/
204     joy_hidlib_exit();
205 }
206 
joy_hid_reload(void)207 int  joy_hid_reload(void)
208 {
209     /* reinit */
210     joy_hid_exit();
211     return joy_hid_init();
212 }
213 
joy_hid_get_devices(void)214 const joy_hid_device_array_t *joy_hid_get_devices(void)
215 {
216     return device_array;
217 }
218 
joy_hid_map_device(struct joystick_descriptor * joy,joy_hid_device_t * device)219 int  joy_hid_map_device(struct joystick_descriptor *joy, joy_hid_device_t *device)
220 {
221     /* already a device mapped? */
222     if(joy->hid->device != NULL) {
223         joy_hid_unmap_device(joy);
224     }
225 
226     /* open device */
227     int result = joy_hidlib_open_device(device);
228     if(result != 0) {
229         return result;
230     }
231 
232     /* enumerate all elements */
233     result = joy_hidlib_enumerate_elements(device);
234     if(result < 0) {
235         return result;
236     }
237 
238     joy->hid->device = device;
239     joy->mapped = 1;
240 
241     /* return number of undetected elements */
242     return detect_elements(joy);
243 }
244 
joy_hid_unmap_device(struct joystick_descriptor * joy)245 void joy_hid_unmap_device(struct joystick_descriptor *joy)
246 {
247     joy_hid_descriptor_t *hid = joy->hid;
248     joy_hid_device_t *device = hid->device;
249 
250     if(device != NULL) {
251         joy_hidlib_free_elements(device);
252         joy_hidlib_close_device(device);
253         hid->device = NULL;
254     }
255 
256     /* clear all */
257     memset( hid, 0, sizeof(joy_hid_descriptor_t) );
258 
259     joy->mapped = 0;
260 }
261 
262 /* ----- axis ----- */
263 
joy_hid_element_by_usage(struct joystick_descriptor * joy,int usage)264 joy_hid_element_t *joy_hid_element_by_usage(struct joystick_descriptor *joy, int usage)
265 {
266     joy_hid_descriptor_t *hid = joy->hid;
267 
268     /* search axis element with usage */
269     int num_axis = joy->num_hid_axis;
270     int i;
271     for(i=0;i<num_axis;i++) {
272         if(hid->all_axis[i]->usage == usage) {
273             return hid->all_axis[i];
274         }
275     }
276     return NULL;
277 }
278 
joy_hid_reset_axis_range(struct joystick_descriptor * joy,int id,int usage,int logical)279 int joy_hid_reset_axis_range(struct joystick_descriptor *joy, int id, int usage, int logical)
280 {
281     joy_hid_element_t *e = joy_hid_element_by_usage(joy, usage);
282     if(e == NULL) {
283         joy->axis[id].min = 0;
284         joy->axis[id].max = 0;
285         return -1;
286     }
287 
288     joy->axis[id].min = logical ? e->min_lvalue : e->min_pvalue;
289     joy->axis[id].max = logical ? e->max_lvalue : e->max_pvalue;
290     return 0;
291 }
292 
joy_hid_assign_axis(struct joystick_descriptor * joy,int id,int usage,int logical)293 int  joy_hid_assign_axis(struct joystick_descriptor *joy, int id, int usage, int logical)
294 {
295     joy_hid_descriptor_t *hid = joy->hid;
296 
297     joy_hid_element_t *e = joy_hid_element_by_usage(joy, usage);
298     if(e == NULL) {
299         joy->axis[id].mapped = 0;
300         return -1;
301     }
302 
303     hid->mapped_axis[id] = e;
304     joy->axis[id].mapped = 1;
305 
306     joy->axis[id].logical = logical;
307 
308     joy_calc_threshold(joy->axis[id].min,
309                        joy->axis[id].max,
310                        joy->axis[id].threshold,
311                        &joy->axis[id].min_threshold,
312                        &joy->axis[id].max_threshold);
313     return 0;
314 }
315 
joy_hid_detect_axis(struct joystick_descriptor * joy,int id,int logical)316 int  joy_hid_detect_axis(struct joystick_descriptor *joy, int id, int logical)
317 {
318     int threshold = joy->axis[id].threshold;
319 
320     /* search axis that is moved to min or max */
321     int num_axis = joy->num_hid_axis;
322     int i;
323     joy_hid_device_t *device = joy->hid->device;
324     for(i=0;i<num_axis;i++) {
325         joy_hid_element_t *element = joy->hid->all_axis[i];
326 
327         /* calc threshold for this axis */
328         int min = logical ? element->min_lvalue : element->min_pvalue;
329         int max = logical ? element->max_lvalue : element->max_pvalue;
330         int tmin, tmax;
331         joy_calc_threshold(min, max, threshold, &tmin, &tmax);
332 
333         int value;
334         if(joy_hidlib_get_value(device, element, &value, !logical)==0) {
335             if(value < tmin) {
336                 /* auto adjust range */
337                 if(value < min) {
338                     if(logical) {
339                         element->min_lvalue = min;
340                     } else {
341                         element->min_pvalue = min;
342                     }
343                 }
344                 return element->usage;
345             } else if(value > tmax) {
346                 /* auto adjust range */
347                 if(value > max) {
348                     if(logical) {
349                         element->max_lvalue = max;
350                     } else {
351                         element->max_pvalue = max;
352                     }
353                 }
354                 return element->usage;
355             }
356         }
357     }
358     return -1;
359 }
360 
joy_hid_read_axis(struct joystick_descriptor * joy,int id,int * value,int logical)361 int  joy_hid_read_axis(struct joystick_descriptor *joy,int id,int *value, int logical)
362 {
363     joy_hid_device_t *device = joy->hid->device;
364     joy_hid_element_t *element = joy->hid->mapped_axis[id];
365     return joy_hidlib_get_value(device, element, value, !logical);
366 }
367 
joy_hid_info_axis(struct joystick_descriptor * joy,int id,int * min,int * max,int logical)368 int joy_hid_info_axis(struct joystick_descriptor *joy,int id,int *min, int *max, int logical)
369 {
370     joy_hid_element_t *element = joy->hid->mapped_axis[id];
371     if(element != NULL) {
372         *min = logical ? element->min_lvalue : element->min_pvalue;
373         *max = logical ? element->max_lvalue : element->max_pvalue;
374         return 0;
375     } else {
376         *min = 0;
377         *max = 0;
378         return -1;
379     }
380 }
381 
382 /* ----- buttons ----- */
383 
joy_hid_assign_button(struct joystick_descriptor * joy,int id,int usage)384 int  joy_hid_assign_button(struct joystick_descriptor *joy, int id, int usage)
385 {
386     joy_hid_descriptor_t *hid = joy->hid;
387 
388     /* search axis element with usage */
389     int num_buttons = joy->num_hid_buttons;
390     int i;
391     for(i=0;i<num_buttons;i++) {
392         if(hid->all_buttons[i]->usage == usage) {
393             break;
394         }
395     }
396     if(i == num_buttons) {
397         joy->buttons[id].mapped = 0;
398         return -1;
399     }
400 
401     hid->mapped_buttons[id] = hid->all_buttons[i];
402     joy->buttons[id].mapped = 1;
403     return 0;
404 }
405 
joy_hid_detect_button(struct joystick_descriptor * joy)406 int  joy_hid_detect_button(struct joystick_descriptor *joy)
407 {
408     /* search button that is pressed */
409     int num_buttons = joy->num_hid_buttons;
410     int i;
411     joy_hid_device_t *device = joy->hid->device;
412     for(i=0;i<num_buttons;i++) {
413         joy_hid_element_t *element = joy->hid->all_buttons[i];
414 
415         int value;
416         if(joy_hidlib_get_value(device, element, &value, 0)==0) {
417             if(value != 0) {
418                 return element->usage;
419             }
420         }
421     }
422     return -1;
423 }
424 
joy_hid_read_button(struct joystick_descriptor * joy,int id,int * value)425 int  joy_hid_read_button(struct joystick_descriptor *joy, int id, int *value)
426 {
427     joy_hid_device_t *device = joy->hid->device;
428     joy_hid_element_t *element = joy->hid->mapped_buttons[id];
429     return joy_hidlib_get_value(device, element, value, 0);
430 }
431 
432 /* ----- Hat Switch ----- */
433 
joy_hid_assign_hat_switch(struct joystick_descriptor * joy,int serial)434 int  joy_hid_assign_hat_switch(struct joystick_descriptor *joy, int serial)
435 {
436     joy_hid_descriptor_t *hid = joy->hid;
437 
438     /* search hat switch element with usage */
439     int num_hat_switches = joy->num_hid_hat_switches;
440     int i;
441     for(i=0;i<num_hat_switches;i++) {
442         if((i+1) == serial) {
443             break;
444         }
445     }
446     if(i == num_hat_switches) {
447         joy->hat_switch.mapped = 0;
448         return -1;
449     }
450 
451     hid->mapped_hat_switch = hid->all_hat_switches[i];
452     joy->hat_switch.mapped = 1;
453     return 0;
454 }
455 
joy_hid_detect_hat_switch(struct joystick_descriptor * joy)456 int  joy_hid_detect_hat_switch(struct joystick_descriptor *joy)
457 {
458     /* search hat switch that is pressed */
459     int num_hat_switches = joy->num_hid_hat_switches;
460     int i;
461     joy_hid_device_t *device = joy->hid->device;
462     for(i=0;i<num_hat_switches;i++) {
463         joy_hid_element_t *element = joy->hid->all_hat_switches[i];
464 
465         int value;
466         if(joy_hidlib_get_value(device, element, &value, 0)==0) {
467             if((value >= 0)&&(value <= 7)) {
468                 return i + 1;
469             }
470         }
471     }
472     return -1;
473 }
474 
joy_hid_read_hat_switch(struct joystick_descriptor * joy,int * value)475 int  joy_hid_read_hat_switch(struct joystick_descriptor *joy, int *value)
476 {
477     joy_hid_device_t *device = joy->hid->device;
478     joy_hid_element_t *element = joy->hid->mapped_hat_switch;
479     return joy_hidlib_get_value(device, element, value, 0);
480 }
481 
482 /* ----- Tools ----- */
483 
joy_hid_get_axis_name(int usage)484 const char *joy_hid_get_axis_name(int usage)
485 {
486     joy_hid_axis_info_t *ptr = joy_hid_axis_infos;;
487     while(ptr->name != NULL) {
488         if (ptr->usage == usage) {
489             return ptr->name;
490         }
491         ptr++;
492     }
493     return NULL;
494 }
495 
joy_hid_get_axis_usage(const char * name)496 int joy_hid_get_axis_usage(const char *name)
497 {
498     if (name == NULL) {
499         return -1;
500     }
501 
502     joy_hid_axis_info_t *ptr = joy_hid_axis_infos;;
503     while(ptr->name != NULL) {
504         if (strcmp(ptr->name, name) == 0) {
505             return ptr->usage;
506         }
507         ptr++;
508     }
509     return -1;
510 }
511 
512 #endif /* HAS_JOYSTICK */
513 #endif
514 
515