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 <ailuruxx@gmail.com>
Co-committed-by: ailurux <ailuruxx@gmail.com>
custom
ailurux 2 months ago committed by cooljqln
parent 275b931e0b
commit 48495ddafe
  1. 5
      lua/settings.lua
  2. 10
      src/drivers/include/drivers/nvs.hpp
  3. 29
      src/drivers/nvs.cpp
  4. 2
      src/tangara/input/device_factory.cpp
  5. 2
      src/tangara/input/feedback_device.hpp
  6. 49
      src/tangara/input/feedback_haptics.cpp
  7. 8
      src/tangara/input/feedback_haptics.hpp
  8. 6
      src/tangara/input/feedback_tts.cpp
  9. 1
      src/tangara/input/feedback_tts.hpp
  10. 3
      src/tangara/input/input_device.hpp
  11. 18
      src/tangara/input/input_events.hpp
  12. 2
      src/tangara/input/input_hard_reset.cpp
  13. 2
      src/tangara/input/input_hard_reset.hpp
  14. 6
      src/tangara/input/input_hook.cpp
  15. 2
      src/tangara/input/input_hook.hpp
  16. 2
      src/tangara/input/input_nav_buttons.cpp
  17. 2
      src/tangara/input/input_nav_buttons.hpp
  18. 2
      src/tangara/input/input_touch_dpad.cpp
  19. 2
      src/tangara/input/input_touch_dpad.hpp
  20. 9
      src/tangara/input/input_touch_wheel.cpp
  21. 2
      src/tangara/input/input_touch_wheel.hpp
  22. 2
      src/tangara/input/input_trigger.cpp
  23. 1
      src/tangara/input/input_trigger.hpp
  24. 2
      src/tangara/input/input_volume_buttons.cpp
  25. 2
      src/tangara/input/input_volume_buttons.hpp
  26. 17
      src/tangara/input/lvgl_input_driver.cpp
  27. 2
      src/tangara/input/lvgl_input_driver.hpp
  28. 20
      src/tangara/lua/lua_controls.cpp
  29. 1
      src/tangara/ui/ui_fsm.cpp

