master
ブランチ master
ShaderPlayground / deps / glfw / src / win32_joystick.c
25.7 KB、755行 生 Blame 履歴
1//========================================================================
2// GLFW 3.3 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//
42typedef 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//
55static const GUID _glfw_IID_IDirectInput8W =
56 {0xbf798031,0x483a,0x4da2,{0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00}};
57static const GUID _glfw_GUID_XAxis =
58 {0xa36d02e0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
59static const GUID _glfw_GUID_YAxis =
60 {0xa36d02e1,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
61static const GUID _glfw_GUID_ZAxis =
62 {0xa36d02e2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
63static const GUID _glfw_GUID_RxAxis =
64 {0xa36d02f4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
65static const GUID _glfw_GUID_RyAxis =
66 {0xa36d02f5,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
67static const GUID _glfw_GUID_RzAxis =
68 {0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
69static const GUID _glfw_GUID_Slider =
70 {0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
71static 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//
87static 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//
137static 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//
149static 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//
179static 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//
193static 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//
257static 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//
274static 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//
346static 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 _GLFWjoystick* 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// Initialize joystick interface
495//
496void _glfwInitJoysticksWin32(void)
497{
498 if (_glfw.win32.dinput8.instance)
499 {
500 if (FAILED(DirectInput8Create(GetModuleHandle(NULL),
501 DIRECTINPUT_VERSION,
502 &IID_IDirectInput8W,
503 (void**) &_glfw.win32.dinput8.api,
504 NULL)))
505 {
506 _glfwInputError(GLFW_PLATFORM_ERROR,
507 "Win32: Failed to create interface");
508 }
509 }
510
511 _glfwDetectJoystickConnectionWin32();
512}
513
514// Close all opened joystick handles
515//
516void _glfwTerminateJoysticksWin32(void)
517{
518 int jid;
519
520 for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++)
521 closeJoystick(_glfw.joysticks + jid);
522
523 if (_glfw.win32.dinput8.api)
524 IDirectInput8_Release(_glfw.win32.dinput8.api);
525}
526
527// Checks for new joysticks after DBT_DEVICEARRIVAL
528//
529void _glfwDetectJoystickConnectionWin32(void)
530{
531 if (_glfw.win32.xinput.instance)
532 {
533 DWORD index;
534
535 for (index = 0; index < XUSER_MAX_COUNT; index++)
536 {
537 int jid;
538 char guid[33];
539 XINPUT_CAPABILITIES xic;
540 _GLFWjoystick* js;
541
542 for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
543 {
544 if (_glfw.joysticks[jid].present &&
545 _glfw.joysticks[jid].win32.device == NULL &&
546 _glfw.joysticks[jid].win32.index == index)
547 {
548 break;
549 }
550 }
551
552 if (jid <= GLFW_JOYSTICK_LAST)
553 continue;
554
555 if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS)
556 continue;
557
558 // Generate a joystick GUID that matches the SDL 2.0.5+ one
559 sprintf(guid, "78696e707574%02x000000000000000000",
560 xic.SubType & 0xff);
561
562 js = _glfwAllocJoystick(getDeviceDescription(&xic), guid, 6, 10, 1);
563 if (!js)
564 continue;
565
566 js->win32.index = index;
567
568 _glfwInputJoystick(js, GLFW_CONNECTED);
569 }
570 }
571
572 if (_glfw.win32.dinput8.api)
573 {
574 if (FAILED(IDirectInput8_EnumDevices(_glfw.win32.dinput8.api,
575 DI8DEVCLASS_GAMECTRL,
576 deviceCallback,
577 NULL,
578 DIEDFL_ALLDEVICES)))
579 {
580 _glfwInputError(GLFW_PLATFORM_ERROR,
581 "Failed to enumerate DirectInput8 devices");
582 return;
583 }
584 }
585}
586
587// Checks for joystick disconnection after DBT_DEVICEREMOVECOMPLETE
588//
589void _glfwDetectJoystickDisconnectionWin32(void)
590{
591 int jid;
592
593 for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
594 {
595 _GLFWjoystick* js = _glfw.joysticks + jid;
596 if (js->present)
597 _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE);
598 }
599}
600
601
602//////////////////////////////////////////////////////////////////////////
603////// GLFW platform API //////
604//////////////////////////////////////////////////////////////////////////
605
606int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
607{
608 if (js->win32.device)
609 {
610 int i, ai = 0, bi = 0, pi = 0;
611 HRESULT result;
612 DIJOYSTATE state;
613
614 IDirectInputDevice8_Poll(js->win32.device);
615 result = IDirectInputDevice8_GetDeviceState(js->win32.device,
616 sizeof(state),
617 &state);
618 if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST)
619 {
620 IDirectInputDevice8_Acquire(js->win32.device);
621 IDirectInputDevice8_Poll(js->win32.device);
622 result = IDirectInputDevice8_GetDeviceState(js->win32.device,
623 sizeof(state),
624 &state);
625 }
626
627 if (FAILED(result))
628 {
629 closeJoystick(js);
630 return GLFW_FALSE;
631 }
632
633 if (mode == _GLFW_POLL_PRESENCE)
634 return GLFW_TRUE;
635
636 for (i = 0; i < js->win32.objectCount; i++)
637 {
638 const void* data = (char*) &state + js->win32.objects[i].offset;
639
640 switch (js->win32.objects[i].type)
641 {
642 case _GLFW_TYPE_AXIS:
643 case _GLFW_TYPE_SLIDER:
644 {
645 const float value = (*((LONG*) data) + 0.5f) / 32767.5f;
646 _glfwInputJoystickAxis(js, ai, value);
647 ai++;
648 break;
649 }
650
651 case _GLFW_TYPE_BUTTON:
652 {
653 const char value = (*((BYTE*) data) & 0x80) != 0;
654 _glfwInputJoystickButton(js, bi, value);
655 bi++;
656 break;
657 }
658
659 case _GLFW_TYPE_POV:
660 {
661 const int states[9] =
662 {
663 GLFW_HAT_UP,
664 GLFW_HAT_RIGHT_UP,
665 GLFW_HAT_RIGHT,
666 GLFW_HAT_RIGHT_DOWN,
667 GLFW_HAT_DOWN,
668 GLFW_HAT_LEFT_DOWN,
669 GLFW_HAT_LEFT,
670 GLFW_HAT_LEFT_UP,
671 GLFW_HAT_CENTERED
672 };
673
674 // Screams of horror are appropriate at this point
675 int state = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES);
676 if (state < 0 || state > 8)
677 state = 8;
678
679 _glfwInputJoystickHat(js, pi, states[state]);
680 pi++;
681 break;
682 }
683 }
684 }
685 }
686 else
687 {
688 int i, dpad = 0;
689 DWORD result;
690 XINPUT_STATE xis;
691 const WORD buttons[10] =
692 {
693 XINPUT_GAMEPAD_A,
694 XINPUT_GAMEPAD_B,
695 XINPUT_GAMEPAD_X,
696 XINPUT_GAMEPAD_Y,
697 XINPUT_GAMEPAD_LEFT_SHOULDER,
698 XINPUT_GAMEPAD_RIGHT_SHOULDER,
699 XINPUT_GAMEPAD_BACK,
700 XINPUT_GAMEPAD_START,
701 XINPUT_GAMEPAD_LEFT_THUMB,
702 XINPUT_GAMEPAD_RIGHT_THUMB
703 };
704
705 result = XInputGetState(js->win32.index, &xis);
706 if (result != ERROR_SUCCESS)
707 {
708 if (result == ERROR_DEVICE_NOT_CONNECTED)
709 closeJoystick(js);
710
711 return GLFW_FALSE;
712 }
713
714 if (mode == _GLFW_POLL_PRESENCE)
715 return GLFW_TRUE;
716
717 _glfwInputJoystickAxis(js, 0, (xis.Gamepad.sThumbLX + 0.5f) / 32767.5f);
718 _glfwInputJoystickAxis(js, 1, -(xis.Gamepad.sThumbLY + 0.5f) / 32767.5f);
719 _glfwInputJoystickAxis(js, 2, (xis.Gamepad.sThumbRX + 0.5f) / 32767.5f);
720 _glfwInputJoystickAxis(js, 3, -(xis.Gamepad.sThumbRY + 0.5f) / 32767.5f);
721 _glfwInputJoystickAxis(js, 4, xis.Gamepad.bLeftTrigger / 127.5f - 1.f);
722 _glfwInputJoystickAxis(js, 5, xis.Gamepad.bRightTrigger / 127.5f - 1.f);
723
724 for (i = 0; i < 10; i++)
725 {
726 const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0;
727 _glfwInputJoystickButton(js, i, value);
728 }
729
730 if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP)
731 dpad |= GLFW_HAT_UP;
732 if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT)
733 dpad |= GLFW_HAT_RIGHT;
734 if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN)
735 dpad |= GLFW_HAT_DOWN;
736 if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT)
737 dpad |= GLFW_HAT_LEFT;
738
739 _glfwInputJoystickHat(js, 0, dpad);
740 }
741
742 return GLFW_TRUE;
743}
744
745void _glfwPlatformUpdateGamepadGUID(char* guid)
746{
747 if (strcmp(guid + 20, "504944564944") == 0)
748 {
749 char original[33];
750 strncpy(original, guid, sizeof(original) - 1);
751 sprintf(guid, "03000000%.4s0000%.4s000000000000",
752 original, original + 4);
753 }
754}
755