289 lines
10 KiB
C++
289 lines
10 KiB
C++
|
/****************************************************************************
|
||
|
Copyright (c) 2007,Radon Labs GmbH
|
||
|
Copyright (c) 2011-2013,WebJet Business Division,CYOU
|
||
|
|
||
|
http://www.genesis-3d.com.cn
|
||
|
|
||
|
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.
|
||
|
****************************************************************************/
|
||
|
#if WIN32
|
||
|
|
||
|
#include "input/input_stdneb.h"
|
||
|
#include "input/xinput/xinputgamepad.h"
|
||
|
#include "framesync/framesynctimer.h"
|
||
|
|
||
|
namespace XInput
|
||
|
{
|
||
|
__ImplementClass(XInput::XInputGamePad, 'XIGP', Input::InputGamePadBase);
|
||
|
|
||
|
using namespace Math;
|
||
|
using namespace FrameSync;
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
/**
|
||
|
*/
|
||
|
XInputGamePad::XInputGamePad() :
|
||
|
lastPacketNumber(0xffffffff),
|
||
|
lastCheckConnectedTime(0)
|
||
|
{
|
||
|
// empty
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
/**
|
||
|
*/
|
||
|
XInputGamePad::~XInputGamePad()
|
||
|
{
|
||
|
// empty
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
/**
|
||
|
*/
|
||
|
void
|
||
|
XInputGamePad::OnAttach(const GPtr<Input::InputServerBase>& inputServer)
|
||
|
{
|
||
|
InputGamePadBase::OnAttach(inputServer);
|
||
|
|
||
|
// start our timer, the timer is used to measure time since the last
|
||
|
// XInputGetState() for disconnected game pads, this is an expensive
|
||
|
// operation, thus we're only doing it every half a second or so
|
||
|
this->lastCheckConnectedTime = FrameSyncTimer::Instance()->GetTicks() - CheckConnectedInterval;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
/**
|
||
|
This compares the current state of the game pad against the
|
||
|
previous state and sets the internal state accordingly.
|
||
|
|
||
|
FIXME: Calling XInputGetState() on non-connected controllers is very
|
||
|
expensive, thus if XInputGetState return ERROR_DEVICE_NOT_CONNECTED, only
|
||
|
call XInputGetState() every 2 seconds to check if a device has actually
|
||
|
been connected!!!
|
||
|
*/
|
||
|
void
|
||
|
XInputGamePad::OnBeginFrame()
|
||
|
{
|
||
|
InputGamePadBase::OnBeginFrame();
|
||
|
|
||
|
// get current state of the game pad, this looks a bit complicated
|
||
|
// because disconnected game pads are only checked once in a while
|
||
|
// (getting state from a non-connected device is fairly expensive)
|
||
|
Timing::Tick curTime = FrameSyncTimer::Instance()->GetTicks();
|
||
|
XINPUT_STATE curState = { 0 };
|
||
|
DWORD result = ERROR_DEVICE_NOT_CONNECTED;
|
||
|
if (!this->isConnected)
|
||
|
{
|
||
|
// if we're not currently connected, only check every little while
|
||
|
if ((curTime - this->lastCheckConnectedTime) >= CheckConnectedInterval)
|
||
|
{
|
||
|
result = XInputGetState(this->playerIndex, &curState);
|
||
|
this->lastCheckConnectedTime = curTime;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// if we're currently connected, getting the current state is cheap
|
||
|
result = XInputGetState(this->playerIndex, &curState);
|
||
|
this->lastCheckConnectedTime = curTime;
|
||
|
}
|
||
|
|
||
|
// check result
|
||
|
if (ERROR_DEVICE_NOT_CONNECTED == result)
|
||
|
{
|
||
|
// game pad is currently not connected, if it just has been
|
||
|
// disconnected we need to reset the game pad
|
||
|
if (this->isConnected)
|
||
|
{
|
||
|
this->OnReset();
|
||
|
this->isConnected = false;
|
||
|
}
|
||
|
}
|
||
|
else if (ERROR_SUCCESS == result)
|
||
|
{
|
||
|
this->isConnected = true;
|
||
|
|
||
|
// check if state of controller has actually changed
|
||
|
if (curState.dwPacketNumber != this->lastPacketNumber)
|
||
|
{
|
||
|
this->lastPacketNumber = curState.dwPacketNumber;
|
||
|
|
||
|
// update button states
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_DPAD_UP, DPadUpButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_DPAD_DOWN, DPadDownButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_DPAD_LEFT, DPadLeftButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_DPAD_RIGHT, DPadRightButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_START, StartButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_BACK, BackButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_LEFT_THUMB, LeftThumbButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_RIGHT_THUMB, RightThumbButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_LEFT_SHOULDER, LeftShoulderButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_RIGHT_SHOULDER, RightShoulderButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_A, AButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_B, BButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_X, XButton);
|
||
|
this->UpdateButtonState(curState.Gamepad, XINPUT_GAMEPAD_Y, YButton);
|
||
|
|
||
|
// update axis states
|
||
|
this->UpdateTriggerAxis(curState.Gamepad, LeftTriggerAxis);
|
||
|
this->UpdateTriggerAxis(curState.Gamepad, RightTriggerAxis);
|
||
|
this->UpdateThumbAxis(curState.Gamepad, LeftThumbXAxis);
|
||
|
this->UpdateThumbAxis(curState.Gamepad, LeftThumbYAxis);
|
||
|
this->UpdateThumbAxis(curState.Gamepad, RightThumbXAxis);
|
||
|
this->UpdateThumbAxis(curState.Gamepad, RightThumbYAxis);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// reset button up state
|
||
|
IndexT btnIdx;
|
||
|
for (btnIdx = 0; btnIdx < this->buttonStates.Size(); ++btnIdx)
|
||
|
{
|
||
|
this->buttonStates[btnIdx].up = false;
|
||
|
this->buttonStates[btnIdx].down = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// can't happen?
|
||
|
n_error("XInputGamePad: can't happen (invalid return value from XInputGetState!)");
|
||
|
}
|
||
|
|
||
|
// if the vibrator settings have changed, update accordingly
|
||
|
if (this->isConnected && this->vibratorsDirty)
|
||
|
{
|
||
|
XINPUT_VIBRATION vib;
|
||
|
vib.wLeftMotorSpeed = (WORD) (this->lowFreqVibrator * 65535.0f);
|
||
|
vib.wRightMotorSpeed = (WORD) (this->highFreqVibrator * 65535.0f);
|
||
|
XInputSetState(this->playerIndex, &vib);
|
||
|
this->vibratorsDirty = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
/**
|
||
|
Compares the previous and current state of a game pad button
|
||
|
and updates the parent class' state accordingly.
|
||
|
*/
|
||
|
void
|
||
|
XInputGamePad::UpdateButtonState(const XINPUT_GAMEPAD& curState, WORD xiBtn, Button btn)
|
||
|
{
|
||
|
if (0 != (curState.wButtons & xiBtn))
|
||
|
{
|
||
|
// has button been down-pressed in this frame?
|
||
|
if (!this->buttonStates[btn].pressed)
|
||
|
{
|
||
|
this->buttonStates[btn].down = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this->buttonStates[btn].down = false;
|
||
|
}
|
||
|
this->buttonStates[btn].pressed = true;
|
||
|
this->buttonStates[btn].up = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// has button been released in this frame?
|
||
|
if (this->buttonStates[btn].pressed)
|
||
|
{
|
||
|
this->buttonStates[btn].up = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this->buttonStates[btn].up = false;
|
||
|
}
|
||
|
this->buttonStates[btn].pressed = false;
|
||
|
this->buttonStates[btn].down = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
/**
|
||
|
*/
|
||
|
void
|
||
|
XInputGamePad::UpdateTriggerAxis(const XINPUT_GAMEPAD& curState, Axis axis)
|
||
|
{
|
||
|
n_assert((axis == LeftTriggerAxis) || (axis == RightTriggerAxis));
|
||
|
|
||
|
BYTE rawValue = 0;
|
||
|
if (LeftTriggerAxis == axis)
|
||
|
{
|
||
|
rawValue = curState.bLeftTrigger;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rawValue = curState.bRightTrigger;
|
||
|
}
|
||
|
this->axisValues[axis] = n_max(0.0f, float(rawValue - XINPUT_GAMEPAD_TRIGGER_THRESHOLD)) / (255.0f - XINPUT_GAMEPAD_TRIGGER_THRESHOLD);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
/**
|
||
|
*/
|
||
|
void
|
||
|
XInputGamePad::UpdateThumbAxis(const XINPUT_GAMEPAD& curState, Axis axis)
|
||
|
{
|
||
|
n_assert((axis == LeftThumbXAxis) || (axis == LeftThumbYAxis) || (axis == RightThumbXAxis) || (axis == RightThumbYAxis));
|
||
|
|
||
|
SHORT rawValue = 0;
|
||
|
SHORT deadZone = 0;
|
||
|
switch (axis)
|
||
|
{
|
||
|
case LeftThumbXAxis:
|
||
|
rawValue = curState.sThumbLX;
|
||
|
deadZone = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
|
||
|
break;
|
||
|
|
||
|
case LeftThumbYAxis:
|
||
|
rawValue = curState.sThumbLY;
|
||
|
deadZone = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
|
||
|
break;
|
||
|
|
||
|
case RightThumbXAxis:
|
||
|
rawValue = curState.sThumbRX;
|
||
|
deadZone = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
|
||
|
break;
|
||
|
|
||
|
case RightThumbYAxis:
|
||
|
rawValue = curState.sThumbRY;
|
||
|
deadZone = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
|
||
|
break;
|
||
|
}
|
||
|
float val = 0.0f;
|
||
|
if ((rawValue > deadZone) || (rawValue < -deadZone))
|
||
|
{
|
||
|
// outside dead zone, scale from -1.0f to +1.0f
|
||
|
if (rawValue > 0)
|
||
|
{
|
||
|
// in positive range
|
||
|
val = n_max(0.0f, float(rawValue - deadZone)) / (32767.0f - deadZone);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// in negative range
|
||
|
val = n_min(0.0f, float(rawValue + deadZone)) / (32768.0f - deadZone);
|
||
|
}
|
||
|
}
|
||
|
this->axisValues[axis] = val;
|
||
|
}
|
||
|
|
||
|
} // namespace XInput
|
||
|
|
||
|
#endif
|