Skip to content
This repository was archived by the owner on Feb 19, 2026. It is now read-only.
Merged
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
2 changes: 1 addition & 1 deletion misc/fcitx5-vmk-server@.service
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Type=simple
ExecStart=/usr/bin/fcitx5-vmk-server -u %i

Restart=on-failure
RestartSec=5
RestartSec=0

[Install]
WantedBy=multi-user.target
15 changes: 12 additions & 3 deletions server/vmk-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,15 @@ int main(int argc, char* argv[]) {
if (uinput_fd_ >= 0) {
ioctl(uinput_fd_, UI_SET_EVBIT, EV_KEY);
ioctl(uinput_fd_, UI_SET_KEYBIT, KEY_BACKSPACE);
struct uinput_user_dev uidev{};
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "Fcitx5_Uinput_Server");
(void)write(uinput_fd_, &uidev, sizeof(uidev));
struct uinput_setup usetup;
memset(&usetup, 0, sizeof(usetup));
usetup.id.bustype = BUS_USB;
usetup.id.vendor = 0x1234;
usetup.id.product = 0x5678;
strncpy(usetup.name, "VMK-Uinput-Server", UINPUT_MAX_NAME_SIZE - 1);
ioctl(uinput_fd_, UI_DEV_SETUP, &usetup);
ioctl(uinput_fd_, UI_DEV_CREATE);
sleep(1);
}

int server_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); // Non-blocking socket
Expand Down Expand Up @@ -200,6 +205,10 @@ int main(int argc, char* argv[]) {
if (pending_backspaces > 0) {
send_single_backspace();
--pending_backspaces;
if (pending_backspaces == 0) {
char ack = '7';
send(fds[3].fd, &ack, sizeof(ack), MSG_NOSIGNAL | MSG_DONTWAIT);
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/ack-apps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#include <string>
#include <vector>

static std::vector<std::string> ack_apps = {"chrome", "chromium", "brave", "edge", "vivaldi", "opera", "coccoc", "cromite", "helium", "thorium", "slimjet", "yandex"};
1 change: 1 addition & 0 deletions src/vmk-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ namespace fcitx {
Option<bool> autoNonVnRestore{this, "AutoNonVnRestore", _("Auto restore keys with invalid words"), true};
Option<bool> modernStyle{this, "ModernStyle", _("Use oà, _uý (instead of òa, úy)"), true};
Option<bool> freeMarking{this, "FreeMarking", _("Allow type with more freedom"), true};
Option<bool> fixVmk1WithAck{this, "FixVmk1WithAck", _("Fix uinput mode with ack"), false};
SubConfigOption customKeymap{this, "CustomKeymap", _("Custom Keymap"), "fcitx://config/addon/vmk/custom_keymap"};);

} // namespace fcitx
Expand Down
54 changes: 50 additions & 4 deletions src/vmk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*
*/
#include "vmk.h"
#include "vmk-config.h"
#include "ack-apps.h"

#include <cstdint>
#include <fcitx-config/iniparser.h>
Expand Down Expand Up @@ -68,16 +68,16 @@ std::atomic<bool> needEngineReset{false};
std::string BASE_SOCKET_PATH;
// Global flag to signal mouse click for closing app mode menu
static std::atomic<bool> g_mouse_clicked{false};

std::atomic<bool> is_deleting_{false};
static const int MAX_BACKSPACE_COUNT = 15;
static const int MAX_BACKSPACE_COUNT = 8;
std::string SubstrChar(const std::string& s, size_t start, size_t len);
int compareAndSplitStrings(const std::string& A, const std::string& B, std::string& commonPrefix, std::string& deletedPart, std::string& addedPart);
std::once_flag monitor_init_flag;
std::atomic<bool> stop_flag_monitor{false};
std::atomic<bool> monitor_running{false};
int uinput_client_fd_ = -1;
int realtextLen = 0;
bool waitAck = false;
std::atomic<int> mouse_socket_fd{-1};

std::atomic<int64_t> replacement_start_ms_{0};
Expand Down Expand Up @@ -305,6 +305,11 @@ namespace fcitx {
send(uinput_client_fd_, &count, sizeof(count), MSG_NOSIGNAL);
}
}

if (waitAck) {
char ack;
recv(uinput_client_fd_, &ack, sizeof(ack), MSG_NOSIGNAL);
}
}

