diff --git a/patch_v2/bootmenu/meow_bootmenu.c b/patch_v2/bootmenu/meow_bootmenu.c index f3d8a12..432c71f 100644 --- a/patch_v2/bootmenu/meow_bootmenu.c +++ b/patch_v2/bootmenu/meow_bootmenu.c @@ -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); diff --git a/patch_v2/bootmenu/test_bm_fw.c b/patch_v2/bootmenu/test_bm_fw.c index 4f08564..415d86c 100644 --- a/patch_v2/bootmenu/test_bm_fw.c +++ b/patch_v2/bootmenu/test_bm_fw.c @@ -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");