Migrate all existing control schemes to the cool new world

custom
jacqueline 1 year ago
parent ed82063af5
commit 33919e9e3f
  1. 4
      src/drivers/include/touchwheel.hpp
  2. 7
      src/drivers/touchwheel.cpp
  3. 3
      src/input/CMakeLists.txt
  4. 58
      src/input/device_factory.cpp
  5. 39
      src/input/include/device_factory.hpp
  6. 6
      src/input/include/input_device.hpp
  7. 34
      src/input/include/input_nav_buttons.hpp
  8. 6
      src/input/include/input_touch_dpad.hpp
  9. 20
      src/input/include/input_touch_wheel.hpp
  10. 37
      src/input/include/input_trigger.hpp
  11. 4
      src/input/include/input_volume_buttons.hpp
  12. 17
      src/input/include/lvgl_input_driver.hpp
  13. 44
      src/input/input_nav_buttons.cpp
  14. 42
      src/input/input_touch_dpad.cpp
  15. 76
      src/input/input_touch_wheel.cpp
  16. 72
      src/input/input_trigger.cpp
  17. 17
      src/input/input_volume_buttons.cpp
  18. 59
      src/input/lvgl_input_driver.cpp
  19. 1
      src/ui/include/ui_events.hpp
  20. 9
      src/ui/include/ui_fsm.hpp
  21. 84
      src/ui/ui_fsm.cpp

