]> git.sesse.net Git - pistorm/blob - raylib_pi4_test/external/glfw/src/cocoa_joystick.m
Update raylib files and Makefile for Pi 4 testing
[pistorm] / raylib_pi4_test / external / glfw / src / cocoa_joystick.m
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 // It is fine to use C99 in this file because it will not be built with VS
28 //========================================================================
29
30 #include "internal.h"
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     int i;
100
101     if (!js->present)
102         return;
103
104     for (i = 0;  i < CFArrayGetCount(js->ns.axes);  i++)
105         free((void*) CFArrayGetValueAtIndex(js->ns.axes, i));
106     CFRelease(js->ns.axes);
107
108     for (i = 0;  i < CFArrayGetCount(js->ns.buttons);  i++)
109         free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i));
110     CFRelease(js->ns.buttons);
111
112     for (i = 0;  i < CFArrayGetCount(js->ns.hats);  i++)
113         free((void*) CFArrayGetValueAtIndex(js->ns.hats, i));
114     CFRelease(js->ns.hats);
115
116     _glfwFreeJoystick(js);
117     _glfwInputJoystick(js, GLFW_DISCONNECTED);
118 }
119
120 // Callback for user-initiated joystick addition
121 //
122 static void matchCallback(void* context,
123                           IOReturn result,
124                           void* sender,
125                           IOHIDDeviceRef device)
126 {
127     int jid;
128     char name[256];
129     char guid[33];
130     CFIndex i;
131     CFTypeRef property;
132     uint32_t vendor = 0, product = 0, version = 0;
133     _GLFWjoystick* js;
134     CFMutableArrayRef axes, buttons, hats;
135
136     for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
137     {
138         if (_glfw.joysticks[jid].ns.device == device)
139             return;
140     }
141
142     axes    = CFArrayCreateMutable(NULL, 0, NULL);
143     buttons = CFArrayCreateMutable(NULL, 0, NULL);
144     hats    = CFArrayCreateMutable(NULL, 0, NULL);
145
146     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
147     if (property)
148     {
149         CFStringGetCString(property,
150                            name,
151                            sizeof(name),
152                            kCFStringEncodingUTF8);
153     }
154     else
155         strncpy(name, "Unknown", sizeof(name));
156
157     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
158     if (property)
159         CFNumberGetValue(property, kCFNumberSInt32Type, &vendor);
160
161     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
162     if (property)
163         CFNumberGetValue(property, kCFNumberSInt32Type, &product);
164
165     property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVersionNumberKey));
166     if (property)
167         CFNumberGetValue(property, kCFNumberSInt32Type, &version);
168
169     // Generate a joystick GUID that matches the SDL 2.0.5+ one
170     if (vendor && product)
171     {
172         sprintf(guid, "03000000%02x%02x0000%02x%02x0000%02x%02x0000",
173                 (uint8_t) vendor, (uint8_t) (vendor >> 8),
174                 (uint8_t) product, (uint8_t) (product >> 8),
175                 (uint8_t) version, (uint8_t) (version >> 8));
176     }
177     else
178     {
179         sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
180                 name[0], name[1], name[2], name[3],
181                 name[4], name[5], name[6], name[7],
182                 name[8], name[9], name[10]);
183     }
184
185     CFArrayRef elements =
186         IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
187
188     for (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 = 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     int jid;
294
295     for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
296     {
297         if (_glfw.joysticks[jid].ns.device == device)
298         {
299             closeJoystick(_glfw.joysticks + jid);
300             break;
301         }
302     }
303 }
304
305
306 //////////////////////////////////////////////////////////////////////////
307 //////                       GLFW platform API                      //////
308 //////////////////////////////////////////////////////////////////////////
309
310 GLFWbool _glfwPlatformInitJoysticks(void)
311 {
312     CFMutableArrayRef matching;
313     const long usages[] =
314     {
315         kHIDUsage_GD_Joystick,
316         kHIDUsage_GD_GamePad,
317         kHIDUsage_GD_MultiAxisController
318     };
319
320     _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault,
321                                              kIOHIDOptionsTypeNone);
322
323     matching = CFArrayCreateMutable(kCFAllocatorDefault,
324                                     0,
325                                     &kCFTypeArrayCallBacks);
326     if (!matching)
327     {
328         _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array");
329         return GLFW_FALSE;
330     }
331
332     for (size_t i = 0;  i < sizeof(usages) / sizeof(long);  i++)
333     {
334         const long page = kHIDPage_GenericDesktop;
335
336         CFMutableDictionaryRef dict =
337             CFDictionaryCreateMutable(kCFAllocatorDefault,
338                                       0,
339                                       &kCFTypeDictionaryKeyCallBacks,
340                                       &kCFTypeDictionaryValueCallBacks);
341         if (!dict)
342             continue;
343
344         CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault,
345                                              kCFNumberLongType,
346                                              &page);
347         CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault,
348                                               kCFNumberLongType,
349                                               &usages[i]);
350         if (pageRef && usageRef)
351         {
352             CFDictionarySetValue(dict,
353                                  CFSTR(kIOHIDDeviceUsagePageKey),
354                                  pageRef);
355             CFDictionarySetValue(dict,
356                                  CFSTR(kIOHIDDeviceUsageKey),
357                                  usageRef);
358             CFArrayAppendValue(matching, dict);
359         }
360
361         if (pageRef)
362             CFRelease(pageRef);
363         if (usageRef)
364             CFRelease(usageRef);
365
366         CFRelease(dict);
367     }
368
369     IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, matching);
370     CFRelease(matching);
371
372     IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager,
373                                                &matchCallback, NULL);
374     IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager,
375                                               &removeCallback, NULL);
376     IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager,
377                                     CFRunLoopGetMain(),
378                                     kCFRunLoopDefaultMode);
379     IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone);
380
381     // Execute the run loop once in order to register any initially-attached
382     // joysticks
383     CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
384     return GLFW_TRUE;
385 }
386
387 void _glfwPlatformTerminateJoysticks(void)
388 {
389     int jid;
390
391     for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
392         closeJoystick(_glfw.joysticks + jid);
393
394     if (_glfw.ns.hidManager)
395     {
396         CFRelease(_glfw.ns.hidManager);
397         _glfw.ns.hidManager = NULL;
398     }
399 }
400
401
402 int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
403 {
404     if (mode & _GLFW_POLL_AXES)
405     {
406         CFIndex i;
407
408         for (i = 0;  i < CFArrayGetCount(js->ns.axes);  i++)
409         {
410             _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*)
411                 CFArrayGetValueAtIndex(js->ns.axes, i);
412
413             const long raw = getElementValue(js, axis);
414             // Perform auto calibration
415             if (raw < axis->minimum)
416                 axis->minimum = raw;
417             if (raw > axis->maximum)
418                 axis->maximum = raw;
419
420             const long size = axis->maximum - axis->minimum;
421             if (size == 0)
422                 _glfwInputJoystickAxis(js, (int) i, 0.f);
423             else
424             {
425                 const float value = (2.f * (raw - axis->minimum) / size) - 1.f;
426                 _glfwInputJoystickAxis(js, (int) i, value);
427             }
428         }
429     }
430
431     if (mode & _GLFW_POLL_BUTTONS)
432     {
433         CFIndex i;
434
435         for (i = 0;  i < CFArrayGetCount(js->ns.buttons);  i++)
436         {
437             _GLFWjoyelementNS* button = (_GLFWjoyelementNS*)
438                 CFArrayGetValueAtIndex(js->ns.buttons, i);
439             const char value = getElementValue(js, button) - button->minimum;
440             const int state = (value > 0) ? GLFW_PRESS : GLFW_RELEASE;
441             _glfwInputJoystickButton(js, (int) i, state);
442         }
443
444         for (i = 0;  i < CFArrayGetCount(js->ns.hats);  i++)
445         {
446             const int states[9] =
447             {
448                 GLFW_HAT_UP,
449                 GLFW_HAT_RIGHT_UP,
450                 GLFW_HAT_RIGHT,
451                 GLFW_HAT_RIGHT_DOWN,
452                 GLFW_HAT_DOWN,
453                 GLFW_HAT_LEFT_DOWN,
454                 GLFW_HAT_LEFT,
455                 GLFW_HAT_LEFT_UP,
456                 GLFW_HAT_CENTERED
457             };
458
459             _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*)
460                 CFArrayGetValueAtIndex(js->ns.hats, i);
461             long state = getElementValue(js, hat) - hat->minimum;
462             if (state < 0 || state > 8)
463                 state = 8;
464
465             _glfwInputJoystickHat(js, (int) i, states[state]);
466         }
467     }
468
469     return js->present;
470 }
471
472 void _glfwPlatformUpdateGamepadGUID(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