Skip to content

Commit

Permalink
Allow fallback to repeat info from wl_keyboard of wl_seat.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wengxt committed Apr 4, 2024
1 parent 9d3b0e7 commit 10d15ee
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/frontend/waylandim/waylandim.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
30 changes: 22 additions & 8 deletions src/frontend/waylandim/waylandimserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
*/
#include "waylandimserver.h"
#include <sys/mman.h>
#include <algorithm>
#include <memory>
#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 <linux/input-event-codes.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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";
Expand All @@ -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.
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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();
}
}
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
6 changes: 5 additions & 1 deletion src/frontend/waylandim/waylandimserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -152,6 +153,9 @@ class WaylandIMInputContextV1 : public VirtualInputContextGlue {
return modifiers;
}

int32_t repeatRate() const;
int32_t repeatDelay() const;

WaylandIMServer *server_;
std::unique_ptr<wayland::ZwpInputMethodContextV1> ic_;
std::unique_ptr<wayland::WlKeyboard> keyboard_;
Expand All @@ -164,7 +168,7 @@ class WaylandIMInputContextV1 : public VirtualInputContextGlue {
uint32_t repeatTime_ = 0;
KeySym repeatSym_ = FcitxKey_None;

int32_t repeatRate_ = 40, repeatDelay_ = 400;
std::optional<std::tuple<int32_t, int32_t>> repeatInfo_;
};

} // namespace fcitx
Expand Down
44 changes: 41 additions & 3 deletions src/frontend/waylandim/waylandimserverbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
#include <optional>
#include <string>
#include <wayland-client-core.h>
#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<wayland::Display *>(wl_display_get_user_data(display))) {}

Expand All @@ -37,4 +37,42 @@ WaylandIMServerBase::mayCommitAsText(const Key &key, uint32_t state) const {
return std::nullopt;
}

std::optional<std::tuple<int32_t, int32_t>> WaylandIMServerBase::repeatInfo(
const std::shared_ptr<wayland::WlSeat> &seat,
const std::optional<std::tuple<int32_t, int32_t>> &defaultValue) const {
if (defaultValue) {
return defaultValue;
}
auto seatPtr = seat;
if (!seatPtr) {
seatPtr = display_->getGlobal<wayland::WlSeat>();
}
if (seatPtr) {
auto repeatInfo = parent_->wayland()->call<IWaylandModule::repeatInfo>(
name_, *seatPtr);
if (repeatInfo) {
return repeatInfo;
}
}
return std::nullopt;
}

int32_t WaylandIMServerBase::repeatRate(
const std::shared_ptr<wayland::WlSeat> &seat,
const std::optional<std::tuple<int32_t, int32_t>> &defaultValue) const {
if (auto info = repeatInfo(seat, defaultValue)) {
return std::get<0>(info.value());
}
return 25;
}

int32_t WaylandIMServerBase::repeatDelay(
const std::shared_ptr<wayland::WlSeat> &seat,
const std::optional<std::tuple<int32_t, int32_t>> &defaultValue) const {
if (auto info = repeatInfo(seat, defaultValue)) {
return std::get<1>(info.value());
}
return 600;
}

} // namespace fcitx
16 changes: 15 additions & 1 deletion src/frontend/waylandim/waylandimserverbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> mayCommitAsText(const Key &key,
uint32_t state) const;

int32_t repeatRate(
const std::shared_ptr<wayland::WlSeat> &seat,
const std::optional<std::tuple<int32_t, int32_t>> &defaultValue) const;
int32_t repeatDelay(
const std::shared_ptr<wayland::WlSeat> &seat,
const std::optional<std::tuple<int32_t, int32_t>> &defaultValue) const;

protected:
FocusGroup *group_;
std::string name_;
Expand All @@ -37,6 +46,11 @@ class WaylandIMServerBase {
UniqueCPtr<struct xkb_state, xkb_state_unref> state_;

KeyStates modifiers_;

private:
std::optional<std::tuple<int32_t, int32_t>> repeatInfo(
const std::shared_ptr<wayland::WlSeat> &seat,
const std::optional<std::tuple<int32_t, int32_t>> &defaultValue) const;
};

} // namespace fcitx
Expand Down
25 changes: 17 additions & 8 deletions src/frontend/waylandim/waylandimserverv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
#include "waylandimserverv2.h"
#include <sys/mman.h>
#include <ctime>
#include "fcitx-utils/keysymgen.h"
#include "fcitx-utils/unixfd.h"
#include "fcitx-utils/utf8.h"
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -202,7 +204,6 @@ WaylandIMInputContextV2::WaylandIMInputContextV2(
[this](int32_t rate, int32_t delay) {
repeatInfoCallback(rate, delay);
});
repeatInfoCallback(repeatRate_, repeatDelay_);
focusInWrapper();
}
}
Expand All @@ -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;
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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();
}
}
Expand All @@ -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*/,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
5 changes: 4 additions & 1 deletion src/frontend/waylandim/waylandimserverv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<wayland::WlSeat> seat_;
std::unique_ptr<wayland::ZwpInputMethodV2> ic_;
Expand All @@ -134,7 +137,7 @@ class WaylandIMInputContextV2 : public VirtualInputContextGlue {
uint32_t repeatTime_ = 0;
KeySym repeatSym_ = FcitxKey_None;

int32_t repeatRate_ = 40, repeatDelay_ = 400;
std::optional<std::tuple<int32_t, int32_t>> repeatInfo_;

mutable OrderedMap<uint32_t, uint32_t> pressedVKKey_;
};
Expand Down
13 changes: 8 additions & 5 deletions src/modules/wayland/wayland_public.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
#include <fcitx-utils/metastring.h>
#include <fcitx/addoninstance.h>
#include <fcitx/focusgroup.h>
#include <wayland-client-protocol.h>
#include <wayland-client.h>

namespace fcitx {

typedef std::function<void(const std::string &name, wl_display *display,
FocusGroup *group)>
WaylandConnectionCreated;
typedef std::function<void(const std::string &name, wl_display *display)>
WaylandConnectionClosed;
using WaylandConnectionCreated =
std::function<void(const std::string &, wl_display *, FocusGroup *)>;
using WaylandConnectionClosed =
std::function<void(const std::string &, wl_display *)>;
} // namespace fcitx

FCITX_ADDON_DECLARE_FUNCTION(
Expand All @@ -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<std::tuple<int32_t, int32_t>>(
const std::string &name, wl_seat *));

#endif // _FCITX_MODULES_WAYLAND_WAYLAND_PUBLIC_H_
Loading

0 comments on commit 10d15ee

Please sign in to comment.