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