mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-01-07 02:00:49 +00:00
830 lines
17 KiB
C++
830 lines
17 KiB
C++
/*
|
|
** i_joystick.cpp
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 2012-2014 Alexey Lysiuk
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
#include "m_joy.h"
|
|
|
|
#include <AvailabilityMacros.h>
|
|
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
|
|
|
|
#include "hid/HID_Utilities_External.h"
|
|
|
|
#include "d_event.h"
|
|
#include "doomdef.h"
|
|
#include "templates.h"
|
|
#include "i_osversion.h"
|
|
|
|
|
|
namespace
|
|
{
|
|
|
|
FString ToFString( const CFStringRef string )
|
|
{
|
|
if ( NULL == string )
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
const CFIndex stringLength = CFStringGetLength( string );
|
|
|
|
if ( 0 == stringLength )
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
const size_t bufferSize = CFStringGetMaximumSizeForEncoding( stringLength, kCFStringEncodingUTF8 ) + 1;
|
|
|
|
char buffer[ bufferSize ];
|
|
memset( buffer, 0, bufferSize );
|
|
|
|
CFStringGetCString( string, buffer, bufferSize, kCFStringEncodingUTF8 );
|
|
|
|
return FString( buffer );
|
|
}
|
|
|
|
|
|
class IOKitJoystick : public IJoystickConfig
|
|
{
|
|
public:
|
|
explicit IOKitJoystick( IOHIDDeviceRef device );
|
|
virtual ~IOKitJoystick();
|
|
|
|
virtual FString GetName();
|
|
virtual float GetSensitivity();
|
|
virtual void SetSensitivity( float scale );
|
|
|
|
virtual int GetNumAxes();
|
|
virtual float GetAxisDeadZone( int axis );
|
|
virtual EJoyAxis GetAxisMap( int axis );
|
|
virtual const char* GetAxisName( int axis );
|
|
virtual float GetAxisScale( int axis );
|
|
|
|
virtual void SetAxisDeadZone( int axis, float deadZone );
|
|
virtual void SetAxisMap( int axis, EJoyAxis gameAxis );
|
|
virtual void SetAxisScale( int axis, float scale );
|
|
|
|
virtual bool IsSensitivityDefault();
|
|
virtual bool IsAxisDeadZoneDefault( int axis );
|
|
virtual bool IsAxisMapDefault( int axis );
|
|
virtual bool IsAxisScaleDefault( int axis );
|
|
|
|
virtual void SetDefaultConfig();
|
|
virtual FString GetIdentifier();
|
|
|
|
void AddAxes( float axes[ NUM_JOYAXIS ] ) const;
|
|
|
|
void Update();
|
|
|
|
private:
|
|
IOHIDDeviceRef m_device;
|
|
|
|
float m_sensitivity;
|
|
|
|
struct AxisInfo
|
|
{
|
|
char name[ 64 ];
|
|
|
|
float value;
|
|
|
|
float deadZone;
|
|
float defaultDeadZone;
|
|
float sensitivity;
|
|
float defaultSensitivity;
|
|
|
|
EJoyAxis gameAxis;
|
|
EJoyAxis defaultGameAxis;
|
|
|
|
IOHIDElementRef element;
|
|
};
|
|
|
|
TArray< AxisInfo > m_axes;
|
|
|
|
TArray< IOHIDElementRef > m_buttons;
|
|
TArray< IOHIDElementRef > m_POVs;
|
|
|
|
|
|
static const float DEFAULT_DEADZONE;
|
|
static const float DEFAULT_SENSITIVITY;
|
|
|
|
|
|
bool ProcessAxis ( const IOHIDValueRef value );
|
|
bool ProcessButton( const IOHIDValueRef value );
|
|
bool ProcessPOV ( const IOHIDValueRef value );
|
|
|
|
};
|
|
|
|
|
|
const float IOKitJoystick::DEFAULT_DEADZONE = 0.25f;
|
|
const float IOKitJoystick::DEFAULT_SENSITIVITY = 1.0f;
|
|
|
|
|
|
IOKitJoystick::IOKitJoystick( IOHIDDeviceRef device )
|
|
: m_device( device )
|
|
, m_sensitivity( DEFAULT_SENSITIVITY )
|
|
{
|
|
assert(NULL != device);
|
|
assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
|
|
|
|
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
|
|
assert(NULL != elements);
|
|
assert(CFArrayGetTypeID() == CFGetTypeID(elements));
|
|
|
|
for (CFIndex i = 0, count = CFArrayGetCount(elements); i < count; ++i)
|
|
{
|
|
const IOHIDElementRef element =
|
|
static_cast<IOHIDElementRef>(const_cast<void*>(CFArrayGetValueAtIndex(elements, i)));
|
|
assert(NULL != element);
|
|
assert(IOHIDElementGetTypeID() == CFGetTypeID(element));
|
|
|
|
const uint32_t usagePage = IOHIDElementGetUsagePage( element );
|
|
|
|
if ( kHIDPage_GenericDesktop == usagePage )
|
|
{
|
|
const uint32_t usage = IOHIDElementGetUsage( element );
|
|
|
|
if ( kHIDUsage_GD_Slider == usage
|
|
|| kHIDUsage_GD_X == usage || kHIDUsage_GD_Y == usage || kHIDUsage_GD_Z == usage
|
|
|| kHIDUsage_GD_Rx == usage || kHIDUsage_GD_Ry == usage || kHIDUsage_GD_Rz == usage )
|
|
{
|
|
AxisInfo axis;
|
|
memset( &axis, 0, sizeof( axis ) );
|
|
|
|
if ( const CFStringRef name = IOHIDElementGetName( element ) )
|
|
{
|
|
CFStringGetCString( name, axis.name, sizeof( axis.name ) - 1, kCFStringEncodingUTF8 );
|
|
}
|
|
else
|
|
{
|
|
snprintf( axis.name, sizeof( axis.name ), "Axis %i", m_axes.Size() + 1 );
|
|
}
|
|
|
|
axis.element = element;
|
|
|
|
m_axes.Push( axis );
|
|
|
|
IOHIDElement_SetCalibrationMin( element, -1 );
|
|
IOHIDElement_SetCalibrationMax( element, 1 );
|
|
|
|
HIDQueueElement( m_device, element );
|
|
}
|
|
else if ( kHIDUsage_GD_Hatswitch == usage && m_POVs.Size() < 4 )
|
|
{
|
|
m_POVs.Push( element );
|
|
|
|
HIDQueueElement( m_device, element );
|
|
}
|
|
}
|
|
else if ( kHIDPage_Button == usagePage )
|
|
{
|
|
m_buttons.Push( element );
|
|
|
|
HIDQueueElement( m_device, element );
|
|
}
|
|
}
|
|
|
|
CFRelease(elements);
|
|
|
|
SetDefaultConfig();
|
|
}
|
|
|
|
IOKitJoystick::~IOKitJoystick()
|
|
{
|
|
M_SaveJoystickConfig( this );
|
|
}
|
|
|
|
|
|
FString IOKitJoystick::GetName()
|
|
{
|
|
FString result;
|
|
|
|
result += ToFString( IOHIDDevice_GetManufacturer( m_device ) );
|
|
result += " ";
|
|
result += ToFString( IOHIDDevice_GetProduct( m_device ) );
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
float IOKitJoystick::GetSensitivity()
|
|
{
|
|
return m_sensitivity;
|
|
}
|
|
|
|
void IOKitJoystick::SetSensitivity( float scale )
|
|
{
|
|
m_sensitivity = scale;
|
|
}
|
|
|
|
|
|
int IOKitJoystick::GetNumAxes()
|
|
{
|
|
return static_cast< int >( m_axes.Size() );
|
|
}
|
|
|
|
#define IS_AXIS_VALID ( static_cast< unsigned int >( axis ) < m_axes.Size() )
|
|
|
|
float IOKitJoystick::GetAxisDeadZone( int axis )
|
|
{
|
|
return IS_AXIS_VALID ? m_axes[ axis ].deadZone : 0.0f;
|
|
}
|
|
|
|
EJoyAxis IOKitJoystick::GetAxisMap( int axis )
|
|
{
|
|
return IS_AXIS_VALID ? m_axes[ axis ].gameAxis : JOYAXIS_None;
|
|
}
|
|
|
|
const char* IOKitJoystick::GetAxisName( int axis )
|
|
{
|
|
return IS_AXIS_VALID ? m_axes[ axis ].name : "Invalid";
|
|
}
|
|
|
|
float IOKitJoystick::GetAxisScale( int axis )
|
|
{
|
|
return IS_AXIS_VALID ? m_axes[ axis ].sensitivity : 0.0f;
|
|
}
|
|
|
|
void IOKitJoystick::SetAxisDeadZone( int axis, float deadZone )
|
|
{
|
|
if ( IS_AXIS_VALID )
|
|
{
|
|
m_axes[ axis ].deadZone = clamp( deadZone, 0.0f, 1.0f );
|
|
}
|
|
}
|
|
|
|
void IOKitJoystick::SetAxisMap( int axis, EJoyAxis gameAxis )
|
|
{
|
|
if ( IS_AXIS_VALID )
|
|
{
|
|
m_axes[ axis ].gameAxis = ( gameAxis > JOYAXIS_None && gameAxis < NUM_JOYAXIS )
|
|
? gameAxis
|
|
: JOYAXIS_None;
|
|
}
|
|
}
|
|
|
|
void IOKitJoystick::SetAxisScale( int axis, float scale )
|
|
{
|
|
if ( IS_AXIS_VALID )
|
|
{
|
|
m_axes[ axis ].sensitivity = scale;
|
|
}
|
|
}
|
|
|
|
|
|
bool IOKitJoystick::IsSensitivityDefault()
|
|
{
|
|
return DEFAULT_SENSITIVITY == m_sensitivity;
|
|
}
|
|
|
|
bool IOKitJoystick::IsAxisDeadZoneDefault( int axis )
|
|
{
|
|
return IS_AXIS_VALID
|
|
? ( m_axes[ axis ].deadZone == m_axes[ axis ].defaultDeadZone )
|
|
: true;
|
|
}
|
|
|
|
bool IOKitJoystick::IsAxisMapDefault( int axis )
|
|
{
|
|
return IS_AXIS_VALID
|
|
? ( m_axes[ axis ].gameAxis == m_axes[ axis ].defaultGameAxis )
|
|
: true;
|
|
}
|
|
|
|
bool IOKitJoystick::IsAxisScaleDefault( int axis )
|
|
{
|
|
return IS_AXIS_VALID
|
|
? ( m_axes[ axis ].sensitivity == m_axes[ axis ].defaultSensitivity )
|
|
: true;
|
|
}
|
|
|
|
#undef IS_AXIS_VALID
|
|
|
|
void IOKitJoystick::SetDefaultConfig()
|
|
{
|
|
m_sensitivity = DEFAULT_SENSITIVITY;
|
|
|
|
const size_t axisCount = m_axes.Size();
|
|
|
|
for ( size_t i = 0; i < axisCount; ++i )
|
|
{
|
|
m_axes[i].deadZone = DEFAULT_DEADZONE;
|
|
m_axes[i].sensitivity = DEFAULT_SENSITIVITY;
|
|
m_axes[i].gameAxis = JOYAXIS_None;
|
|
}
|
|
|
|
// Two axes? Horizontal is yaw and vertical is forward.
|
|
|
|
if ( 2 == axisCount)
|
|
{
|
|
m_axes[0].gameAxis = JOYAXIS_Yaw;
|
|
m_axes[1].gameAxis = JOYAXIS_Forward;
|
|
}
|
|
|
|
// Three axes? First two are movement, third is yaw.
|
|
|
|
else if ( axisCount >= 3 )
|
|
{
|
|
m_axes[0].gameAxis = JOYAXIS_Side;
|
|
m_axes[1].gameAxis = JOYAXIS_Forward;
|
|
m_axes[2].gameAxis = JOYAXIS_Yaw;
|
|
|
|
// Four axes? First two are movement, last two are looking around.
|
|
|
|
if ( axisCount >= 4 )
|
|
{
|
|
m_axes[3].gameAxis = JOYAXIS_Pitch;
|
|
// ??? m_axes[3].sensitivity = 0.75f;
|
|
|
|
// Five axes? Use the fifth one for moving up and down.
|
|
|
|
if ( axisCount >= 5 )
|
|
{
|
|
m_axes[4].gameAxis = JOYAXIS_Up;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there is only one axis, then we make no assumptions about how
|
|
// the user might want to use it.
|
|
|
|
// Preserve defaults for config saving.
|
|
|
|
for ( size_t i = 0; i < axisCount; ++i )
|
|
{
|
|
m_axes[i].defaultDeadZone = m_axes[i].deadZone;
|
|
m_axes[i].defaultSensitivity = m_axes[i].sensitivity;
|
|
m_axes[i].defaultGameAxis = m_axes[i].gameAxis;
|
|
}
|
|
}
|
|
|
|
|
|
FString IOKitJoystick::GetIdentifier()
|
|
{
|
|
char identifier[ 32 ] = {0};
|
|
|
|
snprintf( identifier, sizeof( identifier ), "VID_%04x_PID_%04x",
|
|
IOHIDDevice_GetVendorID( m_device ), IOHIDDevice_GetProductID( m_device ) );
|
|
|
|
return FString( identifier );
|
|
}
|
|
|
|
|
|
void IOKitJoystick::AddAxes( float axes[ NUM_JOYAXIS ] ) const
|
|
{
|
|
for ( size_t i = 0, count = m_axes.Size(); i < count; ++i )
|
|
{
|
|
const EJoyAxis axis = m_axes[i].gameAxis;
|
|
|
|
if ( JOYAXIS_None == axis )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
axes[ axis ] -= m_axes[i].value;
|
|
}
|
|
}
|
|
|
|
|
|
void IOKitJoystick::Update()
|
|
{
|
|
IOHIDValueRef value = NULL;
|
|
|
|
while ( HIDGetEvent( m_device, &value ) && NULL != value )
|
|
{
|
|
ProcessAxis( value ) || ProcessButton( value ) || ProcessPOV( value );
|
|
|
|
CFRelease( value );
|
|
}
|
|
}
|
|
|
|
|
|
bool IOKitJoystick::ProcessAxis( const IOHIDValueRef value )
|
|
{
|
|
const IOHIDElementRef element = IOHIDValueGetElement( value );
|
|
|
|
if ( NULL == element )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for ( size_t i = 0, count = m_axes.Size(); i < count; ++i )
|
|
{
|
|
if ( element != m_axes[i].element )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AxisInfo& axis = m_axes[i];
|
|
|
|
const double scaledValue = IOHIDValueGetScaledValue( value, kIOHIDValueScaleTypeCalibrated );
|
|
const double filteredValue = Joy_RemoveDeadZone( scaledValue, axis.deadZone, NULL );
|
|
|
|
axis.value = static_cast< float >( filteredValue * m_sensitivity * axis.sensitivity );
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool IOKitJoystick::ProcessButton( const IOHIDValueRef value )
|
|
{
|
|
const IOHIDElementRef element = IOHIDValueGetElement( value );
|
|
|
|
if ( NULL == element )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for ( size_t i = 0, count = m_buttons.Size(); i < count; ++i )
|
|
{
|
|
if ( element != m_buttons[i] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const int newButton = IOHIDValueGetIntegerValue( value ) & 1;
|
|
const int oldButton = ~newButton;
|
|
|
|
Joy_GenerateButtonEvents( oldButton, newButton, 1,
|
|
static_cast< int >( KEY_FIRSTJOYBUTTON + i ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool IOKitJoystick::ProcessPOV( const IOHIDValueRef value )
|
|
{
|
|
const IOHIDElementRef element = IOHIDValueGetElement( value );
|
|
|
|
if ( NULL == element )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for ( size_t i = 0, count = m_POVs.Size(); i < count; ++i )
|
|
{
|
|
if ( element != m_POVs[i] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const CFIndex direction = IOHIDValueGetIntegerValue( value );
|
|
|
|
// Default values is for Up/North
|
|
int oldButtons = 0;
|
|
int newButtons = 1;
|
|
int numButtons = 1;
|
|
int baseButton = KEY_JOYPOV1_UP;
|
|
|
|
switch ( direction )
|
|
{
|
|
case 0: // N
|
|
break;
|
|
|
|
case 1: // NE
|
|
newButtons = 3;
|
|
numButtons = 2;
|
|
break;
|
|
|
|
case 2: // E
|
|
baseButton = KEY_JOYPOV1_RIGHT;
|
|
break;
|
|
|
|
case 3: // SE
|
|
newButtons = 3;
|
|
numButtons = 2;
|
|
baseButton = KEY_JOYPOV1_RIGHT;
|
|
break;
|
|
|
|
case 4: // S
|
|
baseButton = KEY_JOYPOV1_DOWN;
|
|
break;
|
|
|
|
case 5: // SW
|
|
newButtons = 3;
|
|
numButtons = 2;
|
|
baseButton = KEY_JOYPOV1_DOWN;
|
|
break;
|
|
|
|
case 6: // W
|
|
baseButton = KEY_JOYPOV1_LEFT;
|
|
break;
|
|
|
|
case 7: // NW
|
|
newButtons = 9; // UP and LEFT
|
|
numButtons = 4;
|
|
break;
|
|
|
|
default:
|
|
// release all four directions
|
|
oldButtons = 15;
|
|
newButtons = 0;
|
|
numButtons = 4;
|
|
break;
|
|
}
|
|
|
|
Joy_GenerateButtonEvents( oldButtons, newButtons, numButtons,
|
|
static_cast< int >( baseButton + i * 4 ) );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
class IOKitJoystickManager
|
|
{
|
|
public:
|
|
IOKitJoystickManager();
|
|
~IOKitJoystickManager();
|
|
|
|
void GetJoysticks( TArray< IJoystickConfig* >& joysticks ) const;
|
|
|
|
void AddAxes( float axes[ NUM_JOYAXIS ] ) const;
|
|
|
|
// Updates axes/buttons states
|
|
void Update();
|
|
|
|
// Rebuilds device list
|
|
void Rescan();
|
|
|
|
private:
|
|
TArray< IOKitJoystick* > m_joysticks;
|
|
|
|
static void OnDeviceChanged( void* context, IOReturn result, void* sender, IOHIDDeviceRef device );
|
|
|
|
void ReleaseJoysticks();
|
|
|
|
void EnableCallbacks();
|
|
void DisableCallbacks();
|
|
|
|
};
|
|
|
|
|
|
IOKitJoystickManager::IOKitJoystickManager()
|
|
{
|
|
Rescan();
|
|
}
|
|
|
|
IOKitJoystickManager::~IOKitJoystickManager()
|
|
{
|
|
ReleaseJoysticks();
|
|
DisableCallbacks();
|
|
|
|
HIDReleaseDeviceList();
|
|
}
|
|
|
|
|
|
void IOKitJoystickManager::GetJoysticks( TArray< IJoystickConfig* >& joysticks ) const
|
|
{
|
|
const size_t joystickCount = m_joysticks.Size();
|
|
|
|
joysticks.Resize( joystickCount );
|
|
|
|
for ( size_t i = 0; i < joystickCount; ++i )
|
|
{
|
|
M_LoadJoystickConfig( m_joysticks[i] );
|
|
|
|
joysticks[i] = m_joysticks[i];
|
|
}
|
|
}
|
|
|
|
void IOKitJoystickManager::AddAxes( float axes[ NUM_JOYAXIS ] ) const
|
|
{
|
|
for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i )
|
|
{
|
|
m_joysticks[i]->AddAxes( axes );
|
|
}
|
|
}
|
|
|
|
|
|
void IOKitJoystickManager::Update()
|
|
{
|
|
for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i )
|
|
{
|
|
m_joysticks[i]->Update();
|
|
}
|
|
}
|
|
|
|
|
|
void IOKitJoystickManager::Rescan()
|
|
{
|
|
ReleaseJoysticks();
|
|
DisableCallbacks();
|
|
|
|
const int usageCount = 2;
|
|
|
|
const UInt32 usagePages[ usageCount ] =
|
|
{
|
|
kHIDPage_GenericDesktop,
|
|
kHIDPage_GenericDesktop
|
|
};
|
|
|
|
const UInt32 usages[ usageCount ] =
|
|
{
|
|
kHIDUsage_GD_Joystick,
|
|
kHIDUsage_GD_GamePad
|
|
};
|
|
|
|
if ( HIDUpdateDeviceList( usagePages, usages, usageCount ) )
|
|
{
|
|
IOHIDDeviceRef device = HIDGetFirstDevice();
|
|
|
|
while ( NULL != device )
|
|
{
|
|
IOKitJoystick* joystick = new IOKitJoystick( device );
|
|
m_joysticks.Push( joystick );
|
|
|
|
device = HIDGetNextDevice( device );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Printf( "IOKitJoystickManager: Failed to build gamepad/joystick device list.\n" );
|
|
}
|
|
|
|
EnableCallbacks();
|
|
}
|
|
|
|
|
|
void IOKitJoystickManager::OnDeviceChanged( void* context, IOReturn result, void* sender, IOHIDDeviceRef device )
|
|
{
|
|
event_t event;
|
|
|
|
memset( &event, 0, sizeof( event ) );
|
|
event.type = EV_DeviceChange;
|
|
|
|
D_PostEvent( &event );
|
|
}
|
|
|
|
|
|
void IOKitJoystickManager::ReleaseJoysticks()
|
|
{
|
|
for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i )
|
|
{
|
|
delete m_joysticks[i];
|
|
}
|
|
|
|
m_joysticks.Clear();
|
|
}
|
|
|
|
|
|
void IOKitJoystickManager::EnableCallbacks()
|
|
{
|
|
if ( NULL == gIOHIDManagerRef )
|
|
{
|
|
return;
|
|
}
|
|
|
|
IOHIDManagerRegisterDeviceMatchingCallback( gIOHIDManagerRef, OnDeviceChanged, this );
|
|
IOHIDManagerRegisterDeviceRemovalCallback ( gIOHIDManagerRef, OnDeviceChanged, this );
|
|
IOHIDManagerScheduleWithRunLoop( gIOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
|
|
}
|
|
|
|
void IOKitJoystickManager::DisableCallbacks()
|
|
{
|
|
if ( NULL == gIOHIDManagerRef )
|
|
{
|
|
return;
|
|
}
|
|
|
|
IOHIDManagerUnscheduleFromRunLoop( gIOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
|
|
IOHIDManagerRegisterDeviceMatchingCallback( gIOHIDManagerRef, NULL, NULL );
|
|
IOHIDManagerRegisterDeviceRemovalCallback ( gIOHIDManagerRef, NULL, NULL );
|
|
}
|
|
|
|
|
|
IOKitJoystickManager* s_joystickManager;
|
|
|
|
|
|
} // unnamed namespace
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
void I_StartupJoysticks()
|
|
{
|
|
// HID Manager API is available on 10.5 (Darwin 9.x) or newer
|
|
|
|
if (darwinVersion.major >= 9)
|
|
{
|
|
s_joystickManager = new IOKitJoystickManager;
|
|
}
|
|
}
|
|
|
|
void I_ShutdownJoysticks()
|
|
{
|
|
delete s_joystickManager;
|
|
}
|
|
|
|
void I_GetJoysticks( TArray< IJoystickConfig* >& sticks )
|
|
{
|
|
if ( NULL != s_joystickManager )
|
|
{
|
|
s_joystickManager->GetJoysticks( sticks );
|
|
}
|
|
}
|
|
|
|
void I_GetAxes( float axes[ NUM_JOYAXIS ] )
|
|
{
|
|
for ( size_t i = 0; i < NUM_JOYAXIS; ++i )
|
|
{
|
|
axes[i] = 0.0f;
|
|
}
|
|
|
|
if ( use_joystick && NULL != s_joystickManager )
|
|
{
|
|
s_joystickManager->AddAxes( axes );
|
|
}
|
|
}
|
|
|
|
IJoystickConfig* I_UpdateDeviceList()
|
|
{
|
|
if ( use_joystick && NULL != s_joystickManager )
|
|
{
|
|
s_joystickManager->Rescan();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
void I_ProcessJoysticks()
|
|
{
|
|
if ( use_joystick && NULL != s_joystickManager )
|
|
{
|
|
s_joystickManager->Update();
|
|
}
|
|
}
|
|
|
|
#else // prior to 10.5
|
|
|
|
void I_StartupJoysticks()
|
|
{
|
|
}
|
|
|
|
void I_ShutdownJoysticks()
|
|
{
|
|
}
|
|
|
|
void I_GetJoysticks(TArray<IJoystickConfig*>& sticks)
|
|
{
|
|
sticks.Clear();
|
|
}
|
|
|
|
void I_GetAxes(float axes[NUM_JOYAXIS])
|
|
{
|
|
for (size_t i = 0; i < NUM_JOYAXIS; ++i)
|
|
{
|
|
axes[i] = 0.0f;
|
|
}
|
|
}
|
|
|
|
IJoystickConfig *I_UpdateDeviceList()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
void I_ProcessJoysticks()
|
|
{
|
|
}
|
|
|
|
#endif // 10.5 or higher
|