feat: X3 gyroscope-based tilt page turning via QMI8658 IMU (#1636)

Co-authored-by: justinian <juicecutlus@gmail.com>
Co-authored-by: Vincent Politzer <vincent@skipwithjoy.com>
This commit is contained in:
Justinian
2026-04-29 15:29:33 -04:00
committed by GitHub
co-authored by justinian Vincent Politzer
parent bc9651b664
commit 5d2e5596b4
31 changed files with 524 additions and 134 deletions
+1
View File
@@ -290,3 +290,4 @@ STR_NO_FOOTNOTES: "На гэтай старонцы няма зносак"
STR_LINK: "[спасылка]"
STR_AUTO_TURN_ENABLED: "Аўтаперагортванне: "
STR_AUTO_TURN_PAGES_PER_MIN: "Аўтаперагортванне (старонак за хвіліну)"
STR_TILT_PAGE_TURN: "Перагортванне нахілам"
+1
View File
@@ -290,3 +290,4 @@ STR_LINK: "[enllaç]"
STR_SCREENSHOT_BUTTON: "Fes una captura de pantalla"
STR_AUTO_TURN_ENABLED: "Passar automàtic activat: "
STR_AUTO_TURN_PAGES_PER_MIN: "Passar automàtic (pàgines per minut)"
STR_TILT_PAGE_TURN: "Pas de pàgina per inclinació"
+1
View File
@@ -262,3 +262,4 @@ STR_BOOK_S_STYLE: "Styl knihy"
STR_EMBEDDED_STYLE: "Vložený styl"
STR_OPDS_SERVER_URL: "URL serveru OPDS"
STR_SCREENSHOT_BUTTON: "Udělat snímek obrazovky"
STR_TILT_PAGE_TURN: "Otáčení stránek nakloněním"
+1
View File
@@ -290,3 +290,4 @@ STR_LINK: "[link]"
STR_SCREENSHOT_BUTTON: "Tag skærmbillede"
STR_AUTO_TURN_ENABLED: "Automatisk sidevendning aktiveret: "
STR_AUTO_TURN_PAGES_PER_MIN: "Automatisk vending (sider per minut)"
STR_TILT_PAGE_TURN: "Vip for at vende side"
+2 -1
View File
@@ -289,4 +289,5 @@ STR_NO_FOOTNOTES: "Geen voetnoten op deze pagina"
STR_LINK: "[link]"
STR_SCREENSHOT_BUTTON: "Screenshot maken"
STR_AUTO_TURN_ENABLED: "Automatisch omslaan ingeschakeld: "
STR_AUTO_TURN_PAGES_PER_MIN: "Autom. omslaan (pagina's per minuut)"
STR_AUTO_TURN_PAGES_PER_MIN: "Autom. omslaan (pagina's per minuut)"
STR_TILT_PAGE_TURN: "Kantel om te bladeren"
+1
View File
@@ -305,6 +305,7 @@ STR_CRASH_TITLE: "System Crash"
STR_CRASH_DESCRIPTION: "A detailed report was saved to crash_report.txt. Please include this file in your bug report."
STR_CRASH_REASON: "Crash reason:"
STR_CRASH_NO_REASON: "(No reason was recorded)"
STR_TILT_PAGE_TURN: "Tilt Page Turn"
STR_KB_HINT_MOVE_CURSOR: "Press LEFT or RIGHT to move cursor"
STR_KB_HINT_RETURN_CURSOR: "Press LEFT to return to cursor position"
STR_KB_HINT_HIDE_PASSWORD: "Hold RIGHT then press [***] to hide password"
+1
View File
@@ -260,3 +260,4 @@ STR_BOOK_S_STYLE: "Kirjan tyyli"
STR_EMBEDDED_STYLE: "Upotettu tyyli"
STR_OPDS_SERVER_URL: "OPDS-palvelimen osoite"
STR_SCREENSHOT_BUTTON: "Ota kuvakaappaus"
STR_TILT_PAGE_TURN: "Sivunkääntö kallistamalla"
+1
View File
@@ -291,3 +291,4 @@ STR_LINK: "[lien]"
STR_SCREENSHOT_BUTTON: "Capture d'écran"
STR_AUTO_TURN_ENABLED: "Tourne-page auto : "
STR_AUTO_TURN_PAGES_PER_MIN: "Tourne-page auto (pages par minute)"
STR_TILT_PAGE_TURN: "Tourner par inclinaison"
+1
View File
@@ -321,3 +321,4 @@ STR_UPTIME: "Laufzeit"
STR_CHARGING: "Lädt"
STR_GATHERING_DATA: "Daten werden gesammelt..."
STR_READING: "Lese..."
STR_TILT_PAGE_TURN: "Neigung zum Blättern"
+1
View File
@@ -290,3 +290,4 @@ STR_LINK: "[link]"
STR_SCREENSHOT_BUTTON: "Képernyőkép készítése"
STR_AUTO_TURN_ENABLED: "Automatikus lapozás bekapcsolva: "
STR_AUTO_TURN_PAGES_PER_MIN: "Automatikus lapozás (oldal/perc)"
STR_TILT_PAGE_TURN: "Döntéses lapozás"
+1
View File
@@ -290,3 +290,4 @@ STR_LINK: "[link]"
STR_SCREENSHOT_BUTTON: "Screenshot"
STR_AUTO_TURN_ENABLED: "Cambio pagina automatico: "
STR_AUTO_TURN_PAGES_PER_MIN: "Cambio pagina automatico (pag/min)"
STR_TILT_PAGE_TURN: "Gira pagina con inclinazione"
+1
View File
@@ -289,3 +289,4 @@ STR_LINK: "[сілтеме]"
STR_SCREENSHOT_BUTTON: "Скриншот түсіру"
STR_AUTO_TURN_ENABLED: "Автоматты бет аудару қосулы: "
STR_AUTO_TURN_PAGES_PER_MIN: "Автоматты бет аудару (минутына бет саны)"
STR_TILT_PAGE_TURN: "Еңкейту арқылы бет аудару"
+1
View File
@@ -290,3 +290,4 @@ STR_LINK: "[nuoroda]"
STR_SCREENSHOT_BUTTON: "Ekrano nuotrauka"
STR_AUTO_TURN_ENABLED: "Auto-vertimas: "
STR_AUTO_TURN_PAGES_PER_MIN: "Auto-vertimas (psl/min)"
STR_TILT_PAGE_TURN: "Puslapio vertimas pakreipiant"
+1
View File
@@ -290,3 +290,4 @@ STR_LINK: "[link]"
STR_SCREENSHOT_BUTTON: "Zrób zrzut ekranu"
STR_AUTO_TURN_ENABLED: "Auto-kartkowanie: "
STR_AUTO_TURN_PAGES_PER_MIN: "Auto-kartkowanie (str./min)"
STR_TILT_PAGE_TURN: "Kartkowanie przechyłem"
+1
View File
@@ -262,3 +262,4 @@ STR_BOOK_S_STYLE: "Estilo do livro"
STR_EMBEDDED_STYLE: "Estilo embutido"
STR_OPDS_SERVER_URL: "URL do servidor OPDS"
STR_SCREENSHOT_BUTTON: "Capturar tela"
STR_TILT_PAGE_TURN: "Virar página por inclinação"
+1
View File
@@ -290,3 +290,4 @@ STR_LINK: "[link]"
STR_SCREENSHOT_BUTTON: "Captură ecran"
STR_AUTO_TURN_ENABLED: "Răsfoire automată: "
STR_AUTO_TURN_PAGES_PER_MIN: "Pagini pe minut"
STR_TILT_PAGE_TURN: "Întoarcere pagină prin înclinare"
+1
View File
@@ -291,6 +291,7 @@ STR_OPDS_SERVER_URL: "URL OPDS сервера"
STR_SCREENSHOT_BUTTON: "Сделать снимок экрана"
STR_AUTO_TURN_ENABLED: "Автоперелистывание: "
STR_AUTO_TURN_PAGES_PER_MIN: "Автоперелистывание (стр./мин)"
STR_TILT_PAGE_TURN: "Перелистывание наклоном"
STR_ADD_SERVER: "Добавить сервер"
STR_SERVER_NAME: "Имя сервера"
STR_NO_SERVERS: "Нет настроенных серверов OPDS"
+1
View File
@@ -290,3 +290,4 @@ STR_LINK: "[povezava]"
STR_SCREENSHOT_BUTTON: "Naredi posnetek zaslona"
STR_AUTO_TURN_ENABLED: "Samodejno obračanje: "
STR_AUTO_TURN_PAGES_PER_MIN: "Samodejno obračanje (strani na minuto)"
STR_TILT_PAGE_TURN: "Obračanje s priklonom"
+1
View File
@@ -289,5 +289,6 @@ STR_FOOTNOTES: "Pie de página"
STR_NO_FOOTNOTES: "No hay notas al pie de esta página"
STR_LINK: "[enlace]"
STR_SCREENSHOT_BUTTON: "Tomar captura de pantalla"
STR_TILT_PAGE_TURN: "Pasar página inclinando"
STR_AUTO_TURN_ENABLED: "Avance activado: "
STR_AUTO_TURN_PAGES_PER_MIN: "Avance auto. (pág./min)"
+1
View File
@@ -305,6 +305,7 @@ STR_CRASH_TITLE: "Systemkrasch"
STR_CRASH_DESCRIPTION: "En detaljerad rapport sparades till crash_report.txt. Vänligen inkludera den här filen i din felrapport."
STR_CRASH_REASON: "Orsak till kraschen:"
STR_CRASH_NO_REASON: "(Ingen orsak registrerades)"
STR_TILT_PAGE_TURN: "Luta för att vända sida"
STR_KB_HINT_MOVE_CURSOR: "Tryck VÄNSTER eller HÖGER för att flytta markören"
STR_KB_HINT_RETURN_CURSOR: "Tryck VÄNSTER för att återgå till markörpositionen"
STR_KB_HINT_HIDE_PASSWORD: "Håll HÖGER och tryck sedan på [***] för att dölja lösenordet"
+1
View File
@@ -290,3 +290,4 @@ STR_SCREENSHOT_BUTTON: "Ekran görüntüsü al"
STR_SELECTED: "Seçili"
STR_SHOW: "Göster"
STR_TITLE: "Başlık"
STR_TILT_PAGE_TURN: "Eğerek sayfa çevirme"
+1
View File
@@ -291,3 +291,4 @@ STR_LINK: "[посилання]"
STR_SCREENSHOT_BUTTON: "Знімок екрана"
STR_AUTO_TURN_ENABLED: "Автоперегортання увімк: "
STR_AUTO_TURN_PAGES_PER_MIN: "Автоперегортання (ст/хв)"
STR_TILT_PAGE_TURN: "Перегортання нахилом"
+234
View File
@@ -0,0 +1,234 @@
#include "HalTiltSensor.h"
#include <Logging.h>
HalTiltSensor halTiltSensor; // Singleton instance
bool HalTiltSensor::writeReg(uint8_t reg, uint8_t val) const {
Wire.beginTransmission(_i2cAddr);
Wire.write(reg);
Wire.write(val);
return Wire.endTransmission() == 0;
}
bool HalTiltSensor::readReg(uint8_t reg, uint8_t* val) const {
Wire.beginTransmission(_i2cAddr);
Wire.write(reg);
if (Wire.endTransmission(false) != 0) {
return false;
}
Wire.requestFrom(_i2cAddr, (uint8_t)1);
if (Wire.available() < 1) {
return false;
}
*val = Wire.read();
return true;
}
bool HalTiltSensor::readGyro(float& gx, float& gy, float& gz) const {
Wire.beginTransmission(_i2cAddr);
Wire.write(REG_GX_L); // Start reading at Gyro X Low
if (Wire.endTransmission(false) != 0) {
return false;
}
Wire.requestFrom(_i2cAddr, (uint8_t)6);
if (Wire.available() < 6) {
return false;
}
auto readInt16 = [&]() -> int16_t {
const uint8_t lo = Wire.read();
const uint8_t hi = Wire.read();
return static_cast<int16_t>((hi << 8) | lo);
};
// If Full Scale is ±512 dps, the scale factor is 32768 / 512 = 64 LSB/dps
constexpr float SCALE = 1.0f / 64.0f;
gx = readInt16() * SCALE;
gy = readInt16() * SCALE;
gz = readInt16() * SCALE;
return true;
}
void HalTiltSensor::begin() {
if (!gpio.deviceIsX3()) {
_available = false;
return;
}
// Try primary address, then alternate
uint8_t whoami = 0;
_i2cAddr = I2C_ADDR_QMI8658;
if (!readReg(QMI8658_WHO_AM_I_REG, &whoami) || whoami != QMI8658_WHO_AM_I_VALUE) {
_i2cAddr = I2C_ADDR_QMI8658_ALT;
if (!readReg(QMI8658_WHO_AM_I_REG, &whoami) || whoami != QMI8658_WHO_AM_I_VALUE) {
LOG_ERR("GYR", "QMI8658 IMU not found");
_available = false;
return;
}
}
LOG_INF("GYR", "QMI8658 IMU found at 0x%02X", _i2cAddr);
if (!writeReg(REG_CTRL7, CTRL7_DISABLE_ALL) || !writeReg(REG_CTRL3, CTRL3_FS_512DPS | CTRL3_ODR_28HZ) ||
!writeReg(REG_CTRL1, CTRL1_BASE | CTRL1_SENSOR_DISABLE)) {
LOG_ERR("GYR", "QMI8658 register configuration failed");
_available = false;
return;
}
_available = true;
_initMs = millis();
_lastPollMs = millis();
LOG_INF("GYR", "QMI8658 gyro initialized and put to sleep");
}
bool HalTiltSensor::wake() {
if (!_available) {
return false;
}
// Wait for init to complete before waking
if ((millis() - _initMs) < SLEEP_STABILIZE_MS) {
return false;
}
if (writeReg(REG_CTRL1, CTRL1_BASE) && writeReg(REG_CTRL7, CTRL7_GYRO_ENABLE)) {
_lastPollMs = millis();
_lastTiltMs = millis();
_wakeMs = millis();
LOG_INF("GYR", "QMI8658 woke up");
return true;
} else {
LOG_ERR("GYR", "Failed to wake QMI8658");
return false;
}
}
bool HalTiltSensor::deepSleep() {
if (!_available) {
return false;
}
if ((millis() - _wakeMs) < SLEEP_STABILIZE_MS) {
return false;
}
if (writeReg(REG_CTRL7, CTRL7_DISABLE_ALL) && writeReg(REG_CTRL1, CTRL1_BASE | CTRL1_SENSOR_DISABLE)) {
// Clear any residual state so it doesn't immediately trigger upon waking
clearPendingEvents();
_inTilt = false;
LOG_INF("GYR", "QMI8658 entered sleep mode");
return true;
} else {
LOG_ERR("GYR", "Failed to put QMI8658 to sleep");
return false;
}
}
void HalTiltSensor::update(const uint8_t mode, const uint8_t orientation, const bool inReader) {
if (!_available) {
return;
}
// State machine: wake up or sleep based on the enabled flag
if ((mode != CrossPointTiltPageTurn::TILT_OFF) && !_isAwake) {
_isAwake = wake();
return;
} else if ((mode == CrossPointTiltPageTurn::TILT_OFF) && _isAwake) {
_isAwake = !deepSleep();
return;
}
// If disabled, skip the rest of the polling logic and avoid unnecessary I2C traffic in non-reader activities
if ((mode == CrossPointTiltPageTurn::TILT_OFF) || !inReader) {
return;
}
const unsigned long now = millis();
// Stabilization: discard readings during gyro startup transient
if ((now - _wakeMs) < WAKE_STABILIZE_MS) {
return;
}
if ((now - _lastPollMs) < POLL_INTERVAL_MS) {
return;
}
_lastPollMs = now;
float gx, gy, gz;
if (!readGyro(gx, gy, gz)) {
return;
}
// Map the gyro axis to left/right tilt based on reader orientation.
// On the X3 PCB: X axis = left/right in portrait, Y axis = left/right in landscape.
float tiltAxis;
switch (orientation) {
case CrossPointOrientation::PORTRAIT:
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? -gx : gx;
break;
case CrossPointOrientation::INVERTED:
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? gx : -gx;
break;
case CrossPointOrientation::LANDSCAPE_CW:
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? gy : -gy;
break;
case CrossPointOrientation::LANDSCAPE_CCW:
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? -gy : gy;
break;
default:
tiltAxis = gx;
break;
}
if (_inTilt) {
// Wait for device to return to neutral before allowing next trigger
if (fabsf(tiltAxis) < NEUTRAL_RATE_DPS) {
_inTilt = false;
}
} else {
// Check for new tilt gesture (with cooldown)
if ((now - _lastTiltMs) >= COOLDOWN_MS) {
if (tiltAxis > RATE_THRESHOLD_DPS) {
_tiltForwardEvent = true;
_hadActivity = true;
_inTilt = true;
_lastTiltMs = now;
LOG_INF("GYR", "Forward Trigger=(%.1f) dps", tiltAxis);
} else if (tiltAxis < -RATE_THRESHOLD_DPS) {
_tiltBackEvent = true;
_hadActivity = true;
_inTilt = true;
_lastTiltMs = now;
LOG_INF("GYR", "Backward Trigger=(%.1f) dps", tiltAxis);
}
}
}
}
bool HalTiltSensor::wasTiltedForward() {
const bool val = _tiltForwardEvent;
_tiltForwardEvent = false;
return val;
}
bool HalTiltSensor::wasTiltedBack() {
const bool val = _tiltBackEvent;
_tiltBackEvent = false;
return val;
}
bool HalTiltSensor::hadActivity() {
const bool val = _hadActivity;
_hadActivity = false;
return val;
}
void HalTiltSensor::clearPendingEvents() {
_tiltForwardEvent = false;
_tiltBackEvent = false;
_hadActivity = false;
// Intentionally preserve _inTilt so a held tilt doesn't retrigger on next poll
}
+100
View File
@@ -0,0 +1,100 @@
#pragma once
#include <Arduino.h>
#include <Wire.h>
#include "HalGPIO.h"
// TODO: Move enums into new header and share with CrossPointSettings.h
namespace CrossPointOrientation {
enum Value : uint8_t { PORTRAIT = 0, LANDSCAPE_CW = 1, INVERTED = 2, LANDSCAPE_CCW = 3 };
}
namespace CrossPointTiltPageTurn {
enum Value : uint8_t { TILT_OFF = 0, TILT_NORMAL = 1, TILT_INVERTED = 2 };
}
class HalTiltSensor;
extern HalTiltSensor halTiltSensor; // Singleton
class HalTiltSensor {
bool _available = false;
uint8_t _i2cAddr = 0;
// Tilt gesture state machine
bool _tiltForwardEvent = false; // Consumed by wasTiltedForward()
bool _tiltBackEvent = false; // Consumed by wasTiltedBack()
bool _hadActivity = false; // Non-consuming flag for sleep timer
bool _inTilt = false; // Currently tilted past threshold
bool _isAwake = false; // Tracks power state
unsigned long _initMs = 0; // Timestamp of sensor init
unsigned long _lastTiltMs = 0; // Debounce / cooldown
unsigned long _wakeMs = 0; // Timestamp of last wake() for stabilization
// Tuning constants
static constexpr float RATE_THRESHOLD_DPS = 270.0f; // Deg/sec speed to trigger flick
static constexpr float NEUTRAL_RATE_DPS = 50.0f; // Must stop moving below this rate before next trigger
static constexpr unsigned long COOLDOWN_MS = 600; // Minimum ms between triggers
static constexpr unsigned long POLL_INTERVAL_MS = 50; // 20 Hz polling
static constexpr unsigned long WAKE_STABILIZE_MS = 300; // Ignore readings after wake
static constexpr unsigned long SLEEP_STABILIZE_MS = 15; // Sleep turn on/off delay
mutable unsigned long _lastPollMs = 0;
// --- QMI8658 registers ---
static constexpr uint8_t REG_CTRL1 = 0x02;
static constexpr uint8_t REG_CTRL3 = 0x04;
static constexpr uint8_t REG_CTRL7 = 0x08;
static constexpr uint8_t REG_GX_L = 0x3B;
// --- Register Bit Flags ---
// REG_CTRL1 (0x02)
static constexpr uint8_t CTRL1_BIG_ENDIAN = (1 << 5); // 0x20: Default state (1 = Big Endian)
static constexpr uint8_t CTRL1_AUTO_INC = (1 << 6); // 0x40: Enable address auto-increment
static constexpr uint8_t CTRL1_SENSOR_DISABLE = (1 << 0); // 0x01: Power down sensor engine
static constexpr uint8_t CTRL1_BASE = CTRL1_AUTO_INC | CTRL1_BIG_ENDIAN; // 0x60
// REG_CTRL3 (0x04) - Gyro Config
static constexpr uint8_t CTRL3_FS_512DPS = (0b101 << 4); // Bits 6:4 = 101
static constexpr uint8_t CTRL3_ODR_28HZ = 0b1000; // Bits 3:0 = 1000 (28.025 Hz)
// REG_CTRL7 (0x08) - Enable
static constexpr uint8_t CTRL7_DISABLE_ALL = 0x00;
static constexpr uint8_t CTRL7_GYRO_ENABLE = (1 << 1); // Bit 1 = 1
bool writeReg(uint8_t reg, uint8_t val) const;
bool readReg(uint8_t reg, uint8_t* val) const;
bool readGyro(float& gx, float& gy, float& gz) const;
public:
// Call after gpio.begin() and powerManager.begin() (I2C already initialised for X3)
void begin();
// Enables the QMI8658 internal sensor engine
bool wake();
// Puts the QMI8658 into a low-power standby state
bool deepSleep();
// True if the QMI8658 IMU is present on this device
bool isAvailable() const { return _available; }
// Poll the accelerometer and update tilt gesture state.
void update(const uint8_t mode, const uint8_t orientation, const bool inReader);
// Returns true once per tilt-forward gesture (next page direction).
// Consumed on read — subsequent calls return false until next gesture.
bool wasTiltedForward();
// Returns true once per tilt-back gesture (previous page direction).
// Consumed on read.
bool wasTiltedBack();
// Non-consuming: true if any tilt activity occurred since last call.
// Used to reset the auto-sleep inactivity timer.
bool hadActivity();
// Discard any pending tilt events (call when leaving reader or disabling tilt).
void clearPendingEvents();
};
+4
View File
@@ -137,6 +137,8 @@ class CrossPointSettings {
// Image rendering in EPUB reader
enum IMAGE_RENDERING { IMAGES_DISPLAY = 0, IMAGES_PLACEHOLDER = 1, IMAGES_SUPPRESS = 2, IMAGE_RENDERING_COUNT };
enum TILT_PAGE_TURN { TILT_OFF = 0, TILT_NORMAL = 1, TILT_NVERTED = 2, TILT_PAGE_TURN_COUNT };
// Sleep screen settings
uint8_t sleepScreen = DARK;
// Sleep screen cover mode settings
@@ -199,6 +201,8 @@ class CrossPointSettings {
uint8_t showHiddenFiles = 0;
// Image rendering mode in EPUB reader
uint8_t imageRendering = IMAGES_DISPLAY;
// Tilt-based page turning (X3 only — requires QMI8658 IMU)
uint8_t tiltPageTurn = TILT_OFF;
~CrossPointSettings() = default;
+126 -110
View File
@@ -1,5 +1,6 @@
#pragma once
#include <HalTiltSensor.h>
#include <I18n.h>
#include <vector>
@@ -12,122 +13,137 @@
// Each entry has a key (for JSON API) and category (for grouping).
// ACTION-type entries and entries without a key are device-only.
inline const std::vector<SettingInfo>& getSettingsList() {
static const std::vector<SettingInfo> list = {
// --- Display ---
SettingInfo::Enum(StrId::STR_SLEEP_SCREEN, &CrossPointSettings::sleepScreen,
{StrId::STR_DARK, StrId::STR_LIGHT, StrId::STR_CUSTOM, StrId::STR_COVER, StrId::STR_NONE_OPT,
StrId::STR_COVER_CUSTOM},
"sleepScreen", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_SLEEP_COVER_MODE, &CrossPointSettings::sleepScreenCoverMode,
{StrId::STR_FIT, StrId::STR_CROP}, "sleepScreenCoverMode", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_SLEEP_COVER_FILTER, &CrossPointSettings::sleepScreenCoverFilter,
{StrId::STR_NONE_OPT, StrId::STR_FILTER_CONTRAST, StrId::STR_INVERTED},
"sleepScreenCoverFilter", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_HIDE_BATTERY, &CrossPointSettings::hideBatteryPercentage,
{StrId::STR_NEVER, StrId::STR_IN_READER, StrId::STR_ALWAYS}, "hideBatteryPercentage",
StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(
StrId::STR_REFRESH_FREQ, &CrossPointSettings::refreshFrequency,
{StrId::STR_PAGES_1, StrId::STR_PAGES_5, StrId::STR_PAGES_10, StrId::STR_PAGES_15, StrId::STR_PAGES_30},
"refreshFrequency", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_UI_THEME, &CrossPointSettings::uiTheme,
{StrId::STR_THEME_CLASSIC, StrId::STR_THEME_LYRA, StrId::STR_THEME_LYRA_EXTENDED,
StrId::STR_THEME_ROUNDEDRAFF},
"uiTheme", StrId::STR_CAT_DISPLAY),
SettingInfo::Toggle(StrId::STR_SUNLIGHT_FADING_FIX, &CrossPointSettings::fadingFix, "fadingFix",
static const std::vector<SettingInfo> list = [] {
std::vector<SettingInfo> v = {
// --- Display ---
SettingInfo::Enum(StrId::STR_SLEEP_SCREEN, &CrossPointSettings::sleepScreen,
{StrId::STR_DARK, StrId::STR_LIGHT, StrId::STR_CUSTOM, StrId::STR_COVER, StrId::STR_NONE_OPT,
StrId::STR_COVER_CUSTOM},
"sleepScreen", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_SLEEP_COVER_MODE, &CrossPointSettings::sleepScreenCoverMode,
{StrId::STR_FIT, StrId::STR_CROP}, "sleepScreenCoverMode", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_SLEEP_COVER_FILTER, &CrossPointSettings::sleepScreenCoverFilter,
{StrId::STR_NONE_OPT, StrId::STR_FILTER_CONTRAST, StrId::STR_INVERTED},
"sleepScreenCoverFilter", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_HIDE_BATTERY, &CrossPointSettings::hideBatteryPercentage,
{StrId::STR_NEVER, StrId::STR_IN_READER, StrId::STR_ALWAYS}, "hideBatteryPercentage",
StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(
StrId::STR_REFRESH_FREQ, &CrossPointSettings::refreshFrequency,
{StrId::STR_PAGES_1, StrId::STR_PAGES_5, StrId::STR_PAGES_10, StrId::STR_PAGES_15, StrId::STR_PAGES_30},
"refreshFrequency", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_UI_THEME, &CrossPointSettings::uiTheme,
{StrId::STR_THEME_CLASSIC, StrId::STR_THEME_LYRA, StrId::STR_THEME_LYRA_EXTENDED,
StrId::STR_THEME_ROUNDEDRAFF},
"uiTheme", StrId::STR_CAT_DISPLAY),
SettingInfo::Toggle(StrId::STR_SUNLIGHT_FADING_FIX, &CrossPointSettings::fadingFix, "fadingFix",
StrId::STR_CAT_DISPLAY),
// --- Reader ---
SettingInfo::Enum(StrId::STR_FONT_FAMILY, &CrossPointSettings::fontFamily,
{StrId::STR_NOTO_SERIF, StrId::STR_NOTO_SANS, StrId::STR_OPEN_DYSLEXIC}, "fontFamily",
StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_FONT_SIZE, &CrossPointSettings::fontSize,
{StrId::STR_SMALL, StrId::STR_MEDIUM, StrId::STR_LARGE, StrId::STR_X_LARGE}, "fontSize",
StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_LINE_SPACING, &CrossPointSettings::lineSpacing,
{StrId::STR_TIGHT, StrId::STR_NORMAL, StrId::STR_WIDE}, "lineSpacing", StrId::STR_CAT_READER),
SettingInfo::Value(StrId::STR_SCREEN_MARGIN, &CrossPointSettings::screenMargin, {5, 40, 5}, "screenMargin",
StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_PARA_ALIGNMENT, &CrossPointSettings::paragraphAlignment,
{StrId::STR_JUSTIFY, StrId::STR_ALIGN_LEFT, StrId::STR_CENTER, StrId::STR_ALIGN_RIGHT,
StrId::STR_BOOK_S_STYLE},
"paragraphAlignment", StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_EMBEDDED_STYLE, &CrossPointSettings::embeddedStyle, "embeddedStyle",
// --- Reader ---
SettingInfo::Enum(StrId::STR_FONT_FAMILY, &CrossPointSettings::fontFamily,
{StrId::STR_NOTO_SERIF, StrId::STR_NOTO_SANS, StrId::STR_OPEN_DYSLEXIC}, "fontFamily",
StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_HYPHENATION, &CrossPointSettings::hyphenationEnabled, "hyphenationEnabled",
SettingInfo::Enum(StrId::STR_FONT_SIZE, &CrossPointSettings::fontSize,
{StrId::STR_SMALL, StrId::STR_MEDIUM, StrId::STR_LARGE, StrId::STR_X_LARGE}, "fontSize",
StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_ORIENTATION, &CrossPointSettings::orientation,
{StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_INVERTED, StrId::STR_LANDSCAPE_CCW},
"orientation", StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_EXTRA_SPACING, &CrossPointSettings::extraParagraphSpacing, "extraParagraphSpacing",
StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_TEXT_AA, &CrossPointSettings::textAntiAliasing, "textAntiAliasing",
StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_IMAGES, &CrossPointSettings::imageRendering,
{StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS},
"imageRendering", StrId::STR_CAT_READER),
// --- Controls ---
SettingInfo::Enum(StrId::STR_SIDE_BTN_LAYOUT, &CrossPointSettings::sideButtonLayout,
{StrId::STR_PREV_NEXT, StrId::STR_NEXT_PREV}, "sideButtonLayout", StrId::STR_CAT_CONTROLS),
SettingInfo::Toggle(StrId::STR_LONG_PRESS_SKIP, &CrossPointSettings::longPressChapterSkip, "longPressChapterSkip",
StrId::STR_CAT_CONTROLS),
SettingInfo::Enum(StrId::STR_SHORT_PWR_BTN, &CrossPointSettings::shortPwrBtn,
{StrId::STR_IGNORE, StrId::STR_SLEEP, StrId::STR_PAGE_TURN, StrId::STR_FORCE_REFRESH},
"shortPwrBtn", StrId::STR_CAT_CONTROLS),
SettingInfo::Enum(StrId::STR_LINE_SPACING, &CrossPointSettings::lineSpacing,
{StrId::STR_TIGHT, StrId::STR_NORMAL, StrId::STR_WIDE}, "lineSpacing", StrId::STR_CAT_READER),
SettingInfo::Value(StrId::STR_SCREEN_MARGIN, &CrossPointSettings::screenMargin, {5, 40, 5}, "screenMargin",
StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_PARA_ALIGNMENT, &CrossPointSettings::paragraphAlignment,
{StrId::STR_JUSTIFY, StrId::STR_ALIGN_LEFT, StrId::STR_CENTER, StrId::STR_ALIGN_RIGHT,
StrId::STR_BOOK_S_STYLE},
"paragraphAlignment", StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_EMBEDDED_STYLE, &CrossPointSettings::embeddedStyle, "embeddedStyle",
StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_HYPHENATION, &CrossPointSettings::hyphenationEnabled, "hyphenationEnabled",
StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_ORIENTATION, &CrossPointSettings::orientation,
{StrId::STR_PORTRAIT, StrId::STR_LANDSCAPE_CW, StrId::STR_INVERTED, StrId::STR_LANDSCAPE_CCW},
"orientation", StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_EXTRA_SPACING, &CrossPointSettings::extraParagraphSpacing,
"extraParagraphSpacing", StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_TEXT_AA, &CrossPointSettings::textAntiAliasing, "textAntiAliasing",
StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_IMAGES, &CrossPointSettings::imageRendering,
{StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS},
"imageRendering", StrId::STR_CAT_READER),
// --- Controls ---
SettingInfo::Enum(StrId::STR_SIDE_BTN_LAYOUT, &CrossPointSettings::sideButtonLayout,
{StrId::STR_PREV_NEXT, StrId::STR_NEXT_PREV}, "sideButtonLayout", StrId::STR_CAT_CONTROLS),
SettingInfo::Toggle(StrId::STR_LONG_PRESS_SKIP, &CrossPointSettings::longPressChapterSkip,
"longPressChapterSkip", StrId::STR_CAT_CONTROLS),
SettingInfo::Enum(StrId::STR_SHORT_PWR_BTN, &CrossPointSettings::shortPwrBtn,
{StrId::STR_IGNORE, StrId::STR_SLEEP, StrId::STR_PAGE_TURN, StrId::STR_FORCE_REFRESH},
"shortPwrBtn", StrId::STR_CAT_CONTROLS),
// --- System ---
SettingInfo::Enum(StrId::STR_TIME_TO_SLEEP, &CrossPointSettings::sleepTimeout,
{StrId::STR_MIN_1, StrId::STR_MIN_5, StrId::STR_MIN_10, StrId::STR_MIN_15, StrId::STR_MIN_30},
"sleepTimeout", StrId::STR_CAT_SYSTEM),
SettingInfo::Toggle(StrId::STR_SHOW_HIDDEN_FILES, &CrossPointSettings::showHiddenFiles, "showHiddenFiles",
StrId::STR_CAT_SYSTEM),
// --- System ---
SettingInfo::Enum(StrId::STR_TIME_TO_SLEEP, &CrossPointSettings::sleepTimeout,
{StrId::STR_MIN_1, StrId::STR_MIN_5, StrId::STR_MIN_10, StrId::STR_MIN_15, StrId::STR_MIN_30},
"sleepTimeout", StrId::STR_CAT_SYSTEM),
SettingInfo::Toggle(StrId::STR_SHOW_HIDDEN_FILES, &CrossPointSettings::showHiddenFiles, "showHiddenFiles",
StrId::STR_CAT_SYSTEM),
// --- KOReader Sync (web-only, uses KOReaderCredentialStore) ---
SettingInfo::DynamicString(
StrId::STR_KOREADER_USERNAME, [] { return KOREADER_STORE.getUsername(); },
[](const std::string& v) {
KOREADER_STORE.setCredentials(v, KOREADER_STORE.getPassword());
KOREADER_STORE.saveToFile();
},
"koUsername", StrId::STR_KOREADER_SYNC),
SettingInfo::DynamicString(
StrId::STR_KOREADER_PASSWORD, [] { return KOREADER_STORE.getPassword(); },
[](const std::string& v) {
KOREADER_STORE.setCredentials(KOREADER_STORE.getUsername(), v);
KOREADER_STORE.saveToFile();
},
"koPassword", StrId::STR_KOREADER_SYNC),
SettingInfo::DynamicString(
StrId::STR_SYNC_SERVER_URL, [] { return KOREADER_STORE.getServerUrl(); },
[](const std::string& v) {
KOREADER_STORE.setServerUrl(v);
KOREADER_STORE.saveToFile();
},
"koServerUrl", StrId::STR_KOREADER_SYNC),
SettingInfo::DynamicEnum(
StrId::STR_DOCUMENT_MATCHING, {StrId::STR_FILENAME, StrId::STR_BINARY},
[] { return static_cast<uint8_t>(KOREADER_STORE.getMatchMethod()); },
[](uint8_t v) {
KOREADER_STORE.setMatchMethod(static_cast<DocumentMatchMethod>(v));
KOREADER_STORE.saveToFile();
},
"koMatchMethod", StrId::STR_KOREADER_SYNC),
// --- Status Bar Settings (web-only, uses StatusBarSettingsActivity) ---
SettingInfo::Toggle(StrId::STR_CHAPTER_PAGE_COUNT, &CrossPointSettings::statusBarChapterPageCount,
"statusBarChapterPageCount", StrId::STR_CUSTOMISE_STATUS_BAR),
SettingInfo::Toggle(StrId::STR_BOOK_PROGRESS_PERCENTAGE, &CrossPointSettings::statusBarBookProgressPercentage,
"statusBarBookProgressPercentage", StrId::STR_CUSTOMISE_STATUS_BAR),
SettingInfo::Enum(StrId::STR_PROGRESS_BAR, &CrossPointSettings::statusBarProgressBar,
{StrId::STR_BOOK, StrId::STR_CHAPTER, StrId::STR_HIDE}, "statusBarProgressBar",
StrId::STR_CUSTOMISE_STATUS_BAR),
SettingInfo::Enum(StrId::STR_PROGRESS_BAR_THICKNESS, &CrossPointSettings::statusBarProgressBarThickness,
{StrId::STR_PROGRESS_BAR_THIN, StrId::STR_PROGRESS_BAR_MEDIUM, StrId::STR_PROGRESS_BAR_THICK},
"statusBarProgressBarThickness", StrId::STR_CUSTOMISE_STATUS_BAR),
SettingInfo::Enum(StrId::STR_TITLE, &CrossPointSettings::statusBarTitle,
{StrId::STR_BOOK, StrId::STR_CHAPTER, StrId::STR_HIDE}, "statusBarTitle",
StrId::STR_CUSTOMISE_STATUS_BAR),
SettingInfo::Toggle(StrId::STR_BATTERY, &CrossPointSettings::statusBarBattery, "statusBarBattery",
// --- KOReader Sync (web-only, uses KOReaderCredentialStore) ---
SettingInfo::DynamicString(
StrId::STR_KOREADER_USERNAME, [] { return KOREADER_STORE.getUsername(); },
[](const std::string& v) {
KOREADER_STORE.setCredentials(v, KOREADER_STORE.getPassword());
KOREADER_STORE.saveToFile();
},
"koUsername", StrId::STR_KOREADER_SYNC),
SettingInfo::DynamicString(
StrId::STR_KOREADER_PASSWORD, [] { return KOREADER_STORE.getPassword(); },
[](const std::string& v) {
KOREADER_STORE.setCredentials(KOREADER_STORE.getUsername(), v);
KOREADER_STORE.saveToFile();
},
"koPassword", StrId::STR_KOREADER_SYNC),
SettingInfo::DynamicString(
StrId::STR_SYNC_SERVER_URL, [] { return KOREADER_STORE.getServerUrl(); },
[](const std::string& v) {
KOREADER_STORE.setServerUrl(v);
KOREADER_STORE.saveToFile();
},
"koServerUrl", StrId::STR_KOREADER_SYNC),
SettingInfo::DynamicEnum(
StrId::STR_DOCUMENT_MATCHING, {StrId::STR_FILENAME, StrId::STR_BINARY},
[] { return static_cast<uint8_t>(KOREADER_STORE.getMatchMethod()); },
[](uint8_t v) {
KOREADER_STORE.setMatchMethod(static_cast<DocumentMatchMethod>(v));
KOREADER_STORE.saveToFile();
},
"koMatchMethod", StrId::STR_KOREADER_SYNC),
// --- Status Bar Settings (web-only, uses StatusBarSettingsActivity) ---
SettingInfo::Toggle(StrId::STR_CHAPTER_PAGE_COUNT, &CrossPointSettings::statusBarChapterPageCount,
"statusBarChapterPageCount", StrId::STR_CUSTOMISE_STATUS_BAR),
SettingInfo::Toggle(StrId::STR_BOOK_PROGRESS_PERCENTAGE, &CrossPointSettings::statusBarBookProgressPercentage,
"statusBarBookProgressPercentage", StrId::STR_CUSTOMISE_STATUS_BAR),
SettingInfo::Enum(StrId::STR_PROGRESS_BAR, &CrossPointSettings::statusBarProgressBar,
{StrId::STR_BOOK, StrId::STR_CHAPTER, StrId::STR_HIDE}, "statusBarProgressBar",
StrId::STR_CUSTOMISE_STATUS_BAR),
};
SettingInfo::Enum(StrId::STR_PROGRESS_BAR_THICKNESS, &CrossPointSettings::statusBarProgressBarThickness,
{StrId::STR_PROGRESS_BAR_THIN, StrId::STR_PROGRESS_BAR_MEDIUM, StrId::STR_PROGRESS_BAR_THICK},
"statusBarProgressBarThickness", StrId::STR_CUSTOMISE_STATUS_BAR),
SettingInfo::Enum(StrId::STR_TITLE, &CrossPointSettings::statusBarTitle,
{StrId::STR_BOOK, StrId::STR_CHAPTER, StrId::STR_HIDE}, "statusBarTitle",
StrId::STR_CUSTOMISE_STATUS_BAR),
SettingInfo::Toggle(StrId::STR_BATTERY, &CrossPointSettings::statusBarBattery, "statusBarBattery",
StrId::STR_CUSTOMISE_STATUS_BAR),
};
// Only show tilt page turn setting when the QMI8658 IMU is present (X3)
if (halTiltSensor.isAvailable()) {
// Insert after the short power button setting (end of Controls section)
for (auto it = v.begin(); it != v.end(); ++it) {
if (it->nameId == StrId::STR_SHORT_PWR_BTN) {
v.insert(it + 1, SettingInfo::Enum(StrId::STR_TILT_PAGE_TURN, &CrossPointSettings::tiltPageTurn,
{StrId::STR_STATE_OFF, StrId::STR_NORMAL, StrId::STR_INVERTED},
"tiltPageTurn", StrId::STR_CAT_CONTROLS));
break;
}
}
}
return v;
}();
return list;
}
+2 -2
View File
@@ -184,7 +184,7 @@ void EpubReaderActivity::loop() {
return;
}
auto [prevTriggered, nextTriggered] = ReaderUtils::detectPageTurn(mappedInput);
auto [prevTriggered, nextTriggered, fromTilt] = ReaderUtils::detectPageTurn(mappedInput);
if (!prevTriggered && !nextTriggered) {
return;
}
@@ -202,7 +202,7 @@ void EpubReaderActivity::loop() {
return;
}
const bool skipChapter = SETTINGS.longPressChapterSkip && mappedInput.getHeldTime() > skipChapterMs;
const bool skipChapter = !fromTilt && SETTINGS.longPressChapterSkip && mappedInput.getHeldTime() > skipChapterMs;
// Don't skip chapter after screenshot
if (gpio.wasReleased(HalGPIO::BTN_POWER) && gpio.wasReleased(HalGPIO::BTN_DOWN)) {
+13 -9
View File
@@ -2,6 +2,7 @@
#include <CrossPointSettings.h>
#include <GfxRenderer.h>
#include <HalTiltSensor.h>
#include <Logging.h>
#include "MappedInputManager.h"
@@ -32,21 +33,24 @@ inline void applyOrientation(GfxRenderer& renderer, const uint8_t orientation) {
struct PageTurnResult {
bool prev;
bool next;
bool fromTilt;
};
inline PageTurnResult detectPageTurn(const MappedInputManager& input) {
const bool usePress = !SETTINGS.longPressChapterSkip;
const bool prev = usePress ? (input.wasPressed(MappedInputManager::Button::PageBack) ||
input.wasPressed(MappedInputManager::Button::Left))
: (input.wasReleased(MappedInputManager::Button::PageBack) ||
input.wasReleased(MappedInputManager::Button::Left));
const bool tiltNext = SETTINGS.tiltPageTurn && halTiltSensor.wasTiltedForward();
const bool tiltPrev = SETTINGS.tiltPageTurn && halTiltSensor.wasTiltedBack();
const bool prev = tiltPrev || (usePress ? (input.wasPressed(MappedInputManager::Button::PageBack) ||
input.wasPressed(MappedInputManager::Button::Left))
: (input.wasReleased(MappedInputManager::Button::PageBack) ||
input.wasReleased(MappedInputManager::Button::Left)));
const bool powerTurn = SETTINGS.shortPwrBtn == CrossPointSettings::SHORT_PWRBTN::PAGE_TURN &&
input.wasReleased(MappedInputManager::Button::Power);
const bool next = usePress ? (input.wasPressed(MappedInputManager::Button::PageForward) || powerTurn ||
input.wasPressed(MappedInputManager::Button::Right))
: (input.wasReleased(MappedInputManager::Button::PageForward) || powerTurn ||
input.wasReleased(MappedInputManager::Button::Right));
return {prev, next};
const bool next = tiltNext || (usePress ? (input.wasPressed(MappedInputManager::Button::PageForward) || powerTurn ||
input.wasPressed(MappedInputManager::Button::Right))
: (input.wasReleased(MappedInputManager::Button::PageForward) || powerTurn ||
input.wasReleased(MappedInputManager::Button::Right)));
return {prev, next, tiltPrev || tiltNext};
}
inline void displayWithRefreshCycle(const GfxRenderer& renderer, int& pagesUntilFullRefresh) {
+1 -1
View File
@@ -71,7 +71,7 @@ void TxtReaderActivity::loop() {
return;
}
auto [prevTriggered, nextTriggered] = ReaderUtils::detectPageTurn(mappedInput);
auto [prevTriggered, nextTriggered, fromTilt] = ReaderUtils::detectPageTurn(mappedInput);
if (!prevTriggered && !nextTriggered) {
return;
}
+15 -10
View File
@@ -10,6 +10,7 @@
#include <FsHelpers.h>
#include <GfxRenderer.h>
#include <HalStorage.h>
#include <HalTiltSensor.h>
#include <I18n.h>
#include "CrossPointSettings.h"
@@ -82,17 +83,20 @@ void XtcReaderActivity::loop() {
// When long-press chapter skip is disabled, turn pages on press instead of release.
const bool usePressForPageTurn = !SETTINGS.longPressChapterSkip;
const bool prevTriggered = usePressForPageTurn ? (mappedInput.wasPressed(MappedInputManager::Button::PageBack) ||
mappedInput.wasPressed(MappedInputManager::Button::Left))
: (mappedInput.wasReleased(MappedInputManager::Button::PageBack) ||
mappedInput.wasReleased(MappedInputManager::Button::Left));
const bool tiltNext = SETTINGS.tiltPageTurn && halTiltSensor.wasTiltedForward();
const bool tiltPrev = SETTINGS.tiltPageTurn && halTiltSensor.wasTiltedBack();
const bool prevTriggered =
tiltPrev || (usePressForPageTurn ? (mappedInput.wasPressed(MappedInputManager::Button::PageBack) ||
mappedInput.wasPressed(MappedInputManager::Button::Left))
: (mappedInput.wasReleased(MappedInputManager::Button::PageBack) ||
mappedInput.wasReleased(MappedInputManager::Button::Left)));
const bool powerPageTurn = SETTINGS.shortPwrBtn == CrossPointSettings::SHORT_PWRBTN::PAGE_TURN &&
mappedInput.wasReleased(MappedInputManager::Button::Power);
const bool nextTriggered = usePressForPageTurn
? (mappedInput.wasPressed(MappedInputManager::Button::PageForward) || powerPageTurn ||
mappedInput.wasPressed(MappedInputManager::Button::Right))
: (mappedInput.wasReleased(MappedInputManager::Button::PageForward) || powerPageTurn ||
mappedInput.wasReleased(MappedInputManager::Button::Right));
const bool nextTriggered =
tiltNext || (usePressForPageTurn ? (mappedInput.wasPressed(MappedInputManager::Button::PageForward) ||
powerPageTurn || mappedInput.wasPressed(MappedInputManager::Button::Right))
: (mappedInput.wasReleased(MappedInputManager::Button::PageForward) ||
powerPageTurn || mappedInput.wasReleased(MappedInputManager::Button::Right)));
if (!prevTriggered && !nextTriggered) {
return;
@@ -109,7 +113,8 @@ void XtcReaderActivity::loop() {
return;
}
const bool skipPages = SETTINGS.longPressChapterSkip && mappedInput.getHeldTime() > skipPageMs;
const bool fromTilt = tiltPrev || tiltNext;
const bool skipPages = !fromTilt && SETTINGS.longPressChapterSkip && mappedInput.getHeldTime() > skipPageMs;
const int skipAmount = skipPages ? 10 : 1;
if (prevTriggered) {
+6 -1
View File
@@ -8,6 +8,7 @@
#include <HalPowerManager.h>
#include <HalStorage.h>
#include <HalSystem.h>
#include <HalTiltSensor.h>
#include <I18n.h>
#include <Logging.h>
#include <SPI.h>
@@ -186,6 +187,7 @@ void enterDeepSleep() {
activityManager.goToSleep();
halTiltSensor.deepSleep();
display.deepSleep();
LOG_DBG("MAIN", "Entering deep sleep");
@@ -231,6 +233,7 @@ void setup() {
HalSystem::begin();
gpio.begin();
powerManager.begin();
halTiltSensor.begin();
#ifdef ENABLE_SERIAL_LOG
if (gpio.isUsbConnected()) {
@@ -318,6 +321,7 @@ void loop() {
static unsigned long lastMemPrint = 0;
gpio.update();
halTiltSensor.update(SETTINGS.tiltPageTurn, SETTINGS.orientation, activityManager.isReaderActivity());
renderer.setFadingFix(SETTINGS.fadingFix);
@@ -346,7 +350,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
}