Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/test_subpage_widgets.c
T
AskaEth 7c7d891570 feat: M6.44+M6.45 收编 — 真机 0.8.45 现场保全(reader章节/key_router按键路由/WiFi连接修复/ncm bench+IME修复)
脏工作区现场收编:devdocs/local/m6_44_45_dirty_tracked.patch(46 个已跟踪文本文件,
+3090/-585, 已对 M6.43 基线干净适配) + m6_44_45_untracked_src.tar.gz 中 patch_v2/ 下
13 个新增源文件, 合成单个提交, 内容与容器工作树(R3ProII/patch_v2)逐字节一致。

- reader 章节: 新增 core/rdchapter.{h,c} — TXT 章节规范解析器, 扫描 512KB 码位对齐段文件
  逐段流式识别标题行(行首缩进≤4/显示宽≤40/行内标点≤1 三重约束防误判), 输出标题+全书字符
  偏移+段号/段内偏移+阿拉伯章号(中文数字归一), 首章前内容归"卷首"; book/reader/rdenc/
  rdlayout/settings 接入章节跳转与进度锚点, 附 chapter_smoke/turn_repro/build_turn_repro.sh
- key_router 按键路由: 新增 meow_launcherd/key_router.{h,c} — launcher-UI 页物理键统一路由
  (P1 仅音量键), 把读取边沿+防抖+长按 held+ramp 推进从 5 个会话收敛为进程内单开;
  各页每圈 key_router_pump() 取防抖后边沿, 动作(调音量/光标导航)仍由调用页决定,
  保留 run_menu 的 meow_mode 分支; App 前台 volume_page 武装时边沿抑制;
  main_menu/now_playing/notif_center/settings_page/subpage 接入, 附 test_key_router_vol/test_np_keys
- WiFi 连接修复: meow_netmgrd connect 有界等待 wpa_state=COMPLETED(普通 10s/隐藏网络 15s,
  env 可调)修设置页连接无响应; list_networks/scan_results 的 \xNN 转义还原始字节, 修中文 SSID
  永不命中致新建同名 open 记录顶影; launcher netmgr_ipc 配读超时; 附 test_wifi_connect(桩
  wpa_cli 驱动 static wifi_connect_ex), test_netmgrd.sh 补用例; wifi_page 同步
- ncm bench: 新增 bench/lyric_sim.c、bench/plpage_sim.c, vol_sync_sim 与 ncm core/app 同步
- SDK IME 修复: shift 大小写、K_ENTER 只提交不关、阻塞 IME 循环喂 on_tick 不被遮挡、
  IME 打开 ~200ms TAP 冷却窗吞 stray-TAP; 附 sdk/test_ime_fix.c
- 同期: meow_audiod 音量异步 ACK 线程(代际防 fd/槽复用竞态)、watchdog 音量收编(首选 audiod
  权威, settings.conf 覆盖路径)、panel 文件搜索(F1-④)与蓝牙只读详情(F1-③)、各类 build 脚本同步

范围红线: third_party/legado-E-main 原开发者未跟踪, 本次不入库(保留 devdocs/local tar 参考);
二进制不入库(后续重编); devdocs/ 不入库。59 文件全部在 patch_v2/ 下, 无 third_party/devdocs。
验证: git apply --check 干净; 59 文件 md5 与容器 patch_v2 逐一比对 59/59 一致(含
key_router.c/rdchapter.c/meow_ui_ime.c/test_wifi_connect.c 抽查); 可执行位保守
(check_lgpl_release.sh 100755, build_turn_repro.sh 100644 与容器一致)。
2026-11-04 20:02:28 +08:00

