Add quick resume
This commit is contained in:
@@ -674,3 +674,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Запаўняльнік для вялікіх в
|
|||||||
STR_LARGE_IMAGE: "Вялікая выява"
|
STR_LARGE_IMAGE: "Вялікая выява"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Націсніце Пацв. для загрузкі"
|
STR_PRESS_CONFIRM_TO_LOAD: "Націсніце Пацв. для загрузкі"
|
||||||
STR_TOOL_UTILITIES: "Дапаможныя"
|
STR_TOOL_UTILITIES: "Дапаможныя"
|
||||||
|
STR_QUICK_RESUME: "Хуткае узнаўленне"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Хуткае узнаўленне пасля таймаўту"
|
||||||
|
|||||||
@@ -294,3 +294,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Text de mostra per a imatges grans"
|
|||||||
STR_LARGE_IMAGE: "Imatge gran"
|
STR_LARGE_IMAGE: "Imatge gran"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Premeu Confirma per carregar"
|
STR_PRESS_CONFIRM_TO_LOAD: "Premeu Confirma per carregar"
|
||||||
STR_TOOL_UTILITIES: "Utilitats"
|
STR_TOOL_UTILITIES: "Utilitats"
|
||||||
|
STR_QUICK_RESUME: "Represa ràpida"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Represa ràpida després del temps"
|
||||||
|
|||||||
@@ -265,3 +265,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Zástupný symbol pro velké obrázky"
|
|||||||
STR_LARGE_IMAGE: "Velký obrázek"
|
STR_LARGE_IMAGE: "Velký obrázek"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Stiskněte Potvrdit pro načtení"
|
STR_PRESS_CONFIRM_TO_LOAD: "Stiskněte Potvrdit pro načtení"
|
||||||
STR_TOOL_UTILITIES: "Nástroje"
|
STR_TOOL_UTILITIES: "Nástroje"
|
||||||
|
STR_QUICK_RESUME: "Rychlé navázání"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Rychlé navázání po vypršení"
|
||||||
|
|||||||
@@ -294,3 +294,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Pladsholder for store billeder"
|
|||||||
STR_LARGE_IMAGE: "Stort billede"
|
STR_LARGE_IMAGE: "Stort billede"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Tryk på Bekræft for at indlæse"
|
STR_PRESS_CONFIRM_TO_LOAD: "Tryk på Bekræft for at indlæse"
|
||||||
STR_TOOL_UTILITIES: "Hjælpeværktøjer"
|
STR_TOOL_UTILITIES: "Hjælpeværktøjer"
|
||||||
|
STR_QUICK_RESUME: "Hurtig genoptagelse"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Hurtig genoptagelse ved timeout"
|
||||||
|
|||||||
@@ -672,3 +672,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Placeholder voor grote afbeeldingen"
|
|||||||
STR_LARGE_IMAGE: "Grote afbeelding"
|
STR_LARGE_IMAGE: "Grote afbeelding"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Druk op Bevestig om te laden"
|
STR_PRESS_CONFIRM_TO_LOAD: "Druk op Bevestig om te laden"
|
||||||
STR_TOOL_UTILITIES: "Hulpmiddelen"
|
STR_TOOL_UTILITIES: "Hulpmiddelen"
|
||||||
|
STR_QUICK_RESUME: "Snel hervatten"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Snel hervatten bij timeout"
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ STR_CAT_READER: "Reader"
|
|||||||
STR_CAT_CONTROLS: "Controls"
|
STR_CAT_CONTROLS: "Controls"
|
||||||
STR_CAT_SYSTEM: "System"
|
STR_CAT_SYSTEM: "System"
|
||||||
STR_SLEEP_SCREEN: "Sleep Screen"
|
STR_SLEEP_SCREEN: "Sleep Screen"
|
||||||
|
STR_QUICK_RESUME: "Quick Resume"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Quick Resume on Timeout"
|
||||||
STR_SLEEP_COVER_MODE: "Sleep Screen Cover Mode"
|
STR_SLEEP_COVER_MODE: "Sleep Screen Cover Mode"
|
||||||
STR_HIDE_BATTERY: "Hide Battery %"
|
STR_HIDE_BATTERY: "Hide Battery %"
|
||||||
STR_EXTRA_SPACING: "Extra Paragraph Spacing"
|
STR_EXTRA_SPACING: "Extra Paragraph Spacing"
|
||||||
|
|||||||
@@ -264,3 +264,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Paikkamerkki suurille kuville"
|
|||||||
STR_LARGE_IMAGE: "Suuri kuva"
|
STR_LARGE_IMAGE: "Suuri kuva"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Paina Vahvista ladataksesi"
|
STR_PRESS_CONFIRM_TO_LOAD: "Paina Vahvista ladataksesi"
|
||||||
STR_TOOL_UTILITIES: "Apuvälineet"
|
STR_TOOL_UTILITIES: "Apuvälineet"
|
||||||
|
STR_QUICK_RESUME: "Pikajatko"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Pikajatko aikakatkaisulla"
|
||||||
|
|||||||
@@ -673,3 +673,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Espace réservé pour les grandes images"
|
|||||||
STR_LARGE_IMAGE: "Grande image"
|
STR_LARGE_IMAGE: "Grande image"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Appuyez sur Confirmer pour charger"
|
STR_PRESS_CONFIRM_TO_LOAD: "Appuyez sur Confirmer pour charger"
|
||||||
STR_TOOL_UTILITIES: "Utilitaire"
|
STR_TOOL_UTILITIES: "Utilitaire"
|
||||||
|
STR_QUICK_RESUME: "Reprise rapide"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Reprise rapide après délai"
|
||||||
|
|||||||
@@ -674,3 +674,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Platzhalter für große Bilder"
|
|||||||
STR_LARGE_IMAGE: "Großes Bild"
|
STR_LARGE_IMAGE: "Großes Bild"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Zum Laden Bestätigen drücken"
|
STR_PRESS_CONFIRM_TO_LOAD: "Zum Laden Bestätigen drücken"
|
||||||
STR_TOOL_UTILITIES: "Hilfsfunktionen"
|
STR_TOOL_UTILITIES: "Hilfsfunktionen"
|
||||||
|
STR_QUICK_RESUME: "Schnelles Fortsetzen"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Schnelles Fortsetzen nach Timeout"
|
||||||
|
|||||||
@@ -294,3 +294,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Helyőrző nagy képekhez"
|
|||||||
STR_LARGE_IMAGE: "Nagy kép"
|
STR_LARGE_IMAGE: "Nagy kép"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Nyomja meg a Megerősítés gombot a betöltéshez"
|
STR_PRESS_CONFIRM_TO_LOAD: "Nyomja meg a Megerősítés gombot a betöltéshez"
|
||||||
STR_TOOL_UTILITIES: "Segédeszközök"
|
STR_TOOL_UTILITIES: "Segédeszközök"
|
||||||
|
STR_QUICK_RESUME: "Gyors folytatás"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Gyors folytatás időtúllépéskor"
|
||||||
|
|||||||
@@ -673,3 +673,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Segnaposto per immagini grandi"
|
|||||||
STR_LARGE_IMAGE: "Immagine grande"
|
STR_LARGE_IMAGE: "Immagine grande"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Premi Conferma per caricare"
|
STR_PRESS_CONFIRM_TO_LOAD: "Premi Conferma per caricare"
|
||||||
STR_TOOL_UTILITIES: "Utilità"
|
STR_TOOL_UTILITIES: "Utilità"
|
||||||
|
STR_QUICK_RESUME: "Ripresa rapida"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Ripresa rapida dopo timeout"
|
||||||
|
|||||||
@@ -293,3 +293,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Үлкен суреттерге арналған
|
|||||||
STR_LARGE_IMAGE: "Үлкен сурет"
|
STR_LARGE_IMAGE: "Үлкен сурет"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Жүктеу үшін Растау түймесін басыңыз"
|
STR_PRESS_CONFIRM_TO_LOAD: "Жүктеу үшін Растау түймесін басыңыз"
|
||||||
STR_TOOL_UTILITIES: "Құралдар"
|
STR_TOOL_UTILITIES: "Құралдар"
|
||||||
|
STR_QUICK_RESUME: "Жылдам жалғастыру"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Таймауттан кейін жылдам жалғастыру"
|
||||||
|
|||||||
@@ -294,3 +294,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Vietaženklis dideliems paveikslėliams"
|
|||||||
STR_LARGE_IMAGE: "Didelis paveikslėlis"
|
STR_LARGE_IMAGE: "Didelis paveikslėlis"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Paspauskite Gerai, kad įkeltumėte"
|
STR_PRESS_CONFIRM_TO_LOAD: "Paspauskite Gerai, kad įkeltumėte"
|
||||||
STR_TOOL_UTILITIES: "Pagalbiniai įrankiai"
|
STR_TOOL_UTILITIES: "Pagalbiniai įrankiai"
|
||||||
|
STR_QUICK_RESUME: "Greitas tęsimas"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Greitas tęsimas po skirtojo laiko"
|
||||||
|
|||||||
@@ -687,3 +687,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Ramka dużego obrazka"
|
|||||||
STR_LARGE_IMAGE: "Duży obrazek"
|
STR_LARGE_IMAGE: "Duży obrazek"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Naciśnij Potwierdź, aby wczytać"
|
STR_PRESS_CONFIRM_TO_LOAD: "Naciśnij Potwierdź, aby wczytać"
|
||||||
STR_TOOL_UTILITIES: "Narzędzia"
|
STR_TOOL_UTILITIES: "Narzędzia"
|
||||||
|
STR_QUICK_RESUME: "Szybkie wznawianie"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Szybkie wznawianie po czasie"
|
||||||
|
|||||||
@@ -673,3 +673,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Marcador para imagens grandes"
|
|||||||
STR_LARGE_IMAGE: "Imagem grande"
|
STR_LARGE_IMAGE: "Imagem grande"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Pressione Confirmar para carregar"
|
STR_PRESS_CONFIRM_TO_LOAD: "Pressione Confirmar para carregar"
|
||||||
STR_TOOL_UTILITIES: "Utilitários"
|
STR_TOOL_UTILITIES: "Utilitários"
|
||||||
|
STR_QUICK_RESUME: "Retomada rápida"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Retomada rápida após tempo limite"
|
||||||
|
|||||||
@@ -672,3 +672,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Espaço reservado para imagens grandes"
|
|||||||
STR_LARGE_IMAGE: "Imagem grande"
|
STR_LARGE_IMAGE: "Imagem grande"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Prima Confirmar para carregar"
|
STR_PRESS_CONFIRM_TO_LOAD: "Prima Confirmar para carregar"
|
||||||
STR_TOOL_UTILITIES: "Utilitários"
|
STR_TOOL_UTILITIES: "Utilitários"
|
||||||
|
STR_QUICK_RESUME: "Retomada rápida"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Retomada rápida após tempo limite"
|
||||||
|
|||||||
@@ -294,3 +294,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Substituent pentru imagini mari"
|
|||||||
STR_LARGE_IMAGE: "Imagine mare"
|
STR_LARGE_IMAGE: "Imagine mare"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Apăsați Confirmă pentru încărcare"
|
STR_PRESS_CONFIRM_TO_LOAD: "Apăsați Confirmă pentru încărcare"
|
||||||
STR_TOOL_UTILITIES: "Utilitare"
|
STR_TOOL_UTILITIES: "Utilitare"
|
||||||
|
STR_QUICK_RESUME: "Reluare rapidă"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Reluare rapidă la timeout"
|
||||||
|
|||||||
@@ -673,3 +673,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Заглушка для больших изобр
|
|||||||
STR_LARGE_IMAGE: "Большое изображение"
|
STR_LARGE_IMAGE: "Большое изображение"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Нажмите Подтв. для загрузки"
|
STR_PRESS_CONFIRM_TO_LOAD: "Нажмите Подтв. для загрузки"
|
||||||
STR_TOOL_UTILITIES: "Утилиты"
|
STR_TOOL_UTILITIES: "Утилиты"
|
||||||
|
STR_QUICK_RESUME: "Быстрое возобновление"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Быстрое возобновление по таймауту"
|
||||||
|
|||||||
@@ -672,3 +672,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Oznaka mesta za velike slike"
|
|||||||
STR_LARGE_IMAGE: "Velika slika"
|
STR_LARGE_IMAGE: "Velika slika"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Pritisnite Potrdi za nalaganje"
|
STR_PRESS_CONFIRM_TO_LOAD: "Pritisnite Potrdi za nalaganje"
|
||||||
STR_TOOL_UTILITIES: "Pomočnik"
|
STR_TOOL_UTILITIES: "Pomočnik"
|
||||||
|
STR_QUICK_RESUME: "Hitro nadaljevanje"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Hitro nadaljevanje po izteku"
|
||||||
|
|||||||
@@ -673,3 +673,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Marcador para imágenes grandes"
|
|||||||
STR_LARGE_IMAGE: "Imagen grande"
|
STR_LARGE_IMAGE: "Imagen grande"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Pulsa Confirmar para cargar"
|
STR_PRESS_CONFIRM_TO_LOAD: "Pulsa Confirmar para cargar"
|
||||||
STR_TOOL_UTILITIES: "Utilidades"
|
STR_TOOL_UTILITIES: "Utilidades"
|
||||||
|
STR_QUICK_RESUME: "Reanudación rápida"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Reanudación rápida tras tiempo"
|
||||||
|
|||||||
@@ -672,3 +672,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Platshållare för stora bilder"
|
|||||||
STR_LARGE_IMAGE: "Stor bild"
|
STR_LARGE_IMAGE: "Stor bild"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Tryck på Bekräfta för att läsa in"
|
STR_PRESS_CONFIRM_TO_LOAD: "Tryck på Bekräfta för att läsa in"
|
||||||
STR_TOOL_UTILITIES: "Hjälpverktyg"
|
STR_TOOL_UTILITIES: "Hjälpverktyg"
|
||||||
|
STR_QUICK_RESUME: "Snabb återupptagning"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Snabb återupptagning efter timeout"
|
||||||
|
|||||||
@@ -672,3 +672,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Büyük görseller için yer tutucu"
|
|||||||
STR_LARGE_IMAGE: "Büyük görsel"
|
STR_LARGE_IMAGE: "Büyük görsel"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Yüklemek için Onayla'ya basın"
|
STR_PRESS_CONFIRM_TO_LOAD: "Yüklemek için Onayla'ya basın"
|
||||||
STR_TOOL_UTILITIES: "Yardımcı"
|
STR_TOOL_UTILITIES: "Yardımcı"
|
||||||
|
STR_QUICK_RESUME: "Hızlı Devam"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Zaman aşımında Hızlı Devam"
|
||||||
|
|||||||
@@ -672,3 +672,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Заглушка для великих зобра
|
|||||||
STR_LARGE_IMAGE: "Велике зображення"
|
STR_LARGE_IMAGE: "Велике зображення"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Натисніть Підтвердити, щоб завантажити"
|
STR_PRESS_CONFIRM_TO_LOAD: "Натисніть Підтвердити, щоб завантажити"
|
||||||
STR_TOOL_UTILITIES: "Корисні інструменти"
|
STR_TOOL_UTILITIES: "Корисні інструменти"
|
||||||
|
STR_QUICK_RESUME: "Швидке продовження"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Швидке продовження після таймауту"
|
||||||
|
|||||||
@@ -672,3 +672,5 @@ STR_LARGE_IMAGE_PLACEHOLDER: "Giữ chỗ cho hình ảnh lớn"
|
|||||||
STR_LARGE_IMAGE: "Hình ảnh lớn"
|
STR_LARGE_IMAGE: "Hình ảnh lớn"
|
||||||
STR_PRESS_CONFIRM_TO_LOAD: "Nhấn Xác nhận để tải"
|
STR_PRESS_CONFIRM_TO_LOAD: "Nhấn Xác nhận để tải"
|
||||||
STR_TOOL_UTILITIES: "Trợ giúp"
|
STR_TOOL_UTILITIES: "Trợ giúp"
|
||||||
|
STR_QUICK_RESUME: "Tiếp tục nhanh"
|
||||||
|
STR_QUICK_RESUME_TIMEOUT: "Tiếp tục nhanh sau khi hết thời gian"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ HalDisplay::HalDisplay() : einkDisplay(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_R
|
|||||||
|
|
||||||
HalDisplay::~HalDisplay() {}
|
HalDisplay::~HalDisplay() {}
|
||||||
|
|
||||||
void HalDisplay::begin() {
|
void HalDisplay::begin(bool seamless) {
|
||||||
// Set X3-specific panel mode before initializing.
|
// Set X3-specific panel mode before initializing.
|
||||||
if (gpio.deviceIsX3()) {
|
if (gpio.deviceIsX3()) {
|
||||||
einkDisplay.setDisplayX3();
|
einkDisplay.setDisplayX3();
|
||||||
@@ -18,11 +18,14 @@ void HalDisplay::begin() {
|
|||||||
|
|
||||||
einkDisplay.begin();
|
einkDisplay.begin();
|
||||||
|
|
||||||
// Request resync after specific wakeup events to ensure clean display state
|
// Request resync after specific wakeup events to ensure clean display state.
|
||||||
const auto wakeupReason = gpio.getWakeupReason();
|
// Skip when seamless=true so the current panel content is preserved (Quick Resume).
|
||||||
if (wakeupReason == HalGPIO::WakeupReason::PowerButton || wakeupReason == HalGPIO::WakeupReason::AfterFlash ||
|
if (!seamless) {
|
||||||
wakeupReason == HalGPIO::WakeupReason::Other) {
|
const auto wakeupReason = gpio.getWakeupReason();
|
||||||
einkDisplay.requestResync();
|
if (wakeupReason == HalGPIO::WakeupReason::PowerButton || wakeupReason == HalGPIO::WakeupReason::AfterFlash ||
|
||||||
|
wakeupReason == HalGPIO::WakeupReason::Other) {
|
||||||
|
einkDisplay.requestResync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ class HalDisplay {
|
|||||||
FAST_REFRESH // Fast refresh using custom LUT
|
FAST_REFRESH // Fast refresh using custom LUT
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize the display hardware and driver
|
// Initialize the display hardware and driver.
|
||||||
void begin();
|
// When seamless=true, skip the on-wake resync so existing panel content is preserved
|
||||||
|
// (used by Quick Resume to bring back the last reader page without a boot screen).
|
||||||
|
void begin(bool seamless = false);
|
||||||
|
|
||||||
// Display dimensions
|
// Display dimensions
|
||||||
static constexpr uint16_t DISPLAY_WIDTH = EInkDisplay::DISPLAY_WIDTH;
|
static constexpr uint16_t DISPLAY_WIDTH = EInkDisplay::DISPLAY_WIDTH;
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ void enforceFixedShortActions(CrossPointSettings& settings) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
void CrossPointSettings::normalizeDependentSettings(CrossPointSettings& settings) {
|
||||||
|
if (settings.sleepScreen == SLEEP_SCREEN_MODE::QUICK_RESUME) {
|
||||||
|
settings.quickResumeSleepScreen = QUICK_RESUME_SLEEP_SCREEN::QUICK_RESUME_AFTER_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CrossPointSettings::validateFrontButtonMapping(CrossPointSettings& settings) {
|
void CrossPointSettings::validateFrontButtonMapping(CrossPointSettings& settings) {
|
||||||
const uint8_t mapping[] = {settings.frontButtonBack, settings.frontButtonConfirm, settings.frontButtonLeft,
|
const uint8_t mapping[] = {settings.frontButtonBack, settings.frontButtonConfirm, settings.frontButtonLeft,
|
||||||
settings.frontButtonRight};
|
settings.frontButtonRight};
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class CrossPointSettings {
|
|||||||
BLANK = 4,
|
BLANK = 4,
|
||||||
COVER_CUSTOM = 5,
|
COVER_CUSTOM = 5,
|
||||||
OVERLAY = 6,
|
OVERLAY = 6,
|
||||||
|
QUICK_RESUME = 7,
|
||||||
SLEEP_SCREEN_MODE_COUNT
|
SLEEP_SCREEN_MODE_COUNT
|
||||||
};
|
};
|
||||||
enum SLEEP_SCREEN_COVER_MODE { FIT = 0, CROP = 1, SLEEP_SCREEN_COVER_MODE_COUNT };
|
enum SLEEP_SCREEN_COVER_MODE { FIT = 0, CROP = 1, SLEEP_SCREEN_COVER_MODE_COUNT };
|
||||||
@@ -35,6 +36,11 @@ class CrossPointSettings {
|
|||||||
INVERTED_BLACK_AND_WHITE = 2,
|
INVERTED_BLACK_AND_WHITE = 2,
|
||||||
SLEEP_SCREEN_COVER_FILTER_COUNT
|
SLEEP_SCREEN_COVER_FILTER_COUNT
|
||||||
};
|
};
|
||||||
|
enum QUICK_RESUME_SLEEP_SCREEN {
|
||||||
|
QUICK_RESUME_NEVER = 0,
|
||||||
|
QUICK_RESUME_AFTER_TIMEOUT = 1,
|
||||||
|
QUICK_RESUME_SLEEP_SCREEN_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
// Status bar enum - legacy
|
// Status bar enum - legacy
|
||||||
enum STATUS_BAR_MODE {
|
enum STATUS_BAR_MODE {
|
||||||
@@ -198,6 +204,9 @@ class CrossPointSettings {
|
|||||||
uint8_t sleepCoverOverlay = 0;
|
uint8_t sleepCoverOverlay = 0;
|
||||||
// Sleep image pick mode (random vs sequential walk-through)
|
// Sleep image pick mode (random vs sequential walk-through)
|
||||||
uint8_t sleepImagePickMode = PICK_RANDOM;
|
uint8_t sleepImagePickMode = PICK_RANDOM;
|
||||||
|
// Quick Resume on Timeout: keep current page on display with a moon icon when sleeping by timeout,
|
||||||
|
// and on wake restore the page directly (skipping the boot screen).
|
||||||
|
uint8_t quickResumeSleepScreen = QUICK_RESUME_NEVER;
|
||||||
// Status bar settings (statusBar, statusBarProgressBar, statusBarProgressBarThickness retained for migration only)
|
// Status bar settings (statusBar, statusBarProgressBar, statusBarProgressBarThickness retained for migration only)
|
||||||
uint8_t statusBar = FULL;
|
uint8_t statusBar = FULL;
|
||||||
uint8_t statusBarChapterPageCount = 1;
|
uint8_t statusBarChapterPageCount = 1;
|
||||||
@@ -379,6 +388,10 @@ class CrossPointSettings {
|
|||||||
|
|
||||||
static void validateFrontButtonMapping(CrossPointSettings& settings);
|
static void validateFrontButtonMapping(CrossPointSettings& settings);
|
||||||
|
|
||||||
|
// Enforce settings whose values depend on others (e.g. sleepScreen=QUICK_RESUME implies
|
||||||
|
// quickResumeSleepScreen=ON). Call after any setting mutation that could invalidate the pair.
|
||||||
|
static void normalizeDependentSettings(CrossPointSettings& settings);
|
||||||
|
|
||||||
float getReaderLineCompression() const;
|
float getReaderLineCompression() const;
|
||||||
unsigned long getSleepTimeoutMs() const;
|
unsigned long getSleepTimeoutMs() const;
|
||||||
int getRefreshFrequency() const;
|
int getRefreshFrequency() const;
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ class CrossPointState {
|
|||||||
uint8_t readerActivityLoadCount = 0;
|
uint8_t readerActivityLoadCount = 0;
|
||||||
bool lastSleepFromReader = false;
|
bool lastSleepFromReader = false;
|
||||||
bool recentBooksGridView = false; // true = grid/thumbnail view, false = list view
|
bool recentBooksGridView = false; // true = grid/thumbnail view, false = list view
|
||||||
|
// When false, setup() skips the boot screen on wake and instead restores the
|
||||||
|
// saved framebuffer overlaid with a loading icon (Quick Resume).
|
||||||
|
bool showBootScreen = true;
|
||||||
KOReaderSyncSessionState koReaderSyncSession;
|
KOReaderSyncSessionState koReaderSyncSession;
|
||||||
PendingBookmarkJumpState pendingBookmarkJump;
|
PendingBookmarkJumpState pendingBookmarkJump;
|
||||||
~CrossPointState() = default;
|
~CrossPointState() = default;
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ bool JsonSettingsIO::saveState(const CrossPointState& s, const char* path) {
|
|||||||
doc["readerActivityLoadCount"] = s.readerActivityLoadCount;
|
doc["readerActivityLoadCount"] = s.readerActivityLoadCount;
|
||||||
doc["lastSleepFromReader"] = s.lastSleepFromReader;
|
doc["lastSleepFromReader"] = s.lastSleepFromReader;
|
||||||
doc["recentBooksGridView"] = s.recentBooksGridView;
|
doc["recentBooksGridView"] = s.recentBooksGridView;
|
||||||
|
doc["showBootScreen"] = s.showBootScreen;
|
||||||
// Information about a pending KOReader sync session
|
// Information about a pending KOReader sync session
|
||||||
JsonObject sync = doc["koReaderSyncSession"].to<JsonObject>();
|
JsonObject sync = doc["koReaderSyncSession"].to<JsonObject>();
|
||||||
sync["active"] = s.koReaderSyncSession.active;
|
sync["active"] = s.koReaderSyncSession.active;
|
||||||
@@ -146,6 +147,7 @@ bool JsonSettingsIO::loadState(CrossPointState& s, const char* json) {
|
|||||||
s.readerActivityLoadCount = doc["readerActivityLoadCount"] | (uint8_t)0;
|
s.readerActivityLoadCount = doc["readerActivityLoadCount"] | (uint8_t)0;
|
||||||
s.lastSleepFromReader = doc["lastSleepFromReader"] | false;
|
s.lastSleepFromReader = doc["lastSleepFromReader"] | false;
|
||||||
s.recentBooksGridView = doc["recentBooksGridView"] | false;
|
s.recentBooksGridView = doc["recentBooksGridView"] | false;
|
||||||
|
s.showBootScreen = doc["showBootScreen"] | true;
|
||||||
JsonObject sync = doc["koReaderSyncSession"].as<JsonObject>();
|
JsonObject sync = doc["koReaderSyncSession"].as<JsonObject>();
|
||||||
s.koReaderSyncSession.active = sync["active"] | false;
|
s.koReaderSyncSession.active = sync["active"] | false;
|
||||||
s.koReaderSyncSession.epubPath = sync["epubPath"] | std::string("");
|
s.koReaderSyncSession.epubPath = sync["epubPath"] | std::string("");
|
||||||
@@ -344,6 +346,10 @@ bool JsonSettingsIO::loadSettings(CrossPointSettings& s, const char* json, bool*
|
|||||||
s.moveFinishedBooksToCompleted = doc["moveFinishedBooksToCompleted"] | (uint8_t)0;
|
s.moveFinishedBooksToCompleted = doc["moveFinishedBooksToCompleted"] | (uint8_t)0;
|
||||||
s.removeFinishedBooksFromRecents = doc["removeFinishedBooksFromRecents"] | (uint8_t)0;
|
s.removeFinishedBooksFromRecents = doc["removeFinishedBooksFromRecents"] | (uint8_t)0;
|
||||||
|
|
||||||
|
const uint8_t quickResumeBeforeNormalize = s.quickResumeSleepScreen;
|
||||||
|
CrossPointSettings::normalizeDependentSettings(s);
|
||||||
|
if (s.quickResumeSleepScreen != quickResumeBeforeNormalize && needsResave) *needsResave = true;
|
||||||
|
|
||||||
LOG_DBG("CPS", "Settings loaded from file");
|
LOG_DBG("CPS", "Settings loaded from file");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+3
-1
@@ -54,7 +54,7 @@ inline const std::vector<SettingInfo> list = {
|
|||||||
"sleepTimeout", StrId::STR_CAT_DISPLAY),
|
"sleepTimeout", StrId::STR_CAT_DISPLAY),
|
||||||
SettingInfo::Enum(StrId::STR_SLEEP_SCREEN, &CrossPointSettings::sleepScreen,
|
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_DARK, StrId::STR_LIGHT, StrId::STR_CUSTOM, StrId::STR_COVER, StrId::STR_NONE_OPT,
|
||||||
StrId::STR_COVER_CUSTOM, StrId::STR_PAGE_OVERLAY},
|
StrId::STR_COVER_CUSTOM, StrId::STR_PAGE_OVERLAY, StrId::STR_QUICK_RESUME},
|
||||||
"sleepScreen", StrId::STR_CAT_DISPLAY)
|
"sleepScreen", StrId::STR_CAT_DISPLAY)
|
||||||
.withSubcategory(StrId::STR_MENU_DISP_SLEEP),
|
.withSubcategory(StrId::STR_MENU_DISP_SLEEP),
|
||||||
SettingInfo::Enum(StrId::STR_SLEEP_COVER_MODE, &CrossPointSettings::sleepScreenCoverMode,
|
SettingInfo::Enum(StrId::STR_SLEEP_COVER_MODE, &CrossPointSettings::sleepScreenCoverMode,
|
||||||
@@ -68,6 +68,8 @@ inline const std::vector<SettingInfo> list = {
|
|||||||
"sleepCoverOverlay", StrId::STR_CAT_DISPLAY),
|
"sleepCoverOverlay", StrId::STR_CAT_DISPLAY),
|
||||||
SettingInfo::Enum(StrId::STR_SLEEP_IMAGE_PICK_MODE, &CrossPointSettings::sleepImagePickMode,
|
SettingInfo::Enum(StrId::STR_SLEEP_IMAGE_PICK_MODE, &CrossPointSettings::sleepImagePickMode,
|
||||||
{StrId::STR_RANDOM, StrId::STR_SEQUENTIAL}, "sleepImagePickMode", StrId::STR_CAT_DISPLAY),
|
{StrId::STR_RANDOM, StrId::STR_SEQUENTIAL}, "sleepImagePickMode", StrId::STR_CAT_DISPLAY),
|
||||||
|
SettingInfo::Enum(StrId::STR_QUICK_RESUME_TIMEOUT, &CrossPointSettings::quickResumeSleepScreen,
|
||||||
|
{StrId::STR_STATE_OFF, StrId::STR_STATE_ON}, "quickResumeSleepScreen", StrId::STR_CAT_DISPLAY),
|
||||||
SettingInfo::Enum(StrId::STR_HIDE_BATTERY, &CrossPointSettings::hideBatteryPercentage,
|
SettingInfo::Enum(StrId::STR_HIDE_BATTERY, &CrossPointSettings::hideBatteryPercentage,
|
||||||
{StrId::STR_NEVER, StrId::STR_IN_READER, StrId::STR_ALWAYS}, "hideBatteryPercentage",
|
{StrId::STR_NEVER, StrId::STR_IN_READER, StrId::STR_ALWAYS}, "hideBatteryPercentage",
|
||||||
StrId::STR_CAT_DISPLAY)
|
StrId::STR_CAT_DISPLAY)
|
||||||
|
|||||||
@@ -351,8 +351,8 @@ void ActivityManager::returnFromChild() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActivityManager::goToSleep() {
|
void ActivityManager::goToSleep(bool fromTimeout) {
|
||||||
replaceActivity(std::make_unique<SleepActivity>(renderer, mappedInput));
|
replaceActivity(std::make_unique<SleepActivity>(renderer, mappedInput, fromTimeout));
|
||||||
loop(); // Important: sleep screen must be rendered immediately, the caller will go to sleep right after this returns
|
loop(); // Important: sleep screen must be rendered immediately, the caller will go to sleep right after this returns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ class ActivityManager {
|
|||||||
void goToBrowser();
|
void goToBrowser();
|
||||||
void goToReader(std::string path);
|
void goToReader(std::string path);
|
||||||
void goToKOReaderSync();
|
void goToKOReaderSync();
|
||||||
void goToSleep();
|
// fromTimeout=true marks this as an auto-sleep (used to gate Quick Resume on Timeout).
|
||||||
|
void goToSleep(bool fromTimeout = false);
|
||||||
void goToBoot();
|
void goToBoot();
|
||||||
void goToFullScreenMessage(std::string message, EpdFontFamily::Style style = EpdFontFamily::REGULAR);
|
void goToFullScreenMessage(std::string message, EpdFontFamily::Style style = EpdFontFamily::REGULAR);
|
||||||
void goToWeather();
|
void goToWeather();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
#include "images/Logo120.h"
|
#include "images/Logo120.h"
|
||||||
|
#include "images/MoonIcon.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -353,6 +354,18 @@ size_t pickSleepImageIndex(size_t numFiles) {
|
|||||||
void SleepActivity::onEnter() {
|
void SleepActivity::onEnter() {
|
||||||
Activity::onEnter();
|
Activity::onEnter();
|
||||||
RenderLock lock(*this);
|
RenderLock lock(*this);
|
||||||
|
|
||||||
|
// Quick Resume: paint a moon icon over the current page and keep the framebuffer
|
||||||
|
// intact for the next wake. Applies always when the user picked Quick Resume as
|
||||||
|
// sleep screen, or only on timeout sleeps when "Quick Resume on Timeout" is on.
|
||||||
|
const bool renderQuickResume =
|
||||||
|
SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::QUICK_RESUME ||
|
||||||
|
(fromTimeout &&
|
||||||
|
SETTINGS.quickResumeSleepScreen == CrossPointSettings::QUICK_RESUME_SLEEP_SCREEN::QUICK_RESUME_AFTER_TIMEOUT);
|
||||||
|
if (renderQuickResume) {
|
||||||
|
return renderLastScreenSleepScreen();
|
||||||
|
}
|
||||||
|
|
||||||
// For OVERLAY mode the popup is suppressed so the frame buffer (reader page) stays intact
|
// For OVERLAY mode the popup is suppressed so the frame buffer (reader page) stays intact
|
||||||
if (SETTINGS.sleepScreen != CrossPointSettings::SLEEP_SCREEN_MODE::OVERLAY) {
|
if (SETTINGS.sleepScreen != CrossPointSettings::SLEEP_SCREEN_MODE::OVERLAY) {
|
||||||
GUI.drawPopup(renderer, tr(STR_ENTERING_SLEEP));
|
GUI.drawPopup(renderer, tr(STR_ENTERING_SLEEP));
|
||||||
@@ -804,6 +817,15 @@ void SleepActivity::renderBlankSleepScreen() const {
|
|||||||
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SleepActivity::renderLastScreenSleepScreen() const {
|
||||||
|
// Keep whatever is currently in the framebuffer (the reader page) and overlay a small moon
|
||||||
|
// icon to signal sleep. main.cpp persists the framebuffer to SD so the next wake can restore
|
||||||
|
// it before the boot screen would otherwise paint.
|
||||||
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
renderer.drawImage(MoonIcon, 0, pageHeight - MOONICON_HEIGHT, MOONICON_WIDTH, MOONICON_HEIGHT);
|
||||||
|
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||||
|
}
|
||||||
|
|
||||||
void SleepActivity::renderOverlaySleepScreen() const {
|
void SleepActivity::renderOverlaySleepScreen() const {
|
||||||
// Overlay pictures always use portrait orientation regardless of the reader's orientation preference.
|
// Overlay pictures always use portrait orientation regardless of the reader's orientation preference.
|
||||||
const auto savedOrientation = renderer.getOrientation();
|
const auto savedOrientation = renderer.getOrientation();
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ struct BookOverlayInfo {
|
|||||||
|
|
||||||
class SleepActivity final : public Activity {
|
class SleepActivity final : public Activity {
|
||||||
public:
|
public:
|
||||||
explicit SleepActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
explicit SleepActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, bool fromTimeout = false)
|
||||||
: Activity("Sleep", renderer, mappedInput) {}
|
: Activity("Sleep", renderer, mappedInput), fromTimeout(fromTimeout) {}
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -28,5 +28,9 @@ class SleepActivity final : public Activity {
|
|||||||
bool topAlignForCoverFit = false) const;
|
bool topAlignForCoverFit = false) const;
|
||||||
void renderBlankSleepScreen() const;
|
void renderBlankSleepScreen() const;
|
||||||
void renderOverlaySleepScreen() const;
|
void renderOverlaySleepScreen() const;
|
||||||
|
// Quick Resume: leaves the framebuffer (reader page) intact and overlays a small moon icon.
|
||||||
|
void renderLastScreenSleepScreen() const;
|
||||||
BookOverlayInfo getBookOverlayInfo(const std::string& bookPath) const;
|
BookOverlayInfo getBookOverlayInfo(const std::string& bookPath) const;
|
||||||
|
|
||||||
|
const bool fromTimeout = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -286,6 +286,7 @@ void SettingsActivity::toggleCurrentSetting() {
|
|||||||
: FontSelectionActivity::Target::EPUB;
|
: FontSelectionActivity::Target::EPUB;
|
||||||
startActivityForResult(std::make_unique<FontSelectionActivity>(renderer, mappedInput, target),
|
startActivityForResult(std::make_unique<FontSelectionActivity>(renderer, mappedInput, target),
|
||||||
[this](const ActivityResult&) {
|
[this](const ActivityResult&) {
|
||||||
|
CrossPointSettings::normalizeDependentSettings(SETTINGS);
|
||||||
SETTINGS.saveToFile();
|
SETTINGS.saveToFile();
|
||||||
needsHalfRefresh = true;
|
needsHalfRefresh = true;
|
||||||
});
|
});
|
||||||
@@ -294,6 +295,7 @@ void SettingsActivity::toggleCurrentSetting() {
|
|||||||
|
|
||||||
if (setting.type == SettingType::ACTION) {
|
if (setting.type == SettingType::ACTION) {
|
||||||
auto resultHandler = [this](const ActivityResult& result) {
|
auto resultHandler = [this](const ActivityResult& result) {
|
||||||
|
CrossPointSettings::normalizeDependentSettings(SETTINGS);
|
||||||
SETTINGS.saveToFile();
|
SETTINGS.saveToFile();
|
||||||
needsHalfRefresh = true;
|
needsHalfRefresh = true;
|
||||||
const auto* menuResult = std::get_if<MenuResult>(&result.data);
|
const auto* menuResult = std::get_if<MenuResult>(&result.data);
|
||||||
@@ -323,6 +325,7 @@ void SettingsActivity::toggleCurrentSetting() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setting.toggleValue();
|
setting.toggleValue();
|
||||||
|
CrossPointSettings::normalizeDependentSettings(SETTINGS);
|
||||||
SETTINGS.saveToFile();
|
SETTINGS.saveToFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// Image dimensions: 48x48, diagonal dots
|
||||||
|
#define LOADINGICON_WIDTH 48
|
||||||
|
#define LOADINGICON_HEIGHT 48
|
||||||
|
|
||||||
|
static const uint8_t LoadingIcon[] = {
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x07, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x07, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x07, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// Image dimensions: 48x48
|
||||||
|
#define MOONICON_WIDTH 48
|
||||||
|
#define MOONICON_HEIGHT 48
|
||||||
|
|
||||||
|
static const uint8_t MoonIcon[] = {
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xF0, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xC0, 0x1F, 0xF0, 0x3F, 0xFF, 0xFF, 0x00, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFC, 0x07, 0xFF,
|
||||||
|
0xFF, 0xF9, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xF9, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xF9, 0xFF, 0xFE, 0x01, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0xFC, 0x00, 0xFF,
|
||||||
|
0xFF, 0xC7, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xC3, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xC3, 0xFF, 0xF0, 0x00, 0x7F,
|
||||||
|
0xFF, 0xC1, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xC0, 0x7F, 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0xFF, 0xC0, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x03, 0xFF,
|
||||||
|
0xFF, 0xF0, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x0F, 0xFF,
|
||||||
|
0xFF, 0xFE, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
+86
-12
@@ -38,6 +38,7 @@
|
|||||||
#include "activities/settings/SdFirmwareUpdateActivity.h"
|
#include "activities/settings/SdFirmwareUpdateActivity.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
#include "images/LoadingIcon.h"
|
||||||
#include "util/ButtonNavigator.h"
|
#include "util/ButtonNavigator.h"
|
||||||
#include "util/ScreenshotUtil.h"
|
#include "util/ScreenshotUtil.h"
|
||||||
|
|
||||||
@@ -150,8 +151,44 @@ void silentRestartToReader() {
|
|||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enter deep sleep mode
|
// ---- Quick Resume framebuffer persistence ----
|
||||||
void enterDeepSleep() {
|
//
|
||||||
|
// Quick Resume keeps the last reader page on screen during deep sleep (just overlaid with a moon
|
||||||
|
// icon) and skips the boot screen on wake by restoring the saved framebuffer + a loading icon.
|
||||||
|
// The buffer is persisted to SD only when the user actually enabled the feature.
|
||||||
|
constexpr char SLEEP_FRAME_FILE[] = "/.crosspoint/sleep_frame.bin";
|
||||||
|
|
||||||
|
static void saveSleepFrameBuffer() {
|
||||||
|
FsFile file;
|
||||||
|
if (!Storage.openFileForWrite("SLP", SLEEP_FRAME_FILE, file)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file.write(renderer.getFrameBuffer(), renderer.getBufferSize());
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restores the previously saved framebuffer into the display buffer. Returns false if the file is
|
||||||
|
// missing or the size does not match — in that case the caller should fall back to a boot screen.
|
||||||
|
// The file is always removed (success or failure) so a future cold boot does not see a stale frame.
|
||||||
|
static bool loadSleepFrameBuffer() {
|
||||||
|
FsFile file;
|
||||||
|
if (!Storage.openFileForRead("SLP", SLEEP_FRAME_FILE, file)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const size_t bufferSize = display.getBufferSize();
|
||||||
|
const int bytesRead = file.read(display.getFrameBuffer(), bufferSize);
|
||||||
|
file.close();
|
||||||
|
Storage.remove(SLEEP_FRAME_FILE);
|
||||||
|
return static_cast<size_t>(bytesRead) == bufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Earliest millis() value at which a held-power-button press is allowed to trigger sleep.
|
||||||
|
// Set near the end of setup() so a wake-press held a bit too long does not bounce straight
|
||||||
|
// back into deep sleep before the user sees the page.
|
||||||
|
static unsigned long allowSleepAt = 0;
|
||||||
|
|
||||||
|
// Enter deep sleep mode. fromTimeout=true marks an auto-sleep (gates "Quick Resume on Timeout").
|
||||||
|
void enterDeepSleep(bool fromTimeout = false) {
|
||||||
LOG_DBG("MAIN", "enterDeepSleep called at millis=%lu, powerBtn isPressed=%d, rawPin=%d", millis(),
|
LOG_DBG("MAIN", "enterDeepSleep called at millis=%lu, powerBtn isPressed=%d, rawPin=%d", millis(),
|
||||||
gpio.isPressed(HalGPIO::BTN_POWER), digitalRead(InputManager::POWER_BUTTON_PIN) == LOW);
|
gpio.isPressed(HalGPIO::BTN_POWER), digitalRead(InputManager::POWER_BUTTON_PIN) == LOW);
|
||||||
HalPowerManager::Lock powerLock; // Ensure we are at normal CPU frequency for sleep preparation
|
HalPowerManager::Lock powerLock; // Ensure we are at normal CPU frequency for sleep preparation
|
||||||
@@ -166,6 +203,13 @@ void enterDeepSleep() {
|
|||||||
if (APP_STATE.lastSleepFromReader) {
|
if (APP_STATE.lastSleepFromReader) {
|
||||||
APP_STATE.readerActivityLoadCount = 0;
|
APP_STATE.readerActivityLoadCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool isQuickResumeSleep =
|
||||||
|
SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::QUICK_RESUME ||
|
||||||
|
(fromTimeout &&
|
||||||
|
SETTINGS.quickResumeSleepScreen == CrossPointSettings::QUICK_RESUME_SLEEP_SCREEN::QUICK_RESUME_AFTER_TIMEOUT);
|
||||||
|
APP_STATE.showBootScreen = !isQuickResumeSleep;
|
||||||
|
|
||||||
APP_STATE.saveToFile();
|
APP_STATE.saveToFile();
|
||||||
// Tear down WiFi so the modem power domain isn't held alive across deep sleep.
|
// Tear down WiFi so the modem power domain isn't held alive across deep sleep.
|
||||||
// Wake from deep sleep is effectively a chip reset, so no state needs to survive.
|
// Wake from deep sleep is effectively a chip reset, so no state needs to survive.
|
||||||
@@ -173,7 +217,13 @@ void enterDeepSleep() {
|
|||||||
WiFi.disconnect(true);
|
WiFi.disconnect(true);
|
||||||
WiFi.mode(WIFI_OFF);
|
WiFi.mode(WIFI_OFF);
|
||||||
}
|
}
|
||||||
activityManager.goToSleep();
|
activityManager.goToSleep(fromTimeout);
|
||||||
|
|
||||||
|
// Persist the moon-icon-overlaid framebuffer after goToSleep() has painted it.
|
||||||
|
if (isQuickResumeSleep) {
|
||||||
|
saveSleepFrameBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
halTiltSensor.deepSleep();
|
halTiltSensor.deepSleep();
|
||||||
|
|
||||||
display.deepSleep();
|
display.deepSleep();
|
||||||
@@ -183,8 +233,8 @@ void enterDeepSleep() {
|
|||||||
powerManager.startDeepSleep(gpio, keepLpAlive);
|
powerManager.startDeepSleep(gpio, keepLpAlive);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupDisplayAndFonts() {
|
void setupDisplayAndFonts(bool seamless = false) {
|
||||||
display.begin();
|
display.begin(seamless);
|
||||||
renderer.begin();
|
renderer.begin();
|
||||||
activityManager.begin();
|
activityManager.begin();
|
||||||
LOG_DBG("MAIN", "Display initialized");
|
LOG_DBG("MAIN", "Display initialized");
|
||||||
@@ -354,6 +404,9 @@ void setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SETTINGS.loadFromFile();
|
SETTINGS.loadFromFile();
|
||||||
|
// APP_STATE is needed before display init so Quick Resume can skip the on-wake resync
|
||||||
|
// and so the seamless-wake path can paint the LoadingIcon over the restored framebuffer.
|
||||||
|
APP_STATE.loadFromFile();
|
||||||
|
|
||||||
HalSystem::checkPanic();
|
HalSystem::checkPanic();
|
||||||
HalSystem::clearPanic(); // TODO: move this to an activity when we have one to display the panic info
|
HalSystem::clearPanic(); // TODO: move this to an activity when we have one to display the panic info
|
||||||
@@ -368,20 +421,36 @@ void setup() {
|
|||||||
// First serial output only here to avoid timing inconsistencies for power button press duration verification
|
// First serial output only here to avoid timing inconsistencies for power button press duration verification
|
||||||
LOG_DBG("MAIN", "Starting CrossPoint version " CROSSPOINT_VERSION);
|
LOG_DBG("MAIN", "Starting CrossPoint version " CROSSPOINT_VERSION);
|
||||||
|
|
||||||
setupDisplayAndFonts();
|
const bool seamlessWake = !isSilentReboot && !APP_STATE.showBootScreen;
|
||||||
|
setupDisplayAndFonts(seamlessWake);
|
||||||
|
|
||||||
if (!isSilentReboot) {
|
if (isSilentReboot) {
|
||||||
activityManager.goToBoot();
|
|
||||||
} else {
|
|
||||||
// After a silent reboot the panel still shows the previous session's pixels but
|
// After a silent reboot the panel still shows the previous session's pixels but
|
||||||
// the SDK's RED-RAM diff buffer was cleared by begin(). A FAST refresh would only
|
// the SDK's RED-RAM diff buffer was cleared by begin(). A FAST refresh would only
|
||||||
// flip pixels the SDK *thinks* changed, leaving the old screen visible. Force the
|
// flip pixels the SDK *thinks* changed, leaving the old screen visible. Force the
|
||||||
// first paint to HALF_REFRESH so the panel cleanly repaints; subsequent paints
|
// first paint to HALF_REFRESH so the panel cleanly repaints; subsequent paints
|
||||||
// resume FAST as normal.
|
// resume FAST as normal.
|
||||||
renderer.setNextDisplayRefreshMode(HalDisplay::HALF_REFRESH);
|
renderer.setNextDisplayRefreshMode(HalDisplay::HALF_REFRESH);
|
||||||
|
} else if (seamlessWake && loadSleepFrameBuffer()) {
|
||||||
|
// Quick Resume wake: framebuffer restored. Overlay a small loading icon to signal
|
||||||
|
// the device is busy reaching the reader (replacing the moon icon painted at sleep).
|
||||||
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
renderer.drawImage(LoadingIcon, 0, pageHeight - LOADINGICON_HEIGHT, LOADINGICON_WIDTH, LOADINGICON_HEIGHT);
|
||||||
|
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||||
|
// Next sleep (whether crash, cold boot, or anything that doesn't go through enterDeepSleep)
|
||||||
|
// must fall back to a proper boot screen.
|
||||||
|
APP_STATE.showBootScreen = true;
|
||||||
|
APP_STATE.saveToFile();
|
||||||
|
} else {
|
||||||
|
if (seamlessWake) {
|
||||||
|
// We promised a seamless wake but the framebuffer file was missing or unreadable —
|
||||||
|
// reset the flag so future cycles don't hit the same bad state.
|
||||||
|
APP_STATE.showBootScreen = true;
|
||||||
|
APP_STATE.saveToFile();
|
||||||
|
}
|
||||||
|
activityManager.goToBoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
APP_STATE.loadFromFile();
|
|
||||||
HalClock::restore();
|
HalClock::restore();
|
||||||
RECENT_BOOKS.loadFromFile();
|
RECENT_BOOKS.loadFromFile();
|
||||||
GLOBAL_BOOKMARKS.load();
|
GLOBAL_BOOKMARKS.load();
|
||||||
@@ -420,6 +489,11 @@ void setup() {
|
|||||||
// Flush any pin state transitions that occurred during boot before entering the main loop
|
// Flush any pin state transitions that occurred during boot before entering the main loop
|
||||||
mappedInputManager.update();
|
mappedInputManager.update();
|
||||||
buttonEventManager.drain();
|
buttonEventManager.drain();
|
||||||
|
|
||||||
|
// Block held-power sleep for the first 2 seconds. On Quick Resume the user often releases
|
||||||
|
// the wake-press a fraction late; without this guard the loop() power-hold check would
|
||||||
|
// immediately fire enterDeepSleep().
|
||||||
|
allowSleepAt = millis() + 2000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
@@ -496,7 +570,7 @@ void loop() {
|
|||||||
const unsigned long sleepTimeoutMs = SETTINGS.getSleepTimeoutMs();
|
const unsigned long sleepTimeoutMs = SETTINGS.getSleepTimeoutMs();
|
||||||
if (millis() - lastActivityTime >= sleepTimeoutMs) {
|
if (millis() - lastActivityTime >= sleepTimeoutMs) {
|
||||||
LOG_DBG("SLP", "Auto-sleep triggered after %lu ms of inactivity", sleepTimeoutMs);
|
LOG_DBG("SLP", "Auto-sleep triggered after %lu ms of inactivity", sleepTimeoutMs);
|
||||||
enterDeepSleep();
|
enterDeepSleep(/*fromTimeout=*/true);
|
||||||
// This should never be hit as `enterDeepSleep` calls esp_deep_sleep_start
|
// This should never be hit as `enterDeepSleep` calls esp_deep_sleep_start
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -510,7 +584,7 @@ void loop() {
|
|||||||
powerHoldStart = millis();
|
powerHoldStart = millis();
|
||||||
LOG_DBG("MAIN", "loop: power button press detected (fresh edge)");
|
LOG_DBG("MAIN", "loop: power button press detected (fresh edge)");
|
||||||
}
|
}
|
||||||
if (gpio.isPressed(HalGPIO::BTN_POWER) && powerHoldStart > 0) {
|
if (millis() >= allowSleepAt && gpio.isPressed(HalGPIO::BTN_POWER) && powerHoldStart > 0) {
|
||||||
const unsigned long heldTime = millis() - powerHoldStart;
|
const unsigned long heldTime = millis() - powerHoldStart;
|
||||||
if (heldTime > SETTINGS.getPowerButtonDuration()) {
|
if (heldTime > SETTINGS.getPowerButtonDuration()) {
|
||||||
// If the screenshot combination is potentially being pressed, don't sleep
|
// If the screenshot combination is potentially being pressed, don't sleep
|
||||||
|
|||||||
@@ -1505,6 +1505,7 @@ void CrossPointWebServer::handlePostSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CrossPointSettings::normalizeDependentSettings(SETTINGS);
|
||||||
SETTINGS.saveToFile();
|
SETTINGS.saveToFile();
|
||||||
|
|
||||||
LOG_DBG("WEB", "Applied %d setting(s)", applied);
|
LOG_DBG("WEB", "Applied %d setting(s)", applied);
|
||||||
|
|||||||
Reference in New Issue
Block a user