//----------------------------------------------------------------------------- // Copyright (c) 2013 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 "math/mMath.h" #include "platformWin32/winDInputDevice.h" #include "console/console.h" #include "game/gameInterface.h" #include "string/unicode.h" // Static class data: LPDIRECTINPUT8 DInputDevice::smDInputInterface; U8 DInputDevice::smKeyboardCount; U8 DInputDevice::smMouseCount; U8 DInputDevice::smJoystickCount; U8 DInputDevice::smUnknownCount; U8 DInputDevice::smModifierKeys; bool DInputDevice::smKeyStates[256]; bool DInputDevice::smInitialized = false; //------------------------------------------------------------------------------ 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; mPrevPOVPos = 0; switch ( GET_DIDEVICE_TYPE( mDeviceInstance.dwDevType ) ) { case DI8DEVTYPE_KEYBOARD: mDeviceType = KeyboardDeviceType; mDeviceID = smKeyboardCount++; dSprintf( mName, 29, "keyboard%d", mDeviceID ); break; case DI8DEVTYPE_MOUSE: mDeviceType = MouseDeviceType; mDeviceID = smMouseCount++; dSprintf( mName, 29, "mouse%d", mDeviceID ); break; case DI8DEVTYPE_JOYSTICK: case DI8DEVTYPE_GAMEPAD: mDeviceType = JoystickDeviceType; mDeviceID = smJoystickCount++; dSprintf( mName, 29, "joystick%d", mDeviceID ); break; default: mDeviceType = UnknownDeviceType; mDeviceID = smUnknownCount++; dSprintf( mName, 29, "unknown%d", mDeviceID ); break; } } //------------------------------------------------------------------------------ DInputDevice::~DInputDevice() { destroy(); } //------------------------------------------------------------------------------ void DInputDevice::init() { if ( !smInitialized ) { dMemset( smKeyStates, 0, sizeof( smKeyStates ) ); smInitialized = true; } // Reset all of the static variables: smDInputInterface = NULL; smKeyboardCount = 0; smMouseCount = 0; smJoystickCount = 0; smUnknownCount = 0; smModifierKeys = 0; } //------------------------------------------------------------------------------ void DInputDevice::resetModifierKeys() { smModifierKeys = 0; setModifierKeys( 0 ); } //------------------------------------------------------------------------------ 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 ); return false; } 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 ); 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 ); 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 ); return false; } } } } else { 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(); 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) coopLevel |= DISCL_EXCLUSIVE; else coopLevel |= DISCL_NONEXCLUSIVE; result = mDevice->SetCooperativeLevel( winState.appWindow, coopLevel ); if ( FAILED( result ) ) { Con::errorf( "Failed to set the cooperative level for the %s input device.", mName ); return false; } S32 code = mDevice->Acquire(); result = SUCCEEDED( code ); if ( result ) { #ifdef TORQUE_DEBUG Con::printf( "%s input device acquired.", mName ); #endif mAcquired = true; // 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 ); } return( result ); } return( false ); } //------------------------------------------------------------------------------ bool DInputDevice::unacquire() { if ( mDevice ) { if ( !mAcquired ) return( true ); bool result = false; result = SUCCEEDED( mDevice->Unacquire() ); if ( result ) { #ifdef TORQUE_DEBUG Con::printf( "%s input device unacquired.", mName ); #endif mAcquired = false; } else { Con::warnf( ConsoleLogEntry::General, "%s input device NOT unacquired.", mName ); } 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; // Enumerate all of the 'objects' detected on the device: if ( FAILED( mDevice->EnumObjects( EnumObjectsProc, this, DIDFT_ALL ) ) ) return false; // if we enumerated fewer objects that 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 xAxisCount = 0; U32 yAxisCount = 0; U32 zAxisCount = 0; U32 rAxisCount = 0; U32 uAxisCount = 0; U32 vAxisCount = 0; U32 sliderCount = 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 = KEY_BUTTON0 + buttonCount++; } else if ( mObjInstance[i].guidType == GUID_POV ) { mObjInfo[i].mType = SI_POV; mObjInfo[i].mInst = povCount++; } else if ( mObjInstance[i].guidType == GUID_XAxis ) { mObjInfo[i].mType = SI_XAXIS; mObjInfo[i].mInst = xAxisCount++; } else if ( mObjInstance[i].guidType == GUID_YAxis ) { mObjInfo[i].mType = SI_YAXIS; mObjInfo[i].mInst = yAxisCount++; } else if ( mObjInstance[i].guidType == GUID_ZAxis ) { mObjInfo[i].mType = SI_ZAXIS; mObjInfo[i].mInst = zAxisCount++; } else if ( mObjInstance[i].guidType == GUID_RxAxis ) { mObjInfo[i].mType = SI_RXAXIS; mObjInfo[i].mInst = rAxisCount++; } else if ( mObjInstance[i].guidType == GUID_RyAxis ) { mObjInfo[i].mType = SI_RYAXIS; mObjInfo[i].mInst = uAxisCount++; } else if ( mObjInstance[i].guidType == GUID_RzAxis ) { mObjInfo[i].mType = SI_RZAXIS; mObjInfo[i].mInst = vAxisCount++; } else if ( mObjInstance[i].guidType == GUID_Slider ) { mObjInfo[i].mType = SI_SLIDER; mObjInfo[i].mInst = sliderCount++; } else if ( mObjInstance[i].guidType == GUID_Key ) { mObjInfo[i].mType = SI_KEY; mObjInfo[i].mInst = DIK_to_Key( (U8)DIDFT_GETINSTANCE( mObjFormat[i].dwType ) ); keyCount++; } else { mObjInfo[i].mType = SI_UNKNOWN; mObjInfo[i].mInst = 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; } } } 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; // Test for the "need sync" flag: if ( mNeedSync ) { // For now, only sync the keyboard: if ( mDeviceType == KeyboardDeviceType ) syncKeyboardState(); mNeedSync = 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 ); break; case DIERR_NOTACQUIRED: // We can't get the device, so quit: mAcquired = false; // Don't error out - this is actually a natural occurrence... 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 ); 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 ); break; case DIERR_NOTACQUIRED: Con::errorf( "DInputDevice::processPolled -- GetDeviceState called when %s input device is not acquired!", mName ); break; case E_PENDING: Con::warnf( "DInputDevice::processPolled -- Data not yet available for the %s input device!", mName ); break; } return false; } // Loop through all of the objects and produce events where // the states have changed: S32 newData, oldData; 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; } //------------------------------------------------------------------------------ bool DInputDevice::processKeyEvent( InputEvent &event ) { if ( event.deviceType != KeyboardDeviceType || event.objType != SI_KEY ) return false; bool modKey = false; U8 DIKeyCode = Key_to_DIK( event.objInst ); if ( event.action == SI_MAKE ) { // Maintain the key structure: smKeyStates[DIKeyCode] = true; switch ( event.objInst ) { case KEY_LSHIFT: smModifierKeys |= SI_LSHIFT; modKey = true; break; case KEY_RSHIFT: smModifierKeys |= SI_RSHIFT; modKey = true; break; case KEY_LCONTROL: smModifierKeys |= SI_LCTRL; modKey = true; break; case KEY_RCONTROL: smModifierKeys |= SI_RCTRL; modKey = true; break; case KEY_LALT: smModifierKeys |= SI_LALT; modKey = true; break; case KEY_RALT: smModifierKeys |= SI_RALT; modKey = true; break; } } else { // Maintain the keys structure: smKeyStates[DIKeyCode] = false; switch ( event.objInst ) { case KEY_LSHIFT: smModifierKeys &= ~SI_LSHIFT; modKey = true; break; case KEY_RSHIFT: smModifierKeys &= ~SI_RSHIFT; modKey = true; break; case KEY_LCONTROL: smModifierKeys &= ~SI_LCTRL; modKey = true; break; case KEY_RCONTROL: smModifierKeys &= ~SI_RCTRL; modKey = true; break; case KEY_LALT: smModifierKeys &= ~SI_LALT; modKey = true; break; case KEY_RALT: smModifierKeys &= ~SI_RALT; modKey = true; break; } } if ( modKey ) { setModifierKeys( smModifierKeys ); event.modifier = 0; } else event.modifier = smModifierKeys; // TODO: alter this getAscii call KEY_STATE state = STATE_LOWER; if (event.modifier & (SI_CTRL|SI_ALT) ) { state = STATE_GOOFY; } // else if ( event.modifier & SI_SHIFT ) if ( event.modifier & SI_SHIFT ) { state = STATE_UPPER; } event.ascii = Input::getAscii( event.objInst, state ); return modKey; } //------------------------------------------------------------------------------ void DInputDevice::syncKeyboardState() { AssertFatal( mDeviceType == KeyboardDeviceType, "DInputDevice::syncKeyboardState - device is not a keyboard!" ); U8* keyBuffer = new U8[mObjBufferSize]; dMemset( keyBuffer, 0, sizeof( U8 ) * mObjBufferSize ); HRESULT result = mDevice->GetDeviceState( mObjBufferSize, keyBuffer ); if ( SUCCEEDED( result ) ) { S32 keyState; bool keyIsDown, keyWasDown; for ( DWORD i = 1; i < mObjCount; i++ ) // Valid key codes start at 1 { keyState = *( (U8*) ( keyBuffer + mObjFormat[i].dwOfs ) ); keyWasDown = smKeyStates[i]; keyIsDown = bool( keyState & 0x80 ); if ( keyWasDown != keyIsDown ) buildEvent( i - 1, ( keyState & 0x80 ), ( keyWasDown ? 0x80 : 0 ) ); } } else { 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; case E_PENDING: errorString = "E_PENDING"; break; default: errorString = "Unknown Error"; } } delete [] keyBuffer; } //------------------------------------------------------------------------------ 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; } #define _Win32LogPOVInput (a) //------------------------------------------------------------------------------ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData ) { DIDEVICEOBJECTINSTANCE &objInstance = mObjInstance[offset]; ObjInfo &objInfo = mObjInfo[offset]; if ( objInfo.mType == SI_UNKNOWN ) return false; InputEvent newEvent; newEvent.deviceType = mDeviceType; newEvent.deviceInst = mDeviceID; newEvent.objType = objInfo.mType; newEvent.objInst = objInfo.mInst; newEvent.modifier = smModifierKeys; InputEvent newEvent2; newEvent2.deviceType = mDeviceType; newEvent2.deviceInst = mDeviceID; newEvent2.objType = objInfo.mType; newEvent2.objInst = objInfo.mInst; newEvent2.modifier = smModifierKeys; switch ( newEvent.objType ) { case SI_XAXIS: case SI_YAXIS: case SI_ZAXIS: case SI_RXAXIS: case SI_RYAXIS: case SI_RZAXIS: case SI_SLIDER: newEvent.action = SI_MOVE; if ( newEvent.deviceType == MouseDeviceType ) { newEvent.fValues[0] = float( newData ); } 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.fValues[0] = float( newData - objInfo.mMin ) / range; newEvent.fValues[0] = float( ( 2 * newData ) - objInfo.mMax - objInfo.mMin ) / range; } else newEvent.fValues[0] = (F32)newData; } Game->postEvent( newEvent ); break; case SI_BUTTON: newEvent.action = ( newData & 0x80 ) ? SI_MAKE : SI_BREAK; newEvent.fValues[0] = ( newEvent.action == SI_MAKE ) ? 1.0f : 0.0f; Game->postEvent( newEvent ); break; case SI_KEY: newEvent.action = ( newData & 0x80 ) ? SI_MAKE : SI_BREAK; newEvent.fValues[0] = ( newEvent.action == SI_MAKE ) ? 1.0f : 0.0f; processKeyEvent( newEvent ); Game->postEvent( newEvent ); 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.fValues[0] = 0.0f; // post events for all buttons that need to be cleared. if( clearkeys & POV_up) { newEvent.objInst = ( objInst == 0 ) ? SI_UPOV : SI_UPOV2; Game->postEvent(newEvent); } if( clearkeys & POV_right) { newEvent.objInst = ( objInst == 0 ) ? SI_RPOV : SI_RPOV2; Game->postEvent(newEvent); } if( clearkeys & POV_down) { newEvent.objInst = ( objInst == 0 ) ? SI_DPOV : SI_DPOV2; Game->postEvent(newEvent); } if( clearkeys & POV_left) { newEvent.objInst = ( objInst == 0 ) ? SI_LPOV : SI_LPOV2; Game->postEvent(newEvent); } } // clear keys if ( setkeys ) { newEvent.action = SI_MAKE; newEvent.fValues[0] = 1.0f; // post events for all buttons that need to be set. if( setkeys & POV_up) { newEvent.objInst = ( objInst == 0 ) ? SI_UPOV : SI_UPOV2; Game->postEvent(newEvent); } if( setkeys & POV_right) { newEvent.objInst = ( objInst == 0 ) ? SI_RPOV : SI_RPOV2; Game->postEvent(newEvent); } if( setkeys & POV_down) { newEvent.objInst = ( objInst == 0 ) ? SI_DPOV : SI_DPOV2; Game->postEvent(newEvent); } if( setkeys & POV_left) { newEvent.objInst = ( objInst == 0 ) ? SI_LPOV : SI_LPOV2; Game->postEvent(newEvent); } } // 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) { 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 ) ) ) { Con::errorf( "DInputDevice::rumbleJoystick - %s does not support force feedback.\n", mName ); return; } else { 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"; } 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). // //------------------------------------------------------------------------------ U16 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 0; case DIK_CONVERT: return 0; case DIK_NOCONVERT: return 0; case DIK_YEN: return 0; case DIK_NUMPADEQUALS: return 0; case DIK_CIRCUMFLEX: return 0; case DIK_AT: return 0; case DIK_COLON: return 0; case DIK_UNDERLINE: return 0; case DIK_KANJI: return 0; case DIK_STOP: return 0; case DIK_AX: return 0; case DIK_UNLABELED: return 0; 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; } //------------------------------------------------------------------------------ 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].mType ) { 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( smJoystickCount > 0 ); } U8 DInputDevice::getJoystickCount() { return(smJoystickCount); }