Fix Xbox One controller on Windows, add untested support for flight sticks and steering wheels
This commit is contained in:
parent
01e57e1bdd
commit
fd16e34660
|
|
@ -23,11 +23,54 @@
|
|||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
|
||||
#define test_bit(bit, array) ((array)[(bit)>>3] & (1<<((bit)&7)))
|
||||
|
||||
static InputDevice::ControlAxis axis_map[] = {
|
||||
InputDevice::C_left_x, InputDevice::C_left_y, InputDevice::C_left_trigger,
|
||||
InputDevice::C_right_x, InputDevice::C_right_y, InputDevice::C_right_trigger
|
||||
// ABS_X = 0x00
|
||||
InputDevice::C_left_x,
|
||||
// ABS_Y = 0x01
|
||||
InputDevice::C_left_y,
|
||||
// ABS_Z = 0x02
|
||||
InputDevice::C_left_trigger,
|
||||
// ABS_RX = 0x03
|
||||
InputDevice::C_right_x,
|
||||
// ABS_RY = 0x04
|
||||
InputDevice::C_right_y,
|
||||
// ABS_RZ = 0x05
|
||||
InputDevice::C_right_trigger,
|
||||
// ABS_THROTTLE = 0x06
|
||||
InputDevice::C_throttle,
|
||||
// ABS_RUDDER = 0x07
|
||||
InputDevice::C_rudder,
|
||||
// ABS_WHEEL = 0x08
|
||||
InputDevice::C_wheel,
|
||||
// ABS_GAS = 0x09
|
||||
InputDevice::C_accelerator,
|
||||
// ABS_BRAKE = 0x0a
|
||||
InputDevice::C_brake,
|
||||
|
||||
InputDevice::C_none,
|
||||
InputDevice::C_none,
|
||||
InputDevice::C_none,
|
||||
InputDevice::C_none,
|
||||
InputDevice::C_none,
|
||||
|
||||
// ABS_HAT0X = 0x10
|
||||
InputDevice::C_hat_x,
|
||||
// ABS_HAT0Y = 0x11
|
||||
InputDevice::C_hat_y,
|
||||
|
||||
// ABS_HAT1X = 0x12
|
||||
// ABS_HAT1Y = 0x13
|
||||
// ABS_HAT2X = 0x14
|
||||
// ABS_HAT2Y = 0x15
|
||||
// ABS_HAT3X = 0x16
|
||||
// ABS_HAT3Y = 0x17
|
||||
// ABS_PRESSURE = 0x18
|
||||
// ABS_DISTANCE = 0x19
|
||||
// ABS_TILT_X = 0x1a
|
||||
// ABS_TILT_Y = 0x1b
|
||||
// ABS_TOOL_WIDTH = 0x1c
|
||||
};
|
||||
|
||||
TypeHandle EvdevInputDevice::_type_handle;
|
||||
|
|
@ -189,7 +232,7 @@ init_device() {
|
|||
}
|
||||
|
||||
bool all_values_zero = true;
|
||||
bool have_dpad_buttons = false;
|
||||
bool emulate_dpad = true;
|
||||
|
||||
if (test_bit(EV_KEY, evtypes)) {
|
||||
// Check which buttons are on the device.
|
||||
|
|
@ -213,7 +256,7 @@ init_device() {
|
|||
_buttons[bi]._state = S_up;
|
||||
}
|
||||
if (_buttons[bi]._handle == GamepadButton::dpad_left()) {
|
||||
have_dpad_buttons = true;
|
||||
emulate_dpad = false;
|
||||
}
|
||||
++bi;
|
||||
}
|
||||
|
|
@ -241,6 +284,10 @@ init_device() {
|
|||
}
|
||||
}
|
||||
|
||||
if (_device_class != DC_gamepad) {
|
||||
emulate_dpad = false;
|
||||
}
|
||||
|
||||
if (test_bit(EV_ABS, evtypes)) {
|
||||
// Check which axes are on the device.
|
||||
uint8_t axes[(ABS_CNT + 7) >> 3];
|
||||
|
|
@ -254,7 +301,25 @@ init_device() {
|
|||
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]);
|
||||
// Emulate D-Pad buttons if necessary.
|
||||
if (i > ABS_HAT0Y) {
|
||||
set_control_map(i, C_none);
|
||||
|
||||
} else if (i == ABS_HAT0X && 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 if (i == ABS_HAT0Y && 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 {
|
||||
set_control_map(i, axis_map[i]);
|
||||
}
|
||||
|
||||
// Check the initial value and ranges.
|
||||
struct input_absinfo absinfo;
|
||||
|
|
@ -284,20 +349,6 @@ init_device() {
|
|||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,9 +170,25 @@ set_button_state(int index, bool down) {
|
|||
_buttons.resize(index + 1, ButtonState());
|
||||
}
|
||||
|
||||
_buttons[index]._state = down ? S_down : S_up;
|
||||
State new_state = down ? S_down : S_up;
|
||||
if (_buttons[index]._state == new_state) {
|
||||
return;
|
||||
}
|
||||
_buttons[index]._state = new_state;
|
||||
|
||||
ButtonHandle handle = _buttons[index]._handle;
|
||||
|
||||
if (device_cat.is_spam()) {
|
||||
device_cat.spam()
|
||||
<< "Changed button " << index;
|
||||
|
||||
if (handle != ButtonHandle::none()) {
|
||||
device_cat.spam(false) << " (" << handle << ")";
|
||||
}
|
||||
|
||||
device_cat.spam(false) << " to " << (down ? "down" : "up") << "\n";
|
||||
}
|
||||
|
||||
if (handle != ButtonHandle::none()) {
|
||||
_button_events->add_event(ButtonEvent(handle, down ? ButtonEvent::T_down : ButtonEvent::T_up));
|
||||
}
|
||||
|
|
@ -192,7 +208,7 @@ set_control_state(int index, double state) {
|
|||
_controls.resize(index + 1, AnalogState());
|
||||
}
|
||||
|
||||
if (device_cat.is_spam()) {
|
||||
if (device_cat.is_spam() && _controls[index]._state != state) {
|
||||
device_cat.spam()
|
||||
<< "Changed control " << index;
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,10 @@ PUBLISHED:
|
|||
|
||||
DC_flight_stick,
|
||||
DC_steering_wheel,
|
||||
DC_dance_pad,
|
||||
|
||||
// Head-mounted display.
|
||||
DC_hmd,
|
||||
};
|
||||
|
||||
protected:
|
||||
|
|
@ -101,6 +105,14 @@ PUBLISHED:
|
|||
C_y,
|
||||
C_trigger,
|
||||
C_throttle,
|
||||
C_rudder,
|
||||
C_hat_x,
|
||||
C_hat_y,
|
||||
|
||||
// Steering wheel / pedals
|
||||
C_wheel,
|
||||
C_accelerator,
|
||||
C_brake,
|
||||
};
|
||||
|
||||
INLINE string get_name() const;
|
||||
|
|
|
|||
|
|
@ -257,26 +257,50 @@ open_device() {
|
|||
axis = C_right_trigger;
|
||||
break;
|
||||
|
||||
case ABS_THROTTLE:
|
||||
axis = InputDevice::C_throttle;
|
||||
break;
|
||||
|
||||
case ABS_RUDDER:
|
||||
axis = InputDevice::C_rudder;
|
||||
break;
|
||||
|
||||
case ABS_WHEEL:
|
||||
axis = InputDevice::C_wheel;
|
||||
break;
|
||||
|
||||
case ABS_GAS:
|
||||
axis = InputDevice::C_accelerator;
|
||||
break;
|
||||
|
||||
case ABS_BRAKE:
|
||||
axis = InputDevice::C_brake;
|
||||
break;
|
||||
|
||||
case ABS_HAT0X:
|
||||
if (_dpad_left_button == -1) {
|
||||
if (_dpad_left_button == -1 && _device_class == DC_gamepad) {
|
||||
// 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;
|
||||
} else {
|
||||
axis = C_hat_x;
|
||||
}
|
||||
axis = C_none;
|
||||
break;
|
||||
|
||||
case ABS_HAT0Y:
|
||||
if (_dpad_up_button == -1) {
|
||||
if (_dpad_up_button == -1 && _device_class == DC_gamepad) {
|
||||
// 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;
|
||||
} else {
|
||||
axis = C_hat_y;
|
||||
}
|
||||
axis = C_none;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -28,11 +28,33 @@
|
|||
#ifndef XINPUT_CAPS_FFB_SUPPORTED
|
||||
#define XINPUT_CAPS_FFB_SUPPORTED 0x0001
|
||||
#endif
|
||||
#ifndef XINPUT_CAPS_NO_NAVIGATION
|
||||
#define XINPUT_CAPS_NO_NAVIGATION 0x0010
|
||||
#endif
|
||||
|
||||
#ifndef BATTERY_DEVTYPE_GAMEPAD
|
||||
#define BATTERY_DEVTYPE_GAMEPAD 0x00
|
||||
#endif
|
||||
|
||||
#ifndef XINPUT_DEVSUBTYPE_WHEEL
|
||||
#define XINPUT_DEVSUBTYPE_WHEEL 0x02
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK
|
||||
#define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK
|
||||
#define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_DANCE_PAD
|
||||
#define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_GUITAR
|
||||
#define XINPUT_DEVSUBTYPE_GUITAR 0x06
|
||||
#endif
|
||||
#ifndef XINPUT_DEVSUBTYPE_DRUM_KIT
|
||||
#define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08
|
||||
#endif
|
||||
|
||||
#ifndef BATTERY_TYPE_DISCONNECTED
|
||||
#define BATTERY_TYPE_DISCONNECTED 0x00
|
||||
#endif
|
||||
|
|
@ -96,29 +118,6 @@ XInputDevice(DWORD user_index) :
|
|||
_controls.resize(6);
|
||||
_buttons.resize(16);
|
||||
|
||||
set_control_map(0, C_left_trigger);
|
||||
set_control_map(1, C_right_trigger);
|
||||
set_control_map(2, C_left_x);
|
||||
set_control_map(3, C_left_y);
|
||||
set_control_map(4, C_right_x);
|
||||
set_control_map(5, C_right_y);
|
||||
|
||||
set_button_map(0, GamepadButton::dpad_up());
|
||||
set_button_map(1, GamepadButton::dpad_down());
|
||||
set_button_map(2, GamepadButton::dpad_left());
|
||||
set_button_map(3, GamepadButton::dpad_right());
|
||||
set_button_map(4, GamepadButton::start());
|
||||
set_button_map(5, GamepadButton::back());
|
||||
set_button_map(6, GamepadButton::lstick());
|
||||
set_button_map(7, GamepadButton::rstick());
|
||||
set_button_map(8, GamepadButton::lshoulder());
|
||||
set_button_map(9, GamepadButton::rshoulder());
|
||||
set_button_map(10, GamepadButton::guide());
|
||||
set_button_map(11, GamepadButton::action_a());
|
||||
set_button_map(12, GamepadButton::action_b());
|
||||
set_button_map(13, GamepadButton::action_x());
|
||||
set_button_map(14, GamepadButton::action_y());
|
||||
|
||||
// Check if the device is connected. If so, initialize it.
|
||||
XINPUT_CAPABILITIES caps;
|
||||
XINPUT_STATE state;
|
||||
|
|
@ -242,19 +241,87 @@ init_xinput() {
|
|||
*/
|
||||
void XInputDevice::
|
||||
init_device(const XINPUT_CAPABILITIES &caps, const XINPUT_STATE &state) {
|
||||
if (caps.Type == XINPUT_DEVTYPE_GAMEPAD) {
|
||||
// It seems that the Xbox One controller is reported as having a DevType of
|
||||
// zero, at least when I tested in with XInput 1.3 on Windows 7.
|
||||
//if (caps.Type == XINPUT_DEVTYPE_GAMEPAD) {
|
||||
|
||||
// For subtypes and mappings, see this page:
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/hh405050.aspx
|
||||
switch (caps.SubType) {
|
||||
default:
|
||||
case XINPUT_DEVSUBTYPE_GAMEPAD:
|
||||
_device_class = DC_gamepad;
|
||||
} else {
|
||||
_device_class = DC_unknown;
|
||||
set_control_map(0, C_left_trigger);
|
||||
set_control_map(1, C_right_trigger);
|
||||
set_control_map(2, C_left_x);
|
||||
set_control_map(3, C_left_y);
|
||||
set_control_map(4, C_right_x);
|
||||
set_control_map(5, C_right_y);
|
||||
break;
|
||||
|
||||
case XINPUT_DEVSUBTYPE_WHEEL:
|
||||
_device_class = DC_steering_wheel;
|
||||
set_control_map(0, C_brake);
|
||||
set_control_map(1, C_accelerator);
|
||||
set_control_map(2, C_wheel);
|
||||
set_control_map(3, C_none);
|
||||
set_control_map(4, C_none);
|
||||
set_control_map(5, C_none);
|
||||
break;
|
||||
|
||||
case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
|
||||
_device_class = DC_flight_stick;
|
||||
set_control_map(0, C_rudder);
|
||||
set_control_map(1, C_throttle);
|
||||
set_control_map(2, C_x);
|
||||
set_control_map(3, C_y);
|
||||
set_control_map(4, C_hat_x);
|
||||
set_control_map(5, C_hat_y);
|
||||
break;
|
||||
|
||||
case XINPUT_DEVSUBTYPE_DANCE_PAD:
|
||||
_device_class = DC_dance_pad;
|
||||
set_control_map(0, C_none);
|
||||
set_control_map(1, C_none);
|
||||
set_control_map(2, C_none);
|
||||
set_control_map(3, C_none);
|
||||
set_control_map(4, C_none);
|
||||
set_control_map(5, C_none);
|
||||
break;
|
||||
}
|
||||
|
||||
if (caps.Flags & XINPUT_CAPS_NO_NAVIGATION) {
|
||||
set_button_map(0, ButtonHandle::none());
|
||||
set_button_map(1, ButtonHandle::none());
|
||||
set_button_map(2, ButtonHandle::none());
|
||||
set_button_map(3, ButtonHandle::none());
|
||||
set_button_map(4, ButtonHandle::none());
|
||||
set_button_map(5, ButtonHandle::none());
|
||||
} else {
|
||||
set_button_map(0, GamepadButton::dpad_up());
|
||||
set_button_map(1, GamepadButton::dpad_down());
|
||||
set_button_map(2, GamepadButton::dpad_left());
|
||||
set_button_map(3, GamepadButton::dpad_right());
|
||||
set_button_map(4, GamepadButton::start());
|
||||
set_button_map(5, GamepadButton::back());
|
||||
}
|
||||
set_button_map(6, GamepadButton::lstick());
|
||||
set_button_map(7, GamepadButton::rstick());
|
||||
set_button_map(8, GamepadButton::lshoulder());
|
||||
set_button_map(9, GamepadButton::rshoulder());
|
||||
set_button_map(10, GamepadButton::guide());
|
||||
set_button_map(11, GamepadButton::action_a());
|
||||
set_button_map(12, GamepadButton::action_b());
|
||||
set_button_map(13, GamepadButton::action_x());
|
||||
set_button_map(14, GamepadButton::action_y());
|
||||
|
||||
if (caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) {
|
||||
_flags |= IDF_has_vibration;
|
||||
}
|
||||
|
||||
if (get_battery_information != NULL) {
|
||||
XINPUT_BATTERY_INFORMATION batt;
|
||||
if (get_battery_information(_index, 0, &batt) == ERROR_SUCCESS) {
|
||||
if (get_battery_information(_index, BATTERY_DEVTYPE_GAMEPAD, &batt) == ERROR_SUCCESS) {
|
||||
if (batt.BatteryType != BATTERY_TYPE_DISCONNECTED &&
|
||||
batt.BatteryType != BATTERY_TYPE_WIRED) {
|
||||
// This device has a battery. Report the battery level.
|
||||
|
|
@ -276,7 +343,7 @@ init_device(const XINPUT_CAPABILITIES &caps, const XINPUT_STATE &state) {
|
|||
{
|
||||
// Reformat the serial number into its original hex string form.
|
||||
char sn[10];
|
||||
sprintf(sn, "%08X", businfo.InstanceID);
|
||||
sprintf_s(sn, 10, "%08X", businfo.InstanceID);
|
||||
_serial_number.assign(sn, 8);
|
||||
}
|
||||
|
||||
|
|
@ -284,7 +351,7 @@ init_device(const XINPUT_CAPABILITIES &caps, const XINPUT_STATE &state) {
|
|||
// first need to construct the device path. Fortunately, we now have
|
||||
// enough information to do so.
|
||||
char path[32];
|
||||
sprintf(path, "USB\\VID_%04X&PID_%04X\\%08X", businfo.VendorID, businfo.ProductID, businfo.InstanceID);
|
||||
sprintf_s(path, 32, "USB\\VID_%04X&PID_%04X\\%08X", businfo.VendorID, businfo.ProductID, businfo.InstanceID);
|
||||
|
||||
DEVINST inst;
|
||||
if (CM_Locate_DevNodeA(&inst, path, 0) != 0) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue