1 /*  Copyright 2006,2013 Theo Berkau
2 
3     This file is part of Yabause.
4 
5     Yabause is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     Yabause is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with Yabause; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18 */
19 
20 /*! \file perdx.c
21     \brief Direct X peripheral interface.
22 */
23 
24 #include <windows.h>
25 #ifdef __MINGW32__
26 #undef HAVE_XINPUT
27 #endif
28 #define COBJMACROS
29 #ifdef HAVE_XINPUT
30 #include <wbemidl.h>
31 #include <wbemcli.h>
32 #include <oleauto.h>
33 #include <xinput.h>
34 #endif
35 #include "debug.h"
36 #include "peripheral.h"
37 #include "perdx.h"
38 #include "vdp1.h"
39 #include "vdp2.h"
40 #include "yui.h"
41 #include "movie.h"
42 #include "error.h"
43 
44 enum {
45    EMUTYPE_NONE=0,
46    EMUTYPE_STANDARDPAD,
47    EMUTYPE_ANALOGPAD,
48    EMUTYPE_STUNNER,
49    EMUTYPE_MOUSE,
50    EMUTYPE_KEYBOARD
51 };
52 
53 #define DX_PADOFFSET 24
54 #define DX_STICKOFFSET 8
55 #define DX_MAKEKEY(p, s, a) ( ((p) << DX_PADOFFSET) | ((s) << DX_STICKOFFSET) | (a) )
56 #define DX_PerAxisValue(p, s, a, v) PerAxisValue(DX_MAKEKEY(p, s, a), (v))
57 #define DX_PerKeyUp(p, s, a) PerKeyUp(DX_MAKEKEY(p, s, a) )
58 #define DX_PerKeyDown(p, s, a) PerKeyDown(DX_MAKEKEY(p, s, a) )
59 
60 int Check_Skip_Key();
61 
62 PerInterface_struct PERDIRECTX = {
63 PERCORE_DIRECTX,
64 #ifdef HAVE_XINPUT
65 "DirectX/XInput Input Interface",
66 #else
67 "DirectX Input Interface",
68 #endif
69 PERDXInit,
70 PERDXDeInit,
71 PERDXHandleEvents,
72 PERDXScan,
73 TRUE,
74 PERDXFlush,
75 };
76 
77 LPDIRECTINPUT8 lpDI8 = NULL;
78 struct
79 {
80 	BOOL is_xinput_device;
81 	int user_index;
82 	LPDIRECTINPUTDEVICE8 lpDIDevice;
83 } dev_list[256]; // I hope that's enough
84 
85 u32 num_devices=0;
86 
87 static unsigned int PERCORE_INITIALIZED = 0;
88 
89 const char *mouse_names[] = {
90 "A",
91 "B",
92 "C",
93 "Start",
94 NULL
95 };
96 
97 #define PAD_DIR_AXISLEFT        0
98 #define PAD_DIR_AXISRIGHT       1
99 #define PAD_DIR_AXISUP          2
100 #define PAD_DIR_AXISDOWN        3
101 #define PAD_DIR_POVUP           0
102 #define PAD_DIR_POVRIGHT        1
103 #define PAD_DIR_POVDOWN         2
104 #define PAD_DIR_POVLEFT         3
105 
106 HWND DXGetWindow ();
107 
108 #ifdef HAVE_XINPUT
109 #define SAFE_RELEASE(p)      { if(p) { (p)->lpVtbl->Release((p)); (p)=NULL; } }
110 #define SAFE_DELETE(p)  { if(p) { free (p);     (p)=NULL; } }
111 
112 typedef struct
113 {
114 	DWORD dwVidPid;
115 	struct XINPUT_DEVICE_NODE* pNext;
116 } XINPUT_DEVICE_NODE;
117 
118 XINPUT_DEVICE_NODE *g_pXInputDeviceList = NULL;
119 BOOL bCleanupCOM=FALSE;
120 
121 //////////////////////////////////////////////////////////////////////////////
122 
SetupForIsXInputDevice()123 HRESULT SetupForIsXInputDevice()
124 {
125 IWbemLocator * pIWbemLocator = NULL;
126 	IEnumWbemClassObject * pEnumDevices = NULL;
127 	IWbemClassObject * pDevices[20] = {0};
128 	IWbemServices * pIWbemServices = NULL;
129 	BSTR bstrNamespace = NULL;
130 	BSTR bstrDeviceID = NULL;
131 	BSTR bstrClassName = NULL;
132 	DWORD uReturned = 0;
133 	BOOL bIsXinputDevice = FALSE;
134 	UINT iDevice = 0;
135 	VARIANT var;
136 	HRESULT hr;
137 
138 	hr = CoInitialize(NULL);
139 	bCleanupCOM = SUCCEEDED(hr);
140 
141 	hr = CoCreateInstance(&CLSID_WbemLocator,
142 	                      NULL,
143 	                      CLSCTX_INPROC_SERVER,
144 	                      &IID_IWbemLocator,
145 	                      (LPVOID *) &pIWbemLocator);
146 	if (FAILED(hr) || pIWbemLocator == NULL) {
147 		YabSetError(YAB_ERR_CANNOTINIT, "pIWbemLocator");
148 		goto bail;
149 	}
150 
151 	bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); if (bstrNamespace == NULL) {goto bail;}
152 	bstrClassName = SysAllocString(L"Win32_PNPEntity");    if (bstrClassName == NULL) {goto bail;}
153 	bstrDeviceID  = SysAllocString(L"DeviceID");           if (bstrDeviceID == NULL)  {goto bail;}
154 
155 	hr = IWbemLocator_ConnectServer(pIWbemLocator, bstrNamespace, NULL, NULL, 0L,
156 	                                0L, NULL, NULL, &pIWbemServices);
157 	if (FAILED(hr) || pIWbemServices == NULL)
158    {
159 		YabSetError(YAB_ERR_CANNOTINIT, "pIWbemServices");
160 		goto bail;
161 	}
162 
163 	CoSetProxyBlanket((IUnknown *) pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
164 	                  RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
165 
166 	hr = IWbemServices_CreateInstanceEnum(pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices);
167 	if (FAILED(hr) || pEnumDevices == NULL)
168    {
169 		YabSetError(YAB_ERR_CANNOTINIT, "pEnumDevices");
170 		goto bail;
171 	}
172 
173 	for (;;)
174    {
175 		// Get 20 at a time
176 		hr = IEnumWbemClassObject_Next(pEnumDevices, 10000, 20, pDevices, &uReturned);
177 		if( FAILED(hr) ||
178 			uReturned == 0 )
179 			break;
180 		for (iDevice = 0; iDevice < uReturned; iDevice++)
181       {
182 			hr = IWbemClassObject_Get(pDevices[iDevice], bstrDeviceID, 0L, &var, NULL, NULL);
183 			if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) {
184 				if (wcsstr(var.bstrVal, L"IG_")) {
185 					// If it does, then get the VID/PID from var.bstrVal
186 					DWORD dwPid = 0, dwVid = 0;
187 					WCHAR* strVid;
188 					WCHAR* strPid;
189 					DWORD dwVidPid;
190 					XINPUT_DEVICE_NODE* pNewNode;
191 
192 					strVid = wcsstr( var.bstrVal, L"VID_" );
193 					if( strVid && swscanf( strVid, L"VID_%4X", &dwVid ) != 1 )
194 						dwVid = 0;
195 					strPid = wcsstr( var.bstrVal, L"PID_" );
196 					if( strPid && swscanf( strPid, L"PID_%4X", &dwPid ) != 1 )
197 						dwPid = 0;
198 
199 					dwVidPid = MAKELONG( dwVid, dwPid );
200 
201 					// Add the VID/PID to a linked list
202 					pNewNode = malloc(sizeof(XINPUT_DEVICE_NODE));
203 					if( pNewNode )
204 					{
205 						pNewNode->dwVidPid = dwVidPid;
206 						pNewNode->pNext = (struct XINPUT_DEVICE_NODE *)g_pXInputDeviceList;
207 						g_pXInputDeviceList = pNewNode;
208 					}
209 				}
210 			}
211 			SAFE_RELEASE( pDevices[iDevice] );
212 		}
213 	}
214 
215 bail:
216 	if(bstrNamespace)
217 		SysFreeString(bstrNamespace);
218 	if(bstrDeviceID)
219 		SysFreeString(bstrDeviceID);
220 	if(bstrClassName)
221 		SysFreeString(bstrClassName);
222 	for( iDevice=0; iDevice<20; iDevice++ )
223 		SAFE_RELEASE( pDevices[iDevice] );
224 	SAFE_RELEASE( pEnumDevices );
225 	SAFE_RELEASE( pIWbemLocator );
226 	SAFE_RELEASE( pIWbemServices );
227 
228 	return hr;
229 }
230 
231 //////////////////////////////////////////////////////////////////////////////
232 
CleanupForIsXInputDevice()233 void CleanupForIsXInputDevice()
234 {
235 	// Cleanup linked list
236 	XINPUT_DEVICE_NODE* pNode = g_pXInputDeviceList;
237 	while( pNode )
238 	{
239 		XINPUT_DEVICE_NODE* pDelete = pNode;
240 		pNode = (XINPUT_DEVICE_NODE *)pNode->pNext;
241 		SAFE_DELETE( pDelete );
242 	}
243    g_pXInputDeviceList = NULL;
244 
245 	if( bCleanupCOM )
246 		CoUninitialize();
247 }
248 
249 //////////////////////////////////////////////////////////////////////////////
250 
IsXInputDevice(const GUID * guidProduct)251 BOOL IsXInputDevice( const GUID* guidProduct )
252 {
253 	// Check each xinput device to see if this device's vid/pid matches
254 	XINPUT_DEVICE_NODE* pNode = g_pXInputDeviceList;
255 	while( pNode )
256 	{
257 		if( pNode->dwVidPid == guidProduct->Data1 )
258 			return TRUE;
259 		pNode = (XINPUT_DEVICE_NODE*)pNode->pNext;
260 	}
261 
262    return FALSE;
263 }
264 #endif
265 
266 //////////////////////////////////////////////////////////////////////////////
267 
EnumPeripheralsCallback(LPCDIDEVICEINSTANCE lpddi,LPVOID pvRef)268 BOOL CALLBACK EnumPeripheralsCallback (LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
269 {
270    if (GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_GAMEPAD ||
271        GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_JOYSTICK ||
272        GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_1STPERSON)//xbox one controller uses this type
273    {
274 #ifdef HAVE_XINPUT
275 		if (IsXInputDevice(&lpddi->guidProduct))
276 		{
277 			dev_list[num_devices].lpDIDevice = NULL;
278 			dev_list[num_devices].is_xinput_device = TRUE;
279 			dev_list[num_devices].user_index=((int *)pvRef)[0];
280 			((int *)pvRef)[0]++;
281 			num_devices++;
282 		}
283 		else
284 #endif
285 		{
286 			dev_list[num_devices].is_xinput_device = FALSE;
287 			if (SUCCEEDED(IDirectInput8_CreateDevice(lpDI8, &lpddi->guidInstance, &dev_list[num_devices].lpDIDevice,
288 				NULL) ))
289 			   num_devices++;
290 		}
291 	}
292 
293 	return DIENUM_CONTINUE;
294 }
295 
296 //////////////////////////////////////////////////////////////////////////////
297 
PERDXInit(void)298 int PERDXInit(void)
299 {
300 	char tempstr[512];//sprintf(tempstr, "Input. DirectInput8Create error: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret));
301 	HRESULT ret;
302 	int user_index=0;
303 	u32 i;
304 
305 	if (PERCORE_INITIALIZED)
306 		return 0;
307 
308 	if (FAILED((ret = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
309 		&IID_IDirectInput8, (LPVOID *)&lpDI8, NULL)) ))
310 	{
311 		sprintf(tempstr, "Input. DirectInput8Create error: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret));
312 		YabSetError(YAB_ERR_CANNOTINIT, tempstr);
313 		return -1;
314 	}
315 
316 #ifdef HAVE_XINPUT
317 	SetupForIsXInputDevice();
318 #endif
319 	num_devices = 0;
320 	IDirectInput8_EnumDevices(lpDI8, DI8DEVCLASS_ALL, EnumPeripheralsCallback,
321 							&user_index, DIEDFL_ATTACHEDONLY);
322 
323 #ifdef HAVE_XINPUT
324 	CleanupForIsXInputDevice();
325 #endif
326 
327 	for (i = 0; i < num_devices; i++)
328 	{
329 		if (!dev_list[i].is_xinput_device)
330 		{
331 			if( FAILED( ret = IDirectInputDevice8_SetDataFormat(dev_list[i].lpDIDevice, &c_dfDIJoystick2 ) ) )
332 				return -1;
333 
334 			// Set the cooperative level to let DInput know how this device should
335 			// interact with the system and with other DInput applications.
336 			if( FAILED( ret = IDirectInputDevice8_SetCooperativeLevel( dev_list[i].lpDIDevice, DXGetWindow(),
337 				DISCL_NONEXCLUSIVE | DISCL_BACKGROUND
338 				/* DISCL_EXCLUSIVE |
339 				DISCL_FOREGROUND */ ) ) )
340 				return -1;
341 		}
342 	}
343 
344 	//LoadDefaultPort1A();
345 	PERCORE_INITIALIZED = 1;
346 	return 0;
347 }
348 
349 //////////////////////////////////////////////////////////////////////////////
350 
PERDXDeInit(void)351 void PERDXDeInit(void)
352 {
353 	u32 i;
354 
355 	for (i = 0; i < num_devices; i++)
356 	{
357 		if (dev_list[i].lpDIDevice)
358 		{
359 			IDirectInputDevice8_Unacquire(dev_list[i].lpDIDevice);
360 			IDirectInputDevice8_Release(dev_list[i].lpDIDevice);
361 			dev_list[i].lpDIDevice = NULL;
362 		}
363 	}
364 
365 	if (lpDI8)
366 	{
367 		IDirectInput8_Release(lpDI8);
368 		lpDI8 = NULL;
369 	}
370 	PERCORE_INITIALIZED = 0;
371 }
372 
373 //////////////////////////////////////////////////////////////////////////////
374 
PollAxisAsButton(u32 pad,int min_id,int max_id,int deadzone,int val)375 void PollAxisAsButton(u32 pad, int min_id, int max_id, int deadzone, int val)
376 {
377     //deadzone is part of the id
378 	if ( val < -deadzone )
379 	{
380 		DX_PerKeyUp( pad, deadzone, max_id );
381 		DX_PerKeyDown( pad, deadzone, min_id );
382 	}
383 	else if ( val > deadzone )
384 	{
385 		DX_PerKeyUp( pad, deadzone, min_id );
386 		DX_PerKeyDown( pad, deadzone, max_id );
387 	}
388 	else
389 	{
390 		DX_PerKeyUp( pad, deadzone, min_id );
391 		DX_PerKeyUp( pad, deadzone, max_id );
392 	}
393 }
394 
395 //////////////////////////////////////////////////////////////////////////////
396 
PollTriggerAsButton(u32 pad,int min_id,int max_id,int deadzone,int val)397 void PollTriggerAsButton(u32 pad, int min_id, int max_id, int deadzone, int val)
398 {
399 #ifdef HAVE_XINPUT
400    //stick value is set to this PollKeys
401    u32 stick = XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
402    if (val > deadzone)
403    {
404       DX_PerKeyUp(pad, stick, max_id);
405       DX_PerKeyDown(pad, stick, min_id);
406    }
407    else
408    {
409       DX_PerKeyUp(pad, stick, min_id);
410       DX_PerKeyUp(pad, stick, max_id);
411    }
412 #endif
413 }
414 
415 //////////////////////////////////////////////////////////////////////////////
416 
417 #ifdef HAVE_XINPUT
PollXInputButtons(u32 pad,XINPUT_STATE * state)418 void PollXInputButtons(u32 pad, XINPUT_STATE *state)
419 {
420 	int i;
421 
422 	// Check buttons
423 	for ( i = 0; i < 16; i++ )
424 	{
425 		WORD mask = 1 << i;
426 
427 		if ( (state->Gamepad.wButtons & mask) == mask )
428 			DX_PerKeyDown( pad, 0, DIJOFS_BUTTON(i) );
429 		else
430 			DX_PerKeyUp( pad, 0, DIJOFS_BUTTON(i) );
431 	}
432 }
433 #endif
434 
435 //////////////////////////////////////////////////////////////////////////////
436 
PollKeys(void)437 void PollKeys(void)
438 {
439 	u32 i, j;
440 	DWORD size=8;
441 	HRESULT hr;
442 
443 	for (i = 0; i < num_devices; i++)
444 	{
445 		LPDIRECTINPUTDEVICE8 curdevice;
446 		DIJOYSTATE2 js;
447 
448 #ifdef HAVE_XINPUT
449 		if (dev_list[i].is_xinput_device)
450 		{
451 			XINPUT_STATE state;
452          u8 axislx, axisly, axisrx, axisry;
453 			ZeroMemory( &state, sizeof(XINPUT_STATE) );
454 			if (XInputGetState(dev_list[i].user_index, &state) == ERROR_DEVICE_NOT_CONNECTED)
455 				continue;
456 
457          //convert signed values to 0-255
458          axislx = ((state.Gamepad.sThumbLX >> 8) - 0x80) & 0xff;
459          axisly = ~(((state.Gamepad.sThumbLY >> 8) - 0x80) & 0xff);//invert so that up == 0x00
460          axisrx = ((state.Gamepad.sThumbRX >> 8) - 0x80) & 0xff;
461          axisry = ~(((state.Gamepad.sThumbRY >> 8) - 0x80) & 0xff);
462 
463 			// Handle axis
464 			DX_PerAxisValue(i, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XI_THUMBLX + PAD_DIR_AXISLEFT, axislx);
465 			DX_PerAxisValue(i, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XI_THUMBLY + PAD_DIR_AXISDOWN, axisly);
466 			DX_PerAxisValue(i, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XI_THUMBRX + PAD_DIR_AXISLEFT, axisrx);
467 			DX_PerAxisValue(i, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XI_THUMBRY + PAD_DIR_AXISDOWN, axisry);
468 			DX_PerAxisValue(i, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, XI_TRIGGERL, state.Gamepad.bLeftTrigger);
469 			DX_PerAxisValue(i, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, XI_TRIGGERR, state.Gamepad.bRightTrigger);
470 
471 			// Left Stick
472 			PollAxisAsButton(i, XI_THUMBLX+PAD_DIR_AXISLEFT, XI_THUMBLX+PAD_DIR_AXISRIGHT,
473 								XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, state.Gamepad.sThumbLX);
474 			PollAxisAsButton(i, XI_THUMBLY+PAD_DIR_AXISUP, XI_THUMBLY+PAD_DIR_AXISDOWN,
475 								XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, state.Gamepad.sThumbLY);
476 
477 			// Right Stick
478 			PollAxisAsButton(i, XI_THUMBRX+PAD_DIR_AXISLEFT, XI_THUMBRX+PAD_DIR_AXISRIGHT,
479 								XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, state.Gamepad.sThumbRX);
480 			PollAxisAsButton(i, XI_THUMBRY+PAD_DIR_AXISUP, XI_THUMBRY+PAD_DIR_AXISDOWN,
481 								XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, state.Gamepad.sThumbRY);
482 
483 			PollXInputButtons(i, &state);
484          PollTriggerAsButton(i, XI_TRIGGERL, XI_TRIGGERL, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, state.Gamepad.bLeftTrigger);
485          PollTriggerAsButton(i, XI_TRIGGERR, XI_TRIGGERR, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, state.Gamepad.bRightTrigger);
486 			continue;
487 		}
488 		else if (dev_list[i].lpDIDevice == NULL)
489 			continue;
490 #else
491 		if (dev_list[i].lpDIDevice == NULL)
492 			continue;
493 #endif
494 
495 		curdevice=dev_list[i].lpDIDevice;
496 
497 		// Poll the device to read the current state
498 		hr = IDirectInputDevice8_Poll(curdevice);
499 		if( FAILED( hr ) )
500 		{
501 			hr = IDirectInputDevice8_Acquire(curdevice);
502 			while( hr == DIERR_INPUTLOST)
503 				hr = IDirectInputDevice8_Acquire(curdevice);
504 
505 			continue;
506 		}
507 
508 		// Get the input's device state
509 		if( FAILED( hr = IDirectInputDevice8_GetDeviceState( curdevice, sizeof( DIJOYSTATE2 ), &js ) ) )
510 			continue;
511 
512 
513 		// Handle axis
514 		DX_PerAxisValue(i, 0x3FFF, XI_THUMBLX, (u8)((js.lX) >> 8));
515 		DX_PerAxisValue(i, 0x3FFF, XI_THUMBLY, (u8)((js.lY) >> 8));
516 		DX_PerAxisValue(i, 0x3FFF, XI_THUMBRX, (u8)((js.lRx) >> 8));
517 		DX_PerAxisValue(i, 0x3FFF, XI_THUMBRY, (u8)((js.lRy) >> 8));
518 
519 		// Left Stick
520 		PollAxisAsButton(i, XI_THUMBL+PAD_DIR_AXISLEFT, XI_THUMBL+PAD_DIR_AXISRIGHT,
521 							0x3FFF, js.lX-0x7FFF);
522 		PollAxisAsButton(i, XI_THUMBL+PAD_DIR_AXISUP, XI_THUMBL+PAD_DIR_AXISDOWN,
523 							0x3FFF, js.lY-0x7FFF);
524 
525 		// Right Stick
526 		PollAxisAsButton(i, XI_THUMBR+PAD_DIR_AXISLEFT, XI_THUMBR+PAD_DIR_AXISRIGHT,
527 							0x3FFF, js.lRx-0x7FFF);
528 		PollAxisAsButton(i, XI_THUMBR+PAD_DIR_AXISUP, XI_THUMBR+PAD_DIR_AXISDOWN,
529 							0x3FFF, js.lRy-0x7FFF);
530 
531 		for (j = 0; j < 4; j++)
532 		{
533 			if (LOWORD(js.rgdwPOV[j]) == 0xFFFF)
534 			{
535 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP);
536 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT);
537 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN);
538 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT);
539 			}
540 			// POV Up
541 			else if (js.rgdwPOV[j] < 4500)
542 			{
543 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP);
544 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT);
545 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT);
546 			}
547 			// POV Up-right
548 			else if (js.rgdwPOV[j] < 9000)
549 			{
550 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP);
551 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT);
552 			}
553 			// POV Right
554 			else if (js.rgdwPOV[j] < 13500)
555 			{
556 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT);
557 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN);
558 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP);
559 			}
560 			// POV Right-down
561 			else if (js.rgdwPOV[j] < 18000)
562 			{
563 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT);
564 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN);
565 			}
566 			// POV Down
567 			else if (js.rgdwPOV[j] < 22500)
568 			{
569 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN);
570 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT);
571 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT);
572 			}
573 			// POV Down-left
574 			else if (js.rgdwPOV[j] < 27000)
575 			{
576 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN);
577 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT);
578 			}
579 			// POV Left
580 			else if (js.rgdwPOV[j] < 31500)
581 			{
582 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT);
583 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP);
584 				DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN);
585 			}
586 			// POV Left-up
587 			else if (js.rgdwPOV[j] < 36000)
588 			{
589 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT);
590 				DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP);
591 			}
592 		}
593 
594 		for (j = 0; j < 32; j++)
595 		{
596 			if (js.rgbButtons[j] & 0x80)
597 				DX_PerKeyDown(i,0,DIJOFS_BUTTON(j));
598 			else
599 				DX_PerKeyUp(i,0,DIJOFS_BUTTON(j));
600 		}
601 	}
602 }
603 
604 //////////////////////////////////////////////////////////////////////////////
605 
PERDXHandleEvents(void)606 int PERDXHandleEvents(void)
607 {
608 	PollKeys();
609 
610 	if (YabauseExec() != 0)
611 		return -1;
612 
613    return 0;
614 }
615 
616 //////////////////////////////////////////////////////////////////////////////
617 
ScanXInputAxis(int pad,LONG axis,LONG deadzone,SHORT stick,int min_id,int max_id)618 u32 ScanXInputAxis(int pad, LONG axis, LONG deadzone, SHORT stick, int min_id, int max_id)
619 {
620 	if (axis < -deadzone)
621 		return DX_MAKEKEY(pad, stick, min_id);
622 	else if (axis > deadzone)
623 		return DX_MAKEKEY(pad, stick, max_id);
624 	else
625 		return 0;
626 }
627 
628 //////////////////////////////////////////////////////////////////////////////
629 
ScanXInputTrigger(int pad,BYTE value,BYTE deadzone,SHORT stick,int id)630 u32 ScanXInputTrigger(int pad, BYTE value, BYTE deadzone, SHORT stick, int id)
631 {
632 	if (value > deadzone)
633 		return DX_MAKEKEY(pad, stick, id);
634 	else
635 		return 0;
636 }
637 
638 //////////////////////////////////////////////////////////////////////////////
639 
PERDXScan(u32 flags)640 u32 PERDXScan(u32 flags)
641 {
642 	int i, j;
643 	HRESULT hr;
644 
645 	for (i = 0; i < num_devices; i++)
646 	{
647 		LPDIRECTINPUTDEVICE8 curdevice;
648 		DIJOYSTATE2 js;
649 		u32 scan;
650 
651 #ifdef HAVE_XINPUT
652 		if (dev_list[i].is_xinput_device)
653 		{
654 			XINPUT_STATE state;
655 			ZeroMemory( &state, sizeof(XINPUT_STATE) );
656 			if (XInputGetState(dev_list[i].user_index, &state) == ERROR_DEVICE_NOT_CONNECTED)
657 				continue;
658 
659             //min_id max_id have to match for scan and poll
660 			// Handle axis
661 			if (flags & PERSF_AXIS)
662 			{
663 				// L Thumb
664 				if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbLX,
665 													XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE,
666                XI_THUMBLX + PAD_DIR_AXISLEFT, XI_THUMBLX + PAD_DIR_AXISRIGHT)) != 0)
667 					return scan;
668 				if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbLY,
669 													XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE,
670                XI_THUMBLY + PAD_DIR_AXISUP, XI_THUMBLY + PAD_DIR_AXISDOWN)) != 0)
671 					return scan;
672 
673 				// R Thumb
674 				if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbRX,
675 													XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE,
676                XI_THUMBRX + PAD_DIR_AXISLEFT, XI_THUMBRX + PAD_DIR_AXISRIGHT)) != 0)
677 					return scan;
678 
679 				if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbRY,
680 													XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE,
681                XI_THUMBRY + PAD_DIR_AXISUP, XI_THUMBRY + PAD_DIR_AXISDOWN)) != 0)
682 					return scan;
683 
684 				// L Trigger
685 				if ((scan = ScanXInputTrigger(i, state.Gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD,
686 													XINPUT_GAMEPAD_TRIGGER_THRESHOLD, XI_TRIGGERL)) != 0)
687 					return scan;
688 
689 				// R Trigger
690 				if ((scan = ScanXInputTrigger(i, state.Gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD,
691 					XINPUT_GAMEPAD_TRIGGER_THRESHOLD, XI_TRIGGERR)) != 0)
692 					return scan;
693 			}
694 
695 			if (flags & PERSF_HAT)
696 			{
697 				// L Thumb
698 				if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbLX,
699 													XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, 0,
700 													XI_THUMBL+PAD_DIR_AXISLEFT, XI_THUMBL+PAD_DIR_AXISRIGHT)) != 0)
701 					return scan;
702 				if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbLY,
703 													XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, 0,
704 													XI_THUMBL+PAD_DIR_AXISUP, XI_THUMBL+PAD_DIR_AXISDOWN)) != 0)
705 					return scan;
706 
707 				// R Thumb
708 				if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbRX,
709 													XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, 0,
710 													XI_THUMBR+PAD_DIR_AXISLEFT, XI_THUMBR+PAD_DIR_AXISRIGHT)) != 0)
711 					return scan;
712 				if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbRY,
713 													XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, 0,
714 													XI_THUMBR+PAD_DIR_AXISUP, XI_THUMBR+PAD_DIR_AXISDOWN)) != 0)
715 					return scan;
716 
717 				if (state.Gamepad.wButtons & 0xF)
718 				{
719 					// Return lowest bit
720 					for (j = 0; j < 16; j++)
721 					{
722 						if (state.Gamepad.wButtons & (1 << j))
723 							return DX_MAKEKEY(i, 0, DIJOFS_BUTTON(j));
724 					}
725 				}
726 			}
727 
728 			if (flags & PERSF_BUTTON)
729 			{
730 				if (state.Gamepad.wButtons & 0xF3F0)
731 				{
732 					// Return lowest bit
733 					for (j = 0; j < 16; j++)
734 					{
735 						if (state.Gamepad.wButtons & (1 << j))
736 							return DX_MAKEKEY(i, 0, DIJOFS_BUTTON(j));
737 					}
738 				}
739 			}
740 
741 			continue;
742 		}
743 		else if (dev_list[i].lpDIDevice == NULL)
744 			continue;
745 #else
746 		if (dev_list[i].lpDIDevice == NULL)
747 			continue;
748 #endif
749 
750 		curdevice=dev_list[i].lpDIDevice;
751 
752 		// Poll the device to read the current state
753 		hr = IDirectInputDevice8_Poll(curdevice);
754 		if( FAILED( hr ) )
755 		{
756 			hr = IDirectInputDevice8_Acquire(curdevice);
757 			while( hr == DIERR_INPUTLOST)
758 				hr = IDirectInputDevice8_Acquire(curdevice);
759 
760 			continue;
761 		}
762 
763 		// Get the input's device state
764 		if( FAILED( hr = IDirectInputDevice8_GetDeviceState( curdevice, sizeof( DIJOYSTATE2 ), &js ) ) )
765 			continue;
766 
767 		if (flags & PERSF_AXIS)
768 		{
769 			// Left Stick
770 			if ((scan = ScanXInputAxis(i, js.lX-0x7FFF, 0x3FFF, 0x3FFF, XI_THUMBLX, XI_THUMBLX)) != 0)
771 				return scan;
772 			if ((scan = ScanXInputAxis(i, js.lY-0x7FFF, 0x3FFF, 0x3FFF, XI_THUMBLY, XI_THUMBLY)) != 0)
773 				return scan;
774 
775 			// Right Stick
776 			if ((scan = ScanXInputAxis(i, js.lRx-0x7FFF, 0x3FFF, 0x3FFF, XI_THUMBRX, XI_THUMBRX)) != 0)
777 				return scan;
778 			if ((scan = ScanXInputAxis(i, js.lRy-0x7FFF, 0x3FFF, 0x3FFF, XI_THUMBRY, XI_THUMBRY)) != 0)
779 				return scan;
780 		}
781 
782 		if (flags & PERSF_HAT)
783 		{
784 			// L Thumb
785 			if ((scan = ScanXInputAxis(i, js.lX-0x7FFF, 0x3FFF, 0,
786 											XI_THUMBL+PAD_DIR_AXISLEFT, XI_THUMBL+PAD_DIR_AXISRIGHT)) != 0)
787 				return scan;
788 			if ((scan = ScanXInputAxis(i, js.lY-0x7FFF, 0x3FFF, 0,
789 											XI_THUMBL+PAD_DIR_AXISUP, XI_THUMBL+PAD_DIR_AXISDOWN)) != 0)
790 				return scan;
791 
792 			// R Thumb
793 			if ((scan = ScanXInputAxis(i, js.lRx-0x7FFF, 0x3FFF, 0,
794 											XI_THUMBR+PAD_DIR_AXISLEFT, XI_THUMBR+PAD_DIR_AXISRIGHT)) != 0)
795 				return scan;
796 			if ((scan = ScanXInputAxis(i, js.lRy-0x7FFF, 0x3FFF, 0,
797 											XI_THUMBR+PAD_DIR_AXISUP, XI_THUMBR+PAD_DIR_AXISDOWN)) != 0)
798 				return scan;
799 
800 			for (j = 0; j < 4; j++)
801 			{
802 				// POV Up
803 				if (js.rgdwPOV[j] < 4500)
804 					return DX_MAKEKEY(i,0,DIJOFS_POV(j)+PAD_DIR_POVUP);
805 				// POV Right
806 				else if (js.rgdwPOV[j] < 13500)
807 					return DX_MAKEKEY(i,0,DIJOFS_POV(j)+PAD_DIR_POVRIGHT);
808 				// POV Down
809 				else if (js.rgdwPOV[j] < 22500)
810 					return DX_MAKEKEY(i,0,DIJOFS_POV(j)+PAD_DIR_POVDOWN);
811 				// POV Left
812 				else if (js.rgdwPOV[j] < 31500)
813 					return DX_MAKEKEY(i,0,DIJOFS_POV(j)+PAD_DIR_POVLEFT);
814 			}
815 		}
816 
817 		if (flags & PERSF_BUTTON)
818 		{
819 			for (j = 0; j < 32; j++)
820 			{
821 				if (js.rgbButtons[j] & 0x80)
822 					return DX_MAKEKEY(i,0,DIJOFS_BUTTON(j));
823 			}
824 		}
825    }
826 
827    return 0;
828 }
829 
830 //////////////////////////////////////////////////////////////////////////////
831 
PERDXFlush(void)832 void PERDXFlush(void)
833 {
834 }
835 
836 //////////////////////////////////////////////////////////////////////////////