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