JIS かな Key Detection in Karukan: Returning to Hiragana Mode on macOS
Karukan's macOS frontend intercepts the JIS かな key (keyCode 104) and translates it into a Super_R keysym (0xffec) that commands the input engine to toggle back to hiragana mode, consuming the event to prevent duplicate system processing.
Karukan is an open-source Japanese input method editor that provides consistent behavior across Linux and macOS platforms. Understanding how JIS かな key detection functions on macOS requires examining the platform-specific adapter code in karukan-macos. This implementation ensures that pressing the physical かな key instantly returns the input mode to hiragana without interfering with the system's normal key-processing pipeline.
How JIS かな Key Detection Works
The macOS implementation follows a strict interception-and-translation pattern to handle the hardware-specific JIS かな key. This process involves identifying the physical key event, blocking default system behavior, and issuing a cross-platform command to the core engine.
Key Code Identification
In KeyCodeMap.swift, Karukan defines the macOS virtual key code for the JIS かな key as a constant value. This represents the hardware key code (kVK_JIS_Kana) that macOS reports when users press the かな key on JIS-layout keyboards.
/// JIS keyboard かな key (kVK_JIS_Kana).
static let kanaKeyCode: UInt16 = 104
/// XKB Super_R keysym — the engine's katakana→hiragana toggle.
static let superRKeysym: UInt32 = 0xffec
The superRKeysym constant (0xffec) serves as the universal signal across platforms. While macOS uses the JIS-specific key code 104, the engine expects the standard XKB Super_R keysym to trigger the hiragana toggle.
Event Interception in KarukanInputController
Inside KarukanInputController.swift, the handle(_:) method acts as the primary entry point for all key events. When the controller detects the JIS かな key, it immediately consumes the event by returning true. This prevents macOS from inserting the raw key code after the engine processes it, avoiding duplicate input.
// JIS かな key (and Karabiner right-Command tap → かな): always
// consume so the system doesn't process keyCode 104 after the engine
// returns not_consumed (already in hiragana mode).
if event.keyCode == KeyCodeMap.kanaKeyCode {
let key = EngineKeyEvent(keysym: KeyCodeMap.superRKeysym,
modifiers: KeyModifiers())
if let result = engineClient.processKeySync(key) {
apply(actions: result.actions, client: client)
}
return true
}
The EngineKeyEvent struct encapsulates the keysym and modifier state, creating a platform-agnostic message that the Rust-based core engine can interpret regardless of the operating system.
Engine Processing and Mode Toggle
Once the fabricated event reaches the engine via engineClient.processKeySync(key), the core logic in engine/src/core/keycode.rs interprets the 0xffec keysym as a direct command to switch input modes. The engine toggles its internal state from katakana back to hiragana and returns a set of actions to update the user interface.
The synchronous processing ensures that the mode change completes before the method returns, allowing the controller to immediately apply the results to the text client through apply(actions:client:).
Implementation Details and Code Examples
The following simplified example demonstrates how to intercept the JIS かな key in a custom InputMethodKit controller:
func handleKanaKey(event: NSEvent, client: IMKTextInput) {
// Detect JIS kana key using the defined constant
guard event.keyCode == KeyCodeMap.kanaKeyCode else { return }
// Translate to engine toggle keysym without modifiers
let toggle = EngineKeyEvent(keysym: KeyCodeMap.superRKeysym,
modifiers: KeyModifiers())
// Send to the engine and apply UI updates synchronously
if let result = engineClient.processKeySync(toggle) {
apply(actions: result.actions, client: client)
}
}
From the engine perspective, the Rust implementation handles the toggle keysym as follows:
// In the engine (karukan-im), the Super_R keysym toggles Katakana→Hiragana
match keysym {
0xffec => self.toggle_input_mode(),
_ => … // regular processing
}
Summary
- KeyCodeMap.swift defines
kanaKeyCode(104) andsuperRKeysym(0xffec) to bridge macOS hardware codes with the engine's expected input. - KarukanInputController.swift intercepts keyCode 104 and consumes the event to prevent system-level duplicate processing.
- The controller fabricates an
EngineKeyEventwith the Super_R keysym and dispatches it viaengineClient.processKeySync(key). - The engine core interprets 0xffec as a hiragana toggle command, switching modes and returning UI update actions.
- This architecture ensures consistent JIS かな key behavior across macOS and Linux platforms while maintaining separation between platform-specific event handling and input logic.
Frequently Asked Questions
What is the virtual key code for the JIS かな key on macOS?
The JIS かな key maps to virtual key code 104 (kVK_JIS_Kana) in macOS. Karukan defines this as KeyCodeMap.kanaKeyCode in KeyCodeMap.swift to identify the physical key regardless of keyboard layout variations.
Why does Karukan use the Super_R keysym for the かな key?
Karukan uses the Super_R keysym (0xffec) as a cross-platform standard to represent the hiragana toggle command. While macOS reports the JIS かな key as code 104, the Rust-based engine expects standard XKB keysyms. This abstraction allows the same core logic to handle the toggle on both macOS and Linux (Fcitx5) without platform-specific code in the engine.
How does Karukan prevent duplicate character insertion when handling the かな key?
The KarukanInputController.handle(_:) method returns true immediately after processing the JIS かな key, which marks the event as consumed in the InputMethodKit pipeline. This prevents macOS from passing the raw key code 104 to the active application after the engine has already handled the mode switch, eliminating duplicate input.
Can the JIS かな key detection be customized for different keyboard layouts?
Currently, the key code detection is hardcoded to value 104 in KeyCodeMap.swift. Users employing remapping tools (such as Karabiner-Elements) can configure their systems to emit keyCode 104 for other physical keys (like right-Command tap), and Karukan will treat these remapped events identically to native JIS かな key presses.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →