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 long LONG;
typedef long UINT;
typedef unsigned char BYTE;
typedef unsigned long ULONG;
typedef long long LONGLONG;
typedef long HRESULT;

View File

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

View File

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

View File

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

View File

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

View File

@ -143,7 +143,7 @@ add_device(InputDevice *device) {
LightMutexHolder holder(_lock);
_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);
}
throw_event("device-removed", device);
throw_event("disconnect-device", device);
}
////////////////////////////////////////////////////////////////////
@ -220,7 +220,7 @@ poll() {
device_cat.info()
<< "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)) {

View File

@ -18,6 +18,7 @@
#include "gamepadButton.h"
#include <XInput.h>
#include <CfgMgr32.h>
#ifndef XUSER_MAX_COUNT
#define XUSER_MAX_COUNT 4
@ -27,13 +28,47 @@
#define XINPUT_CAPS_FFB_SUPPORTED 0x0001
#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 (*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 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;
@ -73,10 +108,9 @@ XInputDevice(DWORD user_index) :
}
}
#if 0
if (get_battery_information != NULL) {
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) {
_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);
// Get the initial state.
@ -103,6 +181,19 @@ XInputDevice(DWORD user_index) :
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_state(0, (buttons & XINPUT_GAMEPAD_DPAD_UP) != 0);
set_button_map(1, GamepadButton::dpad_down());
@ -146,6 +237,9 @@ bool XInputDevice::
init_xinput() {
_initialized = true;
HMODULE module = LoadLibraryA("Xinput1_4.dll");
if (!module) {
module = LoadLibraryA("Xinput1_3.dll");
}
if (module) {
// Undocumented version (XInputGetStateEx) that includes a
// state bit for the guide button.
@ -160,11 +254,13 @@ init_xinput() {
}
get_capabilities = (pXInputGetCapabilities)GetProcAddress(module, "XInputGetCapabilities");
get_battery_information = (pXInputGetBatteryInformation)GetProcAddress(module, "XInputGetBatteryInformation");
get_base_bus_information = (pXInputGetBaseBusInformation)GetProcAddress(module, MAKEINTRESOURCE(104));
return true;
}
device_cat.error()
<< "Failed to load Xinput1_4.dll.\n";
<< "Failed to load XInput DLL.\n";
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_packet = state.dwPacketNumber;
}