diff --git a/panda/src/device/evdevInputDevice.cxx b/panda/src/device/evdevInputDevice.cxx index 191edabbc1..73e247462b 100644 --- a/panda/src/device/evdevInputDevice.cxx +++ b/panda/src/device/evdevInputDevice.cxx @@ -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]; diff --git a/panda/src/device/evdevInputDevice.h b/panda/src/device/evdevInputDevice.h index a86a7d1434..6eff8534a5 100644 --- a/panda/src/device/evdevInputDevice.h +++ b/panda/src/device/evdevInputDevice.h @@ -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; diff --git a/panda/src/device/inputDevice.I b/panda/src/device/inputDevice.I index 5083e3fdda..06e8d2227e 100644 --- a/panda/src/device/inputDevice.I +++ b/panda/src/device/inputDevice.I @@ -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 diff --git a/panda/src/device/inputDevice.cxx b/panda/src/device/inputDevice.cxx index 4637673672..0b4279e34e 100644 --- a/panda/src/device/inputDevice.cxx +++ b/panda/src/device/inputDevice.cxx @@ -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 diff --git a/panda/src/device/inputDevice.h b/panda/src/device/inputDevice.h index 5e33115a3e..99a8e059ca 100644 --- a/panda/src/device/inputDevice.h +++ b/panda/src/device/inputDevice.h @@ -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: diff --git a/panda/src/device/inputDeviceManager.cxx b/panda/src/device/inputDeviceManager.cxx index e160a70746..1b12786d62 100644 --- a/panda/src/device/inputDeviceManager.cxx +++ b/panda/src/device/inputDeviceManager.cxx @@ -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;