diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp index 16a9609d..ffaa0a5e 100644 --- a/src/drivers/nvs.cpp +++ b/src/drivers/nvs.cpp @@ -31,6 +31,7 @@ static constexpr char kKeyBrightness[] = "bright"; static constexpr char kKeyAmpMaxVolume[] = "hp_vol_max"; static constexpr char kKeyAmpCurrentVolume[] = "hp_vol"; static constexpr char kKeyOnboarded[] = "intro"; +static constexpr char kKeyPrimaryInput[] = "in_pri"; auto NvsStorage::OpenSync() -> NvsStorage* { esp_err_t err = nvs_flash_init(); @@ -166,4 +167,27 @@ auto NvsStorage::HasShownOnboarding(bool val) -> bool { return nvs_commit(handle_) == ESP_OK; } +auto NvsStorage::PrimaryInput() -> InputModes { + uint8_t out = 3; + nvs_get_u8(handle_, kKeyPrimaryInput, &out); + switch (out) { + case static_cast(InputModes::kButtonsOnly): + return InputModes::kButtonsOnly; + case static_cast(InputModes::kButtonsWithWheel): + return InputModes::kButtonsWithWheel; + case static_cast(InputModes::kDirectionalWheel): + return InputModes::kDirectionalWheel; + case static_cast(InputModes::kRotatingWheel): + return InputModes::kRotatingWheel; + default: + return InputModes::kRotatingWheel; + } +} + +auto NvsStorage::PrimaryInput(InputModes mode) -> bool { + uint8_t as_int = static_cast(mode); + nvs_set_u8(handle_, kKeyPrimaryInput, as_int); + return nvs_commit(handle_) == ESP_OK; +} + } // namespace drivers diff --git a/src/ui/encoder_input.cpp b/src/ui/encoder_input.cpp index f6f74aaf..9aa5d29a 100644 --- a/src/ui/encoder_input.cpp +++ b/src/ui/encoder_input.cpp @@ -40,7 +40,8 @@ EncoderInput::EncoderInput(drivers::IGpios& gpios, drivers::TouchWheel& wheel) raw_wheel_(wheel), relative_wheel_(std::make_unique(wheel)), scroller_(std::make_unique()), - mode_(drivers::NvsStorage::InputModes::kRotatingWheel) { + mode_(drivers::NvsStorage::InputModes::kRotatingWheel), + is_locked_(false) { lv_indev_drv_init(&driver_); driver_.type = LV_INDEV_TYPE_ENCODER; driver_.read_cb = encoder_read; @@ -50,6 +51,10 @@ EncoderInput::EncoderInput(drivers::IGpios& gpios, drivers::TouchWheel& wheel) } auto EncoderInput::Read(lv_indev_data_t* data) -> void { + if (is_locked_) { + return; + } + raw_wheel_.Update(); relative_wheel_->Update(); // GPIOs updating is handled by system_fsm. diff --git a/src/ui/include/screen_settings.hpp b/src/ui/include/screen_settings.hpp index 1a4672ed..ae0b6aed 100644 --- a/src/ui/include/screen_settings.hpp +++ b/src/ui/include/screen_settings.hpp @@ -89,7 +89,10 @@ class Appearance : public MenuScreen { class InputMethod : public MenuScreen { public: - InputMethod(models::TopBar&); + InputMethod(models::TopBar&, drivers::NvsStorage& nvs); + + private: + drivers::NvsStorage& nvs_; }; class Storage : public MenuScreen { diff --git a/src/ui/include/ui_events.hpp b/src/ui/include/ui_events.hpp index fb3bb2d4..e08f8888 100644 --- a/src/ui/include/ui_events.hpp +++ b/src/ui/include/ui_events.hpp @@ -36,6 +36,8 @@ struct IndexSelected : tinyfsm::Event { database::IndexInfo index; }; +struct ControlSchemeChanged : tinyfsm::Event {}; + struct BackPressed : tinyfsm::Event {}; struct ShowNowPlaying : tinyfsm::Event {}; struct ShowSettingsPage : tinyfsm::Event { diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 4db8257d..eef96b50 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -74,6 +74,7 @@ class UiState : public tinyfsm::Fsm { sCurrentModal.reset(); } virtual void react(const internal::OnboardingNavigate&) {} + void react(const internal::ControlSchemeChanged&); virtual void react(const system_fsm::DisplayReady&) {} virtual void react(const system_fsm::BootComplete&) {} diff --git a/src/ui/screen_settings.cpp b/src/ui/screen_settings.cpp index a2dc4296..6ee8405e 100644 --- a/src/ui/screen_settings.cpp +++ b/src/ui/screen_settings.cpp @@ -422,19 +422,40 @@ auto Appearance::CommitBrightness() -> void { nvs_.ScreenBrightness(current_brightness_); } -InputMethod::InputMethod(models::TopBar& bar) - : MenuScreen(bar, "Input Method") { - lv_obj_t* wheel_label = lv_label_create(content_); - lv_label_set_text(wheel_label, "What does the wheel do?"); - lv_obj_t* wheel_dropdown = lv_dropdown_create(content_); - lv_dropdown_set_options(wheel_dropdown, "Scroll\nDirectional\nBig Button"); - lv_group_add_obj(group_, wheel_dropdown); - - lv_obj_t* buttons_label = lv_label_create(content_); - lv_label_set_text(buttons_label, "What do the buttons do?"); - lv_obj_t* buttons_dropdown = lv_dropdown_create(content_); - lv_dropdown_set_options(buttons_dropdown, "Volume\nScroll"); - lv_group_add_obj(group_, buttons_dropdown); +InputMethod::InputMethod(models::TopBar& bar, drivers::NvsStorage& nvs) + : MenuScreen(bar, "Input Method"), nvs_(nvs) { + lv_obj_t* primary_label = lv_label_create(content_); + lv_label_set_text(primary_label, "Control scheme"); + lv_obj_t* primary_dropdown = lv_dropdown_create(content_); + lv_dropdown_set_options( + primary_dropdown, + "Side buttons only\nButtons and touch\nD-Pad\nClickwheel"); + lv_group_add_obj(group_, primary_dropdown); + + lv_dropdown_set_selected(primary_dropdown, + static_cast(nvs.PrimaryInput())); + + lv_bind(primary_dropdown, LV_EVENT_VALUE_CHANGED, [this](lv_obj_t* obj) { + drivers::NvsStorage::InputModes mode; + switch (lv_dropdown_get_selected(obj)) { + 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; + } + nvs_.PrimaryInput(mode); + events::Ui().Dispatch(internal::ControlSchemeChanged{}); + }); } Storage::Storage(models::TopBar& bar) : MenuScreen(bar, "Storage") { diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index bc976eab..03e20882 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -114,6 +114,13 @@ void UiState::react(const audio::QueueUpdate&) { sPlaybackModel.upcoming_tracks.set(queue.GetUpcoming(10)); } +void UiState::react(const internal::ControlSchemeChanged&) { + if (!sInput) { + return; + } + sInput->mode(sServices->nvs().PrimaryInput()); +} + namespace states { void Splash::exit() { @@ -138,6 +145,7 @@ void Splash::react(const system_fsm::BootComplete& ev) { auto touchwheel = sServices->touchwheel(); if (touchwheel) { sInput = std::make_shared(sServices->gpios(), **touchwheel); + sInput->mode(sServices->nvs().PrimaryInput()); sTask->input(sInput); } else { ESP_LOGE(kTag, "no input devices initialised!"); @@ -252,7 +260,7 @@ void Browse::react(const internal::ShowSettingsPage& ev) { new screens::Appearance(sTopBarModel, sServices->nvs(), *sDisplay)); break; case internal::ShowSettingsPage::Page::kInput: - screen.reset(new screens::InputMethod(sTopBarModel)); + screen.reset(new screens::InputMethod(sTopBarModel, sServices->nvs())); break; case internal::ShowSettingsPage::Page::kStorage: screen.reset(new screens::Storage(sTopBarModel));