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 >**_
This commit is contained in:
committed by
GitHub
parent
bf87a7dc60
commit
c49a819939
@@ -22,8 +22,59 @@ void readAndValidate(FsFile& file, uint8_t& member, const uint8_t maxValue) {
|
||||
namespace {
|
||||
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
||||
// Increment this when adding new persisted settings fields
|
||||
constexpr uint8_t SETTINGS_COUNT = 24;
|
||||
constexpr uint8_t SETTINGS_COUNT = 28;
|
||||
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
||||
|
||||
// Validate front button mapping to ensure each hardware button is unique.
|
||||
// If duplicates are detected, reset to the default physical order to prevent invalid mappings.
|
||||
void validateFrontButtonMapping(CrossPointSettings& settings) {
|
||||
// Snapshot the logical->hardware mapping so we can compare for duplicates.
|
||||
const uint8_t mapping[] = {settings.frontButtonBack, settings.frontButtonConfirm, settings.frontButtonLeft,
|
||||
settings.frontButtonRight};
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
for (size_t j = i + 1; j < 4; j++) {
|
||||
if (mapping[i] == mapping[j]) {
|
||||
// Duplicate detected: restore the default physical order (Back, Confirm, Left, Right).
|
||||
settings.frontButtonBack = CrossPointSettings::FRONT_HW_BACK;
|
||||
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_CONFIRM;
|
||||
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_LEFT;
|
||||
settings.frontButtonRight = CrossPointSettings::FRONT_HW_RIGHT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert legacy front button layout into explicit logical->hardware mapping.
|
||||
void applyLegacyFrontButtonLayout(CrossPointSettings& settings) {
|
||||
switch (static_cast<CrossPointSettings::FRONT_BUTTON_LAYOUT>(settings.frontButtonLayout)) {
|
||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||
settings.frontButtonBack = CrossPointSettings::FRONT_HW_LEFT;
|
||||
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_RIGHT;
|
||||
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_BACK;
|
||||
settings.frontButtonRight = CrossPointSettings::FRONT_HW_CONFIRM;
|
||||
break;
|
||||
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||
settings.frontButtonBack = CrossPointSettings::FRONT_HW_CONFIRM;
|
||||
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_LEFT;
|
||||
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_BACK;
|
||||
settings.frontButtonRight = CrossPointSettings::FRONT_HW_RIGHT;
|
||||
break;
|
||||
case CrossPointSettings::BACK_CONFIRM_RIGHT_LEFT:
|
||||
settings.frontButtonBack = CrossPointSettings::FRONT_HW_BACK;
|
||||
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_CONFIRM;
|
||||
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_RIGHT;
|
||||
settings.frontButtonRight = CrossPointSettings::FRONT_HW_LEFT;
|
||||
break;
|
||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||
default:
|
||||
settings.frontButtonBack = CrossPointSettings::FRONT_HW_BACK;
|
||||
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_CONFIRM;
|
||||
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_LEFT;
|
||||
settings.frontButtonRight = CrossPointSettings::FRONT_HW_RIGHT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool CrossPointSettings::saveToFile() const {
|
||||
@@ -42,7 +93,7 @@ bool CrossPointSettings::saveToFile() const {
|
||||
serialization::writePod(outputFile, shortPwrBtn);
|
||||
serialization::writePod(outputFile, statusBar);
|
||||
serialization::writePod(outputFile, orientation);
|
||||
serialization::writePod(outputFile, frontButtonLayout);
|
||||
serialization::writePod(outputFile, frontButtonLayout); // legacy
|
||||
serialization::writePod(outputFile, sideButtonLayout);
|
||||
serialization::writePod(outputFile, fontFamily);
|
||||
serialization::writePod(outputFile, fontSize);
|
||||
@@ -60,8 +111,12 @@ bool CrossPointSettings::saveToFile() const {
|
||||
serialization::writeString(outputFile, std::string(opdsUsername));
|
||||
serialization::writeString(outputFile, std::string(opdsPassword));
|
||||
serialization::writePod(outputFile, sleepScreenCoverFilter);
|
||||
// New fields added at end for backward compatibility
|
||||
serialization::writePod(outputFile, uiTheme);
|
||||
serialization::writePod(outputFile, frontButtonBack);
|
||||
serialization::writePod(outputFile, frontButtonConfirm);
|
||||
serialization::writePod(outputFile, frontButtonLeft);
|
||||
serialization::writePod(outputFile, frontButtonRight);
|
||||
// New fields added at end for backward compatibility
|
||||
outputFile.close();
|
||||
|
||||
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
||||
@@ -87,6 +142,8 @@ bool CrossPointSettings::loadFromFile() {
|
||||
|
||||
// load settings that exist (support older files with fewer fields)
|
||||
uint8_t settingsRead = 0;
|
||||
// Track whether remap fields were present in the settings file.
|
||||
bool frontButtonMappingRead = false;
|
||||
do {
|
||||
readAndValidate(inputFile, sleepScreen, SLEEP_SCREEN_MODE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
@@ -98,7 +155,7 @@ bool CrossPointSettings::loadFromFile() {
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, orientation, ORIENTATION_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, frontButtonLayout, FRONT_BUTTON_LAYOUT_COUNT);
|
||||
readAndValidate(inputFile, frontButtonLayout, FRONT_BUTTON_LAYOUT_COUNT); // legacy
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, sideButtonLayout, SIDE_BUTTON_LAYOUT_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
@@ -149,11 +206,25 @@ bool CrossPointSettings::loadFromFile() {
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, sleepScreenCoverFilter, SLEEP_SCREEN_COVER_FILTER_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
// New fields added at end for backward compatibility
|
||||
serialization::readPod(inputFile, uiTheme);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, frontButtonBack, FRONT_BUTTON_HARDWARE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, frontButtonConfirm, FRONT_BUTTON_HARDWARE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, frontButtonLeft, FRONT_BUTTON_HARDWARE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, frontButtonRight, FRONT_BUTTON_HARDWARE_COUNT);
|
||||
frontButtonMappingRead = true;
|
||||
// New fields added at end for backward compatibility
|
||||
} while (false);
|
||||
|
||||
if (frontButtonMappingRead) {
|
||||
validateFrontButtonMapping(*this);
|
||||
} else {
|
||||
applyLegacyFrontButtonLayout(*this);
|
||||
}
|
||||
|
||||
inputFile.close();
|
||||
Serial.printf("[%lu] [CPS] Settings loaded from file\n", millis());
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user