fix: M6.41批 — 启动失败原因透传 + 阅读器左缘返回/阅读页接入view栈 + 后台管理按钮点不动 + 阅读器瘦身(fonts common+页位图释放) + files txt换行
- launcher(453): app_launcher.c g_launch_fail_reason+launch_fail()统一出口(5失败点 fork OOM/exec/no such file/missing dep/execpipe)+main_menu.c launch_fail_toast(缺依赖/CANNOT START: name(reason))+toast256 - launcher(457): bg_page.c bgm_enumerate跳过term_at_ms(杀后当下消失)+不可杀行按钮置灰+row_tap/top_action返回1; subpage.c 消warning - reader(454): reader_on_swipe LEFT/RIGHT→返回(先关overlay否则page_pop回书架)+app.c书架根LEFT→exit - reader(458瘦身A+B): app.json fonts full→common(省13MB atlas)+reader.c RD_PAGE_CACHE 3→2+g_anim_old懒分配/anim_done释放+on_enter/on_leave页位图离开释放重建 - reader(459导航): reader_open返回int+shelf点书push ACT_READER(阅读页接入view栈,on_enter/on_leave驱动) — 使454左缘返回+458瘦身B真正生效 - files(455): viewer.c viewer_cp_len+按码位折行(txt中文不冲出右缘) 0warning;宿主测试全过;files二进制已热更
This commit is contained in:
1 parent
626a87a30d
commit
ddc8d35eb9
11 files changed
+154
-66
No files matched your search
@@ -39,7 +39,27 @@ static int viewer_vp_h(void) { return r_content.h - APP_FOOTER_H; }
|
||||
/* 屏幕行高(px)。 */
|
||||
static int viewer_rowh(const meow_theme *th) { return meow_ui_text_height(th->font_body) + 6; }
|
||||
|
||||
/* 折行:返回逻辑行 li 折成的屏幕行数;若 intra>=0,把该屏幕行文本写入 out(NUL 结尾)。 */
|
||||
/* UTF-8 码位字节长(1..4):s[bi] 起、串内有效字节数 lim。
|
||||
* 首字节非法/续字节非法/序列不完整 → 保守按 1(单字节);保证折行绝不断在多字节码位中间,
|
||||
* 也保证宽测量(meow_ui_text_width 按码位测)对中文/混合文本正确。 */
|
||||
static int viewer_cp_len(const unsigned char *s, int bi, int lim)
|
||||
{
|
||||
unsigned char c = s[bi];
|
||||
int n;
|
||||
if (c < 0x80) return 1;
|
||||
else if ((c & 0xE0) == 0xC0) n = 2;
|
||||
else if ((c & 0xF0) == 0xE0) n = 3;
|
||||
else if ((c & 0xF8) == 0xF0) n = 4;
|
||||
else return 1;
|
||||
if (lim - bi < n) return 1;
|
||||
for (int i = 1; i < n; i++)
|
||||
if ((s[bi + i] & 0xC0) != 0x80) return 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* 折行:返回逻辑行 li 折成的屏幕行数;若 intra>=0,把该屏幕行文本写入 out(NUL 结尾)。
|
||||
* 按「UTF-8 码位」逐字累加显示宽度(meow_ui_text_width 按码位测>0),到内容宽 colw 即断行;
|
||||
* 多字节(CJK)整字不断开。 */
|
||||
static int viewer_wrap_line(int li, int intra, char *out, int outsz)
|
||||
{
|
||||
if (!g_buf || li < 0 || li >= g_nline) return 0;
|
||||
@@ -49,13 +69,15 @@ static int viewer_wrap_line(int li, int intra, char *out, int outsz)
|
||||
while (len > 0 && (g_buf[start + len - 1] == '\n' || g_buf[start + len - 1] == '\r')) len--;
|
||||
int colw = viewer_colw();
|
||||
int col = 0, row = 0, outlen = 0;
|
||||
char ch[2]; ch[1] = '\0';
|
||||
for (int i = 0; i < len; i++) {
|
||||
ch[0] = g_buf[start + i];
|
||||
if (ch[0] == '\r') continue;
|
||||
int w = meow_ui_text_width(ch, 20);
|
||||
const unsigned char *s = (const unsigned char *)g_buf + start;
|
||||
for (int bi = 0; bi < len; ) {
|
||||
int clen = viewer_cp_len(s, bi, len);
|
||||
if (clen == 1 && s[bi] == '\r') { bi++; continue; }
|
||||
char cp[5]; cp[clen] = '\0';
|
||||
memcpy(cp, s + bi, (size_t)clen); bi += clen;
|
||||
int w = meow_ui_text_width(cp, 20);
|
||||
if (col + w > colw && col > 0) { row++; col = 0; }
|
||||
if (row == intra && out && outlen < outsz - 1) out[outlen++] = ch[0];
|
||||
if (row == intra && out && outlen + clen < outsz) { memcpy(out + outlen, cp, (size_t)clen); outlen += clen; }
|
||||
col += w;
|
||||
}
|
||||
if (out) out[outlen] = '\0';
|
||||
|
||||
@@ -23,6 +23,28 @@ static void app_name_copy(char *dst, int cap, const char *src)
|
||||
}
|
||||
|
||||
|
||||
/* M6.40 启动失败原因透传:launch_app/launch_activity(→launch_app_chain)返回 -1 前按实际
|
||||
* 失败点填具体原因(fork OOM / exec 失败 / 缺依赖 / 路径不存在等),供 main_menu.c 的
|
||||
* launch_fail_toast 显示「CANNOT START: %s(%s)」;success 路径恒为 ""。 */
|
||||
static char g_launch_fail_reason[160] = "";
|
||||
|
||||
const char *launch_fail_reason(void)
|
||||
{
|
||||
return g_launch_fail_reason;
|
||||
}
|
||||
|
||||
/* 内部失败出口:设原因 + 记统一失败日志 + 返回 -1(各失败分支复用)。 */
|
||||
static int launch_fail(struct app *a, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(g_launch_fail_reason, sizeof g_launch_fail_reason, fmt, ap);
|
||||
va_end(ap);
|
||||
logmsg("launch failed: %s reason=%s", a->name, g_launch_fail_reason);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* ---- amsd 上报 / 控制挂点(AMSD_DESIGN §4):best-effort,amsd 不在不影响 launcher ---- */
|
||||
#define MEOW_AMS_SOCK_DEFAULT "/usr/data/meow_ams.sock"
|
||||
|
||||
@@ -1585,8 +1607,9 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
uint32_t pick_req = 0;
|
||||
meow_ams_take_picker(pick_action, sizeof pick_action,
|
||||
pick_caller, sizeof pick_caller, &pick_req);
|
||||
if (!a->ok) { logmsg("launch blocked: exec not ok"); return -1; }
|
||||
meow_dep_clear_reason(); /* 每次 launch 入口清 stale 依赖阻断原因(防上一 app 残留) */
|
||||
g_launch_fail_reason[0] = 0; /* M6.40:同上,清 stale 启动失败原因 */
|
||||
if (!a->ok) { logmsg("launch blocked: exec not ok"); return launch_fail(a, "no such file: %s", a->exec); }
|
||||
|
||||
/* M4 后台保活恢复:该 App 驻留后台(播放中经 nav-exit 转过)→ 原进程回前台,
|
||||
* 不 fork/exec(进程仍存无法注入新 env:chain/picker 上下文忽略,记日志)。
|
||||
@@ -1613,7 +1636,7 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
char dep_missing[32] = "";
|
||||
if (meow_check_app_depends(a, dep_missing, sizeof dep_missing) != 0) {
|
||||
logmsg("launch blocked: dep '%s' missing (CANNOT START: %s)", dep_missing, a->name);
|
||||
return -1;
|
||||
return launch_fail(a, "missing dep: %s", dep_missing);
|
||||
}
|
||||
}
|
||||
/* 冷启动:标记内容置为前台归属(a->id),让本 App 首帧即不被自抑(后台常驻 App 继续自抑)。
|
||||
@@ -1641,7 +1664,7 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
* watchdog and waitpid — so an exec failure never leaves an orphan watchdog
|
||||
* and never masquerades as "app ran and exited". */
|
||||
int execpipe[2];
|
||||
if (pipe(execpipe) < 0) { logmsg("exec pipe failed: %s", strerror(errno)); return -1; }
|
||||
if (pipe(execpipe) < 0) { logmsg("exec pipe failed: %s", strerror(errno)); return launch_fail(a, "exec pipe failed: %s", strerror(errno)); }
|
||||
fcntl(execpipe[1], F_SETFD, FD_CLOEXEC);
|
||||
|
||||
/* AMSD_DESIGN §5:stdout/stderr 管道捕获(logcat 思路)。fork 前建管道;子进程 dup2 到
|
||||
@@ -1661,7 +1684,7 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
close(execpipe[0]); close(execpipe[1]);
|
||||
if (out_pipe[0] >= 0) close(out_pipe[0]);
|
||||
if (out_pipe[1] >= 0) close(out_pipe[1]);
|
||||
logmsg("fork failed: %s", strerror(errno)); return -1;
|
||||
logmsg("fork failed: %s", strerror(errno)); return launch_fail(a, "fork failed: %s", strerror(errno));
|
||||
}
|
||||
if (pid == 0) {
|
||||
/* child: exec the app. M2.12 注入 MEOW_LOG_DIR + MEOW_DATA_DIR/MEOW_CACHE_DIR
|
||||
@@ -1850,7 +1873,7 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
int st = 0;
|
||||
reap_wait(pid, &st); /* reap the failed child(有界) */
|
||||
if (out_fd >= 0) close(out_fd);
|
||||
return -1;
|
||||
return launch_fail(a, "exec failed: %s (errno %d)", strerror(exe_err), exe_err);
|
||||
}
|
||||
} /* end if (!resumed):冷启动分支(prepare/fork/exec/确认) */
|
||||
|
||||
|
||||
@@ -38,8 +38,6 @@ typedef struct {
|
||||
int napps;
|
||||
bgmgr_row_t rows[MEOW_BGMR_ROW_MAX];
|
||||
int n;
|
||||
char toast[64];
|
||||
long toast_ms;
|
||||
} bgm_ctx_t;
|
||||
|
||||
/* 找 struct app(launcher 应用表)by id */
|
||||
@@ -71,19 +69,24 @@ static int bgm_app_meta(void *ctx, const char *app_id, pid_t pid,
|
||||
static int bgm_enumerate(void *ctx, bgmgr_row_t *rows, int max)
|
||||
{
|
||||
(void)ctx;
|
||||
int n = meow_bg_count();
|
||||
if (n > max) n = max;
|
||||
for (int i = 0; i < n; i++) {
|
||||
/* 逐槽扫描并**压缩**(g_bg 可能有空洞),跳过已发出终止的条目:
|
||||
* meow_bg_terminate 只置 term_at_ms 发 SIGTERM,真正的 reap/摘表在 meow_bg_tick(launcher
|
||||
* 主循环),它在本页 meow_subpage_run 阻塞期间**不跑**——若不在此过滤,kill/清后旧行会一直
|
||||
* 残留(pid 已死/0 CPU),页面上看就是「点不动」。过滤后 kill/一键清当下即从列表消失(可见反馈)。 */
|
||||
int k = 0;
|
||||
for (int i = 0; i < MEOW_BG_MAX && k < max; i++) {
|
||||
const meow_bg_entry_t *e = meow_bg_at(i);
|
||||
if (!e || !e->used || !e->app_id[0]) continue;
|
||||
memset(&rows[i], 0, sizeof rows[i]);
|
||||
snprintf(rows[i].app_id, sizeof rows[i].app_id, "%s", e->app_id);
|
||||
rows[i].pid = e->pid;
|
||||
rows[i].keepalive = (e->keepalive ? 1 : 0);
|
||||
rows[i].on_card = (e->on_card ? 1 : 0);
|
||||
rows[i].bg_since_ms = e->bg_since_ms;
|
||||
if (e->term_at_ms) continue; /* 已进入终止序列(kill/宽限回收/卡拔)→ 行不再显示 */
|
||||
memset(&rows[k], 0, sizeof rows[k]);
|
||||
snprintf(rows[k].app_id, sizeof rows[k].app_id, "%s", e->app_id);
|
||||
rows[k].pid = e->pid;
|
||||
rows[k].keepalive = (e->keepalive ? 1 : 0);
|
||||
rows[k].on_card = (e->on_card ? 1 : 0);
|
||||
rows[k].bg_since_ms = e->bg_since_ms;
|
||||
k++;
|
||||
}
|
||||
return n;
|
||||
return k;
|
||||
}
|
||||
|
||||
/* proc_sample:/proc/<pid>/stat(utime+stime 差分) + /proc/<pid>/status(VmRSS)。
|
||||
@@ -226,12 +229,16 @@ static void bgm_row_draw(void *ctx, int idx, sub_row_t *r,
|
||||
int ry = cy + (ch - (th + tsub + 4)) / 2; /* 内容在卡内垂直居中(行中心=卡中心) */
|
||||
if (r->label) meow_ui_text(rx, ry, r->label, 20, mt->onSurface);
|
||||
if (r->sub) meow_ui_text(rx, ry + th + 4, r->sub, 16, mt->onSurfaceVariant);
|
||||
/* 行内杀按钮(全形=胶囊(bh/2);「杀」;error=红=杀)——P1 按钮胶囊化 */
|
||||
/* 行内杀按钮(全形=胶囊(bh/2);「杀」;error=红=杀)——P1 按钮胶囊化。
|
||||
* 不可杀行(框架/前台/无进程,bgmgr_can_kill=0)→ 置灰禁点外观,避免误导(按钮可点但不可杀)。 */
|
||||
int bx, by, bw, bh; bgm_btn_rect(x, y, w, h, &bx, &by, &bw, &bh);
|
||||
meow_ui_draw_rect_round(bx, by, bw, bh, bh / 2, mt->error);
|
||||
uint32_t btn_col = mt->error; /* 可杀:error 红 */
|
||||
if (i >= 0 && i < b->n && !b->rows[i].killable)
|
||||
btn_col = mt->onSurfaceVariant; /* 禁点:置灰(onSurfaceVariant) */
|
||||
meow_ui_draw_rect_round(bx, by, bw, bh, bh / 2, btn_col);
|
||||
int bth = meow_ui_text_height(16);
|
||||
if (bth > bh) bth = bh;
|
||||
meow_ui_text_center(bx + bw / 2, by + (bh - bth) / 2, "杀", 16, mt->error);
|
||||
meow_ui_text_center(bx + bw / 2, by + (bh - bth) / 2, "杀", 16, btn_col);
|
||||
}
|
||||
|
||||
/* ---- 行内自定义命中(sub_page_t.row_tap;仅按钮区 kill,行体不响应/不误杀)---- */
|
||||
@@ -244,14 +251,9 @@ static int bgm_row_tap(void *ctx, int idx, sub_row_t *r, int x, int y,
|
||||
if (i < 0 || i >= b->n) return 0; /* 空态行无按钮 */
|
||||
int bx, by, bw, bh; bgm_btn_rect(rx, ry, rw, rh, &bx, &by, &bw, &bh);
|
||||
if (x < bx || x >= bx + bw || y < by || y >= by + bh) return 0; /* 行体/按钮外 → 不杀 */
|
||||
if (!bgmgr_can_kill(&b->rows[i])) {
|
||||
snprintf(b->toast, sizeof b->toast, "该 App 不可杀(框架/前台)");
|
||||
b->toast_ms = (long)now_ms();
|
||||
return 1;
|
||||
}
|
||||
meow_bg_terminate(b->rows[i].app_id);
|
||||
snprintf(b->toast, sizeof b->toast, "已请求终止 %s", b->rows[i].app_id);
|
||||
b->toast_ms = (long)now_ms();
|
||||
if (!bgmgr_can_kill(&b->rows[i]))
|
||||
return 1; /* 禁点:不可杀(框架/前台/无进程)→ 按钮已置灰,不 kill(返回 1=重绘) */
|
||||
meow_bg_terminate(b->rows[i].app_id); /* 置 term_at_ms → bgm_enumerate 下一跳行消失(可见反馈) */
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -270,11 +272,10 @@ static int bgm_top_action(void *ctx)
|
||||
memset(&ops, 0, sizeof ops);
|
||||
ops.ctx = b;
|
||||
ops.enumerate = bgm_enumerate; /* 行已由 bgm_rows 建 */
|
||||
int k = bgmgr_kill_all(&ops, b->rows, b->n, bgm_kill_fn);
|
||||
if (k > 0) snprintf(b->toast, sizeof b->toast, "已清 %d 个后台 App", k);
|
||||
else snprintf(b->toast, sizeof b->toast, "无后台 App 可清");
|
||||
b->toast_ms = (long)now_ms();
|
||||
return 1;
|
||||
/* kill 只置 term_at_ms(真退/摘表在 launcher 主循环 bgm_tick),此处由 bgm_enumerate 过滤
|
||||
* term_at_ms 让清后的行当下消失(可见反馈;不可杀行本就在上/一键不杀)。 */
|
||||
bgmgr_kill_all(&ops, b->rows, b->n, bgm_kill_fn);
|
||||
return 1; /* 重绘:重扫行(已清行消失) */
|
||||
}
|
||||
|
||||
/* ---- 入口:运行后台管理页(阻塞到返回)---- */
|
||||
|
||||
@@ -16,11 +16,14 @@ int menu_opened_blank = 0;
|
||||
|
||||
|
||||
/* launch_app/launch_activity 返回 -1 时的 toast:缺二进制依赖(app.json depends)→ 「缺依赖: <name>」;
|
||||
* 其余启动失败沿用「CANNOT START: <name>」。原因由 meow_dep.c 的 meow_dep_block_reason() 提供。 */
|
||||
* 其余启动失败优先显示具体失败原因(app_launcher.c 的 launch_fail_reason():fork OOM / exec 失败 /
|
||||
* 路径不存在等)→ 「CANNOT START: <name>(<reason>)」;无具体原因则回退「CANNOT START: <name>」。 */
|
||||
static void launch_fail_toast(char *toast, size_t n, const char *name)
|
||||
{
|
||||
const char *reason = launch_fail_reason(); /* M6.40:具体失败原因(成功/未失败时为 "") */
|
||||
const char *bd = meow_dep_block_reason();
|
||||
if (bd && bd[0]) snprintf(toast, n, meow_str_zh("menu.missing_dep", "缺依赖: %s"), bd);
|
||||
else if (reason && reason[0]) snprintf(toast, n, "CANNOT START: %s(%s)", name, reason);
|
||||
else snprintf(toast, n, "CANNOT START: %s", name);
|
||||
}
|
||||
|
||||
@@ -1121,7 +1124,7 @@ static void run_menu(struct app apps[MAX_APPS], int napps, int state,
|
||||
int cursor = 0;
|
||||
unsigned long long t0 = now_ms();
|
||||
int toast_ms = 0;
|
||||
char toast[64] = "";
|
||||
char toast[256] = ""; /* M6.40:失败 toast 带具体原因(CANNOT START: <name>(<reason>))需更大缓冲 */
|
||||
int want_exit = 0;
|
||||
/* M2.17 超时自动息屏:任何输入重置 last_input;空闲 > screen_off_timeout → meow_panel_off()。
|
||||
* meow_auto_slept 记录「自动息屏」态(手动电源息屏不置位),任意输入唤醒 + meow_repaint_surface。 */
|
||||
|
||||
@@ -675,6 +675,10 @@ int kp_open(struct keypoll *kp, const char *dev);
|
||||
int kp_poll(struct keypoll *kp, int (*cb)(int code, int down, void *ctx),
|
||||
void *ctx);
|
||||
int launch_app(struct app *a);
|
||||
/* M6.40:最近一次启动失败的具体原因(fork OOM / exec 失败 / 缺依赖 / 路径不存在等;
|
||||
* launch 成功或未失败则 "")。launch_app/launch_activity 返回 -1 前由 app_launcher.c 填好,
|
||||
* main_menu.c 的 launch_fail_toast 据以显示「CANNOT START: %s(%s)」。 */
|
||||
const char *launch_fail_reason(void);
|
||||
/* ---- app.json 二进制依赖(depends)解析与启动前检查(meow_dep.c)---- */
|
||||
int meow_parse_depends(const char *val, struct meow_dep *deps, int max);
|
||||
int meow_check_app_depends(const struct app *a, char *missing, int mlen);
|
||||
|
||||
@@ -601,7 +601,10 @@ static void sp_lv_draw(int idx, int x, int y, int w, int h, int state, void *ctx
|
||||
/* ---- 顶栏标题栏右侧动作按钮矩形(批③:sub_page_t.top_action)---- */
|
||||
static void sp_top_action_rect(int *x, int *y, int *w, int *h)
|
||||
{
|
||||
if (x) *x = 0; if (y) *y = 0; if (w) *w = 0; if (h) *h = 0;
|
||||
if (x) *x = 0;
|
||||
if (y) *y = 0;
|
||||
if (w) *w = 0;
|
||||
if (h) *h = 0;
|
||||
if (!g_sp || !g_sp->top_action) return;
|
||||
int tw = ui_width(g_sp->top_action, 20);
|
||||
if (tw <= 0) return;
|
||||
|
||||
@@ -29,7 +29,11 @@ static void app_on_swipe(int dir, int dx, int dy)
|
||||
(void)dy; (void)dx;
|
||||
if (settings_is_active()) { return; } /* 设置页滑动不翻页 */
|
||||
if (reader_is_active()) { reader_on_swipe(dir, dx, dy); return; }
|
||||
if (shelf_is_active()) { shelf_on_swipe(dir, dx, dy); return; }
|
||||
if (shelf_is_active()) {
|
||||
/* 书架根页左缘右滑(返回手势)→ 退出 App(镜像 hello_app/files_app 的 LEFT→返回)。 */
|
||||
if (dir == MEOW_SWIPE_LEFT) { meow_ui_request_exit(); return; }
|
||||
shelf_on_swipe(dir, dx, dy); return;
|
||||
}
|
||||
if (dir == MEOW_SWIPE_LEFT) { meow_ui_request_exit(); }
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"name":"Reader","exec":"/usr/data/mnt/sd_0/MeowAPPs/os.meow.reader/reader","package":"os.meow.reader","version":"0.1","version_code":1,"framework":1,"fonts":"full","author":"MeowOS","icon":"icon.png","volume_page":true}
|
||||
{"name":"Reader","exec":"/usr/data/mnt/sd_0/MeowAPPs/os.meow.reader/reader","package":"os.meow.reader","version":"0.1","version_code":1,"framework":1,"fonts":"common","author":"MeowOS","icon":"icon.png","volume_page":true}
|
||||
@@ -28,9 +28,9 @@ static int g_reader_active;
|
||||
static meow_widget *g_canvas;
|
||||
static int g_pw, g_ph, g_top, g_mx, g_my;
|
||||
static int g_fs, g_ls;
|
||||
#define RD_PAGE_CACHE 3
|
||||
#define RD_PAGE_SLOT_CUR 1
|
||||
static uint16_t *g_page_buf[RD_PAGE_CACHE]; /* [0]=prev [1]=cur [2]=next(FreeType 页位图) */
|
||||
#define RD_PAGE_CACHE 2
|
||||
#define RD_PAGE_SLOT_CUR 0
|
||||
static uint16_t *g_page_buf[RD_PAGE_CACHE]; /* [0]=cur [1]=next(FreeType 页位图;B.1 降 3→2 槽,省 1 页 0.69MB) */
|
||||
static int g_has_font; /* 1=FreeType 路径;0=SDK 字库兜底 */
|
||||
static int g_menu_open;
|
||||
static int g_ui_open; /* 「界面」排版面板(B) */
|
||||
@@ -500,7 +500,19 @@ static void reader_free_page_bufs(void)
|
||||
if (g_anim_old) { free(g_anim_old); g_anim_old = NULL; }
|
||||
}
|
||||
|
||||
/* 渲染当前页 + 前后各一页到 3 槽缓存(对齐设计「当前±前±后」;动画档为 无/滑动/淡入 R1 默认无)。 */
|
||||
/* 分配 FreeType 页位图槽(cur+next 两槽)。已备当前槽则幂等返回(开书/重入共用)。
|
||||
* 动画快照 g_anim_old 不在此常驻:翻页时懒分配、动画完成即释放(见 turn_animated/anim_done)。
|
||||
* 分配失败 → g_has_font=0,走字库兜底路径。 */
|
||||
static void reader_alloc_page_bufs(void)
|
||||
{
|
||||
if (!(g_has_font || rd_book_is_image(g_book))) return;
|
||||
if (g_page_buf[RD_PAGE_SLOT_CUR]) return; /* 已备当前槽(重入)→ 幂等 */
|
||||
for (int i = 0; i < RD_PAGE_CACHE; i++)
|
||||
g_page_buf[i] = (uint16_t *)malloc((size_t)g_pw * (size_t)g_ph * 2);
|
||||
if (!g_page_buf[RD_PAGE_SLOT_CUR]) { reader_free_page_bufs(); g_has_font = 0; }
|
||||
}
|
||||
|
||||
/* 渲染当前页 + 下一预取页到 2 槽缓存(B.1:降 3→2,去 prev 槽;动画档 无/滑动/淡入 R1 默认无)。 */
|
||||
static int reader_refill_page(void)
|
||||
{
|
||||
if (!g_book) return -1;
|
||||
@@ -508,7 +520,7 @@ static int reader_refill_page(void)
|
||||
uint16_t bg = rgb565(g_page_bg), fg = rgb565(g_page_fg);
|
||||
int cur = rd_book_current(g_book);
|
||||
int np = rd_book_pages(g_book);
|
||||
int slots[RD_PAGE_CACHE] = { cur - 1, cur, cur + 1 };
|
||||
int slots[RD_PAGE_CACHE] = { cur, cur + 1 };
|
||||
for (int i = 0; i < RD_PAGE_CACHE; i++) {
|
||||
int p = slots[i];
|
||||
if (p >= 0 && p < np) rd_book_render(g_book, p, g_page_buf[i], g_pw * 2, bg, fg);
|
||||
@@ -539,6 +551,8 @@ static void reader_anim_done(meow_anim *a, int canceled, void *ctx)
|
||||
{
|
||||
(void)a; (void)ctx;
|
||||
g_anim = NULL;
|
||||
/* 动画完成/取消即释放旧页快照位图,不驻留;下一翻页懒分配(见 reader_turn_animated)。 */
|
||||
if (g_anim_old) { free(g_anim_old); g_anim_old = NULL; }
|
||||
if (!canceled) { reader_redraw(); reader_reflow_persist(); } /* 顶栏 n/N+% 更新 + 进度保存 */
|
||||
}
|
||||
static void reader_turn_animated(int dir)
|
||||
@@ -619,9 +633,9 @@ static void reader_scrub_begin(int x, int y)
|
||||
}
|
||||
|
||||
/* ---------- 打开 ---------- */
|
||||
void reader_open(const char *path)
|
||||
int reader_open(const char *path)
|
||||
{
|
||||
if (!path || !path[0]) return;
|
||||
if (!path || !path[0]) return -1;
|
||||
if (g_book) { rd_book_close(g_book); g_book = NULL; }
|
||||
reader_free_page_bufs();
|
||||
|
||||
@@ -653,16 +667,13 @@ void reader_open(const char *path)
|
||||
meow_log(MEOW_LOG_WARN, "book open failed rc=%d path=%s", rc, path);
|
||||
g_book = NULL; g_has_font = 0;
|
||||
g_notice_ms = 3000;
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
chapter_name();
|
||||
|
||||
if (g_has_font || rd_book_is_image(g_book)) {
|
||||
reader_free_page_bufs();
|
||||
for (int i = 0; i < RD_PAGE_CACHE; i++)
|
||||
g_page_buf[i] = (uint16_t *)malloc((size_t)g_pw * (size_t)g_ph * 2);
|
||||
if (!g_page_buf[RD_PAGE_SLOT_CUR]) { reader_free_page_bufs(); g_has_font = 0; }
|
||||
else g_anim_old = (uint16_t *)malloc((size_t)g_pw * (size_t)g_ph * 2); /* 动画快照位图 */
|
||||
reader_free_page_bufs(); /* 丢弃上一本书的页位图(重开/换书) */
|
||||
reader_alloc_page_bufs(); /* 重建 cur+next 两槽;g_anim_old 翻页时懒分配 */
|
||||
}
|
||||
|
||||
/* 复位动画/刮擦状态(跨书) */
|
||||
@@ -691,6 +702,7 @@ void reader_open(const char *path)
|
||||
reader_refill_page();
|
||||
|
||||
if (g_reader_active) reader_redraw();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------- 视图生命周期 ---------- */
|
||||
@@ -699,13 +711,18 @@ static void reader_on_enter(meow_view *v, void *ctx)
|
||||
{
|
||||
(void)v; (void)ctx;
|
||||
g_reader_active = 1;
|
||||
if (g_book) { reader_refill_page(); chapter_name(); }
|
||||
if (g_book) {
|
||||
reader_alloc_page_bufs(); /* 离开已释放页位图 → 重建;未释放则幂等返回 */
|
||||
reader_refill_page();
|
||||
chapter_name();
|
||||
}
|
||||
reader_redraw();
|
||||
}
|
||||
static void reader_on_leave(meow_view *v, void *ctx)
|
||||
{
|
||||
(void)v; (void)ctx;
|
||||
reader_reflow_persist(); /* 离开保存进度 */
|
||||
reader_free_page_bufs(); /* 离开阅读页释放页位图(cur+next 两槽+动画快照)→ on_enter 重建 */
|
||||
g_reader_active = 0;
|
||||
}
|
||||
static void reader_on_destroy(meow_view *v, void *ctx)
|
||||
@@ -842,10 +859,18 @@ void reader_on_swipe(int dir, int dx, int dy)
|
||||
(void)dx; (void)dy;
|
||||
if (!g_book) return;
|
||||
if (g_scrub) g_scrub = 0; /* 刮擦中被滑走 → 退出刮擦 */
|
||||
if (g_ui_open) { g_ui_open = 0; reader_redraw(); return; }
|
||||
/* 水平滑 = 返回语义(镜像菜单「返回书架」,见上文 g_menu_open 分支)。SDK 把「左缘右滑
|
||||
* (返回手势)」判为 MEOW_SWIPE_LEFT 下发;旧语义拿去翻下一页,会与 SDK 自绘的左缘返回
|
||||
* 指示条错位(M6.40 真机 bug:有指示条却翻页/不返回)。故水平滑统一「先关一层 overlay,
|
||||
* 否则回书架」。翻页不再交给水平滑——保留三区点按(左 1/3 上页 / 右 1/3 下页)与右缘
|
||||
* 刮擦(reader_scrub_begin,由 touch_down/move 驱动)两条既有路径。 */
|
||||
if (g_ui_open) { g_ui_open = 0; reader_redraw(); return; }
|
||||
if (g_dir_open) { g_dir_open = 0; reader_redraw(); return; }
|
||||
if (g_menu_open) { g_menu_open = 0; reader_redraw(); return; }
|
||||
if (dir == MEOW_SWIPE_LEFT) reader_turn_animated(1);
|
||||
else if (dir == MEOW_SWIPE_RIGHT) reader_turn_animated(-1);
|
||||
if (dir == MEOW_SWIPE_LEFT || dir == MEOW_SWIPE_RIGHT) {
|
||||
reader_reflow_persist();
|
||||
meow_view_page_pop();
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- R3 段刮擦触控(app.c on_touch_down/on_touch_move 路由) ---- */
|
||||
|
||||
@@ -171,7 +171,9 @@ static void shelf_list_tapcb(int idx, int op, void *ctx)
|
||||
if (idx < 0 || idx >= g_nrows || g_rows[idx].section) return;
|
||||
int bi = g_rows[idx].book;
|
||||
if (bi < 0 || bi >= g_nbooks) return;
|
||||
reader_open(g_books[bi].path);
|
||||
/* 点书开读:reader_open 打开书(含页位图)→ push ACT_READER 让阅读页成为 view activity,
|
||||
* 驱动 reader_on_enter/on_leave(重建/释放页位图);「返回书架」/左缘返回走 page_pop 回书架。 */
|
||||
if (reader_open(g_books[bi].path) == 0) meow_view_page_push(ACT_READER);
|
||||
}
|
||||
|
||||
/* ---- grid 数据源(meow_lazygrid,4 列封面卡) ---- */
|
||||
@@ -228,7 +230,7 @@ static void shelf_grid_tapcb(int idx, void *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
if (idx < 0 || idx >= g_nbooks) return;
|
||||
reader_open(g_books[idx].path);
|
||||
if (reader_open(g_books[idx].path) == 0) meow_view_page_push(ACT_READER);
|
||||
}
|
||||
|
||||
/* ---- 控件装配(按当前布局重建 list / grid) ---- */
|
||||
|
||||
@@ -30,8 +30,9 @@ void reader_on_tick(uint64_t now);
|
||||
void reader_on_touch_down(int x, int y); /* R3 段刮擦:右缘拖动快速定位 */
|
||||
void reader_on_touch_move(int x, int y);
|
||||
|
||||
/* 打开一本书(书架调;成功进入阅读页)。path=TXT 完整路径。 */
|
||||
void reader_open(const char *path);
|
||||
/* 打开一本书(书架调;成功后由书架 push ACT_READER 进入阅读页)。path=TXT 完整路径。
|
||||
* 返回 0=成功(书已打开、页位图已备、可 push);非 0=失败(未能打开,调用方不 push)。 */
|
||||
int reader_open(const char *path);
|
||||
|
||||
/* ---- 全局设置页(app/settings.c,C)---- */
|
||||
meow_view *settings_view_create(void);
|
||||
|
||||
Reference in new issue
Block a user