Files
crosspoint-reader-mod/src/MappedInputManager.cpp
Arthur Tazhitdinov c49a819939 feat: front button remapper (#664)
## Summary

* Custom remapper to create any variant of front button layout.

## Additional Context

* Included migration from previous frontlayout setting
* This will solve:
    *  https://github.com/crosspoint-reader/crosspoint-reader/issues/654
    * https://github.com/crosspoint-reader/crosspoint-reader/issues/652
    * https://github.com/crosspoint-reader/crosspoint-reader/issues/620
    * https://github.com/crosspoint-reader/crosspoint-reader/issues/468

<img width="860" height="1147" alt="image"
src="https://github.com/user-attachments/assets/457356ed-7a7d-4e1c-8683-e187a1df47c0"
/>



---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**< PARTIALLY >**_
2026-02-05 22:37:17 +11:00

109 lines
3.8 KiB
C++

#include "MappedInputManager.h"
#include "CrossPointSettings.h"
namespace {
using ButtonIndex = uint8_t;
struct SideLayoutMap {
ButtonIndex pageBack;
ButtonIndex pageForward;
};
// Order matches CrossPointSettings::SIDE_BUTTON_LAYOUT.
constexpr SideLayoutMap kSideLayouts[] = {
{HalGPIO::BTN_UP, HalGPIO::BTN_DOWN},
{HalGPIO::BTN_DOWN, HalGPIO::BTN_UP},
};
} // namespace
bool MappedInputManager::mapButton(const Button button, bool (HalGPIO::*fn)(uint8_t) const) const {
const auto sideLayout = static_cast<CrossPointSettings::SIDE_BUTTON_LAYOUT>(SETTINGS.sideButtonLayout);
const auto& side = kSideLayouts[sideLayout];
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.
return (gpio.*fn)(side.pageBack);
case Button::PageForward:
// Reader page navigation uses side buttons and can be swapped via settings.
return (gpio.*fn)(side.pageForward);
}
return false;
}
bool MappedInputManager::wasPressed(const Button button) const { return mapButton(button, &HalGPIO::wasPressed); }
bool MappedInputManager::wasReleased(const Button button) const { 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 {
// 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 previous;
}
if (hw == SETTINGS.frontButtonRight) {
return next;
}
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;
}