From c1423e345e8c54a2aa1280998473a378d125994e Mon Sep 17 00:00:00 2001 From: pixl Date: Fri, 21 Aug 2020 16:05:20 -0400 Subject: [PATCH] Support HiresScroll gesture remapping This commit allows HiresScroll (when target is true) to map the up and down events to gestures that support it (i.e.AxisGesture/ IntervalGesture). This check is done by checking if wheelCompatibility() is true. This also allows hires scroll events to send low-res scroll events as well. TODO: Fix bug w/ Chromium (and some other programs?) where mapping scroll wheel to REL_WHEEL_HI_RES will cause the program to skip events occassionally. I have literally been stuck on this bug for a week and I still don't know what causes it. evtest shows proper scroll events, Firefox works fine, and libinput test-gui reports proper scrolling. --- src/logid/InputDevice.cpp | 11 ++ src/logid/InputDevice.h | 2 + src/logid/actions/gesture/AxisGesture.cpp | 53 ++++++- src/logid/actions/gesture/AxisGesture.h | 8 +- src/logid/actions/gesture/Gesture.h | 3 +- src/logid/actions/gesture/IntervalGesture.cpp | 9 +- src/logid/actions/gesture/IntervalGesture.h | 3 +- src/logid/actions/gesture/NullGesture.cpp | 9 +- src/logid/actions/gesture/NullGesture.h | 3 +- src/logid/actions/gesture/ReleaseGesture.cpp | 9 +- src/logid/actions/gesture/ReleaseGesture.h | 3 +- .../backend/hidpp20/features/HiresScroll.h | 2 +- src/logid/features/HiresScroll.cpp | 143 +++++++++++++++++- src/logid/features/HiresScroll.h | 10 ++ 14 files changed, 251 insertions(+), 17 deletions(-) diff --git a/src/logid/InputDevice.cpp b/src/logid/InputDevice.cpp index aecf548..62a74bc 100644 --- a/src/logid/InputDevice.cpp +++ b/src/logid/InputDevice.cpp @@ -89,6 +89,17 @@ uint InputDevice::toAxisCode(const std::string& name) return _toEventCode(EV_REL, name); } +/* Returns -1 if axis_code is not hi-res */ +int InputDevice::getLowResAxis(const uint axis_code) +{ + if(axis_code == REL_WHEEL_HI_RES) + return REL_WHEEL; + if(axis_code == REL_HWHEEL_HI_RES) + return REL_HWHEEL; + + return -1; +} + uint InputDevice::_toEventCode(uint type, const std::string& name) { int code = libevdev_event_code_from_name(type, name.c_str()); diff --git a/src/logid/InputDevice.h b/src/logid/InputDevice.h index 5b2cd22..8815d05 100644 --- a/src/logid/InputDevice.h +++ b/src/logid/InputDevice.h @@ -49,6 +49,8 @@ namespace logid static uint toKeyCode(const std::string& name); static uint toAxisCode(const std::string& name); + static int getLowResAxis(uint axis_code); + private: void _sendEvent(uint type, uint code, int value); diff --git a/src/logid/actions/gesture/AxisGesture.cpp b/src/logid/actions/gesture/AxisGesture.cpp index 615c893..9325e8c 100644 --- a/src/logid/actions/gesture/AxisGesture.cpp +++ b/src/logid/actions/gesture/AxisGesture.cpp @@ -27,10 +27,11 @@ AxisGesture::AxisGesture(Device *device, libconfig::Setting &root) : { } -void AxisGesture::press() +void AxisGesture::press(bool init_threshold) { - _axis = 0; + _axis = init_threshold ? _config.threshold() : 0; _axis_remainder = 0; + _hires_remainder = 0; } void AxisGesture::release(bool primary) @@ -41,7 +42,10 @@ void AxisGesture::release(bool primary) void AxisGesture::move(int16_t axis) { - int16_t new_axis = _axis + axis; + int16_t new_axis = _axis+axis; + int low_res_axis = InputDevice::getLowResAxis(_config.axis()); + int hires_remainder = _hires_remainder; + if(new_axis > _config.threshold()) { double move = axis; if(_axis < _config.threshold()) @@ -63,7 +67,22 @@ void AxisGesture::move(int16_t axis) if(negative_multiplier) move_floor = -move_floor; - virtual_input->moveAxis(_config.axis(), move_floor); + if(low_res_axis != -1) { + int lowres_movement = 0, hires_movement = move_floor; + virtual_input->moveAxis(_config.axis(), hires_movement); + hires_remainder += hires_movement; + if(abs(hires_remainder) >= 60) { + lowres_movement = hires_remainder/120; + if(lowres_movement == 0) + lowres_movement = hires_remainder > 0 ? 1 : -1; + hires_remainder -= lowres_movement*120; + virtual_input->moveAxis(low_res_axis, lowres_movement); + } + + _hires_remainder = hires_remainder; + } else { + virtual_input->moveAxis(_config.axis(), move_floor); + } } _axis = new_axis; } @@ -73,6 +92,11 @@ bool AxisGesture::metThreshold() const return _axis >= _config.threshold(); } +void AxisGesture::setHiresMultiplier(double multiplier) +{ + _config.setHiresMultiplier(multiplier); +} + AxisGesture::Config::Config(Device *device, libconfig::Setting &setting) : Gesture::Config(device, setting, false) { @@ -113,6 +137,9 @@ AxisGesture::Config::Config(Device *device, libconfig::Setting &setting) : } catch(libconfig::SettingNotFoundException& e) { // Ignore } + + if(InputDevice::getLowResAxis(_axis) != -1) + _multiplier *= 120; } unsigned int AxisGesture::Config::axis() const @@ -123,4 +150,22 @@ unsigned int AxisGesture::Config::axis() const double AxisGesture::Config::multiplier() const { return _multiplier; +} + +bool AxisGesture::wheelCompatibility() const +{ + return true; +} + +void AxisGesture::Config::setHiresMultiplier(double multiplier) +{ + if(_hires_multiplier == multiplier || multiplier == 0) + return; + + if(InputDevice::getLowResAxis(_axis) != -1) { + _multiplier *= _hires_multiplier; + _multiplier /= multiplier; + } + + _hires_multiplier = multiplier; } \ No newline at end of file diff --git a/src/logid/actions/gesture/AxisGesture.h b/src/logid/actions/gesture/AxisGesture.h index 2b69d41..5352f9f 100644 --- a/src/logid/actions/gesture/AxisGesture.h +++ b/src/logid/actions/gesture/AxisGesture.h @@ -28,26 +28,32 @@ namespace logid { public: AxisGesture(Device* device, libconfig::Setting& root); - virtual void press(); + virtual void press(bool init_threshold=false); virtual void release(bool primary=false); virtual void move(int16_t axis); + virtual bool wheelCompatibility() const; virtual bool metThreshold() const; + void setHiresMultiplier(double multiplier); + class Config : public Gesture::Config { public: Config(Device* device, libconfig::Setting& setting); unsigned int axis() const; double multiplier() const; + void setHiresMultiplier(double multiplier); private: unsigned int _axis; double _multiplier = 1; + double _hires_multiplier = 1; }; protected: int16_t _axis; double _axis_remainder; + int _hires_remainder; Config _config; }; }} diff --git a/src/logid/actions/gesture/Gesture.h b/src/logid/actions/gesture/Gesture.h index 731c50f..648f1df 100644 --- a/src/logid/actions/gesture/Gesture.h +++ b/src/logid/actions/gesture/Gesture.h @@ -42,10 +42,11 @@ namespace actions class Gesture { public: - virtual void press() = 0; + virtual void press(bool init_threshold=false) = 0; virtual void release(bool primary=false) = 0; virtual void move(int16_t axis) = 0; + virtual bool wheelCompatibility() const = 0; virtual bool metThreshold() const = 0; class Config diff --git a/src/logid/actions/gesture/IntervalGesture.cpp b/src/logid/actions/gesture/IntervalGesture.cpp index 355c230..a3d8438 100644 --- a/src/logid/actions/gesture/IntervalGesture.cpp +++ b/src/logid/actions/gesture/IntervalGesture.cpp @@ -25,9 +25,9 @@ IntervalGesture::IntervalGesture(Device *device, libconfig::Setting &root) : { } -void IntervalGesture::press() +void IntervalGesture::press(bool init_threshold) { - _axis = 0; + _axis = init_threshold ? _config.threshold() : 0; _interval_pass_count = 0; } @@ -52,6 +52,11 @@ void IntervalGesture::move(int16_t axis) _interval_pass_count = new_interval_count; } +bool IntervalGesture::wheelCompatibility() const +{ + return true; +} + bool IntervalGesture::metThreshold() const { return _axis >= _config.threshold(); diff --git a/src/logid/actions/gesture/IntervalGesture.h b/src/logid/actions/gesture/IntervalGesture.h index 29dcb47..268822d 100644 --- a/src/logid/actions/gesture/IntervalGesture.h +++ b/src/logid/actions/gesture/IntervalGesture.h @@ -28,10 +28,11 @@ namespace actions public: IntervalGesture(Device* device, libconfig::Setting& root); - virtual void press(); + virtual void press(bool init_threshold=false); virtual void release(bool primary=false); virtual void move(int16_t axis); + virtual bool wheelCompatibility() const; virtual bool metThreshold() const; class Config : public Gesture::Config diff --git a/src/logid/actions/gesture/NullGesture.cpp b/src/logid/actions/gesture/NullGesture.cpp index c97b45d..dbb7e57 100644 --- a/src/logid/actions/gesture/NullGesture.cpp +++ b/src/logid/actions/gesture/NullGesture.cpp @@ -24,9 +24,9 @@ NullGesture::NullGesture(Device *device, libconfig::Setting& setting) : { } -void NullGesture::press() +void NullGesture::press(bool init_threshold) { - _axis = 0; + _axis = init_threshold ? _config.threshold() : 0; } void NullGesture::release(bool primary) @@ -40,6 +40,11 @@ void NullGesture::move(int16_t axis) _axis += axis; } +bool NullGesture::wheelCompatibility() const +{ + return true; +} + bool NullGesture::metThreshold() const { return _axis > _config.threshold(); diff --git a/src/logid/actions/gesture/NullGesture.h b/src/logid/actions/gesture/NullGesture.h index 9ea63c5..6b0f980 100644 --- a/src/logid/actions/gesture/NullGesture.h +++ b/src/logid/actions/gesture/NullGesture.h @@ -28,10 +28,11 @@ namespace actions public: NullGesture(Device* device, libconfig::Setting& setting); - virtual void press(); + virtual void press(bool init_threshold=false); virtual void release(bool primary=false); virtual void move(int16_t axis); + virtual bool wheelCompatibility() const; virtual bool metThreshold() const; protected: int16_t _axis; diff --git a/src/logid/actions/gesture/ReleaseGesture.cpp b/src/logid/actions/gesture/ReleaseGesture.cpp index 52b3ea1..613412b 100644 --- a/src/logid/actions/gesture/ReleaseGesture.cpp +++ b/src/logid/actions/gesture/ReleaseGesture.cpp @@ -24,9 +24,9 @@ ReleaseGesture::ReleaseGesture(Device *device, libconfig::Setting &root) : { } -void ReleaseGesture::press() +void ReleaseGesture::press(bool init_threshold) { - _axis = 0; + _axis = init_threshold ? _config.threshold() : 0; } void ReleaseGesture::release(bool primary) @@ -42,6 +42,11 @@ void ReleaseGesture::move(int16_t axis) _axis += axis; } +bool ReleaseGesture::wheelCompatibility() const +{ + return false; +} + bool ReleaseGesture::metThreshold() const { return _axis >= _config.threshold(); diff --git a/src/logid/actions/gesture/ReleaseGesture.h b/src/logid/actions/gesture/ReleaseGesture.h index b35fda2..59cc528 100644 --- a/src/logid/actions/gesture/ReleaseGesture.h +++ b/src/logid/actions/gesture/ReleaseGesture.h @@ -28,10 +28,11 @@ namespace actions public: ReleaseGesture(Device* device, libconfig::Setting& root); - virtual void press(); + virtual void press(bool init_threshold=false); virtual void release(bool primary=false); virtual void move(int16_t axis); + virtual bool wheelCompatibility() const; virtual bool metThreshold() const; protected: diff --git a/src/logid/backend/hidpp20/features/HiresScroll.h b/src/logid/backend/hidpp20/features/HiresScroll.h index 69dee11..e580f82 100644 --- a/src/logid/backend/hidpp20/features/HiresScroll.h +++ b/src/logid/backend/hidpp20/features/HiresScroll.h @@ -75,7 +75,7 @@ namespace hidpp20 { bool hiRes; uint8_t periods; - uint16_t deltaV; + int16_t deltaV; }; explicit HiresScroll(Device* device); diff --git a/src/logid/features/HiresScroll.cpp b/src/logid/features/HiresScroll.cpp index 7ed285f..92a6e5d 100644 --- a/src/logid/features/HiresScroll.cpp +++ b/src/logid/features/HiresScroll.cpp @@ -17,10 +17,15 @@ */ #include "HiresScroll.h" #include "../Device.h" +#include "../InputDevice.h" +#include "../actions/gesture/Gesture.h" +#include "../actions/gesture/AxisGesture.h" using namespace logid::features; using namespace logid::backend; +#define MOVE_EVENTHANDLER_NAME "HIRES_SCROLL" + HiresScroll::HiresScroll(Device *dev) : DeviceFeature(dev), _config(dev) { try { @@ -28,6 +33,32 @@ HiresScroll::HiresScroll(Device *dev) : DeviceFeature(dev), _config(dev) } catch(hidpp20::UnsupportedFeature& e) { throw UnsupportedFeature(); } + + if(_config.upAction()) { + try { + auto up_axis = std::dynamic_pointer_cast( + _config.upAction()); + if(up_axis) + up_axis->setHiresMultiplier( + _hires_scroll->getCapabilities().multiplier); + } catch(std::bad_cast& e) { } + + _config.upAction()->press(true); + } + + if(_config.downAction()) { + try { + auto down_axis = std::dynamic_pointer_cast( + _config.downAction()); + if(down_axis) + down_axis->setHiresMultiplier( + _hires_scroll->getCapabilities().multiplier); + } catch(std::bad_cast& e) { } + + _config.downAction()->press(true); + } + + _last_scroll = std::chrono::system_clock::now(); } void HiresScroll::configure() @@ -40,7 +71,21 @@ void HiresScroll::configure() void HiresScroll::listen() { - ///TODO: Map hires scroll events + if(_device->hidpp20().eventHandlers().find(MOVE_EVENTHANDLER_NAME) == + _device->hidpp20().eventHandlers().end()) { + auto handler = std::make_shared(); + handler->condition = [index=_hires_scroll->featureIndex()] + (hidpp::Report& report)->bool { + return (report.feature() == index) && (report.function() == + hidpp20::HiresScroll::WheelMovement); + }; + + handler->callback = [this](hidpp::Report& report)->void { + this->_handleScroll(_hires_scroll->wheelMovementEvent(report)); + }; + + _device->hidpp20().addEventHandler(MOVE_EVENTHANDLER_NAME, handler); + } } uint8_t HiresScroll::getMode() @@ -53,6 +98,48 @@ void HiresScroll::setMode(uint8_t mode) _hires_scroll->setMode(mode); } +void HiresScroll::_handleScroll(hidpp20::HiresScroll::WheelStatus event) +{ + auto now = std::chrono::system_clock::now(); + if(std::chrono::duration_cast( + now - _last_scroll).count() >= 1) { + if(_config.upAction()) { + _config.upAction()->release(); + _config.upAction()->press(true); + } + if(_config.downAction()) { + _config.downAction()->release(); + _config.downAction()->press(true); + } + + _last_direction = 0; + } + + if(event.deltaV > 0) { + if(_last_direction == -1) { + if(_config.downAction()){ + _config.downAction()->release(); + _config.downAction()->press(true); + } + } + if(_config.upAction()) + _config.upAction()->move(event.deltaV); + _last_direction = 1; + } else if(event.deltaV < 0) { + if(_last_direction == 1) { + if(_config.upAction()){ + _config.upAction()->release(); + _config.upAction()->press(true); + } + } + if(_config.downAction()) + _config.downAction()->move(-event.deltaV); + _last_direction = -1; + } + + _last_scroll = now; +} + HiresScroll::Config::Config(Device *dev) : DeviceFeature::Config(dev) { try { @@ -99,6 +186,48 @@ HiresScroll::Config::Config(Device *dev) : DeviceFeature::Config(dev) target.getSourceLine()); } } catch(libconfig::SettingNotFoundException& e) { } + + if(_mode & hidpp20::HiresScroll::Mode::Target) { + try { + auto& up = config_root.lookup("up"); + try { + auto g = actions::Gesture::makeGesture(dev, up); + if(g->wheelCompatibility()) { + _up_action = g; + } else { + logPrintf(WARN, "Line %d: This gesture cannot be used" + " as a scroll action.", + up.getSourceLine()); + } + } catch(actions::InvalidGesture& e) { + logPrintf(WARN, "Line %d: Invalid scroll action", + up.getSourceLine()); + } + } catch(libconfig::SettingNotFoundException&) { + logPrintf(WARN, "Line %d: target is true but no up action was" + " set", config_root.getSourceLine()); + } + + try { + auto& down = config_root.lookup("down"); + try { + auto g = actions::Gesture::makeGesture(dev, down); + if(g->wheelCompatibility()) { + _down_action = g; + } else { + logPrintf(WARN, "Line %d: This gesture cannot be used" + " as a scroll action.", + down.getSourceLine()); + } + } catch(actions::InvalidGesture& e) { + logPrintf(WARN, "Line %d: Invalid scroll action", + down.getSourceLine()); + } + } catch(libconfig::SettingNotFoundException&) { + logPrintf(WARN, "Line %d: target is true but no down action was" + " set", config_root.getSourceLine()); + } + } } catch(libconfig::SettingNotFoundException& e) { // HiresScroll not configured, use default } @@ -112,4 +241,16 @@ uint8_t HiresScroll::Config::getMode() const uint8_t HiresScroll::Config::getMask() const { return _mask; +} + +const std::shared_ptr& + HiresScroll::Config::upAction() const +{ + return _up_action; +} + +const std::shared_ptr& + HiresScroll::Config::downAction() const +{ + return _down_action; } \ No newline at end of file diff --git a/src/logid/features/HiresScroll.h b/src/logid/features/HiresScroll.h index f413d56..782b26f 100644 --- a/src/logid/features/HiresScroll.h +++ b/src/logid/features/HiresScroll.h @@ -20,6 +20,7 @@ #include "../backend/hidpp20/features/HiresScroll.h" #include "DeviceFeature.h" +#include "../actions/gesture/Gesture.h" namespace logid { namespace features @@ -40,12 +41,21 @@ namespace features explicit Config(Device* dev); uint8_t getMode() const; uint8_t getMask() const; + + const std::shared_ptr& upAction() const; + const std::shared_ptr& downAction() const; protected: uint8_t _mode; uint8_t _mask; + + std::shared_ptr _up_action; + std::shared_ptr _down_action; }; private: + void _handleScroll(backend::hidpp20::HiresScroll::WheelStatus event); std::shared_ptr _hires_scroll; + std::chrono::time_point _last_scroll; + int16_t _last_direction = 0; Config _config; }; }}