fix(watchdog+launcher+sdk): M40 电源键 time64 ABI + 锁屏/叠加残留链

- sdk/meow_time32: 抽出可独立链接的 o32 time32 取时/睡眠设施(不依赖 meow_ui.a,
  watchdog 等独立静态组件可直接链;可选 -DMEOW_TIME32_LIBC_OVERRIDES 接管 libc 入口)
- watchdog: now_ms/usleep 改走 time32(修复 held 变天文数字→每次单击都弹菜单);
  阈值读 power.longpress_menu_ms/forceoff_ms(缺省 3000/8000),删除
  PWR_TOGGLE_MS==PWR_MENU_MS;新增 wd_power.h 纯判定 + 宿主行为矩阵测试
- launcher: App 前台电源菜单补齐 §10.5(notif 全抑制 + 帧恢复 + SIGCONT 排空 +
  标记翻回前台);show_power_menu 恢复进入前背景帧(关闭后不再停留菜单画面);
  重绘挪到首轮按键采样之后;activity 标记排空窗 + 会话入口清理 + manual_off 去闩锁
- lock_screen: 电源改按下边沿 + 进场 300ms 才熄屏;电源熄屏退出也恢复底层帧;
  launcher 在 SIGCONT 前写 /tmp/meow_app_repaint,SDK present 消费并 dirty_full
- power_menu: 菜单内强关接 power.forceoff_ms(按 longpress 抵扣),日志不再谎称 8s
- 重编 meow_ui.a 并重链 meow_launcherd/ncm/files/hello/reader(picbrowser 无源码)
- fw_content.conf: 同步 launcherd/watchdog 新 md5;顺带修正 M35 遗留的 nyai 失效钉值

Host: test_wd_fw 66/66、test_overlay_gate 40/40、test_meow_time64/time/repaint PASS。
真机未验证(设备关机);未刷机、未写设备。
This commit is contained in:
AskaEth committed 2026-11-10 22:51:00 +08:00
1 parent b1acc9096b
commit 63b278e13e
28 files changed
+977 -217

No files matched your search

