ソースを参照

Add initial XInput support

Camilla Berglund 11 年 前
コミット
eb3577c1eb
5 ファイル変更322 行追加120 行削除
  1. 46 7
      src/win32_init.c
  2. 202 97
      src/win32_joystick.c
  3. 7 2
      src/win32_joystick.h
  4. 41 11
      src/win32_platform.h
  5. 26 3
      src/win32_window.c

+ 46 - 7
src/win32_init.c

@@ -30,6 +30,8 @@
 #include <stdlib.h>
 #include <malloc.h>
 
+#include <initguid.h>
+DEFINE_GUID(GUID_DEVINTERFACE_HID,0x4d1e55b2,0xf16f,0x11cf,0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30);
 
 #if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG)
 
@@ -69,12 +71,6 @@ static GLFWbool loadLibraries(void)
         return GLFW_FALSE;
     }
 
-    _glfw.win32.winmm.joyGetDevCaps = (JOYGETDEVCAPS_T)
-        GetProcAddress(_glfw.win32.winmm.instance, "joyGetDevCapsW");
-    _glfw.win32.winmm.joyGetPos = (JOYGETPOS_T)
-        GetProcAddress(_glfw.win32.winmm.instance, "joyGetPos");
-    _glfw.win32.winmm.joyGetPosEx = (JOYGETPOSEX_T)
-        GetProcAddress(_glfw.win32.winmm.instance, "joyGetPosEx");
     _glfw.win32.winmm.timeGetTime = (TIMEGETTIME_T)
         GetProcAddress(_glfw.win32.winmm.instance, "timeGetTime");
 
@@ -90,6 +86,33 @@ static GLFWbool loadLibraries(void)
     _glfw.win32.user32.ChangeWindowMessageFilterEx = (CHANGEWINDOWMESSAGEFILTEREX_T)
         GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx");
 
+    {
+        int i;
+        const char* names[] =
+        {
+            "xinput1_4.dll",
+            "xinput1_3.dll",
+            "xinput9_1_0.dll",
+            "xinput1_2.dll",
+            "xinput1_1.dll",
+            NULL
+        };
+
+        for (i = 0;  names[i];  i++)
+        {
+            _glfw.win32.xinput.instance = LoadLibraryA(names[i]);
+            if (_glfw.win32.xinput.instance)
+            {
+                _glfw.win32.xinput.XInputGetCapabilities = (XINPUTGETCAPABILITIES_T)
+                    GetProcAddress(_glfw.win32.xinput.instance, "XInputGetCapabilities");
+                _glfw.win32.xinput.XInputGetState = (XINPUTGETSTATE_T)
+                    GetProcAddress(_glfw.win32.xinput.instance, "XInputGetState");
+
+                break;
+            }
+        }
+    }
+
     _glfw.win32.dwmapi.instance = LoadLibraryA("dwmapi.dll");
     if (_glfw.win32.dwmapi.instance)
     {
@@ -113,6 +136,9 @@ static GLFWbool loadLibraries(void)
 //
 static void freeLibraries(void)
 {
+    if (_glfw.win32.xinput.instance)
+        FreeLibrary(_glfw.win32.xinput.instance);
+
     if (_glfw.win32.winmm.instance)
         FreeLibrary(_glfw.win32.winmm.instance);
 
@@ -283,7 +309,20 @@ static HWND createHelperWindow(void)
         return NULL;
     }
 
-    return window;
+    // Register for HID device notifications
+    {
+        DEV_BROADCAST_DEVICEINTERFACE_W dbi;
+        ZeroMemory(&dbi, sizeof(dbi));
+        dbi.dbcc_size = sizeof(dbi);
+        dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+        dbi.dbcc_classguid = GUID_DEVINTERFACE_HID;
+
+        RegisterDeviceNotificationW(window,
+                                    (DEV_BROADCAST_HDR*) &dbi,
+                                    DEVICE_NOTIFY_WINDOW_HANDLE);
+    }
+
+   return window;
 }
 
 

