Introduce extended button handler

This commit is contained in:
jpirnay
2026-04-26 12:26:29 +02:00
parent 499702a754
commit a1d0ddec49
19 changed files with 833 additions and 33 deletions
+121
View File
@@ -0,0 +1,121 @@
#include "ButtonEventManager.h"
#include "CrossPointSettings.h"
// Required for constexpr array definition in .cpp
constexpr ButtonEventManager::Button ButtonEventManager::ALL_BUTTONS[ButtonEventManager::NUM_BUTTONS];
bool ButtonEventManager::hasDoubleAction(const Button button) {
using BA = CrossPointSettings::BUTTON_ACTION;
switch (button) {
case Button::Back:
return SETTINGS.btnDoubleBack != BA::BTN_DEFAULT;
case Button::Confirm:
return SETTINGS.btnDoubleConfirm != BA::BTN_DEFAULT;
case Button::Left:
return SETTINGS.btnDoubleLeft != BA::BTN_DEFAULT;
case Button::Right:
return SETTINGS.btnDoubleRight != BA::BTN_DEFAULT;
case Button::Up:
return SETTINGS.btnDoubleUp != BA::BTN_DEFAULT;
case Button::Down:
return SETTINGS.btnDoubleDown != BA::BTN_DEFAULT;
case Button::PageBack:
return SETTINGS.btnDoublePageBack != BA::BTN_DEFAULT;
case Button::PageForward:
return SETTINGS.btnDoublePageForward != BA::BTN_DEFAULT;
case Button::Power:
return SETTINGS.btnDoublePower != BA::BTN_DEFAULT;
}
return false;
}
void ButtonEventManager::pushEvent(const Button button, const PressType type) {
const int next = (eventTail + 1) % EVENT_BUF;
if (next == eventHead) return; // buffer full, drop oldest not possible — just drop newest
eventBuf[eventTail] = {button, type};
eventTail = next;
}
bool ButtonEventManager::consumeEvent(ButtonEvent& out) {
if (eventHead == eventTail) return false;
out = eventBuf[eventHead];
eventHead = (eventHead + 1) % EVENT_BUF;
return true;
}
void ButtonEventManager::drain() {
for (auto& b : buttons) {
b.state = State::Idle;
b.pressDownTime = 0;
b.releaseTime = 0;
}
eventHead = eventTail = 0;
}
void ButtonEventManager::processButton(const int idx, const Button btn) {
PerButton& s = buttons[idx];
const unsigned long now = millis();
const bool pressed = input.wasPressed(btn);
const bool released = input.wasReleased(btn);
const bool held = input.isPressed(btn);
switch (s.state) {
case State::Idle:
if (pressed) {
s.state = State::Pressed;
s.pressDownTime = now;
}
break;
case State::Pressed:
if (released) {
const unsigned long heldMs = now - s.pressDownTime;
if (heldMs >= LONG_PRESS_MS) {
pushEvent(btn, PressType::Long);
s.state = State::Idle;
} else if (hasDoubleAction(btn)) {
// Delay short-press decision until double-click window expires
s.releaseTime = now;
s.state = State::ReleasedOnce;
} else {
// No double action configured — fire immediately
pushEvent(btn, PressType::Short);
s.state = State::Idle;
}
} else if (!held) {
// Button disappeared without wasReleased edge (e.g. after drain) — reset
s.state = State::Idle;
}
break;
case State::ReleasedOnce:
if (pressed) {
// Second press within window — start tracking it
s.state = State::DoublePressed;
s.pressDownTime = now;
} else if (now - s.releaseTime >= DOUBLE_WINDOW_MS) {
// Window expired without a second press — it was a short press
pushEvent(btn, PressType::Short);
s.state = State::Idle;
}
break;
case State::DoublePressed:
if (released) {
pushEvent(btn, PressType::Double);
s.state = State::Idle;
} else if (!held) {
// Disappeared without edge — treat as double anyway
pushEvent(btn, PressType::Double);
s.state = State::Idle;
}
break;
}
}
void ButtonEventManager::update() {
for (int i = 0; i < NUM_BUTTONS; i++) {
processButton(i, ALL_BUTTONS[i]);
}
}