volviewer

Volumetric Data Toy Viewer
git clone anongit@rnpnr.xyz:volviewer.git
Log | Files | Refs | Feed | LICENSE

cocoa_joystick.m (15740B)


      1 //========================================================================
      2 // GLFW 3.4 Cocoa - www.glfw.org
      3 //------------------------------------------------------------------------
      4 // Copyright (c) 2009-2019 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 #if defined(_GLFW_COCOA)
     31 
     32 #include <unistd.h>
     33 #include <ctype.h>
     34 #include <string.h>
     35 
     36 #include <mach/mach.h>
     37 #include <mach/mach_error.h>
     38 
     39 #include <CoreFoundation/CoreFoundation.h>
     40 #include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
     41 
     42 
     43 // Joystick element information
     44 //
     45 typedef struct _GLFWjoyelementNS
     46 {
     47     IOHIDElementRef native;
     48     uint32_t        usage;
     49     int             index;
     50     long            minimum;
     51     long            maximum;
     52 
     53 } _GLFWjoyelementNS;
     54 
     55 
     56 // Returns the value of the specified element of the specified joystick
     57 //
     58 static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element)
     59 {
     60     IOHIDValueRef valueRef;
     61     long value = 0;
     62 
     63     if (js->ns.device)
     64     {
     65         if (IOHIDDeviceGetValue(js->ns.device,
     66                                 element->native,
     67                                 &valueRef) == kIOReturnSuccess)
     68         {
     69             value = IOHIDValueGetIntegerValue(valueRef);
     70         }
     71     }
     72 
     73     return value;
     74 }
     75 
     76 // Comparison function for matching the SDL element order
     77 //
     78 static CFComparisonResult compareElements(const void* fp,
     79                                           const void* sp,
     80                                           void* user)
     81 {
     82     const _GLFWjoyelementNS* fe = fp;
     83     const _GLFWjoyelementNS* se = sp;
     84     if (fe->usage < se->usage)
     85         return kCFCompareLessThan;
     86     if (fe->usage > se->usage)
     87         return kCFCompareGreaterThan;
     88     if (fe->index < se->index)
     89         return kCFCompareLessThan;
     90     if (fe->index > se->index)
     91         return kCFCompareGreaterThan;
     92     return kCFCompareEqualTo;
     93 }
     94 
     95 // Removes the specified joystick
     96 //
     97 static void closeJoystick(_GLFWjoystick* js)
     98 {
     99     _glfwInputJoystick(js, GLFW_DISCONNECTED);
    100 
    101     for (int i = 0;  i < CFArrayGetCount(js->ns.axes);  i++)
    102         _glfw_free((void*) CFArrayGetValueAtIndex(js->ns.axes, i));
    103     CFRelease(js->ns.axes);
    104 
    105     for (int i = 0;  i < CFArrayGetCount(js->ns.buttons);  i++)
    106         _glfw_free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i));
    107     CFRelease(js->ns.buttons);
    108 
    109     for (int i = 0;  i < CFArrayGetCount(js->ns.hats);  i++)
    110         _glfw_free((void*) CFArrayGetValueAtIndex(js->ns.hats, i));
    111     CFRelease(js->ns.hats);
    112 
    113     _glfwFreeJoystick(js);
    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     CFTypeRef property;
    127     uint32_t vendor = 0, product = 0, version = 0;
    128     _GLFWjoystick* js;
    129     CFMutableArrayRef axes, buttons, hats;
    130 
    131     for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
    132     {
    133         if (_glfw.joysticks[jid].ns.device == device)
    134             return;
    135     }
    136 
    137     CFArrayRef elements =
    138         IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
    139 
    140     // It is reportedly possible for this to fail on macOS 13 Ventura
    141     // if the application does not have input monitoring permissions
    142     if (!elements)
    143         return;
    144 
    145     axes    = CFArrayCreateMutable(NULL, 0, NULL);
    146     buttons = CFArrayCreateMutable(NULL, 0, NULL);
    147     hats    = CFArrayCreateMutable(NULL, 0, NULL);
    148 
    149     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
    150     if (property)
    151     {
    152         CFStringGetCString(property,
    153                            name,
    154                            sizeof(name),
    155                            kCFStringEncodingUTF8);
    156     }
    157     else
    158         strncpy(name, "Unknown", sizeof(name));
    159 
    160     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
    161     if (property)
    162         CFNumberGetValue(property, kCFNumberSInt32Type, &vendor);
    163 
    164     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
    165     if (property)
    166         CFNumberGetValue(property, kCFNumberSInt32Type, &product);
    167 
    168     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVersionNumberKey));
    169     if (property)
    170         CFNumberGetValue(property, kCFNumberSInt32Type, &version);
    171 
    172     // Generate a joystick GUID that matches the SDL 2.0.5+ one
    173     if (vendor && product)
    174     {
    175         sprintf(guid, "03000000%02x%02x0000%02x%02x0000%02x%02x0000",
    176                 (uint8_t) vendor, (uint8_t) (vendor >> 8),
    177                 (uint8_t) product, (uint8_t) (product >> 8),
    178                 (uint8_t) version, (uint8_t) (version >> 8));
    179     }
    180     else
    181     {
    182         sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
    183                 name[0], name[1], name[2], name[3],
    184                 name[4], name[5], name[6], name[7],
    185                 name[8], name[9], name[10]);
    186     }
    187 
    188     for (CFIndex i = 0;  i < CFArrayGetCount(elements);  i++)
    189     {
    190         IOHIDElementRef native = (IOHIDElementRef)
    191             CFArrayGetValueAtIndex(elements, i);
    192         if (CFGetTypeID(native) != IOHIDElementGetTypeID())
    193             continue;
    194 
    195         const IOHIDElementType type = IOHIDElementGetType(native);
    196         if ((type != kIOHIDElementTypeInput_Axis) &&
    197             (type != kIOHIDElementTypeInput_Button) &&
    198             (type != kIOHIDElementTypeInput_Misc))
    199         {
    200             continue;
    201         }
    202 
    203         CFMutableArrayRef target = NULL;
    204 
    205         const uint32_t usage = IOHIDElementGetUsage(native);
    206         const uint32_t page = IOHIDElementGetUsagePage(native);
    207         if (page == kHIDPage_GenericDesktop)
    208         {
    209             switch (usage)
    210             {
    211                 case kHIDUsage_GD_X:
    212                 case kHIDUsage_GD_Y:
    213                 case kHIDUsage_GD_Z:
    214                 case kHIDUsage_GD_Rx:
    215                 case kHIDUsage_GD_Ry:
    216                 case kHIDUsage_GD_Rz:
    217                 case kHIDUsage_GD_Slider:
    218                 case kHIDUsage_GD_Dial:
    219                 case kHIDUsage_GD_Wheel:
    220                     target = axes;
    221                     break;
    222                 case kHIDUsage_GD_Hatswitch:
    223                     target = hats;
    224                     break;
    225                 case kHIDUsage_GD_DPadUp:
    226                 case kHIDUsage_GD_DPadRight:
    227                 case kHIDUsage_GD_DPadDown:
    228                 case kHIDUsage_GD_DPadLeft:
    229                 case kHIDUsage_GD_SystemMainMenu:
    230                 case kHIDUsage_GD_Select:
    231                 case kHIDUsage_GD_Start:
    232                     target = buttons;
    233                     break;
    234             }
    235         }
    236         else if (page == kHIDPage_Simulation)
    237         {
    238             switch (usage)
    239             {
    240                 case kHIDUsage_Sim_Accelerator:
    241                 case kHIDUsage_Sim_Brake:
    242                 case kHIDUsage_Sim_Throttle:
    243                 case kHIDUsage_Sim_Rudder:
    244                 case kHIDUsage_Sim_Steering:
    245                     target = axes;
    246                     break;
    247             }
    248         }
    249         else if (page == kHIDPage_Button || page == kHIDPage_Consumer)
    250             target = buttons;
    251 
    252         if (target)
    253         {
    254             _GLFWjoyelementNS* element = _glfw_calloc(1, sizeof(_GLFWjoyelementNS));
    255             element->native  = native;
    256             element->usage   = usage;
    257             element->index   = (int) CFArrayGetCount(target);
    258             element->minimum = IOHIDElementGetLogicalMin(native);
    259             element->maximum = IOHIDElementGetLogicalMax(native);
    260             CFArrayAppendValue(target, element);
    261         }
    262     }
    263 
    264     CFRelease(elements);
    265 
    266     CFArraySortValues(axes, CFRangeMake(0, CFArrayGetCount(axes)),
    267                       compareElements, NULL);
    268     CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)),
    269                       compareElements, NULL);
    270     CFArraySortValues(hats, CFRangeMake(0, CFArrayGetCount(hats)),
    271                       compareElements, NULL);
    272 
    273     js = _glfwAllocJoystick(name, guid,
    274                             (int) CFArrayGetCount(axes),
    275                             (int) CFArrayGetCount(buttons),
    276                             (int) CFArrayGetCount(hats));
    277 
    278     js->ns.device  = device;
    279     js->ns.axes    = axes;
    280     js->ns.buttons = buttons;
    281     js->ns.hats    = hats;
    282 
    283     _glfwInputJoystick(js, GLFW_CONNECTED);
    284 }
    285 
    286 // Callback for user-initiated joystick removal
    287 //
    288 static void removeCallback(void* context,
    289                            IOReturn result,
    290                            void* sender,
    291                            IOHIDDeviceRef device)
    292 {
    293     for (int jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
    294     {
    295         if (_glfw.joysticks[jid].connected && _glfw.joysticks[jid].ns.device == device)
    296         {
    297             closeJoystick(&_glfw.joysticks[jid]);
    298             break;
    299         }
    300     }
    301 }
    302 
    303 
    304 //////////////////////////////////////////////////////////////////////////
    305 //////                       GLFW platform API                      //////
    306 //////////////////////////////////////////////////////////////////////////
    307 
    308 GLFWbool _glfwInitJoysticksCocoa(void)
    309 {
    310     CFMutableArrayRef matching;
    311     const long usages[] =
    312     {
    313         kHIDUsage_GD_Joystick,
    314         kHIDUsage_GD_GamePad,
    315         kHIDUsage_GD_MultiAxisController
    316     };
    317 
    318     _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault,
    319                                              kIOHIDOptionsTypeNone);
    320 
    321     matching = CFArrayCreateMutable(kCFAllocatorDefault,
    322                                     0,
    323                                     &kCFTypeArrayCallBacks);
    324     if (!matching)
    325     {
    326         _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array");
    327         return GLFW_FALSE;
    328     }
    329 
    330     for (size_t i = 0;  i < sizeof(usages) / sizeof(long);  i++)
    331     {
    332         const long page = kHIDPage_GenericDesktop;
    333 
    334         CFMutableDictionaryRef dict =
    335             CFDictionaryCreateMutable(kCFAllocatorDefault,
    336                                       0,
    337                                       &kCFTypeDictionaryKeyCallBacks,
    338                                       &kCFTypeDictionaryValueCallBacks);
    339         if (!dict)
    340             continue;
    341 
    342         CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault,
    343                                              kCFNumberLongType,
    344                                              &page);
    345         CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault,
    346                                               kCFNumberLongType,
    347                                               &usages[i]);
    348         if (pageRef && usageRef)
    349         {
    350             CFDictionarySetValue(dict,
    351                                  CFSTR(kIOHIDDeviceUsagePageKey),
    352                                  pageRef);
    353             CFDictionarySetValue(dict,
    354                                  CFSTR(kIOHIDDeviceUsageKey),
    355                                  usageRef);
    356             CFArrayAppendValue(matching, dict);
    357         }
    358 
    359         if (pageRef)
    360             CFRelease(pageRef);
    361         if (usageRef)
    362             CFRelease(usageRef);
    363 
    364         CFRelease(dict);
    365     }
    366 
    367     IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, matching);
    368     CFRelease(matching);
    369 
    370     IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager,
    371                                                &matchCallback, NULL);
    372     IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager,
    373                                               &removeCallback, NULL);
    374     IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager,
    375                                     CFRunLoopGetMain(),
    376                                     kCFRunLoopDefaultMode);
    377     IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone);
    378 
    379     // Execute the run loop once in order to register any initially-attached
    380     // joysticks
    381     CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
    382     return GLFW_TRUE;
    383 }
    384 
    385 void _glfwTerminateJoysticksCocoa(void)
    386 {
    387     for (int jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
    388     {
    389         if (_glfw.joysticks[jid].connected)
    390             closeJoystick(&_glfw.joysticks[jid]);
    391     }
    392 
    393     if (_glfw.ns.hidManager)
    394     {
    395         CFRelease(_glfw.ns.hidManager);
    396         _glfw.ns.hidManager = NULL;
    397     }
    398 }
    399 
    400 
    401 GLFWbool _glfwPollJoystickCocoa(_GLFWjoystick* js, int mode)
    402 {
    403     if (mode & _GLFW_POLL_AXES)
    404     {
    405         for (CFIndex i = 0;  i < CFArrayGetCount(js->ns.axes);  i++)
    406         {
    407             _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*)
    408                 CFArrayGetValueAtIndex(js->ns.axes, i);
    409 
    410             const long raw = getElementValue(js, axis);
    411             // Perform auto calibration
    412             if (raw < axis->minimum)
    413                 axis->minimum = raw;
    414             if (raw > axis->maximum)
    415                 axis->maximum = raw;
    416 
    417             const long size = axis->maximum - axis->minimum;
    418             if (size == 0)
    419                 _glfwInputJoystickAxis(js, (int) i, 0.f);
    420             else
    421             {
    422                 const float value = (2.f * (raw - axis->minimum) / size) - 1.f;
    423                 _glfwInputJoystickAxis(js, (int) i, value);
    424             }
    425         }
    426     }
    427 
    428     if (mode & _GLFW_POLL_BUTTONS)
    429     {
    430         for (CFIndex i = 0;  i < CFArrayGetCount(js->ns.buttons);  i++)
    431         {
    432             _GLFWjoyelementNS* button = (_GLFWjoyelementNS*)
    433                 CFArrayGetValueAtIndex(js->ns.buttons, i);
    434             const char value = getElementValue(js, button) - button->minimum;
    435             const int state = (value > 0) ? GLFW_PRESS : GLFW_RELEASE;
    436             _glfwInputJoystickButton(js, (int) i, state);
    437         }
    438 
    439         for (CFIndex i = 0;  i < CFArrayGetCount(js->ns.hats);  i++)
    440         {
    441             const int states[9] =
    442             {
    443                 GLFW_HAT_UP,
    444                 GLFW_HAT_RIGHT_UP,
    445                 GLFW_HAT_RIGHT,
    446                 GLFW_HAT_RIGHT_DOWN,
    447                 GLFW_HAT_DOWN,
    448                 GLFW_HAT_LEFT_DOWN,
    449                 GLFW_HAT_LEFT,
    450                 GLFW_HAT_LEFT_UP,
    451                 GLFW_HAT_CENTERED
    452             };
    453 
    454             _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*)
    455                 CFArrayGetValueAtIndex(js->ns.hats, i);
    456             long state = getElementValue(js, hat) - hat->minimum;
    457             if (state < 0 || state > 8)
    458                 state = 8;
    459 
    460             _glfwInputJoystickHat(js, (int) i, states[state]);
    461         }
    462     }
    463 
    464     return js->connected;
    465 }
    466 
    467 const char* _glfwGetMappingNameCocoa(void)
    468 {
    469     return "Mac OS X";
    470 }
    471 
    472 void _glfwUpdateGamepadGUIDCocoa(char* guid)
    473 {
    474     if ((strncmp(guid + 4, "000000000000", 12) == 0) &&
    475         (strncmp(guid + 20, "000000000000", 12) == 0))
    476     {
    477         char original[33];
    478         strncpy(original, guid, sizeof(original) - 1);
    479         sprintf(guid, "03000000%.4s0000%.4s000000000000",
    480                 original, original + 16);
    481     }
    482 }
    483 
    484 #endif // _GLFW_COCOA
    485