feat: M6.10–M6.19 批量收编 + M6.19 推卡验收

- M7 launcher 重写 5 期(虚拟化宫格/meow_theme 单一权威/横幅动画/自定义 PIN 键盘)
- OOM 通用化(cachegov 上收 SDK)、自动切歌根治、meowpanel jv_free 泄漏修复、Hello 滚动卡顿修复
- ncm 歌词 UI / 媒体通知封面 / 封面分辨率 640
- 5 bug 修复:ncm 后台化 UI 叠层+点击穿透、再进 ncm 桌面叠层、播放页新旧 UI 叠层抢刷脏区(meow_ui_gfx 恢复边沿整帧重画)、PIN 亮屏亮度跳(keys.c meow_panel_on 按 settings_brightness_clamped 恢复)、通知中心封面显歌单封面(cover.c save_cover_raw 加 is_song + 命中块守卫)
- 四 App 统一重链新 meow_ui.a 51caee4a;M6.19=0.8.19 固件 2d2331f2 推卡自刷验收全绿
This commit is contained in:
AskaEth committed 2026-09-03 12:16:01 +08:00
1 parent c2ee7626a4
commit 3d70a3dd2c
176 files changed
+15507 -597

No files matched your search

+360 -23
View File
@@ -20,7 +20,7 @@ int menu_opened_blank = 0;
static void launch_fail_toast(char *toast, size_t n, const char *name)
{
const char *bd = meow_dep_block_reason();
if (bd && bd[0]) snprintf(toast, n, "缺依赖: %s", bd);
if (bd && bd[0]) snprintf(toast, n, meow_str_zh("menu.missing_dep", "缺依赖: %s"), bd);
else snprintf(toast, n, "CANNOT START: %s", name);
}
@@ -102,7 +102,8 @@ static void draw_header_content(void)
int tsize = menu_title_size(); /* 标题字号接 font.size.title */
/* M2.6 标题真字体(字号接 font.size.title;回退点阵3x);MD3 on_surface 主文字色 */
if (mfont_ready) {
meow_font_drawtext((uint16_t *)fb, 24, 48, "MeowOS 应用", rgb565(th->on_surface), tsize);
meow_font_drawtext((uint16_t *)fb, 24, 48, meow_str_zh("menu.title", "MeowOS 应用"),
rgb565(th->on_surface), tsize);
fill_rect(0, 48 + meow_font_line_height(tsize) + 2, fb_xres, 2, th->outline);
} else {
draw_text(24, 28, "MEOWAPPS", 3, th->on_surface);
@@ -171,6 +172,37 @@ static meow_img *s_app_icons[MAX_APPS];
/* 当前按压命中的应用格(-1=无);触摸按下置位、抬起清除(按压态视觉) */
static int g_menu_pressed = -1;
/* ================================================================== */
/* M7.4:主界面应用区迁 SDK 虚拟化控件(meow_lazygrid 宫格 + meow_listview 列表 + meow_scroll)。
*
* 铁律(M7 §4/§5):开机必经主界面,最高风险。本块只替换「绘制函数体 + 命中函数体」——
* draw_menu 应用区体、menu_hit_app 体、menu_cell_rect 体;run_menu 会话骨架(触摸/热键/IPC/
* 息屏/自动超时/跨会话)逐字保留。旧手绘路径(draw_menu 手绘循环/menu_draw_app_cell/menu_cell_rect
* 旧体/menu_hit_app 旧体)各以 #if 0 保留为回退位;新 SDK 路径默认启用。
*
* 控件实例惰性创建一次(首跑 ensure),跨多次 run_menu 复用;热路径(draw/hit/scroll)零 malloc
* (SDK lazygrid/listview 均静态有界池)。布局切换(ui.home_layout)在绘/命中时按 menu_layout()
* 选择对应控件(两者同存,互不干扰);主题色全取 s_ui_theme()(= SDK meow_theme 单一权威)。
*
* 几何同源:应用区视口菜单由 menu_app_region() 计算(与 menu_cell_rect/menu_hit_app/draw_menu
* 共用),保证虚拟化控件绘制的格/行位置 = 命中所用几何 = 脏区标脏几何,三者不错位。 */
#define MENU_SDK_ACT 0x55 /* 主界面控件 owner 哨兵(不与电源菜单0x50/NowPlaying0x51/子页0x52/
* 设置页0x53/通知中心0x54 冲突) */
/* 数据源 ctx(bind/count/draw 回调读应用数组;run_menu 的 apps[]/napps 每帧经此对齐)。 */
typedef struct { struct app *apps; int napps; } menu_sdk_ctx_t;
static menu_sdk_ctx_t s_menu_ctx; /* 应用数据源(count/bind/draw 读) */
static meow_lazygrid *s_menu_grid = NULL; /* 宫格(layout=0) */
static meow_scroll *s_menu_grid_sc = NULL;/* 宫格滚动物理(scroll_attach) */
static meow_widget *s_menu_list = NULL; /* 列表(layout=1) */
static meow_scroll *s_menu_list_sc = NULL;/* 列表滚动物理 */
static int s_menu_sdk_init = 0; /* 控件集已建(首跑建一次,跨次复用) */
/* 前置声明(menu_cell_rect 定义在下方 451 行,早于本块函数定义;此处仅给签名)。 */
static void menu_app_region(int apps_top, int layout, int *vx, int *vy, int *vw, int *vh);
static int menu_grid_cell_w(void);
static void menu_sdk_ensure(int apps_top);
/* 背景状态(display.background:纯色名 or 图片路径) */
static meow_img *s_bg_img = NULL; /* 解码后的背景图(缓存,NULL=纯色) */
static int s_bg_is_img = 0; /* 1=图片背景 */
@@ -334,6 +366,27 @@ static void menu_bg_paint(void)
}
}
/* 局部重铺背景(x,y,w,h 区域):图片缓存 blit 对应行或纯色填充。供宫格滚动局部刷时重铺背景区,
* 修「自定义图片背景 + 超屏滚动」残影(M7.4 遗留②)——lazygrid_scroll_attach 的 redraw 只重画
* 格而不重铺背景,超屏滚动后视口内遗留旧帧像素。与 menu_bg_paint 同源(s_bg_cache/menu_bg)。 */
static void menu_bg_paint_region(int x, int y, int w, int h)
{
int W = fb_xres, H = fb_yres;
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if (x + w > W) w = W - x;
if (y + h > H) h = H - y;
if (w <= 0 || h <= 0) return;
if (s_bg_is_img && s_bg_img && s_bg_cache) {
int pitch = meow_ui_back_buf_pitch();
for (int r = 0; r < h; r++)
memcpy(fb + (size_t)(y + r) * pitch + (size_t)x * 2,
s_bg_cache + (size_t)(y + r) * W + x, (size_t)w * 2);
} else {
fill_rect(x, y, w, h, menu_bg);
}
}
/* 应用图标路径:app.json icon 相对应用目录(dirname(exec))/ 绝对;失败回退卡根相对。 */
static void menu_icon_path(const struct app *a, char *out, int olen)
{
@@ -447,10 +500,30 @@ static void menu_draw_icon(const struct app *a, meow_img *icon, int x, int y, in
if (icon) { meow_ui_img_cover(x, y, sz, icon); }
}
/* 应用格几何(grid/list 统一入口;供 draw 与 touch 命中共用)。 */
/* 应用格几何(grid/list 统一入口;供 draw 与 touch 命中共用)。
* M7.4:改从 SDK 控件取当前滚动偏移(虚拟化后格/行位置随 offset 移动),并用 menu_app_region
* 同源几何 —— 保证与 meow_lazygrid/listview 绘制、命中的坐标一致。 */
static void menu_cell_rect(int i, int layout, int apps_top,
int *cx, int *cy, int *cw, int *ch)
{
int vx, vy, vw, vh;
menu_app_region(apps_top, layout, &vx, &vy, &vw, &vh);
(void)vh;
int sc = (layout == 0) ? (s_menu_grid ? meow_lazygrid_scroll(s_menu_grid) : 0)
: (s_menu_list ? meow_listview_scroll(s_menu_list) : 0);
if (layout == 0) { /* card grid */
*cw = menu_grid_cell_w();
*ch = MENU_GRID_CARD_H;
int row = i / MENU_GRID_COLS, col = i % MENU_GRID_COLS;
*cx = vx + col * (*cw + MENU_GRID_GAP);
*cy = vy + row * (*ch + MENU_GRID_GAP) - sc;
} else { /* list */
*cw = vw;
*ch = MENU_LIST_ROW_H;
*cx = vx;
*cy = vy + i * MENU_LIST_ROW_H - sc;
}
#if 0 /* ===== M7.4 回退位:旧手绘几何(无滚动;删 #if 0 行切回)===== */
if (layout == 0) { /* card grid */
int row = i / MENU_GRID_COLS, col = i % MENU_GRID_COLS;
*cw = (fb_xres - 2 * MENU_GRID_PAD - (MENU_GRID_COLS - 1) * MENU_GRID_GAP) / MENU_GRID_COLS;
@@ -463,13 +536,22 @@ static void menu_cell_rect(int i, int layout, int apps_top,
*cx = 8;
*cy = apps_top + i * MENU_LIST_ROW_H;
}
#endif
}
/* 触摸命中应用格;未命中返回 -1。 */
/* 触摸命中应用格;未命中返回 -1。
* M7.4:手绘坐标循环替换为 SDK 控件命中(meow_lazygrid_hit / meow_listview_hit,含滚动换算 +
* 禁用/越界不响应);入口/会话判断(state/napps 门)保留。 */
static int menu_hit_app(int x, int y, struct app *apps, int napps, int state,
int layout, int apps_top)
{
(void)apps; /* 命中判定只依赖几何;apps 保留作接口(0-warning) */
(void)apps; /* 命中交由 SDK 控件(滚动+网格映射);apps 保留作接口(0-warning) */
(void)apps_top; /* SDK 控件自持视口几何;无需外部 apps_top */
if (state != 0 || napps <= 0) return -1;
if (layout == 0) return s_menu_grid ? meow_lazygrid_hit(s_menu_grid, x, y) : -1;
else return s_menu_list ? meow_listview_hit(s_menu_list, x, y) : -1;
#if 0 /* ===== M7.4 回退位:旧手绘命中循环(删 #if 0 行切回)===== */
(void)apps;
if (state != 0 || napps <= 0) return -1;
for (int i = 0; i < napps; i++) {
int cx, cy, cw, ch;
@@ -477,6 +559,7 @@ static int menu_hit_app(int x, int y, struct app *apps, int napps, int state,
if (x >= cx && x < cx + cw && y >= cy && y < cy + ch) return i;
}
return -1;
#endif
}
/* 宫格卡片:surface_container 底 + 圆角 + 选中/按压态 tonal 高亮 + 图标 + 名称。 */
@@ -498,12 +581,14 @@ static void menu_draw_grid_cell(const struct app *a, meow_img *icon, int x, int
int nw = ui_width(nm, 18);
ui_draw(x + (w - nw) / 2, name_y, nm, a->ok ? th->on_surface : th->error, 18);
if (!a->ok) {
int ew = ui_width("[缺失]", 14);
ui_draw(x + (w - ew) / 2, name_y + meow_font_line_height(18) + 2, "[缺失]", th->error, 14);
const char *miss = meow_str_zh("menu.missing", "[缺失]");
int ew = ui_width(miss, 14);
ui_draw(x + (w - ew) / 2, name_y + meow_font_line_height(18) + 2, miss, th->error, 14);
} else if (meow_bg_is_backgrounded(a->id)) {
/* M4:后台保活中标注(播放中转后台的 App;run_menu 1s tick 按 change_seq 刷新) */
int bw = ui_width("·后台", 14);
ui_draw(x + (w - bw) / 2, name_y + meow_font_line_height(18) + 2, "·后台",
const char *bg = meow_str_zh("menu.background", "·后台");
int bw = ui_width(bg, 14);
ui_draw(x + (w - bw) / 2, name_y + meow_font_line_height(18) + 2, bg,
th->on_surface_variant, 14);
}
}
@@ -537,7 +622,7 @@ static void menu_draw_list_cell(const struct app *a, meow_img *icon, int x, int
/* 副标题:package(无则 id),on_surface_variant;M4 后台保活中前缀「·后台 」 */
char sub[48];
snprintf(sub, sizeof sub, "%s%s",
meow_bg_is_backgrounded(a->id) ? "·后台 " : "",
meow_bg_is_backgrounded(a->id) ? meow_str_zh("menu.background_sp", "·后台 ") : "",
a->package[0] ? a->package : (a->id[0] ? a->id : ""));
if (sub[0]) {
char subf[44];
@@ -546,6 +631,7 @@ static void menu_draw_list_cell(const struct app *a, meow_img *icon, int x, int
}
}
#if 0 /* ===== M7.4 回退位:旧应用格分派器(新路径由 menu_sdk_*_draw 适配器经 SDK self-draw 分派)===== */
/* 应用格绘制入口:按布局分派。 */
static void menu_draw_app_cell(const struct app *a, meow_img *icon, int x, int y,
int w, int h, int sel, int pressed, int layout,
@@ -554,6 +640,145 @@ static void menu_draw_app_cell(const struct app *a, meow_img *icon, int x, int y
if (layout == 0) menu_draw_grid_cell(a, icon, x, y, w, h, sel, pressed, th);
else menu_draw_list_cell(a, icon, x, y, w, h, sel, pressed, th);
}
#endif
/* ================================================================== */
/* M7.4:主界面 SDK 控件辅助(menu_app_region 几何 / menu_grid_cell_w / 数据源 bind / SDK 自绘
* 适配器 / meow_scroll 回调 / menu_sdk_ensure 惰性创建)。与 draw_menu 同放,故可引用其上的
* menu_apps_top/menu_draw_grid_cell/menu_draw_list_cell/menu_name_fit 等。 */
/* 应用区视口矩形(与 menu_cell_rect/menu_hit_app/draw_menu 共用);layout 0=宫格 1=列表。
* 顶=apps_top,底=设置行上方;宫格视口 x=12(PAD)、宽=xres-2*PAD;列表视口 x=8、宽=xres-16。 */
static void menu_app_region(int apps_top, int layout, int *vx, int *vy, int *vw, int *vh)
{
int bottom = EXIT_ROW - ROW_H - 4; /* 设置行顶(draw_menu fill_round(8, sy-4)) */
*vy = apps_top;
if (layout == 0) { *vx = MENU_GRID_PAD; *vw = fb_xres - 2 * MENU_GRID_PAD; }
else { *vx = 8; *vw = fb_xres - 16; }
*vh = bottom - apps_top;
if (*vh < 0) *vh = 0;
}
/* 宫格单格宽(与旧 menu_cell_rect 同式:总宽-左右 PAD-内 gap,均分到 cols) */
static int menu_grid_cell_w(void)
{
return (fb_xres - 2 * MENU_GRID_PAD - (MENU_GRID_COLS - 1) * MENU_GRID_GAP) / MENU_GRID_COLS;
}
/* ---- 数据源(count 每帧实时查;bind 仅进视口时填一次 item;阅读 s_menu_ctx)---- */
static int menu_grid_count(void *ctx) { menu_sdk_ctx_t *c = (menu_sdk_ctx_t *)ctx; return c ? c->napps : 0; }
static void menu_grid_bind(int index, meow_lazygrid_item *item, void *ctx)
{
menu_sdk_ctx_t *c = (menu_sdk_ctx_t *)ctx;
memset(item, 0, sizeof *item);
if (!c || index < 0 || index >= c->napps) return;
struct app *a = &c->apps[index];
item->title = a->name;
item->subtitle = NULL; /* 名为主文本;状态/缺失/后台标注由自绘 draw 分色绘制 */
item->icon = (index < MAX_APPS) ? s_app_icons[index] : NULL;
item->enabled = 1; /* 命中即启动(含缺 app → 启动失败→toast,同旧行为) */
item->user = (void *)a;
}
static int menu_list_count(void *ctx) { menu_sdk_ctx_t *c = (menu_sdk_ctx_t *)ctx; return c ? c->napps : 0; }
static void menu_list_bind(int index, meow_lv_item *item, void *ctx)
{
menu_sdk_ctx_t *c = (menu_sdk_ctx_t *)ctx;
memset(item, 0, sizeof *item);
if (!c || index < 0 || index >= c->napps) return;
struct app *a = &c->apps[index];
item->title = a->name;
item->subtitle = NULL; /* 自绘 draw 显示 package/id 副标题(含「·后台」前缀) */
item->icon = (index < MAX_APPS) ? s_app_icons[index] : NULL;
item->icon_kind = item->icon ? MEOW_LIST_ICON_IMG : MEOW_LIST_ICON_NONE;
item->enabled = 1;
item->user = (void *)a;
}
/* ---- SDK 自绘适配器(把 SDK 的 (idx,x,y,w,h,state) 映射到旧格/行绘制内核,迁移其内容)---- */
static void menu_sdk_grid_draw(int index, int x, int y, int w, int h, int state, void *ctx)
{
menu_sdk_ctx_t *c = (menu_sdk_ctx_t *)ctx;
const meow_ui_theme_t *th = s_ui_theme();
if (!c || index < 0 || index >= c->napps) return;
int sel = (state & MEOW_LAZYGRID_ST_SELECTED) ? 1 : 0;
int pressed = (g_menu_pressed == index) ? 1 : 0;
menu_draw_grid_cell(&c->apps[index], (index < MAX_APPS) ? s_app_icons[index] : NULL,
x, y, w, h, sel, pressed, th);
}
static void menu_sdk_list_draw(int index, int x, int y, int w, int h, int state, void *ctx)
{
menu_sdk_ctx_t *c = (menu_sdk_ctx_t *)ctx;
const meow_ui_theme_t *th = s_ui_theme();
if (!c || index < 0 || index >= c->napps) return;
int sel = (state & MEOW_LISTVIEW_ST_SELECTED) ? 1 : 0;
int pressed = ((state & MEOW_LISTVIEW_ST_PRESSED) || g_menu_pressed == index) ? 1 : 0;
menu_draw_list_cell(&c->apps[index], (index < MAX_APPS) ? s_app_icons[index] : NULL,
x, y, w, h, sel, pressed, th);
}
/* ---- meow_scroll 回调(列表;包 listview。宫格用 meow_lazygrid_scroll_attach 自带回调)---- */
static void menu_lv_sc_set(void *ctx, int off) { (void)ctx; if (s_menu_list) meow_listview_set_scroll(s_menu_list, off); }
static int menu_lv_sc_max(void *ctx) { (void)ctx; return s_menu_list ? meow_listview_max_scroll(s_menu_list) : 0; }
static int menu_lv_sc_get(void *ctx) { (void)ctx; return s_menu_list ? meow_listview_scroll(s_menu_list) : 0; }
/* 列表区重刷(含内容底 + listview),供 fling/滚动局部刷;不整屏。 */
static void menu_lv_sc_redraw(void *ctx)
{
(void)ctx;
int vx, vy, vw, vh; menu_app_region(menu_apps_top(), 1, &vx, &vy, &vw, &vh);
if (vh <= 0) return;
meow_ui_fill_rect(vx, vy, vw, vh, s_ui_theme()->surface);
if (s_menu_list) meow_widget_draw(s_menu_list);
meow_dirty_add(vx, vy, vw, vh);
meow_ui_flush();
}
/* ---- 宫格滚动物理回调(M7.4 遗留②:不用 lazygrid_scroll_attach,改自定义 meow_scroll 会话,
* 使 redraw 先重铺背景区再画格——attach 自带的 redraw 只刷视口 meow_gfx,自定义图片背景 +
* 超屏滚动会产生残影。set/max/get 仍指回 lazygrid,滚动语义不变。---- */
static void menu_grid_sc_set(void *ctx, int off) { (void)ctx; if (s_menu_grid) meow_lazygrid_set_scroll(s_menu_grid, off); }
static int menu_grid_sc_max(void *ctx) { (void)ctx; return s_menu_grid ? meow_lazygrid_max_scroll(s_menu_grid) : 0; }
static int menu_grid_sc_get(void *ctx) { (void)ctx; return s_menu_grid ? meow_lazygrid_scroll(s_menu_grid) : 0; }
static void menu_grid_sc_redraw(void *ctx)
{
(void)ctx;
int vx, vy, vw, vh; menu_app_region(menu_apps_top(), 0, &vx, &vy, &vw, &vh);
if (vh <= 0) return;
menu_bg_paint_region(vx, vy, vw, vh); /* 重铺背景区:防图片背景残影 */
if (s_menu_grid) meow_lazygrid_draw(s_menu_grid, meow_theme_get());
meow_dirty_add(vx, vy, vw, vh);
meow_ui_flush();
}
/* ---- 惰性创建一次(首跑 ensure;跨多次 run_menu 复用)。几何以当前 apps_top 定 baseline。---- */
static void menu_sdk_ensure(int apps_top)
{
if (s_menu_sdk_init) return;
meow_ui_activity_push(MENU_SDK_ACT); /* 控件 owner 钉哨兵(与设置页/通知中心同款) */
/* 宫格 */
int gx, gy, gw, gh; menu_app_region(apps_top, 0, &gx, &gy, &gw, &gh);
s_menu_grid = meow_lazygrid_create(gx, gy, gw, gh, MENU_GRID_COLS, menu_grid_cell_w(),
MENU_GRID_CARD_H, MENU_GRID_GAP);
if (s_menu_grid) {
meow_lazygrid_set_vsource(s_menu_grid, menu_grid_count, menu_grid_bind, &s_menu_ctx);
meow_lazygrid_set_item_draw(s_menu_grid, menu_sdk_grid_draw, &s_menu_ctx);
meow_lazygrid_set_empty_text(s_menu_grid, meow_str_zh("menu.empty_app", "没有找到应用"));
/* M7.4 遗留②:自定义会话(redraw 重铺背景),替代 lazygrid_scroll_attach(见 menu_grid_sc_redraw 注) */
s_menu_grid_sc = meow_scroll_create(menu_grid_sc_set, menu_grid_sc_max, menu_grid_sc_get,
menu_grid_sc_redraw, NULL);
}
/* 列表 */
int lx, ly, lw, lh; menu_app_region(apps_top, 1, &lx, &ly, &lw, &lh);
s_menu_list = meow_listview_create(lx, ly, lw, lh, 0, NULL, NULL, MEOW_LIST_SEL_SINGLE);
if (s_menu_list) {
meow_listview_set_vsource(s_menu_list, menu_list_count, menu_list_bind, &s_menu_ctx);
meow_listview_set_item_draw(s_menu_list, menu_sdk_list_draw, &s_menu_ctx);
meow_listview_set_item_h(s_menu_list, MENU_LIST_ROW_H);
meow_listview_set_empty_text(s_menu_list, meow_str_zh("menu.empty_app", "没有找到应用"));
s_menu_list_sc = meow_scroll_create(menu_lv_sc_set, menu_lv_sc_max, menu_lv_sc_get,
menu_lv_sc_redraw, NULL);
}
meow_ui_activity_pop();
s_menu_sdk_init = 1;
}
static void draw_menu(struct app apps[MAX_APPS], int napps, int state,
@@ -572,6 +797,96 @@ static void draw_menu(struct app apps[MAX_APPS], int napps, int state,
menu_bg_paint();
draw_header_content();
if (state == 1) {
if (mfont_ready) {
ui_draw(30, 200, meow_str_zh("menu.no_card", "没有存储卡"), th->on_surface, bsize);
ui_draw(30, 234, meow_str_zh("menu.no_card_hint", "请插入TF卡后重开菜单"),
th->on_surface_variant, bsize);
} else {
draw_text(30, 200, "NO STORAGE CARD", 2, th->on_surface);
draw_text(30, 240, "INSERT TF CARD", 2, th->on_surface_variant);
draw_text(30, 272, "THEN RE-OPEN MENU", 2, th->on_surface_variant);
}
} else if (state == 2 || napps == 0) {
if (mfont_ready) {
ui_draw(30, 200, meow_str_zh("menu.empty_app", "没有找到应用"), th->on_surface, bsize);
ui_draw(30, 234, meow_str_zh("menu.empty_app_hint", "请放入应用到"),
th->on_surface_variant, bsize);
ui_draw(30, 268, "MeowAPPs/<id>/app.json", th->on_surface_variant, bsize);
} else {
draw_text(30, 200, "NO APPS FOUND", 2, th->on_surface);
draw_text(30, 240, "PUT APP IN", 2, th->on_surface_variant);
draw_text(30, 272, "MeowAPPs/<id>/app.json", 2, th->on_surface_variant);
}
} else {
/* M7.4:应用区迁 SDK 虚拟化控件(layout=0 → meow_lazygrid;layout=1 → meow_listview)。
* 数据源 ctx 对齐当前 apps[]/napps;控件首跑建一次,跨次复用;绘制/命中/脏区几何同源
* (menu_app_region)。超屏滚动由 meow_scroll 接管(宫格 scroll_attach / 列表手动会话)。 */
s_menu_ctx.apps = apps; s_menu_ctx.napps = napps;
menu_sdk_ensure(apps_top);
int vx, vy, vw, vh; menu_app_region(apps_top, layout, &vx, &vy, &vw, &vh);
if (layout == 0) {
if (s_menu_grid) {
meow_lazygrid_set_rect(s_menu_grid, vx, vy, vw, vh);
meow_lazygrid_set_sel(s_menu_grid, (cursor >= 0 && cursor < napps) ? cursor : -1);
meow_lazygrid_draw(s_menu_grid, meow_theme_get());
}
} else {
if (s_menu_list) {
meow_widget_set_rect(s_menu_list, vx, vy, vw, vh);
meow_listview_set_sel(s_menu_list, (cursor >= 0 && cursor < napps) ? cursor : -1);
meow_widget_draw(s_menu_list);
}
}
}
/* M2.36 B2b:系统设置 builtin 行(过渡入口)。位于应用项之后、Exit 行之前;
* 音量导航可把光标移到此处(cursor==napps),触摸点击该行进入设置页。 */
{
int sy = EXIT_ROW - ROW_H; /* 固定在 Exit 行上方 588 */
int selset = (cursor == napps); /* 光标已移到设置行 */
meow_gfx_fill_round(8, sy - 4, fb_xres - 16, ROW_H - 8, 12, th->surface_container);
if (selset) meow_gfx_fill_round_alpha(8, sy - 4, fb_xres - 16, ROW_H - 8, 12, th->primary, 40);
if (mfont_ready) ui_draw(24, sy, meow_str_zh("menu.settings", "系统设置"), th->on_surface, bsize);
else draw_text(24, sy, "SETTINGS", 2, th->on_surface);
s_draw_chevron(fb_xres - 22, sy + ROW_H / 2, th->on_surface_variant);
}
/* exit row */
meow_gfx_fill_round(8, EXIT_ROW - 4, fb_xres - 16, ROW_H - 8, 12, th->surface_container);
/* M2.2/M2.6:MeowOS 常驻主界面(播放器已死)时改「重启返回播放器」。 */
if (mfont_ready) ui_draw(24, EXIT_ROW, meow_mode ? meow_str_zh("menu.reboot_player", "重启返回播放器")
: meow_str_zh("menu.back_player", "返回播放器"),
th->on_surface, bsize);
else draw_text(24, EXIT_ROW, meow_mode ? "REBOOT TO PLAYER" : "BACK TO PLAYER", 2, th->on_surface);
/* M2.18 Bug 3: 底部对齐,弃用硬编码 y=700。字号 20 真实行高 meow_font_line_height(20)。
* 非 mfont 回退分支(5x7 点阵,字高=7*scale)同样底部对齐,两分支都处理。 */
#define FOOTY_PAD 8
{
char ver[32];
snprintf(ver, sizeof ver, "MeowOS v%s dev", g_meowos_version);
int foot_y = fb_yres - meow_font_line_height(bsize) - FOOTY_PAD;
ui_draw(fb_xres - 8 - ui_width(ver, bsize), foot_y, ver, th->on_surface_variant, bsize);
}
if (toast_ms > 0 && toast && toast[0]) {
fill_rect(20, TOAST_Y - 6, fb_xres - 40, 40, th->surface_variant);
ui_draw(30, TOAST_Y, toast, th->error, bsize);
}
#if 0 /* ===== M7.4 回退位:旧手绘主界面全屏绘制(应用区手绘宫格/列表)。
* 回退 = 删本 #if 0 / #endif 行 + 注释上方新 SDK 路径;run_menu 会话骨架不在此。 ===== */
const meow_ui_theme_t *th = s_ui_theme();
int layout = menu_layout();
int apps_top = menu_apps_top();
int bsize = 20;
meow_settings_get_int("font.size.body", &bsize);
if (bsize < 12) bsize = 12;
if (bsize > 28) bsize = 28;
menu_bg_prepare(th);
menu_bg_paint();
draw_header_content();
if (state == 1) {
if (mfont_ready) {
ui_draw(30, 200, "没有存储卡", th->on_surface, bsize);
@@ -600,27 +915,23 @@ static void draw_menu(struct app apps[MAX_APPS], int napps, int state,
layout, th);
}
}
/* M2.36 B2b:系统设置 builtin 行(过渡入口)。位于应用项之后、Exit 行之前;
* 音量导航可把光标移到此处(cursor==napps),触摸点击该行进入设置页。 */
/* M2.36 B2b:系统设置 builtin 行(同上方新路径) */
{
int sy = EXIT_ROW - ROW_H; /* 固定在 Exit 行上方 588 */
int selset = (cursor == napps); /* 光标已移到设置行 */
int sy = EXIT_ROW - ROW_H;
int selset = (cursor == napps);
meow_gfx_fill_round(8, sy - 4, fb_xres - 16, ROW_H - 8, 12, th->surface_container);
if (selset) meow_gfx_fill_round_alpha(8, sy - 4, fb_xres - 16, ROW_H - 8, 12, th->primary, 40);
if (mfont_ready) ui_draw(24, sy, "系统设置", th->on_surface, bsize);
if (mfont_ready) ui_draw(24, sy, meow_str_zh("menu.settings", "系统设置"), th->on_surface, bsize);
else draw_text(24, sy, "SETTINGS", 2, th->on_surface);
s_draw_chevron(fb_xres - 22, sy + ROW_H / 2, th->on_surface_variant);
}
/* exit row */
meow_gfx_fill_round(8, EXIT_ROW - 4, fb_xres - 16, ROW_H - 8, 12, th->surface_container);
/* M2.2/M2.6:MeowOS 常驻主界面(播放器已死)时改「重启返回播放器」。 */
if (mfont_ready) ui_draw(24, EXIT_ROW, meow_mode ? "重启返回播放器" : "返回播放器", th->on_surface, bsize);
if (mfont_ready) ui_draw(24, EXIT_ROW, meow_mode ? meow_str_zh("menu.reboot_player", "重启返回播放器")
: meow_str_zh("menu.back_player", "返回播放器"),
th->on_surface, bsize);
else draw_text(24, EXIT_ROW, meow_mode ? "REBOOT TO PLAYER" : "BACK TO PLAYER", 2, th->on_surface);
/* M2.18 Bug 3: 底部对齐,弃用硬编码 y=700。字号 20 真实行高 meow_font_line_height(20)。
* 非 mfont 回退分支(5x7 点阵,字高=7*scale)同样底部对齐,两分支都处理。 */
/* footer(底部对齐) */
#define FOOTY_PAD 8
{
char ver[32];
@@ -628,11 +939,11 @@ static void draw_menu(struct app apps[MAX_APPS], int napps, int state,
int foot_y = fb_yres - meow_font_line_height(bsize) - FOOTY_PAD;
ui_draw(fb_xres - 8 - ui_width(ver, bsize), foot_y, ver, th->on_surface_variant, bsize);
}
if (toast_ms > 0 && toast && toast[0]) {
fill_rect(20, TOAST_Y - 6, fb_xres - 40, 40, th->surface_variant);
ui_draw(30, TOAST_Y, toast, th->error, bsize);
}
#endif
}
@@ -784,6 +1095,13 @@ static void run_menu(struct app apps[MAX_APPS], int napps, int state,
long meow_last_poll = 0; /* M2.2:MeowOS 后台拔营(卡/网)轮询时间戳 */
unsigned long long apps_sig = 0; /* M2.77:应用目录签名(0=未初始化,首圈建立基线) */
unsigned long long fonts_sig = 0; /* M2.83:字库目录签名(门控 scan_presets,防 2s 轮询 sha256 风暴) */
#if 0 /* ===== M7.4 遗留① 补位(默认关闭):主界面宫格/列表超屏滚动喂给。
* 作用:应用数 > 视口(max_scroll>0)时,run_menu 触摸循环内喂 meow_scroll_track(拖动跟
* 手)+ 松手 meow_scroll_fling(惯性)。当前 MAX_APPS=8 装得下(max_scroll==0),喂给为
* no-op(安全);未来应用 >8 时删除下述三处 #if 0 行即启用,run_menu 会话骨架逐字不变
* (只补喂、不删骨架)。------------------------------------- */
int sg_track_y = -1; /* 本次手势上一 y(增量 dw 用;<=8 应用从不喂) */
#endif
load_config(); /* meow.conf on the card: bg_color etc. */
menu_icons_load(apps, napps); /* MD3:应用图标解码一次进缓存(不每帧解码) */
@@ -1289,6 +1607,19 @@ static void run_menu(struct app apps[MAX_APPS], int napps, int state,
need_menu_paint = 1;
}
}
#if 0 /* M7.4 遗留① 补位:拖动超屏滚动喂 meow_scroll_track(增量 dw)。≤8 应用 max_scroll==0 → no-op。
* 启用(删本 #if 0/#endif)后:移动手指即在应用区/列表区跟手滚动,骨架其它判定不变。 */
if (t_down && t_down_valid && t_last_y >= 0) {
if (sg_track_y < 0) sg_track_y = t_last_y;
int dw = t_last_y - sg_track_y;
if (dw != 0) {
if (menu_layout() == 0) { if (s_menu_grid_sc) meow_scroll_track(s_menu_grid_sc, dw, now_ms()); }
else { if (s_menu_list_sc) meow_scroll_track(s_menu_list_sc, dw, now_ms()); }
sg_track_y = t_last_y;
need_menu_paint = 1;
}
}
#endif
/* ⑨ 顶部热区「跟手下拉」打开通知中心(拖动中即进,真跟手):
* 原点锁定 + 起点在顶部热区(y<SB_BAR_H) + 下拉达 NCP_DRAG_ENTER_SLOP →
* 立即切入 run_notif_center(此时手指仍按下拖动),由面板内拖动路径把 progress
@@ -1372,6 +1703,12 @@ static void run_menu(struct app apps[MAX_APPS], int napps, int state,
}
else if (!val && t_down) {
t_down = 0;
#if 0 /* M7.4 遗留① 补位:松手 fling(惯性滚动)。≤8 应用 max_scroll==0 → fling 无异动(no-op)。
* 启用(删本 #if 0/#endif)后:快速滑动松开即惯性停,跟手已由上方 track 喂给。 */
if (s_menu_grid_sc) meow_scroll_fling(s_menu_grid_sc);
if (s_menu_list_sc) meow_scroll_fling(s_menu_list_sc);
sg_track_y = -1;
#endif
if (!t_down_valid) {
/* 本次按下从未锁到有效坐标(坐标滞后/ghost 空按下):丢本次。 */
logmsg("menu tap dropped: no valid origin (last %d,%d)",