Hi there!
I´m writing a wrapper around XInput (http://msdn.microsoft.com/en-us/library/windows/desktop/ee417001(v=vs.85).aspx), trying to keep the interface as similar to the SFML API (http://www.sfml-dev.org/documentation/2.0/classsf_1_1Joystick.php) as possible.
If you guys like this code and find it usefull, I will add it to the wiki page 8)
The code compiles and works fine with MinGW
It works with my wired X360 controller, I don´t know if it will work with the wireless controller
Of course you will need the DirectX SDK installed :'(
Note that this will work only with XBox 360 compatible devices, for another kind of gamepads you still need to use the SFML Joystick API
Ok, here is the code:
X360Controller.hpp
#ifndef X360_CONTROLLER_HPP
#define X360_CONTROLLER_HPP
#include <SFML/System/Vector2.hpp>
namespace xb {
class Joystick {
public:
// Typedefs
typedef unsigned int t_joyNum;
typedef unsigned short t_buttonNum;
// Enums
enum {
Count = 4 // Player 0-3
};
enum {
DPAD_UP = 0x0001,
DPAD_DOWN = 0x0002,
DPAD_LEFT = 0x0004,
DPAD_RIGHT = 0x0008,
START = 0x0010,
BACK = 0x0020,
LEFT_THUMB = 0x0040,
RIGHT_THUMB = 0x0080,
LB = 0x0100,
RB = 0x0200,
A = 0x1000,
B = 0x2000,
X = 0x4000,
Y = 0x8000,
};
// Functions (similar to SFML API)
static bool isConnected (t_joyNum joyNum);
static unsigned int getButtonCount (t_joyNum joyNum) { return 14; }
static bool isButtonPressed (t_joyNum joyNum, t_buttonNum buttonNum);
// X360 specific functions
static bool isAnyXBox360ControllerConnected();
static void getTriggers (t_joyNum joyNum, float &left, float &right);
static void getSticksPosition (t_joyNum joyNum, sf::Vector2f &left, sf::Vector2f &right);
static void setVibration (t_joyNum, float leftMotor = 0.0f, float rightMotor = 0.0f);
}; // class
} // ns
#endif // X360_CONTROLLER_HPP
X360Controller.cpp
#include "X360Controller.hpp"
#ifdef __MINGW32__
// Useless MS defines (needed to compile under MinGW)
#define __in
#define __out
#define __reserved
#endif
// This define makes your program compile faster by excluding things we are not using
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <XInput.h>
namespace xb {
// Returns true if the joystick is connected (and is an XBox 360 Controller)
bool Joystick::isConnected (t_joyNum joyNum)
{
XINPUT_STATE state;
ZeroMemory (&state, sizeof (XINPUT_STATE));
auto result = XInputGetState (joyNum, &state);
return (result == ERROR_SUCCESS);
}
// Convenience function: Returns true if there is at least one X360 controller connected
bool Joystick::isAnyXBox360ControllerConnected()
{
return (isConnected(0) || isConnected(1) || isConnected(2) || isConnected(3));
}
/*
// Returns true if the device supports audio capabilities (headset)
// Uncomment if you need this function
bool Joystick::voiceSupported (t_joyNum joyNum)
{
XINPUT_CAPABILITIES caps;
ZeroMemory (&caps, sizeof (XINPUT_CAPABILITIES));
auto result = XInputGetCapabilities (joyNum, XINPUT_FLAG_GAMEPAD, &caps);
if (result != ERROR_SUCCESS)
return false;
return (caps.Flags & XINPUT_CAPS_VOICE_SUPPORTED);
}
*/
// Returns true if the specified button is pressed
// Note that the triggers are NOT recognized as buttons.. You must use
// the getTriggers function for reading the triggers state
bool Joystick::isButtonPressed (t_joyNum joyNum, t_buttonNum buttonNum)
{
XINPUT_STATE state;
ZeroMemory (&state, sizeof (XINPUT_STATE));
XInputGetState (joyNum, &state);
return (state.Gamepad.wButtons & buttonNum);
}
// This function returns nothing
// It fills the variables left and right with the current state of the triggers (LT and RT)
// The values will always be in the range 0..1
// TODO: TAKE CARE OF THE DEAD ZONE ??????????????????????????????????
void Joystick::getTriggers (t_joyNum joyNum, float &left, float &right)
{
XINPUT_STATE state;
ZeroMemory (&state, sizeof (XINPUT_STATE));
XInputGetState (joyNum, &state);
// Normalize and take care of the Dead Zone
left = static_cast <float> (state.Gamepad.bLeftTrigger) / 255;
right = static_cast <float> (state.Gamepad.bRightTrigger) / 255;
}
// This function returns nothing
// It fills the vectors left and right with the stick positions,
// wich are in the range -100..100, similar to the SFML function
// getAxisPosition
void Joystick::getSticksPosition (t_joyNum joyNum, sf::Vector2f &left, sf::Vector2f &right)
{
XINPUT_STATE state;
ZeroMemory (&state, sizeof (XINPUT_STATE));
XInputGetState (joyNum, &state);
// Check for the "DEAD ZONE"
// Left Stick
if ( (state.Gamepad.sThumbLX < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
state.Gamepad.sThumbLX > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) &&
(state.Gamepad.sThumbLY < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
state.Gamepad.sThumbLY > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) ) {
state.Gamepad.sThumbLX = 0;
state.Gamepad.sThumbLY = 0;
}
// Right Stick
if ( (state.Gamepad.sThumbRX < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
state.Gamepad.sThumbRX > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) &&
(state.Gamepad.sThumbRY < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
state.Gamepad.sThumbRY > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) ) {
state.Gamepad.sThumbRX = 0;
state.Gamepad.sThumbRY = 0;
}
// Convert values to SFML style (-100..100)
left.x = static_cast <float> (state.Gamepad.sThumbLX / 327);
left.y = static_cast <float> (state.Gamepad.sThumbLY / 327);
right.x = static_cast <float> (state.Gamepad.sThumbRX / 327);
right.y = static_cast <float> (state.Gamepad.sThumbRY / 327);
}
// Set vibration (0.0 to 1.0)
// 0 stops the vibration
void Joystick::setVibration (t_joyNum joyNum, float leftMotor, float rightMotor)
{
XINPUT_VIBRATION vib;
ZeroMemory (&vib, sizeof (XINPUT_VIBRATION));
vib.wLeftMotorSpeed = static_cast <WORD> (leftMotor * 65535.0f);
vib.wRightMotorSpeed = static_cast <WORD> (rightMotor * 65535.0f);
XInputSetState (joyNum, &vib);
}
} // ns
Test (main.cpp)
#include <iostream>
using std::cout;
using std::endl;
#include <string>
#include <sstream>
#include <SFML/Graphics.hpp>
// CodeBlocks:
// PROYECT BUILD OPTIONS / SEARCH DIRECTORIES, add
// (DirectX SDK INCLUDE/LIB FOLDERS)
// Linker settings, link libraries, add "XInput" without ".lib"
#include "X360Controller.hpp"
template <class T>
std::string numberToString (const T& t) {
std::stringstream ss;
ss << t;
return ss.str();
}
int main ()
{
// If not X360 controllers found, exit
if (!xb::Joystick::isAnyXBox360ControllerConnected()) {
cout << "Error: XBox 360 controllers not detected!" << endl;
return 1;
}
// Create the main window
sf::RenderWindow window (sf::VideoMode (800, 600), "XBox 360 Controller for SFML");
// Load font
sf::Font myFont;
if (!myFont.loadFromFile("steelfish_rg.ttf")) {
cout << "Error loading font file!" << endl;
return 1;
}
sf::Text buttonText ("Press any button", myFont);
sf::Text triggerText ("", myFont);
sf::Text stickText ("", myFont);
triggerText.setPosition(0.0f, 50.0f);
stickText.setPosition(0.0f, 150.0f);
// Start the game loop
while (window.isOpen()) {
// Process events
sf::Event event;
while (window.pollEvent(event)) {
// Close window : exit
if (event.type == sf::Event::Closed)
window.close();
}
// Check for buttons
if (xb::Joystick::isButtonPressed (0, xb::Joystick::A))
buttonText.setString ("A");
if (xb::Joystick::isButtonPressed (0, xb::Joystick::B))
buttonText.setString ("B");
if (xb::Joystick::isButtonPressed (0, xb::Joystick::X))
buttonText.setString ("X");
if (xb::Joystick::isButtonPressed (0, xb::Joystick::Y))
buttonText.setString ("Y");
if (xb::Joystick::isButtonPressed (0, xb::Joystick::LB))
buttonText.setString ("LB");
if (xb::Joystick::isButtonPressed (0, xb::Joystick::RB))
buttonText.setString ("RB");
// Check for the triggers (LT and RT)
float lt, rt;
xb::Joystick::getTriggers(0, lt, rt);
triggerText.setString("Left trigger: " + numberToString(lt) + "\nRight trigger: " + numberToString(rt));
// Triggers controls the vibration
xb::Joystick::setVibration(0, lt, rt);
// Get sticks positions
sf::Vector2f ls, rs;
xb::Joystick::getSticksPosition(0, ls, rs);
stickText.setString("Left stick: " + numberToString(ls.x) + "," + numberToString(ls.y) +
"\nRight stick: " + numberToString(rs.x) + "," + numberToString(rs.y));
// Clear screen
window.clear();
// Dibujar
window.draw(buttonText);
window.draw(triggerText);
window.draw(stickText);
// Update the window
window.display();
}
// Stop vibration
xb::Joystick::setVibration(0);
return 0;
}