2005-10-15 07:30:16 +00:00
|
|
|
#include "global.h"
|
|
|
|
|
#include "RageLog.h"
|
|
|
|
|
|
|
|
|
|
#include <Carbon/Carbon.h>
|
|
|
|
|
|
|
|
|
|
#include "InputHandler_Carbon.h"
|
2005-12-27 05:55:06 +00:00
|
|
|
#include "Foreach.h"
|
2005-10-15 07:30:16 +00:00
|
|
|
#include "RageUtil.h"
|
2005-12-27 05:55:06 +00:00
|
|
|
#include "PrefsManager.h"
|
2005-10-24 11:21:46 +00:00
|
|
|
#include "archutils/Darwin/DarwinThreadHelpers.h"
|
2006-01-23 03:48:03 +00:00
|
|
|
#include "archutils/Darwin/KeyboardDevice.h"
|
|
|
|
|
#include "archutils/Darwin/JoystickDevice.h"
|
2005-10-19 11:23:21 +00:00
|
|
|
|
2005-10-16 23:09:00 +00:00
|
|
|
void InputHandler_Carbon::QueueCallBack( void *target, int result, void *refcon, void *sender )
|
2005-10-15 07:30:16 +00:00
|
|
|
{
|
|
|
|
|
// The result seems useless as you can't actually return anything...
|
2005-10-19 11:23:21 +00:00
|
|
|
// refcon is the Device number
|
2005-10-15 07:30:16 +00:00
|
|
|
|
|
|
|
|
RageTimer now;
|
|
|
|
|
InputHandler_Carbon *This = (InputHandler_Carbon *)target;
|
|
|
|
|
IOHIDQueueInterface **queue = (IOHIDQueueInterface **)sender;
|
|
|
|
|
IOHIDEventStruct event;
|
2005-10-16 23:09:00 +00:00
|
|
|
AbsoluteTime zeroTime = { 0, 0 };
|
2006-01-23 01:37:11 +00:00
|
|
|
HIDDevice *dev = This->m_vDevices[int( refcon )];
|
2006-01-23 03:48:03 +00:00
|
|
|
vector<pair<DeviceInput, bool> > vPresses;
|
2005-10-19 11:23:21 +00:00
|
|
|
|
2005-10-16 23:09:00 +00:00
|
|
|
while( (result = CALL(queue, getNextEvent, &event, zeroTime, 0)) == kIOReturnSuccess )
|
2006-01-23 03:48:03 +00:00
|
|
|
dev->GetButtonPresses( vPresses, int(event.elementCookie), int(event.value), now );
|
|
|
|
|
for( vector<pair<DeviceInput, bool> >::const_iterator i = vPresses.begin(); i != vPresses.end(); ++i )
|
|
|
|
|
This->ButtonPressed( i->first, i->second );
|
2005-10-15 07:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
2005-10-17 11:04:31 +00:00
|
|
|
static void RunLoopStarted( CFRunLoopObserverRef o, CFRunLoopActivity a, void *sem )
|
|
|
|
|
{
|
2005-12-27 05:55:06 +00:00
|
|
|
CFRunLoopObserverInvalidate( o );
|
2005-12-19 01:32:36 +00:00
|
|
|
CFRelease( o ); // we don't need this any longer
|
2005-10-17 11:04:31 +00:00
|
|
|
((RageSemaphore *)sem)->Post();
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-19 01:32:36 +00:00
|
|
|
int InputHandler_Carbon::Run( void *data )
|
2005-10-17 11:04:31 +00:00
|
|
|
{
|
|
|
|
|
InputHandler_Carbon *This = (InputHandler_Carbon *)data;
|
|
|
|
|
|
2006-01-23 01:30:38 +00:00
|
|
|
This->m_LoopRef = CFRunLoopGetCurrent();
|
|
|
|
|
CFRetain( This->m_LoopRef );
|
2005-10-17 11:04:31 +00:00
|
|
|
|
2005-12-27 05:55:06 +00:00
|
|
|
This->StartDevices();
|
2006-01-21 11:14:13 +00:00
|
|
|
SetThreadPrecedence( 1.0f );
|
2005-10-24 11:21:46 +00:00
|
|
|
|
2006-01-27 07:33:11 +00:00
|
|
|
// Add an observer for the start of the run loop
|
|
|
|
|
{
|
|
|
|
|
/* 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->m_Sem, NULL, NULL, NULL };
|
|
|
|
|
CFRunLoopObserverRef o = CFRunLoopObserverCreate( kCFAllocatorDefault, kCFRunLoopEntry,
|
|
|
|
|
false, 0, RunLoopStarted, &context);
|
|
|
|
|
CFRunLoopAddObserver( This->m_LoopRef, o, kCFRunLoopDefaultMode );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add a source for ending the run loop. This serves two purposes:
|
|
|
|
|
* 1. it provides a way to terminate the run loop when IH_Carbon exists, and
|
|
|
|
|
* 2. it ensures that CFRunLoopRun() doesn't return immediately if there are no other sources. */
|
|
|
|
|
{
|
|
|
|
|
/* Being a little tricky here, the perform callback takes a void* and returns nothing.
|
|
|
|
|
* CFRunLoopStop takes a CFRunLoopRef (a pointer) so cast the function and pass the loop ref. */
|
|
|
|
|
void *info = This->m_LoopRef;
|
|
|
|
|
void (*perform)(void *) = (void (*)(void *))CFRunLoopStop;
|
|
|
|
|
// { version, info, retain, release, copyDescription, equal, hash, schedule, cancel, perform }
|
|
|
|
|
CFRunLoopSourceContext context = { 0, info, NULL, NULL, NULL, NULL, NULL, NULL, NULL, perform };
|
|
|
|
|
|
|
|
|
|
// Pass 1 so that it is called after all inputs have been handled (they will have order = 0)
|
|
|
|
|
This->m_SourceRef = CFRunLoopSourceCreate( kCFAllocatorDefault, 1, &context );
|
|
|
|
|
|
|
|
|
|
CFRunLoopAddSource( This->m_LoopRef, This->m_SourceRef, kCFRunLoopDefaultMode );
|
|
|
|
|
}
|
2005-10-17 11:04:31 +00:00
|
|
|
CFRunLoopRun();
|
2005-12-27 05:55:06 +00:00
|
|
|
LOG->Trace( "Shutting down input handler thread..." );
|
2005-10-17 11:04:31 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-23 01:30:38 +00:00
|
|
|
// m_LoopRef needs to be set before this is called
|
2005-12-27 05:55:06 +00:00
|
|
|
void InputHandler_Carbon::StartDevices()
|
|
|
|
|
{
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
2006-01-23 01:30:38 +00:00
|
|
|
ASSERT( m_LoopRef );
|
2006-01-23 01:37:11 +00:00
|
|
|
FOREACH( HIDDevice *, m_vDevices, i )
|
2006-01-23 01:30:38 +00:00
|
|
|
(*i)->StartQueue( m_LoopRef, InputHandler_Carbon::QueueCallBack, this, n++ );
|
2005-12-27 05:55:06 +00:00
|
|
|
}
|
|
|
|
|
|
2005-10-15 07:30:16 +00:00
|
|
|
InputHandler_Carbon::~InputHandler_Carbon()
|
|
|
|
|
{
|
2006-01-23 01:37:11 +00:00
|
|
|
FOREACH( HIDDevice *, m_vDevices, i )
|
2005-10-15 07:30:16 +00:00
|
|
|
delete *i;
|
2005-12-27 05:55:06 +00:00
|
|
|
if( PREFSMAN->m_bThreadedInput )
|
|
|
|
|
{
|
2006-01-27 07:33:11 +00:00
|
|
|
CFRunLoopSourceSignal( m_SourceRef );
|
|
|
|
|
CFRunLoopWakeUp( m_LoopRef );
|
2006-01-23 01:30:38 +00:00
|
|
|
m_InputThread.Wait();
|
2006-01-27 07:33:11 +00:00
|
|
|
CFRelease( m_SourceRef );
|
|
|
|
|
// Don't release the loop ref.
|
2005-12-27 05:55:06 +00:00
|
|
|
LOG->Trace( "Input handler thread shut down." );
|
|
|
|
|
}
|
2005-10-15 07:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-14 01:04:48 +00:00
|
|
|
static io_iterator_t GetDeviceIterator( int usagePage, int usage )
|
2005-10-19 21:12:18 +00:00
|
|
|
{
|
|
|
|
|
// Build the matching dictionary.
|
|
|
|
|
CFMutableDictionaryRef dict;
|
|
|
|
|
|
|
|
|
|
if( (dict = IOServiceMatching(kIOHIDDeviceKey)) == NULL )
|
|
|
|
|
{
|
|
|
|
|
LOG->Warn( "Couldn't create a matching dictionary." );
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
// Refine the search by only looking for joysticks
|
|
|
|
|
CFNumberRef usagePageRef = CFInt( usagePage );
|
|
|
|
|
CFNumberRef usageRef = CFInt( usage );
|
|
|
|
|
|
|
|
|
|
CFDictionarySetValue( dict, CFSTR(kIOHIDPrimaryUsagePageKey), usagePageRef );
|
|
|
|
|
CFDictionarySetValue( dict, CFSTR(kIOHIDPrimaryUsageKey), usageRef );
|
|
|
|
|
|
|
|
|
|
// Cleanup after ourselves
|
|
|
|
|
CFRelease( usagePageRef );
|
|
|
|
|
CFRelease( usageRef );
|
|
|
|
|
|
|
|
|
|
// Find the HID devices.
|
|
|
|
|
io_iterator_t iter;
|
|
|
|
|
|
2006-01-14 01:04:48 +00:00
|
|
|
/* Get an iterator to the matching devies. This consumes a reference to the dictionary
|
|
|
|
|
* so we do not have to release it later. */
|
|
|
|
|
if( IOServiceGetMatchingServices(kIOMasterPortDefault, dict, &iter) != kIOReturnSuccess )
|
2005-10-19 21:12:18 +00:00
|
|
|
{
|
|
|
|
|
LOG->Warn( "Couldn't get matching services" );
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return iter;
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-23 01:30:38 +00:00
|
|
|
InputHandler_Carbon::InputHandler_Carbon() : m_Sem( "Input thread started" )
|
2005-10-15 07:30:16 +00:00
|
|
|
{
|
2006-01-23 03:48:03 +00:00
|
|
|
// Find the keyboards.
|
|
|
|
|
io_iterator_t iter = GetDeviceIterator( kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard );
|
2005-10-17 11:04:31 +00:00
|
|
|
|
2005-10-19 21:12:18 +00:00
|
|
|
if( iter )
|
|
|
|
|
{
|
|
|
|
|
io_object_t device;
|
|
|
|
|
|
|
|
|
|
while( (device = IOIteratorNext(iter)) )
|
|
|
|
|
{
|
2006-01-23 01:37:11 +00:00
|
|
|
HIDDevice *kd = new KeyboardDevice;
|
2005-10-19 21:12:18 +00:00
|
|
|
|
|
|
|
|
if( kd->Open(device) )
|
2006-01-23 01:30:38 +00:00
|
|
|
m_vDevices.push_back( kd );
|
2005-10-19 21:12:18 +00:00
|
|
|
else
|
|
|
|
|
delete kd;
|
|
|
|
|
|
|
|
|
|
IOObjectRelease( device );
|
|
|
|
|
}
|
|
|
|
|
IOObjectRelease( iter );
|
|
|
|
|
}
|
2006-01-23 03:48:03 +00:00
|
|
|
|
|
|
|
|
// Find the joysticks.
|
|
|
|
|
iter = GetDeviceIterator( kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick );
|
|
|
|
|
|
|
|
|
|
if( iter )
|
|
|
|
|
{
|
|
|
|
|
// Iterate over the devices and add them
|
|
|
|
|
io_object_t device;
|
|
|
|
|
InputDevice id = DEVICE_JOY1;
|
|
|
|
|
|
|
|
|
|
while( (device = IOIteratorNext(iter)) )
|
|
|
|
|
{
|
|
|
|
|
HIDDevice *jd = new JoystickDevice;
|
|
|
|
|
|
|
|
|
|
if( jd->Open(device) )
|
|
|
|
|
{
|
|
|
|
|
int num = jd->AssignIDs( id );
|
|
|
|
|
|
|
|
|
|
enum_add( id, num );
|
|
|
|
|
m_vDevices.push_back( jd );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
delete jd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IOObjectRelease( device );
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
IOObjectRelease( iter );
|
|
|
|
|
}
|
2005-12-27 05:55:06 +00:00
|
|
|
|
|
|
|
|
if( PREFSMAN->m_bThreadedInput )
|
|
|
|
|
{
|
2006-01-23 01:30:38 +00:00
|
|
|
m_InputThread.SetName( "Input thread" );
|
|
|
|
|
m_InputThread.Create( InputHandler_Carbon::Run, this );
|
2005-12-27 05:55:06 +00:00
|
|
|
// Wait for the run loop to start before returning.
|
2006-01-23 01:30:38 +00:00
|
|
|
m_Sem.Wait();
|
2005-12-27 05:55:06 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2006-01-23 01:30:38 +00:00
|
|
|
m_LoopRef = CFRunLoopRef( GetCFRunLoopFromEventLoop(GetMainEventLoop()) );
|
2005-12-27 05:55:06 +00:00
|
|
|
StartDevices();
|
|
|
|
|
}
|
2005-10-15 07:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
void InputHandler_Carbon::GetDevicesAndDescriptions( vector<InputDevice>& dev, vector<RString>& desc )
|
2005-10-15 07:30:16 +00:00
|
|
|
{
|
2006-01-23 01:37:11 +00:00
|
|
|
FOREACH_CONST( HIDDevice *, m_vDevices, i )
|
2006-01-23 03:48:03 +00:00
|
|
|
(*i)->GetDevicesAndDescriptions( dev, desc );
|
2005-10-15 07:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2006-01-14 01:04:48 +00:00
|
|
|
* (c) 2005, 2006 Steve Checkoway
|
2005-10-15 07:30:16 +00:00
|
|
|
* 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.
|
|
|
|
|
*/
|