Added XInput support.

This commit is contained in:
Branimir Karadžić 2014-12-17 21:07:40 -08:00
parent 341eb4e609
commit cdc0c01503
3 changed files with 242 additions and 13 deletions

View File

@ -193,6 +193,13 @@ namespace entry
case Event::Exit:
return true;
case Event::Gamepad:
{
const GamepadEvent* gev = static_cast<const GamepadEvent*>(ev);
DBG("gamepad %d, %d", gev->m_gamepad.idx, gev->m_connected);
}
break;
case Event::Mouse:
{
const MouseEvent* mouse = static_cast<const MouseEvent*>(ev);
@ -327,6 +334,13 @@ namespace entry
case Event::Exit:
return true;
case Event::Gamepad:
{
const GamepadEvent* gev = static_cast<const GamepadEvent*>(ev);
DBG("gamepad %d, %d", gev->m_gamepad.idx, gev->m_connected);
}
break;
case Event::Mouse:
{
const MouseEvent* mouse = static_cast<const MouseEvent*>(ev);

View File

@ -51,6 +51,7 @@ namespace entry
Axis,
Char,
Exit,
Gamepad,
Key,
Mouse,
Size,
@ -90,6 +91,23 @@ namespace entry
uint8_t m_char[4];
};
struct GamepadEvent : public Event
{
ENTRY_IMPLEMENT_EVENT(GamepadEvent, Event::Gamepad);
GamepadHandle m_gamepad;
bool m_connected;
};
struct KeyEvent : public Event
{
ENTRY_IMPLEMENT_EVENT(KeyEvent, Event::Key);
Key::Enum m_key;
uint8_t m_modifiers;
bool m_down;
};
struct MouseEvent : public Event
{
ENTRY_IMPLEMENT_EVENT(MouseEvent, Event::Mouse);
@ -102,15 +120,6 @@ namespace entry
bool m_move;
};
struct KeyEvent : public Event
{
ENTRY_IMPLEMENT_EVENT(KeyEvent, Event::Key);
Key::Enum m_key;
uint8_t m_modifiers;
bool m_down;
};
struct SizeEvent : public Event
{
ENTRY_IMPLEMENT_EVENT(SizeEvent, Event::Size);
@ -156,6 +165,14 @@ namespace entry
m_queue.push(ev);
}
void postGamepadEvent(WindowHandle _handle, GamepadHandle _gamepad, bool _connected)
{
GamepadEvent* ev = new GamepadEvent(_handle);
ev->m_gamepad = _gamepad;
ev->m_connected = _connected;
m_queue.push(ev);
}
void postKeyEvent(WindowHandle _handle, Key::Enum _key, uint8_t _modifiers, bool _down)
{
KeyEvent* ev = new KeyEvent(_handle);

View File

@ -17,9 +17,202 @@
#include <tinystl/string.h>
#include <windowsx.h>
#include <xinput.h>
namespace entry
{
typedef DWORD (WINAPI* PFN_XINPUT_GET_STATE)(DWORD dwUserIndex, XINPUT_STATE* pState);
typedef void (WINAPI* PFN_XINPUT_ENABLE)(BOOL enable); // 1.4+
PFN_XINPUT_GET_STATE XInputGetState;
PFN_XINPUT_ENABLE XInputEnable;
struct XInputRemap
{
uint16_t m_bit;
Key::Enum m_key;
};
static XInputRemap s_xinputRemap[] =
{
{ XINPUT_GAMEPAD_DPAD_UP, Key::GamepadUp },
{ XINPUT_GAMEPAD_DPAD_DOWN, Key::GamepadDown },
{ XINPUT_GAMEPAD_DPAD_LEFT, Key::GamepadLeft },
{ XINPUT_GAMEPAD_DPAD_RIGHT, Key::GamepadRight },
{ XINPUT_GAMEPAD_START, Key::GamepadStart },
{ XINPUT_GAMEPAD_BACK, Key::GamepadBack },
{ XINPUT_GAMEPAD_LEFT_THUMB, Key::GamepadThumbL },
{ XINPUT_GAMEPAD_RIGHT_THUMB, Key::GamepadThumbR },
{ XINPUT_GAMEPAD_LEFT_SHOULDER, Key::GamepadShoulderL },
{ XINPUT_GAMEPAD_RIGHT_SHOULDER, Key::GamepadShoulderR },
{ XINPUT_GAMEPAD_A, Key::GamepadA },
{ XINPUT_GAMEPAD_B, Key::GamepadB },
{ XINPUT_GAMEPAD_X, Key::GamepadX },
{ XINPUT_GAMEPAD_Y, Key::GamepadY },
};
struct XInput
{
XInput()
: m_xinputdll(NULL)
{
memset(m_connected, 0, sizeof(m_connected) );
memset(m_state, 0, sizeof(m_state) );
m_deadzone[GamepadAxis::LeftX ] =
m_deadzone[GamepadAxis::LeftY ] = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
m_deadzone[GamepadAxis::RightX] =
m_deadzone[GamepadAxis::RightY] = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
m_deadzone[GamepadAxis::LeftZ ] =
m_deadzone[GamepadAxis::RightZ] = XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
memset(m_flip, 1, sizeof(m_flip) );
m_flip[GamepadAxis::LeftY ] =
m_flip[GamepadAxis::RightY] = -1;
}
BX_NO_INLINE void init()
{
m_xinputdll = bx::dlopen(XINPUT_DLL_A);
if (NULL != m_xinputdll)
{
XInputGetState = (PFN_XINPUT_GET_STATE)bx::dlsym(m_xinputdll, "XInputGetState");
// XInputEnable = (PFN_XINPUT_ENABLE )bx::dlsym(m_xinputdll, "XInputEnable" );
}
}
void shutdown()
{
bx::dlclose(m_xinputdll);
}
bool filter(GamepadAxis::Enum _axis, int32_t _old, int32_t* _value)
{
const int32_t deadzone = m_deadzone[_axis];
int32_t value = *_value;
value = value > deadzone || value < -deadzone ? value : 0;
*_value = value * m_flip[_axis];
return _old != value;
}
void update(EventQueue& _eventQueue)
{
WindowHandle defaultWindow = { 0 };
GamepadHandle handle = { 0 };
for (uint32_t ii = 0; ii < BX_COUNTOF(m_state); ++ii)
{
XINPUT_STATE state;
DWORD result = XInputGetState(ii, &state);
bool connected = ERROR_SUCCESS == result;
if (connected != m_connected[ii])
{
_eventQueue.postGamepadEvent(defaultWindow, handle, connected);
}
m_connected[ii] = connected;
if (connected
&& m_state[ii].dwPacketNumber != state.dwPacketNumber)
{
XINPUT_GAMEPAD& gamepad = m_state[ii].Gamepad;
const uint16_t changed = gamepad.wButtons ^ state.Gamepad.wButtons;
const uint16_t current = gamepad.wButtons;
if (0 != changed)
{
for (uint32_t jj = 0; jj < BX_COUNTOF(s_xinputRemap); ++jj)
{
uint16_t bit = s_xinputRemap[jj].m_bit;
if (bit & changed)
{
_eventQueue.postKeyEvent(defaultWindow, s_xinputRemap[jj].m_key, 0, 0 == (current & bit) );
}
}
gamepad.wButtons = state.Gamepad.wButtons;
}
if (gamepad.bLeftTrigger != state.Gamepad.bLeftTrigger)
{
int32_t value = state.Gamepad.bLeftTrigger;
if (filter(GamepadAxis::LeftZ, gamepad.bLeftTrigger, &value) )
{
_eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::LeftZ, value);
}
gamepad.bLeftTrigger = state.Gamepad.bLeftTrigger;
}
if (gamepad.bRightTrigger != state.Gamepad.bRightTrigger)
{
int32_t value = state.Gamepad.bRightTrigger;
if (filter(GamepadAxis::RightZ, gamepad.bRightTrigger, &value) )
{
_eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::RightZ, value);
}
gamepad.bRightTrigger = state.Gamepad.bRightTrigger;
}
if (gamepad.sThumbLX != state.Gamepad.sThumbLX)
{
int32_t value = state.Gamepad.sThumbLX;
if (filter(GamepadAxis::LeftX, gamepad.sThumbLX, &value) )
{
_eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::LeftX, value);
}
gamepad.sThumbLX = state.Gamepad.sThumbLX;
}
if (gamepad.sThumbLY != state.Gamepad.sThumbLY)
{
int32_t value = state.Gamepad.sThumbLY;
if (filter(GamepadAxis::LeftY, gamepad.sThumbLY, &value) )
{
_eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::LeftY, value);
}
gamepad.sThumbLY = state.Gamepad.sThumbLY;
}
if (gamepad.sThumbRX != state.Gamepad.sThumbRX)
{
int32_t value = state.Gamepad.sThumbRX;
if (filter(GamepadAxis::RightX, gamepad.sThumbRX, &value) )
{
_eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::RightX, value);
}
gamepad.sThumbRX = state.Gamepad.sThumbRX;
}
if (gamepad.sThumbRY != state.Gamepad.sThumbRY)
{
int32_t value = state.Gamepad.sThumbRY;
if (filter(GamepadAxis::RightY, gamepad.sThumbRY, &value) )
{
_eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::RightY, value);
}
gamepad.sThumbRY = state.Gamepad.sThumbRY;
}
}
}
}
void* m_xinputdll;
int32_t m_deadzone[GamepadAxis::Count];
int8_t m_flip[GamepadAxis::Count];
XINPUT_STATE m_state[ENTRY_CONFIG_MAX_GAMEPADS];
bool m_connected[ENTRY_CONFIG_MAX_GAMEPADS];
};
XInput s_xinput;
enum
{
WM_USER_WINDOW_CREATE = WM_USER,
@ -184,6 +377,8 @@ namespace entry
{
SetDllDirectory(".");
s_xinput.init();
HINSTANCE instance = (HINSTANCE)GetModuleHandle(NULL);
WNDCLASSEX wnd;
@ -222,9 +417,9 @@ namespace entry
adjust(m_hwnd[0], ENTRY_DEFAULT_WIDTH, ENTRY_DEFAULT_HEIGHT, true);
clear(m_hwnd[0]);
m_width = ENTRY_DEFAULT_WIDTH;
m_height = ENTRY_DEFAULT_HEIGHT;
m_oldWidth = ENTRY_DEFAULT_WIDTH;
m_width = ENTRY_DEFAULT_WIDTH;
m_height = ENTRY_DEFAULT_HEIGHT;
m_oldWidth = ENTRY_DEFAULT_WIDTH;
m_oldHeight = ENTRY_DEFAULT_HEIGHT;
MainThreadEntry mte;
@ -242,7 +437,8 @@ namespace entry
while (!m_exit)
{
WaitMessage();
s_xinput.update(m_eventQueue);
WaitForInputIdle(GetCurrentProcess(), 16);
while (0 != PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) )
{
@ -255,6 +451,8 @@ namespace entry
DestroyWindow(m_hwnd[0]);
s_xinput.shutdown();
return 0;
}