Added app modes, extracted into mode controllers.
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
#ifndef _APPMODE_H_
|
||||
#define _APPMODE_H_
|
||||
|
||||
class AppMode
|
||||
{
|
||||
public:
|
||||
virtual void start();
|
||||
virtual void loop();
|
||||
virtual void finish();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "core/common.h"
|
||||
#include "core/display.h"
|
||||
#include "ModeRunner.h"
|
||||
#include "MainMenuMode.h"
|
||||
#include "BootMode.h"
|
||||
|
||||
void BootMode::start()
|
||||
{}
|
||||
|
||||
void BootMode::loop()
|
||||
{
|
||||
display_splash_screen();
|
||||
delay(500);
|
||||
|
||||
getModeRunner()->startMainMode(new MainMenuMode());
|
||||
}
|
||||
|
||||
void BootMode::finish()
|
||||
{}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "AppMode.h"
|
||||
|
||||
class BootMode : public AppMode
|
||||
{
|
||||
public:
|
||||
virtual void start();
|
||||
virtual void loop();
|
||||
virtual void finish();
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "core/common.h"
|
||||
#include "core/buttons.h"
|
||||
#include "core/display.h"
|
||||
#include <EPD.h>
|
||||
#include "MainMenuMode.h"
|
||||
|
||||
void MainMenuMode::start()
|
||||
{}
|
||||
|
||||
void MainMenuMode::loop()
|
||||
{
|
||||
display_clear();
|
||||
EPD_print("Main Menu", CENTER, 0);
|
||||
display_update();
|
||||
|
||||
while(1) {
|
||||
delay(5);
|
||||
if (buttons_pressed_ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainMenuMode::finish()
|
||||
{}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "AppMode.h"
|
||||
|
||||
class MainMenuMode : public AppMode
|
||||
{
|
||||
public:
|
||||
virtual void start();
|
||||
virtual void loop();
|
||||
virtual void finish();
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "ModeRunner.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "ModeRunner";
|
||||
|
||||
void ModeRunner::init()
|
||||
{
|
||||
this->modeStackDepth = -1;
|
||||
}
|
||||
|
||||
void ModeRunner::loop()
|
||||
{
|
||||
if (this->modeStackDepth < 0) {
|
||||
ESP_LOGE(TAG, "Cannot run app mode. None activated (depth %d).", this->modeStackDepth);
|
||||
return;
|
||||
}
|
||||
this->modeStack[this->modeStackDepth]->loop();
|
||||
}
|
||||
|
||||
void ModeRunner::startMainMode(AppMode* mode)
|
||||
{
|
||||
this->finishMode(NULL);
|
||||
this->modeStackDepth = -1;
|
||||
this->startInnerMode(mode);
|
||||
}
|
||||
|
||||
void ModeRunner::startInnerMode(AppMode* mode)
|
||||
{
|
||||
if (this->modeStackDepth + 1 >= MODE_STACK_SIZE) {
|
||||
ESP_LOGE(TAG, "Cannot start inner mode. Stack overflow.");
|
||||
return;
|
||||
}
|
||||
this->modeStackDepth++;
|
||||
this->modeStack[this->modeStackDepth] = mode;
|
||||
ESP_LOGI(TAG, "Starting new mode (depth %d).", this->modeStackDepth);
|
||||
mode->start();
|
||||
}
|
||||
|
||||
void ModeRunner::finishMode(AppMode* mode)
|
||||
{
|
||||
for (int i = this->modeStackDepth; i >= 0; i--) {
|
||||
ESP_LOGI(TAG, "Finishing mode (depth %d).", this->modeStackDepth);
|
||||
this->modeStack[i]->finish();
|
||||
this->modeStackDepth--;
|
||||
if (mode == this->modeStack[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModeRunner modeRunner;
|
||||
ModeRunner* getModeRunner()
|
||||
{
|
||||
return &modeRunner;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "AppMode.h"
|
||||
#define MODE_STACK_SIZE 5
|
||||
|
||||
class ModeRunner
|
||||
{
|
||||
public:
|
||||
void init();
|
||||
void loop();
|
||||
|
||||
void startMainMode(AppMode* mode);
|
||||
void startInnerMode(AppMode* mode);
|
||||
|
||||
void finishMode(AppMode* mode);
|
||||
|
||||
private:
|
||||
AppMode* modeStack[MODE_STACK_SIZE];
|
||||
int modeStackDepth = -1;
|
||||
};
|
||||
|
||||
ModeRunner* getModeRunner();
|
||||
@@ -0,0 +1,106 @@
|
||||
#include "core/common.h"
|
||||
#include "string.h"
|
||||
#include "core/buttons.h"
|
||||
#include "core/display.h"
|
||||
#include "reader/Typesetter.h"
|
||||
#include "reader/TextStorage.h"
|
||||
#include "reader/PagePrinter.h"
|
||||
#include "ReaderMode.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "ReaderMode";
|
||||
|
||||
// TODO: class members
|
||||
Typesetter typesetter;
|
||||
PagePrinter pagePrinter;
|
||||
TextStorage textStorage;
|
||||
TextReader* textReader = textStorage.open("/sdcard/book.txt");
|
||||
Page* pageLast = NULL;
|
||||
Page* pageCurrent = NULL;
|
||||
|
||||
long bookmark = 0;
|
||||
//bool displaySleeping = false;
|
||||
|
||||
void ReaderMode::start()
|
||||
{
|
||||
}
|
||||
|
||||
void ReaderMode::loop()
|
||||
{
|
||||
char text[1024];
|
||||
if (textReader != NULL) {
|
||||
if (pageCurrent == NULL) {
|
||||
size_t read = textReader->read(bookmark, text, sizeof(text));
|
||||
pageCurrent = typesetter.preparePage(text, read);
|
||||
pageCurrent->start = bookmark;
|
||||
}
|
||||
if (pageLast == NULL) {
|
||||
// align with the start?
|
||||
if (bookmark < sizeof(text)) {
|
||||
size_t read = textReader->read(0, text, sizeof(text));
|
||||
pageLast = typesetter.preparePage(text, read);
|
||||
pageLast->start = 0;
|
||||
} else {
|
||||
size_t read = textReader->read((bookmark - sizeof(text)), text, sizeof(text));
|
||||
pageLast = typesetter.preparePreviousPage(text, read);
|
||||
pageLast->start = bookmark - pageLast->len;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
typesetter.destroyPage(pageCurrent);
|
||||
strcpy(text, "File could not be opened.");
|
||||
pageCurrent = typesetter.preparePage(text, sizeof(text));
|
||||
}
|
||||
|
||||
display_clear();
|
||||
pagePrinter.print(pageCurrent);
|
||||
display_update();
|
||||
|
||||
//time_t idleStart = clock();
|
||||
while (1) {
|
||||
delay(10);
|
||||
if (buttons_pressed_ok()) {
|
||||
ESP_LOGI(TAG, "Clear page.");
|
||||
display_refresh();
|
||||
break;
|
||||
}
|
||||
if (buttons_pressed_plus()) {
|
||||
ESP_LOGI(TAG, "Turn page PLUS.");
|
||||
if (pageCurrent != NULL) {
|
||||
bookmark = pageCurrent->start + pageCurrent->len;
|
||||
// TODO: limit bookmark to file size
|
||||
typesetter.destroyPage(pageLast);
|
||||
pageLast = pageCurrent;
|
||||
pageCurrent = NULL;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "No current page.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (buttons_pressed_minus()) {
|
||||
ESP_LOGI(TAG, "Turn page MINUS.");
|
||||
if (pageLast != NULL) {
|
||||
bookmark = pageLast->start;
|
||||
typesetter.destroyPage(pageCurrent);
|
||||
pageCurrent = pageLast;
|
||||
pageLast = NULL;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "No last page.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*if (!displaySleeping && (clock() - idleStart > DISPLAY_SLEEP_TIMEOUT)) {
|
||||
displaySleeping = true;
|
||||
ESP_LOGI(TAG, "Display going to sleep after %d ms.", DISPLAY_SLEEP_TIMEOUT);
|
||||
display_sleep();
|
||||
}*/
|
||||
}
|
||||
/*if (displaySleeping) {
|
||||
displaySleeping = false;
|
||||
ESP_LOGI(TAG, "Display waking up.");
|
||||
display_wake();
|
||||
}*/
|
||||
}
|
||||
|
||||
void ReaderMode::finish()
|
||||
{}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "AppMode.h"
|
||||
|
||||
class ReaderMode : AppMode
|
||||
{
|
||||
public:
|
||||
virtual void start();
|
||||
virtual void loop();
|
||||
virtual void finish();
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
#include "Page.h"
|
||||
|
||||
Page::Page()
|
||||
{}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef _PAGE_H_
|
||||
#define _PAGE_H_
|
||||
#include <stdlib.h>
|
||||
|
||||
class Page
|
||||
{
|
||||
public:
|
||||
Page();
|
||||
|
||||
size_t start;
|
||||
char* text;
|
||||
size_t len;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "display.h"
|
||||
#include "EPD.h"
|
||||
#include "PagePrinter.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "PagePrinter";
|
||||
|
||||
//int pageFont = DEFAULT_FONT;
|
||||
int pageFont = DEJAVU18_FONT;
|
||||
|
||||
PagePrinter::PagePrinter()
|
||||
{}
|
||||
|
||||
void PagePrinter::print(Page* page)
|
||||
{
|
||||
if (page->text == NULL) {
|
||||
ESP_LOGE(TAG, "Page text is NULL");
|
||||
return;
|
||||
}
|
||||
if (page->len == 0) {
|
||||
return;
|
||||
}
|
||||
EPD_setFont(pageFont, NULL);
|
||||
text_wrap = 1;
|
||||
EPD_print(page->text, 0, 0);
|
||||
//EPD_UpdateScreen();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "Page.h"
|
||||
|
||||
class PagePrinter
|
||||
{
|
||||
public:
|
||||
PagePrinter();
|
||||
|
||||
void print(Page* page);
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "PageSettingsProvider.h"
|
||||
#include "EPD.h"
|
||||
#include "EPDspi.h" // TODO: remove after display config is extracted
|
||||
|
||||
int PageSettingsProvider::getWidth()
|
||||
{
|
||||
return EPD_DISPLAY_WIDTH;
|
||||
}
|
||||
|
||||
int PageSettingsProvider::getHeight()
|
||||
{
|
||||
return EPD_DISPLAY_HEIGHT;
|
||||
}
|
||||
|
||||
int PageSettingsProvider::getCharWidth(char c)
|
||||
{
|
||||
char txt[2] = { c, 0x00 };
|
||||
return this->getStringWidth(txt);
|
||||
}
|
||||
|
||||
extern int pageFont;
|
||||
void activatePageFont()
|
||||
{
|
||||
EPD_setFont(pageFont, NULL); // TODO: hack to get the same font as the printer
|
||||
}
|
||||
|
||||
int PageSettingsProvider::getStringWidth(char* string)
|
||||
{
|
||||
activatePageFont();
|
||||
int ret = EPD_getStringWidth(string);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PageSettingsProvider::getCharSpace()
|
||||
{
|
||||
return font_x_space;
|
||||
}
|
||||
|
||||
int PageSettingsProvider::getLineHeight()
|
||||
{
|
||||
activatePageFont();
|
||||
return EPD_getfontheight();
|
||||
}
|
||||
|
||||
int PageSettingsProvider::getLineSpace()
|
||||
{
|
||||
return font_line_space;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class PageSettingsProvider
|
||||
{
|
||||
public:
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
|
||||
int getCharWidth(char c);
|
||||
int getStringWidth(char* string);
|
||||
int getCharSpace();
|
||||
int getLineHeight();
|
||||
int getLineSpace();
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <string.h>
|
||||
#include "TextReader.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "TextReader";
|
||||
|
||||
TextReader::TextReader(FILE* file)
|
||||
{
|
||||
this->f = file;
|
||||
}
|
||||
|
||||
void TextReader::close()
|
||||
{
|
||||
fclose(this->f);
|
||||
}
|
||||
|
||||
size_t TextReader::read(long pos, char* text, size_t len)
|
||||
{
|
||||
memset(text, 0, len);
|
||||
if (pos < 0) {
|
||||
len += pos;
|
||||
pos = 0;
|
||||
}
|
||||
if (len <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (this->f == NULL) {
|
||||
ESP_LOGE(TAG, "File not opened.");
|
||||
sprintf(text, "File could not be opened.");
|
||||
return 0;
|
||||
} else {
|
||||
fseek(this->f, pos, SEEK_SET);
|
||||
size_t read = fread(text, 1, len, this->f);
|
||||
if (read > 0) {
|
||||
//ESP_LOGI(TAG, "Read content: %s", text);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "End of file. Closing.");
|
||||
fclose(this->f);
|
||||
this->f = NULL;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
class TextReader
|
||||
{
|
||||
public:
|
||||
TextReader(FILE* file);
|
||||
|
||||
size_t read(long pos, char* text, size_t len);
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
|
||||
FILE* f;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "TextStorage.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "TextStorage";
|
||||
|
||||
TextStorage::TextStorage()
|
||||
{}
|
||||
|
||||
TextReader* TextStorage::open(char* filename)
|
||||
{
|
||||
FILE* f = fopen("/sdcard/book.txt", "r");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "File could not be opened");
|
||||
return NULL;
|
||||
}
|
||||
ESP_LOGI(TAG, "File opened for reading.");
|
||||
return new TextReader(f);
|
||||
}
|
||||
|
||||
void TextStorage::close(TextReader* reader)
|
||||
{
|
||||
reader->close();
|
||||
delete reader;
|
||||
ESP_LOGI(TAG, "File closed.");
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "TextReader.h"
|
||||
|
||||
class TextStorage
|
||||
{
|
||||
public:
|
||||
TextStorage();
|
||||
|
||||
TextReader* open(char* filename);
|
||||
void close(TextReader* reader);
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
#include <string.h>
|
||||
#include "Typesetter.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "Typesetter";
|
||||
|
||||
Typesetter::Typesetter()
|
||||
{
|
||||
this->pageSettingsProvider = new PageSettingsProvider(); // TODO: make it a param
|
||||
}
|
||||
|
||||
Typesetter::~Typesetter()
|
||||
{
|
||||
delete this->pageSettingsProvider;
|
||||
}
|
||||
|
||||
static Page* extractPage(char* text, size_t len)
|
||||
{
|
||||
Page* page = new Page;
|
||||
page->text = new char[len+1];
|
||||
memcpy(page->text, text, len);
|
||||
page->text[len] = 0;
|
||||
page->len = len;
|
||||
ESP_LOGI(TAG, "Extracted page (%d bytes):\n%s\n----", page->len, page->text);
|
||||
return page;
|
||||
}
|
||||
|
||||
Page* Typesetter::preparePage(char* text, size_t len)
|
||||
{
|
||||
return this->preparePageInternal(text, len, 0);
|
||||
}
|
||||
|
||||
Page* Typesetter::preparePreviousPage(char* text, size_t len)
|
||||
{
|
||||
return this->preparePageInternal(text, len, 1);
|
||||
}
|
||||
|
||||
void Typesetter::destroyPage(Page* page)
|
||||
{
|
||||
if (page == NULL) {
|
||||
return;
|
||||
}
|
||||
delete page->text;
|
||||
delete page;
|
||||
}
|
||||
|
||||
Page* Typesetter::preparePageInternal(char* text, size_t len, int reverse)
|
||||
{
|
||||
int page_width = this->pageSettingsProvider->getWidth();
|
||||
int page_height = this->pageSettingsProvider->getHeight();
|
||||
int line_height = this->pageSettingsProvider->getLineHeight()
|
||||
+ this->pageSettingsProvider->getLineSpace();
|
||||
int line_count = page_height / line_height;
|
||||
|
||||
char buf[len+1];
|
||||
memset(buf, 0, len+1);
|
||||
|
||||
int line_start = 0;
|
||||
ESP_LOGI(TAG, " === Typesetting%s page === ", reverse ? " reversed" : "");
|
||||
for (int line = 0; line < line_count && line_start < len; line++) {
|
||||
int i;
|
||||
for (i = line_start; i < len; i++) {
|
||||
buf[i] = text[reverse ? (len - i) : (i)];
|
||||
if (buf[i] == '\n') {
|
||||
break;
|
||||
}
|
||||
int line_width = this->pageSettingsProvider->getStringWidth(&buf[line_start]);
|
||||
if (line_width > page_width) {
|
||||
// backtrack
|
||||
buf[i] = 0;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "Line %d | %s", line, &buf[line_start]);
|
||||
//ESP_LOGI(TAG, "Line %d [%04d:%04d] | %s | %03d / %03d", line, line_start, i, &buf[line_start],
|
||||
// this->pageSettingsProvider->getStringWidth(&buf[line_start]), page_width);
|
||||
line_start = i + 1;
|
||||
}
|
||||
|
||||
return extractPage(text, line_start);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include <stdlib.h>
|
||||
#include "Page.h"
|
||||
#include "PageSettingsProvider.h"
|
||||
|
||||
class Typesetter
|
||||
{
|
||||
public:
|
||||
Typesetter();
|
||||
~Typesetter();
|
||||
|
||||
Page* preparePage(char* text, size_t len);
|
||||
Page* preparePreviousPage(char* text, size_t len);
|
||||
void destroyPage(Page* page);
|
||||
|
||||
private:
|
||||
Page* preparePageInternal(char* text, size_t len, int direction);
|
||||
|
||||
PageSettingsProvider* pageSettingsProvider; // TODO: expose so it may be changed
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user