Emulate d-pad from hat axes, fixes Xbox One d-pad buttons on Linux
This commit is contained in:
parent
b98d3c06ee
commit
01e57e1bdd
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -382,6 +382,16 @@ ButtonState() :
|
|||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE InputDevice::ButtonState::
|
||||
ButtonState(ButtonHandle handle) :
|
||||
_handle(handle),
|
||||
_state(S_unknown)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ public:
|
|||
class ButtonState {
|
||||
public:
|
||||
INLINE ButtonState();
|
||||
INLINE ButtonState(ButtonHandle handle);
|
||||
|
||||
ButtonHandle _handle;
|
||||
State _state;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue