Skip to content

fix: USB 手柄断开恢复后 ABXY 按键可能无响应#10

Merged
qiin2333 merged 2 commits intomasterfrom
fix/usb-abxy-recovery
Apr 9, 2026
Merged

fix: USB 手柄断开恢复后 ABXY 按键可能无响应#10
qiin2333 merged 2 commits intomasterfrom
fix/usb-abxy-recovery

Conversation

@qiin2333
Copy link
Copy Markdown
Contributor

@qiin2333 qiin2333 commented Apr 8, 2026

问题

USB 驱动模式下,手柄意外断开并恢复后,ABXY 按键偶尔无响应(其他按键正常)。

根因

DDK 轮询层的 HID 报文去重缓存 (lastInputData) 在 IO 错误期间未失效。

当手柄断开前后的 HID 报文恰好相同(如都处于空闲状态),恢复后第一帧被去重逻辑跳过,导致 JS 层残留的按钮状态无法被新数据覆盖。

修复

IO 错误发生时使去重缓存失效 (lastInputValid = false)。

错误打断了数据流的连续性,去重缓存在语义上已不可信。恢复后第一帧作为新序列的起点,一定会传递给 JS 层。

// usb_ddk_poller.cpp - IO 错误处理
ctx->lastInputValid = false;  // 错误打断数据连续性,去重缓存失效

不影响长按行为:去重缓存仅控制 HID 帧是否入队,JS 层的 buttonFlags(逻辑输入状态)在瞬态错误期间照常保留。

Summary by CodeRabbit

发布说明

  • Bug Fixes
    • 修正触摸板两指滚动方向显示
    • 优化USB设备输入处理的错误恢复机制,确保故障恢复后能正确处理新输入帧

qiin2333 added 2 commits April 8, 2026 11:56
- confirmedScroll 未在手指全部抬起时重置,导致后续单指轻触被跳过
- resetGestureFlags() 补充 confirmedScroll 重置
- 滚动方向从自然滚动改为传统方向(手指下滑=页面上滚)
IO 错误打断数据连续性时,使去重缓存失效 (lastInputValid=false),
确保恢复后第一帧 HID 报文一定传递给 JS 层,
防止旧缓存导致按键状态变化被跳过。
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

📝 Walkthrough

Walkthrough

修改了两个独立模块的控制逻辑:触控板手势处理器反转了高分辨率滚动增量符号并重置确认滚动状态;USB轮询器在错误恢复时显式清除输入去重缓存,改变后续帧的处理行为。

Changes

Cohort / File(s) Summary
触控板手势处理
entry/src/main/ets/service/TrackpadGestureHandler.ets
反转滚动增量符号(dy-dy)改变两指滚动方向;在重置手势标志和手指抬起时均设置 confirmedScroll = false,确保手势间状态清空。
USB设备轮询
nativelib/src/main/cpp/usb_ddk_poller.cpp
错误恢复路径中新增 ctx->lastInputValid = false 设置,清除输入去重缓存,使恢复后的下一帧被视为新数据转发至JS回调。

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了主要改动:修复 USB 手柄断开恢复后 ABXY 按键无响应的问题,与两个提交中的核心修复相符。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/usb-abxy-recovery

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
entry/src/main/ets/service/TrackpadGestureHandler.ets (1)

254-271: 滚动方向变更:从自然滚动改为传统滚动

移除负号后,滚动方向从"自然滚动"(内容跟随手指)变为"传统滚动"(滚动条跟随手指)。这是用户可感知的行为变更。

