device: overhaul InputDevice API:

- Rename "controls" terminology for analog inputs to "axes"
- Change some naming of accessors
- Unexpose methods where they are available as properties
- Add serial_number field
- Put battery levels under a "battery" field
This commit is contained in:
rdb 2018-09-16 15:12:17 +02:00
parent 8d8c5a546d
commit 8edc019307
19 changed files with 439 additions and 424 deletions

View File

@ -35,7 +35,7 @@ is_valid() const {
*/ */
INLINE int AnalogNode:: INLINE int AnalogNode::
get_num_controls() const { get_num_controls() const {
return _analog->get_num_controls(); return _analog->get_num_axes();
} }
/** /**
@ -45,7 +45,7 @@ get_num_controls() const {
*/ */
INLINE double AnalogNode:: INLINE double AnalogNode::
get_control_state(int index) const { get_control_state(int index) const {
return _analog->get_control_state(index); return _analog->get_axis_value(index);
} }
/** /**
@ -54,7 +54,7 @@ get_control_state(int index) const {
*/ */
INLINE bool AnalogNode:: INLINE bool AnalogNode::
is_control_known(int index) const { is_control_known(int index) const {
return _analog->is_control_known(index); return _analog->is_axis_known(index);
} }
/** /**

View File

@ -81,7 +81,7 @@ write(std::ostream &out, int indent_level) const {
DataNode::write(out, indent_level); DataNode::write(out, indent_level);
if (_analog != nullptr) { if (_analog != nullptr) {
_analog->write_controls(out, indent_level + 2); _analog->write_axes(out, indent_level + 2);
} }
} }
@ -101,12 +101,14 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
LPoint2 out(0.0f, 0.0f); LPoint2 out(0.0f, 0.0f);
for (int i = 0; i < max_outputs; i++) { for (int i = 0; i < max_outputs; i++) {
if (_outputs[i]._index >= 0 && if (_outputs[i]._index >= 0) {
_analog->is_control_known(_outputs[i]._index)) { InputDevice::AxisState state = _analog->get_axis(_outputs[i]._index);
if (state.known) {
if (_outputs[i]._flip) { if (_outputs[i]._flip) {
out[i] = -_analog->get_control_state(_outputs[i]._index); out[i] = -state.value;
} else { } else {
out[i] = _analog->get_control_state(_outputs[i]._index); out[i] = state.value;
}
} }
} }
} }

View File

@ -43,7 +43,7 @@ get_num_buttons() const {
*/ */
INLINE void ButtonNode:: INLINE void ButtonNode::
set_button_map(int index, ButtonHandle button) { set_button_map(int index, ButtonHandle button) {
_device->set_button_map(index, button); _device->map_button(index, button);
} }
/** /**
@ -62,7 +62,7 @@ get_button_map(int index) const {
*/ */
INLINE bool ButtonNode:: INLINE bool ButtonNode::
get_button_state(int index) const { get_button_state(int index) const {
return _device->get_button_state(index); return _device->is_button_pressed(index);
} }
/** /**

View File

@ -23,5 +23,5 @@ TypeHandle ClientAnalogDevice::_type_handle;
void ClientAnalogDevice:: void ClientAnalogDevice::
write(std::ostream &out, int indent_level) const { write(std::ostream &out, int indent_level) const {
indent(out, indent_level) << get_type() << " " << get_name() << ":\n"; indent(out, indent_level) << get_type() << " " << get_name() << ":\n";
write_controls(out, indent_level + 2); write_axes(out, indent_level + 2);
} }

View File

@ -392,10 +392,10 @@ init_device() {
} }
if (test_bit(i, states)) { if (test_bit(i, states)) {
button.state = S_down; button._state = S_down;
all_values_zero = false; all_values_zero = false;
} else { } else {
button.state = S_up; button._state = S_up;
} }
if (button.handle == GamepadButton::dpad_left()) { if (button.handle == GamepadButton::dpad_left()) {
emulate_dpad = false; emulate_dpad = false;
@ -415,7 +415,7 @@ init_device() {
} }
if (has_axes) { if (has_axes) {
_control_indices.resize(num_bits, -1); _axis_indices.resize(num_bits, -1);
for (int i = 0; i < num_bits; ++i) { for (int i = 0; i < num_bits; ++i) {
if (test_bit(i, axes)) { if (test_bit(i, axes)) {
@ -545,12 +545,12 @@ init_device() {
std::swap(absinfo.maximum, absinfo.minimum); std::swap(absinfo.maximum, absinfo.minimum);
} }
if (axis == Axis::throttle && (quirks & QB_centered_throttle) != 0) { if (axis == Axis::throttle && (quirks & QB_centered_throttle) != 0) {
index = add_control(axis, absinfo.maximum, absinfo.minimum, true); index = add_axis(axis, absinfo.maximum, absinfo.minimum, true);
} else { } else {
index = add_control(axis, absinfo.minimum, absinfo.maximum); index = add_axis(axis, absinfo.minimum, absinfo.maximum);
} }
control_changed(index, absinfo.value); axis_changed(index, absinfo.value);
_control_indices[i] = index; _axis_indices[i] = index;
if (absinfo.value != 0) { if (absinfo.value != 0) {
all_values_zero = false; all_values_zero = false;
@ -583,9 +583,9 @@ init_device() {
if (_ltrigger_code >= 0 && _rtrigger_code >= 0 && !have_analog_triggers) { if (_ltrigger_code >= 0 && _rtrigger_code >= 0 && !have_analog_triggers) {
// Emulate analog triggers. // Emulate analog triggers.
_ltrigger_control = (int)_controls.size(); _ltrigger_axis = (int)_axes.size();
add_control(Axis::left_trigger, 0, 1, false); add_axis(Axis::left_trigger, 0, 1, false);
add_control(Axis::right_trigger, 0, 1, false); add_axis(Axis::right_trigger, 0, 1, false);
} else { } else {
_ltrigger_code = -1; _ltrigger_code = -1;
_rtrigger_code = -1; _rtrigger_code = -1;
@ -709,10 +709,10 @@ process_events() {
button_changed(_dpad_up_button, events[i].value < 0); button_changed(_dpad_up_button, events[i].value < 0);
button_changed(_dpad_up_button+1, events[i].value > 0); button_changed(_dpad_up_button+1, events[i].value > 0);
} }
nassertd(code >= 0 && (size_t)code < _control_indices.size()) break; nassertd(code >= 0 && (size_t)code < _axis_indices.size()) break;
index = _control_indices[code]; index = _axis_indices[code];
if (index >= 0) { if (index >= 0) {
control_changed(index, events[i].value); axis_changed(index, events[i].value);
} }
break; break;
@ -723,9 +723,9 @@ process_events() {
button_changed(index, events[i].value != 0); button_changed(index, events[i].value != 0);
} }
if (code == _ltrigger_code) { if (code == _ltrigger_code) {
control_changed(_ltrigger_control, events[i].value); axis_changed(_ltrigger_axis, events[i].value);
} else if (code == _rtrigger_code) { } else if (code == _rtrigger_code) {
control_changed(_ltrigger_control + 1, events[i].value); axis_changed(_ltrigger_axis + 1, events[i].value);
} }
break; break;

View File

@ -49,7 +49,7 @@ private:
int _ff_strong; int _ff_strong;
int _ff_weak; int _ff_weak;
pvector<int> _control_indices; pvector<int> _axis_indices;
pvector<int> _button_indices; pvector<int> _button_indices;
// These are used for D-pad emulation. // These are used for D-pad emulation.
@ -59,7 +59,7 @@ private:
int _dpad_up_button; int _dpad_up_button;
// This is used for axis emulation. // This is used for axis emulation.
int _ltrigger_control; int _ltrigger_axis;
int _ltrigger_code; int _ltrigger_code;
int _rtrigger_code; int _rtrigger_code;

View File

@ -24,9 +24,7 @@ InputDevice() :
_product_id(0), _product_id(0),
_is_connected(false), _is_connected(false),
_event_sequence(0), _event_sequence(0),
_enable_pointer_events(false), _enable_pointer_events(false)
_battery_level(-1),
_max_battery_level(-1)
{ {
_button_events = new ButtonEventList; _button_events = new ButtonEventList;
} }
@ -50,6 +48,16 @@ get_manufacturer() const {
return _manufacturer; return _manufacturer;
} }
/**
* Returns a string containing the serial number of the device, if this
* information is known.
*/
INLINE std::string InputDevice::
get_serial_number() const {
LightMutexHolder holder(_lock);
return _serial_number;
}
/** /**
* Returns a string containing the USB vendor ID of the device, if this * Returns a string containing the USB vendor ID of the device, if this
* information is known. * information is known.
@ -160,23 +168,12 @@ get_tracker() const {
/** /**
* Returns a rough indication of the battery level, ranging from 0 (completely * Returns a rough indication of the battery level, ranging from 0 (completely
* empty battery) to the number reported by get_max_battery_level(), which * empty battery) to the indicated max_level value.
* represents a full battery. If this information is not known, returns -1.
*/ */
INLINE short InputDevice:: INLINE InputDevice::BatteryData InputDevice::
get_battery_level() const { get_battery() const {
LightMutexHolder holder(_lock); LightMutexHolder holder(_lock);
return _battery_level; return _battery_data;
}
/**
* Returns the maximum value that may be reported by get_battery_level(),
* representing a full battery. Returns -1 if no battery information is known.
*/
INLINE short InputDevice::
get_max_battery_level() const {
LightMutexHolder holder(_lock);
return _max_battery_level;
} }
/** /**
@ -202,7 +199,7 @@ get_num_buttons() const {
* generate ButtonEvents when the buttons change state. * generate ButtonEvents when the buttons change state.
*/ */
INLINE void InputDevice:: INLINE void InputDevice::
set_button_map(size_t index, ButtonHandle button) { map_button(size_t index, ButtonHandle button) {
LightMutexHolder holder(_lock); LightMutexHolder holder(_lock);
if (index >= _buttons.size()) { if (index >= _buttons.size()) {
_buttons.resize(index + 1, ButtonState()); _buttons.resize(index + 1, ButtonState());
@ -213,7 +210,7 @@ set_button_map(size_t index, ButtonHandle button) {
/** /**
* Returns the ButtonHandle that was previously associated with the given index * Returns the ButtonHandle that was previously associated with the given index
* number by a call to set_button_map(), or ButtonHandle::none() if no button * number by a call to map_button(), or ButtonHandle::none() if no button
* was associated. * was associated.
*/ */
INLINE ButtonHandle InputDevice:: INLINE ButtonHandle InputDevice::
@ -230,9 +227,9 @@ get_button_map(size_t index) const {
* currently known to be down, or false if it is up or unknown. * currently known to be down, or false if it is up or unknown.
*/ */
INLINE bool InputDevice:: INLINE bool InputDevice::
get_button_state(size_t index) const { is_button_pressed(size_t index) const {
if (index < _buttons.size()) { if (index < _buttons.size()) {
return (_buttons[index].state == S_down); return (_buttons[index]._state == S_down);
} else { } else {
return false; return false;
} }
@ -245,14 +242,14 @@ get_button_state(size_t index) const {
INLINE bool InputDevice:: INLINE bool InputDevice::
is_button_known(size_t index) const { is_button_known(size_t index) const {
if (index < _buttons.size()) { if (index < _buttons.size()) {
return _buttons[index].state != S_unknown; return _buttons[index]._state != S_unknown;
} else { } else {
return false; return false;
} }
} }
/** /**
* Returns the ButtonState that is set at the given index, or throw an assection * Returns the ButtonState that is set at the given index, or throw an assert
* if the index was not found in the list. * if the index was not found in the list.
*/ */
INLINE InputDevice::ButtonState InputDevice:: INLINE InputDevice::ButtonState InputDevice::
@ -261,13 +258,13 @@ get_button(size_t index) const {
return _buttons[index]; return _buttons[index];
} else { } else {
device_cat.error() device_cat.error()
<< "Index " << index << " was not found in the controls list\n"; << "Index " << index << " was not found in the axes list\n";
return ButtonState(); return ButtonState();
} }
} }
/** /**
* Returns the first ButtonState found with the given axis, or throw an assection * Returns the first ButtonState found with the given axis, or throw an assert
* if the button handle was not found in the list. * if the button handle was not found in the list.
*/ */
INLINE InputDevice::ButtonState InputDevice:: INLINE InputDevice::ButtonState InputDevice::
@ -278,103 +275,89 @@ find_button(ButtonHandle handle) const {
} }
} }
device_cat.error() device_cat.error()
<< "Handle " << handle.get_name() << " was not found in the controls list\n"; << "Handle " << handle.get_name() << " was not found in the axes list\n";
return ButtonState(); return ButtonState();
} }
/** /**
* Returns the number of analog controls known to the InputDevice. This number * Returns the number of analog axes known to the InputDevice. This number
* may change as more controls are discovered. * may change as more axes are discovered.
*/ */
INLINE size_t InputDevice:: INLINE size_t InputDevice::
get_num_controls() const { get_num_axes() const {
return _controls.size(); return _axes.size();
} }
/** /**
* Associates the indicated Axis with the control of the indicated index * Associates the indicated Axis with the axis of the indicated index
* number. Pass Axis::none to turn off any association. * number. Pass Axis::none to turn off any association.
* *
* It is not necessary to call this if you simply want to query the state of * It is not necessary to call this if you simply want to query the state of
* the various controls by index number. * the various axes by index number.
*/ */
INLINE void InputDevice:: INLINE void InputDevice::
set_control_map(size_t index, InputDevice::Axis axis) { map_axis(size_t index, InputDevice::Axis axis) {
LightMutexHolder holder(_lock); LightMutexHolder holder(_lock);
if (index >= _controls.size()) { if (index >= _axes.size()) {
_controls.resize(index + 1, AnalogState()); _axes.resize(index + 1, AxisState());
} }
_controls[index].axis = axis; _axes[index].axis = axis;
} }
/** /**
* Returns the Axis that was previously associated with the given index * Returns the current position of indicated analog axis (identified by its
* number by a call to set_control_map(), or Axis::none if no control was * index number), or 0.0 if the axis is unknown. The normal range of a
* associated. * single axis is -1.0 to 1.0.
*/
INLINE InputDevice::Axis InputDevice::
get_control_map(size_t index) const {
if (index < _controls.size()) {
return _controls[index].axis;
} else {
return Axis::none;
}
}
/**
* Returns the current position of indicated analog control (identified by its
* index number), or 0.0 if the control is unknown. The normal range of a
* single control is -1.0 to 1.0.
*/ */
INLINE double InputDevice:: INLINE double InputDevice::
get_control_state(size_t index) const { get_axis_value(size_t index) const {
if (index < _controls.size()) { if (index < _axes.size()) {
return _controls[index].state; return _axes[index].value;
} else { } else {
return 0.0; return 0.0;
} }
} }
/** /**
* Returns the AnalogAxis that is set at the given index, or throw an assection * Returns the axis state that is set at the given index, or throw an assert
* if the index was not found in the list. * if the index was not found in the list.
*/ */
INLINE InputDevice::AnalogState InputDevice:: INLINE InputDevice::AxisState InputDevice::
get_control(size_t index) const { get_axis(size_t index) const {
if (index < _controls.size()) { if (index < _axes.size()) {
return _controls[index]; return _axes[index];
} else { } else {
device_cat.error() device_cat.error()
<< "Index " << index<< " was not found in the controls list\n"; << "Index " << index << " was not found in the axes list\n";
return AnalogState(); return AxisState();
} }
} }
/** /**
* Returns the first AnalogAxis found with the given axis, or throw an assection * Returns the first AnalogAxis found with the given axis, or throw an assert
* if the axis was not found in the list. * if the axis was not found in the list.
*/ */
INLINE InputDevice::AnalogState InputDevice:: INLINE InputDevice::AxisState InputDevice::
find_control(InputDevice::Axis axis) const { find_axis(InputDevice::Axis axis) const {
for (size_t i = 0; i < _controls.size(); ++i) { for (size_t i = 0; i < _axes.size(); ++i) {
if (_controls[i].axis == axis) { if (_axes[i].axis == axis) {
return _controls[i]; return _axes[i];
} }
} }
device_cat.error() device_cat.error()
<< "Axis " << axis << " was not found in the controls list\n"; << "Axis " << axis << " was not found in the axes list\n";
return AnalogState(); return AxisState();
} }
/** /**
* Returns true if the state of the indicated analog control is known, or false * Returns true if the state of the indicated analog axis is known, or false
* if we have never heard anything about this particular control. * if we have never heard anything about this particular axis.
*/ */
INLINE bool InputDevice:: INLINE bool InputDevice::
is_control_known(size_t index) const { is_axis_known(size_t index) const {
if (index < _controls.size()) { if (index < _axes.size()) {
return _controls[index].known; return _axes[index].known;
} else { } else {
return false; return false;
} }
@ -382,8 +365,8 @@ is_control_known(size_t index) const {
/** /**
* Sets the strength of the vibration effect, if supported. The values are * Sets the strength of the vibration effect, if supported. The values are
* clamped to 0-1 range. The first value controls the low-frequency rumble * clamped to 0-1 range. The first value axes the low-frequency rumble
* motor, whereas the second controls the high-frequency motor, if present. * motor, whereas the second axes the high-frequency motor, if present.
*/ */
INLINE void InputDevice:: INLINE void InputDevice::
set_vibration(double strong, double weak) { set_vibration(double strong, double weak) {
@ -420,36 +403,26 @@ set_connected(bool connected) {
_is_connected = connected; _is_connected = connected;
} }
/**
*
*/
INLINE bool InputDevice::
operator == (const InputDevice &) const {
return true;
}
/**
*
*/
INLINE bool InputDevice::
operator != (const InputDevice &) const {
return false;
}
/**
*
*/
INLINE bool InputDevice::
operator < (const InputDevice &) const {
return false;
}
/** /**
* *
*/ */
INLINE InputDevice::ButtonState:: INLINE InputDevice::ButtonState::
ButtonState(ButtonHandle handle) : ButtonState(ButtonHandle handle) :
handle(handle), handle(handle) {
state(S_unknown) }
{
/**
* True if the button state is currently known.
*/
ALWAYS_INLINE bool InputDevice::ButtonState::
is_known() const {
return (_state != S_unknown);
}
/**
* True if the button is currently known to be pressed.
*/
ALWAYS_INLINE bool InputDevice::ButtonState::
is_pressed() const {
return (_state == S_down);
} }

View File

@ -32,8 +32,6 @@ InputDevice(const std::string &name, DeviceClass dev_class, int flags) :
_is_connected(true), _is_connected(true),
_event_sequence(0), _event_sequence(0),
_enable_pointer_events(false), _enable_pointer_events(false),
_battery_level(-1),
_max_battery_level(-1),
_lock("InputDevice") _lock("InputDevice")
{ {
_button_events = new ButtonEventList; _button_events = new ButtonEventList;
@ -119,13 +117,22 @@ get_pointer_events() {
return result; return result;
} }
/** /**
* Called by the implementation to add a new known control. * Called by the implementation to add a new known button.
*/ */
int InputDevice:: int InputDevice::
add_control(Axis axis, int minimum, int maximum, bool centered) { add_button(ButtonHandle button) {
AnalogState state; int index = (int)_buttons.size();
_buttons.push_back(ButtonState(button));
return index;
}
/**
* Called by the implementation to add a new known axis.
*/
int InputDevice::
add_axis(Axis axis, int minimum, int maximum, bool centered) {
AxisState state;
state.axis = axis; state.axis = axis;
if (centered) { if (centered) {
// Centered, eg. for sticks. // Centered, eg. for sticks.
@ -136,17 +143,17 @@ add_control(Axis axis, int minimum, int maximum, bool centered) {
state._scale = 1.0 / maximum; state._scale = 1.0 / maximum;
state._bias = 0.0; state._bias = 0.0;
} }
int index = (int)_controls.size(); int index = (int)_axes.size();
_controls.push_back(state); _axes.push_back(state);
return index; return index;
} }
/** /**
* Called by the implementation to add a new known control. This version * Called by the implementation to add a new known axis. This version tries
* tries to guess whether the control is centered or not. * to guess whether the axis is centered or not.
*/ */
int InputDevice:: int InputDevice::
add_control(Axis axis, int minimum, int maximum) { add_axis(Axis axis, int minimum, int maximum) {
bool centered = (minimum < 0) bool centered = (minimum < 0)
|| axis == Axis::x || axis == Axis::x
|| axis == Axis::y || axis == Axis::y
@ -160,7 +167,7 @@ add_control(Axis axis, int minimum, int maximum) {
|| axis == Axis::right_y || axis == Axis::right_y
|| axis == Axis::wheel || axis == Axis::wheel
|| axis == Axis::rudder; || axis == Axis::rudder;
return add_control(axis, minimum, maximum, centered); return add_axis(axis, minimum, maximum, centered);
} }
/** /**
@ -246,10 +253,10 @@ button_changed(int index, bool down) {
} }
State new_state = down ? S_down : S_up; State new_state = down ? S_down : S_up;
if (_buttons[index].state == new_state) { if (_buttons[index]._state == new_state) {
return; return;
} }
_buttons[index].state = new_state; _buttons[index]._state = new_state;
ButtonHandle handle = _buttons[index].handle; ButtonHandle handle = _buttons[index].handle;
@ -272,65 +279,67 @@ button_changed(int index, bool down) {
/** /**
* Sets the state of the indicated analog index. The caller should ensure that * Sets the state of the indicated analog index. The caller should ensure that
* the lock is held while this call is made. This should be a number in the * the lock is held while this call is made. This should be a number in the
* range -1.0 to 1.0, representing the current position of the control within * range -1.0 to 1.0, representing the current position of the axis within its
* its total range of movement. * total range of movement.
*/ */
void InputDevice:: void InputDevice::
set_control_state(int index, double state) { set_axis_value(int index, double value) {
nassertv(_lock.debug_is_locked()); LightMutexHolder holder(_lock);
nassertv(index >= 0); nassertv(index >= 0);
if (index >= (int)_controls.size()) { if ((size_t)index >= _axes.size()) {
_controls.resize(index + 1, AnalogState()); _axes.resize((size_t)index + 1u, AxisState());
} }
if (device_cat.is_spam() && _controls[index].state != state) { if (device_cat.is_spam() && _axes[index].value != value) {
device_cat.spam() device_cat.spam()
<< "Changed control " << index; << "Changed axis " << index;
if (_controls[index].axis != Axis::none) { if (_axes[index].axis != Axis::none) {
device_cat.spam(false) << " (" << _controls[index].axis << ")"; device_cat.spam(false) << " (" << _axes[index].axis << ")";
} }
device_cat.spam(false) << " to " << state << "\n"; device_cat.spam(false) << " to " << value << "\n";
} }
_controls[index].state = state; _axes[index].value = value;
_controls[index].known = true; _axes[index].known = true;
} }
/** /**
* Like set_control_state, but instead passes a raw, unscaled value. * Called by the implementation during do_poll to indicate that the indicated
* axis has received a new raw value. Assumes the lock is held.
*/ */
void InputDevice:: void InputDevice::
control_changed(int index, int state) { axis_changed(int index, int state) {
nassertv(_lock.debug_is_locked()); nassertv(_lock.debug_is_locked());
nassertv(index >= 0); nassertv(index >= 0);
if (index >= (int)_controls.size()) { if ((size_t)index >= _axes.size()) {
_controls.resize(index + 1, AnalogState()); _axes.resize((size_t)index + 1u, AxisState());
} }
double new_state = fma((double)state, _controls[index]._scale, _controls[index]._bias); double value = fma((double)state, _axes[index]._scale, _axes[index]._bias);
if (device_cat.is_spam() && _controls[index].state != new_state) { if (device_cat.is_spam() && !IS_NEARLY_EQUAL(_axes[index].value, value)) {
device_cat.spam() device_cat.spam()
<< "Changed control " << index; << "Changed axis " << index;
if (_controls[index].axis != Axis::none) { if (_axes[index].axis != Axis::none) {
device_cat.spam(false) << " (" << _controls[index].axis << ")"; device_cat.spam(false) << " (" << _axes[index].axis << ")";
} }
device_cat.spam(false) << " to " << new_state << " (raw value " << state << ")\n"; device_cat.spam(false) << " to " << value << " (raw value " << state << ")\n";
} }
_controls[index].state = new_state; _axes[index].value = value;
_controls[index].known = true; _axes[index].known = true;
} }
/** /**
* Records that a tracker movement has taken place. * Records that a tracker movement has taken place.
*/ */
void InputDevice:: void InputDevice::
set_tracker(const LPoint3 &pos, const LOrientation &orient, double time) { tracker_changed(const LPoint3 &pos, const LOrientation &orient, double time) {
nassertv(_lock.debug_is_locked()); nassertv(_lock.debug_is_locked());
_tracker_data.set_pos(pos); _tracker_data.set_pos(pos);
@ -364,11 +373,9 @@ output(std::ostream &out) const {
} }
} }
if (_controls.size() > 0) { if (_axes.size() > 0) {
out << ", " << _controls.size() << " control"; out << ", " << _axes.size() << " ax"
if (_controls.size() != 1) { << (_axes.size() != 1 ? 'e' : 'i') << 's';
out.put('s');
}
} }
if (_flags & IDF_has_pointer) { if (_flags & IDF_has_pointer) {
@ -386,13 +393,13 @@ output(std::ostream &out) const {
if (_flags & IDF_has_battery) { if (_flags & IDF_has_battery) {
out << ", battery"; out << ", battery";
if (_battery_level > 0 && _max_battery_level > 0) { if (_battery_data.level > 0 && _battery_data.max_level > 0) {
out << " ["; out << " [";
short i = 0; short i = 0;
for (; i < _battery_level; ++i) { for (; i < _battery_data.level; ++i) {
out << '='; out << '=';
} }
for (; i < _max_battery_level; ++i) { for (; i < _battery_data.max_level; ++i) {
out << ' '; out << ' ';
} }
out << ']'; out << ']';
@ -411,13 +418,13 @@ output_buttons(std::ostream &out) const {
Buttons::const_iterator bi; Buttons::const_iterator bi;
for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) { for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) {
const ButtonState &state = (*bi); const ButtonState &state = (*bi);
if (state.state != S_unknown) { if (state.is_known()) {
if (any_buttons) { if (any_buttons) {
out << ", "; out << ", ";
} }
any_buttons = true; any_buttons = true;
out << (int)(bi - _buttons.begin()) << "="; out << (int)(bi - _buttons.begin()) << "=";
if (state.state == S_up) { if (state._state == S_up) {
out << "up"; out << "up";
} else { } else {
out << "down"; out << "down";
@ -439,7 +446,7 @@ write_buttons(std::ostream &out, int indent_level) const {
Buttons::const_iterator bi; Buttons::const_iterator bi;
for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) { for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) {
const ButtonState &state = (*bi); const ButtonState &state = (*bi);
if (state.state != S_unknown) { if (state.is_known()) {
any_buttons = true; any_buttons = true;
indent(out, indent_level) indent(out, indent_level)
@ -449,7 +456,7 @@ write_buttons(std::ostream &out, int indent_level) const {
out << "(" << state.handle << ") "; out << "(" << state.handle << ") ";
} }
if (state.state == S_up) { if (state._state == S_up) {
out << "up"; out << "up";
} else { } else {
out << "down"; out << "down";
@ -465,27 +472,27 @@ write_buttons(std::ostream &out, int indent_level) const {
} }
/** /**
* Writes a multi-line description of the current analog control states. * Writes a multi-line description of the current analog axis states.
*/ */
void InputDevice:: void InputDevice::
write_controls(std::ostream &out, int indent_level) const { write_axes(std::ostream &out, int indent_level) const {
LightMutexHolder holder(_lock); LightMutexHolder holder(_lock);
bool any_controls = false; bool any_axis = false;
Controls::const_iterator ai; Axes::const_iterator ai;
for (ai = _controls.begin(); ai != _controls.end(); ++ai) { for (ai = _axes.begin(); ai != _axes.end(); ++ai) {
const AnalogState &state = (*ai); const AxisState &state = (*ai);
if (state.known) { if (state.known) {
any_controls = true; any_axis = true;
indent(out, indent_level) indent(out, indent_level)
<< (int)(ai - _controls.begin()) << ". " << state.state << "\n"; << (int)(ai - _axes.begin()) << ". " << state.value << "\n";
} }
} }
if (!any_controls) { if (!any_axis) {
indent(out, indent_level) indent(out, indent_level)
<< "(no known analog controls)\n"; << "(no known analog axes)\n";
} }
} }

View File

@ -33,12 +33,12 @@ typedef MouseData PointerData;
/** /**
* This is a structure representing a single input device. Input devices may * This is a structure representing a single input device. Input devices may
* have zero or more buttons, pointers, or controls associated with them, and * have zero or more buttons, pointers, or axes associated with them, and
* optionally a motion tracker. * optionally a motion tracker.
* *
* These devices are brought under a common interface because there is such a * These devices are brought under a common interface because there is such a
* large range of devices out there that may support any number of these types * large range of devices out there that may support any number of these types
* of controls, we couldn't even begin to cover them with type-specific * of axes, we couldn't even begin to cover them with type-specific
* subclasses. * subclasses.
* *
* Use the various has_() and get_num_() methods to determine information about * Use the various has_() and get_num_() methods to determine information about
@ -85,16 +85,6 @@ PUBLISHED:
DC_COUNT, DC_COUNT,
}; };
protected:
InputDevice(const std::string &name, DeviceClass dev_class, int flags);
public:
InputDevice();
InputDevice(const InputDevice &copy);
void operator = (const InputDevice &copy);
~InputDevice();
PUBLISHED:
enum class Axis { enum class Axis {
none, none,
@ -126,19 +116,94 @@ PUBLISHED:
brake, brake,
}; };
enum State {
S_unknown,
S_up,
S_down
};
class ButtonState {
public:
constexpr ButtonState() = default;
INLINE ButtonState(ButtonHandle handle);
ALWAYS_INLINE bool is_known() const;
ALWAYS_INLINE bool is_pressed() const;
PUBLISHED:
MAKE_PROPERTY(known, is_known);
MAKE_PROPERTY(pressed, is_pressed);
ButtonHandle handle = ButtonHandle::none();
public:
State _state = S_unknown;
};
class AxisState {
public:
constexpr AxisState() = default;
PUBLISHED:
Axis axis = Axis::none;
double value = 0.0;
bool known = false;
public:
double _scale = 1.0;
double _bias = 0.0;
};
class BatteryData {
PUBLISHED:
// Ranges from 0 through max_level.
short level = -1;
// Maximum value of 'level' field.
short max_level = -1;
};
protected:
InputDevice(const std::string &name, DeviceClass dev_class, int flags);
public:
InputDevice();
InputDevice(const InputDevice &copy);
void operator = (const InputDevice &copy);
~InputDevice();
INLINE std::string get_name() const; INLINE std::string get_name() const;
INLINE std::string get_manufacturer() const; INLINE std::string get_manufacturer() const;
INLINE std::string get_serial_number() const;
INLINE unsigned short get_vendor_id() const; INLINE unsigned short get_vendor_id() const;
INLINE unsigned short get_product_id() const; INLINE unsigned short get_product_id() const;
INLINE bool is_connected() const; INLINE bool is_connected() const;
INLINE DeviceClass get_device_class() const; INLINE DeviceClass get_device_class() const;
INLINE PointerData get_pointer() const;
INLINE TrackerData get_tracker() const;
INLINE BatteryData get_battery() const;
INLINE size_t get_num_buttons() const;
INLINE ButtonHandle get_button_map(size_t index) const;
INLINE bool is_button_pressed(size_t index) const;
INLINE bool is_button_known(size_t index) const;
INLINE ButtonState get_button(size_t index) const;
INLINE size_t get_num_axes() const;
INLINE double get_axis_value(size_t index) const;
INLINE bool is_axis_known(size_t index) const;
INLINE AxisState get_axis(size_t index) const;
PUBLISHED:
// The human-readable name of this input device. // The human-readable name of this input device.
MAKE_PROPERTY(name, get_name); MAKE_PROPERTY(name, get_name);
// The device's manufacturer, or the empty string if not known. // The device's manufacturer, or the empty string if not known.
MAKE_PROPERTY(manufacturer, get_manufacturer); MAKE_PROPERTY(manufacturer, get_manufacturer);
// The device's serial number, or the empty string if not known.
MAKE_PROPERTY(serial_number, get_serial_number);
// USB vendor ID of the device, or 0 if not known. // USB vendor ID of the device, or 0 if not known.
MAKE_PROPERTY(vendor_id, get_vendor_id); MAKE_PROPERTY(vendor_id, get_vendor_id);
@ -159,24 +224,22 @@ PUBLISHED:
INLINE bool has_vibration() const; INLINE bool has_vibration() const;
INLINE bool has_battery() const; INLINE bool has_battery() const;
INLINE PointerData get_pointer() const; // Getters for the various types of device data.
INLINE TrackerData get_tracker() const; MAKE_PROPERTY2(pointer, has_pointer, get_pointer);
MAKE_PROPERTY2(tracker, has_tracker, get_tracker);
MAKE_PROPERTY2(battery, has_battery, get_battery);
INLINE short get_battery_level() const; // Make device buttons and axes iterable
INLINE short get_max_battery_level() const; MAKE_SEQ_PROPERTY(buttons, get_num_buttons, get_button);
MAKE_SEQ_PROPERTY(axes, get_num_axes, get_axis);
INLINE size_t get_num_buttons() const; // Associate buttons/axes with symbolic handles.
INLINE void set_button_map(size_t index, ButtonHandle button); INLINE void map_button(size_t index, ButtonHandle handle);
INLINE ButtonHandle get_button_map(size_t index) const; INLINE void map_axis(size_t index, Axis axis);
INLINE bool get_button_state(size_t index) const; INLINE ButtonState find_button(ButtonHandle handle) const;
INLINE bool is_button_known(size_t index) const; INLINE AxisState find_axis(Axis axis) const;
INLINE size_t get_num_controls() const;
INLINE void set_control_map(size_t index, Axis axis);
INLINE Axis get_control_map(size_t index) const;
INLINE double get_control_state(size_t index) const;
INLINE bool is_control_known(size_t index) const;
// Enable rumble force-feedback effects
INLINE void set_vibration(double strong, double weak); INLINE void set_vibration(double strong, double weak);
INLINE void enable_pointer_events(); INLINE void enable_pointer_events();
@ -194,19 +257,20 @@ PUBLISHED:
static std::string format_axis(Axis axis); static std::string format_axis(Axis axis);
protected: protected:
// Called during the constructor to add new controls or buttons // Called during the constructor to add new axes or buttons
int add_control(Axis axis, int minimum, int maximum, bool centered); int add_button(ButtonHandle handle);
int add_control(Axis axis, int minimum, int maximum); int add_axis(Axis axis, int minimum, int maximum, bool centered);
int add_axis(Axis axis, int minimum, int maximum);
void set_pointer(bool inwin, double x, double y, double time); void set_pointer(bool inwin, double x, double y, double time);
void set_pointer_out_of_window(double time); void set_pointer_out_of_window(double time);
void pointer_moved(double x, double y, double time); void pointer_moved(double x, double y, double time);
void button_changed(int index, bool down); void button_changed(int index, bool down);
void control_changed(int index, int value); void axis_changed(int index, int value);
void set_control_state(int index, double state); void set_axis_value(int index, double state);
void set_tracker(const LPoint3 &pos, const LOrientation &orient, double time); void tracker_changed(const LPoint3 &pos, const LOrientation &orient, double time);
virtual void do_set_vibration(double low, double high); virtual void do_set_vibration(double low, double high);
virtual void do_poll(); virtual void do_poll();
@ -214,16 +278,9 @@ protected:
public: public:
INLINE void set_connected(bool connected); INLINE void set_connected(bool connected);
// We need these methods to make VC++ happy when we try to
// instantiate a pvector<InputDevice>. They don't do
// anything useful.
INLINE bool operator == (const InputDevice &other) const;
INLINE bool operator != (const InputDevice &other) const;
INLINE bool operator < (const InputDevice &other) const;
void output_buttons(std::ostream &out) const; void output_buttons(std::ostream &out) const;
void write_buttons(std::ostream &out, int indent_level) const; void write_buttons(std::ostream &out, int indent_level) const;
void write_controls(std::ostream &out, int indent_level) const; void write_axes(std::ostream &out, int indent_level) const;
protected: protected:
enum InputDeviceFlags { enum InputDeviceFlags {
@ -263,56 +320,14 @@ protected:
PT(PointerEventList) _pointer_events; PT(PointerEventList) _pointer_events;
PUBLISHED: PUBLISHED:
enum State {
S_unknown,
S_up,
S_down
};
class ButtonState {
public:
constexpr ButtonState() = default;
INLINE ButtonState(ButtonHandle handle);
PUBLISHED:
ButtonHandle handle = ButtonHandle::none();
State state = S_unknown;
};
typedef pvector<ButtonState> Buttons; typedef pvector<ButtonState> Buttons;
typedef pvector<AxisState> Axes;
Buttons _buttons; Buttons _buttons;
Axes _axes;
class AnalogState { BatteryData _battery_data;
public:
constexpr AnalogState() = default;
PUBLISHED:
Axis axis = Axis::none;
double state = 0.0;
bool known = false;
public:
double _scale = 1.0;
double _bias = 0.0;
};
typedef pvector<AnalogState> Controls;
Controls _controls;
short _battery_level;
short _max_battery_level;
TrackerData _tracker_data; TrackerData _tracker_data;
INLINE ButtonState get_button(size_t index) const;
INLINE ButtonState find_button(ButtonHandle handle) const;
INLINE AnalogState get_control(size_t index) const;
INLINE AnalogState find_control(Axis axis) const;
// Make device buttons and controls iterable
MAKE_SEQ_PROPERTY(buttons, get_num_buttons, get_button);
MAKE_SEQ_PROPERTY(controls, get_num_controls, get_control);
public: public:
static TypeHandle get_class_type() { static TypeHandle get_class_type() {
return _type_handle; return _type_handle;

View File

@ -46,9 +46,6 @@ PUBLISHED:
INLINE static InputDeviceManager *get_global_ptr(); INLINE static InputDeviceManager *get_global_ptr();
// The set of all currently connected devices.
MAKE_PROPERTY(devices, get_devices);
protected: protected:
LightMutex _lock; LightMutex _lock;

View File

@ -54,9 +54,27 @@ get_device() const {
void InputDeviceNode:: void InputDeviceNode::
do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
DataNodeTransmit &output) { DataNodeTransmit &output) {
if (_device == nullptr && !_device->is_connected()) {
return;
}
_device->poll();
// get all button events of the device and forward them to the data graph // get all button events of the device and forward them to the data graph
if (_device->has_button_event()) { if (_device->has_button_event()) {
PT(ButtonEventList) bel = _device->get_button_events(); PT(ButtonEventList) bel = _device->get_button_events();
// Register the current state for each button.
for (int i = 0; i < bel->get_num_events(); ++i) {
const ButtonEvent &event = bel->get_event(i);
if (event._type == ButtonEvent::T_down) {
_button_states[event._button] = true;
} else if (event._type == ButtonEvent::T_down) {
_button_states[event._button] = false;
}
}
output.set_data(_button_events_output, EventParameter(bel)); output.set_data(_button_events_output, EventParameter(bel));
} }

View File

@ -21,17 +21,22 @@
#include "linmath_events.h" #include "linmath_events.h"
/** /**
* Reads the controler data sent from the InputDeviceManager, and * Reads the controller data sent from the InputDeviceManager, and transmits
* transmits it down the data graph. * it down the data graph.
*
* *
* This is intended to only be accessed from the app thread.
*/ */
class EXPCL_PANDA_DEVICE InputDeviceNode : public DataNode { class EXPCL_PANDA_DEVICE InputDeviceNode : public DataNode {
PUBLISHED: PUBLISHED:
InputDeviceNode(InputDevice *device, const std::string &name); InputDeviceNode(InputDevice *device, const std::string &name);
public:
void set_device(InputDevice *device); void set_device(InputDevice *device);
PT(InputDevice) get_device() const; PT(InputDevice) get_device() const;
PUBLISHED:
MAKE_PROPERTY(device, get_device, set_device);
protected: protected:
// Inherited from DataNode // Inherited from DataNode
virtual void do_transmit_data(DataGraphTraverser *trav, virtual void do_transmit_data(DataGraphTraverser *trav,
@ -39,6 +44,9 @@ protected:
DataNodeTransmit &output); DataNodeTransmit &output);
private: private:
pmap<ButtonHandle, bool> _button_states;
pmap<InputDevice::Axis, double> _control_states;
// outputs // outputs
int _button_events_output; int _button_events_output;
int _low_battery_event_output; int _low_battery_event_output;

View File

@ -287,14 +287,14 @@ parse_element(IOHIDElementRef element) {
int max = IOHIDElementGetLogicalMax(element); int max = IOHIDElementGetLogicalMax(element);
if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) { if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) {
// T.Flight Hotas X throttle is reversed and can go backwards. // T.Flight Hotas X throttle is reversed and can go backwards.
add_control(axis, max, min, true); add_axis(axis, max, min, true);
} else if (axis == Axis::yaw || axis == Axis::rudder || axis == Axis::left_y || axis == Axis::right_y || } else if (axis == Axis::yaw || axis == Axis::rudder || axis == Axis::left_y || axis == Axis::right_y ||
(_device_class == DC_3d_mouse && (axis == Axis::y || axis == Axis::z || axis == Axis::roll))) { (_device_class == DC_3d_mouse && (axis == Axis::y || axis == Axis::z || axis == Axis::roll))) {
// We'd like to reverse the Y axis to match the XInput behavior. // We'd like to reverse the Y axis to match the XInput behavior.
// We also reverse yaw to obey the right-hand rule. // We also reverse yaw to obey the right-hand rule.
add_control(axis, max, min); add_axis(axis, max, min);
} else { } else {
add_control(axis, min, max); add_axis(axis, min, max);
} }
_analog_elements.push_back(element); _analog_elements.push_back(element);
@ -734,7 +734,7 @@ do_poll() {
IOHIDValueRef value_ref; IOHIDValueRef value_ref;
if (IOHIDDeviceGetValue(_device, _analog_elements[i], &value_ref) == kIOReturnSuccess) { if (IOHIDDeviceGetValue(_device, _analog_elements[i], &value_ref) == kIOReturnSuccess) {
int value = IOHIDValueGetIntegerValue(value_ref); int value = IOHIDValueGetIntegerValue(value_ref);
control_changed(i, value); axis_changed(i, value);
} }
} }

View File

@ -121,7 +121,7 @@ open_device() {
ioctl(_fd, JSIOCGBUTTONS, &num_buttons); ioctl(_fd, JSIOCGBUTTONS, &num_buttons);
_buttons.resize(num_buttons); _buttons.resize(num_buttons);
_controls.resize(num_axes); _axes.resize(num_axes);
if (num_buttons > 0) { if (num_buttons > 0) {
uint16_t btnmap[512]; uint16_t btnmap[512];
@ -225,11 +225,11 @@ open_device() {
_dpad_x_axis = i; _dpad_x_axis = i;
_dpad_left_button = (int)_buttons.size(); _dpad_left_button = (int)_buttons.size();
if (_device_class == DC_gamepad) { if (_device_class == DC_gamepad) {
_buttons.push_back(ButtonState(GamepadButton::dpad_left())); add_button(GamepadButton::dpad_left());
_buttons.push_back(ButtonState(GamepadButton::dpad_right())); add_button(GamepadButton::dpad_right());
} else { } else {
_buttons.push_back(ButtonState(GamepadButton::hat_left())); add_button(GamepadButton::hat_left());
_buttons.push_back(ButtonState(GamepadButton::hat_right())); add_button(GamepadButton::hat_right());
} }
axis = Axis::none; axis = Axis::none;
} }
@ -241,11 +241,11 @@ open_device() {
_dpad_y_axis = i; _dpad_y_axis = i;
_dpad_up_button = (int)_buttons.size(); _dpad_up_button = (int)_buttons.size();
if (_device_class == DC_gamepad) { if (_device_class == DC_gamepad) {
_buttons.push_back(ButtonState(GamepadButton::dpad_up())); add_button(GamepadButton::dpad_up());
_buttons.push_back(ButtonState(GamepadButton::dpad_down())); add_button(GamepadButton::dpad_down());
} else { } else {
_buttons.push_back(ButtonState(GamepadButton::hat_up())); add_button(GamepadButton::hat_up());
_buttons.push_back(ButtonState(GamepadButton::hat_down())); add_button(GamepadButton::hat_down());
} }
axis = Axis::none; axis = Axis::none;
} }
@ -259,28 +259,28 @@ open_device() {
axis = Axis::none; axis = Axis::none;
break; break;
} }
_controls[i].axis = axis; _axes[i].axis = axis;
if (axis == Axis::left_trigger || axis == Axis::right_trigger) { if (axis == Axis::left_trigger || axis == Axis::right_trigger) {
// We'd like to use 0.0 to indicate the resting position. // We'd like to use 0.0 to indicate the resting position.
_controls[i]._scale = 1.0 / 65534.0; _axes[i]._scale = 1.0 / 65534.0;
_controls[i]._bias = 0.5; _axes[i]._bias = 0.5;
have_analog_triggers = true; have_analog_triggers = true;
} else if (axis == Axis::left_y || axis == Axis::right_y || axis == Axis::y) { } else if (axis == Axis::left_y || axis == Axis::right_y || axis == Axis::y) {
_controls[i]._scale = 1.0 / -32767.0; _axes[i]._scale = 1.0 / -32767.0;
_controls[i]._bias = 0.0; _axes[i]._bias = 0.0;
} else { } else {
_controls[i]._scale = 1.0 / 32767.0; _axes[i]._scale = 1.0 / 32767.0;
_controls[i]._bias = 0.0; _axes[i]._bias = 0.0;
} }
} }
} }
if (_ltrigger_button >= 0 && _rtrigger_button >= 0 && !have_analog_triggers) { if (_ltrigger_button >= 0 && _rtrigger_button >= 0 && !have_analog_triggers) {
// Emulate analog triggers. // Emulate analog triggers.
_ltrigger_control = (int)_controls.size(); _ltrigger_control = (int)_axes.size();
add_control(Axis::left_trigger, 0, 1, false); add_axis(Axis::left_trigger, 0, 1, false);
add_control(Axis::right_trigger, 0, 1, false); add_axis(Axis::right_trigger, 0, 1, false);
} else { } else {
_ltrigger_button = -1; _ltrigger_button = -1;
_rtrigger_button = -1; _rtrigger_button = -1;
@ -343,8 +343,8 @@ open_device() {
// are all 0, which indicates that the driver hasn't received any data for // are all 0, which indicates that the driver hasn't received any data for
// this gamepad yet (which means it hasn't been plugged in for this session) // this gamepad yet (which means it hasn't been plugged in for this session)
if (strncmp(name, "Xbox 360 Wireless Receiver", 26) == 0) { if (strncmp(name, "Xbox 360 Wireless Receiver", 26) == 0) {
for (const auto &control : _controls) { for (const auto &control : _axes) {
if (control.state != 0.0) { if (control.value != 0.0) {
_is_connected = true; _is_connected = true;
return true; return true;
} }
@ -398,9 +398,9 @@ process_events() {
if (events[i].type & JS_EVENT_BUTTON) { if (events[i].type & JS_EVENT_BUTTON) {
if (index == _ltrigger_button) { if (index == _ltrigger_button) {
control_changed(_ltrigger_control, events[i].value); axis_changed(_ltrigger_control, events[i].value);
} else if (index == _rtrigger_button) { } else if (index == _rtrigger_button) {
control_changed(_ltrigger_control + 1, events[i].value); axis_changed(_ltrigger_control + 1, events[i].value);
} }
button_changed(index, (events[i].value != 0)); button_changed(index, (events[i].value != 0));
@ -413,7 +413,7 @@ process_events() {
button_changed(_dpad_up_button+1, events[i].value > 1000); button_changed(_dpad_up_button+1, events[i].value > 1000);
} }
control_changed(index, events[i].value); axis_changed(index, events[i].value);
} }
} }

View File

@ -362,11 +362,11 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
gamepad_buttons = gamepad_buttons_snes; gamepad_buttons = gamepad_buttons_snes;
} }
// Prepare a mapping of data indices to button/control indices. // Prepare a mapping of data indices to button/axis indices.
_indices.resize(caps.NumberInputDataIndices); _indices.resize(caps.NumberInputDataIndices);
_buttons.clear(); _buttons.clear();
_controls.clear(); _axes.clear();
USHORT num_button_caps = caps.NumberInputButtonCaps; USHORT num_button_caps = caps.NumberInputButtonCaps;
PHIDP_BUTTON_CAPS button_caps = (PHIDP_BUTTON_CAPS)alloca(num_button_caps * sizeof(HIDP_BUTTON_CAPS)); PHIDP_BUTTON_CAPS button_caps = (PHIDP_BUTTON_CAPS)alloca(num_button_caps * sizeof(HIDP_BUTTON_CAPS));
@ -553,17 +553,17 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
break; break;
} }
int control_index; int axis_index;
if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) { if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) {
// T.Flight Hotas X throttle is reversed and can go backwards. // T.Flight Hotas X throttle is reversed and can go backwards.
control_index = add_control(axis, cap.LogicalMax, cap.LogicalMin, true); axis_index = add_axis(axis, cap.LogicalMax, cap.LogicalMin, true);
} else if (!is_signed) { } else if (!is_signed) {
// All axes on the weird XInput-style mappings go from -1 to 1 // All axes on the weird XInput-style mappings go from -1 to 1
control_index = add_control(axis, cap.LogicalMin, cap.LogicalMax, true); axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax, true);
} else { } else {
control_index = add_control(axis, cap.LogicalMin, cap.LogicalMax); axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax);
} }
_indices[data_index] = Index::control(control_index, is_signed); _indices[data_index] = Index::axis(axis_index, is_signed);
} }
} }
@ -635,11 +635,11 @@ on_input(PRAWINPUT input) {
for (ULONG di = 0; di < count; ++di) { for (ULONG di = 0; di < count; ++di) {
if (data[di].DataIndex != _hat_data_index) { if (data[di].DataIndex != _hat_data_index) {
const Index &idx = _indices[data[di].DataIndex]; const Index &idx = _indices[data[di].DataIndex];
if (idx._control >= 0) { if (idx._axis >= 0) {
if (idx._signed) { if (idx._signed) {
control_changed(idx._control, (SHORT)data[di].RawValue); axis_changed(idx._axis, (SHORT)data[di].RawValue);
} else { } else {
control_changed(idx._control, data[di].RawValue); axis_changed(idx._axis, data[di].RawValue);
} }
} }
if (idx._button >= 0) { if (idx._button >= 0) {

View File

@ -49,28 +49,28 @@ private:
// Indexed by report ID // Indexed by report ID
pvector<BitArray> _report_buttons; pvector<BitArray> _report_buttons;
// Either a button index or a control index. // Either a button index or a axis index.
struct Index { struct Index {
Index() : _button(-1), _control(-1) {} Index() : _button(-1), _axis(-1) {}
static Index button(int index) { static Index button(int index) {
Index idx; Index idx;
idx._button = index; idx._button = index;
return idx; return idx;
} }
static Index control(int index, bool is_signed=true) { static Index axis(int index, bool is_signed=true) {
Index idx; Index idx;
idx._control = index; idx._axis = index;
idx._signed = is_signed; idx._signed = is_signed;
return idx; return idx;
} }
int _button; int _button;
int _control; int _axis;
bool _signed; bool _signed;
}; };
// Maps a "data index" to either button index or control index. // Maps a "data index" to either button index or axis index.
pvector<Index> _indices; pvector<Index> _indices;
int _hat_data_index; int _hat_data_index;
int _hat_data_minimum; int _hat_data_minimum;

View File

@ -126,7 +126,7 @@ XInputDevice(DWORD user_index) :
nassertv(user_index >= 0 && user_index < XUSER_MAX_COUNT); nassertv(user_index >= 0 && user_index < XUSER_MAX_COUNT);
_controls.resize(6); _axes.resize(6);
_buttons.resize(16); _buttons.resize(16);
} }
@ -301,6 +301,7 @@ init_xinput() {
/** /**
* Initializes the device. Called when the device was just connected. * Initializes the device. Called when the device was just connected.
* Assumes either the lock is held or this is called from the constructor.
*/ */
void XInputDevice:: void XInputDevice::
init_device(const XINPUT_CAPABILITIES_EX &caps, const XINPUT_STATE &state) { init_device(const XINPUT_CAPABILITIES_EX &caps, const XINPUT_STATE &state) {
@ -315,81 +316,81 @@ init_device(const XINPUT_CAPABILITIES_EX &caps, const XINPUT_STATE &state) {
default: default:
case XINPUT_DEVSUBTYPE_GAMEPAD: case XINPUT_DEVSUBTYPE_GAMEPAD:
_device_class = DC_gamepad; _device_class = DC_gamepad;
set_control_map(0, Axis::left_trigger); _axes[0].axis = Axis::left_trigger;
set_control_map(1, Axis::right_trigger); _axes[1].axis = Axis::right_trigger;
set_control_map(2, Axis::left_x); _axes[2].axis = Axis::left_x;
set_control_map(3, Axis::left_y); _axes[3].axis = Axis::left_y;
set_control_map(4, Axis::right_x); _axes[4].axis = Axis::right_x;
set_control_map(5, Axis::right_y); _axes[5].axis = Axis::right_y;
break; break;
case XINPUT_DEVSUBTYPE_WHEEL: case XINPUT_DEVSUBTYPE_WHEEL:
_device_class = DC_steering_wheel; _device_class = DC_steering_wheel;
set_control_map(0, Axis::brake); _axes[0].axis = Axis::brake;
set_control_map(1, Axis::accelerator); _axes[1].axis = Axis::accelerator;
set_control_map(2, Axis::wheel); _axes[2].axis = Axis::wheel;
set_control_map(3, Axis::none); _axes[3].axis = Axis::none;
set_control_map(4, Axis::none); _axes[4].axis = Axis::none;
set_control_map(5, Axis::none); _axes[5].axis = Axis::none;
break; break;
case XINPUT_DEVSUBTYPE_FLIGHT_STICK: case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
_device_class = DC_flight_stick; _device_class = DC_flight_stick;
set_control_map(0, Axis::yaw); _axes[0].axis = Axis::yaw;
set_control_map(1, Axis::throttle); _axes[1].axis = Axis::throttle;
set_control_map(2, Axis::roll); _axes[2].axis = Axis::roll;
set_control_map(3, Axis::pitch); _axes[3].axis = Axis::pitch;
set_control_map(4, Axis::none); _axes[4].axis = Axis::none;
set_control_map(5, Axis::none); _axes[5].axis = Axis::none;
break; break;
case XINPUT_DEVSUBTYPE_DANCE_PAD: case XINPUT_DEVSUBTYPE_DANCE_PAD:
_device_class = DC_dance_pad; _device_class = DC_dance_pad;
set_control_map(0, Axis::none); _axes[0].axis = Axis::none;
set_control_map(1, Axis::none); _axes[1].axis = Axis::none;
set_control_map(2, Axis::none); _axes[2].axis = Axis::none;
set_control_map(3, Axis::none); _axes[3].axis = Axis::none;
set_control_map(4, Axis::none); _axes[4].axis = Axis::none;
set_control_map(5, Axis::none); _axes[5].axis = Axis::none;
break; break;
} }
_controls[0]._scale = 1.0 / 255.0; _axes[0]._scale = 1.0 / 255.0;
_controls[1]._scale = 1.0 / 255.0; _axes[1]._scale = 1.0 / 255.0;
_controls[2]._scale = 1.0 / 32767.5; _axes[2]._scale = 1.0 / 32767.5;
_controls[3]._scale = 1.0 / 32767.5; _axes[3]._scale = 1.0 / 32767.5;
_controls[4]._scale = 1.0 / 32767.5; _axes[4]._scale = 1.0 / 32767.5;
_controls[5]._scale = 1.0 / 32767.5; _axes[5]._scale = 1.0 / 32767.5;
_controls[2]._bias = 0.5 / 32767.5; _axes[2]._bias = 0.5 / 32767.5;
_controls[3]._bias = 0.5 / 32767.5; _axes[3]._bias = 0.5 / 32767.5;
_controls[4]._bias = 0.5 / 32767.5; _axes[4]._bias = 0.5 / 32767.5;
_controls[5]._bias = 0.5 / 32767.5; _axes[5]._bias = 0.5 / 32767.5;
if (caps.Flags & XINPUT_CAPS_NO_NAVIGATION) { if (caps.Flags & XINPUT_CAPS_NO_NAVIGATION) {
set_button_map(0, ButtonHandle::none()); _buttons[0].handle = ButtonHandle::none();
set_button_map(1, ButtonHandle::none()); _buttons[1].handle = ButtonHandle::none();
set_button_map(2, ButtonHandle::none()); _buttons[2].handle = ButtonHandle::none();
set_button_map(3, ButtonHandle::none()); _buttons[3].handle = ButtonHandle::none();
set_button_map(4, ButtonHandle::none()); _buttons[4].handle = ButtonHandle::none();
set_button_map(5, ButtonHandle::none()); _buttons[5].handle = ButtonHandle::none();
} else { } else {
set_button_map(0, GamepadButton::dpad_up()); _buttons[0].handle = GamepadButton::dpad_up();
set_button_map(1, GamepadButton::dpad_down()); _buttons[1].handle = GamepadButton::dpad_down();
set_button_map(2, GamepadButton::dpad_left()); _buttons[2].handle = GamepadButton::dpad_left();
set_button_map(3, GamepadButton::dpad_right()); _buttons[3].handle = GamepadButton::dpad_right();
set_button_map(4, GamepadButton::start()); _buttons[4].handle = GamepadButton::start();
set_button_map(5, GamepadButton::back()); _buttons[5].handle = GamepadButton::back();
} }
set_button_map(6, GamepadButton::lstick()); _buttons[6].handle = GamepadButton::lstick();
set_button_map(7, GamepadButton::rstick()); _buttons[7].handle = GamepadButton::rstick();
set_button_map(8, GamepadButton::lshoulder()); _buttons[8].handle = GamepadButton::lshoulder();
set_button_map(9, GamepadButton::rshoulder()); _buttons[9].handle = GamepadButton::rshoulder();
set_button_map(10, GamepadButton::guide()); _buttons[10].handle = GamepadButton::guide();
set_button_map(11, GamepadButton::face_a()); _buttons[11].handle = GamepadButton::face_a();
set_button_map(12, GamepadButton::face_b()); _buttons[12].handle = GamepadButton::face_b();
set_button_map(13, GamepadButton::face_x()); _buttons[13].handle = GamepadButton::face_x();
set_button_map(14, GamepadButton::face_y()); _buttons[14].handle = GamepadButton::face_y();
if (caps.Vibration.wLeftMotorSpeed != 0 || if (caps.Vibration.wLeftMotorSpeed != 0 ||
caps.Vibration.wRightMotorSpeed != 0) { caps.Vibration.wRightMotorSpeed != 0) {
@ -403,8 +404,8 @@ init_device(const XINPUT_CAPABILITIES_EX &caps, const XINPUT_STATE &state) {
batt.BatteryType != BATTERY_TYPE_WIRED) { batt.BatteryType != BATTERY_TYPE_WIRED) {
// This device has a battery. Report the battery level. // This device has a battery. Report the battery level.
_flags |= IDF_has_battery; _flags |= IDF_has_battery;
_battery_level = batt.BatteryLevel; _battery_data.level = batt.BatteryLevel;
_max_battery_level = BATTERY_LEVEL_FULL; _battery_data.max_level = BATTERY_LEVEL_FULL;
} }
} }
} }
@ -413,7 +414,7 @@ init_device(const XINPUT_CAPABILITIES_EX &caps, const XINPUT_STATE &state) {
WORD mask = 1; WORD mask = 1;
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
// Set the state without triggering a button event. // Set the state without triggering a button event.
_buttons[i].state = (buttons & mask) ? S_down : S_up; _buttons[i]._state = (buttons & mask) ? S_down : S_up;
mask <<= 1; mask <<= 1;
if (i == 10) { if (i == 10) {
// XInput skips 0x0800. // XInput skips 0x0800.
@ -421,12 +422,12 @@ init_device(const XINPUT_CAPABILITIES_EX &caps, const XINPUT_STATE &state) {
} }
} }
control_changed(0, state.Gamepad.bLeftTrigger); axis_changed(0, state.Gamepad.bLeftTrigger);
control_changed(1, state.Gamepad.bRightTrigger); axis_changed(1, state.Gamepad.bRightTrigger);
control_changed(2, state.Gamepad.sThumbLX); axis_changed(2, state.Gamepad.sThumbLX);
control_changed(3, state.Gamepad.sThumbLY); axis_changed(3, state.Gamepad.sThumbLY);
control_changed(4, state.Gamepad.sThumbRX); axis_changed(4, state.Gamepad.sThumbRX);
control_changed(5, state.Gamepad.sThumbRY); axis_changed(5, state.Gamepad.sThumbRY);
_last_buttons = buttons; _last_buttons = buttons;
_last_packet = state.dwPacketNumber; _last_packet = state.dwPacketNumber;
@ -491,12 +492,12 @@ do_poll() {
} }
} }
control_changed(0, state.Gamepad.bLeftTrigger); axis_changed(0, state.Gamepad.bLeftTrigger);
control_changed(1, state.Gamepad.bRightTrigger); axis_changed(1, state.Gamepad.bRightTrigger);
control_changed(2, state.Gamepad.sThumbLX); axis_changed(2, state.Gamepad.sThumbLX);
control_changed(3, state.Gamepad.sThumbLY); axis_changed(3, state.Gamepad.sThumbLY);
control_changed(4, state.Gamepad.sThumbRX); axis_changed(4, state.Gamepad.sThumbRX);
control_changed(5, state.Gamepad.sThumbRY); axis_changed(5, state.Gamepad.sThumbRY);
_last_buttons = state.Gamepad.wButtons; _last_buttons = state.Gamepad.wButtons;
_last_packet = state.dwPacketNumber; _last_packet = state.dwPacketNumber;

View File

@ -100,13 +100,7 @@ vrpn_analog_callback(void *userdata, const vrpn_ANALOGCB info) {
for (di = self->_devices.begin(); di != self->_devices.end(); ++di) { for (di = self->_devices.begin(); di != self->_devices.end(); ++di) {
VrpnAnalogDevice *device = (*di); VrpnAnalogDevice *device = (*di);
for (int i = 0; i < info.num_channel; i++) { for (int i = 0; i < info.num_channel; i++) {
if (vrpn_cat.is_debug()) { device->set_axis_value(i, info.channel[i]);
if (device->get_control_state(i) != info.channel[i]) {
vrpn_cat.debug()
<< *self << " got analog " << i << " = " << info.channel[i] << "\n";
}
}
device->set_control_state(i, info.channel[i]);
} }
} }
} }

View File

@ -107,7 +107,7 @@ vrpn_position_callback(void *userdata, const vrpn_TRACKERCB info) {
VrpnTrackerDevice *device = (*di); VrpnTrackerDevice *device = (*di);
if (device->get_sensor() == info.sensor && if (device->get_sensor() == info.sensor &&
device->get_data_type() == VrpnTrackerDevice::DT_position) { device->get_data_type() == VrpnTrackerDevice::DT_position) {
device->set_tracker(LPoint3(info.pos[0], info.pos[1], info.pos[2]), device->tracker_changed(LPoint3(info.pos[0], info.pos[1], info.pos[2]),
LOrientation(info.quat[3], info.quat[0], LOrientation(info.quat[3], info.quat[0],
info.quat[1], info.quat[2]), info.quat[1], info.quat[2]),
VrpnClient::convert_to_secs(info.msg_time)); VrpnClient::convert_to_secs(info.msg_time));
@ -132,7 +132,7 @@ vrpn_velocity_callback(void *userdata, const vrpn_TRACKERVELCB info) {
VrpnTrackerDevice *device = (*di); VrpnTrackerDevice *device = (*di);
if (device->get_sensor() == info.sensor && if (device->get_sensor() == info.sensor &&
device->get_data_type() == VrpnTrackerDevice::DT_velocity) { device->get_data_type() == VrpnTrackerDevice::DT_velocity) {
device->set_tracker(LPoint3(info.vel[0], info.vel[1], info.vel[2]), device->tracker_changed(LPoint3(info.vel[0], info.vel[1], info.vel[2]),
LOrientation(info.vel_quat[3], info.vel_quat[0], LOrientation(info.vel_quat[3], info.vel_quat[0],
info.vel_quat[1], info.vel_quat[2]), info.vel_quat[1], info.vel_quat[2]),
VrpnClient::convert_to_secs(info.msg_time)); VrpnClient::convert_to_secs(info.msg_time));
@ -157,7 +157,7 @@ vrpn_acceleration_callback(void *userdata, const vrpn_TRACKERACCCB info) {
VrpnTrackerDevice *device = (*di); VrpnTrackerDevice *device = (*di);
if (device->get_sensor() == info.sensor && if (device->get_sensor() == info.sensor &&
device->get_data_type() == VrpnTrackerDevice::DT_acceleration) { device->get_data_type() == VrpnTrackerDevice::DT_acceleration) {
device->set_tracker(LPoint3(info.acc[0], info.acc[1], info.acc[2]), device->tracker_changed(LPoint3(info.acc[0], info.acc[1], info.acc[2]),
LOrientation(info.acc_quat[3], info.acc_quat[0], LOrientation(info.acc_quat[3], info.acc_quat[0],
info.acc_quat[1], info.acc_quat[2]), info.acc_quat[1], info.acc_quat[2]),
VrpnClient::convert_to_secs(info.msg_time)); VrpnClient::convert_to_secs(info.msg_time));