Skip to content

handle physical keyboard key states#52

Open
WhiredPlanck wants to merge 2 commits into
mainfrom
modifier
Open

handle physical keyboard key states#52
WhiredPlanck wants to merge 2 commits into
mainfrom
modifier

Conversation

@WhiredPlanck

@WhiredPlanck WhiredPlanck commented Jun 28, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • Refactor
    • Updated SDK version targets (compatible: 6.0.0, target: 6.1.1).
    • Enhanced keyboard input handling to properly detect and process key modifier states (Shift, Ctrl, Alt, Caps Lock, etc.), improving key event accuracy and responsiveness in the input method.

@github-actions

Copy link
Copy Markdown

arm64-v8a comparison

No difference.

x86_64 comparison

No difference.

compatibleSdkVersion: 6.0.0(20)
targetSdkVersion: 6.1.1(24)
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds key state bitmask support to the input event pipeline. The changes thread a states parameter through the C++ Fcitx backend, N-API binding, and TypeScript types, then update the HarmonyOS ETS keyboard handler to compute and pass modifier states from KeyEvent to the C++ layer. A new KeyState enum and converter function bridge HarmonyOS modifier flags to a numeric bitmask format.

Changes

Key State Bitmask Integration

Layer / File(s) Summary
C++ key state contract and implementation
entry/src/main/cpp/src/fcitx.h, entry/src/main/cpp/src/fcitx.cpp, entry/src/main/cpp/src/keycode.h, entry/src/main/cpp/src/keycode.cpp
fcitx::processKey and fcitx::ohKeyToFcitxKey signatures are updated to accept a states parameter (key-state bitmask). Implementation threads this through key conversion and event processing. <cstdint> headers added for fixed-width integer types.
N-API binding and TypeScript types
entry/src/main/cpp/napi_init.cpp, entry/src/main/cpp/types/libentry/Index.d.ts
N-API processKey binding now reads four arguments (unicode, keyCode, states, isRelease) and passes all four to the C++ function. TypeScript definition updated to match the four-parameter signature.
KeyState enum and converter
entry/src/main/ets/InputMethodExtensionAbility/model/KeyState.ets
New KeyState enum defines modifier and lock state bitmasks (SHIFT, CTRL, ALT, CAPS_LOCK, NUM_LOCK, META, plus combined and virtual masks). Function statesFromKeyEvent(e: KeyEvent) derives a numeric states bitmask from the KeyEvent's boolean modifier properties.
KeyboardController event handling update
entry/src/main/ets/InputMethodExtensionAbility/model/KeyboardController.ets
Import list updated to include Action, KeyAction, KeyEvent for richer event data. Physical key event handler replaced: computes release state from Action.UP, derives states via statesFromKeyEvent, routes unicode character and key code inputs through fcitx.processKey with derived states. Listener registration switched from keyDown/keyUp callbacks to unified keyEvent listener.
SDK version targets update
build-profile.json5
HarmonyOS SDK compatibleSdkVersion and targetSdkVersion updated to new API levels.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 A state of each key we now declare,
Modifier bitmasks flow through the air,
From ETS down to C++, with care,
Shift, Ctrl, Alt—no key unaware! 🔑

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'handle physical keyboard key states' directly reflects the main objective: adding support for keyboard key states (modifier flags and release state) across the input pipeline. The PR modifies key event handling to compute and pass key states from KeyEvent to the C++ backend.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 modifier

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Infer (1.2.0)
entry/src/main/cpp/napi_init.cpp

entry/src/main/cpp/napi_init.cpp:3:10: fatal error: 'fcitx.h' file not found
3 | #include "fcitx.h"
| ^~~~~~~~~
1 error generated.
entry/src/main/cpp/napi_init.cpp:33:5-12: ERROR translating statement 'ReturnStmt'
Aborting translation of method 'reset' in file 'entry/src/main/cpp/napi_init.cpp': "Assert_failure src/clang/cAst_utils.ml:249:53"
Uncaught Internal Error: "Assert_failure src/clang/cAst_utils.ml:249:53"
Error backtrace:
Raised at ClangFrontend__CAst_utils.get_decl_from_typ_ptr in file "src/clang/cAst_utils.ml", line 249, characters 53-65
Called from ClangFrontend__CTrans.CTrans_funct.get_destructor_decl_ref in file "src/clang/cTrans.ml", line 658, characters 12-59
Called from ClangFrontend__CTrans.CTrans_funct.destructor_calls.(fun) in file "src/clang/cTrans.ml", line 2048, characters 12-69
Called from Base__List.rev_filter_map.loop in file "src/list.ml", line 944, characters 13-17
Called from Base__List.filter_map in file "src/list.ml" (inlined), line 951

... [truncated 2200 characters] ...

