Something went wrong. Try again.
A fork of https://github.com/crosspoint-reader/crosspoint-reader
Something went wrong. Try again.
19 kB · 452 lines
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453#pragma once
#include <ArduinoJson.h>#include <Epub/ReaderRenderSpec.h>#include <PersistableStore.h>
#include <cstdint>
#include "util/HomeButtonInput.h"
class CrossPointSettings : public PersistableStore<CrossPointSettings> { private: // Private constructor for singleton CrossPointSettings() = default;
friend class PersistableStore<CrossPointSettings>;
public: enum SLEEP_SCREEN_MODE { DARK = 0, LIGHT = 1, CUSTOM = 2, COVER = 3, COVER_CUSTOM = 4, BLANK = 5, QUICK_RESUME = 6, TRANSPARENT_CUSTOM = 7, SLEEP_SCREEN_MODE_COUNT }; enum SLEEP_SCREEN_COVER_MODE { FIT = 0, CROP = 1, SLEEP_SCREEN_COVER_MODE_COUNT }; enum SLEEP_SCREEN_COVER_FILTER { NO_FILTER = 0, BLACK_AND_WHITE = 1, INVERTED_BLACK_AND_WHITE = 2, SLEEP_SCREEN_COVER_FILTER_COUNT }; enum STATUS_BAR_PROGRESS_BAR { BOOK_PROGRESS = 0, CHAPTER_PROGRESS = 1, HIDE_PROGRESS = 2, STATUS_BAR_PROGRESS_BAR_COUNT }; enum STATUS_BAR_PROGRESS_BAR_THICKNESS { PROGRESS_BAR_THIN = 0, PROGRESS_BAR_NORMAL = 1, PROGRESS_BAR_THICK = 2, STATUS_BAR_PROGRESS_BAR_THICKNESS_COUNT }; enum STATUS_BAR_TITLE { BOOK_TITLE = 0, CHAPTER_TITLE = 1, HIDE_TITLE = 2, STATUS_BAR_TITLE_COUNT }; enum XTC_STATUS_BAR_MODE { XTC_STATUS_BAR_HIDE = 0, XTC_STATUS_BAR_BOTTOM = 1, XTC_STATUS_BAR_TOP = 2, XTC_STATUS_BAR_MODE_COUNT };
enum STATUS_BAR_CLOCK_MODE { STATUS_BAR_CLOCK_HIDE = 0, STATUS_BAR_CLOCK_RIGHT = 1, STATUS_BAR_CLOCK_LEFT = 2, STATUS_BAR_CLOCK_MODE_COUNT };
// Auto follows the timezone's baked DST rule; On/Off override it — the // escape hatch for a zone whose law changed before the firmware caught up. enum CLOCK_DST_MODE { CLOCK_DST_AUTO = 0, CLOCK_DST_ON = 1, CLOCK_DST_OFF = 2, CLOCK_DST_MODE_COUNT };
enum ORIENTATION { PORTRAIT = 0, // 480x800 logical coordinates (current default) LANDSCAPE_CW = 1, // 800x480 logical coordinates, rotated 180° (swap top/bottom) INVERTED = 2, // 480x800 logical coordinates, inverted LANDSCAPE_CCW = 3, // 800x480 logical coordinates, native panel orientation ORIENTATION_COUNT };
// Front button layout options (legacy) // Default: Back, Confirm, Left, Right // Swapped: Left, Right, Back, Confirm enum FRONT_BUTTON_LAYOUT { BACK_CONFIRM_LEFT_RIGHT = 0, LEFT_RIGHT_BACK_CONFIRM = 1, LEFT_BACK_CONFIRM_RIGHT = 2, BACK_CONFIRM_RIGHT_LEFT = 3, FRONT_BUTTON_LAYOUT_COUNT };
// Front button hardware identifiers (for remapping) enum FRONT_BUTTON_HARDWARE { FRONT_HW_BACK = 0, FRONT_HW_CONFIRM = 1, FRONT_HW_LEFT = 2, FRONT_HW_RIGHT = 3, FRONT_BUTTON_HARDWARE_COUNT };
// Side button layout options // Default: Up = Previous, Down = Next. NEXT_NEXT / PREV_PREV assign both // buttons to the same direction for one-handed reading. enum SIDE_BUTTON_LAYOUT { PREV_NEXT = 0, NEXT_PREV = 1, SIDE_BUTTONS_DISABLED = 2, NEXT_NEXT = 3, PREV_PREV = 4, SIDE_BUTTON_LAYOUT_COUNT };
// Font family options (built-in fonts only; SD card fonts use sdFontFamilyName) enum FONT_FAMILY { NOTOSERIF = 0, NOTOSANS = 1, FONT_FAMILY_COUNT }; static constexpr uint8_t LEGACY_OPENDYSLEXIC = 2; static constexpr uint8_t BUILTIN_FONT_COUNT = FONT_FAMILY_COUNT; // Reader font size is a point size, not an enum slot — see fontPointSize. // Legacy 1.4-and-earlier files stored a 0..3 SMALL/MEDIUM/LARGE/EXTRA_LARGE // slot; fromJson() folds that range up (see LEGACY_FONT_SIZE_MAX). static constexpr uint8_t LEGACY_FONT_SIZE_MAX = 3; static constexpr uint8_t DEFAULT_FONT_POINT_SIZE = 14; enum LINE_COMPRESSION { TIGHT = 0, NORMAL = 1, WIDE = 2, EXTRA_WIDE = 3, LINE_COMPRESSION_COUNT }; enum PARAGRAPH_ALIGNMENT { JUSTIFIED = 0, LEFT_ALIGN = 1, CENTER_ALIGN = 2, RIGHT_ALIGN = 3, BOOK_STYLE = 4, PARAGRAPH_ALIGNMENT_COUNT };
// Auto-sleep timeout options (in minutes) enum SLEEP_TIMEOUT { SLEEP_1_MIN = 0, SLEEP_5_MIN = 1, SLEEP_10_MIN = 2, SLEEP_15_MIN = 3, SLEEP_30_MIN = 4, SLEEP_TIMEOUT_COUNT };
// E-ink refresh frequency (pages between full refreshes). enum REFRESH_FREQUENCY { REFRESH_1 = 0, REFRESH_5 = 1, REFRESH_10 = 2, REFRESH_15 = 3, REFRESH_30 = 4, REFRESH_NEVER = 5, REFRESH_FREQUENCY_COUNT };
// Short power button press actions. PWR_CONFIRM is only offered on touch // boards (see SettingsList.h). enum SHORT_PWRBTN { IGNORE = 0, SLEEP = 1, PAGE_TURN = 2, FORCE_REFRESH = 3, FOOTNOTES = 4, PWR_CONFIRM = 5, SHORT_PWRBTN_COUNT };
// Long-press Confirm action while reading an EPUB. The setting cycles through these values. // Persisted in settings.json by index: any new function (e.g. dictionary, bookmark) MUST use a // value >= 2 and be appended at the END of the enumValues array in SettingsList.h, otherwise the // stored indices shift and existing saves are silently misinterpreted. enum LONG_PRESS_MENU_FUNCTION { LP_MENU_KOSYNC = 0, LP_MENU_DISABLED = 1, LP_MENU_BOOKMARK = 2, LP_MENU_DICTIONARY = 3, LP_MENU_READER_MENU = 4, LONG_PRESS_MENU_FUNCTION_COUNT };
// Hide battery percentage enum HIDE_BATTERY_PERCENTAGE { HIDE_NEVER = 0, HIDE_READER = 1, HIDE_ALWAYS = 2, HIDE_BATTERY_PERCENTAGE_COUNT };
// Page turn button long press behavior enum LONG_PRESS_BUTTON_BEHAVIOR { OFF = 0, CHAPTER_SKIP = 1, ORIENTATION_CHANGE = 2, LONG_PRESS_BUTTON_BEHAVIOR_COUNT };
// UI Theme enum UI_THEME { CLASSIC = 0, LYRA = 1, LYRA_3_COVERS = 2, ROUNDEDRAFF = 3, COVER_GRID = 4 };
// Image rendering in EPUB reader enum IMAGE_RENDERING { IMAGES_DISPLAY = 0, IMAGES_PLACEHOLDER = 1, IMAGES_SUPPRESS = 2, IMAGE_RENDERING_COUNT };
// How Select opens the reader menu: the classic full-screen list, or a toolbar // overlay (top/bottom bars with Contents / Text / More bottom-sheet panels) // painted over the page. enum READER_MENU_STYLE { READER_MENU_LIST = 0, READER_MENU_TOOLBAR = 1, READER_MENU_STYLE_COUNT };
enum TILT_PAGE_TURN { TILT_OFF = 0, TILT_NORMAL = 1, TILT_NVERTED = 2, TILT_PAGE_TURN_COUNT };
enum TOUCH_READER_CONTROLS { TOUCH_READER_OFF = 0, TOUCH_READER_ON = 1, TOUCH_READER_CONTROLS_COUNT };
// Per-direction reader page-turn gestures. INVERTED_TAP is tap-only; either // direction set to it swaps both directions' shared tap zones (see // ReaderUtils::detectTouchPageTurn). enum PAGE_TURN_GESTURE { TAP_AND_SWIPE = 0, TAP_ONLY = 1, SWIPE_ONLY = 2, INVERTED_TAP = 3, PAGE_TURN_GESTURE_DISABLED = 4, PAGE_TURN_GESTURE_COUNT };
// How the reader menu opens on touch boards. Persisted under the legacy // "tapForReaderMenu" key: 0/1 keep their old Off/Tap meaning. enum SHOW_READER_MENU { READER_MENU_OFF = 0, READER_MENU_TAP = 1, READER_MENU_SWIPE_UP = 2, SHOW_READER_MENU_COUNT };
enum QUICK_RESUME_SLEEP_SCREEN { QUICK_RESUME_NEVER = 0, QUICK_RESUME_AFTER_TIMEOUT = 1, QUICK_RESUME_SLEEP_SCREEN_COUNT };
// Sleep screen settings uint8_t sleepScreen = DARK; // Night mode: inverted output polarity, applied to every activity per // render by ActivityManager. The sleep screen opts out itself. uint8_t screenInverted = 0; // Sleep screen cover mode settings uint8_t sleepScreenCoverMode = FIT; // Sleep screen cover filter uint8_t sleepScreenCoverFilter = NO_FILTER; // Status bar settings uint8_t statusBarChapterPageCount = 1; uint8_t statusBarBookProgressPercentage = 1; uint8_t statusBarProgressBar = HIDE_PROGRESS; uint8_t statusBarProgressBarThickness = PROGRESS_BAR_NORMAL; uint8_t statusBarTitle = CHAPTER_TITLE; uint8_t statusBarBattery = 1; uint8_t xtcStatusBarMode = XTC_STATUS_BAR_HIDE; // Clock display in status bar (any board whose RTC probe succeeds) uint8_t statusBarClock = STATUS_BAR_CLOCK_HIDE; // LEGACY, kept for migration only: quarter-hour UTC offset biased by 48 // (48 = UTC+0). Superseded by clockTimezone; read once by // timezones::activeIndex() when clockTimezone is unset. uint8_t clockUtcOffsetQ = 48; // Clock display format: 0 = 24-hour, 1 = 12-hour uint8_t clockFormat = 0; // Index into the timezone table (src/util/Timezones.cpp, append-only). // 255 = never chosen; falls back to the legacy UTC offset, then UTC. uint8_t clockTimezone = 255; // CLOCK_DST_MODE: follow the zone's DST rule, or force it on/off. uint8_t clockDst = CLOCK_DST_AUTO; // Show the clock opposite the battery in every header band that draws one. uint8_t clockShowInHeader = 0; // Set once an NTP sync succeeds. Used to skip re-syncing on every WiFi connect. // Resetting to 0 (e.g. via the web UI) forces a re-sync on next WiFi connect. uint8_t clockHasBeenSynced = 0; // Text rendering settings uint8_t extraParagraphSpacing = 1; static constexpr uint8_t WORD_SPACING_MIN = 50; static constexpr uint8_t WORD_SPACING_MAX = 200; static constexpr uint8_t WORD_SPACING_STEP = 25; uint8_t wordSpacing = 100; // percent of the font's space advance static constexpr uint8_t CHARACTER_SPACING_OFFSET = 2; // stored 0..4 maps to -2..+2 px uint8_t characterSpacing = CHARACTER_SPACING_OFFSET; int8_t getCharacterSpacing() const { return static_cast<int8_t>(characterSpacing - CHARACTER_SPACING_OFFSET); } uint8_t textAntiAliasing = 1; // Short power button click behaviour uint8_t shortPwrBtn = IGNORE; // X4 Pro: double-click power toggles the frontlight. Disabling frees the // power button for shortPwrBtn actions without the double-click wait. uint8_t doubleClickPwrLight = 1; uint8_t homeButtonTapAction = static_cast<uint8_t>(HomeButtonAction::Home); uint8_t homeButtonDoubleTapAction = static_cast<uint8_t>(HomeButtonAction::ToggleFrontlight); uint8_t homeButtonLongPressAction = static_cast<uint8_t>(HomeButtonAction::ReaderMenu); // EPUB reading orientation settings // 0 = portrait (default), 1 = landscape clockwise, 2 = inverted, 3 = landscape counter-clockwise uint8_t orientation = PORTRAIT; // Button layouts (front layout retained for migration only) uint8_t frontButtonLayout = BACK_CONFIRM_LEFT_RIGHT; uint8_t sideButtonLayout = PREV_NEXT; uint8_t frontButtonFollowOrientation = 0; // Front button remap (logical -> hardware) // Used by MappedInputManager to translate logical buttons into physical front buttons. uint8_t frontButtonBack = FRONT_HW_BACK; uint8_t frontButtonConfirm = FRONT_HW_CONFIRM; uint8_t frontButtonLeft = FRONT_HW_LEFT; uint8_t frontButtonRight = FRONT_HW_RIGHT; // Reader font settings uint8_t fontFamily = NOTOSERIF; // Point size of the reader font. Only sizes the active family actually ships // are selectable; SdCardFontSystem::ensureLoaded() snaps this to the nearest // available size (and persists the snap) whenever the family changes. uint8_t fontPointSize = DEFAULT_FONT_POINT_SIZE; uint8_t lineSpacing = NORMAL; uint8_t paragraphAlignment = JUSTIFIED; // Auto-sleep timeout setting (default 10 minutes). Legacy sleepTimeout enum values are migration-only. uint8_t sleepTimeoutMinutes = 10; // E-ink refresh frequency (default 15 pages) uint8_t refreshFrequency = REFRESH_15; uint8_t hyphenationEnabled = 0;
// Reader screen margin settings static constexpr uint8_t SCREEN_MARGIN_MIN = 5; static constexpr uint8_t SCREEN_MARGIN_MAX = 40; static constexpr uint8_t SCREEN_MARGIN_STEP = 5; uint8_t screenMargin = SCREEN_MARGIN_MIN; // OPDS download destination folder ("" = SD root). Global; edited from the // OPDS server list. Persisted via a category-less SettingInfo::String in // SettingsList.h, so it stays out of the on-device Settings screen. char opdsDownloadFolder[64] = ""; // On-disk filename format for OPDS downloads (0=Author-Title default, 1=Title-Author, // 2=Title). See OpdsFilenameFormat. Persisted via a category-less SettingInfo::Enum, // edited from the OPDS server list; hidden from the on-device Settings screen. uint8_t opdsFilenameFormat = 0; // Hide battery percentage uint8_t hideBatteryPercentage = HIDE_NEVER; // Long-press page turn button behavior uint8_t longPressButtonBehavior = OFF; // Long-press Confirm function in EPUB reader (cycles through LONG_PRESS_MENU_FUNCTION values). // Defaults to Disabled so shortcut-based bookmark toggling remains opt-in. uint8_t longPressMenuFunction = LP_MENU_DISABLED; // UI Theme uint8_t uiTheme = LYRA; // Sunlight fading compensation uint8_t fadingFix = 0; // Power button return from footnotes (1 = enabled, 0 = disabled) uint8_t pwrBtnFootnoteBack = 1; // Use book's embedded CSS styles for EPUB rendering (1 = enabled, 0 = disabled) uint8_t embeddedStyle = 1; // Focus Reading - emphasizes the first part of words with bold uint8_t focusReadingEnabled = 0; uint8_t readerMenuStyle = READER_MENU_LIST; // SD card font family name (empty = use built-in fontFamily) char sdFontFamilyName[32] = ""; // Dictionary folder name under /dictionaries (empty = no dictionary) char dictionaryName[32] = ""; // Show hidden files/directories (starting with '.') in the file browser (0 = hidden, 1 = show) uint8_t showHiddenFiles = 0; // Show the title and author read from inside each book rather than its // filename. Users can disable this to make index rebuilds skip EPUB parsing. uint8_t libraryUseMetadata = 1; // Remove a book from the Recent Books list when its End-of-Book screen is reached (0 = off, 1 = on) uint8_t removeReadBooksFromRecents = 0; // Move epub to /Read/ folder on SD card when finished (0 = disabled, 1 = enabled) uint8_t moveFinishedToReadFolder = 0; // Short press Back goes to file browser instead of home (0 = disabled, 1 = enabled) uint8_t backShortToFileBrowser = 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; // Master reader-touch toggle on boards with a touch controller. uint8_t touchReaderControls = TOUCH_READER_ON; // Which gestures turn the page in each direction (PAGE_TURN_GESTURE). uint8_t pageTurnGesture = SWIPE_ONLY; uint8_t previousPageGesture = SWIPE_ONLY; // Reader menu open gesture (SHOW_READER_MENU: off / center tap / bottom-edge // up-swipe). Only surfaced on home-key boards, where Home is the capacitive // key and the bottom edge is free; elsewhere it stays at the Tap default. uint8_t showReaderMenu = READER_MENU_TAP; // Frontlight quick-panel state. Category-less SettingsList entries persist // these without adding them to the regular Settings screen. uint8_t frontlightBrightness = 60; uint8_t frontlightWarmth = 50; // 0 = cool .. 100 = warm uint8_t frontlightOn = 0; // Restore the saved on/off state after a normal boot or wake. Brightness and // warmth are always remembered even when this is disabled. uint8_t frontlightRestoreOnWake = 1; // Language setting (Language enum index, default 0 = EN) uint8_t language = 0; // Keyboard layouts the user can reach, using keyboard_layouts::ALL table bits. // 0 means "not configured", resolved to the UI language's layout plus English. // Any other value is an explicit choice and is used as-is: the language of the // books someone reads is not necessarily the language of their UI. // See keyboard_layouts:: for the bit assignment and the defaulting rules. uint16_t keyboardLayouts = 0; // Quick Resume: keep current content visible with moon icon instead of showing a static sleep screen. uint8_t quickResumeSleepScreen = QUICK_RESUME_NEVER;
static constexpr uint8_t MIN_SLEEP_TIMEOUT_MINUTES = 1; static constexpr uint8_t SLEEP_TIMEOUT_NEVER_MINUTES = 31; static constexpr uint8_t MAX_SLEEP_TIMEOUT_MINUTES = SLEEP_TIMEOUT_NEVER_MINUTES;
// Callback to resolve SD card font IDs. Set by SdCardFontSystem::begin(). // Returns font ID or 0 if not found. using SdFontIdResolver = int (*)(void* ctx, const char* familyName, uint8_t fontSize); SdFontIdResolver sdFontIdResolver = nullptr; void* sdFontResolverCtx = nullptr;
uint16_t getPowerButtonDuration() const { return (shortPwrBtn == CrossPointSettings::SHORT_PWRBTN::SLEEP) ? 10 : 400; } int getReaderFontId() const;
// Drop the SD font selection and fall back to the built-in family. The reader // point size comes back into BUILTIN_READER_POINT_SIZES with it, since that is // the only set a built-in family ships — otherwise the settings UI would keep // offering a size nothing renders at. Both fields are persisted in one write. void clearSdFontFamily();
// Resolved status-bar composition. Consumers read the spec; only settings // editors read the raw fields. // // Deliberately NOT built under storeMutex: every field it reads is a single // byte, so a concurrent settings write can never produce a corrupt value — // only a snapshot mixing pre- and post-change fields. That costs at most one // e-ink frame drawn with a mixed status bar, which self-corrects on the next // refresh. Locking here would instead put a mutex on the render path and // stall it behind the SD write inside saveToFile(). Don't add one back. struct StatusBarSpec { bool showChapterPageCount = false; bool showBookProgressPercent = false; uint8_t titleMode = HIDE_TITLE; // STATUS_BAR_TITLE bool showBattery = false; bool showBatteryPercent = false; uint8_t clockMode = STATUS_BAR_CLOCK_HIDE; // STATUS_BAR_CLOCK_MODE bool clock12h = false; uint8_t progressBarMode = HIDE_PROGRESS; // STATUS_BAR_PROGRESS_BAR uint8_t progressBarHeightPx = 0; // (thickness+1)*2; 0 when the bar is hidden uint8_t xtcMode = XTC_STATUS_BAR_HIDE; // XTC_STATUS_BAR_MODE
bool showsProgressBar() const { return progressBarMode != HIDE_PROGRESS; } bool showsTitle() const { return titleMode != HIDE_TITLE; } bool showsClock() const { return clockMode != STATUS_BAR_CLOCK_HIDE; } // Visibility of the text lane. Clock hardware presence is the caller's // concern: pass halClock.isAvailable(), or true for layout reservation. bool textLaneVisible(bool clockAvailable) const { return showChapterPageCount || showBookProgressPercent || showsTitle() || showBattery || (showsClock() && clockAvailable); } }; StatusBarSpec statusBarSpec() const;
// Resolved text-rendering configuration for the Epub layout engine. The // viewport is renderer/orientation-derived, so the caller supplies it — // passing it in keeps a spec from ever existing in a half-filled state. // Unlocked for the same reason as statusBarSpec(); see the note above. ReaderRenderSpec readerRenderSpec(uint16_t viewportWidth, uint16_t viewportHeight) const;
static const char* getFilePath() { return "/.crosspoint/settings.json"; } void toJson(JsonDocument& doc) const; bool fromJson(JsonVariantConst doc);
static void validateFrontButtonMapping(CrossPointSettings& settings); static uint8_t sleepTimeoutEnumToMinutes(uint8_t legacyValue);
float getReaderLineCompression() const; unsigned long getSleepTimeoutMs() const; int getRefreshFrequency() const;};
// Helper macro to access settings#define SETTINGS CrossPointSettings::getInstance()