Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/test_settings_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

407 lines
19 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_settings_widgets.c — M7.2 设置页主列表迁 SDK 控件(宿主验证)。
*
* 复现 settings_page.c M7.2 SDK 路径所建控件集(meow_listview 虚拟化 vsource + section 段头 +
* set_item_draw 自绘 + 行内真 meow_switch + 拖拽同布局 meow_slider),验证:
* P1 listview 虚拟化渲染:section 段头 + 开关/枚举/滑条/幂等行非背景像素;
* P2 meow_listview 命中:行号(含滚动换算 + 段头行 enabled=1 可命中);
* P3 开关写回:行 tap → meow_settings_set_switch(key,!on) → settings.conf 键正确(读改写单键);
* P4 滑条写回:track 映射 x→值 → meow_settings_set_int(key,v)(含息屏时间档位索引→秒)→ 键正确;
* P5 枚举写回:enum 选项 → meow_settings_set_enum(key,idx) → settings.conf 键值正确。
* 写回全部走 meow_settings_set_*(单键读改写,勿整文件覆盖——音量存盘教训)+ meow_settings_save。
*
* 几何/控件创建与 settings_page.c「M7.2 SDK 路径」镜像(owner=哨兵 0x53,listview 用 set_vsource
* + set_item_draw + set_item_h 68)。SMENU 行表取代表性子集(键/类型/min/max 与真表一致)。
*
* 编译(在 patch_v2/meow_launcherd 下):
* gcc -Wall -Wextra -o test_settings_widgets test_settings_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 \
* -DMEOW_SETTINGS_FILE='"/tmp/meow_set_test/settings.conf"' -pthread -lm
* 运行:./test_settings_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>
#include <sys/stat.h>
#include <sys/types.h>
/* 覆盖存储路径到临时目录(需父目录存在;test 内初始化时 mkdir)。
* 注意:须在 include "meow_settings.c" 前定义(SDK meow_ui_sys/theme 自带默认,勿用 -D 以免重定义)。 */
#define MEOW_SETTINGS_FILE "/tmp/meow_set_test/settings.conf"
#include "meow_settings.c"
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)
/* ---- 与 settings_page.c / meow_launcher_internal.h 同源几何(镜像) ---- */
#define XRES 480
#define YRES 720
#define SET_CARD_X 8
#define SET_CARD_W (XRES - 16)
#define SET_CONTENT_W (SET_CARD_W - 24)
#define S_SLIDER_X (SET_CARD_X + 16) /* 24 */
#define S_SLIDER_W SET_CONTENT_W /* 440 */
#define SET_LIST_Y 100
#define SET_FOOT_H 40
#define SET_ITEM_H 68
#define SET_SLIDER_YOFF 32
#define SET_TRACK_CY 52
#define SET_SENTINEL 0x53
#define LV_H (YRES - SET_LIST_Y - SET_FOOT_H) /* 580 */
/* 息屏时间档位(镜像 settings_page.c s_time_tiers) */
static const int s_time_tiers[] = { 0, 5, 10, 15, 20, 30, 45, 60, 90, 120, 180, 300, 600, 900, 1800, 3600, 7200 };
#define S_TIME_TIERS_N ((int)(sizeof s_time_tiers / sizeof s_time_tiers[0]))
/* ---- srow_t 行表镜像(键/类型/min/max 与 settings_page.c SMENU 代表行一致) ---- */
typedef enum { SC_HEADER, SC_SWITCH, SC_SLIDER, SC_ENUM, SC_PRESET, SC_INFO, SC_LIST } sctrl_t;
typedef struct { const char *label; const char *key; sctrl_t ctrl;
int min, max, step; } srow_t;
static const srow_t ROWS[] = {
{ "显示", NULL, SC_HEADER, 0, 0, 0 },
{ "亮度", "display.brightness", SC_SLIDER, 1, 100, 1 },
{ "最高刷新率", "display.refresh_always_max", SC_SWITCH, 0, 0, 0 },
{ "息屏模式", "display.screen_off_mode", SC_ENUM, 0, 0, 0 },
{ "息屏时间", "display.screen_off_timeout", SC_SLIDER, 0, S_TIME_TIERS_N - 1, 1 },
{ "UI风格", "ui.style", SC_ENUM, 0, 0, 0 },
{ "滑动方向", "input.swipe_invert", SC_SWITCH, 0, 0, 0 },
{ "网络", NULL, SC_HEADER, 0, 0, 0 },
{ "WiFi", NULL, SC_LIST, 0, 0, 0 },
};
#define NROWS ((int)(sizeof ROWS / sizeof ROWS[0]))
static meow_widget *lv = NULL;
/* ---- 值格式化/映射(镜像 settings_page.c s_fmt_value/s_time_tier_idx/s_time_tier_val) ---- */
static int s_time_tier_idx(int sec)
{
if (sec <= s_time_tiers[0]) return 0;
for (int i = 1; i < S_TIME_TIERS_N; i++) {
if (sec == s_time_tiers[i]) return i;
if (sec < s_time_tiers[i]) {
int lo = s_time_tiers[i-1], hi = s_time_tiers[i];
return (sec - lo < hi - sec) ? i - 1 : i;
}
}
return S_TIME_TIERS_N - 1;
}
static int s_time_tier_val(int idx)
{
if (idx < 0) idx = 0;
if (idx >= S_TIME_TIERS_N) idx = S_TIME_TIERS_N - 1;
return s_time_tiers[idx];
}
static void s_fmt_time(int sec, char *buf, int len)
{
if (sec <= 0) { snprintf(buf, len, "常亮"); return; }
if (sec < 60) { snprintf(buf, len, "%d 秒", sec); return; }
if (sec < 3600) { snprintf(buf, len, "%d 分", sec / 60); return; }
snprintf(buf, len, "%d 时", sec / 3600);
}
static void s_fmt_value(const srow_t *r, char *buf, int len)
{
int v = 0;
if (meow_settings_get_int(r->key, &v) != 0) { snprintf(buf, len, "-"); return; }
if (!strcmp(r->key, "display.screen_off_timeout")) { s_fmt_time(v, buf, len); return; }
snprintf(buf, len, "%d", v);
}
static void s_display_value(const srow_t *r, char *buf, int len)
{
int idx = 0; meow_settings_get_enum(r->key, &idx);
if (meow_settings_enum_label(r->key, idx, buf, len) != 0) snprintf(buf, len, "-");
}
static int s_slider_val_from_x(const srow_t *r, int px)
{
int range = r->max - r->min;
int v = r->min + (range ? (px - S_SLIDER_X) * range / S_SLIDER_W : 0);
if (v < r->min) v = r->min;
if (v > r->max) v = r->max;
return v;
}
/* 滑条落值(含息屏时间 挡位→秒 换算;镜像 s_slider_set_from_track) */
static void s_slider_set(const srow_t *r, int sl_v, int save)
{
(void)save;
if (!strcmp(r->key, "display.screen_off_timeout")) sl_v = s_time_tier_val(sl_v);
meow_settings_set_int(r->key, sl_v);
}
/* 开关 tap:读改写单键(镜像 run_settings_page SC_SWITCH 分支) */
static void s_switch_toggle(const srow_t *r)
{
int on = 0; meow_settings_get_switch(r->key, &on);
meow_settings_set_switch(r->key, !on);
}
/* ---- listview 虚拟化数据源(镜像 settings_page.c set_lv_bind) ---- */
static int lv_count(void *ctx) { (void)ctx; return NROWS; }
static void 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 >= NROWS) { out->title = ""; return; }
const srow_t *r = &ROWS[idx];
out->title = r->label ? r->label : "";
out->section = (r->ctrl == SC_HEADER) ? 1 : 0;
if (r->ctrl == SC_SWITCH) {
int on = 0; meow_settings_get_switch(r->key, &on);
out->right = MEOW_LIST_RIGHT_SWITCH; out->on = on;
}
}
/* 行内开关池(镜像 settings_page.c g_set_sw pool) */
#define SET_SWITCH_POOL 12
static meow_widget *g_set_sw[SET_SWITCH_POOL];
static int g_set_sw_n = 0, g_set_sw_use = 0;
static meow_widget *set_sw_take(void)
{
if (g_set_sw_use < g_set_sw_n) return g_set_sw[g_set_sw_use++];
if (g_set_sw_n >= SET_SWITCH_POOL) return NULL;
meow_widget *w = meow_switch_create(0, 0, 0);
if (w) { g_set_sw[g_set_sw_n++] = w; return g_set_sw[g_set_sw_use++]; }
return NULL;
}
/* ---- 整行自绘(镜像 settings_page.c set_lv_draw) ---- */
static void lv_draw(int idx, int x, int y, int w, int h, int state, void *ctx)
{
(void)ctx; (void)h;
const meow_theme *mt = meow_theme_get();
if (idx < 0) return;
const srow_t *r = &ROWS[idx];
int sel = (state & MEOW_LISTVIEW_ST_SELECTED);
int press = (state & MEOW_LISTVIEW_ST_PRESSED);
int thh = meow_ui_text_height(mt->font_body);
uint32_t textcol = sel ? mt->onPrimaryContainer : mt->onSurface;
uint32_t subcol = sel ? mt->onPrimaryContainer : mt->onSurfaceVariant;
/* MD3 容器化(P0):行=surface 上 surfaceContainer 圆角卡;段头=surface 上的 primary 段标签(无卡
* 无分隔线);与 settings_page.c set_lv_draw 同构造。 */
int cx = x + SET_CARD_X;
int cw = w - 2 * SET_CARD_X;
int cy = y + 8 / 2;
int ch = SET_ITEM_H - 8;
if (r->ctrl == SC_HEADER) {
int ty = y + (SET_ITEM_H - thh) / 2;
if (ty >= 0) meow_ui_text(cx + 16, ty, r->label, mt->font_body, mt->primary);
return;
}
meow_gfx_fill_round(cx, cy, cw, ch, 12, mt->surfaceContainer);
if (sel) meow_gfx_fill_round(cx, cy, cw, ch, 12, mt->primaryContainer);
else if (press) meow_gfx_fill_round_alpha(cx, cy, cw, ch, 12, mt->primary, 26);
int ly = y + (SET_ITEM_H - thh) / 2;
int rgt = cx + cw - 16;
switch (r->ctrl) {
case SC_SWITCH: {
meow_ui_text(cx + 16, ly, r->label, mt->font_body, textcol);
meow_widget *sw = set_sw_take();
if (sw) {
int on = 0; meow_settings_get_switch(r->key, &on);
meow_widget_set_rect(sw, rgt - 56, y + (SET_ITEM_H - 32) / 2, 56, 32);
meow_switch_set(sw, on);
meow_widget_draw(sw);
}
break;
}
case SC_SLIDER: {
/* 宿主测试不引入 launcher s_draw_slider/s_ui_theme:只验证行文字渲染 + 命中,track 视觉由
* 真机 s_draw_slider 呈现(settings_page.c set_lv_draw 已复用)。 */
meow_ui_text(cx + 16, ly, r->label, mt->font_body, textcol);
char vb[32]; s_fmt_value(r, vb, sizeof vb);
meow_ui_text(rgt - meow_ui_text_width(vb, mt->font_body), ly, vb, mt->font_body, subcol);
break;
}
case SC_LIST: {
meow_ui_text(cx + 16, ly, r->label, mt->font_body, textcol);
meow_ui_text(rgt - meow_ui_text_width("已连接", mt->font_body), ly, "已连接", mt->font_body, subcol);
break;
}
default: { /* SC_ENUM / SC_PRESET */
meow_ui_text(cx + 16, ly, r->label, mt->font_body, textcol);
char vb[40]; s_display_value(r, vb, sizeof vb);
int vx = rgt - meow_ui_text_width(vb, mt->font_body);
meow_ui_text(vx, ly, vb, mt->font_body, subcol);
break;
}
}
if (state & MEOW_LISTVIEW_ST_DISABLED) meow_gfx_fill_round_alpha(cx, cy, cw, ch, 12, mt->onSurface, 90);
}
static void mk_widgets(void)
{
meow_ui_activity_push(SET_SENTINEL);
lv = meow_listview_create(0, SET_LIST_Y, XRES, LV_H, 0, NULL, NULL, MEOW_LIST_SEL_SINGLE);
meow_listview_set_vsource(lv, lv_count, lv_bind, NULL);
meow_listview_set_item_draw(lv, lv_draw, NULL);
meow_listview_set_item_h(lv, SET_ITEM_H);
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);
meow_ui_fill_rect(0, SET_LIST_Y, XRES, LV_H, th->surface); /* P0:内容底 surface(卡在其上抬升) */
if (lv) { g_set_sw_use = 0; meow_widget_draw(lv); }
}
/* ---- 像素探测 ---- */
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;
}
/* ---- meow_settings 写回验证辅助:清盘后 save,再 grep conf(格式 `key = value`,去空格比较) ---- */
static int conf_has(const char *key, const char *val)
{
char line[256], expect[256];
FILE *f = fopen(MEOW_SETTINGS_FILE, "r");
if (!f) return 0;
snprintf(expect, sizeof expect, "%s=%s", key, val);
int found = 0;
while (fgets(line, sizeof line, f)) {
char stripped[256]; int k = 0;
for (int i = 0; line[i] && k < 255; i++)
if (line[i] != ' ' && line[i] != '\n' && line[i] != '\r') stripped[k++] = line[i];
stripped[k] = 0;
if (!strcmp(stripped, expect)) { found = 1; break; }
}
fclose(f);
return found;
}
int main(void)
{
setbuf(stdout, NULL);
mkdir("/tmp/meow_set_test", 0777);
setenv("MEOW_UI_DUMP", "/tmp/settings.ppm", 1);
if (meow_settings_init() != 0) printf("settings init (defaults; file may be absent)\n");
meow_ui_opts o = { XRES, YRES };
if (meow_ui_init(&o) != 0) { printf("init failed\n"); return 1; }
mk_widgets();
/* ============ P1 listview 虚拟化渲染(section 段头 + 各型行非背景) ============ */
printf("P1 listview virtualized render (settings rows)\n");
meow_ui_activity_push(SET_SENTINEL);
const meow_theme *th = meow_theme_get();
draw_all();
/* MD3 容器化(P0):段头=surface 上的 primary 段标签(无满幅 band/卡,宿主文本为 no-op →
* 段头区应回 surface);普通行=surfaceContainer 圆角卡(region_nonbg 应见大量非 surface 像素)。
* 注意宿主无 mfont 时 CJK 标题回退点阵不显形,故以「卡/段头 vs 列表底」判渲染而非文字像素。 */
int hdrcnt = region_nonbg(2, SET_LIST_Y + 4, XRES - 4, SET_ITEM_H - 8, th->surface);
int rowcnt = region_nonbg(2, SET_LIST_Y + SET_ITEM_H + 4, XRES - 4, SET_ITEM_H - 8, th->surface);
CHECK(hdrcnt == 0, "section header no-band(label-on-surface) nb=%d", hdrcnt);
CHECK(rowcnt > 40, "row card renders (title/value/switch) cnt=%d", rowcnt);
/* P1b:开关行(ROWS[2]=最高刷新率 right switch)中心右缘区非背景(卡内 switch) */
{
int swy = SET_LIST_Y + 2 * SET_ITEM_H + SET_ITEM_H / 2;
int swc = region_nonbg(XRES - 16 - 56, swy - 4, 56, 8, th->surface);
CHECK(swc > 0, "switch row right tail (meow_switch) renders swc=%d", swc);
}
/* ============ P2 meow_listview 命中(行号 + 段头可命中) ============ */
printf("P2 meow_listview hit\n");
CHECK(meow_listview_hit(lv, 10, SET_LIST_Y + 0 * SET_ITEM_H + SET_ITEM_H / 2) == 0,
"row0 (header 显示) hit idx0 (enabled=1)");
CHECK(meow_listview_hit(lv, 10, SET_LIST_Y + 1 * SET_ITEM_H + SET_ITEM_H / 2) == 1,
"row1 (亮度 slider) hit idx1");
CHECK(meow_listview_hit(lv, 10, SET_LIST_Y + 3 * SET_ITEM_H + SET_ITEM_H / 2) == 3,
"row3 (息屏模式 enum) hit idx3");
CHECK(meow_listview_hit(lv, 10, SET_LIST_Y + 8 * SET_ITEM_H + SET_ITEM_H / 2) == 8,
"row8 (WiFi list) hit idx8");
CHECK(meow_listview_hit(lv, 10, SET_LIST_Y - 2) == -1, "above list -> -1");
/* 滚动后命中(listview set_scroll 换算):scroll=20(<9*68-视口=32)时点 row1 中心 → idx1 */
meow_listview_set_scroll(lv, 20);
CHECK(meow_listview_hit(lv, 10, SET_LIST_Y + SET_ITEM_H + SET_ITEM_H / 2) == 1,
"scrolled +20px: row1 center hits idx1");
CHECK(meow_listview_max_scroll(lv) > 0, "max_scroll > 0 (list taller than viewport) got %d",
meow_listview_max_scroll(lv));
meow_listview_set_scroll(lv, 0);
/* ============ P3 开关写回(读改写单键 → settings.conf) ============ */
printf("P3 switch write-back to settings.conf\n");
meow_settings_set_switch("input.swipe_invert", 0); /* reset */
meow_settings_save();
int before_on = 0; meow_settings_get_switch("input.swipe_invert", &before_on);
{
/* 模拟 tap ROWS[6]=滑动方向 */
const srow_t *r = &ROWS[6];
s_switch_toggle(r);
meow_settings_save();
}
int after_on = 0; meow_settings_get_switch("input.swipe_invert", &after_on);
CHECK(before_on == 0 && after_on == 1,
"input.swipe_invert toggled 0->1 (read-modify-write single key)");
CHECK(conf_has("input.swipe_invert", "1"), "settings.conf has input.swipe_invert=1");
CHECK(!conf_has("display.refresh_always_max", "1"), "untouched sibling key unchanged in conf");
/* ============ P4 滑条写回(x→值 → settings.conf;含息屏时间 挡位→秒) ============ */
printf("P4 slider write-back to settings.conf\n");
meow_settings_set_int("display.brightness", 50); meow_settings_save();
{
const srow_t *r = &ROWS[1]; /* 亮度 1..100 */
int v = s_slider_val_from_x(r, S_SLIDER_X + S_SLIDER_W / 2); /* 中点 ≈ 50 */
s_slider_set(r, v, 1);
meow_settings_save();
int got = 0; meow_settings_get_int("display.brightness", &got);
CHECK(got == v, "display.brightness set via track -> %d (expect %d)", got, v);
char kv[64]; snprintf(kv, sizeof kv, "%d", v);
CHECK(conf_has("display.brightness", kv), "settings.conf has display.brightness=%d", v);
}
{ /* 息屏时间:秒→挡位索引(显示用)与 挡位索引→秒(落盘用)双向映射 */
const srow_t *r = &ROWS[4]; /* 0..S_TIME_TIERS_N-1 */
int idx = 5; /* 第 5 挡 = 30 秒 */
int sec = s_time_tier_val(idx);
CHECK(s_time_tier_idx(30) == idx, "screen_off_timeout sec 30 -> tier idx %d", idx);
s_slider_set(r, idx, 1);
meow_settings_save();
int got = 0; meow_settings_get_int("display.screen_off_timeout", &got);
CHECK(sec == 30 && got == 30, "screen_off_timeout tier idx5 -> 30s (got %d)", got);
CHECK(conf_has("display.screen_off_timeout", "30"), "settings.conf has display.screen_off_timeout=30");
}
/* ============ P5 枚举写回(enum 选项 → settings.conf 键值正确) ============ */
printf("P5 enum write-back to settings.conf\n");
{
const char *names[8]; int kn = 0;
meow_settings_enum_names("display.screen_off_mode", names, &kn);
CHECK(kn > 0, "display.screen_off_mode enum names count=%d", kn);
int pick = 1; /* "black"(黑屏) */
meow_settings_set_enum("display.screen_off_mode", pick);
meow_settings_save();
int got = 0; meow_settings_get_enum("display.screen_off_mode", &got);
CHECK(got == pick, "display.screen_off_mode enum -> %d (expect %d)", got, pick);
CHECK(conf_has("display.screen_off_mode", "black"), "settings.conf has display.screen_off_mode=black");
}
meow_ui_activity_pop();
meow_ui_shutdown();
printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail);
return g_fail;
}