diff --git a/src/frontend/waylandim/waylandimserver.cpp b/src/frontend/waylandim/waylandimserver.cpp index 0b86f62aa..3b281c0ad 100644 --- a/src/frontend/waylandim/waylandimserver.cpp +++ b/src/frontend/waylandim/waylandimserver.cpp @@ -427,13 +427,44 @@ void WaylandIMInputContextV1::keyCallback(uint32_t serial, uint32_t time, server_->modifiers_, code), state == WL_KEYBOARD_KEY_STATE_RELEASED, time); - if (state == WL_KEYBOARD_KEY_STATE_RELEASED && key == repeatKey_) { + processKeyEvent(event, false, serial); +} + +void WaylandIMInputContextV1::virtualKeyEventImpl(KeyEvent &event) { + // `mods_locked` value seems to be 16 usually. + // If we need to implement behavior such as `capslocked` for virtual key events, + // we will need to manage this `mods_locked` value. + if (event.rawKey().hasModifier()) { + if (event.rawKey().states().test(KeyState::Shift)) { + modifiersCallback(serial_, (uint32_t)KeyState::Shift, 0, 16, 0); + } + } else { + modifiersCallback(serial_, 0, 0, 16, 0); + } + + processKeyEvent(event, true, serial_); +} + +void WaylandIMInputContextV1::processKeyEvent(KeyEvent &event, + bool isVirtualKey, + uint32_t serial) { + const uint32_t key = event.rawKey().code() > 8 ? event.rawKey().code() - 8 + : 0; + const auto state = event.isRelease() ? WL_KEYBOARD_KEY_STATE_RELEASED + : WL_KEYBOARD_KEY_STATE_PRESSED; + + const auto cancelRepeat = isVirtualKey + ? (state == WL_KEYBOARD_KEY_STATE_RELEASED) + // Strict condition for physical keys. + : (state == WL_KEYBOARD_KEY_STATE_RELEASED && key == repeatKey_); + + if (cancelRepeat) { timeEvent_->setEnabled(false); } else if (state == WL_KEYBOARD_KEY_STATE_PRESSED && - xkb_keymap_key_repeats(server_->keymap_.get(), code)) { + xkb_keymap_key_repeats(server_->keymap_.get(), event.rawKey().code())) { if (repeatRate_) { repeatKey_ = key; - repeatTime_ = time; + repeatTime_ = event.time(); repeatSym_ = event.rawKey().sym(); // Let's trick the key event system by fake our first. // Remove 100 from the initial interval. @@ -445,10 +476,11 @@ void WaylandIMInputContextV1::keyCallback(uint32_t serial, uint32_t time, WAYLANDIM_DEBUG() << event.key().toString() << " IsRelease=" << event.isRelease(); if (!keyEvent(event)) { - ic_->key(serial, time, key, state); + ic_->key(serial, event.time(), key, state); } server_->display_->flush(); } + void WaylandIMInputContextV1::modifiersCallback(uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, diff --git a/src/frontend/waylandim/waylandimserver.h b/src/frontend/waylandim/waylandimserver.h index 1ffed14db..3b7871a2f 100644 --- a/src/frontend/waylandim/waylandimserver.h +++ b/src/frontend/waylandim/waylandimserver.h @@ -84,6 +84,7 @@ class WaylandIMInputContextV1 : public InputContext { void deactivate(wayland::ZwpInputMethodContextV1 *id); protected: + void virtualKeyEventImpl(KeyEvent &event) override; void commitStringImpl(const std::string &text) override { if (!ic_) { return; @@ -120,6 +121,7 @@ class WaylandIMInputContextV1 : public InputContext { void keymapCallback(uint32_t format, int32_t fd, uint32_t size); void keyCallback(uint32_t serial, uint32_t time, uint32_t key, uint32_t state); + void processKeyEvent(KeyEvent &event, bool isVirtualKey, uint32_t serial); void modifiersCallback(uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group); diff --git a/src/lib/fcitx/inputcontext.cpp b/src/lib/fcitx/inputcontext.cpp index e6138036f..1fdc16358 100644 --- a/src/lib/fcitx/inputcontext.cpp +++ b/src/lib/fcitx/inputcontext.cpp @@ -243,6 +243,19 @@ bool InputContext::keyEvent(KeyEvent &event) { return result; } +void InputContext::virtualKeyEvent(KeyEvent &event) { + decltype(std::chrono::steady_clock::now()) start; + // Don't query time if we don't want log. + if (::keyTrace().checkLogLevel(LogLevel::Debug)) { + start = std::chrono::steady_clock::now(); + } + virtualKeyEventImpl(event); + FCITX_KEYTRACE() << "KeyEvent handling time: " + << std::chrono::duration_cast( + std::chrono::steady_clock::now() - start) + .count(); +} + void InputContext::invokeAction(InvokeActionEvent &event) { FCITX_D(); RETURN_IF_HAS_NO_FOCUS(); @@ -333,6 +346,11 @@ StatusArea &InputContext::statusArea() { return d->statusArea_; } +void InputContext::virtualKeyEventImpl(KeyEvent &event) { + FCITX_D(); + d->postEvent(event); +} + void InputContext::updateClientSideUIImpl() {} InputContextEventBlocker::InputContextEventBlocker(InputContext *inputContext) diff --git a/src/lib/fcitx/inputcontext.h b/src/lib/fcitx/inputcontext.h index 58363b506..8e56f7628 100644 --- a/src/lib/fcitx/inputcontext.h +++ b/src/lib/fcitx/inputcontext.h @@ -133,6 +133,10 @@ class FCITXCORE_EXPORT InputContext : public TrackableObject { /// Send a key event to current input context. bool keyEvent(KeyEvent &event); + /// Send a virtual key event to current input context. + /// This handles the event the same way as physical key events. + void virtualKeyEvent(KeyEvent &event); + /// Returns whether the input context holds the input focus. Input context /// need to have focus. bool hasFocus() const; @@ -253,6 +257,15 @@ class FCITXCORE_EXPORT InputContext : public TrackableObject { void updateProperty(const InputContextPropertyFactory *factory); protected: + /** + * Process the virtual key event. + * + * @param event KeyEvent + * + * @see virtualKeyEvent + */ + virtual void virtualKeyEventImpl(KeyEvent &event); + /** * Send the committed string to client *