From 03914d60d321df32d4eff072fe89d8f4cfa15ec6 Mon Sep 17 00:00:00 2001 From: Michael Putters Date: Sat, 24 May 2014 21:05:00 +0200 Subject: [PATCH] Basic XInput support --- rpcs3/Emu/Io/Pad.cpp | 9 ++ rpcs3/Emu/Io/PadHandler.h | 2 +- rpcs3/Emu/Io/XInput/XInputPadHandler.cpp | 181 +++++++++++++++++++++++ rpcs3/Emu/Io/XInput/XInputPadHandler.h | 30 ++++ rpcs3/Gui/MainFrame.cpp | 3 + rpcs3/rpcs3.vcxproj | 2 + rpcs3/rpcs3.vcxproj.filters | 9 ++ 7 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 rpcs3/Emu/Io/XInput/XInputPadHandler.cpp create mode 100644 rpcs3/Emu/Io/XInput/XInputPadHandler.h diff --git a/rpcs3/Emu/Io/Pad.cpp b/rpcs3/Emu/Io/Pad.cpp index 1dde2828c3..48e9e67f46 100644 --- a/rpcs3/Emu/Io/Pad.cpp +++ b/rpcs3/Emu/Io/Pad.cpp @@ -2,6 +2,9 @@ #include "Pad.h" #include "Null/NullPadHandler.h" #include "Windows/WindowsPadHandler.h" +#if defined(_WIN32) +#include "XInput/XInputPadHandler.h" +#endif PadManager::PadManager() : m_pad_handler(nullptr) @@ -25,6 +28,12 @@ void PadManager::Init(const u32 max_connect) m_pad_handler.reset(new WindowsPadHandler()); break; +#if defined(_WIN32) + case 2: + m_pad_handler.reset(new XInputPadHandler()); + break; +#endif + default: case 0: m_pad_handler.reset(new NullPadHandler()); diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 050a5fce3f..104af38a86 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -220,7 +220,7 @@ public: virtual ~PadHandlerBase() = default; //Set value to set pressure/axi to certain level, otherwise 0/255 default - void Key(const u32 code, bool pressed, u16 value=256) + void Key(const u32 code, bool pressed, u16 value=255) { for(Pad& pad : m_pads) { diff --git a/rpcs3/Emu/Io/XInput/XInputPadHandler.cpp b/rpcs3/Emu/Io/XInput/XInputPadHandler.cpp new file mode 100644 index 0000000000..4dcad1e056 --- /dev/null +++ b/rpcs3/Emu/Io/XInput/XInputPadHandler.cpp @@ -0,0 +1,181 @@ +#include "stdafx.h" +#if defined (_WIN32) +#include "XInputPadHandler.h" +#include + +namespace { + const DWORD THREAD_TIMEOUT = 1000; + const DWORD THREAD_SLEEP = 10; + const DWORD THREAD_SLEEP_INACTIVE = 100; + const DWORD MAX_GAMEPADS = 4; + const DWORD XINPUT_GAMEPAD_GUIDE = 0x0400; + const DWORD XINPUT_GAMEPAD_BUTTONS = 16; + const LPCWSTR LIBRARY_FILENAMES[] = { + L"xinput1_4.dll", + L"xinput1_3.dll", + L"xinput1_2.dll", + L"xinput9_1_0.dll" + }; + + inline u16 ConvertAxis(SHORT value) + { + return static_cast((value + 32768l) >> 8); + } +} + +XInputPadHandler::XInputPadHandler() : active(false), thread(nullptr), library(nullptr), xinputGetState(nullptr), xinputEnable(nullptr) +{ +} + +XInputPadHandler::~XInputPadHandler() +{ + Close(); +} + +void XInputPadHandler::Init(const u32 max_connect) +{ + for (auto it : LIBRARY_FILENAMES) + { + library = LoadLibrary(it); + if (library) + { + xinputEnable = reinterpret_cast(GetProcAddress(library, "XInputEnable")); + xinputGetState = reinterpret_cast(GetProcAddress(library, reinterpret_cast(100))); + if (!xinputGetState) + { + xinputGetState = reinterpret_cast(GetProcAddress(library, "XInputGetState")); + } + + if (xinputEnable && xinputGetState) + { + break; + } + + FreeLibrary(library); + library = nullptr; + xinputEnable = nullptr; + xinputGetState = nullptr; + } + } + + if (library) + { + std::memset(&m_info, 0, sizeof m_info); + m_info.max_connect = max_connect; + + for (u32 i = 0, max = std::min(max_connect, u32(MAX_GAMEPADS)); i != max; ++i) + { + m_pads.emplace_back( + CELL_PAD_STATUS_ASSIGN_CHANGES, + CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF, + CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE, + CELL_PAD_DEV_TYPE_STANDARD + ); + auto & pad = m_pads.back(); + + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_UP, CELL_PAD_CTRL_UP); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_DOWN, CELL_PAD_CTRL_DOWN); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_LEFT, CELL_PAD_CTRL_LEFT); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_RIGHT, CELL_PAD_CTRL_RIGHT); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_START, CELL_PAD_CTRL_START); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_BACK, CELL_PAD_CTRL_SELECT); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_LEFT_THUMB, CELL_PAD_CTRL_L3); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_RIGHT_THUMB, CELL_PAD_CTRL_R3); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_LEFT_SHOULDER, CELL_PAD_CTRL_L1); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_RIGHT_SHOULDER, CELL_PAD_CTRL_R1); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_GUIDE, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x0); // Reserved + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_A, CELL_PAD_CTRL_CROSS); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_B, CELL_PAD_CTRL_CIRCLE); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_X, CELL_PAD_CTRL_SQUARE); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_Y, CELL_PAD_CTRL_TRIANGLE); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_L2); + pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_R2); + + pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, 0, 0); + pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, 0, 0); + pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, 0, 0); + pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, 0, 0); + } + + active = true; + thread = CreateThread(NULL, 0, &XInputPadHandler::ThreadProcProxy, this, 0, NULL); + } +} + +void XInputPadHandler::Close() +{ + if (library) + { + if (thread) + { + active = false; + if (WaitForSingleObject(thread, THREAD_TIMEOUT) != WAIT_OBJECT_0) + ConLog.Error("XInput thread could not stop within %d milliseconds", THREAD_TIMEOUT); + thread = nullptr; + } + + FreeLibrary(library); + library = nullptr; + xinputGetState = nullptr; + xinputEnable = nullptr; + } + + m_pads.clear(); +} + +DWORD XInputPadHandler::ThreadProcedure() +{ + while (active) + { + XINPUT_STATE state; + DWORD result; + DWORD online = 0; + + for (DWORD i = 0; i != m_pads.size(); ++i) + { + auto & pad = m_pads[i]; + + result = (* xinputGetState)(i, &state); + switch (result) + { + case ERROR_DEVICE_NOT_CONNECTED: + pad.m_port_status &= ~CELL_PAD_STATUS_CONNECTED; + break; + + case ERROR_SUCCESS: + ++online; + pad.m_port_status |= CELL_PAD_STATUS_CONNECTED; + for (DWORD j = 0; j != XINPUT_GAMEPAD_BUTTONS; ++j) + { + bool pressed = state.Gamepad.wButtons & (1 << j); + pad.m_buttons[j].m_pressed = pressed; + pad.m_buttons[j].m_value = pressed ? 255 : 0; + } + + pad.m_buttons[XINPUT_GAMEPAD_BUTTONS].m_pressed = state.Gamepad.bLeftTrigger > 0; + pad.m_buttons[XINPUT_GAMEPAD_BUTTONS].m_value = state.Gamepad.bLeftTrigger; + pad.m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_pressed = state.Gamepad.bRightTrigger > 0; + pad.m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_value = state.Gamepad.bRightTrigger; + + pad.m_sticks[0].m_value = ConvertAxis(state.Gamepad.sThumbLX); + pad.m_sticks[1].m_value = ConvertAxis(state.Gamepad.sThumbLY); + pad.m_sticks[2].m_value = ConvertAxis(state.Gamepad.sThumbRX); + pad.m_sticks[3].m_value = ConvertAxis(state.Gamepad.sThumbRY); + break; + } + } + + Sleep((online > 0) ? THREAD_SLEEP : THREAD_SLEEP_INACTIVE); + m_info.now_connect = online; + } + + return 0; +} + +DWORD WINAPI XInputPadHandler::ThreadProcProxy(LPVOID parameter) +{ + return reinterpret_cast(parameter)->ThreadProcedure(); +} + +#endif \ No newline at end of file diff --git a/rpcs3/Emu/Io/XInput/XInputPadHandler.h b/rpcs3/Emu/Io/XInput/XInputPadHandler.h new file mode 100644 index 0000000000..ef1db245cb --- /dev/null +++ b/rpcs3/Emu/Io/XInput/XInputPadHandler.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Emu/Io/PadHandler.h" +#include + +class XInputPadHandler : public PadHandlerBase +{ +public: + XInputPadHandler(); + ~XInputPadHandler(); + + void Init(const u32 max_connect) override; + void Close() override; + +private: + typedef void (WINAPI * PFN_XINPUTENABLE)(BOOL); + typedef DWORD (WINAPI * PFN_XINPUTGETSTATE)(DWORD, XINPUT_STATE *); + +private: + DWORD ThreadProcedure(); + static DWORD WINAPI ThreadProcProxy(LPVOID parameter); + +private: + mutable bool active; + HANDLE thread; + HMODULE library; + PFN_XINPUTGETSTATE xinputGetState; + PFN_XINPUTENABLE xinputEnable; + +}; diff --git a/rpcs3/Gui/MainFrame.cpp b/rpcs3/Gui/MainFrame.cpp index 423f39c43e..2762c0988f 100644 --- a/rpcs3/Gui/MainFrame.cpp +++ b/rpcs3/Gui/MainFrame.cpp @@ -401,6 +401,9 @@ void MainFrame::Config(wxCommandEvent& WXUNUSED(event)) cbox_pad_handler->Append("Null"); cbox_pad_handler->Append("Windows"); +#if defined (_WIN32) + cbox_pad_handler->Append("XInput"); +#endif //cbox_pad_handler->Append("DirectInput"); cbox_keyboard_handler->Append("Null"); diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 17809ade40..40ca3c691e 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -350,6 +350,7 @@ + @@ -544,6 +545,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index e16c56bc86..c949f78f34 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -71,6 +71,9 @@ {899523fa-c26a-44ea-b272-73c4585e3821} + + {a77ab520-4399-4c95-a7ee-985c9a5ad694} + @@ -505,6 +508,9 @@ Crypto + + Emu\Io\XInput + @@ -990,5 +996,8 @@ Gui + + Emu\Io\XInput + \ No newline at end of file