input: joystick buttons, use yaw/pitch/roll for axes, more devices

Also adds format_* functions to get string from enum value.
This commit is contained in:
rdb 2018-01-07 14:50:54 +01:00
parent 27bd7ae4ac
commit 4f739e88cf
9 changed files with 256 additions and 148 deletions

View File

@ -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();

View File

@ -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() {

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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<ButtonHandle> 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");
}

View File

@ -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();