From 87f4ad0a82345667bad046854ba5d7ae1ad4e3dc Mon Sep 17 00:00:00 2001 From: AskaEth Date: Fri, 6 Nov 2026 11:02:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=BE=E5=BA=93:=20=E9=94=81=E5=B1=8F?= =?UTF-8?q?=E9=95=BF=E6=8C=89=E9=80=80=E6=A0=BC=E5=9C=A8=E7=9C=9F=E6=9C=BA?= =?UTF-8?q?=E5=8F=AF=E8=BE=BE=EF=BC=88=E4=BB=85=E5=9C=A8=E9=94=81=E5=B1=8F?= =?UTF-8?q?=E9=A1=B5=E5=90=AF=E7=94=A8=E9=95=BF=E6=8C=89=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M29 评审 P2-1:SDK 长按是 opt-in,全仓没有运行时调用 meow_ui_set_long_press_cb,meow_input_long_press_poll 恒返回 0,真机上 MEOW_EV_LONG_PRESS 永不产生——「长按退格清空」只表现为删一位。 - app.c 注册 no-op 长按回调,但只在 ACT_LOCK 为顶层页时启用(进主循环前对齐 一次 + on_tick 每拍对齐),解锁 pop 后置 NULL。全程开启会让长按触发后的 抬手不再产生 TAP/SWIPE,吞掉书库/阅读器里「按住 >=500ms」的点击。 - test_settings_lock_host:长按用例改走真实 SDK 路径——未注册 poll==0(P2 根因)、注册后按住 700ms 产出 LONG_PRESS 且坐标=退格键心、被锁屏 view 消费、 抬手不补发 TAP、解锁后关掉再 poll==0。 - lock.c 头注释补上该 opt-in 依赖,避免以后只看锁屏页时误判可达。 --- tuku/app.c | 25 ++++++++++++++++ tuku/test/test_settings_lock_host.c | 45 +++++++++++++++++++++++------ tuku/ui/lock/lock.c | 4 ++- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/tuku/app.c b/tuku/app.c index 1e56c25..9be1600 100644 --- a/tuku/app.c +++ b/tuku/app.c @@ -81,8 +81,32 @@ static void app_on_swipe(int dir, int dx, int dy) if (app_active() == ACT_LIBRARY) library_on_swipe(dir, dx); } +/* 通用长按(SDK §6.3)是 opt-in:未注册回调时 meow_input_long_press_poll 直接返回 0, + * MEOW_EV_LONG_PRESS 永远不会产生 —— 锁屏的「长按退格清空」在真机上就退化成单删一位。 + * 这里注册一个 no-op 回调把长按打开;事件仍然先经 meow_view_dispatch_event 路由,锁屏页 + * 自己消费 LONG_PRESS(其它页命中不到就走 no-op)。 + * + * 只在锁屏为顶层页时启用:长按一旦触发,其抬手不再产生 TAP/SWIPE(SDK 抑制点击), + * 全程开启会让书库/阅读器里「按住稍久」的点击被吞掉。锁屏页解锁 pop 后由 app_on_tick + * 下一拍关掉,避免把长按语义泄漏到其它页。 */ +static int g_long_press_on; + +static void app_on_long_press(int x, int y) +{ + (void)x; (void)y; /* 锁屏已在 view 分发里消费;其余页没有长按语义 */ +} + +static void app_sync_long_press(void) +{ + int on = (app_active() == ACT_LOCK); + if (on == g_long_press_on) return; + meow_ui_set_long_press_cb(on ? app_on_long_press : NULL); + g_long_press_on = on; +} + static void app_on_tick(void) { + app_sync_long_press(); library_on_tick(); } @@ -126,6 +150,7 @@ int main(void) meow_view *lock = lock_view_create(); meow_view_page_push(lock_view_should_show() ? ACT_LOCK : ACT_LIBRARY); + app_sync_long_press(); /* 进主循环前先对齐一次:启动即锁屏时首帧长按就可用 */ meow_ui_events ev = { app_on_key, app_on_tap, app_on_swipe, app_on_tick, diff --git a/tuku/test/test_settings_lock_host.c b/tuku/test/test_settings_lock_host.c index 93cebd7..32be336 100644 --- a/tuku/test/test_settings_lock_host.c +++ b/tuku/test/test_settings_lock_host.c @@ -11,7 +11,8 @@ * 主题模式切换后整帧重绘不崩,以及两条端到端路径: * - **锁屏页内自绘 12 键数字键盘**(1-9 / 退格·0·确认):错 PIN 留在锁屏 → 对 PIN * page_pop 回书库;位数不足确认不可用、超长输入截断、退格/长按清空、错误后清空重输; - * 键高/间距/屏底 28px 热区几何契约; + * 键高/间距/屏底 28px 热区几何契约;长按退格走真实 SDK opt-in 路径(未注册回调不产生 + * MEOW_EV_LONG_PRESS = M29 评审 P2 的根因,注册后才清空); * - 设置页两遍输入开启 PIN(仍走真实 MEOW_IME_NUM 键盘注入)。 * * 编译:见 test/run_tests.sh —— 它把本测试、settings/lock/pin 源码与 SDK 源码 @@ -124,12 +125,8 @@ static void lock_type(meow_view *lock, const char *digits) for (const char *s = digits; *s; s++) lock_tap_key(lock, lock_key_idx(*s)); } -static void lock_long_back(meow_view *lock) -{ - int cx, cy; - if (lock_key_center(lock, LK_KBACK, &cx, &cy)) - meow_view_dispatch_event(MEOW_EV_LONG_PRESS, cx, cy); -} +/* app.c 注册的 no-op 长按回调(事件先经 view 分发,锁屏页消费;其它页落到这里)。 */ +static void app_long_press_noop(int x, int y) { (void)x; (void)y; } static void settings_tap_pin_row(void){ /* 布局:sb + 56 顶栏 + 6 + 段头34 + 方向行64 + 12 + 段头34 → PIN 列表首行中心 +32 */ @@ -287,16 +284,46 @@ int main(void) lock_tap_key(lock, LK_KOK); CHECK(meow_ui_activity_active() == ACT_LIBRARY, "backspace on empty buffer is a no-op"); - /* 长按退格清空:先输 3 位 → 长按清空 → 再输 4 位正确 PIN 才解锁。 */ + /* 长按退格清空(M29 评审 P2-1)走真实 SDK 路径: + * SDK 长按是 opt-in —— 未注册回调时 meow_input_long_press_poll 恒返回 0, + * MEOW_EV_LONG_PRESS 永远不会产生(真机上「长按清空」只会退化成单删一位,P2 的根因); + * app.c 在锁屏为顶层页时注册 no-op 回调后,按住 700ms(≥500 阈值)应真正产出 LONG_PRESS, + * 并按主循环顺序先交 view 分发(锁屏页消费),且其抬手不再补一个 TAP。 */ meow_view_page_push(ACT_LOCK); + int bx = 0, by = 0; + CHECK(lock_key_center(lock, LK_KBACK, &bx, &by), "backspace key has a rect"); + + meow_ui_set_long_press_cb(NULL); + meow_input_touch_set(1, bx, by, 700); + CHECK(meow_input_long_press_poll() == 0, + "no LONG_PRESS while callback unregistered (opt-in gate = P2 root cause)"); + meow_input_touch_release(); + meow_input_drain(); /* 未成长按的这次按压,抬手会补一个 TAP,清掉 */ + lock_type(lock, "135"); - lock_long_back(lock); + meow_ui_set_long_press_cb(app_long_press_noop); /* 同 app.c:锁屏为顶层页时注册 */ + meow_input_touch_set(1, bx, by, 700); + CHECK(meow_input_long_press_poll() == 1, "registered long-press crosses the 500ms threshold"); + meow_input_ev lp = { 0, 0, 0 }; + CHECK(meow_input_pop(&lp) == 1 && lp.type == MEOW_EV_LONG_PRESS && + lp.a == bx && lp.b == by, "LONG_PRESS queued at the pressed backspace key"); + CHECK(meow_view_dispatch_event(MEOW_EV_LONG_PRESS, lp.a, lp.b) == 1, + "lock view consumes LONG_PRESS before the no-op callback"); + meow_input_touch_release(); + CHECK(meow_input_pop(&lp) == 0, "long-press release does not emit a stray TAP"); lock_tap_key(lock, LK_KOK); CHECK(meow_ui_activity_active() == ACT_LOCK, "long-press backspace cleared input"); lock_type(lock, "1357"); lock_tap_key(lock, LK_KOK); CHECK(meow_ui_activity_active() == ACT_LIBRARY, "unlock after long-press clear"); + /* 解锁后 app.c 关掉长按:不得把长按语义泄漏到书库/阅读器(按住稍久的点击仍算 TAP)。 */ + meow_ui_set_long_press_cb(NULL); + meow_input_touch_set(1, bx, by, 700); + CHECK(meow_input_long_press_poll() == 0, "long-press off again after leaving the lock page"); + meow_input_touch_release(); + meow_input_drain(); + /* ---- 设置页两遍输入开启 PIN(MEOW_IME_NUM) ---- */ mr_pin_disable(); meow_view_page_push(ACT_SETTINGS); diff --git a/tuku/ui/lock/lock.c b/tuku/ui/lock/lock.c index 542d6ff..159d198 100644 --- a/tuku/ui/lock/lock.c +++ b/tuku/ui/lock/lock.c @@ -8,7 +8,9 @@ * 「点好几下才弹键盘」;页内键盘完全离线、无 IME 会话,首次点按必响应; * - 4-8 位数字(logic/pin 的 MR_PIN_MIN_LEN/MAX_LEN),顶部只显示星号/圆点数位,不回显明文; * 满 4 位后「确认」才可用;错误 → 清空 + 提示;通过 → page_pop 回书库; - * - 长按「退格」清空已输入(不做惩罚性计数/锁定); + * - 长按「退格」清空已输入(不做惩罚性计数/锁定);SDK 长按是 opt-in,需 app.c 在锁屏 + * 为顶层页时注册 meow_ui_set_long_press_cb(事件仍先走本页 view 分发),否则真机上 + * MEOW_EV_LONG_PRESS 不会产生、长按只会退化成单删一位; * - 左缘返回**必须**由本页 on_event 消费 SWIPE(meow_view_dispatch_event 先于 app.c 的 * on_swipe;否则 app.c 对非 library 页无条件 page_pop,PIN 会被绕过);TAP/LONG_PRESS * 也一律消费,物理键不接(返回 0,交 app.c 的 on_key 仅日志);屏底 28px 上滑热区