Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.sh text eol=lf
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ build_*.log
*.carousel
release/*
!release/README.md

.history
RELEASE_NOTES.md
2 changes: 1 addition & 1 deletion lib/EpdFont/builtinFonts/source_han_sans_tc_10_regular.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* name: source_han_sans_tc_10_regular
* size: 10
* mode: 1-bit
* Command used: D:\RURU-ALL\Library\工具\閱星曈刷機\Carousel-繁中版\lib\EpdFont\scripts\fontconvert.py source_han_sans_tc_10_regular 10 D:\RURU-ALL\Library\工具\閱星曈刷機\Carousel-繁中版\lib\EpdFont\builtinFonts\source\SourceHanSansTC\SourceHanSansTC-Regular.otf --charset-file D:\RURU-ALL\Library\工具\閱星曈刷機\Carousel-繁中版\scripts\charsets\ui_charset_merged.txt
* Command used: fontconvert.py source_han_sans_tc_10_regular 10 SourceHanSansTC-Regular.otf --charset-file ui_charset_merged.txt
*/
#pragma once
#include "EpdFontData.h"
Expand Down
2 changes: 1 addition & 1 deletion lib/EpdFont/builtinFonts/source_han_sans_tc_12_regular.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* name: source_han_sans_tc_12_regular
* size: 12
* mode: 1-bit
* Command used: D:\RURU-ALL\Library\工具\閱星曈刷機\Carousel-繁中版\lib\EpdFont\scripts\fontconvert.py source_han_sans_tc_12_regular 12 D:\RURU-ALL\Library\工具\閱星曈刷機\Carousel-繁中版\lib\EpdFont\builtinFonts\source\SourceHanSansTC\SourceHanSansTC-Regular.otf --charset-file D:\RURU-ALL\Library\工具\閱星曈刷機\Carousel-繁中版\scripts\charsets\ui_charset.txt
* Command used: fontconvert.py source_han_sans_tc_12_regular 12 SourceHanSansTC-Regular.otf --charset-file ui_charset.txt
*/
#pragma once
#include "EpdFontData.h"
Expand Down
2 changes: 1 addition & 1 deletion lib/EpdFont/builtinFonts/source_han_sans_tc_17_regular.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* name: source_han_sans_tc_17_regular
* size: 17
* mode: 1-bit
* Command used: D:\RURU-ALL\Library\工具\閱星曈刷機\Carousel-繁中版\lib\EpdFont\scripts\fontconvert.py source_han_sans_tc_17_regular 17 D:\RURU-ALL\Library\工具\閱星曈刷機\Carousel-繁中版\lib\EpdFont\builtinFonts\source\SourceHanSansTC\SourceHanSansTC-Regular.otf --charset-file D:\RURU-ALL\Library\工具\閱星曈刷機\Carousel-繁中版\scripts\charsets\ui_charset_reader.txt
* Command used: fontconvert.py source_han_sans_tc_17_regular 17 SourceHanSansTC-Regular.otf --charset-file ui_charset_reader.txt
*/
#pragma once
#include "EpdFontData.h"
Expand Down
4 changes: 3 additions & 1 deletion lib/EpdFont/scripts/fontconvert.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import struct
import argparse
from collections import namedtuple
from pathlib import Path

# Originally from https://github.com/vroland/epdiy

