Rotation Support

This commit is contained in:
Tannay 2025-12-19 22:28:17 -05:00
parent cfe838e03b
commit 32d747c6da
9 changed files with 158 additions and 39 deletions

View File

@ -10,8 +10,8 @@
#include "parsers/ChapterHtmlSlimParser.h" #include "parsers/ChapterHtmlSlimParser.h"
namespace { namespace {
constexpr uint8_t SECTION_FILE_VERSION = 5; constexpr uint8_t SECTION_FILE_VERSION = 6;
} } // namespace
void Section::onPageComplete(std::unique_ptr<Page> page) { void Section::onPageComplete(std::unique_ptr<Page> page) {
const auto filePath = cachePath + "/page_" + std::to_string(pageCount) + ".bin"; const auto filePath = cachePath + "/page_" + std::to_string(pageCount) + ".bin";
@ -27,7 +27,8 @@ void Section::onPageComplete(std::unique_ptr<Page> page) {
void Section::writeCacheMetadata(const int fontId, const float lineCompression, const int marginTop, void Section::writeCacheMetadata(const int fontId, const float lineCompression, const int marginTop,
const int marginRight, const int marginBottom, const int marginLeft, const int marginRight, const int marginBottom, const int marginLeft,
const bool extraParagraphSpacing) const { const bool extraParagraphSpacing, const int screenWidth,
const int screenHeight) const {
std::ofstream outputFile(("/sd" + cachePath + "/section.bin").c_str()); std::ofstream outputFile(("/sd" + cachePath + "/section.bin").c_str());
serialization::writePod(outputFile, SECTION_FILE_VERSION); serialization::writePod(outputFile, SECTION_FILE_VERSION);
serialization::writePod(outputFile, fontId); serialization::writePod(outputFile, fontId);
@ -37,13 +38,15 @@ void Section::writeCacheMetadata(const int fontId, const float lineCompression,
serialization::writePod(outputFile, marginBottom); serialization::writePod(outputFile, marginBottom);
serialization::writePod(outputFile, marginLeft); serialization::writePod(outputFile, marginLeft);
serialization::writePod(outputFile, extraParagraphSpacing); serialization::writePod(outputFile, extraParagraphSpacing);
serialization::writePod(outputFile, screenWidth);
serialization::writePod(outputFile, screenHeight);
serialization::writePod(outputFile, pageCount); serialization::writePod(outputFile, pageCount);
outputFile.close(); outputFile.close();
} }
bool Section::loadCacheMetadata(const int fontId, const float lineCompression, const int marginTop, bool Section::loadCacheMetadata(const int fontId, const float lineCompression, const int marginTop,
const int marginRight, const int marginBottom, const int marginLeft, const int marginRight, const int marginBottom, const int marginLeft,
const bool extraParagraphSpacing) { const bool extraParagraphSpacing, const int screenWidth, const int screenHeight) {
if (!SD.exists(cachePath.c_str())) { if (!SD.exists(cachePath.c_str())) {
return false; return false;
} }
@ -69,6 +72,7 @@ bool Section::loadCacheMetadata(const int fontId, const float lineCompression, c
int fileFontId, fileMarginTop, fileMarginRight, fileMarginBottom, fileMarginLeft; int fileFontId, fileMarginTop, fileMarginRight, fileMarginBottom, fileMarginLeft;
float fileLineCompression; float fileLineCompression;
bool fileExtraParagraphSpacing; bool fileExtraParagraphSpacing;
int fileScreenWidth, fileScreenHeight;
serialization::readPod(inputFile, fileFontId); serialization::readPod(inputFile, fileFontId);
serialization::readPod(inputFile, fileLineCompression); serialization::readPod(inputFile, fileLineCompression);
serialization::readPod(inputFile, fileMarginTop); serialization::readPod(inputFile, fileMarginTop);
@ -76,10 +80,13 @@ bool Section::loadCacheMetadata(const int fontId, const float lineCompression, c
serialization::readPod(inputFile, fileMarginBottom); serialization::readPod(inputFile, fileMarginBottom);
serialization::readPod(inputFile, fileMarginLeft); serialization::readPod(inputFile, fileMarginLeft);
serialization::readPod(inputFile, fileExtraParagraphSpacing); serialization::readPod(inputFile, fileExtraParagraphSpacing);
serialization::readPod(inputFile, fileScreenWidth);
serialization::readPod(inputFile, fileScreenHeight);
if (fontId != fileFontId || lineCompression != fileLineCompression || marginTop != fileMarginTop || if (fontId != fileFontId || lineCompression != fileLineCompression || marginTop != fileMarginTop ||
marginRight != fileMarginRight || marginBottom != fileMarginBottom || marginLeft != fileMarginLeft || marginRight != fileMarginRight || marginBottom != fileMarginBottom || marginLeft != fileMarginLeft ||
extraParagraphSpacing != fileExtraParagraphSpacing) { extraParagraphSpacing != fileExtraParagraphSpacing || screenWidth != fileScreenWidth ||
screenHeight != fileScreenHeight) {
inputFile.close(); inputFile.close();
Serial.printf("[%lu] [SCT] Deserialization failed: Parameters do not match\n", millis()); Serial.printf("[%lu] [SCT] Deserialization failed: Parameters do not match\n", millis());
clearCache(); clearCache();
@ -116,7 +123,7 @@ bool Section::clearCache() const {
bool Section::persistPageDataToSD(const int fontId, const float lineCompression, const int marginTop, bool Section::persistPageDataToSD(const int fontId, const float lineCompression, const int marginTop,
const int marginRight, const int marginBottom, const int marginLeft, const int marginRight, const int marginBottom, const int marginLeft,
const bool extraParagraphSpacing) { const bool extraParagraphSpacing, const int screenWidth, const int screenHeight) {
const auto localPath = epub->getSpineItem(spineIndex); const auto localPath = epub->getSpineItem(spineIndex);
// TODO: Should we get rid of this file all together? // TODO: Should we get rid of this file all together?
@ -147,7 +154,8 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
return false; return false;
} }
writeCacheMetadata(fontId, lineCompression, marginTop, marginRight, marginBottom, marginLeft, extraParagraphSpacing); writeCacheMetadata(fontId, lineCompression, marginTop, marginRight, marginBottom, marginLeft, extraParagraphSpacing,
screenWidth, screenHeight);
return true; return true;
} }

View File

@ -13,7 +13,7 @@ class Section {
std::string cachePath; std::string cachePath;
void writeCacheMetadata(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom, void writeCacheMetadata(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom,
int marginLeft, bool extraParagraphSpacing) const; int marginLeft, bool extraParagraphSpacing, int screenWidth, int screenHeight) const;
void onPageComplete(std::unique_ptr<Page> page); void onPageComplete(std::unique_ptr<Page> page);
public: public:
@ -26,10 +26,10 @@ class Section {
} }
~Section() = default; ~Section() = default;
bool loadCacheMetadata(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom, bool loadCacheMetadata(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom,
int marginLeft, bool extraParagraphSpacing); int marginLeft, bool extraParagraphSpacing, int screenWidth, int screenHeight);
void setupCacheDir() const; void setupCacheDir() const;
bool clearCache() const; bool clearCache() const;
bool persistPageDataToSD(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom, bool persistPageDataToSD(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom,
int marginLeft, bool extraParagraphSpacing); int marginLeft, bool extraParagraphSpacing, int screenWidth, int screenHeight);
std::unique_ptr<Page> loadPageFromSD() const; std::unique_ptr<Page> loadPageFromSD() const;
}; };

View File

@ -2,6 +2,9 @@
#include <Utf8.h> #include <Utf8.h>
// Default to portrait orientation for all callers
GfxRenderer::Orientation GfxRenderer::orientation = GfxRenderer::Orientation::Portrait;
void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) { fontMap.insert({fontId, font}); } void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) { fontMap.insert({fontId, font}); }
void GfxRenderer::drawPixel(const int x, const int y, const bool state) const { void GfxRenderer::drawPixel(const int x, const int y, const bool state) const {
@ -13,15 +16,35 @@ void GfxRenderer::drawPixel(const int x, const int y, const bool state) const {
return; return;
} }
// Rotate coordinates: portrait (480x800) -> landscape (800x480) int rotatedX = 0;
// Rotation: 90 degrees clockwise int rotatedY = 0;
const int rotatedX = y;
const int rotatedY = EInkDisplay::DISPLAY_HEIGHT - 1 - x;
// Bounds checking (portrait: 480x800) switch (orientation) {
case Orientation::Portrait: {
// Logical portrait (480x800) → panel (800x480)
// Rotation: 90 degrees clockwise
rotatedX = y;
rotatedY = EInkDisplay::DISPLAY_HEIGHT - 1 - x;
break;
}
case Orientation::LandscapeNormal: {
// Logical landscape (800x480) aligned with panel orientation
rotatedX = x;
rotatedY = y;
break;
}
case Orientation::LandscapeFlipped: {
// Logical landscape (800x480) rotated 180° (swap top/bottom and left/right)
rotatedX = EInkDisplay::DISPLAY_WIDTH - 1 - x;
rotatedY = EInkDisplay::DISPLAY_HEIGHT - 1 - y;
break;
}
}
// Bounds checking against physical panel dimensions
if (rotatedX < 0 || rotatedX >= EInkDisplay::DISPLAY_WIDTH || rotatedY < 0 || if (rotatedX < 0 || rotatedX >= EInkDisplay::DISPLAY_WIDTH || rotatedY < 0 ||
rotatedY >= EInkDisplay::DISPLAY_HEIGHT) { rotatedY >= EInkDisplay::DISPLAY_HEIGHT) {
Serial.printf("[%lu] [GFX] !! Outside range (%d, %d)\n", millis(), x, y); Serial.printf("[%lu] [GFX] !! Outside range (%d, %d) -> (%d, %d)\n", millis(), x, y, rotatedX, rotatedY);
return; return;
} }
@ -115,8 +138,17 @@ void GfxRenderer::fillRect(const int x, const int y, const int width, const int
} }
void GfxRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, const int width, const int height) const { void GfxRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, const int width, const int height) const {
// Flip X and Y for portrait mode switch (orientation) {
einkDisplay.drawImage(bitmap, y, x, height, width); case Orientation::Portrait:
// Flip X and Y for portrait mode
einkDisplay.drawImage(bitmap, y, x, height, width);
break;
case Orientation::LandscapeNormal:
case Orientation::LandscapeFlipped:
// Native landscape coordinates
einkDisplay.drawImage(bitmap, x, y, width, height);
break;
}
} }
void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, const int maxWidth, void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, const int maxWidth,
@ -193,22 +225,55 @@ void GfxRenderer::displayBuffer(const EInkDisplay::RefreshMode refreshMode) cons
} }
void GfxRenderer::displayWindow(const int x, const int y, const int width, const int height) const { void GfxRenderer::displayWindow(const int x, const int y, const int width, const int height) const {
// Rotate coordinates from portrait (480x800) to landscape (800x480) switch (orientation) {
// Rotation: 90 degrees clockwise case Orientation::Portrait: {
// Portrait coordinates: (x, y) with dimensions (width, height) // Rotate coordinates from portrait (480x800) to landscape (800x480)
// Landscape coordinates: (rotatedX, rotatedY) with dimensions (rotatedWidth, rotatedHeight) // Rotation: 90 degrees clockwise
// Portrait coordinates: (x, y) with dimensions (width, height)
// Landscape coordinates: (rotatedX, rotatedY) with dimensions (rotatedWidth, rotatedHeight)
const int rotatedX = y; const int rotatedX = y;
const int rotatedY = EInkDisplay::DISPLAY_HEIGHT - 1 - x - width + 1; const int rotatedY = EInkDisplay::DISPLAY_HEIGHT - 1 - x - width + 1;
const int rotatedWidth = height; const int rotatedWidth = height;
const int rotatedHeight = width; const int rotatedHeight = width;
einkDisplay.displayWindow(rotatedX, rotatedY, rotatedWidth, rotatedHeight); einkDisplay.displayWindow(rotatedX, rotatedY, rotatedWidth, rotatedHeight);
break;
}
case Orientation::LandscapeNormal:
case Orientation::LandscapeFlipped:
// Native landscape coordinates
einkDisplay.displayWindow(x, y, width, height);
break;
}
} }
// Note: Internal driver treats screen in command orientation, this library treats in portrait orientation // Note: Internal driver treats screen in command orientation; this library exposes a logical orientation
int GfxRenderer::getScreenWidth() { return EInkDisplay::DISPLAY_HEIGHT; } int GfxRenderer::getScreenWidth() {
int GfxRenderer::getScreenHeight() { return EInkDisplay::DISPLAY_WIDTH; } switch (orientation) {
case Orientation::Portrait:
// 480px wide in portrait logical coordinates
return EInkDisplay::DISPLAY_HEIGHT;
case Orientation::LandscapeNormal:
case Orientation::LandscapeFlipped:
// 800px wide in landscape logical coordinates
return EInkDisplay::DISPLAY_WIDTH;
}
return EInkDisplay::DISPLAY_HEIGHT;
}
int GfxRenderer::getScreenHeight() {
switch (orientation) {
case Orientation::Portrait:
// 800px tall in portrait logical coordinates
return EInkDisplay::DISPLAY_WIDTH;
case Orientation::LandscapeNormal:
case Orientation::LandscapeFlipped:
// 480px tall in landscape logical coordinates
return EInkDisplay::DISPLAY_HEIGHT;
}
return EInkDisplay::DISPLAY_WIDTH;
}
int GfxRenderer::getSpaceWidth(const int fontId) const { int GfxRenderer::getSpaceWidth(const int fontId) const {
if (fontMap.count(fontId) == 0) { if (fontMap.count(fontId) == 0) {

View File

@ -12,12 +12,22 @@ class GfxRenderer {
public: public:
enum RenderMode { BW, GRAYSCALE_LSB, GRAYSCALE_MSB }; enum RenderMode { BW, GRAYSCALE_LSB, GRAYSCALE_MSB };
// Logical screen orientation from the perspective of callers
enum class Orientation {
Portrait, // 480x800 logical coordinates (current default)
LandscapeNormal, // 800x480 logical coordinates, native panel orientation
LandscapeFlipped // 800x480 logical coordinates, rotated 180° (swap top/bottom)
};
private: private:
static constexpr size_t BW_BUFFER_CHUNK_SIZE = 8000; // 8KB chunks to allow for non-contiguous memory static constexpr size_t BW_BUFFER_CHUNK_SIZE = 8000; // 8KB chunks to allow for non-contiguous memory
static constexpr size_t BW_BUFFER_NUM_CHUNKS = EInkDisplay::BUFFER_SIZE / BW_BUFFER_CHUNK_SIZE; static constexpr size_t BW_BUFFER_NUM_CHUNKS = EInkDisplay::BUFFER_SIZE / BW_BUFFER_CHUNK_SIZE;
static_assert(BW_BUFFER_CHUNK_SIZE * BW_BUFFER_NUM_CHUNKS == EInkDisplay::BUFFER_SIZE, static_assert(BW_BUFFER_CHUNK_SIZE * BW_BUFFER_NUM_CHUNKS == EInkDisplay::BUFFER_SIZE,
"BW buffer chunking does not line up with display buffer size"); "BW buffer chunking does not line up with display buffer size");
// Global orientation used for all rendering operations
static Orientation orientation;
EInkDisplay& einkDisplay; EInkDisplay& einkDisplay;
RenderMode renderMode; RenderMode renderMode;
uint8_t* bwBufferChunks[BW_BUFFER_NUM_CHUNKS] = {nullptr}; uint8_t* bwBufferChunks[BW_BUFFER_NUM_CHUNKS] = {nullptr};
@ -33,11 +43,15 @@ class GfxRenderer {
// Setup // Setup
void insertFont(int fontId, EpdFontFamily font); void insertFont(int fontId, EpdFontFamily font);
// Orientation control (affects logical width/height and coordinate transforms)
static void setOrientation(Orientation o) { orientation = o; }
static Orientation getOrientation() { return orientation; }
// Screen ops // Screen ops
static int getScreenWidth(); static int getScreenWidth();
static int getScreenHeight(); static int getScreenHeight();
void displayBuffer(EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH) const; void displayBuffer(EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH) const;
// EXPERIMENTAL: Windowed update - display only a rectangular region (portrait coordinates) // EXPERIMENTAL: Windowed update - display only a rectangular region
void displayWindow(int x, int y, int width, int height) const; void displayWindow(int x, int y, int width, int height) const;
void invertScreen() const; void invertScreen() const;
void clearScreen(uint8_t color = 0xFF) const; void clearScreen(uint8_t color = 0xFF) const;

View File

@ -12,7 +12,8 @@ CrossPointSettings CrossPointSettings::instance;
namespace { namespace {
constexpr uint8_t SETTINGS_FILE_VERSION = 1; constexpr uint8_t SETTINGS_FILE_VERSION = 1;
constexpr uint8_t SETTINGS_COUNT = 3; // Increment this when adding new persisted settings fields
constexpr uint8_t SETTINGS_COUNT = 5;
constexpr char SETTINGS_FILE[] = "/sd/.crosspoint/settings.bin"; constexpr char SETTINGS_FILE[] = "/sd/.crosspoint/settings.bin";
} // namespace } // namespace
@ -26,6 +27,8 @@ bool CrossPointSettings::saveToFile() const {
serialization::writePod(outputFile, whiteSleepScreen); serialization::writePod(outputFile, whiteSleepScreen);
serialization::writePod(outputFile, extraParagraphSpacing); serialization::writePod(outputFile, extraParagraphSpacing);
serialization::writePod(outputFile, shortPwrBtn); serialization::writePod(outputFile, shortPwrBtn);
serialization::writePod(outputFile, landscapeReading);
serialization::writePod(outputFile, landscapeFlipped);
outputFile.close(); outputFile.close();
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis()); Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
@ -51,7 +54,7 @@ bool CrossPointSettings::loadFromFile() {
uint8_t fileSettingsCount = 0; uint8_t fileSettingsCount = 0;
serialization::readPod(inputFile, fileSettingsCount); serialization::readPod(inputFile, fileSettingsCount);
// load settings that exist // load settings that exist (support older files with fewer fields)
uint8_t settingsRead = 0; uint8_t settingsRead = 0;
do { do {
serialization::readPod(inputFile, whiteSleepScreen); serialization::readPod(inputFile, whiteSleepScreen);
@ -60,6 +63,10 @@ bool CrossPointSettings::loadFromFile() {
if (++settingsRead >= fileSettingsCount) break; if (++settingsRead >= fileSettingsCount) break;
serialization::readPod(inputFile, shortPwrBtn); serialization::readPod(inputFile, shortPwrBtn);
if (++settingsRead >= fileSettingsCount) break; if (++settingsRead >= fileSettingsCount) break;
serialization::readPod(inputFile, landscapeReading);
if (++settingsRead >= fileSettingsCount) break;
serialization::readPod(inputFile, landscapeFlipped);
if (++settingsRead >= fileSettingsCount) break;
} while (false); } while (false);
inputFile.close(); inputFile.close();

View File

@ -21,6 +21,11 @@ class CrossPointSettings {
uint8_t extraParagraphSpacing = 1; uint8_t extraParagraphSpacing = 1;
// Duration of the power button press // Duration of the power button press
uint8_t shortPwrBtn = 0; uint8_t shortPwrBtn = 0;
// EPUB reading orientation settings
// 0 = portrait (default), 1 = landscape
uint8_t landscapeReading = 0;
// When in landscape mode: 0 = normal, 1 = flipped (swap top/bottom)
uint8_t landscapeFlipped = 0;
~CrossPointSettings() = default; ~CrossPointSettings() = default;

View File

@ -29,6 +29,17 @@ void EpubReaderActivity::onEnter() {
return; return;
} }
// Configure screen orientation based on settings
if (SETTINGS.landscapeReading) {
if (SETTINGS.landscapeFlipped) {
GfxRenderer::setOrientation(GfxRenderer::Orientation::LandscapeFlipped);
} else {
GfxRenderer::setOrientation(GfxRenderer::Orientation::LandscapeNormal);
}
} else {
GfxRenderer::setOrientation(GfxRenderer::Orientation::Portrait);
}
renderingMutex = xSemaphoreCreateMutex(); renderingMutex = xSemaphoreCreateMutex();
epub->setupCacheDir(); epub->setupCacheDir();
@ -56,6 +67,9 @@ void EpubReaderActivity::onEnter() {
} }
void EpubReaderActivity::onExit() { void EpubReaderActivity::onExit() {
// Reset orientation back to portrait for the rest of the UI
GfxRenderer::setOrientation(GfxRenderer::Orientation::Portrait);
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD // Wait until not rendering to delete task to avoid killing mid-instruction to EPD
xSemaphoreTake(renderingMutex, portMAX_DELAY); xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) { if (displayTaskHandle) {
@ -208,7 +222,8 @@ void EpubReaderActivity::renderScreen() {
Serial.printf("[%lu] [ERS] Loading file: %s, index: %d\n", millis(), filepath.c_str(), currentSpineIndex); Serial.printf("[%lu] [ERS] Loading file: %s, index: %d\n", millis(), filepath.c_str(), currentSpineIndex);
section = std::unique_ptr<Section>(new Section(epub, currentSpineIndex, renderer)); section = std::unique_ptr<Section>(new Section(epub, currentSpineIndex, renderer));
if (!section->loadCacheMetadata(READER_FONT_ID, lineCompression, marginTop, marginRight, marginBottom, marginLeft, if (!section->loadCacheMetadata(READER_FONT_ID, lineCompression, marginTop, marginRight, marginBottom, marginLeft,
SETTINGS.extraParagraphSpacing)) { SETTINGS.extraParagraphSpacing, GfxRenderer::getScreenWidth(),
GfxRenderer::getScreenHeight())) {
Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis()); Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
{ {
@ -227,7 +242,8 @@ void EpubReaderActivity::renderScreen() {
section->setupCacheDir(); section->setupCacheDir();
if (!section->persistPageDataToSD(READER_FONT_ID, lineCompression, marginTop, marginRight, marginBottom, if (!section->persistPageDataToSD(READER_FONT_ID, lineCompression, marginTop, marginRight, marginBottom,
marginLeft, SETTINGS.extraParagraphSpacing)) { marginLeft, SETTINGS.extraParagraphSpacing, GfxRenderer::getScreenWidth(),
GfxRenderer::getScreenHeight())) {
Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis()); Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis());
section.reset(); section.reset();
return; return;
@ -322,7 +338,9 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page) {
} }
void EpubReaderActivity::renderStatusBar() const { void EpubReaderActivity::renderStatusBar() const {
constexpr auto textY = 776; // Position status bar near the bottom of the logical screen, regardless of orientation
const auto screenHeight = GfxRenderer::getScreenHeight();
const auto textY = screenHeight - 24;
// Calculate progress in book // Calculate progress in book
float sectionChapterProg = static_cast<float>(section->currentPage) / section->pageCount; float sectionChapterProg = static_cast<float>(section->currentPage) / section->pageCount;
@ -345,7 +363,7 @@ void EpubReaderActivity::renderStatusBar() const {
constexpr int batteryWidth = 15; constexpr int batteryWidth = 15;
constexpr int batteryHeight = 10; constexpr int batteryHeight = 10;
constexpr int x = marginLeft; constexpr int x = marginLeft;
constexpr int y = 783; const int y = screenHeight - 17;
// Top line // Top line
renderer.drawLine(x, y, x + batteryWidth - 4, y); renderer.drawLine(x, y, x + batteryWidth - 4, y);

View File

@ -10,7 +10,9 @@
const SettingInfo SettingsActivity::settingsList[settingsCount] = { const SettingInfo SettingsActivity::settingsList[settingsCount] = {
{"White Sleep Screen", SettingType::TOGGLE, &CrossPointSettings::whiteSleepScreen}, {"White Sleep Screen", SettingType::TOGGLE, &CrossPointSettings::whiteSleepScreen},
{"Extra Paragraph Spacing", SettingType::TOGGLE, &CrossPointSettings::extraParagraphSpacing}, {"Extra Paragraph Spacing", SettingType::TOGGLE, &CrossPointSettings::extraParagraphSpacing},
{"Short Power Button Click", SettingType::TOGGLE, &CrossPointSettings::shortPwrBtn}}; {"Short Power Button Click", SettingType::TOGGLE, &CrossPointSettings::shortPwrBtn},
{"Landscape Reading", SettingType::TOGGLE, &CrossPointSettings::landscapeReading},
{"Flip Landscape (swap top/bottom)", SettingType::TOGGLE, &CrossPointSettings::landscapeFlipped}};
void SettingsActivity::taskTrampoline(void* param) { void SettingsActivity::taskTrampoline(void* param) {
auto* self = static_cast<SettingsActivity*>(param); auto* self = static_cast<SettingsActivity*>(param);

View File

@ -29,7 +29,7 @@ class SettingsActivity final : public Activity {
const std::function<void()> onGoHome; const std::function<void()> onGoHome;
// Static settings list // Static settings list
static constexpr int settingsCount = 3; // Number of settings static constexpr int settingsCount = 5; // Number of settings
static const SettingInfo settingsList[settingsCount]; static const SettingInfo settingsList[settingsCount];
static void taskTrampoline(void* param); static void taskTrampoline(void* param);