Browse Source

device: various button/axis mapping fixes in Windows raw impl

Now uses a table like on Linux with the various mappings.
rdb 6 years ago
parent
commit
0ef9f9bef5
1 changed files with 90 additions and 48 deletions
  1. 90 48
      panda/src/device/winRawInputDevice.cxx

+ 90 - 48
panda/src/device/winRawInputDevice.cxx

@@ -14,6 +14,7 @@
 #include "winRawInputDevice.h"
 #include "gamepadButton.h"
 #include "mouseButton.h"
+#include "buttonRegistry.h"
 
 #if defined(_WIN32) && !defined(CPPPARSER)
 
@@ -21,6 +22,49 @@
 #include <devpkey.h>
 #include "phidsdi.h"
 
+enum QuirkBits : int {
+  // Has no trigger axes.
+  QB_no_analog_triggers = 1,
+
+  // Throttle goes from -1 to 1 rather than from 0 to 1.
+  QB_centered_throttle = 2,
+
+  // Throttle is reversed.
+  QB_reversed_throttle = 4,
+};
+
+// Some nonstandard gamepads have different button mappings.
+static const struct DeviceMapping {
+  unsigned short vendor;
+  unsigned short product;
+  InputDevice::DeviceClass device_class;
+  int quirks;
+  const char *buttons[16];
+} mapping_presets[] = {
+  // SNES-style USB gamepad
+  {0x0810, 0xe501, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers,
+    {"face_x", "face_a", "face_b", "face_y", "lshoulder", "rshoulder", "none", "none", "back", "start"}
+  },
+  // SPEED Link SL-6535-SBK-01
+  {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers,
+    {"face_y", "face_b", "face_a", "face_x", "lshoulder", "rshoulder", "ltrigger", "rtrigger", "back", "start", "lstick", "rstick"}
+  },
+  // T.Flight Hotas X
+  {0x044f, 0xb108, InputDevice::DeviceClass::flight_stick, QB_centered_throttle | QB_reversed_throttle,
+    {0}
+  },
+  // NVIDIA Shield Controller
+  {0x0955, 0x7214, InputDevice::DeviceClass::gamepad, 0,
+    {"face_a", "face_b", "n", "face_x", "face_y", "rshoulder", "lshoulder", "rshoulder", "e", "f", "g", "start", "h", "lstick", "rstick", "i"}
+  },
+  {0},
+};
+
+// This is our fallback button mapping, used with Xbox 360 and other devices.
+static const char *default_gamepad_mapping[16] = {
+  "face_a", "face_b", "face_x", "face_y", "lshoulder", "rshoulder", "back", "start", "lstick", "rstick"
+};
+
 static pHidP_GetCaps _HidP_GetCaps = nullptr;
 static pHidP_GetButtonCaps _HidP_GetButtonCaps = nullptr;
 static pHidP_GetValueCaps _HidP_GetValueCaps = nullptr;
@@ -102,6 +146,9 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
 
   _name = std::move(name);
 
+  int quirks = 0;
+  const char *const *gamepad_buttons = default_gamepad_mapping;
+
   switch (info.dwType) {
   case RIM_TYPEMOUSE:
     _device_class = DeviceClass::mouse;
@@ -120,7 +167,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
         info.hid.usUsage == HID_USAGE_GENERIC_GAMEPAD) {
       _device_class = DeviceClass::gamepad;
 
-    // Flight sticks
+    // Various game controllers, incl. flight sticks and some gamepads
     } else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
                info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) {
       _device_class = DeviceClass::flight_stick;
@@ -129,11 +176,6 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
         // Well, it claims to be a gamepad...
         _device_class = DeviceClass::gamepad;
       }
-      //TODO: better solution for this
-      if (_vendor_id == 0x0079 && _product_id == 0x0006) {
-        // Trust GXT 24
-        _device_class = DeviceClass::gamepad;
-      }
 
     // Mice
     } else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
@@ -162,6 +204,28 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
     return false;
   }
 
+  if (_device_class == DeviceClass::gamepad ||
+      _device_class == DeviceClass::flight_stick) {
+    // Do we have a built-in mapping?
+    const DeviceMapping *mapping = mapping_presets;
+    while (mapping->vendor != 0) {
+      if (info.hid.dwVendorId == mapping->vendor &&
+          info.hid.dwProductId == mapping->product) {
+        _device_class = mapping->device_class;
+        gamepad_buttons = mapping->buttons;
+        quirks = mapping->quirks;
+        if (device_cat.is_debug()) {
+          device_cat.debug()
+            << "Using preset mapping for " << mapping->device_class
+            << " with VID=" << std::hex << mapping->vendor
+            << " PID=" << mapping->product << std::dec << "\n";
+        }
+        break;
+      }
+      ++mapping;
+    }
+  }
+
   // Initialize hid.dll, which provides the HID parser functions.
   static bool hid_initialized = false;
   if (!hid_initialized) {
@@ -198,38 +262,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
       << caps.NumberInputValueCaps << " value caps\n";
   }
 
