Added support for interfacing with joysticks via DirectInput when it is available.

This commit is contained in:
binary1248 2017-12-21 20:54:58 +01:00 committed by Lukas Dürrenberger
parent d972216c57
commit 22f1b85515
6 changed files with 3315 additions and 14 deletions

117
extlibs/headers/mingw/_mingw_dxhelper.h vendored Normal file
View File

@ -0,0 +1,117 @@
/**
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
* No warranty is given; refer to the file DISCLAIMER within this package.
*/
#if defined(_MSC_VER) && !defined(_MSC_EXTENSIONS)
#define NONAMELESSUNION 1
#endif
#if defined(NONAMELESSSTRUCT) && \
!defined(NONAMELESSUNION)
#define NONAMELESSUNION 1
#endif
#if defined(NONAMELESSUNION) && \
!defined(NONAMELESSSTRUCT)
#define NONAMELESSSTRUCT 1
#endif
#if !defined(__GNU_EXTENSION)
#if defined(__GNUC__) || defined(__GNUG__)
#define __GNU_EXTENSION __extension__
#else
#define __GNU_EXTENSION
#endif
#endif /* __extension__ */
#ifndef __ANONYMOUS_DEFINED
#define __ANONYMOUS_DEFINED
#if defined(__GNUC__) || defined(__GNUG__)
#define _ANONYMOUS_UNION __extension__
#define _ANONYMOUS_STRUCT __extension__
#else
#define _ANONYMOUS_UNION
#define _ANONYMOUS_STRUCT
#endif
#ifndef NONAMELESSUNION
#define _UNION_NAME(x)
#define _STRUCT_NAME(x)
#else /* NONAMELESSUNION */
#define _UNION_NAME(x) x
#define _STRUCT_NAME(x) x
#endif
#endif /* __ANONYMOUS_DEFINED */
#ifndef DUMMYUNIONNAME
# ifdef NONAMELESSUNION
# define DUMMYUNIONNAME u
# define DUMMYUNIONNAME1 u1 /* Wine uses this variant */
# define DUMMYUNIONNAME2 u2
# define DUMMYUNIONNAME3 u3
# define DUMMYUNIONNAME4 u4
# define DUMMYUNIONNAME5 u5
# define DUMMYUNIONNAME6 u6
# define DUMMYUNIONNAME7 u7
# define DUMMYUNIONNAME8 u8
# define DUMMYUNIONNAME9 u9
# else /* NONAMELESSUNION */
# define DUMMYUNIONNAME
# define DUMMYUNIONNAME1 /* Wine uses this variant */
# define DUMMYUNIONNAME2
# define DUMMYUNIONNAME3
# define DUMMYUNIONNAME4
# define DUMMYUNIONNAME5
# define DUMMYUNIONNAME6
# define DUMMYUNIONNAME7
# define DUMMYUNIONNAME8
# define DUMMYUNIONNAME9
# endif
#endif /* DUMMYUNIONNAME */
#if !defined(DUMMYUNIONNAME1) /* MinGW does not define this one */
# ifdef NONAMELESSUNION
# define DUMMYUNIONNAME1 u1 /* Wine uses this variant */
# else
# define DUMMYUNIONNAME1 /* Wine uses this variant */
# endif
#endif /* DUMMYUNIONNAME1 */
#ifndef DUMMYSTRUCTNAME
# ifdef NONAMELESSUNION
# define DUMMYSTRUCTNAME s
# define DUMMYSTRUCTNAME1 s1 /* Wine uses this variant */
# define DUMMYSTRUCTNAME2 s2
# define DUMMYSTRUCTNAME3 s3
# define DUMMYSTRUCTNAME4 s4
# define DUMMYSTRUCTNAME5 s5
# else
# define DUMMYSTRUCTNAME
# define DUMMYSTRUCTNAME1 /* Wine uses this variant */
# define DUMMYSTRUCTNAME2
# define DUMMYSTRUCTNAME3
# define DUMMYSTRUCTNAME4
# define DUMMYSTRUCTNAME5
# endif
#endif /* DUMMYSTRUCTNAME */
/* These are for compatibility with the Wine source tree */
#ifndef WINELIB_NAME_AW
# ifdef __MINGW_NAME_AW
# define WINELIB_NAME_AW __MINGW_NAME_AW
# else
# ifdef UNICODE
# define WINELIB_NAME_AW(func) func##W
# else
# define WINELIB_NAME_AW(func) func##A
# endif
# endif
#endif /* WINELIB_NAME_AW */
#ifndef DECL_WINELIB_TYPE_AW
# ifdef __MINGW_TYPEDEF_AW
# define DECL_WINELIB_TYPE_AW __MINGW_TYPEDEF_AW
# else
# define DECL_WINELIB_TYPE_AW(type) typedef WINELIB_NAME_AW(type) type;
# endif
#endif /* DECL_WINELIB_TYPE_AW */