确认这是预期行为。如果用户群体有不同偏好,考虑将滚动方向做成可配置项加入 TrackpadConfig

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@entry/src/main/ets/service/TrackpadGestureHandler.ets` around lines 254 -
271, The code changed scrolling from natural to traditional by removing the
negative sign; update handling so direction is configurable: add a boolean (e.g.
naturalScroll) to TrackpadConfig and expose it to TrackpadGestureHandler, then
compute a direction multiplier (dir = trackpadConfig.naturalScroll ? -1 : 1) and
apply it when calling sink.sendMouseHighResScroll(Math.round(dy *
SCROLL_SPEED_FACTOR * dir)); keep the existing finger state updates
(this.confirmedScroll, this.fingers) and ensure default config preserves
previous/default behavior.
nativelib/src/main/cpp/usb_ddk_poller.cpp (1)

515-547: 修复逻辑正确,缓存失效位置恰当。

此修复正确解决了 PR 描述的问题:

  1. IO 错误发生时立即使去重缓存失效(lastInputValid = false
  2. 恢复后第一帧不再与断开前最后一帧比较,避免被错误跳过
  3. lastInputValid 仅在单一轮询线程中访问,无需额外同步

可选改进:第 536 行的 napi_call_threadsafe_function 调用未检查返回值,若失败会泄漏 pedZero(与第 542-543 行对比)。这是预存问题,影响较小,可后续统一修复。

,

♻️ 可选:统一 tsfn 调用的错误处理
                     // 发送 DDK_RECOVERY_STARTED 让 JS 层归零输入
                     DdkErrorData *pedZero = (DdkErrorData *)malloc(sizeof(DdkErrorData));
                     if (pedZero) {
                         pedZero->errorCode = -1;  // DDK_RECOVERY_STARTED → 归零
                         pedZero->pollerId = pollerId;
-                        napi_call_threadsafe_function(ctx->errorTsfn, pedZero, napi_tsfn_nonblocking);
+                        napi_status st = napi_call_threadsafe_function(ctx->errorTsfn, pedZero, napi_tsfn_nonblocking);
+                        if (st != napi_ok) free(pedZero);
                     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nativelib/src/main/cpp/usb_ddk_poller.cpp` around lines 515 - 547, The
napi_call_threadsafe_function call for pedZero in the error-recovery branch does
not check its return value and can leak pedZero on failure; update the block
that allocates pedZero (DdkErrorData *pedZero) to capture the napi_status result
of napi_call_threadsafe_function(ctx->errorTsfn, pedZero,
napi_tsfn_nonblocking), and if the call fails free(pedZero); apply the same
pattern used for ped (check napi_status and free on failure) so both pedZero and
ped are freed when napi_call_threadsafe_function returns a non-ok status,
referencing pedZero, ped, napi_call_threadsafe_function, ctx->errorTsfn and
DdkErrorData to locate the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@entry/src/main/ets/service/TrackpadGestureHandler.ets`:
- Around line 254-271: The code changed scrolling from natural to traditional by
removing the negative sign; update handling so direction is configurable: add a
boolean (e.g. naturalScroll) to TrackpadConfig and expose it to
TrackpadGestureHandler, then compute a direction multiplier (dir =
trackpadConfig.naturalScroll ? -1 : 1) and apply it when calling
sink.sendMouseHighResScroll(Math.round(dy * SCROLL_SPEED_FACTOR * dir)); keep
the existing finger state updates (this.confirmedScroll, this.fingers) and
ensure default config preserves previous/default behavior.

In `@nativelib/src/main/cpp/usb_ddk_poller.cpp`:
- Around line 515-547: The napi_call_threadsafe_function call for pedZero in the
error-recovery branch does not check its return value and can leak pedZero on
failure; update the block that allocates pedZero (DdkErrorData *pedZero) to
capture the napi_status result of napi_call_threadsafe_function(ctx->errorTsfn,
pedZero, napi_tsfn_nonblocking), and if the call fails free(pedZero); apply the
same pattern used for ped (check napi_status and free on failure) so both
pedZero and ped are freed when napi_call_threadsafe_function returns a non-ok
status, referencing pedZero, ped, napi_call_threadsafe_function, ctx->errorTsfn
and DdkErrorData to locate the code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a985e82d-2f37-4440-bf65-57628e5c1b44

📥 Commits

Reviewing files that changed from the base of the PR and between 81ec172 and 4bd7981.

📒 Files selected for processing (2)
  • entry/src/main/ets/service/TrackpadGestureHandler.ets
  • nativelib/src/main/cpp/usb_ddk_poller.cpp

@qiin2333 qiin2333 merged commit 66bda9e into master Apr 9, 2026
2 checks passed
@qiin2333 qiin2333 deleted the fix/usb-abxy-recovery branch April 9, 2026 03:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant