refactor(panel): 前后端按模块拆分+屏幕/控制合并页 — 后端meowpanel.c 6372行拆src/*.c+mp.h(235函数原型,20端点output byte-identical,0warning)+前端拆mp_shared/mp_pages/meowpanel三层;屏幕·遥控合并(左屏实时画面+右MD3 SVG矢量按钮无emoji);重编后热更TF卡

This commit is contained in:
AskaEth committed 2026-09-04 00:56:13 +08:00
1 parent f3ae06d7cd
commit 2dc4cad454
26 files changed
+9317 -2755

No files matched your search

+196
View File
@@ -0,0 +1,196 @@
/* mp_ams.c — App 运行表域:am list 解析 + amd 历史 + start/kill 控制。 */
#include "mp.h"
const char *ams_bin(void)
{
const char *e = getenv("MEOW_AMS_BIN");
if (e && e[0]) return e;
if (!access(AMS_BIN, X_OK)) return AMS_BIN;
return AMS_BIN_CARD;
}
void ams_fill(sbuf_t *s)
{
char out[16384];
char cmd[300];
snprintf(cmd, sizeof cmd, "%s list 2>/dev/null", ams_bin());
int code = run_cmd(cmd, out, sizeof out);
if (code != 0 || !strstr(out, "APP")) { sA(s, "{\"available\":false}"); return; }
panel_strip_ansi(out);
char *toks[256];
sbuf_t apps = {0};
int first = 1, 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) {
/* 布局:APP <pkg> <name...> <state> <pid> <launches> <crashes> <last_kind> <last_code>
* tok[0]="APP" 前缀;自右向左固定:state=tok[n-6], pid=n-5, launches=n-4, crashes=n-3,
* last_kind=n-2, last_code=n-1;pkg=tok[1],name=join(tok[2..n-6))。 */
char nm[128];
join_tokens(toks, 2, n - 6, nm, sizeof nm);
if (!first) sA(&apps, ",");
first = 0; count++;
sA(&apps, "{\"pkg\":"); sA_str(&apps, toks[1]);
sA(&apps, ",\"name\":"); sA_str(&apps, nm);
sA(&apps, ",\"state\":"); sA_str(&apps, toks[n-6]);
sA(&apps, ",\"pid\":%d", atoi(toks[n-5]));
sA(&apps, ",\"launches\":%d", atoi(toks[n-4]));
sA(&apps, ",\"crashes\":%d", atoi(toks[n-3]));
sA(&apps, ",\"last_kind\":"); sA_str(&apps, toks[n-2]);
sA(&apps, ",\"last_code\":%d}", atoi(toks[n-1]));
}
}
if (nl) { *nl = '\n'; ln = nl + 1; } else break;
}
sA(s, "{\"available\":true,\"count\":%d,\"apps\":[%s]", count, apps.b ? apps.b : "");
sbuf_free(&apps);
ams_fg_fill(s); /* 追加 `,"fg":{..},"running":[..]`(首页前台 App 高亮;additive) */
sA(s, "}");
}
void ams_fg_fill(sbuf_t *s)
{
char out[16384];
char cmd[300];
snprintf(cmd, sizeof cmd, "%s status 2>/dev/null", ams_bin());
int code = run_cmd(cmd, out, sizeof out);
if (code != 0 || !strstr(out, "RUN")) { sA(s, ",\"fg\":null,\"running\":[]"); return; }
panel_strip_ansi(out);
char *toks[256];
char fg_pkg[128] = "", fg_act[160] = ""; int fg_pid = 0;
sbuf_t run = {0}; int first = 1;
char *ln = out;
while (*ln) {
char *nl = strchr(ln, '\n');
if (nl) *nl = 0;
if (strncmp(ln, "RUN ", 4) == 0) {
int n = txt_tokens(ln, toks, 256);
if (n >= 6) { /* RUN pkg state pid dur activity... */
char act[160];
join_tokens(toks, 5, n, act, sizeof act);
const char *st = toks[2];
if (!first) sA(&run, ",");
first = 0;
sA(&run, "{\"pkg\":"); sA_str(&run, toks[1]);
sA(&run, ",\"state\":"); sA_str(&run, st);
sA(&run, ",\"pid\":%d", atoi(toks[3]));
sA(&run, ",\"activity\":"); sA_str(&run, act);
sA(&run, "}");
if (strcmp(st, "fg") == 0 && !fg_pkg[0]) {
snprintf(fg_pkg, sizeof fg_pkg, "%s", toks[1]);
snprintf(fg_act, sizeof fg_act, "%s", act);
fg_pid = atoi(toks[3]);
}
}
}
if (nl) { *nl = '\n'; ln = nl + 1; } else break;
}
sA(s, ",\"fg\":");
if (fg_pkg[0]) { sA(s, "{\"pkg\":"); sA_str(s, fg_pkg); sA(s, ",\"activity\":"); sA_str(s, fg_act); sA(s, ",\"pid\":%d}", fg_pid); }
else sA(s, "null");
sA(s, ",\"running\":[%s]", run.b ? run.b : "");
sbuf_free(&run);
}
int api_ams(int sock)
{
sbuf_t s = {0};
ams_fill(&s);
int rc = send_json(sock, 200, s.b ? s.b : "{\"available\":false}");
sbuf_free(&s);
return rc;
}
int ams_pkg_ok(const char *pkg)
{
if (!pkg || !pkg[0]) return 0;
if (strlen(pkg) >= MEOW_APP_ID_MAX) return 0;
for (const char *p = pkg; *p; p++) {
unsigned char c = (unsigned char)*p;
if (c == '/' || c == ':' || c == ' ' || c == '\t' || c == '\n' || c == '\r') return 0;
if (c < 0x21 || c > 0x7e) return 0; /* 包名仅 ASCII 可见字符 */
}
return 1;
}
void ams_history_parse(char *out, sbuf_t *s, const char *pkg) /* out 被 panel_strip_ansi 就地改 */
{
if (!utf8_valid_n(out, strlen(out))) { sA(s, "{\"available\":false}"); return; } /* 崩溃 detail 坏字节 → 降级 */
panel_strip_ansi(out);
char *toks[256];
sbuf_t ev = {0};
int first = 1, count = 0;
char *ln = out;
while (*ln) {
char *nl = strchr(ln, '\n');
if (nl) *nl = 0;
if (strncmp(ln, "EVT ", 4) == 0) {
int n = txt_tokens(ln, toks, 256);
if (n >= 5) { /* EVT epoch up kind pkg detail... */
char det[256];
join_tokens(toks, 5, n, det, sizeof det);
if (!first) sA(&ev, ",");
first = 0; count++;
sA(&ev, "{\"ts\":%ld,\"uptime_sec\":%ld,\"kind\":", atol(toks[1]), atol(toks[2]));
sA_str(&ev, toks[3]);
sA(&ev, ",\"detail\":"); sA_str(&ev, det);
sA(&ev, "}");
}
}
if (nl) { *nl = '\n'; ln = nl + 1; } else break;
}
sA(s, "{\"available\":true,\"pkg\":"); sA_str(s, pkg);
sA(s, ",\"count\":%d,\"events\":[%s]}", count, ev.b ? ev.b : "");
sbuf_free(&ev);
}
void ams_history_fill(sbuf_t *s, const char *pkg)
{
char out[16384];
char cmd[320];
snprintf(cmd, sizeof cmd, "%s history %s 2>/dev/null", ams_bin(), pkg);
int code = run_cmd(cmd, out, sizeof out);
if (code != 0 || !strstr(out, "EVT")) { sA(s, "{\"available\":false}"); return; }
ams_history_parse(out, s, pkg);
}
int api_ams_history(int sock, const char *pkg)
{
if (!ams_pkg_ok(pkg)) return send_err(sock, 400, "BAD_REQUEST", "包名非法");
sbuf_t s = {0};
ams_history_fill(&s, pkg);
int rc = send_json(sock, 200, s.b ? s.b : "{\"available\":false}");
sbuf_free(&s);
return rc;
}
int api_ams_ctl(int sock, const char *pkg, int kill)
{
if (!ams_pkg_ok(pkg)) return send_err(sock, 400, "BAD_REQUEST", "包名非法");
char out[256];
char cmd[320];
snprintf(cmd, sizeof cmd, "%s %s %s 2>/dev/null", ams_bin(), kill ? "kill" : "start", pkg);
int code = run_cmd(cmd, out, sizeof out);
if (code == 0 && strstr(out, "OK ")) {
sbuf_t s = {0};
sA(&s, "{\"accepted\":true,\"op\":\"%s\",\"pkg\":", kill ? "kill" : "start");
sA_str(&s, pkg);
sA(&s, ",\"message\":"); sA_str(&s, out);
sA(&s, "}");
int rc2 = send_json(sock, 200, s.b ? s.b : "");
sbuf_free(&s);
return rc2;
}
/* am CLI 以文本报告失败(ERR ...);按内容映射 HTTP 码(如实,绝不掩盖)。 */
const char *detail = out[0] ? out : (kill ? "am kill 失败" : "am start 失败");
if (strstr(detail, "not registered")) return send_err(sock, 404, "NOT_FOUND", detail);
if (strstr(detail, "busy")) return send_err(sock, 409, "CONFLICT", detail);
if (strstr(detail, "not running")) return send_err(sock, 409, "CONFLICT", detail);
if (strstr(detail, "launcher offline")) return send_err(sock, 503, "AMS_UNAVAILABLE", "amsd/launcher 不可达");
return send_err(sock, 500, "INTERNAL", detail);
}