summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio/audio_fsm.cpp10
-rw-r--r--src/audio/include/audio_events.hpp2
-rw-r--r--src/audio/include/audio_fsm.hpp4
-rw-r--r--src/system_fsm/include/system_events.hpp6
-rw-r--r--src/system_fsm/system_fsm.cpp14
-rw-r--r--src/ui/encoder_input.cpp243
-rw-r--r--src/ui/include/encoder_input.hpp29
7 files changed, 204 insertions, 104 deletions
diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp
index 6ea4f60d..e470300f 100644
--- a/src/audio/audio_fsm.cpp
+++ b/src/audio/audio_fsm.cpp
@@ -50,16 +50,14 @@ std::shared_ptr<IAudioOutput> AudioState::sOutput;
std::optional<database::TrackId> AudioState::sCurrentTrack;
-void AudioState::react(const system_fsm::KeyUpChanged& ev) {
- if (ev.falling && sOutput->AdjustVolumeUp()) {
- ESP_LOGI(kTag, "volume up!");
+void AudioState::react(const StepUpVolume& ev) {
+ if (sOutput->AdjustVolumeUp()) {
events::Ui().Dispatch(VolumeChanged{});
}
}
-void AudioState::react(const system_fsm::KeyDownChanged& ev) {
- if (ev.falling && sOutput->AdjustVolumeDown()) {
- ESP_LOGI(kTag, "volume down!");
+void AudioState::react(const StepDownVolume& ev) {
+ if (sOutput->AdjustVolumeDown()) {
events::Ui().Dispatch(VolumeChanged{});
}
}
diff --git a/src/audio/include/audio_events.hpp b/src/audio/include/audio_events.hpp
index 6b986462..b130938c 100644
--- a/src/audio/include/audio_events.hpp
+++ b/src/audio/include/audio_events.hpp
@@ -34,6 +34,8 @@ struct PlayFile : tinyfsm::Event {
std::pmr::string filename;
};
+struct StepUpVolume : tinyfsm::Event {};
+struct StepDownVolume : tinyfsm::Event {};
struct VolumeChanged : tinyfsm::Event {};
struct ChangeMaxVolume : tinyfsm::Event {
uint16_t new_max;
diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp
index 1c0b8aaa..256e1430 100644
--- a/src/audio/include/audio_fsm.hpp
+++ b/src/audio/include/audio_fsm.hpp
@@ -41,8 +41,8 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
/* Fallback event handler. Does nothing. */
void react(const tinyfsm::Event& ev) {}
- void react(const system_fsm::KeyUpChanged&);
- void react(const system_fsm::KeyDownChanged&);
+ void react(const StepUpVolume&);
+ void react(const StepDownVolume&);
void react(const system_fsm::HasPhonesChanged&);
void react(const ChangeMaxVolume&);
void react(const OutputModeChanged&);
diff --git a/src/system_fsm/include/system_events.hpp b/src/system_fsm/include/system_events.hpp
index 7b21dbb5..4db9beb0 100644
--- a/src/system_fsm/include/system_events.hpp
+++ b/src/system_fsm/include/system_events.hpp
@@ -41,12 +41,6 @@ struct StorageMounted : tinyfsm::Event {};
struct StorageError : tinyfsm::Event {};
-struct KeyUpChanged : tinyfsm::Event {
- bool falling;
-};
-struct KeyDownChanged : tinyfsm::Event {
- bool falling;
-};
struct KeyLockChanged : tinyfsm::Event {
bool falling;
};
diff --git a/src/system_fsm/system_fsm.cpp b/src/system_fsm/system_fsm.cpp
index e048cec7..9e1a4198 100644
--- a/src/system_fsm/system_fsm.cpp
+++ b/src/system_fsm/system_fsm.cpp
@@ -31,28 +31,14 @@ void SystemState::react(const FatalError& err) {
void SystemState::react(const internal::GpioInterrupt&) {
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);
gpios.Read();
- 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};
- events::Audio().Dispatch(ev);
- events::Ui().Dispatch(ev);
- }
- if (key_down != prev_key_down) {
- KeyDownChanged ev{.falling = prev_key_down};
- events::Audio().Dispatch(ev);
- events::Ui().Dispatch(ev);
- }
if (key_lock != prev_key_lock) {
KeyLockChanged ev{.falling = key_lock};
events::System().Dispatch(ev);
diff --git a/src/ui/encoder_input.cpp b/src/ui/encoder_input.cpp
index 9aa5d29a..0345665b 100644
--- a/src/ui/encoder_input.cpp
+++ b/src/ui/encoder_input.cpp
@@ -9,13 +9,16 @@
#include <sys/_stdint.h>
#include <memory>
+#include "audio_events.hpp"
#include "core/lv_group.h"
#include "esp_timer.h"
+#include "event_queue.hpp"
#include "gpios.hpp"
#include "hal/lv_hal_indev.h"
#include "nvs.hpp"
#include "relative_wheel.hpp"
#include "touchwheel.hpp"
+#include "ui_events.hpp"
constexpr int kDPadAngleThreshold = 20;
constexpr int kLongPressDelayMs = 500;
@@ -70,83 +73,139 @@ auto EncoderInput::Read(lv_indev_data_t* data) -> void {
}
// Check each button.
- HandleKey(Keys::kVolumeUp, now_ms, !gpios_.Get(drivers::IGpios::Pin::kKeyUp));
- HandleKey(Keys::kVolumeDown, now_ms,
- !gpios_.Get(drivers::IGpios::Pin::kKeyDown));
+ HandleKeyState(Keys::kVolumeUp, now_ms,
+ !gpios_.Get(drivers::IGpios::Pin::kKeyUp));
+ HandleKeyState(Keys::kVolumeDown, now_ms,
+ !gpios_.Get(drivers::IGpios::Pin::kKeyDown));
drivers::TouchWheelData wheel_data = raw_wheel_.GetTouchWheelData();
- HandleKey(Keys::kTouchWheel, now_ms, wheel_data.is_wheel_touched);
- HandleKey(Keys::kTouchWheelCenter, now_ms, wheel_data.is_button_touched);
+ HandleKeyState(Keys::kTouchWheel, now_ms, wheel_data.is_wheel_touched);
+ HandleKeyState(Keys::kTouchWheelCenter, now_ms, wheel_data.is_button_touched);
- HandleKey(
+ HandleKeyState(
Keys::kDirectionalUp, now_ms,
wheel_data.is_wheel_touched &&
IsAngleWithin(wheel_data.wheel_position, 0, kDPadAngleThreshold));
- HandleKey(
+ HandleKeyState(
Keys::kDirectionalLeft, now_ms,
wheel_data.is_wheel_touched &&
IsAngleWithin(wheel_data.wheel_position, 63, kDPadAngleThreshold));
- HandleKey(
+ HandleKeyState(
Keys::kDirectionalDown, now_ms,
wheel_data.is_wheel_touched &&
IsAngleWithin(wheel_data.wheel_position, 127, kDPadAngleThreshold));
- HandleKey(
+ HandleKeyState(
Keys::kDirectionalRight, now_ms,
wheel_data.is_wheel_touched &&
IsAngleWithin(wheel_data.wheel_position, 189, kDPadAngleThreshold));
// We now have enough information to give LVGL its update.
+ Trigger trigger;
switch (mode_) {
case drivers::NvsStorage::InputModes::kButtonsOnly:
data->state = LV_INDEV_STATE_RELEASED;
- if (ShortPressTrigger(Keys::kVolumeUp)) {
- data->enc_diff = -1;
- } else if (ShortPressTrigger(Keys::kVolumeDown)) {
- data->enc_diff = 1;
- } else if (LongPressTrigger(Keys::kVolumeDown, now_ms)) {
- data->state = LV_INDEV_STATE_PRESSED;
- } else if (LongPressTrigger(Keys::kVolumeUp, now_ms)) {
- // TODO: Back button event
+
+ trigger = TriggerKey(Keys::kVolumeUp, KeyStyle::kLongPress, now_ms);
+ switch (trigger) {
+ case Trigger::kNone:
+ break;
+ case Trigger::kClick:
+ data->enc_diff = -1;
+ break;
+ case Trigger::kLongPress:
+ events::Ui().Dispatch(internal::BackPressed{});
+ break;
+ }
+
+ trigger = TriggerKey(Keys::kVolumeDown, KeyStyle::kLongPress, now_ms);
+ switch (trigger) {
+ case Trigger::kNone:
+ break;
+ case Trigger::kClick:
+ data->enc_diff = 1;
+ break;
+ case Trigger::kLongPress:
+ data->state = LV_INDEV_STATE_PRESSED;
+ break;
}
+
break;
case drivers::NvsStorage::InputModes::kButtonsWithWheel:
- data->state = ShortPressTrigger(Keys::kTouchWheel)
- ? LV_INDEV_STATE_PRESSED
- : LV_INDEV_STATE_RELEASED;
- if (ShortPressTriggerRepeating(Keys::kVolumeUp, now_ms)) {
+ trigger = TriggerKey(Keys::kTouchWheel, KeyStyle::kLongPress, now_ms);
+ data->state = trigger == Trigger::kClick ? LV_INDEV_STATE_PRESSED
+ : LV_INDEV_STATE_RELEASED;
+
+ trigger = TriggerKey(Keys::kVolumeUp, KeyStyle::kRepeat, now_ms);
+ if (trigger == Trigger::kClick) {
data->enc_diff = scroller_->AddInput(now_ms, -1);
- } else if (ShortPressTriggerRepeating(Keys::kVolumeDown, now_ms)) {
+ }
+
+ trigger = TriggerKey(Keys::kVolumeDown, KeyStyle::kRepeat, now_ms);
+ if (trigger == Trigger::kClick) {
data->enc_diff = scroller_->AddInput(now_ms, 1);
}
+ // Cancel scrolling if the buttons are released.
if (!touch_time_ms_.contains(Keys::kVolumeDown) &&
!touch_time_ms_.contains(Keys::kVolumeUp)) {
data->enc_diff = scroller_->AddInput(now_ms, 0);
}
- // TODO: Long-press events.
+
break;
case drivers::NvsStorage::InputModes::kDirectionalWheel:
- data->state = ShortPressTrigger(Keys::kTouchWheelCenter)
- ? LV_INDEV_STATE_PRESSED
- : LV_INDEV_STATE_RELEASED;
- if (!ShortPressTriggerRepeating(Keys::kTouchWheel, now_ms)) {
- break;
- }
- if (ShortPressTriggerRepeating(Keys::kDirectionalUp, now_ms)) {
+ trigger =
+ TriggerKey(Keys::kTouchWheelCenter, KeyStyle::kLongPress, now_ms);
+ data->state = trigger == Trigger::kClick ? LV_INDEV_STATE_PRESSED
+ : LV_INDEV_STATE_RELEASED;
+
+ trigger = TriggerKey(Keys::kDirectionalUp, KeyStyle::kRepeat, now_ms);
+ if (trigger == Trigger::kClick) {
data->enc_diff = scroller_->AddInput(now_ms, -1);
- } else if (ShortPressTriggerRepeating(Keys::kDirectionalDown, now_ms)) {
+ }
+
+ trigger = TriggerKey(Keys::kDirectionalDown, KeyStyle::kRepeat, now_ms);
+ if (trigger == Trigger::kClick) {
data->enc_diff = scroller_->AddInput(now_ms, 1);
- } else if (ShortPressTrigger(Keys::kDirectionalRight)) {
+ }
+
+ trigger = TriggerKey(Keys::kDirectionalLeft, KeyStyle::kRepeat, now_ms);
+ if (trigger == Trigger::kClick) {
+ events::Ui().Dispatch(internal::BackPressed{});
+ }
+
+ trigger = TriggerKey(Keys::kDirectionalRight, KeyStyle::kRepeat, now_ms);
+ if (trigger == Trigger::kClick) {
// TODO: ???
- } else if (ShortPressTrigger(Keys::kDirectionalLeft)) {
- // TODO: Back button event.
}
+ // Cancel scrolling if the touchpad is released.
if (!touch_time_ms_.contains(Keys::kDirectionalUp) &&
!touch_time_ms_.contains(Keys::kDirectionalDown)) {
data->enc_diff = scroller_->AddInput(now_ms, 0);
}
- // TODO: Long-press events.
+
+ trigger = TriggerKey(Keys::kVolumeUp, KeyStyle::kLongPress, now_ms);
+ switch (trigger) {
+ case Trigger::kNone:
+ break;
+ case Trigger::kClick:
+ events::Audio().Dispatch(audio::StepUpVolume{});
+ break;
+ case Trigger::kLongPress:
+ break;
+ }
+
+ trigger = TriggerKey(Keys::kVolumeDown, KeyStyle::kLongPress, now_ms);
+ switch (trigger) {
+ case Trigger::kNone:
+ break;
+ case Trigger::kClick:
+ events::Audio().Dispatch(audio::StepDownVolume{});
+ break;
+ case Trigger::kLongPress:
+ break;
+ }
+
break;
case drivers::NvsStorage::InputModes::kRotatingWheel:
if (!raw_wheel_.GetTouchWheelData().is_wheel_touched) {
@@ -156,53 +215,97 @@ auto EncoderInput::Read(lv_indev_data_t* data) -> void {
} else {
data->enc_diff = 0;
}
- data->state = relative_wheel_->is_clicking() ? LV_INDEV_STATE_PRESSED
- : LV_INDEV_STATE_RELEASED;
- // TODO: Long-press events.
+
+ trigger =
+ TriggerKey(Keys::kTouchWheelCenter, KeyStyle::kLongPress, now_ms);
+ switch (trigger) {
+ case Trigger::kNone:
+ data->state = LV_INDEV_STATE_RELEASED;
+ break;
+ case Trigger::kClick:
+ data->state = LV_INDEV_STATE_PRESSED;
+ break;
+ case Trigger::kLongPress:
+ // TODO: ???
+ data->state = LV_INDEV_STATE_PRESSED;
+ break;
+ }
+
+ trigger = TriggerKey(Keys::kVolumeUp, KeyStyle::kLongPress, now_ms);
+ switch (trigger) {
+ case Trigger::kNone:
+ break;
+ case Trigger::kClick:
+ events::Audio().Dispatch(audio::StepUpVolume{});
+ break;
+ case Trigger::kLongPress:
+ break;
+ }
+
+ trigger = TriggerKey(Keys::kVolumeDown, KeyStyle::kLongPress, now_ms);
+ switch (trigger) {
+ case Trigger::kNone:
+ break;
+ case Trigger::kClick:
+ events::Audio().Dispatch(audio::StepDownVolume{});
+ break;
+ case Trigger::kLongPress:
+ break;
+ }
+
break;
}
-
- // TODO: Apply inertia / acceleration.
}
-auto EncoderInput::HandleKey(Keys key, uint64_t ms, bool clicked) -> void {
- if (!clicked) {
- touch_time_ms_.erase(key);
- short_press_fired_.erase(key);
- long_press_fired_.erase(key);
+auto EncoderInput::HandleKeyState(Keys key, uint64_t ms, bool clicked) -> void {
+ if (clicked) {
+ if (!touch_time_ms_.contains(key)) {
+ // Key was just pressed
+ touch_time_ms_[key] = ms;
+ just_released_.erase(key);
+ fired_.erase(key);
+ }
return;
}
- if (!touch_time_ms_.contains(key)) {
- touch_time_ms_[key] = ms;
- }
-}
-auto EncoderInput::ShortPressTrigger(Keys key) -> bool {
- if (touch_time_ms_.contains(key) && !short_press_fired_.contains(key)) {
- short_press_fired_[key] = true;
- return true;
+ // Key is not clicked.
+ if (touch_time_ms_.contains(key)) {
+ // Key was just released.
+ just_released_.insert(key);
+ touch_time_ms_.erase(key);
}
- return false;
}
-auto EncoderInput::ShortPressTriggerRepeating(Keys key, uint64_t ms) -> bool {
- if (touch_time_ms_.contains(key) &&
- (!short_press_fired_.contains(key) ||
- ms - touch_time_ms_[key] >= kRepeatDelayMs)) {
- touch_time_ms_[key] = ms;
- short_press_fired_[key] = true;
- return true;
- }
- return false;
-}
+auto EncoderInput::TriggerKey(Keys key, KeyStyle s, uint64_t ms) -> Trigger {
+ if (s == KeyStyle::kRepeat) {
+ bool may_repeat = fired_.contains(key) && touch_time_ms_.contains(key) &&
+ ms - touch_time_ms_[key] >= kRepeatDelayMs;
-auto EncoderInput::LongPressTrigger(Keys key, uint64_t ms) -> bool {
- if (touch_time_ms_.contains(key) && !long_press_fired_.contains(key) &&
- ms - touch_time_ms_[key] >= kLongPressDelayMs) {
- long_press_fired_[key] = true;
- return true;
+ // Repeatable keys trigger on press.
+ if (touch_time_ms_.contains(key) && (!fired_.contains(key) || may_repeat)) {
+ fired_.insert(key);
+ return Trigger::kClick;
+ } else {
+ return Trigger::kNone;
+ }
+ } else if (s == KeyStyle::kLongPress) {
+ // Long press keys trigger on release, or after holding for a delay.
+ if (just_released_.contains(key)) {
+ just_released_.erase(key);
+ if (!fired_.contains(key)) {
+ fired_.insert(key);
+ return Trigger::kClick;
+ }
+ }
+ if (touch_time_ms_.contains(key) &&
+ ms - touch_time_ms_[key] >= kLongPressDelayMs &&
+ !fired_.contains(key)) {
+ fired_.insert(key);
+ return Trigger::kLongPress;
+ }
}
- return false;
+
+ return Trigger::kNone;
}
auto Scroller::AddInput(uint64_t ms, int direction) -> int {
diff --git a/src/ui/include/encoder_input.hpp b/src/ui/include/encoder_input.hpp
index e685a7a2..2386b5c9 100644
--- a/src/ui/include/encoder_input.hpp
+++ b/src/ui/include/encoder_input.hpp
@@ -9,6 +9,7 @@
#include <stdint.h>
#include <deque>
#include <memory>
+#include <set>
#include "core/lv_group.h"
#include "gpios.hpp"
@@ -51,6 +52,7 @@ class EncoderInput {
drivers::NvsStorage::InputModes mode_;
bool is_locked_;
+ // Every kind of distinct input that we could map to an action.
enum class Keys {
kVolumeUp,
kVolumeDown,
@@ -62,14 +64,29 @@ class EncoderInput {
kDirectionalLeft,
};
+ // Map from a Key, to the time that it was first touched in ms. If the key is
+ // currently released, where will be no entry.
std::unordered_map<Keys, uint64_t> touch_time_ms_;
- std::unordered_map<Keys, bool> short_press_fired_;
- std::unordered_map<Keys, bool> long_press_fired_;
+ // Set of keys that were released during the current update.
+ std::set<Keys> just_released_;
+ // Set of keys that have had an event fired for them since being pressed.
+ std::set<Keys> fired_;
+
+ enum class Trigger {
+ kNone,
+ // Regular short-click. Triggered on release for long-pressable keys,
+ // triggered on the initial press for repeatable keys.
+ kClick,
+ kLongPress,
+ };
+
+ enum class KeyStyle {
+ kRepeat,
+ kLongPress,
+ };
- auto HandleKey(Keys key, uint64_t ms, bool clicked) -> void;
- auto ShortPressTrigger(Keys key) -> bool;
- auto ShortPressTriggerRepeating(Keys key, uint64_t ms) -> bool;
- auto LongPressTrigger(Keys key, uint64_t ms) -> bool;
+ auto HandleKeyState(Keys key, uint64_t ms, bool clicked) -> void;
+ auto TriggerKey(Keys key, KeyStyle t, uint64_t ms) -> Trigger;
};
class Scroller {