Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/key_router.c
T
AskaEth 7c7d891570 feat: M6.44+M6.45 收编 — 真机 0.8.45 现场保全(reader章节/key_router按键路由/WiFi连接修复/ncm bench+IME修复)
脏工作区现场收编:devdocs/local/m6_44_45_dirty_tracked.patch(46 个已跟踪文本文件,
+3090/-585, 已对 M6.43 基线干净适配) + m6_44_45_untracked_src.tar.gz 中 patch_v2/ 下
13 个新增源文件, 合成单个提交, 内容与容器工作树(R3ProII/patch_v2)逐字节一致。

- reader 章节: 新增 core/rdchapter.{h,c} — TXT 章节规范解析器, 扫描 512KB 码位对齐段文件
  逐段流式识别标题行(行首缩进≤4/显示宽≤40/行内标点≤1 三重约束防误判), 输出标题+全书字符
  偏移+段号/段内偏移+阿拉伯章号(中文数字归一), 首章前内容归"卷首"; book/reader/rdenc/
  rdlayout/settings 接入章节跳转与进度锚点, 附 chapter_smoke/turn_repro/build_turn_repro.sh
- key_router 按键路由: 新增 meow_launcherd/key_router.{h,c} — launcher-UI 页物理键统一路由
  (P1 仅音量键), 把读取边沿+防抖+长按 held+ramp 推进从 5 个会话收敛为进程内单开;
  各页每圈 key_router_pump() 取防抖后边沿, 动作(调音量/光标导航)仍由调用页决定,
  保留 run_menu 的 meow_mode 分支; App 前台 volume_page 武装时边沿抑制;
  main_menu/now_playing/notif_center/settings_page/subpage 接入, 附 test_key_router_vol/test_np_keys
- WiFi 连接修复: meow_netmgrd connect 有界等待 wpa_state=COMPLETED(普通 10s/隐藏网络 15s,
  env 可调)修设置页连接无响应; list_networks/scan_results 的 \xNN 转义还原始字节, 修中文 SSID
  永不命中致新建同名 open 记录顶影; launcher netmgr_ipc 配读超时; 附 test_wifi_connect(桩
  wpa_cli 驱动 static wifi_connect_ex), test_netmgrd.sh 补用例; wifi_page 同步
- ncm bench: 新增 bench/lyric_sim.c、bench/plpage_sim.c, vol_sync_sim 与 ncm core/app 同步
- SDK IME 修复: shift 大小写、K_ENTER 只提交不关、阻塞 IME 循环喂 on_tick 不被遮挡、
  IME 打开 ~200ms TAP 冷却窗吞 stray-TAP; 附 sdk/test_ime_fix.c
- 同期: meow_audiod 音量异步 ACK 线程(代际防 fd/槽复用竞态)、watchdog 音量收编(首选 audiod
  权威, settings.conf 覆盖路径)、panel 文件搜索(F1-④)与蓝牙只读详情(F1-③)、各类 build 脚本同步

范围红线: third_party/legado-E-main 原开发者未跟踪, 本次不入库(保留 devdocs/local tar 参考);
二进制不入库(后续重编); devdocs/ 不入库。59 文件全部在 patch_v2/ 下, 无 third_party/devdocs。
验证: git apply --check 干净; 59 文件 md5 与容器 patch_v2 逐一比对 59/59 一致(含
key_router.c/rdchapter.c/meow_ui_ime.c/test_wifi_connect.c 抽查); 可执行位保守
(check_lgpl_release.sh 100755, build_turn_repro.sh 100644 与容器一致)。
2026-11-04 20:02:28 +08:00

