From 4f739e88cf6fa97a7af78b6f935003936e04242d Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 7 Jan 2018 14:50:54 +0100 Subject: [PATCH] input: joystick buttons, use yaw/pitch/roll for axes, more devices Also adds format_* functions to get string from enum value. --- panda/src/device/evdevInputDevice.cxx | 105 +++++++++--- panda/src/device/evdevInputDevice.h | 2 +- panda/src/device/inputDevice.cxx | 196 +++++++++++------------ panda/src/device/inputDevice.h | 25 ++- panda/src/device/linuxJoystickDevice.cxx | 32 ++-- panda/src/device/linuxJoystickDevice.h | 2 +- panda/src/device/xInputDevice.cxx | 6 +- panda/src/putil/gamepadButton.cxx | 30 ++++ panda/src/putil/gamepadButton.h | 6 + 9 files changed, 256 insertions(+), 148 deletions(-) diff --git a/panda/src/device/evdevInputDevice.cxx b/panda/src/device/evdevInputDevice.cxx index b6c8c193b9..67fa1193c7 100644 --- a/panda/src/device/evdevInputDevice.cxx +++ b/panda/src/device/evdevInputDevice.cxx @@ -37,6 +37,9 @@ enum QuirkBits { // Only consider the device "connected" if all axes are non-zero. QB_connect_if_nonzero = 8, + + // ABS_THROTTLE maps to rudder + QB_rudder_from_throttle = 16, }; static const struct DeviceMapping { @@ -48,9 +51,25 @@ static const struct DeviceMapping { // NVIDIA Shield Controller {0x0955, 0x7214, InputDevice::DC_gamepad, QB_rstick_from_z}, // T.Flight Hotas X - {0x044f, 0xb108, InputDevice::DC_flight_stick, QB_centered_throttle | QB_reversed_throttle}, + {0x044f, 0xb108, InputDevice::DC_flight_stick, QB_centered_throttle | QB_reversed_throttle | QB_rudder_from_throttle}, // Xbox 360 Wireless Controller {0x045e, 0x0719, InputDevice::DC_gamepad, QB_connect_if_nonzero}, + // Jess Tech Colour Rumble Pad + {0x0f30, 0x0111, InputDevice::DC_gamepad, 0}, + // 3Dconnexion Space Traveller 3D Mouse + {0x046d, 0xc623, InputDevice::DC_3d_mouse, 0}, + // 3Dconnexion Space Pilot 3D Mouse + {0x046d, 0xc625, InputDevice::DC_3d_mouse, 0}, + // 3Dconnexion Space Navigator 3D Mouse + {0x046d, 0xc626, InputDevice::DC_3d_mouse, 0}, + // 3Dconnexion Space Explorer 3D Mouse + {0x046d, 0xc627, InputDevice::DC_3d_mouse, 0}, + // 3Dconnexion Space Navigator for Notebooks + {0x046d, 0xc628, InputDevice::DC_3d_mouse, 0}, + // 3Dconnexion SpacePilot Pro 3D Mouse + {0x046d, 0xc629, InputDevice::DC_3d_mouse, 0}, + // 3Dconnexion Space Mouse Pro + {0x046d, 0xc62b, InputDevice::DC_3d_mouse, 0}, {0}, }; @@ -329,10 +348,6 @@ init_device() { //cerr << "Found highscore class " << _device_class << " with this score: " << highest_score << "\n"; } - if (_device_class != DC_gamepad) { - emulate_dpad = false; - } - if (has_keys) { // Also check whether the buttons are currently pressed. uint8_t states[(KEY_CNT + 7) >> 3]; @@ -342,7 +357,7 @@ init_device() { for (int i = 0; i < KEY_CNT; ++i) { if (test_bit(i, keys)) { ButtonState button; - button.handle = map_button(i); + button.handle = map_button(i, _device_class); int button_index = (int)_buttons.size(); if (button.handle == ButtonHandle::none()) { @@ -378,10 +393,22 @@ init_device() { ControlAxis axis = C_none; switch (i) { case ABS_X: - axis = InputDevice::C_left_x; + if (_device_class == DC_gamepad) { + axis = InputDevice::C_left_x; + } else if (_device_class == DC_flight_stick) { + axis = InputDevice::C_roll; + } else { + axis = InputDevice::C_x; + } break; case ABS_Y: - axis = InputDevice::C_left_y; + if (_device_class == DC_gamepad) { + axis = InputDevice::C_left_y; + } else if (_device_class == DC_flight_stick) { + axis = InputDevice::C_pitch; + } else { + axis = InputDevice::C_y; + } break; case ABS_Z: if (quirks & QB_rstick_from_z) { @@ -408,11 +435,15 @@ init_device() { } else if (_device_class == DC_gamepad) { axis = InputDevice::C_right_trigger; } else { - axis = InputDevice::C_twist; + axis = InputDevice::C_yaw; } break; case ABS_THROTTLE: - axis = InputDevice::C_rudder; + if (quirks & QB_rudder_from_throttle) { + axis = InputDevice::C_rudder; + } else { + axis = InputDevice::C_throttle; + } break; case ABS_RUDDER: axis = InputDevice::C_rudder; @@ -438,20 +469,26 @@ init_device() { if (emulate_dpad) { _dpad_x_axis = i; _dpad_left_button = (int)_buttons.size(); - _buttons.push_back(ButtonState(GamepadButton::dpad_left())); - _buttons.push_back(ButtonState(GamepadButton::dpad_right())); - } else { - axis = C_hat_x; + if (_device_class == DC_gamepad) { + _buttons.push_back(ButtonState(GamepadButton::dpad_left())); + _buttons.push_back(ButtonState(GamepadButton::dpad_right())); + } else { + _buttons.push_back(ButtonState(GamepadButton::hat_left())); + _buttons.push_back(ButtonState(GamepadButton::hat_right())); + } } break; case ABS_HAT0Y: if (emulate_dpad) { _dpad_y_axis = i; _dpad_up_button = (int)_buttons.size(); - _buttons.push_back(ButtonState(GamepadButton::dpad_up())); - _buttons.push_back(ButtonState(GamepadButton::dpad_down())); - } else { - axis = C_hat_y; + if (_device_class == DC_gamepad) { + _buttons.push_back(ButtonState(GamepadButton::dpad_up())); + _buttons.push_back(ButtonState(GamepadButton::dpad_down())); + } else { + _buttons.push_back(ButtonState(GamepadButton::hat_up())); + _buttons.push_back(ButtonState(GamepadButton::hat_down())); + } } break; } @@ -461,8 +498,9 @@ init_device() { if (ioctl(_fd, EVIOCGABS(i), &absinfo) >= 0) { int index; // We'd like to reverse the Y axis to match the XInput behavior. + // Also reverse the yaw axis to match right-hand coordinate system. // Also T.Flight Hotas X throttle is reversed and can go backwards. - if (axis == C_y || axis == C_left_y || axis == C_right_y || + if (axis == C_yaw || axis == C_left_y || axis == C_right_y || (axis == C_throttle && (quirks & QB_reversed_throttle) != 0)) { swap(absinfo.maximum, absinfo.minimum); } @@ -647,7 +685,7 @@ process_events() { * Static function to map an evdev code to a ButtonHandle. */ ButtonHandle EvdevInputDevice:: -map_button(int code) { +map_button(int code, DeviceClass device_class) { if (code >= 0 && code < 0x80) { // See linux/input.h for the source of this mapping. static const ButtonHandle keyboard_map[] = { @@ -802,12 +840,33 @@ map_button(int code) { } else { return MouseButton::button(code - BTN_MOUSE); } + + } else if ((code & 0xfff0) == BTN_JOYSTICK) { + if (device_class == DC_gamepad) { + // Based on "Jess Tech Colour Rumble Pad" + static const ButtonHandle mapping[] = { + GamepadButton::action_x(), + GamepadButton::action_y(), + GamepadButton::action_a(), + GamepadButton::action_b(), + GamepadButton::lshoulder(), + GamepadButton::ltrigger(), + GamepadButton::rshoulder(), + GamepadButton::rtrigger(), + GamepadButton::back(), + GamepadButton::start(), + GamepadButton::lstick(), + GamepadButton::rstick(), + }; + if ((code & 0xf) < 12) { + return mapping[code & 0xf]; + } + } else { + return GamepadButton::joystick(code & 0xf); + } } switch (code) { - case BTN_TRIGGER: - return GamepadButton::trigger(); - case BTN_A: return GamepadButton::action_a(); diff --git a/panda/src/device/evdevInputDevice.h b/panda/src/device/evdevInputDevice.h index bd249b4d0e..9ebaef03f2 100644 --- a/panda/src/device/evdevInputDevice.h +++ b/panda/src/device/evdevInputDevice.h @@ -54,7 +54,7 @@ private: int _dpad_up_button; public: - static ButtonHandle map_button(int code); + static ButtonHandle map_button(int code, DeviceClass device_class = DC_unknown); public: static TypeHandle get_class_type() { diff --git a/panda/src/device/inputDevice.cxx b/panda/src/device/inputDevice.cxx index d1a5576c58..76aa79dc58 100644 --- a/panda/src/device/inputDevice.cxx +++ b/panda/src/device/inputDevice.cxx @@ -143,14 +143,17 @@ add_control(ControlAxis axis, int minimum, int maximum, bool centered) { int InputDevice:: add_control(ControlAxis axis, int minimum, int maximum) { bool centered = (minimum < 0) + || axis == C_x + || axis == C_y + || axis == C_z + || axis == C_yaw + || axis == C_pitch + || axis == C_roll || axis == C_left_x || axis == C_left_y || axis == C_right_x || axis == C_right_y - || axis == C_x - || axis == C_y || axis == C_wheel - || axis == C_twist || axis == C_rudder; return add_control(axis, minimum, maximum, centered); } @@ -499,130 +502,125 @@ void InputDevice:: do_poll() { } -ostream & -operator << (ostream &out, InputDevice::DeviceClass dc) { +/** + * Returns a string describing the given device class enumerant. + */ +string InputDevice:: +format_device_class(DeviceClass dc) { switch (dc) { case InputDevice::DC_unknown: - out << "unknown"; - break; + return "unknown"; case InputDevice::DC_virtual: - out << "virtual"; - break; + return "virtual"; case InputDevice::DC_keyboard: - out << "keyboard"; - break; + return "keyboard"; case InputDevice::DC_mouse: - out << "mouse"; - break; + return "mouse"; case InputDevice::DC_touch: - out << "touch"; - break; + return "touch"; case InputDevice::DC_gamepad: - out << "gamepad"; - break; + return "gamepad"; case InputDevice::DC_flight_stick: - out << "flight_stick"; - break; + return "flight_stick"; case InputDevice::DC_steering_wheel: - out << "steering_wheel"; - break; + return "steering_wheel"; case InputDevice::DC_dance_pad: - out << "dance_pad"; - break; + return "dance_pad"; case InputDevice::DC_hmd: - out << "hmd"; - break; + return "hmd"; + + case InputDevice::DC_3d_mouse: + return "3d_mouse"; case InputDevice::DC_COUNT: break; } + return "**invalid**"; +} + +/** + * Returns a string describing the given axis enumerant. + */ +string InputDevice:: +format_axis(ControlAxis axis) { + switch (axis) { + case InputDevice::C_none: + return "none"; + + case InputDevice::C_x: + return "x"; + + case InputDevice::C_y: + return "y"; + + case InputDevice::C_z: + return "z"; + + case InputDevice::C_yaw: + return "yaw"; + + case InputDevice::C_pitch: + return "pitch"; + + case InputDevice::C_roll: + return "roll"; + + case InputDevice::C_left_x: + return "left_x"; + + case InputDevice::C_left_y: + return "left_y"; + + case InputDevice::C_left_trigger: + return "left_trigger"; + + case InputDevice::C_right_x: + return "right_x"; + + case InputDevice::C_right_y: + return "right_y"; + + case InputDevice::C_right_trigger: + return "right_trigger"; + + //case InputDevice::C_trigger: + // return "trigger"; + + case InputDevice::C_throttle: + return "throttle"; + + case InputDevice::C_rudder: + return "rudder"; + + case InputDevice::C_wheel: + return "wheel"; + + case InputDevice::C_accelerator: + return "accelerator"; + + case InputDevice::C_brake: + return "brake"; + } + return "**invalid**"; +} + +ostream & +operator << (ostream &out, InputDevice::DeviceClass dc) { + out << InputDevice::format_device_class(dc); return out; } ostream & operator << (ostream &out, InputDevice::ControlAxis axis) { - switch (axis) { - case InputDevice::C_none: - out << "none"; - break; - - case InputDevice::C_left_x: - out << "left_x"; - break; - - case InputDevice::C_left_y: - out << "left_y"; - break; - - case InputDevice::C_left_trigger: - out << "left_trigger"; - break; - - case InputDevice::C_right_x: - out << "right_x"; - break; - - case InputDevice::C_right_y: - out << "right_y"; - break; - - case InputDevice::C_right_trigger: - out << "right_trigger"; - break; - - case InputDevice::C_x: - out << "x"; - break; - - case InputDevice::C_y: - out << "y"; - break; - - case InputDevice::C_trigger: - out << "trigger"; - break; - - case InputDevice::C_throttle: - out << "throttle"; - break; - - case InputDevice::C_twist: - out << "twist"; - break; - - case InputDevice::C_rudder: - out << "rudder"; - break; - - case InputDevice::C_hat_x: - out << "hat_x"; - break; - - case InputDevice::C_hat_y: - out << "hat_y"; - break; - - case InputDevice::C_wheel: - out << "wheel"; - break; - - case InputDevice::C_accelerator: - out << "accelerator"; - break; - - case InputDevice::C_brake: - out << "brake"; - break; - } - + out << InputDevice::format_axis(axis); return out; } diff --git a/panda/src/device/inputDevice.h b/panda/src/device/inputDevice.h index f4727e0a9c..43dbe14efe 100644 --- a/panda/src/device/inputDevice.h +++ b/panda/src/device/inputDevice.h @@ -78,6 +78,9 @@ PUBLISHED: // Head-mounted display. DC_hmd, + // 3D mouse, such as produced by 3Dconnexion. + DC_3d_mouse, + // Count of this enum, used for loops DC_COUNT, }; @@ -95,6 +98,16 @@ PUBLISHED: enum ControlAxis { C_none, + // Generic translational axes + C_x, + C_y, + C_z, + + // Generic rotational axes, used by joysticks and 3D mice + C_yaw, + C_pitch, + C_roll, + // Gamepad C_left_x, C_left_y, @@ -103,15 +116,9 @@ PUBLISHED: C_right_y, C_right_trigger, - // Flight stick - C_x, - C_y, - C_trigger, + // Flight stick specific C_throttle, - C_twist, - C_rudder, - C_hat_x, - C_hat_y, + C_rudder, // When available separately from yaw // Steering wheel / pedals C_wheel, @@ -183,6 +190,8 @@ PUBLISHED: PT(PointerEventList) get_pointer_events(); virtual void output(ostream &out) const; + static string format_device_class(DeviceClass dc); + static string format_axis(ControlAxis axis); protected: // Called during the constructor to add new controls or buttons diff --git a/panda/src/device/linuxJoystickDevice.cxx b/panda/src/device/linuxJoystickDevice.cxx index 8cd612f2d7..9aca81ad3d 100644 --- a/panda/src/device/linuxJoystickDevice.cxx +++ b/panda/src/device/linuxJoystickDevice.cxx @@ -251,7 +251,7 @@ open_device() { if (_device_class == DC_gamepad) { axis = C_left_trigger; } else { - axis = C_trigger; + //axis = C_trigger; } break; @@ -288,28 +288,34 @@ open_device() { break; case ABS_HAT0X: - if (_dpad_left_button == -1 && _device_class == DC_gamepad) { - // Emulate D-Pad. + if (_dpad_left_button == -1) { + // Emulate D-Pad or hat switch. _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 (_device_class == DC_gamepad) { + _buttons.push_back(ButtonState(GamepadButton::dpad_left())); + _buttons.push_back(ButtonState(GamepadButton::dpad_right())); + } else { + _buttons.push_back(ButtonState(GamepadButton::hat_left())); + _buttons.push_back(ButtonState(GamepadButton::hat_right())); + } axis = C_none; - } else { - axis = C_hat_x; } break; case ABS_HAT0Y: - if (_dpad_up_button == -1 && _device_class == DC_gamepad) { + 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())); + if (_device_class == DC_gamepad) { + _buttons.push_back(ButtonState(GamepadButton::dpad_up())); + _buttons.push_back(ButtonState(GamepadButton::dpad_down())); + } else { + _buttons.push_back(ButtonState(GamepadButton::hat_up())); + _buttons.push_back(ButtonState(GamepadButton::hat_down())); + } axis = C_none; - } else { - axis = C_hat_y; } break; @@ -323,7 +329,7 @@ open_device() { } _controls[i].axis = axis; - if (axis == C_left_trigger || axis == C_right_trigger || axis == C_trigger) { + if (axis == C_left_trigger || axis == C_right_trigger) { // We'd like to use 0.0 to indicate the resting position. _controls[i]._scale = 1.0 / 65534.0; _controls[i]._bias = 0.5; diff --git a/panda/src/device/linuxJoystickDevice.h b/panda/src/device/linuxJoystickDevice.h index 6507d4d80a..7bd8203039 100644 --- a/panda/src/device/linuxJoystickDevice.h +++ b/panda/src/device/linuxJoystickDevice.h @@ -39,7 +39,7 @@ private: int _fd; int _index; - // These are used for D-pad emulation. + // These are used for D-pad / hat switch emulation. int _dpad_x_axis; int _dpad_y_axis; int _dpad_left_button; diff --git a/panda/src/device/xInputDevice.cxx b/panda/src/device/xInputDevice.cxx index 04d88996f5..57fa73a416 100644 --- a/panda/src/device/xInputDevice.cxx +++ b/panda/src/device/xInputDevice.cxx @@ -271,10 +271,10 @@ init_device(const XINPUT_CAPABILITIES &caps, const XINPUT_STATE &state) { case XINPUT_DEVSUBTYPE_FLIGHT_STICK: _device_class = DC_flight_stick; - set_control_map(0, C_rudder); + set_control_map(0, C_yaw); set_control_map(1, C_throttle); - set_control_map(2, C_x); - set_control_map(3, C_y); + set_control_map(2, C_roll); + set_control_map(3, C_pitch); set_control_map(4, C_hat_x); set_control_map(5, C_hat_y); break; diff --git a/panda/src/putil/gamepadButton.cxx b/panda/src/putil/gamepadButton.cxx index 7ddbc33b17..fc580e6b5e 100644 --- a/panda/src/putil/gamepadButton.cxx +++ b/panda/src/putil/gamepadButton.cxx @@ -48,6 +48,32 @@ DEFINE_GAMEPAD_BUTTON_HANDLE(action_1) DEFINE_GAMEPAD_BUTTON_HANDLE(action_2) DEFINE_GAMEPAD_BUTTON_HANDLE(trigger) +DEFINE_GAMEPAD_BUTTON_HANDLE(hat_up) +DEFINE_GAMEPAD_BUTTON_HANDLE(hat_down) +DEFINE_GAMEPAD_BUTTON_HANDLE(hat_left) +DEFINE_GAMEPAD_BUTTON_HANDLE(hat_right) + +/** + * Returns the ButtonHandle associated with the particular numbered joystick + * button (zero-based), if there is one, or ButtonHandle::none() if there is + * not. + */ +ButtonHandle GamepadButton:: +joystick(int button_number) { + if (button_number >= 0) { + // "button1" does not exist, it is called "trigger" instead + static pvector buttons(1, _trigger); + while (button_number >= buttons.size()) { + char numstr[20]; + sprintf(numstr, "joystick%d", (int)buttons.size() + 1); + ButtonHandle handle; + ButtonRegistry::ptr()->register_button(handle, numstr); + buttons.push_back(handle); + } + return buttons[button_number]; + } + return ButtonHandle::none(); +} /** * This is intended to be called only once, by the static initialization @@ -85,4 +111,8 @@ init_gamepad_buttons() { ButtonRegistry::ptr()->register_button(_action_2, "action_2"); ButtonRegistry::ptr()->register_button(_trigger, "trigger"); + ButtonRegistry::ptr()->register_button(_hat_up, "hat_up"); + ButtonRegistry::ptr()->register_button(_hat_down, "hat_down"); + ButtonRegistry::ptr()->register_button(_hat_left, "hat_left"); + ButtonRegistry::ptr()->register_button(_hat_right, "hat_right"); } diff --git a/panda/src/putil/gamepadButton.h b/panda/src/putil/gamepadButton.h index 0edbbad99b..768f634380 100644 --- a/panda/src/putil/gamepadButton.h +++ b/panda/src/putil/gamepadButton.h @@ -53,7 +53,13 @@ PUBLISHED: static ButtonHandle action_1(); static ButtonHandle action_2(); + // Flight stick buttons, takes zero-based index. First is always trigger. static ButtonHandle trigger(); + static ButtonHandle joystick(int button_number); + static ButtonHandle hat_up(); + static ButtonHandle hat_down(); + static ButtonHandle hat_left(); + static ButtonHandle hat_right(); public: static void init_gamepad_buttons();