Fix fcitx5-t9 * longpress auto-repeat and reset Ctl mode on focus/window change

This commit is contained in:
Luxferre
2026-10-21 21:04:53 +03:00
parent a31595070c
commit 9002560a38
9 changed files with 103 additions and 19 deletions
+91 -16
View File
@@ -252,6 +252,28 @@ T9Engine::T9Engine(fcitx::Instance* instance)
ic->setCursorRect(kFixedPopupRect);
}
});
focusOutWatcher = instance->watchEvent(
fcitx::EventType::InputContextFocusOut,
fcitx::EventWatcherPhase::PreInputMethod,
[this](fcitx::Event& event) {
auto& icEvent = static_cast<fcitx::InputContextEvent&>(event);
if (auto* ic = icEvent.inputContext()) {
if (auto* state = ic->propertyFor(&factory)) {
state->onFocusChange();
}
}
});
destroyWatcher = instance->watchEvent(
fcitx::EventType::InputContextDestroyed,
fcitx::EventWatcherPhase::PreInputMethod,
[this](fcitx::Event& event) {
auto& icEvent = static_cast<fcitx::InputContextEvent&>(event);
if (auto* ic = icEvent.inputContext()) {
if (auto* state = ic->propertyFor(&factory)) {
state->onFocusChange();
}
}
});
writeStatusIndicator("[abc]");
}
@@ -269,7 +291,15 @@ void T9Engine::activate(const fcitx::InputMethodEntry& entry,
FCITX_UNUSED(entry);
auto* ic = event.inputContext();
auto* state = ic->propertyFor(&factory);
state->reset();
state->onFocusChange();
}
void T9Engine::deactivate(const fcitx::InputMethodEntry& entry,
fcitx::InputContextEvent& event) {
FCITX_UNUSED(entry);
auto* ic = event.inputContext();
auto* state = ic->propertyFor(&factory);
state->onFocusChange();
}
void T9Engine::keyEvent(const fcitx::InputMethodEntry& entry,
@@ -652,26 +682,21 @@ bool T9State::handleKeyEvent(fcitx::KeyEvent& keyEvent) {
}
if (keyEvent.isRelease()) {
cancelLongPressTimer();
int releasedId = pressedKeyId;
pressedKeyId = -1;
if (isLongPressHandled) {
isLongPressHandled = false;
return true;
}
if (releasedId != keyId && releasedId != -1) {
return true;
}
if (!activeItems.empty()) {
uint64_t delayUs = (activeItems.size() > 10) ? 1800000 : 1500000;
scheduleCommit(delayUs);
}
scheduleKeyRelease(keyId);
return true;
}
// Key press event
if (releaseTimer) {
if (pressedKeyId == keyId) {
// Synthetic X11 auto-repeat KeyRelease+KeyPress pair while holding key
releaseTimer.reset();
return true;
}
finalizeKeyRelease(pressedKeyId);
}
if (pressedKeyId == keyId) {
// Ignore X11 auto-repeat key presses while holding the same key
// Ignore duplicate key presses while holding the same key
return true;
}
pressedKeyId = keyId;
@@ -720,6 +745,44 @@ void T9State::cancelLongPressTimer() {
}
}
void T9State::scheduleKeyRelease(int keyId) {
if (pressedKeyId != -1 && keyId != pressedKeyId) {
return;
}
if (releaseTimer) {
releaseTimer.reset();
}
// X11 auto-repeat sends synthetic KeyRelease immediately followed by KeyPress.
// Wait briefly to see if a repeat KeyPress arrives before finalizing release.
uint64_t debounceUs = isLongPressHandled ? 80000 : 15000;
releaseTimer = engine->instance->eventLoop().addTimeEvent(
CLOCK_MONOTONIC, fcitx::now(CLOCK_MONOTONIC) + debounceUs, 0,
[this, keyId](fcitx::EventSourceTime*, uint64_t) {
finalizeKeyRelease(keyId);
return true;
});
}
void T9State::finalizeKeyRelease(int keyId) {
if (releaseTimer) {
releaseTimer.reset();
}
cancelLongPressTimer();
int releasedId = pressedKeyId;
pressedKeyId = -1;
if (isLongPressHandled) {
isLongPressHandled = false;
return;
}
if (releasedId != keyId && releasedId != -1) {
return;
}
if (!activeItems.empty()) {
uint64_t delayUs = (activeItems.size() > 10) ? 1800000 : 1500000;
scheduleCommit(delayUs);
}
}
void T9State::onLongPress(int keyId) {
isLongPressHandled = true;
// Discard current key's uncommitted multi-tap candidate and commit long-press action
@@ -771,6 +834,9 @@ void T9State::handleMultiTap(int keyId) {
}
void T9State::reset() {
if (releaseTimer) {
releaseTimer.reset();
}
cancelLongPressTimer();
cancelCommit();
if (bannerTimer) {
@@ -786,6 +852,15 @@ void T9State::reset() {
updateStatusBar();
}
void T9State::onFocusChange() {
if (engine->sharedMode == T9Mode::Ctrl || mode == T9Mode::Ctrl) {
engine->sharedMode = T9Mode::Lower;
mode = T9Mode::Lower;
}
ctrlLatch = false;
reset();
}
void T9State::forwardKeyTap(fcitx::KeySym sym, fcitx::KeyStates states,
uint32_t xkbCode) {
if (states.test(fcitx::KeyState::Ctrl)) {
+8
View File
@@ -42,6 +42,7 @@ class T9State : public fcitx::InputContextProperty {
bool handleKeyEvent(fcitx::KeyEvent& keyEvent);
void cancelCommit();
void reset();
void onFocusChange();
private:
std::vector<T9Item> getItemsForKey(int keyId) const;
@@ -53,6 +54,8 @@ class T9State : public fcitx::InputContextProperty {
void startLongPressTimer(int keyId);
void cancelLongPressTimer();
void onLongPress(int keyId);
void scheduleKeyRelease(int keyId);
void finalizeKeyRelease(int keyId);
void scheduleCommit(uint64_t delayUs = 1500000);
void commit();
void sendItem(const T9Item& item);
@@ -73,6 +76,7 @@ class T9State : public fcitx::InputContextProperty {
std::vector<T9Item> activeItems;
std::unique_ptr<fcitx::EventSourceTime> commitTimer;
std::unique_ptr<fcitx::EventSourceTime> longPressTimer;
std::unique_ptr<fcitx::EventSourceTime> releaseTimer;
std::unique_ptr<fcitx::EventSourceTime> bannerTimer;
bool isLongPressHandled = false;
};
@@ -83,6 +87,8 @@ class T9Engine : public fcitx::InputMethodEngineV2 {
std::vector<fcitx::InputMethodEntry> listInputMethods() override;
void activate(const fcitx::InputMethodEntry& entry,
fcitx::InputContextEvent& event) override;
void deactivate(const fcitx::InputMethodEntry& entry,
fcitx::InputContextEvent& event) override;
void keyEvent(const fcitx::InputMethodEntry& entry,
fcitx::KeyEvent& keyEvent) override;
void reset(const fcitx::InputMethodEntry& entry,
@@ -93,6 +99,8 @@ class T9Engine : public fcitx::InputMethodEngineV2 {
private:
fcitx::FactoryFor<T9State> factory;
std::unique_ptr<fcitx::HandlerTableEntry<fcitx::EventHandler>> cursorWatcher;
std::unique_ptr<fcitx::HandlerTableEntry<fcitx::EventHandler>> focusOutWatcher;
std::unique_ptr<fcitx::HandlerTableEntry<fcitx::EventHandler>> destroyWatcher;
};
class T9EngineFactory : public fcitx::AddonFactory {