/* 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(); }