Files
Crosspoint/src/MappedInputManager.cpp
T
Justin Mitchell c308520f9c Add touch tap gesture for back navigation
Implements a reusable touch tap gesture in the top-left corner that maps to the Back button. The gesture is automatically integrated into wasPressed/wasReleased for Back button, making it available across all screens without per-activity code. Also fixes low-power CPU frequency floor to 80 MHz on PSRAM boards to prevent SD write and PSRAM instability caused by APB clock dropping below safe threshold.
2026-06-08 14:01:30 -04:00

137 lines
5.2 KiB
C++

#include "MappedInputManager.h"
#include "CrossPointSettings.h"
bool MappedInputManager::mapButton(const Button button, bool (HalGPIO::*fn)(uint8_t) const) const {
const auto sideLayout = SETTINGS.sideButtonLayout;
switch (button) {
case Button::Back:
// Logical Back maps to user-configured front button.
return (gpio.*fn)(SETTINGS.frontButtonBack);
case Button::Confirm:
// Logical Confirm maps to user-configured front button.
return (gpio.*fn)(SETTINGS.frontButtonConfirm);
case Button::Left:
// Logical Left maps to user-configured front button.
return (gpio.*fn)(SETTINGS.frontButtonLeft);
case Button::Right:
// Logical Right maps to user-configured front button.
return (gpio.*fn)(SETTINGS.frontButtonRight);
case Button::Up:
// Side buttons remain fixed for Up/Down.
return (gpio.*fn)(HalGPIO::BTN_UP);
case Button::Down:
// Side buttons remain fixed for Up/Down.
return (gpio.*fn)(HalGPIO::BTN_DOWN);
case Button::Power:
// Power button bypasses remapping.
return (gpio.*fn)(HalGPIO::BTN_POWER);
case Button::PageBack:
// Reader page navigation uses side buttons and can be swapped via settings.
switch (sideLayout) {
case CrossPointSettings::PREV_NEXT:
return (gpio.*fn)(HalGPIO::BTN_UP);
case CrossPointSettings::NEXT_PREV:
return (gpio.*fn)(HalGPIO::BTN_DOWN);
case CrossPointSettings::SIDE_BUTTONS_DISABLED:
default:
return false;
}
case Button::PageForward:
// Reader page navigation uses side buttons and can be swapped via settings.
switch (sideLayout) {
case CrossPointSettings::PREV_NEXT:
return (gpio.*fn)(HalGPIO::BTN_DOWN);
case CrossPointSettings::NEXT_PREV:
return (gpio.*fn)(HalGPIO::BTN_UP);
case CrossPointSettings::SIDE_BUTTONS_DISABLED:
default:
return false;
}
}
return false;
}
// Top-left corner of the panel (panel-native, normalized). Generous so it's easy
// to hit; v1 is not yet orientation-mapped (see wasBackGesture NOTE in header).
static constexpr float BACK_GESTURE_FRAC_X = 0.22f;
static constexpr float BACK_GESTURE_FRAC_Y = 0.12f;
bool MappedInputManager::wasBackGesture() const {
float nx = 0.0f, ny = 0.0f;
if (!gpio.wasTouchTap(nx, ny)) return false;
return nx <= BACK_GESTURE_FRAC_X && ny <= BACK_GESTURE_FRAC_Y;
}
bool MappedInputManager::wasPressed(const Button button) const {
// A top-left tap fires on the release frame; expose it on Back's press edge too
// so menus that act on wasPressed(Back) also respond. Deliberately NOT folded
// into isPressed, so a quick tap never satisfies the readers' long-press-home.
if (button == Button::Back && wasBackGesture()) return true;
return mapButton(button, &HalGPIO::wasPressed);
}
bool MappedInputManager::wasReleased(const Button button) const {
if (button == Button::Back && wasBackGesture()) return true;
return mapButton(button, &HalGPIO::wasReleased);
}
bool MappedInputManager::isPressed(const Button button) const { return mapButton(button, &HalGPIO::isPressed); }
bool MappedInputManager::wasAnyPressed() const { return gpio.wasAnyPressed(); }
bool MappedInputManager::wasAnyReleased() const { return gpio.wasAnyReleased(); }
unsigned long MappedInputManager::getHeldTime() const { return gpio.getHeldTime(); }
MappedInputManager::Labels MappedInputManager::mapLabels(const char* back, const char* confirm, const char* previous,
const char* next) const {
// Swap previous/next labels to match the page turn direction swap in INVERTED and LANDSCAPE_CCW.
const bool swapLabels =
SETTINGS.frontButtonFollowOrientation && (SETTINGS.orientation == CrossPointSettings::INVERTED ||
SETTINGS.orientation == CrossPointSettings::LANDSCAPE_CCW);
const char* leftLabel = swapLabels ? next : previous;
const char* rightLabel = swapLabels ? previous : next;
// Build the label order based on the configured hardware mapping.
auto labelForHardware = [&](uint8_t hw) -> const char* {
// Compare against configured logical roles and return the matching label.
if (hw == SETTINGS.frontButtonBack) {
return back;
}
if (hw == SETTINGS.frontButtonConfirm) {
return confirm;
}
if (hw == SETTINGS.frontButtonLeft) {
return leftLabel;
}
if (hw == SETTINGS.frontButtonRight) {
return rightLabel;
}
return "";
};
return {labelForHardware(HalGPIO::BTN_BACK), labelForHardware(HalGPIO::BTN_CONFIRM),
labelForHardware(HalGPIO::BTN_LEFT), labelForHardware(HalGPIO::BTN_RIGHT)};
}
int MappedInputManager::getPressedFrontButton() const {
// Scan the raw front buttons in hardware order.
// This bypasses remapping so the remap activity can capture physical presses.
if (gpio.wasPressed(HalGPIO::BTN_BACK)) {
return HalGPIO::BTN_BACK;
}
if (gpio.wasPressed(HalGPIO::BTN_CONFIRM)) {
return HalGPIO::BTN_CONFIRM;
}
if (gpio.wasPressed(HalGPIO::BTN_LEFT)) {
return HalGPIO::BTN_LEFT;
}
if (gpio.wasPressed(HalGPIO::BTN_RIGHT)) {
return HalGPIO::BTN_RIGHT;
}
return -1;
}