Browse Source

Emulate d-pad from hat axes, fixes Xbox One d-pad buttons on Linux

rdb 9 years ago
parent
commit
01e57e1bdd

+ 77 - 49
panda/src/device/evdevInputDevice.cxx

@@ -43,7 +43,11 @@ EvdevInputDevice(int index) :
   _ff_id(-1),
   _ff_playing(false),
   _ff_strong(-1),
-  _ff_weak(-1) {
+  _ff_weak(-1),
+  _dpad_x_axis(-1),
+  _dpad_y_axis(-1),
+  _dpad_left_button(-1),
+  _dpad_up_button(-1) {
 
   char path[64];
   sprintf(path, "/dev/input/event%d", index);
@@ -185,54 +189,7 @@ init_device() {
   }
 
   bool all_values_zero = true;
-  if (test_bit(EV_ABS, evtypes)) {
-    // Check which axes are on the device.
-    uint8_t axes[(ABS_CNT + 7) >> 3];
-    memset(axes, 0, sizeof(axes));
-
-    AxisRange range;
-    range._scale = 1.0;
-    range._bias = 0.0;
-    _axis_ranges.resize(ABS_CNT, range);
-
-    int num_bits = ioctl(_fd, EVIOCGBIT(EV_ABS, sizeof(axes)), axes) << 3;
-    for (int i = 0; i < num_bits; ++i) {
-      if (test_bit(i, axes)) {
-        if (i >= 0 && i < 6) {
-          set_control_map(i, axis_map[i]);
-
-          // Check the initial value and ranges.
-          struct input_absinfo absinfo;
-          if (ioctl(_fd, EVIOCGABS(i), &absinfo) >= 0) {
-            double factor, bias;
-            if (absinfo.minimum < 0) {
-              // Centered, eg. for sticks.
-              factor = 2.0 / (absinfo.maximum - absinfo.minimum);
-              bias = (absinfo.maximum + absinfo.minimum) / (double)(absinfo.minimum - absinfo.maximum);
-            } else {
-              // 0-based, eg. for triggers.
-              factor = 1.0 / absinfo.maximum;
-              bias = 0.0;
-            }
-
-            // Flip Y axis to match Windows implementation.
-            if (i == ABS_Y || i == ABS_RY) {
-              factor = -factor;
-              bias = -bias;
-            }
-
-            _axis_ranges[i]._scale = factor;
-            _axis_ranges[i]._bias = bias;
-            _controls[i]._state = fma(absinfo.value, factor, bias);
-
-            if (absinfo.value != 0) {
-              all_values_zero = false;
-            }
-          }
-        }
-      }
-    }
-  }
+  bool have_dpad_buttons = false;
 
   if (test_bit(EV_KEY, evtypes)) {
     // Check which buttons are on the device.
@@ -255,6 +212,9 @@ init_device() {
         } else {
           _buttons[bi]._state = S_up;
         }
+        if (_buttons[bi]._handle == GamepadButton::dpad_left()) {
+          have_dpad_buttons = true;
+        }
         ++bi;
       }
     }
@@ -281,6 +241,67 @@ init_device() {
     }
   }
 
+  if (test_bit(EV_ABS, evtypes)) {
+    // Check which axes are on the device.
+    uint8_t axes[(ABS_CNT + 7) >> 3];
+    memset(axes, 0, sizeof(axes));
+
+    AxisRange range;
+    range._scale = 1.0;
+    range._bias = 0.0;
+    _axis_ranges.resize(ABS_CNT, range);
+
+    int num_bits = ioctl(_fd, EVIOCGBIT(EV_ABS, sizeof(axes)), axes) << 3;
+    for (int i = 0; i < num_bits; ++i) {
+      if (test_bit(i, axes)) {
+        set_control_map(i, axis_map[i]);
+
+        // Check the initial value and ranges.
+        struct input_absinfo absinfo;
+        if (ioctl(_fd, EVIOCGABS(i), &absinfo) >= 0) {
+          double factor, bias;
+          if (absinfo.minimum < 0) {
+            // Centered, eg. for sticks.
+            factor = 2.0 / (absinfo.maximum - absinfo.minimum);
+            bias = (absinfo.maximum + absinfo.minimum) / (double)(absinfo.minimum - absinfo.maximum);
+          } else {
+            // 0-based, eg. for triggers.
+            factor = 1.0 / absinfo.maximum;
+            bias = 0.0;
+          }
+
+          // Flip Y axis to match Windows implementation.
+          if (i == ABS_Y || i == ABS_RY) {
+            factor = -factor;
+            bias = -bias;
+          }
+
+          _axis_ranges[i]._scale = factor;
+          _axis_ranges[i]._bias = bias;
+          _controls[i]._state = fma(absinfo.value, factor, bias);
+
+          if (absinfo.value != 0) {
+            all_values_zero = false;
+          }
+        }
+
+        // Emulate D-Pad buttons if necessary.
+        if (i == ABS_HAT0X && !have_dpad_buttons) {
+          _dpad_x_axis = i;
+          _dpad_left_button = (int)_buttons.size();
+          _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
+          _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
+        }
+        if (i == ABS_HAT0Y && !have_dpad_buttons) {
+          _dpad_y_axis = i;
+          _dpad_up_button = (int)_buttons.size();
+          _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
+          _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
+        }
+      }
+    }
+  }
+
   if (test_bit(EV_REL, evtypes)) {
     _flags |= IDF_has_pointer;
   }
