diff --git a/overlay/rootfs/usr/bin/st b/overlay/rootfs/usr/bin/st index 18f6edf..6fc50f5 100755 Binary files a/overlay/rootfs/usr/bin/st and b/overlay/rootfs/usr/bin/st differ diff --git a/overlay/rootfs/usr/lib/fcitx5/libt9.so b/overlay/rootfs/usr/lib/fcitx5/libt9.so index 098fe27..356e00c 100755 Binary files a/overlay/rootfs/usr/lib/fcitx5/libt9.so and b/overlay/rootfs/usr/lib/fcitx5/libt9.so differ diff --git a/overlay/rootfs/usr/lib/fcitx5/t9.so b/overlay/rootfs/usr/lib/fcitx5/t9.so index 098fe27..356e00c 100755 Binary files a/overlay/rootfs/usr/lib/fcitx5/t9.so and b/overlay/rootfs/usr/lib/fcitx5/t9.so differ diff --git a/prebuilt/fcitx5-t9/usr/lib/fcitx5/libt9.so b/prebuilt/fcitx5-t9/usr/lib/fcitx5/libt9.so index 098fe27..356e00c 100755 Binary files a/prebuilt/fcitx5-t9/usr/lib/fcitx5/libt9.so and b/prebuilt/fcitx5-t9/usr/lib/fcitx5/libt9.so differ diff --git a/prebuilt/fcitx5-t9/usr/lib/fcitx5/t9.so b/prebuilt/fcitx5-t9/usr/lib/fcitx5/t9.so index 098fe27..356e00c 100755 Binary files a/prebuilt/fcitx5-t9/usr/lib/fcitx5/t9.so and b/prebuilt/fcitx5-t9/usr/lib/fcitx5/t9.so differ diff --git a/prebuilt/sxmo-x11/usr/bin/st b/prebuilt/sxmo-x11/usr/bin/st index 18f6edf..6fc50f5 100755 Binary files a/prebuilt/sxmo-x11/usr/bin/st and b/prebuilt/sxmo-x11/usr/bin/st differ diff --git a/src/fcitx5-t9/src/t9.cpp b/src/fcitx5-t9/src/t9.cpp index 7f4456e..e865ccb 100644 --- a/src/fcitx5-t9/src/t9.cpp +++ b/src/fcitx5-t9/src/t9.cpp @@ -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(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(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)) { diff --git a/src/fcitx5-t9/src/t9.h b/src/fcitx5-t9/src/t9.h index 2ece94a..200aa40 100644 --- a/src/fcitx5-t9/src/t9.h +++ b/src/fcitx5-t9/src/t9.h @@ -42,6 +42,7 @@ class T9State : public fcitx::InputContextProperty { bool handleKeyEvent(fcitx::KeyEvent& keyEvent); void cancelCommit(); void reset(); + void onFocusChange(); private: std::vector 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 activeItems; std::unique_ptr commitTimer; std::unique_ptr longPressTimer; + std::unique_ptr releaseTimer; std::unique_ptr bannerTimer; bool isLongPressHandled = false; }; @@ -83,6 +87,8 @@ class T9Engine : public fcitx::InputMethodEngineV2 { std::vector 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 factory; std::unique_ptr> cursorWatcher; + std::unique_ptr> focusOutWatcher; + std::unique_ptr> destroyWatcher; }; class T9EngineFactory : public fcitx::AddonFactory { diff --git a/src/sxmo-st/x.c b/src/sxmo-st/x.c index a653fe6..6589f7c 100644 --- a/src/sxmo-st/x.c +++ b/src/sxmo-st/x.c @@ -1787,12 +1787,11 @@ focus(XEvent *ev) { XFocusChangeEvent *e = &ev->xfocus; - if (e->mode == NotifyGrab) - return; - if (ev->type == FocusIn) { if (xw.ime.xic) XSetICFocus(xw.ime.xic); + if (e->mode == NotifyGrab) + return; win.mode |= MODE_FOCUSED; xseturgency(0); if (IS_SET(MODE_FOCUS)) @@ -1800,6 +1799,8 @@ focus(XEvent *ev) } else { if (xw.ime.xic) XUnsetICFocus(xw.ime.xic); + if (e->mode == NotifyGrab) + return; win.mode &= ~MODE_FOCUSED; if (IS_SET(MODE_FOCUS)) ttywrite("\033[O", 3, 0);