revolver

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

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