XInput gamepad control states, battery information, HID properties

This commit is contained in:
rdb 2015-12-12 22:07:43 +01:00
parent 35add447e6
commit dd8344455c
7 changed files with 127 additions and 24 deletions

View File

@ -42,6 +42,7 @@ typedef unsigned long DWORD;
typedef unsigned short WORD; typedef unsigned short WORD;
typedef long LONG; typedef long LONG;
typedef long UINT; typedef long UINT;
typedef unsigned char BYTE;
typedef unsigned long ULONG; typedef unsigned long ULONG;
typedef long long LONGLONG; typedef long long LONGLONG;
typedef long HRESULT; typedef long HRESULT;

View File

@ -578,6 +578,7 @@ if (COMPILER == "MSVC"):
LibName("WINGDI", "gdi32.lib") LibName("WINGDI", "gdi32.lib")
LibName("ADVAPI", "advapi32.lib") LibName("ADVAPI", "advapi32.lib")
LibName("IPHLPAPI", "iphlpapi.lib") LibName("IPHLPAPI", "iphlpapi.lib")
LibName("SETUPAPI", "setupapi.lib")
LibName("GL", "opengl32.lib") LibName("GL", "opengl32.lib")
LibName("GLES", "libgles_cm.lib") LibName("GLES", "libgles_cm.lib")
LibName("GLES2", "libGLESv2.lib") LibName("GLES2", "libGLESv2.lib")
@ -3849,8 +3850,8 @@ if (not RUNTIME):
# #
if (not RUNTIME): if (not RUNTIME):
OPTS=['DIR:panda/metalibs/panda', 'BUILDING:PANDA', 'JPEG', 'PNG', OPTS=['DIR:panda/metalibs/panda', 'BUILDING:PANDA', 'JPEG', 'PNG', 'TIFF',
'TIFF', 'ZLIB', 'OPENSSL', 'FREETYPE', 'FFTW', 'ADVAPI', 'WINSOCK2', 'ZLIB', 'OPENSSL', 'FREETYPE', 'FFTW', 'ADVAPI', 'WINSOCK2', 'SETUPAPI',
'SQUISH', 'NVIDIACG', 'VORBIS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI'] 'SQUISH', 'NVIDIACG', 'VORBIS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI']
TargetAdd('panda_panda.obj', opts=OPTS, input='panda.cxx') TargetAdd('panda_panda.obj', opts=OPTS, input='panda.cxx')

View File

@ -21,6 +21,8 @@ INLINE InputDevice::
InputDevice() : InputDevice() :
_flags(0), _flags(0),
_device_class(DC_unknown), _device_class(DC_unknown),
_vendor_id(0),
_product_id(0),
_is_connected(false), _is_connected(false),
_event_sequence(0), _event_sequence(0),
_enable_pointer_events(false), _enable_pointer_events(false),

View File

