medfall

A super great game engine
Log | Files | Refs

cocoa_joystick.m (14577B)


      1 //========================================================================
      2 // GLFW 3.3 Cocoa - www.glfw.org
      3 //------------------------------------------------------------------------
      4 // Copyright (c) 2009-2016 Camilla Löwy <elmindreda@glfw.org>
      5 // Copyright (c) 2012 Torsten Walluhn <tw@mad-cad.net>
      6 //
      7 // This software is provided 'as-is', without any express or implied
      8 // warranty. In no event will the authors be held liable for any damages
      9 // arising from the use of this software.
     10 //
     11 // Permission is granted to anyone to use this software for any purpose,
     12 // including commercial applications, and to alter it and redistribute it
     13 // freely, subject to the following restrictions:
     14 //
     15 // 1. The origin of this software must not be misrepresented; you must not
     16 //    claim that you wrote the original software. If you use this software
     17 //    in a product, an acknowledgment in the product documentation would
     18 //    be appreciated but is not required.
     19 //
     20 // 2. Altered source versions must be plainly marked as such, and must not
     21 //    be misrepresented as being the original software.
     22 //
     23 // 3. This notice may not be removed or altered from any source
     24 //    distribution.
     25 //
     26 //========================================================================
     27 
     28 #include "internal.h"
     29 
     30 #include <unistd.h>
     31 #include <ctype.h>
     32 #include <string.h>
     33 
     34 #include <mach/mach.h>
     35 #include <mach/mach_error.h>
     36 
     37 #include <CoreFoundation/CoreFoundation.h>
     38 #include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
     39 
     40 
     41 // Joystick element information
     42 //
     43 typedef struct _GLFWjoyelementNS
     44 {
     45     IOHIDElementRef native;
     46     uint32_t        usage;
     47     int             index;
     48     long            minimum;
     49     long            maximum;
     50 
     51 } _GLFWjoyelementNS;
     52 
     53 
     54 // Returns the value of the specified element of the specified joystick
     55 //
     56 static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element)
     57 {
     58     IOHIDValueRef valueRef;
     59     long value = 0;
     60 
     61     if (js->ns.device)
     62     {
     63         if (IOHIDDeviceGetValue(js->ns.device,
     64                                 element->native,
     65                                 &valueRef) == kIOReturnSuccess)
     66         {
     67             value = IOHIDValueGetIntegerValue(valueRef);
     68         }
     69     }
     70 
     71     return value;
     72 }
     73 
     74 // Comparison function for matching the SDL element order
     75 //
     76 static CFComparisonResult compareElements(const void* fp, const void* sp, void* user)
     77 {
     78     const _GLFWjoyelementNS* fe = fp;
     79     const _GLFWjoyelementNS* se = sp;
     80     if (fe->usage < se->usage)
     81         return kCFCompareLessThan;
     82     if (fe->usage > se->usage)
     83         return kCFCompareGreaterThan;
     84     if (fe->index < se->index)
     85         return kCFCompareLessThan;
     86     if (fe->index > se->index)
     87         return kCFCompareGreaterThan;
     88     return kCFCompareEqualTo;
     89 }
     90 
     91 // Removes the specified joystick
     92 //
     93 static void closeJoystick(_GLFWjoystick* js)
     94 {
     95     int i;
     96 
     97     if (!js->present)
     98         return;
     99 
    100     for (i = 0;  i < CFArrayGetCount(js->ns.axes);  i++)
    101         free((void*) CFArrayGetValueAtIndex(js->ns.axes, i));
    102     CFRelease(js->ns.axes);
    103 
    104     for (i = 0;  i < CFArrayGetCount(js->ns.buttons);  i++)
    105         free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i));
    106     CFRelease(js->ns.buttons);
    107 
    108     for (i = 0;  i < CFArrayGetCount(js->ns.hats);  i++)
    109         free((void*) CFArrayGetValueAtIndex(js->ns.hats, i));
    110     CFRelease(js->ns.hats);
    111 
    112     _glfwFreeJoystick(js);
    113     _glfwInputJoystick(js, GLFW_DISCONNECTED);
    114 }
    115 
    116 // Callback for user-initiated joystick addition
    117 //
    118 static void matchCallback(void* context,
    119                           IOReturn result,
    120                           void* sender,
    121                           IOHIDDeviceRef device)
    122 {
    123     int jid;
    124     char name[256];
    125     char guid[33];
    126     CFIndex i;
    127     CFTypeRef property;
    128     uint32_t vendor = 0, product = 0, version = 0;
    129     _GLFWjoystick* js;
    130     CFMutableArrayRef axes, buttons, hats;
    131 
    132     for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
    133     {
    134         if (_glfw.joysticks[jid].ns.device == device)
    135             return;
    136     }
    137 
    138     axes    = CFArrayCreateMutable(NULL, 0, NULL);
    139     buttons = CFArrayCreateMutable(NULL, 0, NULL);
    140     hats    = CFArrayCreateMutable(NULL, 0, NULL);
    141 
    142     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
    143     if (property)
    144     {
    145         CFStringGetCString(property,
    146                            name,
    147                            sizeof(name),
    148                            kCFStringEncodingUTF8);
    149     }
    150     else
    151         strncpy(name, "Unknown", sizeof(name));
    152 
    153     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
    154     if (property)
    155         CFNumberGetValue(property, kCFNumberSInt32Type, &vendor);
    156 
    157     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
    158     if (property)
    159         CFNumberGetValue(property, kCFNumberSInt32Type, &product);
    160 
    161     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVersionNumberKey));
    162     if (property)
    163         CFNumberGetValue(property, kCFNumberSInt32Type, &version);
    164 
    165     // Generate a joystick GUID that matches the SDL 2.0.5+ one
    166     if (vendor && product)
    167     {
    168         sprintf(guid, "03000000%02x%02x0000%02x%02x0000%02x%02x0000",
    169                 (uint8_t) vendor, (uint8_t) (vendor >> 8),
    170                 (uint8_t) product, (uint8_t) (product >> 8),
    171                 (uint8_t) version, (uint8_t) (version >> 8));
    172     }
    173     else
    174     {
    175         sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
    176                 name[0], name[1], name[2], name[3],
    177                 name[4], name[5], name[6], name[7],
    178                 name[8], name[9], name[10]);
    179     }
    180 
    181     CFArrayRef elements =
    182         IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
    183 
    184     for (i = 0;  i < CFArrayGetCount(elements);  i++)
    185     {
    186         IOHIDElementRef native = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i);
    187         if (CFGetTypeID(native) != IOHIDElementGetTypeID())
    188             continue;
    189 
    190         const IOHIDElementType type = IOHIDElementGetType(native);
    191         if ((type != kIOHIDElementTypeInput_Axis) &&
    192             (type != kIOHIDElementTypeInput_Button) &&
    193             (type != kIOHIDElementTypeInput_Misc))
    194         {
    195             continue;
    196         }
    197 
    198         CFMutableArrayRef target = NULL;
    199 
    200         const uint32_t usage = IOHIDElementGetUsage(native);
    201         const uint32_t page = IOHIDElementGetUsagePage(native);
    202         if (page == kHIDPage_GenericDesktop)
    203         {
    204             switch (usage)
    205             {
    206                 case kHIDUsage_GD_X:
    207                 case kHIDUsage_GD_Y:
    208                 case kHIDUsage_GD_Z:
    209                 case kHIDUsage_GD_Rx:
    210                 case kHIDUsage_GD_Ry:
    211                 case kHIDUsage_GD_Rz:
    212                 case kHIDUsage_GD_Slider:
    213                 case kHIDUsage_GD_Dial:
    214                 case kHIDUsage_GD_Wheel:
    215                     target = axes;
    216                     break;
    217                 case kHIDUsage_GD_Hatswitch:
    218                     target = hats;
    219                     break;
    220             }
    221         }
    222         else if (page == kHIDPage_Button)
    223             target = buttons;
    224 
    225         if (target)
    226         {
    227             _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS));
    228             element->native  = native;
    229             element->usage   = usage;
    230             element->index   = (int) CFArrayGetCount(target);
    231             element->minimum = IOHIDElementGetLogicalMin(native);
    232             element->maximum = IOHIDElementGetLogicalMax(native);
    233             CFArrayAppendValue(target, element);
    234         }
    235     }
    236 
    237     CFRelease(elements);
    238 
    239     CFArraySortValues(axes, CFRangeMake(0, CFArrayGetCount(axes)),
    240                       compareElements, NULL);
    241     CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)),
    242                       compareElements, NULL);
    243     CFArraySortValues(hats, CFRangeMake(0, CFArrayGetCount(hats)),
    244                       compareElements, NULL);
    245 
    246     js = _glfwAllocJoystick(name, guid,
    247                             CFArrayGetCount(axes),
    248                             CFArrayGetCount(buttons),
    249                             CFArrayGetCount(hats));
    250 
    251     js->ns.device  = device;
    252     js->ns.axes    = axes;
    253     js->ns.buttons = buttons;
    254     js->ns.hats    = hats;
    255 
    256     _glfwInputJoystick(js, GLFW_CONNECTED);
    257 }
    258 
    259 // Callback for user-initiated joystick removal
    260 //
    261 static void removeCallback(void* context,
    262                            IOReturn result,
    263                            void* sender,
    264                            IOHIDDeviceRef device)
    265 {
    266     int jid;
    267 
    268     for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
    269     {
    270         if (_glfw.joysticks[jid].ns.device == device)
    271         {
    272             closeJoystick(_glfw.joysticks + jid);
    273             break;
    274         }
    275     }
    276 }
    277 
    278 
    279 //////////////////////////////////////////////////////////////////////////
    280 //////                       GLFW internal API                      //////
    281 //////////////////////////////////////////////////////////////////////////
    282 
    283 // Initialize joystick interface
    284 //
    285 void _glfwInitJoysticksNS(void)
    286 {
    287     CFMutableArrayRef matching;
    288     const long usages[] =
    289     {
    290         kHIDUsage_GD_Joystick,
    291         kHIDUsage_GD_GamePad,
    292         kHIDUsage_GD_MultiAxisController
    293     };
    294 
    295     _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault,
    296                                              kIOHIDOptionsTypeNone);
    297 
    298     matching = CFArrayCreateMutable(kCFAllocatorDefault,
    299                                     0,
    300                                     &kCFTypeArrayCallBacks);
    301     if (!matching)
    302     {
    303         _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array");
    304         return;
    305     }
    306 
    307     for (int i = 0;  i < sizeof(usages) / sizeof(long);  i++)
    308     {
    309         const long page = kHIDPage_GenericDesktop;
    310 
    311         CFMutableDictionaryRef dict =
    312             CFDictionaryCreateMutable(kCFAllocatorDefault,
    313                                       0,
    314                                       &kCFTypeDictionaryKeyCallBacks,
    315                                       &kCFTypeDictionaryValueCallBacks);
    316         if (!dict)
    317             continue;
    318 
    319         CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault,
    320                                              kCFNumberLongType,
    321                                              &page);
    322         CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault,
    323                                               kCFNumberLongType,
    324                                               &usages[i]);
    325         if (pageRef && usageRef)
    326         {
    327             CFDictionarySetValue(dict,
    328                                  CFSTR(kIOHIDDeviceUsagePageKey),
    329                                  pageRef);
    330             CFDictionarySetValue(dict,
    331                                  CFSTR(kIOHIDDeviceUsageKey),
    332                                  usageRef);
    333             CFArrayAppendValue(matching, dict);
    334         }
    335 
    336         if (pageRef)
    337             CFRelease(pageRef);
    338         if (usageRef)
    339             CFRelease(usageRef);
    340 
    341         CFRelease(dict);
    342     }
    343 
    344     IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, matching);
    345     CFRelease(matching);
    346 
    347     IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager,
    348                                                &matchCallback, NULL);
    349     IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager,
    350                                               &removeCallback, NULL);
    351     IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager,
    352                                     CFRunLoopGetMain(),
    353                                     kCFRunLoopDefaultMode);
    354     IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone);
    355 
    356     // Execute the run loop once in order to register any initially-attached
    357     // joysticks
    358     CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
    359 }
    360 
    361 // Close all opened joystick handles
    362 //
    363 void _glfwTerminateJoysticksNS(void)
    364 {
    365     int jid;
    366 
    367     for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
    368         closeJoystick(_glfw.joysticks + jid);
    369 
    370     CFRelease(_glfw.ns.hidManager);
    371     _glfw.ns.hidManager = NULL;
    372 }
    373 
    374 
    375 //////////////////////////////////////////////////////////////////////////
    376 //////                       GLFW platform API                      //////
    377 //////////////////////////////////////////////////////////////////////////
    378 
    379 int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
    380 {
    381     if (mode & _GLFW_POLL_AXES)
    382     {
    383         CFIndex i;
    384 
    385         for (i = 0;  i < CFArrayGetCount(js->ns.axes);  i++)
    386         {
    387             _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*)
    388                 CFArrayGetValueAtIndex(js->ns.axes, i);
    389 
    390             const long raw = getElementValue(js, axis);
    391             // Perform auto calibration
    392             if (raw < axis->minimum)
    393                 axis->minimum = raw;
    394             if (raw > axis->maximum)
    395                 axis->maximum = raw;
    396 
    397             const long delta = axis->maximum - axis->minimum;
    398             if (delta == 0)
    399                 _glfwInputJoystickAxis(js, i, 0.f);
    400             else
    401             {
    402                 const float value = (2.f * (raw - axis->minimum) / delta) - 1.f;
    403                 _glfwInputJoystickAxis(js, i, value);
    404             }
    405         }
    406     }
    407 
    408     if (mode & _GLFW_POLL_BUTTONS)
    409     {
    410         CFIndex i;
    411 
    412         for (i = 0;  i < CFArrayGetCount(js->ns.buttons);  i++)
    413         {
    414             _GLFWjoyelementNS* button = (_GLFWjoyelementNS*)
    415                 CFArrayGetValueAtIndex(js->ns.buttons, i);
    416             const char value = getElementValue(js, button) - button->minimum;
    417             _glfwInputJoystickButton(js, i, value);
    418         }
    419 
    420         for (i = 0;  i < CFArrayGetCount(js->ns.hats);  i++)
    421         {
    422             const int states[9] =
    423             {
    424                 GLFW_HAT_UP,
    425                 GLFW_HAT_RIGHT_UP,
    426                 GLFW_HAT_RIGHT,
    427                 GLFW_HAT_RIGHT_DOWN,
    428                 GLFW_HAT_DOWN,
    429                 GLFW_HAT_LEFT_DOWN,
    430                 GLFW_HAT_LEFT,
    431                 GLFW_HAT_LEFT_UP,
    432                 GLFW_HAT_CENTERED
    433             };
    434 
    435             _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*)
    436                 CFArrayGetValueAtIndex(js->ns.hats, i);
    437             long state = getElementValue(js, hat) - hat->minimum;
    438             if (state < 0 || state > 8)
    439                 state = 8;
    440 
    441             _glfwInputJoystickHat(js, i, states[state]);
    442         }
    443     }
    444 
    445     return js->present;
    446 }
    447 
    448 void _glfwPlatformUpdateGamepadGUID(char* guid)
    449 {
    450     if ((strncmp(guid + 4, "000000000000", 12) == 0) &&
    451         (strncmp(guid + 20, "000000000000", 12) == 0))
    452     {
    453         char original[33];
    454         strcpy(original, guid);
    455         sprintf(guid, "03000000%.4s0000%.4s000000000000",
    456                 original, original + 16);
    457     }
    458 }
    459