129 lines
4.0 KiB
C++
129 lines
4.0 KiB
C++
#include "ButtonEventManager.h"
|
|
|
|
#include "CrossPointSettings.h"
|
|
|
|
// Required for constexpr array out-of-class definition (C++14).
|
|
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::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;
|
|
}
|
|
|
|
void ButtonEventManager::pushEventFront(const Button button, const PressType type) {
|
|
const int prev = (eventHead - 1 + EVENT_BUF) % EVENT_BUF;
|
|
if (prev == eventTail) return; // buffer full
|
|
eventHead = prev;
|
|
eventBuf[eventHead] = {button, type};
|
|
}
|
|
|
|
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 && now - s.pressDownTime >= LONG_PRESS_MS) {
|
|
// Fire long press immediately once the threshold is reached.
|
|
pushEvent(btn, PressType::Long);
|
|
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]);
|
|
}
|
|
}
|