+ 202 - 97
src/win32_joystick.c

@@ -1,8 +1,8 @@
 //========================================================================
-// GLFW 3.2 Win32 - www.glfw.org
+// GLFW 3.1 Win32 - www.glfw.org
 //------------------------------------------------------------------------
 // Copyright (c) 2002-2006 Marcus Geelnard
-// Copyright (c) 2006-2010 Camilla Berglund <[email protected]>
+// Copyright (c) 2006-2015 Camilla Berglund <[email protected]>
 //
 // This software is provided 'as-is', without any express or implied
 // warranty. In no event will the authors be held liable for any damages
@@ -27,22 +27,169 @@
 
 #include "internal.h"
 
-#include <stdlib.h>
+#include <math.h>
 
+#define _GLFW_UPDATE_BUTTONS 1
+#define _GLFW_UPDATE_AXES    2
 
-//////////////////////////////////////////////////////////////////////////
-//////                       GLFW internal API                      //////
-//////////////////////////////////////////////////////////////////////////
 
-// Convert axis value to the [-1,1] range
+// Returns a description fitting the specified XInput capabilities
+//
+static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic)
+{
+    switch (xic->SubType)
+    {
+        case XINPUT_DEVSUBTYPE_WHEEL:
+            return "XInput Wheel";
+        case XINPUT_DEVSUBTYPE_ARCADE_STICK:
+            return "XInput Arcade Stick";
+        case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
+            return "XInput Flight Stick";
+        case XINPUT_DEVSUBTYPE_DANCE_PAD:
+            return "XInput Dance Pad";
+        case XINPUT_DEVSUBTYPE_GUITAR:
+            return "XInput Guitar";
+        case XINPUT_DEVSUBTYPE_DRUM_KIT:
+            return "XInput Drum Kit";
+        case XINPUT_DEVSUBTYPE_GAMEPAD:
+        {
+            if (xic->Flags & XINPUT_CAPS_WIRELESS)
+                return "Wireless Xbox 360 Controller";
+            else
+                return "Xbox 360 Controller";
+        }
+    }
+
+    return "Unknown XInput Device";
+}
+
+// Attempt to open the specified joystick device
+// TODO: Pack state arrays for non-gamepad devices
 //
