Add initial DirectInput 8 support

Fixes #232.
This commit is contained in:
Camilla Berglund 2016-02-16 23:06:29 +01:00
parent 3aebb0bfe3
commit 7cbdae1bed
5 changed files with 591 additions and 75 deletions

View File

@ -101,6 +101,7 @@ does not find Doxygen, the documentation will not be generated.
- Removed `_GLFW_USE_OPENGL`, `_GLFW_USE_GLESV1` and `_GLFW_USE_GLESV2` - Removed `_GLFW_USE_OPENGL`, `_GLFW_USE_GLESV1` and `_GLFW_USE_GLESV2`
configuration macros configuration macros
- [Win32] Added support for Windows 8.1 per-monitor DPI - [Win32] Added support for Windows 8.1 per-monitor DPI
- [Win32] Replaced winmm with XInput and DirectInput for joystick input
- [Win32] Bugfix: Window creation would segfault if video mode setting required - [Win32] Bugfix: Window creation would segfault if video mode setting required
the system to be restarted the system to be restarted
- [Win32] Bugfix: MinGW import library lacked the `lib` prefix - [Win32] Bugfix: MinGW import library lacked the `lib` prefix

View File

@ -86,6 +86,13 @@ static GLFWbool loadLibraries(void)
_glfw.win32.user32.ChangeWindowMessageFilterEx = (CHANGEWINDOWMESSAGEFILTEREX_T) _glfw.win32.user32.ChangeWindowMessageFilterEx = (CHANGEWINDOWMESSAGEFILTEREX_T)
GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx");
_glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll");
if (_glfw.win32.dinput8.instance)
{
_glfw.win32.dinput8.DirectInput8Create = (DIRECTINPUT8CREATE_T)
GetProcAddress(_glfw.win32.dinput8.instance, "DirectInput8Create");
}
{ {
int i; int i;
const char* names[] = const char* names[] =
@ -139,6 +146,9 @@ static void freeLibraries(void)
if (_glfw.win32.xinput.instance) if (_glfw.win32.xinput.instance)
FreeLibrary(_glfw.win32.xinput.instance); FreeLibrary(_glfw.win32.xinput.instance);
if (_glfw.win32.dinput8.instance)
FreeLibrary(_glfw.win32.dinput8.instance);
if (_glfw.win32.winmm.instance) if (_glfw.win32.winmm.instance)
FreeLibrary(_glfw.win32.winmm.instance); FreeLibrary(_glfw.win32.winmm.instance);

View File

@ -29,9 +29,104 @@
#include <math.h> #include <math.h>
#define _GLFW_UPDATE_BUTTONS 1 #include <initguid.h>
#define _GLFW_UPDATE_AXES 2
#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 // Returns a description fitting the specified XInput capabilities
// //
@ -63,10 +158,294 @@ static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic)
return "Unknown XInput Device"; 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 // Attempt to open the specified joystick device
// TODO: Pack state arrays for non-gamepad devices // TODO: Pack state arrays for non-gamepad devices
// //
static GLFWbool openJoystickDevice(DWORD index) static GLFWbool openXinputDevice(DWORD index)
{ {
int joy; int joy;
XINPUT_CAPABILITIES xic; XINPUT_CAPABILITIES xic;
@ -74,8 +453,12 @@ static GLFWbool openJoystickDevice(DWORD index)
for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) 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; return GLFW_FALSE;
}
} }
for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) 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 = _glfw.win32_js + joy;
js->axisCount = 6; js->axisCount = 6;
js->axes = calloc(js->axisCount, sizeof(float));
js->buttonCount = 14; js->buttonCount = 14;
js->buttons = calloc(js->buttonCount, 1);
js->present = GLFW_TRUE; js->present = GLFW_TRUE;
js->name = strdup(getDeviceDescription(&xic)); js->name = strdup(getDeviceDescription(&xic));
js->index = index; js->index = index;
@ -104,32 +489,120 @@ static GLFWbool openJoystickDevice(DWORD index)
// Polls for and processes events the specified joystick // 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) if (!js->present)
return GLFW_FALSE; return GLFW_FALSE;
result = _glfw_XInputGetState(js->index, &xis); if (js->device)
if (result != ERROR_SUCCESS)
{ {
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); IDirectInputDevice8_Acquire(js->device);
memset(js, 0, sizeof(_GLFWjoystickWin32)); IDirectInputDevice8_Poll(js->device);
_glfwInputJoystickChange((int) (js - _glfw.win32_js), GLFW_DISCONNECTED); result = IDirectInputDevice8_GetDeviceState(js->device,
sizeof(state),
&state);
} }
return GLFW_FALSE; if (FAILED(result))
} {
closeJoystick(js);
return GLFW_FALSE;
}
if (flags & _GLFW_UPDATE_AXES) if (mode == _GLFW_PRESENCE_ONLY)
return GLFW_TRUE;
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 + if (sqrtf((float) (xis.Gamepad.sThumbLX * xis.Gamepad.sThumbLX +
xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY)) > xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY)) >
(float) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) (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; js->axes[5] = xis.Gamepad.bRightTrigger / 127.5f - 1.f;
else else
js->axes[5] = -1.f; 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++) for (i = 0; i < 14; i++)
js->buttons[i] = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; 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) 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(); _glfwDetectJoystickConnectionWin32();
} }
@ -214,33 +678,47 @@ void _glfwTerminateJoysticksWin32(void)
int joy; int joy;
for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; 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) void _glfwDetectJoystickConnectionWin32(void)
{ {
DWORD i; if (_glfw.win32.xinput.instance)
{
DWORD i;
if (!_glfw.win32.xinput.instance) for (i = 0; i < XUSER_MAX_COUNT; i++)
return; openXinputDevice(i);
}
for (i = 0; i < XUSER_MAX_COUNT; i++) if (_glfw.win32.dinput8.api)
openJoystickDevice(i); {
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) void _glfwDetectJoystickDisconnectionWin32(void)
{ {
DWORD i; int joy;
if (!_glfw.win32.xinput.instance) for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++)
return; pollJoystickState(_glfw.win32_js + joy, _GLFW_PRESENCE_ONLY);
for (i = 0; i < XUSER_MAX_COUNT; i++)
pollJoystickEvents(_glfw.win32_js + i, 0);
} }
@ -251,13 +729,13 @@ void _glfwDetectJoystickDisconnectionWin32(void)
int _glfwPlatformJoystickPresent(int joy) int _glfwPlatformJoystickPresent(int joy)
{ {
_GLFWjoystickWin32* js = _glfw.win32_js + joy; _GLFWjoystickWin32* js = _glfw.win32_js + joy;
return pollJoystickEvents(js, 0); return pollJoystickState(js, _GLFW_PRESENCE_ONLY);
} }
const float* _glfwPlatformGetJoystickAxes(int joy, int* count) const float* _glfwPlatformGetJoystickAxes(int joy, int* count)
{ {
_GLFWjoystickWin32* js = _glfw.win32_js + joy; _GLFWjoystickWin32* js = _glfw.win32_js + joy;
if (!pollJoystickEvents(js, _GLFW_UPDATE_AXES)) if (!pollJoystickState(js, _GLFW_UPDATE_STATE))
return NULL; return NULL;
*count = js->axisCount; *count = js->axisCount;
@ -267,7 +745,7 @@ const float* _glfwPlatformGetJoystickAxes(int joy, int* count)
const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count)
{ {
_GLFWjoystickWin32* js = _glfw.win32_js + joy; _GLFWjoystickWin32* js = _glfw.win32_js + joy;
if (!pollJoystickEvents(js, _GLFW_UPDATE_BUTTONS)) if (!pollJoystickState(js, _GLFW_UPDATE_STATE))
return NULL; return NULL;
*count = js->buttonCount; *count = js->buttonCount;
@ -277,7 +755,7 @@ const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count)
const char* _glfwPlatformGetJoystickName(int joy) const char* _glfwPlatformGetJoystickName(int joy)
{ {
_GLFWjoystickWin32* js = _glfw.win32_js + joy; _GLFWjoystickWin32* js = _glfw.win32_js + joy;
if (!pollJoystickEvents(js, 0)) if (!pollJoystickState(js, _GLFW_PRESENCE_ONLY))
return NULL; return NULL;
return js->name; return js->name;

View File

@ -30,17 +30,29 @@
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \
_GLFWjoystickWin32 win32_js[GLFW_JOYSTICK_LAST + 1] _GLFWjoystickWin32 win32_js[GLFW_JOYSTICK_LAST + 1]
// Spoo
//
typedef struct _GLFWjoyobjectWin32
{
int offset;
int type;
} _GLFWjoyobjectWin32;
// Win32-specific per-joystick data // Win32-specific per-joystick data
// //
typedef struct _GLFWjoystickWin32 typedef struct _GLFWjoystickWin32
{ {
GLFWbool present; GLFWbool present;
float axes[6]; float* axes;
int axisCount; int axisCount;
unsigned char buttons[14]; unsigned char* buttons;
int buttonCount; int buttonCount;
char* name; _GLFWjoyobjectWin32* objects;
DWORD index; int objectCount;
char* name;
IDirectInputDevice8W* device;
DWORD index;
GUID guid;
} _GLFWjoystickWin32; } _GLFWjoystickWin32;

View File

@ -61,8 +61,12 @@
#define _WIN32_WINNT 0x0501 #define _WIN32_WINNT 0x0501
#endif #endif
// GLFW uses DirectInput8 interfaces
#define DIRECTINPUT_VERSION 0x0800
#include <windows.h> #include <windows.h>
#include <mmsystem.h> #include <mmsystem.h>
#include <dinput.h>
#include <xinput.h> #include <xinput.h>
#include <dbt.h> #include <dbt.h>
@ -71,7 +75,7 @@
#define strdup _strdup #define strdup _strdup
#endif #endif
// HACK: Define macros that some older windows.h variants don't // HACK: Define macros that some windows.h variants don't
#ifndef WM_MOUSEHWHEEL #ifndef WM_MOUSEHWHEEL
#define WM_MOUSEHWHEEL 0x020E #define WM_MOUSEHWHEEL 0x020E
#endif #endif
@ -121,7 +125,7 @@ typedef enum PROCESS_DPI_AWARENESS
} PROCESS_DPI_AWARENESS; } PROCESS_DPI_AWARENESS;
#endif /*DPI_ENUMS_DECLARED*/ #endif /*DPI_ENUMS_DECLARED*/
// HACK: Define macros that some older xinput.h variants don't // HACK: Define macros that some xinput.h variants don't
#ifndef XINPUT_CAPS_WIRELESS #ifndef XINPUT_CAPS_WIRELESS
#define XINPUT_CAPS_WIRELESS 0x0002 #define XINPUT_CAPS_WIRELESS 0x0002
#endif #endif
@ -147,6 +151,11 @@ typedef enum PROCESS_DPI_AWARENESS
#define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13 #define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13
#endif #endif
// HACK: Define macros that some dinput.h variants don't
#ifndef DIDFT_OPTIONAL
#define DIDFT_OPTIONAL 0x80000000
#endif
// winmm.dll function pointer typedefs // winmm.dll function pointer typedefs
typedef DWORD (WINAPI * TIMEGETTIME_T)(void); typedef DWORD (WINAPI * TIMEGETTIME_T)(void);
#define _glfw_timeGetTime _glfw.win32.winmm.timeGetTime #define _glfw_timeGetTime _glfw.win32.winmm.timeGetTime
@ -157,6 +166,10 @@ typedef DWORD (WINAPI * XINPUTGETSTATE_T)(DWORD,XINPUT_STATE*);
#define _glfw_XInputGetCapabilities _glfw.win32.xinput.XInputGetCapabilities #define _glfw_XInputGetCapabilities _glfw.win32.xinput.XInputGetCapabilities
#define _glfw_XInputGetState _glfw.win32.xinput.XInputGetState #define _glfw_XInputGetState _glfw.win32.xinput.XInputGetState
// dinput8.dll function pointer typedefs
typedef HRESULT (WINAPI * DIRECTINPUT8CREATE_T)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN);
#define _glfw_DirectInput8Create _glfw.win32.dinput8.DirectInput8Create
// user32.dll function pointer typedefs // user32.dll function pointer typedefs
typedef BOOL (WINAPI * SETPROCESSDPIAWARE_T)(void); typedef BOOL (WINAPI * SETPROCESSDPIAWARE_T)(void);
typedef BOOL (WINAPI * CHANGEWINDOWMESSAGEFILTEREX_T)(HWND,UINT,DWORD,PCHANGEFILTERSTRUCT); typedef BOOL (WINAPI * CHANGEWINDOWMESSAGEFILTEREX_T)(HWND,UINT,DWORD,PCHANGEFILTERSTRUCT);
@ -241,13 +254,17 @@ typedef struct _GLFWlibraryWin32
short int publicKeys[512]; short int publicKeys[512];
short int nativeKeys[GLFW_KEY_LAST + 1]; short int nativeKeys[GLFW_KEY_LAST + 1];
// winmm.dll
struct { struct {
HINSTANCE instance; HINSTANCE instance;
TIMEGETTIME_T timeGetTime; TIMEGETTIME_T timeGetTime;
} winmm; } winmm;
// user32.dll struct {
HINSTANCE instance;
DIRECTINPUT8CREATE_T DirectInput8Create;
IDirectInput8W* api;
} dinput8;
struct { struct {
HINSTANCE instance; HINSTANCE instance;
XINPUTGETCAPABILITIES_T XInputGetCapabilities; XINPUTGETCAPABILITIES_T XInputGetCapabilities;
@ -260,14 +277,12 @@ typedef struct _GLFWlibraryWin32
CHANGEWINDOWMESSAGEFILTEREX_T ChangeWindowMessageFilterEx; CHANGEWINDOWMESSAGEFILTEREX_T ChangeWindowMessageFilterEx;
} user32; } user32;
// dwmapi.dll
struct { struct {
HINSTANCE instance; HINSTANCE instance;
DWMISCOMPOSITIONENABLED_T DwmIsCompositionEnabled; DWMISCOMPOSITIONENABLED_T DwmIsCompositionEnabled;
DWMFLUSH_T DwmFlush; DWMFLUSH_T DwmFlush;
} dwmapi; } dwmapi;
// shcore.dll
struct { struct {
HINSTANCE instance; HINSTANCE instance;
SETPROCESSDPIAWARENESS_T SetProcessDpiAwareness; SETPROCESSDPIAWARENESS_T SetProcessDpiAwareness;