diff options
Diffstat (limited to 'src/input')
23 files changed, 0 insertions, 1457 deletions
diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt deleted file mode 100644 index 4754ba47..00000000 --- a/src/input/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2023 jacqueline <me@jacqueline.id.au> -# -# SPDX-License-Identifier: GPL-3.0-only - -idf_component_register( - SRCS "input_touch_wheel.cpp" "input_touch_dpad.cpp" "input_trigger.cpp" - "input_volume_buttons.cpp" "lvgl_input_driver.cpp" "feedback_haptics.cpp" - "device_factory.cpp" "input_nav_buttons.cpp" "input_hook.cpp" - "input_hook_actions.cpp" - INCLUDE_DIRS "include" - REQUIRES "drivers" "lvgl" "events" "system_fsm") - -target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/input/device_factory.cpp b/src/input/device_factory.cpp deleted file mode 100644 index 65f4d785..00000000 --- a/src/input/device_factory.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2023 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "device_factory.hpp" - -#include <memory> - -#include "feedback_haptics.hpp" -#include "input_device.hpp" -#include "input_nav_buttons.hpp" -#include "input_touch_dpad.hpp" -#include "input_touch_wheel.hpp" -#include "input_volume_buttons.hpp" - -namespace input { - -DeviceFactory::DeviceFactory( - std::shared_ptr<system_fsm::ServiceLocator> services) - : services_(services) { - if (services->touchwheel()) { - wheel_ = - std::make_shared<TouchWheel>(services->nvs(), **services->touchwheel()); - } -} - -auto DeviceFactory::createInputs(drivers::NvsStorage::InputModes mode) - -> std::vector<std::shared_ptr<IInputDevice>> { - std::vector<std::shared_ptr<IInputDevice>> ret; - switch (mode) { - case drivers::NvsStorage::InputModes::kButtonsOnly: - ret.push_back(std::make_shared<NavButtons>(services_->gpios())); - break; - case drivers::NvsStorage::InputModes::kDirectionalWheel: - ret.push_back(std::make_shared<VolumeButtons>(services_->gpios())); - if (services_->touchwheel()) { - ret.push_back(std::make_shared<TouchDPad>(**services_->touchwheel())); - } - break; - case drivers::NvsStorage::InputModes::kRotatingWheel: - default: // Don't break input over a bad enum value. - ret.push_back(std::make_shared<VolumeButtons>(services_->gpios())); - if (wheel_) { - ret.push_back(wheel_); - } - break; - } - return ret; -} - -auto DeviceFactory::createFeedbacks() - -> std::vector<std::shared_ptr<IFeedbackDevice>> { - return {std::make_shared<Haptics>(services_->haptics())}; -} - -} // namespace input diff --git a/src/input/feedback_haptics.cpp b/src/input/feedback_haptics.cpp deleted file mode 100644 index 5e83d0d6..00000000 --- a/src/input/feedback_haptics.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "feedback_haptics.hpp" - -#include <cstdint> - -#include "lvgl/lvgl.h" - -#include "core/lv_event.h" -#include "esp_log.h" - -#include "haptics.hpp" - -namespace input { - -using Effect = drivers::Haptics::Effect; - -Haptics::Haptics(drivers::Haptics& haptics_) : haptics_(haptics_) {} - -auto Haptics::feedback(uint8_t event_type) -> void { - switch (event_type) { - case LV_EVENT_FOCUSED: - haptics_.PlayWaveformEffect(Effect::kMediumClick1_100Pct); - break; - case LV_EVENT_CLICKED: - haptics_.PlayWaveformEffect(Effect::kSharpClick_100Pct); - break; - default: - break; - } -} - -} // namespace input diff --git a/src/input/include/device_factory.hpp b/src/input/include/device_factory.hpp deleted file mode 100644 index dd9c7133..00000000 --- a/src/input/include/device_factory.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2023 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <cstdint> -#include <memory> - -#include "feedback_device.hpp" -#include "input_device.hpp" -#include "input_touch_wheel.hpp" -#include "nvs.hpp" -#include "service_locator.hpp" - -namespace input { - -class DeviceFactory { - public: - DeviceFactory(std::shared_ptr<system_fsm::ServiceLocator>); - - auto createInputs(drivers::NvsStorage::InputModes mode) - -> std::vector<std::shared_ptr<IInputDevice>>; - - auto createFeedbacks() -> std::vector<std::shared_ptr<IFeedbackDevice>>; - - auto touch_wheel() -> std::shared_ptr<TouchWheel> { return wheel_; } - - private: - std::shared_ptr<system_fsm::ServiceLocator> services_; - - // HACK: the touchwheel is current a special case, since it's the only input - // device that has some kind of setting/configuration; scroll sensitivity. - std::shared_ptr<TouchWheel> wheel_; -}; - -} // namespace input diff --git a/src/input/include/feedback_device.hpp b/src/input/include/feedback_device.hpp deleted file mode 100644 index 4faeeafd..00000000 --- a/src/input/include/feedback_device.hpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <cstdint> - -namespace input { - -/* - * Interface for providing non-visual feedback to the user as a result of LVGL - * events. 'Feedback Devices' are able to observe all events that are generated - * by LVGL as a result of Input Devices. - * - * Implementations of this interface are a mix of hardware features (e.g. a - * haptic motor buzzing when your selection changes) and firmware features - * (e.g. playing audio feedback that describes the selected element). - */ -class IFeedbackDevice { - public: - virtual ~IFeedbackDevice() {} - - virtual auto feedback(uint8_t event_type) -> void = 0; - - // TODO: Add configuration; likely the same shape of interface that - // IInputDevice uses. -}; - -} // namespace input diff --git a/src/input/include/feedback_haptics.hpp b/src/input/include/feedback_haptics.hpp deleted file mode 100644 index a307a429..00000000 --- a/src/input/include/feedback_haptics.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <cstdint> - -#include "feedback_device.hpp" -#include "haptics.hpp" - -namespace input { - -class Haptics : public IFeedbackDevice { - public: - Haptics(drivers::Haptics& haptics_); - - auto feedback(uint8_t event_type) -> void override; - - private: - drivers::Haptics& haptics_; -}; - -} // namespace input diff --git a/src/input/include/input_device.hpp b/src/input/include/input_device.hpp deleted file mode 100644 index d944c3bf..00000000 --- a/src/input/include/input_device.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <functional> -#include <string> -#include <vector> - -#include "hal/lv_hal_indev.h" -#include "input_hook.hpp" -#include "property.hpp" - -namespace input { - -/* - * Interface for all device input methods. Each 'Input Device' is polled by - * LVGL at regular intervals, and can effect the device either via LVGL's input - * device driver API, or by emitting events for other parts of the system to - * react to (e.g. issuing a play/pause event, or altering the volume). - */ -class IInputDevice { - public: - virtual ~IInputDevice() {} - - virtual auto read(lv_indev_data_t* data) -> void = 0; - - virtual auto name() -> std::string = 0; - virtual auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> { - return {}; - } -}; - -} // namespace input diff --git a/src/input/include/input_hook.hpp b/src/input/include/input_hook.hpp deleted file mode 100644 index a8705210..00000000 --- a/src/input/include/input_hook.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <functional> -#include <optional> -#include <string> - -#include "hal/lv_hal_indev.h" -#include "lua.hpp" - -#include "input_trigger.hpp" - -namespace input { - -struct HookCallback { - std::string name; - std::function<void(lv_indev_data_t*)> fn; -}; - -class Hook { - public: - Hook(std::string name, std::optional<HookCallback> cb); - - auto invoke(lv_indev_data_t*) -> void; - auto override(std::optional<HookCallback>) -> void; - - auto name() const -> const std::string& { return name_; } - auto callback() -> std::optional<HookCallback>; - - // Not copyable or movable. - Hook(const Hook&) = delete; - Hook& operator=(const Hook&) = delete; - - private: - std::string name_; - std::optional<HookCallback> default_; - std::optional<HookCallback> override_; -}; - -class TriggerHooks { - public: - TriggerHooks(std::string name, std::optional<HookCallback> cb) - : TriggerHooks(name, cb, cb, cb, cb) {} - TriggerHooks(std::string name, - std::optional<HookCallback> click, - std::optional<HookCallback> double_click, - std::optional<HookCallback> long_press, - std::optional<HookCallback> repeat); - - auto update(bool, lv_indev_data_t*) -> void; - auto override(Trigger::State, std::optional<HookCallback>) -> void; - - auto name() const -> const std::string&; - auto hooks() -> std::vector<std::reference_wrapper<Hook>>; - - // Not copyable or movable. - TriggerHooks(const TriggerHooks&) = delete; - TriggerHooks& operator=(const TriggerHooks&) = delete; - - private: - std::string name_; - Trigger trigger_; - - Hook click_; - Hook double_click_; - Hook long_press_; - Hook repeat_; -}; - -} // namespace input diff --git a/src/input/include/input_hook_actions.hpp b/src/input/include/input_hook_actions.hpp deleted file mode 100644 index 105bd10d..00000000 --- a/src/input/include/input_hook_actions.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include "hal/lv_hal_indev.h" -#include "input_hook.hpp" - -namespace input { -namespace actions { - -auto select() -> HookCallback; - -auto scrollUp() -> HookCallback; -auto scrollDown() -> HookCallback; - -auto scrollToTop() -> HookCallback; -auto scrollToBottom() -> HookCallback; - -auto goBack() -> HookCallback; - -auto volumeUp() -> HookCallback; -auto volumeDown() -> HookCallback; - -auto allActions() -> std::vector<HookCallback>; - -} // namespace actions -} // namespace input diff --git a/src/input/include/input_nav_buttons.hpp b/src/input/include/input_nav_buttons.hpp deleted file mode 100644 index 9feeb375..00000000 --- a/src/input/include/input_nav_buttons.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <cstdint> - -#include "gpios.hpp" -#include "hal/lv_hal_indev.h" - -#include "haptics.hpp" -#include "input_device.hpp" -#include "input_hook.hpp" -#include "input_trigger.hpp" -#include "touchwheel.hpp" - -namespace input { - -class NavButtons : public IInputDevice { - public: - NavButtons(drivers::IGpios&); - - auto read(lv_indev_data_t* data) -> void override; - - auto name() -> std::string override; - auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override; - - private: - drivers::IGpios& gpios_; - - TriggerHooks up_; - TriggerHooks down_; -}; - -} // namespace input diff --git a/src/input/include/input_touch_dpad.hpp b/src/input/include/input_touch_dpad.hpp deleted file mode 100644 index 0c45b2d9..00000000 --- a/src/input/include/input_touch_dpad.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <cstdint> - -#include "hal/lv_hal_indev.h" - -#include "haptics.hpp" -#include "input_device.hpp" -#include "input_hook.hpp" -#include "input_trigger.hpp" -#include "touchwheel.hpp" - -namespace input { - -class TouchDPad : public IInputDevice { - public: - TouchDPad(drivers::TouchWheel&); - - auto read(lv_indev_data_t* data) -> void override; - - auto name() -> std::string override; - auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override; - - private: - drivers::TouchWheel& wheel_; - - TriggerHooks centre_; - TriggerHooks up_; - TriggerHooks right_; - TriggerHooks down_; - TriggerHooks left_; -}; - -} // namespace input diff --git a/src/input/include/input_touch_wheel.hpp b/src/input/include/input_touch_wheel.hpp deleted file mode 100644 index 764cc68d..00000000 --- a/src/input/include/input_touch_wheel.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <sys/_stdint.h> -#include <cstdint> - -#include "hal/lv_hal_indev.h" - -#include "haptics.hpp" -#include "input_device.hpp" -#include "input_hook.hpp" -#include "input_trigger.hpp" -#include "nvs.hpp" -#include "property.hpp" -#include "touchwheel.hpp" - -namespace input { - -class TouchWheel : public IInputDevice { - public: - TouchWheel(drivers::NvsStorage&, drivers::TouchWheel&); - - auto read(lv_indev_data_t* data) -> void override; - - auto name() -> std::string override; - auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override; - - auto sensitivity() -> lua::Property&; - - private: - auto calculateTicks(const drivers::TouchWheelData& data) -> int8_t; - auto calculateThreshold(uint8_t sensitivity) -> uint8_t; - - drivers::NvsStorage& nvs_; - drivers::TouchWheel& wheel_; - - lua::Property sensitivity_; - - TriggerHooks centre_; - TriggerHooks up_; - TriggerHooks right_; - TriggerHooks down_; - TriggerHooks left_; - - bool is_scrolling_; - uint8_t threshold_; - bool is_first_read_; - uint8_t last_angle_; -}; - -} // namespace input diff --git a/src/input/include/input_trigger.hpp b/src/input/include/input_trigger.hpp deleted file mode 100644 index bcafa8ad..00000000 --- a/src/input/include/input_trigger.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <cstdint> -#include <optional> - -#include "hal/lv_hal_indev.h" - -namespace input { - -const uint16_t kDoubleClickDelayMs = 500; -const uint16_t kLongPressDelayMs = LV_INDEV_DEF_LONG_PRESS_TIME; -const uint16_t kRepeatDelayMs = LV_INDEV_DEF_LONG_PRESS_REP_TIME; - -class Trigger { - public: - enum class State { - kNone, - kClick, - kDoubleClick, - kLongPress, - kRepeatPress, - }; - - Trigger(); - - auto update(bool is_pressed) -> State; - - private: - std::optional<uint64_t> touch_time_ms_; - bool was_pressed_; - - bool was_double_click_; - uint16_t times_long_pressed_; -}; - -} // namespace input diff --git a/src/input/include/input_volume_buttons.hpp b/src/input/include/input_volume_buttons.hpp deleted file mode 100644 index e3246df4..00000000 --- a/src/input/include/input_volume_buttons.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <cstdint> - -#include "gpios.hpp" -#include "hal/lv_hal_indev.h" - -#include "haptics.hpp" -#include "input_device.hpp" -#include "input_hook.hpp" -#include "touchwheel.hpp" - -namespace input { - -class VolumeButtons : public IInputDevice { - public: - VolumeButtons(drivers::IGpios&); - - auto read(lv_indev_data_t* data) -> void override; - - auto name() -> std::string override; - auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override; - - private: - drivers::IGpios& gpios_; - - TriggerHooks up_; - TriggerHooks down_; -}; - -} // namespace input diff --git a/src/input/include/lvgl_input_driver.hpp b/src/input/include/lvgl_input_driver.hpp deleted file mode 100644 index 9adaf143..00000000 --- a/src/input/include/lvgl_input_driver.hpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2023 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include <cstdint> -#include <deque> -#include <memory> -#include <set> - -#include "core/lv_group.h" -#include "device_factory.hpp" -#include "feedback_device.hpp" -#include "gpios.hpp" -#include "hal/lv_hal_indev.h" - -#include "input_device.hpp" -#include "input_hook.hpp" -#include "lua_thread.hpp" -#include "nvs.hpp" -#include "property.hpp" -#include "touchwheel.hpp" - -namespace input { - -/* - * Implementation of an LVGL input device. This class composes multiple - * IInputDevice and IFeedbackDevice instances together into a single LVGL - * device. - */ -class LvglInputDriver { - public: - LvglInputDriver(drivers::NvsStorage& nvs, DeviceFactory&); - - auto mode() -> lua::Property& { return mode_; } - - auto read(lv_indev_data_t* data) -> void; - auto feedback(uint8_t) -> void; - - auto registration() -> lv_indev_t* { return registration_; } - auto lock(bool l) -> void { is_locked_ = l; } - - auto pushHooks(lua_State* L) -> int; - - private: - drivers::NvsStorage& nvs_; - DeviceFactory& factory_; - - lua::Property mode_; - lv_indev_drv_t driver_; - lv_indev_t* registration_; - - std::vector<std::shared_ptr<IInputDevice>> inputs_; - std::vector<std::shared_ptr<IFeedbackDevice>> feedbacks_; - - /* - * Key for identifying which device, trigger, and specific hook are being - * overriden by Lua. - */ - struct OverrideSelector { - std::string device_name; - std::string trigger_name; - std::string hook_name; - - friend bool operator<(const OverrideSelector& l, - const OverrideSelector& r) { - return std::tie(l.device_name, l.trigger_name, l.hook_name) < - std::tie(r.device_name, r.trigger_name, r.hook_name); - } - }; - - /* Userdata object for tracking the Lua mirror of a TriggerHooks object. */ - class LuaTrigger { - public: - LuaTrigger(LvglInputDriver&, IInputDevice&, TriggerHooks&); - - static auto get(lua_State*, int idx) -> LuaTrigger&; - static auto luaGc(lua_State*) -> int; - static auto luaToString(lua_State*) -> int; - static auto luaNewIndex(lua_State*) -> int; - - static constexpr struct luaL_Reg kFuncs[] = {{"__gc", luaGc}, - {"__tostring", luaToString}, - {"__newindex", luaNewIndex}, - {NULL, NULL}}; - - private: - LvglInputDriver* driver_; - - std::string device_; - std::string trigger_; - std::map<std::string, std::string> hooks_; - }; - - /* A hook override implemented as a lua callback */ - struct LuaOverride { - lua_State* L; - int ref; - }; - - std::map<OverrideSelector, LuaOverride> overrides_; - - auto setOverride(lua_State* L, const OverrideSelector&) -> void; - auto applyOverride(const OverrideSelector&, LuaOverride&) -> void; - - bool is_locked_; -}; - -} // namespace input diff --git a/src/input/input_hook.cpp b/src/input/input_hook.cpp deleted file mode 100644 index bf9f3596..00000000 --- a/src/input/input_hook.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "input_hook.hpp" - -#include <functional> -#include <optional> -#include "hal/lv_hal_indev.h" -#include "input_trigger.hpp" -#include "lua.h" - -namespace input { - -Hook::Hook(std::string name, std::optional<HookCallback> cb) - : name_(name), default_(cb), override_() {} - -auto Hook::invoke(lv_indev_data_t* d) -> void { - auto cb = callback(); - if (cb) { - std::invoke(cb->fn, d); - } -} - -auto Hook::override(std::optional<HookCallback> cb) -> void { - override_ = cb; -} - -auto Hook::callback() -> std::optional<HookCallback> { - if (override_) { - return override_; - } else if (default_) { - return default_; - } - return {}; -} - -TriggerHooks::TriggerHooks(std::string name, - std::optional<HookCallback> click, - std::optional<HookCallback> double_click, - std::optional<HookCallback> long_press, - std::optional<HookCallback> repeat) - : name_(name), - click_("click", click), - double_click_("double_click", double_click), - long_press_("long_press", long_press), - repeat_("repeat", repeat) {} - -auto TriggerHooks::update(bool pressed, lv_indev_data_t* d) -> void { - switch (trigger_.update(pressed)) { - case Trigger::State::kClick: - click_.invoke(d); - break; - case Trigger::State::kDoubleClick: - double_click_.invoke(d); - break; - case Trigger::State::kLongPress: - long_press_.invoke(d); - break; - case Trigger::State::kRepeatPress: - repeat_.invoke(d); - break; - case Trigger::State::kNone: - default: - break; - } -} - -auto TriggerHooks::override(Trigger::State s, std::optional<HookCallback> cb) - -> void { - switch (s) { - case Trigger::State::kClick: - click_.override(cb); - break; - case Trigger::State::kLongPress: - long_press_.override(cb); - break; - case Trigger::State::kRepeatPress: - repeat_.override(cb); - break; - case Trigger::State::kNone: - default: - break; - } -} - -auto TriggerHooks::name() const -> const std::string& { - return name_; -} - -auto TriggerHooks::hooks() -> std::vector<std::reference_wrapper<Hook>> { - return {click_, long_press_, repeat_}; -} - -} // namespace input diff --git a/src/input/input_hook_actions.cpp b/src/input/input_hook_actions.cpp deleted file mode 100644 index 26075c4c..00000000 --- a/src/input/input_hook_actions.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "input_hook_actions.hpp" - -#include <cstdint> - -#include "hal/lv_hal_indev.h" - -#include "event_queue.hpp" -#include "ui_events.hpp" - -namespace input { -namespace actions { - -auto select() -> HookCallback { - return HookCallback{.name = "select", .fn = [&](lv_indev_data_t* d) { - d->state = LV_INDEV_STATE_PRESSED; - }}; -} - -auto scrollUp() -> HookCallback { - return HookCallback{.name = "scroll_up", - .fn = [&](lv_indev_data_t* d) { d->enc_diff = -1; }}; -} - -auto scrollDown() -> HookCallback { - return HookCallback{.name = "scroll_down", - .fn = [&](lv_indev_data_t* d) { d->enc_diff = 1; }}; -} - -auto scrollToTop() -> HookCallback { - return HookCallback{.name = "scroll_to_top", .fn = [&](lv_indev_data_t* d) { - d->enc_diff = INT16_MIN; - }}; -} - -auto scrollToBottom() -> HookCallback { - return HookCallback{ - .name = "scroll_to_bottom", - .fn = [&](lv_indev_data_t* d) { d->enc_diff = INT16_MAX; }}; -} - -auto goBack() -> HookCallback { - return HookCallback{.name = "back", .fn = [&](lv_indev_data_t* d) { - events::Ui().Dispatch(ui::internal::BackPressed{}); - }}; -} - -auto volumeUp() -> HookCallback { - return HookCallback{.name = "volume_up", .fn = [&](lv_indev_data_t* d) { - events::Audio().Dispatch(audio::StepUpVolume{}); - }}; -} - -auto volumeDown() -> HookCallback { - return HookCallback{.name = "volume_down", .fn = [&](lv_indev_data_t* d) { - events::Audio().Dispatch(audio::StepDownVolume{}); - }}; -} - -auto allActions() -> std::vector<HookCallback> { - return { - select(), scrollUp(), scrollDown(), scrollToTop(), - scrollToBottom(), goBack(), volumeUp(), volumeDown(), - }; -} - -} // namespace actions -} // namespace input diff --git a/src/input/input_nav_buttons.cpp b/src/input/input_nav_buttons.cpp deleted file mode 100644 index 61d80075..00000000 --- a/src/input/input_nav_buttons.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "input_nav_buttons.hpp" - -#include "event_queue.hpp" -#include "gpios.hpp" -#include "hal/lv_hal_indev.h" -#include "input_hook_actions.hpp" - -namespace input { - -NavButtons::NavButtons(drivers::IGpios& gpios) - : gpios_(gpios), - up_("upper", {}, actions::scrollUp(), actions::select(), {}), - down_("lower", {}, actions::scrollDown(), actions::select(), {}) {} - -auto NavButtons::read(lv_indev_data_t* data) -> void { - up_.update(!gpios_.Get(drivers::IGpios::Pin::kKeyUp), data); - down_.update(!gpios_.Get(drivers::IGpios::Pin::kKeyDown), data); -} - -auto NavButtons::name() -> std::string { - return "buttons"; -} - -auto NavButtons::triggers() - -> std::vector<std::reference_wrapper<TriggerHooks>> { - return {up_, down_}; -} - -} // namespace input diff --git a/src/input/input_touch_dpad.cpp b/src/input/input_touch_dpad.cpp deleted file mode 100644 index df17d766..00000000 --- a/src/input/input_touch_dpad.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "input_touch_dpad.hpp" - -#include <cstdint> - -#include "hal/lv_hal_indev.h" - -#include "event_queue.hpp" -#include "haptics.hpp" -#include "input_device.hpp" -#include "input_hook_actions.hpp" -#include "input_touch_dpad.hpp" -#include "touchwheel.hpp" - -namespace input { - -TouchDPad::TouchDPad(drivers::TouchWheel& wheel) - : wheel_(wheel), - centre_("centre", actions::select(), {}, {}, {}), - up_("up", actions::scrollUp()), - right_("right", {}), - down_("down", actions::scrollDown()), - left_("left", actions::goBack()) {} - -auto TouchDPad::read(lv_indev_data_t* data) -> void { - wheel_.Update(); - auto wheel_data = wheel_.GetTouchWheelData(); - - centre_.update(wheel_data.is_button_touched, data); - - up_.update( - wheel_data.is_wheel_touched && - drivers::TouchWheel::isAngleWithin(wheel_data.wheel_position, 0, 32), - data); - right_.update( - wheel_data.is_wheel_touched && drivers::TouchWheel::isAngleWithin( - wheel_data.wheel_position, 192, 32), - data); - down_.update( - wheel_data.is_wheel_touched && drivers::TouchWheel::isAngleWithin( - wheel_data.wheel_position, 128, 32), - data); - left_.update( - wheel_data.is_wheel_touched && - drivers::TouchWheel::isAngleWithin(wheel_data.wheel_position, 64, 32), - data); -} - -auto TouchDPad::name() -> std::string { - return "dpad"; -} - -auto TouchDPad::triggers() - -> std::vector<std::reference_wrapper<TriggerHooks>> { - return {centre_, up_, right_, down_, left_}; -} - -} // namespace input diff --git a/src/input/input_touch_wheel.cpp b/src/input/input_touch_wheel.cpp deleted file mode 100644 index 41fd73bc..00000000 --- a/src/input/input_touch_wheel.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "input_touch_wheel.hpp" - -#include <cstdint> -#include <variant> - -#include "hal/lv_hal_indev.h" - -#include "event_queue.hpp" -#include "haptics.hpp" -#include "input_device.hpp" -#include "input_hook_actions.hpp" -#include "input_trigger.hpp" -#include "nvs.hpp" -#include "property.hpp" -#include "touchwheel.hpp" -#include "ui_events.hpp" - -namespace input { - -TouchWheel::TouchWheel(drivers::NvsStorage& nvs, drivers::TouchWheel& wheel) - : nvs_(nvs), - wheel_(wheel), - sensitivity_(static_cast<int>(nvs.ScrollSensitivity()), - [&](const lua::LuaValue& val) { - if (!std::holds_alternative<int>(val)) { - return false; - } - int int_val = std::get<int>(val); - if (int_val < 0 || int_val > UINT8_MAX) { - return false; - } - nvs.ScrollSensitivity(int_val); - threshold_ = calculateThreshold(int_val); - return true; - }), - centre_("centre", actions::select(), {}, {}, {}), - up_("up", {}, actions::scrollToTop(), {}, {}), - right_("right", {}), - down_("down", {}, actions::scrollToBottom(), {}, {}), - left_("left", {}, actions::goBack(), {}, {}), - is_scrolling_(false), - threshold_(calculateThreshold(nvs.ScrollSensitivity())), - is_first_read_(true), - last_angle_(0) {} - -auto TouchWheel::read(lv_indev_data_t* data) -> void { - wheel_.Update(); - auto wheel_data = wheel_.GetTouchWheelData(); - int8_t ticks = calculateTicks(wheel_data); - - if (!wheel_data.is_wheel_touched) { - // User has released the wheel. - is_scrolling_ = false; - data->enc_diff = 0; - } else if (ticks != 0) { - // User is touching the wheel, and has just passed the sensitivity - // threshold for a scroll tick. - is_scrolling_ = true; - data->enc_diff = ticks; - } else { - // User is touching the wheel, but hasn't moved. - data->enc_diff = 0; - } - - centre_.update(wheel_data.is_button_touched && !wheel_data.is_wheel_touched, - data); - - // If the user is touching the wheel but not scrolling, then they may be - // clicking on one of the wheel's cardinal directions. - bool pressing = wheel_data.is_wheel_touched && !is_scrolling_; - - up_.update(pressing && drivers::TouchWheel::isAngleWithin( - wheel_data.wheel_position, 0, 32), - data); - right_.update(pressing && drivers::TouchWheel::isAngleWithin( - wheel_data.wheel_position, 192, 32), - data); - down_.update(pressing && drivers::TouchWheel::isAngleWithin( - wheel_data.wheel_position, 128, 32), - data); - left_.update(pressing && drivers::TouchWheel::isAngleWithin( - wheel_data.wheel_position, 64, 32), - data); -} - -auto TouchWheel::name() -> std::string { - return "wheel"; -} - -auto TouchWheel::triggers() - -> std::vector<std::reference_wrapper<TriggerHooks>> { - return {centre_, up_, right_, down_, left_}; -} - -auto TouchWheel::sensitivity() -> lua::Property& { - return sensitivity_; -} - -auto TouchWheel::calculateTicks(const drivers::TouchWheelData& data) -> int8_t { - if (!data.is_wheel_touched) { - is_first_read_ = true; - return 0; - } - - uint8_t new_angle = data.wheel_position; - if (is_first_read_) { - is_first_read_ = false; - last_angle_ = new_angle; - return 0; - } - - int delta = 128 - last_angle_; - uint8_t rotated_angle = new_angle + delta; - if (rotated_angle < 128 - threshold_) { - last_angle_ = new_angle; - return 1; - } else if (rotated_angle > 128 + threshold_) { - last_angle_ = new_angle; - return -1; - } else { - return 0; - } -} - -auto TouchWheel::calculateThreshold(uint8_t sensitivity) -> uint8_t { - int tmax = 35; - int tmin = 5; - return (((255. - sensitivity) / 255.) * (tmax - tmin) + tmin); -} - -} // namespace input diff --git a/src/input/input_trigger.cpp b/src/input/input_trigger.cpp deleted file mode 100644 index 00d4a32d..00000000 --- a/src/input/input_trigger.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "input_trigger.hpp" -#include <sys/_stdint.h> - -#include <cstdint> -#include "esp_timer.h" - -namespace input { - -Trigger::Trigger() - : touch_time_ms_(), - was_pressed_(false), - was_double_click_(false), - times_long_pressed_(0) {} - -auto Trigger::update(bool is_pressed) -> State { - // Bail out early if we're in a steady-state of not pressed. - if (!is_pressed && !was_pressed_) { - was_double_click_ = false; - times_long_pressed_ = 0; - return State::kNone; - } - - uint64_t now_ms = esp_timer_get_time() / 1000; - - // This key wasn't being pressed, but now it is. - if (is_pressed && !was_pressed_) { - // Is this a double click? - if (now_ms - *touch_time_ms_ < kDoubleClickDelayMs) { - // Don't update touch_time_ms_, since we don't want triple clicks to - // register as double clicks. - was_double_click_ = true; - was_pressed_ = true; - return State::kDoubleClick; - } - // Not a double click; update our accounting info and wait for the next - // call. - touch_time_ms_ = now_ms; - was_double_click_ = false; - times_long_pressed_ = 0; - was_pressed_ = true; - return State::kNone; - } - - // The key was released. If there were no long-press events fired during the - // press, then this was a standard click. - if (!is_pressed && was_pressed_) { - was_pressed_ = false; - if (!was_double_click_ && times_long_pressed_ == 0) { - return State::kClick; - } else { - return State::kNone; - } - } - - // Now the more complicated case: the user is continuing to press the button. - if (times_long_pressed_ == 0) { - // We haven't fired yet, so we wait for the long-press event. - if (now_ms - *touch_time_ms_ >= kLongPressDelayMs) { - times_long_pressed_++; - return State::kLongPress; - } - } else { - // We've already fired at least once. How long has the user been holding - // the key for? - uint64_t time_since_long_press = - now_ms - (*touch_time_ms_ + kLongPressDelayMs); - - // How many times should we have fired? - // 1 initial fire (for the long-press), plus one additional fire every - // kRepeatDelayMs since the long-press event. - uint16_t expected_times_fired = - 1 + (time_since_long_press / kRepeatDelayMs); - if (times_long_pressed_ < expected_times_fired) { - times_long_pressed_++; - return State::kRepeatPress; - } - } - - return State::kNone; -} - -} // namespace input diff --git a/src/input/input_volume_buttons.cpp b/src/input/input_volume_buttons.cpp deleted file mode 100644 index 37cf90e5..00000000 --- a/src/input/input_volume_buttons.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "input_volume_buttons.hpp" -#include "event_queue.hpp" -#include "gpios.hpp" -#include "input_hook_actions.hpp" - -namespace input { - -VolumeButtons::VolumeButtons(drivers::IGpios& gpios) - : gpios_(gpios), - up_("upper", actions::volumeUp()), - down_("lower", actions::volumeDown()) {} - -auto VolumeButtons::read(lv_indev_data_t* data) -> void { - up_.update(!gpios_.Get(drivers::IGpios::Pin::kKeyUp), data); - down_.update(!gpios_.Get(drivers::IGpios::Pin::kKeyDown), data); -} - -auto VolumeButtons::name() -> std::string { - return "buttons"; -} - -auto VolumeButtons::triggers() - -> std::vector<std::reference_wrapper<TriggerHooks>> { - return {up_, down_}; -} - -} // namespace input diff --git a/src/input/lvgl_input_driver.cpp b/src/input/lvgl_input_driver.cpp deleted file mode 100644 index a82b7438..00000000 --- a/src/input/lvgl_input_driver.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright 2023 jacqueline <me@jacqueline.id.au> - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "lvgl_input_driver.hpp" -#include <stdint.h> - -#include <cstdint> -#include <memory> -#include <variant> - -#include "device_factory.hpp" -#include "feedback_haptics.hpp" -#include "input_hook.hpp" -#include "input_touch_wheel.hpp" -#include "input_trigger.hpp" -#include "input_volume_buttons.hpp" -#include "lauxlib.h" -#include "lua.h" -#include "lua_thread.hpp" -#include "lvgl.h" -#include "nvs.hpp" -#include "property.hpp" - -[[maybe_unused]] static constexpr char kTag[] = "input"; - -static constexpr char kLuaTriggerMetatableName[] = "input_trigger"; -static constexpr char kLuaOverrideText[] = "lua_callback"; - -namespace input { - -static void read_cb(lv_indev_drv_t* drv, lv_indev_data_t* data) { - LvglInputDriver* instance = - reinterpret_cast<LvglInputDriver*>(drv->user_data); - instance->read(data); -} - -static void feedback_cb(lv_indev_drv_t* drv, uint8_t event) { - LvglInputDriver* instance = - reinterpret_cast<LvglInputDriver*>(drv->user_data); - instance->feedback(event); -} - -auto intToMode(int raw) -> std::optional<drivers::NvsStorage::InputModes> { - switch (raw) { - case 0: - return drivers::NvsStorage::InputModes::kButtonsOnly; - case 1: - return drivers::NvsStorage::InputModes::kButtonsWithWheel; - case 2: - return drivers::NvsStorage::InputModes::kDirectionalWheel; - case 3: - return drivers::NvsStorage::InputModes::kRotatingWheel; - default: - return {}; - } -} - -LvglInputDriver::LvglInputDriver(drivers::NvsStorage& nvs, - DeviceFactory& factory) - : nvs_(nvs), - factory_(factory), - mode_(static_cast<int>(nvs.PrimaryInput()), - [&](const lua::LuaValue& val) { - if (!std::holds_alternative<int>(val)) { - return false; - } - auto mode = intToMode(std::get<int>(val)); - if (!mode) { - return false; - } - nvs.PrimaryInput(*mode); - inputs_ = factory.createInputs(*mode); - return true; - }), - driver_(), - registration_(nullptr), - inputs_(factory.createInputs(nvs.PrimaryInput())), - feedbacks_(factory.createFeedbacks()), - is_locked_(false) { - lv_indev_drv_init(&driver_); - driver_.type = LV_INDEV_TYPE_ENCODER; - driver_.read_cb = read_cb; - driver_.feedback_cb = feedback_cb; - driver_.user_data = this; - driver_.long_press_time = kLongPressDelayMs; - driver_.long_press_repeat_time = kRepeatDelayMs; - - registration_ = lv_indev_drv_register(&driver_); -} - -auto LvglInputDriver::read(lv_indev_data_t* data) -> void { - // TODO: we should pass lock state on to the individual devices, since they - // may wish to either ignore the lock state, or power down until unlock. - if (is_locked_) { - return; - } - for (auto&& device : inputs_) { - device->read(data); - } -} - -auto LvglInputDriver::feedback(uint8_t event) -> void { - if (is_locked_) { - return; - } - for (auto&& device : feedbacks_) { - device->feedback(event); - } -} - -LvglInputDriver::LuaTrigger::LuaTrigger(LvglInputDriver& driver, - IInputDevice& dev, - TriggerHooks& trigger) - : driver_(&driver), device_(dev.name()), trigger_(trigger.name()) { - for (auto& hook : trigger.hooks()) { - auto cb = hook.get().callback(); - if (cb) { - hooks_[hook.get().name()] = hook.get().callback()->name; - } else { - hooks_[hook.get().name()] = ""; - } - } -} - -auto LvglInputDriver::LuaTrigger::get(lua_State* L, int idx) -> LuaTrigger& { - return **reinterpret_cast<LuaTrigger**>( - luaL_checkudata(L, idx, kLuaTriggerMetatableName)); -} - -auto LvglInputDriver::LuaTrigger::luaGc(lua_State* L) -> int { - LuaTrigger& trigger = LuaTrigger::get(L, 1); - delete &trigger; - return 0; -} - -auto LvglInputDriver::LuaTrigger::luaToString(lua_State* L) -> int { - LuaTrigger& trigger = LuaTrigger::get(L, 1); - std::stringstream out; - out << "{ "; - for (const auto& hook : trigger.hooks_) { - if (!hook.second.empty()) { - out << hook.first << "=" << hook.second << " "; - } - } - out << "}"; - lua_pushlstring(L, out.str().data(), out.str().size()); - return 1; -} - -auto LvglInputDriver::LuaTrigger::luaNewIndex(lua_State* L) -> int { - LuaTrigger& trigger = LuaTrigger::get(L, 1); - luaL_checktype(L, 3, LUA_TFUNCTION); - - size_t len = 0; - const char* str = luaL_checklstring(L, 2, &len); - if (!str) { - return 0; - } - OverrideSelector selector{ - .device_name = trigger.device_, - .trigger_name = trigger.trigger_, - .hook_name = std::string{str, len}, - }; - for (const auto& hook : trigger.hooks_) { - if (hook.first == selector.hook_name) { - trigger.driver_->setOverride(L, selector); - trigger.hooks_[hook.first] = kLuaOverrideText; - return 0; - } - } - return 0; -} - -auto LvglInputDriver::pushHooks(lua_State* L) -> int { - if (luaL_getmetatable(L, kLuaTriggerMetatableName) == LUA_TNIL) { - luaL_newmetatable(L, kLuaTriggerMetatableName); - luaL_setfuncs(L, LuaTrigger::kFuncs, 0); - lua_pop(L, 1); - } - lua_pop(L, 1); - - lua_newtable(L); - - for (auto& dev : inputs_) { - lua_pushlstring(L, dev->name().data(), dev->name().size()); - lua_newtable(L); - - for (auto& trigger : dev->triggers()) { - lua_pushlstring(L, trigger.get().name().data(), - trigger.get().name().size()); - LuaTrigger** lua_obj = reinterpret_cast<LuaTrigger**>( - lua_newuserdatauv(L, sizeof(LuaTrigger*), 0)); - *lua_obj = new LuaTrigger(*this, *dev, trigger); - luaL_setmetatable(L, kLuaTriggerMetatableName); - lua_rawset(L, -3); - } - - lua_rawset(L, -3); - } - - return 1; -} - -auto LvglInputDriver::setOverride(lua_State* L, - const OverrideSelector& selector) -> void { - if (overrides_.contains(selector)) { - LuaOverride& prev = overrides_[selector]; - luaL_unref(prev.L, LUA_REGISTRYINDEX, prev.ref); - } - - int ref = luaL_ref(L, LUA_REGISTRYINDEX); - LuaOverride override{ - .L = L, - .ref = ref, - }; - overrides_[selector] = override; - applyOverride(selector, override); -} - -auto LvglInputDriver::applyOverride(const OverrideSelector& selector, - LuaOverride& override) -> void { - // In general, this algorithm is a very slow approach. We could do better - // by maintaing maps from [device|trigger|hook]_name to the relevant - // trigger, but in practice I expect maybe like 5 overrides total ever, - // spread across 2 devices with 2 or 5 hooks each. So it's not that big a - // deal. Don't worry about it!! - - // Look for a matching device. - for (auto& device : inputs_) { - if (device->name() != selector.device_name) { - continue; - } - // Look for a matching trigger - for (auto& trigger : device->triggers()) { - if (trigger.get().name() != selector.trigger_name) { - continue; - } - // Look for a matching hook - for (auto& hook : trigger.get().hooks()) { - if (hook.get().name() != selector.hook_name) { - continue; - } - // We found the target! Apply the override. - auto lua_callback = [=](lv_indev_data_t* d) { - lua_rawgeti(override.L, LUA_REGISTRYINDEX, override.ref); - lua::CallProtected(override.L, 0, 0); - }; - hook.get().override( - HookCallback{.name = kLuaOverrideText, .fn = lua_callback}); - } - } - } -} - -} // namespace input |
