CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Windows/XinputDevice.cpp
Views: 1401
#include "ppsspp_config.h"12#include <climits>3#include <algorithm>45#include "Common/System/NativeApp.h"6#include "Common/CommonWindows.h"7#include "Common/Log.h"8#include "Common/StringUtils.h"9#include "Common/TimeUtil.h"10#include "Common/Input/InputState.h"11#include "Common/Input/KeyCodes.h"12#include "XinputDevice.h"13#include "Core/Config.h"14#include "Core/Core.h"15#include "Core/KeyMap.h"16#include "Core/HLE/sceCtrl.h"1718// Utilities to dynamically load XInput. Adapted from SDL.1920#if !PPSSPP_PLATFORM(UWP)2122struct XINPUT_CAPABILITIES_EX {23XINPUT_CAPABILITIES Capabilities;24WORD vendorId;25WORD productId;26WORD revisionId;27DWORD a4; //unknown28};2930typedef DWORD (WINAPI *XInputGetState_t) (DWORD dwUserIndex, XINPUT_STATE* pState);31typedef DWORD (WINAPI *XInputSetState_t) (DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);32typedef DWORD (WINAPI *XInputGetCapabilitiesEx_t) (DWORD unknown, DWORD dwUserIndex, DWORD flags, XINPUT_CAPABILITIES_EX *pCapabilities);3334static XInputGetState_t PPSSPP_XInputGetState = nullptr;35static XInputSetState_t PPSSPP_XInputSetState = nullptr;36static XInputGetCapabilitiesEx_t PPSSPP_XInputGetCapabilitiesEx = nullptr;37static DWORD PPSSPP_XInputVersion = 0;38static HMODULE s_pXInputDLL = nullptr;39static int s_XInputDLLRefCount = 0;4041static void UnloadXInputDLL();4243static int LoadXInputDLL() {44DWORD version = 0;4546if (s_pXInputDLL) {47s_XInputDLLRefCount++;48return 0; /* already loaded */49}5051version = (1 << 16) | 4;52s_pXInputDLL = LoadLibrary( L"XInput1_4.dll" ); // 1.4 Ships with Windows 8.53if (!s_pXInputDLL) {54version = (1 << 16) | 3;55s_pXInputDLL = LoadLibrary( L"XInput1_3.dll" ); // 1.3 Ships with Vista and Win7, can be installed as a restributable component.56if (!s_pXInputDLL) {57version = (1 << 16) | 0;58s_pXInputDLL = LoadLibrary( L"XInput9_1_0.dll" ); // 1.0 ships with any Windows since WinXP59}60}61if (!s_pXInputDLL) {62return -1;63}6465PPSSPP_XInputVersion = version;66s_XInputDLLRefCount = 1;6768/* 100 is the ordinal for _XInputGetStateEx, which returns the same struct as XinputGetState, but with extra data in wButtons for the guide button, we think...69Let's try the name first, though - then fall back to ordinal, then to a non-Ex version (xinput9_1_0.dll doesn't have Ex) */70PPSSPP_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetStateEx" );71if ( !PPSSPP_XInputGetState ) {72PPSSPP_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, (LPCSTR)100 );73if ( !PPSSPP_XInputGetState ) {74PPSSPP_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetState" );75}76}7778if ( !PPSSPP_XInputGetState ) {79UnloadXInputDLL();80return -1;81}8283/* Let's try the name first, then fall back to a non-Ex version (xinput9_1_0.dll doesn't have Ex) */84PPSSPP_XInputSetState = (XInputSetState_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputSetStateEx");85if (!PPSSPP_XInputSetState) {86PPSSPP_XInputSetState = (XInputSetState_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputSetState");87}8889if (!PPSSPP_XInputSetState) {90UnloadXInputDLL();91return -1;92}9394if (PPSSPP_XInputVersion >= ((1 << 16) | 4)) {95PPSSPP_XInputGetCapabilitiesEx = (XInputGetCapabilitiesEx_t)GetProcAddress((HMODULE)s_pXInputDLL, (LPCSTR)108);96}9798return 0;99}100101static void UnloadXInputDLL() {102if ( s_pXInputDLL ) {103if (--s_XInputDLLRefCount == 0) {104FreeLibrary( s_pXInputDLL );105s_pXInputDLL = nullptr;106}107}108}109110#else111static int LoadXInputDLL() { return 0; }112static void UnloadXInputDLL() {}113#define PPSSPP_XInputGetState XInputGetState114#define PPSSPP_XInputSetState XInputSetState115#endif116117#ifndef XUSER_MAX_COUNT118#define XUSER_MAX_COUNT 4119#endif120121// Undocumented. Steam annoyingly grabs this button though....122#define XINPUT_GUIDE_BUTTON 0x400123124125// Permanent map. Actual mapping happens elsewhere.126static const struct { int from; InputKeyCode to; } xinput_ctrl_map[] = {127{XINPUT_GAMEPAD_A, NKCODE_BUTTON_A},128{XINPUT_GAMEPAD_B, NKCODE_BUTTON_B},129{XINPUT_GAMEPAD_X, NKCODE_BUTTON_X},130{XINPUT_GAMEPAD_Y, NKCODE_BUTTON_Y},131{XINPUT_GAMEPAD_BACK, NKCODE_BUTTON_SELECT},132{XINPUT_GAMEPAD_START, NKCODE_BUTTON_START},133{XINPUT_GAMEPAD_LEFT_SHOULDER, NKCODE_BUTTON_L1},134{XINPUT_GAMEPAD_RIGHT_SHOULDER, NKCODE_BUTTON_R1},135{XINPUT_GAMEPAD_LEFT_THUMB, NKCODE_BUTTON_THUMBL},136{XINPUT_GAMEPAD_RIGHT_THUMB, NKCODE_BUTTON_THUMBR},137{XINPUT_GAMEPAD_DPAD_UP, NKCODE_DPAD_UP},138{XINPUT_GAMEPAD_DPAD_DOWN, NKCODE_DPAD_DOWN},139{XINPUT_GAMEPAD_DPAD_LEFT, NKCODE_DPAD_LEFT},140{XINPUT_GAMEPAD_DPAD_RIGHT, NKCODE_DPAD_RIGHT},141{XINPUT_GUIDE_BUTTON, NKCODE_HOME},142};143144XinputDevice::XinputDevice() {145if (LoadXInputDLL() != 0) {146WARN_LOG(Log::sceCtrl, "Failed to load XInput! DLL missing");147}148149for (size_t i = 0; i < ARRAY_SIZE(check_delay); ++i) {150check_delay[i] = (int)i;151}152}153154XinputDevice::~XinputDevice() {155UnloadXInputDLL();156}157158int XinputDevice::UpdateState() {159#if !PPSSPP_PLATFORM(UWP)160if (!s_pXInputDLL)161return 0;162#endif163164bool anySuccess = false;165for (int i = 0; i < XUSER_MAX_COUNT; i++) {166XINPUT_STATE state{};167if (check_delay[i]-- > 0)168continue;169DWORD dwResult = PPSSPP_XInputGetState(i, &state);170if (dwResult == ERROR_SUCCESS) {171XINPUT_VIBRATION vibration{};172UpdatePad(i, state, vibration);173anySuccess = true;174} else {175check_delay[i] = 30;176}177}178179// If we get XInput, skip the others. This might not actually be a good idea,180// and was done to avoid conflicts between DirectInput and XInput.181return anySuccess ? UPDATESTATE_SKIP_PAD : 0;182}183184void XinputDevice::UpdatePad(int pad, const XINPUT_STATE &state, XINPUT_VIBRATION &vibration) {185if (!notified_[pad]) {186notified_[pad] = true;187#if !PPSSPP_PLATFORM(UWP)188XINPUT_CAPABILITIES_EX caps{};189if (PPSSPP_XInputGetCapabilitiesEx != nullptr && PPSSPP_XInputGetCapabilitiesEx(1, pad, 0, &caps) == ERROR_SUCCESS) {190KeyMap::NotifyPadConnected(DEVICE_ID_XINPUT_0 + pad, StringFromFormat("Xbox 360 Pad: %d/%d", caps.vendorId, caps.productId));191} else {192#else193{194#endif195KeyMap::NotifyPadConnected(DEVICE_ID_XINPUT_0 + pad, "Xbox 360 Pad");196}197}198ApplyButtons(pad, state);199ApplyVibration(pad, vibration);200201AxisInput axis[6];202int axisCount = 0;203for (int i = 0; i < ARRAY_SIZE(axis); i++) {204axis[i].deviceId = (InputDeviceID)(DEVICE_ID_XINPUT_0 + pad);205}206auto sendAxis = [&](InputAxis axisId, float value, int axisIndex) {207if (value != prevAxisValue_[pad][axisIndex]) {208prevAxisValue_[pad][axisIndex] = value;209axis[axisCount].axisId = axisId;210axis[axisCount].value = value;211axisCount++;212}213};214215sendAxis(JOYSTICK_AXIS_X, (float)state.Gamepad.sThumbLX / 32767.0f, 0);216sendAxis(JOYSTICK_AXIS_Y, (float)state.Gamepad.sThumbLY / 32767.0f, 1);217sendAxis(JOYSTICK_AXIS_Z, (float)state.Gamepad.sThumbRX / 32767.0f, 2);218sendAxis(JOYSTICK_AXIS_RZ, (float)state.Gamepad.sThumbRY / 32767.0f, 3);219sendAxis(JOYSTICK_AXIS_LTRIGGER, (float)state.Gamepad.bLeftTrigger / 255.0f, 4);220sendAxis(JOYSTICK_AXIS_RTRIGGER, (float)state.Gamepad.bRightTrigger / 255.0f, 5);221222if (axisCount) {223NativeAxis(axis, axisCount);224}225226prevState[pad] = state;227check_delay[pad] = 0;228}229230void XinputDevice::ApplyButtons(int pad, const XINPUT_STATE &state) {231const u32 buttons = state.Gamepad.wButtons;232233const u32 downMask = buttons & (~prevButtons_[pad]);234const u32 upMask = (~buttons) & prevButtons_[pad];235prevButtons_[pad] = buttons;236237for (int i = 0; i < ARRAY_SIZE(xinput_ctrl_map); i++) {238if (downMask & xinput_ctrl_map[i].from) {239KeyInput key;240key.deviceId = DEVICE_ID_XINPUT_0 + pad;241key.flags = KEY_DOWN;242key.keyCode = xinput_ctrl_map[i].to;243NativeKey(key);244}245if (upMask & xinput_ctrl_map[i].from) {246KeyInput key;247key.deviceId = DEVICE_ID_XINPUT_0 + pad;248key.flags = KEY_UP;249key.keyCode = xinput_ctrl_map[i].to;250NativeKey(key);251}252}253}254255256void XinputDevice::ApplyVibration(int pad, XINPUT_VIBRATION &vibration) {257if (PSP_IsInited()) {258newVibrationTime_ = time_now_d();259// We have to run PPSSPP_XInputSetState at time intervals260// since it bugs otherwise with very high fast-forward speeds261// and freezes at constant vibration or no vibration at all.262if (newVibrationTime_ - prevVibrationTime >= 1.0 / 64.0) {263if (GetUIState() == UISTATE_INGAME) {264vibration.wLeftMotorSpeed = sceCtrlGetLeftVibration(); // use any value between 0-65535 here265vibration.wRightMotorSpeed = sceCtrlGetRightVibration(); // use any value between 0-65535 here266} else {267vibration.wLeftMotorSpeed = 0;268vibration.wRightMotorSpeed = 0;269}270271if (prevVibration[pad].wLeftMotorSpeed != vibration.wLeftMotorSpeed || prevVibration[pad].wRightMotorSpeed != vibration.wRightMotorSpeed) {272PPSSPP_XInputSetState(pad, &vibration);273prevVibration[pad] = vibration;274}275prevVibrationTime = newVibrationTime_;276}277} else {278DWORD dwResult = PPSSPP_XInputSetState(pad, &vibration);279if (dwResult != ERROR_SUCCESS) {280check_delay[pad] = 30;281}282}283}284285286