Neaten up the various kinds of inputs, and move volumes to The New Way
This commit is contained in:
@@ -50,16 +50,14 @@ std::shared_ptr<IAudioOutput> AudioState::sOutput;
|
|||||||
|
|
||||||
std::optional<database::TrackId> AudioState::sCurrentTrack;
|
std::optional<database::TrackId> AudioState::sCurrentTrack;
|
||||||
|
|
||||||
void AudioState::react(const system_fsm::KeyUpChanged& ev) {
|
void AudioState::react(const StepUpVolume& ev) {
|
||||||
if (ev.falling && sOutput->AdjustVolumeUp()) {
|
if (sOutput->AdjustVolumeUp()) {
|
||||||
ESP_LOGI(kTag, "volume up!");
|
|
||||||
events::Ui().Dispatch(VolumeChanged{});
|
events::Ui().Dispatch(VolumeChanged{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioState::react(const system_fsm::KeyDownChanged& ev) {
|
void AudioState::react(const StepDownVolume& ev) {
|
||||||
if (ev.falling && sOutput->AdjustVolumeDown()) {
|
if (sOutput->AdjustVolumeDown()) {
|
||||||
ESP_LOGI(kTag, "volume down!");
|
|
||||||
events::Ui().Dispatch(VolumeChanged{});
|
events::Ui().Dispatch(VolumeChanged{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ struct PlayFile : tinyfsm::Event {
|
|||||||
std::pmr::string filename;
|
std::pmr::string filename;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct StepUpVolume : tinyfsm::Event {};
|
||||||
|
struct StepDownVolume : tinyfsm::Event {};
|
||||||
struct VolumeChanged : tinyfsm::Event {};
|
struct VolumeChanged : tinyfsm::Event {};
|
||||||
struct ChangeMaxVolume : tinyfsm::Event {
|
struct ChangeMaxVolume : tinyfsm::Event {
|
||||||
uint16_t new_max;
|
uint16_t new_max;
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
|
|||||||
/* Fallback event handler. Does nothing. */
|
/* Fallback event handler. Does nothing. */
|
||||||
void react(const tinyfsm::Event& ev) {}
|
void react(const tinyfsm::Event& ev) {}
|
||||||
|
|
||||||
void react(const system_fsm::KeyUpChanged&);
|
void react(const StepUpVolume&);
|
||||||
void react(const system_fsm::KeyDownChanged&);
|
void react(const StepDownVolume&);
|
||||||
void react(const system_fsm::HasPhonesChanged&);
|
void react(const system_fsm::HasPhonesChanged&);
|
||||||
void react(const ChangeMaxVolume&);
|
void react(const ChangeMaxVolume&);
|
||||||
void react(const OutputModeChanged&);
|
void react(const OutputModeChanged&);
|
||||||
|
|||||||
@@ -41,12 +41,6 @@ struct StorageMounted : tinyfsm::Event {};
|
|||||||
|
|
||||||
struct StorageError : tinyfsm::Event {};
|
struct StorageError : tinyfsm::Event {};
|
||||||
|
|
||||||
struct KeyUpChanged : tinyfsm::Event {
|
|
||||||
bool falling;
|
|
||||||
};
|
|
||||||
struct KeyDownChanged : tinyfsm::Event {
|
|
||||||
bool falling;
|
|
||||||
};
|
|
||||||
struct KeyLockChanged : tinyfsm::Event {
|
struct KeyLockChanged : tinyfsm::Event {
|
||||||
bool falling;
|
bool falling;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,28 +31,14 @@ void SystemState::react(const FatalError& err) {
|
|||||||
|
|
||||||
void SystemState::react(const internal::GpioInterrupt&) {
|
void SystemState::react(const internal::GpioInterrupt&) {
|
||||||
auto& gpios = sServices->gpios();
|
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_key_lock = gpios.Get(drivers::Gpios::Pin::kKeyLock);
|
||||||
bool prev_has_headphones = !gpios.Get(drivers::Gpios::Pin::kPhoneDetect);
|
bool prev_has_headphones = !gpios.Get(drivers::Gpios::Pin::kPhoneDetect);
|
||||||
|
|
||||||
gpios.Read();
|
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 key_lock = gpios.Get(drivers::Gpios::Pin::kKeyLock);
|
||||||
bool has_headphones = !gpios.Get(drivers::Gpios::Pin::kPhoneDetect);
|
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) {
|
if (key_lock != prev_key_lock) {
|
||||||
KeyLockChanged ev{.falling = key_lock};
|
KeyLockChanged ev{.falling = key_lock};
|
||||||
events::System().Dispatch(ev);
|
events::System().Dispatch(ev);
|
||||||
|
|||||||
+168
-65
@@ -9,13 +9,16 @@
|
|||||||
#include <sys/_stdint.h>
|
#include <sys/_stdint.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "audio_events.hpp"
|
||||||
#include "core/lv_group.h"
|
#include "core/lv_group.h"
|
||||||
#include "esp_timer.h"
|
#include "esp_timer.h"
|
||||||
|
#include "event_queue.hpp"
|
||||||
#include "gpios.hpp"
|
#include "gpios.hpp"
|
||||||
#include "hal/lv_hal_indev.h"
|
#include "hal/lv_hal_indev.h"
|
||||||
#include "nvs.hpp"
|
#include "nvs.hpp"
|
||||||
#include "relative_wheel.hpp"
|
#include "relative_wheel.hpp"
|
||||||
#include "touchwheel.hpp"
|
#include "touchwheel.hpp"
|
||||||
|
#include "ui_events.hpp"
|
||||||
|
|
||||||
constexpr int kDPadAngleThreshold = 20;
|
constexpr int kDPadAngleThreshold = 20;
|
||||||
constexpr int kLongPressDelayMs = 500;
|
constexpr int kLongPressDelayMs = 500;
|
||||||
@@ -70,83 +73,139 @@ auto EncoderInput::Read(lv_indev_data_t* data) -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check each button.
|
// Check each button.
|
||||||
HandleKey(Keys::kVolumeUp, now_ms, !gpios_.Get(drivers::IGpios::Pin::kKeyUp));
|
HandleKeyState(Keys::kVolumeUp, now_ms,
|
||||||
HandleKey(Keys::kVolumeDown, now_ms,
|
!gpios_.Get(drivers::IGpios::Pin::kKeyUp));
|
||||||
|
HandleKeyState(Keys::kVolumeDown, now_ms,
|
||||||
!gpios_.Get(drivers::IGpios::Pin::kKeyDown));
|
!gpios_.Get(drivers::IGpios::Pin::kKeyDown));
|
||||||
|
|
||||||
drivers::TouchWheelData wheel_data = raw_wheel_.GetTouchWheelData();
|
drivers::TouchWheelData wheel_data = raw_wheel_.GetTouchWheelData();
|
||||||
HandleKey(Keys::kTouchWheel, now_ms, wheel_data.is_wheel_touched);
|
HandleKeyState(Keys::kTouchWheel, now_ms, wheel_data.is_wheel_touched);
|
||||||
HandleKey(Keys::kTouchWheelCenter, now_ms, wheel_data.is_button_touched);
|
HandleKeyState(Keys::kTouchWheelCenter, now_ms, wheel_data.is_button_touched);
|
||||||
|
|
||||||
HandleKey(
|
HandleKeyState(
|
||||||
Keys::kDirectionalUp, now_ms,
|
Keys::kDirectionalUp, now_ms,
|
||||||
wheel_data.is_wheel_touched &&
|
wheel_data.is_wheel_touched &&
|
||||||
IsAngleWithin(wheel_data.wheel_position, 0, kDPadAngleThreshold));
|
IsAngleWithin(wheel_data.wheel_position, 0, kDPadAngleThreshold));
|
||||||
HandleKey(
|
HandleKeyState(
|
||||||
Keys::kDirectionalLeft, now_ms,
|
Keys::kDirectionalLeft, now_ms,
|
||||||
wheel_data.is_wheel_touched &&
|
wheel_data.is_wheel_touched &&
|
||||||
IsAngleWithin(wheel_data.wheel_position, 63, kDPadAngleThreshold));
|
IsAngleWithin(wheel_data.wheel_position, 63, kDPadAngleThreshold));
|
||||||
HandleKey(
|
HandleKeyState(
|
||||||
Keys::kDirectionalDown, now_ms,
|
Keys::kDirectionalDown, now_ms,
|
||||||
wheel_data.is_wheel_touched &&
|
wheel_data.is_wheel_touched &&
|
||||||
IsAngleWithin(wheel_data.wheel_position, 127, kDPadAngleThreshold));
|
IsAngleWithin(wheel_data.wheel_position, 127, kDPadAngleThreshold));
|
||||||
HandleKey(
|
HandleKeyState(
|
||||||
Keys::kDirectionalRight, now_ms,
|
Keys::kDirectionalRight, now_ms,
|
||||||
wheel_data.is_wheel_touched &&
|
wheel_data.is_wheel_touched &&
|
||||||
IsAngleWithin(wheel_data.wheel_position, 189, kDPadAngleThreshold));
|
IsAngleWithin(wheel_data.wheel_position, 189, kDPadAngleThreshold));
|
||||||
|
|
||||||
// We now have enough information to give LVGL its update.
|
// We now have enough information to give LVGL its update.
|
||||||
|
Trigger trigger;
|
||||||
switch (mode_) {
|
switch (mode_) {
|
||||||
case drivers::NvsStorage::InputModes::kButtonsOnly:
|
case drivers::NvsStorage::InputModes::kButtonsOnly:
|
||||||
data->state = LV_INDEV_STATE_RELEASED;
|
data->state = LV_INDEV_STATE_RELEASED;
|
||||||
if (ShortPressTrigger(Keys::kVolumeUp)) {
|
|
||||||
|
trigger = TriggerKey(Keys::kVolumeUp, KeyStyle::kLongPress, now_ms);
|
||||||
|
switch (trigger) {
|
||||||
|
case Trigger::kNone:
|
||||||
|
break;
|
||||||
|
case Trigger::kClick:
|
||||||
data->enc_diff = -1;
|
data->enc_diff = -1;
|
||||||
} else if (ShortPressTrigger(Keys::kVolumeDown)) {
|
break;
|
||||||
data->enc_diff = 1;
|
case Trigger::kLongPress:
|
||||||
} else if (LongPressTrigger(Keys::kVolumeDown, now_ms)) {
|
events::Ui().Dispatch(internal::BackPressed{});
|
||||||
data->state = LV_INDEV_STATE_PRESSED;
|
break;
|
||||||
} else if (LongPressTrigger(Keys::kVolumeUp, now_ms)) {
|
|
||||||
// TODO: Back button event
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
break;
|
||||||
case drivers::NvsStorage::InputModes::kButtonsWithWheel:
|
case drivers::NvsStorage::InputModes::kButtonsWithWheel:
|
||||||
data->state = ShortPressTrigger(Keys::kTouchWheel)
|
trigger = TriggerKey(Keys::kTouchWheel, KeyStyle::kLongPress, now_ms);
|
||||||
? LV_INDEV_STATE_PRESSED
|
data->state = trigger == Trigger::kClick ? LV_INDEV_STATE_PRESSED
|
||||||
: LV_INDEV_STATE_RELEASED;
|
: LV_INDEV_STATE_RELEASED;
|
||||||
if (ShortPressTriggerRepeating(Keys::kVolumeUp, now_ms)) {
|
|
||||||
|
trigger = TriggerKey(Keys::kVolumeUp, KeyStyle::kRepeat, now_ms);
|
||||||
|
if (trigger == Trigger::kClick) {
|
||||||
data->enc_diff = scroller_->AddInput(now_ms, -1);
|
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);
|
data->enc_diff = scroller_->AddInput(now_ms, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cancel scrolling if the buttons are released.
|
||||||
if (!touch_time_ms_.contains(Keys::kVolumeDown) &&
|
if (!touch_time_ms_.contains(Keys::kVolumeDown) &&
|
||||||
!touch_time_ms_.contains(Keys::kVolumeUp)) {
|
!touch_time_ms_.contains(Keys::kVolumeUp)) {
|
||||||
data->enc_diff = scroller_->AddInput(now_ms, 0);
|
data->enc_diff = scroller_->AddInput(now_ms, 0);
|
||||||
}
|
}
|
||||||
// TODO: Long-press events.
|
|
||||||
break;
|
break;
|
||||||
case drivers::NvsStorage::InputModes::kDirectionalWheel:
|
case drivers::NvsStorage::InputModes::kDirectionalWheel:
|
||||||
data->state = ShortPressTrigger(Keys::kTouchWheelCenter)
|
trigger =
|
||||||
? LV_INDEV_STATE_PRESSED
|
TriggerKey(Keys::kTouchWheelCenter, KeyStyle::kLongPress, now_ms);
|
||||||
|
data->state = trigger == Trigger::kClick ? LV_INDEV_STATE_PRESSED
|
||||||
: LV_INDEV_STATE_RELEASED;
|
: LV_INDEV_STATE_RELEASED;
|
||||||
if (!ShortPressTriggerRepeating(Keys::kTouchWheel, now_ms)) {
|
|
||||||
break;
|
trigger = TriggerKey(Keys::kDirectionalUp, KeyStyle::kRepeat, now_ms);
|
||||||
}
|
if (trigger == Trigger::kClick) {
|
||||||
if (ShortPressTriggerRepeating(Keys::kDirectionalUp, now_ms)) {
|
|
||||||
data->enc_diff = scroller_->AddInput(now_ms, -1);
|
data->enc_diff = scroller_->AddInput(now_ms, -1);
|
||||||
} else if (ShortPressTriggerRepeating(Keys::kDirectionalDown, now_ms)) {
|
|
||||||
data->enc_diff = scroller_->AddInput(now_ms, 1);
|
|
||||||
} else if (ShortPressTrigger(Keys::kDirectionalRight)) {
|
|
||||||
// TODO: ???
|
|
||||||
} else if (ShortPressTrigger(Keys::kDirectionalLeft)) {
|
|
||||||
// TODO: Back button event.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trigger = TriggerKey(Keys::kDirectionalDown, KeyStyle::kRepeat, now_ms);
|
||||||
|
if (trigger == Trigger::kClick) {
|
||||||
|
data->enc_diff = scroller_->AddInput(now_ms, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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: ???
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel scrolling if the touchpad is released.
|
||||||
if (!touch_time_ms_.contains(Keys::kDirectionalUp) &&
|
if (!touch_time_ms_.contains(Keys::kDirectionalUp) &&
|
||||||
!touch_time_ms_.contains(Keys::kDirectionalDown)) {
|
!touch_time_ms_.contains(Keys::kDirectionalDown)) {
|
||||||
data->enc_diff = scroller_->AddInput(now_ms, 0);
|
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;
|
break;
|
||||||
case drivers::NvsStorage::InputModes::kRotatingWheel:
|
case drivers::NvsStorage::InputModes::kRotatingWheel:
|
||||||
if (!raw_wheel_.GetTouchWheelData().is_wheel_touched) {
|
if (!raw_wheel_.GetTouchWheelData().is_wheel_touched) {
|
||||||
@@ -156,53 +215,97 @@ auto EncoderInput::Read(lv_indev_data_t* data) -> void {
|
|||||||
} else {
|
} else {
|
||||||
data->enc_diff = 0;
|
data->enc_diff = 0;
|
||||||
}
|
}
|
||||||
data->state = relative_wheel_->is_clicking() ? LV_INDEV_STATE_PRESSED
|
|
||||||
: LV_INDEV_STATE_RELEASED;
|
trigger =
|
||||||
// TODO: Long-press events.
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Apply inertia / acceleration.
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto EncoderInput::HandleKey(Keys key, uint64_t ms, bool clicked) -> void {
|
trigger = TriggerKey(Keys::kVolumeDown, KeyStyle::kLongPress, now_ms);
|
||||||
if (!clicked) {
|
switch (trigger) {
|
||||||
touch_time_ms_.erase(key);
|
case Trigger::kNone:
|
||||||
short_press_fired_.erase(key);
|
break;
|
||||||
long_press_fired_.erase(key);
|
case Trigger::kClick:
|
||||||
|
events::Audio().Dispatch(audio::StepDownVolume{});
|
||||||
|
break;
|
||||||
|
case Trigger::kLongPress:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if (!touch_time_ms_.contains(key)) {
|
|
||||||
touch_time_ms_[key] = ms;
|
// Key is not clicked.
|
||||||
|
if (touch_time_ms_.contains(key)) {
|
||||||
|
// Key was just released.
|
||||||
|
just_released_.insert(key);
|
||||||
|
touch_time_ms_.erase(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto EncoderInput::ShortPressTrigger(Keys key) -> bool {
|
auto EncoderInput::TriggerKey(Keys key, KeyStyle s, uint64_t ms) -> Trigger {
|
||||||
if (touch_time_ms_.contains(key) && !short_press_fired_.contains(key)) {
|
if (s == KeyStyle::kRepeat) {
|
||||||
short_press_fired_[key] = true;
|
bool may_repeat = fired_.contains(key) && touch_time_ms_.contains(key) &&
|
||||||
return true;
|
ms - touch_time_ms_[key] >= kRepeatDelayMs;
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto EncoderInput::ShortPressTriggerRepeating(Keys key, uint64_t ms) -> bool {
|
// 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) &&
|
if (touch_time_ms_.contains(key) &&
|
||||||
(!short_press_fired_.contains(key) ||
|
ms - touch_time_ms_[key] >= kLongPressDelayMs &&
|
||||||
ms - touch_time_ms_[key] >= kRepeatDelayMs)) {
|
!fired_.contains(key)) {
|
||||||
touch_time_ms_[key] = ms;
|
fired_.insert(key);
|
||||||
short_press_fired_[key] = true;
|
return Trigger::kLongPress;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto EncoderInput::LongPressTrigger(Keys key, uint64_t ms) -> bool {
|
return Trigger::kNone;
|
||||||
if (touch_time_ms_.contains(key) && !long_press_fired_.contains(key) &&
|
|
||||||
ms - touch_time_ms_[key] >= kLongPressDelayMs) {
|
|
||||||
long_press_fired_[key] = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Scroller::AddInput(uint64_t ms, int direction) -> int {
|
auto Scroller::AddInput(uint64_t ms, int direction) -> int {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include "core/lv_group.h"
|
#include "core/lv_group.h"
|
||||||
#include "gpios.hpp"
|
#include "gpios.hpp"
|
||||||
@@ -51,6 +52,7 @@ class EncoderInput {
|
|||||||
drivers::NvsStorage::InputModes mode_;
|
drivers::NvsStorage::InputModes mode_;
|
||||||
bool is_locked_;
|
bool is_locked_;
|
||||||
|
|
||||||
|
// Every kind of distinct input that we could map to an action.
|
||||||
enum class Keys {
|
enum class Keys {
|
||||||
kVolumeUp,
|
kVolumeUp,
|
||||||
kVolumeDown,
|
kVolumeDown,
|
||||||
@@ -62,14 +64,29 @@ class EncoderInput {
|
|||||||
kDirectionalLeft,
|
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, uint64_t> touch_time_ms_;
|
||||||
std::unordered_map<Keys, bool> short_press_fired_;
|
// Set of keys that were released during the current update.
|
||||||
std::unordered_map<Keys, bool> long_press_fired_;
|
std::set<Keys> just_released_;
|
||||||
|
// Set of keys that have had an event fired for them since being pressed.
|
||||||
|
std::set<Keys> fired_;
|
||||||
|
|
||||||
auto HandleKey(Keys key, uint64_t ms, bool clicked) -> void;
|
enum class Trigger {
|
||||||
auto ShortPressTrigger(Keys key) -> bool;
|
kNone,
|
||||||
auto ShortPressTriggerRepeating(Keys key, uint64_t ms) -> bool;
|
// Regular short-click. Triggered on release for long-pressable keys,
|
||||||
auto LongPressTrigger(Keys key, uint64_t ms) -> bool;
|
// triggered on the initial press for repeatable keys.
|
||||||
|
kClick,
|
||||||
|
kLongPress,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class KeyStyle {
|
||||||
|
kRepeat,
|
||||||
|
kLongPress,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto HandleKeyState(Keys key, uint64_t ms, bool clicked) -> void;
|
||||||
|
auto TriggerKey(Keys key, KeyStyle t, uint64_t ms) -> Trigger;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Scroller {
|
class Scroller {
|
||||||
|
|||||||
Reference in New Issue
Block a user