@@ -403,6 +424,13 @@ process_events() {
       break;
 
     case EV_ABS:
+      if (code == _dpad_x_axis) {
+        set_button_state(_dpad_left_button, events[i].value < 0);
+        set_button_state(_dpad_left_button+1, events[i].value > 0);
+      } else if (code == _dpad_y_axis) {
+        set_button_state(_dpad_up_button, events[i].value < 0);
+        set_button_state(_dpad_up_button+1, events[i].value > 0);
+      }
       set_control_state(code, fma(events[i].value, _axis_ranges[code]._scale, _axis_ranges[code]._bias));
       break;
 

+ 6 - 0
panda/src/device/evdevInputDevice.h

@@ -50,6 +50,12 @@ private:
   };
   pvector<AxisRange> _axis_ranges;
 
+  // These are used for D-pad emulation.
+  int _dpad_x_axis;
+  int _dpad_y_axis;
+  int _dpad_left_button;
+  int _dpad_up_button;
+
 public:
   static ButtonHandle map_button(int code);
 

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

@@ -382,6 +382,16 @@ ButtonState() :
 {
 }
 
+/**
+ *
+ */
+INLINE InputDevice::ButtonState::
+ButtonState(ButtonHandle handle) :
+  _handle(handle),
+  _state(S_unknown)
+{
+}
+
 /**
  *
  */

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

@@ -238,6 +238,7 @@ public:
   class ButtonState {
   public:
     INLINE ButtonState();
+    INLINE ButtonState(ButtonHandle handle);
 
     ButtonHandle _handle;
     State _state;

+ 47 - 3
panda/src/device/linuxJoystickDevice.cxx

@@ -28,7 +28,11 @@ TypeHandle LinuxJoystickDevice::_type_handle;
 LinuxJoystickDevice::
 LinuxJoystickDevice(int index) :
   _fd(-1),
-  _index(index)
+  _index(index),
+  _dpad_x_axis(-1),
+  _dpad_y_axis(-1),
+  _dpad_left_button(-1),
+  _dpad_up_button(-1)
 {
   LightMutexHolder holder(_lock);
   if (!open_device()) {
@@ -115,7 +119,7 @@ open_device() {
     uint16_t btnmap[512];
     ioctl(_fd, JSIOCGBTNMAP, btnmap);
 
-    for (char i = 0; i < num_buttons; ++i) {
+    for (uint8_t i = 0; i < num_buttons; ++i) {
       ButtonHandle handle = ButtonHandle::none();
       switch (btnmap[i]) {
       case BTN_A:
@@ -181,6 +185,7 @@ open_device() {
 
       case BTN_TRIGGER_HAPPY1:
         handle = GamepadButton::dpad_left();
+        _dpad_left_button = i;
         break;
 
       case BTN_TRIGGER_HAPPY2:
@@ -189,6 +194,7 @@ open_device() {
 
       case BTN_TRIGGER_HAPPY3:
         handle = GamepadButton::dpad_up();
+        _dpad_up_button = i;
         break;
 
       case BTN_TRIGGER_HAPPY4:
@@ -196,6 +202,10 @@ open_device() {
         break;
 
       default:
+        if (device_cat.is_debug()) {
+          device_cat.debug() << "Unmapped /dev/input/js" << _index
+            << " button " << (int)i << ": 0x" << hex << btnmap[i] << "\n";
+        }
         handle = ButtonHandle::none();
         break;
       }
@@ -207,7 +217,7 @@ open_device() {
     uint8_t axmap[512];
     ioctl(_fd, JSIOCGAXMAP, axmap);
 
-    for (char i = 0; i < num_axes; ++i) {
+    for (uint8_t i = 0; i < num_axes; ++i) {
       ControlAxis axis = C_none;
 
       switch (axmap[i]) {
@@ -247,7 +257,33 @@ open_device() {
         axis = C_right_trigger;
         break;
 
+      case ABS_HAT0X:
+        if (_dpad_left_button == -1) {
+          // Emulate D-Pad.
+          _dpad_x_axis = i;
+          _dpad_left_button = (int)_buttons.size();
+          _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
+          _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
+        }
+        axis = C_none;
+        break;
+
+      case ABS_HAT0Y:
+        if (_dpad_up_button == -1) {
+          // Emulate D-Pad.
+          _dpad_y_axis = i;
+          _dpad_up_button = (int)_buttons.size();
+          _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
+          _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
+        }
+        axis = C_none;
+        break;
+
       default:
+        if (device_cat.is_debug()) {
+          device_cat.debug() << "Unmapped /dev/input/js" << _index
+            << " axis " << (int)i << ": 0x" << hex << (int)axmap[i] << "\n";
+        }
         axis = C_none;
         break;
       }
@@ -362,6 +398,14 @@ process_events() {
       set_button_state(index, (events[i].value != 0));
 
     } else if (events[i].type & JS_EVENT_AXIS) {
+      if (index == _dpad_x_axis) {
+        set_button_state(_dpad_left_button, events[i].value < -1000);
+        set_button_state(_dpad_left_button+1, events[i].value > 1000);
+      } else if (index == _dpad_y_axis) {
+        set_button_state(_dpad_up_button, events[i].value < -1000);
+        set_button_state(_dpad_up_button+1, events[i].value > 1000);
+      }
+
       ControlAxis axis = _controls[index]._axis;
 
       if (axis == C_left_trigger || axis == C_right_trigger || axis == C_trigger) {

+ 6 - 0
panda/src/device/linuxJoystickDevice.h

@@ -39,6 +39,12 @@ private:
   int _fd;
   int _index;
 
+  // These are used for D-pad emulation.
+  int _dpad_x_axis;
+  int _dpad_y_axis;
+  int _dpad_left_button;
+  int _dpad_up_button;
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;