Torque3D/Engine/source/platformWin32/winDInputDevice.cpp
Scott Przybylski 48c64db840 Fix for Issue_190
Fix for Issue_190:
Without having DI8DEVTYPE_DRIVING here, many steering wheel's will show
up as "unknown" and cannot be mapped to actions. Driving and gamepad
both map to joystick for consistency, since they all can have buttons,
axes, sliders, and pov hat's.
2013-01-22 14:44:22 -05:00

1591 lines
53 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// 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, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// 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. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef INITGUID
#define INITGUID
#endif
#include "platform/platform.h"
#include "platformWin32/winDInputDevice.h"
#include "math/mMath.h"
#include "console/console.h"
#include "core/strings/unicode.h"
#include "windowManager/platformWindowMgr.h"
// Static class data:
LPDIRECTINPUT8 DInputDevice::smDInputInterface;
U8 DInputDevice::smDeviceCount[ NUM_INPUT_DEVICE_TYPES ];
bool DInputDevice::smInitialized = false;
#ifdef LOG_INPUT
const char* getKeyName( U16 key );
#endif
//------------------------------------------------------------------------------
DInputDevice::DInputDevice( const DIDEVICEINSTANCE* dii )
{
mDeviceInstance = *dii;
mDevice = NULL;
mAcquired = false;
mNeedSync = false;
mObjInstance = NULL;
mObjFormat = NULL;
mObjInfo = NULL;
mObjBuffer1 = NULL;
mObjBuffer2 = NULL;
mPrevObjBuffer = NULL;
mForceFeedbackEffect = NULL;
mNumForceFeedbackAxes = 0;
mForceFeedbackAxes[0] = 0;
mForceFeedbackAxes[1] = 0;
const char* deviceTypeName = "unknown";
U8 deviceType = UnknownDeviceType;
switch ( GET_DIDEVICE_TYPE( mDeviceInstance.dwDevType ) )
{
// [rene, 12/09/2008] why do we turn a gamepad into a joystick here?
case DI8DEVTYPE_DRIVING:
case DI8DEVTYPE_GAMEPAD:
case DI8DEVTYPE_JOYSTICK:
deviceTypeName = "joystick";
deviceType = JoystickDeviceType;
break;
case DI8DEVTYPE_KEYBOARD:
deviceTypeName = "keyboard";
deviceType = KeyboardDeviceType;
break;
case DI8DEVTYPE_MOUSE:
deviceTypeName = "mouse";
deviceType = MouseDeviceType;
break;
}
mDeviceType = deviceType;
mDeviceID = smDeviceCount[ deviceType ] ++;
dSprintf( mName, 29, "%s%d", deviceTypeName, mDeviceID );
}
//------------------------------------------------------------------------------
DInputDevice::~DInputDevice()
{
destroy();
}
//------------------------------------------------------------------------------
void DInputDevice::init()
{
// Reset all of the static variables:
smDInputInterface = NULL;
dMemset( smDeviceCount, 0, sizeof( smDeviceCount ) );
}
//------------------------------------------------------------------------------
bool DInputDevice::create()
{
HRESULT result;
if ( smDInputInterface )
{
result = smDInputInterface->CreateDevice( mDeviceInstance.guidInstance, &mDevice, NULL );
if ( result == DI_OK )
{
mDeviceCaps.dwSize = sizeof( DIDEVCAPS );
if ( FAILED( mDevice->GetCapabilities( &mDeviceCaps ) ) )
{
Con::errorf( " Failed to get the capabilities of the %s input device.", mName );
#ifdef LOG_INPUT
Input::log( "Failed to get the capabilities of &s!\n", mName );
#endif
return false;
}
#ifdef LOG_INPUT
Input::log( "%s detected, created as %s (%s).\n", mDeviceInstance.tszProductName, mName, ( isPolled() ? "polled" : "asynchronous" ) );
#endif
if ( enumerateObjects() )
{
// Set the device's data format:
DIDATAFORMAT dataFormat;
dMemset( &dataFormat, 0, sizeof( DIDATAFORMAT ) );
dataFormat.dwSize = sizeof( DIDATAFORMAT );
dataFormat.dwObjSize = sizeof( DIOBJECTDATAFORMAT );
dataFormat.dwFlags = ( mDeviceType == MouseDeviceType ) ? DIDF_RELAXIS : DIDF_ABSAXIS;
dataFormat.dwDataSize = mObjBufferSize;
dataFormat.dwNumObjs = mObjCount;
dataFormat.rgodf = mObjFormat;
result = mDevice->SetDataFormat( &dataFormat );
if ( FAILED( result ) )
{
Con::errorf( " Failed to set the data format for the %s input device.", mName );
#ifdef LOG_INPUT
Input::log( "Failed to set the data format for %s!\n", mName );
#endif
return false;
}
// Set up the data buffer for buffered input:
DIPROPDWORD prop;
prop.diph.dwSize = sizeof( DIPROPDWORD );
prop.diph.dwHeaderSize = sizeof( DIPROPHEADER );
prop.diph.dwObj = 0;
prop.diph.dwHow = DIPH_DEVICE;
if ( isPolled() )
prop.dwData = mObjBufferSize;
else
prop.dwData = QUEUED_BUFFER_SIZE;
result = mDevice->SetProperty( DIPROP_BUFFERSIZE, &prop.diph );
if ( FAILED( result ) )
{
Con::errorf( " Failed to set the buffer size property for the %s input device.", mName );
#ifdef LOG_INPUT
Input::log( "Failed to set the buffer size property for %s!\n", mName );
#endif
return false;
}
// If this device is a mouse, set it to relative axis mode:
if ( mDeviceType == MouseDeviceType )
{
prop.diph.dwObj = 0;
prop.diph.dwHow = DIPH_DEVICE;
prop.dwData = DIPROPAXISMODE_REL;
result = mDevice->SetProperty( DIPROP_AXISMODE, &prop.diph );
if ( FAILED( result ) )
{
Con::errorf( " Failed to set relative axis mode for the %s input device.", mName );
#ifdef LOG_INPUT
Input::log( "Failed to set relative axis mode for %s!\n", mName );
#endif
return false;
}
}
}
}
else
{
#ifdef LOG_INPUT
switch ( result )
{
case DIERR_DEVICENOTREG:
Input::log( "CreateDevice failed -- The device or device instance is not registered with DirectInput.\n" );
break;
case DIERR_INVALIDPARAM:
Input::log( "CreateDevice failed -- Invalid parameter.\n" );
break;
case DIERR_NOINTERFACE:
Input::log( "CreateDevice failed -- The specified interface is not supported by the object.\n" );
break;
case DIERR_OUTOFMEMORY:
Input::log( "CreateDevice failed -- Out of memory.\n" );
break;
default:
Input::log( "CreateDevice failed -- Unknown error.\n" );
break;
};
#endif // LOG_INPUT
Con::printf( " CreateDevice failed for the %s input device!", mName );
return false;
}
}
Con::printf( " %s input device created.", mName );
return true;
}
//------------------------------------------------------------------------------
void DInputDevice::destroy()
{
if ( mDevice )
{
unacquire();
// Tear down our forcefeedback.
if (mForceFeedbackEffect)
{
mForceFeedbackEffect->Release();
mForceFeedbackEffect = NULL;
mNumForceFeedbackAxes = 0;
#ifdef LOG_INPUT
Input::log("DInputDevice::destroy - releasing constant force feeback effect\n");
#endif
}
mDevice->Release();
mDevice = NULL;
delete [] mObjInstance;
delete [] mObjFormat;
delete [] mObjInfo;
delete [] mObjBuffer1;
delete [] mObjBuffer2;
mObjInstance = NULL;
mObjFormat = NULL;
mObjInfo = NULL;
mObjBuffer1 = NULL;
mObjBuffer2 = NULL;
mPrevObjBuffer = NULL;
mName[0] = 0;
}
}
//------------------------------------------------------------------------------
bool DInputDevice::acquire()
{
if ( mDevice )
{
if ( mAcquired )
return( true );
bool result = false;
// Set the cooperative level:
// (do this here so that we have a valid app window)
DWORD coopLevel = DISCL_BACKGROUND;
if ( mDeviceType == JoystickDeviceType
// #ifndef DEBUG
// || mDeviceType == MouseDeviceType
// #endif
)
// Exclusive access is required in order to perform force feedback
coopLevel = DISCL_EXCLUSIVE | DISCL_FOREGROUND;
else
coopLevel |= DISCL_NONEXCLUSIVE;
result = mDevice->SetCooperativeLevel( getWin32WindowHandle(), coopLevel );
if ( FAILED( result ) )
{
Con::errorf( "Failed to set the cooperative level for the %s input device.", mName );
#ifdef LOG_INPUT
Input::log( "Failed to set the cooperative level for %s!\n", mName );
#endif
return false;
}
// Enumerate joystick axes to enable force feedback
if ( NULL == mForceFeedbackEffect && JoystickDeviceType == mDeviceType)
{
// Since we will be playing force feedback effects, we should disable the auto-centering spring.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = FALSE;
if( FAILED( result = mDevice->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph ) ) )
return false;
}
S32 code = mDevice->Acquire();
result = SUCCEEDED( code );
if ( result )
{
Con::printf( "%s input device acquired.", mName );
#ifdef LOG_INPUT
Input::log( "%s acquired.\n", mName );
#endif
mAcquired = true;
// If we were previously playing a force feedback effect, before
// losing acquisition, we do not automatically restart it. This is
// where you could call mForceFeedbackEffect->Start( INFINITE, 0 );
// if you want that behavior.
// Update all of the key states:
if ( !isPolled() )
mNeedSync = true;
}
else
{
const char* reason;
switch ( code )
{
case DIERR_INVALIDPARAM: reason = "Invalid parameter" ; break;
case DIERR_NOTINITIALIZED: reason = "Not initialized"; break;
case DIERR_OTHERAPPHASPRIO: reason = "Other app has priority"; break;
case S_FALSE: reason = "Already acquired"; break;
default: reason = "Unknown error"; break;
}
Con::warnf( "%s input device NOT acquired: %s", mName, reason );
#ifdef LOG_INPUT
Input::log( "Failed to acquire %s: %s\n", mName, reason );
#endif
}
return( result );
}
return( false );
}
//------------------------------------------------------------------------------
bool DInputDevice::unacquire()
{
if ( mDevice )
{
if ( !mAcquired )
return( true );
bool result = false;
result = SUCCEEDED( mDevice->Unacquire() );
if ( result )
{
Con::printf( "%s input device unacquired.", mName );
#ifdef LOG_INPUT
Input::log( "%s unacquired.\n", mName );
#endif
mAcquired = false;
}
else
{
Con::warnf( ConsoleLogEntry::General, "%s input device NOT unacquired.", mName );
#ifdef LOG_INPUT
Input::log( "Failed to unacquire %s!\n", mName );
#endif
}
return( result );
}
return( false );
}
//------------------------------------------------------------------------------
BOOL CALLBACK DInputDevice::EnumObjectsProc( const DIDEVICEOBJECTINSTANCE* doi, LPVOID pvRef )
{
// Don't enumerate unknown types:
if ( doi->guidType == GUID_Unknown )
return (DIENUM_CONTINUE);
// Reduce a couple pointers:
DInputDevice* diDevice = (DInputDevice*) pvRef;
DIDEVICEOBJECTINSTANCE* objInstance = &diDevice->mObjInstance[diDevice->mObjEnumCount];
DIOBJECTDATAFORMAT* objFormat = &diDevice->mObjFormat[diDevice->mObjEnumCount];
// Fill in the object instance structure:
*objInstance = *doi;
// DWORD objects must be DWORD aligned:
if ( !(objInstance->dwType & DIDFT_BUTTON ) )
diDevice->mObjBufferOfs = ( diDevice->mObjBufferOfs + 3 ) & ~3;
objInstance->dwOfs = diDevice->mObjBufferOfs;
// Fill in the object data format structure:
objFormat->pguid = &objInstance->guidType;
objFormat->dwType = objInstance->dwType;
objFormat->dwFlags= 0;
objFormat->dwOfs = diDevice->mObjBufferOfs;
// Advance the enumeration counters:
if ( objFormat->dwType & DIDFT_BUTTON )
diDevice->mObjBufferOfs += SIZEOF_BUTTON;
else
diDevice->mObjBufferOfs += SIZEOF_AXIS;
diDevice->mObjEnumCount++;
return (DIENUM_CONTINUE);
}
//------------------------------------------------------------------------------
bool DInputDevice::enumerateObjects()
{
if ( !mDevice )
return false;
// Calculate the needed buffer sizes and allocate them:
mObjCount = ( mDeviceCaps.dwAxes + mDeviceCaps.dwButtons + mDeviceCaps.dwPOVs );
mObjBufferSize = mObjCount * sizeof( DWORD );
mObjInstance = new DIDEVICEOBJECTINSTANCE[mObjCount];
mObjFormat = new DIOBJECTDATAFORMAT[mObjCount];
mObjInfo = new ObjInfo[mObjCount];
if ( isPolled() )
{
mObjBuffer1 = new U8[mObjBufferSize];
dMemset( mObjBuffer1, 0, mObjBufferSize );
mObjBuffer2 = new U8[mObjBufferSize];
dMemset( mObjBuffer2, 0, mObjBufferSize );
}
mObjEnumCount = 0;
mObjBufferOfs = 0;
// We are about to enumerate, clear the axes we claim to know about
mNumForceFeedbackAxes = 0;
// Enumerate all of the 'objects' detected on the device:
if ( FAILED( mDevice->EnumObjects( EnumObjectsProc, this, DIDFT_ALL ) ) )
return false;
// We only supports one or two axis joysticks
if( mNumForceFeedbackAxes > 2 )
mNumForceFeedbackAxes = 2;
// if we enumerated fewer objects than are supposedly available, reset the
// object count
if (mObjEnumCount < mObjCount)
mObjCount = mObjEnumCount;
mObjBufferSize = ( mObjBufferSize + 3 ) & ~3; // Fill in the actual size to nearest DWORD
U32 buttonCount = 0;
U32 povCount = 0;
U32 keyCount = 0;
U32 unknownCount = 0;
// Fill in the device object's info structure:
for ( U32 i = 0; i < mObjCount; i++ )
{
if ( mObjInstance[i].guidType == GUID_Button )
{
mObjInfo[i].mType = SI_BUTTON;
mObjInfo[i].mInst = (InputObjectInstances)(KEY_BUTTON0 + buttonCount++);
}
else if ( mObjInstance[i].guidType == GUID_POV )
{
// This is actually intentional - the POV handling code lower down
// takes the instance number and converts everything to button events.
mObjInfo[i].mType = SI_POV;
mObjInfo[i].mInst = (InputObjectInstances)povCount++;
}
else if ( mObjInstance[i].guidType == GUID_XAxis )
{
mObjInfo[i].mType = SI_AXIS;
mObjInfo[i].mInst = SI_XAXIS;
if (mObjInstance[i].dwFFMaxForce > 0)
mForceFeedbackAxes[mNumForceFeedbackAxes++] = mObjInstance[i].dwOfs;
}
else if ( mObjInstance[i].guidType == GUID_YAxis )
{
mObjInfo[i].mType = SI_AXIS;
mObjInfo[i].mInst = SI_YAXIS;
if (mObjInstance[i].dwFFMaxForce > 0)
mForceFeedbackAxes[mNumForceFeedbackAxes++] = mObjInstance[i].dwOfs;
}
else if ( mObjInstance[i].guidType == GUID_ZAxis )
{
mObjInfo[i].mType = SI_AXIS;
mObjInfo[i].mInst = SI_ZAXIS;
}
else if ( mObjInstance[i].guidType == GUID_RxAxis )
{
mObjInfo[i].mType = SI_AXIS;
mObjInfo[i].mInst = SI_RXAXIS;
}
else if ( mObjInstance[i].guidType == GUID_RyAxis )
{
mObjInfo[i].mType = SI_AXIS;
mObjInfo[i].mInst = SI_RYAXIS;
}
else if ( mObjInstance[i].guidType == GUID_RzAxis )
{
mObjInfo[i].mType = SI_AXIS;
mObjInfo[i].mInst = SI_RZAXIS;
}
else if ( mObjInstance[i].guidType == GUID_Slider )
{
mObjInfo[i].mType = SI_AXIS;
mObjInfo[i].mInst = SI_SLIDER;
}
else if ( mObjInstance[i].guidType == GUID_Key )
{
mObjInfo[i].mType = SI_KEY;
mObjInfo[i].mInst = DIK_to_Key( DIDFT_GETINSTANCE( mObjFormat[i].dwType ) );
keyCount++;
}
else
{
mObjInfo[i].mType = SI_UNKNOWN;
mObjInfo[i].mInst = (InputObjectInstances)unknownCount++;
}
// Set the device object's min and max values:
if ( mObjInstance[i].guidType == GUID_Button
|| mObjInstance[i].guidType == GUID_Key
|| mObjInstance[i].guidType == GUID_POV )
{
mObjInfo[i].mMin = DIPROPRANGE_NOMIN;
mObjInfo[i].mMax = DIPROPRANGE_NOMAX;
}
else
{
// This is an axis or a slider, so find out its range:
DIPROPRANGE pr;
pr.diph.dwSize = sizeof( pr );
pr.diph.dwHeaderSize = sizeof( pr.diph );
pr.diph.dwHow = DIPH_BYID;
pr.diph.dwObj = mObjFormat[i].dwType;
if ( SUCCEEDED( mDevice->GetProperty( DIPROP_RANGE, &pr.diph ) ) )
{
mObjInfo[i].mMin = pr.lMin;
mObjInfo[i].mMax = pr.lMax;
}
else
{
mObjInfo[i].mMin = DIPROPRANGE_NOMIN;
mObjInfo[i].mMax = DIPROPRANGE_NOMAX;
}
}
}
#ifdef LOG_INPUT
Input::log( " %d total objects detected.\n", mObjCount );
if ( buttonCount )
Input::log( " %d buttons.\n", buttonCount );
if ( povCount )
Input::log( " %d POVs.\n", povCount );
if ( keyCount )
Input::log( " %d keys.\n", keyCount );
if ( unknownCount )
Input::log( " %d unknown objects.\n", unknownCount );
Input::log( "\n" );
#endif
return true;
}
//------------------------------------------------------------------------------
const char* DInputDevice::getName()
{
#ifdef UNICODE
static UTF8 buf[512];
convertUTF16toUTF8(mDeviceInstance.tszInstanceName, buf, sizeof(buf));
return (const char *)buf;
#else
return mDeviceInstance.tszInstanceName;
#endif
}
//------------------------------------------------------------------------------
const char* DInputDevice::getProductName()
{
#ifdef UNICODE
static UTF8 buf[512];
convertUTF16toUTF8(mDeviceInstance.tszProductName, buf, sizeof(buf));
return (const char *)buf;
#else
return mDeviceInstance.tszProductName;
#endif
}
//------------------------------------------------------------------------------
bool DInputDevice::process()
{
if ( mAcquired )
{
if ( isPolled() )
return processImmediate();
else
return processAsync();
}
return false;
}
//------------------------------------------------------------------------------
bool DInputDevice::processAsync()
{
DIDEVICEOBJECTDATA eventBuffer[QUEUED_BUFFER_SIZE];
DWORD numEvents = QUEUED_BUFFER_SIZE;
HRESULT result;
if ( !mDevice )
return false;
do
{
result = mDevice->GetDeviceData( sizeof( DIDEVICEOBJECTDATA ), eventBuffer, &numEvents, 0 );
if ( !SUCCEEDED( result ) )
{
switch ( result )
{
case DIERR_INPUTLOST:
// Data stream was interrupted, so try to reacquire the device:
mAcquired = false;
acquire();
break;
case DIERR_INVALIDPARAM:
Con::errorf( "DInputDevice::processAsync -- Invalid parameter passed to GetDeviceData of the %s input device!", mName );
#ifdef LOG_INPUT
Input::log( "Invalid parameter passed to GetDeviceData for %s!\n", mName );
#endif
break;
case DIERR_NOTACQUIRED:
// We can't get the device, so quit:
mAcquired = false;
// Don't error out - this is actually a natural occurrence...
//Con::errorf( "DInputDevice::processAsync -- GetDeviceData called when %s input device is not acquired!", mName );
#ifdef LOG_INPUT
Input::log( "GetDeviceData called when %s is not acquired!\n", mName );
#endif
break;
}
return false;
}
// We have buffered input, so act on it:
for ( DWORD i = 0; i < numEvents; i++ )
buildEvent( findObjInstance( eventBuffer[i].dwOfs ), eventBuffer[i].dwData, eventBuffer[i].dwData );
// Check for buffer overflow:
if ( result == DI_BUFFEROVERFLOW )
{
// This is a problem, but we can keep going...
Con::errorf( "DInputDevice::processAsync -- %s input device's event buffer overflowed!", mName );
#ifdef LOG_INPUT
Input::log( "%s event buffer overflowed!\n", mName );
#endif
mNeedSync = true; // Let it know to resync next time through...
}
}
while ( numEvents );
return true;
}
//------------------------------------------------------------------------------
bool DInputDevice::processImmediate()
{
if ( !mDevice )
return false;
mDevice->Poll();
U8* buffer = ( mPrevObjBuffer == mObjBuffer1 ) ? mObjBuffer2 : mObjBuffer1;
HRESULT result = mDevice->GetDeviceState( mObjBufferSize, buffer );
if ( !SUCCEEDED( result ) )
{
switch ( result )
{
case DIERR_INPUTLOST:
// Data stream was interrupted, so try to reacquire the device:
mAcquired = false;
acquire();
break;
case DIERR_INVALIDPARAM:
Con::errorf( "DInputDevice::processPolled -- invalid parameter passed to GetDeviceState on %s input device!", mName );
#ifdef LOG_INPUT
Input::log( "Invalid parameter passed to GetDeviceState on %s.\n", mName );
#endif
break;
case DIERR_NOTACQUIRED:
Con::errorf( "DInputDevice::processPolled -- GetDeviceState called when %s input device is not acquired!", mName );
#ifdef LOG_INPUT
Input::log( "GetDeviceState called when %s is not acquired!\n", mName );
#endif
break;
case E_PENDING:
Con::warnf( "DInputDevice::processPolled -- Data not yet available for the %s input device!", mName );
#ifdef LOG_INPUT
Input::log( "Data pending for %s.", mName );
#endif
break;
}
return false;
}
// Loop through all of the objects and produce events where
// the states have changed:
// (oldData = 0 prevents a crashing bug in Torque. There is a case where
// Torque accessed oldData without it ever being set.)
S32 newData, oldData = 0;
for ( DWORD i = 0; i < mObjCount; i++ )
{
if ( mObjFormat[i].dwType & DIDFT_BUTTON )
{
if ( mPrevObjBuffer )
{
newData = *( (U8*) ( buffer + mObjFormat[i].dwOfs ) );
oldData = *( (U8*) ( mPrevObjBuffer + mObjFormat[i].dwOfs ) );
if ( newData == oldData )
continue;
}
else
continue;
}
else if ( mObjFormat[i].dwType & DIDFT_POV )
{
if ( mPrevObjBuffer )
{
newData = *( (S32*) ( buffer + mObjFormat[i].dwOfs ) );
oldData = *( (S32*) ( mPrevObjBuffer + mObjFormat[i].dwOfs ) );
if ( LOWORD( newData ) == LOWORD( oldData ) )
continue;
}
else
continue;
}
else
{
// report normal axes every time through the loop:
newData = *( (S32*) ( buffer + mObjFormat[i].dwOfs ) );
}
// Build an event:
buildEvent( i, newData, oldData );
}
mPrevObjBuffer = buffer;
return true;
}
//------------------------------------------------------------------------------
DWORD DInputDevice::findObjInstance( DWORD offset )
{
DIDEVICEOBJECTINSTANCE *inst = mObjInstance;
for ( U32 i = 0; i < mObjCount; i++, inst++ )
{
if ( inst->dwOfs == offset )
return i;
}
AssertFatal( false, "DInputDevice::findObjInstance -- failed to locate object instance." );
return 0;
}
//------------------------------------------------------------------------------
enum Win32POVDirBits
{
POV_up = 1 << 0,
POV_right = 1 << 1,
POV_down = 1 << 2,
POV_left = 1 << 3,
};
enum Win32POVDirsInQuadrant
{
POVq_center = 0,
POVq_up = POV_up,
POVq_upright = POV_up | POV_right,
POVq_right = POV_right,
POVq_downright = POV_down | POV_right,
POVq_down = POV_down,
POVq_downleft = POV_down | POV_left,
POVq_left = POV_left,
POVq_upleft = POV_up | POV_left,
};
static const U32 Win32POVQuadrantMap[] =
{
POVq_up, POVq_upright,
POVq_right, POVq_downright,
POVq_down, POVq_downleft,
POVq_left, POVq_upleft
};
static U32 _Win32GetPOVDirs(U32 data)
{
U32 quadrant = (data / 4500) % 8;
U32 dirs = (data == 0xffff) ? POVq_center : Win32POVQuadrantMap[quadrant];
return dirs;
}
#if defined(LOG_INPUT)
static void _Win32LogPOVInput(InputEventInfo &newEvent)
{
U32 inst = 0xffff;
const char* sstate = ( newEvent.action == SI_MAKE ) ? "pressed" : "released";
const char* dir = "";
switch( newEvent.objInst )
{
case SI_UPOV:
case SI_UPOV2:
dir = "Up"; inst = (newEvent.objInst == SI_UPOV) ? 1 : 2; break;
case SI_RPOV:
case SI_RPOV2:
dir = "Right"; inst = (newEvent.objInst == SI_RPOV) ? 1 : 2; break;
case SI_DPOV:
case SI_DPOV2:
dir = "Down"; inst = (newEvent.objInst == SI_DPOV) ? 1 : 2; break;
case SI_LPOV:
case SI_LPOV2:
dir = "Left"; inst = (newEvent.objInst == SI_LPOV) ? 1 : 2; break;
}
Con::printf( "EVENT (DInput): %s POV %d %s.\n", dir, inst, sstate);
}
#else
#define _Win32LogPOVInput( a )
#endif
//------------------------------------------------------------------------------
bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
{
ObjInfo &objInfo = mObjInfo[offset];
if ( objInfo.mType == SI_UNKNOWN )
return false;
InputEventInfo newEvent;
newEvent.deviceType = (InputDeviceTypes)mDeviceType;
newEvent.deviceInst = mDeviceID;
newEvent.objType = objInfo.mType;
newEvent.objInst = objInfo.mInst;
newEvent.modifier = (InputModifiers)0;
switch ( newEvent.objType )
{
case SI_AXIS:
newEvent.action = SI_MOVE;
if ( newEvent.deviceType == MouseDeviceType )
{
newEvent.fValue = float( newData );
#ifdef LOG_INPUT
#ifdef LOG_MOUSEMOVE
if ( newEvent.objInst == SI_XAXIS )
Input::log( "EVENT (DInput): %s move (%.1f, 0.0).\n", mName, newEvent.fValue );
else if ( newEvent.objInst == SI_YAXIS )
Input::log( "EVENT (DInput): %s move (0.0, %.1f).\n", mName, newEvent.fValue );
else
#endif
if ( newEvent.objInst == SI_ZAXIS )
Input::log( "EVENT (DInput): %s wheel move %.1f.\n", mName, newEvent.fValue );
#endif
}
else // Joystick or other device:
{
// Scale to the range -1.0 to 1.0:
if ( objInfo.mMin != DIPROPRANGE_NOMIN && objInfo.mMax != DIPROPRANGE_NOMAX )
{
float range = float( objInfo.mMax - objInfo.mMin );
newEvent.fValue = float( ( 2 * newData ) - objInfo.mMax - objInfo.mMin ) / range;
}
else
newEvent.fValue = (F32)newData;
#ifdef LOG_INPUT
// Keep this commented unless you REALLY want these messages for something--
// they come once per each iteration of the main game loop.
//switch ( newEvent.objType )
//{
//case SI_XAXIS:
//if ( newEvent.fValue < -0.01f || newEvent.fValue > 0.01f )
//Input::log( "EVENT (DInput): %s X-axis move %.2f.\n", mName, newEvent.fValue );
//break;
//case SI_YAXIS:
//if ( newEvent.fValue < -0.01f || newEvent.fValue > 0.01f )
//Input::log( "EVENT (DInput): %s Y-axis move %.2f.\n", mName, newEvent.fValue );
//break;
//case SI_ZAXIS:
//Input::log( "EVENT (DInput): %s Z-axis move %.1f.\n", mName, newEvent.fValue );
//break;
//case SI_RXAXIS:
//Input::log( "EVENT (DInput): %s R-axis move %.1f.\n", mName, newEvent.fValue );
//break;
//case SI_RYAXIS:
//Input::log( "EVENT (DInput): %s U-axis move %.1f.\n", mName, newEvent.fValue );
//break;
//case SI_RZAXIS:
//Input::log( "EVENT (DInput): %s V-axis move %.1f.\n", mName, newEvent.fValue );
//break;
//case SI_SLIDER:
//Input::log( "EVENT (DInput): %s slider move %.1f.\n", mName, newEvent.fValue );
//break;
//};
#endif
}
newEvent.postToSignal(Input::smInputEvent);
break;
case SI_BUTTON:
newEvent.action = ( newData & 0x80 ) ? SI_MAKE : SI_BREAK;
newEvent.fValue = ( newEvent.action == SI_MAKE ) ? 1.0f : 0.0f;
#ifdef LOG_INPUT
if ( newEvent.action == SI_MAKE )
Input::log( "EVENT (DInput): %s button%d pressed.\n", mName, newEvent.objInst - KEY_BUTTON0 );
else
Input::log( "EVENT (DInput): %s button%d released.\n", mName, newEvent.objInst - KEY_BUTTON0 );
#endif
newEvent.postToSignal(Input::smInputEvent);
break;
case SI_POV:
// Handle artificial POV up/down/left/right buttons
// If we're not a polling device, oldData and newData are the same, so "fake" transitions
if(!isPolled()) {
oldData = mPrevPOVPos;
mPrevPOVPos = newData;
}
newData = LOWORD( newData );
oldData = LOWORD( oldData );
newData = _Win32GetPOVDirs(newData);
oldData = _Win32GetPOVDirs(oldData);
U32 setkeys = newData & (~oldData);
U32 clearkeys = oldData & (~newData);
U32 objInst = newEvent.objInst;
if ( setkeys || clearkeys )
{
if ( clearkeys )
{
newEvent.action = SI_BREAK;
newEvent.fValue = 0.0f;
// post events for all buttons that need to be cleared.
if( clearkeys & POV_up)
{
newEvent.objInst = ( objInst == 0 ) ? SI_UPOV : SI_UPOV2;
_Win32LogPOVInput(newEvent);
newEvent.postToSignal(Input::smInputEvent);
}
if( clearkeys & POV_right)
{
newEvent.objInst = ( objInst == 0 ) ? SI_RPOV : SI_RPOV2;
_Win32LogPOVInput(newEvent);
newEvent.postToSignal(Input::smInputEvent);
}
if( clearkeys & POV_down)
{
newEvent.objInst = ( objInst == 0 ) ? SI_DPOV : SI_DPOV;
_Win32LogPOVInput(newEvent);
newEvent.postToSignal(Input::smInputEvent);
}
if( clearkeys & POV_left)
{
newEvent.objInst = ( objInst == 0 ) ? SI_LPOV : SI_LPOV2;
_Win32LogPOVInput(newEvent);
newEvent.postToSignal(Input::smInputEvent);
}
} // clear keys
if ( setkeys )
{
newEvent.action = SI_MAKE;
newEvent.fValue = 1.0f;
// post events for all buttons that need to be set.
if( setkeys & POV_up)
{
newEvent.objInst = ( objInst == 0 ) ? SI_UPOV : SI_UPOV2;
_Win32LogPOVInput(newEvent);
newEvent.postToSignal(Input::smInputEvent);
}
if( setkeys & POV_right)
{
newEvent.objInst = ( objInst == 0 ) ? SI_RPOV : SI_RPOV2;
_Win32LogPOVInput(newEvent);
newEvent.postToSignal(Input::smInputEvent);
}
if( setkeys & POV_down)
{
newEvent.objInst = ( objInst == 0 ) ? SI_DPOV : SI_DPOV;
_Win32LogPOVInput(newEvent);
newEvent.postToSignal(Input::smInputEvent);
}
if( setkeys & POV_left)
{
newEvent.objInst = ( objInst == 0 ) ? SI_LPOV : SI_LPOV2;
_Win32LogPOVInput(newEvent);
newEvent.postToSignal(Input::smInputEvent);
}
} // set keys
}
break;
}
return true;
}
void DInputDevice::rumble(float x, float y)
{
LONG rglDirection[2] = { 0, 0 };
DICONSTANTFORCE cf = { 0 };
HRESULT result;
// Now set the new parameters and start the effect immediately.
if (!mForceFeedbackEffect)
{
#ifdef LOG_INPUT
Input::log("DInputDevice::rumbleJoystick - creating constant force feeback effect\n");
#endif
DIEFFECT eff;
ZeroMemory( &eff, sizeof(eff) );
eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.dwDuration = INFINITE;
eff.dwSamplePeriod = 0;
eff.dwGain = DI_FFNOMINALMAX;
eff.dwTriggerButton = DIEB_NOTRIGGER;
eff.dwTriggerRepeatInterval = 0;
eff.cAxes = mNumForceFeedbackAxes;
eff.rgdwAxes = mForceFeedbackAxes;
eff.rglDirection = rglDirection;
eff.lpEnvelope = 0;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &cf;
eff.dwStartDelay = 0;
// Create the prepared effect
if ( FAILED( result = mDevice->CreateEffect( GUID_ConstantForce, &eff, &mForceFeedbackEffect, NULL ) ) )
{
#ifdef LOG_INPUT
Input::log( "DInputDevice::rumbleJoystick - %s does not support force feedback.\n", mName );
#endif
Con::errorf( "DInputDevice::rumbleJoystick - %s does not support force feedback.\n", mName );
return;
}
else
{
#ifdef LOG_INPUT
Input::log( "DInputDevice::rumbleJoystick - %s supports force feedback.\n", mName );
#endif
Con::printf( "DInputDevice::rumbleJoystick - %s supports force feedback.\n", mName );
}
}
// Clamp the input floats to [0 - 1]
x = max(0, min(1, x));
y = max(0, min(1, y));
if ( 1 == mNumForceFeedbackAxes )
{
cf.lMagnitude = (DWORD)( x * DI_FFNOMINALMAX );
}
else
{
rglDirection[0] = (DWORD)( x * DI_FFNOMINALMAX );
rglDirection[1] = (DWORD)( y * DI_FFNOMINALMAX );
cf.lMagnitude = (DWORD)sqrt( (double)(x * x * DI_FFNOMINALMAX * DI_FFNOMINALMAX + y * y * DI_FFNOMINALMAX * DI_FFNOMINALMAX) );
}
DIEFFECT eff;
ZeroMemory( &eff, sizeof(eff) );
eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.dwDuration = INFINITE;
eff.dwSamplePeriod = 0;
eff.dwGain = DI_FFNOMINALMAX;
eff.dwTriggerButton = DIEB_NOTRIGGER;
eff.dwTriggerRepeatInterval = 0;
eff.cAxes = mNumForceFeedbackAxes;
eff.rglDirection = rglDirection;
eff.lpEnvelope = 0;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &cf;
eff.dwStartDelay = 0;
if ( FAILED( result = mForceFeedbackEffect->SetParameters( &eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START ) ) )
{
const char* errorString = NULL;
switch ( result )
{
case DIERR_INPUTLOST:
errorString = "DIERR_INPUTLOST";
break;
case DIERR_INVALIDPARAM:
errorString = "DIERR_INVALIDPARAM";
break;
case DIERR_NOTACQUIRED:
errorString = "DIERR_NOTACQUIRED";
break;
default:
errorString = "Unknown Error";
}
#ifdef LOG_INPUT
Input::log( "DInputDevice::rumbleJoystick - %s - Failed to start rumble effect\n", errorString );
#endif
Con::errorf( "DInputDevice::rumbleJoystick - %s - Failed to start rumble effect\n", errorString );
}
}
//------------------------------------------------------------------------------
//
// This function translates the DirectInput scan code to the associated
// internal key code (as defined in event.h).
//
//------------------------------------------------------------------------------
InputObjectInstances DIK_to_Key( U8 dikCode )
{
switch ( dikCode )
{
case DIK_ESCAPE: return KEY_ESCAPE;
case DIK_1: return KEY_1;
case DIK_2: return KEY_2;
case DIK_3: return KEY_3;
case DIK_4: return KEY_4;
case DIK_5: return KEY_5;
case DIK_6: return KEY_6;
case DIK_7: return KEY_7;
case DIK_8: return KEY_8;
case DIK_9: return KEY_9;
case DIK_0: return KEY_0;
case DIK_MINUS: return KEY_MINUS;
case DIK_EQUALS: return KEY_EQUALS;
case DIK_BACK: return KEY_BACKSPACE;
case DIK_TAB: return KEY_TAB;
case DIK_Q: return KEY_Q;
case DIK_W: return KEY_W;
case DIK_E: return KEY_E;
case DIK_R: return KEY_R;
case DIK_T: return KEY_T;
case DIK_Y: return KEY_Y;
case DIK_U: return KEY_U;
case DIK_I: return KEY_I;
case DIK_O: return KEY_O;
case DIK_P: return KEY_P;
case DIK_LBRACKET: return KEY_LBRACKET;
case DIK_RBRACKET: return KEY_RBRACKET;
case DIK_RETURN: return KEY_RETURN;
case DIK_LCONTROL: return KEY_LCONTROL;
case DIK_A: return KEY_A;
case DIK_S: return KEY_S;
case DIK_D: return KEY_D;
case DIK_F: return KEY_F;
case DIK_G: return KEY_G;
case DIK_H: return KEY_H;
case DIK_J: return KEY_J;
case DIK_K: return KEY_K;
case DIK_L: return KEY_L;
case DIK_SEMICOLON: return KEY_SEMICOLON;
case DIK_APOSTROPHE: return KEY_APOSTROPHE;
case DIK_GRAVE: return KEY_TILDE;
case DIK_LSHIFT: return KEY_LSHIFT;
case DIK_BACKSLASH: return KEY_BACKSLASH;
case DIK_Z: return KEY_Z;
case DIK_X: return KEY_X;
case DIK_C: return KEY_C;
case DIK_V: return KEY_V;
case DIK_B: return KEY_B;
case DIK_N: return KEY_N;
case DIK_M: return KEY_M;
case DIK_COMMA: return KEY_COMMA;
case DIK_PERIOD: return KEY_PERIOD;
case DIK_SLASH: return KEY_SLASH;
case DIK_RSHIFT: return KEY_RSHIFT;
case DIK_MULTIPLY: return KEY_MULTIPLY;
case DIK_LALT: return KEY_LALT;
case DIK_SPACE: return KEY_SPACE;
case DIK_CAPSLOCK: return KEY_CAPSLOCK;
case DIK_F1: return KEY_F1;
case DIK_F2: return KEY_F2;
case DIK_F3: return KEY_F3;
case DIK_F4: return KEY_F4;
case DIK_F5: return KEY_F5;
case DIK_F6: return KEY_F6;
case DIK_F7: return KEY_F7;
case DIK_F8: return KEY_F8;
case DIK_F9: return KEY_F9;
case DIK_F10: return KEY_F10;
case DIK_NUMLOCK: return KEY_NUMLOCK;
case DIK_SCROLL: return KEY_SCROLLLOCK;
case DIK_NUMPAD7: return KEY_NUMPAD7;
case DIK_NUMPAD8: return KEY_NUMPAD8;
case DIK_NUMPAD9: return KEY_NUMPAD9;
case DIK_SUBTRACT: return KEY_SUBTRACT;
case DIK_NUMPAD4: return KEY_NUMPAD4;
case DIK_NUMPAD5: return KEY_NUMPAD5;
case DIK_NUMPAD6: return KEY_NUMPAD6;
case DIK_ADD: return KEY_ADD;
case DIK_NUMPAD1: return KEY_NUMPAD1;
case DIK_NUMPAD2: return KEY_NUMPAD2;
case DIK_NUMPAD3: return KEY_NUMPAD3;
case DIK_NUMPAD0: return KEY_NUMPAD0;
case DIK_DECIMAL: return KEY_DECIMAL;
case DIK_F11: return KEY_F11;
case DIK_F12: return KEY_F12;
case DIK_F13: return KEY_F13;
case DIK_F14: return KEY_F14;
case DIK_F15: return KEY_F15;
case DIK_KANA: return KEY_NULL;
case DIK_CONVERT: return KEY_NULL;
case DIK_NOCONVERT: return KEY_NULL;
case DIK_YEN: return KEY_NULL;
case DIK_NUMPADEQUALS: return KEY_NULL;
case DIK_CIRCUMFLEX: return KEY_NULL;
case DIK_AT: return KEY_NULL;
case DIK_COLON: return KEY_NULL;
case DIK_UNDERLINE: return KEY_NULL;
case DIK_KANJI: return KEY_NULL;
case DIK_STOP: return KEY_NULL;
case DIK_AX: return KEY_NULL;
case DIK_UNLABELED: return KEY_NULL;
case DIK_NUMPADENTER: return KEY_NUMPADENTER;
case DIK_RCONTROL: return KEY_RCONTROL;
case DIK_NUMPADCOMMA: return KEY_SEPARATOR;
case DIK_DIVIDE: return KEY_DIVIDE;
case DIK_SYSRQ: return KEY_PRINT;
case DIK_RALT: return KEY_RALT;
case DIK_PAUSE: return KEY_PAUSE;
case DIK_HOME: return KEY_HOME;
case DIK_UP: return KEY_UP;
case DIK_PGUP: return KEY_PAGE_UP;
case DIK_LEFT: return KEY_LEFT;
case DIK_RIGHT: return KEY_RIGHT;
case DIK_END: return KEY_END;
case DIK_DOWN: return KEY_DOWN;
case DIK_PGDN: return KEY_PAGE_DOWN;
case DIK_INSERT: return KEY_INSERT;
case DIK_DELETE: return KEY_DELETE;
case DIK_LWIN: return KEY_WIN_LWINDOW;
case DIK_RWIN: return KEY_WIN_RWINDOW;
case DIK_APPS: return KEY_WIN_APPS;
case DIK_OEM_102: return KEY_OEM_102;
}
return KEY_NULL;
}
//------------------------------------------------------------------------------
//
// This function translates an internal key code to the associated
// DirectInput scan code
//
//------------------------------------------------------------------------------
U8 Key_to_DIK( U16 keyCode )
{
switch ( keyCode )
{
case KEY_BACKSPACE: return DIK_BACK;
case KEY_TAB: return DIK_TAB;
case KEY_RETURN: return DIK_RETURN;
//KEY_CONTROL:
//KEY_ALT:
//KEY_SHIFT:
case KEY_PAUSE: return DIK_PAUSE;
case KEY_CAPSLOCK: return DIK_CAPSLOCK;
case KEY_ESCAPE: return DIK_ESCAPE;
case KEY_SPACE: return DIK_SPACE;
case KEY_PAGE_DOWN: return DIK_PGDN;
case KEY_PAGE_UP: return DIK_PGUP;
case KEY_END: return DIK_END;
case KEY_HOME: return DIK_HOME;
case KEY_LEFT: return DIK_LEFT;
case KEY_UP: return DIK_UP;
case KEY_RIGHT: return DIK_RIGHT;
case KEY_DOWN: return DIK_DOWN;
case KEY_PRINT: return DIK_SYSRQ;
case KEY_INSERT: return DIK_INSERT;
case KEY_DELETE: return DIK_DELETE;
case KEY_HELP: return 0;
case KEY_0: return DIK_0;
case KEY_1: return DIK_1;
case KEY_2: return DIK_2;
case KEY_3: return DIK_3;
case KEY_4: return DIK_4;
case KEY_5: return DIK_5;
case KEY_6: return DIK_6;
case KEY_7: return DIK_7;
case KEY_8: return DIK_8;
case KEY_9: return DIK_9;
case KEY_A: return DIK_A;
case KEY_B: return DIK_B;
case KEY_C: return DIK_C;
case KEY_D: return DIK_D;
case KEY_E: return DIK_E;
case KEY_F: return DIK_F;
case KEY_G: return DIK_G;
case KEY_H: return DIK_H;
case KEY_I: return DIK_I;
case KEY_J: return DIK_J;
case KEY_K: return DIK_K;
case KEY_L: return DIK_L;
case KEY_M: return DIK_M;
case KEY_N: return DIK_N;
case KEY_O: return DIK_O;
case KEY_P: return DIK_P;
case KEY_Q: return DIK_Q;
case KEY_R: return DIK_R;
case KEY_S: return DIK_S;
case KEY_T: return DIK_T;
case KEY_U: return DIK_U;
case KEY_V: return DIK_V;
case KEY_W: return DIK_W;
case KEY_X: return DIK_X;
case KEY_Y: return DIK_Y;
case KEY_Z: return DIK_Z;
case KEY_TILDE: return DIK_GRAVE;
case KEY_MINUS: return DIK_MINUS;
case KEY_EQUALS: return DIK_EQUALS;
case KEY_LBRACKET: return DIK_LBRACKET;
case KEY_RBRACKET: return DIK_RBRACKET;
case KEY_BACKSLASH: return DIK_BACKSLASH;
case KEY_SEMICOLON: return DIK_SEMICOLON;
case KEY_APOSTROPHE: return DIK_APOSTROPHE;
case KEY_COMMA: return DIK_COMMA;
case KEY_PERIOD: return DIK_PERIOD;
case KEY_SLASH: return DIK_SLASH;
case KEY_NUMPAD0: return DIK_NUMPAD0;
case KEY_NUMPAD1: return DIK_NUMPAD1;
case KEY_NUMPAD2: return DIK_NUMPAD2;
case KEY_NUMPAD3: return DIK_NUMPAD3;
case KEY_NUMPAD4: return DIK_NUMPAD4;
case KEY_NUMPAD5: return DIK_NUMPAD5;
case KEY_NUMPAD6: return DIK_NUMPAD6;
case KEY_NUMPAD7: return DIK_NUMPAD7;
case KEY_NUMPAD8: return DIK_NUMPAD8;
case KEY_NUMPAD9: return DIK_NUMPAD9;
case KEY_MULTIPLY: return DIK_MULTIPLY;
case KEY_ADD: return DIK_ADD;
case KEY_SEPARATOR: return DIK_NUMPADCOMMA;
case KEY_SUBTRACT: return DIK_SUBTRACT;
case KEY_DECIMAL: return DIK_DECIMAL;
case KEY_DIVIDE: return DIK_DIVIDE;
case KEY_NUMPADENTER: return DIK_NUMPADENTER;
case KEY_F1: return DIK_F1;
case KEY_F2: return DIK_F2;
case KEY_F3: return DIK_F3;
case KEY_F4: return DIK_F4;
case KEY_F5: return DIK_F5;
case KEY_F6: return DIK_F6;
case KEY_F7: return DIK_F7;
case KEY_F8: return DIK_F8;
case KEY_F9: return DIK_F9;
case KEY_F10: return DIK_F10;
case KEY_F11: return DIK_F11;
case KEY_F12: return DIK_F12;
case KEY_F13: return DIK_F13;
case KEY_F14: return DIK_F14;
case KEY_F15: return DIK_F15;
case KEY_F16:
case KEY_F17:
case KEY_F18:
case KEY_F19:
case KEY_F20:
case KEY_F21:
case KEY_F22:
case KEY_F23:
case KEY_F24: return 0;
case KEY_NUMLOCK: return DIK_NUMLOCK;
case KEY_SCROLLLOCK: return DIK_SCROLL;
case KEY_LCONTROL: return DIK_LCONTROL;
case KEY_RCONTROL: return DIK_RCONTROL;
case KEY_LALT: return DIK_LALT;
case KEY_RALT: return DIK_RALT;
case KEY_LSHIFT: return DIK_LSHIFT;
case KEY_RSHIFT: return DIK_RSHIFT;
case KEY_WIN_LWINDOW: return DIK_LWIN;
case KEY_WIN_RWINDOW: return DIK_RWIN;
case KEY_WIN_APPS: return DIK_APPS;
case KEY_OEM_102: return DIK_OEM_102;
};
return 0;
}
#ifdef LOG_INPUT
//------------------------------------------------------------------------------
const char* getKeyName( U16 key )
{
switch ( key )
{
case KEY_BACKSPACE: return "Backspace";
case KEY_TAB: return "Tab";
case KEY_RETURN: return "Return";
case KEY_PAUSE: return "Pause";
case KEY_CAPSLOCK: return "CapsLock";
case KEY_ESCAPE: return "Esc";
case KEY_SPACE: return "SpaceBar";
case KEY_PAGE_DOWN: return "PageDown";
case KEY_PAGE_UP: return "PageUp";
case KEY_END: return "End";
case KEY_HOME: return "Home";
case KEY_LEFT: return "Left";
case KEY_UP: return "Up";
case KEY_RIGHT: return "Right";
case KEY_DOWN: return "Down";
case KEY_PRINT: return "PrintScreen";
case KEY_INSERT: return "Insert";
case KEY_DELETE: return "Delete";
case KEY_HELP: return "Help";
case KEY_NUMPAD0: return "Numpad 0";
case KEY_NUMPAD1: return "Numpad 1";
case KEY_NUMPAD2: return "Numpad 2";
case KEY_NUMPAD3: return "Numpad 3";
case KEY_NUMPAD4: return "Numpad 4";
case KEY_NUMPAD5: return "Numpad 5";
case KEY_NUMPAD6: return "Numpad 6";
case KEY_NUMPAD7: return "Numpad 7";
case KEY_NUMPAD8: return "Numpad 8";
case KEY_NUMPAD9: return "Numpad 9";
case KEY_MULTIPLY: return "Multiply";
case KEY_ADD: return "Add";
case KEY_SEPARATOR: return "Separator";
case KEY_SUBTRACT: return "Subtract";
case KEY_DECIMAL: return "Decimal";
case KEY_DIVIDE: return "Divide";
case KEY_NUMPADENTER: return "Numpad Enter";
case KEY_F1: return "F1";
case KEY_F2: return "F2";
case KEY_F3: return "F3";
case KEY_F4: return "F4";
case KEY_F5: return "F5";
case KEY_F6: return "F6";
case KEY_F7: return "F7";
case KEY_F8: return "F8";
case KEY_F9: return "F9";
case KEY_F10: return "F10";
case KEY_F11: return "F11";
case KEY_F12: return "F12";
case KEY_F13: return "F13";
case KEY_F14: return "F14";
case KEY_F15: return "F15";
case KEY_F16: return "F16";
case KEY_F17: return "F17";
case KEY_F18: return "F18";
case KEY_F19: return "F19";
case KEY_F20: return "F20";
case KEY_F21: return "F21";
case KEY_F22: return "F22";
case KEY_F23: return "F23";
case KEY_F24: return "F24";
case KEY_NUMLOCK: return "NumLock";
case KEY_SCROLLLOCK: return "ScrollLock";
case KEY_LCONTROL: return "LCtrl";
case KEY_RCONTROL: return "RCtrl";
case KEY_LALT: return "LAlt";
case KEY_RALT: return "RAlt";
case KEY_LSHIFT: return "LShift";
case KEY_RSHIFT: return "RShift";
case KEY_WIN_LWINDOW: return "LWin";
case KEY_WIN_RWINDOW: return "RWin";
case KEY_WIN_APPS: return "Apps";
}
static char returnString[5];
dSprintf( returnString, sizeof( returnString ), "%c", Input::getAscii( key, STATE_UPPER ) );
return returnString;
}
#endif // LOG_INPUT
//------------------------------------------------------------------------------
const char* DInputDevice::getJoystickAxesString()
{
if ( mDeviceType != JoystickDeviceType )
return( "" );
U32 axisCount = mDeviceCaps.dwAxes;
char buf[64];
dSprintf( buf, sizeof( buf ), "%d", axisCount );
for ( U32 i = 0; i < mObjCount; i++ )
{
switch ( mObjInfo[i].mInst )
{
case SI_XAXIS:
dStrcat( buf, "\tX" );
break;
case SI_YAXIS:
dStrcat( buf, "\tY" );
break;
case SI_ZAXIS:
dStrcat( buf, "\tZ" );
break;
case SI_RXAXIS:
dStrcat( buf, "\tR" );
break;
case SI_RYAXIS:
dStrcat( buf, "\tU" );
break;
case SI_RZAXIS:
dStrcat( buf, "\tV" );
break;
case SI_SLIDER:
dStrcat( buf, "\tS" );
break;
}
}
char* returnString = Con::getReturnBuffer( dStrlen( buf ) + 1 );
dStrcpy( returnString, buf );
return( returnString );
}
//------------------------------------------------------------------------------
bool DInputDevice::joystickDetected()
{
return( smDeviceCount[ JoystickDeviceType ] > 0 );
}