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