Ver código fonte

XInput gamepad control states, battery information, HID properties

rdb 10 anos atrás
pai
commit
dd8344455c

+ 1 - 0
dtool/src/parser-inc/windows.h

@@ -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;

+ 3 - 2
makepanda/makepanda.py

@@ -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')

+ 2 - 0
panda/src/device/inputDevice.I

@@ -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),

+ 5 - 13
panda/src/device/inputDevice.cxx

@@ -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 << ' ';
       }

+ 4 - 0
panda/src/device/inputDevice.h

@@ -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;

+ 3 - 3
panda/src/device/inputDeviceManager.cxx

@@ -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)) {

+ 109 - 6
panda/src/device/xInputDevice.cxx

@@ -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;
 }