medfall

A super great game engine
Log | Files | Refs

win32_joystick.cc (26215B)


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