1 #include "ppsspp_config.h"
2
3 #include <climits>
4 #include <algorithm>
5
6 #include "Common/System/NativeApp.h"
7 #include "Core/Config.h"
8 #include "Common/CommonWindows.h"
9 #include "Common/Log.h"
10 #include "Common/TimeUtil.h"
11 #include "Common/Input/InputState.h"
12 #include "Common/Input/KeyCodes.h"
13 #include "XinputDevice.h"
14 #include "Core/Core.h"
15 #include "Core/KeyMap.h"
16 #include "Core/HLE/sceCtrl.h"
17
18 static double newVibrationTime = 0.0;
19
20 // Utilities to dynamically load XInput. Adapted from SDL.
21
22 #if !PPSSPP_PLATFORM(UWP)
23
24 typedef DWORD (WINAPI *XInputGetState_t) (DWORD dwUserIndex, XINPUT_STATE* pState);
25 typedef DWORD (WINAPI *XInputSetState_t) (DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
26
27 static XInputGetState_t PPSSPP_XInputGetState = nullptr;
28 static XInputSetState_t PPSSPP_XInputSetState = nullptr;
29 static DWORD PPSSPP_XInputVersion = 0;
30 static HMODULE s_pXInputDLL = 0;
31 static int s_XInputDLLRefCount = 0;
32
33 static void UnloadXInputDLL();
34
LoadXInputDLL()35 static int LoadXInputDLL() {
36 DWORD version = 0;
37
38 if (s_pXInputDLL) {
39 s_XInputDLLRefCount++;
40 return 0; /* already loaded */
41 }
42
43 version = (1 << 16) | 4;
44 s_pXInputDLL = LoadLibrary( L"XInput1_4.dll" ); // 1.4 Ships with Windows 8.
45 if (!s_pXInputDLL) {
46 version = (1 << 16) | 3;
47 s_pXInputDLL = LoadLibrary( L"XInput1_3.dll" ); // 1.3 Ships with Vista and Win7, can be installed as a restributable component.
48 if (!s_pXInputDLL) {
49 version = (1 << 16) | 0;
50 s_pXInputDLL = LoadLibrary( L"XInput9_1_0.dll" ); // 1.0 ships with any Windows since WinXP
51 }
52 }
53 if (!s_pXInputDLL) {
54 return -1;
55 }
56
57 PPSSPP_XInputVersion = version;
58 s_XInputDLLRefCount = 1;
59
60 /* 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...
61 Let'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) */
62 PPSSPP_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetStateEx" );
63 if ( !PPSSPP_XInputGetState ) {
64 PPSSPP_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, (LPCSTR)100 );
65 if ( !PPSSPP_XInputGetState ) {
66 PPSSPP_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetState" );
67 }
68 }
69
70 if ( !PPSSPP_XInputGetState ) {
71 UnloadXInputDLL();
72 return -1;
73 }
74
75 /* Let's try the name first, then fall back to a non-Ex version (xinput9_1_0.dll doesn't have Ex) */
76 PPSSPP_XInputSetState = (XInputSetState_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputSetStateEx");
77 if (!PPSSPP_XInputSetState) {
78 PPSSPP_XInputSetState = (XInputSetState_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputSetState");
79 }
80
81 if (!PPSSPP_XInputSetState) {
82 UnloadXInputDLL();
83 return -1;
84 }
85
86 return 0;
87 }
88
UnloadXInputDLL()89 static void UnloadXInputDLL() {
90 if ( s_pXInputDLL ) {
91 if (--s_XInputDLLRefCount == 0) {
92 FreeLibrary( s_pXInputDLL );
93 s_pXInputDLL = NULL;
94 }
95 }
96 }
97
98 #else
LoadXInputDLL()99 static int LoadXInputDLL() { return 0; }
UnloadXInputDLL()100 static void UnloadXInputDLL() {}
101 #define PPSSPP_XInputGetState XInputGetState
102 #define PPSSPP_XInputSetState XInputSetState
103 #endif
104
105 #ifndef XUSER_MAX_COUNT
106 #define XUSER_MAX_COUNT 4
107 #endif
108
109 // Undocumented. Steam annoyingly grabs this button though....
110 #define XINPUT_GUIDE_BUTTON 0x400
111
112
113 // Permanent map. Actual mapping happens elsewhere.
114 static const struct {int from, to;} xinput_ctrl_map[] = {
115 {XINPUT_GAMEPAD_A, NKCODE_BUTTON_A},
116 {XINPUT_GAMEPAD_B, NKCODE_BUTTON_B},
117 {XINPUT_GAMEPAD_X, NKCODE_BUTTON_X},
118 {XINPUT_GAMEPAD_Y, NKCODE_BUTTON_Y},
119 {XINPUT_GAMEPAD_BACK, NKCODE_BUTTON_SELECT},
120 {XINPUT_GAMEPAD_START, NKCODE_BUTTON_START},
121 {XINPUT_GAMEPAD_LEFT_SHOULDER, NKCODE_BUTTON_L1},
122 {XINPUT_GAMEPAD_RIGHT_SHOULDER, NKCODE_BUTTON_R1},
123 {XINPUT_GAMEPAD_LEFT_THUMB, NKCODE_BUTTON_THUMBL},
124 {XINPUT_GAMEPAD_RIGHT_THUMB, NKCODE_BUTTON_THUMBR},
125 {XINPUT_GAMEPAD_DPAD_UP, NKCODE_DPAD_UP},
126 {XINPUT_GAMEPAD_DPAD_DOWN, NKCODE_DPAD_DOWN},
127 {XINPUT_GAMEPAD_DPAD_LEFT, NKCODE_DPAD_LEFT},
128 {XINPUT_GAMEPAD_DPAD_RIGHT, NKCODE_DPAD_RIGHT},
129 {XINPUT_GUIDE_BUTTON, NKCODE_HOME},
130 };
131
132 static const unsigned int xinput_ctrl_map_size = sizeof(xinput_ctrl_map) / sizeof(xinput_ctrl_map[0]);
133
XinputDevice()134 XinputDevice::XinputDevice() {
135 if (LoadXInputDLL() != 0) {
136 WARN_LOG(SCECTRL, "Failed to load XInput! DLL missing");
137 }
138
139 for (size_t i = 0; i < ARRAY_SIZE(check_delay); ++i) {
140 check_delay[i] = (int)i;
141 }
142 }
143
~XinputDevice()144 XinputDevice::~XinputDevice() {
145 UnloadXInputDLL();
146 }
147
148 struct Stick {
StickStick149 Stick (float x_, float y_, float scale) : x(x_ * scale), y(y_ * scale) {}
150 float x;
151 float y;
152 };
153
NormalizedDeadzoneDiffers(u8 x1,u8 x2,const u8 thresh)154 bool NormalizedDeadzoneDiffers(u8 x1, u8 x2, const u8 thresh) {
155 if (x1 > thresh || x2 > thresh) {
156 return x1 != x2;
157 }
158 return false;
159 }
160
UpdateState()161 int XinputDevice::UpdateState() {
162 #if !PPSSPP_PLATFORM(UWP)
163 if (!s_pXInputDLL)
164 return 0;
165 #endif
166
167 bool anySuccess = false;
168 for (int i = 0; i < XUSER_MAX_COUNT; i++) {
169 XINPUT_STATE state;
170 ZeroMemory(&state, sizeof(XINPUT_STATE));
171 XINPUT_VIBRATION vibration;
172 ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
173 if (check_delay[i]-- > 0)
174 continue;
175 DWORD dwResult = PPSSPP_XInputGetState(i, &state);
176 if (dwResult == ERROR_SUCCESS) {
177 UpdatePad(i, state, vibration);
178 anySuccess = true;
179 } else {
180 check_delay[i] = 30;
181 }
182 }
183
184 // If we get XInput, skip the others. This might not actually be a good idea,
185 // and was done to avoid conflicts between DirectInput and XInput.
186 return anySuccess ? UPDATESTATE_SKIP_PAD : 0;
187 }
188
UpdatePad(int pad,const XINPUT_STATE & state,XINPUT_VIBRATION & vibration)189 void XinputDevice::UpdatePad(int pad, const XINPUT_STATE &state, XINPUT_VIBRATION &vibration) {
190 static bool notified = false;
191 if (!notified) {
192 notified = true;
193 KeyMap::NotifyPadConnected("Xbox 360 Pad");
194 }
195 ApplyButtons(pad, state);
196 ApplyVibration(pad, vibration);
197
198 AxisInput axis;
199 axis.deviceId = DEVICE_ID_XINPUT_0 + pad;
200 auto sendAxis = [&](AndroidJoystickAxis axisId, float value) {
201 axis.axisId = axisId;
202 axis.value = value;
203 NativeAxis(axis);
204 };
205
206 sendAxis(JOYSTICK_AXIS_X, (float)state.Gamepad.sThumbLX / 32767.0f);
207 sendAxis(JOYSTICK_AXIS_Y, (float)state.Gamepad.sThumbLY / 32767.0f);
208 sendAxis(JOYSTICK_AXIS_Z, (float)state.Gamepad.sThumbRX / 32767.0f);
209 sendAxis(JOYSTICK_AXIS_RZ, (float)state.Gamepad.sThumbRY / 32767.0f);
210
211 if (NormalizedDeadzoneDiffers(prevState[pad].Gamepad.bLeftTrigger, state.Gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD)) {
212 sendAxis(JOYSTICK_AXIS_LTRIGGER, (float)state.Gamepad.bLeftTrigger / 255.0f);
213 }
214
215 if (NormalizedDeadzoneDiffers(prevState[pad].Gamepad.bRightTrigger, state.Gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD)) {
216 sendAxis(JOYSTICK_AXIS_RTRIGGER, (float)state.Gamepad.bRightTrigger / 255.0f);
217 }
218
219 prevState[pad] = state;
220 check_delay[pad] = 0;
221 }
222
ApplyButtons(int pad,const XINPUT_STATE & state)223 void XinputDevice::ApplyButtons(int pad, const XINPUT_STATE &state) {
224 u32 buttons = state.Gamepad.wButtons;
225
226 u32 downMask = buttons & (~prevButtons[pad]);
227 u32 upMask = (~buttons) & prevButtons[pad];
228 prevButtons[pad] = buttons;
229
230 for (int i = 0; i < xinput_ctrl_map_size; i++) {
231 if (downMask & xinput_ctrl_map[i].from) {
232 KeyInput key;
233 key.deviceId = DEVICE_ID_XINPUT_0 + pad;
234 key.flags = KEY_DOWN;
235 key.keyCode = xinput_ctrl_map[i].to;
236 NativeKey(key);
237 }
238 if (upMask & xinput_ctrl_map[i].from) {
239 KeyInput key;
240 key.deviceId = DEVICE_ID_XINPUT_0 + pad;
241 key.flags = KEY_UP;
242 key.keyCode = xinput_ctrl_map[i].to;
243 NativeKey(key);
244 }
245 }
246 }
247
248
ApplyVibration(int pad,XINPUT_VIBRATION & vibration)249 void XinputDevice::ApplyVibration(int pad, XINPUT_VIBRATION &vibration) {
250 if (PSP_IsInited()) {
251 newVibrationTime = time_now_d();
252 // We have to run PPSSPP_XInputSetState at time intervals
253 // since it bugs otherwise with very high fast-forward speeds
254 // and freezes at constant vibration or no vibration at all.
255 if (newVibrationTime - prevVibrationTime >= 1.0 / 64.0) {
256 if (GetUIState() == UISTATE_INGAME) {
257 vibration.wLeftMotorSpeed = sceCtrlGetLeftVibration(); // use any value between 0-65535 here
258 vibration.wRightMotorSpeed = sceCtrlGetRightVibration(); // use any value between 0-65535 here
259 } else {
260 vibration.wLeftMotorSpeed = 0;
261 vibration.wRightMotorSpeed = 0;
262 }
263
264 if (prevVibration[pad].wLeftMotorSpeed != vibration.wLeftMotorSpeed || prevVibration[pad].wRightMotorSpeed != vibration.wRightMotorSpeed) {
265 PPSSPP_XInputSetState(pad, &vibration);
266 prevVibration[pad] = vibration;
267 }
268 prevVibrationTime = newVibrationTime;
269 }
270 } else {
271 DWORD dwResult = PPSSPP_XInputSetState(pad, &vibration);
272 if (dwResult != ERROR_SUCCESS) {
273 check_delay[pad] = 30;
274 }
275 }
276 }
277
278