Called from ClangFrontend__CTrans.CTrans_funct.instruction in file "src/clang/cTrans.ml" (inlined), line 4765, characters 38-71
Called from ClangFrontend__CTrans.CTrans_funct.exec_with_node_creation in file "src/clang/cTrans.ml" (inlined), line 104, characters 20-38
Called from ClangFrontend__CTrans.CTrans_funct.get_clang_stmt_trans in file "src/clang/cTrans.ml" (inlined), line 5395, characters 4-69
Called from ClangFrontend__CTrans.CTrans_funct.get_custom_stmt_trans in file "src/clang/cTrans.ml", line 5401, characters 8-55
Called from ClangFrontend__CTrans.CTrans_funct.exec_trans_instrs.exec_trans_instrs_rev in file "src/clang/cTrans.ml" (inlined), line 5365, characters 28-54
Called from ClangFrontend__CTrans.CTrans_funct.exec_trans_instrs in file "src/clang/cTrans.ml" (inlined), line 538

entry/src/main/cpp/src/keycode.cpp

In file included from entry/src/main/cpp/src/keycode.cpp:3:
entry/src/main/cpp/src/keycode.h:4:10: fatal error: 'fcitx-utils/key.h' file not found
4 | #include <fcitx-utils/key.h>
| ^~~~~~~~~~~~~~~~~~~
1 error generated.
entry/src/main/cpp/src/keycode.cpp:277:73-280:1: ERROR translating statement 'CompoundStmt'
Aborting translation of method 'fcitx::ohKeyToFcitxKey' in file 'entry/src/main/cpp/src/keycode.cpp': "Assert_failure src/clang/cAst_utils.ml:249:53"
Uncaught Internal Error: "Assert_failure src/clang/cAst_utils.ml:249:53"
Error backtrace:
Raised at ClangFrontend__CAst_utils.get_decl_from_typ_ptr in file "src/clang/cAst_utils.ml", line 249, characters 53-65
Called from ClangFrontend__CTrans.CTrans_funct.get_destructor_decl_ref in file "src/clang/cTrans.ml", line 658, characters 12-59
Called from ClangFrontend__CTrans.CTrans_funct.destructor_calls.(fun) in file "src/clang/cTrans.ml", line 2048, characters 12-69
Called from Base__List.rev_filter_map.loop in file

... [truncated 2200 characters] ...

ontend_decl.CFrontend_decl_funct.add_method in file "src/clang/cFrontend_decl.ml" (inlined), line 54, characters 4-52
Called from ClangFrontend__CFrontend_decl.CFrontend_decl_funct.function_decl in file "src/clang/cFrontend_decl.ml", line 90, characters 12-151
Called from ClangFrontend__CFrontend_decl.CFrontend_decl_funct.translate_one_declaration in file "src/clang/cFrontend_decl.ml", line 453, characters 10-56
Called from ClangFrontend__CFrontend_decl.CFrontend_decl_funct.translate_one_declaration.translate in file "src/clang/cFrontend_decl.ml" (inlined), line 448, characters 24-96
Called from Stdlib__List.iter in file "list.ml", line 110, characters 12-15
Called from Stdlib__List.iter in file "list.ml", line 110, characters 12-15
Called from Stdlib__List.iter in file "list.ml" (inlined)

entry/src/main/cpp/src/fcitx.cpp

In file included from entry/src/main/cpp/src/fcitx.cpp:1:
entry/src/main/cpp/src/fcitx.h:5:10: fatal error: 'napi/native_api.h' file not found
5 | #include "napi/native_api.h"
| ^~~~~~~~~~~~~~~~~~~
1 error generated.
entry/src/main/cpp/src/fcitx.cpp:33:41-36:1: ERROR translating statement 'CompoundStmt'
Aborting translation of method 'notify_main_async' in file 'entry/src/main/cpp/src/fcitx.cpp': "Assert_failure src/clang/cAst_utils.ml:249:53"
Uncaught Internal Error: "Assert_failure src/clang/cAst_utils.ml:249:53"
Error backtrace:
Raised at ClangFrontend__CAst_utils.get_decl_from_typ_ptr in file "src/clang/cAst_utils.ml", line 249, characters 53-65
Called from ClangFrontend__CTrans.CTrans_funct.get_destructor_decl_ref in file "src/clang/cTrans.ml", line 658, characters 12-59
Called from ClangFrontend__CTrans.CTrans_funct.destructor_calls.(fun) in file "src/clang/cTrans.ml", line 2048, characters 12-69
Called from Base__List.rev_filter_map.loop in file "src/list.ml",

... [truncated 2200 characters] ...