260 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.
/* test_subpage_widgets.c — M7.1a 子页渲染层迁移(SDK 控件化)宿主验证。
*
* 复现 subpage.c 所建控件集(topappbar + 顶开关 meow_switch + listview 虚拟化 vsource +
* meow_scroll 包裹 fling),验证:
* P1 listview 虚拟化渲染:section 段头 + 普通/开关/动作行(right=SWITCH/ARROW)非背景像素;
* P2 listview 命中:行号(含滚动换算 + 禁用行不响应)+ right 操作区命中(hit_op);
* P3 topappbar 命中:返回箭头区 → MEOW_TOPBAR_BACK;
* P4 meow_scroll 包裹 listview:跟手 track + 松手 fling(meow_anim_tick 驱动)+ snap 到行边界;
* P5 SDK 输入态(meow_input_touch_set)→ listview 按压态像素变化;
* P6 主题四态(MD3/MIUIx × 明暗)下 topappbar + listview 渲染非背景。
*
* 几何常量与 subpage.c / meow_launcher_internal.h 镜像;控件创建方式与 sp_widgets_create 同
* (owner=哨兵 0x52,listview 用 set_vsource + set_item_renderer(2,0,1) + set_item_h 56)。
*
* 编译(在 patch_v2/meow_launcherd 下):
* gcc -Wall -Wextra -o test_subpage_widgets test_subpage_widgets.c \
* ../sdk/test_host_stub.c ../sdk/meow_ui_gfx.c ../sdk/meow_ui_sys.c \
* ../sdk/meow_ui_theme.c ../sdk/meow_ui_vol.c ../sdk/meow_ui_log.c \
* ../sdk/meow_ui_widget.c ../sdk/meow_ui_input.c ../sdk/meow_ui_activity.c \
* ../sdk/meow_ui_dialog.c ../sdk/meow_ui_ime.c ../sdk/meow_ui_str.c ../sdk/meow_pinyin.c \
* ../sdk/meow_ui_draw.c ../sdk/meow_ui_view.c ../sdk/meow_ui_box.c ../sdk/meow_ui_anim.c \
* ../sdk/meow_ui_scroll.c ../sdk/meow_ui_img.c ../sdk/meow_ui_media.c \
* ../sdk/meow_notify_ipc.c ../sdk/stb_image.c -I../sdk -I. -DMEOW_UI_HOST_TEST -pthread -lm
* 运行:./test_subpage_widgets
*/
#include "meow_ui.h"
#include "meow_ui_internal.h" /* meow_ui_back_buf / meow_gfx_mark_dirty / meow_input_touch_set */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static int g_fail = 0;
#define CHECK(cond, ...) do { \
if (cond) { printf(" PASS: " __VA_ARGS__); printf("\n"); } \
else { printf(" FAIL: " __VA_ARGS__); printf("\n"); g_fail++; } } while (0)
/* ---- meow_lv_item 大列表数据源(P4 滚动测用:40 行) ---- */
static int big_count(void *ctx) { (void)ctx; return 40; }
static void big_bind(int idx, meow_lv_item *out, void *ctx) {
(void)ctx; memset(out, 0, sizeof *out);
out->icon_kind = MEOW_LIST_ICON_NONE; out->enabled = 1;
out->title = "行"; out->subtitle = "sub"; out->right = MEOW_LIST_RIGHT_ARROW;
(void)idx;
}
/* ---- 与 subpage.c / meow_launcher_internal.h 同源几何(镜像) ---- */
#define SUB_TB_Y 28
#define SUB_TB_H 68
#define SUB_TB_BOT (SUB_TB_Y + SUB_TB_H) /* 96 */
#define SUB_CONT_TOP SUB_TB_BOT
#define SUB_FOOT_H 44
#define SUB_ITEM_H 56
#define SUB_SENTINEL 0x52
#define XRES 480
#define YRES 720
#define LV_H (YRES - SUB_CONT_TOP - SUB_FOOT_H) /* 580 */
/* ---- sub_row_t 行缓冲镜像(sub_page_t/sub_row_t 结构在 meow_launcher_internal.h) ---- */
enum { SUB_HDR, SUB_ROW, SUB_SW, SUB_ACT };
typedef struct { int kind; const char *label; const char *sub; int on; int act; } sub_row_t;
static sub_row_t g_rows[16];
static int g_nrows = 0;
static void rows_fill(void)
{
g_nrows = 0;
g_rows[g_nrows].kind = SUB_HDR; g_rows[g_nrows].label = "当前连接"; g_rows[g_nrows].sub = NULL; g_nrows++;
g_rows[g_nrows].kind = SUB_ROW; g_rows[g_nrows].label = "已连接"; g_rows[g_nrows].sub = "MyWiFi"; g_rows[g_nrows].act = -1; g_nrows++;
g_rows[g_nrows].kind = SUB_HDR; g_rows[g_nrows].label = "已保存网络"; g_rows[g_nrows].sub = NULL; g_nrows++;
g_rows[g_nrows].kind = SUB_ACT; g_rows[g_nrows].label = "MyWiFi"; g_rows[g_nrows].sub = "已连接"; g_rows[g_nrows].act = 0; g_nrows++;
g_rows[g_nrows].kind = SUB_HDR; g_rows[g_nrows].label = "附近网络"; g_rows[g_nrows].sub = NULL; g_nrows++;
g_rows[g_nrows].kind = SUB_SW; g_rows[g_nrows].label = "WLAN"; g_rows[g_nrows].sub = NULL; g_rows[g_nrows].on = 1; g_nrows++;
}
/* ---- meow_lv_item 映射(镜像 subpage.c sp_lv_bind) ---- */
static int sp_lv_count(void *ctx) { (void)ctx; return g_nrows; }
static void sp_lv_bind(int idx, meow_lv_item *out, void *ctx)
{
(void)ctx;
memset(out, 0, sizeof *out);
out->icon_kind = MEOW_LIST_ICON_NONE;
out->enabled = 1;
if (idx < 0 || idx >= g_nrows) { out->title = ""; return; }
const sub_row_t *r = &g_rows[idx];
switch (r->kind) {
case SUB_HDR: out->section = 1; out->title = r->label ? r->label : "";
out->right = MEOW_LIST_RIGHT_NONE; out->enabled = 0; break;
case SUB_ROW: out->title = r->label ? r->label : ""; out->subtitle = r->sub;
out->right = MEOW_LIST_RIGHT_NONE; break;
case SUB_SW: out->title = r->label ? r->label : ""; out->subtitle = r->sub;
out->right = MEOW_LIST_RIGHT_SWITCH; out->on = r->on; break;
case SUB_ACT: out->title = r->label ? r->label : ""; out->subtitle = r->sub;
out->right = MEOW_LIST_RIGHT_ARROW; break;
}
}
static meow_widget *tb = NULL, *sw = NULL, *lv = NULL;
static void rd_px(int x, int y, unsigned short *p)
{
unsigned char *bb = meow_ui_back_buf();
int line = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp();
if (!bb || x < 0 || y < 0) { *p = 0; return; }
if (bpp == 16) *p = *(unsigned short *)(bb + (size_t)y * line + (size_t)x * 2);
else *p = (unsigned short)(*(unsigned int *)(bb + (size_t)y * line + (size_t)x * 4) >> 16);
}
static unsigned short px565(int x, int y) { unsigned short p; rd_px(x, y, &p); return p; }
static int region_nonbg(int x, int y, int w, int h, uint32_t bg)
{
unsigned short bg565 = ((bg >> 8) & 0xF800) | ((bg >> 5) & 0x07E0) | ((bg >> 3) & 0x001F);
int cnt = 0;
for (int dy = 0; dy < h; dy++)
for (int dx = 0; dx < w; dx++)
if (px565(x + dx, y + dy) != bg565) cnt++;
return cnt;
}
static void mk_widgets(void)
{
meow_ui_activity_push(SUB_SENTINEL);
tb = meow_topappbar_create(0, SUB_TB_Y, XRES, SUB_TB_H, "WiFi");
meow_topappbar_set_back(tb, 1);
lv = meow_listview_create(0, SUB_CONT_TOP, XRES, LV_H, 0, NULL, NULL, MEOW_LIST_SEL_NONE);
meow_listview_set_vsource(lv, sp_lv_count, sp_lv_bind, NULL);
meow_listview_set_item_renderer(lv, 2, 0, 1);
meow_listview_set_item_h(lv, SUB_ITEM_H);
meow_listview_set_container(lv, 1); /* MD3 容器模式(M7.1a P0):圆角卡+空隙+去分隔线 */
sw = meow_switch_create(XRES - 16 - 56, SUB_TB_Y + (SUB_TB_H - 32) / 2, 1);
meow_ui_activity_pop();
}
static void draw_all(void)
{
const meow_theme *th = meow_theme_get();
meow_ui_fill_rect(0, 0, XRES, YRES, th->surface);
if (tb) meow_widget_draw(tb);
if (sw) meow_widget_draw(sw);
meow_ui_fill_rect(0, SUB_CONT_TOP, XRES, LV_H, th->surface);
if (lv) meow_widget_draw(lv);
}
int main(void)
{
setbuf(stdout, NULL);
setenv("MEOW_UI_DUMP", "/tmp/subpage.ppm", 1);
meow_ui_opts o = { XRES, YRES };
if (meow_ui_init(&o) != 0) { printf("init failed\n"); return 1; }
rows_fill();
mk_widgets();
/* ============ P1 listview 虚拟化渲染(section 段头 + 开关/箭头行) ============ */
printf("P1 listview virtualized render\n");
meow_ui_activity_push(SUB_SENTINEL);
const meow_theme *th = meow_theme_get();
draw_all();
/* MD3 容器模式:段头=surface 上的 primary 段标签(无满幅 band/卡,宿主文本为 no-op →
* 段头区应回 surface);普通行=surfaceContainer 圆角卡(region_nonbg 应见大量非 surface 像素)。 */
int hdrcnt = region_nonbg(2, SUB_CONT_TOP + 4, XRES - 4, SUB_ITEM_H - 8, th->surface);
int rowcnt = region_nonbg(2, SUB_CONT_TOP + SUB_ITEM_H + 4, XRES - 4, SUB_ITEM_H - 8, th->surface);
char nm[80];
snprintf(nm, sizeof nm, "section header no-band(label-on-surface) nb=%d", hdrcnt);
CHECK(hdrcnt == 0, "%s", nm);
snprintf(nm, sizeof nm, "row renders (card) cnt=%d", rowcnt);
CHECK(rowcnt > 40, "%s", nm);
/* ============ P2 listview 命中(行号 + right 操作区) ============ */
printf("P2 listview hit / hit_op\n");
int lvy = SUB_CONT_TOP;
CHECK(meow_listview_hit(lv, 10, lvy + SUB_ITEM_H / 2) == -1, "row0 (hdr) disabled -> -1");
CHECK(meow_listview_hit(lv, 10, lvy + SUB_ITEM_H + SUB_ITEM_H / 2) == 1, "row1 (已连接) hits idx1");
/* 行右 1/3 = 操作区:row1 是 SUB_ROW(right none) → hit_op 0 */
CHECK(meow_listview_hit_op(lv, XRES - 20, lvy + SUB_ITEM_H + SUB_ITEM_H / 2) == 0,
"row1 right-none -> hit_op 0");
/* row3 = SUB_ACT(MyWiFi) 右箭头;row5 = SUB_SW(WLAN) 右开关 → hit_op 1 */
CHECK(meow_listview_hit_op(lv, XRES - 20, lvy + 3 * SUB_ITEM_H + SUB_ITEM_H / 2) == 1,
"row3 (act arrow) right hits op");
CHECK(meow_listview_hit_op(lv, XRES - 20, lvy + 5 * SUB_ITEM_H + SUB_ITEM_H / 2) == 1,
"row5 (sw) right hits op");
/* ============ P3 topappbar 命中(返回箭头) ============ */
printf("P3 topappbar back hit\n");
CHECK(meow_topappbar_hit(tb, 10, SUB_TB_Y + SUB_TB_H / 2) == MEOW_TOPBAR_BACK,
"back arrow region -> MEOW_TOPBAR_BACK");
CHECK(meow_topappbar_hit(tb, XRES - 5, SUB_TB_Y + SUB_TB_H / 2) == -1,
"right region (no action) -> -1");
/* ============ P4 meow_scroll 包裹 listview + fling(惯性,ease-out,无 snap 回拉) ============ */
printf("P4 meow_scroll wrap listview + fling\n");
/* 40 行大列表(独立数据源),撑起滚动 */
meow_widget *scroll_lv = meow_listview_create(0, SUB_CONT_TOP, XRES, LV_H, 0, NULL, NULL,
MEOW_LIST_SEL_NONE);
meow_listview_set_vsource(scroll_lv, big_count, big_bind, NULL);
meow_listview_set_item_renderer(scroll_lv, 2, 0, 1);
meow_listview_set_item_h(scroll_lv, SUB_ITEM_H);
meow_listview_set_container(scroll_lv, 1); /* 与子页一致:MD3 容器模式 */
meow_scroll *s = meow_scroll_wrap(scroll_lv, 1); /* 1=W_LISTVIEW */
CHECK(s != NULL, "meow_scroll_wrap(listview)");
int maxs = meow_listview_max_scroll(scroll_lv);
CHECK(maxs > 0, "max_scroll > 0 (list taller than viewport) got %d", maxs);
/* 快速下划(正步进)建初速 → fling 前进(单调不减 + 不越界 + snap 到行边界) */
uint64_t t = meow_ui_time_ms();
for (int i = 0; i < 12; i++) meow_scroll_track(s, 30, t + (uint64_t)i * 16);
int start = meow_scroll_offset(s);
CHECK(start > 0, "dragged start>0 got %d", start);
meow_scroll_fling(s);
int prev = start, mono = 1, maxoff = start, guard = 0;
while (meow_scroll_active(s) && guard++ < 400) {
meow_anim_tick();
int cur = meow_scroll_offset(s);
if (maxoff < cur) maxoff = cur;
if (cur < prev) mono = 0;
prev = cur;
usleep(8000);
}
meow_anim_tick();
int end = meow_scroll_offset(s);
CHECK(mono == 1, "fling monotonic non-decreasing");
CHECK(end >= start, "fling advances end=%d start=%d", end, start);
CHECK(end <= maxs, "no overflow end=%d max=%d", end, maxs);
meow_scroll_free(s);
meow_widget_free(scroll_lv);
/* ============ P5 SDK 输入态 → listview 按压态像素变化 ============ */
printf("P5 meow_input_touch_set pressed state\n");
meow_ui_activity_push(SUB_SENTINEL);
draw_all();
int px_y = SUB_CONT_TOP + SUB_ITEM_H + SUB_ITEM_H / 2; /* row1 中心 */
unsigned short p0 = px565(10, px_y);
meow_input_touch_set(1, 10, px_y, 0);
draw_all();
unsigned short p1 = px565(10, px_y);
meow_input_touch_set(0, 0, 0, 0);
CHECK(p1 != p0, "pressed row -> pixel changes");
meow_ui_activity_pop();
/* ============ P6 主题四态(MD3/MIUIx × 明暗)渲染非背景 ============ */
printf("P6 theme 4-state render\n");
const int styles[2] = { MEOW_STYLE_MD3, MEOW_STYLE_MIUIX };
const int modes[2] = { MEOW_THEME_LIGHT, MEOW_THEME_DARK };
for (int si = 0; si < 2; si++) {
for (int mi = 0; mi < 2; mi++) {
meow_ui_set_style(styles[si]);
meow_theme_set_mode((meow_theme_mode)modes[mi]);
th = meow_theme_get();
draw_all();
int tbcnt = region_nonbg(0, SUB_TB_Y, XRES, SUB_TB_H, th->surface);
int lvcnt = region_nonbg(0, SUB_CONT_TOP, XRES, LV_H, th->surface);
snprintf(nm, sizeof nm, "style=%s mode=%s topbar+list render",
styles[si] ? "MIUIx" : "MD3", modes[mi] ? "dark" : "light");
CHECK(tbcnt > 0 && lvcnt > 40, "%s", nm);
}
}
meow_ui_activity_pop();
meow_ui_shutdown();
printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail);
return g_fail;
}