fix(sdk): App内物理音量/媒体键无效真根因 (agent-226) — 本机物理键只更新EVIOCGKEY位图不进事件流(launcher用位图轮询故正常,SDK读流故App收不到); 输入线程加EVIOCGKEY位图轮询+120ms同码窗口去重(流+位图双源不双跳); 卡上三App已重链部署(hello 2a1316f7/files ef0082bc/ncm 42e67e15)
真机验证: 音量键改settings.conf+媒体键IPC转发链路通; 宿主全PASS
This commit is contained in:
1 parent
c041facd89
commit
c7d02e66e9
1 file changed
+89
-20
@@ -6,7 +6,8 @@
|
||||
* meow_ui_run / IME 循环弹出分发。应用不再自读 event*。
|
||||
*
|
||||
* 触摸:BTN_TOUCH 按下记录原点,抬起时位移 < 阈值 → TAP,否则按主导轴 → SWIPE。
|
||||
* 物理键:EV_KEY 值 1=按下 / 0=抬起(重复值 2 忽略)。M2.55 起系统按键语义在 SDK 输入层消化:
|
||||
* 物理键:read() 事件流 EV_KEY 值 1=按下 / 0=抬起(重复值 2 忽略)。M2.55/agent-217 起系统按键
|
||||
* 语义在 SDK 输入层消化:
|
||||
* 电源 116 → 维持现状(息屏/亮屏/长按菜单由内核/sys_server/watchdog),SDK 不动、不下发 APP;
|
||||
* 音量 114/115 → 调系统音量(meow_settings audio.volume + CS43198 DAC 写,volume_step/volume_max
|
||||
* 约束,与 launcher 设置页/watchdog 同源);
|
||||
@@ -14,6 +15,11 @@
|
||||
* 否则经 IPC 发 launcher 转发)。
|
||||
* 「App 收到的是语义事件」:上述音量/媒体键在 SDK 消化后**仍下发 MEOW_EV_KEY**(on_key),但
|
||||
* App 不得把它们映射成自定义行为(APPS.md §3.4.1 语义不变)。
|
||||
* 真机链路兜底(本次修复):物理键的按下不一定进入 evdev 事件流(事件流可被 EVIOCGRAB 独占或
|
||||
* 驱动不投递;APPS.md §4.4 明确 launcher 用 EVIOCGKEY 状态轮询读键、「与 grab/投递无关」)。故
|
||||
* 输入线程除 read() 事件流外,每轮还对键设备做一次 EVIOCGKEY 状态位图轮询,把 0→1/1→0 边沿并入
|
||||
* 同一套系统键语义(poll_sys_keys_eviocgkey);事件流与位图双源经 sys_key_press/sys_key_release
|
||||
* 的「同码 120ms 窗口」去重,一次物理按下只触发一步系统语义 + 一次 MEOW_EV_KEY 下发。
|
||||
*/
|
||||
#include "meow_ui.h"
|
||||
#include "meow_ui_internal.h"
|
||||
@@ -30,6 +36,7 @@
|
||||
#include <time.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
/* linux input 事件常量(本设备实测) */
|
||||
#define EV_SYN 0
|
||||
@@ -40,6 +47,12 @@
|
||||
#define ABS_Y 0x01
|
||||
#define ABS_MT_POSITION_X 0x35
|
||||
#define ABS_MT_POSITION_Y 0x36
|
||||
#ifndef EVIOCGKEY
|
||||
#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len)
|
||||
#endif
|
||||
#ifndef KEY_MAX
|
||||
#define KEY_MAX 0x2ff
|
||||
#endif
|
||||
|
||||
#define SWIPE_THRESH 65 /* px:抬起位移超过视为滑动(GESTURE_BACK: 90→65,用户实测 90 偏多,65 更顺手) */
|
||||
#define EDGE_W 24 /* 左滑返回的「左缘起点」宽度(t_dx < EDGE_W 才认返回手势) */
|
||||
@@ -337,24 +350,83 @@ int meow_input_long_press_poll(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---- 输入线程:poll 所有 event 设备 + 解析 ---- */
|
||||
/* ---- 系统键(音量/媒体)去重 + EVIOCGKEY 状态轮询 ----
|
||||
* M2.55/agent-217 之后 SDK 在输入线程消化系统键语义;但真机物理键(jz adc keyboard event2、
|
||||
* md-gpio-keys event0、earpods_adc event3)的按下不一定进入 evdev 事件流(事件流可被其它进程
|
||||
* EVIOCGRAB 独占或驱动不投递),而 launcher 全程用 EVIOCGKEY 状态轮询读键(APPS.md §4.4,
|
||||
* 「与 grab/投递无关」)。为让 App 前台物理音量/媒体键可靠生效,输入线程除 read() 事件流外,
|
||||
* 还按固定节奏轮询内核按键位图(EVIOCGKEY),把 0→1/1→0 边沿并入同一套系统键语义。
|
||||
* 双源(事件流 + 状态位图)对同一次物理按下各报一次,用「同码 120ms 窗口」去重,保证
|
||||
* 一次按下只触发一步系统语义 + 一次 MEOW_EV_KEY 下发(App on_key 收到语义事件,不得映射)。 */
|
||||
static int g_sys_press_code = -1;
|
||||
static uint64_t g_sys_press_ms = 0;
|
||||
#define SYS_KEY_DEDUP_MS 120
|
||||
|
||||
static void sys_key_press(int code)
|
||||
{
|
||||
uint64_t now = now_ms();
|
||||
if (code == g_sys_press_code && now - g_sys_press_ms < SYS_KEY_DEDUP_MS)
|
||||
return; /* 事件流 + EVIOCGKEY 双源去重:同码窗口内只消化一次 */
|
||||
g_sys_press_code = code;
|
||||
g_sys_press_ms = now;
|
||||
meow_input_handle_sys_key(code, 1);
|
||||
push_ev(MEOW_EV_KEY, code, 1);
|
||||
}
|
||||
|
||||
static void sys_key_release(int code)
|
||||
{
|
||||
push_ev(MEOW_EV_KEY, code, 0);
|
||||
}
|
||||
|
||||
/* 对一个 event 设备做一次 EVIOCGKEY 状态边沿检测(prev 为该设备上一状态位图,原地更新)。
|
||||
* 只在系统键码(音量/媒体)上找边沿;电源(116)不进此表——SDK 维持「不动、不下发」纪律。 */
|
||||
static void poll_sys_keys_eviocgkey(int fd, unsigned char *prev)
|
||||
{
|
||||
unsigned char cur[KEY_MAX / 8 + 1];
|
||||
if (ioctl(fd, EVIOCGKEY(sizeof cur), cur) < 0) return;
|
||||
static const int sys_codes[] = {
|
||||
MEOW_KEY_VOLUMEDOWN, MEOW_KEY_VOLUMEUP, MEOW_KEY_PLAYPAUSE,
|
||||
MEOW_KEY_PREVIOUS, MEOW_KEY_NEXT, 19,
|
||||
};
|
||||
for (unsigned ci = 0; ci < sizeof sys_codes / sizeof sys_codes[0]; ci++) {
|
||||
int code = sys_codes[ci];
|
||||
int b = (cur[code / 8] >> (code % 8)) & 1;
|
||||
int pb = (prev[code / 8] >> (code % 8)) & 1;
|
||||
if (b == pb) continue;
|
||||
prev[code / 8] = (unsigned char)(b ? (prev[code / 8] | (1u << (code % 8)))
|
||||
: (prev[code / 8] & (unsigned char)~(1u << (code % 8))));
|
||||
if (b) sys_key_press(code); /* 0→1:按下(去重后触发一步系统语义) */
|
||||
else sys_key_release(code); /* 1→0:抬起 */
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 输入线程:poll 所有 event 设备 + EVIOCGKEY 系统键轮询 ---- */
|
||||
static void *input_thread(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
struct pollfd fds[MAX_DEVS];
|
||||
unsigned char prev_key[MAX_DEVS][KEY_MAX / 8 + 1];
|
||||
int nfd = 0;
|
||||
char dev[32];
|
||||
for (int i = 0; i < MAX_DEVS; i++) {
|
||||
snprintf(dev, sizeof dev, "/dev/input/event%d", i);
|
||||
int e = open(dev, O_RDONLY | O_NONBLOCK);
|
||||
if (e >= 0) { fds[nfd].fd = e; fds[nfd].events = POLLIN; fds[nfd].revents = 0; nfd++; }
|
||||
if (e >= 0) {
|
||||
fds[nfd].fd = e; fds[nfd].events = POLLIN; fds[nfd].revents = 0;
|
||||
/* EVIOCGKEY 上一状态初始化为当前按下位图:启动时已按住的键不误判为新按下 */
|
||||
memset(prev_key[nfd], 0, sizeof prev_key[nfd]);
|
||||
if (ioctl(e, EVIOCGKEY(sizeof prev_key[nfd]), prev_key[nfd]) < 0)
|
||||
memset(prev_key[nfd], 0, sizeof prev_key[nfd]);
|
||||
nfd++;
|
||||
}
|
||||
}
|
||||
|
||||
while (g_thread_run) {
|
||||
if (nfd == 0) { usleep(20000); continue; }
|
||||
int r = poll(fds, nfd, 20);
|
||||
if (r < 0) { if (errno == EINTR) continue; usleep(20000); continue; }
|
||||
if (r == 0) continue;
|
||||
if (r < 0) {
|
||||
if (errno != EINTR) usleep(20000);
|
||||
} else if (r > 0) {
|
||||
for (int i = 0; i < nfd; i++) {
|
||||
if (!(fds[i].revents & POLLIN)) continue;
|
||||
unsigned char ev[16];
|
||||
@@ -385,22 +457,14 @@ static void *input_thread(void *arg)
|
||||
} else if (code == MEOW_KEY_POWER) {
|
||||
/* M2.55(接 M2.24 纪律):电源=息屏/亮屏/长按菜单,由内核/sys_server/
|
||||
* watchdog 统一处理,SDK 不动、也不下发 APP。维持现状。 */
|
||||
} else if (code == MEOW_KEY_VOLUMEDOWN || code == MEOW_KEY_VOLUMEUP) {
|
||||
/* 音量键=系统音量(M2.55:SDK 输入层消化,App 前台也生效)。
|
||||
* 语义动作在 meow_input_handle_sys_key(仅按下边沿一步);随后仍下发
|
||||
* MEOW_EV_KEY(App on_key 收到语义事件,但不得映射成自定义行为)。 */
|
||||
if (val == 1 || val == 0) {
|
||||
meow_input_handle_sys_key(code, val);
|
||||
push_ev(MEOW_EV_KEY, code, (val == 1) ? 1 : 0);
|
||||
}
|
||||
} else if (code == MEOW_KEY_PLAYPAUSE || code == MEOW_KEY_PREVIOUS ||
|
||||
} else if (code == MEOW_KEY_VOLUMEDOWN || code == MEOW_KEY_VOLUMEUP ||
|
||||
code == MEOW_KEY_PLAYPAUSE || code == MEOW_KEY_PREVIOUS ||
|
||||
code == MEOW_KEY_NEXT || code == 19) {
|
||||
/* 媒体键=转发到当前媒体会话(M2.55:SDK 输入层消化)。语义动作在
|
||||
* meow_input_handle_sys_key;随后仍下发 MEOW_EV_KEY(App 不得映射)。 */
|
||||
if (val == 1 || val == 0) {
|
||||
meow_input_handle_sys_key(code, val);
|
||||
push_ev(MEOW_EV_KEY, code, (val == 1) ? 1 : 0);
|
||||
}
|
||||
/* 系统键(音量/媒体):真实物理键可能不进事件流,另经 EVIOCGKEY 状态轮询
|
||||
* 兜底(poll_sys_keys_eviocgkey);双源经 sys_key_press/sys_key_release 去重,
|
||||
* 一次按下只一步系统语义 + 一次 MEOW_EV_KEY 下发(App on_key 不得映射)。 */
|
||||
if (val == 1) sys_key_press(code);
|
||||
else if (val == 0) sys_key_release(code);
|
||||
} else if (val == 1) { /* 按下边沿(忽略重复=2)——仅未知键(本设备无) */
|
||||
push_ev(MEOW_EV_KEY, code, 1);
|
||||
} else if (val == 0) { /* 抬起 */
|
||||
@@ -410,6 +474,11 @@ static void *input_thread(void *arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
/* EVIOCGKEY 系统键状态轮询:无论事件流是否投递都跑(真实物理键可能不进事件流,
|
||||
* 与 launcher kp_poll 同源的按键位图读取兜底;事件流 + 位图双源在 sys_key_press 去重)。 */
|
||||
for (int i = 0; i < nfd; i++)
|
||||
poll_sys_keys_eviocgkey(fds[i].fd, prev_key[i]);
|
||||
}
|
||||
for (int i = 0; i < nfd; i++) close(fds[i].fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user