1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2013-2014 - Jason Fetters
3 * Copyright (C) 2011-2017 - Daniel De Matteis
4 * Courtesy Contributor - Olivier Parra
5 *
6 * RetroArch is free software: you can redistribute it and/or modify it under the terms
7 * of the GNU General Public License as published by the Free Software Found-
8 * ation, either version 3 of the License, or (at your option) any later version.
9 *
10 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 * PURPOSE. See the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along with RetroArch.
15 * If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdlib.h>
19
20 #include <string/stdstring.h>
21
22 #include <IOKit/hid/IOHIDManager.h>
23 #include <IOKit/hid/IOHIDKeys.h>
24
25 #include <retro_miscellaneous.h>
26
27 #include "../input_defines.h"
28 #include "../input_driver.h"
29
30 #include "../connect/joypad_connection.h"
31 #include "../../tasks/tasks_internal.h"
32 #include "../../verbosity.h"
33
34 typedef struct apple_input_rec
35 {
36 IOHIDElementCookie cookie;
37 uint32_t id;
38 struct apple_input_rec *next;
39 } apple_input_rec_t;
40
41 typedef struct apple_hid
42 {
43 IOHIDManagerRef ptr;
44 joypad_connection_t *slots;
45 uint32_t buttons[MAX_USERS];
46 int16_t axes[MAX_USERS][11];
47 int8_t hats[MAX_USERS][2]; /* MacOS only supports 1 hat AFAICT */
48 } iohidmanager_hid_t;
49
50 struct iohidmanager_hid_adapter
51 {
52 uint32_t slot;
53 IOHIDDeviceRef handle;
54 char name[PATH_MAX_LENGTH];
55 apple_input_rec_t *axes;
56 apple_input_rec_t *hats;
57 apple_input_rec_t *buttons;
58 uint8_t data[2048];
59 #if !(defined(__ppc__) || defined(__ppc64__))
60 uint32_t uniqueId;
61 #endif
62 };
63
iohidmanager_sort_elements(const void * val1,const void * val2,void * context)64 CFComparisonResult iohidmanager_sort_elements(const void *val1, const void *val2, void *context)
65 {
66 uint32_t page1 = (uint32_t)IOHIDElementGetUsagePage((IOHIDElementRef)val1);
67 uint32_t page2 = (uint32_t)IOHIDElementGetUsagePage((IOHIDElementRef)val2);
68 uint32_t use1 = (uint32_t)IOHIDElementGetUsage((IOHIDElementRef)val1);
69 uint32_t use2 = (uint32_t)IOHIDElementGetUsage((IOHIDElementRef)val2);
70 uint32_t cookie1 = (uint32_t)IOHIDElementGetCookie((IOHIDElementRef)val1);
71 uint32_t cookie2 = (uint32_t)IOHIDElementGetCookie((IOHIDElementRef)val2);
72
73 if (page1 != page2)
74 return (CFComparisonResult)(page1 > page2);
75
76 if (use1 != use2)
77 return (CFComparisonResult)(use1 > use2);
78
79 return (CFComparisonResult)(cookie1 > cookie2);
80 }
81
iohidmanager_check_for_id(apple_input_rec_t * rec,uint32_t id)82 static bool iohidmanager_check_for_id(apple_input_rec_t *rec, uint32_t id)
83 {
84 while (rec)
85 {
86 if (rec->id == id)
87 return true;
88 rec = rec->next;
89 }
90 return false;
91 }
92
iohidmanager_append_record(apple_input_rec_t * rec,apple_input_rec_t * b)93 static void iohidmanager_append_record(apple_input_rec_t *rec, apple_input_rec_t *b)
94 {
95 apple_input_rec_t *tmp = rec;
96 while (tmp->next)
97 tmp = tmp->next;
98 tmp->next = b;
99 }
100
101 /* Insert a new detected button into a button ordered list.
102 * Button list example with Nimbus Controller:
103 * +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
104 * "id" list member | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 144 | 145 | 146 | 147 | 547 |
105 * Final Button ID | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
106 * +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
107 * Ranges |< X/Y/A/B/L1/L2/R1/R2 buttons >|< D-PAD >|MENU |
108 * In that way, HID button IDs allocation:
109 * - becomes robust and determinist
110 * - remains compatible with previous algorithm (i.e. btn->id = (uint32_t)(use - 1)) and so
111 * compatible with previous autoconfig files.
112 */
iohidmanager_append_record_ordered(apple_input_rec_t ** p_rec,apple_input_rec_t * b)113 static void iohidmanager_append_record_ordered(apple_input_rec_t **p_rec, apple_input_rec_t *b)
114 {
115 apple_input_rec_t *tmp = *p_rec;
116 while (tmp && (tmp->id <= b->id))
117 {
118 p_rec = &tmp->next;
119 tmp = tmp->next;
120 }
121 b->next = tmp;
122 *p_rec = b;
123 }
124
iohidmanager_hid_joypad_query(void * data,unsigned pad)125 static bool iohidmanager_hid_joypad_query(void *data, unsigned pad)
126 {
127 return pad < MAX_USERS;
128 }
129
iohidmanager_hid_joypad_name(void * data,unsigned pad)130 static const char *iohidmanager_hid_joypad_name(void *data, unsigned pad)
131 {
132 /* TODO/FIXME - implement properly */
133 if (pad >= MAX_USERS)
134 return NULL;
135
136 return NULL;
137 }
138
iohidmanager_hid_joypad_get_buttons(void * data,unsigned port,input_bits_t * state)139 static void iohidmanager_hid_joypad_get_buttons(void *data,
140 unsigned port, input_bits_t *state)
141 {
142 iohidmanager_hid_t *hid = (iohidmanager_hid_t*)data;
143 if (hid)
144 return pad_connection_get_buttons(&hid->slots[port], port, state);
145 else
146 BIT256_CLEAR_ALL_PTR(state);
147 }
148
iohidmanager_hid_joypad_button(void * data,unsigned port,uint16_t joykey)149 static int16_t iohidmanager_hid_joypad_button(void *data,
150 unsigned port, uint16_t joykey)
151 {
152 unsigned hat_dir;
153 input_bits_t buttons;
154 iohidmanager_hid_t *hid = (iohidmanager_hid_t*)data;
155
156 if (port >= DEFAULT_MAX_PADS)
157 return 0;
158
159 iohidmanager_hid_joypad_get_buttons(data, port, &buttons);
160
161 hat_dir = GET_HAT_DIR(joykey);
162
163 /* Check hat. */
164 if (hat_dir)
165 {
166 unsigned h = GET_HAT(joykey);
167 if (h >= 1)
168 return 0;
169
170 switch (hat_dir)
171 {
172 case HAT_LEFT_MASK:
173 return (hid->hats[port][0] < 0);
174 case HAT_RIGHT_MASK:
175 return (hid->hats[port][0] > 0);
176 case HAT_UP_MASK:
177 return (hid->hats[port][1] < 0);
178 case HAT_DOWN_MASK:
179 return (hid->hats[port][1] > 0);
180 default:
181 break;
182 }
183 /* hat requested and no hat button down */
184 }
185 else if (joykey < 32)
186 return ((BIT256_GET(buttons, joykey) != 0)
187 || ((hid->buttons[port] & (1 << joykey)) != 0));
188 return 0;
189 }
190
iohidmanager_hid_joypad_axis(void * data,unsigned port,uint32_t joyaxis)191 static int16_t iohidmanager_hid_joypad_axis(void *data,
192 unsigned port, uint32_t joyaxis)
193 {
194 iohidmanager_hid_t *hid = (iohidmanager_hid_t*)data;
195
196 if (AXIS_NEG_GET(joyaxis) < 11)
197 {
198 int16_t val = hid->axes[port][AXIS_NEG_GET(joyaxis)]
199 + pad_connection_get_axis(&hid->slots[port],
200 port, AXIS_NEG_GET(joyaxis));
201
202 if (val < 0)
203 return val;
204 }
205 else if (AXIS_POS_GET(joyaxis) < 11)
206 {
207 int16_t val = hid->axes[port][AXIS_POS_GET(joyaxis)]
208 + pad_connection_get_axis(&hid->slots[port],
209 port, AXIS_POS_GET(joyaxis));
210
211 if (val > 0)
212 return val;
213 }
214 return 0;
215 }
216
217
iohidmanager_hid_joypad_state(void * data,rarch_joypad_info_t * joypad_info,const void * binds_data,unsigned port)218 static int16_t iohidmanager_hid_joypad_state(
219 void *data,
220 rarch_joypad_info_t *joypad_info,
221 const void *binds_data,
222 unsigned port)
223 {
224 unsigned i;
225 int16_t ret = 0;
226 const struct retro_keybind *binds = (const struct retro_keybind*)binds_data;
227 uint16_t port_idx = joypad_info->joy_idx;
228
229 for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++)
230 {
231 /* Auto-binds are per joypad, not per user. */
232 const uint64_t joykey = (binds[i].joykey != NO_BTN)
233 ? binds[i].joykey : joypad_info->auto_binds[i].joykey;
234 const uint32_t joyaxis = (binds[i].joyaxis != AXIS_NONE)
235 ? binds[i].joyaxis : joypad_info->auto_binds[i].joyaxis;
236 if (
237 (uint16_t)joykey != NO_BTN
238 && iohidmanager_hid_joypad_button(data, port_idx, (uint16_t)joykey))
239 ret |= ( 1 << i);
240 else if (joyaxis != AXIS_NONE &&
241 ((float)abs(iohidmanager_hid_joypad_axis(data, port_idx, joyaxis))
242 / 0x8000) > joypad_info->axis_threshold)
243 ret |= (1 << i);
244 }
245
246 return ret;
247 }
248
iohidmanager_hid_joypad_rumble(void * data,unsigned pad,enum retro_rumble_effect effect,uint16_t strength)249 static bool iohidmanager_hid_joypad_rumble(void *data, unsigned pad,
250 enum retro_rumble_effect effect, uint16_t strength)
251 {
252 iohidmanager_hid_t *hid = (iohidmanager_hid_t*)data;
253 if (!hid)
254 return false;
255 return pad_connection_rumble(&hid->slots[pad], pad, effect, strength);
256 }
257
iohidmanager_hid_device_send_control(void * data,uint8_t * data_buf,size_t size)258 static void iohidmanager_hid_device_send_control(void *data,
259 uint8_t* data_buf, size_t size)
260 {
261 struct iohidmanager_hid_adapter *adapter =
262 (struct iohidmanager_hid_adapter*)data;
263
264 if (adapter)
265 IOHIDDeviceSetReport(adapter->handle,
266 kIOHIDReportTypeOutput, 0x01, data_buf + 1, size - 1);
267 }
268
iohidmanager_hid_device_report(void * data,IOReturn result,void * sender,IOHIDReportType type,uint32_t reportID,uint8_t * report,CFIndex reportLength)269 static void iohidmanager_hid_device_report(void *data,
270 IOReturn result, void *sender,
271 IOHIDReportType type, uint32_t reportID, uint8_t *report,
272 CFIndex reportLength)
273 {
274 struct iohidmanager_hid_adapter *adapter =
275 (struct iohidmanager_hid_adapter*)data;
276 iohidmanager_hid_t *hid = (iohidmanager_hid_t*)hid_driver_get_data();
277
278 if (hid && adapter)
279 pad_connection_packet(&hid->slots[adapter->slot], adapter->slot,
280 adapter->data, (uint32_t)(reportLength + 1));
281 }
282
283 /* NOTE: I pieced this together through trial and error,
284 * any corrections are welcome. */
285
iohidmanager_hid_device_input_callback(void * data,IOReturn result,void * sender,IOHIDValueRef value)286 static void iohidmanager_hid_device_input_callback(void *data, IOReturn result,
287 void* sender, IOHIDValueRef value)
288 {
289 iohidmanager_hid_t *hid = (iohidmanager_hid_t*)hid_driver_get_data();
290 struct iohidmanager_hid_adapter *adapter = (struct iohidmanager_hid_adapter*)data;
291 IOHIDElementRef element = IOHIDValueGetElement(value);
292 uint32_t type = (uint32_t)IOHIDElementGetType(element);
293 uint32_t page = (uint32_t)IOHIDElementGetUsagePage(element);
294 uint32_t use = (uint32_t)IOHIDElementGetUsage(element);
295 uint32_t cookie = (uint32_t)IOHIDElementGetCookie(element);
296 apple_input_rec_t *tmp = NULL;
297
298 if (type != kIOHIDElementTypeInput_Misc)
299 if (type != kIOHIDElementTypeInput_Button)
300 if (type != kIOHIDElementTypeInput_Axis)
301 return;
302
303 /* Joystick handler.
304 * TODO: Can GamePad work the same? */
305
306 int pushed_button = 0;
307
308 switch (page)
309 {
310 case kHIDPage_GenericDesktop:
311 switch (type)
312 {
313 case kIOHIDElementTypeInput_Misc:
314 switch (use)
315 {
316 case kHIDUsage_GD_Hatswitch:
317 {
318 tmp = adapter->hats;
319
320 while (tmp && tmp->cookie != (IOHIDElementCookie)cookie)
321 tmp = tmp->next;
322
323 if (tmp && tmp->cookie == (IOHIDElementCookie)cookie)
324 {
325 CFIndex min = IOHIDElementGetLogicalMin(element);
326 CFIndex range = IOHIDElementGetLogicalMax(element) - min;
327 CFIndex val = IOHIDValueGetIntegerValue(value);
328
329 if (range == 3)
330 val *= 2;
331
332 if(min == 1)
333 val--;
334
335 switch(val)
336 {
337 case 0:
338 /* pos = up */
339 hid->hats[adapter->slot][0] = 0;
340 hid->hats[adapter->slot][1] = -1;
341 break;
342 case 1:
343 /* pos = up+right */
344 hid->hats[adapter->slot][0] = 1;
345 hid->hats[adapter->slot][1] = -1;
346 break;
347 case 2:
348 /* pos = right */
349 hid->hats[adapter->slot][0] = 1;
350 hid->hats[adapter->slot][1] = 0;
351 break;
352 case 3:
353 /* pos = down+right */
354 hid->hats[adapter->slot][0] = 1;
355 hid->hats[adapter->slot][1] = 1;
356 break;
357 case 4:
358 /* pos = down */
359 hid->hats[adapter->slot][0] = 0;
360 hid->hats[adapter->slot][1] = 1;
361 break;
362 case 5:
363 /* pos = down+left */
364 hid->hats[adapter->slot][0] = -1;
365 hid->hats[adapter->slot][1] = 1;
366 break;
367 case 6:
368 /* pos = left */
369 hid->hats[adapter->slot][0] = -1;
370 hid->hats[adapter->slot][1] = 0;
371 break;
372 case 7:
373 /* pos = up_left */
374 hid->hats[adapter->slot][0] = -1;
375 hid->hats[adapter->slot][1] = -1;
376 break;
377 default:
378 /* pos = centered */
379 hid->hats[adapter->slot][0] = 0;
380 hid->hats[adapter->slot][1] = 0;
381 break;
382 }
383 }
384 }
385 break;
386 default:
387 tmp = adapter->axes;
388
389 while (tmp && tmp->cookie != (IOHIDElementCookie)cookie)
390 tmp = tmp->next;
391
392 if (tmp)
393 {
394 if (tmp->cookie == (IOHIDElementCookie)cookie)
395 {
396 CFIndex min = IOHIDElementGetPhysicalMin(element);
397 CFIndex state = IOHIDValueGetIntegerValue(value) - min;
398 CFIndex max = IOHIDElementGetPhysicalMax(element) - min;
399 float val = (float)state / (float)max;
400
401 hid->axes[adapter->slot][tmp->id] =
402 ((val * 2.0f) - 1.0f) * 32767.0f;
403 }
404 }
405 else
406 pushed_button = 1;
407 break;
408 }
409 break;
410 }
411 break;
412 case kHIDPage_Consumer:
413 case kHIDPage_Button:
414 switch (type)
415 {
416 case kIOHIDElementTypeInput_Misc:
417 case kIOHIDElementTypeInput_Button:
418 pushed_button = 1;
419 break;
420 }
421 break;
422 case kHIDPage_Simulation:
423 switch (type)
424 {
425 case kIOHIDElementTypeInput_Misc:
426 switch (use)
427 {
428 default:
429 tmp = adapter->axes;
430
431 while (tmp && tmp->cookie != (IOHIDElementCookie)cookie)
432 {
433 tmp = tmp->next;
434 }
435 if (tmp)
436 {
437 if (tmp->cookie == (IOHIDElementCookie)cookie)
438 {
439 CFIndex min = IOHIDElementGetPhysicalMin(element);
440 CFIndex state = IOHIDValueGetIntegerValue(value) - min;
441 CFIndex max = IOHIDElementGetPhysicalMax(element) - min;
442 float val = (float)state / (float)max;
443
444 hid->axes[adapter->slot][tmp->id] =
445 ((val * 2.0f) - 1.0f) * 32767.0f;
446 }
447 }
448 else
449 pushed_button = 1;
450 break;
451 }
452 break;
453 }
454 break;
455 }
456
457 if (pushed_button)
458 {
459 uint8_t bit = 0;
460
461 tmp = adapter->buttons;
462
463 while (tmp && tmp->cookie != (IOHIDElementCookie)cookie)
464 {
465 bit++;
466 tmp = tmp->next;
467 }
468
469 if (tmp && tmp->cookie == (IOHIDElementCookie)cookie)
470 {
471 CFIndex state = IOHIDValueGetIntegerValue(value);
472 if (state)
473 BIT64_SET(hid->buttons[adapter->slot], bit);
474 else
475 BIT64_CLEAR(hid->buttons[adapter->slot], bit);
476 }
477 }
478 }
479
iohidmanager_hid_device_remove(void * data,IOReturn result,void * sender)480 static void iohidmanager_hid_device_remove(void *data,
481 IOReturn result, void* sender)
482 {
483 struct iohidmanager_hid_adapter *adapter =
484 (struct iohidmanager_hid_adapter*)data;
485 iohidmanager_hid_t *hid = (iohidmanager_hid_t*)
486 hid_driver_get_data();
487
488 if (hid && adapter && (adapter->slot < MAX_USERS))
489 {
490 input_autoconfigure_disconnect(adapter->slot, adapter->name);
491
492 hid->buttons[adapter->slot] = 0;
493 memset(hid->axes[adapter->slot], 0, sizeof(hid->axes));
494
495 pad_connection_pad_deinit(&hid->slots[adapter->slot], adapter->slot);
496 }
497
498 if (adapter)
499 {
500 apple_input_rec_t* tmp = NULL;
501 while (adapter->hats != NULL)
502 {
503 tmp = adapter->hats;
504 adapter->hats = adapter->hats->next;
505 free(tmp);
506 }
507
508 while (adapter->axes != NULL)
509 {
510 tmp = adapter->axes;
511 adapter->axes = adapter->axes->next;
512 free(tmp);
513 }
514
515 while (adapter->buttons != NULL)
516 {
517 tmp = adapter->buttons;
518 adapter->buttons = adapter->buttons->next;
519 free(tmp);
520 }
521 free(adapter);
522 }
523 }
524
iohidmanager_hid_device_get_int_property(IOHIDDeviceRef device,CFStringRef key)525 static int32_t iohidmanager_hid_device_get_int_property(
526 IOHIDDeviceRef device, CFStringRef key)
527 {
528 CFNumberRef ref = (CFNumberRef)IOHIDDeviceGetProperty(device, key);
529
530 if (ref && (CFGetTypeID(ref) == CFNumberGetTypeID()))
531 {
532 int32_t value = 0;
533 CFNumberGetValue((CFNumberRef)ref, kCFNumberIntType, &value);
534 return value;
535 }
536
537 return 0;
538 }
539
iohidmanager_hid_device_get_vendor_id(IOHIDDeviceRef device)540 static uint16_t iohidmanager_hid_device_get_vendor_id(IOHIDDeviceRef device)
541 {
542 return iohidmanager_hid_device_get_int_property(device,
543 CFSTR(kIOHIDVendorIDKey));
544 }
545
iohidmanager_hid_device_get_product_id(IOHIDDeviceRef device)546 static uint16_t iohidmanager_hid_device_get_product_id(IOHIDDeviceRef device)
547 {
548 return iohidmanager_hid_device_get_int_property(device,
549 CFSTR(kIOHIDProductIDKey));
550 }
551
iohidmanager_hid_device_get_location_id(IOHIDDeviceRef device)552 static uint32_t iohidmanager_hid_device_get_location_id(IOHIDDeviceRef device)
553 {
554 return iohidmanager_hid_device_get_int_property(device,
555 CFSTR(kIOHIDLocationIDKey));
556 }
557
558 #if !(defined(__ppc__) || defined(__ppc64__))
iohidmanager_hid_device_get_unique_id(IOHIDDeviceRef device)559 static uint32_t iohidmanager_hid_device_get_unique_id(IOHIDDeviceRef device)
560 {
561 /* osx seems to assign an unique id to each device when they are plugged in
562 * the id change if device is unplugged/plugged, but it's unique amongst the
563 * other device plugged */
564 return iohidmanager_hid_device_get_int_property(device,CFSTR(kIOHIDUniqueIDKey));
565 }
566 #endif
567
iohidmanager_hid_device_get_product_string(IOHIDDeviceRef device,char * buf,size_t len)568 static void iohidmanager_hid_device_get_product_string(
569 IOHIDDeviceRef device, char *buf, size_t len)
570 {
571 CFStringRef ref = (CFStringRef)
572 IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
573
574 if (ref)
575 CFStringGetCString(ref, buf, len, kCFStringEncodingUTF8);
576 }
577
iohidmanager_hid_device_add_autodetect(unsigned idx,const char * device_name,const char * driver_name,uint16_t dev_vid,uint16_t dev_pid)578 static void iohidmanager_hid_device_add_autodetect(unsigned idx,
579 const char *device_name, const char *driver_name,
580 uint16_t dev_vid, uint16_t dev_pid)
581 {
582 input_autoconfigure_connect(
583 device_name,
584 NULL,
585 "hid",
586 idx,
587 dev_vid,
588 dev_pid
589 );
590
591 RARCH_LOG("Port %d: %s.\n", idx, device_name);
592 }
593
594 #if defined(__ppc__) || defined(__ppc64__)
iohidmanager_hid_device_add(IOHIDDeviceRef device,iohidmanager_hid_t * hid)595 static void iohidmanager_hid_device_add(IOHIDDeviceRef device,
596 iohidmanager_hid_t* hid)
597 #else
598 static void iohidmanager_hid_device_add_device(
599 IOHIDDeviceRef device, iohidmanager_hid_t* hid)
600 #endif
601 {
602 int i;
603
604 /* get device unique id */
605 #if !(defined(__ppc__) || defined(__ppc64__))
606 uint32_t deviceUniqueId = iohidmanager_hid_device_get_unique_id(device);
607 #endif
608
609 static const uint32_t axis_use_ids[11] =
610 {
611 kHIDUsage_GD_X,
612 kHIDUsage_GD_Y,
613 kHIDUsage_GD_Rx,
614 kHIDUsage_GD_Ry,
615 kHIDUsage_GD_Z,
616 kHIDUsage_GD_Rz,
617 kHIDUsage_Sim_Rudder,
618 kHIDUsage_Sim_Throttle,
619 kHIDUsage_Sim_Steering,
620 kHIDUsage_Sim_Accelerator,
621 kHIDUsage_Sim_Brake
622 };
623
624 #if !(defined(__ppc__) || defined(__ppc64__))
625 /* check if pad was already registered previously (by deterministic method)
626 * if so do not re-add the pad */
627 for (i=0; i<MAX_USERS; i++)
628 {
629 struct iohidmanager_hid_adapter *a = (struct iohidmanager_hid_adapter*)hid->slots[i].data;
630 if (!a)
631 continue;
632 if (a->uniqueId == deviceUniqueId)
633 return;
634 }
635 #endif
636
637 IOReturn ret;
638 uint16_t dev_vid, dev_pid;
639 CFArrayRef elements_raw;
640 int count;
641 CFMutableArrayRef elements;
642 CFRange range;
643 bool found_axis[11] =
644 { false, false, false, false, false, false, false, false, false, false, false };
645 apple_input_rec_t *tmp = NULL;
646 apple_input_rec_t *tmpButtons = NULL;
647 apple_input_rec_t *tmpAxes = NULL;
648 struct iohidmanager_hid_adapter *adapter = (struct iohidmanager_hid_adapter*)
649 calloc(1, sizeof(*adapter));
650
651 if (!adapter)
652 return;
653 if (!hid)
654 goto error;
655
656 adapter->handle = device;
657
658 ret = IOHIDDeviceOpen(device, kIOHIDOptionsTypeNone);
659
660 if (ret != kIOReturnSuccess)
661 goto error;
662
663 /* Move the device's run loop to this thread. */
664 IOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(),
665 kCFRunLoopCommonModes);
666 IOHIDDeviceRegisterRemovalCallback(device,
667 iohidmanager_hid_device_remove, adapter);
668
669 #ifndef IOS
670 iohidmanager_hid_device_get_product_string(device, adapter->name,
671 sizeof(adapter->name));
672 #endif
673
674 dev_vid = iohidmanager_hid_device_get_vendor_id (device);
675 dev_pid = iohidmanager_hid_device_get_product_id (device);
676 #if !(defined(__ppc__) || defined(__ppc64__))
677 adapter->uniqueId = deviceUniqueId;
678 #endif
679
680 adapter->slot = pad_connection_pad_init(hid->slots,
681 adapter->name, dev_vid, dev_pid, adapter,
682 &iohidmanager_hid);
683
684 if (adapter->slot == -1)
685 goto error;
686
687 if (string_is_empty(adapter->name))
688 strcpy(adapter->name, "Unknown Controller With No Name");
689
690 if (pad_connection_has_interface(hid->slots, adapter->slot))
691 IOHIDDeviceRegisterInputReportCallback(device,
692 adapter->data + 1, sizeof(adapter->data) - 1,
693 iohidmanager_hid_device_report, adapter);
694 else
695 IOHIDDeviceRegisterInputValueCallback(device,
696 iohidmanager_hid_device_input_callback, adapter);
697
698 /* scan for buttons, axis, hats */
699 elements_raw = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
700 count = (int)CFArrayGetCount(elements_raw);
701 elements = CFArrayCreateMutableCopy(
702 kCFAllocatorDefault,(CFIndex)count,elements_raw);
703 range = CFRangeMake(0,count);
704
705 CFArraySortValues(elements,
706 range, iohidmanager_sort_elements, NULL);
707
708 for (i = 0; i < count; i++)
709 {
710 IOHIDElementType type;
711 uint32_t page, use, cookie;
712 int detected_button = 0;
713 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
714
715 if (!element)
716 continue;
717
718 type = IOHIDElementGetType(element);
719 page = (uint32_t)IOHIDElementGetUsagePage(element);
720 use = (uint32_t)IOHIDElementGetUsage(element);
721 cookie = (uint32_t)IOHIDElementGetCookie(element);
722
723 switch (page)
724 {
725 case kHIDPage_GenericDesktop:
726 switch (type)
727 {
728 case kIOHIDElementTypeInput_Misc:
729 switch (use)
730 {
731 case kHIDUsage_GD_Hatswitch:
732 {
733 /* as far as I can tell, OSX only reports one Hat */
734 apple_input_rec_t *hat = (apple_input_rec_t *)malloc(sizeof(apple_input_rec_t));
735 hat->id = 0;
736 hat->cookie = (IOHIDElementCookie)cookie;
737 hat->next = NULL;
738 adapter->hats = hat;
739 }
740 break;
741 default:
742 {
743 uint32_t i = 0;
744
745 while (i < 11 && axis_use_ids[i] != use)
746 i++;
747
748 if (i < 11)
749 {
750
751 apple_input_rec_t *axis = (apple_input_rec_t *)malloc(sizeof(apple_input_rec_t));
752 axis->id = i;
753 axis->cookie = (IOHIDElementCookie)cookie;
754 axis->next = NULL;
755
756 if (iohidmanager_check_for_id(adapter->axes,i))
757 {
758 /* axis ID already exists, save to tmp for appending later */
759 if (tmpAxes)
760 iohidmanager_append_record(tmpAxes, axis);
761 else
762 tmpAxes = axis;
763 }
764 else
765 {
766 found_axis[axis->id] = true;
767 if (adapter->axes)
768 iohidmanager_append_record(adapter->axes, axis);
769 else
770 adapter->axes = axis;
771 }
772 }
773 else
774 detected_button = 1;
775 }
776 break;
777 }
778 break;
779 default:
780 /* TODO/FIXME */
781 break;
782 }
783 break;
784 case kHIDPage_Consumer:
785 case kHIDPage_Button:
786 switch (type)
787 {
788 case kIOHIDElementTypeInput_Misc:
789 case kIOHIDElementTypeInput_Button:
790 detected_button = 1;
791 break;
792 default:
793 /* TODO/FIXME */
794 break;
795 }
796 break;
797
798 case kHIDPage_Simulation:
799 switch (use)
800 {
801 default:
802 {
803 uint32_t i = 0;
804
805 while (i < 11 && axis_use_ids[i] != use)
806 i++;
807
808 if (i < 11)
809 {
810 apple_input_rec_t *axis = (apple_input_rec_t *)malloc(sizeof(apple_input_rec_t));
811 axis->id = i;
812 axis->cookie = (IOHIDElementCookie)cookie;
813 axis->next = NULL;
814
815 if (iohidmanager_check_for_id(adapter->axes,i))
816 {
817 /* axis ID already exists, save to tmp for appending later */
818 if (tmpAxes)
819 iohidmanager_append_record(tmpAxes, axis);
820 else
821 tmpAxes = axis;
822 }
823 else
824 {
825 found_axis[axis->id] = true;
826 if (adapter->axes)
827 iohidmanager_append_record(adapter->axes, axis);
828 else
829 adapter->axes = axis;
830 }
831 }
832 else
833 detected_button = 1;
834 }
835 break;
836 }
837 break;
838 }
839
840 if (detected_button)
841 {
842 apple_input_rec_t *btn = (apple_input_rec_t *)malloc(sizeof(apple_input_rec_t));
843 btn->id = (uint32_t)use;
844 btn->cookie = (IOHIDElementCookie)cookie;
845 btn->next = NULL;
846
847 if (iohidmanager_check_for_id(adapter->buttons,btn->id))
848 {
849 if (tmpButtons)
850 iohidmanager_append_record_ordered(&tmpButtons, btn);
851 else
852 tmpButtons = btn;
853 }
854 else
855 {
856 if (adapter->buttons)
857 iohidmanager_append_record_ordered(&adapter->buttons, btn);
858 else
859 adapter->buttons = btn;
860 }
861 }
862 }
863
864 /* take care of buttons/axes with duplicate 'use' values */
865 for (i = 0; i < 11; i++)
866 {
867 if (!found_axis[i] && tmpAxes)
868 {
869 apple_input_rec_t *next = tmpAxes->next;
870 tmpAxes->id = i;
871 tmpAxes->next = NULL;
872 iohidmanager_append_record(adapter->axes, tmpAxes);
873 tmpAxes = next;
874 }
875 }
876
877 tmp = adapter->buttons;
878
879 if (tmp)
880 {
881 while (tmp->next)
882 tmp = tmp->next;
883 }
884
885 while (tmpButtons)
886 {
887 apple_input_rec_t *next = tmpButtons->next;
888
889 tmpButtons->id = tmp->id;
890 tmpButtons->next = NULL;
891 tmp->next = tmpButtons;
892
893 tmp = tmp->next;
894 tmpButtons = next;
895 }
896
897 iohidmanager_hid_device_add_autodetect(adapter->slot,
898 adapter->name, iohidmanager_hid.ident, dev_vid, dev_pid);
899
900 return;
901
902 error:
903 {
904 apple_input_rec_t *tmp = NULL;
905 while (adapter->hats != NULL)
906 {
907 tmp = adapter->hats;
908 adapter->hats = adapter->hats->next;
909 free(tmp);
910 }
911 while (adapter->axes != NULL)
912 {
913 tmp = adapter->axes;
914 adapter->axes = adapter->axes->next;
915 free(tmp);
916 }
917 while (adapter->buttons != NULL)
918 {
919 tmp = adapter->buttons;
920 adapter->buttons = adapter->buttons->next;
921 free(tmp);
922 }
923 while (tmpAxes != NULL)
924 {
925 tmp = tmpAxes;
926 tmpAxes = tmpAxes->next;
927 free(tmp);
928 }
929 while (tmpButtons != NULL)
930 {
931 tmp = tmpButtons;
932 tmpButtons = tmpButtons->next;
933 free(tmp);
934 }
935 free(adapter);
936 }
937 }
938
939 #if !(defined(__ppc__) || defined(__ppc64__))
iohidmanager_hid_device_add(void * data,IOReturn result,void * sender,IOHIDDeviceRef device)940 static void iohidmanager_hid_device_add(void *data, IOReturn result,
941 void* sender, IOHIDDeviceRef device)
942 {
943 iohidmanager_hid_t *hid = (iohidmanager_hid_t*) hid_driver_get_data();
944 iohidmanager_hid_device_add_device(device, hid);
945 }
946 #endif
947
iohidmanager_hid_append_matching_dictionary(CFMutableArrayRef array,uint32_t page,uint32_t use)948 static void iohidmanager_hid_append_matching_dictionary(
949 CFMutableArrayRef array,
950 uint32_t page, uint32_t use)
951 {
952 CFMutableDictionaryRef matcher = CFDictionaryCreateMutable(
953 kCFAllocatorDefault, 0,
954 &kCFTypeDictionaryKeyCallBacks,
955 &kCFTypeDictionaryValueCallBacks);
956 CFNumberRef pagen = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
957 CFNumberRef usen = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &use);
958
959 CFDictionarySetValue(matcher, CFSTR(kIOHIDDeviceUsagePageKey), pagen);
960 CFDictionarySetValue(matcher, CFSTR(kIOHIDDeviceUsageKey), usen);
961 CFArrayAppendValue(array, matcher);
962
963 CFRelease(pagen);
964 CFRelease(usen);
965 CFRelease(matcher);
966 }
967
iohidmanager_hid_manager_init(iohidmanager_hid_t * hid)968 static int iohidmanager_hid_manager_init(iohidmanager_hid_t *hid)
969 {
970 if (!hid)
971 return -1;
972 if (hid->ptr) /* already initialized. */
973 return 0;
974
975 hid->ptr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
976
977 if (!hid->ptr)
978 return -1;
979
980 IOHIDManagerSetDeviceMatching(hid->ptr, NULL);
981 IOHIDManagerScheduleWithRunLoop(hid->ptr, CFRunLoopGetCurrent(),
982 kCFRunLoopDefaultMode);
983 return 0;
984 }
985
iohidmanager_hid_manager_free(iohidmanager_hid_t * hid)986 static int iohidmanager_hid_manager_free(iohidmanager_hid_t *hid)
987 {
988 if (!hid || !hid->ptr)
989 return -1;
990
991 IOHIDManagerUnscheduleFromRunLoop(hid->ptr,
992 CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
993 IOHIDManagerClose(hid->ptr, kIOHIDOptionsTypeNone);
994 CFRelease(hid->ptr);
995 hid->ptr = NULL;
996
997 return 0;
998 }
999
iohidmanager_hid_manager_set_device_matching(iohidmanager_hid_t * hid)1000 static int iohidmanager_hid_manager_set_device_matching(
1001 iohidmanager_hid_t *hid)
1002 {
1003 /* deterministically add all device currently plugged when lanching retroarch
1004 * order by location id which seems to correspond to usb port number */
1005 CFSetRef set = IOHIDManagerCopyDevices(hid->ptr);
1006 CFIndex num_devices = CFSetGetCount(set);
1007 IOHIDDeviceRef *device_array = (IOHIDDeviceRef*)calloc(num_devices, sizeof(IOHIDDeviceRef));
1008 CFSetGetValues(set, (const void **) device_array);
1009
1010 /* re order device by location id */
1011 typedef struct hid_list
1012 {
1013 IOHIDDeviceRef device;
1014 uint32_t lid;
1015 struct hid_list *next;
1016 } hid_list_t;
1017
1018 hid_list_t* devList = NULL;
1019 for (long i=0; i<num_devices;i++)
1020 {
1021 IOHIDDeviceRef dev = device_array[i];
1022 /* filter gamepad/joystick devices */
1023 if ( IOHIDDeviceConformsTo(dev, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick)
1024 || IOHIDDeviceConformsTo(dev, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad)
1025 )
1026 {
1027 if (!devList)
1028 {
1029 devList = (hid_list_t *)malloc(sizeof(hid_list_t));
1030 devList->device = dev;
1031 devList->lid = iohidmanager_hid_device_get_location_id(dev);
1032 devList->next = NULL;
1033 }
1034 else
1035 {
1036 hid_list_t * devnew = (hid_list_t *)malloc(sizeof(hid_list_t));
1037 devnew->device = dev;
1038 devnew->lid = iohidmanager_hid_device_get_location_id(dev);
1039 devnew->next = NULL;
1040
1041 hid_list_t * ptr = devList;
1042 if (devnew->lid < ptr->lid)
1043 {
1044 devnew->next = ptr;
1045 devList = devnew;
1046 }
1047 else
1048 {
1049 while ( ( ptr->lid < devnew->lid ) && (ptr->next != NULL) )
1050 ptr = ptr->next;
1051 devnew->next = ptr->next;
1052 ptr->next = devnew;
1053 }
1054 }
1055 }
1056 }
1057
1058 /* register devices */
1059 hid_list_t * ptr = devList;
1060 while (ptr != NULL)
1061 {
1062 #if defined(__ppc__) || defined(__ppc64__)
1063 iohidmanager_hid_device_add(ptr->device, hid);
1064 #else
1065 iohidmanager_hid_device_add_device(ptr->device, hid);
1066 #endif
1067
1068 //printf("%d\n",ptr->lid);
1069 ptr = ptr->next;
1070 free(devList);
1071 devList = ptr;
1072 }
1073 free(device_array);
1074
1075 #if !(defined(__ppc__) || defined(__ppc64__))
1076 /* register call back to dynamically add device plugged when retroarch is
1077 * running
1078 * those will be added after the one plugged when retroarch was launched,
1079 * and by order they are plugged in (so not deterministic) */
1080 CFMutableArrayRef matcher = CFArrayCreateMutable(kCFAllocatorDefault, 0,
1081 &kCFTypeArrayCallBacks);
1082
1083 if (!matcher)
1084 return -1;
1085
1086 iohidmanager_hid_append_matching_dictionary(matcher,
1087 kHIDPage_GenericDesktop,
1088 kHIDUsage_GD_Joystick);
1089 iohidmanager_hid_append_matching_dictionary(matcher,
1090 kHIDPage_GenericDesktop,
1091 kHIDUsage_GD_GamePad);
1092
1093 IOHIDManagerSetDeviceMatchingMultiple(hid->ptr, matcher);
1094 IOHIDManagerRegisterDeviceMatchingCallback(hid->ptr,
1095 iohidmanager_hid_device_add, 0);
1096
1097 CFRelease(matcher);
1098 #endif
1099
1100 return 0;
1101 }
1102
iohidmanager_hid_init(void)1103 static void *iohidmanager_hid_init(void)
1104 {
1105 iohidmanager_hid_t *hid_apple = (iohidmanager_hid_t*)
1106 calloc(1, sizeof(*hid_apple));
1107
1108 if (!hid_apple)
1109 return NULL;
1110
1111 hid_apple->slots = pad_connection_init(MAX_USERS);
1112
1113 if (!hid_apple->slots)
1114 goto error;
1115 if (iohidmanager_hid_manager_init(hid_apple) == -1)
1116 goto error;
1117 if (iohidmanager_hid_manager_set_device_matching(hid_apple) == -1)
1118 goto error;
1119
1120 return hid_apple;
1121
1122 error:
1123 if (hid_apple->slots)
1124 free(hid_apple->slots);
1125 hid_apple->slots = NULL;
1126 if (hid_apple)
1127 free(hid_apple);
1128 return NULL;
1129 }
1130
iohidmanager_hid_free(const void * data)1131 static void iohidmanager_hid_free(const void *data)
1132 {
1133 iohidmanager_hid_t *hid_apple = (iohidmanager_hid_t*)data;
1134
1135 if (!hid_apple || !hid_apple->ptr)
1136 return;
1137
1138 pad_connection_destroy(hid_apple->slots);
1139 iohidmanager_hid_manager_free(hid_apple);
1140
1141 if (hid_apple)
1142 free(hid_apple);
1143 }
1144
iohidmanager_hid_poll(void * data)1145 static void iohidmanager_hid_poll(void *data)
1146 {
1147 (void)data;
1148 }
1149
1150 hid_driver_t iohidmanager_hid = {
1151 iohidmanager_hid_init,
1152 iohidmanager_hid_joypad_query,
1153 iohidmanager_hid_free,
1154 iohidmanager_hid_joypad_button,
1155 iohidmanager_hid_joypad_state,
1156 iohidmanager_hid_joypad_get_buttons,
1157 iohidmanager_hid_joypad_axis,
1158 iohidmanager_hid_poll,
1159 iohidmanager_hid_joypad_rumble,
1160 iohidmanager_hid_joypad_name,
1161 "iohidmanager",
1162 iohidmanager_hid_device_send_control,
1163 };
1164