Wise&mystical  1.0
Project about Europe
Loading...
Searching...
No Matches
cocoa_joystick.m
Go to the documentation of this file.
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//
45typedef struct _GLFWjoyelementNS
46{
47 IOHIDElementRef native;
49 int index;
50 long minimum;
51 long maximum;
52
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
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
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
309
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
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
403{
404 if (mode & _GLFW_POLL_AXES)
405 {
406 CFIndex i;
407
408 for (i = 0; i < CFArrayGetCount(js->ns.axes); i++)
409 {
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 {
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 {
457 };
458
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
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
void _glfwPlatformTerminateJoysticks(void)
int _glfwPlatformPollJoystick(_GLFWjoystick *js, int mode)
GLFWbool _glfwPlatformInitJoysticks(void)
void _glfwPlatformUpdateGamepadGUID(char *guid)
#define GLFW_DISCONNECTED
Definition: glfw3.h:1231
#define GLFW_CONNECTED
Definition: glfw3.h:1230
#define GLFW_PLATFORM_ERROR
A platform-specific error occurred that does not match any of the more specific categories.
Definition: glfw3.h:758
#define GLFW_HAT_RIGHT
Definition: glfw3.h:355
#define GLFW_HAT_LEFT_UP
Definition: glfw3.h:360
#define GLFW_HAT_LEFT_DOWN
Definition: glfw3.h:361
#define GLFW_HAT_UP
Definition: glfw3.h:354
#define GLFW_HAT_RIGHT_UP
Definition: glfw3.h:358
#define GLFW_HAT_LEFT
Definition: glfw3.h:357
#define GLFW_HAT_DOWN
Definition: glfw3.h:356
#define GLFW_HAT_RIGHT_DOWN
Definition: glfw3.h:359
#define GLFW_HAT_CENTERED
Definition: glfw3.h:353
#define GLFW_TRUE
One.
Definition: glfw3.h:310
#define GLFW_FALSE
Zero.
Definition: glfw3.h:319
#define GLFW_PRESS
The key or mouse button was pressed.
Definition: glfw3.h:336
#define GLFW_RELEASE
The key or mouse button was released.
Definition: glfw3.h:329
#define GLFW_JOYSTICK_LAST
Definition: glfw3.h:606
_GLFWlibrary _glfw
Definition: init.c:46
void _glfwInputError(int code, const char *format,...)
Definition: init.c:160
_GLFWjoystick * _glfwAllocJoystick(const char *name, const char *guid, int axisCount, int buttonCount, int hatCount)
Definition: input.c:429
void _glfwInputJoystick(_GLFWjoystick *js, int event)
Definition: input.c:386
void _glfwInputJoystickAxis(_GLFWjoystick *js, int axis, float value)
Definition: input.c:396
void _glfwInputJoystickHat(_GLFWjoystick *js, int hat, char value)
Definition: input.c:410
void _glfwFreeJoystick(_GLFWjoystick *js)
Definition: input.c:465
void _glfwInputJoystickButton(_GLFWjoystick *js, int button, char value)
Definition: input.c:403
#define _GLFW_POLL_AXES
Definition: internal.h:55
#define _GLFW_POLL_BUTTONS
Definition: internal.h:56
int GLFWbool
Definition: internal.h:61
#define NULL
Definition: miniaudio.h:3718
unsigned int uint32_t
Definition: stdint.h:80
unsigned char uint8_t
Definition: stdint.h:78
IOHIDElementRef native
unsigned char * hats
Definition: internal.h:498
GLFWbool present
Definition: internal.h:493
float * axes
Definition: internal.h:494
unsigned char * buttons
Definition: internal.h:496
_GLFWjoystick joysticks[GLFW_JOYSTICK_LAST+1]
Definition: internal.h:547