diff --git a/panda/src/device/evdevInputDevice.cxx b/panda/src/device/evdevInputDevice.cxx index f3131d0f1d..ef318c3e5d 100644 --- a/panda/src/device/evdevInputDevice.cxx +++ b/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; diff --git a/panda/src/device/evdevInputDevice.h b/panda/src/device/evdevInputDevice.h index 772342a946..5a604b8617 100644 --- a/panda/src/device/evdevInputDevice.h +++ b/panda/src/device/evdevInputDevice.h @@ -50,6 +50,12 @@ private: }; pvector _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); diff --git a/panda/src/device/inputDevice.I b/panda/src/device/inputDevice.I index d1c6cdf5eb..e63930984b 100644 --- a/panda/src/device/inputDevice.I +++ b/panda/src/device/inputDevice.I @@ -382,6 +382,16 @@ ButtonState() : { } +/** + * + */ +INLINE InputDevice::ButtonState:: +ButtonState(ButtonHandle handle) : + _handle(handle), + _state(S_unknown) +{ +} + /** * */ diff --git a/panda/src/device/inputDevice.h b/panda/src/device/inputDevice.h index 889ffec2f8..0df79c3c65 100644 --- a/panda/src/device/inputDevice.h +++ b/panda/src/device/inputDevice.h @@ -238,6 +238,7 @@ public: class ButtonState { public: INLINE ButtonState(); + INLINE ButtonState(ButtonHandle handle); ButtonHandle _handle; State _state; diff --git a/panda/src/device/linuxJoystickDevice.cxx b/panda/src/device/linuxJoystickDevice.cxx index 826b4ac442..ed4e7fa329 100644 --- a/panda/src/device/linuxJoystickDevice.cxx +++ b/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) { diff --git a/panda/src/device/linuxJoystickDevice.h b/panda/src/device/linuxJoystickDevice.h index 0ec90919f8..6507d4d80a 100644 --- a/panda/src/device/linuxJoystickDevice.h +++ b/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;