Add gamepad rumble support on Linux
This commit is contained in:
parent
ab66108661
commit
687e080bdd
|
|
@ -40,11 +40,25 @@ TypeHandle EvdevInputDevice::_type_handle;
|
|||
// device using the given device filename.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
EvdevInputDevice::
|
||||
EvdevInputDevice(int index) : _index(index) {
|
||||
EvdevInputDevice(int index) :
|
||||
_index(index),
|
||||
_fd(-1),
|
||||
_can_write(false),
|
||||
_ff_id(-1),
|
||||
_ff_playing(false),
|
||||
_ff_strong(-1),
|
||||
_ff_weak(-1) {
|
||||
|
||||
char path[64];
|
||||
sprintf(path, "/dev/input/event%d", index);
|
||||
|
||||
_fd = open(path, O_RDONLY | O_NONBLOCK);
|
||||
_fd = open(path, O_RDWR | O_NONBLOCK);
|
||||
if (_fd >= 0) {
|
||||
_can_write = true;
|
||||
} else {
|
||||
// On failure, open device as read-only.
|
||||
_fd = open(path, O_RDONLY | O_NONBLOCK);
|
||||
}
|
||||
|
||||
if (_fd >= 0) {
|
||||
init_device();
|
||||
|
|
@ -63,21 +77,74 @@ EvdevInputDevice(int index) : _index(index) {
|
|||
EvdevInputDevice::
|
||||
~EvdevInputDevice() {
|
||||
if (_fd != -1) {
|
||||
if (_ff_id != -1) {
|
||||
// Remove force-feedback effect.
|
||||
do_set_vibration(0, 0);
|
||||
ioctl(_fd, EVIOCRMFF, _ff_id);
|
||||
_ff_id = -1;
|
||||
}
|
||||
|
||||
close(_fd);
|
||||
_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EvdevInputDevice::check_events
|
||||
// Access: Public
|
||||
// Description: Returns true if there are pending events.
|
||||
// Function: EvdevInputDevice::do_set_vibration
|
||||
// Access: Private, Virtual
|
||||
// Description: Sets the vibration strength. The first argument
|
||||
// controls a low-frequency motor, if present, and
|
||||
// the latter controls a high-frequency motor. The
|
||||
// values are within the 0-1 range.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool EvdevInputDevice::
|
||||
check_events() const {
|
||||
unsigned int avail = 0;
|
||||
ioctl(_fd, FIONREAD, &avail);
|
||||
return (avail != 0);
|
||||
void EvdevInputDevice::
|
||||
do_set_vibration(double strong, double weak) {
|
||||
if (_fd == -1 || !_can_write) {
|
||||
return;
|
||||
}
|
||||
|
||||
int strong_level = strong * 0xffff;
|
||||
int weak_level = weak * 0xffff;
|
||||
|
||||
if (strong_level == _ff_strong && weak_level == _ff_weak) {
|
||||
// No change.
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload the new effect parameters. Do this even if we are about
|
||||
// to stop the effect, because some drivers don't respond to simply
|
||||
// stopping the effect.
|
||||
struct ff_effect effect;
|
||||
effect.type = FF_RUMBLE;
|
||||
effect.id = _ff_id;
|
||||
effect.direction = 0;
|
||||
effect.trigger.button = 0;
|
||||
effect.trigger.interval = 0;
|
||||
effect.replay.length = 0;
|
||||
effect.replay.delay = 0;
|
||||
effect.u.rumble.strong_magnitude = strong_level;
|
||||
effect.u.rumble.weak_magnitude = weak_level;
|
||||
|
||||
if (ioctl(_fd, EVIOCSFF, &effect) < 0) {
|
||||
return;
|
||||
} else {
|
||||
_ff_id = effect.id;
|
||||
_ff_strong = strong_level;
|
||||
_ff_weak = weak_level;
|
||||
}
|
||||
|
||||
if (!_ff_playing) {
|
||||
// Start the effect. We could pass 0 as value to stop the effect
|
||||
// when a level of 0 is requested, but my driver seems to ignore it.
|
||||
_ff_playing = true;
|
||||
|
||||
struct input_event play;
|
||||
play.type = EV_FF;
|
||||
play.code = _ff_id;
|
||||
play.value = 1;
|
||||
|
||||
write(_fd, &play, sizeof(play));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -227,7 +294,20 @@ init_device() {
|
|||
}
|
||||
|
||||
if (test_bit(EV_FF, evtypes)) {
|
||||
_flags |= IDF_has_vibration;
|
||||
uint8_t effects[(FF_CNT + 7) >> 3];
|
||||
memset(effects, 0, sizeof(effects));
|
||||
ioctl(_fd, EVIOCGBIT(EV_FF, sizeof(effects)), effects);
|
||||
|
||||
if (test_bit(FF_RUMBLE, effects)) {
|
||||
if (_can_write) {
|
||||
_flags |= IDF_has_vibration;
|
||||
} else {
|
||||
// Let the user know what he's missing out on.
|
||||
device_cat.warning()
|
||||
<< "/dev/input/event" << _index << " is not writable, vibration "
|
||||
<< "effects will be unavailable.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char path[64];
|
||||
|
|
|
|||
|
|
@ -29,9 +29,8 @@ public:
|
|||
EvdevInputDevice(int index);
|
||||
virtual ~EvdevInputDevice();
|
||||
|
||||
bool check_events() const;
|
||||
|
||||
private:
|
||||
virtual void do_set_vibration(double strong, double weak);
|
||||
virtual void do_poll();
|
||||
|
||||
bool init_device();
|
||||
|
|
@ -40,6 +39,11 @@ private:
|
|||
private:
|
||||
int _index;
|
||||
int _fd;
|
||||
bool _can_write;
|
||||
int _ff_id;
|
||||
bool _ff_playing;
|
||||
int _ff_strong;
|
||||
int _ff_weak;
|
||||
|
||||
struct AxisRange {
|
||||
double _scale;
|
||||
|
|
|
|||
|
|
@ -393,6 +393,21 @@ is_control_known(int index) const {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: InputDevice::set_vibration
|
||||
// Access: Public
|
||||
// Description: 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
|
||||
// motor, whereas the second controls the high-frequency
|
||||
// motor, if present.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void InputDevice::
|
||||
set_vibration(double strong, double weak) {
|
||||
LightMutexHolder holder(_lock);
|
||||
do_set_vibration(max(min(strong, 1.0), 0.0), max(min(weak, 1.0), 0.0));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: InputDevice::enable_pointer_events
|
||||
// Access: Public
|
||||
|
|
|
|||
|
|
@ -417,6 +417,18 @@ write_controls(ostream &out, int indent_level) const {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: InputDevice::do_set_vibration
|
||||
// Access: Private, Virtual
|
||||
// Description: Sets the vibration strength. The first argument
|
||||
// controls a low-frequency motor, if present, and
|
||||
// the latter controls a high-frequency motor. The
|
||||
// values are within the 0-1 range.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void InputDevice::
|
||||
do_set_vibration(double strong, double weak) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: InputDevice::do_poll
|
||||
// Access: Protected, Virtual
|
||||
|
|
|
|||
|
|
@ -160,6 +160,8 @@ PUBLISHED:
|
|||
INLINE double get_control_state(int index) const;
|
||||
INLINE bool is_control_known(int index) const;
|
||||
|
||||
INLINE void set_vibration(double strong, double weak);
|
||||
|
||||
INLINE void enable_pointer_events();
|
||||
INLINE void disable_pointer_events();
|
||||
|
||||
|
|
@ -179,6 +181,7 @@ protected:
|
|||
void set_control_state(int index, double state);
|
||||
void set_tracker(const LPoint3 &pos, const LOrientation &orient, double time);
|
||||
|
||||
virtual void do_set_vibration(double low, double high);
|
||||
virtual void do_poll();
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -202,10 +202,8 @@ consider_add_evdev_device(int ev_index) {
|
|||
// This seemed to work. Display a warning to the user indicating
|
||||
// that they might want to configure udev properly.
|
||||
device_cat.warning()
|
||||
<< "Some features of " << device->get_device_class()
|
||||
<< " device " << device->get_name() << " are not available due"
|
||||
" to lack of read permissions on /dev/input/event" << ev_index
|
||||
<< ".\n";
|
||||
<< "/dev/input/event" << ev_index << " is not readable, some "
|
||||
"features will be unavailable.\n";
|
||||
}
|
||||
_evdev_devices[ev_index] = device;
|
||||
return device;
|
||||
|
|
|
|||
Loading…
Reference in New Issue