#include "global.h" #include "RageLog.h" #include #include #include #include #include #include #include #include #include #include #include "InputHandler_Carbon.h" #include "ForEach.h" #include "RageUtil.h" using namespace std; using __gnu_cxx::hash_map; // 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 ); } static inline int IntValue( const void *o, int *n ) { return 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 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 Device { private: IOHIDDeviceInterface **mInterface; IOHIDQueueInterface **mQueue; bool mRunning; CString mDescription; static void AddLogicalDevice( const void *value, void *context ); static void AddElement( const void *value, void *context ); protected: virtual bool AddLogicalDevice( int usagePage, int usage ) = 0; virtual void AddElement( int usagePage, int usage, int cookie, const CFDictionaryRef dict ) = 0; virtual void Open() = 0; inline void AddElementToQueue( int cookie ) { CALL( mQueue, addElement, IOHIDElementCookie(cookie), 0 ); } public: Device(); virtual ~Device(); bool Open( io_object_t device ); void StartQueue( CFRunLoopRef loopRef, IOHIDCallbackFunction callback, void *target, int refCon ); inline const CString& GetDescription() const { return mDescription; } }; Device::Device() : mInterface( NULL ), mQueue( NULL ), mRunning( false ) { } Device::~Device() { 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 ); } } bool Device::Open( io_object_t device ) { 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 logicalDevices; if ( !(logicalDevices = (CFArrayRef)CFDictionaryGetValue(properties, CFSTR(kIOHIDElementKey))) ) { CFRelease( properties ); return false; } CFRange r = { 0, CFArrayGetCount(logicalDevices) }; CFArrayApplyFunction( logicalDevices, r, Device::AddLogicalDevice, this ); CFRelease( properties ); // Create the interface IOCFPlugInInterface **plugInInterface; 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; } Open(); return true; } void Device::StartQueue( CFRunLoopRef loopRef, IOHIDCallbackFunction callback, void *target, int refCon ) { CFRunLoopSourceRef runLoopSource; IOReturn ret = CALL( mQueue, createAsyncEventSource, &runLoopSource ); if( ret != kIOReturnSuccess ) { PrintIOErr( ret, "Failed to create async event source." ); return; } CFRunLoopRef runLoop; runLoop = CFRunLoopRef( GetCFRunLoopFromEventLoop(GetMainEventLoop()) ); if( !CFRunLoopContainsSource(runLoop, runLoopSource, kCFRunLoopDefaultMode) ) CFRunLoopAddSource( runLoop, runLoopSource, kCFRunLoopDefaultMode ); CALL( mQueue, setEventCallout, callback, target, (void *)refCon ); if( ret != kIOReturnSuccess ) { PrintIOErr( ret, "Failed to set the call back." ); return; } // start the queue ret = CALL( mQueue, start ); if( ret != kIOReturnSuccess ) { PrintIOErr( ret, "Failed to start the queue." ); return; } mRunning = true; } void Device::AddLogicalDevice( const void *value, void *context ) { if( CFGetTypeID(CFTypeRef(value)) != CFDictionaryGetTypeID() ) return; CFDictionaryRef dict = CFDictionaryRef( value ); Device *This = (Device *)context; CFArrayRef elements; CFTypeRef object; int usage, usagePage; CFTypeID numID = CFNumberGetTypeID(); // Get usage page object = CFDictionaryGetValue( dict, CFSTR(kIOHIDElementUsagePageKey) ); if( !object || CFGetTypeID(object) != numID || !IntValue(object, &usagePage) ) return; // Get usage object = CFDictionaryGetValue( dict, CFSTR(kIOHIDElementUsageKey) ); if( !object || CFGetTypeID(object) != numID || !IntValue(object, &usage) ) return; if( !(elements = (CFArrayRef)CFDictionaryGetValue( dict, CFSTR(kIOHIDElementKey))) ) return; if( !This->AddLogicalDevice(usagePage, usage) ) return; CFRange r = { 0, CFArrayGetCount(elements) }; CFArrayApplyFunction( elements, r, Device::AddElement, This ); } void Device::AddElement( const void *value, void *context ) { if( CFGetTypeID(CFTypeRef(value)) != CFDictionaryGetTypeID() ) return; CFDictionaryRef dict = CFDictionaryRef( value ); Device *This = (Device *)context; CFTypeRef object; CFTypeID numID = CFNumberGetTypeID(); int cookie, usage, usagePage; CFArrayRef elements; // Recursively add elements if( (elements = (CFArrayRef)CFDictionaryGetValue(dict, CFSTR(kIOHIDElementKey))) ) { CFRange r = { 0, CFArrayGetCount(elements) }; CFArrayApplyFunction( elements, r, AddElement, context ); } // Get usage page object = CFDictionaryGetValue( dict, CFSTR(kIOHIDElementUsagePageKey) ); if( !object || CFGetTypeID(object) != numID || !IntValue(object, &usagePage) ) return; // Get usage object = CFDictionaryGetValue( dict, CFSTR(kIOHIDElementUsageKey) ); if( !object || CFGetTypeID(object) != numID || !IntValue(object, &usage) ) return; // Get cookie object = CFDictionaryGetValue( dict, CFSTR(kIOHIDElementCookieKey) ); if( !object || CFGetTypeID(object) != numID || !IntValue(object, &cookie) ) return; This->AddElement( usagePage, usage, cookie, dict ); } class JoystickDevice : public Device { private: vector mSticks; protected: bool AddLogicalDevice( int usagePage, int usage ); void AddElement( int usagePage, int usage, int cookie, const CFDictionaryRef dict ); void Open(); public: // 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]; } }; bool JoystickDevice::AddLogicalDevice( int usagePage, int usage ) { if( usagePage != kHIDPage_GenericDesktop || usage != kHIDUsage_GD_Joystick ) return false; mSticks.push_back( Joystick() ); return true; } void JoystickDevice::AddElement( int usagePage, int usage, int cookie, const CFDictionaryRef dict ) { CFTypeRef object; CFTypeID numID = CFNumberGetTypeID(); Joystick& js = mSticks.back(); switch( usagePage ) { case kHIDPage_GenericDesktop: { int min = 0; int max = 0; object = CFDictionaryGetValue( dict, CFSTR(kIOHIDElementMinKey) ); if( object && CFGetTypeID(object) == numID ) IntValue( object, &min ); object = CFDictionaryGetValue( dict, CFSTR(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 JoystickDevice::Open() { // Add elements to the queue for each Joystick FOREACH_CONST( Joystick, mSticks, i ) { const Joystick& js = *i; if( js.x_axis ) AddElementToQueue( js.x_axis ); if( js.y_axis ) AddElementToQueue( js.y_axis ); if( js.z_axis ) AddElementToQueue( js.z_axis ); for( hash_map::const_iterator j = js.mapping.begin(); j != js.mapping.end(); ++j ) AddElementToQueue( j->first ); } } int JoystickDevice::AssignJoystickIDs( int startID ) { for( vector::iterator i = mSticks.begin(); i != mSticks.end(); ++i ) i->id = InputDevice( startID++ ); return mSticks.size(); } class KeyboardDevice : public Device { private: hash_map mMapping; protected: bool AddLogicalDevice( int usagePage, int usage ); void AddElement( int usagePage, int usage, int cookie, const CFDictionaryRef dict ); void Open(); friend void InputHandler_Carbon::QueueCallBack(void *, int, void *, void *); }; bool KeyboardDevice::AddLogicalDevice( int usagePage, int usage ) { return usagePage == kHIDPage_GenericDesktop && usage == kHIDUsage_GD_Keyboard; } void KeyboardDevice::AddElement( int usagePage, int usage, int cookie, const CFDictionaryRef dict ) { if( usagePage != kHIDPage_KeyboardOrKeypad ) return; if( usage < kHIDUsage_KeyboardA ) return; if( usage <= kHIDUsage_KeyboardZ ) { mMapping[cookie] = usage - kHIDUsage_KeyboardA + KEY_Ca; return; } // KEY_C0 = KEY_C1 - 1, kHIDUsage_Keyboard0 = kHIDUsage_Keyboard9 + 1 if( usage <= kHIDUsage_Keyboard9 ) { mMapping[cookie] = usage - kHIDUsage_Keyboard1 + KEY_C1; return; } if( usage >= kHIDUsage_KeyboardF1 && usage <= kHIDUsage_KeyboardF12 ) { mMapping[cookie] = usage - kHIDUsage_KeyboardF1 + KEY_F1; return; } if( usage >= kHIDUsage_KeyboardF13 && usage <= kHIDUsage_KeyboardF16 ) { mMapping[cookie] = usage - kHIDUsage_KeyboardF13 + KEY_F13; return; } // keypad 0 is again backward if( usage >= kHIDUsage_Keypad1 && usage <= kHIDUsage_Keypad9 ) { mMapping[cookie] = usage - kHIDUsage_Keypad1 + KEY_KP_C1; return; } #define OTHER(n) (KEY_OTHER_0 + (n)) // [0, 8] if( usage >= kHIDUsage_KeyboardF17 && usage <= kHIDUsage_KeyboardExecute ) { mMapping[cookie] = usage - kHIDUsage_KeyboardF17 + OTHER(0); return; } // [9, 19] if( usage >= kHIDUsage_KeyboardSelect && usage <= kHIDUsage_KeyboardVolumeDown ) { mMapping[cookie] = usage - kHIDUsage_KeyboardSelect + OTHER(9); return; } // [10, 31] if( usage >= kHIDUsage_KeypadEqualSignAS400 && usage <= kHIDUsage_KeyboardCancel ) { mMapping[cookie] = usage - kHIDUsage_KeypadEqualSignAS400 + OTHER(10); return; } // [32, 37] // XXX kHIDUsage_KeyboardClearOrAgain if( usage >= kHIDUsage_KeyboardSeparator && usage <= kHIDUsage_KeyboardExSel ) { mMapping[cookie] = usage - kHIDUsage_KeyboardSeparator + OTHER(32); return; } #define X(x,y) case x: mMapping[cookie] = y; return // Time for the special cases switch( usage ) { X( kHIDUsage_Keyboard0, KEY_C0 ); X( kHIDUsage_Keypad0, KEY_KP_C0 ); X( kHIDUsage_KeyboardReturnOrEnter, KEY_ENTER ); X( kHIDUsage_KeyboardEscape, KEY_ESC ); X( kHIDUsage_KeyboardDeleteOrBackspace, KEY_BACK ); X( kHIDUsage_KeyboardTab, KEY_TAB ); X( kHIDUsage_KeyboardSpacebar, KEY_SPACE ); X( kHIDUsage_KeyboardHyphen, KEY_HYPHEN ); X( kHIDUsage_KeyboardEqualSign, KEY_EQUAL ); X( kHIDUsage_KeyboardOpenBracket, KEY_LBRACKET ); X( kHIDUsage_KeyboardCloseBracket, KEY_RBRACKET ); X( kHIDUsage_KeyboardBackslash, KEY_BACKSLASH ); X( kHIDUsage_KeyboardNonUSPound, KEY_HASH ); X( kHIDUsage_KeyboardSemicolon, KEY_SEMICOLON ); X( kHIDUsage_KeyboardQuote, KEY_SQUOTE ); X( kHIDUsage_KeyboardGraveAccentAndTilde, KEY_ACCENT ); X( kHIDUsage_KeyboardComma, KEY_COMMA ); X( kHIDUsage_KeyboardPeriod, KEY_PERIOD ); X( kHIDUsage_KeyboardSlash, KEY_SLASH ); X( kHIDUsage_KeyboardCapsLock, KEY_CAPSLOCK ); X( kHIDUsage_KeyboardPrintScreen, KEY_PRTSC ); X( kHIDUsage_KeyboardScrollLock, KEY_SCRLLOCK ); X( kHIDUsage_KeyboardPause, OTHER(0) ); X( kHIDUsage_KeyboardInsert, KEY_INSERT ); X( kHIDUsage_KeyboardHome, KEY_HOME ); X( kHIDUsage_KeyboardPageUp, KEY_PGUP ); X( kHIDUsage_KeyboardDeleteForward, KEY_DEL ); X( kHIDUsage_KeyboardEnd, KEY_END ); X( kHIDUsage_KeyboardPageDown, KEY_PGDN ); X( kHIDUsage_KeyboardRightArrow, KEY_RIGHT ); X( kHIDUsage_KeyboardLeftArrow, KEY_LEFT ); X( kHIDUsage_KeyboardDownArrow, KEY_DOWN ); X( kHIDUsage_KeyboardUpArrow, KEY_UP ); X( kHIDUsage_KeypadNumLock, KEY_NUMLOCK ); X( kHIDUsage_KeypadSlash, KEY_KP_SLASH ); X( kHIDUsage_KeypadEqualSign, KEY_KP_EQUAL ); X( kHIDUsage_KeypadAsterisk, KEY_KP_ASTERISK ); X( kHIDUsage_KeypadHyphen, KEY_KP_HYPHEN ); X( kHIDUsage_KeypadPlus, KEY_KP_PLUS ); X( kHIDUsage_KeypadEnter, KEY_KP_ENTER ); X( kHIDUsage_KeypadPeriod, KEY_KP_PERIOD ); X( kHIDUsage_KeyboardNonUSBackslash, OTHER(38) ); X( kHIDUsage_KeyboardApplication, OTHER(39) ); X( kHIDUsage_KeyboardClear, KEY_NUMLOCK ); // XXX X( kHIDUsage_KeyboardHelp, KEY_INSERT ); X( kHIDUsage_KeyboardMenu, KEY_MENU ); // XXX kHIDUsage_KeyboardLockingCapsLock // XXX kHIDUsage_KeyboardLockingNumLock // XXX kHIDUsage_KeyboardLockingScrollLock X( kHIDUsage_KeypadComma, KEY_KP_PERIOD ); // XXX X( kHIDUsage_KeyboardReturn, KEY_ENTER ); X( kHIDUsage_KeyboardPrior, OTHER(40) ); X( kHIDUsage_KeyboardLeftControl, KEY_LCTRL ); X( kHIDUsage_KeyboardLeftShift, KEY_LSHIFT ); X( kHIDUsage_KeyboardLeftAlt, KEY_LALT ); X( kHIDUsage_KeyboardLeftGUI, KEY_LMETA ); // XXX GUI?? X( kHIDUsage_KeyboardRightControl, KEY_RCTRL ); X( kHIDUsage_KeyboardRightShift, KEY_RSHIFT ); X( kHIDUsage_KeyboardRightAlt, KEY_RALT ); X( kHIDUsage_KeyboardRightGUI, KEY_RMETA ); // XXX } #undef X #undef OTHER } void KeyboardDevice::Open() { for (hash_map::const_iterator i = mMapping.begin(); i != mMapping.end(); ++i) AddElementToQueue( i->first ); } 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 Device number RageTimer now; InputHandler_Carbon *This = (InputHandler_Carbon *)target; IOHIDQueueInterface **queue = (IOHIDQueueInterface **)sender; IOHIDEventStruct event; AbsoluteTime zeroTime = { 0, 0 }; Device *dev = This->mDevices[int( refcon )]; KeyboardDevice *kd = dynamic_cast(dev); JoystickDevice *jd = dynamic_cast(dev); ASSERT(kd || jd ); while( (result = CALL(queue, getNextEvent, &event, zeroTime, 0)) == kIOReturnSuccess ) { int cookie = int( event.elementCookie ); int value = event.value; if( kd ) { hash_map::const_iterator iter = kd->mMapping.find( cookie ); if( iter != kd->mMapping.end() ) This->ButtonPressed( DeviceInput(DEVICE_KEYBOARD, iter->second, value, now), value ); continue; } 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 ); 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 ); 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 ); 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::operator[] is not const hash_map::const_iterator iter; iter = js.mapping.find( cookie ); if( iter != js.mapping.end() ) { This->ButtonPressed( DeviceInput(js.id, iter->second, value, now), value ); break; } } } } } static void RunLoopStarted( CFRunLoopObserverRef o, CFRunLoopActivity a, void *sem ) { CFRelease(o); // we don't need this any longer ((RageSemaphore *)sem)->Post(); } int InputHandler_Carbon::Run(void *data) { InputHandler_Carbon *This = (InputHandler_Carbon *)data; CFRunLoopRef loopRef = CFRunLoopGetCurrent(); int n = 0; CFRetain(loopRef); FOREACH( Device *, This->mDevices, i ) (*i)->StartQueue( loopRef, InputHandler_Carbon::QueueCallBack, This, n++ ); This->mLoopRef = loopRef; /* The function copies the information out of the structure, so the memory pointed * to by context does not need to persist beyond the function call. */ CFRunLoopObserverContext context = { 0, &This->mSem, NULL, NULL, NULL }; CFRunLoopObserverRef o = CFRunLoopObserverCreate( kCFAllocatorDefault, kCFRunLoopEntry, false, 0, RunLoopStarted, &context); CFRunLoopAddObserver( loopRef, o, kCFRunLoopDefaultMode ); CFRunLoopRun(); return 0; } InputHandler_Carbon::~InputHandler_Carbon() { CFRunLoopStop( CFRunLoopRef(mLoopRef) ); mInputThread.Wait(); FOREACH( Device *, mDevices, i ) delete *i; if( mMasterPort ) mach_port_deallocate( mach_task_self(), mMasterPort ); } InputHandler_Carbon::InputHandler_Carbon() : mSem("Input thread started") { // 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( static_cast(jd)->Open(device) ) { id += jd->AssignJoystickIDs( id ); mDevices.push_back( jd ); puts( "Added device." ); } else { delete jd; } IOObjectRelease( device ); } IOObjectRelease( iter ); // XXX now find the keyboards mInputThread.SetName( "Input thread." ); mInputThread.Create(InputHandler_Carbon::Run, this); // Wait for the run loop to start before returning. mSem.Wait(); } void InputHandler_Carbon::GetDevicesAndDescriptions( vector& dev, vector& desc ) { #if 0 // not yet dev.push_back(DEVICE_KEYBOARD); desc.push_back("Keyboard"); #endif FOREACH_CONST( Device *, mDevices, i ) { const JoystickDevice *jd = dynamic_cast(*i); /* This could be break since right now KeyboardDevices follow * the JoystickDevices, but that is brittle. */ if (!jd) continue; 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. */