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