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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+91 -16
View File
@@ -252,6 +252,28 @@ T9Engine::T9Engine(fcitx::Instance* instance)
ic->setCursorRect(kFixedPopupRect); 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]"); writeStatusIndicator("[abc]");
} }
@@ -269,7 +291,15 @@ void T9Engine::activate(const fcitx::InputMethodEntry& entry,
FCITX_UNUSED(entry); FCITX_UNUSED(entry);
auto* ic = event.inputContext(); auto* ic = event.inputContext();
auto* state = ic->propertyFor(&factory); 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, void T9Engine::keyEvent(const fcitx::InputMethodEntry& entry,
@@ -652,26 +682,21 @@ bool T9State::handleKeyEvent(fcitx::KeyEvent& keyEvent) {
} }
if (keyEvent.isRelease()) { if (keyEvent.isRelease()) {
cancelLongPressTimer(); scheduleKeyRelease(keyId);
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);
}
return true; return true;
} }
// Key press event // 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) { 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; return true;
} }
pressedKeyId = keyId; 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) { void T9State::onLongPress(int keyId) {
isLongPressHandled = true; isLongPressHandled = true;
// Discard current key's uncommitted multi-tap candidate and commit long-press action // 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() { void T9State::reset() {
if (releaseTimer) {
releaseTimer.reset();
}
cancelLongPressTimer(); cancelLongPressTimer();
cancelCommit(); cancelCommit();
if (bannerTimer) { if (bannerTimer) {
@@ -786,6 +852,15 @@ void T9State::reset() {
updateStatusBar(); 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, void T9State::forwardKeyTap(fcitx::KeySym sym, fcitx::KeyStates states,
uint32_t xkbCode) { uint32_t xkbCode) {
if (states.test(fcitx::KeyState::Ctrl)) { if (states.test(fcitx::KeyState::Ctrl)) {
+8
View File
@@ -42,6 +42,7 @@ class T9State : public fcitx::InputContextProperty {
bool handleKeyEvent(fcitx::KeyEvent& keyEvent); bool handleKeyEvent(fcitx::KeyEvent& keyEvent);
void cancelCommit(); void cancelCommit();
void reset(); void reset();
void onFocusChange();
private: private:
std::vector<T9Item> getItemsForKey(int keyId) const; std::vector<T9Item> getItemsForKey(int keyId) const;
@@ -53,6 +54,8 @@ class T9State : public fcitx::InputContextProperty {
void startLongPressTimer(int keyId); void startLongPressTimer(int keyId);
void cancelLongPressTimer(); void cancelLongPressTimer();
void onLongPress(int keyId); void onLongPress(int keyId);
void scheduleKeyRelease(int keyId);
void finalizeKeyRelease(int keyId);
void scheduleCommit(uint64_t delayUs = 1500000); void scheduleCommit(uint64_t delayUs = 1500000);
void commit(); void commit();
void sendItem(const T9Item& item); void sendItem(const T9Item& item);
@@ -73,6 +76,7 @@ class T9State : public fcitx::InputContextProperty {
std::vector<T9Item> activeItems; std::vector<T9Item> activeItems;
std::unique_ptr<fcitx::EventSourceTime> commitTimer; std::unique_ptr<fcitx::EventSourceTime> commitTimer;
std::unique_ptr<fcitx::EventSourceTime> longPressTimer; std::unique_ptr<fcitx::EventSourceTime> longPressTimer;
std::unique_ptr<fcitx::EventSourceTime> releaseTimer;
std::unique_ptr<fcitx::EventSourceTime> bannerTimer; std::unique_ptr<fcitx::EventSourceTime> bannerTimer;
bool isLongPressHandled = false; bool isLongPressHandled = false;
}; };
@@ -83,6 +87,8 @@ class T9Engine : public fcitx::InputMethodEngineV2 {
std::vector<fcitx::InputMethodEntry> listInputMethods() override; std::vector<fcitx::InputMethodEntry> listInputMethods() override;
void activate(const fcitx::InputMethodEntry& entry, void activate(const fcitx::InputMethodEntry& entry,
fcitx::InputContextEvent& event) override; fcitx::InputContextEvent& event) override;
void deactivate(const fcitx::InputMethodEntry& entry,
fcitx::InputContextEvent& event) override;
void keyEvent(const fcitx::InputMethodEntry& entry, void keyEvent(const fcitx::InputMethodEntry& entry,
fcitx::KeyEvent& keyEvent) override; fcitx::KeyEvent& keyEvent) override;
void reset(const fcitx::InputMethodEntry& entry, void reset(const fcitx::InputMethodEntry& entry,
@@ -93,6 +99,8 @@ class T9Engine : public fcitx::InputMethodEngineV2 {
private: private:
fcitx::FactoryFor<T9State> factory; fcitx::FactoryFor<T9State> factory;
std::unique_ptr<fcitx::HandlerTableEntry<fcitx::EventHandler>> cursorWatcher; 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 { class T9EngineFactory : public fcitx::AddonFactory {
+4 -3
View File
@@ -1787,12 +1787,11 @@ focus(XEvent *ev)
{ {
XFocusChangeEvent *e = &ev->xfocus; XFocusChangeEvent *e = &ev->xfocus;
if (e->mode == NotifyGrab)
return;
if (ev->type == FocusIn) { if (ev->type == FocusIn) {
if (xw.ime.xic) if (xw.ime.xic)
XSetICFocus(xw.ime.xic); XSetICFocus(xw.ime.xic);
if (e->mode == NotifyGrab)
return;
win.mode |= MODE_FOCUSED; win.mode |= MODE_FOCUSED;
xseturgency(0); xseturgency(0);
if (IS_SET(MODE_FOCUS)) if (IS_SET(MODE_FOCUS))
@@ -1800,6 +1799,8 @@ focus(XEvent *ev)
} else { } else {
if (xw.ime.xic) if (xw.ime.xic)
XUnsetICFocus(xw.ime.xic); XUnsetICFocus(xw.ime.xic);
if (e->mode == NotifyGrab)
return;
win.mode &= ~MODE_FOCUSED; win.mode &= ~MODE_FOCUSED;
if (IS_SET(MODE_FOCUS)) if (IS_SET(MODE_FOCUS))
ttywrite("\033[O", 3, 0); ttywrite("\033[O", 3, 0);