diff --git a/panda/src/device/evdevInputDevice.cxx b/panda/src/device/evdevInputDevice.cxx index ef318c3e5d..4aa94e023d 100644 --- a/panda/src/device/evdevInputDevice.cxx +++ b/panda/src/device/evdevInputDevice.cxx @@ -23,11 +23,54 @@ #include #include -#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())); - } } } } diff --git a/panda/src/device/inputDevice.cxx b/panda/src/device/inputDevice.cxx index 3ee0f05dcb..04dfc4caae 100644 --- a/panda/src/device/inputDevice.cxx +++ b/panda/src/device/inputDevice.cxx @@ -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; diff --git a/panda/src/device/inputDevice.h b/panda/src/device/inputDevice.h index 0df79c3c65..e2e62735ff 100644 --- a/panda/src/device/inputDevice.h +++ b/panda/src/device/inputDevice.h @@ -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; diff --git a/panda/src/device/linuxJoystickDevice.cxx b/panda/src/device/linuxJoystickDevice.cxx index ed4e7fa329..f6ab5c7438 100644 --- a/panda/src/device/linuxJoystickDevice.cxx +++ b/panda/src/device/linuxJoystickDevice.cxx @@ -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: diff --git a/panda/src/device/xInputDevice.cxx b/panda/src/device/xInputDevice.cxx index 8650d1e89b..9fe3778f8c 100644 --- a/panda/src/device/xInputDevice.cxx +++ b/panda/src/device/xInputDevice.cxx @@ -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) {