void replayBufferToEngine(const std::string& buffer) {
Expand Down Expand Up @@ -1170,6 +1175,9 @@ namespace fcitx {
}

void clearAllBuffers() {
if (is_deleting_.load(std::memory_order_acquire)) {
return;
}
oldPreBuffer_.clear();
history_.clear();
if (!is_deleting_.load(std::memory_order_acquire)) {
Expand Down Expand Up @@ -1463,6 +1471,18 @@ namespace fcitx {
}));
uiManager.registerAction("vmk-freemarking", freeMarkingAction_.get());

fixVmk1WithAckAction_ = std::make_unique<SimpleAction>();
fixVmk1WithAckAction_->setLongText(_("Fix uinput mode with ack"));
fixVmk1WithAckAction_->setIcon("network-transmit-receive");
fixVmk1WithAckAction_->setCheckable(true);
connections_.emplace_back(fixVmk1WithAckAction_->connect<SimpleAction::Activated>([this](InputContext* ic) {
config_.fixVmk1WithAck.setValue(!*config_.fixVmk1WithAck);
saveConfig();
refreshOption();
updateFixVmk1WithAckAction(ic);
}));
uiManager.registerAction("vmk-fixvmk1withack", fixVmk1WithAckAction_.get());

reloadConfig();
globalMode_ = modeStringToEnum(config_.mode.value());
updateModeAction(nullptr);
Expand Down Expand Up @@ -1530,6 +1550,7 @@ namespace fcitx {
updateAutoNonVnRestoreAction(nullptr);
updateModernStyleAction(nullptr);
updateFreeMarkingAction(nullptr);
updateFixVmk1WithAckAction(nullptr);
}

void vmkEngine::setSubConfig(const std::string& path, const RawConfig& config) {
Expand Down Expand Up @@ -1577,6 +1598,19 @@ namespace fcitx {
updateInputMethodAction(event.inputContext());
updateCharsetAction(event.inputContext());

if (*config_.fixVmk1WithAck) {
if (targetMode == VMKMode::VMK1 || targetMode == VMKMode::VMK1HC || targetMode == VMKMode::VMKSmooth) {
bool needWaitAck = false;
for (const auto& ackApp : ack_apps) {
if (appName.find(ackApp) != std::string::npos) {
needWaitAck = true;
break;
}
}
waitAck = needWaitAck;
}
}

setMode(targetMode, event.inputContext());

auto state = ic->propertyFor(&factory_);
Expand All @@ -1597,6 +1631,7 @@ namespace fcitx {
statusArea.addAction(StatusGroup::InputMethod, autoNonVnRestoreAction_.get());
statusArea.addAction(StatusGroup::InputMethod, modernStyleAction_.get());
statusArea.addAction(StatusGroup::InputMethod, freeMarkingAction_.get());
statusArea.addAction(StatusGroup::InputMethod, fixVmk1WithAckAction_.get());
}

void vmkEngine::keyEvent(const InputMethodEntry& entry, KeyEvent& keyEvent) {
Expand Down Expand Up @@ -1786,7 +1821,9 @@ namespace fcitx {
return;
}

state->reset();
if (event.type() == EventType::InputContextFocusOut) {
state->reset();
}
}

void vmkEngine::deactivate(const InputMethodEntry& entry, InputContextEvent& event) {
Expand Down Expand Up @@ -1918,6 +1955,15 @@ namespace fcitx {
freeMarkingAction_->update(ic);
}
}

void vmkEngine::updateFixVmk1WithAckAction(InputContext* ic) {
fixVmk1WithAckAction_->setChecked(*config_.fixVmk1WithAck);
fixVmk1WithAckAction_->setShortText(*config_.fixVmk1WithAck ? _("Fix Vmk1 With Ack: On") : _("Fix Vmk1 With Ack: Off"));
if (ic) {
fixVmk1WithAckAction_->update(ic);
}
}

void vmkEngine::loadAppRules() {
appRules_.clear();
std::ifstream file(appRulesPath_);
Expand Down
2 changes: 2 additions & 0 deletions src/vmk.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ namespace fcitx {
void updateAutoNonVnRestoreAction(InputContext* ic);
void updateModernStyleAction(InputContext* ic);
void updateFreeMarkingAction(InputContext* ic);
void updateFixVmk1WithAckAction(InputContext* ic);
void updateInputMethodAction(InputContext* ic);
void updateCharsetAction(InputContext* ic);
void populateConfig();
Expand Down Expand Up @@ -188,6 +189,7 @@ namespace fcitx {
std::unique_ptr<SimpleAction> autoNonVnRestoreAction_;
std::unique_ptr<SimpleAction> modernStyleAction_;
std::unique_ptr<SimpleAction> freeMarkingAction_;
std::unique_ptr<SimpleAction> fixVmk1WithAckAction_;
std::vector<ScopedConnection> connections_;
CGoObject dictionary_;
// ibus-bamboo mode save/load
Expand Down
Loading