csgo-2018-source/inputsystem/joystick_osx.cpp
2021-07-24 21:11:47 -07:00

1026 lines
31 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: OSX Joystick implementation for inputsystem.dll
//
//===========================================================================//
#include <mach/mach.h>
#include <mach/mach_error.h>
/* For force feedback testing. */
#include "inputsystem.h"
#include "tier1/convar.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
ConVar joy_axisbutton_threshold( "joy_axisbutton_threshold", "0.3", FCVAR_ARCHIVE, "Analog axis range before a button press is registered." );
ConVar joy_axis_deadzone( "joy_axis_deadzone", "0.2", FCVAR_ARCHIVE, "Dead zone near the zero point to not report movement." );
//-----------------------------------------------------------------------------
// Initialize all joysticks
//-----------------------------------------------------------------------------
void CInputSystem::InitializeJoysticks( void )
{
// assume no joystick
m_nJoystickCount = 0;
// abort startup if user requests no joystick
if ( CommandLine()->FindParm("-nojoy" ) )
return;
IOReturn result = kIOReturnSuccess;
mach_port_t masterPort = 0;
io_iterator_t hidObjectIterator = 0;
CFMutableDictionaryRef hidMatchDictionary = NULL;
result = IOMasterPort( MACH_PORT_NULL, &masterPort);
if ( kIOReturnSuccess != result )
{
DevMsg( 1, "joystick not found -- IOMasterPort error with bootstrap_port.\n");
return;
}
hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey);
if ( !hidMatchDictionary )
{
DevMsg( 1, "joystick not found -- Failed to get HID CFMutableDictionaryRef via IOServiceMatching.");
return;
}
result = IOServiceGetMatchingServices( masterPort, hidMatchDictionary, &hidObjectIterator );
if ( kIOReturnSuccess != result )
{
DevMsg( 1, "joystick not found -- Couldn't create a HID object iterator.");
return ;
}
if ( !hidObjectIterator )
{
m_nJoystickCount = 0;
return;
}
io_object_t ioHIDDeviceObject = 0;
while ( ( ioHIDDeviceObject = IOIteratorNext(hidObjectIterator ) ) )
{
JoystickInfo_t &info = m_pJoystickInfo[m_nJoystickCount];
info.m_pParent = this;
info.m_bRemoved = false;
info.m_Interface = NULL;
info.m_FFInterface = 0;
info.m_nButtonCount = 0;
info.m_nFlags = 0;
info.m_nAxisFlags = 0;
info.m_bXBoxRumbleEnabled = false;
// grab the device record
if ( !HIDBuildDevice( ioHIDDeviceObject, info ) )
continue;
if ( FFIsForceFeedback(ioHIDDeviceObject) == FF_OK )
{
FFCreateDevice(ioHIDDeviceObject,&info.m_FFInterface);;
}
else
{
info.m_FFInterface = 0;
}
info.m_nDeviceId = m_nJoystickCount;
info.m_nLastPolledButtons = 0;
info.m_nLastPolledAxisButtons = 0;
info.m_nLastPolledPOVState = 0;
memset( info.m_pLastPolledAxes, 0, sizeof(info.m_pLastPolledAxes) );
EnableJoystickInput( m_nJoystickCount, true );
++m_nJoystickCount;
}
result = IOObjectRelease(hidObjectIterator);
}
void HIDReportErrorNum(char *strError, long numError)
{
DevMsg( 1, "%s", strError );
}
int ButtonSort(const void *a, const void *b)
{
CInputSystem::OSXInputValue_t *left = (CInputSystem::OSXInputValue_t *)a;
CInputSystem::OSXInputValue_t *right = (CInputSystem::OSXInputValue_t *)b;
return left->m_Usage - right->m_Usage;
}
void CInputSystem::HIDSortJoystickButtons( JoystickInfo_t &info )
{
qsort( info.m_Buttons, info.m_nButtonCount, sizeof(info.m_Buttons[0]), ButtonSort );
}
bool CInputSystem::HIDBuildDevice( io_object_t ioHIDDeviceObject, JoystickInfo_t &info )
{
CFMutableDictionaryRef hidProperties = 0;
kern_return_t result = IORegistryEntryCreateCFProperties( ioHIDDeviceObject, &hidProperties, kCFAllocatorDefault, kNilOptions);
if ((result == KERN_SUCCESS) && hidProperties)
{
if ( HIDCreateOpenDeviceInterface( ioHIDDeviceObject, info ) )
{
HIDGetDeviceInfo( ioHIDDeviceObject, hidProperties, info );
HIDGetCollectionElements( hidProperties, info );
HIDSortJoystickButtons( info );
}
else
return false;
CFRelease(hidProperties);
}
else
return false;
// Filter device list to non-keyboard/mouse stuff
if ( info.usagePage != kHIDPage_GenericDesktop ||
( ( info.usage != kHIDUsage_GD_Joystick &&
info.usage != kHIDUsage_GD_GamePad &&
info.usage != kHIDUsage_GD_MultiAxisController ) ) )
{
HIDDisposeDevice( info );
return false;
}
return true;
}
static void HIDRemovalCallback(void *target, IOReturn result, void *refcon, void *sender)
{
CInputSystem::JoystickInfo_t *joy = (CInputSystem::JoystickInfo_t *) refcon;
joy->m_bRemoved = true;
}
/* Create and open an interface to device, required prior to extracting values or building queues.
* Note: appliction now owns the device and must close and release it prior to exiting
*/
bool CInputSystem::HIDCreateOpenDeviceInterface( io_object_t hidDevice, JoystickInfo_t &info )
{
IOReturn result = kIOReturnSuccess;
HRESULT plugInResult = S_OK;
SInt32 score = 0;
IOCFPlugInInterface **ppPlugInInterface = NULL;
if ( NULL == info.m_Interface )
{
result = IOCreatePlugInInterfaceForService(hidDevice, kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &ppPlugInInterface, &score);
if (kIOReturnSuccess == result)
{
plugInResult = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),(void **)&(info.m_Interface) );
if ( plugInResult != S_OK )
HIDReportErrorNum("Couldnt query HID class device interface from plugInInterface", plugInResult);
(*ppPlugInInterface)->Release(ppPlugInInterface);
}
else
HIDReportErrorNum("Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.", result);
}
if ( info.m_Interface != NULL )
{
result = (*info.m_Interface)->open( info.m_Interface, 0);
if ( result != kIOReturnSuccess )
{
HIDReportErrorNum("Failed to open pDevice->interface via open.", result);
}
else
{
(*info.m_Interface)->setRemovalCallback( info.m_Interface, HIDRemovalCallback, &info, this );
}
}
return result == kIOReturnSuccess;
}
static void HIDTopLevelElementHandler(const void *value, void *parameter)
{
CFTypeRef refCF = 0;
if (CFGetTypeID(value) != CFDictionaryGetTypeID())
return;
refCF = CFDictionaryGetValue((CFDictionaryRef)value, CFSTR(kIOHIDElementUsagePageKey));
if ( !CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &((CInputSystem::JoystickInfo_t *) parameter)->usagePage) )
DevMsg( 1, "HIDTopLevelElementHandler CFNumberGetValue error retrieving pDevice->usagePage." );
refCF = CFDictionaryGetValue((CFDictionaryRef)value, CFSTR(kIOHIDElementUsageKey));
if ( !CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &((CInputSystem::JoystickInfo_t *) parameter)->usage) )
DevMsg( 1, "HIDTopLevelElementHandler CFNumberGetValue error retrieving pDevice->usage." );
}
void CInputSystem::HIDGetDeviceInfo( io_object_t hidDevice, CFMutableDictionaryRef hidProperties, JoystickInfo_t &info )
{
CFMutableDictionaryRef usbProperties = 0;
io_registry_entry_t parent1, parent2;
if ((KERN_SUCCESS == IORegistryEntryGetParentEntry(hidDevice, kIOServicePlane, &parent1))
&& (KERN_SUCCESS == IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2))
&& (KERN_SUCCESS == IORegistryEntryCreateCFProperties(parent2, &usbProperties, kCFAllocatorDefault, kNilOptions)))
{
if ( usbProperties )
{
CFTypeRef refCF = 0;
long vendorid = 0;
long productid = 0;
refCF = CFDictionaryGetValue((CFDictionaryRef)hidProperties, CFSTR(kIOHIDVendorIDKey) );
if (refCF)
CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &vendorid );
refCF = CFDictionaryGetValue((CFDictionaryRef)hidProperties, CFSTR(kIOHIDProductIDKey) );
if (refCF)
CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &productid );
if ( vendorid == 0x045e )
{
m_bXController = true; // microsoft gamepad, lets call it a 360 controller
}
/* get usage page and usage */
refCF = CFDictionaryGetValue((CFDictionaryRef)hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey) );
if (refCF)
{
if ( !CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &info.usagePage ) )
DevMsg( 1, "CInputSystem::HIDGetDeviceInfo error retrieving pDevice->usagePage." );
refCF = CFDictionaryGetValue((CFDictionaryRef)hidProperties, CFSTR(kIOHIDPrimaryUsageKey));
if (refCF)
if (!CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &info.usage))
DevMsg( 1, "CInputSystem::HIDGetDeviceInfo error retrieving pDevice->usage." );
}
if (NULL == refCF)
{
CFTypeRef refCFTopElement = 0;
refCFTopElement = CFDictionaryGetValue( (CFDictionaryRef)hidProperties, CFSTR(kIOHIDElementKey) );
{
CFRange range = { 0, CFArrayGetCount((CFArrayRef)refCFTopElement) };
CFArrayApplyFunction( (CFArrayRef)refCFTopElement, range, HIDTopLevelElementHandler, &info );
}
}
CFRelease( usbProperties );
}
else
DevMsg( 1, "CInputSystem::HIDGetDeviceInfo IORegistryEntryCreateCFProperties failed to create usbProperties." );
if (kIOReturnSuccess != IOObjectRelease(parent2) )
DevMsg( 1, "CInputSystem::HIDGetDeviceInfo IOObjectRelease error with parent2." );
if (kIOReturnSuccess != IOObjectRelease(parent1))
DevMsg( 1, "CInputSystem::HIDGetDeviceInfo IOObjectRelease error with parent1." );
}
}
void CInputSystem::HIDGetElementInfo( CFTypeRef refElement, OSXInputValue_t &input )
{
long number;
CFTypeRef refType;
refType = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementCookieKey) );
if ( refType && CFNumberGetValue( (CFNumberRef)refType, kCFNumberLongType, &number ) )
input.m_Cookie = (int) number;
refType = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementMinKey) );
if ( refType && CFNumberGetValue( (CFNumberRef)refType, kCFNumberLongType, &number ) )
{
input.m_MinVal = input.m_MinReport = number;
input.m_MaxVal = input.m_MaxReport = input.m_MinVal;
}
refType = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementMaxKey) );
if ( refType && CFNumberGetValue( (CFNumberRef)refType, kCFNumberLongType, &number ) )
{
input.m_MaxVal = input.m_MaxReport = number;
}
input.m_bSet = true;
input.m_RefElement = refElement;
refType = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementUsageKey) );
if ( refType && CFNumberGetValue( (CFNumberRef)refType, kCFNumberLongType, &number ) )
{
input.m_Usage = number;
}
}
void CInputSystem::HIDAddElement(CFTypeRef refElement, JoystickInfo_t &info )
{
long elementType, usagePage, usage;
CFTypeRef refElementType = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementTypeKey) );
CFTypeRef refUsagePage = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementUsagePageKey) );
CFTypeRef refUsage = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementUsageKey) );
if ( refElementType && CFNumberGetValue( (CFNumberRef)refElementType, kCFNumberLongType, &elementType) )
{
if ( (elementType == kIOHIDElementTypeInput_Misc) || (elementType == kIOHIDElementTypeInput_Button) || (elementType == kIOHIDElementTypeInput_Axis) )
{
if ( refUsagePage && CFNumberGetValue( (CFNumberRef)refUsagePage, kCFNumberLongType, &usagePage )
&& refUsage && CFNumberGetValue( (CFNumberRef)refUsage, kCFNumberLongType, &usage ) )
{
switch (usagePage)
{
case kHIDPage_GenericDesktop:
{
switch ( usage )
{
case kHIDUsage_GD_X:
HIDGetElementInfo( refElement, info.m_xaxis );
info.m_nAxisFlags |= 0x3;
break;
case kHIDUsage_GD_Y:
HIDGetElementInfo( refElement, info.m_yaxis );
info.m_nAxisFlags |= 0x3;
break;
case kHIDUsage_GD_Z:
HIDGetElementInfo( refElement, info.m_zaxis );
info.m_nAxisFlags |= 0x4;
break;
case kHIDUsage_GD_Rx:
HIDGetElementInfo( refElement, info.m_uaxis );
info.m_nAxisFlags |= 0x8;
break;
case kHIDUsage_GD_Ry:
HIDGetElementInfo( refElement, info.m_raxis );
info.m_nAxisFlags |= 0x10;
break;
case kHIDUsage_GD_Rz:
HIDGetElementInfo( refElement, info.m_vaxis );
info.m_nAxisFlags |= 0x20;
break;
case kHIDUsage_GD_Slider:
case kHIDUsage_GD_Dial:
case kHIDUsage_GD_Wheel:
// unused
break;
case kHIDUsage_GD_Hatswitch:
HIDGetElementInfo( refElement, info.m_POV );
info.m_bHasPOVControl = true;
break;
default:
break;
}
}
break;
case kHIDPage_Button:
if ( info.m_nButtonCount < MAX_JOYSTICK_BUTTONS )
{
HIDGetElementInfo( refElement, info.m_Buttons[ info.m_nButtonCount ] );
info.m_nButtonCount++;
}
break;
default:
break;
}
}
}
else if (kIOHIDElementTypeCollection == elementType)
{
HIDGetCollectionElements((CFMutableDictionaryRef) refElement, info );
}
}
}
static void HIDGetElementsCFArrayHandler( const void *value, void *parameter )
{
if ( CFGetTypeID(value) == CFDictionaryGetTypeID() )
{
CInputSystem::JoystickInfo_t *info = (CInputSystem::JoystickInfo_t *)parameter;
if ( info )
info->m_pParent->HIDAddElement( (CFTypeRef)value, *info );
}
}
void CInputSystem::HIDGetElements( CFTypeRef refElementCurrent, JoystickInfo_t &info )
{
CFTypeID type = CFGetTypeID(refElementCurrent);
if (type == CFArrayGetTypeID())
{
CFRange range = { 0, CFArrayGetCount((CFArrayRef)refElementCurrent) };
CFArrayApplyFunction((CFArrayRef)refElementCurrent, range, HIDGetElementsCFArrayHandler, &info );
}
}
void CInputSystem::HIDGetCollectionElements( CFMutableDictionaryRef deviceProperties, JoystickInfo_t &info )
{
CFTypeRef refElementTop = CFDictionaryGetValue((CFDictionaryRef)deviceProperties, CFSTR(kIOHIDElementKey));
if (refElementTop)
HIDGetElements( refElementTop, info );
}
void CInputSystem::HIDDisposeDevice( JoystickInfo_t &info )
{
kern_return_t result = KERN_SUCCESS;
if ( info.m_FFInterface )
{
if ( m_bXController && info.m_bXBoxRumbleEnabled )
{
info.m_bXBoxRumbleEnabled = false;
FFEFFESCAPE escape;
char c;
c=0x00;
escape.dwSize=sizeof(escape);
escape.dwCommand=0x00;
escape.cbInBuffer=sizeof(c);
escape.lpvInBuffer=&c;
escape.cbOutBuffer=0;
escape.lpvOutBuffer=NULL;
FFDeviceEscape( info.m_FFInterface ,&escape );
}
FFReleaseDevice( info.m_FFInterface );
}
result = (*info.m_Interface)->close( info.m_Interface );
if ( result == kIOReturnNotOpen )
{
// wasn't open, what?
}
else if ( result != kIOReturnSuccess)
{
HIDReportErrorNum( "Failed to close IOHIDDeviceInterface.", result );
}
result = (*info.m_Interface)->Release( info.m_Interface );
if ( kIOReturnSuccess != result )
{
HIDReportErrorNum( "Failed to release IOHIDDeviceInterface.", result );
}
}
//-----------------------------------------------------------------------------
// Process the event
//-----------------------------------------------------------------------------
void CInputSystem::JoystickButtonEvent( ButtonCode_t button, int sample )
{
// package the key
if ( sample )
{
PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, button, button );
}
else
{
PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, button, button );
}
}
//-----------------------------------------------------------------------------
// Update the joystick button state
//-----------------------------------------------------------------------------
void CInputSystem::UpdateJoystickButtonState( int nJoystick )
{
JoystickInfo_t &info = m_pJoystickInfo[nJoystick];
int nButtons = 0;
for ( int i = 0; i < info.m_nButtonCount; i++ )
{
int value = HIDGetElementValue( info, info.m_Buttons[i] ) - info.m_Buttons[i].m_MinVal;
int mask = info.m_nLastPolledButtons & ( 1 << i );
if ( !mask && !value ) // not pressed last time or this
continue;
ButtonCode_t code = (ButtonCode_t)JOYSTICK_BUTTON( nJoystick, i );
if ( m_bXController )
{
// translate the xcontroller xpad buttons into the POV button presses we expect
if ( i == 11 )
code = KEY_XBUTTON_UP;
if ( i == 12 )
code = KEY_XBUTTON_DOWN;
if ( i == 13 )
code = KEY_XBUTTON_LEFT;
if ( i == 14 )
code = KEY_XBUTTON_RIGHT;
}
if ( value )
{
// down event
JoystickButtonEvent( code, MAX_BUTTONSAMPLE );
}
else
{
// up event
JoystickButtonEvent( code, 0 );
}
info.m_nLastPolledButtons &= ~( 1 << i );
if ( value )
info.m_nLastPolledButtons |= ( 1 << i );
}
// Analog axis buttons
for ( int j = 0 ; j < MAX_JOYSTICK_AXES; ++j )
{
if ( ( info.m_nAxisFlags & (1 << j) ) == 0 )
continue;
// Positive side of the axis
int mask = ( 1 << (j << 1) );
ButtonCode_t code = JOYSTICK_AXIS_BUTTON( nJoystick, (j << 1) );
float value = GetAnalogValue( JOYSTICK_AXIS( nJoystick, j ) );
float minValue = joy_axisbutton_threshold.GetFloat();
switch (j)
{
default:
case 0:
minValue *= info.m_xaxis.m_MaxVal;
break;
case 1:
minValue *= info.m_yaxis.m_MaxVal;
break;
case 2:
minValue *= info.m_zaxis.m_MaxVal;
break;
case 3:
minValue *= info.m_raxis.m_MaxVal;
break;
case 4:
minValue *= info.m_uaxis.m_MaxVal;
break;
case 5:
minValue *= info.m_vaxis.m_MaxVal;
break;
}
if ( j == 5 )
code = KEY_XBUTTON_RTRIGGER; // left and right triggers go 0 to 255 under osx, so drag R axis over to z negative
if ( value > minValue && !(info.m_nLastPolledAxisButtons & mask) )
{
info.m_nLastPolledAxisButtons |= mask;
JoystickButtonEvent( code, MAX_BUTTONSAMPLE );
}
if ( value <= minValue && (info.m_nLastPolledAxisButtons & mask) )
{
info.m_nLastPolledAxisButtons &= ~mask;
JoystickButtonEvent( code, 0 );
}
// Negative side of the axis
mask <<= 1;
code = (ButtonCode_t)( code + 1 );
if ( value < -minValue && !(info.m_nLastPolledAxisButtons & mask) )
{
info.m_nLastPolledAxisButtons |= mask;
JoystickButtonEvent( code, MAX_BUTTONSAMPLE );
}
if ( value >= -minValue && (info.m_nLastPolledAxisButtons & mask) )
{
info.m_nLastPolledAxisButtons &= ~mask;
JoystickButtonEvent( code, 0 );
}
}
}
int CInputSystem::HIDGetElementValue( JoystickInfo_t &info, OSXInputValue_t &value )
{
IOReturn result = kIOReturnSuccess;
IOHIDEventStruct hidEvent;
hidEvent.value = 0;
if ( info.m_Interface != NULL )
{
result = (*info.m_Interface)->getElementValue( info.m_Interface, ( IOHIDElementCookie )value.m_Cookie, &hidEvent);
if (kIOReturnSuccess == result)
{
if ( hidEvent.value < value.m_MinReport )
value.m_MinReport = hidEvent.value;
if ( hidEvent.value > value.m_MaxReport )
value.m_MaxReport = hidEvent.value;
}
}
return hidEvent.value;
}
int CInputSystem::HIDScaledCalibratedValue( JoystickInfo_t &info, OSXInputValue_t &value )
{
float deviceScale = value.m_MaxVal - value.m_MinVal;
float readScale = value.m_MaxReport - value.m_MinReport;
int joyvalue = HIDGetElementValue( info, value );
if (readScale == 0)
return joyvalue;
else
return ( (joyvalue - value.m_MinReport) * deviceScale / readScale) + value.m_MinVal;
}
//-----------------------------------------------------------------------------
// Purpose: Get raw joystick sample along axis
//-----------------------------------------------------------------------------
unsigned int CInputSystem::AxisValue( JoystickAxis_t axis, JoystickInfo_t &info )
{
switch (axis)
{
case JOY_AXIS_X:
return (unsigned int)HIDScaledCalibratedValue( info, info.m_xaxis );
case JOY_AXIS_Y:
return (unsigned int)HIDScaledCalibratedValue( info, info.m_yaxis );
case JOY_AXIS_Z:
return (unsigned int)HIDScaledCalibratedValue( info, info.m_zaxis );
case JOY_AXIS_R:
return (unsigned int)HIDScaledCalibratedValue( info, info.m_raxis );
case JOY_AXIS_U:
return (unsigned int)HIDScaledCalibratedValue( info, info.m_uaxis );
case JOY_AXIS_V:
return (unsigned int)HIDScaledCalibratedValue( info, info.m_vaxis );
}
// FIX: need to do some kind of error
return (unsigned int)HIDScaledCalibratedValue( info, info.m_xaxis );
}
//-----------------------------------------------------------------------------
// Update the joystick POV control
//-----------------------------------------------------------------------------
void CInputSystem::UpdateJoystickPOVControl( int nJoystick )
{
JoystickInfo_t &info = m_pJoystickInfo[nJoystick];
if ( !info.m_bHasPOVControl )
return;
// convert POV information into 4 bits of state information
// this avoids any potential problems related to moving from one
// direction to another without going through the center position
unsigned int povstate = 0;
int range = ( info.m_POV.m_MaxVal - info.m_POV.m_MinVal + 1 );
int value = HIDGetElementValue( info, info.m_POV ) - info.m_POV.m_MinVal;
if (range == 4) /* 4 position hatswitch - scale up value */
value *= 2;
else if (range != 8) /* Neither a 4 nor 8 positions - fall back to default position (centered) */
value = -1;
switch (value)
{
case 0:
povstate |= 0x01;
break;
case 1:
povstate |= 0x01;
if ( info.m_bDiagonalPOVControlEnabled )
povstate |= 0x02;
break;
case 2:
povstate |= 0x02;
break;
case 3:
povstate |= 0x02;
if ( info.m_bDiagonalPOVControlEnabled )
povstate |= 0x04;
break;
case 4:
povstate |= 0x04;
break;
case 5:
povstate |= 0x04;
if ( info.m_bDiagonalPOVControlEnabled )
povstate |= 0x08;
break;
case 6:
povstate |= 0x08;
break;
case 7:
povstate |= 0x08;
if ( info.m_bDiagonalPOVControlEnabled )
povstate |= 0x01;
break;
default:
povstate = 0;
break;
}
// determine which bits have changed and key an auxillary event for each change
unsigned int buttons = povstate ^ info.m_nLastPolledPOVState;
if ( buttons )
{
for ( int i = 0; i < JOYSTICK_POV_BUTTON_COUNT; ++i )
{
unsigned int mask = buttons & ( 1 << i );
if ( !mask )
continue;
ButtonCode_t code = (ButtonCode_t)JOYSTICK_POV_BUTTON( nJoystick, i );
if ( mask & povstate )
{
// Keydown on POV buttons
JoystickButtonEvent( code, MAX_BUTTONSAMPLE );
}
else
{
// KeyUp on POV buttons
JoystickButtonEvent( code, 0 );
}
}
// Latch old values
info.m_nLastPolledPOVState = povstate;
}
}
//-----------------------------------------------------------------------------
// Purpose: Sample the joystick
//-----------------------------------------------------------------------------
void CInputSystem::PollJoystick( void )
{
if ( !m_JoysticksEnabled.IsAnyFlagSet() )
return;
InputState_t &state = m_InputState[ m_bIsPolling ];
for ( int i = 0; i < m_nJoystickCount; ++i )
{
if ( !m_JoysticksEnabled.IsFlagSet( 1 << i ) )
continue;
JoystickInfo_t &info = m_pJoystickInfo[i];
if ( info.m_bRemoved )
continue;
// Poll joystick axes
for ( int j = 0; j < MAX_JOYSTICK_AXES; ++j )
{
if ( ( info.m_nAxisFlags & ( 1 << j ) ) == 0 )
continue;
AnalogCode_t code = JOYSTICK_AXIS( i, j );
int nValue = AxisValue( (JoystickAxis_t)j, info );
float minValue = joy_axis_deadzone.GetFloat();
switch (j)
{
default:
case 0:
minValue *= info.m_xaxis.m_MaxVal;
break;
case 1:
minValue *= info.m_yaxis.m_MaxVal;
break;
case 2:
minValue *= info.m_zaxis.m_MaxVal;
break;
case 3:
minValue *= info.m_raxis.m_MaxVal;
break;
case 4:
minValue *= info.m_uaxis.m_MaxVal;
break;
case 5:
minValue *= info.m_vaxis.m_MaxVal;
break;
}
if ( fabs(nValue) < minValue )
nValue = 0;
state.m_pAnalogDelta[ code ] = nValue - state.m_pAnalogValue[ code ];
state.m_pAnalogValue[ code ] = nValue;
if ( state.m_pAnalogDelta[ code ] != 0 )
{
//printf( "Joystick %i %d %d\n", code, state.m_pAnalogValue[ code ], state.m_pAnalogDelta[ code ] );
PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, code, state.m_pAnalogValue[ code ], state.m_pAnalogDelta[ code ] );
}
}
UpdateJoystickButtonState( i );
UpdateJoystickPOVControl( i );
}
}
void CInputSystem::SetXDeviceRumble( float fLeftMotor, float fRightMotor, int userId )
{
for ( int i = 0; i < m_nJoystickCount; ++i )
{
JoystickInfo_t &info = m_pJoystickInfo[i];
if ( info.m_bRemoved || !info.m_FFInterface )
continue;
if ( m_bXController && !info.m_bXBoxRumbleEnabled )
{
info.m_bXBoxRumbleEnabled = true;
FFEFFESCAPE escape;
char c;
c=0x01;
escape.dwSize=sizeof(escape);
escape.dwCommand=0x00;
escape.cbInBuffer=sizeof(c);
escape.lpvInBuffer=&c;
escape.cbOutBuffer=0;
escape.lpvOutBuffer=NULL;
FFDeviceEscape( info.m_FFInterface ,&escape );
}
FFEFFESCAPE escape;
char c[2];
c[0]=(unsigned char) (fLeftMotor*255.0);
c[1]=(unsigned char) (fRightMotor*255.0);
escape.dwSize=sizeof(escape);
escape.dwCommand=0x01;
escape.cbInBuffer=sizeof(c);
escape.lpvInBuffer=c;
escape.cbOutBuffer=0;
escape.lpvOutBuffer=NULL;
FFDeviceEscape( info.m_FFInterface ,&escape );
}
}
// [will] BEGIN - These were copied from xcontroller.cpp for X360 button press emulation in OSX.
// We don't want to fully support XController on OSX, just enough to emulate Xbox Controller button presses.
// So instead of #defining out half of xcontroller.cpp for OSX, just copied it here.
#if !defined( _CERT )
#define XBX_MAX_BUTTONSAMPLE 32768
#define XBX_MAX_ANALOGSAMPLE 255
#define XBX_MAX_STICKSAMPLE_LEFT 32768
#define XBX_MAX_STICKSAMPLE_RIGHT 32767
#define XBX_MAX_STICKSAMPLE_DOWN 32768
#define XBX_MAX_STICKSAMPLE_UP 32767
#define XBX_STICK_SCALE_LEFT(x) ( ( float )XBX_MAX_STICKSAMPLE_LEFT/( float )( XBX_MAX_STICKSAMPLE_LEFT-(x) ) )
#define XBX_STICK_SCALE_RIGHT(x) ( ( float )XBX_MAX_STICKSAMPLE_RIGHT/( float )( XBX_MAX_STICKSAMPLE_RIGHT-(x) ) )
#define XBX_STICK_SCALE_DOWN(x) ( ( float )XBX_MAX_STICKSAMPLE_DOWN/( float )( XBX_MAX_STICKSAMPLE_DOWN-(x) ) )
#define XBX_STICK_SCALE_UP(x) ( ( float )XBX_MAX_STICKSAMPLE_UP/( float )( XBX_MAX_STICKSAMPLE_UP-(x) ) )
#define XBX_STICK_SMALL_THRESHOLD ((int)( 0.20f * XBX_MAX_STICKSAMPLE_LEFT ))
// Threshold for counting analog movement as a button press
#define JOYSTICK_ANALOG_BUTTON_THRESHOLD XBX_MAX_STICKSAMPLE_LEFT * 0.4f
//-----------------------------------------------------------------------------
// Purpose: Post Xbox events, ignoring key repeats
//-----------------------------------------------------------------------------
void CInputSystem::PostXKeyEvent( int userId, xKey_t xKey, int nSample )
{
AnalogCode_t code = ANALOG_CODE_LAST;
float value = 0.f;
// Map the physical controller slot to the split screen slot
#if defined( _GAMECONSOLE )
int nMsgSlot = XBX_GetSlotByUserId( userId );
#ifdef _PS3
if ( ( XBX_GetNumGameUsers() <= 1 ) && !ps3_joy_ss.GetBool() )
{
// In PS3 START button identification mode START key notification
// is replaced with INACTIVE_START notification that can identify
// controller that pressed the button
if ( ( xKey == XK_BUTTON_START ) && ( nMsgSlot < 0 )
&& ( ( Plat_FloatTime() - g_ps3_flTimeStartButtonIdentificationMode ) < 0.5f ) )
{
xKey = XK_BUTTON_INACTIVE_START;
nMsgSlot = userId;
}
else
{
// When we don't have splitscreen then any controller can
// play and will be visible as controller #0
nMsgSlot = 0;
}
}
#endif
if ( nMsgSlot < 0 )
{
// special case, that if you press start on a controller we've marked inactive, switch it to an
// XK_BUTTON_INACTIVE_START which you can handle joins from inactive controllers
if ( xKey == XK_BUTTON_START )
{
xKey = XK_BUTTON_INACTIVE_START;
nMsgSlot = userId;
}
else
{
return; // We are not listening to this controller (not signed in and assigned)
}
}
#else //defined( _GAMECONSOLE )
int nMsgSlot = userId;
#endif //defined( _GAMECONSOLE )
int nSampleThreshold = 0;
// Look for changes on the analog axes
switch( xKey )
{
case XK_STICK1_LEFT:
case XK_STICK1_RIGHT:
{
code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_X );
value = ( xKey == XK_STICK1_LEFT ) ? -nSample : nSample;
nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD );
}
break;
case XK_STICK1_UP:
case XK_STICK1_DOWN:
{
code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_Y );
value = ( xKey == XK_STICK1_UP ) ? -nSample : nSample;
nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD );
}
break;
case XK_STICK2_LEFT:
case XK_STICK2_RIGHT:
{
code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_U );
value = ( xKey == XK_STICK2_LEFT ) ? -nSample : nSample;
nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD );
}
break;
case XK_STICK2_UP:
case XK_STICK2_DOWN:
{
code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_R );
value = ( xKey == XK_STICK2_UP ) ? -nSample : nSample;
nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD );
}
break;
}
// Store the analog event
if ( ANALOG_CODE_LAST != code )
{
InputState_t &state = m_InputState[ m_bIsPolling ];
state.m_pAnalogDelta[ code ] = ( int )( value - state.m_pAnalogValue[ code ] );
state.m_pAnalogValue[ code ] = ( int )value;
if ( state.m_pAnalogDelta[ code ] != 0 )
{
PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, code, ( int )value, state.m_pAnalogDelta[ code ] );
}
}
// store the key
m_appXKeys[userId][xKey].sample = nSample;
if ( nSample > nSampleThreshold )
{
m_appXKeys[userId][xKey].repeats++;
}
else
{
m_appXKeys[userId][xKey].repeats = 0;
nSample = 0;
}
if ( m_appXKeys[userId][xKey].repeats > 1 )
{
// application cannot handle streaming keys
// first keypress is the only edge trigger
return;
}
// package the key
ButtonCode_t buttonCode = XKeyToButtonCode( nMsgSlot, xKey );
if ( nSample )
{
PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, buttonCode, buttonCode );
// [dkorus] check whether we're trying to set the current controller
if( ( buttonCode == KEY_XBUTTON_A || buttonCode == XK_BUTTON_START )
&& m_setCurrentInputDeviceOnNextButtonPress )
{
if( IsInputDeviceConnected( INPUT_DEVICE_GAMEPAD ) )
{
SetCurrentInputDevice( INPUT_DEVICE_GAMEPAD );
ConVarRef var( "joystick" );
if( var.IsValid( ) )
var.SetValue( 1 );
m_setCurrentInputDeviceOnNextButtonPress = false;
}
}
}
else
{
PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, buttonCode, buttonCode );
}
}
#endif // _CERT
// [will] END - These were copied from xcontroller.cpp for X360 button press emulation in OSX.