2467
extlibs/headers/mingw/dinput.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -245,6 +245,13 @@ if(SFML_OS_LINUX)
endif() endif()
include_directories(${UDEV_INCLUDE_DIR}) include_directories(${UDEV_INCLUDE_DIR})
endif() endif()
if(SFML_OS_WINDOWS AND NOT SFML_COMPILER_MSVC)
include(CheckIncludeFile)
check_include_file(dinput.h DINPUT_H_FOUND)
if(NOT DINPUT_H_FOUND)
include_directories("${PROJECT_SOURCE_DIR}/extlibs/headers/mingw")
endif()
endif()
# build the list of external libraries to link # build the list of external libraries to link

View File

@ -33,9 +33,59 @@
#include <regstr.h> #include <regstr.h>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <cstring>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <vector>
////////////////////////////////////////////////////////////
// DirectInput
////////////////////////////////////////////////////////////
#ifndef DIDFT_OPTIONAL
#define DIDFT_OPTIONAL 0x80000000
#endif
namespace
{
namespace guids
{
const GUID IID_IDirectInput8W = {0xbf798031, 0x483a, 0x4da2, {0xaa, 0x99, 0x5d, 0x64, 0xed, 0x36, 0x97, 0x00}};
const GUID GUID_XAxis = {0xa36d02e0, 0xc9f3, 0x11cf, {0xbf, 0xc7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
const GUID GUID_YAxis = {0xa36d02e1, 0xc9f3, 0x11cf, {0xbf, 0xc7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
const GUID GUID_ZAxis = {0xa36d02e2, 0xc9f3, 0x11cf, {0xbf, 0xc7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
const GUID GUID_RzAxis = {0xa36d02e3, 0xc9f3, 0x11cf, {0xbf, 0xc7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
const GUID GUID_Slider = {0xa36d02e4, 0xc9f3, 0x11cf, {0xbf, 0xc7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
const GUID GUID_POV = {0xa36d02f2, 0xc9f3, 0x11cf, {0xbf, 0xc7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
const GUID GUID_RxAxis = {0xa36d02f4, 0xc9f3, 0x11cf, {0xbf, 0xc7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
const GUID GUID_RyAxis = {0xa36d02f5, 0xc9f3, 0x11cf, {0xbf, 0xc7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
}
HMODULE dinput8dll = NULL;
IDirectInput8W* directInput = NULL;
struct JoystickRecord
{
GUID guid;
unsigned int index;
bool plugged;
};
typedef std::vector<JoystickRecord> JoystickList;
JoystickList joystickList;
}
////////////////////////////////////////////////////////////
// Legacy joystick API
////////////////////////////////////////////////////////////
namespace namespace
{ {
struct ConnectionCache struct ConnectionCache
@ -152,6 +202,12 @@ namespace priv
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void JoystickImpl::initialize() void JoystickImpl::initialize()
{ {
// Try to initialize DirectInput
initializeDInput();
if (!directInput)
err() << "DirectInput not available, falling back to Windows joystick API" << std::endl;
// Perform the initial scan and populate the connection cache // Perform the initial scan and populate the connection cache
updateConnections(); updateConnections();
} }
@ -160,13 +216,17 @@ void JoystickImpl::initialize()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void JoystickImpl::cleanup() void JoystickImpl::cleanup()
{ {
// Nothing to do // Clean up DirectInput
cleanupDInput();
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool JoystickImpl::isConnected(unsigned int index) bool JoystickImpl::isConnected(unsigned int index)
{ {
if (directInput)
return isConnectedDInput(index);
ConnectionCache& cache = connectionCache[index]; ConnectionCache& cache = connectionCache[index];
if (!lazyUpdates && cache.timer.getElapsedTime() > connectionRefreshDelay) if (!lazyUpdates && cache.timer.getElapsedTime() > connectionRefreshDelay)
{ {
@ -189,6 +249,9 @@ void JoystickImpl::setLazyUpdates(bool status)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void JoystickImpl::updateConnections() void JoystickImpl::updateConnections()
{ {
if (directInput)
return updateConnectionsDInput();
for (unsigned int i = 0; i < Joystick::Count; ++i) for (unsigned int i = 0; i < Joystick::Count; ++i)
{ {
JOYINFOEX joyInfo; JOYINFOEX joyInfo;
@ -204,6 +267,9 @@ void JoystickImpl::updateConnections()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool JoystickImpl::open(unsigned int index) bool JoystickImpl::open(unsigned int index)
{ {
if (directInput)
return openDInput(index);
// No explicit "open" action is required // No explicit "open" action is required
m_index = JOYSTICKID1 + index; m_index = JOYSTICKID1 + index;
@ -224,12 +290,16 @@ bool JoystickImpl::open(unsigned int index)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void JoystickImpl::close() void JoystickImpl::close()
{ {
// Nothing to do if (directInput)
closeDInput();
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
JoystickCaps JoystickImpl::getCapabilities() const JoystickCaps JoystickImpl::getCapabilities() const
{ {
if (directInput)
return getCapabilitiesDInput();
JoystickCaps caps; JoystickCaps caps;
caps.buttonCount = m_caps.wNumButtons; caps.buttonCount = m_caps.wNumButtons;
@ -259,6 +329,9 @@ Joystick::Identification JoystickImpl::getIdentification() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
JoystickState JoystickImpl::update() JoystickState JoystickImpl::update()
{ {
if (directInput)
return updateDInput();
JoystickState state; JoystickState state;
// Get the current joystick state // Get the current joystick state
@ -300,6 +373,548 @@ JoystickState JoystickImpl::update()
return state; return state;
} }
////////////////////////////////////////////////////////////
void JoystickImpl::initializeDInput()
{
// Try to load dinput8.dll
dinput8dll = LoadLibraryA("dinput8.dll");
if (dinput8dll)
{
// Try to get the address of the DirectInput8Create entry point
typedef HRESULT(WINAPI *DirectInput8CreateFunc)(HINSTANCE, DWORD, REFIID, LPVOID*, LPUNKNOWN);
DirectInput8CreateFunc directInput8Create = reinterpret_cast<DirectInput8CreateFunc>(GetProcAddress(dinput8dll, "DirectInput8Create"));
if (directInput8Create)
{
// Try to acquire a DirectInput 8.x interface
HRESULT result = directInput8Create(GetModuleHandleW(NULL), 0x0800, guids::IID_IDirectInput8W, reinterpret_cast<void**>(&directInput), NULL);
if (result)
{
// De-initialize everything
directInput = NULL;
FreeLibrary(dinput8dll);
dinput8dll = NULL;
err() << "Failed to initialize DirectInput: " << result << std::endl;
}
}
else
{
// Unload dinput8.dll
FreeLibrary(dinput8dll);
dinput8dll = NULL;
}
}
}
////////////////////////////////////////////////////////////
void JoystickImpl::cleanupDInput()
{
// Release the DirectInput interface
if (directInput)
{
directInput->Release();
directInput = NULL;
}
// Unload dinput8.dll
if (dinput8dll)
FreeLibrary(dinput8dll);
}
////////////////////////////////////////////////////////////
bool JoystickImpl::isConnectedDInput(unsigned int index)
{
// Check if a joystick with the given index is in the connected list
for (std::vector<JoystickRecord>::iterator i = joystickList.begin(); i != joystickList.end(); ++i)
{
if (i->index == index)
return true;
}
return false;
}
////////////////////////////////////////////////////////////
void JoystickImpl::updateConnectionsDInput()
{
// Clear plugged flags so we can determine which devices were added/removed
for (std::size_t i = 0; i < joystickList.size(); ++i)
joystickList[i].plugged = false;
// Enumerate devices
HRESULT result = directInput->EnumDevices(DI8DEVCLASS_GAMECTRL, &JoystickImpl::deviceEnumerationCallback, NULL, DIEDFL_ALLDEVICES);
// Remove devices that were not connected during the enumeration
for (std::vector<JoystickRecord>::iterator i = joystickList.begin(); i != joystickList.end();)
{
if (!i->plugged)
i = joystickList.erase(i);
else
++i;
}
if (result)
{
err() << "Failed to enumerate DirectInput devices: " << result << std::endl;
return;
}
// Assign unused joystick indices to devices that were newly connected
for (unsigned int i = 0; i < Joystick::Count; ++i)
{
for (std::vector<JoystickRecord>::iterator j = joystickList.begin(); j != joystickList.end(); ++j)
{
if (j->index == i)
break;
if (j->index == Joystick::Count)
{
j->index = i;
break;
}
}
}
}
////////////////////////////////////////////////////////////
bool JoystickImpl::openDInput(unsigned int index)
{
// Initialize DirectInput members
m_device = NULL;
for (int i = 0; i < Joystick::AxisCount; ++i)
m_axes[i] = -1;
for (int i = 0; i < Joystick::ButtonCount; ++i)
m_buttons[i] = -1;
std::memset(&m_deviceCaps, 0, sizeof(DIDEVCAPS));
m_deviceCaps.dwSize = sizeof(DIDEVCAPS);
// Search for a joystick with the given index in the connected list
for (std::vector<JoystickRecord>::iterator i = joystickList.begin(); i != joystickList.end(); ++i)
{
if (i->index == index)
{
// Create device
HRESULT result = directInput->CreateDevice(i->guid, &m_device, NULL);
if (result)
{
err() << "Failed to create DirectInput device: " << result << std::endl;
return false;
}
static bool formatInitialized = false;
static DIDATAFORMAT format;
if (!formatInitialized)
{
const DWORD axisType = DIDFT_AXIS | DIDFT_OPTIONAL | DIDFT_ANYINSTANCE;
const DWORD povType = DIDFT_POV | DIDFT_OPTIONAL | DIDFT_ANYINSTANCE;
const DWORD buttonType = DIDFT_BUTTON | DIDFT_OPTIONAL | DIDFT_ANYINSTANCE;
static DIOBJECTDATAFORMAT data[8 + 4 + sf::Joystick::ButtonCount];
data[0].pguid = &guids::GUID_XAxis;
data[0].dwOfs = DIJOFS_X;
data[1].pguid = &guids::GUID_YAxis;
data[1].dwOfs = DIJOFS_Y;
data[2].pguid = &guids::GUID_ZAxis;
data[2].dwOfs = DIJOFS_Z;
data[3].pguid = &guids::GUID_RxAxis;
data[3].dwOfs = DIJOFS_RX;
data[4].pguid = &guids::GUID_RyAxis;
data[4].dwOfs = DIJOFS_RY;
data[5].pguid = &guids::GUID_RzAxis;
data[5].dwOfs = DIJOFS_RZ;
data[6].pguid = &guids::GUID_Slider;
data[6].dwOfs = DIJOFS_SLIDER(0);
data[7].pguid = &guids::GUID_Slider;
data[7].dwOfs = DIJOFS_SLIDER(1);
for (int i = 0; i < 8; ++i)
{
data[i].dwType = axisType;
data[i].dwFlags = DIDOI_ASPECTPOSITION;
}
for (int i = 0; i < 4; ++i)
{
data[8 + i].pguid = &guids::GUID_POV;
data[8 + i].dwOfs = static_cast<DWORD>(DIJOFS_POV(i));
data[8 + i].dwType = povType;
data[8 + i].dwFlags = 0;
}
for (int i = 0; i < sf::Joystick::ButtonCount; ++i)
{
data[8 + 4 + i].pguid = NULL;
data[8 + 4 + i].dwOfs = static_cast<DWORD>(DIJOFS_BUTTON(i));
data[8 + 4 + i].dwType = buttonType;
data[8 + 4 + i].dwFlags = 0;
}
format.dwSize = sizeof(DIDATAFORMAT);
format.dwObjSize = sizeof(DIOBJECTDATAFORMAT);
format.dwFlags = DIDFT_ABSAXIS;
format.dwDataSize = sizeof(DIJOYSTATE);
format.dwNumObjs = 8 + 4 + sf::Joystick::ButtonCount;
format.rgodf = data;
formatInitialized = true;
}
// Set device data format
result = m_device->SetDataFormat(&format);
if (result)
{
err() << "Failed to set DirectInput device data format: " << result << std::endl;
m_device->Release();
m_device = NULL;
return false;
}
// Get device capabilities
result = m_device->GetCapabilities(&m_deviceCaps);
if (result)
{
err() << "Failed to get DirectInput device capabilities: " << result << std::endl;
m_device->Release();
m_device = NULL;
return false;
}
// Set axis mode to absolute
DIPROPDWORD property;
std::memset(&property, 0, sizeof(property));
property.diph.dwSize = sizeof(property);
property.diph.dwHeaderSize = sizeof(property.diph);
property.diph.dwHow = DIPH_DEVICE;
property.dwData = DIPROPAXISMODE_ABS;
result = m_device->SetProperty(DIPROP_AXISMODE, &property.diph);
if (result)
{
err() << "Failed to set DirectInput device axis mode: " << result << std::endl;
m_device->Release();
m_device = NULL;
return false;
}
// Enumerate device objects (axes/povs/buttons)
result = m_device->EnumObjects(&JoystickImpl::deviceObjectEnumerationCallback, this, DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV);
if (result)
{
err() << "Failed to enumerate DirectInput device objects: " << result << std::endl;
m_device->Release();
m_device = NULL;
return false;
}
// Get friendly product name of the device
DIPROPSTRING stringProperty;
std::memset(&stringProperty, 0, sizeof(stringProperty));
stringProperty.diph.dwSize = sizeof(stringProperty);
stringProperty.diph.dwHeaderSize = sizeof(stringProperty.diph);
stringProperty.diph.dwHow = DIPH_DEVICE;
stringProperty.diph.dwObj = 0;
if (!m_device->GetProperty(DIPROP_PRODUCTNAME, &stringProperty.diph))
{
m_identification.name = stringProperty.wsz;
}
// Get vendor and produce id of the device
std::memset(&property, 0, sizeof(property));
property.diph.dwSize = sizeof(property);
property.diph.dwHeaderSize = sizeof(property.diph);
property.diph.dwHow = DIPH_DEVICE;
if (!m_device->GetProperty(DIPROP_VIDPID, &property.diph))
{
m_identification.productId = HIWORD(property.dwData);
m_identification.vendorId = LOWORD(property.dwData);
}
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////
void JoystickImpl::closeDInput()
{
if (m_device)
{
// Release the device
m_device->Release();
m_device = NULL;
}
}
////////////////////////////////////////////////////////////
JoystickCaps JoystickImpl::getCapabilitiesDInput() const
{
JoystickCaps caps;
// Count how many buttons have valid offsets
caps.buttonCount = 0;
for (int i = 0; i < Joystick::ButtonCount; ++i)
{
if (m_buttons[i] != -1)
++caps.buttonCount;
}
// Check which axes have valid offsets
for (int i = 0; i < Joystick::AxisCount; ++i)
caps.axes[i] = (m_axes[i] != -1);
return caps;
}
////////////////////////////////////////////////////////////
JoystickState JoystickImpl::updateDInput()
{
JoystickState state;
if (m_device)
{
// Poll the device
m_device->Poll();
DIJOYSTATE joystate;
// Try to get the device state
HRESULT result = m_device->GetDeviceState(sizeof(joystate), &joystate);
// If we have not acquired or have lost the device, attempt to (re-)acquire it and get the device state again
if ((result == DIERR_NOTACQUIRED) || (result == DIERR_INPUTLOST))
{
m_device->Acquire();
m_device->Poll();
result = m_device->GetDeviceState(sizeof(joystate), &joystate);
}
// If we still can't get the device state, assume it has been disconnected
if ((result == DIERR_NOTACQUIRED) || (result == DIERR_INPUTLOST))
{
m_device->Release();
m_device = NULL;
return state;
}
if (result)
{
err() << "Failed to get DirectInput device state: " << result << std::endl;
return state;
}
// Get the current state of each axis
for (int i = 0; i < Joystick::AxisCount; ++i)
{
if (m_axes[i] != -1)
{
if (i == Joystick::PovX)
{
unsigned short value = LOWORD(*reinterpret_cast<const DWORD*>(reinterpret_cast<const char*>(&joystate) + m_axes[i]));
if (value != 0xFFFF)
{
float angle = (static_cast<float>(value)) * 3.141592654f / DI_DEGREES / 180.f;
state.axes[i] = std::sin(angle) * 100.f;
}
else
{
state.axes[i] = 0;
}
}
else if (i == Joystick::PovY)
{
unsigned short value = LOWORD(*reinterpret_cast<const DWORD*>(reinterpret_cast<const char*>(&joystate) + m_axes[i]));
if (value != 0xFFFF)
{
float angle = (static_cast<float>(value)) * 3.141592654f / DI_DEGREES / 180.f;
state.axes[i] = std::cos(angle) * 100.f;
}
else
{
state.axes[i] = 0.f;
}
}
else
{
state.axes[i] = (static_cast<float>(*reinterpret_cast<const LONG*>(reinterpret_cast<const char*>(&joystate) + m_axes[i])) + 0.5f) * 100.f / 32767.5f;
}
}
else
{
state.axes[i] = 0.f;
}
}
// Get the current state of each button
for (int i = 0; i < Joystick::ButtonCount; ++i)
{
if (m_buttons[i] != -1)
{
BYTE value = *reinterpret_cast<const BYTE*>(reinterpret_cast<const char*>(&joystate) + m_buttons[i]);
state.buttons[i] = ((value & 0x80) != 0);
}
else
{
state.buttons[i] = false;
}
}
state.connected = true;
}
return state;
}
////////////////////////////////////////////////////////////
BOOL CALLBACK JoystickImpl::deviceEnumerationCallback(const DIDEVICEINSTANCE* deviceInstance, void*)
{
for (std::size_t i = 0; i < joystickList.size(); ++i)
{
if (joystickList[i].guid == deviceInstance->guidInstance)
{
joystickList[i].plugged = true;
return DIENUM_CONTINUE;
}
}
JoystickRecord record = { deviceInstance->guidInstance, sf::Joystick::Count, true };
joystickList.push_back(record);
return DIENUM_CONTINUE;
}
////////////////////////////////////////////////////////////
BOOL CALLBACK JoystickImpl::deviceObjectEnumerationCallback(const DIDEVICEOBJECTINSTANCE* deviceObjectInstance, void* userData)
{
sf::priv::JoystickImpl& joystick = *reinterpret_cast<sf::priv::JoystickImpl*>(userData);
if (DIDFT_GETTYPE(deviceObjectInstance->dwType) & DIDFT_AXIS)
{
// Axes
if (deviceObjectInstance->guidType == guids::GUID_XAxis)
joystick.m_axes[Joystick::X] = DIJOFS_X;
else if (deviceObjectInstance->guidType == guids::GUID_YAxis)
joystick.m_axes[Joystick::Y] = DIJOFS_Y;
else if (deviceObjectInstance->guidType == guids::GUID_ZAxis)
joystick.m_axes[Joystick::Z] = DIJOFS_Z;
else if (deviceObjectInstance->guidType == guids::GUID_RzAxis)
joystick.m_axes[Joystick::R] = DIJOFS_RZ;
else if (deviceObjectInstance->guidType == guids::GUID_RxAxis)
joystick.m_axes[Joystick::U] = DIJOFS_RX;
else if (deviceObjectInstance->guidType == guids::GUID_RyAxis)
joystick.m_axes[Joystick::V] = DIJOFS_RY;
else if (deviceObjectInstance->guidType == guids::GUID_Slider)
{
if(joystick.m_axes[Joystick::U] == -1)
joystick.m_axes[Joystick::U] = DIJOFS_SLIDER(0);
else
joystick.m_axes[Joystick::V] = DIJOFS_SLIDER(1);
}
else
return DIENUM_CONTINUE;
// Set the axis' value range to that of a signed short: [-32768, 32767]
DIPROPRANGE propertyRange;
std::memset(&propertyRange, 0, sizeof(propertyRange));
propertyRange.diph.dwSize = sizeof(propertyRange);
propertyRange.diph.dwHeaderSize = sizeof(propertyRange.diph);
propertyRange.diph.dwObj = deviceObjectInstance->dwType;
propertyRange.diph.dwHow = DIPH_BYID;
propertyRange.lMin = -32768;
propertyRange.lMax = 32767;
HRESULT result = joystick.m_device->SetProperty(DIPROP_RANGE, &propertyRange.diph);
if (result)
err() << "Failed to set DirectInput device axis property range: " << result << std::endl;
return DIENUM_CONTINUE;
}
else if (DIDFT_GETTYPE(deviceObjectInstance->dwType) & DIDFT_POV)
{
// POVs
if (deviceObjectInstance->guidType == guids::GUID_POV)
{
if (joystick.m_axes[Joystick::PovX] == -1)
{
joystick.m_axes[Joystick::PovX] = DIJOFS_POV(0);
joystick.m_axes[Joystick::PovY] = DIJOFS_POV(0);
}
}
return DIENUM_CONTINUE;
}
else if (DIDFT_GETTYPE(deviceObjectInstance->dwType) & DIDFT_BUTTON)
{
// Buttons
for (int i = 0; i < Joystick::ButtonCount; ++i)
{
if (joystick.m_buttons[i] == -1)
{
joystick.m_buttons[i] = DIJOFS_BUTTON(i);
break;
}
}
return DIENUM_CONTINUE;
}
return DIENUM_CONTINUE;
}
} // namespace priv } // namespace priv
} // namespace sf } // namespace sf

View File

@ -34,13 +34,15 @@
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINDOWS 0x0501 #define _WIN32_WINDOWS 0x0501
#define _WIN32_WINNT 0x0501 #define _WIN32_WINNT 0x0501
#define DIRECTINPUT_VERSION 0x0800
#include <SFML/Window/Joystick.hpp> #include <SFML/Window/Joystick.hpp>
#include <SFML/Window/JoystickImpl.hpp> #include <SFML/Window/JoystickImpl.hpp>
#include <SFML/System/String.hpp> #include <SFML/System/String.hpp>
#include <windows.h> #include <windows.h>
#include <mmsystem.h> #include <mmsystem.h>
#include <dinput.h>
namespace sf namespace sf
@ -131,14 +133,100 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
JoystickState update(); JoystickState update();
////////////////////////////////////////////////////////////
/// \brief Perform the global initialization of the joystick module (DInput)
///
////////////////////////////////////////////////////////////
static void initializeDInput();
////////////////////////////////////////////////////////////
/// \brief Perform the global cleanup of the joystick module (DInput)
///
////////////////////////////////////////////////////////////
static void cleanupDInput();
////////////////////////////////////////////////////////////
/// \brief Check if a joystick is currently connected (DInput)
///
/// \param index Index of the joystick to check
///
/// \return True if the joystick is connected, false otherwise
///
////////////////////////////////////////////////////////////
static bool isConnectedDInput(unsigned int index);
////////////////////////////////////////////////////////////
/// \brief Update the connection status of all joysticks (DInput)
///
////////////////////////////////////////////////////////////
static void updateConnectionsDInput();
////////////////////////////////////////////////////////////
/// \brief Open the joystick (DInput)
///
/// \param index Index assigned to the joystick
///
/// \return True on success, false on failure
///
////////////////////////////////////////////////////////////
bool openDInput(unsigned int index);
////////////////////////////////////////////////////////////
/// \brief Close the joystick (DInput)
///
////////////////////////////////////////////////////////////
void closeDInput();
////////////////////////////////////////////////////////////
/// \brief Get the joystick capabilities (DInput)
///
/// \return Joystick capabilities
///
////////////////////////////////////////////////////////////
JoystickCaps getCapabilitiesDInput() const;
////////////////////////////////////////////////////////////
/// \brief Update the joystick and get its new state (DInput)
///
/// \return Joystick state
///
////////////////////////////////////////////////////////////
JoystickState updateDInput();
private: private:
////////////////////////////////////////////////////////////
/// \brief Device enumeration callback function passed to EnumDevices in updateConnections
///
/// \param deviceInstance Device object instance
/// \param userData User data (unused)
///
/// \return DIENUM_CONTINUE to continue enumerating devices or DIENUM_STOP to stop
///
////////////////////////////////////////////////////////////
static BOOL CALLBACK deviceEnumerationCallback(const DIDEVICEINSTANCE* deviceInstance, void* userData);
////////////////////////////////////////////////////////////
/// \brief Device object enumeration callback function passed to EnumObjects in open
///
/// \param deviceObjectInstance Device object instance
/// \param userData User data (pointer to our JoystickImpl object)
///
/// \return DIENUM_CONTINUE to continue enumerating objects or DIENUM_STOP to stop
///
////////////////////////////////////////////////////////////
static BOOL CALLBACK deviceObjectEnumerationCallback(const DIDEVICEOBJECTINSTANCE* deviceObjectInstance, void* userData);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
unsigned int m_index; ///< Index of the joystick unsigned int m_index; ///< Index of the joystick
JOYCAPS m_caps; ///< Joystick capabilities JOYCAPS m_caps; ///< Joystick capabilities
Joystick::Identification m_identification; ///< Joystick identification IDirectInputDevice8W* m_device; ///< DirectInput 8.x device
DIDEVCAPS m_deviceCaps; ///< DirectInput device capabilities
int m_axes[Joystick::AxisCount]; ///< Offsets to the bytes containing the axes states, -1 if not available
int m_buttons[Joystick::ButtonCount]; ///< Offsets to the bytes containing the button states, -1 if not available
Joystick::Identification m_identification; ///< Joystick identification
}; };
} // namespace priv } // namespace priv

View File

@ -39,6 +39,7 @@
#include <GL/gl.h> #include <GL/gl.h>
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
#include <SFML/System/Utf.hpp> #include <SFML/System/Utf.hpp>
#include <Dbt.h>
#include <vector> #include <vector>
#include <cstring> #include <cstring>
@ -56,11 +57,6 @@
#define MAPVK_VK_TO_VSC (0) #define MAPVK_VK_TO_VSC (0)
#endif #endif
// Avoid including <Dbt.h> just for one define
#ifndef DBT_DEVNODES_CHANGED
#define DBT_DEVNODES_CHANGED 0x0007
#endif
namespace namespace
{ {
unsigned int windowCount = 0; // Windows owned by SFML unsigned int windowCount = 0; // Windows owned by SFML
@ -216,6 +212,10 @@ m_cursorGrabbed (m_fullscreen)
// Create the window // Create the window
m_handle = CreateWindowW(className, title.toWideString().c_str(), win32Style, left, top, width, height, NULL, NULL, GetModuleHandle(NULL), this); m_handle = CreateWindowW(className, title.toWideString().c_str(), win32Style, left, top, width, height, NULL, NULL, GetModuleHandle(NULL), this);
// Register to receive device interface change notifications (used for joystick connection handling)
DEV_BROADCAST_HDR deviceBroadcastHeader = {sizeof(DEV_BROADCAST_HDR), DBT_DEVTYP_DEVICEINTERFACE, 0};
RegisterDeviceNotification(m_handle, &deviceBroadcastHeader, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
// If we're the first window handle, we only need to poll for joysticks when WM_DEVICECHANGE message is received // If we're the first window handle, we only need to poll for joysticks when WM_DEVICECHANGE message is received
if (m_handle) if (m_handle)
{ {
@ -983,8 +983,15 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam)
case WM_DEVICECHANGE: case WM_DEVICECHANGE:
{ {
// Some sort of device change has happened, update joystick connections // Some sort of device change has happened, update joystick connections
if (wParam == DBT_DEVNODES_CHANGED) if ((wParam == DBT_DEVICEARRIVAL) || (wParam == DBT_DEVICEREMOVECOMPLETE))
JoystickImpl::updateConnections(); {
// Some sort of device change has happened, update joystick connections if it is a device interface
DEV_BROADCAST_HDR* deviceBroadcastHeader = reinterpret_cast<DEV_BROADCAST_HDR*>(lParam);
if (deviceBroadcastHeader && (deviceBroadcastHeader->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE))
JoystickImpl::updateConnections();
}
break; break;
} }
} }