|
@@ -29,9 +29,104 @@
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
-#define _GLFW_UPDATE_BUTTONS 1
|
|
|
-#define _GLFW_UPDATE_AXES 2
|
|
|
+#include <initguid.h>
|
|
|
|
|
|
+#define _GLFW_PRESENCE_ONLY 1
|
|
|
+#define _GLFW_UPDATE_STATE 2
|
|
|
+
|
|
|
+#define _GLFW_TYPE_AXIS 0
|
|
|
+#define _GLFW_TYPE_SLIDER 1
|
|
|
+#define _GLFW_TYPE_BUTTON 2
|
|
|
+#define _GLFW_TYPE_POV 3
|
|
|
+
|
|
|
+// Data produced with DirectInput device object enumeration
|
|
|
+//
|
|
|
+typedef struct _GLFWobjenumWin32
|
|
|
+{
|
|
|
+ IDirectInputDevice8W* device;
|
|
|
+ _GLFWjoyobjectWin32* objects;
|
|
|
+ int objectCount;
|
|
|
+ int axisCount;
|
|
|
+ int sliderCount;
|
|
|
+ int buttonCount;
|
|
|
+ int povCount;
|
|
|
+} _GLFWobjenumWin32;
|
|
|
+
|
|
|
+// Define only the necessary GUIDs (it's bad enough that we're exporting these)
|
|
|
+//
|
|
|
+DEFINE_GUID(IID_IDirectInput8W,0xbf798031,0x483a,0x4da2,0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00);
|
|
|
+DEFINE_GUID(GUID_XAxis,0xa36d02e0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
|
+DEFINE_GUID(GUID_YAxis,0xa36d02e1,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
|
+DEFINE_GUID(GUID_ZAxis,0xa36d02e2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
|
+DEFINE_GUID(GUID_RxAxis,0xa36d02f4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
|
+DEFINE_GUID(GUID_RyAxis,0xa36d02f5,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
|
+DEFINE_GUID(GUID_RzAxis,0xa36d02e3,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
|
+DEFINE_GUID(GUID_Slider,0xa36d02e4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
|
+DEFINE_GUID(GUID_Button,0xa36d02f0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
|
+DEFINE_GUID(GUID_POV,0xa36d02f2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00);
|
|
|
+
|
|
|
+// Object data array for our clone of c_dfDIJoystick
|
|
|
+// Generated with https://github.com/elmindreda/c_dfDIJoystick2
|
|
|
+//
|
|
|
+static DIOBJECTDATAFORMAT _glfwObjectDataFormats[] =
|
|
|
+{
|
|
|
+ { &GUID_XAxis,DIJOFS_X,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
|
|
|
+ { &GUID_YAxis,DIJOFS_Y,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
|
|
|
+ { &GUID_ZAxis,DIJOFS_Z,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
|
|
|
+ { &GUID_RxAxis,DIJOFS_RX,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
|
|
|
+ { &GUID_RyAxis,DIJOFS_RY,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
|
|
|
+ { &GUID_RzAxis,DIJOFS_RZ,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
|
|
|
+ { &GUID_Slider,DIJOFS_SLIDER(0),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
|
|
|
+ { &GUID_Slider,DIJOFS_SLIDER(1),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
|
|
|
+ { &GUID_POV,DIJOFS_POV(0),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { &GUID_POV,DIJOFS_POV(1),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { &GUID_POV,DIJOFS_POV(2),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { &GUID_POV,DIJOFS_POV(3),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(0),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(1),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(2),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(3),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(4),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(5),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(6),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(7),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(8),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(9),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(10),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(11),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(12),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(13),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(14),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(15),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(16),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(17),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(18),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(19),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(20),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(21),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(22),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(23),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(24),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(25),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(26),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(27),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(28),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(29),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(30),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+ { NULL,DIJOFS_BUTTON(31),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
|
|
|
+};
|
|
|
+
|
|
|
+// Our clone of c_dfDIJoystick
|
|
|
+//
|
|
|
+static const DIDATAFORMAT _glfwDataFormat =
|
|
|
+{
|
|
|
+ sizeof(DIDATAFORMAT),
|
|
|
+ sizeof(DIOBJECTDATAFORMAT),
|
|
|
+ DIDFT_ABSAXIS,
|
|
|
+ sizeof(DIJOYSTATE),
|
|
|
+ sizeof(_glfwObjectDataFormats) / sizeof(DIOBJECTDATAFORMAT),
|
|
|
+ _glfwObjectDataFormats
|
|
|
+};
|
|
|
|
|
|
// Returns a description fitting the specified XInput capabilities
|
|
|
//
|
|
@@ -63,10 +158,294 @@ static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic)
|
|
|
return "Unknown XInput Device";
|
|
|
}
|
|
|
|
|
|
+// Lexically compare device objects
|
|
|
+//
|
|
|
+static int compareJoystickObjects(const void* first, const void* second)
|
|
|
+{
|
|
|
+ const _GLFWjoyobjectWin32* fo = first;
|
|
|
+ const _GLFWjoyobjectWin32* so = second;
|
|
|
+
|
|
|
+ if (fo->type != so->type)
|
|
|
+ return fo->type - so->type;
|
|
|
+
|
|
|
+ return fo->offset - so->offset;
|
|
|
+}
|
|
|
+
|
|
|
+// Checks whether the specified device supports XInput
|
|
|
+// Technique from FDInputJoystickManager::IsXInputDeviceFast in ZDoom
|
|
|
+//
|
|
|
+static GLFWbool supportsXInput(const GUID* guid)
|
|
|
+{
|
|
|
+ UINT i, count;
|
|
|
+ RAWINPUTDEVICELIST* ridl;
|
|
|
+ GLFWbool result = GLFW_FALSE;
|
|
|
+
|
|
|
+ if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0)
|
|
|
+ return GLFW_FALSE;
|
|
|
+
|
|
|
+ ridl = calloc(count, sizeof(RAWINPUTDEVICELIST));
|
|
|
+
|
|
|
+ if (GetRawInputDeviceList(ridl, &count, sizeof(RAWINPUTDEVICELIST)) == -1)
|
|
|
+ {
|
|
|
+ free(ridl);
|
|
|
+ return GLFW_FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < count; i++)
|
|
|
+ {
|
|
|
+ RID_DEVICE_INFO rdi;
|
|
|
+ char name[256];
|
|
|
+ UINT size;
|
|
|
+
|
|
|
+ if (ridl[i].dwType != RIM_TYPEHID)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ZeroMemory(&rdi, sizeof(rdi));
|
|
|
+ rdi.cbSize = sizeof(rdi);
|
|
|
+ size = sizeof(rdi);
|
|
|
+
|
|
|
+ if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice,
|
|
|
+ RIDI_DEVICEINFO,
|
|
|
+ &rdi, &size) == -1)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) != guid->Data1)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ memset(name, 0, sizeof(name));
|
|
|
+ size = sizeof(name);
|
|
|
+
|
|
|
+ if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice,
|
|
|
+ RIDI_DEVICENAME,
|
|
|
+ name, &size) == -1)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ name[sizeof(name) - 1] = '\0';
|
|
|
+ if (strstr(name, "IG_"))
|
|
|
+ {
|
|
|
+ result = GLFW_TRUE;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ free(ridl);
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+// Frees all resources associated with the specified joystick
|
|
|
+//
|
|
|
+static void closeJoystick(_GLFWjoystickWin32* js)
|
|
|
+{
|
|
|
+ if (js->device)
|
|
|
+ {
|
|
|
+ IDirectInputDevice8_Unacquire(js->device);
|
|
|
+ IDirectInputDevice8_Release(js->device);
|
|
|
+ }
|
|
|
+
|
|
|
+ free(js->name);
|
|
|
+ free(js->axes);
|
|
|
+ free(js->buttons);
|
|
|
+ free(js->objects);
|
|
|
+ memset(js, 0, sizeof(_GLFWjoystickWin32));
|
|
|
+
|
|
|
+ _glfwInputJoystickChange((int) (js - _glfw.win32_js), GLFW_DISCONNECTED);
|
|
|
+}
|
|
|
+
|
|
|
+// DirectInput device object enumeration callback
|
|
|
+// Insights gleaned from SDL2
|
|
|
+//
|
|
|
+static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi,
|
|
|
+ void* user)
|
|
|
+{
|
|
|
+ _GLFWobjenumWin32* data = user;
|
|
|
+ _GLFWjoyobjectWin32* object = data->objects + data->objectCount;
|
|
|
+
|
|
|
+ if (DIDFT_GETTYPE(doi->dwType) & DIDFT_AXIS)
|
|
|
+ {
|
|
|
+ DIPROPRANGE dipr;
|
|
|
+
|
|
|
+ if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0)
|
|
|
+ object->offset = DIJOFS_SLIDER(data->sliderCount);
|
|
|
+ else if (memcmp(&doi->guidType, &GUID_XAxis, sizeof(GUID)) == 0)
|
|
|
+ object->offset = DIJOFS_X;
|
|
|
+ else if (memcmp(&doi->guidType, &GUID_YAxis, sizeof(GUID)) == 0)
|
|
|
+ object->offset = DIJOFS_Y;
|
|
|
+ else if (memcmp(&doi->guidType, &GUID_ZAxis, sizeof(GUID)) == 0)
|
|
|
+ object->offset = DIJOFS_Z;
|
|
|
+ else if (memcmp(&doi->guidType, &GUID_RxAxis, sizeof(GUID)) == 0)
|
|
|
+ object->offset = DIJOFS_RX;
|
|
|
+ else if (memcmp(&doi->guidType, &GUID_RyAxis, sizeof(GUID)) == 0)
|
|
|
+ object->offset = DIJOFS_RY;
|
|
|
+ else if (memcmp(&doi->guidType, &GUID_RzAxis, sizeof(GUID)) == 0)
|
|
|
+ object->offset = DIJOFS_RZ;
|
|
|
+ else
|
|
|
+ return DIENUM_CONTINUE;
|
|
|
+
|
|
|
+ ZeroMemory(&dipr, sizeof(dipr));
|
|
|
+ dipr.diph.dwSize = sizeof(dipr);
|
|
|
+ dipr.diph.dwHeaderSize = sizeof(dipr.diph);
|
|
|
+ dipr.diph.dwObj = doi->dwType;
|
|
|
+ dipr.diph.dwHow = DIPH_BYID;
|
|
|
+ dipr.lMin = -32768;
|
|
|
+ dipr.lMax = 32767;
|
|
|
+
|
|
|
+ if (FAILED(IDirectInputDevice8_SetProperty(data->device,
|
|
|
+ DIPROP_RANGE,
|
|
|
+ &dipr.diph)))
|
|
|
+ {
|
|
|
+ return DIENUM_CONTINUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0)
|
|
|
+ {
|
|
|
+ object->type = _GLFW_TYPE_SLIDER;
|
|
|
+ data->sliderCount++;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ object->type = _GLFW_TYPE_AXIS;
|
|
|
+ data->axisCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_BUTTON)
|
|
|
+ {
|
|
|
+ object->offset = DIJOFS_BUTTON(data->buttonCount);
|
|
|
+ object->type = _GLFW_TYPE_BUTTON;
|
|
|
+ data->buttonCount++;
|
|
|
+ }
|
|
|
+ else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_POV)
|
|
|
+ {
|
|
|
+ object->offset = DIJOFS_POV(data->povCount);
|
|
|
+ object->type = _GLFW_TYPE_POV;
|
|
|
+ data->povCount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ data->objectCount++;
|
|
|
+ return DIENUM_CONTINUE;
|
|
|
+}
|
|
|
+
|
|
|
+// DirectInput device enumeration callback
|
|
|
+//
|
|
|
+static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user)
|
|
|
+{
|
|
|
+ int joy = 0;
|
|
|
+ DIDEVCAPS dc;
|
|
|
+ DIPROPDWORD dipd;
|
|
|
+ IDirectInputDevice8* device;
|
|
|
+ _GLFWobjenumWin32 data;
|
|
|
+ _GLFWjoystickWin32* js;
|
|
|
+
|
|
|
+ for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
|
|
|
+ {
|
|
|
+ if (memcmp(&_glfw.win32_js[joy].guid, &di->guidInstance, sizeof(GUID)) == 0)
|
|
|
+ return DIENUM_CONTINUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
|
|
|
+ {
|
|
|
+ if (!_glfw.win32_js[joy].present)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (joy > GLFW_JOYSTICK_LAST)
|
|
|
+ return DIENUM_STOP;
|
|
|
+
|
|
|
+ if (supportsXInput(&di->guidProduct))
|
|
|
+ return DIENUM_CONTINUE;
|
|
|
+
|
|
|
+ if (FAILED(IDirectInput8_CreateDevice(_glfw.win32.dinput8.api,
|
|
|
+ &di->guidInstance,
|
|
|
+ &device,
|
|
|
+ NULL)))
|
|
|
+ {
|
|
|
+ _glfwInputError(GLFW_PLATFORM_ERROR, "DI: Failed to create device");
|
|
|
+ return DIENUM_CONTINUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (FAILED(IDirectInputDevice8_SetDataFormat(device, &_glfwDataFormat)))
|
|
|
+ {
|
|
|
+ _glfwInputError(GLFW_PLATFORM_ERROR,
|
|
|
+ "DI: Failed to set device data format");
|
|
|
+
|
|
|
+ IDirectInputDevice8_Release(device);
|
|
|
+ return DIENUM_CONTINUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ ZeroMemory(&dc, sizeof(dc));
|
|
|
+ dc.dwSize = sizeof(dc);
|
|
|
+
|
|
|
+ if (FAILED(IDirectInputDevice8_GetCapabilities(device, &dc)))
|
|
|
+ {
|
|
|
+ _glfwInputError(GLFW_PLATFORM_ERROR,
|
|
|
+ "DI: Failed to query device capabilities");
|
|
|
+
|
|
|
+ IDirectInputDevice8_Release(device);
|
|
|
+ return DIENUM_CONTINUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ ZeroMemory(&dipd, sizeof(dipd));
|
|
|
+ dipd.diph.dwSize = sizeof(dipd);
|
|
|
+ dipd.diph.dwHeaderSize = sizeof(dipd.diph);
|
|
|
+ dipd.diph.dwHow = DIPH_DEVICE;
|
|
|
+ dipd.dwData = DIPROPAXISMODE_ABS;
|
|
|
+
|
|
|
+ if (FAILED(IDirectInputDevice8_SetProperty(device,
|
|
|
+ DIPROP_AXISMODE,
|
|
|
+ &dipd.diph)))
|
|
|
+ {
|
|
|
+ _glfwInputError(GLFW_PLATFORM_ERROR,
|
|
|
+ "DI: Failed to set device axis mode");
|
|
|
+
|
|
|
+ IDirectInputDevice8_Release(device);
|
|
|
+ return DIENUM_CONTINUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ memset(&data, 0, sizeof(data));
|
|
|
+ data.device = device;
|
|
|
+ data.objects = calloc(dc.dwAxes + dc.dwButtons + dc.dwPOVs,
|
|
|
+ sizeof(_GLFWjoyobjectWin32));
|
|
|
+
|
|
|
+ if (FAILED(IDirectInputDevice8_EnumObjects(device,
|
|
|
+ deviceObjectCallback,
|
|
|
+ &data,
|
|
|
+ DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV)))
|
|
|
+ {
|
|
|
+ _glfwInputError(GLFW_PLATFORM_ERROR,
|
|
|
+ "DI: Failed to enumerate device objects");
|
|
|
+
|
|
|
+ IDirectInputDevice8_Release(device);
|
|
|
+ free(data.objects);
|
|
|
+ return DIENUM_CONTINUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ qsort(data.objects, data.objectCount,
|
|
|
+ sizeof(_GLFWjoyobjectWin32),
|
|
|
+ compareJoystickObjects);
|
|
|
+
|
|
|
+ js = _glfw.win32_js + joy;
|
|
|
+ js->device = device;
|
|
|
+ js->guid = di->guidInstance;
|
|
|
+ js->axisCount = data.axisCount + data.sliderCount;
|
|
|
+ js->axes = calloc(js->axisCount, sizeof(float));
|
|
|
+ js->buttonCount += data.buttonCount + data.povCount * 4;
|
|
|
+ js->buttons = calloc(js->buttonCount, 1);
|
|
|
+ js->objects = data.objects;
|
|
|
+ js->objectCount = data.objectCount;
|
|
|
+ js->name = _glfwCreateUTF8FromWideStringWin32(di->tszInstanceName);
|
|
|
+ js->present = GLFW_TRUE;
|
|
|
+
|
|
|
+ _glfwInputJoystickChange(joy, GLFW_CONNECTED);
|
|
|
+ return DIENUM_CONTINUE;
|
|
|
+}
|
|
|
+
|
|
|
// Attempt to open the specified joystick device
|
|
|
// TODO: Pack state arrays for non-gamepad devices
|
|
|
//
|
|
|
-static GLFWbool openJoystickDevice(DWORD index)
|
|
|
+static GLFWbool openXinputDevice(DWORD index)
|
|
|
{
|
|
|
int joy;
|
|
|
XINPUT_CAPABILITIES xic;
|
|
@@ -74,8 +453,12 @@ static GLFWbool openJoystickDevice(DWORD index)
|
|
|
|
|
|
for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
|
|
|
{
|
|
|
- if (_glfw.win32_js[joy].present && _glfw.win32_js[joy].index == index)
|
|
|
+ if (_glfw.win32_js[joy].present &&
|
|
|
+ _glfw.win32_js[joy].device == NULL &&
|
|
|
+ _glfw.win32_js[joy].index == index)
|
|
|
+ {
|
|
|
return GLFW_FALSE;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
|
|
@@ -92,7 +475,9 @@ static GLFWbool openJoystickDevice(DWORD index)
|
|
|
|
|
|
js = _glfw.win32_js + joy;
|
|
|
js->axisCount = 6;
|
|
|
+ js->axes = calloc(js->axisCount, sizeof(float));
|
|
|
js->buttonCount = 14;
|
|
|
+ js->buttons = calloc(js->buttonCount, 1);
|
|
|
js->present = GLFW_TRUE;
|
|
|
js->name = strdup(getDeviceDescription(&xic));
|
|
|
js->index = index;
|
|
@@ -104,32 +489,120 @@ static GLFWbool openJoystickDevice(DWORD index)
|
|
|
|
|
|
// Polls for and processes events the specified joystick
|
|
|
//
|
|
|
-static GLFWbool pollJoystickEvents(_GLFWjoystickWin32* js, int flags)
|
|
|
+static GLFWbool pollJoystickState(_GLFWjoystickWin32* js, int mode)
|
|
|
{
|
|
|
- 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 (js->device)
|
|
|
{
|
|
|
- if (result == ERROR_DEVICE_NOT_CONNECTED)
|
|
|
+ int i, j, ai = 0, bi = 0;
|
|
|
+ HRESULT result;
|
|
|
+ DIJOYSTATE state;
|
|
|
+
|
|
|
+ IDirectInputDevice8_Poll(js->device);
|
|
|
+ result = IDirectInputDevice8_GetDeviceState(js->device,
|
|
|
+ sizeof(state),
|
|
|
+ &state);
|
|
|
+ if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST)
|
|
|
{
|
|
|
- free(js->name);
|
|
|
- memset(js, 0, sizeof(_GLFWjoystickWin32));
|
|
|
- _glfwInputJoystickChange((int) (js - _glfw.win32_js), GLFW_DISCONNECTED);
|
|
|
+ IDirectInputDevice8_Acquire(js->device);
|
|
|
+ IDirectInputDevice8_Poll(js->device);
|
|
|
+ result = IDirectInputDevice8_GetDeviceState(js->device,
|
|
|
+ sizeof(state),
|
|
|
+ &state);
|
|
|
}
|
|
|
|
|
|
- return GLFW_FALSE;
|
|
|
- }
|
|
|
+ if (FAILED(result))
|
|
|
+ {
|
|
|
+ closeJoystick(js);
|
|
|
+ return GLFW_FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mode == _GLFW_PRESENCE_ONLY)
|
|
|
+ return GLFW_TRUE;
|
|
|
|
|
|
- if (flags & _GLFW_UPDATE_AXES)
|
|
|
+ for (i = 0; i < js->objectCount; i++)
|
|
|
+ {
|
|
|
+ const void* data = (char*) &state + js->objects[i].offset;
|
|
|
+
|
|
|
+ switch (js->objects[i].type)
|
|
|
+ {
|
|
|
+ case _GLFW_TYPE_AXIS:
|
|
|
+ case _GLFW_TYPE_SLIDER:
|
|
|
+ {
|
|
|
+ js->axes[ai++] = (*((LONG*) data) + 0.5f) / 32767.5f;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case _GLFW_TYPE_BUTTON:
|
|
|
+ {
|
|
|
+ if (*((BYTE*) data) & 0x80)
|
|
|
+ js->buttons[bi++] = GLFW_PRESS;
|
|
|
+ else
|
|
|
+ js->buttons[bi++] = GLFW_RELEASE;
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case _GLFW_TYPE_POV:
|
|
|
+ {
|
|
|
+ const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 };
|
|
|
+ // Screams of horror are appropriate at this point
|
|
|
+ int value = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES);
|
|
|
+ if (value < 0 || value > 8)
|
|
|
+ value = 8;
|
|
|
+
|
|
|
+ for (j = 0; j < 4; j++)
|
|
|
+ {
|
|
|
+ if (directions[value] & (1 << j))
|
|
|
+ js->buttons[bi++] = GLFW_PRESS;
|
|
|
+ else
|
|
|
+ js->buttons[bi++] = GLFW_RELEASE;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return GLFW_TRUE;
|
|
|
+ }
|
|
|
+ else
|
|
|
{
|
|
|
+ int i;
|
|
|
+ DWORD result;
|
|
|
+ XINPUT_STATE xis;
|
|
|
+ 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
|
|
|
+ };
|
|
|
+
|
|
|
+ result = _glfw_XInputGetState(js->index, &xis);
|
|
|
+ if (result != ERROR_SUCCESS)
|
|
|
+ {
|
|
|
+ if (result == ERROR_DEVICE_NOT_CONNECTED)
|
|
|
+ closeJoystick(js);
|
|
|
+
|
|
|
+ return GLFW_FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mode == _GLFW_PRESENCE_ONLY)
|
|
|
+ return GLFW_TRUE;
|
|
|
+
|
|
|
if (sqrtf((float) (xis.Gamepad.sThumbLX * xis.Gamepad.sThumbLX +
|
|
|
xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY)) >
|
|
|
(float) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
|
|
@@ -165,34 +638,12 @@ static GLFWbool pollJoystickEvents(_GLFWjoystickWin32* js, int flags)
|
|
|
js->axes[5] = xis.Gamepad.bRightTrigger / 127.5f - 1.f;
|
|
|
else
|
|
|
js->axes[5] = -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;
|
|
|
+ return GLFW_TRUE;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
@@ -204,6 +655,19 @@ static GLFWbool pollJoystickEvents(_GLFWjoystickWin32* js, int flags)
|
|
|
//
|
|
|
void _glfwInitJoysticksWin32(void)
|
|
|
{
|
|
|
+ if (_glfw.win32.dinput8.instance)
|
|
|
+ {
|
|
|
+ if (FAILED(_glfw_DirectInput8Create(GetModuleHandle(NULL),
|
|
|
+ DIRECTINPUT_VERSION,
|
|
|
+ &IID_IDirectInput8W,
|
|
|
+ (void**) &_glfw.win32.dinput8.api,
|
|
|
+ NULL)))
|
|
|
+ {
|
|
|
+ _glfwInputError(GLFW_PLATFORM_ERROR,
|
|
|
+ "DI: Failed to create interface");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
_glfwDetectJoystickConnectionWin32();
|
|
|
}
|
|
|
|
|
@@ -214,33 +678,47 @@ void _glfwTerminateJoysticksWin32(void)
|
|
|
int joy;
|
|
|
|
|
|
for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
|
|
|
- free(_glfw.win32_js[joy].name);
|
|
|
+ closeJoystick(_glfw.win32_js + joy);
|
|
|
+
|
|
|
+ if (_glfw.win32.dinput8.api)
|
|
|
+ IDirectInput8_Release(_glfw.win32.dinput8.api);
|
|
|
}
|
|
|
|
|
|
-// Looks for new joysticks
|
|
|
+// Checks for new joysticks after DBT_DEVICEARRIVAL
|
|
|
//
|
|
|
void _glfwDetectJoystickConnectionWin32(void)
|
|
|
{
|
|
|
- DWORD i;
|
|
|
+ if (_glfw.win32.xinput.instance)
|
|
|
+ {
|
|
|
+ DWORD i;
|
|
|
|
|
|
- if (!_glfw.win32.xinput.instance)
|
|
|
- return;
|
|
|
+ for (i = 0; i < XUSER_MAX_COUNT; i++)
|
|
|
+ openXinputDevice(i);
|
|
|
+ }
|
|
|
|
|
|
- for (i = 0; i < XUSER_MAX_COUNT; i++)
|
|
|
- openJoystickDevice(i);
|
|
|
+ if (_glfw.win32.dinput8.api)
|
|
|
+ {
|
|
|
+ if (FAILED(IDirectInput8_EnumDevices(_glfw.win32.dinput8.api,
|
|
|
+ DI8DEVCLASS_GAMECTRL,
|
|
|
+ deviceCallback,
|
|
|
+ NULL,
|
|
|
+ DIEDFL_ALLDEVICES)))
|
|
|
+ {
|
|
|
+ _glfwInputError(GLFW_PLATFORM_ERROR,
|
|
|
+ "Failed to enumerate DirectInput8 devices");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-// Checks if any current joystick has been disconnected
|
|
|
+// Checks for joystick disconnection after DBT_DEVICEREMOVECOMPLETE
|
|
|
//
|
|
|
void _glfwDetectJoystickDisconnectionWin32(void)
|
|
|
{
|
|
|
- DWORD i;
|
|
|
-
|
|
|
- if (!_glfw.win32.xinput.instance)
|
|
|
- return;
|
|
|
+ int joy;
|
|
|
|
|
|
- for (i = 0; i < XUSER_MAX_COUNT; i++)
|
|
|
- pollJoystickEvents(_glfw.win32_js + i, 0);
|
|
|
+ for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
|
|
|
+ pollJoystickState(_glfw.win32_js + joy, _GLFW_PRESENCE_ONLY);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -251,13 +729,13 @@ void _glfwDetectJoystickDisconnectionWin32(void)
|
|
|
int _glfwPlatformJoystickPresent(int joy)
|
|
|
{
|
|
|
_GLFWjoystickWin32* js = _glfw.win32_js + joy;
|
|
|
- return pollJoystickEvents(js, 0);
|
|
|
+ return pollJoystickState(js, _GLFW_PRESENCE_ONLY);
|
|
|
}
|
|
|
|
|
|
const float* _glfwPlatformGetJoystickAxes(int joy, int* count)
|
|
|
{
|
|
|
_GLFWjoystickWin32* js = _glfw.win32_js + joy;
|
|
|
- if (!pollJoystickEvents(js, _GLFW_UPDATE_AXES))
|
|
|
+ if (!pollJoystickState(js, _GLFW_UPDATE_STATE))
|
|
|
return NULL;
|
|
|
|
|
|
*count = js->axisCount;
|
|
@@ -267,7 +745,7 @@ const float* _glfwPlatformGetJoystickAxes(int joy, int* count)
|
|
|
const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count)
|
|
|
{
|
|
|
_GLFWjoystickWin32* js = _glfw.win32_js + joy;
|
|
|
- if (!pollJoystickEvents(js, _GLFW_UPDATE_BUTTONS))
|
|
|
+ if (!pollJoystickState(js, _GLFW_UPDATE_STATE))
|
|
|
return NULL;
|
|
|
|
|
|
*count = js->buttonCount;
|
|
@@ -277,7 +755,7 @@ const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count)
|
|
|
const char* _glfwPlatformGetJoystickName(int joy)
|
|
|
{
|
|
|
_GLFWjoystickWin32* js = _glfw.win32_js + joy;
|
|
|
- if (!pollJoystickEvents(js, 0))
|
|
|
+ if (!pollJoystickState(js, _GLFW_PRESENCE_ONLY))
|
|
|
return NULL;
|
|
|
|
|
|
return js->name;
|