From 48495ddafe2ed59611c9491470f192b790e6146d Mon Sep 17 00:00:00 2001 From: ailurux Date: Fri, 7 Feb 2025 02:44:24 +0000 Subject: [PATCH] Add centre button haptic feedback on touch, and setting to disable/lessen haptics (#246) This adds a way for feedback devices to respond to events from outside of LVGL's event system, being passed from input device to feedback device through a vector. This was done so that touch events and long-press triggers can now give feedback through haptics. This PR also adds haptic modes, saved in nvs similarly to input and locked input modes, to disable or change the haptic effect behaviour based on which mode is selected. Finally, this also fixes a bug in which some click events would not trigger haptics, at the expense of re-introducing the (undesired?) behaviour of clicking a button that transitions to a new screen causing a double click. Relevant issues this should close: #195, #233, and (partially?) #120 Reviewed-on: https://codeberg.org/cool-tech-zone/tangara-fw/pulls/246 Co-authored-by: ailurux Co-committed-by: ailurux --- lua/settings.lua | 5 ++ src/drivers/include/drivers/nvs.hpp | 10 ++++ src/drivers/nvs.cpp | 29 ++++++++++++ src/tangara/input/device_factory.cpp | 2 +- src/tangara/input/feedback_device.hpp | 2 + src/tangara/input/feedback_haptics.cpp | 53 ++++++++++++++++++++-- src/tangara/input/feedback_haptics.hpp | 8 +++- src/tangara/input/feedback_tts.cpp | 6 +++ src/tangara/input/feedback_tts.hpp | 1 + src/tangara/input/input_device.hpp | 3 +- src/tangara/input/input_events.hpp | 18 ++++++++ src/tangara/input/input_hard_reset.cpp | 2 +- src/tangara/input/input_hard_reset.hpp | 2 +- src/tangara/input/input_hook.cpp | 6 ++- src/tangara/input/input_hook.hpp | 2 +- src/tangara/input/input_nav_buttons.cpp | 2 +- src/tangara/input/input_nav_buttons.hpp | 2 +- src/tangara/input/input_touch_dpad.cpp | 2 +- src/tangara/input/input_touch_dpad.hpp | 2 +- src/tangara/input/input_touch_wheel.cpp | 9 +++- src/tangara/input/input_touch_wheel.hpp | 2 +- src/tangara/input/input_trigger.cpp | 2 +- src/tangara/input/input_trigger.hpp | 1 + src/tangara/input/input_volume_buttons.cpp | 2 +- src/tangara/input/input_volume_buttons.hpp | 2 +- src/tangara/input/lvgl_input_driver.cpp | 17 ++++++- src/tangara/input/lvgl_input_driver.hpp | 2 + src/tangara/lua/lua_controls.cpp | 20 ++++++++ src/tangara/ui/ui_fsm.cpp | 1 + 29 files changed, 191 insertions(+), 24 deletions(-) create mode 100644 src/tangara/input/input_events.hpp diff --git a/lua/settings.lua b/lua/settings.lua index 07f1e4d2..0ae4e73d 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -451,6 +451,11 @@ settings.InputSettings = SettingsScreen:new { local controls_locked = make_scheme_control(self, controls.locked_schemes(), controls.locked_scheme) local controls_locked_desc = widgets.Description(controls_locked, "Control scheme when locked") + theme.set_subject(self.content:Label { + text = "Haptics Mode", + }, "settings_title") + make_scheme_control(self, controls.haptics_modes(), controls.haptics_mode) + controls_chooser:focus() theme.set_subject(self.content:Label { diff --git a/src/drivers/include/drivers/nvs.hpp b/src/drivers/include/drivers/nvs.hpp index 18bc5de6..e3a105f8 100644 --- a/src/drivers/include/drivers/nvs.hpp +++ b/src/drivers/include/drivers/nvs.hpp @@ -110,6 +110,15 @@ class NvsStorage { auto OutputMode() -> Output; auto OutputMode(Output) -> void; + enum class HapticsModes : uint8_t { + kDisabled = 0, + kMinimal = 1, + kStrong = 2, + }; + auto HapticsMode() -> HapticsModes; + auto HapticsMode(HapticsModes) -> void; + static auto intToHapticsMode(int raw) -> HapticsModes; + auto ScreenBrightness() -> uint_fast8_t; auto ScreenBrightness(uint_fast8_t) -> void; @@ -177,6 +186,7 @@ class NvsStorage { Setting input_mode_; Setting locked_input_mode_; Setting output_mode_; + Setting haptics_mode_; Setting theme_; diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp index 02a0058b..04a93fd9 100644 --- a/src/drivers/nvs.cpp +++ b/src/drivers/nvs.cpp @@ -35,6 +35,7 @@ static constexpr char kKeyAmpCurrentVolume[] = "hp_vol"; static constexpr char kKeyAmpLeftBias[] = "hp_bias"; static constexpr char kKeyPrimaryInput[] = "in_pri"; static constexpr char kKeyLockedInput[] = "in_locked"; +static constexpr char kKeyHaptics[] = "haptic_mode"; static constexpr char kKeyScrollSensitivity[] = "scroll"; static constexpr char kKeyLockPolarity[] = "lockpol"; static constexpr char kKeyDisplayCols[] = "dispcols"; @@ -275,6 +276,7 @@ NvsStorage::NvsStorage(nvs_handle_t handle) input_mode_(kKeyPrimaryInput), locked_input_mode_(kKeyLockedInput), output_mode_(kKeyOutput), + haptics_mode_(kKeyHaptics), theme_{kKeyInterfaceTheme}, bt_preferred_(kKeyBluetoothPreferred), bt_names_(kKeyBluetoothNames), @@ -304,6 +306,7 @@ auto NvsStorage::Read() -> void { input_mode_.read(handle_); locked_input_mode_.read(handle_); output_mode_.read(handle_); + haptics_mode_.read(handle_); theme_.read(handle_); bt_preferred_.read(handle_); bt_names_.read(handle_); @@ -328,6 +331,7 @@ auto NvsStorage::Write() -> bool { input_mode_.write(handle_); locked_input_mode_.write(handle_); output_mode_.write(handle_); + haptics_mode_.write(handle_); theme_.write(handle_); bt_preferred_.write(handle_); bt_names_.write(handle_); @@ -483,6 +487,31 @@ auto NvsStorage::OutputMode(Output out) -> void { nvs_commit(handle_); } +auto NvsStorage::HapticsMode() -> HapticsModes { + std::lock_guard lock{mutex_}; + int val = haptics_mode_.get().value_or(static_cast(HapticsModes::kMinimal)); + return intToHapticsMode(val); +} + +auto NvsStorage::intToHapticsMode(int raw) -> HapticsModes { + switch (raw) { + case static_cast(HapticsModes::kDisabled): + return HapticsModes::kDisabled; + case static_cast(HapticsModes::kMinimal): + return HapticsModes::kMinimal; + case static_cast(HapticsModes::kStrong): + return HapticsModes::kStrong; + default: + return HapticsModes::kStrong; + } +} + +auto NvsStorage::HapticsMode(HapticsModes mode) -> void { + std::lock_guard lock{mutex_}; + haptics_mode_.set(static_cast(mode)); +} + + auto NvsStorage::FastCharge() -> bool { std::lock_guard lock{mutex_}; return fast_charge_.get().value_or(true); diff --git a/src/tangara/input/device_factory.cpp b/src/tangara/input/device_factory.cpp index 22df4ebe..fe2e2485 100644 --- a/src/tangara/input/device_factory.cpp +++ b/src/tangara/input/device_factory.cpp @@ -56,7 +56,7 @@ auto DeviceFactory::createInputs(drivers::NvsStorage::InputModes mode) auto DeviceFactory::createFeedbacks() -> std::vector> { return { - std::make_shared(services_->haptics()), + std::make_shared(services_->haptics(), services_), std::make_shared(services_->tts()), }; } diff --git a/src/tangara/input/feedback_device.hpp b/src/tangara/input/feedback_device.hpp index 8253642f..31ad1fb1 100644 --- a/src/tangara/input/feedback_device.hpp +++ b/src/tangara/input/feedback_device.hpp @@ -8,6 +8,7 @@ #include #include "core/lv_group.h" +#include "input/input_events.hpp" namespace input { @@ -25,6 +26,7 @@ class IFeedbackDevice { virtual ~IFeedbackDevice() {} virtual auto feedback(lv_group_t* group, uint8_t event_type) -> void = 0; + virtual auto feedback(lv_group_t* group, InputEvent event) -> void = 0; // TODO: Add configuration; likely the same shape of interface that // IInputDevice uses. diff --git a/src/tangara/input/feedback_haptics.cpp b/src/tangara/input/feedback_haptics.cpp index a447a69d..fb203915 100644 --- a/src/tangara/input/feedback_haptics.cpp +++ b/src/tangara/input/feedback_haptics.cpp @@ -18,21 +18,64 @@ namespace input { using Effect = drivers::Haptics::Effect; -Haptics::Haptics(drivers::Haptics& haptics_) : haptics_(haptics_) {} +Haptics::Haptics(drivers::Haptics& haptics_, + std::shared_ptr services) + : haptics_(haptics_), services_(services) {} auto Haptics::feedback(lv_group_t* group, uint8_t event_type) -> void { lv_obj_t* obj = lv_group_get_focused(group); - if (obj == last_selection_) { + + auto haptics_mode = services_->nvs().HapticsMode(); + if (haptics_mode == drivers::NvsStorage::HapticsModes::kDisabled ) { return; } - last_selection_ = obj; switch (event_type) { case LV_EVENT_FOCUSED: - haptics_.PlayWaveformEffect(Effect::kMediumClick1_100Pct); + if (obj == last_selection_) { + return; + } + last_selection_ = obj; + if (haptics_mode == drivers::NvsStorage::HapticsModes::kMinimal) { + haptics_.PlayWaveformEffect(Effect::kStrongClick_30Pct); + } else { + haptics_.PlayWaveformEffect(Effect::kMediumClick1_100Pct); + } break; case LV_EVENT_CLICKED: - haptics_.PlayWaveformEffect(Effect::kSharpClick_100Pct); + if (haptics_mode == drivers::NvsStorage::HapticsModes::kMinimal) { + haptics_.PlayWaveformEffect(Effect::kSharpClick_30Pct); + } else { + haptics_.PlayWaveformEffect(Effect::kSharpClick_100Pct); + } + break; + default: + break; + } +} + +auto Haptics::feedback(lv_group_t* group, InputEvent event) -> void { + lv_obj_t* obj = lv_group_get_focused(group); + + auto haptics_mode = services_->nvs().HapticsMode(); + if (haptics_mode == drivers::NvsStorage::HapticsModes::kDisabled) { + return; + } + + switch (event) { + case InputEvent::kOnPress: + if (haptics_mode == drivers::NvsStorage::HapticsModes::kMinimal) { + haptics_.PlayWaveformEffect(Effect::kSoftBump_30Pct); + } else { + haptics_.PlayWaveformEffect(Effect::kSharpTick2_80Pct); + } + break; + case InputEvent::kOnLongPress: + if (haptics_mode == drivers::NvsStorage::HapticsModes::kMinimal) { + haptics_.PlayWaveformEffect(Effect::kShortDoubleClickStrong4_30Pct); + } else { + haptics_.PlayWaveformEffect(Effect::kShortDoubleSharpTick3_60Pct); + } break; default: break; diff --git a/src/tangara/input/feedback_haptics.hpp b/src/tangara/input/feedback_haptics.hpp index 91d7ec3a..90ed5ff2 100644 --- a/src/tangara/input/feedback_haptics.hpp +++ b/src/tangara/input/feedback_haptics.hpp @@ -12,17 +12,23 @@ #include "drivers/haptics.hpp" #include "input/feedback_device.hpp" +#include "input/input_events.hpp" +#include "drivers/nvs.hpp" +#include "system_fsm/service_locator.hpp" namespace input { class Haptics : public IFeedbackDevice { public: - Haptics(drivers::Haptics& haptics_); + Haptics(drivers::Haptics& haptics_, + std::shared_ptr services); auto feedback(lv_group_t*, uint8_t event_type) -> void override; + auto feedback(lv_group_t*, InputEvent event) -> void override; private: drivers::Haptics& haptics_; + std::shared_ptr services_; lv_obj_t* last_selection_; }; diff --git a/src/tangara/input/feedback_tts.cpp b/src/tangara/input/feedback_tts.cpp index e7e167c6..04c2830d 100644 --- a/src/tangara/input/feedback_tts.cpp +++ b/src/tangara/input/feedback_tts.cpp @@ -22,6 +22,7 @@ #include "tts/events.hpp" #include "tts/provider.hpp" +#include "feedback_tts.hpp" namespace input { @@ -50,6 +51,10 @@ auto TextToSpeech::feedback(lv_group_t* group, uint8_t event_type) -> void { } } +auto TextToSpeech::feedback(lv_group_t*, InputEvent event) -> void { + return; +} + auto TextToSpeech::describe(lv_obj_t& obj) -> void { if (lv_obj_check_type(&obj, &lv_button_class) || lv_obj_check_type(&obj, &lv_list_button_class)) { @@ -94,4 +99,5 @@ auto TextToSpeech::findDescription(lv_obj_t& obj) return {}; } + } // namespace input diff --git a/src/tangara/input/feedback_tts.hpp b/src/tangara/input/feedback_tts.hpp index ddd83ff0..00fbf06d 100644 --- a/src/tangara/input/feedback_tts.hpp +++ b/src/tangara/input/feedback_tts.hpp @@ -22,6 +22,7 @@ class TextToSpeech : public IFeedbackDevice { TextToSpeech(tts::Provider&); auto feedback(lv_group_t*, uint8_t event_type) -> void override; + auto feedback(lv_group_t*, InputEvent event) -> void override; private: tts::Provider& tts_; diff --git a/src/tangara/input/input_device.hpp b/src/tangara/input/input_device.hpp index 424c0da3..7b8d993d 100644 --- a/src/tangara/input/input_device.hpp +++ b/src/tangara/input/input_device.hpp @@ -12,6 +12,7 @@ #include "drivers/nvs.hpp" #include "indev/lv_indev.h" +#include "input/input_events.hpp" #include "input/input_hook.hpp" #include "lua/property.hpp" @@ -27,7 +28,7 @@ class IInputDevice { public: virtual ~IInputDevice() {} - virtual auto read(lv_indev_data_t* data) -> void = 0; + virtual auto read(lv_indev_data_t* data, std::vector& events) -> void = 0; virtual auto name() -> std::string = 0; virtual auto triggers() -> std::vector> { diff --git a/src/tangara/input/input_events.hpp b/src/tangara/input/input_events.hpp new file mode 100644 index 00000000..e9826c67 --- /dev/null +++ b/src/tangara/input/input_events.hpp @@ -0,0 +1,18 @@ + +/* + * Copyright 2025 ailurux + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +namespace input { + +enum class InputEvent { + kNone = 0, + kOnPress = 1, + kOnLongPress = 2, +}; + +} // namespace input \ No newline at end of file diff --git a/src/tangara/input/input_hard_reset.cpp b/src/tangara/input/input_hard_reset.cpp index 7fcca3eb..44de500c 100644 --- a/src/tangara/input/input_hard_reset.cpp +++ b/src/tangara/input/input_hard_reset.cpp @@ -14,7 +14,7 @@ namespace input { HardReset::HardReset(drivers::IGpios& gpios) : gpios_(gpios) {} -auto HardReset::read(lv_indev_data_t* data) -> void { +auto HardReset::read(lv_indev_data_t* data, std::vector& events) -> void { bool buttons_pressed = !gpios_.Get(drivers::IGpios::Pin::kKeyUp) && !gpios_.Get(drivers::IGpios::Pin::kKeyDown); if (!buttons_pressed) { diff --git a/src/tangara/input/input_hard_reset.hpp b/src/tangara/input/input_hard_reset.hpp index 00f218bf..93be56c5 100644 --- a/src/tangara/input/input_hard_reset.hpp +++ b/src/tangara/input/input_hard_reset.hpp @@ -20,7 +20,7 @@ class HardReset : public IInputDevice { public: HardReset(drivers::IGpios&); - auto read(lv_indev_data_t* data) -> void override; + auto read(lv_indev_data_t* data, std::vector& events) -> void override; auto name() -> std::string override; auto triggers() -> std::vector> override; diff --git a/src/tangara/input/input_hook.cpp b/src/tangara/input/input_hook.cpp index 0867bb39..9be1be7c 100644 --- a/src/tangara/input/input_hook.cpp +++ b/src/tangara/input/input_hook.cpp @@ -50,8 +50,9 @@ TriggerHooks::TriggerHooks(std::string name, long_press_("long_press", long_press), repeat_("repeat", repeat) {} -auto TriggerHooks::update(bool pressed, lv_indev_data_t* d) -> void { - switch (trigger_.update(pressed)) { +auto TriggerHooks::update(bool pressed, lv_indev_data_t* d) -> Trigger::State { + auto state = trigger_.update(pressed); + switch (state) { case Trigger::State::kClick: click_.invoke(d); break; @@ -68,6 +69,7 @@ auto TriggerHooks::update(bool pressed, lv_indev_data_t* d) -> void { default: break; } + return state; } auto TriggerHooks::override(Trigger::State s, std::optional cb) diff --git a/src/tangara/input/input_hook.hpp b/src/tangara/input/input_hook.hpp index 94a35850..67122e1f 100644 --- a/src/tangara/input/input_hook.hpp +++ b/src/tangara/input/input_hook.hpp @@ -52,7 +52,7 @@ class TriggerHooks { std::optional long_press, std::optional repeat); - auto update(bool, lv_indev_data_t*) -> void; + auto update(bool, lv_indev_data_t*) -> Trigger::State; auto override(Trigger::State, std::optional) -> void; auto name() const -> const std::string&; diff --git a/src/tangara/input/input_nav_buttons.cpp b/src/tangara/input/input_nav_buttons.cpp index a5e10013..ede6b714 100644 --- a/src/tangara/input/input_nav_buttons.cpp +++ b/src/tangara/input/input_nav_buttons.cpp @@ -20,7 +20,7 @@ NavButtons::NavButtons(drivers::IGpios& gpios) down_("lower", {}, actions::scrollDown(), actions::select(), {}), locked_(false) {} -auto NavButtons::read(lv_indev_data_t* data) -> void { +auto NavButtons::read(lv_indev_data_t* data, std::vector& events) -> void { bool up = !gpios_.Get(drivers::IGpios::Pin::kKeyUp); bool down = !gpios_.Get(drivers::IGpios::Pin::kKeyDown); diff --git a/src/tangara/input/input_nav_buttons.hpp b/src/tangara/input/input_nav_buttons.hpp index c9575fe0..c28cce91 100644 --- a/src/tangara/input/input_nav_buttons.hpp +++ b/src/tangara/input/input_nav_buttons.hpp @@ -23,7 +23,7 @@ class NavButtons : public IInputDevice { public: NavButtons(drivers::IGpios&); - auto read(lv_indev_data_t* data) -> void override; + auto read(lv_indev_data_t* data, std::vector& events) -> void override; auto name() -> std::string override; auto triggers() -> std::vector> override; diff --git a/src/tangara/input/input_touch_dpad.cpp b/src/tangara/input/input_touch_dpad.cpp index 25c2315b..c866f37c 100644 --- a/src/tangara/input/input_touch_dpad.cpp +++ b/src/tangara/input/input_touch_dpad.cpp @@ -28,7 +28,7 @@ TouchDPad::TouchDPad(drivers::TouchWheel& wheel) left_("left", actions::goBack(), {}, {}, {}), locked_(false) {} -auto TouchDPad::read(lv_indev_data_t* data) -> void { +auto TouchDPad::read(lv_indev_data_t* data, std::vector& events) -> void { if (locked_) { return; } diff --git a/src/tangara/input/input_touch_dpad.hpp b/src/tangara/input/input_touch_dpad.hpp index 086f556e..82de26bf 100644 --- a/src/tangara/input/input_touch_dpad.hpp +++ b/src/tangara/input/input_touch_dpad.hpp @@ -22,7 +22,7 @@ class TouchDPad : public IInputDevice { public: TouchDPad(drivers::TouchWheel&); - auto read(lv_indev_data_t* data) -> void override; + auto read(lv_indev_data_t* data, std::vector& events) -> void override; auto name() -> std::string override; auto triggers() -> std::vector> override; diff --git a/src/tangara/input/input_touch_wheel.cpp b/src/tangara/input/input_touch_wheel.cpp index 1aee6fae..29153396 100644 --- a/src/tangara/input/input_touch_wheel.cpp +++ b/src/tangara/input/input_touch_wheel.cpp @@ -57,7 +57,7 @@ TouchWheel::TouchWheel(drivers::NvsStorage& nvs, drivers::TouchWheel& wheel) last_angle_(0), last_wheel_touch_time_(0) {} -auto TouchWheel::read(lv_indev_data_t* data) -> void { +auto TouchWheel::read(lv_indev_data_t* data, std::vector& events) -> void { if (locked_) { return; } @@ -88,9 +88,14 @@ auto TouchWheel::read(lv_indev_data_t* data) -> void { bool wheel_touch_timed_out = esp_timer_get_time() - last_wheel_touch_time_ > SCROLL_TIMEOUT_US; - centre_.update(wheel_touch_timed_out && wheel_data.is_button_touched && + auto centre_state = centre_.update(wheel_touch_timed_out && wheel_data.is_button_touched && !wheel_data.is_wheel_touched, data); + if (centre_state == input::Trigger::State::kPress) { + events.push_back(InputEvent::kOnPress); + } else if (centre_state == input::Trigger::State::kLongPress) { + events.push_back(InputEvent::kOnLongPress); + } // If the user is touching the wheel but not scrolling, then they may be // clicking on one of the wheel's cardinal directions. diff --git a/src/tangara/input/input_touch_wheel.hpp b/src/tangara/input/input_touch_wheel.hpp index 420454b8..b2583e85 100644 --- a/src/tangara/input/input_touch_wheel.hpp +++ b/src/tangara/input/input_touch_wheel.hpp @@ -25,7 +25,7 @@ class TouchWheel : public IInputDevice { public: TouchWheel(drivers::NvsStorage&, drivers::TouchWheel&); - auto read(lv_indev_data_t* data) -> void override; + auto read(lv_indev_data_t* data, std::vector& events) -> void override; auto name() -> std::string override; auto triggers() -> std::vector> override; diff --git a/src/tangara/input/input_trigger.cpp b/src/tangara/input/input_trigger.cpp index eb67bcca..6c97f08c 100644 --- a/src/tangara/input/input_trigger.cpp +++ b/src/tangara/input/input_trigger.cpp @@ -44,7 +44,7 @@ auto Trigger::update(bool is_pressed) -> State { was_double_click_ = false; times_long_pressed_ = 0; was_pressed_ = true; - return State::kNone; + return State::kPress; } // The key was released. If there were no long-press events fired during the diff --git a/src/tangara/input/input_trigger.hpp b/src/tangara/input/input_trigger.hpp index 9f0b7e19..5bf8e13c 100644 --- a/src/tangara/input/input_trigger.hpp +++ b/src/tangara/input/input_trigger.hpp @@ -23,6 +23,7 @@ class Trigger { kDoubleClick, kLongPress, kRepeatPress, + kPress }; Trigger(); diff --git a/src/tangara/input/input_volume_buttons.cpp b/src/tangara/input/input_volume_buttons.cpp index 5c814ffa..ffeea18f 100644 --- a/src/tangara/input/input_volume_buttons.cpp +++ b/src/tangara/input/input_volume_buttons.cpp @@ -17,7 +17,7 @@ VolumeButtons::VolumeButtons(drivers::IGpios& gpios) down_("lower", actions::volumeDown()), locked_() {} -auto VolumeButtons::read(lv_indev_data_t* data) -> void { +auto VolumeButtons::read(lv_indev_data_t* data, std::vector& events) -> void { bool up = !gpios_.Get(drivers::IGpios::Pin::kKeyUp); bool down = !gpios_.Get(drivers::IGpios::Pin::kKeyDown); diff --git a/src/tangara/input/input_volume_buttons.hpp b/src/tangara/input/input_volume_buttons.hpp index 35a44390..17f60d7e 100644 --- a/src/tangara/input/input_volume_buttons.hpp +++ b/src/tangara/input/input_volume_buttons.hpp @@ -22,7 +22,7 @@ class VolumeButtons : public IInputDevice { public: VolumeButtons(drivers::IGpios&); - auto read(lv_indev_data_t* data) -> void override; + auto read(lv_indev_data_t* data, std::vector& events) -> void override; auto name() -> std::string override; auto triggers() -> std::vector> override; diff --git a/src/tangara/input/lvgl_input_driver.cpp b/src/tangara/input/lvgl_input_driver.cpp index 2859c6a8..03d5cbb7 100644 --- a/src/tangara/input/lvgl_input_driver.cpp +++ b/src/tangara/input/lvgl_input_driver.cpp @@ -112,6 +112,15 @@ LvglInputDriver::LvglInputDriver(drivers::NvsStorage& nvs, nvs.LockedInput(*mode); return true; }), + haptics_mode_(static_cast(nvs.HapticsMode()), + [&](const lua::LuaValue& val) { + if (!std::holds_alternative(val)) { + return false; + } + auto mode = drivers::NvsStorage::intToHapticsMode(std::get(val)); + nvs.HapticsMode(mode); + return true; + }), inputs_(factory.createInputs(nvs.PrimaryInput())), feedbacks_(factory.createFeedbacks()), is_locked_(false) { @@ -141,8 +150,14 @@ auto LvglInputDriver::setGroup(lv_group_t* g) -> void { } auto LvglInputDriver::read(lv_indev_data_t* data) -> void { + std::vector events; for (auto&& device : inputs_) { - device->read(data); + device->read(data, events); + } + for (auto event: events) { + for (auto&& device : feedbacks_) { + device->feedback(lv_indev_get_group(device_), event); + } } } diff --git a/src/tangara/input/lvgl_input_driver.hpp b/src/tangara/input/lvgl_input_driver.hpp index ce950621..629a0a78 100644 --- a/src/tangara/input/lvgl_input_driver.hpp +++ b/src/tangara/input/lvgl_input_driver.hpp @@ -37,6 +37,7 @@ class LvglInputDriver { auto mode() -> lua::Property& { return mode_; } auto lockedMode() -> lua::Property& { return locked_mode_; } + auto hapticsMode() -> lua::Property& { return haptics_mode_; } auto setGroup(lv_group_t*) -> void; auto read(lv_indev_data_t* data) -> void; @@ -51,6 +52,7 @@ class LvglInputDriver { lua::Property mode_; lua::Property locked_mode_; + lua::Property haptics_mode_; lv_indev_t* device_; std::vector> inputs_; diff --git a/src/tangara/lua/lua_controls.cpp b/src/tangara/lua/lua_controls.cpp index 87b7ca16..bc2588ac 100644 --- a/src/tangara/lua/lua_controls.cpp +++ b/src/tangara/lua/lua_controls.cpp @@ -58,8 +58,28 @@ static auto locked_controls_schemes(lua_State* L) -> int { return 1; } +static auto haptics_modes(lua_State* L) -> int { + lua_newtable(L); + + lua_pushliteral(L, "Disabled"); + lua_rawseti(L, -2, + static_cast(drivers::NvsStorage::HapticsModes::kDisabled)); + + lua_pushliteral(L, "Minimal"); + lua_rawseti( + L, -2, + static_cast(drivers::NvsStorage::HapticsModes::kMinimal)); + + lua_pushliteral(L, "Strong"); + lua_rawseti( + L, -2, static_cast(drivers::NvsStorage::HapticsModes::kStrong)); + + return 1; +} + static const struct luaL_Reg kControlsFuncs[] = {{"schemes", controls_schemes}, {"locked_schemes", locked_controls_schemes}, + {"haptics_modes", haptics_modes}, {NULL, NULL}}; static auto lua_controls(lua_State* state) -> int { diff --git a/src/tangara/ui/ui_fsm.cpp b/src/tangara/ui/ui_fsm.cpp index 1823f780..b974da53 100644 --- a/src/tangara/ui/ui_fsm.cpp +++ b/src/tangara/ui/ui_fsm.cpp @@ -668,6 +668,7 @@ void Lua::entry() { { {"scheme", &sInput->mode()}, {"locked_scheme", &sInput->lockedMode()}, + {"haptics_mode", &sInput->hapticsMode()}, {"lock_switch", &sLockSwitch}, {"hooks", [&](lua_State* L) { return sInput->pushHooks(L); }}, });