Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/frontend/dbusfrontend/dbusfrontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "fcitx-utils/log.h"
#include "fcitx-utils/misc_p.h"
#include "fcitx-utils/rect.h"
#include "fcitx-utils/utf8.h"
#include "fcitx/addonfactory.h"
#include "fcitx/addoninstance.h"
#include "fcitx/candidatelist.h"
Expand Down Expand Up @@ -120,6 +121,7 @@ class InputMethod1 : public dbus::ObjectVTable<InputMethod1> {
};

class DBusInputContext1 : public InputContext,
public AtomicSurroundingTextInputContext,
public dbus::ObjectVTable<DBusInputContext1> {
public:
DBusInputContext1(int id, InputContextManager &icManager, InputMethod1 *im,
Expand Down Expand Up @@ -172,6 +174,24 @@ class DBusInputContext1 : public InputContext,

const dbus::ObjectPath &path() const { return path_; }

bool supportsAtomicSurroundingTextReplacement() const override {
return blocked_;
}

bool replaceSurroundingTextAtomically(
int offset, unsigned int size, const std::string &text) override {
if (!blocked_ || !utf8::validate(text)) {
return false;
}
blockedEvents_.emplace_back(
BATCHED_DELETE_SURROUNDING,
dbus::DBusStruct<int32_t, uint32_t>(offset, size));
if (!text.empty()) {
blockedEvents_.emplace_back(BATCHED_COMMIT_STRING, text);
}
return true;
}

void updateIM(const InputMethodEntry *entry) {
currentIMTo(name_, entry->name(), entry->uniqueName(),
entry->languageCode());
Expand Down
29 changes: 27 additions & 2 deletions src/frontend/waylandim/virtualinputcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ namespace fcitx {

class VirtualInputContextManager;

class VirtualInputContextGlue : public InputContext {
class VirtualInputContextGlue : public InputContext,
public AtomicSurroundingTextInputContext {
public:
using InputContext::InputContext;
// Qualifier is const to ensure the state is read from ic.
Expand All @@ -32,6 +33,20 @@ class VirtualInputContextGlue : public InputContext {
virtual void forwardKeyDelegate(InputContext *ic,
const ForwardKeyEvent &key) const = 0;
virtual void updatePreeditDelegate(InputContext *ic) const = 0;
virtual bool supportsAtomicSurroundingTextReplacementDelegate(
const InputContext *ic) const = 0;
virtual bool replaceSurroundingTextAtomicallyDelegate(
InputContext *ic, int offset, unsigned int size,
const std::string &text) const = 0;

bool supportsAtomicSurroundingTextReplacement() const final {
return supportsAtomicSurroundingTextReplacementDelegate(this);
}
bool replaceSurroundingTextAtomically(
int offset, unsigned int size, const std::string &text) final {
return replaceSurroundingTextAtomicallyDelegate(
this, offset, size, text);
}

bool realFocus() const {
if (virtualICManager_) {
Expand Down Expand Up @@ -70,7 +85,8 @@ class VirtualInputContextGlue : public InputContext {
VirtualInputContextManager *virtualICManager_ = nullptr;
};

class VirtualInputContext : public InputContext {
class VirtualInputContext : public InputContext,
public AtomicSurroundingTextInputContext {
public:
VirtualInputContext(InputContextManager &manager,
const std::string &program,
Expand All @@ -86,6 +102,15 @@ class VirtualInputContext : public InputContext {

const char *frontend() const override { return parent_->frontend(); }
InputContext *parent() const { return parent_; }
bool supportsAtomicSurroundingTextReplacement() const final {
return parent_->supportsAtomicSurroundingTextReplacementDelegate(
this);
}
bool replaceSurroundingTextAtomically(
int offset, unsigned int size, const std::string &text) final {
return parent_->replaceSurroundingTextAtomicallyDelegate(
this, offset, size, text);
}

protected:
void commitStringImpl(const std::string &text) override {
Expand Down
37 changes: 37 additions & 0 deletions src/frontend/waylandim/waylandimserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,43 @@ void WaylandIMInputContextV1::updatePreeditDelegate(InputContext *ic) const {
preeditCommitString.c_str());
}

bool WaylandIMInputContextV1::
supportsAtomicSurroundingTextReplacementDelegate(
const InputContext * /*ic*/) const {
return static_cast<bool>(ic_);
}

bool WaylandIMInputContextV1::replaceSurroundingTextAtomicallyDelegate(
InputContext *ic, int offset, unsigned int size,
const std::string &text) const {
if (!ic_ || !utf8::validate(text)) {
return false;
}
const size_t cursor = ic->surroundingText().cursor();
if (static_cast<ssize_t>(cursor) + offset < 0) {
return false;
}
const auto &surrounding = ic->surroundingText().text();
const auto len = utf8::length(surrounding);
const size_t start = cursor + offset;
const size_t end = start + size;
if (cursor > len || start > len || end > len) {
return false;
}
const auto startBytes = utf8::ncharByteLength(surrounding.begin(), start);
const auto cursorBytes =
utf8::ncharByteLength(surrounding.begin(), cursor);
const auto sizeBytes =
utf8::ncharByteLength(surrounding.begin() + startBytes, size);
ic_->deleteSurroundingText(startBytes - cursorBytes, sizeBytes);
bool committed = false;
WaylandIMServerBase::commitStringWrapper(text, [&](const char *str) {
ic_->commitString(serial_, str);
committed = true;
});
return committed;
}

void WaylandIMInputContextV1::deleteSurroundingTextDelegate(
InputContext *ic, int offset, unsigned int size) const {
if (!ic_) {
Expand Down
5 changes: 5 additions & 0 deletions src/frontend/waylandim/waylandimserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ class WaylandIMInputContextV1 : public VirtualInputContextGlue {
}
void deleteSurroundingTextDelegate(InputContext *ic, int offset,
unsigned int size) const override;
bool supportsAtomicSurroundingTextReplacementDelegate(
const InputContext *ic) const override;
bool replaceSurroundingTextAtomicallyDelegate(
InputContext *ic, int offset, unsigned int size,
const std::string &text) const override;
void forwardKeyDelegate(InputContext *ic,
const ForwardKeyEvent &key) const override {
FCITX_UNUSED(ic);
Expand Down
43 changes: 43 additions & 0 deletions src/frontend/waylandim/waylandimserverv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,49 @@ void WaylandIMInputContextV2::updatePreeditDelegate(InputContext *ic) const {
ic_->commit(serial_);
}

bool WaylandIMInputContextV2::
supportsAtomicSurroundingTextReplacementDelegate(
const InputContext * /*ic*/) const {
return realFocus() && static_cast<bool>(ic_);
}

bool WaylandIMInputContextV2::replaceSurroundingTextAtomicallyDelegate(
InputContext *ic, int offset, unsigned int size,
const std::string &text) const {
if (!realFocus() || !ic_ || !utf8::validate(text) || offset > 0 ||
offset + static_cast<ssize_t>(size) < 0) {
return false;
}
const size_t cursor = ic->surroundingText().cursor();
if (static_cast<ssize_t>(cursor) + offset < 0) {
return false;
}
const auto &surrounding = ic->surroundingText().text();
const auto len = utf8::length(surrounding);
const size_t start = cursor + offset;
const size_t end = start + size;
if (cursor > len || start > len || end > len) {
return false;
}
const auto startBytes = utf8::ncharByteLength(surrounding.begin(), start);
const auto cursorBytes =
utf8::ncharByteLength(surrounding.begin(), cursor);
const auto sizeBytes =
utf8::ncharByteLength(surrounding.begin() + startBytes, size);
ic_->deleteSurroundingText(cursorBytes - startBytes,
startBytes + sizeBytes - cursorBytes);
bool committed = false;
WaylandIMServerBase::commitStringWrapper(text, [&](const char *str) {
ic_->commitString(str);
committed = true;
});
if (!committed) {
return false;
}
ic_->commit(serial_);
return true;
}

void WaylandIMInputContextV2::deleteSurroundingTextDelegate(
InputContext *ic, int offset, unsigned int size) const {
if (!realFocus()) {
Expand Down
5 changes: 5 additions & 0 deletions src/frontend/waylandim/waylandimserverv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ class WaylandIMInputContextV2 : public VirtualInputContextGlue {
}
void deleteSurroundingTextDelegate(InputContext *ic, int offset,
unsigned int size) const override;
bool supportsAtomicSurroundingTextReplacementDelegate(
const InputContext *ic) const override;
bool replaceSurroundingTextAtomicallyDelegate(
InputContext *ic, int offset, unsigned int size,
const std::string &text) const override;
void forwardKeyDelegate(InputContext * /*ic*/,
const ForwardKeyEvent &key) const override;

Expand Down
3 changes: 3 additions & 0 deletions src/lib/fcitx/inputcontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@

namespace fcitx {

AtomicSurroundingTextInputContext::~AtomicSurroundingTextInputContext() =
default;

namespace {

bool shouldDisablePreeditByDefault(const std::string &program) {
Expand Down
9 changes: 9 additions & 0 deletions src/lib/fcitx/inputcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ class InputPanel;
class StatusArea;
using InputContextVisitor = std::function<bool(InputContext *ic)>;

/** Optional frontend contract for one-transaction surrounding replacement. */
class FCITXCORE_EXPORT AtomicSurroundingTextInputContext {
public:
virtual ~AtomicSurroundingTextInputContext();
virtual bool supportsAtomicSurroundingTextReplacement() const = 0;
virtual bool replaceSurroundingTextAtomically(
int offset, unsigned int size, const std::string &text) = 0;
};

/**
* An input context represents a client of Fcitx. It can be a Window, or a text
* field depending on the application.
Expand Down