1 // Copyright (c) 2012- PPSSPP Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 #include "stdafx.h"
19 #include <limits.h>
20 #include <algorithm>
21 #include <mmsystem.h>
22 #include <XInput.h>
23 
24 #include "Common/Input/InputState.h"
25 #include "Common/Input/KeyCodes.h"
26 #include "Common/System/NativeApp.h"
27 #include "Core/Config.h"
28 #include "Core/HLE/sceCtrl.h"
29 #include "Core/KeyMap.h"
30 #include "Core/Reporting.h"
31 #include "Windows/DinputDevice.h"
32 #pragma comment(lib,"dinput8.lib")
33 
34 //initialize static members of DinputDevice
35 unsigned int                  DinputDevice::pInstances = 0;
36 LPDIRECTINPUT8                DinputDevice::pDI = NULL;
37 std::vector<DIDEVICEINSTANCE> DinputDevice::devices;
38 bool DinputDevice::needsCheck_ = true;
39 
40 // In order from 0.  There can be 128, but most controllers do not have that many.
41 static const int dinput_buttons[] = {
42 	NKCODE_BUTTON_1,
43 	NKCODE_BUTTON_2,
44 	NKCODE_BUTTON_3,
45 	NKCODE_BUTTON_4,
46 	NKCODE_BUTTON_5,
47 	NKCODE_BUTTON_6,
48 	NKCODE_BUTTON_7,
49 	NKCODE_BUTTON_8,
50 	NKCODE_BUTTON_9,
51 	NKCODE_BUTTON_10,
52 	NKCODE_BUTTON_11,
53 	NKCODE_BUTTON_12,
54 	NKCODE_BUTTON_13,
55 	NKCODE_BUTTON_14,
56 	NKCODE_BUTTON_15,
57 	NKCODE_BUTTON_16,
58 };
59 
60 static float NormalizedDeadzoneFilter(short value);
61 
62 #define DIFF  (JOY_POVRIGHT - JOY_POVFORWARD) / 2
63 #define JOY_POVFORWARD_RIGHT	JOY_POVFORWARD + DIFF
64 #define JOY_POVRIGHT_BACKWARD	JOY_POVRIGHT + DIFF
65 #define JOY_POVBACKWARD_LEFT	JOY_POVBACKWARD + DIFF
66 #define JOY_POVLEFT_FORWARD		JOY_POVLEFT + DIFF
67 
68 struct XINPUT_DEVICE_NODE {
69     DWORD dwVidPid;
70     XINPUT_DEVICE_NODE* pNext;
71 };
72 XINPUT_DEVICE_NODE*     g_pXInputDeviceList = NULL;
73 
IsXInputDevice(const GUID * pGuidProductFromDirectInput)74 bool IsXInputDevice( const GUID* pGuidProductFromDirectInput ) {
75     XINPUT_DEVICE_NODE* pNode = g_pXInputDeviceList;
76     while( pNode )
77     {
78         if( pNode->dwVidPid == pGuidProductFromDirectInput->Data1 )
79             return true;
80         pNode = pNode->pNext;
81     }
82 
83     return false;
84 }
85 
getPDI()86 LPDIRECTINPUT8 DinputDevice::getPDI()
87 {
88 	if (pDI == NULL)
89 	{
90 		if (FAILED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&pDI, NULL)))
91 		{
92 			pDI = NULL;
93 		}
94 	}
95 	return pDI;
96 }
97 
DevicesCallback(LPCDIDEVICEINSTANCE lpddi,LPVOID pvRef)98 BOOL CALLBACK DinputDevice::DevicesCallback(
99 	LPCDIDEVICEINSTANCE lpddi,
100 	LPVOID pvRef
101 	)
102 {
103 	//check if a device with the same Instance guid is already saved
104 	auto res = std::find_if(devices.begin(), devices.end(),
105 		[lpddi](const DIDEVICEINSTANCE &to_consider){
106 			return lpddi->guidInstance == to_consider.guidInstance;
107 		});
108 	if (res == devices.end()) //not yet in the devices list
109 	{
110 		// Ignore if device supports XInput
111 		if (!IsXInputDevice(&lpddi->guidProduct)) {
112 			devices.push_back(*lpddi);
113 		}
114 	}
115 	return DIENUM_CONTINUE;
116 }
117 
getDevices(bool refresh)118 void DinputDevice::getDevices(bool refresh)
119 {
120 	if (refresh)
121 	{
122 		getPDI()->EnumDevices(DI8DEVCLASS_GAMECTRL, &DinputDevice::DevicesCallback, NULL, DIEDFL_ATTACHEDONLY);
123 	}
124 }
125 
DinputDevice(int devnum)126 DinputDevice::DinputDevice(int devnum) {
127 	pInstances++;
128 	pDevNum = devnum;
129 	pJoystick = NULL;
130 	memset(lastButtons_, 0, sizeof(lastButtons_));
131 	memset(lastPOV_, 0, sizeof(lastPOV_));
132 	last_lX_ = 0;
133 	last_lY_ = 0;
134 	last_lZ_ = 0;
135 	last_lRx_ = 0;
136 	last_lRy_ = 0;
137 	last_lRz_ = 0;
138 
139 	if (getPDI() == NULL)
140 	{
141 		return;
142 	}
143 
144 	if (devnum >= MAX_NUM_PADS)
145 	{
146 		return;
147 	}
148 
149 	getDevices(false);
150 	if ( (devnum >= (int)devices.size()) || FAILED(getPDI()->CreateDevice(devices.at(devnum).guidInstance, &pJoystick, NULL)))
151 	{
152 		return;
153 	}
154 
155 	if (FAILED(pJoystick->SetDataFormat(&c_dfDIJoystick2))) {
156 		pJoystick->Release();
157 		pJoystick = NULL;
158 		return;
159 	}
160 
161 	DIPROPRANGE diprg;
162 	diprg.diph.dwSize       = sizeof(DIPROPRANGE);
163 	diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
164 	diprg.diph.dwHow        = DIPH_DEVICE;
165 	diprg.diph.dwObj        = 0;
166 	diprg.lMin              = -10000;
167 	diprg.lMax              = 10000;
168 
169 	analog = FAILED(pJoystick->SetProperty(DIPROP_RANGE, &diprg.diph)) ? false : true;
170 
171 	// Other devices suffer if the deadzone is not set.
172 	// TODO: The dead zone will be made configurable in the Control dialog.
173 	DIPROPDWORD dipw;
174 	dipw.diph.dwSize       = sizeof(DIPROPDWORD);
175 	dipw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
176 	dipw.diph.dwHow        = DIPH_DEVICE;
177 	dipw.diph.dwObj        = 0;
178 	// dwData 10000 is deadzone(0% - 100%), multiply by config scalar
179 	dipw.dwData            = 0;
180 
181 	analog |= FAILED(pJoystick->SetProperty(DIPROP_DEADZONE, &dipw.diph)) ? false : true;
182 }
183 
~DinputDevice()184 DinputDevice::~DinputDevice() {
185 	if (pJoystick) {
186 		pJoystick->Release();
187 		pJoystick = NULL;
188 	}
189 
190 	pInstances--;
191 
192 	//the whole instance counter is obviously highly thread-unsafe
193 	//but I don't think creation and destruction operations will be
194 	//happening at the same time and other values like pDI are
195 	//unsafe as well anyway
196 	if (pInstances == 0 && pDI) {
197 		pDI->Release();
198 		pDI = NULL;
199 	}
200 }
201 
SendNativeAxis(int deviceId,int value,int & lastValue,int axisId)202 void SendNativeAxis(int deviceId, int value, int &lastValue, int axisId) {
203 	AxisInput axis;
204 	axis.deviceId = deviceId;
205 	axis.axisId = axisId;
206 	axis.value = (float)value / 10000.0f; // Convert axis to normalised float
207 	NativeAxis(axis);
208 
209 	lastValue = value;
210 }
211 
ValueForAxisId(DIJOYSTATE2 & js,int axisId)212 static LONG *ValueForAxisId(DIJOYSTATE2 &js, int axisId) {
213 	switch (axisId) {
214 	case JOYSTICK_AXIS_X: return &js.lX;
215 	case JOYSTICK_AXIS_Y: return &js.lY;
216 	case JOYSTICK_AXIS_Z: return &js.lZ;
217 	case JOYSTICK_AXIS_RX: return &js.lRx;
218 	case JOYSTICK_AXIS_RY: return &js.lRy;
219 	case JOYSTICK_AXIS_RZ: return &js.lRz;
220 	default: return nullptr;
221 	}
222 }
223 
UpdateState()224 int DinputDevice::UpdateState() {
225 	if (!pJoystick) return -1;
226 
227 	DIJOYSTATE2 js;
228 
229 	if (FAILED(pJoystick->Poll())) {
230 		if(pJoystick->Acquire() == DIERR_INPUTLOST)
231 			return -1;
232 	}
233 
234 	if(FAILED(pJoystick->GetDeviceState(sizeof(DIJOYSTATE2), &js)))
235 		return -1;
236 
237 	ApplyButtons(js);
238 
239 	if (analog)	{
240 		AxisInput axis;
241 		axis.deviceId = DEVICE_ID_PAD_0 + pDevNum;
242 
243 		auto axesToSquare = KeyMap::MappedAxesForDevice(axis.deviceId);
244 
245 		SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lX, last_lX_, JOYSTICK_AXIS_X);
246 		SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lY, last_lY_, JOYSTICK_AXIS_Y);
247 		SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lZ, last_lZ_, JOYSTICK_AXIS_Z);
248 		SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lRx, last_lRx_, JOYSTICK_AXIS_RX);
249 		SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lRy, last_lRy_, JOYSTICK_AXIS_RY);
250 		SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lRz, last_lRz_, JOYSTICK_AXIS_RZ);
251 	}
252 
253 	//check if the values have changed from last time and skip polling the rest of the dinput devices if they did
254 	//this doesn't seem to quite work if only the axis have changed
255 	if ((memcmp(js.rgbButtons, pPrevState.rgbButtons, sizeof(BYTE) * 128) != 0)
256 		|| (memcmp(js.rgdwPOV, pPrevState.rgdwPOV, sizeof(DWORD) * 4) != 0)
257 		|| js.lVX != 0 || js.lVY != 0 || js.lVZ != 0 || js.lVRx != 0 || js.lVRy != 0 || js.lVRz != 0)
258 	{
259 		pPrevState = js;
260 		return UPDATESTATE_SKIP_PAD;
261 	}
262 	return -1;
263 }
264 
ApplyButtons(DIJOYSTATE2 & state)265 void DinputDevice::ApplyButtons(DIJOYSTATE2 &state) {
266 	BYTE *buttons = state.rgbButtons;
267 	u32 downMask = 0x80;
268 
269 	for (int i = 0; i < ARRAY_SIZE(dinput_buttons); ++i) {
270 		if (state.rgbButtons[i] == lastButtons_[i]) {
271 			continue;
272 		}
273 
274 		bool down = (state.rgbButtons[i] & downMask) == downMask;
275 		KeyInput key;
276 		key.deviceId = DEVICE_ID_PAD_0 + pDevNum;
277 		key.flags = down ? KEY_DOWN : KEY_UP;
278 		key.keyCode = dinput_buttons[i];
279 		NativeKey(key);
280 
281 		lastButtons_[i] = state.rgbButtons[i];
282 	}
283 
284 	// Now the POV hat, which can technically go in any degree but usually does not.
285 	if (LOWORD(state.rgdwPOV[0]) != lastPOV_[0]) {
286 		KeyInput dpad[4];
287 		for (int i = 0; i < 4; ++i) {
288 			dpad[i].deviceId = DEVICE_ID_PAD_0 + pDevNum;
289 			dpad[i].flags = KEY_UP;
290 		}
291 		dpad[0].keyCode = NKCODE_DPAD_UP;
292 		dpad[1].keyCode = NKCODE_DPAD_LEFT;
293 		dpad[2].keyCode = NKCODE_DPAD_DOWN;
294 		dpad[3].keyCode = NKCODE_DPAD_RIGHT;
295 
296 		if (LOWORD(state.rgdwPOV[0]) != JOY_POVCENTERED) {
297 			// These are the edges, so we use or.
298 			if (state.rgdwPOV[0] >= JOY_POVLEFT_FORWARD || state.rgdwPOV[0] <= JOY_POVFORWARD_RIGHT) {
299 				dpad[0].flags = KEY_DOWN;
300 			}
301 			if (state.rgdwPOV[0] >= JOY_POVBACKWARD_LEFT && state.rgdwPOV[0] <= JOY_POVLEFT_FORWARD) {
302 				dpad[1].flags = KEY_DOWN;
303 			}
304 			if (state.rgdwPOV[0] >= JOY_POVRIGHT_BACKWARD && state.rgdwPOV[0] <= JOY_POVBACKWARD_LEFT) {
305 				dpad[2].flags = KEY_DOWN;
306 			}
307 			if (state.rgdwPOV[0] >= JOY_POVFORWARD_RIGHT && state.rgdwPOV[0] <= JOY_POVRIGHT_BACKWARD) {
308 				dpad[3].flags = KEY_DOWN;
309 			}
310 		}
311 
312 		NativeKey(dpad[0]);
313 		NativeKey(dpad[1]);
314 		NativeKey(dpad[2]);
315 		NativeKey(dpad[3]);
316 
317 		lastPOV_[0] = LOWORD(state.rgdwPOV[0]);
318 	}
319 }
320 
getNumPads()321 size_t DinputDevice::getNumPads()
322 {
323 	getDevices(needsCheck_);
324 	needsCheck_ = false;
325 	return devices.size();
326 }
327