143 lines
5.5 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.
/* key_router.c — launcher-UI 页物理键统一路由(KEY_ROUTER_UNIFY,P1:音量键)。
*
* 本模块持有一个进程内唯一的音量键防抖/基线:
* kp_keys / kp_power(事件设备 poll 态,key_router_init 单开)
* kd_vold / kd_volu(40ms 防抖态,KEY_DEBOUNCE_MS)
* m_prev_vold / m_prev_volu(音量边沿基线)
* 各 launcher-UI 页每圈调 key_router_pump() —— 它只产「音量边沿 + 原始 held + ramp 状态」,
* 产出的边沿由「谁在跑循环」天然归属当前页;页据此决定 settings_volume_adj(±step) /
* 光标导航 / ramp 推进。这样 run_menu 的 meow_mode 分支(HibyOS overlay 音量=导航)不被破坏,
* 其余页(设置/通知中心/Now Playing/子页)用默认「音量=调音量」。
*
* 边界(§3 P1 / §4):
* - App 前台 volume_page 武装时(meow_volpage_armed())音量边沿被抑制(防御闸;launcher-UI
* 下 sink 恒空,此闸是「不改变行为」的保险)。
* - 播放键/电源键 P1 不路由(各页现状不动;播放键严禁 cmd=6)。
* - 锁屏(EVIOCGRAB)/App 会话层不调用本模块 —— 边界由「调用栈里当前循环是不是 launcher-UI 页」
* 自动强加。
*/
#include "meow_launcher_internal.h"
static int g_inited = 0;
/* 进程唯一事件设备 poll 态(key_router_init 单开;各页另自开 kp_* 仅供播放/电源键用)。 */
static struct keypoll kp_power, kp_keys;
/* 进程唯一音量键防抖态 + 边沿基线。 */
static struct kd_state kd_vold, kd_volu;
static int m_prev_vold = 0, m_prev_volu = 0;
/* 当前活动页哨兵(key_router_page_enter/exit 登记;与 sdk meow_ui_activity 交叉校验用)。 */
static int g_page_sentinel = 0;
void key_router_init(void)
{
if (g_inited) return;
g_inited = 1;
memset(&kp_power, 0, sizeof kp_power);
memset(&kp_keys, 0, sizeof kp_keys);
memset(&kd_vold, 0, sizeof kd_vold);
memset(&kd_volu, 0, sizeof kd_volu);
m_prev_vold = 0; m_prev_volu = 0;
kp_open(&kp_power, DEV_POWER);
kp_open(&kp_keys, DEV_KEYS);
/* 基线对齐当前 raw:进入时已按住的音量键不产假边沿。 */
key_router_rebase();
}
void key_router_rebase(void)
{
memset(&kd_vold, 0, sizeof kd_vold);
memset(&kd_volu, 0, sizeof kd_volu);
if (!g_inited) return;
kp_poll(&kp_power, NULL, NULL);
kp_poll(&kp_keys, NULL, NULL);
m_prev_vold = kp_is_down(&kp_keys, KEY_VOLUMEDOWN);
m_prev_volu = kp_is_down(&kp_keys, KEY_VOLUMEUP);
}
key_evt_t key_router_pump(void)
{
key_evt_t ev;
memset(&ev, 0, sizeof ev);
if (!g_inited) key_router_init();
/* 设备晚就绪兜底:init 时 open 失败则此处每圈重试(open 失败快速返回,无阻塞)。 */
if (!kp_keys.ok) kp_open(&kp_keys, DEV_KEYS);
if (!kp_power.ok) kp_open(&kp_power, DEV_POWER);
kp_poll(&kp_power, NULL, NULL);
kp_poll(&kp_keys, NULL, NULL);
int vold = 0, volu = 0;
for (int w = 0; w < KEYBUF_WORDS; w++) {
if (kp_keys.cur[w] & (1UL << (KEY_VOLUMEDOWN % 32))) vold = 1;
if (kp_keys.cur[w] & (1UL << (KEY_VOLUMEUP % 32))) volu = 1;
}
/* 原始(未防抖)值:长按 ramp held 用(松开立即停止;同 run_menu/now_playing 现状)。 */
ev.vol_held_dn = vold;
ev.vol_held_up = volu;
(void)kd_tick(&kd_vold, vold);
(void)kd_tick(&kd_volu, volu);
vold = kd_vold.stable; volu = kd_volu.stable;
ev.vol_st_dn = vold; ev.vol_st_up = volu;
if (meow_volpage_armed()) {
/* 防御闸:App 前台 volume_page 武装时(launcher-UI 下恒空)不放行音量边沿。
* 音量键在那态下由 app_launcher 的 meow_volpage_key 消费(翻页),不落系统调音。 */
ev.vol_dn_edge = 0;
ev.vol_up_edge = 0;
} else {
ev.vol_dn_edge = (vold && !m_prev_vold);
ev.vol_up_edge = (volu && !m_prev_volu);
}
ev.ramping = (vol_ramp_dir != 0);
m_prev_vold = vold;
m_prev_volu = volu;
return ev;
}
void key_router_default_volume(const key_evt_t *ev, unsigned long long *last_input)
{
if (ev->vol_dn_edge) {
settings_volume_adj(-settings_volume_step);
if (last_input) *last_input = now_ms();
logmsg("key_router: volume DOWN <- step=%d", settings_volume_step);
}
if (ev->vol_up_edge) {
settings_volume_adj(+settings_volume_step);
if (last_input) *last_input = now_ms();
logmsg("key_router: volume UP <- step=%d", settings_volume_step);
}
/* 在 settings_volume_adj 之后重查 vol_ramp_dir:同圈「起 ramp」即立刻迈第一步(同现状
* 各页「settings_volume_adj 后 if(vol_ramp_dir) vol_ramp_tick」),且长按续爬随每圈推进。 */
if (vol_ramp_dir != 0) {
int held = (vol_ramp_dir > 0) ? ev->vol_held_up : ev->vol_held_dn;
(void)vol_ramp_tick(held);
}
}
void key_router_page_enter(int sentinel)
{
g_page_sentinel = sentinel;
/* 与 sdk 活动栈交叉校验:哨兵非 0 且栈顶不一致 → 记录(不中止,防宿主/真机栈深度差异)。 */
if (sentinel) {
int act = meow_ui_activity_active();
if (act && act != sentinel)
logmsg("key_router: page_enter sentinel=%#x but active=%#x", sentinel, act);
}
}
void key_router_page_exit(int sentinel)
{
if (g_page_sentinel == sentinel) g_page_sentinel = 0;
/* 页退出即重播基线(§6 嵌套返回风险项:返回瞬间不产假边沿)。 */
key_router_rebase();
}