+5 -2
View File
@@ -12,13 +12,16 @@ INC="$ROOT/patch_v2/meow_launcherd" # meow_ipc.h(音量收编 v2:MSG_AUDIO
if [ "$1" = "host" ]; then
gcc -Wall -Wextra -Os -I"$INC" -DWd_NO_MAIN -Wno-unused-function -Wno-unused-parameter \
-Wno-unused-variable test_wd_fw.c -o test_wd_fw -lm
-Wno-unused-variable test_wd_fw.c ../sdk/meow_time32.c -o test_wd_fw -lm
./test_wd_fw
exit $?
fi
echo "== meow_watchdog (mipsel static) =="
"$CC" -Os -Wall -Wextra -static -I"$INC" meow_watchdog.c -o meow_watchdog
# meow_time32.c:独立 time32 取时/睡眠(watchdog 不链 meow_ui.a)。LIBC_OVERRIDES 让
# 未逐个改写的 libc 取时/睡眠入口也一并修正(静态链接,本对象强定义覆盖 libc 归档成员)。
"$CC" -Os -Wall -Wextra -static -I"$INC" -DMEOW_TIME32_LIBC_OVERRIDES \
meow_watchdog.c ../sdk/meow_time32.c -o meow_watchdog
ls -la meow_watchdog
md5sum meow_watchdog
file meow_watchdog
Binary file not shown.
+92 -32
View File
@@ -12,13 +12,14 @@
* run — short press (<3s release) = toggle screen off/on (brightness 0 +
* FBIOBLANK(4) / FBIOBLANK(0)+brightness restore, with a force re-pan to
* buffer0 on wake).
* - M2.24 全面按键接管(KEY_CONTROL_DESIGN):应用运行期所有物理键由本进程统一消费,
* - M2.24/M40 全面按键接管(KEY_CONTROL_DESIGN):应用运行期所有物理键由本进程统一消费,
* 应用绝不碰物理键。
* 电源单击(<2s) -> 息屏/亮屏(沿用 M2.22);
* 电源长按 ≥2s -> 写 /tmp/meow_power_menu,由 launcher SIGSTOP app 后弹统一电源菜单
* (launcher 在 waitpid 轮询中消费;菜单期该进程靠 /tmp/meow_power_menu_active
* 让位,绝不与 launcher 抢屏/抢键);launcher 画菜单,本进程不自绘。
* 电源长按 ≥8s -> 强制关机兜底(仅当菜单未生效/launcher 未接管)。
* 电源单击(松开时 held < power.longpress_menu_ms,缺省 3000)-> 息屏/亮屏(沿用 M2.22);
* 电源长按 ≥power.longpress_menu_ms -> 写 /tmp/meow_power_menu,由 launcher SIGSTOP app
* 后弹统一电源菜单(launcher 在 waitpid 轮询中消费;菜单期该进程靠
* /tmp/meow_power_menu_active 让位,绝不与 launcher 抢屏/抢键);
* launcher 画菜单,本进程不自绘。
* 电源长按 ≥power.forceoff_ms(缺省 8000)-> 强制关机兜底(仅当菜单未被 launcher 接管)。
* 音量±(115/114) -> 音量收编 v2(FUTURE_AUDIO 事项三):首选发 audiod 权威
* MSG_AUDIO_VOLUME(audiod 写 DAC numid=1/2 + DSD_VOLUME + 持久化
* settings.conf,同源同键单写者);audiod 未起(HibyOS/开机早期)
@@ -62,6 +63,8 @@
#include <stdint.h>
#include <time.h>
#include "meow_ipc.h" /* MSG_AUDIO_VOLUME/MSG_ACK/帧格式(音量收编 v2:首选 audiod 权威) */
#include "../sdk/meow_time32.h" /* o32 time32 取时/睡眠(watchdog 不链 meow_ui.a) */
#include "wd_power.h" /* 电源阈值/边沿纯判定(宿主单测同源) */
/* device-verified: VSCREENINFO=0x4600, FSCREENINFO=0x4602 (do not swap) */
#define FBIOGET_VSCREENINFO 0x4600
@@ -95,11 +98,10 @@
#define KEY_NEXTSONG 163
#define KEY_PREVIOUS 19
#define BACKLIGHT "/sys/class/backlight/backlight_pwm0/brightness"
/* M2.24 全面按键接管:电源单击(<2s)=息屏/亮屏;长按≥2s=通知 launcher 弹统一电源菜单;
* ≥8s=强制关机兜底(仅当 launcher 未接管菜单时)。音量± = 写真实 DAC;播放/上下曲 = 抓消费。 */
#define PWR_TOGGLE_MS 2000 /* 短按上限:<2s 松开=单击(镜像 launcher 单单击语义) */
#define PWR_MENU_MS 2000 /* 长按≥2s -> 写 /tmp/meow_power_menu 请 launcher 弹菜单 */
#define PWR_FORCE_MS 8000 /* 长按≥8s -> 强制关机(launcher 未接管时的兜底) */
/* M2.24 全面按键接管:电源单击(<power.longpress_menu_ms)=息屏/亮屏;长按≥该值=通知
* launcher 弹统一电源菜单;≥power.forceoff_ms=强制关机兜底(launcher 未接管菜单时)。
* 阈值不硬编码:启动时读 settings.conf(缺省 3000/8000),与 launcher 主屏同一语义。
* 音量± = 写真实 DAC;播放/上下曲 = 抓消费。 */
#define PWR_MENU_MARKER "/tmp/meow_power_menu" /* watchdog->launcher 菜单请求 */
#define PWR_MENU_ACTIVE "/tmp/meow_power_menu_active" /* launcher 画菜单中,watchdog 让位 */
#define MEOW_SETTINGS_FILE "/usr/data/meow/settings.conf" /* 音量持久化(与 launcher settings.conf 同源) */
@@ -150,11 +152,14 @@ static int read_state_file(void)
* stderr when unset (e.g. no TF card at launch time). */
static int wd_log_fd = -1;
/* 单调毫秒:**必须**走 meow_time32(o32 内核 time32 取时)。
* zig musl 的 time64 ABI 在 mipsel 上自相矛盾:直接 clock_gettime() 时结构按 16 字节写、
* libc 只写 8 字节 time32,tv_sec 高 32 位会读成 nsec → held 变天文数字,每次单击都被
* 判成长按并立刻弹菜单(真机日志 `power hold 12392415168036864000ms`,反汇编证实只有
* 4263/无 4403)。meow_time32.c 不依赖 meow_ui.a,静态链入即可修正。 */
static unsigned long long now_ms(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (unsigned long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
return (unsigned long long)meow_time32_monotonic_ms();
}
static void wd_log_init(void)
@@ -216,6 +221,9 @@ static int wd_fbfd = -1; /* fb0 fd(main 里打开,供 FB
#define WD_SETTINGS_GAP_MS 10
static int wd_saved_bri = WD_BRI_UNKNOWN; /* 熄屏前亮度快照;未知=WD_BRI_UNKNOWN(不再用 60) */
/* 电源阈值(启动时读 settings.conf;缺省 3000/8000,与 launcher 主屏一致)。 */
static unsigned long long wd_pwr_longpress_ms = WD_PWR_LONGPRESS_DEFAULT_MS;
static unsigned long long wd_pwr_forceoff_ms = WD_PWR_FORCEOFF_DEFAULT_MS;
static int pwr_down = 0; /* 电源当前按住 */
static unsigned long long pwr_down_ms = 0;
static int pwr_woke = 0; /* 按下时屏已黑 → 本次只作唤醒,松开不再 toggle */
@@ -278,7 +286,7 @@ static int wd_settings_brightness(void)
for (int i = 0; i < WD_SETTINGS_TRIES; i++) {
f = fopen(wd_settings_path(), "r");
if (f) break;
if (i + 1 < WD_SETTINGS_TRIES) usleep(WD_SETTINGS_GAP_MS * 1000);
if (i + 1 < WD_SETTINGS_TRIES) meow_time32_sleep_us(WD_SETTINGS_GAP_MS * 1000);
}
if (!f) return -1;
int b = 101; /* display.brightness 默认(与 launcher 设置表同值,键缺失时 clamp→100) */
@@ -301,6 +309,42 @@ static int wd_settings_brightness(void)
return b;
}
/* 读电源阈值(power.longpress_menu_ms / power.forceoff_ms,缺省 3000/8000)。
* 与 wd_settings_brightness 同款重试窗口;文件不可读时**保留缺省**(绝不退回 2000——
* 旧硬编码会让 App 内 2s 出菜单、主屏 3s,且 PWR_TOGGLE==PWR_MENU 时「长按不出菜单」
* 的区间不存在)。解析后经 pure helper 清洗(非正数→缺省;forceoff≤longpress→抬 1s)。 */
static void wd_power_settings_load(void)
{
FILE *f = NULL;
for (int i = 0; i < WD_SETTINGS_TRIES; i++) {
f = fopen(wd_settings_path(), "r");
if (f) break;
if (i + 1 < WD_SETTINGS_TRIES) meow_time32_sleep_us(WD_SETTINGS_GAP_MS * 1000);
}
if (!f) {
wd_pwr_resolve_thresholds(0, 0, &wd_pwr_longpress_ms, &wd_pwr_forceoff_ms);
wd_log("power: settings unreadable -> defaults longpress=%llums forceoff=%llums",
wd_pwr_longpress_ms, wd_pwr_forceoff_ms);
return;
}
int lp = 0, fo = 0;
char buf[128];
while (fgets(buf, sizeof buf, f)) {
char *eq = strchr(buf, '=');
if (!eq) continue;
*eq = 0;
char *k = buf; int kl = (int)strlen(k);
while (kl > 0 && (k[kl-1] == ' ' || k[kl-1] == '\t')) k[--kl] = 0;
char *v = eq + 1; while (*v == ' ' || *v == '\t') v++;
if (!strcmp(k, "power.longpress_menu_ms")) lp = atoi(v);
else if (!strcmp(k, "power.forceoff_ms")) fo = atoi(v);
}
fclose(f);
wd_pwr_resolve_thresholds(lp, fo, &wd_pwr_longpress_ms, &wd_pwr_forceoff_ms);
wd_log("power: settings longpress=%llums forceoff=%llums (raw %d/%d)",
wd_pwr_longpress_ms, wd_pwr_forceoff_ms, lp, fo);
}
/* 强制 re-pan 到 buffer0:FBIOBLANK(4)->(0) 后 LCDC 扫描地址可能已复位到非 buffer0/失效
* 位置(launcher M2.8 Bug-3-residual 根因),而应用始终画 buffer0,必须无条件把显示翻回
* buffer0,否则唤醒后黑屏/旧帧。 */
@@ -378,15 +422,17 @@ static void wd_do_poweroff(void)
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_POWER_OFF, NULL);
wd_log("sys_reboot returned: %s", strerror(errno)); /* 正常不会返回 */
for (;;) usleep(1000000);
for (;;) meow_time32_sleep_us(1000000);
}
/* 电源键(KEY_POWER)边沿驱动:单击(短按松开 <3s)= 息屏/亮屏;按下时屏已黑 = 只唤醒。
* 长按(≥3s)由 wd_power_tick() 在轮询中检测 → 写 /tmp/meow_power_menu 请 launcher 弹菜单;
* 松开边沿不再 toggle(菜单/长按语义归 launcher 菜单,防「长按后仍 toggle」)。 */
/* 电源键(KEY_POWER)边沿驱动:单击(松开时 held < power.longpress_menu_ms,缺省 3s)= 息屏/亮屏;
* 按下时屏已黑 = 只唤醒。长按(≥longpress)由 wd_power_tick() 在轮询中检测 → 写
* /tmp/meow_power_menu 请 launcher 弹菜单;松开边沿不再 toggle(菜单/长按语义归 launcher
* 菜单,防「长按后仍 toggle」)。 */
static void handle_power(int val)
{
if (wd_menu_active()) return; /* 电源菜单期:launcher 独占物理键语义 */
if (val == 2) return; /* EV_KEY 重复值:本设备 event0 无 EV_REP,防御性忽略 */
if (val) {
/* 按下边沿 */
pwr_down = 1;
@@ -396,28 +442,40 @@ static void handle_power(int val)
int br = read_brightness();
pwr_woke = (br <= 0) ? 1 : 0; /* 屏已黑:本次电源=唤醒,松开不 toggle */
if (pwr_woke) wd_panel_on(); /* 立即亮屏(launcher 自动息屏唤醒路径同步醒,双保险幂等) */
wd_log("power down (bri=%d woke=%d)", br, pwr_woke);
wd_log("power down (bri=%d woke=%d longpress=%llums)", br, pwr_woke,
wd_pwr_longpress_ms);
return;
}
/* 松开边沿 */
if (!pwr_down) return; /* 未跟踪到按下(启动前已按住/残留):忽略,防误 toggle */
pwr_down = 0;
unsigned long long held = now_ms() - pwr_down_ms;
if (pwr_woke) { pwr_woke = 0; wd_log("power release after wake (%llums) -> no toggle", held); return; }
int br = read_brightness();
wd_pwr_release_act act = wd_pwr_release_action(pwr_woke, pwr_menu_notified, held,
wd_pwr_longpress_ms, br);
if (pwr_woke) {
pwr_woke = 0;
wd_log("power release after wake (%llums) -> no toggle", held);
return;
}
if (pwr_menu_notified) {
/* 本次已请求电源菜单(≥3s):菜单是否画出由 launcher 负责,这里绝不 toggle。 */
/* 本次已请求电源菜单(≥longpress):菜单是否画出由 launcher 负责,这里绝不 toggle。 */
wd_log("power release after menu req (%llums) -> menu owns toggle", held);
pwr_menu_notified = 0;
return;
}
if (held >= PWR_TOGGLE_MS) { wd_log("power hold %llums -> no-op (menu/force-off in tick)", held); return; }
int br = read_brightness();
if (br > 0) wd_panel_off();
else wd_panel_on(); /* 屏已黑仍短按:亮屏 */
if (act == WD_PWR_REL_NONE) {
/* held ≥ longpress 但 tick 还没发出菜单(轮询粒度/边界):仍按长按处理,不 toggle。 */
wd_log("power hold %llums -> no-op (menu/force-off in tick)", held);
return;
}
if (act == WD_PWR_REL_TOGGLE_OFF) wd_panel_off();
else wd_panel_on(); /* 屏已黑仍短按:亮屏 */
wd_log("power short-press %llums -> toggle panel (bri was %d)", held, br);
}
/* 电源长按检测(每轮询 tick 调):≥3s 写菜单请求;≥8s 强制关机兜底(仅当菜单未生效时)。 */
/* 电源长按检测(每轮询 tick 调):≥longpress 写菜单请求;≥forceoff 强制关机兜底
* (launcher 已接管菜单时顶部即让位,不会走到这里)。阈值来自 settings。 */
static void wd_power_tick(void)
{
/* launcher 已接管电源菜单:本进程完全让位,清空电源状态,下一手势从零开始。 */
@@ -428,14 +486,15 @@ static void wd_power_tick(void)
}
if (!pwr_down) return;
unsigned long long held = now_ms() - pwr_down_ms;
if (held >= PWR_MENU_MS && !pwr_menu_notified) {
wd_pwr_tick_act act = wd_pwr_tick_action(0, pwr_down, pwr_menu_notified, pwr_force_done,
held, wd_pwr_longpress_ms, wd_pwr_forceoff_ms);
if (act == WD_PWR_TICK_MENU) {
int fd = open(PWR_MENU_MARKER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd >= 0) { write(fd, "1", 1); close(fd); }
pwr_menu_notified = 1;
wd_log("power hold %llums -> %s (launcher draws menu)", held, PWR_MENU_MARKER);
}
if (held >= PWR_FORCE_MS && !pwr_force_done && !pwr_menu_notified) {
/* 长按 8s 仍无菜单接管(launcher 未响应/未消费)→ 强制关机兜底。 */
} else if (act == WD_PWR_TICK_FORCEOFF) {
/* 长按 ≥forceoff 仍无菜单接管(launcher 未响应/未消费)→ 强制关机兜底。 */
pwr_force_done = 1;
wd_log("power hold %llums -> FORCE POWER OFF", held);
wd_do_poweroff();
@@ -805,6 +864,7 @@ int main(int argc, char **argv)
setvbuf(stderr, NULL, _IONBF, 0);
wd_log_init();
wd_vol_load(); /* M2.24:音量初始值读自与 launcher 同源 settings.conf */
wd_power_settings_load(); /* M40:电源长按/强关阈值(缺省 3000/8000,与 launcher 主屏同源) */
/* M2.86(226):SDK 框架 App 判定(app.json framework=1)-> 音量/媒体键让位 SDK。 */
g_fw_app = wd_framework_from_pid(app_pid);
wd_log("app_pid=%d sim_tap=%d sim_swipe=%d framework=%d%s",
@@ -870,7 +930,7 @@ int main(int argc, char **argv)
if (sim_tap && elapsed >= (long)sim_tap * 1000) {
wd_log("SIM TAP");
kill(app_pid, SIGTERM);
usleep(1000000);
meow_time32_sleep_us(1000000);
kill(app_pid, SIGKILL);
break;
}
Binary file not shown.
+86 -3
View File
@@ -1,9 +1,10 @@
/* test_wd_fw.c — 宿主单测:meow_watchdog 的纯逻辑(M2.86 框架 App 判定 + M20 亮屏亮度)。
/* test_wd_fw.c — 宿主单测:meow_watchdog 的纯逻辑(M2.86 框架 App 判定 + M20 亮屏亮度 +
* M40 time32 取时/睡眠 + 电源阈值行为矩阵)。
*
* 编译:#include meow_watchdog.c(去 main 拿 wd_app_is_framework/json_str/from_pid、
* wd_bri_decide、wd_settings_brightness、wd_panel_off/on)。
* wd_bri_decide、wd_settings_brightness、wd_panel_off/on;wd_power.h 的纯判定)。
* gcc -Wall -Wextra -Os -DWd_NO_MAIN -Wno-unused-function -Wno-unused-parameter \
* test_wd_fw.c -o test_wd_fw
* test_wd_fw.c ../sdk/meow_time32.c -o test_wd_fw
* (meow_watchdog.c 含仅 main 使用的静态函数,宿主去 main 后须 -Wno-unused-function 静音;
* mips 目标构建不定义 WD_NO_MAIN,不受影响。)
* 运行:./test_wd_fw;全 PASS -> exit 0。
@@ -223,6 +224,84 @@ static void test_panel_brightness_e2e(void)
unsetenv("MEOW_SETTINGS_FILE");
}
/* ---- M40:time32 取时/睡眠(与 libc 正常取时一致;o32 syscall 路径由交叉二进制反汇编验证)---- */
static void test_time32(void)
{
struct meow_time32 t;
check("time32 gettime ok", meow_time32_clock_gettime(CLOCK_MONOTONIC, &t) == 0, 1);
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
check("time32 sec == libc sec", t.tv_sec == (int32_t)ts.tv_sec, 1);
long dn = (long)t.tv_nsec - (long)ts.tv_nsec;
if (dn < 0) dn = -dn;
check("time32 nsec == libc nsec (<1ms)", dn < 1000000, 1);
uint64_t a = meow_time32_monotonic_ms();
uint64_t b = meow_time32_monotonic_ms();
check("monotonic nonzero", a > 0, 1);
check("monotonic nondecreasing", b >= a, 1);
check("ms from parts 1135.008s", meow_time_ms_from_parts(1135, 8000000) == 1135008ULL, 1);
unsigned long long s0 = now_ms();
meow_time32_sleep_us(20000);
unsigned long long el = now_ms() - s0;
check("sleep 20ms actually sleeps (>=15ms)", el >= 15, 1);
}
/* ---- M40:电源阈值/边沿行为矩阵(settings 取不同值)---- */
static void test_power_matrix(void)
{
unsigned long long lp = 0, fo = 0;
wd_pwr_resolve_thresholds(0, 0, &lp, &fo);
check("pwr default longpress=3000", lp == 3000, 1);
check("pwr default forceoff=8000", fo == 8000, 1);
wd_pwr_resolve_thresholds(1500, 5000, &lp, &fo);
check("pwr settings longpress=1500", lp == 1500, 1);
check("pwr settings forceoff=5000", fo == 5000, 1);
wd_pwr_resolve_thresholds(-1, 0, &lp, &fo);
check("pwr invalid -> default longpress", lp == 3000, 1);
check("pwr invalid -> default forceoff", fo == 8000, 1);
wd_pwr_resolve_thresholds(3000, 2000, &lp, &fo);
check("pwr forceoff<=longpress lifted to lp+1000", fo == 4000, 1);
/* 松开边沿:单击 / 长按但未到阈值 / 到阈值 / 唤醒 / 菜单已请求 */
check("release 100ms screen-on -> toggle off",
wd_pwr_release_action(0, 0, 100, 3000, 50) == WD_PWR_REL_TOGGLE_OFF, 1);
check("release 100ms screen-off -> toggle on",
wd_pwr_release_action(0, 0, 100, 3000, 0) == WD_PWR_REL_TOGGLE_ON, 1);
check("release 2500ms (<3000) still click -> toggle off",
wd_pwr_release_action(0, 0, 2500, 3000, 50) == WD_PWR_REL_TOGGLE_OFF, 1);
check("release 3000ms (>=longpress) -> no toggle",
wd_pwr_release_action(0, 0, 3000, 3000, 50) == WD_PWR_REL_NONE, 1);
check("release after wake -> no toggle",
wd_pwr_release_action(1, 0, 100, 3000, 0) == WD_PWR_REL_NONE, 1);
check("release after menu req -> no toggle",
wd_pwr_release_action(0, 1, 3500, 3000, 50) == WD_PWR_REL_NONE, 1);
/* 按住 tick:单击窗 / 长按出菜单 / 强关兜底 / 菜单已接管 / 已强关 */
check("tick 2999ms -> none",
wd_pwr_tick_action(0, 1, 0, 0, 2999, 3000, 8000) == WD_PWR_TICK_NONE, 1);
check("tick 3000ms -> menu",
wd_pwr_tick_action(0, 1, 0, 0, 3000, 3000, 8000) == WD_PWR_TICK_MENU, 1);
check("tick 8000ms menu requested, no takeover -> forceoff",
wd_pwr_tick_action(0, 1, 1, 0, 8000, 3000, 8000) == WD_PWR_TICK_FORCEOFF, 1);
check("tick 8000ms launcher menu active -> none",
wd_pwr_tick_action(1, 1, 1, 0, 8000, 3000, 8000) == WD_PWR_TICK_NONE, 1);
check("tick forceoff already done -> none",
wd_pwr_tick_action(0, 1, 1, 1, 9000, 3000, 8000) == WD_PWR_TICK_NONE, 1);
check("tick not down -> none",
wd_pwr_tick_action(0, 0, 0, 0, 9000, 3000, 8000) == WD_PWR_TICK_NONE, 1);
/* settings 取短值:1000/2000 */
check("tick custom 1000ms -> menu",
wd_pwr_tick_action(0, 1, 0, 0, 1000, 1000, 2000) == WD_PWR_TICK_MENU, 1);
check("tick custom 2000ms no takeover -> forceoff",
wd_pwr_tick_action(0, 1, 1, 0, 2000, 1000, 2000) == WD_PWR_TICK_FORCEOFF, 1);
}
int main(void)
{
signal(SIGUSR1, on_sigusr1); /* 捕获 handle_touch 可能发出的 SIGUSR1(测命中) */
@@ -270,6 +349,10 @@ int main(void)
test_settings_brightness();
test_panel_brightness_e2e();
/* ---- M40:time32 取时/睡眠 + 电源阈值行为矩阵 ---- */
test_time32();
test_power_matrix();
/* 清理 */
{
char path[560];
+73
View File
@@ -0,0 +1,73 @@
/* wd_power.h — watchdog 电源键判定(纯函数,宿主可单测,无设备/无文件系统依赖)。
*
* 语义与 launcher 主屏(main_menu.c)统一:
* 单击(松开时 held < power.longpress_menu_ms) → 息屏/亮屏切换;
* 长按 held ≥ power.longpress_menu_ms → 写菜单请求(watchdog 让位 launcher);
* 长按 held ≥ power.forceoff_ms 且菜单未被接管 → 强制关机兜底。
* 旧实现把 PWR_TOGGLE_MS 与 PWR_MENU_MS 都硬编码成 2000,导致「长按但不出菜单」的区间
* 不存在,且 App 内 2s / launcher 主屏 3s 不一致;现在两者都读 settings(缺省 3000/8000)。
*
* 阈值清洗:
* - 非正数 → 默认 3000 / 8000(settings 允许 0,但 0ms 阈值会让每次按下立刻出菜单,无意义);
* - forceoff ≤ longpress → 抬到 longpress+1000,避免「强关先于菜单」的自相矛盾配置。
*/
#ifndef WD_POWER_H
#define WD_POWER_H
#define WD_PWR_LONGPRESS_DEFAULT_MS 3000
#define WD_PWR_FORCEOFF_DEFAULT_MS 8000
static inline void wd_pwr_resolve_thresholds(int cfg_longpress, int cfg_forceoff,
unsigned long long *longpress_ms,
unsigned long long *forceoff_ms)
{
unsigned long long lp = (cfg_longpress > 0)
? (unsigned long long)cfg_longpress
: WD_PWR_LONGPRESS_DEFAULT_MS;
unsigned long long fo = (cfg_forceoff > 0)
? (unsigned long long)cfg_forceoff
: WD_PWR_FORCEOFF_DEFAULT_MS;
if (fo <= lp) fo = lp + 1000;
*longpress_ms = lp;
*forceoff_ms = fo;
}
/* 松开边沿的动作。bright>0=屏亮 → 息屏;否则亮屏。
* woke(按下时屏已黑,本次只唤醒)/ menu_notified(已请求菜单)→ 不再 toggle。 */
typedef enum {
WD_PWR_REL_NONE = 0,
WD_PWR_REL_TOGGLE_OFF,
WD_PWR_REL_TOGGLE_ON
} wd_pwr_release_act;
static inline wd_pwr_release_act wd_pwr_release_action(int woke, int menu_notified,
unsigned long long held_ms,
unsigned long long longpress_ms,
int bright)
{
if (woke || menu_notified) return WD_PWR_REL_NONE;
if (held_ms >= longpress_ms) return WD_PWR_REL_NONE; /* 长按已由 tick 处理 */
return (bright > 0) ? WD_PWR_REL_TOGGLE_OFF : WD_PWR_REL_TOGGLE_ON;
}
/* 轮询 tick(按住中)的动作。menu_active=launcher 已接管 → 全让位。
* 强关不要求 menu_notified:launcher 未接管菜单时 8s 兜底仍须生效。 */
typedef enum {
WD_PWR_TICK_NONE = 0,
WD_PWR_TICK_MENU,
WD_PWR_TICK_FORCEOFF
} wd_pwr_tick_act;
static inline wd_pwr_tick_act wd_pwr_tick_action(int menu_active, int down,
int menu_notified, int force_done,
unsigned long long held_ms,
unsigned long long longpress_ms,
unsigned long long forceoff_ms)
{
if (menu_active || !down) return WD_PWR_TICK_NONE;
if (held_ms >= forceoff_ms && !force_done) return WD_PWR_TICK_FORCEOFF;
if (held_ms >= longpress_ms && !menu_notified) return WD_PWR_TICK_MENU;
return WD_PWR_TICK_NONE;
}
#endif /* WD_POWER_H */