c936441c83
本轮把"自研代码落在门禁之外"的口子收掉,同时落地 LVGL 特性裁剪与 L3 保护定值/元件框架。 门禁(.pre-commit-config.yaml,26 个钩子) - 新增 scripts/check_lv_conf_sync.py + 钩子 lv-conf-sync:比对两份 lv_conf.h 的**生效值**(未定义则取 lv_conf_internal.h 兜底默认), 抓 MISMATCH / LVGL 不认的宏名 / 同文件重复定义 - 顶层 exclude 删掉 simulator/:其下 21 个 tracked 文件全是本项目自研 代码,生成物(build/、SDL2/、tests/ref/、shot*)已在 .gitignore; doc-links 的 exclude 同步删除 - lv-conf-sync 的 files: 覆盖两端配置 → 只改 simulator/lv_conf.h 也触发 (pre-commit 4.x 已移除 always: true,实测只告警并跳过,无兜底开关) - 新增 prot-set-unit-test / prot-elm-unit-test 两个主机侧单测钩子 - S11(2.3.5) 判定细化:禁的是"用路径找文件",尖括号系统头带子目录 (如 <sys/stat.h>)不再误报;全仓库复核仅 2 处项目代码判定变化 模拟器代码整改(纳入钩子后暴露,全部修代码而非豁免) - cppcheck 4 处:sim_syslog.c 判空分支内解引用 p_epoch(真实 bug)、 2 处 struct tm 未初始化、1 处指针可加 const - coding-standard 61 处:44 处函数左大括号并到右括号同行、8 处补函数 注释、3 处补文件头注释块、2 处 __ 前缀保护宏改 XXX_H、2 处 sprintf 改 snprintf(写入字节数与原来完全一致) LVGL 特性裁剪(F-1/F-2/F-3) - 两端 lv_conf.h 对齐同一特性子集:删 26 个 LVGL 9.3 不存在的选项名、 3 处重复定义,10 项靠默认值隐式生效的改为显式固化 - 实测对镜像体积中性;价值在防漂移,且模拟器 golden 回归跑的正是固件 实际编译的配置 L3 软件侧 - App/algo/prot_set.* Q10 定值模型(范围校验 / 整表 all-or-nothing / 校验和封存-自检-闭锁)+ prot_elm.* 通用"滞回+定时限"元件框架 7 实例 - test/host/prot_set、test/host/prot_elm 主机侧单测;uvprojx 挂载两处 文档同步 - pre-commit使用说明:§2.8 S11 口径、§2.15 lv-conf-sync 详解、新增 §5.5 复盘(教训:exclude 只写真正不该进钩子的路径) - AGENTS.md、UI_设计架构 §1.4 第 10 条、RAM 分析、审查报告刷新与销项 验证 - @build BUILD_OK 0/0(Code=194276 RO=15872 RW=876 ZI=139892) - 模拟器重建:菜单 8 状态像素逐字节一致(池 5368 B)、11 内容页黄金参考 全过(往返 5448 B)、remo sink 0/25600 位差异、--logtest/--cfgtest PASS - 全仓库 cppcheck / coding-standard / no-mojibake --all-files 全过 - check_doc_links --all 129/0;check_mem_numbers --check 内存数字一致 - 端到端:往 simulator/lv_conf.h 注入一行漂移 → 钩子 FAIL(exit 1)
139 lines
4.5 KiB
C
139 lines
4.5 KiB
C
/*
|
||
* 模块名称:Simulator Display Port (SDL2 模拟器端口)
|
||
* 模块功能:为 PC 模拟器提供 LVGL SDL2 显示与键盘输入端口
|
||
* 适用平台:Windows / Linux / macOS (SDL2)
|
||
* 说明:替代 MCU 端的 ui_port.c,用于 PC 上 UI 开发与调试
|
||
*/
|
||
#include "sim_port.h"
|
||
|
||
#include "lvgl.h"
|
||
|
||
/* SDL 驱动头文件(由 lv_conf.h 中 LV_SDL_INCLUDE_PATH 指定) */
|
||
#define SDL_MAIN_HANDLED
|
||
#include <SDL.h>
|
||
|
||
/* 键盘输入设备句柄(全局,供 main.c SDL 事件循环读取) */
|
||
static lv_indev_t *g_kbd_indev = NULL;
|
||
|
||
/*
|
||
* 函数功能:初始化 SDL2 显示窗口与键盘输入设备
|
||
* 入口参数:hor_res - 水平分辨率 int32_t
|
||
* ver_res - 垂直分辨率 int32_t
|
||
* 返回值:0 - 成功,-1 - 失败
|
||
* 限定条件:lv_init() 已调用
|
||
* 函数说明:1. 创建 SDL 窗口显示设备
|
||
* 2. 创建键盘输入设备及默认输入组
|
||
* 3. 不创建鼠标/触摸设备(模拟器仅用键盘导航)
|
||
*/
|
||
int sim_port_init(int32_t hor_res, int32_t ver_res) {
|
||
/* 创建默认输入组 */
|
||
lv_group_t *g = lv_group_create();
|
||
if (g == NULL) {
|
||
return -1;
|
||
}
|
||
lv_group_set_default(g);
|
||
|
||
/* 创建 SDL 窗口显示设备 */
|
||
lv_display_t *disp = lv_sdl_window_create(hor_res, ver_res);
|
||
if (disp == NULL) {
|
||
return -1;
|
||
}
|
||
lv_display_set_default(disp);
|
||
|
||
/* 初始化 mono 主题(1bpp 单色屏:白字黑底),并应用默认字体 */
|
||
#if LV_USE_THEME_MONO
|
||
lv_theme_t *th = lv_theme_mono_init(disp, true, LV_FONT_DEFAULT);
|
||
if (th != NULL) {
|
||
lv_display_set_theme(disp, th);
|
||
}
|
||
#endif
|
||
/* 创建键盘输入设备(方向键/确认/返回 → LVGL keypad) */
|
||
g_kbd_indev = lv_sdl_keyboard_create();
|
||
if (g_kbd_indev == NULL) {
|
||
return -1;
|
||
}
|
||
lv_indev_set_group(g_kbd_indev, lv_group_get_default());
|
||
lv_indev_set_display(g_kbd_indev, disp);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获取 SDL 显示设备句柄
|
||
* 入口参数:无
|
||
* 返回值:显示设备指针 lv_display_t * 可能为 NULL
|
||
* 限定条件:sim_port_init() 已成功调用
|
||
*/
|
||
void *sim_port_get_display(void) {
|
||
return (void *)lv_display_get_default();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读取当前屏幕内容并转换为 1bpp 单色位图(MSB-first)
|
||
* 入口参数:buf - 输出缓冲(调用方分配,≥ 宽*高/8 字节)
|
||
* buf_size - buf 容量(字节)
|
||
* 返回值:0 - 成功,-1 - 失败
|
||
* 限定条件:sim_port_init() 已成功;须在 SDL 所属线程(主循环线程)调用
|
||
* 函数说明:1. lv_sdl_window_get_renderer 取 SDL 渲染器
|
||
* 2. SDL_RenderReadPixels 读回屏幕实际呈现的 ARGB8888 像素
|
||
* 3. 按红通道阈值 128 判白(=1/墨)/黑(=0),逐行打包为 1bpp MSB-first
|
||
* 窗口模式为 I1+PARTIAL,LVGL 显示缓冲系分块且经 I1→ARGB8888
|
||
* 转换,直接读 get_buf_active 不可靠;读回屏幕呈现像素最稳,
|
||
* 且完全避开快照离屏重渲染(SDL 驱动下会卡派发循环)
|
||
*/
|
||
int sim_port_get_screen_mono(uint8_t *buf, uint32_t buf_size) {
|
||
if (buf == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
lv_display_t *disp = lv_display_get_default();
|
||
if (disp == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
int32_t w = lv_display_get_horizontal_resolution(disp);
|
||
int32_t h = lv_display_get_vertical_resolution(disp);
|
||
if (w <= 0 || h <= 0) {
|
||
return -1;
|
||
}
|
||
|
||
uint32_t stride = (uint32_t)(w + 7) / 8u;
|
||
uint32_t need = stride * (uint32_t)h;
|
||
if (buf_size < need) {
|
||
return -1;
|
||
}
|
||
|
||
SDL_Renderer *renderer = (SDL_Renderer *)lv_sdl_window_get_renderer(disp);
|
||
if (renderer == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
/* 读回屏幕实际呈现的像素(ARGB8888,4 字节/像素) */
|
||
uint32_t pitch = (uint32_t)w * 4u;
|
||
uint8_t *px = (uint8_t *)SDL_malloc((size_t)w * (size_t)h * 4u);
|
||
if (px == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
int rc = SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_ARGB8888, px, (int)pitch);
|
||
if (rc != 0) {
|
||
SDL_free(px);
|
||
return -1;
|
||
}
|
||
|
||
/* 先清零(黑像素=0),ARGB8888 内存序 [B,G,R,A],红通道在偏移 2 */
|
||
lv_memset(buf, 0, need);
|
||
for (int32_t y = 0; y < h; y++) {
|
||
const uint8_t *row = px + (size_t)y * (size_t)pitch;
|
||
uint8_t *out = buf + (size_t)y * (size_t)stride;
|
||
for (int32_t x = 0; x < w; x++) {
|
||
if (row[(size_t)x * 4u + 2u] >= 128u) {
|
||
out[(uint32_t)x >> 3] |= (uint8_t)(0x80u >> (x & 7));
|
||
}
|
||
}
|
||
}
|
||
|
||
SDL_free(px);
|
||
return 0;
|
||
}
|