ontend_decl_funct.add_method in file "src/clang/cFrontend_decl.ml" (inlined), line 54, characters 4-52
Called from ClangFrontend__CFrontend_decl.CFrontend_decl_funct.function_decl in file "src/clang/cFrontend_decl.ml", line 90, characters 12-151
Called from ClangFrontend__CFrontend_decl.CFrontend_decl_funct.translate_one_declaration in file "src/clang/cFrontend_decl.ml", line 453, characters 10-56
Called from Stdlib__List.iter in file "list.ml", line 110, characters 12-15
Called from Stdlib__List.iter in file "list.ml" (inlined), line 110, characters 17-25
Called from Base__List0.iter in file "src/list0.ml" (inlined), line 25, characters 16-35
Called from ClangFrontend__CFrontend.compute_icfg in file "src/clang/cFrontend.ml", line 28, characters 6-130
Called from ClangFrontend__Capture.run


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
entry/src/main/ets/InputMethodExtensionAbility/model/KeyboardController.ets (1)

403-403: ⚡ Quick win

Use strict equality operator.

Replace != with !== for type-safe comparison.

🔧 Proposed fix
-    if (e.key.code != KeyCode.KEYCODE_UNKNOWN) {
+    if (e.key.code !== KeyCode.KEYCODE_UNKNOWN) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@entry/src/main/ets/InputMethodExtensionAbility/model/KeyboardController.ets`
at line 403, In KeyboardController (the conditional checking key codes), replace
the non-strict comparison in the if statement that tests e.key.code against
KeyCode.KEYCODE_UNKNOWN by using the strict equality operator (===) or strict
inequality (!==) as appropriate; locate the expression "e.key.code !=
KeyCode.KEYCODE_UNKNOWN" and change it to "e.key.code !==
KeyCode.KEYCODE_UNKNOWN" to ensure type-safe comparison in the code that handles
key events.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@build-profile.json5`:
- Around line 8-9: The build-profile.json5 change raises compatibleSdkVersion to
"6.0.0(20)" but there’s no rationale tying that SDK bump to the keyboard code
which listens to keyboardDelegate.on('keyEvent', ...) and computes modifier
state via statesFromKeyEvent; either add a concise justification in the PR or in
build-profile.json5 explaining exactly which HarmonyOS API(s) (e.g.,
KeyEvent.getModifierKeyState or specific properties) require 6.0.0(20) and cite
the minimal API level, or reduce compatibleSdkVersion to the lowest SDK that
actually supports the used modifier APIs after verifying support for
KeyEvent.getModifierKeyState (API 12+) and any isShiftDown/isCtrlDown
equivalents in your statesFromKeyEvent implementation.

---

Nitpick comments:
In `@entry/src/main/ets/InputMethodExtensionAbility/model/KeyboardController.ets`:
- Line 403: In KeyboardController (the conditional checking key codes), replace
the non-strict comparison in the if statement that tests e.key.code against
KeyCode.KEYCODE_UNKNOWN by using the strict equality operator (===) or strict
inequality (!==) as appropriate; locate the expression "e.key.code !=
KeyCode.KEYCODE_UNKNOWN" and change it to "e.key.code !==
KeyCode.KEYCODE_UNKNOWN" to ensure type-safe comparison in the code that handles
key events.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f2ea28ea-aa07-478e-953c-0390a07bc9da

📥 Commits

Reviewing files that changed from the base of the PR and between 19d6129 and 09ea75e.

📒 Files selected for processing (9)
  • build-profile.json5
  • entry/src/main/cpp/napi_init.cpp
  • entry/src/main/cpp/src/fcitx.cpp
  • entry/src/main/cpp/src/fcitx.h
  • entry/src/main/cpp/src/keycode.cpp
  • entry/src/main/cpp/src/keycode.h
  • entry/src/main/cpp/types/libentry/Index.d.ts
  • entry/src/main/ets/InputMethodExtensionAbility/model/KeyState.ets
  • entry/src/main/ets/InputMethodExtensionAbility/model/KeyboardController.ets

Comment thread build-profile.json5
Comment on lines +8 to +9
"compatibleSdkVersion": "6.0.0(20)",
"targetSdkVersion": "6.1.1(24)",

@coderabbitai coderabbitai Bot Jun 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check for SDK version requirement documentation in the repository

# Check for any documentation about SDK version requirements
rg -i "sdk.*(version|6\.0|6\.1|requirement)" --type md --type txt -C 2

# Check for KeyEvent API usage that might require specific SDK versions
rg -n "KeyEvent|keyEvent|isShiftDown|isCtrlDown" entry/src/main/ets/ -C 2

Repository: fcitx-contrib/fcitx5-harmony

Length of output: 4258


🌐 Web query:

Do HarmonyOS SDK 6.0.0(20) and 6.1.1(24) introduce new keyboard event APIs such as KeyEvent modifier properties (isShiftDown, isCtrlDown, isAltDown, isMetaDown) or the keyEvent listener that are used in fcitx5-harmony?

💡 Result:

HarmonyOS SDK 6.0.0(20) and 6.1.1(24) do not introduce specific property-based modifier APIs like isShiftDown, isCtrlDown, isAltDown, or isMetaDown directly on the KeyEvent object [1][2][3]. Instead, these SDK versions utilize the getModifierKeyState(keys: Array<string>): boolean method, which was introduced in API version 12 and is available in HarmonyOS 6.x [1][2][4]. Key takeaways regarding your query: 1. Modifier Key Handling: Rather than individual boolean properties, developers should use event.getModifierKeyState(['Ctrl', 'Shift', 'Alt', 'Fn']) to check the pressed status of modifier keys [1][2][4]. This method provides a centralized and cleaner way to handle combinations compared to checking individual flags [4]. 2. Input Listener: The onKeyEvent listener is the standard mechanism for handling physical keyboard events in ArkTS [5][6]. Additionally, in HarmonyOS 6, the onKeyPreIme callback is available to intercept key events before they reach the input method framework, providing more control for custom keyboard or text-processing applications [4][7]. 3. API Compatibility: The getModifierKeyState API is supported from API version 12 onwards, making it fully available in the HarmonyOS 6.0.0(20) and 6.1.1(24) SDKs [1][2][4]. For fcitx5-harmony or similar input method implementations, the InputMethodEngine also supports subscribing to keyEvent via getKeyboardDelegate().on('keyEvent', callback), which provides the KeyEvent object containing these interaction capabilities [8][9].

Citations:


Document/justify compatibleSdkVersion: "6.0.0(20)" for HarmonyOS keyboard keyEvent handling

build-profile.json5 bumps the minimum to compatibleSdkVersion: "6.0.0(20)" (while targetSdkVersion is "6.1.1(24)"). The keyboard code listens to keyboardDelegate.on('keyEvent', ...) and computes modifier state from KeyEvent (e.g., e.altKey via statesFromKeyEvent). There’s no accompanying documentation/rationale in the repo tying 6.0.0(20) (or 6.x generally) specifically to the APIs used, and HarmonyOS docs describe modifier handling via KeyEvent.getModifierKeyState(...) (API 12+) rather than isShiftDown/isCtrlDown-style properties—so this SDK bump isn’t clearly justified by the modifier approach.

Add a short rationale in the PR/file (or lower compatibleSdkVersion if the used keyEvent + modifier APIs are available on earlier SDK levels).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build-profile.json5` around lines 8 - 9, The build-profile.json5 change
raises compatibleSdkVersion to "6.0.0(20)" but there’s no rationale tying that
SDK bump to the keyboard code which listens to keyboardDelegate.on('keyEvent',
...) and computes modifier state via statesFromKeyEvent; either add a concise
justification in the PR or in build-profile.json5 explaining exactly which
HarmonyOS API(s) (e.g., KeyEvent.getModifierKeyState or specific properties)
require 6.0.0(20) and cite the minimal API level, or reduce compatibleSdkVersion
to the lowest SDK that actually supports the used modifier APIs after verifying
support for KeyEvent.getModifierKeyState (API 12+) and any
isShiftDown/isCtrlDown equivalents in your statesFromKeyEvent implementation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Our CI SDK is outdated so this fails. Is it needed for your changes?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the new KeyState.ets file for specific KeyEvent properties used
cat entry/src/main/ets/InputMethodExtensionAbility/model/KeyState.ets

echo "---"
# Check KeyboardController.ets for the physical key event handler
cat entry/src/main/ets/InputMethodExtensionAbility/model/KeyboardController.ets

Length of output: 18370


🌐 Web query:

HarmonyOS InputKit KeyEvent unicodeChar numLock logoKey API level introduced version

Length of output: 11227


🌐 Web query:

HarmonyOS inputMethodEngine KeyboardDelegate on keyEvent API level version introduced js-apis-inputmethodengine

Length of output: 19060



@eagleoflqj, based on the docs research, the SDK bump does not appear to be required for the key state handling changes in this PR:

  • keyboardDelegate.on('keyEvent', ...) is marked API 10+
  • All InputKit KeyEvent properties used in statesFromKeyEvent (altKey, ctrlKey, shiftKey, capsLock, numLock, logoKey) and in physicalKeyEventHandler (unicodeChar, action, key.code) are available since API 9

The old compatibleSdkVersion: "5.0.1(13)" (API level 13) already covers all of these. The build-profile.json5 change can safely be reverted independently of the keyboard key state changes, which should unblock your CI.

Comment thread build-profile.json5
Comment on lines +8 to +9
"compatibleSdkVersion": "6.0.0(20)",
"targetSdkVersion": "6.1.1(24)",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Our CI SDK is outdated so this fails. Is it needed for your changes?

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.

2 participants