Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0d92da8f2 | ||
|
|
8679c8f57c | ||
|
|
899caab70c | ||
|
|
98c8e7e77c | ||
|
|
7198d943b0 | ||
|
|
248af4b8fb | ||
|
|
05a027e2bf | ||
|
|
fa0f27df6a | ||
|
|
2631613b8d | ||
|
|
72aa7ba3f6 | ||
|
|
e08bac2e10 | ||
|
|
12d28e2148 | ||
|
|
85502b417e |
13
README.md
13
README.md
@@ -32,7 +32,7 @@ This project is **not affiliated with Xteink**; it's built as a community projec
|
|||||||
- [x] Saved reading position
|
- [x] Saved reading position
|
||||||
- [ ] File explorer with file picker
|
- [ ] File explorer with file picker
|
||||||
- [x] Basic EPUB picker from root directory
|
- [x] Basic EPUB picker from root directory
|
||||||
- [ ] Support nested folders
|
- [x] Support nested folders
|
||||||
- [ ] EPUB picker with cover art
|
- [ ] EPUB picker with cover art
|
||||||
- [ ] Image support within EPUB
|
- [ ] Image support within EPUB
|
||||||
- [ ] Configurable font, layout, and display options
|
- [ ] Configurable font, layout, and display options
|
||||||
@@ -48,6 +48,17 @@ This project is **not affiliated with Xteink**; it's built as a community projec
|
|||||||
* USB-C cable for flashing the ESP32-C3
|
* USB-C cable for flashing the ESP32-C3
|
||||||
* Xteink X4
|
* Xteink X4
|
||||||
|
|
||||||
|
### Checking out the code
|
||||||
|
|
||||||
|
CrossPoint uses PlatformIO for building and flashing the firmware. To get started, clone the repository:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone --recursive https://github.com/daveallie/crosspoint-reader
|
||||||
|
|
||||||
|
# Or, if you've already cloned without --recursive:
|
||||||
|
git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
|
||||||
### Flashing your device
|
### Flashing your device
|
||||||
|
|
||||||
#### Command line
|
#### Command line
|
||||||
|
|||||||
37
lib/EpdFont/EpdFontFamily.cpp
Normal file
37
lib/EpdFont/EpdFontFamily.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#include "EpdFontFamily.h"
|
||||||
|
|
||||||
|
const EpdFont* EpdFontFamily::getFont(const EpdFontStyle style) const {
|
||||||
|
if (style == BOLD && bold) {
|
||||||
|
return bold;
|
||||||
|
}
|
||||||
|
if (style == ITALIC && italic) {
|
||||||
|
return italic;
|
||||||
|
}
|
||||||
|
if (style == BOLD_ITALIC) {
|
||||||
|
if (boldItalic) {
|
||||||
|
return boldItalic;
|
||||||
|
}
|
||||||
|
if (bold) {
|
||||||
|
return bold;
|
||||||
|
}
|
||||||
|
if (italic) {
|
||||||
|
return italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return regular;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpdFontFamily::getTextDimensions(const char* string, int* w, int* h, const EpdFontStyle style) const {
|
||||||
|
getFont(style)->getTextDimensions(string, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EpdFontFamily::hasPrintableChars(const char* string, const EpdFontStyle style) const {
|
||||||
|
return getFont(style)->hasPrintableChars(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
const EpdFontData* EpdFontFamily::getData(const EpdFontStyle style) const { return getFont(style)->data; }
|
||||||
|
|
||||||
|
const EpdGlyph* EpdFontFamily::getGlyph(const uint32_t cp, const EpdFontStyle style) const {
|
||||||
|
return getFont(style)->getGlyph(cp);
|
||||||
|
};
|
||||||
24
lib/EpdFont/EpdFontFamily.h
Normal file
24
lib/EpdFont/EpdFontFamily.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "EpdFont.h"
|
||||||
|
|
||||||
|
enum EpdFontStyle { REGULAR, BOLD, ITALIC, BOLD_ITALIC };
|
||||||
|
|
||||||
|
class EpdFontFamily {
|
||||||
|
const EpdFont* regular;
|
||||||
|
const EpdFont* bold;
|
||||||
|
const EpdFont* italic;
|
||||||
|
const EpdFont* boldItalic;
|
||||||
|
|
||||||
|
const EpdFont* getFont(EpdFontStyle style) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit EpdFontFamily(const EpdFont* regular, const EpdFont* bold = nullptr, const EpdFont* italic = nullptr,
|
||||||
|
const EpdFont* boldItalic = nullptr)
|
||||||
|
: regular(regular), bold(bold), italic(italic), boldItalic(boldItalic) {}
|
||||||
|
~EpdFontFamily() = default;
|
||||||
|
void getTextDimensions(const char* string, int* w, int* h, EpdFontStyle style = REGULAR) const;
|
||||||
|
bool hasPrintableChars(const char* string, EpdFontStyle style = REGULAR) const;
|
||||||
|
|
||||||
|
const EpdFontData* getData(EpdFontStyle style = REGULAR) const;
|
||||||
|
const EpdGlyph* getGlyph(uint32_t cp, EpdFontStyle style = REGULAR) const;
|
||||||
|
};
|
||||||
1934
lib/EpdFont/builtinFonts/ubuntu_10.h
Normal file
1934
lib/EpdFont/builtinFonts/ubuntu_10.h
Normal file
File diff suppressed because it is too large
Load Diff
2120
lib/EpdFont/builtinFonts/ubuntu_bold_10.h
Normal file
2120
lib/EpdFont/builtinFonts/ubuntu_bold_10.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <EpdFont.h>
|
#include <EpdFontFamily.h>
|
||||||
#include <HardwareSerial.h>
|
#include <HardwareSerial.h>
|
||||||
#include <Utf8.h>
|
#include <Utf8.h>
|
||||||
#include <miniz.h>
|
#include <miniz.h>
|
||||||
@@ -12,13 +12,14 @@ static tinfl_decompressor decomp;
|
|||||||
template <typename Renderable>
|
template <typename Renderable>
|
||||||
class EpdFontRenderer {
|
class EpdFontRenderer {
|
||||||
Renderable* renderer;
|
Renderable* renderer;
|
||||||
void renderChar(uint32_t cp, int* x, const int* y, uint16_t color);
|
void renderChar(uint32_t cp, int* x, const int* y, uint16_t color, EpdFontStyle style = REGULAR);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const EpdFont* font;
|
const EpdFontFamily* fontFamily;
|
||||||
explicit EpdFontRenderer(const EpdFont* font, Renderable* renderer);
|
explicit EpdFontRenderer(const EpdFontFamily* fontFamily, Renderable* renderer)
|
||||||
|
: fontFamily(fontFamily), renderer(renderer) {}
|
||||||
~EpdFontRenderer() = default;
|
~EpdFontRenderer() = default;
|
||||||
void renderString(const char* string, int* x, int* y, uint16_t color);
|
void renderString(const char* string, int* x, int* y, uint16_t color, EpdFontStyle style = REGULAR);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline int uncompress(uint8_t* dest, size_t uncompressedSize, const uint8_t* source, size_t sourceSize) {
|
inline int uncompress(uint8_t* dest, size_t uncompressedSize, const uint8_t* source, size_t sourceSize) {
|
||||||
@@ -38,37 +39,33 @@ inline int uncompress(uint8_t* dest, size_t uncompressedSize, const uint8_t* sou
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Renderable>
|
template <typename Renderable>
|
||||||
EpdFontRenderer<Renderable>::EpdFontRenderer(const EpdFont* font, Renderable* renderer) {
|
void EpdFontRenderer<Renderable>::renderString(const char* string, int* x, int* y, const uint16_t color,
|
||||||
this->font = font;
|
const EpdFontStyle style) {
|
||||||
this->renderer = renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Renderable>
|
|
||||||
void EpdFontRenderer<Renderable>::renderString(const char* string, int* x, int* y, const uint16_t color) {
|
|
||||||
// cannot draw a NULL / empty string
|
// cannot draw a NULL / empty string
|
||||||
if (string == nullptr || *string == '\0') {
|
if (string == nullptr || *string == '\0') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no printable characters
|
// no printable characters
|
||||||
if (!font->hasPrintableChars(string)) {
|
if (!fontFamily->hasPrintableChars(string, style)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t cp;
|
uint32_t cp;
|
||||||
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&string)))) {
|
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&string)))) {
|
||||||
renderChar(cp, x, y, color);
|
renderChar(cp, x, y, color, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
*y += font->data->advanceY;
|
*y += fontFamily->getData(style)->advanceY;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Renderable>
|
template <typename Renderable>
|
||||||
void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const int* y, uint16_t color) {
|
void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const int* y, uint16_t color,
|
||||||
const EpdGlyph* glyph = font->getGlyph(cp);
|
const EpdFontStyle style) {
|
||||||
|
const EpdGlyph* glyph = fontFamily->getGlyph(cp, style);
|
||||||
if (!glyph) {
|
if (!glyph) {
|
||||||
// TODO: Replace with fallback glyph property?
|
// TODO: Replace with fallback glyph property?
|
||||||
glyph = font->getGlyph('?');
|
glyph = fontFamily->getGlyph('?', style);
|
||||||
}
|
}
|
||||||
|
|
||||||
// no glyph?
|
// no glyph?
|
||||||
@@ -86,17 +83,17 @@ void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const in
|
|||||||
const unsigned long bitmapSize = byteWidth * height;
|
const unsigned long bitmapSize = byteWidth * height;
|
||||||
const uint8_t* bitmap = nullptr;
|
const uint8_t* bitmap = nullptr;
|
||||||
|
|
||||||
if (font->data->compressed) {
|
if (fontFamily->getData(style)->compressed) {
|
||||||
auto* tmpBitmap = static_cast<uint8_t*>(malloc(bitmapSize));
|
auto* tmpBitmap = static_cast<uint8_t*>(malloc(bitmapSize));
|
||||||
if (tmpBitmap == nullptr && bitmapSize) {
|
if (tmpBitmap == nullptr && bitmapSize) {
|
||||||
// ESP_LOGE("font", "malloc failed.");
|
Serial.println("Failed to allocate memory for decompression buffer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uncompress(tmpBitmap, bitmapSize, &font->data->bitmap[offset], glyph->compressedSize);
|
uncompress(tmpBitmap, bitmapSize, &fontFamily->getData(style)->bitmap[offset], glyph->compressedSize);
|
||||||
bitmap = tmpBitmap;
|
bitmap = tmpBitmap;
|
||||||
} else {
|
} else {
|
||||||
bitmap = &font->data->bitmap[offset];
|
bitmap = &fontFamily->getData(style)->bitmap[offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap != nullptr) {
|
if (bitmap != nullptr) {
|
||||||
@@ -123,7 +120,7 @@ void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (font->data->compressed) {
|
if (fontFamily->getData(style)->compressed) {
|
||||||
free(const_cast<uint8_t*>(bitmap));
|
free(const_cast<uint8_t*>(bitmap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,18 @@
|
|||||||
#include "builtinFonts/bookerly_bold.h"
|
#include "builtinFonts/bookerly_bold.h"
|
||||||
#include "builtinFonts/bookerly_bold_italic.h"
|
#include "builtinFonts/bookerly_bold_italic.h"
|
||||||
#include "builtinFonts/bookerly_italic.h"
|
#include "builtinFonts/bookerly_italic.h"
|
||||||
|
#include "builtinFonts/ubuntu_10.h"
|
||||||
|
#include "builtinFonts/ubuntu_bold_10.h"
|
||||||
|
|
||||||
EpdRenderer::EpdRenderer(XteinkDisplay* display) {
|
EpdRenderer::EpdRenderer(XteinkDisplay* display) {
|
||||||
|
const auto bookerlyFontFamily = new EpdFontFamily(new EpdFont(&bookerly), new EpdFont(&bookerly_bold),
|
||||||
|
new EpdFont(&bookerly_italic), new EpdFont(&bookerly_bold_italic));
|
||||||
|
const auto ubuntuFontFamily = new EpdFontFamily(new EpdFont(&ubuntu_10), new EpdFont(&ubuntu_bold_10));
|
||||||
|
|
||||||
this->display = display;
|
this->display = display;
|
||||||
this->regularFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly), display);
|
this->regularFontRenderer = new EpdFontRenderer<XteinkDisplay>(bookerlyFontFamily, display);
|
||||||
this->boldFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_bold), display);
|
this->smallFontRenderer = new EpdFontRenderer<XteinkDisplay>(new EpdFontFamily(new EpdFont(&babyblue)), display);
|
||||||
this->italicFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_italic), display);
|
this->uiFontRenderer = new EpdFontRenderer<XteinkDisplay>(ubuntuFontFamily, display);
|
||||||
this->bold_italicFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_bold_italic), display);
|
|
||||||
this->smallFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&babyblue), display);
|
|
||||||
|
|
||||||
this->marginTop = 11;
|
this->marginTop = 11;
|
||||||
this->marginBottom = 30;
|
this->marginBottom = 30;
|
||||||
@@ -21,50 +25,53 @@ EpdRenderer::EpdRenderer(XteinkDisplay* display) {
|
|||||||
this->lineCompression = 0.95f;
|
this->lineCompression = 0.95f;
|
||||||
}
|
}
|
||||||
|
|
||||||
EpdFontRenderer<XteinkDisplay>* EpdRenderer::getFontRenderer(const bool bold, const bool italic) const {
|
int EpdRenderer::getTextWidth(const char* text, const EpdFontStyle style) const {
|
||||||
if (bold && italic) {
|
|
||||||
return bold_italicFont;
|
|
||||||
}
|
|
||||||
if (bold) {
|
|
||||||
return boldFont;
|
|
||||||
}
|
|
||||||
if (italic) {
|
|
||||||
return italicFont;
|
|
||||||
}
|
|
||||||
return regularFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
int EpdRenderer::getTextWidth(const char* text, const bool bold, const bool italic) const {
|
|
||||||
int w = 0, h = 0;
|
int w = 0, h = 0;
|
||||||
|
|
||||||
getFontRenderer(bold, italic)->font->getTextDimensions(text, &w, &h);
|
regularFontRenderer->fontFamily->getTextDimensions(text, &w, &h, style);
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
int EpdRenderer::getSmallTextWidth(const char* text) const {
|
int EpdRenderer::getUiTextWidth(const char* text, const EpdFontStyle style) const {
|
||||||
int w = 0, h = 0;
|
int w = 0, h = 0;
|
||||||
|
|
||||||
smallFont->font->getTextDimensions(text, &w, &h);
|
uiFontRenderer->fontFamily->getTextDimensions(text, &w, &h, style);
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpdRenderer::drawText(const int x, const int y, const char* text, const bool bold, const bool italic,
|
int EpdRenderer::getSmallTextWidth(const char* text, const EpdFontStyle style) const {
|
||||||
const uint16_t color) const {
|
int w = 0, h = 0;
|
||||||
|
|
||||||
|
smallFontRenderer->fontFamily->getTextDimensions(text, &w, &h, style);
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpdRenderer::drawText(const int x, const int y, const char* text, const uint16_t color,
|
||||||
|
const EpdFontStyle style) const {
|
||||||
int ypos = y + getLineHeight() + marginTop;
|
int ypos = y + getLineHeight() + marginTop;
|
||||||
int xpos = x + marginLeft;
|
int xpos = x + marginLeft;
|
||||||
getFontRenderer(bold, italic)->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
regularFontRenderer->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpdRenderer::drawSmallText(const int x, const int y, const char* text, const uint16_t color) const {
|
void EpdRenderer::drawUiText(const int x, const int y, const char* text, const uint16_t color,
|
||||||
int ypos = y + smallFont->font->data->advanceY + marginTop;
|
const EpdFontStyle style) const {
|
||||||
|
int ypos = y + uiFontRenderer->fontFamily->getData(style)->advanceY + marginTop;
|
||||||
int xpos = x + marginLeft;
|
int xpos = x + marginLeft;
|
||||||
smallFont->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
uiFontRenderer->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpdRenderer::drawSmallText(const int x, const int y, const char* text, const uint16_t color,
|
||||||
|
const EpdFontStyle style) const {
|
||||||
|
int ypos = y + smallFontRenderer->fontFamily->getData(style)->advanceY + marginTop;
|
||||||
|
int xpos = x + marginLeft;
|
||||||
|
smallFontRenderer->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text, const int width, const int height,
|
void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text, const int width, const int height,
|
||||||
const bool bold, const bool italic) const {
|
const EpdFontStyle style) const {
|
||||||
const size_t length = text.length();
|
const size_t length = text.length();
|
||||||
// fit the text into the box
|
// fit the text into the box
|
||||||
int start = 0;
|
int start = 0;
|
||||||
@@ -72,7 +79,7 @@ void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text,
|
|||||||
int ypos = 0;
|
int ypos = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (end >= length) {
|
if (end >= length) {
|
||||||
drawText(x, y + ypos, text.substr(start, length - start).c_str(), bold, italic);
|
drawText(x, y + ypos, text.substr(start, length - start).c_str(), 1, style);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,15 +88,15 @@ void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (text[end - 1] == '\n') {
|
if (text[end - 1] == '\n') {
|
||||||
drawText(x, y + ypos, text.substr(start, end - start).c_str(), bold, italic);
|
drawText(x, y + ypos, text.substr(start, end - start).c_str(), 1, style);
|
||||||
ypos += getLineHeight();
|
ypos += getLineHeight();
|
||||||
start = end;
|
start = end;
|
||||||
end = start + 1;
|
end = start + 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getTextWidth(text.substr(start, end - start).c_str(), bold, italic) > width) {
|
if (getTextWidth(text.substr(start, end - start).c_str(), style) > width) {
|
||||||
drawText(x, y + ypos, text.substr(start, end - start - 1).c_str(), bold, italic);
|
drawText(x, y + ypos, text.substr(start, end - start - 1).c_str(), 1, style);
|
||||||
ypos += getLineHeight();
|
ypos += getLineHeight();
|
||||||
start = end - 1;
|
start = end - 1;
|
||||||
continue;
|
continue;
|
||||||
@@ -108,44 +115,45 @@ void EpdRenderer::drawRect(const int x, const int y, const int width, const int
|
|||||||
display->drawRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
display->drawRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpdRenderer::fillRect(const int x, const int y, const int width, const int height,
|
void EpdRenderer::fillRect(const int x, const int y, const int width, const int height, const uint16_t color) const {
|
||||||
const uint16_t color = 0) const {
|
|
||||||
display->fillRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
display->fillRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EpdRenderer::drawCircle(const int x, const int y, const int radius, const uint16_t color) const {
|
||||||
|
display->drawCircle(x + marginLeft, y + marginTop, radius, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpdRenderer::fillCircle(const int x, const int y, const int radius, const uint16_t color) const {
|
||||||
|
display->fillCircle(x + marginLeft, y + marginTop, radius, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpdRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, const int width, const int height,
|
||||||
|
const bool invert, const bool mirrorY) const {
|
||||||
|
drawImageNoMargin(bitmap, x + marginLeft, y + marginTop, width, height, invert, mirrorY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpdRenderer::drawImageNoMargin(const uint8_t bitmap[], const int x, const int y, const int width, const int height,
|
||||||
|
const bool invert, const bool mirrorY) const {
|
||||||
|
display->drawImage(bitmap, x, y, width, height, invert, mirrorY);
|
||||||
|
}
|
||||||
|
|
||||||
void EpdRenderer::clearScreen(const bool black) const {
|
void EpdRenderer::clearScreen(const bool black) const {
|
||||||
Serial.println("Clearing screen");
|
Serial.println("Clearing screen");
|
||||||
display->fillScreen(black ? GxEPD_BLACK : GxEPD_WHITE);
|
display->fillScreen(black ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpdRenderer::flushDisplay() const { display->display(true); }
|
void EpdRenderer::flushDisplay(const bool partialUpdate) const { display->display(partialUpdate); }
|
||||||
|
|
||||||
void EpdRenderer::flushArea(int x, int y, int width, int height) const {
|
void EpdRenderer::flushArea(const int x, const int y, const int width, const int height) const {
|
||||||
// TODO: Fix
|
display->displayWindow(x + marginLeft, y + marginTop, width, height);
|
||||||
display->display(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int EpdRenderer::getPageWidth() const { return display->width() - marginLeft - marginRight; }
|
int EpdRenderer::getPageWidth() const { return display->width() - marginLeft - marginRight; }
|
||||||
|
|
||||||
int EpdRenderer::getPageHeight() const { return display->height() - marginTop - marginBottom; }
|
int EpdRenderer::getPageHeight() const { return display->height() - marginTop - marginBottom; }
|
||||||
|
|
||||||
int EpdRenderer::getSpaceWidth() const { return regularFont->font->getGlyph(' ')->advanceX; }
|
int EpdRenderer::getSpaceWidth() const { return regularFontRenderer->fontFamily->getGlyph(' ', REGULAR)->advanceX; }
|
||||||
|
|
||||||
int EpdRenderer::getLineHeight() const { return regularFont->font->data->advanceY * lineCompression; }
|
int EpdRenderer::getLineHeight() const {
|
||||||
|
return regularFontRenderer->fontFamily->getData(REGULAR)->advanceY * lineCompression;
|
||||||
// deep sleep helper - persist any state to disk that may be needed on wake
|
}
|
||||||
bool EpdRenderer::dehydrate() {
|
|
||||||
// TODO: Implement
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// deep sleep helper - retrieve any state from disk after wake
|
|
||||||
bool EpdRenderer::hydrate() {
|
|
||||||
// TODO: Implement
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// really really clear the screen
|
|
||||||
void EpdRenderer::reset() {
|
|
||||||
// TODO: Implement
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -8,32 +8,36 @@
|
|||||||
|
|
||||||
class EpdRenderer {
|
class EpdRenderer {
|
||||||
XteinkDisplay* display;
|
XteinkDisplay* display;
|
||||||
EpdFontRenderer<XteinkDisplay>* regularFont;
|
EpdFontRenderer<XteinkDisplay>* regularFontRenderer;
|
||||||
EpdFontRenderer<XteinkDisplay>* boldFont;
|
EpdFontRenderer<XteinkDisplay>* smallFontRenderer;
|
||||||
EpdFontRenderer<XteinkDisplay>* italicFont;
|
EpdFontRenderer<XteinkDisplay>* uiFontRenderer;
|
||||||
EpdFontRenderer<XteinkDisplay>* bold_italicFont;
|
|
||||||
EpdFontRenderer<XteinkDisplay>* smallFont;
|
|
||||||
int marginTop;
|
int marginTop;
|
||||||
int marginBottom;
|
int marginBottom;
|
||||||
int marginLeft;
|
int marginLeft;
|
||||||
int marginRight;
|
int marginRight;
|
||||||
float lineCompression;
|
float lineCompression;
|
||||||
EpdFontRenderer<XteinkDisplay>* getFontRenderer(bool bold, bool italic) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit EpdRenderer(XteinkDisplay* display);
|
explicit EpdRenderer(XteinkDisplay* display);
|
||||||
~EpdRenderer() = default;
|
~EpdRenderer() = default;
|
||||||
int getTextWidth(const char* text, bool bold = false, bool italic = false) const;
|
int getTextWidth(const char* text, EpdFontStyle style = REGULAR) const;
|
||||||
int getSmallTextWidth(const char* text) const;
|
int getUiTextWidth(const char* text, EpdFontStyle style = REGULAR) const;
|
||||||
void drawText(int x, int y, const char* text, bool bold = false, bool italic = false, uint16_t color = 1) const;
|
int getSmallTextWidth(const char* text, EpdFontStyle style = REGULAR) const;
|
||||||
void drawSmallText(int x, int y, const char* text, uint16_t color = 1) const;
|
void drawText(int x, int y, const char* text, uint16_t color = 1, EpdFontStyle style = REGULAR) const;
|
||||||
void drawTextBox(int x, int y, const std::string& text, int width, int height, bool bold = false,
|
void drawUiText(int x, int y, const char* text, uint16_t color = 1, EpdFontStyle style = REGULAR) const;
|
||||||
bool italic = false) const;
|
void drawSmallText(int x, int y, const char* text, uint16_t color = 1, EpdFontStyle style = REGULAR) const;
|
||||||
void drawLine(int x1, int y1, int x2, int y2, uint16_t color) const;
|
void drawTextBox(int x, int y, const std::string& text, int width, int height, EpdFontStyle style = REGULAR) const;
|
||||||
void drawRect(int x, int y, int width, int height, uint16_t color) const;
|
void drawLine(int x1, int y1, int x2, int y2, uint16_t color = 1) const;
|
||||||
void fillRect(int x, int y, int width, int height, uint16_t color) const;
|
void drawRect(int x, int y, int width, int height, uint16_t color = 1) const;
|
||||||
|
void fillRect(int x, int y, int width, int height, uint16_t color = 1) const;
|
||||||
|
void drawCircle(int x, int y, int radius, uint16_t color = 1) const;
|
||||||
|
void fillCircle(int x, int y, int radius, uint16_t color = 1) const;
|
||||||
|
void drawImage(const uint8_t bitmap[], int x, int y, int width, int height, bool invert = false,
|
||||||
|
bool mirrorY = false) const;
|
||||||
|
void drawImageNoMargin(const uint8_t bitmap[], int x, int y, int width, int height, bool invert = false,
|
||||||
|
bool mirrorY = false) const;
|
||||||
void clearScreen(bool black = false) const;
|
void clearScreen(bool black = false) const;
|
||||||
void flushDisplay() const;
|
void flushDisplay(bool partialUpdate = true) const;
|
||||||
void flushArea(int x, int y, int width, int height) const;
|
void flushArea(int x, int y, int width, int height) const;
|
||||||
|
|
||||||
int getPageWidth() const;
|
int getPageWidth() const;
|
||||||
@@ -45,12 +49,4 @@ class EpdRenderer {
|
|||||||
void setMarginBottom(const int newMarginBottom) { this->marginBottom = newMarginBottom; }
|
void setMarginBottom(const int newMarginBottom) { this->marginBottom = newMarginBottom; }
|
||||||
void setMarginLeft(const int newMarginLeft) { this->marginLeft = newMarginLeft; }
|
void setMarginLeft(const int newMarginLeft) { this->marginLeft = newMarginLeft; }
|
||||||
void setMarginRight(const int newMarginRight) { this->marginRight = newMarginRight; }
|
void setMarginRight(const int newMarginRight) { this->marginRight = newMarginRight; }
|
||||||
// deep sleep helper - persist any state to disk that may be needed on wake
|
|
||||||
bool dehydrate();
|
|
||||||
// deep sleep helper - retrieve any state from disk after wake
|
|
||||||
bool hydrate();
|
|
||||||
// really really clear the screen
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
uint8_t temperature = 20;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -107,11 +107,11 @@ void Section::renderPage() {
|
|||||||
delete p;
|
delete p;
|
||||||
} else if (pageCount == 0) {
|
} else if (pageCount == 0) {
|
||||||
Serial.println("No pages to render");
|
Serial.println("No pages to render");
|
||||||
const int width = renderer->getTextWidth("Empty chapter", true);
|
const int width = renderer->getTextWidth("Empty chapter", BOLD);
|
||||||
renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Empty chapter", true);
|
renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Empty chapter", 1, BOLD);
|
||||||
} else {
|
} else {
|
||||||
Serial.printf("Page out of bounds: %d (max %d)\n", currentPage, pageCount);
|
Serial.printf("Page out of bounds: %d (max %d)\n", currentPage, pageCount);
|
||||||
const int width = renderer->getTextWidth("Out of bounds", true);
|
const int width = renderer->getTextWidth("Out of bounds", BOLD);
|
||||||
renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Out of bounds", true);
|
renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Out of bounds", 1, BOLD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,17 @@ std::list<TextBlock*> TextBlock::splitIntoLines(const EpdRenderer* renderer) {
|
|||||||
uint16_t wordWidths[totalWordCount];
|
uint16_t wordWidths[totalWordCount];
|
||||||
for (int i = 0; i < words.size(); i++) {
|
for (int i = 0; i < words.size(); i++) {
|
||||||
// measure the word
|
// measure the word
|
||||||
const int width = renderer->getTextWidth(words[i].c_str(), wordStyles[i] & BOLD_SPAN, wordStyles[i] & ITALIC_SPAN);
|
EpdFontStyle fontStyle = REGULAR;
|
||||||
|
if (wordStyles[i] & BOLD_SPAN) {
|
||||||
|
if (wordStyles[i] & ITALIC_SPAN) {
|
||||||
|
fontStyle = BOLD_ITALIC;
|
||||||
|
} else {
|
||||||
|
fontStyle = BOLD;
|
||||||
|
}
|
||||||
|
} else if (wordStyles[i] & ITALIC_SPAN) {
|
||||||
|
fontStyle = ITALIC;
|
||||||
|
}
|
||||||
|
const int width = renderer->getTextWidth(words[i].c_str(), fontStyle);
|
||||||
wordWidths[i] = width;
|
wordWidths[i] = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +192,18 @@ void TextBlock::render(const EpdRenderer* renderer, const int x, const int y) co
|
|||||||
// get the style
|
// get the style
|
||||||
const uint8_t wordStyle = wordStyles[i];
|
const uint8_t wordStyle = wordStyles[i];
|
||||||
// render the word
|
// render the word
|
||||||
renderer->drawText(x + wordXpos[i], y, words[i].c_str(), wordStyle & BOLD_SPAN, wordStyle & ITALIC_SPAN);
|
EpdFontStyle fontStyle = REGULAR;
|
||||||
|
if (wordStyles[i] & BOLD_SPAN) {
|
||||||
|
if (wordStyles[i] & ITALIC_SPAN) {
|
||||||
|
fontStyle = BOLD_ITALIC;
|
||||||
|
} else {
|
||||||
|
fontStyle = BOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (wordStyles[i] & ITALIC_SPAN) {
|
||||||
|
fontStyle = ITALIC;
|
||||||
|
}
|
||||||
|
renderer->drawText(x + wordXpos[i], y, words[i].c_str(), 1, fontStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Submodule open-x4-sdk updated: be0cb2bb34...90a19bb8a7
@@ -29,15 +29,15 @@ Button getPressedButton() {
|
|||||||
return NONE;
|
return NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Input getInput(const bool skipWait) {
|
Input getInput(const long maxHoldMs) {
|
||||||
const Button button = getPressedButton();
|
const Button button = getPressedButton();
|
||||||
if (button == NONE) return {NONE, 0};
|
if (button == NONE) return {NONE, 0};
|
||||||
|
|
||||||
if (skipWait) {
|
|
||||||
return {button, 0};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto start = millis();
|
const auto start = millis();
|
||||||
while (getPressedButton() == button) delay(50);
|
unsigned long held = 0;
|
||||||
return {button, millis() - start};
|
while (getPressedButton() == button && (maxHoldMs < 0 || held < maxHoldMs)) {
|
||||||
|
delay(50);
|
||||||
|
held = millis() - start;
|
||||||
|
}
|
||||||
|
return {button, held};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,4 +25,4 @@ struct Input {
|
|||||||
|
|
||||||
void setupInputPinModes();
|
void setupInputPinModes();
|
||||||
Button getPressedButton();
|
Button getPressedButton();
|
||||||
Input getInput(bool skipWait = false);
|
Input getInput(long maxHoldMs = -1);
|
||||||
|
|||||||
113
src/images/CrossLarge.h
Normal file
113
src/images/CrossLarge.h
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
extern const uint8_t CrossLarge[] = {
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xF8, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x1F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xE0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x01,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFE, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x00,
|
||||||
|
0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xF8, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00,
|
||||||
|
0x00, 0x07, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xFF,
|
||||||
|
0xFF, 0x80, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF,
|
||||||
|
0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFC, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
|
||||||
|
0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFC,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF,
|
||||||
|
0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF8, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x3F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF,
|
||||||
|
0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
|
||||||
|
0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF,
|
||||||
|
0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFE, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||||
|
0x80, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x00,
|
||||||
|
0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x0F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xF8, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00,
|
||||||
|
0x00, 0x1F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xFF,
|
||||||
|
0xFF, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00,
|
||||||
|
0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
|
||||||
|
0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x0F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
};
|
||||||
2532
src/images/SleepScreenImg.h
Normal file
2532
src/images/SleepScreenImg.h
Normal file
File diff suppressed because it is too large
Load Diff
66
src/main.cpp
66
src/main.cpp
@@ -8,9 +8,11 @@
|
|||||||
#include "Battery.h"
|
#include "Battery.h"
|
||||||
#include "CrossPointState.h"
|
#include "CrossPointState.h"
|
||||||
#include "Input.h"
|
#include "Input.h"
|
||||||
|
#include "screens/BootLogoScreen.h"
|
||||||
#include "screens/EpubReaderScreen.h"
|
#include "screens/EpubReaderScreen.h"
|
||||||
#include "screens/FileSelectionScreen.h"
|
#include "screens/FileSelectionScreen.h"
|
||||||
#include "screens/FullScreenMessageScreen.h"
|
#include "screens/FullScreenMessageScreen.h"
|
||||||
|
#include "screens/SleepScreen.h"
|
||||||
|
|
||||||
#define SPI_FQ 40000000
|
#define SPI_FQ 40000000
|
||||||
// Display SPI pins (custom pins for XteinkX4, not hardware SPI defaults)
|
// Display SPI pins (custom pins for XteinkX4, not hardware SPI defaults)
|
||||||
@@ -50,15 +52,18 @@ Epub* loadEpub(const std::string& path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Serial.println("Failed to load epub");
|
Serial.println("Failed to load epub");
|
||||||
free(epub);
|
delete epub;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void enterNewScreen(Screen* screen) {
|
void exitScreen() {
|
||||||
if (currentScreen) {
|
if (currentScreen) {
|
||||||
currentScreen->onExit();
|
currentScreen->onExit();
|
||||||
delete currentScreen;
|
delete currentScreen;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void enterNewScreen(Screen* screen) {
|
||||||
currentScreen = screen;
|
currentScreen = screen;
|
||||||
currentScreen->onEnter();
|
currentScreen->onEnter();
|
||||||
}
|
}
|
||||||
@@ -67,10 +72,10 @@ void enterNewScreen(Screen* screen) {
|
|||||||
void verifyWakeupLongPress() {
|
void verifyWakeupLongPress() {
|
||||||
// Give the user up to 1000ms to start holding the power button, and must hold for POWER_BUTTON_WAKEUP_MS
|
// Give the user up to 1000ms to start holding the power button, and must hold for POWER_BUTTON_WAKEUP_MS
|
||||||
const auto start = millis();
|
const auto start = millis();
|
||||||
auto input = getInput();
|
auto input = getInput(POWER_BUTTON_WAKEUP_MS);
|
||||||
while (input.button != POWER && millis() - start < 1000) {
|
while (input.button != POWER && millis() - start < 1000) {
|
||||||
delay(50);
|
delay(50);
|
||||||
input = getInput();
|
input = getInput(POWER_BUTTON_WAKEUP_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.button != POWER || input.pressTime < POWER_BUTTON_WAKEUP_MS) {
|
if (input.button != POWER || input.pressTime < POWER_BUTTON_WAKEUP_MS) {
|
||||||
@@ -81,9 +86,16 @@ void verifyWakeupLongPress() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waitForNoButton() {
|
||||||
|
while (getInput().button != NONE) {
|
||||||
|
delay(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Enter deep sleep mode
|
// Enter deep sleep mode
|
||||||
void enterDeepSleep() {
|
void enterDeepSleep() {
|
||||||
enterNewScreen(new FullScreenMessageScreen(renderer, "Sleeping", true, false, true));
|
exitScreen();
|
||||||
|
enterNewScreen(new SleepScreen(renderer));
|
||||||
|
|
||||||
Serial.println("Power button released after a long press. Entering deep sleep.");
|
Serial.println("Power button released after a long press. Entering deep sleep.");
|
||||||
delay(1000); // Allow Serial buffer to empty and display to update
|
delay(1000); // Allow Serial buffer to empty and display to update
|
||||||
@@ -97,39 +109,39 @@ void enterDeepSleep() {
|
|||||||
esp_deep_sleep_start();
|
esp_deep_sleep_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupSerial() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
// Wait for serial monitor
|
|
||||||
const unsigned long start = millis();
|
|
||||||
while (!Serial && (millis() - start) < 3000) {
|
|
||||||
delay(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Serial) {
|
|
||||||
// delay for monitor to start reading
|
|
||||||
delay(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onGoHome();
|
void onGoHome();
|
||||||
void onSelectEpubFile(const std::string& path) {
|
void onSelectEpubFile(const std::string& path) {
|
||||||
|
exitScreen();
|
||||||
enterNewScreen(new FullScreenMessageScreen(renderer, "Loading..."));
|
enterNewScreen(new FullScreenMessageScreen(renderer, "Loading..."));
|
||||||
|
|
||||||
Epub* epub = loadEpub(path);
|
Epub* epub = loadEpub(path);
|
||||||
if (epub) {
|
if (epub) {
|
||||||
appState->openEpubPath = path;
|
appState->openEpubPath = path;
|
||||||
appState->saveToFile();
|
appState->saveToFile();
|
||||||
|
exitScreen();
|
||||||
enterNewScreen(new EpubReaderScreen(renderer, epub, onGoHome));
|
enterNewScreen(new EpubReaderScreen(renderer, epub, onGoHome));
|
||||||
} else {
|
} else {
|
||||||
enterNewScreen(new FullScreenMessageScreen(renderer, "Failed to load epub"));
|
exitScreen();
|
||||||
|
enterNewScreen(new FullScreenMessageScreen(renderer, "Failed to load epub", REGULAR, false, false));
|
||||||
|
delay(2000);
|
||||||
|
onGoHome();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void onGoHome() { enterNewScreen(new FileSelectionScreen(renderer, onSelectEpubFile)); }
|
|
||||||
|
void onGoHome() {
|
||||||
|
exitScreen();
|
||||||
|
enterNewScreen(new FileSelectionScreen(renderer, onSelectEpubFile));
|
||||||
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
setupInputPinModes();
|
setupInputPinModes();
|
||||||
verifyWakeupLongPress();
|
verifyWakeupLongPress();
|
||||||
setupSerial();
|
|
||||||
|
// Begin serial only if USB connected
|
||||||
|
pinMode(UART0_RXD, INPUT);
|
||||||
|
if (digitalRead(UART0_RXD) == HIGH) {
|
||||||
|
Serial.begin(115200);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize pins
|
// Initialize pins
|
||||||
pinMode(BAT_GPIO0, INPUT);
|
pinMode(BAT_GPIO0, INPUT);
|
||||||
@@ -144,7 +156,8 @@ void setup() {
|
|||||||
display.setTextColor(GxEPD_BLACK);
|
display.setTextColor(GxEPD_BLACK);
|
||||||
Serial.println("Display initialized");
|
Serial.println("Display initialized");
|
||||||
|
|
||||||
enterNewScreen(new FullScreenMessageScreen(renderer, "Booting...", true));
|
exitScreen();
|
||||||
|
enterNewScreen(new BootLogoScreen(renderer));
|
||||||
|
|
||||||
// SD Card Initialization
|
// SD Card Initialization
|
||||||
SD.begin(SD_SPI_CS, SPI, SPI_FQ);
|
SD.begin(SD_SPI_CS, SPI, SPI_FQ);
|
||||||
@@ -153,12 +166,19 @@ void setup() {
|
|||||||
if (!appState->openEpubPath.empty()) {
|
if (!appState->openEpubPath.empty()) {
|
||||||
Epub* epub = loadEpub(appState->openEpubPath);
|
Epub* epub = loadEpub(appState->openEpubPath);
|
||||||
if (epub) {
|
if (epub) {
|
||||||
|
exitScreen();
|
||||||
enterNewScreen(new EpubReaderScreen(renderer, epub, onGoHome));
|
enterNewScreen(new EpubReaderScreen(renderer, epub, onGoHome));
|
||||||
|
// Ensure we're not still holding the power button before leaving setup
|
||||||
|
waitForNoButton();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exitScreen();
|
||||||
enterNewScreen(new FileSelectionScreen(renderer, onSelectEpubFile));
|
enterNewScreen(new FileSelectionScreen(renderer, onSelectEpubFile));
|
||||||
|
|
||||||
|
// Ensure we're not still holding the power button before leaving setup
|
||||||
|
waitForNoButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
|||||||
14
src/screens/BootLogoScreen.cpp
Normal file
14
src/screens/BootLogoScreen.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include "BootLogoScreen.h"
|
||||||
|
|
||||||
|
#include <EpdRenderer.h>
|
||||||
|
|
||||||
|
#include "images/CrossLarge.h"
|
||||||
|
|
||||||
|
void BootLogoScreen::onEnter() {
|
||||||
|
const auto pageWidth = renderer->getPageWidth();
|
||||||
|
const auto pageHeight = renderer->getPageHeight();
|
||||||
|
|
||||||
|
renderer->clearScreen();
|
||||||
|
// Location for images is from top right in landscape orientation
|
||||||
|
renderer->drawImage(CrossLarge, (pageHeight - 128) / 2, (pageWidth - 128) / 2, 128, 128);
|
||||||
|
}
|
||||||
8
src/screens/BootLogoScreen.h
Normal file
8
src/screens/BootLogoScreen.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Screen.h"
|
||||||
|
|
||||||
|
class BootLogoScreen final : public Screen {
|
||||||
|
public:
|
||||||
|
explicit BootLogoScreen(EpdRenderer* renderer) : Screen(renderer) {}
|
||||||
|
void onEnter() override;
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "Battery.h"
|
#include "Battery.h"
|
||||||
|
|
||||||
|
constexpr int PAGES_PER_REFRESH = 10;
|
||||||
constexpr unsigned long SKIP_CHAPTER_MS = 700;
|
constexpr unsigned long SKIP_CHAPTER_MS = 700;
|
||||||
|
|
||||||
void EpubReaderScreen::taskTrampoline(void* param) {
|
void EpubReaderScreen::taskTrampoline(void* param) {
|
||||||
@@ -13,7 +14,11 @@ void EpubReaderScreen::taskTrampoline(void* param) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EpubReaderScreen::onEnter() {
|
void EpubReaderScreen::onEnter() {
|
||||||
sectionMutex = xSemaphoreCreateMutex();
|
if (!epub) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderingMutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
epub->setupCacheDir();
|
epub->setupCacheDir();
|
||||||
|
|
||||||
@@ -40,11 +45,18 @@ void EpubReaderScreen::onEnter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EpubReaderScreen::onExit() {
|
void EpubReaderScreen::onExit() {
|
||||||
|
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
if (displayTaskHandle) {
|
||||||
vTaskDelete(displayTaskHandle);
|
vTaskDelete(displayTaskHandle);
|
||||||
displayTaskHandle = nullptr;
|
displayTaskHandle = nullptr;
|
||||||
xSemaphoreTake(sectionMutex, portMAX_DELAY);
|
}
|
||||||
vSemaphoreDelete(sectionMutex);
|
vSemaphoreDelete(renderingMutex);
|
||||||
sectionMutex = nullptr;
|
renderingMutex = nullptr;
|
||||||
|
delete section;
|
||||||
|
section = nullptr;
|
||||||
|
delete epub;
|
||||||
|
epub = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpubReaderScreen::handleInput(const Input input) {
|
void EpubReaderScreen::handleInput(const Input input) {
|
||||||
@@ -71,23 +83,25 @@ void EpubReaderScreen::handleInput(const Input input) {
|
|||||||
if (section->currentPage > 0) {
|
if (section->currentPage > 0) {
|
||||||
section->currentPage--;
|
section->currentPage--;
|
||||||
} else {
|
} else {
|
||||||
xSemaphoreTake(sectionMutex, portMAX_DELAY);
|
// We don't want to delete the section mid-render, so grab the semaphore
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
nextPageNumber = UINT16_MAX;
|
nextPageNumber = UINT16_MAX;
|
||||||
currentSpineIndex--;
|
currentSpineIndex--;
|
||||||
delete section;
|
delete section;
|
||||||
section = nullptr;
|
section = nullptr;
|
||||||
xSemaphoreGive(sectionMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
}
|
}
|
||||||
} else if (input.button == VOLUME_DOWN || input.button == RIGHT) {
|
} else if (input.button == VOLUME_DOWN || input.button == RIGHT) {
|
||||||
if (section->currentPage < section->pageCount - 1) {
|
if (section->currentPage < section->pageCount - 1) {
|
||||||
section->currentPage++;
|
section->currentPage++;
|
||||||
} else {
|
} else {
|
||||||
xSemaphoreTake(sectionMutex, portMAX_DELAY);
|
// We don't want to delete the section mid-render, so grab the semaphore
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
nextPageNumber = 0;
|
nextPageNumber = 0;
|
||||||
currentSpineIndex++;
|
currentSpineIndex++;
|
||||||
delete section;
|
delete section;
|
||||||
section = nullptr;
|
section = nullptr;
|
||||||
xSemaphoreGive(sectionMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +115,9 @@ void EpubReaderScreen::displayTaskLoop() {
|
|||||||
while (true) {
|
while (true) {
|
||||||
if (updateRequired) {
|
if (updateRequired) {
|
||||||
updateRequired = false;
|
updateRequired = false;
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
renderPage();
|
renderPage();
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
}
|
}
|
||||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
@@ -116,19 +132,31 @@ void EpubReaderScreen::renderPage() {
|
|||||||
currentSpineIndex = 0;
|
currentSpineIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
xSemaphoreTake(sectionMutex, portMAX_DELAY);
|
|
||||||
if (!section) {
|
if (!section) {
|
||||||
const auto filepath = epub->getSpineItem(currentSpineIndex);
|
const auto filepath = epub->getSpineItem(currentSpineIndex);
|
||||||
Serial.printf("Loading file: %s, index: %d\n", filepath.c_str(), currentSpineIndex);
|
Serial.printf("Loading file: %s, index: %d\n", filepath.c_str(), currentSpineIndex);
|
||||||
section = new Section(epub, currentSpineIndex, renderer);
|
section = new Section(epub, currentSpineIndex, renderer);
|
||||||
if (!section->hasCache()) {
|
if (!section->hasCache()) {
|
||||||
Serial.println("Cache not found, building...");
|
Serial.println("Cache not found, building...");
|
||||||
|
|
||||||
|
{
|
||||||
|
const int textWidth = renderer->getTextWidth("Indexing...");
|
||||||
|
constexpr int margin = 20;
|
||||||
|
const int x = (renderer->getPageWidth() - textWidth - margin * 2) / 2;
|
||||||
|
constexpr int y = 50;
|
||||||
|
const int w = textWidth + margin * 2;
|
||||||
|
const int h = renderer->getLineHeight() + margin * 2;
|
||||||
|
renderer->fillRect(x, y, w, h, 0);
|
||||||
|
renderer->drawText(x + margin, y + margin, "Indexing...");
|
||||||
|
renderer->drawRect(x + 5, y + 5, w - 10, h - 10);
|
||||||
|
renderer->flushArea(x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
section->setupCacheDir();
|
section->setupCacheDir();
|
||||||
if (!section->persistPageDataToSD()) {
|
if (!section->persistPageDataToSD()) {
|
||||||
Serial.println("Failed to persist page data to SD");
|
Serial.println("Failed to persist page data to SD");
|
||||||
free(section);
|
delete section;
|
||||||
section = nullptr;
|
section = nullptr;
|
||||||
xSemaphoreGive(sectionMutex);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -145,7 +173,13 @@ void EpubReaderScreen::renderPage() {
|
|||||||
renderer->clearScreen();
|
renderer->clearScreen();
|
||||||
section->renderPage();
|
section->renderPage();
|
||||||
renderStatusBar();
|
renderStatusBar();
|
||||||
|
if (pagesUntilFullRefresh <= 1) {
|
||||||
|
renderer->flushDisplay(false);
|
||||||
|
pagesUntilFullRefresh = PAGES_PER_REFRESH;
|
||||||
|
} else {
|
||||||
renderer->flushDisplay();
|
renderer->flushDisplay();
|
||||||
|
pagesUntilFullRefresh--;
|
||||||
|
}
|
||||||
|
|
||||||
File f = SD.open((epub->getCachePath() + "/progress.bin").c_str(), FILE_WRITE);
|
File f = SD.open((epub->getCachePath() + "/progress.bin").c_str(), FILE_WRITE);
|
||||||
uint8_t data[4];
|
uint8_t data[4];
|
||||||
@@ -155,46 +189,44 @@ void EpubReaderScreen::renderPage() {
|
|||||||
data[3] = (section->currentPage >> 8) & 0xFF;
|
data[3] = (section->currentPage >> 8) & 0xFF;
|
||||||
f.write(data, 4);
|
f.write(data, 4);
|
||||||
f.close();
|
f.close();
|
||||||
|
|
||||||
xSemaphoreGive(sectionMutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpubReaderScreen::renderStatusBar() const {
|
void EpubReaderScreen::renderStatusBar() const {
|
||||||
const auto pageWidth = renderer->getPageWidth();
|
const auto pageWidth = renderer->getPageWidth();
|
||||||
|
|
||||||
std::string progress = std::to_string(section->currentPage + 1) + " / " + std::to_string(section->pageCount);
|
const std::string progress = std::to_string(section->currentPage + 1) + " / " + std::to_string(section->pageCount);
|
||||||
const auto progressTextWidth = renderer->getSmallTextWidth(progress.c_str());
|
const auto progressTextWidth = renderer->getSmallTextWidth(progress.c_str());
|
||||||
renderer->drawSmallText(pageWidth - progressTextWidth, 765, progress.c_str());
|
renderer->drawSmallText(pageWidth - progressTextWidth, 765, progress.c_str());
|
||||||
|
|
||||||
const uint16_t percentage = battery.readPercentage();
|
const uint16_t percentage = battery.readPercentage();
|
||||||
auto percentageText = std::to_string(percentage) + "%";
|
const auto percentageText = std::to_string(percentage) + "%";
|
||||||
const auto percentageTextWidth = renderer->getSmallTextWidth(percentageText.c_str());
|
const auto percentageTextWidth = renderer->getSmallTextWidth(percentageText.c_str());
|
||||||
renderer->drawSmallText(20, 765, percentageText.c_str());
|
renderer->drawSmallText(20, 765, percentageText.c_str());
|
||||||
|
|
||||||
// 1 column on left, 2 columns on right, 5 columns of battery body
|
// 1 column on left, 2 columns on right, 5 columns of battery body
|
||||||
constexpr int batteryWidth = 15;
|
constexpr int batteryWidth = 15;
|
||||||
constexpr int batteryHeight = 10;
|
constexpr int batteryHeight = 10;
|
||||||
const int x = 0;
|
constexpr int x = 0;
|
||||||
const int y = 772;
|
constexpr int y = 772;
|
||||||
|
|
||||||
// Top line
|
// Top line
|
||||||
renderer->drawLine(x, y, x + batteryWidth - 4, y, 1);
|
renderer->drawLine(x, y, x + batteryWidth - 4, y);
|
||||||
// Bottom line
|
// Bottom line
|
||||||
renderer->drawLine(x, y + batteryHeight - 1, x + batteryWidth - 4, y + batteryHeight - 1, 1);
|
renderer->drawLine(x, y + batteryHeight - 1, x + batteryWidth - 4, y + batteryHeight - 1);
|
||||||
// Left line
|
// Left line
|
||||||
renderer->drawLine(x, y, x, y + batteryHeight - 1, 1);
|
renderer->drawLine(x, y, x, y + batteryHeight - 1);
|
||||||
// Battery end
|
// Battery end
|
||||||
renderer->drawLine(x + batteryWidth - 4, y, x + batteryWidth - 4, y + batteryHeight - 1, 1);
|
renderer->drawLine(x + batteryWidth - 4, y, x + batteryWidth - 4, y + batteryHeight - 1);
|
||||||
renderer->drawLine(x + batteryWidth - 3, y + 2, x + batteryWidth - 3, y + batteryHeight - 3, 1);
|
renderer->drawLine(x + batteryWidth - 3, y + 2, x + batteryWidth - 3, y + batteryHeight - 3);
|
||||||
renderer->drawLine(x + batteryWidth - 2, y + 2, x + batteryWidth - 2, y + batteryHeight - 3, 1);
|
renderer->drawLine(x + batteryWidth - 2, y + 2, x + batteryWidth - 2, y + batteryHeight - 3);
|
||||||
renderer->drawLine(x + batteryWidth - 1, y + 2, x + batteryWidth - 1, y + batteryHeight - 3, 1);
|
renderer->drawLine(x + batteryWidth - 1, y + 2, x + batteryWidth - 1, y + batteryHeight - 3);
|
||||||
|
|
||||||
// The +1 is to round up, so that we always fill at least one pixel
|
// The +1 is to round up, so that we always fill at least one pixel
|
||||||
int filledWidth = percentage * (batteryWidth - 5) / 100 + 1;
|
int filledWidth = percentage * (batteryWidth - 5) / 100 + 1;
|
||||||
if (filledWidth > batteryWidth - 5) {
|
if (filledWidth > batteryWidth - 5) {
|
||||||
filledWidth = batteryWidth - 5; // Ensure we don't overflow
|
filledWidth = batteryWidth - 5; // Ensure we don't overflow
|
||||||
}
|
}
|
||||||
renderer->fillRect(x + 1, y + 1, filledWidth, batteryHeight - 2, 1);
|
renderer->fillRect(x + 1, y + 1, filledWidth, batteryHeight - 2);
|
||||||
|
|
||||||
// Page width minus existing content with 30px padding on each side
|
// Page width minus existing content with 30px padding on each side
|
||||||
const int leftMargin = 20 + percentageTextWidth + 30;
|
const int leftMargin = 20 + percentageTextWidth + 30;
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ class EpubReaderScreen final : public Screen {
|
|||||||
Epub* epub;
|
Epub* epub;
|
||||||
Section* section = nullptr;
|
Section* section = nullptr;
|
||||||
TaskHandle_t displayTaskHandle = nullptr;
|
TaskHandle_t displayTaskHandle = nullptr;
|
||||||
SemaphoreHandle_t sectionMutex = nullptr;
|
SemaphoreHandle_t renderingMutex = nullptr;
|
||||||
int currentSpineIndex = 0;
|
int currentSpineIndex = 0;
|
||||||
int nextPageNumber = 0;
|
int nextPageNumber = 0;
|
||||||
|
int pagesUntilFullRefresh = 0;
|
||||||
bool updateRequired = false;
|
bool updateRequired = false;
|
||||||
const std::function<void()> onGoHome;
|
const std::function<void()> onGoHome;
|
||||||
|
|
||||||
@@ -25,7 +26,6 @@ class EpubReaderScreen final : public Screen {
|
|||||||
public:
|
public:
|
||||||
explicit EpubReaderScreen(EpdRenderer* renderer, Epub* epub, const std::function<void()>& onGoHome)
|
explicit EpubReaderScreen(EpdRenderer* renderer, Epub* epub, const std::function<void()>& onGoHome)
|
||||||
: Screen(renderer), epub(epub), onGoHome(onGoHome) {}
|
: Screen(renderer), epub(epub), onGoHome(onGoHome) {}
|
||||||
~EpubReaderScreen() override { free(section); }
|
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
void onExit() override;
|
void onExit() override;
|
||||||
void handleInput(Input input) override;
|
void handleInput(Input input) override;
|
||||||
|
|||||||
@@ -8,26 +8,33 @@ void FileSelectionScreen::taskTrampoline(void* param) {
|
|||||||
self->displayTaskLoop();
|
self->displayTaskLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSelectionScreen::onEnter() {
|
void FileSelectionScreen::loadFiles() {
|
||||||
files.clear();
|
files.clear();
|
||||||
auto root = SD.open("/");
|
selectorIndex = 0;
|
||||||
File file;
|
auto root = SD.open(basepath.c_str());
|
||||||
while ((file = root.openNextFile())) {
|
for (File file = root.openNextFile(); file; file = root.openNextFile()) {
|
||||||
if (file.isDirectory()) {
|
|
||||||
file.close();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto filename = std::string(file.name());
|
auto filename = std::string(file.name());
|
||||||
if (filename.substr(filename.length() - 5) != ".epub" || filename[0] == '.') {
|
if (filename[0] == '.') {
|
||||||
file.close();
|
file.close();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
files.emplace_back(filename + "/");
|
||||||
|
} else if (filename.substr(filename.length() - 5) == ".epub") {
|
||||||
files.emplace_back(filename);
|
files.emplace_back(filename);
|
||||||
|
}
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
root.close();
|
root.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSelectionScreen::onEnter() {
|
||||||
|
renderingMutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
|
basepath = "/";
|
||||||
|
loadFiles();
|
||||||
|
selectorIndex = 0;
|
||||||
|
|
||||||
// Trigger first update
|
// Trigger first update
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
@@ -41,9 +48,16 @@ void FileSelectionScreen::onEnter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FileSelectionScreen::onExit() {
|
void FileSelectionScreen::onExit() {
|
||||||
|
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
if (displayTaskHandle) {
|
||||||
vTaskDelete(displayTaskHandle);
|
vTaskDelete(displayTaskHandle);
|
||||||
displayTaskHandle = nullptr;
|
displayTaskHandle = nullptr;
|
||||||
}
|
}
|
||||||
|
vSemaphoreDelete(renderingMutex);
|
||||||
|
renderingMutex = nullptr;
|
||||||
|
files.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void FileSelectionScreen::handleInput(const Input input) {
|
void FileSelectionScreen::handleInput(const Input input) {
|
||||||
if (input.button == VOLUME_DOWN || input.button == RIGHT) {
|
if (input.button == VOLUME_DOWN || input.button == RIGHT) {
|
||||||
@@ -53,8 +67,23 @@ void FileSelectionScreen::handleInput(const Input input) {
|
|||||||
selectorIndex = (selectorIndex + files.size() - 1) % files.size();
|
selectorIndex = (selectorIndex + files.size() - 1) % files.size();
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
} else if (input.button == CONFIRM) {
|
} else if (input.button == CONFIRM) {
|
||||||
Serial.printf("Selected file: %s\n", files[selectorIndex].c_str());
|
if (files.empty()) {
|
||||||
onSelect("/" + files[selectorIndex]);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files[selectorIndex].back() == '/') {
|
||||||
|
if (basepath.back() != '/') basepath += "/";
|
||||||
|
basepath += files[selectorIndex].substr(0, files[selectorIndex].length() - 1);
|
||||||
|
loadFiles();
|
||||||
|
updateRequired = true;
|
||||||
|
} else {
|
||||||
|
onSelect(basepath + files[selectorIndex]);
|
||||||
|
}
|
||||||
|
} else if (input.button == BACK && basepath != "/") {
|
||||||
|
basepath = basepath.substr(0, basepath.rfind('/'));
|
||||||
|
if (basepath.empty()) basepath = "/";
|
||||||
|
loadFiles();
|
||||||
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +91,9 @@ void FileSelectionScreen::displayTaskLoop() {
|
|||||||
while (true) {
|
while (true) {
|
||||||
if (updateRequired) {
|
if (updateRequired) {
|
||||||
updateRequired = false;
|
updateRequired = false;
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
render();
|
render();
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
}
|
}
|
||||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
@@ -72,15 +103,19 @@ void FileSelectionScreen::render() const {
|
|||||||
renderer->clearScreen();
|
renderer->clearScreen();
|
||||||
|
|
||||||
const auto pageWidth = renderer->getPageWidth();
|
const auto pageWidth = renderer->getPageWidth();
|
||||||
const auto titleWidth = renderer->getTextWidth("CrossPoint Reader", true);
|
const auto titleWidth = renderer->getTextWidth("CrossPoint Reader", BOLD);
|
||||||
renderer->drawText((pageWidth - titleWidth) / 2, 0, "CrossPoint Reader", true);
|
renderer->drawText((pageWidth - titleWidth) / 2, 0, "CrossPoint Reader", 1, BOLD);
|
||||||
|
|
||||||
|
if (files.empty()) {
|
||||||
|
renderer->drawUiText(10, 50, "No EPUBs found");
|
||||||
|
} else {
|
||||||
// Draw selection
|
// Draw selection
|
||||||
renderer->fillRect(0, 50 + selectorIndex * 20 + 2, pageWidth - 1, 20, 1);
|
renderer->fillRect(0, 50 + selectorIndex * 30 + 2, pageWidth - 1, 30);
|
||||||
|
|
||||||
for (size_t i = 0; i < files.size(); i++) {
|
for (size_t i = 0; i < files.size(); i++) {
|
||||||
const auto file = files[i];
|
const auto file = files[i];
|
||||||
renderer->drawSmallText(50, 50 + i * 20, file.c_str(), i == selectorIndex ? 0 : 1);
|
renderer->drawUiText(10, 50 + i * 30, file.c_str(), i == selectorIndex ? 0 : 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer->flushDisplay();
|
renderer->flushDisplay();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -10,6 +11,8 @@
|
|||||||
|
|
||||||
class FileSelectionScreen final : public Screen {
|
class FileSelectionScreen final : public Screen {
|
||||||
TaskHandle_t displayTaskHandle = nullptr;
|
TaskHandle_t displayTaskHandle = nullptr;
|
||||||
|
SemaphoreHandle_t renderingMutex = nullptr;
|
||||||
|
std::string basepath = "/";
|
||||||
std::vector<std::string> files;
|
std::vector<std::string> files;
|
||||||
int selectorIndex = 0;
|
int selectorIndex = 0;
|
||||||
bool updateRequired = false;
|
bool updateRequired = false;
|
||||||
@@ -18,6 +21,7 @@ class FileSelectionScreen final : public Screen {
|
|||||||
static void taskTrampoline(void* param);
|
static void taskTrampoline(void* param);
|
||||||
[[noreturn]] void displayTaskLoop();
|
[[noreturn]] void displayTaskLoop();
|
||||||
void render() const;
|
void render() const;
|
||||||
|
void loadFiles();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FileSelectionScreen(EpdRenderer* renderer, const std::function<void(const std::string&)>& onSelect)
|
explicit FileSelectionScreen(EpdRenderer* renderer, const std::function<void(const std::string&)>& onSelect)
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
#include <EpdRenderer.h>
|
#include <EpdRenderer.h>
|
||||||
|
|
||||||
void FullScreenMessageScreen::onEnter() {
|
void FullScreenMessageScreen::onEnter() {
|
||||||
const auto width = renderer->getTextWidth(text.c_str(), bold, italic);
|
const auto width = renderer->getUiTextWidth(text.c_str(), style);
|
||||||
const auto height = renderer->getLineHeight();
|
const auto height = renderer->getLineHeight();
|
||||||
const auto left = (renderer->getPageWidth() - width) / 2;
|
const auto left = (renderer->getPageWidth() - width) / 2;
|
||||||
const auto top = (renderer->getPageHeight() - height) / 2;
|
const auto top = (renderer->getPageHeight() - height) / 2;
|
||||||
|
|
||||||
renderer->clearScreen(invert);
|
renderer->clearScreen(invert);
|
||||||
renderer->drawText(left, top, text.c_str(), bold, italic, invert ? 0 : 1);
|
renderer->drawUiText(left, top, text.c_str(), invert ? 0 : 1, style);
|
||||||
renderer->flushDisplay();
|
renderer->flushDisplay(partialUpdate);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,18 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "EpdFontFamily.h"
|
||||||
#include "Screen.h"
|
#include "Screen.h"
|
||||||
|
|
||||||
class FullScreenMessageScreen final : public Screen {
|
class FullScreenMessageScreen final : public Screen {
|
||||||
std::string text;
|
std::string text;
|
||||||
bool bold;
|
EpdFontStyle style;
|
||||||
bool italic;
|
|
||||||
bool invert;
|
bool invert;
|
||||||
|
bool partialUpdate;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FullScreenMessageScreen(EpdRenderer* renderer, std::string text, const bool bold = false,
|
explicit FullScreenMessageScreen(EpdRenderer* renderer, std::string text, const EpdFontStyle style = REGULAR,
|
||||||
const bool italic = false, const bool invert = false)
|
const bool invert = false, const bool partialUpdate = true)
|
||||||
: Screen(renderer), text(std::move(text)), bold(bold), italic(italic), invert(invert) {}
|
: Screen(renderer), text(std::move(text)), style(style), invert(invert), partialUpdate(partialUpdate) {}
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
};
|
};
|
||||||
|
|||||||
7
src/screens/SleepScreen.cpp
Normal file
7
src/screens/SleepScreen.cpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#include "SleepScreen.h"
|
||||||
|
|
||||||
|
#include <EpdRenderer.h>
|
||||||
|
|
||||||
|
#include "images/SleepScreenImg.h"
|
||||||
|
|
||||||
|
void SleepScreen::onEnter() { renderer->drawImageNoMargin(SleepScreenImg, 0, 0, 800, 480, false, true); }
|
||||||
8
src/screens/SleepScreen.h
Normal file
8
src/screens/SleepScreen.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Screen.h"
|
||||||
|
|
||||||
|
class SleepScreen final : public Screen {
|
||||||
|
public:
|
||||||
|
explicit SleepScreen(EpdRenderer* renderer) : Screen(renderer) {}
|
||||||
|
void onEnter() override;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user