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