feat: M6.43 — reader分段流式读取(Phase1,只txt) + 面板前台管理/返回回桌面按钮
- reader分段(只txt): core/seg.{h,c} 512KB码位对齐切段(尾字完整)+manifest+按需读单段; book.c rd_book_open只布局每页所在段(治CPU100%/返回无效) load_seg只读当前段(非txt原路径); reader.c 进度实时保存(翻页/刮擦/字号/编码dirty+on_tick节流落盘+退出即时写,锚点存段号,崩溃不丢进度); rdtxt.c fread替代fgetc(去开书扫描卡顿); Phase2(双段拼接+4%释放)留待
- 面板: /api/ams/bg加fg/fg_count(后台保持向后兼容)+POST /api/ams/:pkg/killfg(前台救命:优先am kill,launcher拒则pid直信号SIGTERM->SIGKILL绕过门控)+前端/#/bg前台组'终止'绿徽标+确认弹窗(一键清仍只后台不误杀前台); POST /api/input/gesture(返回=左缘右滑/回桌面=底部上滑,注入滑动轨迹复用SDK手势)+前端屏幕控制页加返回/回桌面MD3按钮
0warning;宿主test_reader_core 72-0/seg_smoke 19-0/loaders_smoke/book_smoke/formats_smoke PASS;面板selftest/node check PASS
This commit is contained in:
1 parent
a64ae06eb6
commit
f40abbd494
17 files changed
+1069
-70
No files matched your search
+147
-19
@@ -253,7 +253,32 @@ static void bg_proc_sample(int pid, double *pcpu, long *rss_kb)
|
||||
}
|
||||
}
|
||||
|
||||
/* 解析已 strip 的 `am list` 全文 → bg 列表 JSON(state=bg 的 APP 行)。 */
|
||||
/* 发射单条 APP 行 JSON(复用于 bg/fg 列表;带 state + is_fg + cpu_pct/rss_kb)。
|
||||
* toks 为已切分的 `APP ...` 行(n>=9);*first 控制数组元素逗号(调用方初始化为 1)。
|
||||
* cpu_pct/rss_kb 诚实原则:pid 读不到/不作为可用进程 → null(前端显「—」),绝不编造。 */
|
||||
static void ams_entry_emit(sbuf_t *out, char **toks, int n, int *first)
|
||||
{
|
||||
const char *st = toks[n - 6]; /* 自右向左:state=tok[n-6], pid=n-5(与 ams_fill 同布局) */
|
||||
char nm[128];
|
||||
join_tokens(toks, 2, n - 6, nm, sizeof nm);
|
||||
int pid = atoi(toks[n - 5]);
|
||||
double pcpu = -1.0; long rss = -1;
|
||||
if (pid > 0) bg_proc_sample(pid, &pcpu, &rss);
|
||||
if (!*first) sA(out, ",");
|
||||
*first = 0;
|
||||
sA(out, "{\"pkg\":"); sA_str(out, toks[1]);
|
||||
sA(out, ",\"name\":"); sA_str(out, nm);
|
||||
sA(out, ",\"pid\":%d", pid);
|
||||
sA(out, ",\"state\":"); sA_str(out, st);
|
||||
sA(out, ",\"is_fg\":%s", strcmp(st, "fg") == 0 ? "true" : "false");
|
||||
sA(out, ",\"cpu_pct\":");
|
||||
if (pcpu < 0) sA(out, "null"); else sA(out, "%.1f", pcpu);
|
||||
sA(out, ",\"rss_kb\":");
|
||||
if (rss < 0) sA(out, "null"); else sA(out, "%ld", rss);
|
||||
sA(out, "}");
|
||||
}
|
||||
|
||||
/* 解析已 strip 的 `am list` 全文 → bg 列表 JSON(state=bg 的 APP 行;向后兼容专用,selftest 覆盖)。 */
|
||||
void ams_bg_parse(char *list_text, sbuf_t *s)
|
||||
{
|
||||
char *toks[256];
|
||||
@@ -266,25 +291,10 @@ void ams_bg_parse(char *list_text, sbuf_t *s)
|
||||
if (strncmp(ln, "APP ", 4) == 0) {
|
||||
int n = txt_tokens(ln, toks, 256);
|
||||
if (n >= 9) {
|
||||
/* 自右向左固定(与 ams_fill 同布局):state=tok[n-6], pid=n-5;pkg=tok[1],name=join(tok[2..n-6)) */
|
||||
const char *st = toks[n - 6];
|
||||
if (strcmp(st, "bg") == 0) {
|
||||
char nm[128];
|
||||
join_tokens(toks, 2, n - 6, nm, sizeof nm);
|
||||
int pid = atoi(toks[n - 5]);
|
||||
double pcpu; long rss;
|
||||
bg_proc_sample(pid, &pcpu, &rss);
|
||||
if (!first) sA(&apps, ",");
|
||||
first = 0; count++;
|
||||
sA(&apps, "{\"pkg\":"); sA_str(&apps, toks[1]);
|
||||
sA(&apps, ",\"name\":"); sA_str(&apps, nm);
|
||||
sA(&apps, ",\"pid\":%d", pid);
|
||||
sA(&apps, ",\"state\":"); sA_str(&apps, st);
|
||||
sA(&apps, ",\"cpu_pct\":");
|
||||
if (pcpu < 0) sA(&apps, "null"); else sA(&apps, "%.1f", pcpu);
|
||||
sA(&apps, ",\"rss_kb\":");
|
||||
if (rss < 0) sA(&apps, "null"); else sA(&apps, "%ld", rss);
|
||||
sA(&apps, "}");
|
||||
count++;
|
||||
ams_entry_emit(&apps, toks, n, &first);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,6 +305,8 @@ void ams_bg_parse(char *list_text, sbuf_t *s)
|
||||
sbuf_free(&apps);
|
||||
}
|
||||
|
||||
/* am list 全文 → bg+fg 双列表 JSON。`apps`(bg)保持向后兼容(count=bg 数量);
|
||||
* 追加 `fg` + `fg_count`(前台 App,前端救命分组)。state=stop 无进程,不在本页(proc 页可见)。 */
|
||||
void ams_bg_fill(sbuf_t *s)
|
||||
{
|
||||
char out[16384];
|
||||
@@ -303,7 +315,28 @@ void ams_bg_fill(sbuf_t *s)
|
||||
int code = run_cmd(cmd, out, sizeof out);
|
||||
if (code != 0 || !strstr(out, "APP")) { sA(s, "{\"available\":false}"); return; }
|
||||
panel_strip_ansi(out);
|
||||
ams_bg_parse(out, s);
|
||||
char *toks[256];
|
||||
sbuf_t bg = {0}, fg = {0};
|
||||
int bg_first = 1, fg_first = 1, bg_count = 0, fg_count = 0;
|
||||
char *ln = out;
|
||||
while (*ln) {
|
||||
char *nl = strchr(ln, '\n');
|
||||
if (nl) *nl = 0;
|
||||
if (strncmp(ln, "APP ", 4) == 0) {
|
||||
int n = txt_tokens(ln, toks, 256);
|
||||
if (n >= 9) {
|
||||
const char *st = toks[n - 6];
|
||||
if (strcmp(st, "bg") == 0) { bg_count++; ams_entry_emit(&bg, toks, n, &bg_first); }
|
||||
else if (strcmp(st, "fg") == 0) { fg_count++; ams_entry_emit(&fg, toks, n, &fg_first); }
|
||||
}
|
||||
}
|
||||
if (nl) { *nl = '\n'; ln = nl + 1; } else break;
|
||||
}
|
||||
sA(s, "{\"available\":true,\"count\":%d,\"ts\":%ld,\"apps\":[%s]",
|
||||
bg_count, (long)time(NULL), bg.b ? bg.b : "");
|
||||
sA(s, ",\"fg_count\":%d,\"fg\":[%s]}", fg_count, fg.b ? fg.b : "");
|
||||
sbuf_free(&bg);
|
||||
sbuf_free(&fg);
|
||||
}
|
||||
|
||||
int api_ams_bg(int sock)
|
||||
@@ -314,3 +347,98 @@ int api_ams_bg(int sock)
|
||||
sbuf_free(&s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 从 am list 解析某 pkg 的当前 pid(未注册/无进程 → 0)。 */
|
||||
static int ams_pkg_pid(const char *pkg)
|
||||
{
|
||||
char out[16384];
|
||||
char cmd[300];
|
||||
snprintf(cmd, sizeof cmd, "%s list 2>/dev/null", ams_bin());
|
||||
if (run_cmd(cmd, out, sizeof out) != 0 || !strstr(out, "APP")) return 0;
|
||||
panel_strip_ansi(out);
|
||||
char *toks[256];
|
||||
char *ln = out;
|
||||
while (*ln) {
|
||||
char *nl = strchr(ln, '\n');
|
||||
if (nl) *nl = 0;
|
||||
if (strncmp(ln, "APP ", 4) == 0) {
|
||||
int n = txt_tokens(ln, toks, 256);
|
||||
if (n >= 9 && strcmp(toks[1], pkg) == 0) {
|
||||
int pid = atoi(toks[n - 5]);
|
||||
if (nl) *nl = '\n';
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
if (nl) { *nl = '\n'; ln = nl + 1; } else break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* /proc/<pid>/stat 进程是否已消失/僵尸:-1=已消失,1=僵尸,0=存活。 */
|
||||
static int proc_zombie(int pid)
|
||||
{
|
||||
char path[64], line[256];
|
||||
snprintf(path, sizeof path, "/proc/%d/stat", pid);
|
||||
if (read_small_file(path, line, sizeof line) < 0) return -1;
|
||||
char *rp = strrchr(line, ')');
|
||||
if (!rp) return -1;
|
||||
const char *p = rp + 2;
|
||||
while (*p == ' ' || *p == '\t') p++;
|
||||
return (*p == 'Z') ? 1 : 0;
|
||||
}
|
||||
|
||||
/* 前台强制终止(救命):优先走 `am kill`(launcher 干净 SIGTERM 序列,accepted);若 launcher 拒
|
||||
* (framework / 无前台会话 → "launcher refused"),回退:解析 am list 拿当前 pid → 直接 SIGTERM,
|
||||
* 宽限后仍存活走 SIGKILL(与 launcher 杀序列同语义)。这是用户明示「前台卡死救命」的强通道——
|
||||
* 前端已二次确认 + 标注「前台」。require_auth 在路由层已护。 */
|
||||
int api_ams_kill_fg(int sock, const char *pkg)
|
||||
{
|
||||
if (!ams_pkg_ok(pkg)) return send_err(sock, 400, "BAD_REQUEST", "包名非法");
|
||||
|
||||
/* ① 干净通道:am kill(launcher 执行 SIGTERM 序列)。 */
|
||||
{
|
||||
char out[256];
|
||||
char cmd[320];
|
||||
snprintf(cmd, sizeof cmd, "%s kill %s 2>/dev/null", ams_bin(), pkg);
|
||||
int code = run_cmd(cmd, out, sizeof out);
|
||||
if (code == 0 && strstr(out, "OK ")) {
|
||||
sbuf_t s = {0};
|
||||
sA(&s, "{\"accepted\":true,\"op\":\"kill-fg\",\"pkg\":");
|
||||
sA_str(&s, pkg);
|
||||
sA(&s, ",\"force\":false,\"message\":"); sA_str(&s, out);
|
||||
sA(&s, "}");
|
||||
int rc = send_json(sock, 200, s.b ? s.b : "");
|
||||
sbuf_free(&s);
|
||||
return rc;
|
||||
}
|
||||
panel_log("ams kill-fg %s via am refused: %s -> direct signal fallback", pkg,
|
||||
out[0] ? out : "(empty)");
|
||||
}
|
||||
|
||||
/* ② 回退:直接按 pid 信号(绕过 launcher 门控)。 */
|
||||
int pid = ams_pkg_pid(pkg);
|
||||
if (pid <= 0) return send_err(sock, 409, "CONFLICT", "该 App 当前无进程可终止");
|
||||
|
||||
kill(pid, SIGTERM);
|
||||
int killed_hard = 0;
|
||||
for (int i = 0; i < 5; i++) { /* SIGTERM 宽限 ~0.6s(与 launcher 杀序列同语义) */
|
||||
struct timespec ts = {0, 120 * 1000 * 1000};
|
||||
nanosleep(&ts, NULL);
|
||||
int z = proc_zombie(pid);
|
||||
if (z < 0 || z == 1) break; /* 消失/僵尸 → 已终止 */
|
||||
}
|
||||
if (proc_zombie(pid) == 0) { kill(pid, SIGKILL); killed_hard = 1; }
|
||||
|
||||
char msg[192];
|
||||
snprintf(msg, sizeof msg, "force terminated pid %d via direct signal%s", pid,
|
||||
killed_hard ? " (SIGKILL 强杀)" : " (SIGTERM)");
|
||||
sbuf_t s = {0};
|
||||
sA(&s, "{\"accepted\":true,\"op\":\"kill-fg\",\"pkg\":");
|
||||
sA_str(&s, pkg);
|
||||
sA(&s, ",\"force\":%s,\"pid\":%d,\"message\":", killed_hard ? "true" : "false", pid);
|
||||
sA_str(&s, msg);
|
||||
sA(&s, "}");
|
||||
int rc = send_json(sock, 200, s.b ? s.b : "");
|
||||
sbuf_free(&s);
|
||||
return rc;
|
||||
}
|
||||
Reference in new issue
Block a user