Joypad.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. CChar* Joypad::_button_name[32];
  6. MemtN<Joypad, 4> Joypads;
  7. /******************************************************************************/
  8. #if MAC
  9. struct MacJoypad
  10. {
  11. struct Elm
  12. {
  13. enum TYPE : Byte
  14. {
  15. PAD ,
  16. BUTTON,
  17. AXIS ,
  18. };
  19. TYPE type;
  20. Int index;
  21. IOHIDElementCookie cookie;
  22. Int avg, max; // button_on=(val>avg);
  23. Flt mul, add;
  24. void setPad (C IOHIDElementCookie &cookie , Int max) {T.type=PAD ; T.cookie=cookie; T.max =max+1; T.mul=-PI2/T.max; T.add=PI_2;}
  25. void setButton(C IOHIDElementCookie &cookie, Int index, Int min, Int max) {T.type=BUTTON; T.cookie=cookie; T.index=index; T.avg=(min+max)/2;}
  26. void setAxis (C IOHIDElementCookie &cookie, Int index, Int min, Int max) {T.type=AXIS ; T.cookie=cookie; T.index=index; T.mul=2.0f/(max-min); T.add=-1-min*T.mul; if(index&1){CHS(mul); CHS(add);}} // change sign for vertical
  27. };
  28. static Int Compare(C Elm &a, C Elm &b) {return ::Compare(UIntPtr(a.cookie), UIntPtr(b.cookie));}
  29. static Int Compare(C Elm &a, C IOHIDElementCookie &b) {return ::Compare(UIntPtr(a.cookie), UIntPtr(b ));}
  30. Mems<Elm> elms;
  31. Byte button[32];
  32. void zero() {Zero(button);}
  33. MacJoypad() {zero();}
  34. NO_COPY_CONSTRUCTOR(MacJoypad);
  35. };
  36. static MemtN<MacJoypad, 4> MacJoypads;
  37. static IOHIDManagerRef HidManager;
  38. static UInt JoypadsID;
  39. /******************************************************************************/
  40. static NSMutableDictionary* JoypadCriteria(UInt32 inUsagePage, UInt32 inUsage)
  41. {
  42. NSMutableDictionary* dict=[[NSMutableDictionary alloc] init];
  43. [dict setObject: [NSNumber numberWithInt: inUsagePage] forKey: (NSString*)CFSTR(kIOHIDDeviceUsagePageKey)];
  44. [dict setObject: [NSNumber numberWithInt: inUsage ] forKey: (NSString*)CFSTR(kIOHIDDeviceUsageKey )];
  45. return dict;
  46. }
  47. static void JoypadAdded(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef device)
  48. {
  49. Memt<MacJoypad::Elm> elms;
  50. Int buttons=0, axes=0;
  51. NSArray *elements=(NSArray*)IOHIDDeviceCopyMatchingElements(device, null, kIOHIDOptionsTypeNone);
  52. FREP([elements count]) // process in order
  53. {
  54. IOHIDElementRef element=(IOHIDElementRef)[elements objectAtIndex: i];
  55. Int type =IOHIDElementGetType (element),
  56. usage=IOHIDElementGetUsage (element),
  57. page =IOHIDElementGetUsagePage (element),
  58. min =IOHIDElementGetPhysicalMin(element),
  59. max =IOHIDElementGetPhysicalMax(element),
  60. lmin =IOHIDElementGetLogicalMin (element),
  61. lmax =IOHIDElementGetLogicalMax (element);
  62. IOHIDElementCookie cookie=IOHIDElementGetCookie(element);
  63. //CFStringRef elm_name=IOHIDElementGetName(element); NSLog(@"%@", (NSString*)elm_name);
  64. if(type==kIOHIDElementTypeInput_Misc || type==kIOHIDElementTypeInput_Axis || type==kIOHIDElementTypeInput_Button)
  65. {
  66. if((max-min==1) || page==kHIDPage_Button || type==kIOHIDElementTypeInput_Button){if(InRange(buttons, MEMBER(MacJoypad, button)))elms.New().setButton(cookie, buttons++, min, max);}else
  67. if(usage>=0x30 && usage<0x36 ) elms.New().setAxis (cookie, axes ++, min, max); else
  68. if(usage==0x39 ) elms.New().setPad (cookie, lmax);
  69. }
  70. }
  71. if(elms.elms())
  72. {
  73. elms.sort(MacJoypad::Compare); // sort so 'binaryFind' can be used later
  74. NSString *name =(NSString*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey )); // do not release this !!
  75. //NSString *serial=(NSString*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDSerialNumberKey)); // do not release this ? this was null on "Logitech Rumblepad 2"
  76. Int vendorId=[(NSNumber*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey )) intValue];
  77. Int productId=[(NSNumber*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey )) intValue];
  78. Joypad & jp= Joypads.New();
  79. MacJoypad &mjp=MacJoypads.New();
  80. jp._id =JoypadsID++;
  81. jp._name=name;
  82. jp._did =device;
  83. mjp. elms=elms;
  84. }
  85. }
  86. static void JoypadRemoved(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef device)
  87. {
  88. REPA(Joypads)if(Joypads[i]._did==device)
  89. {
  90. Joypads.remove(i, true);
  91. MacJoypads.remove(i, true);
  92. }
  93. }
  94. static void JoypadAction(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef value)
  95. {
  96. IOHIDElementRef element=IOHIDValueGetElement (value );
  97. IOHIDDeviceRef device =IOHIDElementGetDevice(element); // or IOHIDQueueGetDevice((IOHIDQueueRef)inSender);
  98. REPA(Joypads)if(Joypads[i]._did==device)
  99. {
  100. Joypad & jp= Joypads[i];
  101. MacJoypad &mjp=MacJoypads[i];
  102. if(MacJoypad::Elm *elm=mjp.elms.binaryFind(IOHIDElementGetCookie(element), MacJoypad::Compare))
  103. {
  104. Int val=IOHIDValueGetIntegerValue(value);
  105. switch(elm->type)
  106. {
  107. case MacJoypad::Elm::PAD:
  108. {
  109. if(InRange(val, elm->max))
  110. {
  111. CosSin(jp.dir.x, jp.dir.y, val*elm->mul+elm->add);
  112. }else jp.dir.zero();
  113. }break;
  114. case MacJoypad::Elm::BUTTON:
  115. {
  116. mjp.button[elm->index]=(val>elm->avg);
  117. }break;
  118. case MacJoypad::Elm::AXIS:
  119. {
  120. switch(elm->index)
  121. {
  122. case 0: jp.dir_a[0].x=val*elm->mul+elm->add; break;
  123. case 1: jp.dir_a[0].y=val*elm->mul+elm->add; break;
  124. case 2: jp.dir_a[1].x=val*elm->mul+elm->add; break;
  125. case 3: jp.dir_a[1].y=val*elm->mul+elm->add; break;
  126. }
  127. }break;
  128. }
  129. }
  130. break;
  131. }
  132. }
  133. #endif
  134. /******************************************************************************/
  135. Joypad::~Joypad()
  136. {
  137. #if WINDOWS_OLD
  138. if(_did){_did->Unacquire(); RELEASE(_effect); RELEASE(_did);}
  139. #endif
  140. }
  141. Joypad::Joypad()
  142. {
  143. _vibration_axes=_xinput1=_offset_x=_offset_y=_connected=0;
  144. _id=0;
  145. _did=null;
  146. _effect=null;
  147. zero();
  148. }
  149. Str Joypad::buttonName(Int x)C
  150. {
  151. if(InRange(x, _button_name))return _button_name[x];
  152. return S;
  153. }
  154. /******************************************************************************/
  155. Bool Joypad::supportsVibrations()C
  156. {
  157. return _xinput1 || _effect!=null;
  158. }
  159. Int Joypad::index()C {return Joypads.index(this);}
  160. /******************************************************************************/
  161. Joypad& Joypad::vibration(C Vec2 &force)
  162. {
  163. #if WINDOWS
  164. if(_xinput1)
  165. {
  166. XINPUT_VIBRATION vibration;
  167. vibration.wLeftMotorSpeed =RoundU(Sat(Abs(force.x))*0xFFFF);
  168. vibration.wRightMotorSpeed=RoundU(Sat(Abs(force.y))*0xFFFF);
  169. XInputSetState(_xinput1-1, &vibration);
  170. }
  171. #if WINDOWS_OLD
  172. else
  173. if(_effect && _vibration_axes>=1 && _vibration_axes<=2)
  174. {
  175. LONG rglDirection[2]={0, 0};
  176. DICONSTANTFORCE cf;
  177. if(_vibration_axes==1)
  178. {
  179. cf.lMagnitude=RoundU(Min(force.length(), 1.0f)*DI_FFNOMINALMAX);
  180. }else
  181. {
  182. rglDirection[0]=Round (Mid(force.x, -1.0f, 1.0f)*DI_FFNOMINALMAX);
  183. rglDirection[1]=Round (Mid(force.y, -1.0f, 1.0f)*DI_FFNOMINALMAX);
  184. cf.lMagnitude =RoundU(Min(force.length(), 1.0f)*DI_FFNOMINALMAX);
  185. }
  186. DIEFFECT eff; Zero(eff);
  187. eff.dwSize=SIZE(DIEFFECT);
  188. eff.dwFlags=DIEFF_CARTESIAN|DIEFF_OBJECTOFFSETS;
  189. eff.cAxes=_vibration_axes;
  190. eff.rglDirection=rglDirection;
  191. eff.lpEnvelope=0;
  192. eff. cbTypeSpecificParams=SIZE(DICONSTANTFORCE);
  193. eff.lpvTypeSpecificParams=&cf;
  194. eff.dwStartDelay=0;
  195. _effect->SetParameters(&eff, DIEP_DIRECTION|DIEP_TYPESPECIFICPARAMS|DIEP_START);
  196. }
  197. #endif
  198. #endif
  199. return T;
  200. }
  201. /******************************************************************************/
  202. void Joypad::zero()
  203. {
  204. Zero(_button);
  205. REPAO(_last_t)=-FLT_MAX;
  206. dir .zero();
  207. REPAO(dir_a ).zero();
  208. REPAO(dir_an ).zero();
  209. REPAO(trigger)=0;
  210. }
  211. void Joypad::clear()
  212. {
  213. REPAO(_button)&=~BS_NOT_ON;
  214. }
  215. void Joypad::update(C Byte *on, Int elms)
  216. {
  217. MIN(elms, Elms(_button));
  218. REP(elms)if((on[i]!=0)!=ButtonOn(_button[i]))if(on[i])push(i);else release(i);
  219. }
  220. void Joypad::update()
  221. {
  222. #if WINDOWS
  223. if(_xinput1)
  224. {
  225. XINPUT_STATE state;
  226. if(XInputGetState(_xinput1-1, &state)==ERROR_SUCCESS)
  227. {
  228. // buttons
  229. Byte x_button[]=
  230. {
  231. FlagTest(state.Gamepad.wButtons , XINPUT_GAMEPAD_A ),
  232. FlagTest(state.Gamepad.wButtons , XINPUT_GAMEPAD_B ),
  233. FlagTest(state.Gamepad.wButtons , XINPUT_GAMEPAD_X ),
  234. FlagTest(state.Gamepad.wButtons , XINPUT_GAMEPAD_Y ),
  235. FlagTest(state.Gamepad.wButtons , XINPUT_GAMEPAD_LEFT_SHOULDER ),
  236. FlagTest(state.Gamepad.wButtons , XINPUT_GAMEPAD_RIGHT_SHOULDER ),
  237. state.Gamepad. bLeftTrigger>=XINPUT_GAMEPAD_TRIGGER_THRESHOLD ,
  238. state.Gamepad.bRightTrigger>=XINPUT_GAMEPAD_TRIGGER_THRESHOLD ,
  239. FlagTest(state.Gamepad.wButtons , XINPUT_GAMEPAD_LEFT_THUMB ),
  240. FlagTest(state.Gamepad.wButtons , XINPUT_GAMEPAD_RIGHT_THUMB ),
  241. FlagTest(state.Gamepad.wButtons , XINPUT_GAMEPAD_START ),
  242. FlagTest(state.Gamepad.wButtons , XINPUT_GAMEPAD_BACK ),
  243. };
  244. ASSERT(ELMS(x_button)<ELMS(T._button));
  245. update(x_button, Elms(x_button));
  246. // digital pad
  247. dir.x=FlagTest(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT)-FlagTest(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT);
  248. dir.y=FlagTest(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP )-FlagTest(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN);
  249. Flt l=dir.length(); if(l>1)dir/=l;
  250. dir_a[0].x=state.Gamepad.sThumbLX/32768.0f;
  251. dir_a[0].y=state.Gamepad.sThumbLY/32768.0f;
  252. dir_a[1].x=state.Gamepad.sThumbRX/32768.0f;
  253. dir_a[1].y=state.Gamepad.sThumbRY/32768.0f;
  254. trigger[0]=state.Gamepad.bLeftTrigger /255.0f;
  255. trigger[1]=state.Gamepad.bRightTrigger/255.0f;
  256. }else zero();
  257. }
  258. #if WINDOWS_OLD
  259. else
  260. if(_did->Poll())
  261. {
  262. // get data
  263. DIJOYSTATE dijs;
  264. if(!OK(_did->GetDeviceState(SIZE(dijs), &dijs)))
  265. {
  266. if(App.active())acquire(true); Zero(dijs);
  267. dijs.rgdwPOV[0]=UINT_MAX;
  268. dijs.lX =32768;
  269. dijs.lY =32768;
  270. dijs.lZ =32768;
  271. dijs.lRx =32768;
  272. dijs.lRy =32768;
  273. dijs.lRz =32768;
  274. dijs.rglSlider[0]=32768;
  275. dijs.rglSlider[1]=32768;
  276. }
  277. // process data
  278. ASSERT(ELMS(T._button)==ELMS(dijs.rgbButtons));
  279. update(dijs.rgbButtons, Elms(dijs.rgbButtons));
  280. switch(dijs.rgdwPOV[0])
  281. {
  282. case UINT_MAX: dir.zero(); break;
  283. case 0: dir.set( 0, 1); break;
  284. case 9000: dir.set( 1, 0); break;
  285. case 18000: dir.set( 0, -1); break;
  286. case 27000: dir.set(-1, 0); break;
  287. default : CosSin(dir.x, dir.y, PI_2-DegToRad(dijs.rgdwPOV[0]/100.0f)); break;
  288. }
  289. const Flt mul=1.0f/32768;
  290. dir_a[0].x= (dijs.lX-32768)*mul;
  291. dir_a[0].y=-(dijs.lY-32768)*mul;
  292. if(_offset_x && _offset_y)
  293. {
  294. ASSERT(SIZE(dijs.lZ)==SIZE(Int));
  295. dir_a[1].x= (*(Int*)(((Byte*)&dijs)+_offset_x)-32768)*mul;
  296. dir_a[1].y=-(*(Int*)(((Byte*)&dijs)+_offset_y)-32768)*mul;
  297. }
  298. trigger[0]=(dijs.rglSlider[0]-32768)*mul;
  299. trigger[1]=(dijs.rglSlider[1]-32768)*mul;
  300. }
  301. #endif
  302. #elif MAC
  303. Int index=T.index(); if(InRange(index, MacJoypads))
  304. {
  305. C MacJoypad &mjp=MacJoypads[index];
  306. ASSERT(ELMS(T._button)==ELMS(mjp.button));
  307. update(mjp.button, Elms(mjp.button));
  308. }
  309. #else
  310. #endif
  311. REPA(dir_an)
  312. {
  313. dir_an[i]=dir_a[i];
  314. Flt div=Max(Abs(dir_an[i].x), Abs(dir_an[i].y));
  315. if( div>EPS)dir_an[i]/=dir_an[i].length()/div;
  316. }
  317. }
  318. void Joypad::push(Byte b)
  319. {
  320. if(InRange(b, _button) && !(_button[b]&BS_ON))
  321. {
  322. Int device=index(); if(device>=0)InputCombo.add(InputButton(INPUT_JOYPAD, b, device));
  323. _button[b]|=BS_PUSHED|BS_ON;
  324. if(Time.appTime()-_last_t[b]<=DoubleClickTime+Time.ad())
  325. {
  326. _button[b]|=BS_DOUBLE;
  327. _last_t[b] =-FLT_MAX;
  328. }else
  329. {
  330. _last_t[b]=Time.appTime();
  331. }
  332. }
  333. }
  334. void Joypad::release(Byte b)
  335. {
  336. if(InRange(b, _button) && (_button[b]&BS_ON))
  337. {
  338. _button[b]&=~BS_ON;
  339. _button[b]|= BS_RELEASED;
  340. }
  341. }
  342. /******************************************************************************/
  343. void Joypad::acquire(Bool on)
  344. {
  345. #if WINDOWS_OLD
  346. if(_did){if(on){_did->Acquire(); if(_effect)_effect->Start(1, 0);}else _did->Unacquire();}
  347. #endif
  348. }
  349. /******************************************************************************/
  350. Joypad* FindJoypad(UInt id)
  351. {
  352. REPA(Joypads)if(Joypads[i].id()==id)return &Joypads[i];
  353. return null;
  354. }
  355. static Joypad& GetJoypad(UInt id, Bool &added)
  356. {
  357. added=false;
  358. Joypad *joypad=FindJoypad(id); if(!joypad){added=true; joypad=&Joypads.New(); joypad->_id=id;} joypad->_connected=true;
  359. return *joypad;
  360. }
  361. #if WINDOWS_OLD
  362. static Bool IsXInputDevice(C GUID &pGuidProductFromDirectInput)
  363. {
  364. IWbemLocator* pIWbemLocator =null;
  365. IEnumWbemClassObject* pEnumDevices =null;
  366. IWbemClassObject* pDevices[20] ={0};
  367. IWbemServices* pIWbemServices=null;
  368. BSTR bstrNamespace =null;
  369. BSTR bstrDeviceID =null;
  370. BSTR bstrClassName =null;
  371. DWORD uReturned =0;
  372. bool bIsXinputDevice=false;
  373. UINT iDevice =0;
  374. VARIANT var;
  375. HRESULT hr;
  376. // CoInit if needed
  377. hr=CoInitialize(null);
  378. bool bCleanupCOM=SUCCEEDED(hr);
  379. // Create WMI
  380. hr=CoCreateInstance(__uuidof(WbemLocator), null, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*)&pIWbemLocator);
  381. if(FAILED(hr) || pIWbemLocator==null)goto LCleanup;
  382. bstrNamespace=SysAllocString(L"\\\\.\\root\\cimv2"); if(!bstrNamespace)goto LCleanup;
  383. bstrClassName=SysAllocString(L"Win32_PNPEntity"); if(!bstrClassName)goto LCleanup;
  384. bstrDeviceID =SysAllocString(L"DeviceID"); if(!bstrDeviceID )goto LCleanup;
  385. // Connect to WMI
  386. hr=pIWbemLocator->ConnectServer(bstrNamespace, null, null, 0L, 0L, null, null, &pIWbemServices);
  387. if(FAILED(hr) || pIWbemServices==null)goto LCleanup;
  388. // Switch security level to IMPERSONATE
  389. CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, null, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, null, EOAC_NONE);
  390. hr=pIWbemServices->CreateInstanceEnum(bstrClassName, 0, null, &pEnumDevices);
  391. if(FAILED(hr) || pEnumDevices==null)goto LCleanup;
  392. // Loop over all devices
  393. for(;;)
  394. {
  395. hr=pEnumDevices->Next(10000, Elms(pDevices), pDevices, &uReturned); // Get 20 at a time
  396. if(FAILED(hr))goto LCleanup;
  397. if(uReturned==0)break;
  398. FREP(uReturned) // For each device, get its device ID
  399. {
  400. hr=pDevices[i]->Get(bstrDeviceID, 0L, &var, null, null);
  401. if(SUCCEEDED(hr) && var.vt==VT_BSTR && var.bstrVal!=null)
  402. {
  403. if(wcsstr(var.bstrVal, L"IG_")) // Check if the device ID contains "IG_". If it does, then it's an XInput device
  404. {
  405. DWORD dwPid=0, dwVid=0; // If it does, then get the VID/PID from var.bstrVal
  406. #pragma warning(push)
  407. #pragma warning(disable:4996)
  408. WCHAR *strVid=wcsstr(var.bstrVal, L"VID_"); if(strVid && swscanf(strVid, L"VID_%4X", &dwVid)!=1)dwVid=0;
  409. WCHAR *strPid=wcsstr(var.bstrVal, L"PID_"); if(strPid && swscanf(strPid, L"PID_%4X", &dwPid)!=1)dwPid=0;
  410. #pragma warning(pop)
  411. if(MAKELONG(dwVid, dwPid)==pGuidProductFromDirectInput.Data1) // Compare the VID/PID to the DInput device
  412. {
  413. bIsXinputDevice=true;
  414. goto LCleanup;
  415. }
  416. }
  417. }
  418. RELEASE(pDevices[i]);
  419. }
  420. }
  421. LCleanup:
  422. if(bstrNamespace)SysFreeString(bstrNamespace);
  423. if(bstrDeviceID )SysFreeString(bstrDeviceID );
  424. if(bstrClassName)SysFreeString(bstrClassName);
  425. FREPA(pDevices)RELEASE(pDevices[i]);
  426. RELEASE(pEnumDevices );
  427. RELEASE(pIWbemLocator );
  428. RELEASE(pIWbemServices);
  429. if(bCleanupCOM)CoUninitialize();
  430. return bIsXinputDevice;
  431. }
  432. static BOOL CALLBACK EnumAxes(const DIDEVICEOBJECTINSTANCE *pdidoi, VOID *user)
  433. {
  434. Joypad &joypad=*(Joypad*)user;
  435. if(pdidoi->dwFlags&DIDOI_FFACTUATOR)joypad._vibration_axes++;
  436. // Logitech RumblePad 2 uses: (x0=lX, y0=lY, x1=lZ, y1=lRz)
  437. Int offset=0;
  438. if(pdidoi->guidType==GUID_ZAxis )offset=OFFSET(DIJOYSTATE, lZ );else
  439. if(pdidoi->guidType==GUID_RxAxis)offset=OFFSET(DIJOYSTATE, lRx);else
  440. if(pdidoi->guidType==GUID_RyAxis)offset=OFFSET(DIJOYSTATE, lRy);else
  441. if(pdidoi->guidType==GUID_RzAxis)offset=OFFSET(DIJOYSTATE, lRz);
  442. if(offset)
  443. {
  444. if(!joypad._offset_x )joypad._offset_x=offset;else
  445. if( joypad._offset_x<offset)joypad._offset_y=offset;else // X axis is assumed to be specified before Y axis
  446. {
  447. joypad._offset_y=joypad._offset_x;
  448. joypad._offset_x= offset;
  449. }
  450. }
  451. return DIENUM_CONTINUE;
  452. }
  453. static BOOL CALLBACK EnumJoypads(const DIDEVICEINSTANCE *DIDevInst, void*)
  454. {
  455. if(!IsXInputDevice(DIDevInst->guidProduct)) // x controllers are listed elsewhere
  456. {
  457. UInt id=0; ASSERT(SIZE(DIDevInst->guidInstance)==SIZE(UID)); C UID &uid=(UID&)DIDevInst->guidInstance; REPA(uid.i)id^=uid.i[i];
  458. Bool added; Joypad &joypad=GetJoypad(id, added);
  459. if( added)
  460. {
  461. IDirectInputDevice8 *did=null;
  462. if(OK(InputDevices.DI->CreateDevice(DIDevInst->guidInstance, &did, null)))
  463. if(OK(did->SetDataFormat (&c_dfDIJoystick)))
  464. if(OK(did->SetCooperativeLevel(App.Hwnd(), DISCL_EXCLUSIVE|DISCL_FOREGROUND)))
  465. {
  466. Swap(joypad._did, did);
  467. joypad._name=DIDevInst->tszProductName;
  468. // disable auto centering ?
  469. DIPROPDWORD dipdw; Zero(dipdw);
  470. dipdw.diph.dwSize =SIZE(DIPROPDWORD );
  471. dipdw.diph.dwHeaderSize=SIZE(DIPROPHEADER);
  472. dipdw.diph.dwObj =0;
  473. dipdw.diph.dwHow =DIPH_DEVICE;
  474. dipdw.dwData =FALSE;
  475. OK(joypad._did->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph));
  476. // enumerate ff axes
  477. joypad._did->EnumObjects(EnumAxes, &joypad, DIDFT_AXIS);
  478. MIN(joypad._vibration_axes, 2);
  479. // create ff effect
  480. if(joypad._vibration_axes)
  481. {
  482. DWORD rgdwAxes [2]={DIJOFS_X, DIJOFS_Y};
  483. LONG rglDirection[2]={0, 0};
  484. DICONSTANTFORCE cf ={0};
  485. DIEFFECT eff; Zero(eff);
  486. eff.dwSize=SIZE(DIEFFECT);
  487. eff.dwFlags=DIEFF_CARTESIAN|DIEFF_OBJECTOFFSETS;
  488. eff.dwDuration=INFINITE;
  489. eff.dwSamplePeriod=0;
  490. eff.dwGain=DI_FFNOMINALMAX;
  491. eff.dwTriggerButton=DIEB_NOTRIGGER;
  492. eff.dwTriggerRepeatInterval=0;
  493. eff.cAxes=joypad._vibration_axes;
  494. eff.rgdwAxes=rgdwAxes;
  495. eff.rglDirection=rglDirection;
  496. eff.lpEnvelope=0;
  497. eff.cbTypeSpecificParams=SIZE(DICONSTANTFORCE);
  498. eff.lpvTypeSpecificParams=&cf;
  499. eff.dwStartDelay=0;
  500. // Create the prepared effect
  501. joypad._did->CreateEffect(GUID_ConstantForce, &eff, &joypad._effect, null);
  502. }
  503. }
  504. if(!joypad._did)Joypads.removeData(&joypad); // if failed to create it then remove it
  505. RELEASE(did);
  506. }
  507. }
  508. return DIENUM_CONTINUE;
  509. }
  510. #endif
  511. /******************************************************************************/
  512. static Int CompareJoypad(C Joypad &a, C Joypad &b) {return Compare(a.id(), b.id());}
  513. void ListJoypads()
  514. {
  515. #if WINDOWS
  516. REPAO(Joypads)._connected=false; // assume that all are disconnected
  517. FREP(4) // XInput supports only 4 controllers (process in order)
  518. {
  519. XINPUT_STATE state; if(XInputGetState(i, &state)==ERROR_SUCCESS) // if returned valid input
  520. {
  521. Bool added; Joypad &joypad=GetJoypad(i, added); // index is used for the ID for XInput controllers
  522. if( added)
  523. {
  524. joypad._xinput1=i+1;
  525. joypad._name ="X Controller";
  526. }
  527. }
  528. }
  529. #if WINDOWS_OLD
  530. InputDevices.DI->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoypads, null, DIEDFL_ATTACHEDONLY/*|DIEDFL_FORCEFEEDBACK*/); // this would enumerate only devices with FF
  531. #endif
  532. #if WINDOWS_NEW
  533. //Windows::Gaming::Input::Gamepad::Gamepads;
  534. //Windows::Gaming::Input::Gamepad::GetCurrentReading;
  535. //Windows::Gaming::Input::Gamepad::Vibration;
  536. #endif
  537. REPA(Joypads)if(!Joypads[i]._connected)Joypads.remove(i); // remove disconnected joypads
  538. Joypads.sort(CompareJoypad); // sort remaining by their ID
  539. #elif MAC
  540. if(HidManager=IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone))
  541. {
  542. NSMutableDictionary *criteria[]=
  543. {
  544. JoypadCriteria(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick),
  545. JoypadCriteria(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad),
  546. JoypadCriteria(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController),
  547. };
  548. NSArray *criteria_array=[NSArray arrayWithObjects: criteria[0], criteria[1], criteria[2], __null];
  549. IOHIDManagerSetDeviceMatchingMultiple(HidManager, (CFArrayRef)criteria_array);
  550. REPA(criteria)[criteria[i] release];
  551. IOHIDManagerScheduleWithRunLoop(HidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  552. IOReturn hid_open=IOHIDManagerOpen(HidManager, kIOHIDOptionsTypeNone);
  553. IOHIDManagerRegisterDeviceMatchingCallback(HidManager, JoypadAdded , null);
  554. IOHIDManagerRegisterDeviceRemovalCallback (HidManager, JoypadRemoved, null);
  555. IOHIDManagerRegisterInputValueCallback (HidManager, JoypadAction , null);
  556. }
  557. #endif
  558. }
  559. void InitJoypads()
  560. {
  561. if(LogInit)LogN("InitJoypads");
  562. Joypad::_button_name[ 0]=u"Joypad1";
  563. Joypad::_button_name[ 1]=u"Joypad2";
  564. Joypad::_button_name[ 2]=u"Joypad3";
  565. Joypad::_button_name[ 3]=u"Joypad4";
  566. Joypad::_button_name[ 4]=u"Joypad5";
  567. Joypad::_button_name[ 5]=u"Joypad6";
  568. Joypad::_button_name[ 6]=u"Joypad7";
  569. Joypad::_button_name[ 7]=u"Joypad8";
  570. Joypad::_button_name[ 8]=u"Joypad9";
  571. Joypad::_button_name[ 9]=u"Joypad10";
  572. Joypad::_button_name[10]=u"Joypad11";
  573. Joypad::_button_name[11]=u"Joypad12";
  574. Joypad::_button_name[12]=u"Joypad13";
  575. Joypad::_button_name[13]=u"Joypad14";
  576. Joypad::_button_name[14]=u"Joypad15";
  577. Joypad::_button_name[15]=u"Joypad16";
  578. Joypad::_button_name[16]=u"Joypad17";
  579. Joypad::_button_name[17]=u"Joypad18";
  580. Joypad::_button_name[18]=u"Joypad19";
  581. Joypad::_button_name[19]=u"Joypad20";
  582. Joypad::_button_name[20]=u"Joypad21";
  583. Joypad::_button_name[21]=u"Joypad22";
  584. Joypad::_button_name[22]=u"Joypad23";
  585. Joypad::_button_name[23]=u"Joypad24";
  586. Joypad::_button_name[24]=u"Joypad25";
  587. Joypad::_button_name[25]=u"Joypad26";
  588. Joypad::_button_name[26]=u"Joypad27";
  589. Joypad::_button_name[27]=u"Joypad28";
  590. Joypad::_button_name[28]=u"Joypad29";
  591. Joypad::_button_name[29]=u"Joypad30";
  592. Joypad::_button_name[30]=u"Joypad31";
  593. Joypad::_button_name[31]=u"Joypad32";
  594. ListJoypads();
  595. }
  596. void ShutJoypads()
  597. {
  598. Joypads.del();
  599. #if MAC
  600. MacJoypads.del();
  601. if(HidManager)
  602. {
  603. IOHIDManagerClose(HidManager, kIOHIDOptionsTypeNone);
  604. CFRelease(HidManager); HidManager=null;
  605. }
  606. #endif
  607. }
  608. /******************************************************************************/
  609. }
  610. /******************************************************************************/