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