Skip to content

Add option to consume redundant Activate/Deactivate key press - #1616

Open
akovalenko wants to merge 3 commits into
fcitx:masterfrom
akovalenko:consume-redundant-activate-keys
Open

Add option to consume redundant Activate/Deactivate key press#1616
akovalenko wants to merge 3 commits into
fcitx:masterfrom
akovalenko:consume-redundant-activate-keys

Conversation

@akovalenko

@akovalenko akovalenko commented Jul 26, 2026

Copy link
Copy Markdown

The current behavior — an Activate/Deactivate key press that doesn't
change state falls through to the application — is clearly intentional
and fine for most setups (as discussed in #845), so this PR changes no
defaults.

My use case is a modeless switch: dedicated ActivateKeys=Super+N /
DeactivateKeys=Super+S, picked to be idempotent — a redundant press
should be a no-op. Instead it leaks into the client and types a letter:
with the IM already active, Super+N inserts "n" (easy to reproduce with
two input methods in a group).

This adds an opt-in Hotkey option "Consume redundant Activate/Deactivate
key" (ConsumeRedundantActivateKeys, default false). When enabled, a
non-modifier press of an activate/deactivate key that no state-gated
handler claimed is consumed after the handler loop instead of being
forwarded. Modifier-only keys are never consumed (a bare modifier press
still reaches the client), and a key present in both lists (toggle
setup) matches a handler earlier and is unaffected.

Summary by CodeRabbit

  • New Features

    • Added an option to consume redundant Activate/Deactivate key events when the input method is already in the requested state.
    • The setting is disabled by default and does not affect modifier-only keys.
  • Bug Fixes

    • Prevented redundant activation or deactivation keystrokes from being passed to applications when the option is enabled.

Dedicated ActivateKeys/DeactivateKeys act as modeless switches: pressing
the activate key when the input method is already active is expected to
be a no-op. Currently such a press falls through the state-gated hotkey
handlers (their check() is canActivate/canDeactivate) and leaks to the
client, typing the letter into the application.

Add a Hotkey/ConsumeRedundantActivateKeys boolean option, default false
to preserve current behavior. When enabled, a non-modifier press of an
activate/deactivate key that no handler claimed is consumed after the
handler loop instead of being forwarded. Modifier-only keys are left
alone, so a bare modifier press still reaches the client; a key shared
between both lists (toggle setup) always matches a handler earlier and
is unaffected.

Related: fcitx#845.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: efa197df-9d0a-4124-bd8f-9017af689d2e

📥 Commits

Reviewing files that changed from the base of the PR and between 198574e and 7dbe5a8.

📒 Files selected for processing (2)
  • src/lib/fcitx/globalconfig.cpp
  • src/lib/fcitx/instance.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/lib/fcitx/instance.cpp
  • src/lib/fcitx/globalconfig.cpp

📝 Walkthrough

Walkthrough

Adds a configurable ConsumeRedundantActivateKeys option and applies it to activation and deactivation checks. When enabled, redundant key events can be consumed.

Changes

Redundant activation key consumption

Layer / File(s) Summary
Configuration and public accessor
src/lib/fcitx/globalconfig.cpp, src/lib/fcitx/globalconfig.h
Adds the ConsumeRedundantActivateKeys option with a default value of false. Exposes the value through GlobalConfig::consumeRedundantActivateKeys().
Redundant activation event handling
src/lib/fcitx/instance.cpp
When the option is enabled, canActivate() and canDeactivate() allow redundant activation and deactivation key events to proceed.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HotkeyConfig
  participant GlobalConfig
  participant InstancePrivate
  HotkeyConfig->>GlobalConfig: configure consumeRedundantActivateKeys
  InstancePrivate->>GlobalConfig: read configured value
  GlobalConfig-->>InstancePrivate: return configured value
  InstancePrivate->>InstancePrivate: allow redundant activation or deactivation
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the new option for consuming redundant Activate/Deactivate key presses.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 2

🧹 Nitpick comments (1)
src/lib/fcitx/instance.cpp (1)

876-888: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

Add regression coverage for the option branches.

Cover the default-off behavior, redundant activate/deactivate consumption, non-redundant state changes, modifier-only forwarding, and shared-list ordering.

🤖 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 `@src/lib/fcitx/instance.cpp` around lines 876 - 888, Add regression tests for
the activate/deactivate handling around the consumeRedundantActivateKeys branch
in instance.cpp. Cover the option disabled path, consumption of redundant
activate and deactivate keys, forwarding of non-redundant state changes and
modifier-only keys, and ordering when activate/deactivate keys share a list;
preserve the expected client forwarding and state transitions for each case.
🤖 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 `@src/lib/fcitx/instance.cpp`:
- Around line 876-888: Update the redundant activate/deactivate handling around
globalConfig_.consumeRedundantActivateKeys() to record consumed presses and
consume their matching release events as well. Ensure the release path runs even
when no input-method engine or entry is available, preventing the key-up from
reaching the client; preserve the existing keyEvent.filterAndAccept() behavior
for the press.
- Around line 876-888: Move the redundant activate/deactivate key consumption
currently in the Instance event dispatcher into an addon or module, leaving
Instance free of this feature-specific special case. Preserve the existing
consumeRedundantActivateKeys, modifier, canTrigger, activate/deactivate key
matching, and filterAndAccept behavior through the new runtime extension; only
retain it in Instance if an approved exception is documented.

---

Nitpick comments:
In `@src/lib/fcitx/instance.cpp`:
- Around line 876-888: Add regression tests for the activate/deactivate handling
around the consumeRedundantActivateKeys branch in instance.cpp. Cover the option
disabled path, consumption of redundant activate and deactivate keys, forwarding
of non-redundant state changes and modifier-only keys, and ordering when
activate/deactivate keys share a list; preserve the expected client forwarding
and state transitions for each case.
🪄 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: 489e5ad9-acfa-4c83-8b0a-e68992f6beaf

📥 Commits

Reviewing files that changed from the base of the PR and between 16465b0 and b515818.

📒 Files selected for processing (3)
  • src/lib/fcitx/globalconfig.cpp
  • src/lib/fcitx/globalconfig.h
  • src/lib/fcitx/instance.cpp

Comment thread src/lib/fcitx/instance.cpp Outdated
Comment on lines +876 to +888
if (d->globalConfig_.consumeRedundantActivateKeys() &&
!isModifier && canTrigger() &&
(origKey.keyListIndex(d->globalConfig_.activateKeys()) >=
0 ||
origKey.keyListIndex(d->globalConfig_.deactivateKeys()) >=
0)) {
// Activate/Deactivate keys act as modeless switches: when
// the state already matches, the state-gated handlers
// above do not claim the key. Consume it instead of
// leaking it to the client.
keyEvent.filterAndAccept();
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Consume the matching release as well.

The normal hotkey path records keyReleased_ before consuming a press; this fallback does not. The redundant key’s release can therefore bypass the early release filter and reach the client when no input-method engine or entry is available, leaving an unmatched key-up. Track the consumed press or add an equivalent release path.

🤖 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 `@src/lib/fcitx/instance.cpp` around lines 876 - 888, Update the redundant
activate/deactivate handling around globalConfig_.consumeRedundantActivateKeys()
to record consumed presses and consume their matching release events as well.
Ensure the release path runs even when no input-method engine or entry is
available, preventing the key-up from reaching the client; preserve the existing
keyEvent.filterAndAccept() behavior for the press.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Move this runtime behavior behind an addon or module.

This adds feature-specific behavior directly to the server-side Instance event dispatcher. Please avoid another special case in Instance and implement the consumption through an addon/module, or document an approved exception.

As per coding guidelines, src/**/*.cpp must “Implement new runtime behavior as an addon or module rather than adding special cases to the server binary.”

🤖 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 `@src/lib/fcitx/instance.cpp` around lines 876 - 888, Move the redundant
activate/deactivate key consumption currently in the Instance event dispatcher
into an addon or module, leaving Instance free of this feature-specific special
case. Preserve the existing consumeRedundantActivateKeys, modifier, canTrigger,
activate/deactivate key matching, and filterAndAccept behavior through the new
runtime extension; only retain it in Instance if an approved exception is
documented.

Source: Coding guidelines

@wengxt

wengxt commented Jul 28, 2026

Copy link
Copy Markdown
Member

I think there the code can be much simpler:

change canActivate / canDeactivate's return value to be inputState->isActive() || your new option.

Do not special-case into filterAndAccept when the option is on and
the key is not a modifier etc.  Pretend instead, when the option is
on, that we always canActivate and canDeactivate.
@akovalenko

akovalenko commented Jul 30, 2026

Copy link
Copy Markdown
Author

I think there the code can be much simpler:

change canActivate / canDeactivate's return value to be inputState->isActive() || your new option.

Thank you, tried your approach, much simpler indeed. Works for me for several days. PR updated.

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