From 07c556e907b6a0a0ea97a8d499284596052f58de Mon Sep 17 00:00:00 2001 From: MeowOS Dev Date: Sun, 30 Aug 2026 14:30:25 +0800 Subject: [PATCH] =?UTF-8?q?meow=5Fsvcd=20status=20=E7=BE=8E=E5=8C=96(117)?= =?UTF-8?q?=20+=20=E6=8C=89=E9=92=AE=E6=8C=89=E4=BD=8F/=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F+=E6=BB=91=E6=9D=A1=E9=BB=91=E6=AE=8B?= =?UTF-8?q?=E5=BF=AB=E7=85=A7(118)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- patch_v2/meow_svcd/meow_svcd | Bin 101132 -> 118524 bytes patch_v2/meow_svcd/meow_svcd.c | 223 +++++++++++++++++++++++++++++--- patch_v2/meow_svcd/meow_svcd.md | 25 ++-- patch_v2/sdk/meow_ui_gfx.c | 61 +++++++-- patch_v2/sdk/meow_ui_input.c | 33 ++++- patch_v2/sdk/meow_ui_internal.h | 12 ++ patch_v2/sdk/meow_ui_widget.c | 41 +++++- 7 files changed, 352 insertions(+), 43 deletions(-) diff --git a/patch_v2/meow_svcd/meow_svcd b/patch_v2/meow_svcd/meow_svcd index 846cadffc0d7346559b0e83409ac6e768b3a38a5..b27e072e080389feaf4859f87fe745efc5044a49 100755 GIT binary patch delta 44135 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 diff --git a/patch_v2/meow_svcd/meow_svcd.c b/patch_v2/meow_svcd/meow_svcd.c index 48cda6b..6d23e31 100644 --- a/patch_v2/meow_svcd/meow_svcd.c +++ b/patch_v2/meow_svcd/meow_svcd.c @@ -27,10 +27,16 @@ * - boot:配置在卡 → 随卡存在即启动(拔卡即停)。 * * 控制通道:AF_UNIX 单行文本协议(默认 /tmp/meow_svcd.sock): - * start|stop|restart / status / reload / pid + * start|stop|restart / status [name|all] / reload / pid * 后台常驻模式默认(无子命令即启动守护进程);`-f/--foreground` 供 supervisor * fork/exec 托管(保证 supervisor 可 waitpid)。 * + * status 回显(详细 + ANSI 配色): + * 每服务列出 状态/pid/重启次数+max_restart/backoff_ms/trigger/来源(板载|TF)/ + * 最近退出码/退出时间/exec 路径;标题+列名+对齐;关键字段着色(RUNNING=绿、 + * FAILED=红、STOPPED·STOPPING=黄、DISABLED=灰、服务名=青、数字=黄、表头=dim)。 + * ANSI 色由守护进程统一产出,客户端在 stdout 非 TTY(重定向/SSH 管道)时去色。 + * * 状态文件:`/tmp/meow_svcd.status`(可 --status 覆盖),状态变更+周期落盘, * 板载 tmpfs 存活期内可读。 * @@ -91,6 +97,16 @@ #define SVCD_SIGCHLD_CHAR 'C' /* 自管道字节(拉起 poll) */ #define SVCD_SIGTERM_CHAR 'T' +/* ---- ANSI 终端配色(status 回显用;仅客户端 stdout 为 TTY 时保留,非 TTY 去色) ---- */ +#define AN_RESET "\033[0m" +#define AN_BOLD "\033[1m" +#define AN_DIM "\033[2m" +#define AN_RED "\033[31m" +#define AN_GREEN "\033[32m" +#define AN_YELLOW "\033[33m" +#define AN_CYAN "\033[36m" +#define AN_GREY "\033[90m" + /* ---- 服务状态机 ---- */ typedef enum { SV_DISABLED = 0, /* enabled=0,永不自动启动 */ @@ -124,6 +140,8 @@ typedef struct { sv_state_t state; int restart_count; int last_exit_status; /* waitpid status */ + int has_exit; /* 1=曾退出(last_exit_* 有效) */ + unsigned long long last_exit_wall; /* 上次退出墙钟秒(0=未知;仅供 status 展示) */ unsigned long long restart_at_ms; /* 退避到期时间;0=无到期 */ unsigned long long stop_since_ms; /* SIGTERM 发出时刻 */ unsigned long long run_since_ms; /* 本轮启动时刻 */ @@ -433,6 +451,8 @@ static void apply_exit(svc_t *s, int status) int failed = !(WIFEXITED(status) && WEXITSTATUS(status) == 0); s->pid = 0; s->last_exit_status = status; + s->has_exit = 1; + s->last_exit_wall = (unsigned long long)time(NULL); if (s->restart_pending) { /* restart 命令:停后原地重启,无视崩溃策略 */ @@ -680,6 +700,8 @@ static void scan_and_reconcile(void) /* 保留运行期字段,其余用 tmp 覆盖 */ int keep_count = s->restart_count; int keep_exit = s->last_exit_status; + int keep_has = s->has_exit; + unsigned long long keep_wall = s->last_exit_wall; pid_t keep_pid = s->pid; int keep_stop = s->stop_requested; int keep_rp = s->restart_pending; @@ -688,6 +710,8 @@ static void scan_and_reconcile(void) *s = tmp; s->restart_count = keep_count; s->last_exit_status = keep_exit; + s->has_exit = keep_has; + s->last_exit_wall = keep_wall; s->pid = keep_pid; s->stop_requested = keep_stop; s->restart_pending = keep_rp; @@ -841,22 +865,153 @@ static int cmd_restart(const char *name) return 0; } -static void cmd_status(int fd) +/* ---- status 回显(详细 + ANSI 配色;非 TTY 由客户端去色) ---- */ + +/* 追加一个字段:可选上色(color 包裹 plain + 复位),再按纯文本宽度补空格对齐。 + * width<0 表示最后一列(不补齐)。b 为累积缓冲,*o 为偏移。 */ +static void put_field(char *b, size_t cap, size_t *o, const char *color, const char *plain, int width) { - send_resp(fd, "# meow_svcd status\n"); - char line[384]; - int k = snprintf(line, sizeof line, "card_present=%d wifi_up=%d services=%d\n", - g_card_present, g_wifi_up, g_nsvc); - send_resp(fd, line); - (void)k; - for (int i = 0; i < g_nsvc; i++) { - svc_t *s = &g_svcs[i]; - snprintf(line, sizeof line, - "svc=%.63s state=%.16s pid=%d restart=%d exit=%d trigger=%.15s\n", - s->name, g_state_str[s->state], (int)s->pid, s->restart_count, - s->last_exit_status, s->trigger); - send_resp(fd, line); + size_t pl = strlen(plain); + int w; + if (color && color[0]) + w = snprintf(b + *o, cap - *o, "%s%s" AN_RESET, color, plain); + else + w = snprintf(b + *o, cap - *o, "%s", plain); + if (w > 0) *o += (size_t)w; + if (*o >= cap) *o = cap - 1; /* 截断保护:o 永不超过 cap-1 */ + if (width > 0) { + int pad = width - (int)pl; + while (pad-- > 0 && *o < cap - 1) b[(*o)++] = ' '; } + b[*o] = '\0'; +} + +static void put_str(char *b, size_t cap, size_t *o, const char *s) +{ + int w = snprintf(b + *o, cap - *o, "%s", s); + if (w > 0) *o += (size_t)w; + if (*o >= cap) *o = cap - 1; + b[*o] = '\0'; +} + +/* 状态 → 关键色:RUNNING=绿 / FAILED=红 / STOPPED·STOPPING=黄 / DISABLED=灰。 */ +static const char *state_color(int st) +{ + switch (st) { + case SV_RUNNING: return AN_GREEN; + case SV_FAILED: return AN_RED; + case SV_STOPPED: return AN_YELLOW; + case SV_STOPPING: return AN_YELLOW; + case SV_DISABLED: return AN_GREY; + default: return ""; + } +} + +/* 解码 waitpid status 为短串:exit=N / sig=N / "-"(未曾退出)。 */ +static void fmt_exit_str(const svc_t *s, char *out, size_t cap) +{ + if (!s->has_exit) { snprintf(out, cap, "-"); return; } + int st = s->last_exit_status; + if (WIFEXITED(st)) snprintf(out, cap, "%d", WEXITSTATUS(st)); + else if (WIFSIGNALED(st)) snprintf(out, cap, "sig%d", WTERMSIG(st)); + else snprintf(out, cap, "-"); +} + +/* 上次退出时间 HH:MM:SS;无则 "-"。 */ +static void fmt_exit_time(const svc_t *s, char *out, size_t cap) +{ + if (!s->has_exit || s->last_exit_wall == 0) { snprintf(out, cap, "-"); return; } + time_t t = (time_t)s->last_exit_wall; + struct tm tm; + if (!localtime_r(&t, &tm)) { snprintf(out, cap, "-"); return; } + strftime(out, cap, "%H:%M:%S", &tm); +} + +/* 回显状态:`status`(全部+全局)或 `status `(单服务)。 */ +static void cmd_status(int fd, const char *name) +{ + int idx = -1; /* -1=全部 */ + if (name && strcmp(name, "all") != 0) { + idx = find_svc(name); + if (idx < 0) { send_resp(fd, "ERR no-such-service\n"); return; } + } + int shown = (idx >= 0) ? 1 : g_nsvc; + + /* NAME 列宽(按纯文本计) */ + int nw = 4; /* 至少等于 "NAME" */ + for (int i = 0; i < g_nsvc; i++) { + if (idx >= 0 && i != idx) continue; + int l = (int)strlen(g_svcs[i].name); + if (l > nw) nw = l; + } + if (nw > 40) nw = 40; /* 防超宽 */ + + const int sw = 8, pw = 7, rw = 7, bw = 8, tw = 11, scw = 5, ew = 6, etw = 9; + size_t cap = (size_t)(shown + 3) * 512 + 1024; + char *b = malloc(cap); + if (!b) { send_resp(fd, "ERR out-of-memory\n"); return; } + size_t o = 0; + + /* 标题 + 全局汇总(services= 恒为已知总数) */ + put_field(b, cap, &o, AN_BOLD AN_CYAN, "meow_svcd status", -1); + put_str(b, cap, &o, "\n"); + char hdr[64]; + snprintf(hdr, sizeof hdr, "card_present=%d wifi_up=%d services=%d\n", + g_card_present, g_wifi_up, g_nsvc); + put_str(b, cap, &o, hdr); + + /* 表头(dim) */ + put_field(b, cap, &o, AN_DIM, "NAME", nw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_DIM, "STATE", sw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_DIM, "PID", pw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_DIM, "RESTART", rw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_DIM, "BACKOFF", bw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_DIM, "TRIGGER", tw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_DIM, "SRC", scw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_DIM, "EXIT", ew); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_DIM, "EXIT_TIME", etw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_DIM, "EXEC", -1); + put_str(b, cap, &o, "\n"); + + /* 分隔线(dim) */ + int total = nw + 1 + sw + 1 + pw + 1 + rw + 1 + bw + 1 + tw + 1 + scw + 1 + ew + 1 + etw + 1 + 4; + put_str(b, cap, &o, AN_DIM); + for (int x = 0; x < total && o < cap - 1; x++) b[o++] = '-'; + put_str(b, cap, &o, AN_RESET "\n"); + + /* 服务行 */ + for (int i = 0; i < g_nsvc; i++) { + if (idx >= 0 && i != idx) continue; + svc_t *s = &g_svcs[i]; + char pidstr[16], rstr[16], bstr[16], estr[16], etstr[16]; + snprintf(pidstr, sizeof pidstr, "%d", (int)s->pid); + if (s->max_restart < 0) snprintf(rstr, sizeof rstr, "%d/inf", s->restart_count); + else snprintf(rstr, sizeof rstr, "%d/%d", s->restart_count, s->max_restart); + snprintf(bstr, sizeof bstr, "%ldms", s->backoff_ms); + fmt_exit_str(s, estr, sizeof estr); + fmt_exit_time(s, etstr, sizeof etstr); + + put_field(b, cap, &o, AN_BOLD AN_CYAN, s->name, nw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, state_color(s->state), g_state_str[s->state], sw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_YELLOW, pidstr, pw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_YELLOW, rstr, rw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, AN_YELLOW, bstr, bw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, "", s->trigger, tw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, "", (s->src == 1) ? "board" : "tf", scw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, "", estr, ew); put_str(b, cap, &o, " "); + put_field(b, cap, &o, "", etstr, etw); put_str(b, cap, &o, " "); + put_field(b, cap, &o, "", s->exec, -1); + put_str(b, cap, &o, "\n"); + } + + /* 分块写回(避免单次大 write 阻塞/截断) */ + size_t sent = 0; + while (sent < o) { + ssize_t w = write(fd, b + sent, o - sent); + if (w <= 0) break; + sent += (size_t)w; + } + free(b); } /* 读一条命令并分发。返回响应内容(静态/栈缓冲由调用方使用)。 */ @@ -874,7 +1029,7 @@ static void handle_connection(int fd) if (strcmp(line, "pid") == 0) { char r[64]; snprintf(r, sizeof r, "%d\n", (int)getpid()); send_resp(fd, r); } else if (strcmp(line, "status") == 0) { - cmd_status(fd); + cmd_status(fd, sp); /* sp=NULL=全部;=单服务 */ } else if (strcmp(line, "reload") == 0) { scan_and_reconcile(); send_resp(fd, "OK\n"); @@ -1069,6 +1224,24 @@ static int start_daemon(void) } /* ---- CLI ---- */ + +/* 就地去除 ANSI 转义序列(\033[...);用于 stdout 非 TTY 时去色。 */ +static char *strip_ansi(char *s) +{ + char *r = s, *w = s; + while (*r) { + if (*r == '\033' && r[1] == '[') { + r += 2; + while (*r && !(*r >= '@' && *r <= '~')) r++; /* 跳过参数,找到终结字节 */ + if (*r) r++; /* 跳过终结字节 */ + } else { + *w++ = *r++; + } + } + *w = '\0'; + return s; +} + static int run_client(const char *cmdline) { int fd = socket(AF_UNIX, SOCK_STREAM, 0); @@ -1089,13 +1262,21 @@ static int run_client(const char *cmdline) ssize_t w = write(fd, cmdline, strlen(cmdline)); (void)w; shutdown(fd, SHUT_WR); - char rbuf[8192]; + + /* 累积完整响应(可能含 ANSI 色码),再按 stdout 是否 TTY 决定去色:非 TTY/重定向去色。 */ + size_t cap = 65536, len = 0; + char *rbuf = malloc(cap); + if (!rbuf) { close(fd); return 2; } ssize_t n; - while ((n = read(fd, rbuf, sizeof rbuf - 1)) > 0) { - rbuf[n] = '\0'; - fputs(rbuf, stdout); + while ((n = read(fd, rbuf + len, cap - len - 1)) > 0) { + len += (size_t)n; + if (len >= cap - 1) break; /* 响应超缓冲上限,截断兜底 */ } close(fd); + rbuf[len] = '\0'; + if (!isatty(STDOUT_FILENO)) strip_ansi(rbuf); + fputs(rbuf, stdout); + free(rbuf); return 0; } @@ -1104,7 +1285,7 @@ static void usage(const char *argv0) fprintf(stderr, "usage: %s [options] [command]\n" " commands: start | stop | restart \n" - " | status | reload | pid\n" + " | status [name|all] | reload | pid\n" " no command -> start daemon (default; -f/--foreground keeps fg for supervisor)\n" " options:\n" " -f, --foreground stay in foreground (supervisor fork/exec 托管)\n" diff --git a/patch_v2/meow_svcd/meow_svcd.md b/patch_v2/meow_svcd/meow_svcd.md index a5b0e07..e2b0e5a 100644 --- a/patch_v2/meow_svcd/meow_svcd.md +++ b/patch_v2/meow_svcd/meow_svcd.md @@ -148,7 +148,7 @@ FAILED 崩溃且不再重启(超 max_restart / restart=never) AF_UNIX 单行文本: ``` -meow_svcd status # 列出(daemon 未运行则打印状态文件) +meow_svcd status [name|all] # 列出各服务+全局(daemon 未运行则打印状态文件) meow_svcd reload # 重读卡配置 meow_svcd start meow_svcd stop @@ -156,19 +156,26 @@ meow_svcd restart meow_svcd pid ``` -应答:`OK` / `ERR no-such-service` / 多行 `svc=... state=... pid=... restart=... exit=...`。 +应答:`OK` / `ERR no-such-service` / 多列对齐表格。每服务一行:服务名/状态/pid/ +重启次数+`max_restart`/`backoff_ms`/`trigger`/来源(`board`|`tf`)/最近退出码/退出时间/`exec` 路径; +TTY 时带 ANSI 配色(服务名=加粗青、RUNNING=绿、FAILED=红、STOPPED·STOPPING=黄、DISABLED=灰、 +数字=黄、表头/分隔线=dim),stdout 非 TTY(重定向/SSH 管道)自动去色。 ### 6.1 `systemctl` 式 CLI 用法(SSH 终端示例) 守护已由固件 `etc/init.d/S89meow_svcd` 开机自启,SSH 登录设备后**直接敲**命令即可,无需先知道 socket: ```bash -meow_svcd status # 列出所有服务 + 全局状态 -# # meow_svcd status +meow_svcd status # 列出所有服务 + 全局状态(TTY 带色;管道/重定向自动去色) +# meow_svcd status # card_present=1 wifi_up=0 services=3 -# svc=mdnsd state=running pid=1234 restart=0 exit=0 trigger=boot -# svc=meow_netd state=stopped pid=0 restart=0 exit=0 trigger=card-mount -# svc=ntpc state=stopped pid=0 restart=0 exit=0 trigger=card-mount +# NAME STATE PID RESTART BACKOFF TRIGGER SRC EXIT EXIT_TIME EXEC +# ------------------------------------------------------------------------------ +# mdnsd running 1234 0/5 2000ms boot board - - /usr/bin/meow_mdns +# meow_netd stopped 0 0/5 1000ms card-mount tf - - /usr/bin/meow_netd +# ntpc disabled 0 0/inf 1000ms card-mount tf - - /usr/bin/ntpc + +meow_svcd status mdnsd # 只显示单个服务(其余列同;`status all` 等效于无参) meow_svcd restart mdnsd # 重启单个服务(如板载 mDNS 应答器) meow_svcd start meow_netd # 启动单个服务 @@ -181,8 +188,8 @@ meow_svcd pid # 打印守护进程 pid 守护**未运行**时命令回退打印状态文件 `/tmp/meow_svcd.status`(或 `card_present=0 services=0`)并以退出码 1 返回。 - **守护前台**:`-f`/`--foreground` —— 守护自身跑前台,供 supervisor `fork/exec`+`waitpid` 托管; 默认(无子命令)则为后台常驻。 -- 应答:成功 `OK`(`pid`→pid 数字);服务不存在 `ERR no-such-service`;`svc=... state=...` 行 - 含 `pid/restart(重启计数)/exit(上次退出码)/trigger`。 +- 应答:成功 `OK`(`pid`→pid 数字);服务不存在 `ERR no-such-service`;`status` 返回对齐表格, + 行内含 `state/pid/restart(重启计数)/exit(上次退出码)/trigger/来源/backoff_ms/exec`。 ## 7. 编译 diff --git a/patch_v2/sdk/meow_ui_gfx.c b/patch_v2/sdk/meow_ui_gfx.c index e26f796..a31e971 100644 --- a/patch_v2/sdk/meow_ui_gfx.c +++ b/patch_v2/sdk/meow_ui_gfx.c @@ -353,6 +353,49 @@ void meow_ui_fill_rect_alpha(int x, int y, int w, int h, uint32_t color, int alp #define MUI_HINT_H 5 static int g_hint_last = 0; /* 上次画过的条长(清条时只清到它,避免残留) */ +/* 顶部条区「快照恢复」:画条前把 [0..宽)×[hy..hy+MUI_HINT_H) 拍进小缓冲区;回缩/清空时 + * 回填快照还原「真实底色」而非 th->bg —— 全屏页面底=主题 accent,若填 th->bg(黑) 会在 + * accent 底上留下黑色残条(直到下次区域刷新才消失)。快照只在首现条时拍一次(后续 grow + * 不再覆盖快照),每行/全长布局全保留;缓冲为一次性分配(宽×H 行,480×5×2B≈4.8KB)。 */ +static unsigned char *g_hint_snap = NULL; /* 快照(g_line×MUI_HINT_H 字节;NULL=未拍/分配失败) */ +static int g_hint_snap_y = 0; /* 快照行的起始 y(本次手势时的 hy) */ + +/* 拍下「顶部条区」当前 back_buf 内容(只在 g_hint_last==0 的首现条时调用)。 */ +static void hint_snapshot(void) +{ + int hy = meow_ui_statusbar_height(); + if (hy < 0) hy = 0; + g_hint_snap_y = hy; + if (hy + MUI_HINT_H > g_yres) return; /* 越界:放弃(回缩走 bg 兜底) */ + size_t len = (size_t)MUI_HINT_H * (size_t)g_line; + if (!g_hint_snap) { + g_hint_snap = malloc(len); + if (!g_hint_snap) return; + } + memcpy(g_hint_snap, g_fb + (size_t)hy * g_line, len); +} + +/* 从快照回填列区间 [x0,x1) 的 MUI_HINT_H 行(还原条下真实底色)。x0/x1 已夹到屏宽。 */ +static void hint_restore(int x0, int x1) +{ + if (x1 <= x0) return; + if (x0 < 0) x0 = 0; + if (x1 > g_xres) x1 = g_xres; + if (!g_hint_snap) { /* 无快照(分配失败):兜底填背景(旧行为) */ + const meow_theme *th = meow_theme_get(); + meow_ui_fill_rect(x0, g_hint_snap_y, x1 - x0, MUI_HINT_H, th->bg); + return; + } + int bpp = g_bpp / 8; + for (int r = 0; r < MUI_HINT_H; r++) { + size_t row = (size_t)(g_hint_snap_y + r) * g_line; + memcpy(g_fb + row + (size_t)x0 * bpp, + g_hint_snap + (size_t)r * g_line + (size_t)x0 * bpp, + (size_t)(x1 - x0) * bpp); + } + meow_gfx_mark_dirty(x0, g_hint_snap_y, x1 - x0, MUI_HINT_H); /* 标脏:调用方 flush_region 上屏 */ +} + void meow_ui_swipe_hint(int x) { if (!g_init_ok) return; @@ -361,20 +404,19 @@ void meow_ui_swipe_hint(int x) * 有状态栏(非全屏)→ y = 状态栏高度;收合(collapsed)→ y=提示线下缘 * (MUI_SBAR_COLLAPSE_H,与提示线不重叠);无状态栏(全屏/隐藏)→ y=0。 */ int hy = meow_ui_statusbar_height(); - /* 单一实现(与非全屏 Hello 完全一致,全屏仅 hy 不同):grow 画 0..x、shrunk 先清旧长 - * 再画 0..x、x<=0 清空。x<0 视为清空;条长上限=屏宽。 */ + /* 单一实现(与非全屏 Hello 完全一致,全屏仅 hy 不同):grow 画 0..x、shrunk 先把 + * 超出部分还原为快照、x<=0 整条还原。x<0 视为清空;条长上限=屏宽。条色仍取 th->fg。 */ if (x > 0) { if (x > g_xres) x = g_xres; - /* 往回滑(新长度 < 上次)时,先清掉「上次画到旧长度、但本次超出的部分」, - * 否则条只增不减(右滑变长、左滑回缩实时可见)。 */ - if (g_hint_last > x) meow_ui_fill_rect(0, hy, g_hint_last, MUI_HINT_H, th->bg); - /* 条色取主题前景(fg):fg 定义为与背景对比,能同时覆盖「非全屏(底=主题bg)」与 - * 「全屏 Hello 类(底=主题accent)」两种情况——accent 在 Hello 全屏 accent 底上同色不可见。 */ + if (g_hint_last == 0) hint_snapshot(); /* 首现条:拍下未污染区,供此后回填 */ + /* 往回滑(新长度 < 上次)时,把「上次画到旧长度、本次超出的 [x,old)」还原为快照 + * 真实底色(全屏 accent),而非填 th->bg —— 否则左滑回缩在 accent 底上漏黑。 */ + if (g_hint_last > x) hint_restore(x, g_hint_last); meow_ui_fill_rect(0, hy, x, MUI_HINT_H, th->fg); g_hint_last = x; } else if (g_hint_last > 0) { - /* 手指回原点/抬手未达标/抬手达标:清掉此前画的条(填回背景色) */ - meow_ui_fill_rect(0, hy, g_hint_last, MUI_HINT_H, th->bg); + /* 手指回原点/抬手未达标/抬手达标:整条还原为快照真实底色(全屏 accent 上无黑残) */ + hint_restore(0, g_hint_last); g_hint_last = 0; } /* 不发 flush:由调用方 meow_ui_flush_region(0,0,宽,6) 只刷顶部条 */ @@ -832,6 +874,7 @@ void meow_ui_shutdown(void) if (g_fb_fd >= 0) close(g_fb_fd); if (g_back_buf) free(g_back_buf); if (g_dump_path) { free(g_dump_path); g_dump_path = NULL; } + if (g_hint_snap) { free(g_hint_snap); g_hint_snap = NULL; } g_back_buf = NULL; g_fb = NULL; g_fb_mem = NULL; g_fb_fd = -1; g_init_ok = 0; g_mfont_ready = 0; dirty_reset(); diff --git a/patch_v2/sdk/meow_ui_input.c b/patch_v2/sdk/meow_ui_input.c index 1f27d2d..aa19944 100644 --- a/patch_v2/sdk/meow_ui_input.c +++ b/patch_v2/sdk/meow_ui_input.c @@ -125,6 +125,7 @@ static int t_press_valid = 0; /* BTN_TOUCH=1 已见(本次按下的 gate static int t_dx = 0, t_dy = 0; /* 按下原点(BTN_TOUCH 后首个有效 ABS 锁定) */ static int t_cx = 0, t_cy = 0; /* 当前坐标(-1=本次尚未见到有效坐标) */ static int t_origin_valid = 0; /* 原点是否已锁定 */ +static uint64_t t_down_ms = 0; /* 按下时刻(monotonic ms),供实时按住时长用 */ /* 按下边沿:只标记按下,绝不用传入坐标立即锁原点(避免用上一手势残留坐标当原点)。 * t_cx/t_cy 归 -1 —— 本次手势以「无坐标」起步,只接受随后到来的有效 ABS。 */ @@ -136,6 +137,7 @@ static void touch_press(int x, int y) t_origin_valid = 0; t_cx = -1; t_cy = -1; t_dx = 0; t_dy = 0; + t_down_ms = now_ms(); /* 记按下时刻:供实时按住时长/按住视觉 */ } /* 由 ABS_X/ABS_Y 驱动:若本次按下且原点未锁且坐标有效 → 锁原点;再更新当前坐标。 */ static void touch_update(int x, int y) @@ -151,7 +153,7 @@ static void touch_release(void) if (!t_down) return; t_down = 0; t_press_valid = 0; /* 本次按下从未锁到有效坐标(幽灵触摸/坐标未就绪):丢弃,且清空全部轨迹。 */ - if (!t_origin_valid) { t_cx = t_cy = -1; t_dx = t_dy = 0; return; } + if (!t_origin_valid) { t_cx = t_cy = -1; t_dx = t_dy = 0; t_down_ms = 0; return; } t_origin_valid = 0; int mdx = t_cx - t_dx, mdy = t_cy - t_dy; int adx = mdx < 0 ? -mdx : mdx, ady = mdy < 0 ? -mdy : mdy; @@ -192,7 +194,34 @@ static void touch_release(void) push_ev(MEOW_EV_TAP, t_cx, t_cy); } /* 手势级干净起点:清空全部轨迹,下一手势从零开始,杜绝污染无自愈。 */ - t_cx = t_cy = -1; t_dx = t_dy = 0; + t_cx = t_cy = -1; t_dx = t_dy = 0; t_down_ms = 0; +} + +/* ---- 实时触摸态(内部:控件绘制按压/按住视觉用)---- + * 直接读触摸状态机当前值(与语义事件队列无关;无按下/坐标未就绪时 x/y 返回 -1)。 */ +int meow_input_touch_down(void) { return t_down; } +int meow_input_touch_x(void) { return t_cx; } +int meow_input_touch_y(void) { return t_cy; } +unsigned int meow_input_touch_hold_ms(void) +{ + if (!t_down) return 0; + uint64_t e = now_ms() - t_down_ms; + if (e > 0xFFFFFFFFu) e = 0xFFFFFFFFu; + return (unsigned int)e; +} + +/* 宿主单测/内部注入:模拟按压(down=1 时 x,y=压点、hold_ms=已按住时长),设备运行时无调用方。 */ +void meow_input_touch_set(int down, int x, int y, unsigned hold_ms) +{ + if (down) { + t_down = 1; t_press_valid = 1; t_origin_valid = 1; + t_cx = x; t_cy = y; t_dx = x; t_dy = y; + uint64_t now = now_ms(); + t_down_ms = (now > (uint64_t)hold_ms) ? (now - (uint64_t)hold_ms) : 0; + } else { + t_down = 0; t_press_valid = 0; t_origin_valid = 0; + t_cx = -1; t_cy = -1; t_dx = 0; t_dy = 0; t_down_ms = 0; + } } /* ---- 输入线程:poll 所有 event 设备 + 解析 ---- */ diff --git a/patch_v2/sdk/meow_ui_internal.h b/patch_v2/sdk/meow_ui_internal.h index 849c6d2..7767568 100644 --- a/patch_v2/sdk/meow_ui_internal.h +++ b/patch_v2/sdk/meow_ui_internal.h @@ -52,6 +52,18 @@ void meow_input_inject(int type, int a, int b); /* 清空队列(IME 打开前丢弃残留触摸,避免误敲)。 */ void meow_input_drain(void); +/* ---- 实时触摸态(供控件绘制按压/按住视觉;输入线程写、app 主线读)---- + * 与语义事件队列无关:这些直接读输入线程的触摸状态机当前值,让 meow_widget_draw + * 在「手指按下期间」能如实反映按钮的按下/按住视觉,而无需 app 自行追踪按下。 */ +int meow_input_touch_down(void); /* 当前是否有触摸按下 */ +int meow_input_touch_x(void); /* 当前 X(无按下=-1) */ +int meow_input_touch_y(void); /* 当前 Y(无按下=-1) */ +unsigned int meow_input_touch_hold_ms(void); /* 按下已持续毫秒(抬起后 0) */ + +/* 宿主单测/内部注入:模拟一次按压。down=1 时 x,y 为压点、hold_ms 可指定「已按住时长」 + * (把按下时刻回拨),便于测按住态;down=0 复位。设备运行时无调用方。 */ +void meow_input_touch_set(int down, int x, int y, unsigned hold_ms); + /* ---- IME(meow_ui_ime.c)---- */ /* 当前是否显示软键盘。 */ int meow_ime_active(void); diff --git a/patch_v2/sdk/meow_ui_widget.c b/patch_v2/sdk/meow_ui_widget.c index e7d0cac..58c2f4d 100644 --- a/patch_v2/sdk/meow_ui_widget.c +++ b/patch_v2/sdk/meow_ui_widget.c @@ -11,6 +11,20 @@ enum { W_BTN = 0, W_SLIDER = 1, W_SWITCH = 2, W_LIST = 3 }; +/* 按钮「按住」(长按)判定阈值(ms):超过即进入更强态(更深状态层 + 描边环)。 + * 视觉区分「短按按下」与「按住」;触发语义不变(on_tap 仍按抬手判定)。 */ +#define MEOW_BTN_HOLD_MS 500u + +/* MD3 状态层混色:c 为基色、o 为状态色、t 为 o 的占比(0-100)。 + * 用于把主题 accent 向黑/白调出「按下/按住」状态层(日/夜主题各配色)。 */ +static uint32_t blend_color(uint32_t c, uint32_t o, int t) +{ + int rr = (((c >> 16) & 0xFF) * (100 - t) + ((o >> 16) & 0xFF) * t) / 100; + int gg = (((c >> 8) & 0xFF) * (100 - t) + ((o >> 8) & 0xFF) * t) / 100; + int bb = ((c & 0xFF) * (100 - t) + (o & 0xFF) * t) / 100; + return ((uint32_t)rr << 16) | ((uint32_t)gg << 8) | (uint32_t)bb; +} + struct meow_widget { int type; int x, y, w, h; @@ -89,8 +103,31 @@ void meow_widget_draw(meow_widget *w) const meow_theme *th = meow_theme_get(); switch (w->type) { case W_BTN: { - uint32_t bg = w->pressed ? th->dim : th->accent; - meow_gfx_fill_round(w->x, w->y, w->w, w->h, 10, bg); + /* 按下/按住 = 视觉态(不改 on_tap/btn_pressed/hit 语义)。按下来源两路: + * - w->pressed:应用在 on_tap 命中后标记(既有语义,保留); + * - 实时触摸命中:SDK 输入层当前正按在该控件内(无需 app 追踪)。 + * 按住=实时触摸命中且持续 ≥MEOW_BTN_HOLD_MS;按下=任一来源。 */ + int live = meow_input_touch_down() && + meow_input_touch_x() >= w->x && meow_input_touch_x() < w->x + w->w && + meow_input_touch_y() >= w->y && meow_input_touch_y() < w->y + w->h; + int press = w->pressed || live; + int hold = live && meow_input_touch_hold_ms() >= MEOW_BTN_HOLD_MS; + if (hold) { + /* 按住:更强态——更深状态层 + 内缩圆角描边环(浅 accent),贴 MD3 强调; + * 动效取舍:状态层按规范即时切换(MD3 ripple 属额外动画,依赖 on_tick 逐帧, + * 非本「加视觉态」范围),不做 1-2 帧插值。 */ + uint32_t hbg = blend_color(th->accent, 0x000000, 24); + meow_gfx_fill_round(w->x, w->y, w->w, w->h, 10, hbg); + if (w->w > 6 && w->h > 6) + meow_ui_draw_rect_round(w->x + 1, w->y + 1, w->w - 2, w->h - 2, 9, + blend_color(th->accent, 0xFFFFFF, 45)); + } else if (press) { + /* 按下:轻状态层(accent 向黑调 12%,即变深/偏移),释放恢复;MD3 表面按压。 */ + meow_gfx_fill_round(w->x, w->y, w->w, w->h, 10, + blend_color(th->accent, 0x000000, 12)); + } else { + meow_gfx_fill_round(w->x, w->y, w->w, w->h, 10, th->accent); + } meow_ui_text_center(w->x + w->w / 2, w->y + (w->h - meow_ui_text_height(th->font_body)) / 2, w->label, th->font_body, 0xFFFFFF); break;