diff options
Diffstat (limited to 'src/tangara/input')
| -rw-r--r-- | src/tangara/input/device_factory.cpp | 6 | ||||
| -rw-r--r-- | src/tangara/input/feedback_device.hpp | 3 | ||||
| -rw-r--r-- | src/tangara/input/feedback_haptics.cpp | 9 | ||||
| -rw-r--r-- | src/tangara/input/feedback_haptics.hpp | 5 | ||||
| -rw-r--r-- | src/tangara/input/feedback_tts.cpp | 97 | ||||
| -rw-r--r-- | src/tangara/input/feedback_tts.hpp | 36 | ||||
| -rw-r--r-- | src/tangara/input/lvgl_input_driver.cpp | 14 | ||||
| -rw-r--r-- | src/tangara/input/lvgl_input_driver.hpp | 6 |
8 files changed, 168 insertions, 8 deletions
diff --git a/src/tangara/input/device_factory.cpp b/src/tangara/input/device_factory.cpp index 8e1c5155..09fd2fd2 100644 --- a/src/tangara/input/device_factory.cpp +++ b/src/tangara/input/device_factory.cpp @@ -9,6 +9,7 @@ #include <memory> #include "input/feedback_haptics.hpp" +#include "input/feedback_tts.hpp" #include "input/input_device.hpp" #include "input/input_nav_buttons.hpp" #include "input/input_touch_dpad.hpp" @@ -52,7 +53,10 @@ auto DeviceFactory::createInputs(drivers::NvsStorage::InputModes mode) auto DeviceFactory::createFeedbacks() -> std::vector<std::shared_ptr<IFeedbackDevice>> { - return {std::make_shared<Haptics>(services_->haptics())}; + return { + std::make_shared<Haptics>(services_->haptics()), + std::make_shared<TextToSpeech>(services_->tts()), + }; } } // namespace input diff --git a/src/tangara/input/feedback_device.hpp b/src/tangara/input/feedback_device.hpp index 4faeeafd..8253642f 100644 --- a/src/tangara/input/feedback_device.hpp +++ b/src/tangara/input/feedback_device.hpp @@ -7,6 +7,7 @@ #pragma once #include <cstdint> +#include "core/lv_group.h" namespace input { @@ -23,7 +24,7 @@ class IFeedbackDevice { public: virtual ~IFeedbackDevice() {} - virtual auto feedback(uint8_t event_type) -> void = 0; + virtual auto feedback(lv_group_t* group, uint8_t event_type) -> 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 e690eb9f..c834ca54 100644 --- a/src/tangara/input/feedback_haptics.cpp +++ b/src/tangara/input/feedback_haptics.cpp @@ -8,6 +8,7 @@ #include <cstdint> +#include "core/lv_group.h" #include "lvgl/lvgl.h" #include "core/lv_event.h" @@ -21,7 +22,13 @@ using Effect = drivers::Haptics::Effect; Haptics::Haptics(drivers::Haptics& haptics_) : haptics_(haptics_) {} -auto Haptics::feedback(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); + if (obj == last_selection_) { + return; + } + last_selection_ = obj; + switch (event_type) { case LV_EVENT_FOCUSED: haptics_.PlayWaveformEffect(Effect::kMediumClick1_100Pct); diff --git a/src/tangara/input/feedback_haptics.hpp b/src/tangara/input/feedback_haptics.hpp index bde5f345..91d7ec3a 100644 --- a/src/tangara/input/feedback_haptics.hpp +++ b/src/tangara/input/feedback_haptics.hpp @@ -8,6 +8,8 @@ #include <cstdint> +#include "core/lv_obj.h" + #include "drivers/haptics.hpp" #include "input/feedback_device.hpp" @@ -17,10 +19,11 @@ class Haptics : public IFeedbackDevice { public: Haptics(drivers::Haptics& haptics_); - auto feedback(uint8_t event_type) -> void override; + auto feedback(lv_group_t*, uint8_t event_type) -> void override; private: drivers::Haptics& haptics_; + lv_obj_t* last_selection_; }; } // namespace input diff --git a/src/tangara/input/feedback_tts.cpp b/src/tangara/input/feedback_tts.cpp new file mode 100644 index 00000000..a9267aa8 --- /dev/null +++ b/src/tangara/input/feedback_tts.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2024 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "input/feedback_tts.hpp" + +#include <cstdint> +#include <variant> + +#include "lvgl/lvgl.h" + +#include "core/lv_event.h" +#include "core/lv_group.h" +#include "core/lv_obj.h" +#include "core/lv_obj_class.h" +#include "core/lv_obj_tree.h" +#include "extra/widgets/list/lv_list.h" +#include "tts/events.hpp" +#include "widgets/lv_label.h" + +#include "tts/events.hpp" +#include "tts/provider.hpp" + +namespace input { + +TextToSpeech::TextToSpeech(tts::Provider& tts) + : tts_(tts), last_obj_(nullptr) {} + +auto TextToSpeech::feedback(lv_group_t* group, uint8_t event_type) -> void { + if (group != last_group_) { + last_group_ = group; + last_obj_ = nullptr; + if (group) { + tts_.feed(tts::SimpleEvent::kContextChanged); + } + } + + if (group) { + lv_obj_t* focused = lv_group_get_focused(group); + if (focused == last_obj_) { + return; + } + + last_obj_ = focused; + if (focused != nullptr) { + describe(*focused); + } + } +} + +auto TextToSpeech::describe(lv_obj_t& obj) -> void { + if (lv_obj_check_type(&obj, &lv_btn_class) || + lv_obj_check_type(&obj, &lv_list_btn_class)) { + auto desc = findDescription(obj); + tts_.feed(tts::SelectionChanged{ + .new_selection = + tts::SelectionChanged::Selection{ + .description = desc, + .is_interactive = true, + }, + }); + } else { + auto desc = findDescription(obj); + tts_.feed(tts::SelectionChanged{ + .new_selection = + tts::SelectionChanged::Selection{ + .description = desc, + .is_interactive = false, + }, + }); + } +} + +auto TextToSpeech::findDescription(lv_obj_t& obj) + -> std::optional<std::string> { + if (lv_obj_get_child_cnt(&obj) > 0) { + for (size_t i = 0; i < lv_obj_get_child_cnt(&obj); i++) { + auto res = findDescription(*lv_obj_get_child(&obj, i)); + if (res) { + return res; + } + } + } + + if (lv_obj_check_type(&obj, &lv_label_class)) { + std::string text = lv_label_get_text(&obj); + if (!text.empty()) { + return text; + } + } + + return {}; +} + +} // namespace input diff --git a/src/tangara/input/feedback_tts.hpp b/src/tangara/input/feedback_tts.hpp new file mode 100644 index 00000000..ddd83ff0 --- /dev/null +++ b/src/tangara/input/feedback_tts.hpp @@ -0,0 +1,36 @@ +/* + * Copyright 2024 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include <cstdint> + +#include "core/lv_obj.h" +#include "drivers/haptics.hpp" + +#include "input/feedback_device.hpp" +#include "tts/events.hpp" +#include "tts/provider.hpp" + +namespace input { + +class TextToSpeech : public IFeedbackDevice { + public: + TextToSpeech(tts::Provider&); + + auto feedback(lv_group_t*, uint8_t event_type) -> void override; + + private: + tts::Provider& tts_; + + auto describe(lv_obj_t&) -> void; + auto findDescription(lv_obj_t&) -> std::optional<std::string>; + + lv_group_t* last_group_; + lv_obj_t* last_obj_; +}; + +} // namespace input diff --git a/src/tangara/input/lvgl_input_driver.cpp b/src/tangara/input/lvgl_input_driver.cpp index 8d10bb13..9c1ccff9 100644 --- a/src/tangara/input/lvgl_input_driver.cpp +++ b/src/tangara/input/lvgl_input_driver.cpp @@ -10,6 +10,8 @@ #include <memory> #include <variant> +#include "core/lv_event.h" +#include "core/lv_indev.h" #include "lua.hpp" #include "lvgl.h" @@ -91,6 +93,16 @@ LvglInputDriver::LvglInputDriver(drivers::NvsStorage& nvs, registration_ = lv_indev_drv_register(&driver_); } +auto LvglInputDriver::setGroup(lv_group_t* g) -> void { + if (!g) { + return; + } + lv_indev_set_group(registration_, g); + // Emit a synthetic 'focus' event for the current selection, since otherwise + // our feedback devices won't know that the selection changed. + feedback(LV_EVENT_FOCUSED); +} + 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. @@ -107,7 +119,7 @@ auto LvglInputDriver::feedback(uint8_t event) -> void { return; } for (auto&& device : feedbacks_) { - device->feedback(event); + device->feedback(registration_->group, event); } } diff --git a/src/tangara/input/lvgl_input_driver.hpp b/src/tangara/input/lvgl_input_driver.hpp index 8ede1855..0b6a7e76 100644 --- a/src/tangara/input/lvgl_input_driver.hpp +++ b/src/tangara/input/lvgl_input_driver.hpp @@ -17,12 +17,12 @@ #include "input/device_factory.hpp" #include "input/feedback_device.hpp" +#include "drivers/nvs.hpp" +#include "drivers/touchwheel.hpp" #include "input/input_device.hpp" #include "input/input_hook.hpp" #include "lua/lua_thread.hpp" #include "lua/property.hpp" -#include "drivers/nvs.hpp" -#include "drivers/touchwheel.hpp" namespace input { @@ -37,10 +37,10 @@ class LvglInputDriver { auto mode() -> lua::Property& { return mode_; } + auto setGroup(lv_group_t*) -> void; 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; |