@ -451,6 +451,11 @@ settings.InputSettings = SettingsScreen:new {
local controls_locked = make_scheme_control(self, controls.locked_schemes(), controls.locked_scheme) 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") 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() controls_chooser:focus()
theme.set_subject(self.content:Label { theme.set_subject(self.content:Label {

@ -110,6 +110,15 @@ class NvsStorage {
auto OutputMode() -> Output; auto OutputMode() -> Output;
auto OutputMode(Output) -> void; 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;
auto ScreenBrightness(uint_fast8_t) -> void; auto ScreenBrightness(uint_fast8_t) -> void;
@ -177,6 +186,7 @@ class NvsStorage {
Setting<uint8_t> input_mode_; Setting<uint8_t> input_mode_;
Setting<uint8_t> locked_input_mode_; Setting<uint8_t> locked_input_mode_;
Setting<uint8_t> output_mode_; Setting<uint8_t> output_mode_;
Setting<uint8_t> haptics_mode_;
Setting<std::string> theme_; Setting<std::string> theme_;

@ -35,6 +35,7 @@ static constexpr char kKeyAmpCurrentVolume[] = "hp_vol";
static constexpr char kKeyAmpLeftBias[] = "hp_bias"; static constexpr char kKeyAmpLeftBias[] = "hp_bias";
static constexpr char kKeyPrimaryInput[] = "in_pri"; static constexpr char kKeyPrimaryInput[] = "in_pri";
static constexpr char kKeyLockedInput[] = "in_locked"; static constexpr char kKeyLockedInput[] = "in_locked";
static constexpr char kKeyHaptics[] = "haptic_mode";
static constexpr char kKeyScrollSensitivity[] = "scroll"; static constexpr char kKeyScrollSensitivity[] = "scroll";
static constexpr char kKeyLockPolarity[] = "lockpol"; static constexpr char kKeyLockPolarity[] = "lockpol";
static constexpr char kKeyDisplayCols[] = "dispcols"; static constexpr char kKeyDisplayCols[] = "dispcols";
@ -275,6 +276,7 @@ NvsStorage::NvsStorage(nvs_handle_t handle)
input_mode_(kKeyPrimaryInput), input_mode_(kKeyPrimaryInput),
locked_input_mode_(kKeyLockedInput), locked_input_mode_(kKeyLockedInput),
output_mode_(kKeyOutput), output_mode_(kKeyOutput),
haptics_mode_(kKeyHaptics),
theme_{kKeyInterfaceTheme}, theme_{kKeyInterfaceTheme},
bt_preferred_(kKeyBluetoothPreferred), bt_preferred_(kKeyBluetoothPreferred),
bt_names_(kKeyBluetoothNames), bt_names_(kKeyBluetoothNames),
@ -304,6 +306,7 @@ auto NvsStorage::Read() -> void {
input_mode_.read(handle_); input_mode_.read(handle_);
locked_input_mode_.read(handle_); locked_input_mode_.read(handle_);
output_mode_.read(handle_); output_mode_.read(handle_);
haptics_mode_.read(handle_);
theme_.read(handle_); theme_.read(handle_);
bt_preferred_.read(handle_); bt_preferred_.read(handle_);
bt_names_.read(handle_); bt_names_.read(handle_);
@ -328,6 +331,7 @@ auto NvsStorage::Write() -> bool {
input_mode_.write(handle_); input_mode_.write(handle_);
locked_input_mode_.write(handle_); locked_input_mode_.write(handle_);
output_mode_.write(handle_); output_mode_.write(handle_);
haptics_mode_.write(handle_);
theme_.write(handle_); theme_.write(handle_);
bt_preferred_.write(handle_); bt_preferred_.write(handle_);
bt_names_.write(handle_); bt_names_.write(handle_);
@ -483,6 +487,31 @@ auto NvsStorage::OutputMode(Output out) -> void {
nvs_commit(handle_); nvs_commit(handle_);
} }
auto NvsStorage::HapticsMode() -> HapticsModes {
std::lock_guard<std::mutex> lock{mutex_};
int val = haptics_mode_.get().value_or(static_cast<uint8_t>(HapticsModes::kMinimal));
return intToHapticsMode(val);
}
auto NvsStorage::intToHapticsMode(int raw) -> HapticsModes {
switch (raw) {
case static_cast<int>(HapticsModes::kDisabled):
return HapticsModes::kDisabled;
case static_cast<int>(HapticsModes::kMinimal):
return HapticsModes::kMinimal;
case static_cast<int>(HapticsModes::kStrong):
return HapticsModes::kStrong;
default:
return HapticsModes::kStrong;
}
}
auto NvsStorage::HapticsMode(HapticsModes mode) -> void {
std::lock_guard<std::mutex> lock{mutex_};
haptics_mode_.set(static_cast<uint8_t>(mode));
}
auto NvsStorage::FastCharge() -> bool { auto NvsStorage::FastCharge() -> bool {
std::lock_guard<std::mutex> lock{mutex_}; std::lock_guard<std::mutex> lock{mutex_};
return fast_charge_.get().value_or(true); return fast_charge_.get().value_or(true);

@ -56,7 +56,7 @@ auto DeviceFactory::createInputs(drivers::NvsStorage::InputModes mode)
auto DeviceFactory::createFeedbacks() auto DeviceFactory::createFeedbacks()
-> std::vector<std::shared_ptr<IFeedbackDevice>> { -> std::vector<std::shared_ptr<IFeedbackDevice>> {
return { return {
std::make_shared<Haptics>(services_->haptics()), std::make_shared<Haptics>(services_->haptics(), services_),
std::make_shared<TextToSpeech>(services_->tts()), std::make_shared<TextToSpeech>(services_->tts()),
}; };
} }

@ -8,6 +8,7 @@
#include <cstdint> #include <cstdint>
#include "core/lv_group.h" #include "core/lv_group.h"
#include "input/input_events.hpp"
namespace input { namespace input {
@ -25,6 +26,7 @@ class IFeedbackDevice {
virtual ~IFeedbackDevice() {} virtual ~IFeedbackDevice() {}
virtual auto feedback(lv_group_t* group, uint8_t event_type) -> void = 0; 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 // TODO: Add configuration; likely the same shape of interface that
// IInputDevice uses. // IInputDevice uses.

@ -18,21 +18,64 @@ namespace input {
using Effect = drivers::Haptics::Effect; using Effect = drivers::Haptics::Effect;
Haptics::Haptics(drivers::Haptics& haptics_) : haptics_(haptics_) {} Haptics::Haptics(drivers::Haptics& haptics_,
std::shared_ptr<system_fsm::ServiceLocator> services)
: haptics_(haptics_), services_(services) {}
auto Haptics::feedback(lv_group_t* group, uint8_t event_type) -> void { auto Haptics::feedback(lv_group_t* group, uint8_t event_type) -> void {
lv_obj_t* obj = lv_group_get_focused(group); 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; return;
} }
last_selection_ = obj;
switch (event_type) { switch (event_type) {
case LV_EVENT_FOCUSED: case LV_EVENT_FOCUSED:
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); haptics_.PlayWaveformEffect(Effect::kMediumClick1_100Pct);
}
break; break;
case LV_EVENT_CLICKED: case LV_EVENT_CLICKED:
if (haptics_mode == drivers::NvsStorage::HapticsModes::kMinimal) {
haptics_.PlayWaveformEffect(Effect::kSharpClick_30Pct);
} else {
haptics_.PlayWaveformEffect(Effect::kSharpClick_100Pct); 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; break;
default: default:
break; break;

@ -12,17 +12,23 @@
#include "drivers/haptics.hpp" #include "drivers/haptics.hpp"
#include "input/feedback_device.hpp" #include "input/feedback_device.hpp"
#include "input/input_events.hpp"
#include "drivers/nvs.hpp"
#include "system_fsm/service_locator.hpp"
namespace input { namespace input {
class Haptics : public IFeedbackDevice { class Haptics : public IFeedbackDevice {
public: public:
Haptics(drivers::Haptics& haptics_); Haptics(drivers::Haptics& haptics_,
std::shared_ptr<system_fsm::ServiceLocator> services);
auto feedback(lv_group_t*, uint8_t event_type) -> void override; auto feedback(lv_group_t*, uint8_t event_type) -> void override;
auto feedback(lv_group_t*, InputEvent event) -> void override;
private: private:
drivers::Haptics& haptics_; drivers::Haptics& haptics_;
std::shared_ptr<system_fsm::ServiceLocator> services_;
lv_obj_t* last_selection_; lv_obj_t* last_selection_;
}; };

@ -22,6 +22,7 @@
#include "tts/events.hpp" #include "tts/events.hpp"
#include "tts/provider.hpp" #include "tts/provider.hpp"
#include "feedback_tts.hpp"
namespace input { 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 { auto TextToSpeech::describe(lv_obj_t& obj) -> void {
if (lv_obj_check_type(&obj, &lv_button_class) || if (lv_obj_check_type(&obj, &lv_button_class) ||
lv_obj_check_type(&obj, &lv_list_button_class)) { lv_obj_check_type(&obj, &lv_list_button_class)) {
@ -94,4 +99,5 @@ auto TextToSpeech::findDescription(lv_obj_t& obj)
return {}; return {};
} }
} // namespace input } // namespace input

@ -22,6 +22,7 @@ class TextToSpeech : public IFeedbackDevice {
TextToSpeech(tts::Provider&); TextToSpeech(tts::Provider&);
auto feedback(lv_group_t*, uint8_t event_type) -> void override; auto feedback(lv_group_t*, uint8_t event_type) -> void override;
auto feedback(lv_group_t*, InputEvent event) -> void override;
private: private:
tts::Provider& tts_; tts::Provider& tts_;

@ -12,6 +12,7 @@
#include "drivers/nvs.hpp" #include "drivers/nvs.hpp"
#include "indev/lv_indev.h" #include "indev/lv_indev.h"
#include "input/input_events.hpp"
#include "input/input_hook.hpp" #include "input/input_hook.hpp"
#include "lua/property.hpp" #include "lua/property.hpp"
@ -27,7 +28,7 @@ class IInputDevice {
public: public:
virtual ~IInputDevice() {} virtual ~IInputDevice() {}
virtual auto read(lv_indev_data_t* data) -> void = 0; virtual auto read(lv_indev_data_t* data, std::vector<InputEvent>& events) -> void = 0;
virtual auto name() -> std::string = 0; virtual auto name() -> std::string = 0;
virtual auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> { virtual auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> {

@ -0,0 +1,18 @@
/*
* Copyright 2025 ailurux <ailuruxx@gmail.com>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
namespace input {
enum class InputEvent {
kNone = 0,
kOnPress = 1,
kOnLongPress = 2,
};
} // namespace input

@ -14,7 +14,7 @@ namespace input {
HardReset::HardReset(drivers::IGpios& gpios) : gpios_(gpios) {} 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<InputEvent>& events) -> void {
bool buttons_pressed = !gpios_.Get(drivers::IGpios::Pin::kKeyUp) && bool buttons_pressed = !gpios_.Get(drivers::IGpios::Pin::kKeyUp) &&
!gpios_.Get(drivers::IGpios::Pin::kKeyDown); !gpios_.Get(drivers::IGpios::Pin::kKeyDown);
if (!buttons_pressed) { if (!buttons_pressed) {

@ -20,7 +20,7 @@ class HardReset : public IInputDevice {
public: public:
HardReset(drivers::IGpios&); HardReset(drivers::IGpios&);
auto read(lv_indev_data_t* data) -> void override; auto read(lv_indev_data_t* data, std::vector<InputEvent>& events) -> void override;
auto name() -> std::string override; auto name() -> std::string override;
auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override; auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;

@ -50,8 +50,9 @@ TriggerHooks::TriggerHooks(std::string name,
long_press_("long_press", long_press), long_press_("long_press", long_press),
repeat_("repeat", repeat) {} repeat_("repeat", repeat) {}
auto TriggerHooks::update(bool pressed, lv_indev_data_t* d) -> void { auto TriggerHooks::update(bool pressed, lv_indev_data_t* d) -> Trigger::State {
switch (trigger_.update(pressed)) { auto state = trigger_.update(pressed);
switch (state) {
case Trigger::State::kClick: case Trigger::State::kClick:
click_.invoke(d); click_.invoke(d);
break; break;
@ -68,6 +69,7 @@ auto TriggerHooks::update(bool pressed, lv_indev_data_t* d) -> void {
default: default:
break; break;
} }
return state;
} }
auto TriggerHooks::override(Trigger::State s, std::optional<HookCallback> cb) auto TriggerHooks::override(Trigger::State s, std::optional<HookCallback> cb)

@ -52,7 +52,7 @@ class TriggerHooks {
std::optional<HookCallback> long_press, std::optional<HookCallback> long_press,
std::optional<HookCallback> repeat); std::optional<HookCallback> repeat);
auto update(bool, lv_indev_data_t*) -> void; auto update(bool, lv_indev_data_t*) -> Trigger::State;
auto override(Trigger::State, std::optional<HookCallback>) -> void; auto override(Trigger::State, std::optional<HookCallback>) -> void;
auto name() const -> const std::string&; auto name() const -> const std::string&;

@ -20,7 +20,7 @@ NavButtons::NavButtons(drivers::IGpios& gpios)
down_("lower", {}, actions::scrollDown(), actions::select(), {}), down_("lower", {}, actions::scrollDown(), actions::select(), {}),
locked_(false) {} locked_(false) {}
auto NavButtons::read(lv_indev_data_t* data) -> void { auto NavButtons::read(lv_indev_data_t* data, std::vector<InputEvent>& events) -> void {
bool up = !gpios_.Get(drivers::IGpios::Pin::kKeyUp); bool up = !gpios_.Get(drivers::IGpios::Pin::kKeyUp);
bool down = !gpios_.Get(drivers::IGpios::Pin::kKeyDown); bool down = !gpios_.Get(drivers::IGpios::Pin::kKeyDown);

@ -23,7 +23,7 @@ class NavButtons : public IInputDevice {
public: public:
NavButtons(drivers::IGpios&); NavButtons(drivers::IGpios&);
auto read(lv_indev_data_t* data) -> void override; auto read(lv_indev_data_t* data, std::vector<InputEvent>& events) -> void override;
auto name() -> std::string override; auto name() -> std::string override;
auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override; auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;

@ -28,7 +28,7 @@ TouchDPad::TouchDPad(drivers::TouchWheel& wheel)
left_("left", actions::goBack(), {}, {}, {}), left_("left", actions::goBack(), {}, {}, {}),
locked_(false) {} locked_(false) {}
auto TouchDPad::read(lv_indev_data_t* data) -> void { auto TouchDPad::read(lv_indev_data_t* data, std::vector<InputEvent>& events) -> void {
if (locked_) { if (locked_) {
return; return;
} }

@ -22,7 +22,7 @@ class TouchDPad : public IInputDevice {
public: public:
TouchDPad(drivers::TouchWheel&); TouchDPad(drivers::TouchWheel&);
auto read(lv_indev_data_t* data) -> void override; auto read(lv_indev_data_t* data, std::vector<InputEvent>& events) -> void override;
auto name() -> std::string override; auto name() -> std::string override;
auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override; auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;

@ -57,7 +57,7 @@ TouchWheel::TouchWheel(drivers::NvsStorage& nvs, drivers::TouchWheel& wheel)
last_angle_(0), last_angle_(0),
last_wheel_touch_time_(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<InputEvent>& events) -> void {
if (locked_) { if (locked_) {
return; return;
} }
@ -88,9 +88,14 @@ auto TouchWheel::read(lv_indev_data_t* data) -> void {
bool wheel_touch_timed_out = bool wheel_touch_timed_out =
esp_timer_get_time() - last_wheel_touch_time_ > SCROLL_TIMEOUT_US; 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, !wheel_data.is_wheel_touched,
data); 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 // If the user is touching the wheel but not scrolling, then they may be
// clicking on one of the wheel's cardinal directions. // clicking on one of the wheel's cardinal directions.

@ -25,7 +25,7 @@ class TouchWheel : public IInputDevice {
public: public:
TouchWheel(drivers::NvsStorage&, drivers::TouchWheel&); TouchWheel(drivers::NvsStorage&, drivers::TouchWheel&);
auto read(lv_indev_data_t* data) -> void override; auto read(lv_indev_data_t* data, std::vector<InputEvent>& events) -> void override;
auto name() -> std::string override; auto name() -> std::string override;
auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override; auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;

@ -44,7 +44,7 @@ auto Trigger::update(bool is_pressed) -> State {
was_double_click_ = false; was_double_click_ = false;
times_long_pressed_ = 0; times_long_pressed_ = 0;
was_pressed_ = true; was_pressed_ = true;
return State::kNone; return State::kPress;
} }
// The key was released. If there were no long-press events fired during the // The key was released. If there were no long-press events fired during the

@ -23,6 +23,7 @@ class Trigger {
kDoubleClick, kDoubleClick,
kLongPress, kLongPress,
kRepeatPress, kRepeatPress,
kPress
}; };
Trigger(); Trigger();

@ -17,7 +17,7 @@ VolumeButtons::VolumeButtons(drivers::IGpios& gpios)
down_("lower", actions::volumeDown()), down_("lower", actions::volumeDown()),
locked_() {} locked_() {}
auto VolumeButtons::read(lv_indev_data_t* data) -> void { auto VolumeButtons::read(lv_indev_data_t* data, std::vector<InputEvent>& events) -> void {
bool up = !gpios_.Get(drivers::IGpios::Pin::kKeyUp); bool up = !gpios_.Get(drivers::IGpios::Pin::kKeyUp);
bool down = !gpios_.Get(drivers::IGpios::Pin::kKeyDown); bool down = !gpios_.Get(drivers::IGpios::Pin::kKeyDown);

@ -22,7 +22,7 @@ class VolumeButtons : public IInputDevice {
public: public:
VolumeButtons(drivers::IGpios&); VolumeButtons(drivers::IGpios&);
auto read(lv_indev_data_t* data) -> void override; auto read(lv_indev_data_t* data, std::vector<InputEvent>& events) -> void override;
auto name() -> std::string override; auto name() -> std::string override;
auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override; auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;

@ -112,6 +112,15 @@ LvglInputDriver::LvglInputDriver(drivers::NvsStorage& nvs,
nvs.LockedInput(*mode); nvs.LockedInput(*mode);
return true; return true;
}), }),
haptics_mode_(static_cast<int>(nvs.HapticsMode()),
[&](const lua::LuaValue& val) {
if (!std::holds_alternative<int>(val)) {
return false;
}
auto mode = drivers::NvsStorage::intToHapticsMode(std::get<int>(val));
nvs.HapticsMode(mode);
return true;
}),
inputs_(factory.createInputs(nvs.PrimaryInput())), inputs_(factory.createInputs(nvs.PrimaryInput())),
feedbacks_(factory.createFeedbacks()), feedbacks_(factory.createFeedbacks()),
is_locked_(false) { is_locked_(false) {
@ -141,8 +150,14 @@ auto LvglInputDriver::setGroup(lv_group_t* g) -> void {
} }
auto LvglInputDriver::read(lv_indev_data_t* data) -> void { auto LvglInputDriver::read(lv_indev_data_t* data) -> void {
std::vector<InputEvent> events;
for (auto&& device : inputs_) { 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);
}
} }
} }

@ -37,6 +37,7 @@ class LvglInputDriver {
auto mode() -> lua::Property& { return mode_; } auto mode() -> lua::Property& { return mode_; }
auto lockedMode() -> lua::Property& { return locked_mode_; } auto lockedMode() -> lua::Property& { return locked_mode_; }
auto hapticsMode() -> lua::Property& { return haptics_mode_; }
auto setGroup(lv_group_t*) -> void; auto setGroup(lv_group_t*) -> void;
auto read(lv_indev_data_t* data) -> void; auto read(lv_indev_data_t* data) -> void;
@ -51,6 +52,7 @@ class LvglInputDriver {
lua::Property mode_; lua::Property mode_;
lua::Property locked_mode_; lua::Property locked_mode_;
lua::Property haptics_mode_;
lv_indev_t* device_; lv_indev_t* device_;
std::vector<std::shared_ptr<IInputDevice>> inputs_; std::vector<std::shared_ptr<IInputDevice>> inputs_;

@ -58,8 +58,28 @@ static auto locked_controls_schemes(lua_State* L) -> int {
return 1; return 1;
} }
static auto haptics_modes(lua_State* L) -> int {
lua_newtable(L);
lua_pushliteral(L, "Disabled");
lua_rawseti(L, -2,
static_cast<int>(drivers::NvsStorage::HapticsModes::kDisabled));
lua_pushliteral(L, "Minimal");
lua_rawseti(
L, -2,
static_cast<int>(drivers::NvsStorage::HapticsModes::kMinimal));
lua_pushliteral(L, "Strong");
lua_rawseti(
L, -2, static_cast<int>(drivers::NvsStorage::HapticsModes::kStrong));
return 1;
}
static const struct luaL_Reg kControlsFuncs[] = {{"schemes", controls_schemes}, static const struct luaL_Reg kControlsFuncs[] = {{"schemes", controls_schemes},
{"locked_schemes", locked_controls_schemes}, {"locked_schemes", locked_controls_schemes},
{"haptics_modes", haptics_modes},
{NULL, NULL}}; {NULL, NULL}};
static auto lua_controls(lua_State* state) -> int { static auto lua_controls(lua_State* state) -> int {

@ -668,6 +668,7 @@ void Lua::entry() {
{ {
{"scheme", &sInput->mode()}, {"scheme", &sInput->mode()},
{"locked_scheme", &sInput->lockedMode()}, {"locked_scheme", &sInput->lockedMode()},
{"haptics_mode", &sInput->hapticsMode()},
{"lock_switch", &sLockSwitch}, {"lock_switch", &sLockSwitch},
{"hooks", [&](lua_State* L) { return sInput->pushHooks(L); }}, {"hooks", [&](lua_State* L) { return sInput->pushHooks(L); }},
}); });

Loading…
Cancel
Save