-static float normalizeAxis(DWORD pos, DWORD min, DWORD max)
+static GLFWbool openJoystickDevice(DWORD index)
 {
-    float fpos = (float) pos;
-    float fmin = (float) min;
-    float fmax = (float) max;
+    int joy;
+    XINPUT_CAPABILITIES xic;
+    _GLFWjoystickWin32* js;
+
+    for (joy = GLFW_JOYSTICK_1;  joy <= GLFW_JOYSTICK_LAST;  joy++)
+    {
+        if (_glfw.win32_js[joy].present && _glfw.win32_js[joy].index == index)
+            return GLFW_FALSE;
+    }
+
+    for (joy = GLFW_JOYSTICK_1;  joy <= GLFW_JOYSTICK_LAST;  joy++)
+    {
+        if (!_glfw.win32_js[joy].present)
+            break;
+    }
+
+    if (joy > GLFW_JOYSTICK_LAST)
+        return GLFW_FALSE;
+
+    if (_glfw_XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS)
+        return GLFW_FALSE;
+
+    js = _glfw.win32_js + joy;
+    js->axisCount = 6;
+    js->buttonCount = 14;
+    js->present = GLFW_TRUE;
+    js->name = strdup(getDeviceDescription(&xic));
+    js->index = index;
+
+    return GLFW_TRUE;
+}
+
+// Polls for and processes events the specified joystick
+//
+static GLFWbool pollJoystickEvents(_GLFWjoystickWin32* js, int flags)
+{
+    XINPUT_STATE xis;
+    DWORD result;
+
+    if (!_glfw.win32.xinput.instance)
+        return GLFW_FALSE;
+
+    if (!js->present)
+        return GLFW_FALSE;
+
+    result = _glfw_XInputGetState(js->index, &xis);
+    if (result != ERROR_SUCCESS)
+    {
+        if (result == ERROR_DEVICE_NOT_CONNECTED)
+        {
+            free(js->name);
+            memset(js, 0, sizeof(_GLFWjoystickWin32));
+        }
+
+        return GLFW_FALSE;
+    }
+
+    if (flags & _GLFW_UPDATE_AXES)
+    {
+        if (sqrtf((float) (xis.Gamepad.sThumbLX * xis.Gamepad.sThumbLX +
+                           xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY)) >
+            (float) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
+        {
+            js->axes[0] = (xis.Gamepad.sThumbLX + 0.5f) / 32767.f;
+            js->axes[1] = (xis.Gamepad.sThumbLY + 0.5f) / 32767.f;
+        }
+        else
+        {
+            js->axes[0] = 0.f;
+            js->axes[1] = 0.f;
+        }
+
+        if (sqrtf((float) (xis.Gamepad.sThumbRX * xis.Gamepad.sThumbRX +
+                           xis.Gamepad.sThumbRY * xis.Gamepad.sThumbRY)) >
+            (float) XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
+        {
+            js->axes[2] = (xis.Gamepad.sThumbRX + 0.5f) / 32767.f;
+            js->axes[3] = (xis.Gamepad.sThumbRY + 0.5f) / 32767.f;
+        }
+        else
+        {
+            js->axes[2] = 0.f;
+            js->axes[3] = 0.f;
+        }
+
+        if (xis.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
+            js->axes[4] = xis.Gamepad.bLeftTrigger / 127.5f - 1.f;
+        else
+            js->axes[4] = -1.f;
+
+        if (xis.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
+            js->axes[5] = xis.Gamepad.bRightTrigger / 127.5f - 1.f;
+        else
+            js->axes[5] = -1.f;
+    }
 
-    return (2.f * (fpos - fmin) / (fmax - fmin)) - 1.f;
+    if (flags & _GLFW_UPDATE_BUTTONS)
+    {
+        int i;
+        const WORD buttons[14] =
+        {
+            XINPUT_GAMEPAD_A,
+            XINPUT_GAMEPAD_B,
+            XINPUT_GAMEPAD_X,
+            XINPUT_GAMEPAD_Y,
+            XINPUT_GAMEPAD_LEFT_SHOULDER,
+            XINPUT_GAMEPAD_RIGHT_SHOULDER,
+            XINPUT_GAMEPAD_BACK,
+            XINPUT_GAMEPAD_START,
+            XINPUT_GAMEPAD_LEFT_THUMB,
+            XINPUT_GAMEPAD_RIGHT_THUMB,
+            XINPUT_GAMEPAD_DPAD_UP,
+            XINPUT_GAMEPAD_DPAD_RIGHT,
+            XINPUT_GAMEPAD_DPAD_DOWN,
+            XINPUT_GAMEPAD_DPAD_LEFT
+        };
+
+        for (i = 0;  i < 14;  i++)
+            js->buttons[i] = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0;
+    }
+
+    return GLFW_TRUE;
 }
 
 
@@ -54,124 +201,82 @@ static float normalizeAxis(DWORD pos, DWORD min, DWORD max)
 //
 void _glfwInitJoysticksWin32(void)
 {
+    _glfwDetectJoystickConnectionWin32();
 }
 
 // Close all opened joystick handles
 //
 void _glfwTerminateJoysticksWin32(void)
 {
-    int i;
+    int joy;
 
-    for (i = 0;  i < GLFW_JOYSTICK_LAST;  i++)
-        free(_glfw.win32_js[i].name);
+    for (joy = GLFW_JOYSTICK_1;  joy <= GLFW_JOYSTICK_LAST;  joy++)
+        free(_glfw.win32_js[joy].name);
 }
 
-
-//////////////////////////////////////////////////////////////////////////
-//////                       GLFW platform API                      //////
-//////////////////////////////////////////////////////////////////////////
-
-int _glfwPlatformJoystickPresent(int joy)
+// Looks for new joysticks
+//
+void _glfwDetectJoystickConnectionWin32(void)
 {
-    JOYINFO ji;
+    DWORD i;
 
-    if (_glfw_joyGetPos(joy, &ji) != JOYERR_NOERROR)
-        return GLFW_FALSE;
+    if (!_glfw.win32.xinput.instance)
+        return;
 
-    return GLFW_TRUE;
+    for (i = 0;  i < XUSER_MAX_COUNT;  i++)
+        openJoystickDevice(i);
 }
 
-const float* _glfwPlatformGetJoystickAxes(int joy, int* count)
+// Checks if any current joystick has been disconnected
+//
+void _glfwDetectJoystickDisconnectionWin32(void)
 {
-    JOYCAPS jc;
-    JOYINFOEX ji;
-    float* axes = _glfw.win32_js[joy].axes;
+    DWORD i;
 
-    if (_glfw_joyGetDevCaps(joy, &jc, sizeof(JOYCAPS)) != JOYERR_NOERROR)
-        return NULL;
-
-    ji.dwSize = sizeof(JOYINFOEX);
-    ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ |
-                 JOY_RETURNR | JOY_RETURNU | JOY_RETURNV;
-    if (_glfw_joyGetPosEx(joy, &ji) != JOYERR_NOERROR)
-        return NULL;
+    if (!_glfw.win32.xinput.instance)
+        return;
 
-    axes[(*count)++] = normalizeAxis(ji.dwXpos, jc.wXmin, jc.wXmax);
-    axes[(*count)++] = normalizeAxis(ji.dwYpos, jc.wYmin, jc.wYmax);
+    for (i = 0;  i < XUSER_MAX_COUNT;  i++)
+        pollJoystickEvents(_glfw.win32_js + i, 0);
+}
 
-    if (jc.wCaps & JOYCAPS_HASZ)
-        axes[(*count)++] = normalizeAxis(ji.dwZpos, jc.wZmin, jc.wZmax);
 
-    if (jc.wCaps & JOYCAPS_HASR)
-        axes[(*count)++] = normalizeAxis(ji.dwRpos, jc.wRmin, jc.wRmax);
+//////////////////////////////////////////////////////////////////////////
+//////                       GLFW platform API                      //////
+//////////////////////////////////////////////////////////////////////////
 
-    if (jc.wCaps & JOYCAPS_HASU)
-        axes[(*count)++] = normalizeAxis(ji.dwUpos, jc.wUmin, jc.wUmax);
+int _glfwPlatformJoystickPresent(int joy)
+{
+    _GLFWjoystickWin32* js = _glfw.win32_js + joy;
+    return pollJoystickEvents(js, 0);
+}
 
-    if (jc.wCaps & JOYCAPS_HASV)
-        axes[(*count)++] = normalizeAxis(ji.dwVpos, jc.wVmin, jc.wVmax);
+const float* _glfwPlatformGetJoystickAxes(int joy, int* count)
+{
+    _GLFWjoystickWin32* js = _glfw.win32_js + joy;
+    if (!pollJoystickEvents(js, _GLFW_UPDATE_AXES))
+        return NULL;
 
-    return axes;
+    *count = js->axisCount;
+    return js->axes;
 }
 
 const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count)
 {
-    JOYCAPS jc;
-    JOYINFOEX ji;
-    unsigned char* buttons = _glfw.win32_js[joy].buttons;
-
-    if (_glfw_joyGetDevCaps(joy, &jc, sizeof(JOYCAPS)) != JOYERR_NOERROR)
-        return NULL;
-
-    ji.dwSize = sizeof(JOYINFOEX);
-    ji.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNPOV;
-    if (_glfw_joyGetPosEx(joy, &ji) != JOYERR_NOERROR)
+    _GLFWjoystickWin32* js = _glfw.win32_js + joy;
+    if (!pollJoystickEvents(js, _GLFW_UPDATE_BUTTONS))
         return NULL;
 
-    while (*count < (int) jc.wNumButtons)
-    {
-        buttons[*count] = (unsigned char)
-            (ji.dwButtons & (1UL << *count) ? GLFW_PRESS : GLFW_RELEASE);
-        (*count)++;
-    }
-
-    // Virtual buttons - Inject data from hats
-    // Each hat is exposed as 4 buttons which exposes 8 directions with
-    // concurrent button presses
-    // NOTE: this API exposes only one hat
-
-    if ((jc.wCaps & JOYCAPS_HASPOV) && (jc.wCaps & JOYCAPS_POV4DIR))
-    {
-        int i, value = ji.dwPOV / 100 / 45;
-
-        // Bit fields of button presses for each direction, including nil
-        const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 };
-
-        if (value < 0 || value > 8)
-            value = 8;
-
-        for (i = 0;  i < 4;  i++)
-        {
-            if (directions[value] & (1 << i))
-                buttons[(*count)++] = GLFW_PRESS;
-            else
-                buttons[(*count)++] = GLFW_RELEASE;
-        }
-    }
-
-    return buttons;
+    *count = js->buttonCount;
+    return js->buttons;
 }
 
 const char* _glfwPlatformGetJoystickName(int joy)
 {
-    JOYCAPS jc;
-
-    if (_glfw_joyGetDevCaps(joy, &jc, sizeof(JOYCAPS)) != JOYERR_NOERROR)
+    _GLFWjoystickWin32* js = _glfw.win32_js + joy;
+    if (!pollJoystickEvents(js, 0))
         return NULL;
 
-    free(_glfw.win32_js[joy].name);
-    _glfw.win32_js[joy].name = _glfwCreateUTF8FromWideStringWin32(jc.szPname);
-
-    return _glfw.win32_js[joy].name;
+    return js->name;
 }
 

+ 7 - 2
src/win32_joystick.h

@@ -30,18 +30,23 @@
 #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \
     _GLFWjoystickWin32 win32_js[GLFW_JOYSTICK_LAST + 1]
 
-
 // Win32-specific per-joystick data
 //
 typedef struct _GLFWjoystickWin32
 {
+    GLFWbool        present;
     float           axes[6];
-    unsigned char   buttons[36]; // 32 buttons plus one hat
+    int             axisCount;
+    unsigned char   buttons[14];
+    int             buttonCount;
     char*           name;
+    DWORD           index;
 } _GLFWjoystickWin32;
 
 
 void _glfwInitJoysticksWin32(void);
 void _glfwTerminateJoysticksWin32(void);
+void _glfwDetectJoystickConnectionWin32(void);
+void _glfwDetectJoystickDisconnectionWin32(void);
 
 #endif // _glfw3_win32_joystick_h_

+ 41 - 11
src/win32_platform.h

@@ -63,6 +63,7 @@
 
 #include <windows.h>
 #include <mmsystem.h>
+#include <xinput.h>
 #include <dbt.h>
 
 #if defined(_MSC_VER)
@@ -120,16 +121,42 @@ typedef enum PROCESS_DPI_AWARENESS
 } PROCESS_DPI_AWARENESS;
 #endif /*DPI_ENUMS_DECLARED*/
 
+// HACK: Define macros that some older xinput.h variants don't
+#ifndef XINPUT_CAPS_WIRELESS
+ #define XINPUT_CAPS_WIRELESS 0x0002
+#endif
+#ifndef XINPUT_DEVSUBTYPE_WHEEL
+ #define XINPUT_DEVSUBTYPE_WHEEL 0x02
+#endif
+#ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK
+ #define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03
+#endif
+#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK
+ #define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04
+#endif
+#ifndef XINPUT_DEVSUBTYPE_DANCE_PAD
+ #define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05
+#endif
+#ifndef XINPUT_DEVSUBTYPE_GUITAR
+ #define XINPUT_DEVSUBTYPE_GUITAR 0x06
+#endif
+#ifndef XINPUT_DEVSUBTYPE_DRUM_KIT
+ #define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08
+#endif
+#ifndef XINPUT_DEVSUBTYPE_ARCADE_PAD
+ #define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13
+#endif
+
 // winmm.dll function pointer typedefs
-typedef MMRESULT (WINAPI * JOYGETDEVCAPS_T)(UINT,LPJOYCAPS,UINT);
-typedef MMRESULT (WINAPI * JOYGETPOS_T)(UINT,LPJOYINFO);
-typedef MMRESULT (WINAPI * JOYGETPOSEX_T)(UINT,LPJOYINFOEX);
 typedef DWORD (WINAPI * TIMEGETTIME_T)(void);
-#define _glfw_joyGetDevCaps _glfw.win32.winmm.joyGetDevCaps
-#define _glfw_joyGetPos _glfw.win32.winmm.joyGetPos
-#define _glfw_joyGetPosEx _glfw.win32.winmm.joyGetPosEx
 #define _glfw_timeGetTime _glfw.win32.winmm.timeGetTime
 
+// xinput.dll function pointer typedefs
+typedef DWORD (WINAPI * XINPUTGETCAPABILITIES_T)(DWORD,DWORD,XINPUT_CAPABILITIES*);
+typedef DWORD (WINAPI * XINPUTGETSTATE_T)(DWORD,XINPUT_STATE*);
+#define _glfw_XInputGetCapabilities _glfw.win32.xinput.XInputGetCapabilities
+#define _glfw_XInputGetState _glfw.win32.xinput.XInputGetState
+
 // user32.dll function pointer typedefs
 typedef BOOL (WINAPI * SETPROCESSDPIAWARE_T)(void);
 typedef BOOL (WINAPI * CHANGEWINDOWMESSAGEFILTEREX_T)(HWND,UINT,DWORD,PCHANGEFILTERSTRUCT);
@@ -217,16 +244,19 @@ typedef struct _GLFWlibraryWin32
     // winmm.dll
     struct {
         HINSTANCE       instance;
-        JOYGETDEVCAPS_T joyGetDevCaps;
-        JOYGETPOS_T     joyGetPos;
-        JOYGETPOSEX_T   joyGetPosEx;
         TIMEGETTIME_T   timeGetTime;
     } winmm;
 
     // user32.dll
     struct {
-        HINSTANCE       instance;
-        SETPROCESSDPIAWARE_T SetProcessDPIAware;
+        HINSTANCE               instance;
+        XINPUTGETCAPABILITIES_T XInputGetCapabilities;
+        XINPUTGETSTATE_T        XInputGetState;
+    } xinput;
+
+    struct {
+        HINSTANCE                     instance;
+        SETPROCESSDPIAWARE_T          SetProcessDPIAware;
         CHANGEWINDOWMESSAGEFILTEREX_T ChangeWindowMessageFilterEx;
     } user32;
 

+ 26 - 3
src/win32_window.c

@@ -376,10 +376,33 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
 
         switch (uMsg)
         {
-            case WM_DISPLAYCHANGE:
+            case WM_DEVICECHANGE:
             {
-                _glfwInputMonitorChange();
-                return 0;
+                if (wParam == DBT_DEVNODES_CHANGED)
+                {
+                    _glfwInputMonitorChange();
+                    return TRUE;
+                }
+                else if (wParam == DBT_DEVICEARRIVAL)
+                {
+                    DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
+                    if (dbh)
+                    {
+                        if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
+                            _glfwDetectJoystickConnectionWin32();
+                    }
+                }
+                else if (wParam == DBT_DEVICEREMOVECOMPLETE)
+                {
+                    DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
+                    if (dbh)
+                    {
+                        if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
+                            _glfwDetectJoystickDisconnectionWin32();
+                    }
+                }
+
+                break;
             }
         }