Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/test_settings_widgets.c
T
AskaEth 3d70a3dd2c 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 推卡自刷验收全绿
2026-09-03 12:16:01 +08:00

401 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;
if (r->ctrl == SC_HEADER) {
meow_ui_fill_rect(x, y, w, SET_ITEM_H, mt->surfaceContainerHigh);
int ty = y + (SET_ITEM_H - thh) / 2;
if (ty >= 0) meow_ui_text(x + 16, ty, r->label, mt->font_body, mt->primary);
meow_ui_fill_rect(x, y + SET_ITEM_H - 1, w, 1, mt->outlineVariant);
return;
}
if (sel) meow_ui_fill_rect(x, y, w, SET_ITEM_H, mt->primaryContainer);
else if (press) meow_ui_fill_rect_alpha(x, y, w, SET_ITEM_H, mt->primary, 26);
int ly = y + (SET_ITEM_H - thh) / 2;
switch (r->ctrl) {
case SC_SWITCH: {
meow_ui_text(x + 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, x + w - 16 - 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(x + 16, ly, r->label, mt->font_body, textcol);
char vb[32]; s_fmt_value(r, vb, sizeof vb);
meow_ui_text(x + w - 16 - meow_ui_text_width(vb, mt->font_body), ly, vb, mt->font_body, subcol);
break;
}
case SC_LIST: {
meow_ui_text(x + 16, ly, r->label, mt->font_body, textcol);
meow_ui_text(x + w - 16 - meow_ui_text_width("已连接", mt->font_body), ly, "已连接", mt->font_body, subcol);
break;
}
default: { /* SC_ENUM / SC_PRESET */
meow_ui_text(x + 16, ly, r->label, mt->font_body, textcol);
char vb[40]; s_display_value(r, vb, sizeof vb);
int vx = x + w - 16 - meow_ui_text_width(vb, mt->font_body);
meow_ui_text(vx, ly, vb, mt->font_body, subcol);
break;
}
}
meow_ui_fill_rect(x, y + SET_ITEM_H - 1, w, 1, mt->outlineVariant);
if (state & MEOW_LISTVIEW_ST_DISABLED) meow_ui_fill_rect_alpha(x, y, w, SET_ITEM_H, 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->surfaceVariant);
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();
/* 段头行整体填 surfaceContainerHigh(覆盖列表底 surfaceVariant)→ 对列表底背景即非背景;
* 注意宿主无 mfont 时 CJK 标题回退点阵不显形,故以「段头底 vs 列表底」判渲染而非文字像素。 */
int hdrcnt = region_nonbg(2, SET_LIST_Y + 4, XRES - 4, SET_ITEM_H - 8, th->surfaceVariant);
int rowcnt = region_nonbg(2, SET_LIST_Y + SET_ITEM_H + 4, XRES - 4, SET_ITEM_H - 8, th->surfaceVariant);
CHECK(hdrcnt > 0, "section header band renders (hdr=%d)", hdrcnt);
CHECK(rowcnt > 40, "row band renders (title/value/switch) cnt=%d", rowcnt);
/* P1b:开关行(ROWS[2]=最高刷新率 right 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->surfaceVariant);
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;
}