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