gamecontroller.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #define HL_NAME(n) directx_##n
  2. #include <hl.h>
  3. #include "hlsystem.h"
  4. #include <xinput.h>
  5. #include <InitGuid.h>
  6. #define DIRECTINPUT_VERSION 0x0800
  7. #include <dinput.h>
  8. #include <dbt.h>
  9. #define HAT_CENTERED 0x00
  10. #define HAT_UP 0x01
  11. #define HAT_RIGHT 0x02
  12. #define HAT_DOWN 0x04
  13. #define HAT_LEFT 0x08
  14. typedef struct {
  15. hl_type *t;
  16. int num;
  17. int mask; // for hat only
  18. int axis;
  19. } dinput_mapping_btn;
  20. typedef struct {
  21. hl_type *t;
  22. unsigned int guid;
  23. vbyte *name;
  24. varray *button; // dinput_mapping_btn
  25. } dinput_mapping;
  26. typedef struct _dx_gctrl_device dx_gctrl_device;
  27. struct _dx_gctrl_device {
  28. char *name;
  29. bool isNew;
  30. int xUID;
  31. GUID dGuid;
  32. LPDIRECTINPUTDEVICE8 dDevice;
  33. dinput_mapping *dMapping;
  34. dx_gctrl_device *next;
  35. };
  36. typedef struct {
  37. hl_type *t;
  38. dx_gctrl_device *device;
  39. int index;
  40. vstring *name;
  41. int buttons;
  42. vbyte *axes;
  43. vdynamic *rumbleEnd;
  44. } dx_gctrl_data;
  45. static dx_gctrl_device *dx_gctrl_devices = NULL;
  46. static dx_gctrl_device *dx_gctrl_removed = NULL;
  47. static CRITICAL_SECTION dx_gctrl_cs;
  48. static bool dx_gctrl_syncNeeded = FALSE;
  49. // DirectInput specific
  50. static LPDIRECTINPUT8 gctrl_dinput = NULL;
  51. static varray *gctrl_dinput_mappings = NULL;
  52. static HWND gctrl_dinput_win = NULL;
  53. static bool gctrl_dinput_changed = FALSE;
  54. // XInput
  55. static void gctrl_xinput_add(int uid, dx_gctrl_device **current) {
  56. dx_gctrl_device *device = *current;
  57. dx_gctrl_device *prev = NULL;
  58. while (device) {
  59. if (device->xUID == uid) {
  60. if (device == *current) *current = device->next;
  61. else if (prev) prev->next = device->next;
  62. device->next = dx_gctrl_devices;
  63. dx_gctrl_devices = device;
  64. return;
  65. }
  66. prev = device;
  67. device = device->next;
  68. }
  69. device = (dx_gctrl_device*)malloc(sizeof(dx_gctrl_device));
  70. ZeroMemory(device, sizeof(dx_gctrl_device));
  71. device->name = "XInput controller";
  72. device->xUID = uid;
  73. device->isNew = TRUE;
  74. device->next = dx_gctrl_devices;
  75. dx_gctrl_devices = device;
  76. dx_gctrl_syncNeeded = TRUE;
  77. }
  78. static void gctrl_xinput_update(dx_gctrl_data *data) {
  79. XINPUT_STATE state;
  80. HRESULT r = XInputGetState(data->device->xUID, &state);
  81. if (FAILED(r))
  82. return;
  83. data->buttons = (state.Gamepad.wButtons & 0x0FFF) | (state.Gamepad.wButtons & 0xF000) >> 2;
  84. ((double*)data->axes)[0] = (double)(state.Gamepad.sThumbLX < -32767 ? -1. : (state.Gamepad.sThumbLX / 32767.));
  85. ((double*)data->axes)[1] = (double)(state.Gamepad.sThumbLY < -32767 ? -1. : (state.Gamepad.sThumbLY / 32767.));
  86. ((double*)data->axes)[2] = (double)(state.Gamepad.sThumbRX < -32767 ? -1. : (state.Gamepad.sThumbRX / 32767.));
  87. ((double*)data->axes)[3] = (double)(state.Gamepad.sThumbRY < -32767 ? -1. : (state.Gamepad.sThumbRY / 32767.));
  88. ((double*)data->axes)[4] = (double)(state.Gamepad.bLeftTrigger / 255.);
  89. ((double*)data->axes)[5] = (double)(state.Gamepad.bRightTrigger / 255.);
  90. }
  91. // DirectInput
  92. static LRESULT CALLBACK gctrl_WndProc(HWND wnd, UINT umsg, WPARAM wparam, LPARAM lparam) {
  93. switch (umsg) {
  94. case WM_DEVICECHANGE:
  95. switch (wparam) {
  96. case DBT_DEVICEARRIVAL:
  97. case DBT_DEVICEREMOVECOMPLETE:
  98. if (((DEV_BROADCAST_HDR*)lparam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
  99. gctrl_dinput_changed = TRUE;
  100. }
  101. break;
  102. }
  103. }
  104. return DefWindowProc(wnd, umsg, wparam, lparam);
  105. }
  106. static void gctrl_dinput_init( varray *mappings ) {
  107. if( !gctrl_dinput) {
  108. DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8, &gctrl_dinput, NULL);
  109. if (gctrl_dinput) {
  110. WNDCLASSEX wc;
  111. memset(&wc, 0, sizeof(wc));
  112. wc.lpfnWndProc = gctrl_WndProc;
  113. wc.hInstance = GetModuleHandle(NULL);
  114. wc.lpszClassName = USTR("HL_GCTRL");
  115. wc.cbSize = sizeof(WNDCLASSEX);
  116. RegisterClassEx(&wc);
  117. gctrl_dinput_win = CreateWindowEx(0, USTR("HL_GCTRL"), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
  118. GUID guid = { 0x4D1E55B2L, 0xF16F, 0x11CF,{ 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
  119. DEV_BROADCAST_DEVICEINTERFACE dbh;
  120. memset(&dbh, 0, sizeof(dbh));
  121. dbh.dbcc_size = sizeof(dbh);
  122. dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
  123. dbh.dbcc_classguid = guid;
  124. RegisterDeviceNotification(gctrl_dinput_win, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE);
  125. gctrl_dinput_changed = TRUE;
  126. }
  127. }
  128. if( mappings ) {
  129. if( gctrl_dinput_mappings )
  130. hl_remove_root(&gctrl_dinput_mappings);
  131. gctrl_dinput_mappings = mappings;
  132. hl_add_root(&gctrl_dinput_mappings);
  133. }
  134. }
  135. static BOOL CALLBACK gctrl_dinput_deviceCb(const DIDEVICEINSTANCE *instance, void *context) {
  136. dx_gctrl_device **current = (dx_gctrl_device**)context;
  137. dx_gctrl_device *device = *current;
  138. dx_gctrl_device *prev = NULL;
  139. LPDIRECTINPUTDEVICE8 di_device;
  140. HRESULT result;
  141. dinput_mapping *mapping = NULL;
  142. while( device ) {
  143. if(device->xUID < 0 && !memcmp(&device->dGuid, &instance->guidInstance, sizeof(device->dGuid)) ) {
  144. if( device == *current ) *current = device->next;
  145. else if( prev ) prev->next = device->next;
  146. device->next = dx_gctrl_devices;
  147. dx_gctrl_devices = device;
  148. return DIENUM_CONTINUE;
  149. }
  150. prev = device;
  151. device = device->next;
  152. }
  153. // Find mapping
  154. for( int i=0; i<gctrl_dinput_mappings->size; i++ ){
  155. dinput_mapping *m = hl_aptr(gctrl_dinput_mappings, dinput_mapping*)[i];
  156. if( instance->guidProduct.Data1 == m->guid ) {
  157. mapping = m;
  158. break;
  159. }
  160. }
  161. if (!mapping ) return DIENUM_CONTINUE;
  162. result = IDirectInput8_CreateDevice(gctrl_dinput, &instance->guidInstance, &di_device, NULL);
  163. if( FAILED(result) ) return DIENUM_CONTINUE;
  164. device = (dx_gctrl_device*)malloc(sizeof(dx_gctrl_device));
  165. ZeroMemory(device, sizeof(dx_gctrl_device));
  166. result = IDirectInputDevice8_QueryInterface(di_device, &IID_IDirectInputDevice8, (LPVOID *)&device->dDevice);
  167. IDirectInputDevice8_Release(di_device);
  168. if( FAILED(result) ) {
  169. free(device);
  170. return DIENUM_CONTINUE;
  171. }
  172. result = IDirectInputDevice8_SetDataFormat(device->dDevice, &c_dfDIJoystick2);
  173. if( FAILED(result) ) {
  174. free(device);
  175. return DIENUM_CONTINUE;
  176. }
  177. device->name = mapping->name;
  178. device->xUID = -1;
  179. device->dMapping = mapping;
  180. memcpy(&device->dGuid, &instance->guidInstance, sizeof(device->dGuid));
  181. device->isNew = TRUE;
  182. device->next = dx_gctrl_devices;
  183. dx_gctrl_devices = device;
  184. dx_gctrl_syncNeeded = TRUE;
  185. return DIENUM_CONTINUE;
  186. }
  187. static void gctrl_dinput_remove(dx_gctrl_device *device) {
  188. IDirectInputDevice8_Unacquire(device->dDevice);
  189. IDirectInputDevice8_Release(device->dDevice);
  190. }
  191. // Based on SDL ( https://hg.libsdl.org/SDL/file/007dfe83abf8/src/joystick/windows/SDL_dinputjoystick.c )
  192. static int gctrl_dinput_translatePOV(DWORD value) {
  193. const int HAT_VALS[] = {
  194. HAT_UP,
  195. HAT_UP | HAT_RIGHT,
  196. HAT_RIGHT,
  197. HAT_DOWN | HAT_RIGHT,
  198. HAT_DOWN,
  199. HAT_DOWN | HAT_LEFT,
  200. HAT_LEFT,
  201. HAT_UP | HAT_LEFT
  202. };
  203. if( LOWORD(value) == 0xFFFF ) return HAT_CENTERED;
  204. value += 4500 / 2;
  205. value %= 36000;
  206. value /= 4500;
  207. if( value < 0 || value >= 8 ) return HAT_CENTERED;
  208. return HAT_VALS[value];
  209. }
  210. static void gctrl_dinput_update(dx_gctrl_data *data) {
  211. DIJOYSTATE2 state;
  212. long *p = (long*)&state;
  213. dinput_mapping *mapping = data->device->dMapping;
  214. LPDIRECTINPUTDEVICE8 dDevice = data->device->dDevice;
  215. HRESULT result = IDirectInputDevice8_GetDeviceState(dDevice, sizeof(DIJOYSTATE2), &state);
  216. if( result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED ) {
  217. IDirectInputDevice8_Acquire(dDevice);
  218. result = IDirectInputDevice8_GetDeviceState(dDevice, sizeof(DIJOYSTATE2), &state);
  219. }
  220. if( result != DI_OK )
  221. return;
  222. data->buttons = 0;
  223. for( int i = 0; i < mapping->button->size; i++ ) {
  224. dinput_mapping_btn *bmap = hl_aptr(mapping->button, dinput_mapping_btn*)[i];
  225. if (!bmap) continue;
  226. int val;
  227. if( bmap->axis < 0 && bmap->mask == 0 ) {
  228. val = state.rgbButtons[bmap->num];
  229. } else if( bmap->axis < 0 ){
  230. val = gctrl_dinput_translatePOV(state.rgdwPOV[bmap->num])&bmap->mask;
  231. } else {
  232. val = *(p + bmap->axis);
  233. }
  234. switch (i) {
  235. case 0:
  236. case 2:
  237. ((double*)data->axes)[i] = (double)(val / 65535.) * 2 - 1.;
  238. break;
  239. case 1:
  240. case 3:
  241. ((double*)data->axes)[i] = (double)(val / 65535.) * - 2 + 1.;
  242. break;
  243. case 4:
  244. case 5:
  245. ((double*)data->axes)[i] = bmap->axis < 0 ? (val > 0 ? 1 : 0) : (double)(val / 65535.);
  246. break;
  247. default:
  248. data->buttons |= (val > 0 ? 1 : 0) << (i - 6);
  249. break;
  250. }
  251. }
  252. }
  253. //
  254. void gctrl_detect_thread(void *p) {
  255. while (true) {
  256. EnterCriticalSection(&dx_gctrl_cs);
  257. dx_gctrl_device *current = dx_gctrl_devices;
  258. dx_gctrl_devices = NULL;
  259. // XInput
  260. for (int uid = XUSER_MAX_COUNT - 1; uid >= 0; uid--) {
  261. XINPUT_CAPABILITIES capabilities;
  262. if (XInputGetCapabilities(uid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS)
  263. gctrl_xinput_add(uid, &current);
  264. }
  265. // DInput
  266. if (gctrl_dinput ) {
  267. if (gctrl_dinput_changed) {
  268. IDirectInput8_EnumDevices(gctrl_dinput, DI8DEVCLASS_GAMECTRL, gctrl_dinput_deviceCb, &current, DIEDFL_ATTACHEDONLY);
  269. gctrl_dinput_changed = FALSE;
  270. } else {
  271. while (current) {
  272. dx_gctrl_device *next = current->next;
  273. if (current->xUID < 0) {
  274. current->next = dx_gctrl_devices;
  275. dx_gctrl_devices = current;
  276. }
  277. current = next;
  278. }
  279. }
  280. }
  281. while (current) {
  282. dx_gctrl_device *next = current->next;
  283. current->next = dx_gctrl_removed;
  284. dx_gctrl_removed = current;
  285. dx_gctrl_syncNeeded = TRUE;
  286. current = next;
  287. }
  288. LeaveCriticalSection(&dx_gctrl_cs);
  289. Sleep(300);
  290. }
  291. }
  292. HL_PRIM void HL_NAME(gctrl_init)( varray *mappings ) {
  293. gctrl_dinput_init( mappings );
  294. InitializeCriticalSection(&dx_gctrl_cs);
  295. hl_thread_start(gctrl_detect_thread, NULL, FALSE);
  296. }
  297. static void gctrl_onDetect( vclosure *onDetect, dx_gctrl_device *device, const char *name) {
  298. if( !onDetect ) return;
  299. if( onDetect->hasValue )
  300. ((void(*)(void *, dx_gctrl_device*, vbyte*))onDetect->fun)(onDetect->value, device, (vbyte*)name);
  301. else
  302. ((void(*)(dx_gctrl_device*, vbyte*))onDetect->fun)(device, (vbyte*)name);
  303. }
  304. HL_PRIM void HL_NAME(gctrl_detect)( vclosure *onDetect ) {
  305. if (dx_gctrl_syncNeeded) {
  306. EnterCriticalSection(&dx_gctrl_cs);
  307. dx_gctrl_device *device = dx_gctrl_devices;
  308. while (device) {
  309. if (device->isNew) {
  310. gctrl_onDetect(onDetect, device, device->name);
  311. device->isNew = FALSE;
  312. }
  313. device = device->next;
  314. }
  315. while (dx_gctrl_removed) {
  316. device = dx_gctrl_removed;
  317. dx_gctrl_removed = device->next;
  318. gctrl_onDetect(onDetect, device, NULL);
  319. if (device->dDevice)
  320. gctrl_dinput_remove(device);
  321. free(device);
  322. }
  323. dx_gctrl_syncNeeded = FALSE;
  324. LeaveCriticalSection(&dx_gctrl_cs);
  325. }
  326. }
  327. HL_PRIM void HL_NAME(gctrl_update)(dx_gctrl_data *data) {
  328. if( data->device->xUID >= 0 )
  329. gctrl_xinput_update(data);
  330. else if( data->device->dDevice && data->device->dMapping )
  331. gctrl_dinput_update(data);
  332. }
  333. HL_PRIM void HL_NAME(gctrl_set_vibration)(dx_gctrl_device *device, double strength) {
  334. if( device->xUID >= 0 ){
  335. XINPUT_VIBRATION vibration;
  336. vibration.wLeftMotorSpeed = vibration.wRightMotorSpeed = (WORD)(strength * 65535);
  337. XInputSetState(device->xUID, &vibration);
  338. }
  339. }
  340. #define TGAMECTRL _ABSTRACT(dx_gctrl_device)
  341. DEFINE_PRIM(_VOID, gctrl_init, _ARR);
  342. DEFINE_PRIM(_VOID, gctrl_detect, _FUN(_VOID, TGAMECTRL _BYTES));
  343. DEFINE_PRIM(_VOID, gctrl_update, _OBJ(TGAMECTRL _I32 _STRING _I32 _BYTES _NULL(_F64)));
  344. DEFINE_PRIM(_VOID, gctrl_set_vibration, TGAMECTRL _F64);