#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2013 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_INPUT_SOURCE 1 #include "XInputGamePad.h" #define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN #include namespace Nuclex { namespace Input { namespace Devices { // ------------------------------------------------------------------------------------------- // XInputGamePad::XInputGamePad(std::size_t playerIndex) : playerIndex(static_cast(playerIndex)), isAttached(false), xInputState(nullptr) { this->currentState.Reset(); this->currentState.AxisMask = static_cast( Axes::PositionX | Axes::PositionY ); this->currentState.SliderMask = static_cast( Sliders::Slider1 | Sliders::Slider2 ); this->currentState.ButtonCount = 10; this->currentState.DirectionalPadCount = 1; this->xInputState = new XINPUT_STATE(); readState(*this->xInputState); convertState(); } // ------------------------------------------------------------------------------------------- // XInputGamePad::~XInputGamePad() { delete this->xInputState; while(this->pool.TryPop(this->xInputState)) { delete this->xInputState; } } // ------------------------------------------------------------------------------------------- // void XInputGamePad::Update() { XINPUT_STATE *previousState = this->xInputState; // Obtain the new state, either by taking the next one from the snapshot queue or, // if the snapshot queue is empty, by capturing a new state { XINPUT_STATE *newState; if(this->snapshots.TryPop(newState)) { this->xInputState = newState; } else { if(!this->pool.TryPop(newState)) { newState = new XINPUT_STATE(); } readState(*this->xInputState); } } DWORD previousPacketNumber = previousState->dwPacketNumber; DWORD packetNumber = this->xInputState->dwPacketNumber; if(packetNumber != previousPacketNumber) { convertState(); //generateEvents(previousState, this->xInputState); this->pool.Add(previousState); } } // ------------------------------------------------------------------------------------------- // void XInputGamePad::TakeSnapshot() { XINPUT_STATE *state; if(!this->pool.TryPop(state)) { state = new XINPUT_STATE(); } readState(*state); this->snapshots.Add(state); } // ------------------------------------------------------------------------------------------- // void XInputGamePad::ClearSnapshots() { XINPUT_STATE *state; while(this->snapshots.TryPop(state)) { this->pool.Add(state); } } // ------------------------------------------------------------------------------------------- // void XInputGamePad::readState(XINPUT_STATE &state) { DWORD result = ::XInputGetState(this->playerIndex, &state); switch(result) { case ERROR_SUCCESS: { this->isAttached = true; break; } case ERROR_DEVICE_NOT_CONNECTED: { ::ZeroMemory(&state, sizeof(state)); this->isAttached = false; break; } default: { throw std::runtime_error("Could not read Xinput game pad state"); break; } } } // ------------------------------------------------------------------------------------------- // void XInputGamePad::generateEvents(const XINPUT_STATE &previous, const XINPUT_STATE ¤t) { WORD previousButtons = previous.Gamepad.wButtons; WORD currentButtons = current.Gamepad.wButtons; WORD buttonDelta = (previousButtons ^ currentButtons); if(buttonDelta & XINPUT_GAMEPAD_A) { generateButtonEvent((currentButtons & XINPUT_GAMEPAD_A) != 0, 0); } if(buttonDelta & XINPUT_GAMEPAD_B) { generateButtonEvent((currentButtons & XINPUT_GAMEPAD_B) != 0, 0); } if(buttonDelta & XINPUT_GAMEPAD_X) { generateButtonEvent((currentButtons & XINPUT_GAMEPAD_X) != 0, 0); } if(buttonDelta & XINPUT_GAMEPAD_Y) { generateButtonEvent((currentButtons & XINPUT_GAMEPAD_Y) != 0, 0); } if(buttonDelta & XINPUT_GAMEPAD_LEFT_SHOULDER) { generateButtonEvent((currentButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0, 0); } if(buttonDelta & XINPUT_GAMEPAD_RIGHT_SHOULDER) { generateButtonEvent((currentButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0, 0); } if(buttonDelta & XINPUT_GAMEPAD_START) { generateButtonEvent((currentButtons & XINPUT_GAMEPAD_START) != 0, 0); } if(buttonDelta & XINPUT_GAMEPAD_BACK) { generateButtonEvent((currentButtons & XINPUT_GAMEPAD_BACK) != 0, 0); } if(buttonDelta & XINPUT_GAMEPAD_LEFT_THUMB) { generateButtonEvent((currentButtons & XINPUT_GAMEPAD_LEFT_THUMB) != 0, 0); } if(buttonDelta & XINPUT_GAMEPAD_RIGHT_THUMB) { generateButtonEvent((currentButtons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0, 0); } } // ------------------------------------------------------------------------------------------- // void XInputGamePad::generateButtonEvent(bool pressed, std::size_t buttonIndex) { if(pressed) { ButtonPressed(buttonIndex); } else { ButtonReleased(buttonIndex); } } // ------------------------------------------------------------------------------------------- // void XInputGamePad::convertState() { convertTriggers(); convertLeftThumbStick(); convertRightThumbStick(); convertButtons(); convertDirectionalPad(); } // ------------------------------------------------------------------------------------------- // void XInputGamePad::convertTriggers() { const XINPUT_GAMEPAD &gamepad = this->xInputState->Gamepad; float slider1 = static_cast(gamepad.bLeftTrigger) / 255.0f; this->currentState.Sliders[0] = slider1; float slider2 = static_cast(gamepad.bRightTrigger) / 255.0f; this->currentState.Sliders[1] = slider2; } // ------------------------------------------------------------------------------------------- // void XInputGamePad::convertLeftThumbStick() { const XINPUT_GAMEPAD &gamepad = this->xInputState->Gamepad; float x; if(gamepad.sThumbLX < 0) { x = static_cast(gamepad.sThumbLX + 1) / 32767.0f; } else { x = static_cast(gamepad.sThumbLX) / 32767.0f; } this->currentState.Axes[0] = x; float y; if(gamepad.sThumbLY < 0) { y = static_cast(gamepad.sThumbLY + 1) / 32767.0f; } else { y = static_cast(gamepad.sThumbLY) / 32767.0f; } this->currentState.Axes[1] = y; } // ------------------------------------------------------------------------------------------- // void XInputGamePad::convertRightThumbStick() { const XINPUT_GAMEPAD &gamepad = this->xInputState->Gamepad; float x; if(gamepad.sThumbRX < 0) { x = static_cast(gamepad.sThumbRX + 1) / 32767.0f; } else { x = static_cast(gamepad.sThumbRX) / 32767.0f; } this->currentState.Axes[12] = x; float y; if(gamepad.sThumbRY < 0) { y = static_cast(gamepad.sThumbRY + 1) / 32767.0f; } else { y = static_cast(gamepad.sThumbRY) / 32767.0f; } this->currentState.Axes[13] = y; } // ------------------------------------------------------------------------------------------- // void XInputGamePad::convertButtons() { WORD buttons = this->xInputState->Gamepad.wButtons; this->currentState.Buttons.set(0, (buttons & XINPUT_GAMEPAD_A) != 0); this->currentState.Buttons.set(1, (buttons & XINPUT_GAMEPAD_B) != 0); this->currentState.Buttons.set(2, (buttons & XINPUT_GAMEPAD_X) != 0); this->currentState.Buttons.set(3, (buttons & XINPUT_GAMEPAD_Y) != 0); this->currentState.Buttons.set(4, (buttons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0); this->currentState.Buttons.set(5, (buttons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0); this->currentState.Buttons.set(6, (buttons & XINPUT_GAMEPAD_START) != 0); this->currentState.Buttons.set(7, (buttons & XINPUT_GAMEPAD_BACK) != 0); this->currentState.Buttons.set(8, (buttons & XINPUT_GAMEPAD_LEFT_THUMB) != 0); this->currentState.Buttons.set(9, (buttons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0); } // ------------------------------------------------------------------------------------------- // void XInputGamePad::convertDirectionalPad() { WORD buttons = this->xInputState->Gamepad.wButtons; this->currentState.DirectionalPads[0].Left = (buttons & XINPUT_GAMEPAD_DPAD_LEFT) != 0; this->currentState.DirectionalPads[0].Right = (buttons & XINPUT_GAMEPAD_DPAD_RIGHT) != 0; this->currentState.DirectionalPads[0].Up = (buttons & XINPUT_GAMEPAD_DPAD_UP) != 0; this->currentState.DirectionalPads[0].Down = (buttons & XINPUT_GAMEPAD_DPAD_DOWN) != 0; } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Input::Devices