fix(bootmenu): M23 评审 r2 — 电源键锁存取代「3s 抑制」,缺键视同不可信

P2-2(r2):删除「电源键按下后 3s 内不补」的定时抑制 —— 它到期就会把用户刚熄的屏点亮
(窗内可重复),且该行为被单测固定。改为**锁存**:扫描窗内一看到电源键按下边沿就置
scan_pwr_seen=1,本次窗口内此后一律不补(why="power-key-latched"),窗口结束才清零。
理由:Bug 4 保护的是「无人值守时媒体扫描导致黑屏几分钟」;用户已按键介入,需要亮屏时
自己再按一次即可。锁存同时覆盖「播放器只写 brightness=0 不改 bl_power」与「HibyOS 自动
超时熄屏」两种残余。

bl_power 门保留但降级为纯保险,注释改成诚实表述:据 patch_v2/meow_launcherd/backlight.c:90-95
实测,本机播放器熄屏(brightness 0 + FBIOBLANK 4)后 bl_power **仍为 0**,该门在播放器熄屏
场景不生效;真正的门是电源键锁存。(上一轮注释里的「播放器灭屏会置 bl_power>0」是反向推断,
已删。)

顺手关掉评审指出的竞态:亮度保底判定块从主循环前半(keypoll 之前)移到电源键边沿更新之后,
电源按下与播放器写 0 落在同一 poll 间隙时不会再出现一轮「没看到电源键就补亮」。

非阻塞 1(采纳):settings 文件存在但缺 display.brightness 键 → 返回 -1(视同不可信),
不再走 launcher 设置表的 101→100;否则扫描窗待机补 100 比老 bug 的 60 更亮。

单测(52 → 56 条):
- 删「power key expired -> floors」(把 bug 固定成期望的那条),换「latch 在窗口内永不过期」
  「下一轮窗口(锁存清零)恢复正常补」「latched 时面板暗也不写、why=power-key-latched」;
- 「missing key -> 100」改「missing key -> -1 (untrusted)」,并补 e2e「缺键 → saved=10 不是 100」;
- 两条名字去 degraded:`floor: saved 80 (from settings) floors at 80`、
  `hotkey: blank -> saved 60 (settings value)`(降级路径已封顶 10,80/60 只可能来自 settings)。

只交源码:不含二进制、不改 fw_content.conf、不动 build_bootmenu.sh。
This commit is contained in:
AskaEth committed 2026-11-11 18:42:41 +08:00
1 parent 8cd6e21cad
commit a9281c2d64
2 files changed
+133 -93

No files matched your search

