2
0

gamecontroller.c 11 KB

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