@ -32,6 +32,8 @@ InputDevice(const string &name, DeviceClass dev_class, int flags) :
_name(name), _name(name),
_flags(flags), _flags(flags),
_device_class(dev_class), _device_class(dev_class),
_vendor_id(0),
_product_id(0),
_is_connected(true), _is_connected(true),
_event_sequence(0), _event_sequence(0),
_enable_pointer_events(false), _enable_pointer_events(false),
@ -48,7 +50,7 @@ InputDevice(const string &name, DeviceClass dev_class, int flags) :
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
InputDevice:: InputDevice::
InputDevice(const InputDevice &copy) { InputDevice(const InputDevice &copy) {
*this = copy; nassertv(false);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -58,16 +60,7 @@ InputDevice(const InputDevice &copy) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void InputDevice:: void InputDevice::
operator = (const InputDevice &copy) { operator = (const InputDevice &copy) {
LightMutexHolder holder(_lock); nassertv(false);
LightMutexHolder holder1(copy._lock);
_name = copy._name;
_flags = copy._flags;
_is_connected = copy._is_connected;
_event_sequence = copy._event_sequence;
_enable_pointer_events = copy._enable_pointer_events;
_pointer_data = copy._pointer_data;
_button_events = copy._button_events;
_pointer_events = copy._pointer_events;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -315,10 +308,9 @@ output(ostream &out) const {
if (_battery_level > 0 && _max_battery_level > 0) { if (_battery_level > 0 && _max_battery_level > 0) {
out << " ["; out << " [";
short i = 0; short i = 0;
for (; i < _battery_level - 1; ++i) { for (; i < _battery_level; ++i) {
out << '='; out << '=';
} }
out << '/';
for (; i < _max_battery_level; ++i) { for (; i < _max_battery_level; ++i) {
out << ' '; out << ' ';
} }

View File

@ -206,9 +206,13 @@ protected:
LightMutex _lock; LightMutex _lock;
string _name; string _name;
string _serial_number;
string _manufacturer;
DeviceClass _device_class; DeviceClass _device_class;
int _flags; int _flags;
int _event_sequence; int _event_sequence;
short _vendor_id;
short _product_id;
bool _is_connected; bool _is_connected;
bool _enable_pointer_events; bool _enable_pointer_events;
PointerData _pointer_data; PointerData _pointer_data;

View File

@ -143,7 +143,7 @@ add_device(InputDevice *device) {
LightMutexHolder holder(_lock); LightMutexHolder holder(_lock);
_all_devices.push_back(device); _all_devices.push_back(device);
} }
throw_event("device-added", device); throw_event("connect-device", device);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -163,7 +163,7 @@ remove_device(InputDevice *device) {
_all_devices.erase(it); _all_devices.erase(it);
} }
throw_event("device-removed", device); throw_event("disconnect-device", device);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -220,7 +220,7 @@ poll() {
device_cat.info() device_cat.info()
<< "Removed input device " << *device << "\n"; << "Removed input device " << *device << "\n";
} }
throw_event("device-removed", device.p()); throw_event("disconnect-device", device.p());
} }
} else if (event->mask & (IN_CREATE | IN_ATTRIB)) { } else if (event->mask & (IN_CREATE | IN_ATTRIB)) {

View File

@ -18,6 +18,7 @@
#include "gamepadButton.h" #include "gamepadButton.h"
#include <XInput.h> #include <XInput.h>
#include <CfgMgr32.h>
#ifndef XUSER_MAX_COUNT #ifndef XUSER_MAX_COUNT
#define XUSER_MAX_COUNT 4 #define XUSER_MAX_COUNT 4
@ -27,13 +28,47 @@
#define XINPUT_CAPS_FFB_SUPPORTED 0x0001 #define XINPUT_CAPS_FFB_SUPPORTED 0x0001
#endif #endif
#ifndef BATTERY_DEVTYPE_GAMEPAD
#define BATTERY_DEVTYPE_GAMEPAD 0x00
#endif
#ifndef BATTERY_TYPE_DISCONNECTED
#define BATTERY_TYPE_DISCONNECTED 0x00
#endif
#ifndef BATTERY_TYPE_WIRED
#define BATTERY_TYPE_WIRED 0x01
#endif
#ifndef BATTERY_LEVEL_FULL
#define BATTERY_LEVEL_FULL 0x03
#endif
typedef struct _XINPUT_BATTERY_INFORMATION {
BYTE BatteryType;
BYTE BatteryLevel;
} XINPUT_BATTERY_INFORMATION;
// Undocumented, I figured out how this looks by trial and error.
struct XINPUT_BUSINFO {
WORD VendorID;
WORD ProductID;
WORD RevisionID;
WORD Unknown1; // Unknown - padding?
DWORD InstanceID;
DWORD Unknown2;
//WORD Unknown3;
};
typedef DWORD (*pXInputGetState)(DWORD, XINPUT_STATE *); typedef DWORD (*pXInputGetState)(DWORD, XINPUT_STATE *);
typedef DWORD (*pXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *); typedef DWORD (*pXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *);
//typedef DWORD (*pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *); typedef DWORD (*pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *);
typedef DWORD (*pXInputGetBaseBusInformation)(DWORD, XINPUT_BUSINFO *);
static pXInputGetState get_state = NULL; static pXInputGetState get_state = NULL;
static pXInputGetCapabilities get_capabilities = NULL; static pXInputGetCapabilities get_capabilities = NULL;
//static pXInputGetBatteryInformation get_battery_information = NULL; static pXInputGetBatteryInformation get_battery_information = NULL;
static pXInputGetBaseBusInformation get_base_bus_information = NULL;
bool XInputDevice::_initialized = false; bool XInputDevice::_initialized = false;
@ -73,10 +108,9 @@ XInputDevice(DWORD user_index) :
} }
} }
#if 0
if (get_battery_information != NULL) { if (get_battery_information != NULL) {
XINPUT_BATTERY_INFORMATION batt; XINPUT_BATTERY_INFORMATION batt;
if (XInputGetBatteryInformation(_index, BATTERY_DEVTYPE_GAMEPAD, &batt) == ERROR_SUCCESS) { if (get_battery_information(_index, 0, &batt) == ERROR_SUCCESS) {
if (batt.BatteryType == BATTERY_TYPE_DISCONNECTED) { if (batt.BatteryType == BATTERY_TYPE_DISCONNECTED) {
_is_connected = false; _is_connected = false;
@ -89,8 +123,52 @@ XInputDevice(DWORD user_index) :
} }
} }
} }
#endif
// Get information about the USB device.
// This is not documented at all. I'm probably the first to try this.
XINPUT_BUSINFO businfo;
if (get_base_bus_information != NULL &&
get_base_bus_information(0, &businfo) == ERROR_SUCCESS) {
_vendor_id = businfo.VendorID;
_product_id = businfo.ProductID;
{
// Reformat the serial number into its original hex string form.
char sn[10];
sprintf(sn, "%08X", businfo.InstanceID);
_serial_number.assign(sn, 8);
}
// Get information about the device from Windows. For that, we'll
// first need to construct the device path. Fortunately, we now have
// enough information to do so.
char path[32];
sprintf(path, "USB\\VID_%04X&PID_%04X\\%08X", businfo.VendorID, businfo.ProductID, businfo.InstanceID);
DEVINST inst;
if (CM_Locate_DevNodeA(&inst, path, 0) != 0) {
if (device_cat.is_debug()) {
device_cat.debug()
<< "Could not locate device node " << path << "\n";
}
} else {
// Get the device properties we need.
char buffer[4096];
ULONG buflen = 4096;
if (CM_Get_DevNode_Registry_Property(inst, CM_DRP_DEVICEDESC, 0, buffer, &buflen, 0) == CR_SUCCESS) {
_name.assign(buffer);
}
buflen = 4096;
if (CM_Get_DevNode_Registry_Property(inst, CM_DRP_MFG, 0, buffer, &buflen, 0) == CR_SUCCESS) {
_manufacturer.assign(buffer);
}
}
} else {
// We need something to name it.
_name = "XInput Device";
}
_controls.resize(6);
_buttons.resize(16); _buttons.resize(16);
// Get the initial state. // Get the initial state.
@ -103,6 +181,19 @@ XInputDevice(DWORD user_index) :
WORD buttons = state.Gamepad.wButtons; WORD buttons = state.Gamepad.wButtons;
set_control_map(0, C_left_trigger);
set_control_state(0, state.Gamepad.bLeftTrigger / 255.0);
set_control_map(1, C_right_trigger);
set_control_state(1, state.Gamepad.bRightTrigger / 255.0);
set_control_map(2, C_left_x);
set_control_state(2, state.Gamepad.sThumbLX / 32767.0);
set_control_map(3, C_left_y);
set_control_state(3, state.Gamepad.sThumbLY / 32767.0);
set_control_map(4, C_right_x);
set_control_state(4, state.Gamepad.sThumbRX / 32767.0);
set_control_map(5, C_right_y);
set_control_state(5, state.Gamepad.sThumbRY / 32767.0);
set_button_map(0, GamepadButton::dpad_up()); set_button_map(0, GamepadButton::dpad_up());
set_button_state(0, (buttons & XINPUT_GAMEPAD_DPAD_UP) != 0); set_button_state(0, (buttons & XINPUT_GAMEPAD_DPAD_UP) != 0);
set_button_map(1, GamepadButton::dpad_down()); set_button_map(1, GamepadButton::dpad_down());
@ -146,6 +237,9 @@ bool XInputDevice::
init_xinput() { init_xinput() {
_initialized = true; _initialized = true;
HMODULE module = LoadLibraryA("Xinput1_4.dll"); HMODULE module = LoadLibraryA("Xinput1_4.dll");
if (!module) {
module = LoadLibraryA("Xinput1_3.dll");
}
if (module) { if (module) {
// Undocumented version (XInputGetStateEx) that includes a // Undocumented version (XInputGetStateEx) that includes a
// state bit for the guide button. // state bit for the guide button.
@ -160,11 +254,13 @@ init_xinput() {
} }
get_capabilities = (pXInputGetCapabilities)GetProcAddress(module, "XInputGetCapabilities"); get_capabilities = (pXInputGetCapabilities)GetProcAddress(module, "XInputGetCapabilities");
get_battery_information = (pXInputGetBatteryInformation)GetProcAddress(module, "XInputGetBatteryInformation");
get_base_bus_information = (pXInputGetBaseBusInformation)GetProcAddress(module, MAKEINTRESOURCE(104));
return true; return true;
} }
device_cat.error() device_cat.error()
<< "Failed to load Xinput1_4.dll.\n"; << "Failed to load XInput DLL.\n";
return false; return false;
} }
@ -207,6 +303,13 @@ do_poll() {
} }
} }
set_control_state(0, state.Gamepad.bLeftTrigger / 255.0);
set_control_state(1, state.Gamepad.bRightTrigger / 255.0);
set_control_state(2, state.Gamepad.sThumbLX / 32767.0);
set_control_state(3, state.Gamepad.sThumbLY / 32767.0);
set_control_state(4, state.Gamepad.sThumbRX / 32767.0);
set_control_state(5, state.Gamepad.sThumbRY / 32767.0);
_last_buttons = state.Gamepad.wButtons; _last_buttons = state.Gamepad.wButtons;
_last_packet = state.dwPacketNumber; _last_packet = state.dwPacketNumber;
} }