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 IDirectInputDevice8 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 IDirectInputDevice8 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 js.win32.device.Unacquire(); 276 js.win32.device.Release(); 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(data.device.SetProperty(DIPROP_RANGE, &dipr.diph))) 322 { 323 return DIENUM_CONTINUE; 324 } 325 326 if (memcmp(&doi.guidType, &GUID_Slider, GUID.sizeof) == 0) 327 { 328 object.type = _GLFW_TYPE_SLIDER; 329 data.sliderCount++; 330 } 331 else 332 { 333 object.type = _GLFW_TYPE_AXIS; 334 data.axisCount++; 335 } 336 } 337 else if (DIDFT_GETTYPE(doi.dwType) & DIDFT_BUTTON) 338 { 339 object.offset = DIJOFS_BUTTON(data.buttonCount); 340 object.type = _GLFW_TYPE_BUTTON; 341 data.buttonCount++; 342 } 343 else if (DIDFT_GETTYPE(doi.dwType) & DIDFT_POV) 344 { 345 object.offset = DIJOFS_POV(data.povCount); 346 object.type = _GLFW_TYPE_POV; 347 data.povCount++; 348 } 349 350 data.objectCount++; 351 return DIENUM_CONTINUE; 352 } 353 354 // DirectInput device enumeration callback 355 // 356 private extern(Windows) BOOL deviceCallback(const(DIDEVICEINSTANCE)* di, void* user) { 357 int jid = 0; 358 DIDEVCAPS dc; 359 DIPROPDWORD dipd; 360 IDirectInputDevice8 device; 361 _GLFWobjenumWin32 data; 362 //_GLFWjoystick* js; moved down to avoid shadowing 363 char[33] guid; 364 char[256] name; 365 366 for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) 367 { 368 _GLFWjoystick* js = _glfw.joysticks.ptr + jid; 369 if (js.present) 370 { 371 if (memcmp(&js.win32.guid, &di.guidInstance, GUID.sizeof) == 0) 372 return DIENUM_CONTINUE; 373 } 374 } 375 376 if (supportsXInput(&di.guidProduct)) 377 return DIENUM_CONTINUE; 378 379 if (FAILED(_glfw.win32.dinput8.api.CreateDevice(&di.guidInstance, 380 &device, 381 null))) 382 { 383 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create device"); 384 return DIENUM_CONTINUE; 385 } 386 387 if (FAILED(device.SetDataFormat(&_glfwDataFormat))) 388 { 389 _glfwInputError(GLFW_PLATFORM_ERROR, 390 "Win32: Failed to set device data format"); 391 392 device.Release(); 393 return DIENUM_CONTINUE; 394 } 395 396 memset(&dc, 0, typeof(dc).sizeof); 397 dc.dwSize = typeof(dc).sizeof; 398 399 if (FAILED(device.GetCapabilities(&dc))) 400 { 401 _glfwInputError(GLFW_PLATFORM_ERROR, 402 "Win32: Failed to query device capabilities"); 403 404 device.Release(); 405 return DIENUM_CONTINUE; 406 } 407 408 memset(&dipd, 0, typeof(dipd).sizeof); 409 dipd.diph.dwSize = typeof(dipd).sizeof; 410 dipd.diph.dwHeaderSize = typeof(dipd.diph).sizeof; 411 dipd.diph.dwHow = DIPH_DEVICE; 412 dipd.dwData = DIPROPAXISMODE_ABS; 413 414 if (FAILED(device.SetProperty(DIPROP_AXISMODE, &dipd.diph))) 415 { 416 _glfwInputError(GLFW_PLATFORM_ERROR, 417 "Win32: Failed to set device axis mode"); 418 419 device.Release(); 420 return DIENUM_CONTINUE; 421 } 422 423 memset(&data, 0, typeof(data).sizeof); 424 data.device = device; 425 data.objects = cast(_GLFWjoyobjectWin32*) calloc(dc.dwAxes + cast(size_t) dc.dwButtons + dc.dwPOVs, 426 _GLFWjoyobjectWin32.sizeof); 427 428 if (FAILED(device.EnumObjects(&deviceObjectCallback, 429 &data, 430 DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV))) 431 { 432 _glfwInputError(GLFW_PLATFORM_ERROR, 433 "Win32: Failed to enumerate device objects"); 434 435 device.Release(); 436 free(data.objects); 437 return DIENUM_CONTINUE; 438 } 439 440 qsort(data.objects, data.objectCount, 441 _GLFWjoyobjectWin32.sizeof, 442 &compareJoystickObjects); 443 444 if (!WideCharToMultiByte(CP_UTF8, 0, 445 di.tszInstanceName.ptr, -1, 446 name.ptr, typeof(name).sizeof, 447 null, null)) 448 { 449 _glfwInputError(GLFW_PLATFORM_ERROR, 450 "Win32: Failed to convert joystick name to UTF-8"); 451 452 device.Release(); 453 free(data.objects); 454 return DIENUM_STOP; 455 } 456 457 // Generate a joystick GUID that matches the SDL 2.0.5+ one 458 if (memcmp(&di.guidProduct.Data4[2], "PIDVID".ptr, 6) == 0) 459 { 460 sprintf(guid.ptr, "03000000%02x%02x0000%02x%02x000000000000", 461 cast(ubyte) di.guidProduct.Data1, 462 cast(ubyte) (di.guidProduct.Data1 >> 8), 463 cast(ubyte) (di.guidProduct.Data1 >> 16), 464 cast(ubyte) (di.guidProduct.Data1 >> 24)); 465 } 466 else 467 { 468 sprintf(guid.ptr, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", 469 name[0], name[1], name[2], name[3], 470 name[4], name[5], name[6], name[7], 471 name[8], name[9], name[10]); 472 } 473 474 _GLFWjoystick* js = _glfwAllocJoystick(name.ptr, guid.ptr, 475 data.axisCount + data.sliderCount, 476 data.buttonCount, 477 data.povCount); 478 if (!js) 479 { 480 device.Release(); 481 free(data.objects); 482 return DIENUM_STOP; 483 } 484 485 js.win32.device = device; 486 js.win32.guid = di.guidInstance; 487 js.win32.objects = data.objects; 488 js.win32.objectCount = data.objectCount; 489 490 _glfwInputJoystick(js, GLFW_CONNECTED); 491 return DIENUM_CONTINUE; 492 } 493 494 495 ////////////////////////////////////////////////////////////////////////// 496 ////// GLFW internal API ////// 497 ////////////////////////////////////////////////////////////////////////// 498 499 // Initialize joystick interface 500 // 501 void _glfwInitJoysticksWin32() { 502 if (_glfw.win32.dinput8.instance) 503 { 504 if (FAILED(mixin(DirectInput8Create)(GetModuleHandle(null), 505 DIRECTINPUT_VERSION, 506 &IID_IDirectInput8W, 507 cast(void**) &_glfw.win32.dinput8.api, 508 null))) 509 { 510 _glfwInputError(GLFW_PLATFORM_ERROR, 511 "Win32: Failed to create interface"); 512 } 513 } 514 _glfwDetectJoystickConnectionWin32(); 515 } 516 517 // Close all opened joystick handles 518 // 519 void _glfwTerminateJoysticksWin32() { 520 int jid; 521 522 for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) 523 closeJoystick(_glfw.joysticks.ptr + jid); 524 525 if (_glfw.win32.dinput8.api) 526 _glfw.win32.dinput8.api.Release(); 527 } 528 529 // Checks for new joysticks after DBT_DEVICEARRIVAL 530 // 531 void _glfwDetectJoystickConnectionWin32() { 532 if (_glfw.win32.xinput.instance) 533 { 534 DWORD index; 535 536 for (index = 0; index < XUSER_MAX_COUNT; index++) 537 { 538 int jid; 539 char[33] guid; 540 XINPUT_CAPABILITIES xic; 541 _GLFWjoystick* js; 542 543 for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) 544 { 545 if (_glfw.joysticks[jid].present && 546 _glfw.joysticks[jid].win32.device is null && 547 _glfw.joysticks[jid].win32.index == index) 548 { 549 break; 550 } 551 } 552 553 if (jid <= GLFW_JOYSTICK_LAST) 554 continue; 555 556 if (mixin(XInputGetCapabilities)(index, 0, &xic) != ERROR_SUCCESS) 557 continue; 558 559 // Generate a joystick GUID that matches the SDL 2.0.5+ one 560 sprintf(guid.ptr, "78696e707574%02x000000000000000000", 561 xic.SubType & 0xff); 562 563 js = _glfwAllocJoystick(getDeviceDescription(&xic), guid.ptr, 6, 10, 1); 564 if (!js) 565 continue; 566 567 js.win32.index = index; 568 569 _glfwInputJoystick(js, GLFW_CONNECTED); 570 } 571 } 572 if (_glfw.win32.dinput8.api) 573 { 574 if (FAILED(_glfw.win32.dinput8.api.EnumDevices(DI8DEVCLASS_GAMECTRL, 575 &deviceCallback, 576 null, 577 DIEDFL_ALLDEVICES))) 578 { 579 _glfwInputError(GLFW_PLATFORM_ERROR, 580 "Failed to enumerate DirectInput8 devices"); 581 return; 582 } 583 } 584 } 585 586 // Checks for joystick disconnection after DBT_DEVICEREMOVECOMPLETE 587 // 588 void _glfwDetectJoystickDisconnectionWin32() { 589 int jid; 590 591 for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) 592 { 593 _GLFWjoystick* js = _glfw.joysticks.ptr + jid; 594 if (js.present) 595 _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); 596 } 597 } 598 599 600 ////////////////////////////////////////////////////////////////////////// 601 ////// GLFW platform API ////// 602 ////////////////////////////////////////////////////////////////////////// 603 604 int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { 605 if (js.win32.device) 606 { 607 int i;int ai = 0;int bi = 0;int pi = 0; 608 HRESULT result; 609 DIJOYSTATE state; 610 611 js.win32.device.Poll(); 612 result = js.win32.device.GetDeviceState(typeof(state).sizeof, 613 &state); 614 if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) 615 { 616 js.win32.device.Acquire(); 617 js.win32.device.Poll(); 618 result = js.win32.device.GetDeviceState(typeof(state).sizeof, 619 &state); 620 } 621 622 if (FAILED(result)) 623 { 624 closeJoystick(js); 625 return GLFW_FALSE; 626 } 627 628 if (mode == _GLFW_POLL_PRESENCE) 629 return GLFW_TRUE; 630 631 for (i = 0; i < js.win32.objectCount; i++) 632 { 633 const(void)* data = cast(char*) &state + js.win32.objects[i].offset; 634 635 switch (js.win32.objects[i].type) 636 { 637 case _GLFW_TYPE_AXIS: 638 case _GLFW_TYPE_SLIDER: 639 { 640 const(float) value = (*(cast(LONG*) data) + 0.5f) / 32767.5f; 641 _glfwInputJoystickAxis(js, ai, value); 642 ai++; 643 break; 644 } 645 646 case _GLFW_TYPE_BUTTON: 647 { 648 const(char) value = (*(cast(BYTE*) data) & 0x80) != 0; 649 _glfwInputJoystickButton(js, bi, value); 650 bi++; 651 break; 652 } 653 654 case _GLFW_TYPE_POV: 655 { 656 const(int)[9] states = [ 657 GLFW_HAT_UP, 658 GLFW_HAT_RIGHT_UP, 659 GLFW_HAT_RIGHT, 660 GLFW_HAT_RIGHT_DOWN, 661 GLFW_HAT_DOWN, 662 GLFW_HAT_LEFT_DOWN, 663 GLFW_HAT_LEFT, 664 GLFW_HAT_LEFT_UP, 665 GLFW_HAT_CENTERED 666 ]; 667 668 // Screams of horror are appropriate at this point 669 int state1 = LOWORD(*cast(DWORD*) data) / (45 * DI_DEGREES); 670 if (state1 < 0 || state1 > 8) 671 state1 = 8; 672 673 _glfwInputJoystickHat(js, pi, cast(char) states[state1]); 674 pi++; 675 break; 676 } 677 default: break; 678 } 679 } 680 } 681 else 682 { 683 int i;int dpad = 0; 684 DWORD result; 685 XINPUT_STATE xis; 686 const(WORD)[10] buttons = [ 687 XINPUT_GAMEPAD_A, 688 XINPUT_GAMEPAD_B, 689 XINPUT_GAMEPAD_X, 690 XINPUT_GAMEPAD_Y, 691 XINPUT_GAMEPAD_LEFT_SHOULDER, 692 XINPUT_GAMEPAD_RIGHT_SHOULDER, 693 XINPUT_GAMEPAD_BACK, 694 XINPUT_GAMEPAD_START, 695 XINPUT_GAMEPAD_LEFT_THUMB, 696 XINPUT_GAMEPAD_RIGHT_THUMB 697 ]; 698 699 result = mixin(XInputGetState)(js.win32.index, &xis); 700 if (result != ERROR_SUCCESS) 701 { 702 if (result == ERROR_DEVICE_NOT_CONNECTED) 703 closeJoystick(js); 704 705 return GLFW_FALSE; 706 } 707 708 if (mode == _GLFW_POLL_PRESENCE) 709 return GLFW_TRUE; 710 711 _glfwInputJoystickAxis(js, 0, (xis.Gamepad.sThumbLX + 0.5f) / 32767.5f); 712 _glfwInputJoystickAxis(js, 1, -(xis.Gamepad.sThumbLY + 0.5f) / 32767.5f); 713 _glfwInputJoystickAxis(js, 2, (xis.Gamepad.sThumbRX + 0.5f) / 32767.5f); 714 _glfwInputJoystickAxis(js, 3, -(xis.Gamepad.sThumbRY + 0.5f) / 32767.5f); 715 _glfwInputJoystickAxis(js, 4, xis.Gamepad.bLeftTrigger / 127.5f - 1.0f); 716 _glfwInputJoystickAxis(js, 5, xis.Gamepad.bRightTrigger / 127.5f - 1.0f); 717 718 for (i = 0; i < 10; i++) 719 { 720 const(char) value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; 721 _glfwInputJoystickButton(js, i, value); 722 } 723 724 if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) 725 dpad |= GLFW_HAT_UP; 726 if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) 727 dpad |= GLFW_HAT_RIGHT; 728 if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) 729 dpad |= GLFW_HAT_DOWN; 730 if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) 731 dpad |= GLFW_HAT_LEFT; 732 733 _glfwInputJoystickHat(js, 0, cast(char) dpad); 734 } 735 736 return GLFW_TRUE; 737 } 738 739 void _glfwPlatformUpdateGamepadGUID(char* guid) { 740 if (strcmp(guid + 20, "504944564944") == 0) 741 { 742 char[33] original; 743 strncpy(original.ptr, guid, original.length - 1); 744 sprintf(guid, "03000000%.4s0000%.4s000000000000", 745 original.ptr, original.ptr + 4); 746 } 747 }