How Shift+Letter Triggers Alphabet Mode Entry on Linux Fcitx5 with Karukan

Karukan detects Shift+letter combinations in its Fcitx5 front-end by checking for uppercase keysyms or Shift-modified ASCII alphabetic characters, then immediately switches the input_mode to Alphabet and inserts the character directly without romaji conversion.

Karukan is a Japanese input method engine that implements intelligent romaji-to-kana conversion for Linux systems via Fcitx5. Understanding how it handles Shift+letter alphabet mode entry on Linux Fcitx5 is crucial for users who need to seamlessly switch between Japanese kana and direct ASCII input. The implementation relies on detecting specific key combinations in the core engine logic and dynamically switching the input mode before processing the character.

Detecting Shift+Letter Combinations

The detection logic resides in karukan-im/src/core/engine/input.rs within the main key processing flow. When the engine receives a printable character event, it evaluates whether the combination constitutes a Shift+letter press using a dual-check strategy:

// karukan-im/src/core/engine/input.rs
if let Some(ch) = key.to_char()
    && !key.modifiers.control_key
    && !key.modifiers.alt_key
{
    // Detect Shift+letter: uppercase keysym OR Shift modifier + ASCII alphabetic
    let is_shift_alpha =
        ch.is_ascii_uppercase() || (shift_active && ch.is_ascii_alphabetic());
    
    // ... mode switching logic
}

This approach accounts for Fcitx5 implementation variations where the system may emit either a lowercase keysym with the Shift flag set, or an already-uppercase keysym. The shift_active variable represents the current modifier state from the KeyEvent struct.

Switching from Hiragana/Katakana to Alphabet Mode

When is_shift_alpha evaluates to true while the engine is in Hiragana or Katakana mode, the code performs an immediate state transition:

if is_shift_alpha && self.input_mode != InputMode::Alphabet {
    self.input_mode = InputMode::Alphabet;
    self.flush_romaji_to_composed();
}

The mode switch sets self.input_mode to InputMode::Alphabet, defined in karukan-im/src/core/engine/types.rs. The flush_romaji_to_composed() call ensures any pending kana conversion is cleared before entering alphabet mode, preventing partial romaji sequences from contaminating the ASCII input.

The InputMode enum supports three distinct states:

  • Hiragana – Standard romaji-to-hiragana conversion
  • Katakana – Forces output to katakana characters
  • Alphabet – Direct ASCII insertion without conversion

Handling Shift+Letter During Active Composition

Karukan handles Shift+letter even when the user has already started composing kana. The process_key_composing function in karukan-im/src/core/engine/input.rs contains similar detection logic with additional safeguards:

// karukan-im/src/core/engine/input.rs (process_key_composing)
if is_shift_alpha && self.input_mode != InputMode::Alphabet {
    // If we were in Katakana, bake it first so the pre-edit stays sane
    if self.input_mode == InputMode::Katakana {
        self.bake_katakana();
    }
    self.input_mode = InputMode::Alphabet;
    self.flush_romaji_to_composed();
    self.live.text.clear();  // reset live-conversion buffer
}

If the engine was in Katakana mode, it first calls bake_katakana() to commit the existing katakana state before switching. The live conversion buffer is cleared to ensure the pre-edit display accurately reflects the transition to direct ASCII input.

Character Insertion and Case Preservation

Once in Alphabet mode, the engine preserves the uppercase nature of the input:

let ch = if self.input_mode == InputMode::Alphabet && is_shift_alpha {
    ch.to_ascii_uppercase()
} else {
    ch
};
return self.input_buf.insert(&ch.to_string());

This bypasses the romaji-to-hiragana converter entirely, inserting the character directly into the pre-edit buffer. The mode indicator (displayed via karukan-im/src/core/engine/display.rs) updates to show "[A]" for Alphabet mode, providing visual feedback that the shift was successful.

Fcitx5 Integration and Testing

The behavior is validated through FFI tests in karukan-fcitx5/src/ffi/tests.rs, which simulate Shift+letter sequences against the engine:

// Simulating a Shift-A press in a Fcitx5 test (simplified)
let mut engine = Engine::default();  // starts in Hiragana mode
engine.process_key(&KeyEvent::new(Keysym::KEY_A, Shift::Pressed));
// → engine.input_mode == InputMode::Alphabet
// → pre-edit shows "A"

The C FFI layer in karukan-fcitx5/src/lib.rs exposes these capabilities to the Fcitx5 framework, ensuring that Linux users experience consistent alphabet mode entry regardless of their specific Fcitx5 configuration.

Summary

  • Dual detection strategy: Karukan checks for ch.is_ascii_uppercase() or shift_active && ch.is_ascii_alphabetic() to handle varying Fcitx5 keysym behaviors.
  • Immediate mode switch: The engine sets self.input_mode = InputMode::Alphabet and flushes pending conversions via flush_romaji_to_composed().
  • Composition-aware handling: During active composition, process_key_composing calls bake_katakana() if needed before switching modes.
  • Direct insertion: Characters bypass the romaji converter and insert directly into input_buf with uppercase preservation.
  • Source locations: Core logic resides in karukan-im/src/core/engine/input.rs with the InputMode enum defined in karukan-im/src/core/engine/types.rs.

Frequently Asked Questions

How does Karukan distinguish between Shift+letter and regular letter input?

Karukan checks both the character case and modifier state using ch.is_ascii_uppercase() || (shift_active && ch.is_ascii_alphabetic()). This catches both uppercase keysyms and lowercase keysyms with active Shift modifiers, ensuring reliable detection across different Fcitx5 configurations.

What happens to pending kana conversion when switching to alphabet mode?

The engine calls flush_romaji_to_composed() immediately upon switching to Alphabet mode. This clears any pending romaji-to-kana conversion state, ensuring that partial sequences do not interfere with the direct ASCII input.

Can I trigger alphabet mode with Shift+letter while composing kana?

Yes. The process_key_composing handler specifically supports this case. If you were in Katakana mode, the engine first invokes bake_katakana() to finalize the existing composition, then switches to Alphabet mode and clears the live conversion buffer to maintain pre-edit consistency.

Where is the mode indicator displayed in the Fcitx5 interface?

The mode indicator is rendered by the display logic in karukan-im/src/core/engine/display.rs, which shows "[A]" for Alphabet mode alongside the pre-edit text. This visual feedback confirms that Shift+letter successfully triggered the alphabet mode entry.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →