From 10d15ee91ef37b6c5274dcc1bbb4b30499ca2ca1 Mon Sep 17 00:00:00 2001 From: Weng Xuetian Date: Wed, 3 Apr 2024 17:34:49 -0700 Subject: [PATCH] Allow fallback to repeat info from wl_keyboard of wl_seat. none of kwin/weston wl_keyboard from keyboard grab send over repeat infomation, thus under kwin, the fcitx internal default value (40,400) is used, which doesn't match the default value used on most system. However, we can still read the repeat information from the actual wl_keyboard. Add repeatInfo to allow us to query repeat info for a seat so we can use the actual repeat information from compositor. --- src/frontend/waylandim/waylandim.h | 2 +- src/frontend/waylandim/waylandimserver.cpp | 30 +++++++++---- src/frontend/waylandim/waylandimserver.h | 6 ++- .../waylandim/waylandimserverbase.cpp | 44 +++++++++++++++++-- src/frontend/waylandim/waylandimserverbase.h | 16 ++++++- src/frontend/waylandim/waylandimserverv2.cpp | 25 +++++++---- src/frontend/waylandim/waylandimserverv2.h | 5 ++- src/modules/wayland/wayland_public.h | 13 +++--- src/modules/wayland/waylandmodule.cpp | 29 +++++++++++- src/modules/wayland/waylandmodule.h | 17 ++++++- 10 files changed, 157 insertions(+), 30 deletions(-) diff --git a/src/frontend/waylandim/waylandim.h b/src/frontend/waylandim/waylandim.h index b65a32251..d512a927a 100644 --- a/src/frontend/waylandim/waylandim.h +++ b/src/frontend/waylandim/waylandim.h @@ -31,7 +31,7 @@ FCITX_CONFIGURATION( _("Forward key event instead of commiting text if it is not handled"), true};); -constexpr int32_t repeatHackDelay = 1000; +constexpr int32_t repeatHackDelay = 3000; class WaylandIMServer; class WaylandIMServerV2; diff --git a/src/frontend/waylandim/waylandimserver.cpp b/src/frontend/waylandim/waylandimserver.cpp index 30ced5fe5..1085d8670 100644 --- a/src/frontend/waylandim/waylandimserver.cpp +++ b/src/frontend/waylandim/waylandimserver.cpp @@ -6,12 +6,15 @@ */ #include "waylandimserver.h" #include +#include #include #include "fcitx-utils/macros.h" #include "fcitx-utils/utf8.h" #include "virtualinputcontext.h" #include "wayland-text-input-unstable-v1-client-protocol.h" +#include "wayland_public.h" #include "waylandim.h" +#include "wl_seat.h" #ifdef __linux__ #include @@ -121,7 +124,7 @@ WaylandIMInputContextV1::WaylandIMInputContextV1( InputContextManager &inputContextManager, WaylandIMServer *server) : VirtualInputContextGlue(inputContextManager), server_(server) { timeEvent_ = server_->instance()->eventLoop().addTimeEvent( - CLOCK_MONOTONIC, now(CLOCK_MONOTONIC), 0, + CLOCK_MONOTONIC, now(CLOCK_MONOTONIC), 1, [this](EventSourceTime *, uint64_t) { repeat(); return true; @@ -174,7 +177,6 @@ void WaylandIMInputContextV1::activate(wayland::ZwpInputMethodContextV1 *ic) { keyboard_->repeatInfo().connect([this](int32_t rate, int32_t delay) { repeatInfoCallback(rate, delay); }); - repeatInfoCallback(repeatRate_, repeatDelay_); wl_array array; wl_array_init(&array); constexpr char data[] = "Shift\0Control\0Mod1\0Mod4"; @@ -193,6 +195,7 @@ void WaylandIMInputContextV1::deactivate(wayland::ZwpInputMethodContextV1 *ic) { if (ic_.get() == ic) { ic_.reset(); keyboard_.reset(); + repeatInfo_.reset(); // This is the only place we update wayland display mask, so it is ok to // reset it to 0. This breaks the caps lock or num lock. But we have no // other option until we can listen to the mod change globally. @@ -222,7 +225,7 @@ void WaylandIMInputContextV1::repeat() { sendKeyToVK(repeatTime_, event.rawKey(), WL_KEYBOARD_KEY_STATE_PRESSED); } - uint64_t interval = 1000000 / repeatRate_; + uint64_t interval = 1000000 / repeatRate(); timeEvent_->setTime(timeEvent_->time() + interval); timeEvent_->setOneShot(); } @@ -460,13 +463,14 @@ void WaylandIMInputContextV1::keyCallback(uint32_t serial, uint32_t time, timeEvent_->setEnabled(false); } else if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(server_->keymap_.get(), code)) { - if (repeatRate_) { + if (repeatRate() > 0) { repeatKey_ = key; repeatTime_ = time; repeatSym_ = event.rawKey().sym(); // Let's trick the key event system by fake our first. // Remove 100 from the initial interval. - timeEvent_->setNextInterval(repeatDelay_ * 1000 - repeatHackDelay); + timeEvent_->setNextInterval(std::max( + 0, std::max(0, repeatDelay() * 1000 - repeatHackDelay))); timeEvent_->setOneShot(); } } @@ -486,7 +490,7 @@ void WaylandIMInputContextV1::keyCallback(uint32_t serial, uint32_t time, WAYLANDIM_DEBUG() << "Engine handling speed can not keep up with key " "repetition rate."; timeEvent_->setNextInterval( - std::min(1000, repeatDelay_ * 1000 - repeatHackDelay)); + std::clamp(0, repeatDelay() * 1000 - repeatHackDelay, 1000)); } } void WaylandIMInputContextV1::modifiersCallback(uint32_t serial, @@ -544,9 +548,10 @@ void WaylandIMInputContextV1::modifiersCallback(uint32_t serial, } } +// This is not sent by either kwin/weston, but since it's unclear whether any +// one would send it, so keep it as is. void WaylandIMInputContextV1::repeatInfoCallback(int32_t rate, int32_t delay) { - repeatRate_ = rate; - repeatDelay_ = delay; + repeatInfo_ = std::make_tuple(rate, delay); } void WaylandIMInputContextV1::sendKey(uint32_t time, uint32_t sym, @@ -626,4 +631,13 @@ void WaylandIMInputContextV1::deleteSurroundingTextDelegate( ic_->deleteSurroundingText(startBytes - cursorBytes, sizeBytes); ic_->commitString(serial_, ""); } + +int32_t WaylandIMInputContextV1::repeatRate() const { + return server_->repeatRate(nullptr, repeatInfo_); +} + +int32_t WaylandIMInputContextV1::repeatDelay() const { + return server_->repeatDelay(nullptr, repeatInfo_); +} + } // namespace fcitx diff --git a/src/frontend/waylandim/waylandimserver.h b/src/frontend/waylandim/waylandimserver.h index e97e8fab2..40f581ef4 100644 --- a/src/frontend/waylandim/waylandimserver.h +++ b/src/frontend/waylandim/waylandimserver.h @@ -12,6 +12,7 @@ #include "fcitx-utils/key.h" #include "fcitx-utils/keysymgen.h" #include "fcitx-utils/macros.h" +#include "fcitx-utils/signals.h" #include "virtualinputcontext.h" #include "waylandimserverbase.h" #include "wl_keyboard.h" @@ -152,6 +153,9 @@ class WaylandIMInputContextV1 : public VirtualInputContextGlue { return modifiers; } + int32_t repeatRate() const; + int32_t repeatDelay() const; + WaylandIMServer *server_; std::unique_ptr ic_; std::unique_ptr keyboard_; @@ -164,7 +168,7 @@ class WaylandIMInputContextV1 : public VirtualInputContextGlue { uint32_t repeatTime_ = 0; KeySym repeatSym_ = FcitxKey_None; - int32_t repeatRate_ = 40, repeatDelay_ = 400; + std::optional> repeatInfo_; }; } // namespace fcitx diff --git a/src/frontend/waylandim/waylandimserverbase.cpp b/src/frontend/waylandim/waylandimserverbase.cpp index bd3c6af93..726da7978 100644 --- a/src/frontend/waylandim/waylandimserverbase.cpp +++ b/src/frontend/waylandim/waylandimserverbase.cpp @@ -8,16 +8,16 @@ #include #include #include -#include "fcitx-utils/event.h" #include "fcitx-utils/utf8.h" #include "waylandim.h" +#include "wl_seat.h" namespace fcitx { WaylandIMServerBase::WaylandIMServerBase(wl_display *display, FocusGroup *group, - const std::string &name, + std::string name, WaylandIMModule *waylandim) - : group_(group), name_(name), parent_(waylandim), + : group_(group), name_(std::move(name)), parent_(waylandim), display_( static_cast(wl_display_get_user_data(display))) {} @@ -37,4 +37,42 @@ WaylandIMServerBase::mayCommitAsText(const Key &key, uint32_t state) const { return std::nullopt; } +std::optional> WaylandIMServerBase::repeatInfo( + const std::shared_ptr &seat, + const std::optional> &defaultValue) const { + if (defaultValue) { + return defaultValue; + } + auto seatPtr = seat; + if (!seatPtr) { + seatPtr = display_->getGlobal(); + } + if (seatPtr) { + auto repeatInfo = parent_->wayland()->call( + name_, *seatPtr); + if (repeatInfo) { + return repeatInfo; + } + } + return std::nullopt; +} + +int32_t WaylandIMServerBase::repeatRate( + const std::shared_ptr &seat, + const std::optional> &defaultValue) const { + if (auto info = repeatInfo(seat, defaultValue)) { + return std::get<0>(info.value()); + } + return 25; +} + +int32_t WaylandIMServerBase::repeatDelay( + const std::shared_ptr &seat, + const std::optional> &defaultValue) const { + if (auto info = repeatInfo(seat, defaultValue)) { + return std::get<1>(info.value()); + } + return 600; +} + } // namespace fcitx diff --git a/src/frontend/waylandim/waylandimserverbase.h b/src/frontend/waylandim/waylandimserverbase.h index 95b5e2533..4a1183e58 100644 --- a/src/frontend/waylandim/waylandimserverbase.h +++ b/src/frontend/waylandim/waylandimserverbase.h @@ -12,20 +12,29 @@ #include "fcitx-utils/misc.h" #include "display.h" #include "waylandim.h" +#include "wl_seat.h" namespace fcitx { class WaylandIMServerBase { public: WaylandIMServerBase(wl_display *display, FocusGroup *group, - const std::string &name, WaylandIMModule *waylandim); + std::string name, WaylandIMModule *waylandim); virtual ~WaylandIMServerBase() = default; auto *parent() { return parent_; } + auto *display() { return display_; } std::optional mayCommitAsText(const Key &key, uint32_t state) const; + int32_t repeatRate( + const std::shared_ptr &seat, + const std::optional> &defaultValue) const; + int32_t repeatDelay( + const std::shared_ptr &seat, + const std::optional> &defaultValue) const; + protected: FocusGroup *group_; std::string name_; @@ -37,6 +46,11 @@ class WaylandIMServerBase { UniqueCPtr state_; KeyStates modifiers_; + +private: + std::optional> repeatInfo( + const std::shared_ptr &seat, + const std::optional> &defaultValue) const; }; } // namespace fcitx diff --git a/src/frontend/waylandim/waylandimserverv2.cpp b/src/frontend/waylandim/waylandimserverv2.cpp index 522281ece..f17e704ca 100644 --- a/src/frontend/waylandim/waylandimserverv2.cpp +++ b/src/frontend/waylandim/waylandimserverv2.cpp @@ -6,6 +6,7 @@ */ #include "waylandimserverv2.h" #include +#include #include "fcitx-utils/keysymgen.h" #include "fcitx-utils/unixfd.h" #include "fcitx-utils/utf8.h" @@ -143,6 +144,7 @@ WaylandIMInputContextV2::WaylandIMInputContextV2( if (pendingDeactivate_) { pendingDeactivate_ = false; keyboardGrab_.reset(); + repeatInfo_.reset(); // This is the only place we update wayland xkb mask, so it is ok to // reset it to 0. This breaks the caps lock or num lock. But we have // no other option until we can listen to the mod change globally. @@ -202,7 +204,6 @@ WaylandIMInputContextV2::WaylandIMInputContextV2( [this](int32_t rate, int32_t delay) { repeatInfoCallback(rate, delay); }); - repeatInfoCallback(repeatRate_, repeatDelay_); focusInWrapper(); } } @@ -216,7 +217,7 @@ WaylandIMInputContextV2::WaylandIMInputContextV2( }); ic_->unavailable().connect([]() { WAYLANDIM_DEBUG() << "UNAVAILABLE"; }); timeEvent_ = server_->instance()->eventLoop().addTimeEvent( - CLOCK_MONOTONIC, now(CLOCK_MONOTONIC), 0, + CLOCK_MONOTONIC, now(CLOCK_MONOTONIC), 1, [this](EventSourceTime *, uint64_t) { repeat(); return true; @@ -249,7 +250,7 @@ void WaylandIMInputContextV2::repeat() { if (!ic->keyEvent(event)) { sendKeyToVK(repeatTime_, event.rawKey(), WL_KEYBOARD_KEY_STATE_PRESSED); } - uint64_t interval = 1000000 / repeatRate_; + uint64_t interval = 1000000 / repeatRate(); timeEvent_->setTime(timeEvent_->time() + interval); timeEvent_->setOneShot(); } @@ -457,13 +458,13 @@ void WaylandIMInputContextV2::keyCallback(uint32_t serial, uint32_t time, timeEvent_->setEnabled(false); } else if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(server_->keymap_.get(), code)) { - if (repeatRate_) { + if (repeatRate() > 0) { repeatKey_ = key; repeatTime_ = time; repeatSym_ = event.rawKey().sym(); // Let's trick the key event system by fake our first. // Remove 100 from the initial interval. - timeEvent_->setNextInterval(repeatDelay_ * 1000 - repeatHackDelay); + timeEvent_->setNextInterval(repeatDelay() * 1000 - repeatHackDelay); timeEvent_->setOneShot(); } } @@ -485,7 +486,7 @@ void WaylandIMInputContextV2::keyCallback(uint32_t serial, uint32_t time, WAYLANDIM_DEBUG() << "Engine handling speed can not keep up with key " "repetition rate."; timeEvent_->setNextInterval( - std::min(1000, repeatDelay_ * 1000 - repeatHackDelay)); + std::clamp(0, repeatDelay() * 1000 - repeatHackDelay, 1000)); } } void WaylandIMInputContextV2::modifiersCallback(uint32_t /*serial*/, @@ -539,8 +540,7 @@ void WaylandIMInputContextV2::modifiersCallback(uint32_t /*serial*/, } void WaylandIMInputContextV2::repeatInfoCallback(int32_t rate, int32_t delay) { - repeatRate_ = rate; - repeatDelay_ = delay; + repeatInfo_ = std::make_tuple(rate, delay); } void WaylandIMInputContextV2::sendKeyToVK(uint32_t time, const Key &key, @@ -672,4 +672,13 @@ void WaylandIMInputContextV2::deleteSurroundingTextDelegate( startBytes + sizeBytes - cursorBytes); ic_->commit(serial_); } + +int32_t WaylandIMInputContextV2::repeatRate() const { + return server_->repeatRate(seat_, repeatInfo_); +} + +int32_t WaylandIMInputContextV2::repeatDelay() const { + return server_->repeatDelay(seat_, repeatInfo_); +} + } // namespace fcitx diff --git a/src/frontend/waylandim/waylandimserverv2.h b/src/frontend/waylandim/waylandimserverv2.h index b46f07633..733474bff 100644 --- a/src/frontend/waylandim/waylandimserverv2.h +++ b/src/frontend/waylandim/waylandimserverv2.h @@ -115,6 +115,9 @@ class WaylandIMInputContextV2 : public VirtualInputContextGlue { void repeatInfoCallback(int32_t rate, int32_t delay); void sendKeyToVK(uint32_t time, const Key &key, uint32_t state) const; + int32_t repeatRate() const; + int32_t repeatDelay() const; + WaylandIMServerV2 *server_; std::shared_ptr seat_; std::unique_ptr ic_; @@ -134,7 +137,7 @@ class WaylandIMInputContextV2 : public VirtualInputContextGlue { uint32_t repeatTime_ = 0; KeySym repeatSym_ = FcitxKey_None; - int32_t repeatRate_ = 40, repeatDelay_ = 400; + std::optional> repeatInfo_; mutable OrderedMap pressedVKKey_; }; diff --git a/src/modules/wayland/wayland_public.h b/src/modules/wayland/wayland_public.h index ecb054d88..a46311d6c 100644 --- a/src/modules/wayland/wayland_public.h +++ b/src/modules/wayland/wayland_public.h @@ -13,15 +13,15 @@ #include #include #include +#include #include namespace fcitx { -typedef std::function - WaylandConnectionCreated; -typedef std::function - WaylandConnectionClosed; +using WaylandConnectionCreated = + std::function; +using WaylandConnectionClosed = + std::function; } // namespace fcitx FCITX_ADDON_DECLARE_FUNCTION( @@ -38,5 +38,8 @@ FCITX_ADDON_DECLARE_FUNCTION(WaylandModule, openConnection, FCITX_ADDON_DECLARE_FUNCTION(WaylandModule, openConnectionSocket, bool(int fd)); FCITX_ADDON_DECLARE_FUNCTION(WaylandModule, reopenConnectionSocket, bool(const std::string &name, int fd)); +FCITX_ADDON_DECLARE_FUNCTION(WaylandModule, repeatInfo, + std::optional>( + const std::string &name, wl_seat *)); #endif // _FCITX_MODULES_WAYLAND_WAYLAND_PUBLIC_H_ diff --git a/src/modules/wayland/waylandmodule.cpp b/src/modules/wayland/waylandmodule.cpp index 831571eda..d619e46fc 100644 --- a/src/modules/wayland/waylandmodule.cpp +++ b/src/modules/wayland/waylandmodule.cpp @@ -9,11 +9,12 @@ #include #include #include -#include #include +#include #include #include #include +#include #include "fcitx-config/iniparser.h" #include "fcitx-utils/event.h" #include "fcitx-utils/log.h" @@ -22,6 +23,7 @@ #include "fcitx-utils/standardpath.h" #include "fcitx-utils/stringutils.h" #include "fcitx-utils/trackableobject.h" +#include "fcitx/addonfactory.h" #include "fcitx/inputcontext.h" #include "fcitx/instance.h" #include "fcitx/misc_p.h" @@ -157,6 +159,14 @@ void WaylandConnection::setupKeyboard(wayland::WlSeat *seat) { }); } +std::optional> +WaylandConnection::repeatInfo(wayland::WlSeat *seat) const { + if (const auto *keyboard = findValue(keyboards_, seat)) { + return keyboard->get()->repeatInfo(); + } + return std::nullopt; +} + WaylandModule::WaylandModule(fcitx::Instance *instance) : instance_(instance), isWaylandSession_(isSessionType("wayland")) { @@ -667,6 +677,23 @@ void WaylandModule::selfDiagnose() { } } +std::optional> +WaylandModule::repeatInfo(const std::string &name, wl_seat *seat) const { + if (!seat) { + return std::nullopt; + } + auto *wlSeat = static_cast(wl_seat_get_user_data(seat)); + if (!wlSeat) { + return std::nullopt; + } + conns_.find(name); + auto iter = conns_.find(name); + if (iter == conns_.end()) { + return std::nullopt; + } + return iter->second->repeatInfo(wlSeat); +} + class WaylandModuleFactory : public AddonFactory { public: AddonInstance *create(AddonManager *manager) override { diff --git a/src/modules/wayland/waylandmodule.h b/src/modules/wayland/waylandmodule.h index 02b97a3e0..03726eb15 100644 --- a/src/modules/wayland/waylandmodule.h +++ b/src/modules/wayland/waylandmodule.h @@ -10,11 +10,11 @@ #include #include #include +#include #include "fcitx-config/iniparser.h" #include "fcitx-utils/event.h" #include "fcitx-utils/i18n.h" #include "fcitx-utils/log.h" -#include "fcitx/addonfactory.h" #include "fcitx/addoninstance.h" #include "fcitx/addonmanager.h" #include "fcitx/instance.h" @@ -49,16 +49,24 @@ class WaylandKeyboard { auto &updateKeymap() { return updateKeymap_; } + const std::optional> &repeatInfo() const { + return repeatInfo_; + } + private: void init() { keyboard_->keymap().connect([this](uint32_t, int32_t fd, uint32_t) { close(fd); updateKeymap_(); }); + keyboard_->repeatInfo().connect([this](int32_t rate, int32_t delay) { + repeatInfo_ = std::make_tuple(rate, delay); + }); } ScopedConnection capConn_; std::unique_ptr keyboard_; Signal updateKeymap_; + std::optional> repeatInfo_; }; class WaylandConnection { @@ -78,6 +86,9 @@ class WaylandConnection { bool isWaylandSocket() const { return isWaylandSocket_; } + std::optional> + repeatInfo(wayland::WlSeat *seat) const; + private: void init(wl_display *display); void finish(); @@ -123,6 +134,9 @@ class WaylandModule : public AddonInstance { void selfDiagnose(); + std::optional> + repeatInfo(const std::string &name, wl_seat *seat) const; + private: void onConnectionCreated(WaylandConnection &conn); void onConnectionClosed(WaylandConnection &conn); @@ -147,6 +161,7 @@ class WaylandModule : public AddonInstance { FCITX_ADDON_EXPORT_FUNCTION(WaylandModule, openConnection); FCITX_ADDON_EXPORT_FUNCTION(WaylandModule, openConnectionSocket); FCITX_ADDON_EXPORT_FUNCTION(WaylandModule, reopenConnectionSocket); + FCITX_ADDON_EXPORT_FUNCTION(WaylandModule, repeatInfo); std::vector>> eventHandlers_;