1 //========================================================================
2 // GLFW 3.5 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
28 #include "internal.h"
29
30 #if defined(_GLFW_WIN32)
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 //
getDeviceDescription(const XINPUT_CAPABILITIES * xic)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 //
compareJoystickObjects(const void * first,const void * second)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 //
supportsXInput(const GUID * guid)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 = _glfw_calloc(count, sizeof(RAWINPUTDEVICELIST));
203
204 if (GetRawInputDeviceList(ridl, &count, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1)
205 {
206 _glfw_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 _glfw_free(ridl);
252 return result;
253 }
254
255 // Frees all resources associated with the specified joystick
256 //
closeJoystick(_GLFWjoystick * js)257 static void closeJoystick(_GLFWjoystick* js)
258 {
259 _glfwInputJoystick(js, GLFW_DISCONNECTED);
260
261 if (js->win32.device)
262 {
263 IDirectInputDevice8_Unacquire(js->win32.device);
264 IDirectInputDevice8_Release(js->win32.device);
265 }
266
267 _glfw_free(js->win32.objects);
268 _glfwFreeJoystick(js);
269 }
270
271 // DirectInput device object enumeration callback
272 // Insights gleaned from SDL
273 //
deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW * doi,void * user)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 //
deviceCallback(const DIDEVICEINSTANCE * di,void * user)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->connected)
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 = _glfw_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 _glfw_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 _glfw_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 _glfw_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 //
_glfwDetectJoystickConnectionWin32(void)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].connected &&
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 //
_glfwDetectJoystickDisconnectionWin32(void)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->connected)
564 _glfwPollJoystickWin32(js, _GLFW_POLL_PRESENCE);
565 }
566 }
567
568
569 //////////////////////////////////////////////////////////////////////////
570 ////// GLFW platform API //////
571 //////////////////////////////////////////////////////////////////////////
572
_glfwInitJoysticksWin32(void)573 GLFWbool _glfwInitJoysticksWin32(void)
574 {
575 if (_glfw.win32.dinput8.instance)
576 {
577 if (FAILED(DirectInput8Create(_glfw.win32.instance,
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
_glfwTerminateJoysticksWin32(void)593 void _glfwTerminateJoysticksWin32(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
_glfwPollJoystickWin32(_GLFWjoystick * js,int mode)604 GLFWbool _glfwPollJoystickWin32(_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 = {0};
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 // Treat invalid combinations as neither being pressed
738 // while preserving what data can be preserved
739 if ((dpad & GLFW_HAT_RIGHT) && (dpad & GLFW_HAT_LEFT))
740 dpad &= ~(GLFW_HAT_RIGHT | GLFW_HAT_LEFT);
741 if ((dpad & GLFW_HAT_UP) && (dpad & GLFW_HAT_DOWN))
742 dpad &= ~(GLFW_HAT_UP | GLFW_HAT_DOWN);
743
744 _glfwInputJoystickHat(js, 0, dpad);
745 }
746
747 return GLFW_TRUE;
748 }
749
_glfwGetMappingNameWin32(void)750 const char* _glfwGetMappingNameWin32(void)
751 {
752 return "Windows";
753 }
754
_glfwUpdateGamepadGUIDWin32(char * guid)755 void _glfwUpdateGamepadGUIDWin32(char* guid)
756 {
757 if (strcmp(guid + 20, "504944564944") == 0)
758 {
759 char original[33];
760 strncpy(original, guid, sizeof(original) - 1);
761 sprintf(guid, "03000000%.4s0000%.4s000000000000",
762 original, original + 4);
763 }
764 }
765
766 #endif // _GLFW_WIN32
767
768