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