-  // Do we have a button mapping?
-  static const ButtonHandle gamepad_buttons_common[] = {
-    ButtonHandle::none(),
-    GamepadButton::face_a(),
-    GamepadButton::face_b(),
-    GamepadButton::face_x(),
-    GamepadButton::face_y(),
-    GamepadButton::lshoulder(),
-    GamepadButton::rshoulder(),
-    GamepadButton::start(),
-    GamepadButton::back(),
-    GamepadButton::lstick(),
-    GamepadButton::rstick(),
-  };
-  const ButtonHandle *gamepad_buttons = gamepad_buttons_common;
-  if (_vendor_id == 0x0810 && _product_id == 0xe501) {
-    // SNES-style USB gamepad
-    static const ButtonHandle gamepad_buttons_snes[] = {
-      ButtonHandle::none(),
-      GamepadButton::face_x(),
-      GamepadButton::face_a(),
-      GamepadButton::face_b(),
-      GamepadButton::face_y(),
-      GamepadButton::lshoulder(),
-      GamepadButton::rshoulder(),
-      ButtonHandle::none(),
-      ButtonHandle::none(),
-      GamepadButton::back(),
-      GamepadButton::start(),
-    };
-    gamepad_buttons = gamepad_buttons_snes;
-  }
+  ButtonRegistry *registry = ButtonRegistry::ptr();
 
   // Prepare a mapping of data indices to button/axis indices.
   _indices.resize(caps.NumberInputDataIndices);
@@ -270,7 +303,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
       }
     }
 
-    nassertd(cap.Range.DataIndexMin + upper < _indices.size()) continue;
+    nassertd(cap.Range.DataIndexMin + upper < (int)_indices.size()) continue;
 
     // Windows will only tell us which buttons in a report are "on", so we
     // need to keep track of which buttons exist in which report so that we
@@ -285,8 +318,10 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
       switch (cap.UsagePage) {
       case HID_USAGE_PAGE_BUTTON:
         if (_device_class == DeviceClass::gamepad) {
-          if (usage < sizeof(gamepad_buttons_common) / sizeof(ButtonHandle)) {
-            handle = gamepad_buttons[usage];
+          if (usage > 0 && usage - 1 < _countof(default_gamepad_mapping)) {
+            if (gamepad_buttons[usage - 1] != nullptr) {
+              handle = registry->find_button(gamepad_buttons[usage - 1]);
+            }
           }
         } else if (_device_class == DeviceClass::flight_stick) {
           if (usage > 0) {
@@ -347,7 +382,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
       }
     }
 
-    nassertd(cap.Range.DataIndexMin + upper < _indices.size()) continue;
+    nassertd(cap.Range.DataIndexMin + upper < (int)_indices.size()) continue;
 
     for (int j = 0; j <= upper; ++j) {
       USAGE usage = j + cap.Range.UsageMin;
@@ -386,9 +421,17 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
           break;
         case HID_USAGE_GENERIC_Z:
           if (_device_class == DeviceClass::gamepad) {
-            axis = Axis::left_trigger;
+            if ((quirks & QB_no_analog_triggers) == 0) {
+              axis = Axis::left_trigger;
+            }
           } else if (_device_class == DeviceClass::flight_stick) {
             axis = Axis::throttle;
+            if ((quirks & QB_reversed_throttle) != 0) {
+              std::swap(cap.LogicalMin, cap.LogicalMax);
+            }
+            if ((quirks & QB_centered_throttle) != 0) {
+              is_signed = false;
+            }
           } else {
             axis = Axis::z;
             swap(cap.LogicalMin, cap.LogicalMax);
@@ -411,7 +454,9 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
           break;
         case HID_USAGE_GENERIC_RZ:
           if (_device_class == DeviceClass::gamepad) {
-            axis = Axis::right_trigger;
+            if ((quirks & QB_no_analog_triggers) == 0) {
+              axis = Axis::right_trigger;
+            }
           } else {
             // Flip to match Panda's convention for heading.
             axis = Axis::yaw;
@@ -436,10 +481,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
       }
 
       int axis_index;
-      if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) {
-        // T.Flight Hotas X throttle is reversed and can go backwards.
-        axis_index = add_axis(axis, cap.LogicalMax, cap.LogicalMin, true);
-      } else if (!is_signed) {
+      if (!is_signed) {
         // All axes on the weird XInput-style mappings go from -1 to 1
         axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax, true);
       } else {