Expand Down Expand Up @@ -437,12 +438,13 @@ def load_glyph(code_point):
# ============================================================
# Output C header (original behavior)
# ============================================================
command_used = " ".join(Path(arg).name if Path(arg).is_absolute() else arg for arg in sys.argv)
print(f"""/**
* generated by fontconvert.py
* name: {font_name}
* size: {size}
* mode: {'2-bit' if is2Bit else '1-bit'}
* Command used: {' '.join(sys.argv)}
* Command used: {command_used}
*/
#pragma once
#include "EpdFontData.h"
Expand Down
1 change: 1 addition & 0 deletions lib/Xtc/Xtc.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ size_t getmaxchapter(){
if (parser) {
return parser->maxChapterCount;
}
return 0;
};


Expand Down
2 changes: 1 addition & 1 deletion lib/Xtc/Xtc/XtcParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ std::string getChapterTitleByIndex(int chapterIndex) {
Serial.printf("[%lu] [XTC] 已进入getChapterTitleByIndex,chapterActualCount=%d\n", millis(),chapterActualCount);
for(int i = 0; i < 25; i++) {
if(ChapterList[i].chapterIndex == chapterIndex) {
return std::string(ChapterList[i].shortTitle);
Serial.printf("[%lu] [XTC] getChapterTitleByIndex里第%d章,名字为:%s %u\n", millis(), i, ChapterList[i].shortTitle);
return std::string(ChapterList[i].shortTitle);
}
}
return ""; // 无此章节返回空字符串
Expand Down
44 changes: 39 additions & 5 deletions scripts/build_ui_fonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
FONT_SOURCE = ROOT / "lib" / "EpdFont" / "builtinFonts" / "source" / "SourceHanSansTC"
FONT_OUTPUT_DIR = ROOT / "lib" / "EpdFont" / "builtinFonts"
FONTCONVERT = ROOT / "lib" / "EpdFont" / "scripts" / "fontconvert.py"
FONTCONVERT_REQUIREMENTS = ROOT / "lib" / "EpdFont" / "scripts" / "requirements.txt"

# UI / 外部文字 / reader 用不同字號,但共用常用字集,避免塞完整閱讀字集
UI_FONT_SIZES = [12]
Expand Down Expand Up @@ -127,6 +128,28 @@ def merged_charset_hash(*charsets):
return h.hexdigest()


def expected_font_outputs():
"""Return the generated font headers required by the firmware build."""
sizes = UI_FONT_SIZES + EXTERNAL_FONT_SIZES + READER_FONT_SIZES
return [
FONT_OUTPUT_DIR / f"source_han_sans_tc_{size}_{style}.h"
for size in sizes
for style in UI_FONT_STYLES
]


def fontconvert_install_hint():
return f"{sys.executable} -m pip install -r {FONTCONVERT_REQUIREMENTS}"


def can_run_fontconvert():
try:
import freetype # noqa: F401
return True
except ModuleNotFoundError:
return False


def run_fontconvert(size, style, charset_file, output_path):
"""跑 fontconvert.py 產 epdfont .h"""
font_name = f"source_han_sans_tc_{size}_{style}"
Expand All @@ -148,7 +171,10 @@ def run_fontconvert(size, style, charset_file, output_path):
str(charset_file),
]
try:
result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
env = os.environ.copy()
env["PYTHONIOENCODING"] = "utf-8"
env["PYTHONUTF8"] = "1"
result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", env=env)
if result.returncode != 0:
print(f" ERROR: fontconvert failed for {font_name}", file=sys.stderr)
print(result.stderr, file=sys.stderr)
Expand Down Expand Up @@ -191,15 +217,23 @@ def main():
HASH_CACHE.parent.mkdir(parents=True, exist_ok=True)
if HASH_CACHE.exists():
last_hash = HASH_CACHE.read_text().strip()
expected_sizes = UI_FONT_SIZES + EXTERNAL_FONT_SIZES + READER_FONT_SIZES
if last_hash == current_hash and all(
(FONT_OUTPUT_DIR / f"source_han_sans_tc_{size}_{style}.h").exists()
for size in expected_sizes
for style in UI_FONT_STYLES
path.exists()
for path in expected_font_outputs()
):
print("[build_ui_fonts] No charset changes since last build, skipping font generation.")
return

if not can_run_fontconvert():
missing_outputs = [path for path in expected_font_outputs() if not path.exists()]
print("[build_ui_fonts] freetype-py is not installed for this Python.", file=sys.stderr)
print(f"[build_ui_fonts] To regenerate fonts, run: {fontconvert_install_hint()}", file=sys.stderr)
if not missing_outputs:
print("[build_ui_fonts] Existing generated font headers found; skipping regeneration.")
return
missing = ", ".join(path.name for path in missing_outputs)
raise SystemExit(f"[build_ui_fonts] Missing generated font headers: {missing}")

# 4. 跑 fontconvert 產生子集字型
print("[build_ui_fonts] Charset changed (or first build), regenerating UI fonts...")
success_count = 0
Expand Down
13 changes: 13 additions & 0 deletions scripts/generate_ui_font_subset.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@

from build_ui_fonts import (
FONT_OUTPUT_DIR,
can_run_fontconvert,
EXTERNAL_FONT_SIZES,
READER_FONT_SIZES,
UI_CHARSET,
UI_CHARSET_MERGED,
UI_CHARSET_READER,
UI_FONT_SIZES,
UI_FONT_STYLES,
expected_font_outputs,
fontconvert_install_hint,
run_fontconvert,
)

Expand All @@ -22,6 +25,16 @@ def main():
if not UI_CHARSET_READER.exists():
raise SystemExit(f"Missing charset file: {UI_CHARSET_READER}")

if not can_run_fontconvert():
missing_outputs = [path for path in expected_font_outputs() if not path.exists()]
print("[generate_ui_font_subset] freetype-py is not installed for this Python.", flush=True)
print(f"[generate_ui_font_subset] To regenerate fonts, run: {fontconvert_install_hint()}", flush=True)
if not missing_outputs:
print("[generate_ui_font_subset] Existing generated font headers found; skipping regeneration.", flush=True)
return
missing = ", ".join(path.name for path in missing_outputs)
raise SystemExit(f"[generate_ui_font_subset] Missing generated font headers: {missing}")

success_count = 0
for size in UI_FONT_SIZES:
for style in UI_FONT_STYLES:
Expand Down
26 changes: 20 additions & 6 deletions src/activities/home/HomeActivity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <SDCardManager.h>
#include <Utf8.h>
#include <Xtc.h>
#include <esp_task_wdt.h>

#include <cstring>
#include <vector>
Expand All @@ -21,6 +22,12 @@
//清理字体内存
#include "CustomEpdFont.h"

namespace {
void resetWatchdog() {
esp_task_wdt_reset();
}
} // namespace

void HomeActivity::taskTrampoline(void* param) {
auto* self = static_cast<HomeActivity*>(param);
self->displayTaskLoop();
Expand Down Expand Up @@ -82,7 +89,9 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
// 原本 if (!coverBmpPath.empty()) 才抓 → 沒點進去過的書永遠拿不到 coverBmpPath
// 現在改:不管 coverBmpPath 有沒有、只要 cache 不存在就主動 load epub 拿 metadata + 生 thumb
int progress = 0;
const int progressStep = recentBooks.empty() ? 0 : 90 / static_cast<int>(recentBooks.size());
for (RecentBook& book : recentBooks) {
resetWatchdog();
// 先看「已知 coverBmpPath」對應的 cache 在不在
bool needBuildThumb = true;
if (!book.coverBmpPath.empty()) {
Expand All @@ -101,24 +110,26 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
// 兩種狀況都要 load epub/xtc 拿 metadata + 生 thumb
if (StringUtils::checkFileExtension(book.path, ".epub")) {
Epub epub(book.path, "/.crosspoint");
resetWatchdog();
epub.load(false, true); // Skip CSS、只要 metadata

if (!showingLoading) {
showingLoading = true;
popupRect = GUI.drawPopup(renderer, "Loading...");
}
GUI.fillPopupProgress(renderer, popupRect, 10 + progress * (90 / recentBooks.size()));
GUI.fillPopupProgress(renderer, popupRect, 10 + progress * progressStep);
resetWatchdog();
bool success = epub.generateThumbBmp(coverHeight);
if (success) {
// 拿到 thumb 後、回寫 metadata 到 RECENT_BOOKS(含 coverBmpPath、title、author)
const std::string thumbPath = epub.getThumbBmpPath();
const std::string title = epub.getTitle();
const std::string author = epub.getAuthor();
RECENT_BOOKS.updateBook(book.path, title, author, thumbPath);
const std::string storedTitle = title.empty() ? book.title : title;
const std::string storedAuthor = author.empty() ? book.author : author;
RECENT_BOOKS.updateBook(book.path, storedTitle, storedAuthor, thumbPath);
book.coverBmpPath = thumbPath;
if (!book.title.empty() && title.empty()) {
// 保留既有 title(避免 metadata 為空時清空)
} else if (!title.empty()) {
if (!title.empty()) {
book.title = title;
book.author = author;
}
Expand All @@ -136,7 +147,8 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
showingLoading = true;
popupRect = GUI.drawPopup(renderer, "Loading...");
}
GUI.fillPopupProgress(renderer, popupRect, 10 + progress * (90 / recentBooks.size()));
GUI.fillPopupProgress(renderer, popupRect, 10 + progress * progressStep);
resetWatchdog();
bool success = xtc.generateThumbBmp(coverHeight);
if (success) {
const std::string thumbPath = xtc.getThumbBmpPath();
Expand Down Expand Up @@ -340,8 +352,10 @@ void HomeActivity::displayTaskLoop() {
while (true) {
if (updateRequired) {
updateRequired = false;
resetWatchdog();
xSemaphoreTake(renderingMutex, portMAX_DELAY);
render();
resetWatchdog();
xSemaphoreGive(renderingMutex);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
Expand Down
23 changes: 19 additions & 4 deletions src/activities/home/MyLibraryActivity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <GfxRenderer.h>
#include <SDCardManager.h>
#include <esp_task_wdt.h>

#include <algorithm>

Expand All @@ -18,6 +19,10 @@ constexpr int SKIP_PAGE_MS = 700;
constexpr unsigned long GO_HOME_MS = 1000;
//防止误删,把删除改为长按confirm
constexpr int COPY_BUF_SIZE = 256; // 256字节缓冲区,适配小运存

void resetWatchdog() {
esp_task_wdt_reset();
}
} // namespace
//把原来几个函数加上
//删除
Expand Down Expand Up @@ -75,6 +80,7 @@ bool copyFile(const char* srcPath, const char* dstPath) {
uint8_t buf[COPY_BUF_SIZE];
size_t readBytes = 0;
while ((readBytes = srcFile.read(buf, COPY_BUF_SIZE)) > 0) {
resetWatchdog();
dstFile.write(buf, readBytes);
}

Expand Down Expand Up @@ -112,6 +118,7 @@ void searchFilesRecursive(const std::string& currentDir, const std::string& keyw
char name[500];
root.rewindDirectory();
for (auto file = root.openNextFile(); file; file = root.openNextFile()) {
resetWatchdog();
file.getName(name, sizeof(name));
if (name[0] == '.' || strcmp(name, "System Volume Information") == 0 || strcmp(name, "fonts") == 0) {
file.close();
Expand Down Expand Up @@ -252,6 +259,7 @@ void MyLibraryActivity::loadFiles() {

char name[500];
for (auto file = root.openNextFile(); file; file = root.openNextFile()) {
resetWatchdog();
file.getName(name, sizeof(name));
if (name[0] == '.' || strcmp(name, "System Volume Information") == 0 || strcmp(name, "fonts") == 0) {
file.close();
Expand Down Expand Up @@ -498,7 +506,7 @@ void MyLibraryActivity::loop() {
const bool downReleased = mappedInput.wasReleased(MappedInputManager::Button::Down);

const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, false);
const int pageItems = std::max(1, UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, false));

//把文件打开的逻辑放上面了
//这里去掉了
Expand Down Expand Up @@ -530,16 +538,21 @@ if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {

const auto& displayList = isSearchMode ? searchResults : files;
int listSize = static_cast<int>(displayList.size());
if (listSize <= 0) {
return;
}
if (upReleased) {
if (skipPage) {
selectorIndex = std::max(static_cast<int>((selectorIndex / pageItems - 1) * pageItems), 0);
const int currentPage = static_cast<int>(selectorIndex) / pageItems;
selectorIndex = static_cast<size_t>(std::max((currentPage - 1) * pageItems, 0));
} else {
selectorIndex = (selectorIndex + listSize - 1) % listSize;
}
updateRequired = true;
} else if (downReleased) {
if (skipPage) {
selectorIndex = std::min(static_cast<int>((selectorIndex / pageItems + 1) * pageItems), listSize - 1);
const int currentPage = static_cast<int>(selectorIndex) / pageItems;
selectorIndex = static_cast<size_t>(std::min((currentPage + 1) * pageItems, listSize - 1));
} else {
selectorIndex = (selectorIndex + 1) % listSize;
}
Expand All @@ -553,8 +566,10 @@ void MyLibraryActivity::displayTaskLoop() {
while (true) {
if (updateRequired) {
updateRequired = false;
resetWatchdog();
xSemaphoreTake(renderingMutex, portMAX_DELAY);
render();
resetWatchdog();
xSemaphoreGive(renderingMutex);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
Expand Down Expand Up @@ -644,4 +659,4 @@ size_t MyLibraryActivity::findEntry(const std::string& name) const {
for (size_t i = 0; i < files.size(); i++)
if (files[i] == name) return i;
return 0;
}
}
2 changes: 1 addition & 1 deletion src/activities/home/MyLibraryActivity.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class MyLibraryActivity final : public ActivityWithSubactivity {
// CANCEL_SEARCH = 6 // 取消搜索
};
TopOption topSelectorIndex = TopOption::OPEN;
const int topOptionCount = 7;
const int topOptionCount = 5;
char SEARCH_KEYWORD[100] = "賽博"; // 搜索关键词(示例:包含“赛博”的文件)


Expand Down
Loading
Loading