win32_joystick.c (26711B)
1 //======================================================================== 2 // GLFW 3.4 Win32 - www.glfw.org 3 //------------------------------------------------------------------------ 4 // Copyright (c) 2002-2006 Marcus Geelnard 5 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org> 6 // 7 // This software is provided 'as-is', without any express or implied 8 // warranty. In no event will the authors be held liable for any damages 9 // arising from the use of this software. 10 // 11 // Permission is granted to anyone to use this software for any purpose, 12 // including commercial applications, and to alter it and redistribute it 13 // freely, subject to the following restrictions: 14 // 15 // 1. The origin of this software must not be misrepresented; you must not 16 // claim that you wrote the original software. If you use this software 17 // in a product, an acknowledgment in the product documentation would 18 // be appreciated but is not required. 19 // 20 // 2. Altered source versions must be plainly marked as such, and must not 21 // be misrepresented as being the original software. 22 // 23 // 3. This notice may not be removed or altered from any source 24 // distribution. 25 // 26 //======================================================================== 27 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 // 149 static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic) 150 { 151 switch (xic->SubType) 152 { 153 case XINPUT_DEVSUBTYPE_WHEEL: 154 return "XInput Wheel"; 155 case XINPUT_DEVSUBTYPE_ARCADE_STICK: 156 return "XInput Arcade Stick"; 157 case XINPUT_DEVSUBTYPE_FLIGHT_STICK: 158 return "XInput Flight Stick"; 159 case XINPUT_DEVSUBTYPE_DANCE_PAD: 160 return "XInput Dance Pad"; 161 case XINPUT_DEVSUBTYPE_GUITAR: 162 return "XInput Guitar"; 163 case XINPUT_DEVSUBTYPE_DRUM_KIT: 164 return "XInput Drum Kit"; 165 case XINPUT_DEVSUBTYPE_GAMEPAD: 166 { 167 if (xic->Flags & XINPUT_CAPS_WIRELESS) 168 return "Wireless Xbox Controller"; 169 else 170 return "Xbox Controller"; 171 } 172 } 173 174 return "Unknown XInput Device"; 175 } 176 177 // Lexically compare device objects 178 // 179 static int compareJoystickObjects(const void* first, const void* second) 180 { 181 const _GLFWjoyobjectWin32* fo = first; 182 const _GLFWjoyobjectWin32* so = second; 183 184 if (fo->type != so->type) 185 return fo->type - so->type; 186 187 return fo->offset - so->offset; 188 } 189 190 // Checks whether the specified device supports XInput 191 // Technique from FDInputJoystickManager::IsXInputDeviceFast in ZDoom 192 // 193 static GLFWbool supportsXInput(const GUID* guid) 194 { 195 UINT i, count = 0; 196 RAWINPUTDEVICELIST* ridl; 197 GLFWbool result = GLFW_FALSE; 198 199 if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0) 200 return GLFW_FALSE; 201 202 ridl = _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 // 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 // 274 static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, 275 void* user) 276 { 277 _GLFWobjenumWin32* data = user; 278 _GLFWjoyobjectWin32* object = data->objects + data->objectCount; 279 280 if (DIDFT_GETTYPE(doi->dwType) & DIDFT_AXIS) 281 { 282 DIPROPRANGE dipr; 283 284 if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) 285 object->offset = DIJOFS_SLIDER(data->sliderCount); 286 else if (memcmp(&doi->guidType, &GUID_XAxis, sizeof(GUID)) == 0) 287 object->offset = DIJOFS_X; 288 else if (memcmp(&doi->guidType, &GUID_YAxis, sizeof(GUID)) == 0) 289 object->offset = DIJOFS_Y; 290 else if (memcmp(&doi->guidType, &GUID_ZAxis, sizeof(GUID)) == 0) 291 object->offset = DIJOFS_Z; 292 else if (memcmp(&doi->guidType, &GUID_RxAxis, sizeof(GUID)) == 0) 293 object->offset = DIJOFS_RX; 294 else if (memcmp(&doi->guidType, &GUID_RyAxis, sizeof(GUID)) == 0) 295 object->offset = DIJOFS_RY; 296 else if (memcmp(&doi->guidType, &GUID_RzAxis, sizeof(GUID)) == 0) 297 object->offset = DIJOFS_RZ; 298 else 299 return DIENUM_CONTINUE; 300 301 ZeroMemory(&dipr, sizeof(dipr)); 302 dipr.diph.dwSize = sizeof(dipr); 303 dipr.diph.dwHeaderSize = sizeof(dipr.diph); 304 dipr.diph.dwObj = doi->dwType; 305 dipr.diph.dwHow = DIPH_BYID; 306 dipr.lMin = -32768; 307 dipr.lMax = 32767; 308 309 if (FAILED(IDirectInputDevice8_SetProperty(data->device, 310 DIPROP_RANGE, 311 &dipr.diph))) 312 { 313 return DIENUM_CONTINUE; 314 } 315 316 if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) 317 { 318 object->type = _GLFW_TYPE_SLIDER; 319 data->sliderCount++; 320 } 321 else 322 { 323 object->type = _GLFW_TYPE_AXIS; 324 data->axisCount++; 325 } 326 } 327 else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_BUTTON) 328 { 329 object->offset = DIJOFS_BUTTON(data->buttonCount); 330 object->type = _GLFW_TYPE_BUTTON; 331 data->buttonCount++; 332 } 333 else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_POV) 334 { 335 object->offset = DIJOFS_POV(data->povCount); 336 object->type = _GLFW_TYPE_POV; 337 data->povCount++; 338 } 339 340 data->objectCount++; 341 return DIENUM_CONTINUE; 342 } 343 344 // DirectInput device enumeration callback 345 // 346 static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) 347 { 348 int jid = 0; 349 DIDEVCAPS dc; 350 DIPROPDWORD dipd; 351 IDirectInputDevice8* device; 352 _GLFWobjenumWin32 data; 353 _GLFWjoystick* js; 354 char guid[33]; 355 char name[256]; 356 357 for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) 358 { 359 js = _glfw.joysticks + jid; 360 if (js->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 // 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 // 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 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 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 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 750 const char* _glfwGetMappingNameWin32(void) 751 { 752 return "Windows"; 753 } 754 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