input: add analog trigger emulation on Linux

This commit is contained in:
rdb 2018-01-07 17:26:48 +01:00
parent 4f739e88cf
commit 9fbdefe120
4 changed files with 68 additions and 96 deletions

View File

@ -90,7 +90,9 @@ EvdevInputDevice(int index) :
_dpad_x_axis(-1),
_dpad_y_axis(-1),
_dpad_left_button(-1),
_dpad_up_button(-1) {
_dpad_up_button(-1),
_ltrigger_code(-1),
_rtrigger_code(-1) {
char path[64];
sprintf(path, "/dev/input/event%d", index);
@ -239,6 +241,7 @@ init_device() {
bool all_values_zero = true;
bool emulate_dpad = true;
bool have_analog_triggers = false;
bool has_keys = false;
bool has_axes = false;
@ -375,7 +378,12 @@ init_device() {
}
if (button.handle == GamepadButton::dpad_left()) {
emulate_dpad = false;
} else if (button.handle == GamepadButton::ltrigger()) {
_ltrigger_code = i;
} else if (button.handle == GamepadButton::rtrigger()) {
_rtrigger_code = i;
}
_buttons.push_back(button);
if (i >= _button_indices.size()) {
_button_indices.resize(i + 1, -1);
@ -415,6 +423,7 @@ init_device() {
axis = InputDevice::C_right_x;
} else if (_device_class == DC_gamepad) {
axis = InputDevice::C_left_trigger;
have_analog_triggers = true;
} else {
axis = InputDevice::C_throttle;
}
@ -434,6 +443,7 @@ init_device() {
axis = InputDevice::C_right_y;
} else if (_device_class == DC_gamepad) {
axis = InputDevice::C_right_trigger;
have_analog_triggers = true;
} else {
axis = InputDevice::C_yaw;
}
@ -454,6 +464,7 @@ init_device() {
case ABS_GAS:
if (_device_class == DC_gamepad) {
axis = InputDevice::C_right_trigger;
have_analog_triggers = true;
} else {
axis = InputDevice::C_accelerator;
}
@ -461,6 +472,7 @@ init_device() {
case ABS_BRAKE:
if (_device_class == DC_gamepad) {
axis = InputDevice::C_left_trigger;
have_analog_triggers = true;
} else {
axis = InputDevice::C_brake;
}
@ -541,6 +553,16 @@ init_device() {
}
}
if (_ltrigger_code >= 0 && _rtrigger_code >= 0 && !have_analog_triggers) {
// Emulate analog triggers.
_ltrigger_control = (int)_controls.size();
add_control(C_left_trigger, 0, 1, false);
add_control(C_right_trigger, 0, 1, false);
} else {
_ltrigger_code = -1;
_rtrigger_code = -1;
}
char path[64];
char buffer[256];
sprintf(path, "/sys/class/input/event%d/device/device/../product", _index);
@ -666,6 +688,11 @@ process_events() {
if (index >= 0) {
button_changed(index, events[i].value != 0);
}
if (code == _ltrigger_code) {
control_changed(_ltrigger_control, events[i].value);
} else if (code == _rtrigger_code) {
control_changed(_ltrigger_control + 1, events[i].value);
}
break;
default:

View File

@ -53,6 +53,11 @@ private:
int _dpad_left_button;
int _dpad_up_button;
// This is used for axis emulation.
int _ltrigger_control;
int _ltrigger_code;
int _rtrigger_code;
public:
static ButtonHandle map_button(int code, DeviceClass device_class = DC_unknown);

View File

@ -12,6 +12,7 @@
*/
#include "linuxJoystickDevice.h"
#include "evdevInputDevice.h"
#ifdef PHAVE_LINUX_INPUT_H
@ -32,7 +33,9 @@ LinuxJoystickDevice(int index) :
_dpad_x_axis(-1),
_dpad_y_axis(-1),
_dpad_left_button(-1),
_dpad_up_button(-1)
_dpad_up_button(-1),
_ltrigger_button(-1),
_rtrigger_button(-1)
{
LightMutexHolder holder(_lock);
if (!open_device()) {
@ -107,6 +110,8 @@ open_device() {
ioctl(_fd, JSIOCGNAME(sizeof(name)), name);
_name = name;
bool have_analog_triggers = false;
// Get the number of axes.
uint8_t num_axes = 0, num_buttons = 0;
ioctl(_fd, JSIOCGAXES, &num_axes);
@ -120,104 +125,18 @@ open_device() {
ioctl(_fd, JSIOCGBTNMAP, btnmap);
for (uint8_t i = 0; i < num_buttons; ++i) {
ButtonHandle handle = ButtonHandle::none();
switch (btnmap[i]) {
case BTN_A:
handle = GamepadButton::action_a();
_device_class = DC_gamepad;
break;
case BTN_B:
handle = GamepadButton::action_b();
break;
case BTN_C:
handle = GamepadButton::action_c();
break;
case BTN_X:
handle = GamepadButton::action_x();
break;
case BTN_Y:
handle = GamepadButton::action_y();
break;
case BTN_Z:
handle = GamepadButton::action_z();
break;
case BTN_TL:
handle = GamepadButton::lshoulder();
break;
case BTN_TR:
handle = GamepadButton::rshoulder();
break;
case BTN_TL2:
handle = GamepadButton::ltrigger();
break;
case BTN_TR2:
handle = GamepadButton::rtrigger();
break;
case BTN_1:
handle = GamepadButton::action_1();
break;
case BTN_2:
handle = GamepadButton::action_2();
break;
case BTN_SELECT:
case KEY_PREVIOUS:
handle = GamepadButton::back();
break;
case BTN_START:
case KEY_NEXT:
handle = GamepadButton::start();
break;
case BTN_MODE:
handle = GamepadButton::guide();
break;
case BTN_THUMBL:
handle = GamepadButton::lstick();
break;
case BTN_THUMBR:
handle = GamepadButton::rstick();
break;
case BTN_TRIGGER_HAPPY1:
handle = GamepadButton::dpad_left();
_dpad_left_button = i;
break;
case BTN_TRIGGER_HAPPY2:
handle = GamepadButton::dpad_right();
break;
case BTN_TRIGGER_HAPPY3:
handle = GamepadButton::dpad_up();
_dpad_up_button = i;
break;
case BTN_TRIGGER_HAPPY4:
handle = GamepadButton::dpad_down();
break;
default:
ButtonHandle handle = EvdevInputDevice::map_button(btnmap[i]);
if (handle == ButtonHandle::none()) {
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;
} else if (handle == GamepadButton::action_a()) {
_device_class = DC_gamepad;
} else if (handle == GamepadButton::ltrigger()) {
_ltrigger_button = i;
} else if (handle == GamepadButton::rtrigger()) {
_rtrigger_button = i;
}
_buttons[i].handle = handle;
}
@ -333,6 +252,7 @@ open_device() {
// We'd like to use 0.0 to indicate the resting position.
_controls[i]._scale = 1.0 / 65534.0;
_controls[i]._bias = 0.5;
have_analog_triggers = true;
} else if (axis == C_left_y || axis == C_right_y || axis == C_y) {
_controls[i]._scale = 1.0 / -32767.0;
_controls[i]._bias = 0.0;
@ -343,6 +263,16 @@ open_device() {
}
}
if (_ltrigger_button >= 0 && _rtrigger_button >= 0 && !have_analog_triggers) {
// Emulate analog triggers.
_ltrigger_control = (int)_controls.size();
add_control(C_left_trigger, 0, 1, false);
add_control(C_right_trigger, 0, 1, false);
} else {
_ltrigger_button = -1;
_rtrigger_button = -1;
}
// Get additional information from sysfs.
sprintf(path, "/sys/class/input/js%d/device/id/vendor", _index);
FILE *f = fopen(path, "r");
@ -454,6 +384,11 @@ process_events() {
int index = events[i].number;
if (events[i].type & JS_EVENT_BUTTON) {
if (index == _ltrigger_button) {
control_changed(_ltrigger_control, events[i].value);
} else if (index == _rtrigger_button) {
control_changed(_ltrigger_control + 1, events[i].value);
}
button_changed(index, (events[i].value != 0));
} else if (events[i].type & JS_EVENT_AXIS) {

View File

@ -45,6 +45,11 @@ private:
int _dpad_left_button;
int _dpad_up_button;
// This is used for axis emulation.
int _ltrigger_control;
int _ltrigger_button;
int _rtrigger_button;
public:
static TypeHandle get_class_type() {
return _type_handle;