@ -24,6 +24,10 @@ struct TouchWheelData {
class TouchWheel { class TouchWheel {
public: public:
static auto isAngleWithin(int16_t wheel_angle,
int16_t target_angle,
int threshold) -> bool;
static auto Create() -> TouchWheel* { return new TouchWheel(); } static auto Create() -> TouchWheel* { return new TouchWheel(); }
TouchWheel(); TouchWheel();
~TouchWheel(); ~TouchWheel();

@ -28,6 +28,13 @@ namespace drivers {
static const uint8_t kTouchWheelAddress = 0x1C; static const uint8_t kTouchWheelAddress = 0x1C;
static const gpio_num_t kIntPin = GPIO_NUM_25; static const gpio_num_t kIntPin = GPIO_NUM_25;
auto TouchWheel::isAngleWithin(int16_t wheel_angle,
int16_t target_angle,
int threshold) -> bool {
int16_t difference = (wheel_angle - target_angle + 127 + 255) % 255 - 127;
return difference <= threshold && difference >= -threshold;
}
TouchWheel::TouchWheel() { TouchWheel::TouchWheel() {
gpio_config_t int_config{ gpio_config_t int_config{
.pin_bit_mask = 1ULL << kIntPin, .pin_bit_mask = 1ULL << kIntPin,

@ -3,8 +3,9 @@
# SPDX-License-Identifier: GPL-3.0-only # SPDX-License-Identifier: GPL-3.0-only
idf_component_register( idf_component_register(
SRCS "input_touch_wheel.cpp" "input_touch_dpad.cpp" SRCS "input_touch_wheel.cpp" "input_touch_dpad.cpp" "input_trigger.cpp"
"input_volume_buttons.cpp" "lvgl_input_driver.cpp" "feedback_haptics.cpp" "input_volume_buttons.cpp" "lvgl_input_driver.cpp" "feedback_haptics.cpp"
"device_factory.cpp" "input_nav_buttons.cpp"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
REQUIRES "drivers" "lvgl" "events" "system_fsm") REQUIRES "drivers" "lvgl" "events" "system_fsm")

@ -0,0 +1,58 @@
/*
* 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

@ -0,0 +1,39 @@
/*
* 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

@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include "hal/lv_hal_indev.h" #include "hal/lv_hal_indev.h"
#include "property.hpp"
namespace input { namespace input {
@ -28,6 +29,11 @@ class IInputDevice {
// TODO: Add hooks and configuration (or are hooks just one kind of // TODO: Add hooks and configuration (or are hooks just one kind of
// configuration?) // configuration?)
virtual auto settings()
-> std::vector<std::pair<std::string, lua::Property>> {
return {};
}
}; };
} // namespace input } // namespace input

@ -0,0 +1,34 @@
/*
* 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_trigger.hpp"
#include "touchwheel.hpp"
namespace input {
class NavButtons : public IInputDevice {
public:
NavButtons(drivers::IGpios&);
auto read(lv_indev_data_t* data) -> void override;
private:
drivers::IGpios& gpios_;
Trigger up_;
Trigger down_;
};
} // namespace input

@ -13,6 +13,7 @@
#include "haptics.hpp" #include "haptics.hpp"
#include "input_device.hpp" #include "input_device.hpp"
#include "input_trigger.hpp"
#include "touchwheel.hpp" #include "touchwheel.hpp"
namespace input { namespace input {
@ -25,6 +26,11 @@ class TouchDPad : public IInputDevice {
private: private:
drivers::TouchWheel& wheel_; drivers::TouchWheel& wheel_;
Trigger up_;
Trigger right_;
Trigger down_;
Trigger left_;
}; };
} // namespace input } // namespace input

@ -6,29 +6,43 @@
#pragma once #pragma once
#include <sys/_stdint.h>
#include <cstdint> #include <cstdint>
#include "hal/lv_hal_indev.h" #include "hal/lv_hal_indev.h"
#include "haptics.hpp" #include "haptics.hpp"
#include "input_device.hpp" #include "input_device.hpp"
#include "input_trigger.hpp"
#include "nvs.hpp"
#include "property.hpp"
#include "touchwheel.hpp" #include "touchwheel.hpp"
namespace input { namespace input {
class TouchWheel : public IInputDevice { class TouchWheel : public IInputDevice {
public: public:
TouchWheel(drivers::TouchWheel&); TouchWheel(drivers::NvsStorage&, drivers::TouchWheel&);
auto read(lv_indev_data_t* data) -> void override; auto read(lv_indev_data_t* data) -> void override;
auto sensitivity() -> lua::Property&;
private: private:
auto calculate_ticks(const drivers::TouchWheelData& data) -> int8_t; auto calculateTicks(const drivers::TouchWheelData& data) -> int8_t;
auto calculateThreshold(uint8_t sensitivity) -> uint8_t;
drivers::NvsStorage& nvs_;
drivers::TouchWheel& wheel_; drivers::TouchWheel& wheel_;
lua::Property sensitivity_;
Trigger up_;
Trigger right_;
Trigger down_;
Trigger left_;
bool is_scrolling_; bool is_scrolling_;
uint8_t sensitivity_;
uint8_t threshold_; uint8_t threshold_;
bool is_first_read_; bool is_first_read_;
uint8_t last_angle_; uint8_t last_angle_;

@ -0,0 +1,37 @@
/*
* 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 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,
kLongPress,
kRepeatPress,
};
Trigger();
auto update(bool is_pressed) -> State;
private:
std::optional<uint64_t> touch_time_ms_;
uint16_t times_fired_;
};
} // namespace input

@ -13,6 +13,7 @@
#include "haptics.hpp" #include "haptics.hpp"
#include "input_device.hpp" #include "input_device.hpp"
#include "input_trigger.hpp"
#include "touchwheel.hpp" #include "touchwheel.hpp"
namespace input { namespace input {
@ -25,6 +26,9 @@ class VolumeButtons : public IInputDevice {
private: private:
drivers::IGpios& gpios_; drivers::IGpios& gpios_;
Trigger up_;
Trigger down_;
}; };
} // namespace input } // namespace input

@ -6,19 +6,20 @@
#pragma once #pragma once
#include <stdint.h> #include <cstdint>
#include <deque> #include <deque>
#include <memory> #include <memory>
#include <set> #include <set>
#include "core/lv_group.h" #include "core/lv_group.h"
#include "device_factory.hpp"
#include "feedback_device.hpp" #include "feedback_device.hpp"
#include "gpios.hpp" #include "gpios.hpp"
#include "hal/lv_hal_indev.h" #include "hal/lv_hal_indev.h"
#include "input_device.hpp" #include "input_device.hpp"
#include "nvs.hpp" #include "nvs.hpp"
#include "service_locator.hpp" #include "property.hpp"
#include "touchwheel.hpp" #include "touchwheel.hpp"
namespace input { namespace input {
@ -30,7 +31,9 @@ namespace input {
*/ */
class LvglInputDriver { class LvglInputDriver {
public: public:
LvglInputDriver(std::shared_ptr<system_fsm::ServiceLocator>); LvglInputDriver(drivers::NvsStorage& nvs, DeviceFactory&);
auto mode() -> lua::Property& { return mode_; }
auto read(lv_indev_data_t* data) -> void; auto read(lv_indev_data_t* data) -> void;
auto feedback(uint8_t) -> void; auto feedback(uint8_t) -> void;
@ -39,13 +42,15 @@ class LvglInputDriver {
auto lock(bool l) -> void { is_locked_ = l; } auto lock(bool l) -> void { is_locked_ = l; }
private: private:
std::shared_ptr<system_fsm::ServiceLocator> services_; drivers::NvsStorage& nvs_;
DeviceFactory& factory_;
lua::Property mode_;
lv_indev_drv_t driver_; lv_indev_drv_t driver_;
lv_indev_t* registration_; lv_indev_t* registration_;
std::vector<std::unique_ptr<IInputDevice>> inputs_; std::vector<std::shared_ptr<IInputDevice>> inputs_;
std::vector<std::unique_ptr<IFeedbackDevice>> feedbacks_; std::vector<std::shared_ptr<IFeedbackDevice>> feedbacks_;
bool is_locked_; bool is_locked_;
}; };

@ -0,0 +1,44 @@
/*
* 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"
namespace input {
NavButtons::NavButtons(drivers::IGpios& gpios) : gpios_(gpios) {}
auto NavButtons::read(lv_indev_data_t* data) -> void {
bool vol_up = gpios_.Get(drivers::IGpios::Pin::kKeyUp);
switch (up_.update(!vol_up)) {
case Trigger::State::kClick:
data->enc_diff = -1;
break;
case Trigger::State::kLongPress:
events::Ui().Dispatch(ui::internal::BackPressed{});
break;
default:
break;
}
bool vol_down = gpios_.Get(drivers::IGpios::Pin::kKeyDown);
switch (down_.update(!vol_down)) {
case Trigger::State::kClick:
data->enc_diff = 1;
break;
case Trigger::State::kLongPress:
data->state = LV_INDEV_STATE_PRESSED;
break;
default:
data->state = LV_INDEV_STATE_RELEASED;
break;
}
}
} // namespace input

@ -10,6 +10,7 @@
#include "hal/lv_hal_indev.h" #include "hal/lv_hal_indev.h"
#include "event_queue.hpp"
#include "haptics.hpp" #include "haptics.hpp"
#include "input_device.hpp" #include "input_device.hpp"
#include "input_touch_dpad.hpp" #include "input_touch_dpad.hpp"
@ -28,8 +29,47 @@ TouchDPad::TouchDPad(drivers::TouchWheel& wheel) : wheel_(wheel) {}
auto TouchDPad::read(lv_indev_data_t* data) -> void { auto TouchDPad::read(lv_indev_data_t* data) -> void {
wheel_.Update(); wheel_.Update();
auto wheel_data = wheel_.GetTouchWheelData();
// TODO: reimplement if (wheel_data.is_button_touched) {
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
switch (up_.update(
wheel_data.is_wheel_touched &&
drivers::TouchWheel::isAngleWithin(wheel_data.wheel_position, 0, 32))) {
case Trigger::State::kNone:
break;
default:
data->enc_diff = -1;
break;
}
switch (right_.update(
wheel_data.is_wheel_touched &&
drivers::TouchWheel::isAngleWithin(wheel_data.wheel_position, 192, 32))) {
default:
break;
}
switch (down_.update(
wheel_data.is_wheel_touched &&
drivers::TouchWheel::isAngleWithin(wheel_data.wheel_position, 128, 32))) {
case Trigger::State::kNone:
break;
default:
data->enc_diff = 1;
break;
}
switch (left_.update(
wheel_data.is_wheel_touched &&
drivers::TouchWheel::isAngleWithin(wheel_data.wheel_position, 64, 32))) {
case Trigger::State::kLongPress:
events::Ui().Dispatch(ui::internal::BackPressed{});
break;
default:
break;
}
} }
} // namespace input } // namespace input

@ -8,27 +8,46 @@
#include <stdint.h> #include <stdint.h>
#include <cstdint> #include <cstdint>
#include <variant>
#include "event_queue.hpp"
#include "hal/lv_hal_indev.h" #include "hal/lv_hal_indev.h"
#include "haptics.hpp" #include "haptics.hpp"
#include "input_device.hpp" #include "input_device.hpp"
#include "input_trigger.hpp"
#include "nvs.hpp"
#include "property.hpp"
#include "touchwheel.hpp" #include "touchwheel.hpp"
#include "ui_events.hpp"
namespace input { namespace input {
TouchWheel::TouchWheel(drivers::TouchWheel& wheel) TouchWheel::TouchWheel(drivers::NvsStorage& nvs, drivers::TouchWheel& wheel)
: wheel_(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;
}),
is_scrolling_(false), is_scrolling_(false),
sensitivity_(128), threshold_(calculateThreshold(nvs.ScrollSensitivity())),
threshold_(10),
is_first_read_(true), is_first_read_(true),
last_angle_(0) {} last_angle_(0) {}
auto TouchWheel::read(lv_indev_data_t* data) -> void { auto TouchWheel::read(lv_indev_data_t* data) -> void {
wheel_.Update(); wheel_.Update();
auto wheel_data = wheel_.GetTouchWheelData(); auto wheel_data = wheel_.GetTouchWheelData();
int8_t ticks = calculate_ticks(wheel_data); int8_t ticks = calculateTicks(wheel_data);
if (!wheel_data.is_wheel_touched) { if (!wheel_data.is_wheel_touched) {
// User has released the wheel. // User has released the wheel.
@ -49,10 +68,47 @@ auto TouchWheel::read(lv_indev_data_t* data) -> void {
} else { } else {
data->state = LV_INDEV_STATE_RELEASED; data->state = LV_INDEV_STATE_RELEASED;
} }
// 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_;
switch (up_.update(pressing && drivers::TouchWheel::isAngleWithin(
wheel_data.wheel_position, 0, 32))) {
case Trigger::State::kLongPress:
data->enc_diff = INT16_MIN;
break;
default:
break;
}
switch (right_.update(pressing && drivers::TouchWheel::isAngleWithin(
wheel_data.wheel_position, 192, 32))) {
default:
break;
}
switch (down_.update(pressing && drivers::TouchWheel::isAngleWithin(
wheel_data.wheel_position, 128, 32))) {
case Trigger::State::kLongPress:
data->enc_diff = INT16_MAX;
break;
default:
break;
}
switch (left_.update(pressing && drivers::TouchWheel::isAngleWithin(
wheel_data.wheel_position, 64, 32))) {
case Trigger::State::kLongPress:
events::Ui().Dispatch(ui::internal::BackPressed{});
break;
default:
break;
}
}
auto TouchWheel::sensitivity() -> lua::Property& {
return sensitivity_;
} }
auto TouchWheel::calculate_ticks(const drivers::TouchWheelData& data) auto TouchWheel::calculateTicks(const drivers::TouchWheelData& data) -> int8_t {
-> int8_t {
if (!data.is_wheel_touched) { if (!data.is_wheel_touched) {
is_first_read_ = true; is_first_read_ = true;
return 0; return 0;
@ -78,4 +134,10 @@ auto TouchWheel::calculate_ticks(const drivers::TouchWheelData& data)
} }
} }
auto TouchWheel::calculateThreshold(uint8_t sensitivity) -> uint8_t {
int tmax = 35;
int tmin = 5;
return (((255. - sensitivity) / 255.) * (tmax - tmin) + tmin);
}
} // namespace input } // namespace input

@ -0,0 +1,72 @@
/*
* 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_log.h"
#include "esp_timer.h"
namespace input {
Trigger::Trigger() : touch_time_ms_(), times_fired_(0) {}
auto Trigger::update(bool is_pressed) -> State {
// Bail out early if we're in a steady-state of not pressed.
if (!is_pressed && !touch_time_ms_) {
return State::kNone;
}
uint64_t now_ms = esp_timer_get_time() / 1000;
// Initial press of this key: record the current time, and report that we
// haven't triggered yet.
if (is_pressed && !touch_time_ms_) {
touch_time_ms_ = now_ms;
times_fired_ = 0;
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 && touch_time_ms_) {
touch_time_ms_.reset();
if (times_fired_ == 0) {
return State::kClick;
} else {
return State::kNone;
}
}
// Now the more complicated case: the user is continuing to press the button.
if (times_fired_ == 0) {
// We haven't fired yet, so we wait for the long-press event.
if (now_ms - *touch_time_ms_ >= kLongPressDelayMs) {
times_fired_++;
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_fired_ < expected_times_fired) {
times_fired_++;
return State::kRepeatPress;
}
}
return State::kNone;
}
} // namespace input

@ -5,6 +5,7 @@
*/ */
#include "input_volume_buttons.hpp" #include "input_volume_buttons.hpp"
#include "event_queue.hpp"
#include "gpios.hpp" #include "gpios.hpp"
namespace input { namespace input {
@ -13,13 +14,21 @@ VolumeButtons::VolumeButtons(drivers::IGpios& gpios) : gpios_(gpios) {}
auto VolumeButtons::read(lv_indev_data_t* data) -> void { auto VolumeButtons::read(lv_indev_data_t* data) -> void {
bool vol_up = gpios_.Get(drivers::IGpios::Pin::kKeyUp); bool vol_up = gpios_.Get(drivers::IGpios::Pin::kKeyUp);
if (!vol_up) { switch (up_.update(!vol_up)) {
ESP_LOGI("volume", "vol up"); case Trigger::State::kNone:
break;
default:
events::Audio().Dispatch(audio::StepUpVolume{});
break;
} }
bool vol_down = gpios_.Get(drivers::IGpios::Pin::kKeyDown); bool vol_down = gpios_.Get(drivers::IGpios::Pin::kKeyDown);
if (!vol_down) { switch (down_.update(!vol_down)) {
ESP_LOGI("volume", "vol down"); case Trigger::State::kNone:
break;
default:
events::Audio().Dispatch(audio::StepDownVolume{});
break;
} }
} }

@ -9,12 +9,16 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <variant>
#include "device_factory.hpp"
#include "feedback_haptics.hpp" #include "feedback_haptics.hpp"
#include "input_touch_wheel.hpp" #include "input_touch_wheel.hpp"
#include "input_trigger.hpp"
#include "input_volume_buttons.hpp" #include "input_volume_buttons.hpp"
#include "lvgl.h" #include "lvgl.h"
#include "service_locator.hpp" #include "nvs.hpp"
#include "property.hpp"
[[maybe_unused]] static constexpr char kTag[] = "input"; [[maybe_unused]] static constexpr char kTag[] = "input";
@ -32,33 +36,52 @@ static void feedback_cb(lv_indev_drv_t* drv, uint8_t event) {
instance->feedback(event); instance->feedback(event);
} }
LvglInputDriver::LvglInputDriver( auto intToMode(int raw) -> std::optional<drivers::NvsStorage::InputModes> {
std::shared_ptr<system_fsm::ServiceLocator> services) switch (raw) {
: services_(services), 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_(), driver_(),
registration_(nullptr), registration_(nullptr),
inputs_(), inputs_(factory.createInputs(nvs.PrimaryInput())),
feedbacks_(), feedbacks_(factory.createFeedbacks()),
is_locked_(false) { is_locked_(false) {
lv_indev_drv_init(&driver_); lv_indev_drv_init(&driver_);
driver_.type = LV_INDEV_TYPE_ENCODER; driver_.type = LV_INDEV_TYPE_ENCODER;
driver_.read_cb = read_cb; driver_.read_cb = read_cb;
driver_.feedback_cb = feedback_cb; driver_.feedback_cb = feedback_cb;
driver_.user_data = this; driver_.user_data = this;
driver_.long_press_time = kLongPressDelayMs;
driver_.long_press_repeat_time = kRepeatDelayMs;
registration_ = lv_indev_drv_register(&driver_); registration_ = lv_indev_drv_register(&driver_);
// TODO: Make these devices configurable. I'm thinking each device gets an id
// and then we have:
// - a factory to create instance given an id
// - add/remove device methods on LvglInputDriver that operate on ids
// - the user's enabled devices (+ their configuration) stored in NVS.
auto touchwheel = services_->touchwheel();
if (touchwheel) {
inputs_.push_back(std::make_unique<TouchWheel>(**touchwheel));
}
inputs_.push_back(std::make_unique<VolumeButtons>(services_->gpios()));
feedbacks_.push_back(std::make_unique<Haptics>(services_->haptics()));
} }
auto LvglInputDriver::read(lv_indev_data_t* data) -> void { auto LvglInputDriver::read(lv_indev_data_t* data) -> void {

@ -32,7 +32,6 @@ struct DumpLuaStack : tinyfsm::Event {};
namespace internal { namespace internal {
struct ControlSchemeChanged : tinyfsm::Event {};
struct ReindexDatabase : tinyfsm::Event {}; struct ReindexDatabase : tinyfsm::Event {};
struct BackPressed : tinyfsm::Event {}; struct BackPressed : tinyfsm::Event {};

@ -13,8 +13,12 @@
#include "audio_events.hpp" #include "audio_events.hpp"
#include "battery.hpp" #include "battery.hpp"
#include "db_events.hpp" #include "db_events.hpp"
#include "device_factory.hpp"
#include "display.hpp" #include "display.hpp"
#include "feedback_haptics.hpp"
#include "gpios.hpp" #include "gpios.hpp"
#include "input_touch_wheel.hpp"
#include "input_volume_buttons.hpp"
#include "lua_thread.hpp" #include "lua_thread.hpp"
#include "lvgl_input_driver.hpp" #include "lvgl_input_driver.hpp"
#include "lvgl_task.hpp" #include "lvgl_task.hpp"
@ -67,7 +71,6 @@ class UiState : public tinyfsm::Fsm<UiState> {
void react(const system_fsm::SamdUsbStatusChanged&); void react(const system_fsm::SamdUsbStatusChanged&);
void react(const internal::DismissAlerts&); void react(const internal::DismissAlerts&);
void react(const internal::ControlSchemeChanged&);
void react(const database::event::UpdateStarted&); void react(const database::event::UpdateStarted&);
void react(const database::event::UpdateProgress&){}; void react(const database::event::UpdateProgress&){};
@ -91,7 +94,9 @@ class UiState : public tinyfsm::Fsm<UiState> {
static std::unique_ptr<UiTask> sTask; static std::unique_ptr<UiTask> sTask;
static std::shared_ptr<system_fsm::ServiceLocator> sServices; static std::shared_ptr<system_fsm::ServiceLocator> sServices;
static std::unique_ptr<drivers::Display> sDisplay; static std::unique_ptr<drivers::Display> sDisplay;
static std::shared_ptr<input::LvglInputDriver> sInput; static std::shared_ptr<input::LvglInputDriver> sInput;
static std::unique_ptr<input::DeviceFactory> sDeviceFactory;
static std::stack<std::shared_ptr<Screen>> sScreens; static std::stack<std::shared_ptr<Screen>> sScreens;
static std::shared_ptr<Screen> sCurrentScreen; static std::shared_ptr<Screen> sCurrentScreen;
@ -125,8 +130,6 @@ class UiState : public tinyfsm::Fsm<UiState> {
static lua::Property sDisplayBrightness; static lua::Property sDisplayBrightness;
static lua::Property sControlsScheme;
static lua::Property sScrollSensitivity;
static lua::Property sLockSwitch; static lua::Property sLockSwitch;
static lua::Property sDatabaseUpdating; static lua::Property sDatabaseUpdating;

@ -12,9 +12,14 @@
#include "bluetooth_types.hpp" #include "bluetooth_types.hpp"
#include "db_events.hpp" #include "db_events.hpp"
#include "device_factory.hpp"
#include "display_init.hpp" #include "display_init.hpp"
#include "feedback_haptics.hpp"
#include "freertos/portmacro.h" #include "freertos/portmacro.h"
#include "freertos/projdefs.h" #include "freertos/projdefs.h"
#include "input_device.hpp"
#include "input_touch_wheel.hpp"
#include "input_volume_buttons.hpp"
#include "lua.h" #include "lua.h"
#include "lua.hpp" #include "lua.hpp"
@ -62,7 +67,9 @@ namespace ui {
std::unique_ptr<UiTask> UiState::sTask; std::unique_ptr<UiTask> UiState::sTask;
std::shared_ptr<system_fsm::ServiceLocator> UiState::sServices; std::shared_ptr<system_fsm::ServiceLocator> UiState::sServices;
std::unique_ptr<drivers::Display> UiState::sDisplay; std::unique_ptr<drivers::Display> UiState::sDisplay;
std::shared_ptr<input::LvglInputDriver> UiState::sInput; std::shared_ptr<input::LvglInputDriver> UiState::sInput;
std::unique_ptr<input::DeviceFactory> UiState::sDeviceFactory;
std::stack<std::shared_ptr<Screen>> UiState::sScreens; std::stack<std::shared_ptr<Screen>> UiState::sScreens;
std::shared_ptr<Screen> UiState::sCurrentScreen; std::shared_ptr<Screen> UiState::sCurrentScreen;
@ -233,59 +240,6 @@ lua::Property UiState::sDisplayBrightness{
return true; return true;
}}; }};
lua::Property UiState::sControlsScheme{0, [](const lua::LuaValue& val) {
/*
if (!std::holds_alternative<int>(val))
{ return false;
}
drivers::NvsStorage::InputModes mode;
switch (std::get<int>(val)) {
case 0:
mode =
drivers::NvsStorage::InputModes::kButtonsOnly;
break;
case 1:
mode =
drivers::NvsStorage::InputModes::kButtonsWithWheel;
break;
case 2:
mode =
drivers::NvsStorage::InputModes::kDirectionalWheel;
break;
case 3:
mode =
drivers::NvsStorage::InputModes::kRotatingWheel;
break;
default:
return false;
}
sServices->nvs().PrimaryInput(mode);
sInput->mode(mode);
*/
return true;
}};
lua::Property UiState::sScrollSensitivity{0, [](const lua::LuaValue& val) {
/*
std::optional<int> sensitivity = 0;
std::visit(
[&](auto&& v) {
using T =
std::decay_t<decltype(v)>; if
constexpr (std::is_same_v<T, int>) {
sensitivity = v;
}
},
val);
if (!sensitivity) {
return false;
}
sInput->scroll_sensitivity(*sensitivity);
sServices->nvs().ScrollSensitivity(*sensitivity);
*/
return true;
}};
lua::Property UiState::sLockSwitch{false}; lua::Property UiState::sLockSwitch{false};
lua::Property UiState::sDatabaseUpdating{false}; lua::Property UiState::sDatabaseUpdating{false};
@ -374,13 +328,6 @@ void UiState::react(const system_fsm::SamdUsbStatusChanged& ev) {
drivers::Samd::UsbStatus::kAttachedBusy); drivers::Samd::UsbStatus::kAttachedBusy);
} }
void UiState::react(const internal::ControlSchemeChanged&) {
if (!sInput) {
return;
}
// sInput->mode(sServices->nvs().PrimaryInput());
}
void UiState::react(const database::event::UpdateStarted&) { void UiState::react(const database::event::UpdateStarted&) {
sDatabaseUpdating.Update(true); sDatabaseUpdating.Update(true);
} }
@ -484,7 +431,10 @@ void Splash::react(const system_fsm::BootComplete& ev) {
sDisplayBrightness.Update(brightness); sDisplayBrightness.Update(brightness);
sDisplay->SetBrightness(brightness); sDisplay->SetBrightness(brightness);
sInput = std::make_shared<input::LvglInputDriver>(sServices); sDeviceFactory = std::make_unique<input::DeviceFactory>(sServices);
sInput = std::make_shared<input::LvglInputDriver>(sServices->nvs(),
*sDeviceFactory);
sTask->input(sInput); sTask->input(sInput);
} }
@ -542,13 +492,17 @@ void Lua::entry() {
{"brightness", &sDisplayBrightness}, {"brightness", &sDisplayBrightness},
}); });
registry.AddPropertyModule("controls", registry.AddPropertyModule("controls", {
{ {"scheme", &sInput->mode()},
{"scheme", &sControlsScheme},
{"scroll_sensitivity", &sScrollSensitivity},
{"lock_switch", &sLockSwitch}, {"lock_switch", &sLockSwitch},
}); });
if (sDeviceFactory->touch_wheel()) {
registry.AddPropertyModule(
"controls", {{"scroll_sensitivity",
&sDeviceFactory->touch_wheel()->sensitivity()}});
}
registry.AddPropertyModule( registry.AddPropertyModule(
"backstack", "backstack",
{ {

Loading…
Cancel
Save