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