+79 -62
View File
@@ -44,9 +44,10 @@
* [display.brightness_min, max_brightness](为什么不会重新引入 M2.9 的开机闪屏见
* bm_bri_pick_saved 注释)。settings 不可读的降级路径**不采信面板瞬时值**,取
* min(面板, BM_BRI_SAFE_LOW=10)(与 watchdog 的 WD_BRI_SAFE_LOW 对齐)。
* 扫描窗保底恢复 pre-M23 的「面板暗 + 未下电」触发(不依赖 /proc 的 D 态,避免媒体扫描
* 在子线程时 Bug 4 防护失效),另用两条可观测信号避让用户熄屏:bl_power>0、电源键按下
* 后 3s 内不补;窗口结束彻底停手;日志同原因 ≥2s 一条。
* 扫描窗保底恢复 pre-M23 的「面板暗」触发(不依赖 /proc 的 D 态,避免媒体扫描在子线程时
* Bug 4 防护失效);不抢用户熄屏靠**电源键锁存**(窗内按过电源键则本次窗口内不再补),
* bl_power>0 仅作纯保险(实测本机播放器熄屏后 bl_power 仍为 0);窗口结束彻底停手;
* 日志同原因 ≥2s 一条。
* 决策全部抽成纯函数(bm_bri_pick_saved / bm_bri_floor_decide / bm_bri_hotkey_saved),
* 宿主单测见 test_bm_fw.c。亮度判定改为 bm_log 落卡(stderr 原行保留)。
* 本单一律**只交源码**:二进制由 M43/M51 统一重编并回写 fw_content.conf 钉值。
@@ -895,7 +896,12 @@ static int read_max_brightness(void)
* 值;settings 文件不可读 → 返回 -1,调用方**不动**(退化为现状,保安全——选单阶段只写 brightness、
* 绝不碰 FBIOBLANK/blank,§18.24 纪律)。
* M23:fopen 失败做 BM_SETTINGS_TRIES 次短重试,覆盖 /usr/data 刚挂载 / 原子替换的瞬时窗口
* (与 M20 的 wd_settings_brightness 同策略);只重试 open(rename 原子,已打开的 fd 内容完整)。 */
* (与 M20 的 wd_settings_brightness 同策略);只重试 open(rename 原子,已打开的 fd 内容完整)。
* M23 评审 r2:文件存在但**缺 display.brightness 键**同样返回 -1(不可信),不再按 launcher
* 设置表的默认 101→clamp 100。理由:bootmenu 的用途是「永不写高于用户值」,把「文件里没有
* 这个键」当成 100 会在扫描窗待机时补到 100(比老 bug 的 60 更亮)。launcher/watchdog 的 100
* 默认是「设置表缺省值」语义,与本进程的「授权写亮度」语义不同,这里有意更严格。
* (注:键存在但值非数字 → atoi=0 → 夹到 brightness_min,与旧行为一致。) */
#define BM_SETTINGS_TRIES 3
#define BM_SETTINGS_GAP_MS 10
@@ -908,7 +914,7 @@ static int bootmenu_settings_brightness(int *min_out)
if (i + 1 < BM_SETTINGS_TRIES) usleep(BM_SETTINGS_GAP_MS * 1000);
}
if (!f) return -1;
int b = 101; /* display.brightness 默认(与 launcher 设置表同值,键缺失时 clamp→100) */
int b = -1; /* -1 = 文件里没看到 display.brightness(视为不可信 → 返回 -1) */
int mn = 1; /* display.brightness_min 默认(极暗但可见;键缺失时保底) */
char buf[128];
while (fgets(buf, sizeof buf, f)) {
@@ -923,9 +929,10 @@ static int bootmenu_settings_brightness(int *min_out)
else if (!strcmp(k, "display.brightness")) { b = iv; }
}
fclose(f);
if (min_out) *min_out = mn;
if (b < 0) return -1;
if (b < mn) b = mn;
if (b > 100) b = 100;
if (min_out) *min_out = mn;
return b;
}
@@ -935,7 +942,6 @@ static int bootmenu_settings_brightness(int *min_out)
* 安全下限,而不是 M20 的「运行期快照 → cur>0 不写」那条链。 */
#define BM_BRI_MIN_DEFAULT 1 /* display.brightness_min 缺省(与 launcher/watchdog 同值:极暗但可见) */
#define BM_BRI_SAFE_LOW 10 /* 与 meow_watchdog 的 WD_BRI_SAFE_LOW 对齐(同一系统同一个安全下限) */
#define BM_BRI_PWR_HOLD_MS 3000 /* 电源键按下后 N ms 内不补亮度(用户主动熄屏信号,见 floor_decide) */
/* 把任意亮度读数夹进面板合法区间 [mn, max_brightness](M23:取代旧 `>60 → 60` 的硬钉)。
* mn < 1 时按 1(防黑屏:亮着总比黑屏好,但也不伪造「比用户下限更亮」);
@@ -979,23 +985,28 @@ static int bm_bri_pick_saved(int user, int cur, int mn, int maxb, const char **s
}
/* 扫描窗保底(Bug 4)决策:返回 >0 = 要写的亮度,0 = 不动;*why 回填原因供日志。
* 触发条件**回到 pre-M23 的形态**(P2-2 评审):窗口内 + 面板暗(bri<=0)+ 面板未下电
* (bl_power<=0)→ 补。**不依赖 /proc 的 D 态**:媒体扫描若在子线程,主线程是 S,D 态门会
* 让 Bug 4 的防护变成空操作(黑屏几分钟回归),那比亮度偏高严重得多。
* 避免「熄屏瞬间爆亮」改由两条**可观测**信号承担:
* ① bl_power>0 —— 播放器 FBIOBLANK 会置面板下电(bootmenu 确认框 unblank 时就把它写回 0,
* 说明本机确实有这个行为),用户主动熄屏走这条;
* ② 电源键按下后 BM_BRI_PWR_HOLD_MS 内不补 —— 10ms 轮询 evdev 就能看到,媒体扫描不会按电源键;
* 这是「播放器只写 brightness=0 但没碰 bl_power」时的第二道保险。
* 窗口结束 → *why="window-over",调用方彻底停手。 */
* 触发条件**回到 pre-M23 的形态**(评审 r1 P2-2):窗口内 + 面板暗(bri<=0)→ 补。
* **不依赖 /proc 的 D 态**:媒体扫描若在子线程,主线程是 S,D 态门会让 Bug 4 的防护变成
* 空操作(黑屏几分钟回归),那比亮度偏高严重得多。
*
* 不抢用户熄屏靠 **电源键锁存**(评审 r2 P2-2):扫描窗内一旦看到电源键按下边沿,
* 本次窗口内此后一律不补(*why="power-key-latched")。理由:Bug 4 保护的是「**无人值守**时
* 媒体扫描导致黑屏几分钟」;用户已经按键介入,需要亮屏时自己再按一次即可。锁存覆盖两种残余:
* 播放器只写 brightness=0 不改 bl_power,以及 HibyOS 自动超时熄屏。
*
* bl_power 门**保留但只是纯保险**:据 patch_v2/meow_launcherd/backlight.c:90-95 的实测注释,
* 本机播放器熄屏(brightness 0 + FBIOBLANK 4)后 **bl_power 仍为 0**,所以这道门在「播放器
* 熄屏」场景**不生效**;保留它只是为了其它可能置位的面板下电状态。真正的门是上面的电源键锁存。
*
* 窗口结束 → *why="window-over",调用方彻底停手(并把锁存清零,逻辑上等价于「下一轮窗口从零开始」)。 */
static int bm_bri_floor_decide(int saved, int cur, int bl_power,
long long win_left_ms, long long since_pwr_ms,
long long win_left_ms, int pwr_latched,
const char **why)
{
if (win_left_ms <= 0) { *why = "window-over"; return 0; }
if (cur > 0) { *why = "visible"; return 0; }
if (bl_power > 0) { *why = "panel-off"; return 0; }
if (since_pwr_ms >= 0 && since_pwr_ms < BM_BRI_PWR_HOLD_MS) { *why = "power-key"; return 0; }
if (pwr_latched) { *why = "power-key-latched"; return 0; }
*why = "scan-floor";
return saved > 0 ? saved : BM_BRI_SAFE_LOW;
}
@@ -1352,13 +1363,14 @@ static void bootmenu_supervisor(int player_pid)
/* Bug 4:HibyOS 开机媒体扫描窗内做亮度保底。播放器忙(媒体扫描)时其背光
* 待机会把亮度降到 0、面板 blank → 黑屏几分钟。**只写 brightness、绝不碰 FBIOBLANK**
* (不抢播放器的面板归属语义,参考 §18.30 面板归属)。扫描窗结束即放手。
* M23:补的值改为用户设置(不再 60);触发条件回到 pre-M23 的「面板暗 + 未下电」,
* 另加「电源键按下后 3s 内不补」(可观测信号,见 bm_bri_floor_decide)。 */
* M23:补的值改为用户设置(不再 60);触发回到 pre-M23 的「面板暗」,不抢用户熄屏靠
* 「电源键锁存」(见 bm_bri_floor_decide)。 */
unsigned long long scan_keep_until = now_ms() + SCAN_KEEP_MS;
unsigned long long last_bri_floor_at = 0; /* 亮度保底节流:≥400ms 才判定/写一次 */
const char *last_floor_why = "init"; /* 判定原因(日志只在跃迁或 ≥2s 时打) */
unsigned long long last_floor_log_at = 0; /* 上次 scan-window 日志时刻(同原因 ≥2s 一条) */
unsigned long long last_pwr_edge_at = 0; /* 最近一次电源键按下边沿(0=本次开机尚未按过) */
int scan_pwr_seen = 0; /* 扫描窗内见过电源键按下 → 本次窗口内不再补亮 */
struct keypoll kp_power, kp_keys;
if (kp_open(&kp_power, DEV_POWER) != 0) fprintf(stderr, "supervisor: WARN no power dev\n");
@@ -1414,45 +1426,10 @@ static void bootmenu_supervisor(int player_pid)
}
}
/* 2a) Bug 4 亮度保底(M23):扫描窗内、面板暗(bri<=0)且**面板未下电**(bl_power<=0)
* → 写回用户设置亮度(不再 60)。触发条件回到 pre-M23 形态,**不依赖 /proc 的 D 态**
* (P2-2:媒体扫描若在子线程,主线程是 S,D 态门会让 Bug 4 防护变成空操作 → 黑屏回归)。
* 避免「熄屏瞬间爆亮」由两条可观测信号承担:bl_power>0(播放器 FBIOBLANK)与
* 电源键按下后 3s 内不补(见 bm_bri_floor_decide)。
* (播放器扫描忙时无法响应唤醒,我们只把背光拉回,画面仍为播放器最后帧;不碰 blank。)
* 判定按 400ms 节流;日志同一原因 ≥2s 一条、跃迁也 ≥1s(防 `visible↔scan-floor`
* 每 400ms 交替刷屏;90s 窗口最坏 ~90 行)。 */
if (player_alive && scan_keep_until) {
unsigned long long now2 = now_ms();
if (now2 - last_bri_floor_at >= 400) {
long long win_left = (scan_keep_until > now2)
? (long long)(scan_keep_until - now2) : 0;
long long since_pwr = (last_pwr_edge_at > 0)
? (long long)(now2 - last_pwr_edge_at) : -1;
int bri = read_brightness();
int blp = read_file_int(BACKLIGHT_POWER);
const char *why = "?";
int floor = bm_bri_floor_decide(saved_brightness, bri, blp, win_left, since_pwr, &why);
last_bri_floor_at = now2;
if (floor > 0) {
write_brightness(floor);
/* 保留 M2 时代原有的 stderr 行(串口调试仍看它),另加一路 bm_log 落卡。 */
fprintf(stderr, "supervisor: scan-window brightness floor -> %d\n", floor);
}
/* 日志节流:同一原因 ≥2s 一条;原因跃迁也至少隔 1s(防 visible↔scan-floor
* 每 400ms 交替时刷屏)。最坏 ~90 行/90s 窗口。 */
unsigned long long gap = now2 - last_floor_log_at;
if ((strcmp(why, last_floor_why) != 0 && gap >= 1000) || gap >= 2000) {
last_floor_why = why;
last_floor_log_at = now2;
bm_log("supervisor: scan-window brightness %s -> %d (user=%d panel=%d bl_power=%d pwr_ago=%lldms left=%lldms)",
(floor > 0) ? "floor" : "skip", floor, bri_user, bri, blp, since_pwr, win_left);
}
/* 窗口结束 → 彻底放手:此后不再读背光/bl_power,绝不与播放器抢面板。 */
if (win_left <= 0) scan_keep_until = 0;
}
}
/* 2a) Bug 4 亮度保底(M23):**已移到主循环后半(按键采样之后)**,见下面
* 「2b) 亮度保底」—— 电源键边沿与播放器写 0 落在同一 poll 间隙时,先采键再判定
* 才不会出现一轮「未看到电源键就补亮」的竞态(评审 r2 非阻塞建议)。
*/
/* 2) EVIOCGKEY 采样(event0 电源 / event2 音量) */
int pwr = 0, vold = 0;
if (kp_power.ok) { kp_poll(&kp_power, NULL, NULL); pwr = kp_is_down(&kp_power, KEY_POWER); }
@@ -1467,13 +1444,53 @@ static void bootmenu_supervisor(int player_pid)
}
/* 3) 热键:电源按下边沿 arming,窗口内音量减按下边沿触发。
* 同一边沿也记到 last_pwr_edge_at:亮度保底据此避让「用户主动熄屏」(可观测信号,
* 不依赖 /proc 的 D 态;媒体扫描不会按电源键)。 */
if (pwr && !prev_pwr) { pwr_hold_armed = 1; pwr_down_at = t; last_pwr_edge_at = t; }
* 同一边沿也记到 last_pwr_edge_at,并**锁存**到本次扫描窗结束:亮度保底据此
* 不再与用户抢屏(用户已按键介入;需要亮屏时自己再按一次)。 */
if (pwr && !prev_pwr) {
pwr_hold_armed = 1; pwr_down_at = t; last_pwr_edge_at = t;
if (scan_keep_until) scan_pwr_seen = 1;
}
if (!pwr && prev_pwr) pwr_hold_armed = 0; /* 松开:disarm */
if (pwr_hold_armed && pwr && (t - pwr_down_at) > HK_PWRWIN_MAX)
pwr_hold_armed = 0; /* 超窗:disarm */
/* 2b) Bug 4 亮度保底(M23,放在按键采样之后以关掉「电源键与写 0 同间隙」竞态):
* 扫描窗内、面板暗(bri<=0)→ 写回用户设置亮度。触发不依赖 /proc 的 D 态;
* 不抢用户熄屏靠**电源键锁存**(why=power-key-latched);bl_power>0 只是纯保险
* (据 backlight.c:90-95 实测,本机播放器熄屏后 bl_power 仍为 0,这道门在该场景不生效)。
* 判定 400ms 节流;日志同一原因 ≥2s、跃迁 ≥1s(90s 窗口最坏 ~90 行)。 */
if (player_alive && scan_keep_until) {
unsigned long long now2 = now_ms();
if (now2 - last_bri_floor_at >= 400) {
long long win_left = (scan_keep_until > now2)
? (long long)(scan_keep_until - now2) : 0;
long long pwr_ago = (last_pwr_edge_at > 0)
? (long long)(now2 - last_pwr_edge_at) : -1;
int bri = read_brightness();
int blp = read_file_int(BACKLIGHT_POWER);
const char *why = "?";
int floor = bm_bri_floor_decide(saved_brightness, bri, blp, win_left,
scan_pwr_seen, &why);
last_bri_floor_at = now2;
if (floor > 0) {
write_brightness(floor);
/* 保留 M2 时代原有的 stderr 行(串口调试仍看它),另加一路 bm_log 落卡。 */
fprintf(stderr, "supervisor: scan-window brightness floor -> %d\n", floor);
}
unsigned long long gap = now2 - last_floor_log_at;
if ((strcmp(why, last_floor_why) != 0 && gap >= 1000) || gap >= 2000) {
last_floor_why = why;
last_floor_log_at = now2;
bm_log("supervisor: scan-window brightness %s -> %d (user=%d panel=%d bl_power=%d latched=%d pwr_ago=%lldms left=%lldms)",
(floor > 0) ? "floor" : "skip", floor, bri_user, bri, blp,
scan_pwr_seen, pwr_ago, win_left);
}
/* 窗口结束 → 彻底放手:此后不再读背光/bl_power;锁存清零(逻辑上等价于
* 「下一轮窗口从零开始」,当前实现每进程只有一轮窗口)。 */
if (win_left <= 0) { scan_keep_until = 0; scan_pwr_seen = 0; }
}
}
int hotkey_fired = 0;
if (pwr_hold_armed && pwr && vold && !prev_vold) {
long since = (long)(t - pwr_down_at);
+54 -31
View File
@@ -138,34 +138,41 @@ static void test_floor_decide(void)
const char *why = NULL;
int v;
/* 触发条件回到 pre-M23:面板暗 + 未下电就补(不依赖 /proc 的 D 态 = P2-2 硬要求) */
v = bm_bri_floor_decide(1, 0, 0, 5000, -1, &why); /* settings=1,播放器待机写 0 */
/* 触发条件回到 pre-M23:面板暗就补(不依赖 /proc 的 D 态 = 评审 r1 P2-2 硬要求) */
v = bm_bri_floor_decide(1, 0, 0, 5000, 0, &why); /* settings=1,播放器待机写 0 */
check("floor: writes user value 1 (not 60)", v == 1 && !strcmp(why, "scan-floor"), 1);
v = bm_bri_floor_decide(80, 0, 0, 5000, -1, &why); /* 降级 saved=80 照写 */
check("floor: degraded saved 80 floors at 80", v == 80 && !strcmp(why, "scan-floor"), 1);
v = bm_bri_floor_decide(0, 0, 0, 5000, -1, &why); /* saved 未知 -> 安全低亮 */
v = bm_bri_floor_decide(80, 0, 0, 5000, 0, &why); /* saved=80 只可能来自 settings */
check("floor: saved 80 (from settings) floors at 80", v == 80 && !strcmp(why, "scan-floor"), 1);
v = bm_bri_floor_decide(0, 0, 0, 5000, 0, &why); /* saved 未知 -> 安全低亮 */
check("floor: unknown saved -> safe-low", v == BM_BRI_SAFE_LOW && !strcmp(why, "scan-floor"), 1);
/* Bug 4 防退化回归用例(评审 P2-2):只要面板暗+未下电就必须能补,不能有任何
* 「播放器看起来不忙」的前提 —— 旧 busy 门会让这两条变成 no-write(黑屏几分钟)。 */
v = bm_bri_floor_decide(7, 0, 0, 89999, -1, &why);
check("floor: Bug4 regression — dark+unpowered always floors", v == 7 &&
!strcmp(why, "scan-floor"), 1);
/* Bug 4 防退化回归用例(评审 r1 P2-2):面板暗就必须能补,不能有任何「播放器看起来不忙」
* 的前提 —— 旧 busy 门会让这条变成 no-write(黑屏几分钟)。 */
v = bm_bri_floor_decide(7, 0, 0, 89999, 0, &why);
check("floor: Bug4 regression — dark+unlatched always floors", v == 7 &&
!strcmp(why, "scan-floor"), 1);
v = bm_bri_floor_decide(1, 5, 0, 5000, -1, &why); /* 面板已亮:不动 */
v = bm_bri_floor_decide(1, 5, 0, 5000, 0, &why); /* 面板已亮:不动 */
check("floor: visible -> no write", v == 0 && !strcmp(why, "visible"), 1);
v = bm_bri_floor_decide(1, 0, 1, 5000, -1, &why); /* 面板已下电(bl_power>0):不动 */
v = bm_bri_floor_decide(1, 0, 1, 5000, 0, &why); /* bl_power>0(纯保险):不动 */
check("floor: panel powered off -> no write", v == 0 && !strcmp(why, "panel-off"), 1);
/* 电源键刚按下(用户主动熄屏的第二道保险):3s 内不补 */
v = bm_bri_floor_decide(1, 0, 0, 5000, 1200, &why);
check("floor: recent power key -> no write", v == 0 && !strcmp(why, "power-key"), 1);
v = bm_bri_floor_decide(1, 0, 0, 5000, BM_BRI_PWR_HOLD_MS, &why); /* 到期即恢复补 */
check("floor: power key expired -> floors", v == 1 && !strcmp(why, "scan-floor"), 1);
v = bm_bri_floor_decide(1, 0, 0, 5000, -1, &why); /* 本次开机没按过电源键 */
check("floor: no power edge ever -> floors", v == 1 && !strcmp(why, "scan-floor"), 1);
v = bm_bri_floor_decide(1, 0, 0, 0, -1, &why); /* 90s 窗口结束:放手 */
/* 评审 r2 P2-2 核心:电源键**锁存**。窗内按过电源键 → 本次窗口内此后一律不补,
* 不再「3s 到期就把用户刚熄的屏点亮」。 */
v = bm_bri_floor_decide(1, 0, 0, 5000, 1, &why);
check("floor: power key latched -> no write", v == 0 && !strcmp(why, "power-key-latched"), 1);
/* 锁存不会过期:窗口剩 1ms 也不补(旧实现 3s 后就会补) */
v = bm_bri_floor_decide(1, 0, 0, 1, 1, &why);
check("floor: latch never expires inside window", v == 0 &&
!strcmp(why, "power-key-latched"), 1);
/* 下一轮窗口(锁存已清零)恢复正常补亮 */
v = bm_bri_floor_decide(1, 0, 0, 90000, 0, &why);
check("floor: new window (latch cleared) floors again", v == 1 &&
!strcmp(why, "scan-floor"), 1);
v = bm_bri_floor_decide(1, 0, 0, 0, 0, &why); /* 90s 窗口结束:放手 */
check("floor: window over -> no write", v == 0 && !strcmp(why, "window-over"), 1);
v = bm_bri_floor_decide(1, 0, 0, -1, -1, &why);
v = bm_bri_floor_decide(1, 0, 0, -1, 0, &why);
check("floor: negative window -> no write", v == 0 && !strcmp(why, "window-over"), 1);
}
@@ -177,8 +184,8 @@ static void test_hotkey_saved(void)
v = bm_bri_hotkey_saved(1, 0, 1, 101, &src); /* 电源已熄屏:恢复用户值 1,不再回 60 */
check("hotkey: blank -> saved (settings 1)", v == 1 && !strcmp(src, "saved"), 1);
v = bm_bri_hotkey_saved(60, 0, 1, 101, &src); /* 降级 saved=60(settings 不可读)时才用 60 */
check("hotkey: blank -> degraded saved 60", v == 60 && !strcmp(src, "saved"), 1);
v = bm_bri_hotkey_saved(60, 0, 1, 101, &src); /* saved=60 只可能来自 settings(降级已封顶 10) */
check("hotkey: blank -> saved 60 (settings value)", v == 60 && !strcmp(src, "saved"), 1);
v = bm_bri_hotkey_saved(1, 77, 1, 101, &src); /* 面板亮着:跟随面板(HibyOS 里改过亮度) */
check("hotkey: panel-now wins", v == 77 && !strcmp(src, "panel-now"), 1);
v = bm_bri_hotkey_saved(1, 200, 1, 101, &src); /* 越界值 clamp 到 max_brightness */
@@ -212,7 +219,11 @@ static void test_settings_parse(void)
check("settings: clamp down to 100", bootmenu_settings_brightness(NULL), 100);
put_file(path, "audio.volume = 10\n");
check("settings: missing key -> 100", bootmenu_settings_brightness(NULL), 100);
/* 评审 r2 非阻塞 1(采纳):「文件里没有这个键」与「文件读不到」同属不可信 →
* 返回 -1,而不是 launcher 设置表的默认 101→100(那会让扫描窗待机补到 100)。 */
int mn2 = 0;
check("settings: missing key -> -1 (untrusted)", bootmenu_settings_brightness(&mn2), -1);
check("settings: missing key still reports min", mn2, 1);
unlink(path);
unsigned long long t0 = now_ms();
@@ -252,24 +263,26 @@ static void test_e2e_backlight(void)
check("e2e: panel file read 60", panel, 60);
check("e2e: saved from settings = 1", saved, 1);
/* 扫描窗:播放器待机把背光写 0(面板未下电)→ 保底写用户值 1 */
/* 扫描窗:播放器待机把背光写 0 → 保底写用户值 1 */
put_file(bl, "0\n");
const char *why = NULL;
int floor = bm_bri_floor_decide(saved, read_brightness(), 0, 5000, -1, &why);
int floor = bm_bri_floor_decide(saved, read_brightness(), 0, 5000, 0, &why);
if (floor > 0) write_brightness(floor);
check("e2e: scan floor writes 1 (not 60)", get_file_int(bl), 1);
/* 用户主动熄屏(播放器已 FBIOBLANK,bl_power>0)→ 保底不写,面板保持 0 */
/* bl_power>0(纯保险分支)→ 保底不写 */
put_file(bl, "0\n");
floor = bm_bri_floor_decide(saved, read_brightness(), 1, 5000, -1, &why);
floor = bm_bri_floor_decide(saved, read_brightness(), 1, 5000, 0, &why);
if (floor > 0) write_brightness(floor);
check("e2e: user screen-off (panel-off) stays dark", get_file_int(bl), 0);
/* 用户主动熄屏但 bl_power 没置位:电源键按下 1.2s 内也不补(第二道保险) */
/* 评审 r2 核心场景:用户按电源熄屏(bl_power 仍 0)→ 锁存 → 本次窗口内**永久**不补,
* 不会再出现「3s 后自己亮回来」 */
put_file(bl, "0\n");
floor = bm_bri_floor_decide(saved, read_brightness(), 0, 5000, 1200, &why);
floor = bm_bri_floor_decide(saved, read_brightness(), 0, 5000, 1, &why);
if (floor > 0) write_brightness(floor);
check("e2e: user screen-off (power key) stays dark", get_file_int(bl), 0);
check("e2e: user screen-off (power key latched) stays dark", get_file_int(bl), 0);
check("e2e: latched why", !strcmp(why, "power-key-latched"), 1);
/* 热键确认框:面板黑着 → 按 saved(用户值 1)点亮,不是 60 */
int hk = bm_bri_hotkey_saved(saved, read_brightness(), mn_e2e, 101, &src);
@@ -293,6 +306,16 @@ static void test_e2e_backlight(void)
check("e2e: settings unreadable -> saved SAFE_LOW (not 60)", dsaved, BM_BRI_SAFE_LOW);
check("e2e: degraded src=panel-capped", !strcmp(src, "panel-capped"), 1);
/* 评审 r2 非阻塞 1(采纳):文件存在但缺 display.brightness 键 → 视同不可信 →
* saved 同样封顶 SAFE_LOW,不会补到 launcher 缺省的 100(比老 bug 的 60 更亮)。 */
put_file(st, "display.brightness_min = 1\n");
put_file(bl, "60\n");
src = "?";
int msaved = bm_bri_pick_saved(bootmenu_settings_brightness(&mn_d), read_brightness(),
mn_d, 101, &src);
check("e2e: missing brightness key -> SAFE_LOW (not 100)", msaved, BM_BRI_SAFE_LOW);
check("e2e: missing key src=panel-capped", !strcmp(src, "panel-capped"), 1);
unlink(bl); unlink(st);
unsetenv("MEOW_BACKLIGHT");
unsetenv("MEOW_SETTINGS_FILE");