Add extended TiltModel
This commit is contained in:
@@ -137,6 +137,14 @@ class CrossPointSettings {
|
||||
// Image rendering in EPUB reader
|
||||
enum IMAGE_RENDERING { IMAGES_DISPLAY = 0, IMAGES_PLACEHOLDER = 1, IMAGES_SUPPRESS = 2, IMAGE_RENDERING_COUNT };
|
||||
|
||||
// Action mapped to each tilt gesture direction.
|
||||
enum TILT_GESTURE_ACTION {
|
||||
TILT_ACT_NONE = 0,
|
||||
TILT_ACT_NEXT_PAGE = 1,
|
||||
TILT_ACT_PREV_PAGE = 2,
|
||||
TILT_GESTURE_ACTION_COUNT
|
||||
};
|
||||
|
||||
// Text darkness for AA glyph rendering (forwarded to GfxRenderer::setTextDarkness)
|
||||
enum TEXT_DARKNESS {
|
||||
DARKNESS_NORMAL = 0, // true 4-level AA
|
||||
@@ -257,6 +265,14 @@ class CrossPointSettings {
|
||||
uint8_t imageRendering = IMAGES_DISPLAY;
|
||||
// Dithering mode for decoded images (EPUB/JPG/PNG)
|
||||
uint8_t imageDithering = IMAGE_DITHER_BAYER;
|
||||
// Tilt-based page turning (X3 only — requires QMI8658 IMU)
|
||||
uint8_t tiltPageTurn = 0;
|
||||
// Tilt input processing mode: 0 = raw accel, 1 = smoothed accel, 2 = gyro/accelerometer fusion
|
||||
uint8_t tiltStabilization = 0;
|
||||
// Action when the computed tilt value crosses the positive threshold.
|
||||
uint8_t tiltPositiveAction = TILT_ACT_NEXT_PAGE;
|
||||
// Action when the computed tilt value crosses the negative threshold.
|
||||
uint8_t tiltNegativeAction = TILT_ACT_PREV_PAGE;
|
||||
// Enable synthetic TOC fallback for malformed/sparse TOC books (1 = enabled, 0 = disabled)
|
||||
uint8_t syntheticTocFallback = 1;
|
||||
// Default bionic reading in EPUB pages when no per-book override is set (1 = enabled, 0 = disabled)
|
||||
|
||||
@@ -180,8 +180,9 @@ bool JsonSettingsIO::loadState(CrossPointState& s, const char* json) {
|
||||
|
||||
bool JsonSettingsIO::saveSettings(const CrossPointSettings& s, const char* path) {
|
||||
JsonDocument doc;
|
||||
const auto settings = getSettingsList();
|
||||
|
||||
for (const auto& info : getSettingsList()) {
|
||||
for (const auto& info : settings) {
|
||||
if (!info.key) continue;
|
||||
// Dynamic entries (KOReader etc.) are stored in their own files — skip.
|
||||
if (!info.valuePtr && !info.stringOffset) continue;
|
||||
@@ -258,8 +259,9 @@ bool JsonSettingsIO::loadSettings(CrossPointSettings& s, const char* json, bool*
|
||||
migrateMissingStatusSetting("statusBarItemsPosition", s.statusBarItemsPosition, "statusBarItemsPosition",
|
||||
CrossPointSettings::STATUS_BAR_ITEMS_BOTTOM,
|
||||
CrossPointSettings::STATUS_BAR_ITEMS_POSITION_COUNT);
|
||||
const auto settings = getSettingsList();
|
||||
|
||||
for (const auto& info : getSettingsList()) {
|
||||
for (const auto& info : settings) {
|
||||
if (!info.key) continue;
|
||||
// Dynamic entries (KOReader etc.) are stored in their own files — skip.
|
||||
if (!info.valuePtr && !info.stringOffset) continue;
|
||||
|
||||
+38
-1
@@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <HalGPIO.h>
|
||||
#include <I18n.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
@@ -25,6 +28,7 @@
|
||||
// works inside a submenu exactly as it does in the parent tab.
|
||||
// Add with .withSubmenu(StrId::STR_MY_SUBMENU).
|
||||
// key — JSON property name used by the web settings API (nullptr = device-only).
|
||||
// deviceTarget — hardware visibility (BOTH by default; override with .withDeviceTarget()).
|
||||
//
|
||||
// ACTION-type entries and entries without a key are device-only and are added directly
|
||||
// in SettingsActivity::onEnter(), not here.
|
||||
@@ -222,6 +226,24 @@ inline const std::vector<SettingInfo> list = {
|
||||
SettingInfo::Enum(StrId::STR_BTN_LONG_PRESS, &CrossPointSettings::btnLongPower, {StrId::STR_BTN_DEF_SLEEP},
|
||||
"btnLongPower", StrId::STR_CAT_CONTROLS)
|
||||
.withSubmenu(StrId::STR_BTN_POWER),
|
||||
SettingInfo::Toggle(StrId::STR_TILT_PAGE_TURN, &CrossPointSettings::tiltPageTurn, "tiltPageTurn",
|
||||
StrId::STR_CAT_CONTROLS)
|
||||
.withSubcategory(StrId::STR_TILT_PAGE_TURN)
|
||||
.withDeviceTarget(SettingDeviceTarget::X3),
|
||||
SettingInfo::Enum(StrId::STR_TILT_STABILIZATION, &CrossPointSettings::tiltStabilization,
|
||||
{StrId::STR_TILT_MODE_RAW, StrId::STR_TILT_MODE_SMOOTH, StrId::STR_TILT_MODE_KALMAN},
|
||||
"tiltStabilization", StrId::STR_CAT_CONTROLS)
|
||||
.withDeviceTarget(SettingDeviceTarget::X3),
|
||||
SettingInfo::Enum(StrId::STR_DIR_RIGHT, &CrossPointSettings::tiltPositiveAction,
|
||||
{StrId::STR_NONE_OPT, StrId::STR_NEXT_PAGE, StrId::STR_PREV_PAGE}, "tiltPositiveAction",
|
||||
StrId::STR_CAT_CONTROLS)
|
||||
.withSubcategory(StrId::STR_TILT_PAGE_TURN)
|
||||
.withDeviceTarget(SettingDeviceTarget::X3),
|
||||
SettingInfo::Enum(StrId::STR_DIR_LEFT, &CrossPointSettings::tiltNegativeAction,
|
||||
{StrId::STR_NONE_OPT, StrId::STR_NEXT_PAGE, StrId::STR_PREV_PAGE}, "tiltNegativeAction",
|
||||
StrId::STR_CAT_CONTROLS)
|
||||
.withSubcategory(StrId::STR_TILT_PAGE_TURN)
|
||||
.withDeviceTarget(SettingDeviceTarget::X3),
|
||||
|
||||
#undef BTN_ACT_OPTIONS
|
||||
|
||||
@@ -322,4 +344,19 @@ inline const std::vector<SettingInfo> list = {
|
||||
};
|
||||
} // namespace SettingsListDetail
|
||||
|
||||
inline const std::vector<SettingInfo>& getSettingsList() { return SettingsListDetail::list; }
|
||||
inline std::vector<SettingInfo> getSettingsList() {
|
||||
std::vector<SettingInfo> settings = SettingsListDetail::list;
|
||||
const bool isX3 = gpio.deviceIsX3();
|
||||
settings.erase(std::remove_if(settings.begin(), settings.end(),
|
||||
[isX3](const SettingInfo& setting) {
|
||||
if (setting.deviceTarget == SettingDeviceTarget::BOTH) {
|
||||
return false;
|
||||
}
|
||||
if (setting.deviceTarget == SettingDeviceTarget::X3) {
|
||||
return !isX3;
|
||||
}
|
||||
return isX3;
|
||||
}),
|
||||
settings.end());
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <CrossPointSettings.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <HalTiltSensor.h>
|
||||
#include <Logging.h>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -94,18 +95,36 @@ inline PageTurnResult detectPageTurn(const MappedInputManager& input) {
|
||||
// Also suppress immediate page-turns if a double-click action is configured for the button,
|
||||
// because the button event system delays short events until the double-click window expires.
|
||||
using BA = CrossPointSettings::BUTTON_ACTION;
|
||||
const bool prev = (SETTINGS.btnShortPageBack == BA::BTN_DEFAULT &&
|
||||
!globalButtonEvents().hasDoubleAction(MappedInputManager::Button::PageBack) &&
|
||||
input.wasReleased(MappedInputManager::Button::PageBack)) ||
|
||||
(SETTINGS.btnShortLeft == BA::BTN_DEFAULT &&
|
||||
!globalButtonEvents().hasDoubleAction(MappedInputManager::Button::Left) &&
|
||||
input.wasReleased(MappedInputManager::Button::Left));
|
||||
const bool next = (SETTINGS.btnShortPageForward == BA::BTN_DEFAULT &&
|
||||
!globalButtonEvents().hasDoubleAction(MappedInputManager::Button::PageForward) &&
|
||||
input.wasReleased(MappedInputManager::Button::PageForward)) ||
|
||||
(SETTINGS.btnShortRight == BA::BTN_DEFAULT &&
|
||||
!globalButtonEvents().hasDoubleAction(MappedInputManager::Button::Right) &&
|
||||
input.wasReleased(MappedInputManager::Button::Right));
|
||||
using TA = CrossPointSettings::TILT_GESTURE_ACTION;
|
||||
const bool tiltNegative = SETTINGS.tiltPageTurn && halTiltSensor.wasTiltedBack();
|
||||
const bool tiltPositive = SETTINGS.tiltPageTurn && halTiltSensor.wasTiltedForward();
|
||||
bool tiltPrev = false;
|
||||
bool tiltNext = false;
|
||||
auto applyTiltAction = [&](uint8_t action) {
|
||||
if (action == TA::TILT_ACT_NEXT_PAGE) {
|
||||
tiltNext = true;
|
||||
} else if (action == TA::TILT_ACT_PREV_PAGE) {
|
||||
tiltPrev = true;
|
||||
}
|
||||
};
|
||||
if (tiltPositive) {
|
||||
applyTiltAction(SETTINGS.tiltPositiveAction);
|
||||
}
|
||||
if (tiltNegative) {
|
||||
applyTiltAction(SETTINGS.tiltNegativeAction);
|
||||
}
|
||||
const bool prev = tiltPrev || ((SETTINGS.btnShortPageBack == BA::BTN_DEFAULT &&
|
||||
!globalButtonEvents().hasDoubleAction(MappedInputManager::Button::PageBack) &&
|
||||
input.wasReleased(MappedInputManager::Button::PageBack)) ||
|
||||
(SETTINGS.btnShortLeft == BA::BTN_DEFAULT &&
|
||||
!globalButtonEvents().hasDoubleAction(MappedInputManager::Button::Left) &&
|
||||
input.wasReleased(MappedInputManager::Button::Left)));
|
||||
const bool next = tiltNext || ((SETTINGS.btnShortPageForward == BA::BTN_DEFAULT &&
|
||||
!globalButtonEvents().hasDoubleAction(MappedInputManager::Button::PageForward) &&
|
||||
input.wasReleased(MappedInputManager::Button::PageForward)) ||
|
||||
(SETTINGS.btnShortRight == BA::BTN_DEFAULT &&
|
||||
!globalButtonEvents().hasDoubleAction(MappedInputManager::Button::Right) &&
|
||||
input.wasReleased(MappedInputManager::Button::Right)));
|
||||
return {prev, next};
|
||||
}
|
||||
|
||||
|
||||
@@ -196,6 +196,25 @@ void TxtReaderActivity::loop() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto [prevTriggered, nextTriggered] = ReaderUtils::detectPageTurn(mappedInput);
|
||||
if (!prevTriggered && !nextTriggered) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevTriggered) {
|
||||
if (currentPage > 0) {
|
||||
currentPage--;
|
||||
requestUpdate();
|
||||
}
|
||||
} else if (nextTriggered) {
|
||||
if (currentPage < totalPages - 1) {
|
||||
currentPage++;
|
||||
requestUpdate();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TxtReaderActivity::initializeReader() {
|
||||
|
||||
@@ -30,18 +30,13 @@ constexpr std::array<ButtonRow, 7> kButtonRows = {{
|
||||
}};
|
||||
|
||||
// Find the SettingInfo for a given (button submenu, press kind) pair in the shared settings list.
|
||||
const SettingInfo* findEntry(StrId submenu, StrId pressKind) {
|
||||
const auto& list = getSettingsList();
|
||||
std::string cellValue(StrId submenu, StrId pressKind) {
|
||||
const auto list = getSettingsList();
|
||||
auto it = std::find_if(list.begin(), list.end(), [submenu, pressKind](const SettingInfo& s) {
|
||||
return s.submenu == submenu && s.nameId == pressKind && s.category == StrId::STR_CAT_CONTROLS;
|
||||
});
|
||||
return it != list.end() ? &*it : nullptr;
|
||||
}
|
||||
|
||||
std::string cellValue(StrId submenu, StrId pressKind) {
|
||||
const SettingInfo* s = findEntry(submenu, pressKind);
|
||||
if (!s) return {};
|
||||
return s->getDisplayValue();
|
||||
if (it == list.end()) return {};
|
||||
return it->getDisplayValue();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
enum class SettingType { TOGGLE, ENUM, ACTION, VALUE, STRING };
|
||||
|
||||
enum class SettingDeviceTarget { BOTH, X3, X4 };
|
||||
|
||||
enum class SettingAction {
|
||||
None,
|
||||
RemapFrontButtons,
|
||||
@@ -56,6 +58,7 @@ struct SettingInfo {
|
||||
const char* key = nullptr; // JSON API key (nullptr for ACTION types)
|
||||
StrId category = StrId::STR_NONE_OPT; // Category for web UI grouping
|
||||
bool obfuscated = false; // Save/load via base64 obfuscation (passwords)
|
||||
SettingDeviceTarget deviceTarget = SettingDeviceTarget::BOTH;
|
||||
|
||||
// Direct char[] string fields (for settings stored in CrossPointSettings)
|
||||
size_t stringOffset = 0;
|
||||
@@ -234,6 +237,13 @@ struct SettingInfo {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Restrict visibility of this setting to a specific hardware target.
|
||||
// Default is BOTH when this method is not used.
|
||||
SettingInfo& withDeviceTarget(SettingDeviceTarget target) {
|
||||
deviceTarget = target;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// For internal use by SettingsActivity: placeholder entry that launches the submenu.
|
||||
static SettingInfo SubmenuEntry(StrId titleId) {
|
||||
SettingInfo s;
|
||||
|
||||
@@ -54,7 +54,7 @@ void SettingsActivity::onEnter() {
|
||||
}
|
||||
vec.push_back(s);
|
||||
};
|
||||
auto addToMoved = [](std::vector<SettingInfo>& vec, StrId& lastSub, SettingInfo&& s) {
|
||||
auto addToMoved = [](std::vector<SettingInfo>& vec, StrId& lastSub, SettingInfo s) {
|
||||
if (s.subcategory != StrId::STR_NONE_OPT && s.subcategory != lastSub) {
|
||||
vec.push_back(SettingInfo::Separator(s.subcategory));
|
||||
lastSub = s.subcategory;
|
||||
|
||||
+6
-1
@@ -9,6 +9,7 @@
|
||||
#include <HalPowerManager.h>
|
||||
#include <HalStorage.h>
|
||||
#include <HalSystem.h>
|
||||
#include <HalTiltSensor.h>
|
||||
#include <I18n.h>
|
||||
#include <Logging.h>
|
||||
#include <SPI.h>
|
||||
@@ -141,6 +142,7 @@ void enterDeepSleep() {
|
||||
APP_STATE.saveToFile();
|
||||
|
||||
activityManager.goToSleep();
|
||||
halTiltSensor.deepSleep();
|
||||
|
||||
display.deepSleep();
|
||||
LOG_DBG("MAIN", "Entering deep sleep (powerBtn isPressed=%d, rawPin=%d)", gpio.isPressed(HalGPIO::BTN_POWER),
|
||||
@@ -200,6 +202,7 @@ void setup() {
|
||||
HalSystem::begin();
|
||||
gpio.begin();
|
||||
powerManager.begin();
|
||||
halTiltSensor.begin();
|
||||
gpio_deep_sleep_hold_dis(); // Release deep sleep GPIO hold state from previous sleep cycle
|
||||
|
||||
const auto wakeupReason = gpio.getWakeupReason();
|
||||
@@ -331,6 +334,7 @@ void loop() {
|
||||
gpio.update();
|
||||
buttonEventManager.update();
|
||||
HalClock::updatePeriodic();
|
||||
halTiltSensor.update(SETTINGS.tiltPageTurn, SETTINGS.tiltStabilization, SETTINGS.orientation);
|
||||
|
||||
renderer.setFadingFix(SETTINGS.fadingFix);
|
||||
renderer.setTextDarkness(SETTINGS.textDarkness);
|
||||
@@ -362,7 +366,8 @@ void loop() {
|
||||
|
||||
// Check for any user activity (button press or release) or active background work
|
||||
static unsigned long lastActivityTime = millis();
|
||||
if (gpio.wasAnyPressed() || gpio.wasAnyReleased() || activityManager.preventAutoSleep()) {
|
||||
if (gpio.wasAnyPressed() || gpio.wasAnyReleased() || halTiltSensor.hadActivity() ||
|
||||
activityManager.preventAutoSleep()) {
|
||||
lastActivityTime = millis(); // Reset inactivity timer
|
||||
powerManager.setPowerSaving(false); // Restore normal CPU frequency on user activity
|
||||
}
|
||||
|
||||
@@ -1232,7 +1232,7 @@ void CrossPointWebServer::handleSettingsPage() const {
|
||||
}
|
||||
|
||||
void CrossPointWebServer::handleGetSettings() const {
|
||||
const auto& settings = getSettingsList();
|
||||
const auto settings = getSettingsList();
|
||||
|
||||
String result;
|
||||
result.reserve(4096);
|
||||
@@ -1351,7 +1351,7 @@ void CrossPointWebServer::handlePostSettings() {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& settings = getSettingsList();
|
||||
const auto settings = getSettingsList();
|
||||
int applied = 0;
|
||||
|
||||
for (const auto& s : settings) {
|
||||
|
||||
Reference in New Issue
Block a user