master
ブランチ master
ShaderPlayground / deps / glfw / src / cocoa_joystick.m
15.4 KB、487行 生 Blame 履歴
1//========================================================================
2// GLFW 3.3 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//
45typedef 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//
58static 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//
78static 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//
97static 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//
122static 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//
288static 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 internal API //////
308//////////////////////////////////////////////////////////////////////////
309
310// Initialize joystick interface
311//
312void _glfwInitJoysticksNS(void)
313{
314 CFMutableArrayRef matching;
315 const long usages[] =
316 {
317 kHIDUsage_GD_Joystick,
318 kHIDUsage_GD_GamePad,
319 kHIDUsage_GD_MultiAxisController
320 };
321
322 _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault,
323 kIOHIDOptionsTypeNone);
324
325 matching = CFArrayCreateMutable(kCFAllocatorDefault,
326 0,
327 &kCFTypeArrayCallBacks);
328 if (!matching)
329 {
330 _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array");
331 return;
332 }
333
334 for (size_t i = 0; i < sizeof(usages) / sizeof(long); i++)
335 {
336 const long page = kHIDPage_GenericDesktop;
337
338 CFMutableDictionaryRef dict =
339 CFDictionaryCreateMutable(kCFAllocatorDefault,
340 0,
341 &kCFTypeDictionaryKeyCallBacks,
342 &kCFTypeDictionaryValueCallBacks);
343 if (!dict)
344 continue;
345
346 CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault,
347 kCFNumberLongType,
348 &page);
349 CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault,
350 kCFNumberLongType,
351 &usages[i]);
352 if (pageRef && usageRef)
353 {
354 CFDictionarySetValue(dict,
355 CFSTR(kIOHIDDeviceUsagePageKey),
356 pageRef);
357 CFDictionarySetValue(dict,
358 CFSTR(kIOHIDDeviceUsageKey),
359 usageRef);
360 CFArrayAppendValue(matching, dict);
361 }
362
363 if (pageRef)
364 CFRelease(pageRef);
365 if (usageRef)
366 CFRelease(usageRef);
367
368 CFRelease(dict);
369 }
370
371 IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, matching);
372 CFRelease(matching);
373
374 IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager,
375 &matchCallback, NULL);
376 IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager,
377 &removeCallback, NULL);
378 IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager,
379 CFRunLoopGetMain(),
380 kCFRunLoopDefaultMode);
381 IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone);
382
383 // Execute the run loop once in order to register any initially-attached
384 // joysticks
385 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
386}
387
388// Close all opened joystick handles
389//
390void _glfwTerminateJoysticksNS(void)
391{
392 int jid;
393
394 for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
395 closeJoystick(_glfw.joysticks + jid);
396
397 CFRelease(_glfw.ns.hidManager);
398 _glfw.ns.hidManager = NULL;
399}
400
401
402//////////////////////////////////////////////////////////////////////////
403////// GLFW platform API //////
404//////////////////////////////////////////////////////////////////////////
405
406int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
407{
408 if (mode & _GLFW_POLL_AXES)
409 {
410 CFIndex i;
411
412 for (i = 0; i < CFArrayGetCount(js->ns.axes); i++)
413 {
414 _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*)
415 CFArrayGetValueAtIndex(js->ns.axes, i);
416
417 const long raw = getElementValue(js, axis);
418 // Perform auto calibration
419 if (raw < axis->minimum)
420 axis->minimum = raw;
421 if (raw > axis->maximum)
422 axis->maximum = raw;
423
424 const long size = axis->maximum - axis->minimum;
425 if (size == 0)
426 _glfwInputJoystickAxis(js, (int) i, 0.f);
427 else
428 {
429 const float value = (2.f * (raw - axis->minimum) / size) - 1.f;
430 _glfwInputJoystickAxis(js, (int) i, value);
431 }
432 }
433 }
434
435 if (mode & _GLFW_POLL_BUTTONS)
436 {
437 CFIndex i;
438
439 for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++)
440 {
441 _GLFWjoyelementNS* button = (_GLFWjoyelementNS*)
442 CFArrayGetValueAtIndex(js->ns.buttons, i);
443 const char value = getElementValue(js, button) - button->minimum;
444 const int state = (value > 0) ? GLFW_PRESS : GLFW_RELEASE;
445 _glfwInputJoystickButton(js, (int) i, state);
446 }
447
448 for (i = 0; i < CFArrayGetCount(js->ns.hats); i++)
449 {
450 const int states[9] =
451 {
452 GLFW_HAT_UP,
453 GLFW_HAT_RIGHT_UP,
454 GLFW_HAT_RIGHT,
455 GLFW_HAT_RIGHT_DOWN,
456 GLFW_HAT_DOWN,
457 GLFW_HAT_LEFT_DOWN,
458 GLFW_HAT_LEFT,
459 GLFW_HAT_LEFT_UP,
460 GLFW_HAT_CENTERED
461 };
462
463 _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*)
464 CFArrayGetValueAtIndex(js->ns.hats, i);
465 long state = getElementValue(js, hat) - hat->minimum;
466 if (state < 0 || state > 8)
467 state = 8;
468
469 _glfwInputJoystickHat(js, (int) i, states[state]);
470 }
471 }
472
473 return js->present;
474}
475
476void _glfwPlatformUpdateGamepadGUID(char* guid)
477{
478 if ((strncmp(guid + 4, "000000000000", 12) == 0) &&
479 (strncmp(guid + 20, "000000000000", 12) == 0))
480 {
481 char original[33];
482 strncpy(original, guid, sizeof(original) - 1);
483 sprintf(guid, "03000000%.4s0000%.4s000000000000",
484 original, original + 16);
485 }
486}
487