• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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