Initial input manager. The HID code works (so joysticks work) but the Carbon event handler is not being called. I might need to completely remove SDL first, or I might need to use some other API.
I'd rather not do this with HID, for fairly obvious reasons.
This commit is contained in:
@@ -0,0 +1,686 @@
|
||||
#include "global.h"
|
||||
#include "RageLog.h"
|
||||
|
||||
#include <ext/hash_map>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_error.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include <IOKit/hid/IOHIDLib.h>
|
||||
#include <IOKit/hid/IOHIDUsageTables.h>
|
||||
#include <IOKit/usb/USB.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
#include "InputHandler_Carbon.h"
|
||||
#include "ForEach.h"
|
||||
#include "RageUtil.h"
|
||||
|
||||
using namespace std;
|
||||
using __gnu_cxx::hash_map;
|
||||
|
||||
static void AddElement(const void *value, void *context);
|
||||
|
||||
// Simple helper, still need to release it
|
||||
static inline CFNumberRef CFInt(int n)
|
||||
{
|
||||
return CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &n);
|
||||
}
|
||||
|
||||
static inline void PrintIOErr(IOReturn err, const char *s)
|
||||
{
|
||||
LOG->Warn("%s - %s(%x,%d)", s, mach_error_string(err), err, err & 0xFFFFFF);
|
||||
}
|
||||
|
||||
// These gets absurdly long
|
||||
#define DictValue(d, k) CFDictionaryGetValue(d, CFSTR(k))
|
||||
#define IntValue(o, n) \
|
||||
CFNumberGetValue(CFNumberRef(o), kCFNumberIntType, n)
|
||||
|
||||
/* This is just awful, these aren't objects, treating them as such leads
|
||||
* to: (*object)->function(object [, argument]...)
|
||||
* Instead, do: CALL(object, function [, argument]...)
|
||||
*/
|
||||
#define CALL(o,f,...) (*(o))->f((o), ## __VA_ARGS__)
|
||||
|
||||
struct Joystick
|
||||
{
|
||||
InputDevice id;
|
||||
// map cookie to button
|
||||
hash_map<int, int> mapping;
|
||||
int x_axis, y_axis, z_axis;
|
||||
int x_min, y_min, z_min;
|
||||
int x_max, y_max, z_max;
|
||||
|
||||
Joystick();
|
||||
};
|
||||
|
||||
Joystick::Joystick() : id(DEVICE_NONE),
|
||||
x_axis(JOYSTICK_BUTTON_INVALID),
|
||||
y_axis(JOYSTICK_BUTTON_INVALID),
|
||||
z_axis(JOYSTICK_BUTTON_INVALID),
|
||||
x_min(0), y_min(0), z_min(0),
|
||||
x_max(0), y_max(0), z_max(0)
|
||||
{
|
||||
}
|
||||
|
||||
class JoystickDevice
|
||||
{
|
||||
private:
|
||||
vector<Joystick> mSticks;
|
||||
IOHIDDeviceInterface **mInterface;
|
||||
IOHIDQueueInterface **mQueue;
|
||||
bool mRunning;
|
||||
CString mDescription;
|
||||
|
||||
static void AddJoystick(const void *value, void *context);
|
||||
|
||||
public:
|
||||
JoystickDevice();
|
||||
~JoystickDevice();
|
||||
|
||||
bool Open(io_object_t device, int num, InputHandler_Carbon *handler);
|
||||
// returns the number of IDs assigned starting from startID
|
||||
int AssignJoystickIDs(int startID);
|
||||
inline int NumberOfSticks() const { return mSticks.size(); }
|
||||
inline const Joystick& GetStick(int index) const { return mSticks[index]; }
|
||||
inline const CString& GetDescription() const { return mDescription; }
|
||||
};
|
||||
|
||||
JoystickDevice::JoystickDevice() : mInterface(NULL), mQueue(NULL),
|
||||
mRunning(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool JoystickDevice::Open(io_object_t device, int num,
|
||||
InputHandler_Carbon *handler)
|
||||
{
|
||||
IOReturn ret;
|
||||
CFMutableDictionaryRef properties;
|
||||
kern_return_t result;
|
||||
|
||||
result = IORegistryEntryCreateCFProperties(device, &properties,
|
||||
kCFAllocatorDefault,
|
||||
kNilOptions);
|
||||
if (result != KERN_SUCCESS || !properties)
|
||||
{
|
||||
LOG->Warn("Couldn't get properties.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// XXX Get Device Info
|
||||
mDescription = "XXX Device Info";
|
||||
|
||||
CFArrayRef sticks;
|
||||
|
||||
if (!(sticks = (CFArrayRef)DictValue(properties, kIOHIDElementKey)))
|
||||
{
|
||||
CFRelease(properties);
|
||||
return false;
|
||||
}
|
||||
size_t count = CFArrayGetCount(sticks);
|
||||
CFRange r = {0, count};
|
||||
|
||||
mSticks.reserve(count);
|
||||
CFArrayApplyFunction(sticks, r, JoystickDevice::AddJoystick, this);
|
||||
|
||||
CFRelease(properties);
|
||||
|
||||
// Create the interface
|
||||
IOCFPlugInInterface **plugInInterface;
|
||||
// Ugh. HRESULT an awful return type
|
||||
HRESULT hresult;
|
||||
SInt32 score;
|
||||
|
||||
ret = IOCreatePlugInInterfaceForService(device,
|
||||
kIOHIDDeviceUserClientTypeID,
|
||||
kIOCFPlugInInterfaceID,
|
||||
&plugInInterface, &score);
|
||||
if (ret != kIOReturnSuccess)
|
||||
{
|
||||
PrintIOErr(ret, "Failed to create plugin interface.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call a method of the plugin to create the device interface
|
||||
CFUUIDBytes bytes = CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID);
|
||||
|
||||
hresult = CALL(plugInInterface, QueryInterface, bytes,
|
||||
(void **)&mInterface);
|
||||
|
||||
CALL(plugInInterface, Release);
|
||||
|
||||
if (hresult != S_OK)
|
||||
{
|
||||
LOG->Warn("Couldn't get device interface from plugin interface.");
|
||||
mInterface = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// open the interface
|
||||
if ((ret = CALL(mInterface, open, 0)) != kIOReturnSuccess)
|
||||
{
|
||||
PrintIOErr(ret, "Failed to open the interface.");
|
||||
CALL(mInterface, Release);
|
||||
mInterface = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// alloc/create queue
|
||||
mQueue = CALL(mInterface, allocQueue);
|
||||
if (!mQueue)
|
||||
{
|
||||
LOG->Warn("Couldn't allocate a queue.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((ret = CALL(mQueue, create, 0, 32)) != kIOReturnSuccess)
|
||||
{
|
||||
PrintIOErr(ret, "Failed to create the queue.");
|
||||
CALL(mQueue, Release);
|
||||
mQueue = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add elements to the queue for each Joystick
|
||||
for (vector<Joystick>::const_iterator i = mSticks.begin();
|
||||
i != mSticks.end(); ++i)
|
||||
{
|
||||
const Joystick& js = *i;
|
||||
|
||||
if (js.x_axis)
|
||||
CALL(mQueue, addElement, IOHIDElementCookie(js.x_axis), 0);
|
||||
if (js.y_axis)
|
||||
CALL(mQueue, addElement, IOHIDElementCookie(js.y_axis), 0);
|
||||
if (js.z_axis)
|
||||
CALL(mQueue, addElement, IOHIDElementCookie(js.z_axis), 0);
|
||||
|
||||
for (hash_map<int, int>::const_iterator j = js.mapping.begin();
|
||||
j != js.mapping.end(); ++j)
|
||||
{
|
||||
CALL(mQueue, addElement, IOHIDElementCookie(j->first), 0);
|
||||
}
|
||||
}
|
||||
// add the callback
|
||||
CFRunLoopSourceRef runLoopSource;
|
||||
|
||||
ret = CALL(mQueue, createAsyncEventSource, &runLoopSource);
|
||||
|
||||
if (ret != kIOReturnSuccess)
|
||||
{
|
||||
PrintIOErr(ret, "Failed to create async event source.");
|
||||
return false;
|
||||
}
|
||||
|
||||
CFRunLoopRef runLoop;
|
||||
|
||||
runLoop = CFRunLoopRef(GetCFRunLoopFromEventLoop(GetMainEventLoop()));
|
||||
|
||||
if (!CFRunLoopContainsSource(runLoop, runLoopSource,
|
||||
kCFRunLoopDefaultMode))
|
||||
{
|
||||
CFRunLoopAddSource(runLoop, runLoopSource,
|
||||
kCFRunLoopDefaultMode);
|
||||
}
|
||||
|
||||
CALL(mQueue, setEventCallout, InputHandler_Carbon::QueueCallBack,
|
||||
handler, (void *)num);
|
||||
|
||||
if (ret != kIOReturnSuccess)
|
||||
{
|
||||
PrintIOErr(ret, "Failed to set the call back.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// start the queue
|
||||
ret = CALL(mQueue, start);
|
||||
|
||||
if (ret != kIOReturnSuccess)
|
||||
{
|
||||
PrintIOErr(ret, "Failed to start the queue.");
|
||||
return false;
|
||||
}
|
||||
mRunning = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
JoystickDevice::~JoystickDevice()
|
||||
{
|
||||
if (mQueue)
|
||||
{
|
||||
CFRunLoopSourceRef runLoopSource;
|
||||
|
||||
if (mRunning)
|
||||
CALL(mQueue, stop);
|
||||
if ((runLoopSource = CALL(mQueue, getAsyncEventSource)))
|
||||
{
|
||||
CFRunLoopRef ref;
|
||||
|
||||
ref = CFRunLoopRef(GetCFRunLoopFromEventLoop(GetMainEventLoop()));
|
||||
|
||||
if (CFRunLoopContainsSource(ref, runLoopSource,
|
||||
kCFRunLoopDefaultMode))
|
||||
{
|
||||
CFRunLoopRemoveSource(ref, runLoopSource,
|
||||
kCFRunLoopDefaultMode);
|
||||
}
|
||||
CFRelease(runLoopSource);
|
||||
}
|
||||
|
||||
CALL(mQueue, dispose);
|
||||
CALL(mQueue, Release);
|
||||
}
|
||||
if (mInterface)
|
||||
{
|
||||
CALL(mInterface, close);
|
||||
CALL(mInterface, Release);
|
||||
}
|
||||
}
|
||||
|
||||
void JoystickDevice::AddJoystick(const void *value, void *context)
|
||||
{
|
||||
if (CFGetTypeID(CFTypeRef(value)) != CFDictionaryGetTypeID())
|
||||
return;
|
||||
|
||||
CFDictionaryRef dict = CFDictionaryRef(value);
|
||||
JoystickDevice *This = (JoystickDevice *)context;
|
||||
CFArrayRef elements;
|
||||
CFTypeRef object;
|
||||
int usage, usagePage;
|
||||
CFTypeID numID = CFNumberGetTypeID();
|
||||
|
||||
// Get usage page
|
||||
object = DictValue(dict, kIOHIDElementUsagePageKey);
|
||||
if (!object || CFGetTypeID(object) != numID ||
|
||||
!IntValue(object, &usagePage))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get usage
|
||||
object = DictValue(dict, kIOHIDElementUsageKey);
|
||||
if (!object || CFGetTypeID(object) != numID ||
|
||||
!IntValue(object, &usage))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (usagePage != kHIDPage_GenericDesktop ||
|
||||
usage != kHIDUsage_GD_Joystick)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(elements = (CFArrayRef)DictValue(dict, kIOHIDElementKey)))
|
||||
return;
|
||||
|
||||
Joystick js;
|
||||
CFRange r = {0, CFArrayGetCount(elements)};
|
||||
|
||||
CFArrayApplyFunction(elements, r, AddElement, &js);
|
||||
This->mSticks.push_back(js);
|
||||
}
|
||||
|
||||
|
||||
int JoystickDevice::AssignJoystickIDs(int startID)
|
||||
{
|
||||
for (vector<Joystick>::iterator i = mSticks.begin();
|
||||
i != mSticks.end(); ++i)
|
||||
{
|
||||
i->id = InputDevice(startID++);
|
||||
}
|
||||
return mSticks.size();
|
||||
}
|
||||
|
||||
// Callback
|
||||
static void AddElement(const void *value, void *context)
|
||||
{
|
||||
if (CFGetTypeID(CFTypeRef(value)) != CFDictionaryGetTypeID())
|
||||
return;
|
||||
|
||||
CFDictionaryRef dict = CFDictionaryRef(value);
|
||||
Joystick& js = *(Joystick *)context;
|
||||
CFTypeRef object;
|
||||
CFTypeID numID = CFNumberGetTypeID();
|
||||
int cookie, usage, usagePage;
|
||||
CFArrayRef elements;
|
||||
|
||||
// Recursively add elements
|
||||
if ((elements = (CFArrayRef)DictValue(dict, kIOHIDElementKey)))
|
||||
{
|
||||
CFRange r = {0, CFArrayGetCount(elements)};
|
||||
|
||||
CFArrayApplyFunction(elements, r, AddElement, context);
|
||||
}
|
||||
|
||||
// Get usage page
|
||||
object = DictValue(dict, kIOHIDElementUsagePageKey);
|
||||
if (!object || CFGetTypeID(object) != numID ||
|
||||
!IntValue(object, &usagePage))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get usage
|
||||
object = DictValue(dict, kIOHIDElementUsageKey);
|
||||
if (!object || CFGetTypeID(object) != numID ||
|
||||
!IntValue(object, &usage))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Get cookie
|
||||
object = DictValue(dict, kIOHIDElementCookieKey);
|
||||
if (!object || CFGetTypeID(object) != numID ||
|
||||
!IntValue(object, &cookie))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (usagePage)
|
||||
{
|
||||
case kHIDPage_GenericDesktop:
|
||||
{
|
||||
int min = 0;
|
||||
int max = 0;
|
||||
|
||||
object = DictValue(dict, kIOHIDElementMinKey);
|
||||
if (object && CFGetTypeID(object) == numID)
|
||||
IntValue(object, &min);
|
||||
|
||||
object = DictValue(dict, kIOHIDElementMaxKey);
|
||||
if (object && CFGetTypeID(object) == numID)
|
||||
IntValue(object, &max);
|
||||
|
||||
switch (usage)
|
||||
{
|
||||
case kHIDUsage_GD_X:
|
||||
js.x_axis = cookie;
|
||||
js.x_min = min;
|
||||
js.x_max = max;
|
||||
break;
|
||||
case kHIDUsage_GD_Y:
|
||||
js.y_axis = cookie;
|
||||
js.y_min = min;
|
||||
js.y_max = max;
|
||||
break;
|
||||
case kHIDUsage_GD_Z:
|
||||
js.z_axis = cookie;
|
||||
js.z_min = min;
|
||||
js.z_max = max;
|
||||
break;
|
||||
case kHIDUsage_GD_DPadUp:
|
||||
js.mapping[cookie] = JOY_UP;
|
||||
break;
|
||||
case kHIDUsage_GD_DPadDown:
|
||||
js.mapping[cookie] = JOY_DOWN;
|
||||
break;
|
||||
case kHIDUsage_GD_DPadRight:
|
||||
js.mapping[cookie] = JOY_RIGHT;
|
||||
break;
|
||||
case kHIDUsage_GD_DPadLeft:
|
||||
js.mapping[cookie] = JOY_LEFT;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kHIDPage_Button:
|
||||
{
|
||||
// button n has usage = n, subtract 1 to ensure
|
||||
// button 1 = JOY_BUTTON_1
|
||||
const int buttonID = usage + JOY_BUTTON_1 - 1;
|
||||
|
||||
if (buttonID < NUM_JOYSTICK_BUTTONS)
|
||||
js.mapping[cookie] = buttonID;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
} // end switch (usagePage)
|
||||
}
|
||||
|
||||
void InputHandler_Carbon::QueueCallBack(void *target, int result,
|
||||
void *refcon, void *sender)
|
||||
{
|
||||
// The result seems useless as you can't actually return anything...
|
||||
// refcon is the JoystickDevice number
|
||||
|
||||
RageTimer now;
|
||||
InputHandler_Carbon *This = (InputHandler_Carbon *)target;
|
||||
IOHIDQueueInterface **queue = (IOHIDQueueInterface **)sender;
|
||||
IOHIDEventStruct event;
|
||||
AbsoluteTime zeroTime = {0,0};
|
||||
JoystickDevice *jd = This->mDevices[int(refcon)];
|
||||
|
||||
while ((result = CALL(queue, getNextEvent, &event, zeroTime, 0))
|
||||
== kIOReturnSuccess)
|
||||
{
|
||||
int cookie = int(event.elementCookie);
|
||||
int value = event.value;
|
||||
|
||||
for (int i = 0; i < jd->NumberOfSticks(); ++i)
|
||||
{
|
||||
const Joystick& js = jd->GetStick(i);
|
||||
|
||||
if (js.x_axis == cookie)
|
||||
{
|
||||
float level = SCALE(value, js.x_min, js.x_max, -1.0f, 1.0f);
|
||||
|
||||
LOG->Trace("(%d) x-axis: %f\n", int(js.id), level);
|
||||
This->ButtonPressed(DeviceInput(js.id, JOY_LEFT, max(-level, 0.0f), now), level < -0.5f);
|
||||
This->ButtonPressed(DeviceInput(js.id, JOY_RIGHT, max(level, 0.0f), now), level > 0.5f);
|
||||
break;
|
||||
}
|
||||
else if (js.y_axis == cookie)
|
||||
{
|
||||
float level = SCALE(value, js.y_min, js.y_max, -1.0f, 1.0f);
|
||||
|
||||
LOG->Trace("(%d) y-axis: %f\n", int(js.id), level);
|
||||
This->ButtonPressed(DeviceInput(js.id, JOY_UP, max(-level, 0.0f), now), level < -0.5f);
|
||||
This->ButtonPressed(DeviceInput(js.id, JOY_DOWN, max(level, 0.0f), now), level > 0.5f);
|
||||
break;
|
||||
}
|
||||
else if (js.z_axis == cookie)
|
||||
{
|
||||
float level = SCALE(value, js.z_min, js.z_max, -1.0f, 1.0f);
|
||||
|
||||
LOG->Trace("(%d) z-axis: %f\n", int(js.id), level);
|
||||
This->ButtonPressed(DeviceInput(js.id, JOY_Z_UP, max(-level, 0.0f), now), level < -0.5f);
|
||||
This->ButtonPressed(DeviceInput(js.id, JOY_Z_DOWN, max(level, 0.0f), now), level > 0.5f);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// hash_map<T,U>::operator[] is not const
|
||||
hash_map<int, int>::const_iterator iter;
|
||||
|
||||
iter = js.mapping.find(cookie);
|
||||
if (iter != js.mapping.end())
|
||||
{
|
||||
LOG->Trace("(%d) Button %d: %s\n", int(js.id),
|
||||
iter->second, (value ? "down" : "up"));
|
||||
This->ButtonPressed(DeviceInput(js.id, iter->second, value, now), value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OSStatus InputHandler_Carbon::EventHandler(EventHandlerCallRef callRef,
|
||||
EventRef event, void *data)
|
||||
{
|
||||
InputHandler_Carbon *This = (InputHandler_Carbon *)data;
|
||||
UInt32 kind = GetEventKind(event);
|
||||
UInt32 keyCode;
|
||||
char charCode;
|
||||
|
||||
GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL,
|
||||
sizeof(keyCode), NULL, &keyCode);
|
||||
GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL,
|
||||
sizeof(charCode), NULL, &charCode);
|
||||
|
||||
const char *type;
|
||||
|
||||
switch (kind)
|
||||
{
|
||||
case kEventRawKeyDown:
|
||||
type = "down";
|
||||
break;
|
||||
case kEventRawKeyRepeat:
|
||||
type = "repeat";
|
||||
break;
|
||||
case kEventRawKeyUp:
|
||||
type = "up";
|
||||
default:
|
||||
type = "unknown";
|
||||
}
|
||||
|
||||
LOG->Trace("(0) %u %c: %s\n", unsigned(keyCode), charCode, type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
InputHandler_Carbon::~InputHandler_Carbon()
|
||||
{
|
||||
for (vector<JoystickDevice *>::iterator i = mDevices.begin();
|
||||
i != mDevices.end(); ++i)
|
||||
{
|
||||
delete *i;
|
||||
}
|
||||
if (mMasterPort)
|
||||
mach_port_deallocate(mach_task_self(), mMasterPort);
|
||||
RemoveEventHandler(mEventHandlerRef);
|
||||
DisposeEventHandlerUPP(mEventHandlerUPP);
|
||||
}
|
||||
|
||||
InputHandler_Carbon::InputHandler_Carbon()
|
||||
{
|
||||
// Install a Carbon Event handler
|
||||
mEventHandlerUPP = NewEventHandlerUPP(EventHandler);
|
||||
EventTypeSpec typeList[] = {
|
||||
{ kEventClassKeyboard, kEventRawKeyDown },
|
||||
{ kEventClassKeyboard, kEventRawKeyRepeat },
|
||||
{ kEventClassKeyboard, kEventRawKeyUp } };
|
||||
|
||||
|
||||
if (InstallEventHandler(GetApplicationEventTarget(), mEventHandlerUPP,
|
||||
3, typeList, this, &mEventHandlerRef))
|
||||
{
|
||||
LOG->Warn("Failed to install the Event Handler.");
|
||||
}
|
||||
|
||||
// Get a Mach port to initiate communication with I/O Kit.
|
||||
mach_port_t masterPort;
|
||||
|
||||
IOReturn ret = IOMasterPort(MACH_PORT_NULL, &masterPort);
|
||||
|
||||
if (ret != kIOReturnSuccess)
|
||||
{
|
||||
PrintIOErr(ret, "Couldn't create a master I/O Kit port.");
|
||||
return;
|
||||
}
|
||||
mMasterPort = masterPort;
|
||||
|
||||
// Build the matching dictionary.
|
||||
CFMutableDictionaryRef dict;
|
||||
|
||||
if ((dict = IOServiceMatching(kIOHIDDeviceKey)) == NULL)
|
||||
{
|
||||
LOG->Warn("Couldn't create a matching dictionary.");
|
||||
mach_port_deallocate(mach_task_self(), masterPort);
|
||||
return;
|
||||
}
|
||||
// Refine the search by only looking for joysticks
|
||||
CFNumberRef usagePage = CFInt(kHIDPage_GenericDesktop);
|
||||
CFNumberRef usage = CFInt(kHIDUsage_GD_Joystick);
|
||||
|
||||
CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsagePageKey),
|
||||
usagePage);
|
||||
CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsageKey),
|
||||
usage);
|
||||
// Cleanup after ourselves
|
||||
CFRelease(usagePage);
|
||||
CFRelease(usage);
|
||||
|
||||
// Find the HID devices.
|
||||
io_iterator_t iter;
|
||||
|
||||
|
||||
/* Get an iterator to the matching devies
|
||||
* This consumes a reference to the dictionary so we don't
|
||||
* have to Release() later.
|
||||
*/
|
||||
ret = IOServiceGetMatchingServices(masterPort, dict, &iter);
|
||||
if ((ret != kIOReturnSuccess) || !iter)
|
||||
{
|
||||
mach_port_deallocate(mach_task_self(), masterPort);
|
||||
masterPort = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate over the devices and add them
|
||||
io_object_t device;
|
||||
int id = DEVICE_JOY1;
|
||||
|
||||
while ((device = IOIteratorNext(iter)))
|
||||
{
|
||||
JoystickDevice *jd = new JoystickDevice;
|
||||
|
||||
if (jd->Open(device, mDevices.size(), this))
|
||||
{
|
||||
id += jd->AssignJoystickIDs(id);
|
||||
mDevices.push_back(jd);
|
||||
puts("Added device.");
|
||||
}
|
||||
else
|
||||
delete jd;
|
||||
|
||||
IOObjectRelease(device);
|
||||
|
||||
}
|
||||
IOObjectRelease(iter);
|
||||
}
|
||||
|
||||
void InputHandler_Carbon::GetDevicesAndDescriptions(vector<InputDevice>& dev,
|
||||
vector<CString>& desc)
|
||||
{
|
||||
for (vector<JoystickDevice *>::const_iterator i = mDevices.begin();
|
||||
i != mDevices.end(); ++i)
|
||||
{
|
||||
const JoystickDevice *jd = *i;
|
||||
|
||||
for (int j = 0; j < jd->NumberOfSticks(); ++j)
|
||||
{
|
||||
dev.push_back(jd->GetStick(j).id);
|
||||
desc.push_back(jd->GetDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2005 Steve Checkoway
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef JOYSTICK_H
|
||||
#define JOYSTICK_H
|
||||
|
||||
#include <vector>
|
||||
#include "InputHandler.h"
|
||||
|
||||
// Avoid pulling the <Carbon/Carbon.h> header in here
|
||||
typedef struct OpaqueEventHandlerCallRef *EventHandlerCallRef;
|
||||
typedef struct OpaqueEventRef *EventRef;
|
||||
typedef struct OpaqueEventHandlerRef *EventHandlerRef;
|
||||
typedef struct OpaqueEventTargetRef *EventTargetRef;
|
||||
typedef long int OSStatus;
|
||||
typedef OSStatus (*EventHandlerProcPtr) (
|
||||
EventHandlerCallRef inHandlerCallRef,
|
||||
EventRef inEvent,
|
||||
void *inUserData
|
||||
);
|
||||
typedef EventHandlerProcPtr EventHandlerUPP;
|
||||
|
||||
class JoystickDevice;
|
||||
|
||||
class InputHandler_Carbon : public InputHandler
|
||||
{
|
||||
private:
|
||||
int mMasterPort;
|
||||
std::vector<JoystickDevice *> mDevices;
|
||||
EventHandlerUPP mEventHandlerUPP;
|
||||
EventHandlerRef mEventHandlerRef;
|
||||
|
||||
public:
|
||||
InputHandler_Carbon();
|
||||
~InputHandler_Carbon();
|
||||
|
||||
void GetDevicesAndDescriptions(vector<InputDevice>& vDevicesOut,
|
||||
vector<CString>& vDescriptionsOut);
|
||||
|
||||
static void QueueCallBack(void *target, int result,
|
||||
void *refcon, void *sender);
|
||||
static OSStatus EventHandler(EventHandlerCallRef callRef, EventRef event,
|
||||
void *data);
|
||||
};
|
||||
#define USE_INPUT_HANDLER_CARBON
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2005 Steve Checkoway
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user