Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/bg_page.c
T

305 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* bg_page.c — 批③ 后台管理页(桌面顶栏居右按钮进入)。
*
* 容器:sub_page_t 脚手架(subpage.c,与 wifi_page/bt_page 同款)。顶栏=meow_topappbar:
* 返回箭头 + 标题「后台管理」+ **顶栏标题栏右侧「一键清除后台」按钮**(sub_page_t.top_action,
* act=bgm_top_action → bgmgr_kill_all,**不杀框架/当前前台/非后台**,经 bgmgr_can_kill 门控)。
* 列表(live=1 约 2s 重扫,实时增删):
* - 每后台 App 一行 SUB_ROW:label=名字,sub=「CPU x% · yMB · 状态」,**行最右一个「杀」按钮**
* (sub_page_t.row_draw 行自绘 + row_tap 自定义命中:命中按钮区=kill 该 App;行体不响应)。
* - 行=MD3 圆角卡(meow_gfx_fill_round r=12 surfaceContainer;卡在行高内上下对称内缩
* BGM_CARD_GAP/2,行中心=卡中心,相邻卡 8px 外距——与主界面列表/ ncm 歌单同手法)。
* - 空态「无后台 App」。
* 数据源=amsd FG/BG 注册表(后台常驻集=launcher meow_bg 表,EV BG/FG 上报进 amsd)+ 每进程
* pcpu/rss(meow_svc_perf 同源 /proc 聚合,本页在 launcher 内同口径取)。
*
* 按钮化(相对 agent-425 的「点行 kill + 顶置清除动作行」):
* - kill:改「点整行」为「行最右独立杀按钮」——行自绘(bgm_row_draw)+ 自定义命中(bgm_row_tap),
* 命中几何=bgm_btn_rect(行右 46×34 圆角按钮);行体其它区域 point 回 0(不杀)。
* - 清:删顶置「一键清除后台」SUB_ACT 动作行,改为顶栏标题栏右侧 action 按钮(top_action)。
*/
#include "meow_launcher_internal.h"
#include "meow_bgplay.h"
#include "meow_bgmgr.h"
#include <ctype.h>
/* 行内杀按钮尺寸(圆心/圆角定位用;命中与绘制共用同一几何,保证所见即所点) */
#define BGM_BTN_W 46
#define BGM_BTN_H 34
/* 圆角卡列表项外边距(item margin/gap):相邻卡片间可见空隙 px。SDK listview 行距(=SUB_ITEM_H)
* 不变——卡片在其行高内上下对称内缩 BGM_CARD_GAP/2,故行内内容(按行高垂直居中)仍落卡中心;
* 相邻卡顶间距恒=SUB_ITEM_H=卡高+BGM_CARD_GAP。要求偶数,保证上下对称。 */
#define BGM_CARD_GAP 8
static inline int bgm_row_card_y(int row_top) { return row_top + BGM_CARD_GAP / 2; }
static inline int bgm_row_card_h(int h) { return h - BGM_CARD_GAP; }
typedef struct {
struct app *apps;
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 */
static struct app *bgm_app_find(struct app *apps, int napps, const char *app_id)
{
if (!apps || !app_id) return NULL;
for (int i = 0; i < napps; i++)
if (apps[i].id[0] && !strcmp(apps[i].id, app_id)) return &apps[i];
return NULL;
}
/* ---- meow_bgmgr ops 注入(UI 侧真实实现)---- */
static int bgm_app_meta(void *ctx, const char *app_id, pid_t pid,
char *name, int nl, char *icon, int il,
char *framework, int *is_fg)
{
(void)pid;
bgm_ctx_t *b = ctx;
struct app *a = bgm_app_find(b->apps, b->napps, app_id);
*framework = 0;
*is_fg = 0;
if (!a) return -1; /* 未注册 → name 回退 app_id */
if (a->name[0]) snprintf(name, nl, "%s", a->name);
if (icon && il > 0) snprintf(icon, il, "%s", a->icon[0] ? a->icon : "");
*framework = (a->framework == '1') ? '1' : 0;
return 0;
}
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++) {
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;
}
return n;
}
/* proc_sample:/proc/<pid>/stat(utime+stime 差分) + /proc/<pid>/status(VmRSS)。
* 与 meow_svc_perf 897-983 每进程 pcpu/rss 聚合同口径(同 /proc 源)。 */
static long g_cpu_pid[MEOW_BG_MAX] = {0};
static unsigned long long g_cpu_tick[MEOW_BG_MAX] = {0};
static int bgm_proc_sample(void *ctx, pid_t pid, double *pcpu, long *rss_kb)
{
(void)ctx;
if (pid <= 0) return -1;
char path[64], line[512];
long rss = 0;
snprintf(path, sizeof path, "/proc/%d/status", pid);
int fd = open(path, O_RDONLY);
if (fd >= 0) {
ssize_t nr = read(fd, line, sizeof line - 1); close(fd);
if (nr > 0) { line[nr] = 0;
char *p = strstr(line, "VmRSS:");
if (p) p = strchr(p, ' ');
if (p) { while (*p == ' ' || *p == '\t') p++; rss = atol(p); }
}
}
double pc = 0.0;
snprintf(path, sizeof path, "/proc/%d/stat", pid);
fd = open(path, O_RDONLY);
if (fd >= 0) {
ssize_t nr = read(fd, line, sizeof line - 1); close(fd);
if (nr > 0) {
line[nr] = 0;
char *rp = strrchr(line, ')');
if (rp) {
char *p = rp + 2;
unsigned long long ut[2] = {0, 0};
for (int f = 0; f < 14; f++) {
while (*p == ' ') p++;
char *end; unsigned long long v = strtoull(p, &end, 10);
if (end == p) { p++; continue; }
p = end;
if (f == 11) ut[0] = v; /* utime */
if (f == 12) ut[1] = v; /* stime */
}
unsigned long long cur = ut[0] + ut[1];
static double hz = 0.0;
if (hz <= 0) { hz = (double)sysconf(_SC_CLK_TCK); if (hz <= 0) hz = 100.0; }
unsigned long long prev = 0; int had = 0, slot = -1;
for (int i = 0; i < MEOW_BG_MAX; i++) {
if (g_cpu_pid[i] == pid) { prev = g_cpu_tick[i]; had = 1; slot = i; break; }
}
if (!had) {
for (int i = 0; i < MEOW_BG_MAX; i++) if (g_cpu_pid[i] == 0) { slot = i; break; }
if (slot < 0) slot = 0;
}
if (slot >= 0) { g_cpu_pid[slot] = pid; g_cpu_tick[slot] = cur; }
if (had) { pc = 100.0 * (double)(cur - prev) / hz; if (pc < 0) pc = 0; }
}
}
}
*pcpu = pc;
*rss_kb = rss;
return 0;
}
/* ---- rows(填 sub_row_t 列表;每后台 App 一行,空态一行)---- */
static int bgm_rows(void *ctx, sub_row_t *rbuf, int max)
{
bgm_ctx_t *b = ctx;
bgmgr_ops_t ops;
memset(&ops, 0, sizeof ops);
ops.ctx = b;
ops.enumerate = bgm_enumerate;
ops.app_meta = bgm_app_meta;
ops.proc_sample = bgm_proc_sample;
b->n = bgmgr_collect(&ops, b->rows, MEOW_BGMR_ROW_MAX);
int r = 0;
if (b->n == 0) {
if (r < max) {
rbuf[r].kind = SUB_ROW;
rbuf[r].label = meow_str_zh("bgmgr.empty", "无后台 App");
rbuf[r].sub = NULL;
rbuf[r].act = -1;
r++;
}
return r;
}
for (int i = 0; i < b->n && r < max; i++) {
char sub[80];
snprintf(sub, sizeof sub, "CPU %.1f%% · %s · %s", b->rows[i].pcpu,
bgmgr_mem_str(b->rows[i].rss_kb), bgmgr_status_str(&b->rows[i]));
rbuf[r].kind = SUB_ROW;
rbuf[r].label = b->rows[i].name[0] ? b->rows[i].name : b->rows[i].app_id;
rbuf[r].sub = sub;
rbuf[r].act = i; /* row_tap/row_draw 用 i 定位行 */
r++;
}
return r;
}
/* kill 通道(bgmgr_kill_all 的函数指针签名:void(*)(const char*)) */
static void bgm_kill_fn(const char *app_id) { meow_bg_terminate(app_id); }
/* ---- 行内杀按钮矩形(绘制与命中共用;行视口矩形 rx,ry,rw,rh → 右侧 46×34 圆角钮)----
* 卡在行高内上下对称内缩 BGM_CARD_GAP/2(行中心=卡中心);按钮垂直居中于卡内,故其中心
* =卡中心=行中心。命中矩形仍为整行距(row_tap 传整行视口),按钮几何不变 → 卡内缩后命中不破。 */
static void bgm_btn_rect(int rx, int ry, int rw, int rh, int *bx, int *by, int *bw, int *bh)
{
*bw = BGM_BTN_W;
*bh = BGM_BTN_H;
*bx = rx + rw - 12 - BGM_BTN_W;
*by = bgm_row_card_y(ry) + (bgm_row_card_h(rh) - BGM_BTN_H) / 2;
}
/* ---- 行完全自绘(sub_page_t.row_draw;覆盖默认 renderer)----
* 空态行(act=-1)居中画「无后台 App」;后台 App 行画 名字 + CPU/内存/状态 + 行右「杀」按钮。 */
static void bgm_row_draw(void *ctx, int idx, sub_row_t *r,
int x, int y, int w, int h, int state)
{
(void)idx;
bgm_ctx_t *b = ctx;
const meow_theme *mt = meow_theme_get();
int i = r->act;
/* MD3 圆角卡:行高内上下对称内缩 BGM_CARD_GAP/2(行中心=卡中心),替代扁平行+底部分隔线。 */
int cy = bgm_row_card_y(y);
int ch = bgm_row_card_h(h);
meow_gfx_fill_round(x, cy, w, ch, 12, mt->surfaceContainer);
if (state & MEOW_LISTVIEW_ST_PRESSED)
meow_gfx_fill_round_alpha(x, cy, w, ch, 12, mt->primary, 26); /* 按压态(实时,限卡内) */
if (i < 0 || i >= b->n) { /* 空态行 */
int th = meow_ui_text_height(18);
meow_ui_text_center(x + w / 2, cy + (ch - th) / 2,
r->label ? r->label : meow_str_zh("bgmgr.empty", "无后台 App"),
18, mt->onSurfaceVariant);
return;
}
int th = meow_ui_text_height(20), tsub = meow_ui_text_height(16);
int rx = x + 16; /* 左内边距 */
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);
/* 行内杀按钮(圆角框 + 「杀」;error=红=杀) */
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, 10, mt->error);
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);
}
/* ---- 行内自定义命中(sub_page_t.row_tap;仅按钮区 kill,行体不响应/不误杀)---- */
static int bgm_row_tap(void *ctx, int idx, sub_row_t *r, int x, int y,
int rx, int ry, int rw, int rh)
{
(void)idx;
bgm_ctx_t *b = ctx;
int i = r->act;
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();
return 1;
}
/* ---- 行体点击(sub_page_t.detail):不响应(kill 只认行内按钮)---- */
static int bgm_detail(void *ctx, int idx, int act)
{
(void)ctx; (void)idx; (void)act;
return 0;
}
/* ---- 顶栏右侧「一键清除后台」按钮(sub_page_t.top_action_cb)---- */
static int bgm_top_action(void *ctx)
{
bgm_ctx_t *b = ctx;
bgmgr_ops_t ops;
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;
}
/* ---- 入口:运行后台管理页(阻塞到返回)---- */
int run_bg_page(struct app *apps, int napps)
{
static bgm_ctx_t b;
memset(&b, 0, sizeof b);
b.apps = apps;
b.napps = napps;
sub_page_t p;
memset(&p, 0, sizeof p);
p.title = "后台管理";
p.rows = bgm_rows;
p.detail = bgm_detail;
p.live = 1; /* 后台表/进程变化实时重扫 */
p.ctx = &b;
p.top_action = "一键清除后台"; /* 顶栏标题栏右侧按钮 */
p.top_action_cb = bgm_top_action;
p.row_draw = bgm_row_draw; /* 行自绘(名字/CPU/内存/状态 + 行右杀按钮) */
p.row_tap = bgm_row_tap; /* 行内自定义命中(仅按钮区 kill) */
meow_subpage_run(&p);
/* 清除 CPU 差分缓存(页面会话结束) */
memset(g_cpu_pid, 0, sizeof g_cpu_pid);
memset(g_cpu_tick, 0, sizeof g_cpu_tick);
return 0;
}