glfw/src/cocoa_joystick.m

398 lines
12 KiB
Objective-C
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
{
IOHIDValueRef valueRef;
long value = 0;
2012-01-29 14:30:01 +00:00
if (js->ns.device)
2012-01-29 14:30:01 +00:00
{
if (IOHIDDeviceGetValue(js->ns.device,
element->native,
&valueRef) == kIOReturnSuccess)
2012-01-29 14:30:01 +00:00
{
value = IOHIDValueGetIntegerValue(valueRef);
2012-01-29 14:30:01 +00:00
}
}
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));
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
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
2012-01-29 14:30:01 +00:00
// Initialize joystick interface
//
void _glfwInitJoysticksNS(void)
{
2017-02-26 21:18:40 +00:00
CFMutableArrayRef matching;
const long usages[] =
{
kHIDUsage_GD_Joystick,
kHIDUsage_GD_GamePad,
kHIDUsage_GD_MultiAxisController
};
2016-06-07 12:11:54 +00:00
_glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault,
kIOHIDOptionsTypeNone);
2012-01-29 14:30:01 +00:00
2017-02-26 21:18:40 +00:00
matching = CFArrayCreateMutable(kCFAllocatorDefault,
0,
&kCFTypeArrayCallBacks);
if (!matching)
{
2017-02-26 21:18:40 +00:00
_glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array");
return;
}
2012-01-29 14:30:01 +00:00
2017-02-26 21:18:40 +00:00
for (int i = 0; i < sizeof(usages) / sizeof(long); i++)
{
const long page = kHIDPage_GenericDesktop;
CFMutableDictionaryRef dict =
CFDictionaryCreateMutable(kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!dict)
continue;
2013-04-24 17:25:42 +00:00
2017-02-26 21:18:40 +00:00
CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault,
kCFNumberLongType,
&page);
CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault,
kCFNumberLongType,
&usages[i]);
if (pageRef && usageRef)
2011-09-18 19:05:00 +00:00
{
2017-02-26 21:18:40 +00:00
CFDictionarySetValue(dict,
CFSTR(kIOHIDDeviceUsagePageKey),
pageRef);
CFDictionarySetValue(dict,
CFSTR(kIOHIDDeviceUsageKey),
usageRef);
CFArrayAppendValue(matching, dict);
2011-09-18 19:05:00 +00:00
}
2017-02-26 21:18:40 +00:00
if (pageRef)
CFRelease(pageRef);
if (usageRef)
CFRelease(usageRef);
CFRelease(dict);
}
2012-01-29 14:30:01 +00:00
2017-02-26 21:18:40 +00:00
IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, matching);
CFRelease(matching);
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);
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 raw = getElementValue(js, axis);
// Perform auto calibration
if (raw < axis->minimum)
axis->minimum = raw;
if (raw > axis->maximum)
axis->maximum = raw;
2012-08-14 19:58:22 +00:00
const long delta = axis->maximum - axis->minimum;
if (delta == 0)
_glfwInputJoystickAxis(jid, i, 0.f);
else
{
const float value = (2.f * (raw - axis->minimum) / delta) - 1.f;
_glfwInputJoystickAxis(jid, i, value);
}
}
}
else if (mode == _GLFW_POLL_BUTTONS)
{
CFIndex i;
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) - button->minimum;
_glfwInputJoystickButton(jid, i, value);
}
for (i = 0; i < CFArrayGetCount(js->ns.hats); i++)
{
const int states[9] =
{
GLFW_HAT_UP,
GLFW_HAT_RIGHT_UP,
GLFW_HAT_RIGHT,
GLFW_HAT_RIGHT_DOWN,
GLFW_HAT_DOWN,
GLFW_HAT_LEFT_DOWN,
GLFW_HAT_LEFT,
GLFW_HAT_LEFT_UP,
GLFW_HAT_CENTERED
};
_GLFWjoyelementNS* hat = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.hats, i);
long state = getElementValue(js, hat) - hat->minimum;
if (state < 0 || state > 8)
state = 8;
_glfwInputJoystickHat(jid, i, states[state]);
}
}
return js->present;
}