1 //========================================================================
2 // GLFW 3.1 Win32 - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2002-2006 Marcus Geelnard
5 // Copyright (c) 2006-2016 Camilla Berglund <elmindreda@glfw.org>
6 //
7 // This software is provided 'as-is', without any express or implied
8 // warranty. In no event will the authors be held liable for any damages
9 // arising from the use of this software.
10 //
11 // Permission is granted to anyone to use this software for any purpose,
12 // including commercial applications, and to alter it and redistribute it
13 // freely, subject to the following restrictions:
14 //
15 // 1. The origin of this software must not be misrepresented; you must not
16 // claim that you wrote the original software. If you use this software
17 // in a product, an acknowledgment in the product documentation would
18 // be appreciated but is not required.
19 //
20 // 2. Altered source versions must be plainly marked as such, and must not
21 // be misrepresented as being the original software.
22 //
23 // 3. This notice may not be removed or altered from any source
24 // distribution.
25 //
26 //========================================================================
27
28 #include "internal.h"
29
30 #include <math.h>
31
32 #include <initguid.h>
33
34 #define _GLFW_PRESENCE_ONLY 1
35 #define _GLFW_UPDATE_STATE 2
36
37 #define _GLFW_TYPE_AXIS 0
38 #define _GLFW_TYPE_SLIDER 1
39 #define _GLFW_TYPE_BUTTON 2
40 #define _GLFW_TYPE_POV 3
41
42 // Data produced with DirectInput device object enumeration
43 //
44 typedef struct _GLFWobjenumWin32
45 {
46 IDirectInputDevice8W* device;
47 _GLFWjoyobjectWin32* objects;
48 int objectCount;
49 int axisCount;
50 int sliderCount;
51 int buttonCount;
52 int povCount;
53 } _GLFWobjenumWin32;
54
55 // Define only the necessary GUIDs (it's bad enough that we're exporting these)
56 //
57 DEFINE_GUID(IID_IDirectInput8W,0xbf798031,0x483a,0x4da2,0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00);
58 DEFINE_GUID(GUID_XAxis,0xa36d02e0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
59 DEFINE_GUID(GUID_YAxis,0xa36d02e1,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
60 DEFINE_GUID(GUID_ZAxis,0xa36d02e2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
61 DEFINE_GUID(GUID_RxAxis,0xa36d02f4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
62 DEFINE_GUID(GUID_RyAxis,0xa36d02f5,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
63 DEFINE_GUID(GUID_RzAxis,0xa36d02e3,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
64 DEFINE_GUID(GUID_Slider,0xa36d02e4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
65 DEFINE_GUID(GUID_Button,0xa36d02f0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
66 DEFINE_GUID(GUID_POV,0xa36d02f2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
67
68 // Object data array for our clone of c_dfDIJoystick
69 // Generated with https://github.com/elmindreda/c_dfDIJoystick2
70 //
71 static DIOBJECTDATAFORMAT _glfwObjectDataFormats[] =
72 {
73 { &GUID_XAxis,DIJOFS_X,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
74 { &GUID_YAxis,DIJOFS_Y,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
75 { &GUID_ZAxis,DIJOFS_Z,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
76 { &GUID_RxAxis,DIJOFS_RX,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
77 { &GUID_RyAxis,DIJOFS_RY,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
78 { &GUID_RzAxis,DIJOFS_RZ,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
79 { &GUID_Slider,DIJOFS_SLIDER(0),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
80 { &GUID_Slider,DIJOFS_SLIDER(1),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
81 { &GUID_POV,DIJOFS_POV(0),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
82 { &GUID_POV,DIJOFS_POV(1),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
83 { &GUID_POV,DIJOFS_POV(2),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
84 { &GUID_POV,DIJOFS_POV(3),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
85 { NULL,DIJOFS_BUTTON(0),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
86 { NULL,DIJOFS_BUTTON(1),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
87 { NULL,DIJOFS_BUTTON(2),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
88 { NULL,DIJOFS_BUTTON(3),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
89 { NULL,DIJOFS_BUTTON(4),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
90 { NULL,DIJOFS_BUTTON(5),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
91 { NULL,DIJOFS_BUTTON(6),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
92 { NULL,DIJOFS_BUTTON(7),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
93 { NULL,DIJOFS_BUTTON(8),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
94 { NULL,DIJOFS_BUTTON(9),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
95 { NULL,DIJOFS_BUTTON(10),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
96 { NULL,DIJOFS_BUTTON(11),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
97 { NULL,DIJOFS_BUTTON(12),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
98 { NULL,DIJOFS_BUTTON(13),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
99 { NULL,DIJOFS_BUTTON(14),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
100 { NULL,DIJOFS_BUTTON(15),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
101 { NULL,DIJOFS_BUTTON(16),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
102 { NULL,DIJOFS_BUTTON(17),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
103 { NULL,DIJOFS_BUTTON(18),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
104 { NULL,DIJOFS_BUTTON(19),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
105 { NULL,DIJOFS_BUTTON(20),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
106 { NULL,DIJOFS_BUTTON(21),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
107 { NULL,DIJOFS_BUTTON(22),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
108 { NULL,DIJOFS_BUTTON(23),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
109 { NULL,DIJOFS_BUTTON(24),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
110 { NULL,DIJOFS_BUTTON(25),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
111 { NULL,DIJOFS_BUTTON(26),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
112 { NULL,DIJOFS_BUTTON(27),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
113 { NULL,DIJOFS_BUTTON(28),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
114 { NULL,DIJOFS_BUTTON(29),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
115 { NULL,DIJOFS_BUTTON(30),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
116 { NULL,DIJOFS_BUTTON(31),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
117 };
118
119 // Our clone of c_dfDIJoystick
120 //
121 static const DIDATAFORMAT _glfwDataFormat =
122 {
123 sizeof(DIDATAFORMAT),
124 sizeof(DIOBJECTDATAFORMAT),
125 DIDFT_ABSAXIS,
126 sizeof(DIJOYSTATE),
127 sizeof(_glfwObjectDataFormats) / sizeof(DIOBJECTDATAFORMAT),
128 _glfwObjectDataFormats
129 };
130
131 // Returns a description fitting the specified XInput capabilities
132 //
getDeviceDescription(const XINPUT_CAPABILITIES * xic)133 static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic)
134 {
135 switch (xic->SubType)
136 {
137 case XINPUT_DEVSUBTYPE_WHEEL:
138 return "XInput Wheel";
139 case XINPUT_DEVSUBTYPE_ARCADE_STICK:
140 return "XInput Arcade Stick";
141 case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
142 return "XInput Flight Stick";
143 case XINPUT_DEVSUBTYPE_DANCE_PAD:
144 return "XInput Dance Pad";
145 case XINPUT_DEVSUBTYPE_GUITAR:
146 return "XInput Guitar";
147 case XINPUT_DEVSUBTYPE_DRUM_KIT:
148 return "XInput Drum Kit";
149 case XINPUT_DEVSUBTYPE_GAMEPAD:
150 {
151 if (xic->Flags & XINPUT_CAPS_WIRELESS)
152 return "Wireless Xbox 360 Controller";
153 else
154 return "Xbox 360 Controller";
155 }
156 }
157
158 return "Unknown XInput Device";
159 }
160
161 // Lexically compare device objects
162 //
compareJoystickObjects(const void * first,const void * second)163 static int compareJoystickObjects(const void* first, const void* second)
164 {
165 const _GLFWjoyobjectWin32* fo = first;
166 const _GLFWjoyobjectWin32* so = second;
167
168 if (fo->type != so->type)
169 return fo->type - so->type;
170
171 return fo->offset - so->offset;
172 }
173
174 // Checks whether the specified device supports XInput
175 // Technique from FDInputJoystickManager::IsXInputDeviceFast in ZDoom
176 //
supportsXInput(const GUID * guid)177 static GLFWbool supportsXInput(const GUID* guid)
178 {
179 UINT i, count = 0;
180 RAWINPUTDEVICELIST* ridl;
181 GLFWbool result = GLFW_FALSE;
182
183 if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0)
184 return GLFW_FALSE;
185
186 ridl = calloc(count, sizeof(RAWINPUTDEVICELIST));
187
188 if (GetRawInputDeviceList(ridl, &count, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1)
189 {
190 free(ridl);
191 return GLFW_FALSE;
192 }
193
194 for (i = 0; i < count; i++)
195 {
196 RID_DEVICE_INFO rdi;
197 char name[256];
198 UINT size;
199
200 if (ridl[i].dwType != RIM_TYPEHID)
201 continue;
202
203 ZeroMemory(&rdi, sizeof(rdi));
204 rdi.cbSize = sizeof(rdi);
205 size = sizeof(rdi);
206
207 if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice,
208 RIDI_DEVICEINFO,
209 &rdi, &size) == -1)
210 {
211 continue;
212 }
213
214 if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) != (LONG) guid->Data1)
215 continue;
216
217 memset(name, 0, sizeof(name));
218 size = sizeof(name);
219
220 if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice,
221 RIDI_DEVICENAME,
222 name, &size) == -1)
223 {
224 break;
225 }
226
227 name[sizeof(name) - 1] = '\0';
228 if (strstr(name, "IG_"))
229 {
230 result = GLFW_TRUE;
231 break;
232 }
233 }
234
235 free(ridl);
236 return result;
237 }
238
239 // Frees all resources associated with the specified joystick
240 //
closeJoystick(_GLFWjoystickWin32 * js)241 static void closeJoystick(_GLFWjoystickWin32* js)
242 {
243 if (js->device)
244 {
245 IDirectInputDevice8_Unacquire(js->device);
246 IDirectInputDevice8_Release(js->device);
247 }
248
249 free(js->name);
250 free(js->axes);
251 free(js->buttons);
252 free(js->objects);
253 memset(js, 0, sizeof(_GLFWjoystickWin32));
254
255 _glfwInputJoystickChange((int) (js - _glfw.win32_js), GLFW_DISCONNECTED);
256 }
257
258 // DirectInput device object enumeration callback
259 // Insights gleaned from SDL2
260 //
deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW * doi,void * user)261 static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi,
262 void* user)
263 {
264 _GLFWobjenumWin32* data = user;
265 _GLFWjoyobjectWin32* object = data->objects + data->objectCount;
266
267 if (DIDFT_GETTYPE(doi->dwType) & DIDFT_AXIS)
268 {
269 DIPROPRANGE dipr;
270
271 if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0)
272 object->offset = DIJOFS_SLIDER(data->sliderCount);
273 else if (memcmp(&doi->guidType, &GUID_XAxis, sizeof(GUID)) == 0)
274 object->offset = DIJOFS_X;
275 else if (memcmp(&doi->guidType, &GUID_YAxis, sizeof(GUID)) == 0)
276 object->offset = DIJOFS_Y;
277 else if (memcmp(&doi->guidType, &GUID_ZAxis, sizeof(GUID)) == 0)
278 object->offset = DIJOFS_Z;
279 else if (memcmp(&doi->guidType, &GUID_RxAxis, sizeof(GUID)) == 0)
280 object->offset = DIJOFS_RX;
281 else if (memcmp(&doi->guidType, &GUID_RyAxis, sizeof(GUID)) == 0)
282 object->offset = DIJOFS_RY;
283 else if (memcmp(&doi->guidType, &GUID_RzAxis, sizeof(GUID)) == 0)
284 object->offset = DIJOFS_RZ;
285 else
286 return DIENUM_CONTINUE;
287
288 ZeroMemory(&dipr, sizeof(dipr));
289 dipr.diph.dwSize = sizeof(dipr);
290 dipr.diph.dwHeaderSize = sizeof(dipr.diph);
291 dipr.diph.dwObj = doi->dwType;
292 dipr.diph.dwHow = DIPH_BYID;
293 dipr.lMin = -32768;
294 dipr.lMax = 32767;
295
296 if (FAILED(IDirectInputDevice8_SetProperty(data->device,
297 DIPROP_RANGE,
298 &dipr.diph)))
299 {
300 return DIENUM_CONTINUE;
301 }
302
303 if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0)
304 {
305 object->type = _GLFW_TYPE_SLIDER;
306 data->sliderCount++;
307 }
308 else
309 {
310 object->type = _GLFW_TYPE_AXIS;
311 data->axisCount++;
312 }
313 }
314 else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_BUTTON)
315 {
316 object->offset = DIJOFS_BUTTON(data->buttonCount);
317 object->type = _GLFW_TYPE_BUTTON;
318 data->buttonCount++;
319 }
320 else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_POV)
321 {
322 object->offset = DIJOFS_POV(data->povCount);
323 object->type = _GLFW_TYPE_POV;
324 data->povCount++;
325 }
326
327 data->objectCount++;
328 return DIENUM_CONTINUE;
329 }
330
331 // DirectInput device enumeration callback
332 //
deviceCallback(const DIDEVICEINSTANCE * di,void * user)333 static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user)
334 {
335 int joy = 0;
336 DIDEVCAPS dc;
337 DIPROPDWORD dipd;
338 IDirectInputDevice8* device;
339 _GLFWobjenumWin32 data;
340 _GLFWjoystickWin32* js;
341
342 for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
343 {
344 if (memcmp(&_glfw.win32_js[joy].guid, &di->guidInstance, sizeof(GUID)) == 0)
345 return DIENUM_CONTINUE;
346 }
347
348 for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
349 {
350 if (!_glfw.win32_js[joy].present)
351 break;
352 }
353
354 if (joy > GLFW_JOYSTICK_LAST)
355 return DIENUM_STOP;
356
357 if (supportsXInput(&di->guidProduct))
358 return DIENUM_CONTINUE;
359
360 if (FAILED(IDirectInput8_CreateDevice(_glfw.win32.dinput8.api,
361 &di->guidInstance,
362 &device,
363 NULL)))
364 {
365 _glfwInputError(GLFW_PLATFORM_ERROR, "DI: Failed to create device");
366 return DIENUM_CONTINUE;
367 }
368
369 if (FAILED(IDirectInputDevice8_SetDataFormat(device, &_glfwDataFormat)))
370 {
371 _glfwInputError(GLFW_PLATFORM_ERROR,
372 "DI: Failed to set device data format");
373
374 IDirectInputDevice8_Release(device);
375 return DIENUM_CONTINUE;
376 }
377
378 ZeroMemory(&dc, sizeof(dc));
379 dc.dwSize = sizeof(dc);
380
381 if (FAILED(IDirectInputDevice8_GetCapabilities(device, &dc)))
382 {
383 _glfwInputError(GLFW_PLATFORM_ERROR,
384 "DI: Failed to query device capabilities");
385
386 IDirectInputDevice8_Release(device);
387 return DIENUM_CONTINUE;
388 }
389
390 ZeroMemory(&dipd, sizeof(dipd));
391 dipd.diph.dwSize = sizeof(dipd);
392 dipd.diph.dwHeaderSize = sizeof(dipd.diph);
393 dipd.diph.dwHow = DIPH_DEVICE;
394 dipd.dwData = DIPROPAXISMODE_ABS;
395
396 if (FAILED(IDirectInputDevice8_SetProperty(device,
397 DIPROP_AXISMODE,
398 &dipd.diph)))
399 {
400 _glfwInputError(GLFW_PLATFORM_ERROR,
401 "DI: Failed to set device axis mode");
402
403 IDirectInputDevice8_Release(device);
404 return DIENUM_CONTINUE;
405 }
406
407 memset(&data, 0, sizeof(data));
408 data.device = device;
409 data.objects = calloc(dc.dwAxes + dc.dwButtons + dc.dwPOVs,
410 sizeof(_GLFWjoyobjectWin32));
411
412 if (FAILED(IDirectInputDevice8_EnumObjects(device,
413 deviceObjectCallback,
414 &data,
415 DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV)))
416 {
417 _glfwInputError(GLFW_PLATFORM_ERROR,
418 "DI: Failed to enumerate device objects");
419
420 IDirectInputDevice8_Release(device);
421 free(data.objects);
422 return DIENUM_CONTINUE;
423 }
424
425 qsort(data.objects, data.objectCount,
426 sizeof(_GLFWjoyobjectWin32),
427 compareJoystickObjects);
428
429 js = _glfw.win32_js + joy;
430 js->device = device;
431 js->guid = di->guidInstance;
432 js->axisCount = data.axisCount + data.sliderCount;
433 js->axes = calloc(js->axisCount, sizeof(float));
434 js->buttonCount += data.buttonCount + data.povCount * 4;
435 js->buttons = calloc(js->buttonCount, 1);
436 js->objects = data.objects;
437 js->objectCount = data.objectCount;
438 js->name = _glfwCreateUTF8FromWideStringWin32(di->tszInstanceName);
439 js->present = GLFW_TRUE;
440
441 _glfwInputJoystickChange(joy, GLFW_CONNECTED);
442 return DIENUM_CONTINUE;
443 }
444
445 // Attempt to open the specified joystick device
446 // TODO: Pack state arrays for non-gamepad devices
447 //
openXinputDevice(DWORD index)448 static GLFWbool openXinputDevice(DWORD index)
449 {
450 int joy;
451 XINPUT_CAPABILITIES xic;
452 _GLFWjoystickWin32* js;
453
454 for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
455 {
456 if (_glfw.win32_js[joy].present &&
457 _glfw.win32_js[joy].device == NULL &&
458 _glfw.win32_js[joy].index == index)
459 {
460 return GLFW_FALSE;
461 }
462 }
463
464 for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
465 {
466 if (!_glfw.win32_js[joy].present)
467 break;
468 }
469
470 if (joy > GLFW_JOYSTICK_LAST)
471 return GLFW_FALSE;
472
473 if (_glfw_XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS)
474 return GLFW_FALSE;
475
476 js = _glfw.win32_js + joy;
477 js->axisCount = 6;
478 js->axes = calloc(js->axisCount, sizeof(float));
479 js->buttonCount = 14;
480 js->buttons = calloc(js->buttonCount, 1);
481 js->present = GLFW_TRUE;
482 js->name = strdup(getDeviceDescription(&xic));
483 js->index = index;
484
485 _glfwInputJoystickChange(joy, GLFW_CONNECTED);
486
487 return GLFW_TRUE;
488 }
489
490 // Polls for and processes events the specified joystick
491 //
pollJoystickState(_GLFWjoystickWin32 * js,int mode)492 static GLFWbool pollJoystickState(_GLFWjoystickWin32* js, int mode)
493 {
494 if (!js->present)
495 return GLFW_FALSE;
496
497 if (js->device)
498 {
499 int i, j, ai = 0, bi = 0;
500 HRESULT result;
501 DIJOYSTATE state;
502
503 IDirectInputDevice8_Poll(js->device);
504 result = IDirectInputDevice8_GetDeviceState(js->device,
505 sizeof(state),
506 &state);
507 if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST)
508 {
509 IDirectInputDevice8_Acquire(js->device);
510 IDirectInputDevice8_Poll(js->device);
511 result = IDirectInputDevice8_GetDeviceState(js->device,
512 sizeof(state),
513 &state);
514 }
515
516 if (FAILED(result))
517 {
518 closeJoystick(js);
519 return GLFW_FALSE;
520 }
521
522 if (mode == _GLFW_PRESENCE_ONLY)
523 return GLFW_TRUE;
524
525 for (i = 0; i < js->objectCount; i++)
526 {
527 const void* data = (char*) &state + js->objects[i].offset;
528
529 switch (js->objects[i].type)
530 {
531 case _GLFW_TYPE_AXIS:
532 case _GLFW_TYPE_SLIDER:
533 {
534 js->axes[ai++] = (*((LONG*) data) + 0.5f) / 32767.5f;
535 break;
536 }
537
538 case _GLFW_TYPE_BUTTON:
539 {
540 if (*((BYTE*) data) & 0x80)
541 js->buttons[bi++] = GLFW_PRESS;
542 else
543 js->buttons[bi++] = GLFW_RELEASE;
544
545 break;
546 }
547
548 case _GLFW_TYPE_POV:
549 {
550 const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 };
551 // Screams of horror are appropriate at this point
552 int value = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES);
553 if (value < 0 || value > 8)
554 value = 8;
555
556 for (j = 0; j < 4; j++)
557 {
558 if (directions[value] & (1 << j))
559 js->buttons[bi++] = GLFW_PRESS;
560 else
561 js->buttons[bi++] = GLFW_RELEASE;
562 }
563
564 break;
565 }
566 }
567 }
568
569 return GLFW_TRUE;
570 }
571 else
572 {
573 int i;
574 DWORD result;
575 XINPUT_STATE xis;
576 const WORD buttons[14] =
577 {
578 XINPUT_GAMEPAD_A,
579 XINPUT_GAMEPAD_B,
580 XINPUT_GAMEPAD_X,
581 XINPUT_GAMEPAD_Y,
582 XINPUT_GAMEPAD_LEFT_SHOULDER,
583 XINPUT_GAMEPAD_RIGHT_SHOULDER,
584 XINPUT_GAMEPAD_BACK,
585 XINPUT_GAMEPAD_START,
586 XINPUT_GAMEPAD_LEFT_THUMB,
587 XINPUT_GAMEPAD_RIGHT_THUMB,
588 XINPUT_GAMEPAD_DPAD_UP,
589 XINPUT_GAMEPAD_DPAD_RIGHT,
590 XINPUT_GAMEPAD_DPAD_DOWN,
591 XINPUT_GAMEPAD_DPAD_LEFT
592 };
593
594 result = _glfw_XInputGetState(js->index, &xis);
595 if (result != ERROR_SUCCESS)
596 {
597 if (result == ERROR_DEVICE_NOT_CONNECTED)
598 closeJoystick(js);
599
600 return GLFW_FALSE;
601 }
602
603 if (mode == _GLFW_PRESENCE_ONLY)
604 return GLFW_TRUE;
605
606 if (sqrt((double) (xis.Gamepad.sThumbLX * xis.Gamepad.sThumbLX +
607 xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY)) >
608 (double) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
609 {
610 js->axes[0] = (xis.Gamepad.sThumbLX + 0.5f) / 32767.f;
611 js->axes[1] = (xis.Gamepad.sThumbLY + 0.5f) / 32767.f;
612 }
613 else
614 {
615 js->axes[0] = 0.f;
616 js->axes[1] = 0.f;
617 }
618
619 if (sqrt((double) (xis.Gamepad.sThumbRX * xis.Gamepad.sThumbRX +
620 xis.Gamepad.sThumbRY * xis.Gamepad.sThumbRY)) >
621 (double) XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
622 {
623 js->axes[2] = (xis.Gamepad.sThumbRX + 0.5f) / 32767.f;
624 js->axes[3] = (xis.Gamepad.sThumbRY + 0.5f) / 32767.f;
625 }
626 else
627 {
628 js->axes[2] = 0.f;
629 js->axes[3] = 0.f;
630 }
631
632 if (xis.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
633 js->axes[4] = xis.Gamepad.bLeftTrigger / 127.5f - 1.f;
634 else
635 js->axes[4] = -1.f;
636
637 if (xis.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
638 js->axes[5] = xis.Gamepad.bRightTrigger / 127.5f - 1.f;
639 else
640 js->axes[5] = -1.f;
641
642 for (i = 0; i < 14; i++)
643 js->buttons[i] = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0;
644
645 return GLFW_TRUE;
646 }
647 }
648
649
650 //////////////////////////////////////////////////////////////////////////
651 ////// GLFW internal API //////
652 //////////////////////////////////////////////////////////////////////////
653
654 // Initialize joystick interface
655 //
_glfwInitJoysticksWin32(void)656 void _glfwInitJoysticksWin32(void)
657 {
658 if (_glfw.win32.dinput8.instance)
659 {
660 if (FAILED(_glfw_DirectInput8Create(GetModuleHandle(NULL),
661 DIRECTINPUT_VERSION,
662 &IID_IDirectInput8W,
663 (void**) &_glfw.win32.dinput8.api,
664 NULL)))
665 {
666 _glfwInputError(GLFW_PLATFORM_ERROR,
667 "DI: Failed to create interface");
668 }
669 }
670
671 // The initialization takes a long time on a specific computer _glfwDetectJoystickConnectionWin32();
672 }
673
674 // Close all opened joystick handles
675 //
_glfwTerminateJoysticksWin32(void)676 void _glfwTerminateJoysticksWin32(void)
677 {
678 int joy;
679
680 for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
681 closeJoystick(_glfw.win32_js + joy);
682
683 if (_glfw.win32.dinput8.api)
684 IDirectInput8_Release(_glfw.win32.dinput8.api);
685 }
686
687 // Checks for new joysticks after DBT_DEVICEARRIVAL
688 //
_glfwDetectJoystickConnectionWin32(void)689 void _glfwDetectJoystickConnectionWin32(void)
690 {
691 if (_glfw.win32.xinput.instance)
692 {
693 DWORD i;
694
695 for (i = 0; i < XUSER_MAX_COUNT; i++)
696 openXinputDevice(i);
697 }
698
699 if (_glfw.win32.dinput8.api)
700 {
701 if (FAILED(IDirectInput8_EnumDevices(_glfw.win32.dinput8.api,
702 DI8DEVCLASS_GAMECTRL,
703 deviceCallback,
704 NULL,
705 DIEDFL_ALLDEVICES)))
706 {
707 _glfwInputError(GLFW_PLATFORM_ERROR,
708 "Failed to enumerate DirectInput8 devices");
709 return;
710 }
711 }
712 }
713
714 // Checks for joystick disconnection after DBT_DEVICEREMOVECOMPLETE
715 //
_glfwDetectJoystickDisconnectionWin32(void)716 void _glfwDetectJoystickDisconnectionWin32(void)
717 {
718 int joy;
719
720 for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
721 pollJoystickState(_glfw.win32_js + joy, _GLFW_PRESENCE_ONLY);
722 }
723
724
725 //////////////////////////////////////////////////////////////////////////
726 ////// GLFW platform API //////
727 //////////////////////////////////////////////////////////////////////////
728
_glfwPlatformJoystickPresent(int joy)729 int _glfwPlatformJoystickPresent(int joy)
730 {
731 _GLFWjoystickWin32* js = _glfw.win32_js + joy;
732 return pollJoystickState(js, _GLFW_PRESENCE_ONLY);
733 }
734
_glfwPlatformGetJoystickAxes(int joy,int * count)735 const float* _glfwPlatformGetJoystickAxes(int joy, int* count)
736 {
737 _GLFWjoystickWin32* js = _glfw.win32_js + joy;
738 if (!pollJoystickState(js, _GLFW_UPDATE_STATE))
739 return NULL;
740
741 *count = js->axisCount;
742 return js->axes;
743 }
744
_glfwPlatformGetJoystickButtons(int joy,int * count)745 const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count)
746 {
747 _GLFWjoystickWin32* js = _glfw.win32_js + joy;
748 if (!pollJoystickState(js, _GLFW_UPDATE_STATE))
749 return NULL;
750
751 *count = js->buttonCount;
752 return js->buttons;
753 }
754
_glfwPlatformGetJoystickName(int joy)755 const char* _glfwPlatformGetJoystickName(int joy)
756 {
757 _GLFWjoystickWin32* js = _glfw.win32_js + joy;
758 if (!pollJoystickState(js, _GLFW_PRESENCE_ONLY))
759 return NULL;
760
761 return js->name;
762 }
763
764