glfw/src/cocoa_joystick.m

417 lines
13 KiB
Mathematica
Raw Normal View History

2010-09-07 15:34:51 +00:00
//========================================================================
2016-08-18 21:42:15 +00:00
// GLFW 3.3 Cocoa - www.glfw.org
2010-09-07 15:34:51 +00:00
//------------------------------------------------------------------------
2016-11-21 15:23:59 +00:00
// Copyright (c) 2009-2016 Camilla Löwy <elmindreda@glfw.org>
// Copyright (c) 2012 Torsten Walluhn <tw@mad-cad.net>
2010-09-07 15:34:51 +00:00
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
#include "internal.h"
2011-09-18 19:05:00 +00:00
#include <unistd.h>
#include <ctype.h>
#include <string.h>
2012-01-29 14:38:22 +00:00
2011-09-18 19:05:00 +00:00
#include <mach/mach.h>
#include <mach/mach_error.h>
2012-01-29 14:38:22 +00:00
2011-09-18 19:05:00 +00:00
#include <CoreFoundation/CoreFoundation.h>
#include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
// Joystick element information
2016-02-08 08:32:48 +00:00
//
typedef struct _GLFWjoyelementNS
2012-01-29 14:30:01 +00:00
{
IOHIDElementRef native;
long minimum;
long maximum;
2012-01-29 14:30:01 +00:00
} _GLFWjoyelementNS;
2011-09-18 19:05:00 +00:00
2012-01-29 14:30:01 +00:00
// Returns the value of the specified element of the specified joystick
2013-02-04 12:22:10 +00:00
//
static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element)
2011-09-18 19:05:00 +00:00
{
2012-01-29 14:30:01 +00:00
IOReturn result = kIOReturnSuccess;
IOHIDValueRef valueRef;
long value = 0;
2012-01-29 14:30:01 +00:00
if (js && element && js->ns.device)
2012-01-29 14:30:01 +00:00
{
result = IOHIDDeviceGetValue(js->ns.device,
element->native,
&valueRef);
2012-01-29 14:30:01 +00:00
if (kIOReturnSuccess == result)
{
value = IOHIDValueGetIntegerValue(valueRef);
2012-08-26 13:38:18 +00:00
// Record min and max for auto calibration
if (value < element->minimum)
element->minimum = value;
if (value > element->maximum)
element->maximum = value;
2012-01-29 14:30:01 +00:00
}
}
2012-08-26 13:38:18 +00:00
// Auto user scale
return value;
2011-09-18 19:05:00 +00:00
}
2012-01-29 14:30:01 +00:00
// Removes the specified joystick
2013-02-04 12:22:10 +00:00
//
static void closeJoystick(_GLFWjoystick* js)
2011-09-18 19:05:00 +00:00
{
2012-01-29 23:02:54 +00:00
int i;
2016-02-08 08:32:48 +00:00
if (!js->present)
2013-04-24 17:25:42 +00:00
return;
2012-01-29 14:30:01 +00:00
for (i = 0; i < CFArrayGetCount(js->ns.axes); i++)
free((void*) CFArrayGetValueAtIndex(js->ns.axes, i));
CFRelease(js->ns.axes);
2012-01-29 14:30:01 +00:00
for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++)
free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i));
CFRelease(js->ns.buttons);
2012-01-29 14:30:01 +00:00
for (i = 0; i < CFArrayGetCount(js->ns.hats); i++)
free((void*) CFArrayGetValueAtIndex(js->ns.hats, i));
CFRelease(js->ns.hats);
2012-01-29 14:30:01 +00:00
_glfwFreeJoystick(js);
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_DISCONNECTED);
2011-09-18 19:05:00 +00:00
}
// Callback for user-initiated joystick addition
2013-02-04 12:22:10 +00:00
//
static void matchCallback(void* context,
IOReturn result,
void* sender,
IOHIDDeviceRef device)
2011-09-18 19:05:00 +00:00
{
2016-10-10 01:24:07 +00:00
int jid;
char name[256];
CFIndex i;
CFStringRef productKey;
_GLFWjoystick* js;
CFMutableArrayRef axes, buttons, hats;
2017-01-12 04:30:56 +00:00
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (_glfw.joysticks[jid].ns.device == device)
return;
}
2012-01-29 14:30:01 +00:00
axes = CFArrayCreateMutable(NULL, 0, NULL);
buttons = CFArrayCreateMutable(NULL, 0, NULL);
hats = CFArrayCreateMutable(NULL, 0, NULL);
2012-01-29 14:30:01 +00:00
productKey = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
if (productKey)
{
CFStringGetCString(productKey,
name,
sizeof(name),
kCFStringEncodingUTF8);
}
else
strncpy(name, "Unknown", sizeof(name));
2012-01-29 14:30:01 +00:00
CFArrayRef elements =
IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
for (i = 0; i < CFArrayGetCount(elements); i++)
{
IOHIDElementRef native = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i);
if (CFGetTypeID(native) != IOHIDElementGetTypeID())
continue;
const IOHIDElementType type = IOHIDElementGetType(native);
if ((type != kIOHIDElementTypeInput_Axis) &&
(type != kIOHIDElementTypeInput_Button) &&
(type != kIOHIDElementTypeInput_Misc))
{
continue;
}
2012-01-29 14:30:01 +00:00
CFMutableArrayRef target = NULL;
2012-08-14 19:58:22 +00:00
switch (IOHIDElementGetUsagePage(native))
{
case kHIDPage_GenericDesktop:
{
switch (IOHIDElementGetUsage(native))
{
case kHIDUsage_GD_X:
case kHIDUsage_GD_Y:
case kHIDUsage_GD_Z:
case kHIDUsage_GD_Rx:
case kHIDUsage_GD_Ry:
case kHIDUsage_GD_Rz:
case kHIDUsage_GD_Slider:
case kHIDUsage_GD_Dial:
case kHIDUsage_GD_Wheel:
target = axes;
break;
case kHIDUsage_GD_Hatswitch:
target = hats;
break;
}
break;
}
case kHIDPage_Button:
target = buttons;
break;
default:
break;
}
if (target)
{
_GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS));
element->native = native;
element->minimum = IOHIDElementGetLogicalMin(native);
element->maximum = IOHIDElementGetLogicalMax(native);
CFArrayAppendValue(target, element);
}
}
CFRelease(elements);
2015-12-13 16:38:50 +00:00
js = _glfwAllocJoystick(name,
CFArrayGetCount(axes),
CFArrayGetCount(buttons) + CFArrayGetCount(hats) * 4);
js->ns.device = device;
js->ns.axes = axes;
js->ns.buttons = buttons;
js->ns.hats = hats;
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED);
}
2012-01-29 14:30:01 +00:00
// Callback for user-initiated joystick removal
//
static void removeCallback(void* context,
IOReturn result,
void* sender,
IOHIDDeviceRef device)
{
2016-10-10 01:24:07 +00:00
int jid;
2012-01-29 14:30:01 +00:00
2017-01-12 04:30:56 +00:00
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (_glfw.joysticks[jid].ns.device == device)
{
closeJoystick(_glfw.joysticks + jid);
break;
}
}
}
2012-01-29 14:30:01 +00:00
// Creates a dictionary to match against devices with the specified usage page
// and usage
//
static CFMutableDictionaryRef createMatchingDictionary(long usagePage,
long usage)
{
CFMutableDictionaryRef result =
CFDictionaryCreateMutable(kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
2012-01-29 14:30:01 +00:00
if (result)
{
CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault,
kCFNumberLongType,
&usagePage);
if (pageRef)
{
CFDictionarySetValue(result,
CFSTR(kIOHIDDeviceUsagePageKey),
pageRef);
CFRelease(pageRef);
CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault,
kCFNumberLongType,
&usage);
if (usageRef)
{
CFDictionarySetValue(result,
CFSTR(kIOHIDDeviceUsageKey),
usageRef);
CFRelease(usageRef);
}
}
}
return result;
}
2012-01-29 14:30:01 +00:00
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
2012-01-29 14:30:01 +00:00
// Initialize joystick interface
//
void _glfwInitJoysticksNS(void)
{
CFMutableArrayRef matchingCFArrayRef;
2016-06-07 12:11:54 +00:00
_glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault,
kIOHIDOptionsTypeNone);
2012-01-29 14:30:01 +00:00
matchingCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault,
0,
&kCFTypeArrayCallBacks);
if (matchingCFArrayRef)
{
CFDictionaryRef matchingCFDictRef =
createMatchingDictionary(kHIDPage_GenericDesktop,
kHIDUsage_GD_Joystick);
if (matchingCFDictRef)
2012-01-29 14:30:01 +00:00
{
CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef);
CFRelease(matchingCFDictRef);
2012-01-29 14:30:01 +00:00
}
matchingCFDictRef = createMatchingDictionary(kHIDPage_GenericDesktop,
kHIDUsage_GD_GamePad);
if (matchingCFDictRef)
{
CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef);
CFRelease(matchingCFDictRef);
}
2013-04-24 17:25:42 +00:00
matchingCFDictRef =
createMatchingDictionary(kHIDPage_GenericDesktop,
kHIDUsage_GD_MultiAxisController);
if (matchingCFDictRef)
2011-09-18 19:05:00 +00:00
{
CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef);
CFRelease(matchingCFDictRef);
2011-09-18 19:05:00 +00:00
}
2016-06-07 12:11:54 +00:00
IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager,
matchingCFArrayRef);
CFRelease(matchingCFArrayRef);
}
2012-01-29 14:30:01 +00:00
2016-06-07 12:11:54 +00:00
IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager,
&matchCallback, NULL);
2016-06-07 12:11:54 +00:00
IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager,
&removeCallback, NULL);
2013-04-24 17:25:42 +00:00
2016-06-07 12:11:54 +00:00
IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager,
CFRunLoopGetMain(),
kCFRunLoopDefaultMode);
2016-06-07 12:11:54 +00:00
IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone);
// Execute the run loop once in order to register any initially-attached
// joysticks
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
2011-09-18 19:05:00 +00:00
}
// Close all opened joystick handles
2013-02-04 12:22:10 +00:00
//
void _glfwTerminateJoysticksNS(void)
2011-09-18 19:05:00 +00:00
{
2016-10-10 01:24:07 +00:00
int jid;
2012-01-29 23:02:54 +00:00
2017-01-12 04:30:56 +00:00
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
closeJoystick(_glfw.joysticks + jid);
2016-06-07 12:11:54 +00:00
CFRelease(_glfw.ns.hidManager);
_glfw.ns.hidManager = NULL;
2011-09-18 19:05:00 +00:00
}
2010-09-16 01:25:36 +00:00
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
2010-09-07 15:34:51 +00:00
int _glfwPlatformPollJoystick(int jid, int mode)
2010-09-07 15:34:51 +00:00
{
_GLFWjoystick* js = _glfw.joysticks + jid;
2010-09-07 15:34:51 +00:00
if (mode == _GLFW_POLL_AXES)
{
CFIndex i;
2012-01-29 14:30:01 +00:00
for (i = 0; i < CFArrayGetCount(js->ns.axes); i++)
{
_GLFWjoyelementNS* axis = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.axes, i);
2010-09-07 15:34:51 +00:00
const long value = getElementValue(js, axis);
const long delta = axis->maximum - axis->minimum;
2012-08-14 19:58:22 +00:00
if (delta == 0)
_glfwInputJoystickAxis(jid, i, value);
else
_glfwInputJoystickAxis(jid, i, (2.f * (value - axis->minimum) / delta) - 1.f);
}
}
else if (mode == _GLFW_POLL_BUTTONS)
{
CFIndex i, bi = 0;
2012-08-14 19:58:22 +00:00
for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++)
{
_GLFWjoyelementNS* button = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.buttons, i);
const char value = getElementValue(js, button) ? 1 : 0;
_glfwInputJoystickButton(jid, bi++, value);
}
for (i = 0; i < CFArrayGetCount(js->ns.hats); i++)
{
_GLFWjoyelementNS* hat = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.hats, i);
// Bit fields of button presses for each direction, including nil
const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 };
2016-03-29 11:42:11 +00:00
long j, state = getElementValue(js, hat);
if (state < 0 || state > 8)
state = 8;
for (j = 0; j < 4; j++)
{
const char value = directions[state] & (1 << j) ? 1 : 0;
_glfwInputJoystickButton(jid, bi++, value);
}
}
}
return js->present;
}