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     IDirectInputDevice8W* 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     IDirectInputDevice8W* 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         IDirectInputDevice8_Unacquire(js.win32.device);
276         IDirectInputDevice8_Release(js.win32.device);
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(IDirectInputDevice8_SetProperty(data.device,
322                                                    DIPROP_RANGE,
323                                                    &dipr.diph)))
324         {
325             return DIENUM_CONTINUE;
326         }
327 
328         if (memcmp(&doi.guidType, &GUID_Slider, GUID.sizeof) == 0)
329         {
330             object.type = _GLFW_TYPE_SLIDER;
331             data.sliderCount++;
332         }
333         else
334         {
335             object.type = _GLFW_TYPE_AXIS;
336             data.axisCount++;
337         }
338     }
339     else if (DIDFT_GETTYPE(doi.dwType) & DIDFT_BUTTON)
340     {
341         object.offset = DIJOFS_BUTTON(data.buttonCount);
342         object.type = _GLFW_TYPE_BUTTON;
343         data.buttonCount++;
344     }
345     else if (DIDFT_GETTYPE(doi.dwType) & DIDFT_POV)
346     {
347         object.offset = DIJOFS_POV(data.povCount);
348         object.type = _GLFW_TYPE_POV;
349         data.povCount++;
350     }
351 
352     data.objectCount++;
353     return DIENUM_CONTINUE;
354 }
355 
356 // DirectInput device enumeration callback
357 //
358 private extern(Windows) BOOL deviceCallback(const(DIDEVICEINSTANCE)* di, void* user) {
359     int jid = 0;
360     DIDEVCAPS dc;
361     DIPROPDWORD dipd;
362     IDirectInputDevice8* device;
363     _GLFWobjenumWin32 data;
364     //_GLFWjoystick* js; moved down to avoid shadowing
365     char[33] guid;
366     char[256] name;
367 
368     for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
369     {
370         _GLFWjoystick* js = _glfw.joysticks.ptr + jid;
371         if (js.present)
372         {
373             if (memcmp(&js.win32.guid, &di.guidInstance, GUID.sizeof) == 0)
374                 return DIENUM_CONTINUE;
375         }
376     }
377 
378     if (supportsXInput(&di.guidProduct))
379         return DIENUM_CONTINUE;
380 
381     if (FAILED(IDirectInput8_CreateDevice(_glfw.win32.dinput8.api,
382                                           &di.guidInstance,
383                                           &device,
384                                           null)))
385     {
386         _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create device");
387         return DIENUM_CONTINUE;
388     }
389 
390     if (FAILED(IDirectInputDevice8_SetDataFormat(device, &_glfwDataFormat)))
391     {
392         _glfwInputError(GLFW_PLATFORM_ERROR,
393                         "Win32: Failed to set device data format");
394 
395         IDirectInputDevice8_Release(device);
396         return DIENUM_CONTINUE;
397     }
398 
399     memset(&dc, 0, typeof(dc).sizeof);
400     dc.dwSize = typeof(dc).sizeof;
401 
402     if (FAILED(IDirectInputDevice8_GetCapabilities(device, &dc)))
403     {
404         _glfwInputError(GLFW_PLATFORM_ERROR,
405                         "Win32: Failed to query device capabilities");
406 
407         IDirectInputDevice8_Release(device);
408         return DIENUM_CONTINUE;
409     }
410 
411     memset(&dipd, 0, typeof(dipd).sizeof);
412     dipd.diph.dwSize = typeof(dipd).sizeof;
413     dipd.diph.dwHeaderSize = typeof((dipd.diph)).sizeof;
414     dipd.diph.dwHow = DIPH_DEVICE;
415     dipd.dwData = DIPROPAXISMODE_ABS;
416 
417     if (FAILED(IDirectInputDevice8_SetProperty(device,
418                                                DIPROP_AXISMODE,
419                                                &dipd.diph)))
420     {
421         _glfwInputError(GLFW_PLATFORM_ERROR,
422                         "Win32: Failed to set device axis mode");
423 
424         IDirectInputDevice8_Release(device);
425         return DIENUM_CONTINUE;
426     }
427 
428     memset(&data, 0, typeof(data).sizeof);
429     data.device = device;
430     data.objects = cast(_GLFWjoyobjectWin32*) calloc(dc.dwAxes + cast(size_t) dc.dwButtons + dc.dwPOVs,
431                           _GLFWjoyobjectWin32.sizeof);
432 
433     if (FAILED(IDirectInputDevice8_EnumObjects(device,
434                                                &deviceObjectCallback,
435                                                &data,
436                                                DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV)))
437     {
438         _glfwInputError(GLFW_PLATFORM_ERROR,
439                         "Win32: Failed to enumerate device objects");
440 
441         IDirectInputDevice8_Release(device);
442         free(data.objects);
443         return DIENUM_CONTINUE;
444     }
445 
446     qsort(data.objects, data.objectCount,
447           _GLFWjoyobjectWin32.sizeof,
448           &compareJoystickObjects);
449 
450     if (!WideCharToMultiByte(CP_UTF8, 0,
451                              di.tszInstanceName.ptr, -1,
452                              name.ptr, typeof(name).sizeof,
453                              null, null))
454     {
455         _glfwInputError(GLFW_PLATFORM_ERROR,
456                         "Win32: Failed to convert joystick name to UTF-8");
457 
458         IDirectInputDevice8_Release(device);
459         free(data.objects);
460         return DIENUM_STOP;
461     }
462 
463     // Generate a joystick GUID that matches the SDL 2.0.5+ one
464     if (memcmp(&di.guidProduct.Data4[2], "PIDVID".ptr, 6) == 0)
465     {
466         sprintf(guid.ptr, "03000000%02x%02x0000%02x%02x000000000000",
467                 cast(ubyte) di.guidProduct.Data1,
468                 cast(ubyte) (di.guidProduct.Data1 >> 8),
469                 cast(ubyte) (di.guidProduct.Data1 >> 16),
470                 cast(ubyte) (di.guidProduct.Data1 >> 24));
471     }
472     else
473     {
474         sprintf(guid.ptr, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
475                 name[0], name[1], name[2], name[3],
476                 name[4], name[5], name[6], name[7],
477                 name[8], name[9], name[10]);
478     }
479 
480     _GLFWjoystick* js = _glfwAllocJoystick(name.ptr, guid.ptr,
481                             data.axisCount + data.sliderCount,
482                             data.buttonCount,
483                             data.povCount);
484     if (!js)
485     {
486         IDirectInputDevice8_Release(device);
487         free(data.objects);
488         return DIENUM_STOP;
489     }
490 
491     js.win32.device = device;
492     js.win32.guid = di.guidInstance;
493     js.win32.objects = data.objects;
494     js.win32.objectCount = data.objectCount;
495 
496     _glfwInputJoystick(js, GLFW_CONNECTED);
497     return DIENUM_CONTINUE;
498 }
499 
500 
501 //////////////////////////////////////////////////////////////////////////
502 //////                       GLFW internal API                      //////
503 //////////////////////////////////////////////////////////////////////////
504 
505 // Initialize joystick interface
506 //
507 void _glfwInitJoysticksWin32() {
508     if (_glfw.win32.dinput8.instance)
509     {
510         if (FAILED(mixin(DirectInput8Create)(GetModuleHandle(null),
511                                       DIRECTINPUT_VERSION,
512                                       &IID_IDirectInput8W,
513                                       cast(void**) &_glfw.win32.dinput8.api,
514                                       null)))
515         {
516             _glfwInputError(GLFW_PLATFORM_ERROR,
517                             "Win32: Failed to create interface");
518         }
519     }
520     _glfwDetectJoystickConnectionWin32();
521 }
522 
523 // Close all opened joystick handles
524 //
525 void _glfwTerminateJoysticksWin32() {
526     int jid;
527 
528     for (jid = GLFW_JOYSTICK_1;  jid <= GLFW_JOYSTICK_LAST;  jid++)
529         closeJoystick(_glfw.joysticks.ptr + jid);
530 
531     if (_glfw.win32.dinput8.api)
532         IDirectInput8_Release(_glfw.win32.dinput8.api);
533 }
534 
535 // Checks for new joysticks after DBT_DEVICEARRIVAL
536 //
537 void _glfwDetectJoystickConnectionWin32() {
538     if (_glfw.win32.xinput.instance)
539     {
540         DWORD index;
541 
542         for (index = 0;  index < XUSER_MAX_COUNT;  index++)
543         {
544             int jid;
545             char[33] guid;
546             XINPUT_CAPABILITIES xic;
547             _GLFWjoystick* js;
548 
549             for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
550             {
551                 if (_glfw.joysticks[jid].present &&
552                     _glfw.joysticks[jid].win32.device == null &&
553                     _glfw.joysticks[jid].win32.index == index)
554                 {
555                     break;
556                 }
557             }
558 
559             if (jid <= GLFW_JOYSTICK_LAST)
560                 continue;
561 
562             if (mixin(XInputGetCapabilities)(index, 0, &xic) != ERROR_SUCCESS)
563                 continue;
564 
565             // Generate a joystick GUID that matches the SDL 2.0.5+ one
566             sprintf(guid.ptr, "78696e707574%02x000000000000000000",
567                     xic.SubType & 0xff);
568 
569             js = _glfwAllocJoystick(getDeviceDescription(&xic), guid.ptr, 6, 10, 1);
570             if (!js)
571                 continue;
572 
573             js.win32.index = index;
574 
575             _glfwInputJoystick(js, GLFW_CONNECTED);
576         }
577     }
578     if (_glfw.win32.dinput8.api)
579     {
580         if (FAILED(IDirectInput8_EnumDevices(_glfw.win32.dinput8.api,
581                                              DI8DEVCLASS_GAMECTRL,
582                                              &deviceCallback,
583                                              null,
584                                              DIEDFL_ALLDEVICES)))
585         {
586             _glfwInputError(GLFW_PLATFORM_ERROR,
587                             "Failed to enumerate DirectInput8 devices");
588             return;
589         }
590     }
591 }
592 
593 // Checks for joystick disconnection after DBT_DEVICEREMOVECOMPLETE
594 //
595 void _glfwDetectJoystickDisconnectionWin32() {
596     int jid;
597 
598     for (jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
599     {
600         _GLFWjoystick* js = _glfw.joysticks.ptr + jid;
601         if (js.present)
602             _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE);
603     }
604 }
605 
606 
607 //////////////////////////////////////////////////////////////////////////
608 //////                       GLFW platform API                      //////
609 //////////////////////////////////////////////////////////////////////////
610 
611 int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) {
612     if (js.win32.device)
613     {
614         int i;int ai = 0;int bi = 0;int pi = 0;
615         HRESULT result;
616         DIJOYSTATE state;
617 
618         IDirectInputDevice8_Poll(js.win32.device);
619         result = IDirectInputDevice8_GetDeviceState(js.win32.device,
620                                                     typeof(state).sizeof,
621                                                     &state);
622         if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST)
623         {
624             IDirectInputDevice8_Acquire(js.win32.device);
625             IDirectInputDevice8_Poll(js.win32.device);
626             result = IDirectInputDevice8_GetDeviceState(js.win32.device,
627                                                         typeof(state).sizeof,
628                                                         &state);
629         }
630 
631         if (FAILED(result))
632         {
633             closeJoystick(js);
634             return GLFW_FALSE;
635         }
636 
637         if (mode == _GLFW_POLL_PRESENCE)
638             return GLFW_TRUE;
639 
640         for (i = 0;  i < js.win32.objectCount;  i++)
641         {
642             const(void)* data = cast(char*) &state + js.win32.objects[i].offset;
643 
644             switch (js.win32.objects[i].type)
645             {
646                 case _GLFW_TYPE_AXIS:
647                 case _GLFW_TYPE_SLIDER:
648                 {
649                     const(float) value = (*(cast(LONG*) data) + 0.5f) / 32767.5f;
650                     _glfwInputJoystickAxis(js, ai, value);
651                     ai++;
652                     break;
653                 }
654 
655                 case _GLFW_TYPE_BUTTON:
656                 {
657                     const(char) value = (*(cast(BYTE*) data) & 0x80) != 0;
658                     _glfwInputJoystickButton(js, bi, value);
659                     bi++;
660                     break;
661                 }
662 
663                 case _GLFW_TYPE_POV:
664                 {
665                     const(int)[9] states = [
666                         GLFW_HAT_UP,
667                         GLFW_HAT_RIGHT_UP,
668                         GLFW_HAT_RIGHT,
669                         GLFW_HAT_RIGHT_DOWN,
670                         GLFW_HAT_DOWN,
671                         GLFW_HAT_LEFT_DOWN,
672                         GLFW_HAT_LEFT,
673                         GLFW_HAT_LEFT_UP,
674                         GLFW_HAT_CENTERED
675                     ];
676 
677                     // Screams of horror are appropriate at this point
678                     int state1 = LOWORD(*cast(DWORD*) data) / (45 * DI_DEGREES);
679                     if (state1 < 0 || state1 > 8)
680                         state1 = 8;
681 
682                     _glfwInputJoystickHat(js, pi, cast(char) states[state1]);
683                     pi++;
684                     break;
685                 }
686                 default: break;
687             }
688         }
689     }
690     else
691     {
692         int i;int dpad = 0;
693         DWORD result;
694         XINPUT_STATE xis;
695         const(WORD)[10] buttons = [
696             XINPUT_GAMEPAD_A,
697             XINPUT_GAMEPAD_B,
698             XINPUT_GAMEPAD_X,
699             XINPUT_GAMEPAD_Y,
700             XINPUT_GAMEPAD_LEFT_SHOULDER,
701             XINPUT_GAMEPAD_RIGHT_SHOULDER,
702             XINPUT_GAMEPAD_BACK,
703             XINPUT_GAMEPAD_START,
704             XINPUT_GAMEPAD_LEFT_THUMB,
705             XINPUT_GAMEPAD_RIGHT_THUMB
706         ];
707 
708         result = mixin(XInputGetState)(js.win32.index, &xis);
709         if (result != ERROR_SUCCESS)
710         {
711             if (result == ERROR_DEVICE_NOT_CONNECTED)
712                 closeJoystick(js);
713 
714             return GLFW_FALSE;
715         }
716 
717         if (mode == _GLFW_POLL_PRESENCE)
718             return GLFW_TRUE;
719 
720         _glfwInputJoystickAxis(js, 0, (xis.Gamepad.sThumbLX + 0.5f) / 32767.5f);
721         _glfwInputJoystickAxis(js, 1, -(xis.Gamepad.sThumbLY + 0.5f) / 32767.5f);
722         _glfwInputJoystickAxis(js, 2, (xis.Gamepad.sThumbRX + 0.5f) / 32767.5f);
723         _glfwInputJoystickAxis(js, 3, -(xis.Gamepad.sThumbRY + 0.5f) / 32767.5f);
724         _glfwInputJoystickAxis(js, 4, xis.Gamepad.bLeftTrigger / 127.5f - 1.0f);
725         _glfwInputJoystickAxis(js, 5, xis.Gamepad.bRightTrigger / 127.5f - 1.0f);
726 
727         for (i = 0;  i < 10;  i++)
728         {
729             const(char) value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0;
730             _glfwInputJoystickButton(js, i, value);
731         }
732 
733         if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP)
734             dpad |= GLFW_HAT_UP;
735         if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT)
736             dpad |= GLFW_HAT_RIGHT;
737         if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN)
738             dpad |= GLFW_HAT_DOWN;
739         if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT)
740             dpad |= GLFW_HAT_LEFT;
741 
742         _glfwInputJoystickHat(js, 0, cast(char) dpad);
743     }
744 
745     return GLFW_TRUE;
746 }
747 
748 void _glfwPlatformUpdateGamepadGUID(char* guid) {
749     if (strcmp(guid + 20, "504944564944") == 0)
750     {
751         char[33] original;
752         strncpy(original.ptr, guid, original.length - 1);
753         sprintf(guid, "03000000%.4s0000%.4s000000000000",
754                 original.ptr, original.ptr + 4);
755     }
756 }