From 320fdeb9d8355d3c361d5c6d60de8afc64501af9 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Wed, 30 Aug 2023 16:48:10 +1000 Subject: [PATCH] Use a service locator instead of passing around subsets of drivers between FSMs --- src/app_console/app_console.cpp | 37 ++++--- src/app_console/include/app_console.hpp | 6 +- src/audio/audio_fsm.cpp | 71 +++++-------- src/audio/fatfs_audio_input.cpp | 5 +- src/audio/i2s_audio_output.cpp | 6 +- src/audio/include/audio_fsm.hpp | 14 +-- src/audio/include/fatfs_audio_input.hpp | 4 +- src/audio/include/i2s_audio_output.hpp | 7 +- src/battery/battery.cpp | 6 +- src/battery/include/battery.hpp | 6 +- src/database/database.cpp | 25 ++--- src/database/include/database.hpp | 10 +- src/drivers/display.cpp | 4 +- src/drivers/i2s_dac.cpp | 16 +-- src/drivers/include/display.hpp | 6 +- src/drivers/include/i2s_dac.hpp | 6 +- src/drivers/include/relative_wheel.hpp | 8 +- src/drivers/include/storage.hpp | 6 +- src/drivers/relative_wheel.cpp | 6 +- src/drivers/storage.cpp | 14 +-- src/system_fsm/CMakeLists.txt | 2 +- src/system_fsm/booting.cpp | 57 +++++------ src/system_fsm/idle.cpp | 26 ++--- src/system_fsm/include/service_locator.hpp | 113 +++++++++++++++++++++ src/system_fsm/include/system_events.hpp | 9 +- src/system_fsm/include/system_fsm.hpp | 19 +--- src/system_fsm/running.cpp | 15 +-- src/system_fsm/service_locator.cpp | 21 ++++ src/system_fsm/system_fsm.cpp | 53 ++++------ src/ui/include/lvgl_task.hpp | 27 ++++- src/ui/include/screen_playing.hpp | 4 +- src/ui/include/screen_settings.hpp | 6 +- src/ui/include/ui_fsm.hpp | 25 ++--- src/ui/include/wheel_encoder.hpp | 4 +- src/ui/lvgl_task.cpp | 79 +++++++------- src/ui/screen_playing.cpp | 6 +- src/ui/screen_settings.cpp | 8 +- src/ui/ui_fsm.cpp | 101 +++++++++--------- src/ui/wheel_encoder.cpp | 15 +-- 39 files changed, 471 insertions(+), 382 deletions(-) create mode 100644 src/system_fsm/include/service_locator.hpp create mode 100644 src/system_fsm/service_locator.cpp diff --git a/src/app_console/app_console.cpp b/src/app_console/app_console.cpp index 9aff20dc..2d287e5d 100644 --- a/src/app_console/app_console.cpp +++ b/src/app_console/app_console.cpp @@ -31,18 +31,16 @@ #include "freertos/FreeRTOSConfig_arch.h" #include "freertos/projdefs.h" #include "index.hpp" +#include "service_locator.hpp" #include "track.hpp" namespace console { -std::weak_ptr AppConsole::sDatabase; -audio::TrackQueue* AppConsole::sTrackQueue; -drivers::Bluetooth* AppConsole::sBluetooth; -drivers::Samd* AppConsole::sSamd; +std::shared_ptr AppConsole::sServices; int CmdListDir(int argc, char** argv) { - auto lock = AppConsole::sDatabase.lock(); - if (lock == nullptr) { + auto db = AppConsole::sServices->database().lock(); + if (!db) { std::cout << "storage is not available" << std::endl; return 1; } @@ -117,7 +115,7 @@ int CmdPlayFile(int argc, char** argv) { if (is_id) { database::TrackId id = std::atoi(argv[1]); - AppConsole::sTrackQueue->AddLast(id); + AppConsole::sServices->track_queue().AddLast(id); } else { std::ostringstream path; path << '/' << argv[1]; @@ -147,7 +145,7 @@ int CmdDbInit(int argc, char** argv) { return 1; } - auto db = AppConsole::sDatabase.lock(); + auto db = AppConsole::sServices->database().lock(); if (!db) { std::cout << "no database open" << std::endl; return 1; @@ -174,7 +172,7 @@ int CmdDbTracks(int argc, char** argv) { return 1; } - auto db = AppConsole::sDatabase.lock(); + auto db = AppConsole::sServices->database().lock(); if (!db) { std::cout << "no database open" << std::endl; return 1; @@ -211,7 +209,7 @@ int CmdDbIndex(int argc, char** argv) { vTaskDelay(1); static const std::string usage = "usage: db_index [id] [choices ...]"; - auto db = AppConsole::sDatabase.lock(); + auto db = AppConsole::sServices->database().lock(); if (!db) { std::cout << "no database open" << std::endl; return 1; @@ -252,9 +250,9 @@ int CmdDbIndex(int argc, char** argv) { return -1; } if (res->values().at(choice).track()) { - AppConsole::sTrackQueue->IncludeLast( - std::make_shared(AppConsole::sDatabase, - res, 0, res, choice)); + AppConsole::sServices->track_queue().IncludeLast( + std::make_shared( + AppConsole::sServices->database(), res, 0, res, choice)); } auto cont = res->values().at(choice).Expand(20); if (!cont) { @@ -296,7 +294,7 @@ int CmdDbDump(int argc, char** argv) { return 1; } - auto db = AppConsole::sDatabase.lock(); + auto db = AppConsole::sServices->database().lock(); if (!db) { std::cout << "no database open" << std::endl; return 1; @@ -448,14 +446,15 @@ int CmdBtList(int argc, char** argv) { return 1; } - auto devices = AppConsole::sBluetooth->KnownDevices(); + auto devices = AppConsole::sServices->bluetooth().KnownDevices(); if (argc == 2) { int index = std::atoi(argv[1]); if (index < 0 || index >= devices.size()) { std::cout << "index out of range" << std::endl; return -1; } - AppConsole::sBluetooth->SetPreferredDevice(devices[index].address); + AppConsole::sServices->bluetooth().SetPreferredDevice( + devices[index].address); } else { std::cout << "mac\t\trssi\tname" << std::endl; for (const auto& device : devices) { @@ -493,9 +492,9 @@ int CmdSamd(int argc, char** argv) { if (cmd == "flash") { std::cout << "resetting samd..." << std::endl; vTaskDelay(pdMS_TO_TICKS(5)); - AppConsole::sSamd->ResetToFlashSamd(); + AppConsole::sServices->samd().ResetToFlashSamd(); } else if (cmd == "charge") { - auto res = AppConsole::sSamd->GetChargeStatus(); + auto res = AppConsole::sServices->samd().GetChargeStatus(); if (res) { switch (res.value()) { case drivers::Samd::ChargeStatus::kNoBattery: @@ -523,7 +522,7 @@ int CmdSamd(int argc, char** argv) { } else if (cmd == "off") { std::cout << "bye !!!" << std::endl; vTaskDelay(pdMS_TO_TICKS(5)); - AppConsole::sSamd->PowerDown(); + AppConsole::sServices->samd().PowerDown(); } else { std::cout << usage << std::endl; return 1; diff --git a/src/app_console/include/app_console.hpp b/src/app_console/include/app_console.hpp index 6c23552e..5981cc04 100644 --- a/src/app_console/include/app_console.hpp +++ b/src/app_console/include/app_console.hpp @@ -12,16 +12,14 @@ #include "console.hpp" #include "database.hpp" #include "samd.hpp" +#include "service_locator.hpp" #include "track_queue.hpp" namespace console { class AppConsole : public Console { public: - static std::weak_ptr sDatabase; - static audio::TrackQueue* sTrackQueue; - static drivers::Bluetooth* sBluetooth; - static drivers::Samd* sSamd; + static std::shared_ptr sServices; protected: virtual auto RegisterExtraComponents() -> void; diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp index 6cca8211..9121cb5a 100644 --- a/src/audio/audio_fsm.cpp +++ b/src/audio/audio_fsm.cpp @@ -25,6 +25,7 @@ #include "future_fetcher.hpp" #include "i2s_audio_output.hpp" #include "i2s_dac.hpp" +#include "service_locator.hpp" #include "system_events.hpp" #include "track.hpp" #include "track_queue.hpp" @@ -33,49 +34,15 @@ namespace audio { static const char kTag[] = "audio_fsm"; -drivers::IGpios* AudioState::sIGpios; -std::shared_ptr AudioState::sDac; -std::weak_ptr AudioState::sDatabase; +std::shared_ptr AudioState::sServices; std::shared_ptr AudioState::sFileSource; std::unique_ptr AudioState::sDecoder; std::shared_ptr AudioState::sSampleConverter; std::shared_ptr AudioState::sOutput; -TrackQueue* AudioState::sTrackQueue; std::optional AudioState::sCurrentTrack; -auto AudioState::Init(drivers::IGpios* gpio_expander, - std::weak_ptr database, - std::shared_ptr tag_parser, - drivers::Bluetooth* bluetooth, - TrackQueue* queue) -> bool { - sIGpios = gpio_expander; - sTrackQueue = queue; - - auto dac = drivers::I2SDac::create(gpio_expander); - if (!dac) { - return false; - } - sDac.reset(dac.value()); - sDatabase = database; - - sFileSource.reset(new FatfsAudioInput(tag_parser)); - sOutput.reset(new I2SAudioOutput(sIGpios, sDac)); - // sOutput.reset(new BluetoothAudioOutput(bluetooth)); - - sSampleConverter.reset(new SampleConverter()); - sSampleConverter->SetOutput(sOutput); - - Decoder::Start(sFileSource, sSampleConverter); - - return true; -} - -void AudioState::react(const system_fsm::StorageMounted& ev) { - sDatabase = ev.db; -} - void AudioState::react(const system_fsm::KeyUpChanged& ev) { if (ev.falling && sOutput->AdjustVolumeUp()) { ESP_LOGI(kTag, "volume up!"); @@ -100,7 +67,26 @@ void AudioState::react(const system_fsm::HasPhonesChanged& ev) { namespace states { -void Uninitialised::react(const system_fsm::BootComplete&) { +void Uninitialised::react(const system_fsm::BootComplete& ev) { + sServices = ev.services; + + auto dac = drivers::I2SDac::create(sServices->gpios()); + if (!dac) { + events::System().Dispatch(system_fsm::FatalError{}); + events::Ui().Dispatch(system_fsm::FatalError{}); + return; + } + + sFileSource.reset(new FatfsAudioInput(sServices->tag_parser())); + sOutput.reset(new I2SAudioOutput(sServices->gpios(), + std::unique_ptr{*dac})); + // sOutput.reset(new BluetoothAudioOutput(bluetooth)); + + sSampleConverter.reset(new SampleConverter()); + sSampleConverter->SetOutput(sOutput); + + Decoder::Start(sFileSource, sSampleConverter); + transit(); } @@ -117,19 +103,18 @@ void Standby::react(const internal::InputFileOpened& ev) { } void Standby::react(const QueueUpdate& ev) { - auto current_track = sTrackQueue->GetCurrent(); + auto current_track = sServices->track_queue().GetCurrent(); if (!current_track || (sCurrentTrack && *sCurrentTrack == *current_track)) { return; } sCurrentTrack = current_track; - auto db = sDatabase.lock(); + auto db = sServices->database().lock(); if (!db) { ESP_LOGW(kTag, "database not open; ignoring play request"); return; } - sFileSource->SetPath(db->GetTrackPath(*current_track)); } @@ -158,7 +143,7 @@ void Playback::react(const QueueUpdate& ev) { if (!ev.current_changed) { return; } - auto current_track = sTrackQueue->GetCurrent(); + auto current_track = sServices->track_queue().GetCurrent(); if (!current_track) { sFileSource->SetPath(); sCurrentTrack.reset(); @@ -168,7 +153,7 @@ void Playback::react(const QueueUpdate& ev) { sCurrentTrack = current_track; - auto db = sDatabase.lock(); + auto db = sServices->database().lock(); if (!db) { return; } @@ -191,8 +176,8 @@ void Playback::react(const internal::InputFileClosed& ev) {} void Playback::react(const internal::InputFileFinished& ev) { ESP_LOGI(kTag, "finished playing file"); - sTrackQueue->Next(); - if (!sTrackQueue->GetCurrent()) { + sServices->track_queue().Next(); + if (!sServices->track_queue().GetCurrent()) { transit(); } } diff --git a/src/audio/fatfs_audio_input.cpp b/src/audio/fatfs_audio_input.cpp index 44653624..6b032632 100644 --- a/src/audio/fatfs_audio_input.cpp +++ b/src/audio/fatfs_audio_input.cpp @@ -40,8 +40,7 @@ static const char* kTag = "SRC"; namespace audio { -FatfsAudioInput::FatfsAudioInput( - std::shared_ptr tag_parser) +FatfsAudioInput::FatfsAudioInput(database::ITagParser& tag_parser) : IAudioSource(), tag_parser_(tag_parser), new_stream_mutex_(), @@ -119,7 +118,7 @@ auto FatfsAudioInput::OpenFile(const std::string& path) -> bool { ESP_LOGI(kTag, "opening file %s", path.c_str()); database::TrackTags tags; - if (!tag_parser_->ReadAndParseTags(path, &tags)) { + if (!tag_parser_.ReadAndParseTags(path, &tags)) { ESP_LOGE(kTag, "failed to read tags"); return false; } diff --git a/src/audio/i2s_audio_output.cpp b/src/audio/i2s_audio_output.cpp index 359cd4c6..927f6541 100644 --- a/src/audio/i2s_audio_output.cpp +++ b/src/audio/i2s_audio_output.cpp @@ -41,11 +41,11 @@ static constexpr uint16_t kDefaultVolume = 0x100; static constexpr size_t kDrainBufferSize = 8 * 1024; -I2SAudioOutput::I2SAudioOutput(drivers::IGpios* expander, - std::weak_ptr dac) +I2SAudioOutput::I2SAudioOutput(drivers::IGpios& expander, + std::unique_ptr dac) : IAudioOutput(kDrainBufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), expander_(expander), - dac_(dac.lock()), + dac_(std::move(dac)), current_config_(), left_difference_(0), current_volume_(kDefaultVolume), diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp index cfa65551..1376feae 100644 --- a/src/audio/include/audio_fsm.hpp +++ b/src/audio/include/audio_fsm.hpp @@ -11,6 +11,7 @@ #include #include "audio_sink.hpp" +#include "service_locator.hpp" #include "tinyfsm.hpp" #include "audio_decoder.hpp" @@ -32,12 +33,6 @@ namespace audio { class AudioState : public tinyfsm::Fsm { public: - static auto Init(drivers::IGpios* gpio_expander, - std::weak_ptr, - std::shared_ptr, - drivers::Bluetooth* bluetooth, - TrackQueue* queue) -> bool; - virtual ~AudioState() {} virtual void entry() {} @@ -46,8 +41,6 @@ class AudioState : public tinyfsm::Fsm { /* Fallback event handler. Does nothing. */ void react(const tinyfsm::Event& ev) {} - void react(const system_fsm::StorageMounted&); - void react(const system_fsm::KeyUpChanged&); void react(const system_fsm::KeyDownChanged&); void react(const system_fsm::HasPhonesChanged&); @@ -65,16 +58,13 @@ class AudioState : public tinyfsm::Fsm { virtual void react(const internal::AudioPipelineIdle&) {} protected: - static drivers::IGpios* sIGpios; - static std::shared_ptr sDac; - static std::weak_ptr sDatabase; + static std::shared_ptr sServices; static std::shared_ptr sFileSource; static std::unique_ptr sDecoder; static std::shared_ptr sSampleConverter; static std::shared_ptr sOutput; - static TrackQueue* sTrackQueue; static std::optional sCurrentTrack; }; diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp index da44d2ec..b67d29dc 100644 --- a/src/audio/include/fatfs_audio_input.hpp +++ b/src/audio/include/fatfs_audio_input.hpp @@ -30,7 +30,7 @@ namespace audio { */ class FatfsAudioInput : public IAudioSource { public: - explicit FatfsAudioInput(std::shared_ptr tag_parser); + explicit FatfsAudioInput(database::ITagParser& tag_parser); ~FatfsAudioInput(); /* @@ -53,7 +53,7 @@ class FatfsAudioInput : public IAudioSource { auto ContainerToStreamType(database::Container) -> std::optional; - std::shared_ptr tag_parser_; + database::ITagParser& tag_parser_; std::mutex new_stream_mutex_; std::shared_ptr new_stream_; diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp index 0068916e..fa09deef 100644 --- a/src/audio/include/i2s_audio_output.hpp +++ b/src/audio/include/i2s_audio_output.hpp @@ -19,7 +19,8 @@ namespace audio { class I2SAudioOutput : public IAudioOutput { public: - I2SAudioOutput(drivers::IGpios* expander, std::weak_ptr dac); + I2SAudioOutput(drivers::IGpios& expander, + std::unique_ptr dac); ~I2SAudioOutput(); auto SetInUse(bool) -> void override; @@ -37,8 +38,8 @@ class I2SAudioOutput : public IAudioOutput { I2SAudioOutput& operator=(const I2SAudioOutput&) = delete; private: - drivers::IGpios* expander_; - std::shared_ptr dac_; + drivers::IGpios& expander_; + std::unique_ptr dac_; std::optional current_config_; int_fast8_t left_difference_; diff --git a/src/battery/battery.cpp b/src/battery/battery.cpp index d73f4f29..9ac6ebf1 100644 --- a/src/battery/battery.cpp +++ b/src/battery/battery.cpp @@ -40,8 +40,8 @@ void check_voltage_cb(TimerHandle_t timer) { instance->Update(); } -Battery::Battery(drivers::Samd* samd, drivers::AdcBattery* adc) - : samd_(samd), adc_(adc) { +Battery::Battery(drivers::Samd& samd, std::unique_ptr adc) + : samd_(samd), adc_(std::move(adc)) { timer_ = xTimerCreate("BATTERY", kBatteryCheckPeriod, true, this, check_voltage_cb); xTimerStart(timer_, portMAX_DELAY); @@ -56,7 +56,7 @@ Battery::~Battery() { auto Battery::Update() -> void { std::lock_guard lock{state_mutex_}; - auto charge_state = samd_->GetChargeStatus(); + auto charge_state = samd_.GetChargeStatus(); if (!charge_state || *charge_state == ChargeStatus::kNoBattery) { if (state_) { EmitEvent(); diff --git a/src/battery/include/battery.hpp b/src/battery/include/battery.hpp index dcb9b4ea..63a8a47b 100644 --- a/src/battery/include/battery.hpp +++ b/src/battery/include/battery.hpp @@ -18,7 +18,7 @@ namespace battery { class Battery { public: - Battery(drivers::Samd* samd, drivers::AdcBattery* adc); + Battery(drivers::Samd& samd, std::unique_ptr adc); ~Battery(); auto Update() -> void; @@ -33,8 +33,8 @@ class Battery { private: auto EmitEvent() -> void; - drivers::Samd* samd_; - drivers::AdcBattery* adc_; + drivers::Samd& samd_; + std::unique_ptr adc_; TimerHandle_t timer_; std::mutex state_mutex_; diff --git a/src/database/database.cpp b/src/database/database.cpp index 2a5b3236..76c2dda1 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -46,9 +46,6 @@ static const char kTrackIdKey[] = "next_track_id"; static std::atomic sIsDbOpen(false); -static FileGathererImpl sFileGatherer; -static TagParserImpl sTagParser; - template auto IterateAndParse(leveldb::Iterator* it, std::size_t limit, Parser p) -> void { @@ -62,11 +59,7 @@ auto IterateAndParse(leveldb::Iterator* it, std::size_t limit, Parser p) } } -auto Database::Open() -> cpp::result { - return Open(&sFileGatherer, &sTagParser); -} - -auto Database::Open(IFileGatherer* gatherer, ITagParser* parser) +auto Database::Open(IFileGatherer& gatherer, ITagParser& parser) -> cpp::result { // TODO(jacqueline): Why isn't compare_and_exchange_* available? if (sIsDbOpen.exchange(true)) { @@ -79,7 +72,7 @@ auto Database::Open(IFileGatherer* gatherer, ITagParser* parser) tasks::Worker::Start()); return worker ->Dispatch>( - [=]() -> cpp::result { + [&]() -> cpp::result { leveldb::DB* db; leveldb::Cache* cache = leveldb::NewLRUCache(24 * 1024); leveldb::Options options; @@ -112,8 +105,8 @@ auto Database::Destroy() -> void { Database::Database(leveldb::DB* db, leveldb::Cache* cache, - IFileGatherer* file_gatherer, - ITagParser* tag_parser, + IFileGatherer& file_gatherer, + ITagParser& tag_parser, std::shared_ptr worker) : db_(db), cache_(cache), @@ -178,7 +171,7 @@ auto Database::Update() -> std::future { } TrackTags tags{}; - if (!tag_parser_->ReadAndParseTags(track->filepath(), &tags) || + if (!tag_parser_.ReadAndParseTags(track->filepath(), &tags) || tags.encoding() == Container::kUnsupported) { // We couldn't read the tags for this track. Either they were // malformed, or perhaps the file is missing. Either way, tombstone @@ -215,9 +208,9 @@ auto Database::Update() -> std::future { events::Ui().Dispatch(event::UpdateProgress{ .stage = event::UpdateProgress::Stage::kScanningForNewTracks, }); - file_gatherer_->FindFiles("", [&](const std::string& path) { + file_gatherer_.FindFiles("", [&](const std::string& path) { TrackTags tags; - if (!tag_parser_->ReadAndParseTags(path, &tags) || + if (!tag_parser_.ReadAndParseTags(path, &tags) || tags.encoding() == Container::kUnsupported) { // No parseable tags; skip this fiile. return; @@ -287,7 +280,7 @@ auto Database::GetTrack(TrackId id) -> std::future> { return {}; } TrackTags tags; - if (!tag_parser_->ReadAndParseTags(data->filepath(), &tags)) { + if (!tag_parser_.ReadAndParseTags(data->filepath(), &tags)) { return {}; } return Track(*data, tags); @@ -628,7 +621,7 @@ auto Database::ParseRecord(const leveldb::Slice& key, return {}; } TrackTags tags; - if (!tag_parser_->ReadAndParseTags(data->filepath(), &tags)) { + if (!tag_parser_.ReadAndParseTags(data->filepath(), &tags)) { return {}; } return Track(*data, tags); diff --git a/src/database/include/database.hpp b/src/database/include/database.hpp index dd6bd7cc..559405cb 100644 --- a/src/database/include/database.hpp +++ b/src/database/include/database.hpp @@ -91,7 +91,7 @@ class Database { ALREADY_OPEN, FAILED_TO_OPEN, }; - static auto Open(IFileGatherer* file_gatherer, ITagParser* tag_parser) + static auto Open(IFileGatherer& file_gatherer, ITagParser& tag_parser) -> cpp::result; static auto Open() -> cpp::result; @@ -133,13 +133,13 @@ class Database { std::shared_ptr worker_task_; // Not owned. - IFileGatherer* file_gatherer_; - ITagParser* tag_parser_; + IFileGatherer& file_gatherer_; + ITagParser& tag_parser_; Database(leveldb::DB* db, leveldb::Cache* cache, - IFileGatherer* file_gatherer, - ITagParser* tag_parser, + IFileGatherer& file_gatherer, + ITagParser& tag_parser, std::shared_ptr worker); auto dbMintNewTrackId() -> TrackId; diff --git a/src/drivers/display.cpp b/src/drivers/display.cpp index 2d480aa6..e04de477 100644 --- a/src/drivers/display.cpp +++ b/src/drivers/display.cpp @@ -85,7 +85,7 @@ extern "C" void FlushDataCallback(lv_disp_drv_t* disp_drv, instance->OnLvglFlush(disp_drv, area, color_map); } -auto Display::Create(IGpios* expander, +auto Display::Create(IGpios& expander, const displays::InitialisationData& init_data) -> Display* { ESP_LOGI(kTag, "Init I/O pins"); @@ -182,7 +182,7 @@ auto Display::Create(IGpios* expander, return display.release(); } -Display::Display(IGpios* gpio, spi_device_handle_t handle) +Display::Display(IGpios& gpio, spi_device_handle_t handle) : gpio_(gpio), handle_(handle), worker_task_(tasks::Worker::Start()), diff --git a/src/drivers/i2s_dac.cpp b/src/drivers/i2s_dac.cpp index af2125a6..bedf7ebf 100644 --- a/src/drivers/i2s_dac.cpp +++ b/src/drivers/i2s_dac.cpp @@ -39,7 +39,7 @@ namespace drivers { static const char* kTag = "i2s_dac"; static const i2s_port_t kI2SPort = I2S_NUM_0; -auto I2SDac::create(IGpios* expander) -> std::optional { +auto I2SDac::create(IGpios& expander) -> std::optional { i2s_chan_handle_t i2s_handle; i2s_chan_config_t channel_config = I2S_CHANNEL_DEFAULT_CONFIG(kI2SPort, I2S_ROLE_MASTER); @@ -77,7 +77,7 @@ auto I2SDac::create(IGpios* expander) -> std::optional { return dac.release(); } -I2SDac::I2SDac(IGpios* gpio, i2s_chan_handle_t i2s_handle) +I2SDac::I2SDac(IGpios& gpio, i2s_chan_handle_t i2s_handle) : gpio_(gpio), i2s_handle_(i2s_handle), i2s_active_(false), @@ -87,7 +87,7 @@ I2SDac::I2SDac(IGpios* gpio, i2s_chan_handle_t i2s_handle) clock_config_.clk_src = I2S_CLK_SRC_APLL; // Keep the 5V circuity off until it's needed. - gpio_->WriteSync(IGpios::Pin::kAmplifierEnable, false); + gpio_.WriteSync(IGpios::Pin::kAmplifierEnable, false); // Reset all registers back to their default values. wm8523::WriteRegister(wm8523::Register::kReset, 1); @@ -103,9 +103,9 @@ I2SDac::~I2SDac() { auto I2SDac::Start() -> void { std::lock_guard lock(configure_mutex_); - gpio_->WriteSync(IGpios::Pin::kAmplifierUnmute, false); + gpio_.WriteSync(IGpios::Pin::kAmplifierUnmute, false); // Ramp up the amplifier power supply. - gpio_->WriteSync(IGpios::Pin::kAmplifierEnable, true); + gpio_.WriteSync(IGpios::Pin::kAmplifierEnable, true); // Wait for voltage to stabilise vTaskDelay(pdMS_TO_TICKS(5)); @@ -124,7 +124,7 @@ auto I2SDac::Start() -> void { wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b11); vTaskDelay(pdMS_TO_TICKS(5)); - gpio_->WriteSync(IGpios::Pin::kAmplifierUnmute, true); + gpio_.WriteSync(IGpios::Pin::kAmplifierUnmute, true); } auto I2SDac::Stop() -> void { @@ -134,7 +134,7 @@ auto I2SDac::Stop() -> void { wm8523::WriteRegister(wm8523::Register::kPsCtrl, 0b10); vTaskDelay(pdMS_TO_TICKS(5)); // Silence the output. - gpio_->WriteSync(IGpios::Pin::kAmplifierUnmute, false); + gpio_.WriteSync(IGpios::Pin::kAmplifierUnmute, false); vTaskDelay(pdMS_TO_TICKS(5)); @@ -143,7 +143,7 @@ auto I2SDac::Stop() -> void { i2s_channel_disable(i2s_handle_); i2s_active_ = false; - gpio_->WriteSync(IGpios::Pin::kAmplifierEnable, false); + gpio_.WriteSync(IGpios::Pin::kAmplifierEnable, false); } auto I2SDac::Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate) diff --git a/src/drivers/include/display.hpp b/src/drivers/include/display.hpp index 77165c99..766fc4ea 100644 --- a/src/drivers/include/display.hpp +++ b/src/drivers/include/display.hpp @@ -30,10 +30,10 @@ class Display { * over SPI. This never fails, since unfortunately these display don't give * us back any kind of signal to tell us we're actually using them correctly. */ - static auto Create(IGpios* expander, + static auto Create(IGpios& expander, const displays::InitialisationData& init_data) -> Display*; - Display(IGpios* gpio, spi_device_handle_t handle); + Display(IGpios& gpio, spi_device_handle_t handle); ~Display(); auto SetDisplayOn(bool) -> void; @@ -49,7 +49,7 @@ class Display { Display& operator=(const Display&) = delete; private: - IGpios* gpio_; + IGpios& gpio_; spi_device_handle_t handle_; std::unique_ptr worker_task_; diff --git a/src/drivers/include/i2s_dac.hpp b/src/drivers/include/i2s_dac.hpp index c7faed2f..6bc5b6a4 100644 --- a/src/drivers/include/i2s_dac.hpp +++ b/src/drivers/include/i2s_dac.hpp @@ -33,9 +33,9 @@ namespace drivers { */ class I2SDac { public: - static auto create(IGpios* expander) -> std::optional; + static auto create(IGpios& expander) -> std::optional; - I2SDac(IGpios* gpio, i2s_chan_handle_t i2s_handle); + I2SDac(IGpios& gpio, i2s_chan_handle_t i2s_handle); ~I2SDac(); auto Start() -> void; @@ -69,7 +69,7 @@ class I2SDac { I2SDac& operator=(const I2SDac&) = delete; private: - IGpios* gpio_; + IGpios& gpio_; i2s_chan_handle_t i2s_handle_; bool i2s_active_; StreamBufferHandle_t buffer_; diff --git a/src/drivers/include/relative_wheel.hpp b/src/drivers/include/relative_wheel.hpp index 5e801aba..b5532a4c 100644 --- a/src/drivers/include/relative_wheel.hpp +++ b/src/drivers/include/relative_wheel.hpp @@ -20,11 +20,7 @@ namespace drivers { class RelativeWheel { public: - static auto Create(TouchWheel* touch) -> RelativeWheel* { - return new RelativeWheel(touch); - } - - explicit RelativeWheel(TouchWheel* touch); + explicit RelativeWheel(TouchWheel& touch); auto Update() -> void; auto SetEnabled(bool) -> void; @@ -37,7 +33,7 @@ class RelativeWheel { RelativeWheel& operator=(const RelativeWheel&) = delete; private: - TouchWheel* touch_; + TouchWheel& touch_; bool is_enabled_; diff --git a/src/drivers/include/storage.hpp b/src/drivers/include/storage.hpp index 65be75f1..0b0cb494 100644 --- a/src/drivers/include/storage.hpp +++ b/src/drivers/include/storage.hpp @@ -31,9 +31,9 @@ class SdStorage { FAILED_TO_MOUNT, }; - static auto Create(IGpios* gpio) -> cpp::result; + static auto Create(IGpios& gpio) -> cpp::result; - SdStorage(IGpios* gpio, + SdStorage(IGpios& gpio, sdspi_dev_handle_t handle_, std::unique_ptr host_, std::unique_ptr card_, @@ -50,7 +50,7 @@ class SdStorage { SdStorage& operator=(const SdStorage&) = delete; private: - IGpios* gpio_; + IGpios& gpio_; // SPI and SD driver info sdspi_dev_handle_t handle_; diff --git a/src/drivers/relative_wheel.cpp b/src/drivers/relative_wheel.cpp index 75b62ae7..c014ab5e 100644 --- a/src/drivers/relative_wheel.cpp +++ b/src/drivers/relative_wheel.cpp @@ -13,7 +13,7 @@ namespace drivers { -RelativeWheel::RelativeWheel(TouchWheel* touch) +RelativeWheel::RelativeWheel(TouchWheel& touch) : touch_(touch), is_enabled_(true), is_clicking_(false), @@ -23,8 +23,8 @@ RelativeWheel::RelativeWheel(TouchWheel* touch) last_angle_(0) {} auto RelativeWheel::Update() -> void { - touch_->Update(); - TouchWheelData d = touch_->GetTouchWheelData(); + touch_.Update(); + TouchWheelData d = touch_.GetTouchWheelData(); is_clicking_ = d.is_button_touched; diff --git a/src/drivers/storage.cpp b/src/drivers/storage.cpp index f253a79a..6acb6870 100644 --- a/src/drivers/storage.cpp +++ b/src/drivers/storage.cpp @@ -32,10 +32,10 @@ namespace drivers { const char* kStoragePath = "/sdcard"; -auto SdStorage::Create(IGpios* gpio) -> cpp::result { - gpio->WriteSync(IGpios::Pin::kSdPowerEnable, 1); - gpio->WriteSync(IGpios::Pin::kSdMuxSwitch, IGpios::SD_MUX_ESP); - gpio->WriteSync(IGpios::Pin::kSdMuxDisable, 0); +auto SdStorage::Create(IGpios& gpio) -> cpp::result { + gpio.WriteSync(IGpios::Pin::kSdPowerEnable, 1); + gpio.WriteSync(IGpios::Pin::kSdMuxSwitch, IGpios::SD_MUX_ESP); + gpio.WriteSync(IGpios::Pin::kSdMuxDisable, 0); sdspi_dev_handle_t handle; FATFS* fs = nullptr; @@ -95,7 +95,7 @@ auto SdStorage::Create(IGpios* gpio) -> cpp::result { return new SdStorage(gpio, handle, std::move(host), std::move(card), fs); } -SdStorage::SdStorage(IGpios* gpio, +SdStorage::SdStorage(IGpios& gpio, sdspi_dev_handle_t handle, std::unique_ptr host, std::unique_ptr card, @@ -117,8 +117,8 @@ SdStorage::~SdStorage() { sdspi_host_remove_device(this->handle_); sdspi_host_deinit(); - gpio_->WriteSync(IGpios::Pin::kSdPowerEnable, 1); - gpio_->WriteSync(IGpios::Pin::kSdMuxDisable, 1); + gpio_.WriteSync(IGpios::Pin::kSdPowerEnable, 1); + gpio_.WriteSync(IGpios::Pin::kSdMuxDisable, 1); } auto SdStorage::GetFs() -> FATFS* { diff --git a/src/system_fsm/CMakeLists.txt b/src/system_fsm/CMakeLists.txt index fced4093..449e14cc 100644 --- a/src/system_fsm/CMakeLists.txt +++ b/src/system_fsm/CMakeLists.txt @@ -3,7 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-only idf_component_register( - SRCS "system_fsm.cpp" "running.cpp" "booting.cpp" "idle.cpp" + SRCS "system_fsm.cpp" "running.cpp" "booting.cpp" "idle.cpp" "service_locator.cpp" INCLUDE_DIRS "include" REQUIRES "tinyfsm" "drivers" "database" "ui" "result" "events" "audio" "app_console" "battery") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp index a988c622..006ed395 100644 --- a/src/system_fsm/booting.cpp +++ b/src/system_fsm/booting.cpp @@ -6,8 +6,10 @@ #include +#include "adc.hpp" #include "assert.h" #include "audio_fsm.hpp" +#include "battery.hpp" #include "bluetooth.hpp" #include "core/lv_obj.h" #include "display_init.hpp" @@ -23,6 +25,7 @@ #include "nvs.hpp" #include "relative_wheel.hpp" #include "samd.hpp" +#include "service_locator.hpp" #include "spi.hpp" #include "system_events.hpp" #include "system_fsm.hpp" @@ -40,65 +43,55 @@ static const char kTag[] = "BOOT"; auto Booting::entry() -> void { ESP_LOGI(kTag, "beginning tangara boot"); - ESP_LOGI(kTag, "installing early drivers"); + sServices.reset(new ServiceLocator()); + ESP_LOGI(kTag, "installing early drivers"); // I2C and SPI are both always needed. We can't even power down or show an // error without these. ESP_ERROR_CHECK(drivers::init_spi()); - sGpios.reset(drivers::Gpios::Create()); - - sSamd.reset(drivers::Samd::Create()); - sAdc.reset(drivers::AdcBattery::Create()); - sNvs.reset(drivers::NvsStorage::OpenSync()); - assert(sSamd.get() && sAdc.get() && sNvs.get()); + sServices->gpios(std::unique_ptr(drivers::Gpios::Create())); - sBattery.reset(new battery::Battery(sSamd.get(), sAdc.get())); - - // Start bringing up LVGL now, since we have all of its prerequisites. - sTrackQueue.reset(new audio::TrackQueue()); ESP_LOGI(kTag, "starting ui"); - if (!ui::UiState::Init(sGpios.get(), sNvs, sTrackQueue.get(), sBattery)) { + if (!ui::UiState::InitBootSplash(sServices->gpios())) { events::System().Dispatch(FatalError{}); return; } - // Install everything else that is certain to be needed. ESP_LOGI(kTag, "installing remaining drivers"); - sTagParser.reset(new database::TagParserImpl()); + sServices->samd(std::unique_ptr(drivers::Samd::Create())); + sServices->nvs( + std::unique_ptr(drivers::NvsStorage::OpenSync())); + sServices->touchwheel( + std::unique_ptr{drivers::TouchWheel::Create()}); + + auto adc = drivers::AdcBattery::Create(); + sServices->battery(std::make_unique( + sServices->samd(), std::unique_ptr(adc))); + + sServices->track_queue(std::make_unique()); + sServices->tag_parser(std::make_unique()); // ESP_LOGI(kTag, "starting bluetooth"); // sBluetooth.reset(new drivers::Bluetooth(sNvs.get())); // sBluetooth->Enable(); - // At this point we've done all of the essential boot tasks. Start remaining - // state machines and inform them that the system is ready. - - ESP_LOGI(kTag, "starting audio"); - if (!audio::AudioState::Init(sGpios.get(), sDatabase, sTagParser, - sBluetooth.get(), sTrackQueue.get())) { - events::System().Dispatch(FatalError{}); - events::Ui().Dispatch(FatalError{}); - return; - } - - events::System().Dispatch(BootComplete{}); - events::Audio().Dispatch(BootComplete{}); - events::Ui().Dispatch(BootComplete{}); + BootComplete ev{.services = sServices}; + events::Audio().Dispatch(ev); + events::Ui().Dispatch(ev); + events::System().Dispatch(ev); } auto Booting::exit() -> void { // TODO(jacqueline): Gate this on something. Debug flag? Flashing mode? sAppConsole = new console::AppConsole(); - sAppConsole->sTrackQueue = sTrackQueue.get(); - sAppConsole->sBluetooth = sBluetooth.get(); - sAppConsole->sSamd = sSamd.get(); + sAppConsole->sServices = sServices; sAppConsole->Launch(); } auto Booting::react(const BootComplete& ev) -> void { ESP_LOGI(kTag, "bootup completely successfully"); - if (sGpios->Get(drivers::Gpios::Pin::kKeyLock)) { + if (sServices->gpios().Get(drivers::Gpios::Pin::kKeyLock)) { transit(); } else { transit(); diff --git a/src/system_fsm/idle.cpp b/src/system_fsm/idle.cpp index 7cc1fa39..bd327134 100644 --- a/src/system_fsm/idle.cpp +++ b/src/system_fsm/idle.cpp @@ -64,30 +64,32 @@ void Idle::react(const internal::IdleTimeout& ev) { // FIXME: It would be neater to just free a bunch of our pointers, deinit the // other state machines, etc. - if (sTouch) { - sTouch->PowerDown(); + auto touchwheel = sServices->touchwheel(); + if (touchwheel) { + touchwheel.value()->PowerDown(); } + auto& gpios = sServices->gpios(); // Pull down to turn things off - sGpios->WriteBuffered(drivers::IGpios::Pin::kAmplifierEnable, false); - sGpios->WriteBuffered(drivers::IGpios::Pin::kSdPowerEnable, false); - sGpios->WriteBuffered(drivers::IGpios::Pin::kDisplayEnable, false); + gpios.WriteBuffered(drivers::IGpios::Pin::kAmplifierEnable, false); + gpios.WriteBuffered(drivers::IGpios::Pin::kSdPowerEnable, false); + gpios.WriteBuffered(drivers::IGpios::Pin::kDisplayEnable, false); // Leave up to match the external pullups - sGpios->WriteBuffered(drivers::IGpios::Pin::kSdMuxSwitch, true); - sGpios->WriteBuffered(drivers::IGpios::Pin::kSdMuxDisable, true); + gpios.WriteBuffered(drivers::IGpios::Pin::kSdMuxSwitch, true); + gpios.WriteBuffered(drivers::IGpios::Pin::kSdMuxDisable, true); // Pull down to prevent sourcing current uselessly from input pins. - sGpios->WriteBuffered(drivers::IGpios::Pin::kSdCardDetect, false); - sGpios->WriteBuffered(drivers::IGpios::Pin::kKeyUp, false); - sGpios->WriteBuffered(drivers::IGpios::Pin::kKeyDown, false); + gpios.WriteBuffered(drivers::IGpios::Pin::kSdCardDetect, false); + gpios.WriteBuffered(drivers::IGpios::Pin::kKeyUp, false); + gpios.WriteBuffered(drivers::IGpios::Pin::kKeyDown, false); - sGpios->Flush(); + gpios.Flush(); // Retry shutting down in case of a transient failure with the SAMD. e.g. i2c // timeouts. This guards against a buggy SAMD firmware preventing idle. for (;;) { - sSamd->PowerDown(); + sServices->samd().PowerDown(); vTaskDelay(pdMS_TO_TICKS(1000)); } } diff --git a/src/system_fsm/include/service_locator.hpp b/src/system_fsm/include/service_locator.hpp new file mode 100644 index 00000000..00285ed5 --- /dev/null +++ b/src/system_fsm/include/service_locator.hpp @@ -0,0 +1,113 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include + +#include "battery.hpp" +#include "bluetooth.hpp" +#include "database.hpp" +#include "gpios.hpp" +#include "nvs.hpp" +#include "samd.hpp" +#include "tag_parser.hpp" +#include "touchwheel.hpp" +#include "track_queue.hpp" + +namespace system_fsm { + +class ServiceLocator { + public: + static auto instance() -> ServiceLocator&; + + auto gpios() -> drivers::Gpios& { + assert(gpios_ != nullptr); + return *gpios_; + } + + auto gpios(std::unique_ptr i) { gpios_ = std::move(i); } + + auto samd() -> drivers::Samd& { + assert(samd_ != nullptr); + return *samd_; + } + + auto samd(std::unique_ptr i) { samd_ = std::move(i); } + + auto nvs() -> drivers::NvsStorage& { + assert(nvs_ != nullptr); + return *nvs_; + } + + auto nvs(std::unique_ptr i) { nvs_ = std::move(i); } + + auto bluetooth() -> drivers::Bluetooth& { + assert(bluetooth_ != nullptr); + return *bluetooth_; + } + + auto bluetooth(std::unique_ptr i) { + bluetooth_ = std::move(i); + } + + auto battery() -> battery::Battery& { + assert(battery_ != nullptr); + return *battery_; + } + + auto battery(std::unique_ptr i) { battery_ = std::move(i); } + + auto touchwheel() -> std::optional { + if (!touchwheel_) { + return {}; + } + return touchwheel_.get(); + } + + auto touchwheel(std::unique_ptr i) { + touchwheel_ = std::move(i); + } + + auto database() -> std::weak_ptr { return database_; } + + auto database(std::unique_ptr i) { + database_ = std::move(i); + } + + auto tag_parser() -> database::ITagParser& { + assert(tag_parser_ != nullptr); + return *tag_parser_; + } + + auto tag_parser(std::unique_ptr i) { + tag_parser_ = std::move(i); + } + + auto track_queue() -> audio::TrackQueue& { + assert(queue_ != nullptr); + return *queue_; + } + + auto track_queue(std::unique_ptr i) { + queue_ = std::move(i); + } + + private: + std::unique_ptr gpios_; + std::unique_ptr samd_; + std::unique_ptr nvs_; + std::unique_ptr touchwheel_; + std::unique_ptr bluetooth_; + + std::unique_ptr queue_; + std::unique_ptr battery_; + + std::shared_ptr database_; + std::unique_ptr tag_parser_; +}; + +} // namespace system_fsm diff --git a/src/system_fsm/include/system_events.hpp b/src/system_fsm/include/system_events.hpp index 64cbd393..e22fe2ae 100644 --- a/src/system_fsm/include/system_events.hpp +++ b/src/system_fsm/include/system_events.hpp @@ -9,6 +9,7 @@ #include #include "database.hpp" +#include "service_locator.hpp" #include "tinyfsm.hpp" namespace system_fsm { @@ -19,7 +20,9 @@ struct DisplayReady : tinyfsm::Event {}; * Sent by SysState when the system has finished with its boot and self-test, * and is now ready to run normally. */ -struct BootComplete : tinyfsm::Event {}; +struct BootComplete : tinyfsm::Event { + std::shared_ptr services; +}; /* * May be sent by any component to indicate that the system has experienced an @@ -33,9 +36,7 @@ struct OnIdle : tinyfsm::Event {}; /* * Sent by SysState when the system storage has been successfully mounted. */ -struct StorageMounted : tinyfsm::Event { - std::weak_ptr db; -}; +struct StorageMounted : tinyfsm::Event {}; struct StorageError : tinyfsm::Event {}; diff --git a/src/system_fsm/include/system_fsm.hpp b/src/system_fsm/include/system_fsm.hpp index 371e5527..28448e5a 100644 --- a/src/system_fsm/include/system_fsm.hpp +++ b/src/system_fsm/include/system_fsm.hpp @@ -18,6 +18,7 @@ #include "nvs.hpp" #include "relative_wheel.hpp" #include "samd.hpp" +#include "service_locator.hpp" #include "storage.hpp" #include "tag_parser.hpp" #include "tinyfsm.hpp" @@ -60,22 +61,8 @@ class SystemState : public tinyfsm::Fsm { protected: auto IdleCondition() -> bool; - static std::shared_ptr sGpios; - static std::shared_ptr sSamd; - static std::shared_ptr sNvs; - - static std::shared_ptr sTouch; - static std::shared_ptr sRelativeTouch; - static std::shared_ptr sAdc; - static std::shared_ptr sBattery; - static std::shared_ptr sStorage; - static std::shared_ptr sDisplay; - static std::shared_ptr sBluetooth; - - static std::shared_ptr sDatabase; - static std::shared_ptr sTagParser; - - static std::shared_ptr sTrackQueue; + static std::shared_ptr sServices; + static std::unique_ptr sStorage; static console::AppConsole* sAppConsole; }; diff --git a/src/system_fsm/running.cpp b/src/system_fsm/running.cpp index 3fc5493f..e42429e7 100644 --- a/src/system_fsm/running.cpp +++ b/src/system_fsm/running.cpp @@ -6,6 +6,7 @@ #include "app_console.hpp" #include "audio_events.hpp" +#include "database.hpp" #include "file_gatherer.hpp" #include "freertos/projdefs.h" #include "result.hpp" @@ -31,7 +32,7 @@ static database::IFileGatherer* sFileGatherer; void Running::entry() { ESP_LOGI(kTag, "mounting sd card"); vTaskDelay(pdMS_TO_TICKS(250)); - auto storage_res = drivers::SdStorage::Create(sGpios.get()); + auto storage_res = drivers::SdStorage::Create(sServices->gpios()); if (storage_res.has_error()) { ESP_LOGW(kTag, "failed to mount!"); @@ -41,11 +42,11 @@ void Running::entry() { return; } sStorage.reset(storage_res.value()); - vTaskDelay(pdMS_TO_TICKS(250)); ESP_LOGI(kTag, "opening database"); sFileGatherer = new database::FileGathererImpl(); - auto database_res = database::Database::Open(sFileGatherer, sTagParser.get()); + auto database_res = + database::Database::Open(*sFileGatherer, sServices->tag_parser()); if (database_res.has_error()) { ESP_LOGW(kTag, "failed to open!"); events::System().Dispatch(StorageError{}); @@ -53,18 +54,18 @@ void Running::entry() { events::Ui().Dispatch(StorageError{}); return; } - sDatabase.reset(database_res.value()); - console::AppConsole::sDatabase = sDatabase; + sServices->database( + std::unique_ptr{database_res.value()}); ESP_LOGI(kTag, "storage loaded okay"); - StorageMounted ev{.db = sDatabase}; + StorageMounted ev{}; events::System().Dispatch(ev); events::Audio().Dispatch(ev); events::Ui().Dispatch(ev); } void Running::exit() { - sDatabase.reset(); + sServices->database({}); sStorage.reset(); } diff --git a/src/system_fsm/service_locator.cpp b/src/system_fsm/service_locator.cpp new file mode 100644 index 00000000..1d4d8a65 --- /dev/null +++ b/src/system_fsm/service_locator.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "service_locator.hpp" + +#include + +#include "nvs.hpp" +#include "touchwheel.hpp" + +namespace system_fsm { + +auto ServiceLocator::instance() -> ServiceLocator& { + static ServiceLocator sInstance{}; + return sInstance; +} + +} // namespace system_fsm diff --git a/src/system_fsm/system_fsm.cpp b/src/system_fsm/system_fsm.cpp index d21e8bcb..e048cec7 100644 --- a/src/system_fsm/system_fsm.cpp +++ b/src/system_fsm/system_fsm.cpp @@ -9,6 +9,7 @@ #include "event_queue.hpp" #include "gpios.hpp" #include "relative_wheel.hpp" +#include "service_locator.hpp" #include "system_events.hpp" #include "tag_parser.hpp" #include "track_queue.hpp" @@ -17,22 +18,8 @@ static const char kTag[] = "system"; namespace system_fsm { -std::shared_ptr SystemState::sGpios; -std::shared_ptr SystemState::sSamd; -std::shared_ptr SystemState::sNvs; - -std::shared_ptr SystemState::sTouch; -std::shared_ptr SystemState::sRelativeTouch; -std::shared_ptr SystemState::sAdc; -std::shared_ptr SystemState::sBattery; -std::shared_ptr SystemState::sStorage; -std::shared_ptr SystemState::sDisplay; -std::shared_ptr SystemState::sBluetooth; - -std::shared_ptr SystemState::sDatabase; -std::shared_ptr SystemState::sTagParser; - -std::shared_ptr SystemState::sTrackQueue; +std::shared_ptr SystemState::sServices; +std::unique_ptr SystemState::sStorage; console::AppConsole* SystemState::sAppConsole; @@ -43,17 +30,18 @@ void SystemState::react(const FatalError& err) { } void SystemState::react(const internal::GpioInterrupt&) { - bool prev_key_up = sGpios->Get(drivers::Gpios::Pin::kKeyUp); - bool prev_key_down = sGpios->Get(drivers::Gpios::Pin::kKeyDown); - bool prev_key_lock = sGpios->Get(drivers::Gpios::Pin::kKeyLock); - bool prev_has_headphones = !sGpios->Get(drivers::Gpios::Pin::kPhoneDetect); + auto& gpios = sServices->gpios(); + bool prev_key_up = gpios.Get(drivers::Gpios::Pin::kKeyUp); + bool prev_key_down = gpios.Get(drivers::Gpios::Pin::kKeyDown); + bool prev_key_lock = gpios.Get(drivers::Gpios::Pin::kKeyLock); + bool prev_has_headphones = !gpios.Get(drivers::Gpios::Pin::kPhoneDetect); - sGpios->Read(); + gpios.Read(); - bool key_up = sGpios->Get(drivers::Gpios::Pin::kKeyUp); - bool key_down = sGpios->Get(drivers::Gpios::Pin::kKeyDown); - bool key_lock = sGpios->Get(drivers::Gpios::Pin::kKeyLock); - bool has_headphones = !sGpios->Get(drivers::Gpios::Pin::kPhoneDetect); + bool key_up = gpios.Get(drivers::Gpios::Pin::kKeyUp); + bool key_down = gpios.Get(drivers::Gpios::Pin::kKeyDown); + bool key_lock = gpios.Get(drivers::Gpios::Pin::kKeyLock); + bool has_headphones = !gpios.Get(drivers::Gpios::Pin::kPhoneDetect); if (key_up != prev_key_up) { KeyUpChanged ev{.falling = prev_key_up}; @@ -77,14 +65,15 @@ void SystemState::react(const internal::GpioInterrupt&) { } void SystemState::react(const internal::SamdInterrupt&) { - auto prev_charge_status = sSamd->GetChargeStatus(); - auto prev_usb_status = sSamd->GetUsbStatus(); + auto& samd = sServices->samd(); + auto prev_charge_status = samd.GetChargeStatus(); + auto prev_usb_status = samd.GetUsbStatus(); - sSamd->UpdateChargeStatus(); - sSamd->UpdateUsbStatus(); + samd.UpdateChargeStatus(); + samd.UpdateUsbStatus(); - auto charge_status = sSamd->GetChargeStatus(); - auto usb_status = sSamd->GetUsbStatus(); + auto charge_status = samd.GetChargeStatus(); + auto usb_status = samd.GetUsbStatus(); if (charge_status != prev_charge_status) { ChargingStatusChanged ev{}; @@ -97,7 +86,7 @@ void SystemState::react(const internal::SamdInterrupt&) { } auto SystemState::IdleCondition() -> bool { - return !sGpios->Get(drivers::IGpios::Pin::kKeyLock) && + return !sServices->gpios().Get(drivers::IGpios::Pin::kKeyLock) && audio::AudioState::is_in_state(); } diff --git a/src/ui/include/lvgl_task.hpp b/src/ui/include/lvgl_task.hpp index 7e60c4b4..6b7e446e 100644 --- a/src/ui/include/lvgl_task.hpp +++ b/src/ui/include/lvgl_task.hpp @@ -12,15 +12,38 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/timers.h" #include "display.hpp" #include "relative_wheel.hpp" +#include "screen.hpp" #include "themes.hpp" #include "touchwheel.hpp" +#include "wheel_encoder.hpp" namespace ui { -auto StartLvgl(std::weak_ptr touch_wheel, - std::weak_ptr display) -> void; +class UiTask { + public: + static auto Start() -> UiTask*; + + ~UiTask(); + + // FIXME: Once we have more input devices, this function should accept a more + // generic interface. + auto SetInputDevice(std::shared_ptr dev) -> void; + + private: + UiTask(); + + auto Main() -> void; + + std::shared_ptr input_device_; + std::shared_ptr current_screen_; + + std::atomic quit_; + SemaphoreHandle_t frame_semaphore_; + TimerHandle_t frame_timer_; +}; } // namespace ui diff --git a/src/ui/include/screen_playing.hpp b/src/ui/include/screen_playing.hpp index c684ddff..f2998c88 100644 --- a/src/ui/include/screen_playing.hpp +++ b/src/ui/include/screen_playing.hpp @@ -29,7 +29,7 @@ namespace screens { class Playing : public Screen { public: explicit Playing(std::weak_ptr db, - audio::TrackQueue* queue); + audio::TrackQueue& queue); ~Playing(); auto Tick() -> void override; @@ -51,7 +51,7 @@ class Playing : public Screen { auto ApplyNextUp(const std::vector& tracks) -> void; std::weak_ptr db_; - audio::TrackQueue* queue_; + audio::TrackQueue& queue_; std::optional track_; std::vector next_tracks_; diff --git a/src/ui/include/screen_settings.hpp b/src/ui/include/screen_settings.hpp index 61375fa9..0ec96d26 100644 --- a/src/ui/include/screen_settings.hpp +++ b/src/ui/include/screen_settings.hpp @@ -37,14 +37,14 @@ class Headphones : public MenuScreen { class Appearance : public MenuScreen { public: - Appearance(drivers::NvsStorage* nvs, drivers::Display* display); + Appearance(drivers::NvsStorage& nvs, drivers::Display& display); auto ChangeBrightness(uint_fast8_t) -> void; auto CommitBrightness() -> void; private: - drivers::NvsStorage* nvs_; - drivers::Display* display_; + drivers::NvsStorage& nvs_; + drivers::Display& display_; lv_obj_t* current_brightness_label_; uint_fast8_t current_brightness_; diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 1fa6bf26..12fe5c69 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -11,9 +11,12 @@ #include "audio_events.hpp" #include "battery.hpp" +#include "gpios.hpp" +#include "lvgl_task.hpp" #include "nvs.hpp" #include "relative_wheel.hpp" #include "screen_playing.hpp" +#include "service_locator.hpp" #include "tinyfsm.hpp" #include "display.hpp" @@ -24,15 +27,13 @@ #include "touchwheel.hpp" #include "track_queue.hpp" #include "ui_events.hpp" +#include "wheel_encoder.hpp" namespace ui { class UiState : public tinyfsm::Fsm { public: - static auto Init(drivers::IGpios*, - std::shared_ptr, - audio::TrackQueue*, - std::shared_ptr) -> bool; + static auto InitBootSplash(drivers::IGpios&) -> bool; virtual ~UiState() {} @@ -46,7 +47,7 @@ class UiState : public tinyfsm::Fsm { /* Fallback event handler. Does nothing. */ void react(const tinyfsm::Event& ev) {} - void react(const system_fsm::BatteryStateChanged&); + virtual void react(const system_fsm::BatteryStateChanged&); virtual void react(const audio::PlaybackStarted&) {} virtual void react(const audio::PlaybackUpdate&) {} @@ -76,15 +77,10 @@ class UiState : public tinyfsm::Fsm { void PopScreen(); void UpdateTopBar(); - static drivers::IGpios* sIGpios; - static audio::TrackQueue* sQueue; - - static std::shared_ptr sTouchWheel; - static std::shared_ptr sRelativeWheel; - static std::shared_ptr sDisplay; - static std::shared_ptr sBattery; - static std::shared_ptr sNvs; - static std::weak_ptr sDb; + static std::unique_ptr sTask; + static std::shared_ptr sServices; + static std::unique_ptr sDisplay; + static std::shared_ptr sEncoder; static std::stack> sScreens; static std::shared_ptr sCurrentScreen; @@ -97,6 +93,7 @@ class Splash : public UiState { public: void exit() override; void react(const system_fsm::BootComplete&) override; + void react(const system_fsm::BatteryStateChanged&) override{}; using UiState::react; }; diff --git a/src/ui/include/wheel_encoder.hpp b/src/ui/include/wheel_encoder.hpp index c49e5929..fcac5edd 100644 --- a/src/ui/include/wheel_encoder.hpp +++ b/src/ui/include/wheel_encoder.hpp @@ -17,7 +17,7 @@ namespace ui { class TouchWheelEncoder { public: - explicit TouchWheelEncoder(std::weak_ptr wheel); + explicit TouchWheelEncoder(std::unique_ptr wheel); auto Read(lv_indev_data_t* data) -> void; auto registration() -> lv_indev_t* { return registration_; } @@ -27,7 +27,7 @@ class TouchWheelEncoder { lv_indev_t* registration_; lv_key_t last_key_; - std::weak_ptr wheel_; + std::unique_ptr wheel_; }; } // namespace ui diff --git a/src/ui/lvgl_task.cpp b/src/ui/lvgl_task.cpp index 9952d0bb..a0f14f7d 100644 --- a/src/ui/lvgl_task.cpp +++ b/src/ui/lvgl_task.cpp @@ -48,67 +48,74 @@ namespace ui { -static const char* kTag = "lv_task"; +static const char* kTag = "ui_task"; static const TickType_t kMaxFrameRate = pdMS_TO_TICKS(100); -static int sTimerId; -static SemaphoreHandle_t sFrameSemaphore; - -auto next_frame(TimerHandle_t) { - xSemaphoreGive(sFrameSemaphore); +static auto next_frame(TimerHandle_t t) { + SemaphoreHandle_t sem = + reinterpret_cast(pvTimerGetTimerID(t)); + xSemaphoreGive(sem); } -void LvglMain(std::weak_ptr weak_touch_wheel, - std::weak_ptr weak_display) { - ESP_LOGI(kTag, "init lvgl"); - lv_init(); - - sFrameSemaphore = xSemaphoreCreateBinary(); - auto timer = - xTimerCreate("lvgl_frame", kMaxFrameRate, pdTRUE, &sTimerId, next_frame); - xTimerStart(timer, portMAX_DELAY); - - lv_theme_t* base_theme = lv_theme_basic_init(NULL); - lv_disp_set_theme(NULL, base_theme); - themes::Theme::instance()->Apply(); +UiTask::UiTask() + : quit_(false), + frame_semaphore_(xSemaphoreCreateBinary()), + frame_timer_(xTimerCreate("ui_frame", + kMaxFrameRate, + pdTRUE, + frame_semaphore_, + next_frame)) { + xTimerStart(frame_timer_, portMAX_DELAY); +} - TouchWheelEncoder encoder(weak_touch_wheel); +UiTask::~UiTask() { + assert(false); +} - std::shared_ptr current_screen; +auto UiTask::Main() -> void { + ESP_LOGI(kTag, "start ui task"); lv_group_t* current_group = nullptr; auto* events = events::queues::Ui(); - while (1) { + while (true) { while (events->Service(0)) { } std::shared_ptr screen = UiState::current_screen(); - if (screen != current_screen && screen != nullptr) { - // TODO(jacqueline): animate this sometimes + if (screen != current_screen_ && screen != nullptr) { lv_scr_load(screen->root()); - lv_indev_set_group(encoder.registration(), screen->group()); - current_screen = screen; + if (input_device_) { + lv_indev_set_group(input_device_->registration(), screen->group()); + } + current_screen_ = screen; } - if (current_screen->group() != current_group) { - current_group = current_screen->group(); - lv_indev_set_group(encoder.registration(), current_group); + if (input_device_ && current_screen_->group() != current_group) { + current_group = current_screen_->group(); + lv_indev_set_group(input_device_->registration(), current_group); } - if (current_screen) { - current_screen->Tick(); + if (current_screen_) { + current_screen_->Tick(); } lv_task_handler(); // Wait for the signal to loop again. - xSemaphoreTake(sFrameSemaphore, portMAX_DELAY); + xSemaphoreTake(frame_semaphore_, portMAX_DELAY); + } +} + +auto UiTask::SetInputDevice(std::shared_ptr dev) -> void { + input_device_ = std::move(dev); + if (current_screen_ && input_device_) { + lv_indev_set_group(input_device_->registration(), current_screen_->group()); } } -auto StartLvgl(std::weak_ptr touch_wheel, - std::weak_ptr display) -> void { - tasks::StartPersistent( - 0, [=]() { LvglMain(touch_wheel, display); }); +auto UiTask::Start() -> UiTask* { + UiTask* ret = new UiTask(); + tasks::StartPersistent(0, [=]() { ret->Main(); }); + return ret; } } // namespace ui diff --git a/src/ui/screen_playing.cpp b/src/ui/screen_playing.cpp index 7538d093..2eb4e09e 100644 --- a/src/ui/screen_playing.cpp +++ b/src/ui/screen_playing.cpp @@ -104,7 +104,7 @@ auto Playing::next_up_label(lv_obj_t* parent, const std::string& text) return button; } -Playing::Playing(std::weak_ptr db, audio::TrackQueue* queue) +Playing::Playing(std::weak_ptr db, audio::TrackQueue& queue) : db_(db), queue_(queue), track_(), @@ -204,7 +204,7 @@ Playing::Playing(std::weak_ptr db, audio::TrackQueue* queue) Playing::~Playing() {} auto Playing::OnTrackUpdate() -> void { - auto current = queue_->GetCurrent(); + auto current = queue_.GetCurrent(); if (!current) { return; } @@ -230,7 +230,7 @@ auto Playing::OnPlaybackUpdate(uint32_t pos_seconds, uint32_t new_duration) auto Playing::OnQueueUpdate() -> void { OnTrackUpdate(); - auto current = queue_->GetUpcoming(kMaxUpcoming); + auto current = queue_.GetUpcoming(kMaxUpcoming); auto db = db_.lock(); if (!db) { return; diff --git a/src/ui/screen_settings.cpp b/src/ui/screen_settings.cpp index 6bafb9a8..01f40cb5 100644 --- a/src/ui/screen_settings.cpp +++ b/src/ui/screen_settings.cpp @@ -161,7 +161,7 @@ static auto brightness_str(uint_fast8_t percent) -> std::string { return std::to_string(percent) + "%"; } -Appearance::Appearance(drivers::NvsStorage* nvs, drivers::Display* display) +Appearance::Appearance(drivers::NvsStorage& nvs, drivers::Display& display) : MenuScreen("Appearance"), nvs_(nvs), display_(display) { lv_obj_t* toggle_container = settings_container(content_); lv_obj_t* toggle_label = lv_label_create(toggle_container); @@ -170,7 +170,7 @@ Appearance::Appearance(drivers::NvsStorage* nvs, drivers::Display* display) lv_obj_t* toggle = lv_switch_create(toggle_container); lv_group_add_obj(group_, toggle); - uint_fast8_t initial_brightness = nvs_->ScreenBrightness().get(); + uint_fast8_t initial_brightness = nvs_.ScreenBrightness().get(); lv_obj_t* brightness_label = lv_label_create(content_); lv_label_set_text(brightness_label, "Brightness"); @@ -192,13 +192,13 @@ Appearance::Appearance(drivers::NvsStorage* nvs, drivers::Display* display) auto Appearance::ChangeBrightness(uint_fast8_t new_level) -> void { current_brightness_ = new_level; - display_->SetBrightness(new_level); + display_.SetBrightness(new_level); lv_label_set_text(current_brightness_label_, brightness_str(new_level).c_str()); } auto Appearance::CommitBrightness() -> void { - nvs_->ScreenBrightness(current_brightness_); + nvs_.ScreenBrightness(current_brightness_); } InputMethod::InputMethod() : MenuScreen("Input Method") { diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index 1febd1c7..0054db23 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -32,6 +32,7 @@ #include "touchwheel.hpp" #include "track_queue.hpp" #include "ui_events.hpp" +#include "wheel_encoder.hpp" #include "widget_top_bar.hpp" namespace ui { @@ -40,51 +41,25 @@ static constexpr char kTag[] = "ui_fsm"; static const std::size_t kRecordsPerPage = 15; -drivers::IGpios* UiState::sIGpios; -audio::TrackQueue* UiState::sQueue; - -std::shared_ptr UiState::sTouchWheel; -std::shared_ptr UiState::sRelativeWheel; -std::shared_ptr UiState::sDisplay; -std::shared_ptr UiState::sBattery; -std::shared_ptr UiState::sNvs; -std::weak_ptr UiState::sDb; +std::unique_ptr UiState::sTask; +std::shared_ptr UiState::sServices; +std::unique_ptr UiState::sDisplay; +std::shared_ptr UiState::sEncoder; std::stack> UiState::sScreens; std::shared_ptr UiState::sCurrentScreen; std::shared_ptr UiState::sCurrentModal; -auto UiState::Init(drivers::IGpios* gpio_expander, - std::shared_ptr nvs, - audio::TrackQueue* queue, - std::shared_ptr battery) -> bool { - sIGpios = gpio_expander; - sNvs = nvs; - sQueue = queue; - sBattery = battery; - +auto UiState::InitBootSplash(drivers::IGpios& gpios) -> bool { + // Init LVGL first, since the display driver registers itself with LVGL. lv_init(); - sDisplay.reset( - drivers::Display::Create(gpio_expander, drivers::displays::kST7735R)); + sDisplay.reset(drivers::Display::Create(gpios, drivers::displays::kST7735R)); if (sDisplay == nullptr) { return false; } - sDisplay->SetBrightness(nvs->ScreenBrightness().get()); - - sTouchWheel.reset(drivers::TouchWheel::Create()); - if (sTouchWheel != nullptr) { - sRelativeWheel.reset(new drivers::RelativeWheel(sTouchWheel.get())); - } sCurrentScreen.reset(new screens::Splash()); - - // Start the UI task even if init ultimately failed, so that we can show some - // kind of error screen to the user. - StartLvgl(sRelativeWheel, sDisplay); - - if (sTouchWheel == nullptr) { - return false; - } + sTask.reset(UiTask::Start()); return true; } @@ -107,7 +82,8 @@ void UiState::PopScreen() { void UiState::react(const system_fsm::KeyLockChanged& ev) { sDisplay->SetDisplayOn(ev.falling); - sRelativeWheel->SetEnabled(ev.falling); + sTask->SetInputDevice(ev.falling ? sEncoder + : std::shared_ptr()); } void UiState::react(const system_fsm::BatteryStateChanged&) { @@ -115,8 +91,8 @@ void UiState::react(const system_fsm::BatteryStateChanged&) { } void UiState::UpdateTopBar() { - auto battery_state = sBattery->State(); - bool has_queue = sQueue->GetCurrent().has_value(); + auto battery_state = sServices->battery().State(); + bool has_queue = sServices->track_queue().GetCurrent().has_value(); bool is_playing = audio::AudioState::is_in_state(); widgets::TopBar::State state{ @@ -140,19 +116,40 @@ namespace states { void Splash::exit() { if (sDisplay != nullptr) { - sDisplay->SetDisplayOn(sIGpios->Get(drivers::IGpios::Pin::kKeyLock)); + sDisplay->SetDisplayOn( + sServices->gpios().Get(drivers::IGpios::Pin::kKeyLock)); } } void Splash::react(const system_fsm::BootComplete& ev) { + sServices = ev.services; + + // The system has finished booting! We now need to prepare to show real UI. + // This basically just involves reading and applying the user's preferences. + + lv_theme_t* base_theme = lv_theme_basic_init(NULL); + lv_disp_set_theme(NULL, base_theme); + themes::Theme::instance()->Apply(); + + sDisplay->SetBrightness(sServices->nvs().ScreenBrightness().get()); + + auto touchwheel = sServices->touchwheel(); + if (touchwheel) { + auto relative_wheel = + std::make_unique(**touchwheel); + sEncoder = std::make_shared(std::move(relative_wheel)); + sTask->SetInputDevice(sEncoder); + } else { + ESP_LOGE(kTag, "no input devices initialised!"); + } + transit(); } void Browse::entry() {} void Browse::react(const system_fsm::StorageMounted& ev) { - sDb = ev.db; - auto db = ev.db.lock(); + auto db = sServices->database().lock(); if (!db) { // TODO(jacqueline): Hmm. return; @@ -177,7 +174,7 @@ void Browse::react(const internal::ShowSettingsPage& ev) { screen.reset(new screens::Headphones()); break; case internal::ShowSettingsPage::Page::kAppearance: - screen.reset(new screens::Appearance(sNvs.get(), sDisplay.get())); + screen.reset(new screens::Appearance(sServices->nvs(), *sDisplay)); break; case internal::ShowSettingsPage::Page::kInput: screen.reset(new screens::InputMethod()); @@ -198,7 +195,7 @@ void Browse::react(const internal::ShowSettingsPage& ev) { } void Browse::react(const internal::RecordSelected& ev) { - auto db = sDb.lock(); + auto db = sServices->database().lock(); if (!db) { return; } @@ -206,9 +203,10 @@ void Browse::react(const internal::RecordSelected& ev) { auto record = ev.page->values().at(ev.record); if (record.track()) { ESP_LOGI(kTag, "selected track '%s'", record.text()->c_str()); - sQueue->Clear(); - sQueue->IncludeLast(std::make_shared( - sDb, ev.initial_page, 0, ev.page, ev.record)); + auto& queue = sServices->track_queue(); + queue.Clear(); + queue.IncludeLast(std::make_shared( + sServices->database(), ev.initial_page, 0, ev.page, ev.record)); transit(); } else { ESP_LOGI(kTag, "selected record '%s'", record.text()->c_str()); @@ -218,21 +216,21 @@ void Browse::react(const internal::RecordSelected& ev) { } auto query = db->GetPage(&cont.value()); std::string title = record.text().value_or("TODO"); - PushScreen( - std::make_shared(sDb, title, std::move(query))); + PushScreen(std::make_shared( + sServices->database(), title, std::move(query))); } } void Browse::react(const internal::IndexSelected& ev) { - auto db = sDb.lock(); + auto db = sServices->database().lock(); if (!db) { return; } ESP_LOGI(kTag, "selected index %s", ev.index.name.c_str()); auto query = db->GetTracksByIndex(ev.index, kRecordsPerPage); - PushScreen(std::make_shared(sDb, ev.index.name, - std::move(query))); + PushScreen(std::make_shared( + sServices->database(), ev.index.name, std::move(query))); } void Browse::react(const internal::BackPressed& ev) { @@ -242,7 +240,8 @@ void Browse::react(const internal::BackPressed& ev) { static std::shared_ptr sPlayingScreen; void Playing::entry() { - sPlayingScreen.reset(new screens::Playing(sDb, sQueue)); + sPlayingScreen.reset( + new screens::Playing(sServices->database(), sServices->track_queue())); PushScreen(sPlayingScreen); } diff --git a/src/ui/wheel_encoder.cpp b/src/ui/wheel_encoder.cpp index a0e12b7f..2f2e7f68 100644 --- a/src/ui/wheel_encoder.cpp +++ b/src/ui/wheel_encoder.cpp @@ -22,8 +22,8 @@ void encoder_feedback(lv_indev_drv_t* drv, uint8_t event_code) { } TouchWheelEncoder::TouchWheelEncoder( - std::weak_ptr wheel) - : last_key_(0), wheel_(wheel) { + std::unique_ptr wheel) + : last_key_(0), wheel_(std::move(wheel)) { lv_indev_drv_init(&driver_); driver_.type = LV_INDEV_TYPE_ENCODER; driver_.read_cb = encoder_read; @@ -34,16 +34,11 @@ TouchWheelEncoder::TouchWheelEncoder( } auto TouchWheelEncoder::Read(lv_indev_data_t* data) -> void { - auto lock = wheel_.lock(); - if (lock == nullptr) { - return; - } + wheel_->Update(); - lock->Update(); - - data->enc_diff = lock->ticks(); + data->enc_diff = wheel_->ticks(); data->state = - lock->is_clicking() ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + wheel_->is_clicking() ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; } } // namespace ui