Browse Source

device: support for Steam Controller on Linux

Adds lgrip and rgrip button handles, which are present on Steam Controller but also on the Oculus Touch
rdb 7 years ago
parent
commit
f3ba1d317c

+ 40 - 3
panda/src/device/evdevInputDevice.cxx

@@ -57,6 +57,9 @@ enum QuirkBits {
 
   // ABS_THROTTLE maps to rudder
   QB_rudder_from_throttle = 16,
+
+  // Special handling for Steam Controller, which has many peculiarities.
+  QB_steam_controller = 32,
 };
 
 static const struct DeviceMapping {
@@ -71,6 +74,10 @@ static const struct DeviceMapping {
   {0x044f, 0xb108, InputDevice::DeviceClass::flight_stick, QB_centered_throttle | QB_reversed_throttle | QB_rudder_from_throttle},
   // Xbox 360 Wireless Controller
   {0x045e, 0x0719, InputDevice::DeviceClass::gamepad, QB_connect_if_nonzero},
+  // Steam Controller (wired)
+  {0x28de, 0x1102, InputDevice::DeviceClass::unknown, QB_steam_controller},
+  // Steam Controller (wireless)
+  {0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller},
   // Jess Tech Colour Rumble Pad
   {0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, 0},
   // Trust GXT 24
@@ -299,6 +306,13 @@ init_device() {
     ++mapping;
   }
 
+  // The Steam Controller reports as multiple devices, one of which a gamepad.
+  if (quirks & QB_steam_controller) {
+    if (test_bit(BTN_GAMEPAD, keys)) {
+      _device_class = DeviceClass::gamepad;
+    }
+  }
+
   // Try to detect which type of device we have here
   if (_device_class == DeviceClass::unknown) {
     int device_scores[(size_t)DeviceClass::spatial_mouse] = {0};
@@ -378,7 +392,7 @@ init_device() {
     for (int i = 0; i <= KEY_MAX; ++i) {
       if (test_bit(i, keys)) {
         ButtonState button;
-        button.handle = map_button(i, _device_class);
+        button.handle = map_button(i, _device_class, quirks);
 
         int button_index = (int)_buttons.size();
         if (button.handle == ButtonHandle::none()) {
@@ -527,6 +541,18 @@ init_device() {
             }
           }
           break;
+        case ABS_HAT2X:
+          if (quirks & QB_steam_controller) {
+            axis = InputDevice::Axis::right_trigger;
+            have_analog_triggers = true;
+          }
+          break;
+        case ABS_HAT2Y:
+          if (quirks & QB_steam_controller) {
+            axis = InputDevice::Axis::left_trigger;
+            have_analog_triggers = true;
+          }
+          break;
         }
 
         // Check the initial value and ranges.
@@ -740,7 +766,7 @@ process_events() {
  * Static function to map an evdev code to a ButtonHandle.
  */
 ButtonHandle EvdevInputDevice::
-map_button(int code, DeviceClass device_class) {
+map_button(int code, DeviceClass device_class, int quirks) {
   if (code >= 0 && code < 0x80) {
     // See linux/input.h for the source of this mapping.
     static const ButtonHandle keyboard_map[] = {
@@ -897,7 +923,11 @@ map_button(int code, DeviceClass device_class) {
     }
 
   } else if ((code & 0xfff0) == BTN_JOYSTICK) {
-    if (device_class == DeviceClass::gamepad) {
+    if (quirks & QB_steam_controller) {
+      // BTN_THUMB and BTN_THUMB2 detect touching the touchpads.
+      return ButtonHandle::none();
+
+    } else if (device_class == DeviceClass::gamepad) {
       // Based on "Jess Tech Colour Rumble Pad"
       static const ButtonHandle mapping[] = {
         GamepadButton::face_x(),
@@ -991,6 +1021,13 @@ map_button(int code, DeviceClass device_class) {
   case BTN_TRIGGER_HAPPY4:
     return GamepadButton::dpad_down();
 
+  // The next two are for the Steam Controller's grip buttons.
+  case BTN_GEAR_DOWN:
+    return GamepadButton::lgrip();
+
+  case BTN_GEAR_UP:
+    return GamepadButton::rgrip();
+
   default:
     return ButtonHandle::none();
   }

+ 3 - 1
panda/src/device/evdevInputDevice.h

@@ -64,7 +64,9 @@ private:
   int _rtrigger_code;
 
 public:
-  static ButtonHandle map_button(int code, DeviceClass device_class = DeviceClass::unknown);
+  static ButtonHandle map_button(int code,
+                                 DeviceClass device_class = DeviceClass::unknown,
+                                 int quirks = 0);
 
 public:
   static TypeHandle get_class_type() {

+ 20 - 5
panda/src/device/linuxJoystickDevice.cxx

@@ -113,6 +113,7 @@ open_device() {
   ioctl(_fd, JSIOCGNAME(sizeof(name)), name);
   _name = name;
 
+  bool emulate_dpad = true;
   bool have_analog_triggers = false;
 
   // Get the number of axes.
@@ -138,6 +139,8 @@ open_device() {
         _device_class = DeviceClass::gamepad;
       } else if (handle == GamepadButton::trigger()) {
         _device_class = DeviceClass::flight_stick;
+      } else if (handle == GamepadButton::dpad_left()) {
+        emulate_dpad = false;
       } else if (handle == GamepadButton::ltrigger()) {
         _ltrigger_button = i;
       } else if (handle == GamepadButton::rtrigger()) {
@@ -220,7 +223,7 @@ open_device() {
         break;
 
       case ABS_HAT0X:
-        if (_dpad_left_button == -1) {
+        if (emulate_dpad) {
           // Emulate D-Pad or hat switch.
           _dpad_x_axis = i;
           _dpad_left_button = (int)_buttons.size();
@@ -236,7 +239,7 @@ open_device() {
         break;
 
       case ABS_HAT0Y:
-        if (_dpad_up_button == -1) {
+        if (emulate_dpad) {
           // Emulate D-Pad.
           _dpad_y_axis = i;
           _dpad_up_button = (int)_buttons.size();
@@ -251,6 +254,18 @@ open_device() {
         }
         break;
 
+      case ABS_HAT2X:
+        if (_device_class == DeviceClass::gamepad) {
+          axis = InputDevice::Axis::right_trigger;
+        }
+        break;
+
+      case ABS_HAT2Y:
+        if (_device_class == DeviceClass::gamepad) {
+          axis = InputDevice::Axis::left_trigger;
+        }
+        break;
+
       default:
         if (device_cat.is_debug()) {
           device_cat.debug() << "Unmapped /dev/input/js" << _index
@@ -278,7 +293,7 @@ open_device() {
 
   if (_ltrigger_button >= 0 && _rtrigger_button >= 0 && !have_analog_triggers) {
     // Emulate analog triggers.
-    _ltrigger_control = (int)_axes.size();
+    _ltrigger_axis = (int)_axes.size();
     add_axis(Axis::left_trigger, 0, 1, false);
     add_axis(Axis::right_trigger, 0, 1, false);
   } else {
@@ -398,9 +413,9 @@ process_events() {
 
     if (events[i].type & JS_EVENT_BUTTON) {
       if (index == _ltrigger_button) {
-        axis_changed(_ltrigger_control, events[i].value);
+        axis_changed(_ltrigger_axis, events[i].value);
       } else if (index == _rtrigger_button) {
-        axis_changed(_ltrigger_control + 1, events[i].value);
+        axis_changed(_ltrigger_axis + 1, events[i].value);
       }
       button_changed(index, (events[i].value != 0));
 

+ 1 - 1
panda/src/device/linuxJoystickDevice.h

@@ -50,7 +50,7 @@ private:
   int _dpad_up_button;
 
   // This is used for axis emulation.
-  int _ltrigger_control;
+  int _ltrigger_axis;
   int _ltrigger_button;
   int _rtrigger_button;
 

+ 4 - 0
panda/src/putil/gamepadButton.cxx

@@ -24,6 +24,8 @@ DEFINE_GAMEPAD_BUTTON_HANDLE(lshoulder)
 DEFINE_GAMEPAD_BUTTON_HANDLE(rshoulder)
 DEFINE_GAMEPAD_BUTTON_HANDLE(ltrigger)
 DEFINE_GAMEPAD_BUTTON_HANDLE(rtrigger)
+DEFINE_GAMEPAD_BUTTON_HANDLE(lgrip)
+DEFINE_GAMEPAD_BUTTON_HANDLE(rgrip)
 
 DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_left)
 DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_right)
@@ -87,6 +89,8 @@ init_gamepad_buttons() {
   ButtonRegistry::ptr()->register_button(_rshoulder, "rshoulder");
   ButtonRegistry::ptr()->register_button(_ltrigger, "ltrigger");
   ButtonRegistry::ptr()->register_button(_rtrigger, "rtrigger");
+  ButtonRegistry::ptr()->register_button(_lgrip, "lgrip");
+  ButtonRegistry::ptr()->register_button(_rgrip, "rgrip");
 
   ButtonRegistry::ptr()->register_button(_dpad_left, "dpad_left");
   ButtonRegistry::ptr()->register_button(_dpad_right, "dpad_right");

+ 2 - 0
panda/src/putil/gamepadButton.h

@@ -30,6 +30,8 @@ PUBLISHED:
   static ButtonHandle rshoulder();
   static ButtonHandle ltrigger();
   static ButtonHandle rtrigger();
+  static ButtonHandle lgrip();
+  static ButtonHandle rgrip();
 
   static ButtonHandle dpad_left();
   static ButtonHandle dpad_right();