From 91279bfe88fed00e6c946eab72f7a4b22b8780c6 Mon Sep 17 00:00:00 2001 From: Loc Huynh <148019203+hthienloc@users.noreply.github.com> Date: Mon, 16 Feb 2026 16:24:24 +0700 Subject: [PATCH 01/10] feat(vmk): improve app mode menu UI, key bindings, mouse interaction (#52) --------- Co-authored-by: Nguyen Hoang Ky <57983253+nhktmdzhg@users.noreply.github.com> --- src/vmk.cpp | 247 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 214 insertions(+), 33 deletions(-) diff --git a/src/vmk.cpp b/src/vmk.cpp index 4885120..bf9c9be 100644 --- a/src/vmk.cpp +++ b/src/vmk.cpp @@ -397,6 +397,24 @@ namespace fcitx { } // Helper function for emoji mode + void updateEmojiPageStatus(CommonCandidateList* commonList) { + if (!commonList || commonList->empty()) { + return; + } + + int pageSize = commonList->pageSize(); + if (pageSize <= 0) { + pageSize = 9; + } + + int totalItems = commonList->totalSize(); + int currentPage = commonList->currentPage() + 1; + int totalPages = (totalItems + pageSize - 1) / pageSize; + + std::string status = _("Page ") + std::to_string(currentPage) + "/" + std::to_string(totalPages); + ic_->inputPanel().setAuxDown(Text(status)); + } + void handleEmojiMode(KeyEvent& keyEvent) { if (keyEvent.key().hasModifier()) { keyEvent.forward(); @@ -482,6 +500,7 @@ namespace fcitx { } if (handled) { + updateEmojiPageStatus(commonList.get()); ic_->updateUserInterface(UserInterfaceComponent::InputPanel); keyEvent.filterAndAccept(); return; @@ -589,6 +608,8 @@ namespace fcitx { candidateList->setGlobalCursorIndex(0); ic_->inputPanel().setCandidateList(std::move(candidateList)); + auto currentList = std::dynamic_pointer_cast(ic_->inputPanel().candidateList()); + updateEmojiPageStatus(currentList.get()); } else { ic_->inputPanel().setCandidateList(nullptr); } @@ -1061,6 +1082,26 @@ namespace fcitx { return; } is_deleting_.store(false); + + // Commit pending preedit text before clearing buffers to prevent data loss. + // Strategy: Call EngineCommitPreedit() first (for Preedit mode finalization), + // then pull any remaining preedit text (for other modes like VMK1/VMKSmooth). + // This ensures text is saved regardless of which mode we're switching from. + if (vmkEngine_) { + // Finalize preedit properly (required for Preedit mode) + EngineCommitPreedit(vmkEngine_.handle()); + UniqueCPtr commit(EnginePullCommit(vmkEngine_.handle())); + if (commit && commit.get()[0]) { + ic_->commitString(commit.get()); + } + + // Pull any remaining preedit text (for VMK1/VMKSmooth modes) + UniqueCPtr preedit(EnginePullPreedit(vmkEngine_.handle())); + if (preedit && preedit.get()[0]) { + ic_->commitString(preedit.get()); + } + } + clearAllBuffers(); switch (realMode) { @@ -1078,8 +1119,6 @@ namespace fcitx { break; } case VMKMode::Emoji: { - emojiBuffer_.clear(); - emojiCandidates_.clear(); ic_->inputPanel().reset(); ic_->updateUserInterface(UserInterfaceComponent::InputPanel); ic_->updatePreedit(); @@ -1105,6 +1144,19 @@ namespace fcitx { ic_->updatePreedit(); break; } + case VMKMode::VMK1: + case VMKMode::VMK1HC: + case VMKMode::VMKSmooth: { + // For uinput modes: commit pending preedit when focus changes + // (e.g., when user tabs to another field while typing) + if (vmkEngine_) { + UniqueCPtr preedit(EnginePullPreedit(vmkEngine_.handle())); + if (preedit && preedit.get()[0]) { + ic_->commitString(preedit.get()); + } + } + break; + } case VMKMode::VMK2: { if (vmkEngine_) ResetEngine(vmkEngine_.handle()); @@ -1553,7 +1605,8 @@ namespace fcitx { FCITX_UNUSED(entry); auto ic = keyEvent.inputContext(); - // Check if mouse was clicked to close app mode menu + // Handle mouse click event from fcitx5-vmk-server to close app mode menu + // The server sends signal via Unix socket when user clicks outside the menu if (isSelectingAppMode_ && g_mouse_clicked.load(std::memory_order_relaxed)) { closeAppModeMenu(); ic->inputPanel().reset(); @@ -1562,19 +1615,79 @@ namespace fcitx { state->reset(); } - // logic when opening app mode menu + // Handle keyboard input when app mode selection menu is active if (isSelectingAppMode_) { if (keyEvent.isRelease()) return; + auto baseList = ic->inputPanel().candidateList(); + auto menuList = std::dynamic_pointer_cast(baseList); + KeySym keySym = keyEvent.key().sym(); + + // Lambda to move cursor in candidate list with wrap-around + // Note: Index 0 is reserved for header ("App: ..."), so valid range is [1, totalSize-1] + auto moveCursor = [&](int delta) { + if (!menuList || menuList->empty()) { + return false; + } + + int totalSize = menuList->totalSize(); + if (totalSize <= 1) { + return false; + } + + int cursorIndex = menuList->globalCursorIndex(); + if (cursorIndex < 1 || cursorIndex >= totalSize) { + cursorIndex = 1; + } + + int nextIndex = cursorIndex + delta; + // Wrap around: bottom → top or top → bottom + if (nextIndex < 1) { + nextIndex = totalSize - 1; + } else if (nextIndex >= totalSize) { + nextIndex = 1; + } + + menuList->setGlobalCursorIndex(nextIndex); + ic->updateUserInterface(UserInterfaceComponent::InputPanel); + return true; + }; + keyEvent.filterAndAccept(); + VMKMode selectedMode = VMKMode::NoMode; bool selectionMade = false; - KeySym keySym = keyEvent.key().sym(); - - // map number key to mode switch (keySym) { + case FcitxKey_Tab: + case FcitxKey_Down: { + if (moveCursor(1)) { + return; + } + break; + } + case FcitxKey_ISO_Left_Tab: + case FcitxKey_Up: { + if (moveCursor(-1)) { + return; + } + break; + } + case FcitxKey_space: + case FcitxKey_Return: { + if (menuList && !menuList->empty()) { + int selectedIndex = menuList->globalCursorIndex(); + if (selectedIndex < 1 || selectedIndex >= menuList->totalSize()) { + selectedIndex = 1; + } + menuList->candidateFromAll(selectedIndex).select(ic); + return; + } + break; + } + // Map keyboard shortcuts to modes + // Numbers [1-4]: VMK input modes, Letters [q/w/e/r]: Special modes/actions case FcitxKey_1: { selectedMode = VMKMode::VMKSmooth; break; @@ -1591,19 +1704,19 @@ namespace fcitx { selectedMode = VMKMode::VMK2; break; } - case FcitxKey_5: { + case FcitxKey_q: { selectedMode = VMKMode::Preedit; break; } - case FcitxKey_6: { + case FcitxKey_w: { selectedMode = VMKMode::Emoji; break; } - case FcitxKey_7: { + case FcitxKey_e: { selectedMode = VMKMode::Off; break; } - case FcitxKey_8: { + case FcitxKey_r: { if (appRules_.count(currentConfigureApp_)) { appRules_.erase(currentConfigureApp_); saveAppRules(); @@ -1649,7 +1762,8 @@ namespace fcitx { return; } - // logic when typing ` + // Open app mode selection menu when user presses backtick/grave key (`) + // Triggered by Shift+` key combination, allows user to change input mode per-app if (!keyEvent.isRelease() && keyEvent.rawKey().check(FcitxKey_grave)) { currentConfigureApp_ = ic->program(); if (currentConfigureApp_.empty()) @@ -1845,6 +1959,25 @@ namespace fcitx { g_mouse_clicked.store(false, std::memory_order_relaxed); } + namespace { + // Custom candidate word class that supports both mouse click and keyboard selection + // Unlike DisplayOnlyCandidateWord, this executes a callback when selected, + // enabling interactive menu items in the app mode selection UI + class AppModeCandidateWord : public CandidateWord { + public: + AppModeCandidateWord(Text text, std::function callback) : CandidateWord(std::move(text)), callback_(std::move(callback)) {} + + void select(InputContext* ic) const override { + if (callback_) { + callback_(ic); + } + } + + private: + std::function callback_; + }; + } // namespace + void vmkEngine::showAppModeMenu(InputContext* ic) { isSelectingAppMode_ = true; @@ -1853,31 +1986,79 @@ namespace fcitx { candidateList->setLayoutHint(CandidateLayoutHint::Vertical); candidateList->setPageSize(10); - VMKMode currentAppRules = VMKMode::Off; - if (appRules_.count(currentConfigureApp_)) { - currentAppRules = appRules_[currentConfigureApp_]; - } else { - currentAppRules = globalMode_; - } - + // Helper lambda: Add ">>" marker to highlight current active mode auto getLabel = [&](const VMKMode& modeName, const std::string& modeLabel) { - if (modeName == currentAppRules) { - return Text(modeLabel + _(" (Default)")); + if (modeName == realMode) { + return Text(">> " + modeLabel); } else { - return Text(modeLabel); + return Text(" " + modeLabel); } }; - candidateList->append(std::make_unique(Text(_("App name detected by fcitx5: ") + currentConfigureApp_))); - candidateList->append(std::make_unique(getLabel(VMKMode::VMKSmooth, _("1. Fake backspace by Uinput (smooth)")))); - candidateList->append(std::make_unique(getLabel(VMKMode::VMK1, _("2. Fake backspace by Uinput")))); - candidateList->append(std::make_unique(getLabel(VMKMode::VMK1HC, _("3. Fake backspace by Uinput for wine apps")))); - candidateList->append(std::make_unique(getLabel(VMKMode::VMK2, _("4. Surrounding Text")))); - candidateList->append(std::make_unique(getLabel(VMKMode::Preedit, _("5. Preedit")))); - candidateList->append(std::make_unique(Text(_("6. Emoji mode")))); - candidateList->append(std::make_unique(getLabel(VMKMode::Off, "7. OFF - Disable Input Method"))); - candidateList->append(std::make_unique(Text(_("8. Remove app settings")))); - candidateList->append(std::make_unique(Text(_("`. Close menu and type `")))); + // Helper lambda: Cleanup after mode selection (reset UI and commit pending text) + auto cleanup = [this](InputContext* ic) { + isSelectingAppMode_ = false; + ic->inputPanel().reset(); + ic->updateUserInterface(UserInterfaceComponent::InputPanel); + auto state = ic->propertyFor(&factory_); + state->reset(); // This will commit any pending preedit text + }; + + // Helper lambda: Create callback to apply selected mode and save app-specific settings + // Note: Emoji mode is transient (not saved to appRules), user must explicitly + // select it each time they open the menu + auto applyMode = [this, cleanup](VMKMode mode) { + return [this, mode, cleanup](InputContext* ic) { + if (mode != VMKMode::Emoji) { + appRules_[currentConfigureApp_] = mode; + saveAppRules(); + } + + realMode = mode; + cleanup(ic); + }; + }; + + // Build candidate list for app mode menu + // Structure: Header + 8 selectable items (4 VMK modes + 4 special options) + candidateList->append(std::make_unique(Text(_("App: ") + currentConfigureApp_))); + candidateList->append(std::make_unique(getLabel(VMKMode::VMKSmooth, _("[1] Fake backspace by Uinput (smooth)")), applyMode(VMKMode::VMKSmooth))); + candidateList->append(std::make_unique(getLabel(VMKMode::VMK1, _("[2] Fake backspace by Uinput")), applyMode(VMKMode::VMK1))); + candidateList->append(std::make_unique(getLabel(VMKMode::VMK1HC, _("[3] Fake backspace by Uinput for wine apps")), applyMode(VMKMode::VMK1HC))); + candidateList->append(std::make_unique(getLabel(VMKMode::VMK2, _("[4] Surrounding Text")), applyMode(VMKMode::VMK2))); + candidateList->append(std::make_unique(getLabel(VMKMode::Preedit, _("[q] Preedit")), applyMode(VMKMode::Preedit))); + candidateList->append(std::make_unique(getLabel(VMKMode::Emoji, _("[w] Emoji mode")), applyMode(VMKMode::Emoji))); + candidateList->append(std::make_unique(getLabel(VMKMode::Off, _("[e] OFF - Disable Input Method")), applyMode(VMKMode::Off))); + + candidateList->append(std::make_unique(Text(_("[r] Remove app settings")), [this, cleanup](InputContext* ic) { + if (appRules_.count(currentConfigureApp_)) { + appRules_.erase(currentConfigureApp_); + saveAppRules(); + } + cleanup(ic); + })); + + candidateList->append(std::make_unique(Text(_("[`] Close menu and type `")), [cleanup](InputContext* ic) { + cleanup(ic); + Key key(FcitxKey_grave); + ic->forwardKey(key, false); + ic->forwardKey(key, true); + })); + + // Set initial cursor position to highlight current active mode + // Index mapping: 1=VMKSmooth, 2=VMK1, 3=VMK1HC, 4=VMK2, 5=Preedit, 6=Emoji, 7=Off + int selectedIndex = 1; + switch (realMode) { + case VMKMode::VMKSmooth: selectedIndex = 1; break; + case VMKMode::VMK1: selectedIndex = 2; break; + case VMKMode::VMK1HC: selectedIndex = 3; break; + case VMKMode::VMK2: selectedIndex = 4; break; + case VMKMode::Preedit: selectedIndex = 5; break; + case VMKMode::Emoji: selectedIndex = 6; break; + case VMKMode::Off: selectedIndex = 7; break; + default: selectedIndex = 1; break; + } + candidateList->setGlobalCursorIndex(selectedIndex); ic->inputPanel().reset(); ic->inputPanel().setCandidateList(std::move(candidateList)); @@ -1921,4 +2102,4 @@ int compareAndSplitStrings(const std::string& A, const std::string& B, std::stri deletedPart.assign(ptrA, endA); addedPart.assign(ptrB, endB); return (deletedPart.empty() && addedPart.empty()) ? 1 : 2; -} \ No newline at end of file +} From 5899b10a71d2185869a6bde4526d454279faacd5 Mon Sep 17 00:00:00 2001 From: Nguyen Hoang Ky Date: Mon, 16 Feb 2026 19:06:33 +0700 Subject: [PATCH 02/10] fix: commit raw string instead of forward grave key --- src/vmk.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/vmk.cpp b/src/vmk.cpp index bf9c9be..9c9dd76 100644 --- a/src/vmk.cpp +++ b/src/vmk.cpp @@ -1734,9 +1734,7 @@ namespace fcitx { ic->updateUserInterface(UserInterfaceComponent::InputPanel); auto state = ic->propertyFor(&factory_); state->reset(); - Key key(FcitxKey_grave); - ic->forwardKey(key, false); - ic->forwardKey(key, true); + ic->commitString("`"); return; } default: break; @@ -2040,9 +2038,7 @@ namespace fcitx { candidateList->append(std::make_unique(Text(_("[`] Close menu and type `")), [cleanup](InputContext* ic) { cleanup(ic); - Key key(FcitxKey_grave); - ic->forwardKey(key, false); - ic->forwardKey(key, true); + ic->commitString("`"); })); // Set initial cursor position to highlight current active mode From d027bde32d301e2edf4066f2b5229ee2c85f789d Mon Sep 17 00:00:00 2001 From: Nguyen Hoang Ky Date: Mon, 16 Feb 2026 19:44:52 +0700 Subject: [PATCH 03/10] feat: Change icon to "E" when switch to "Off" mode --- data/icons/scalable/apps/fcitx-vmk-off.svg | 8 +++++ data/icons/scalable/apps/fcitx-vmk.svg | 10 +++---- .../apps/org.fcitx.Fcitx5.fcitx-vmk-off.svg | 1 + src/vmk.cpp | 30 +++++++++++++------ src/vmk.h | 3 ++ 5 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 data/icons/scalable/apps/fcitx-vmk-off.svg create mode 120000 data/icons/scalable/apps/org.fcitx.Fcitx5.fcitx-vmk-off.svg diff --git a/data/icons/scalable/apps/fcitx-vmk-off.svg b/data/icons/scalable/apps/fcitx-vmk-off.svg new file mode 100644 index 0000000..c091ce6 --- /dev/null +++ b/data/icons/scalable/apps/fcitx-vmk-off.svg @@ -0,0 +1,8 @@ + + + + + E + \ No newline at end of file diff --git a/data/icons/scalable/apps/fcitx-vmk.svg b/data/icons/scalable/apps/fcitx-vmk.svg index 3ed0d28..8a3f41b 100644 --- a/data/icons/scalable/apps/fcitx-vmk.svg +++ b/data/icons/scalable/apps/fcitx-vmk.svg @@ -1,8 +1,8 @@ - - + - - - + V + \ No newline at end of file diff --git a/data/icons/scalable/apps/org.fcitx.Fcitx5.fcitx-vmk-off.svg b/data/icons/scalable/apps/org.fcitx.Fcitx5.fcitx-vmk-off.svg new file mode 120000 index 0000000..7dcb9cd --- /dev/null +++ b/data/icons/scalable/apps/org.fcitx.Fcitx5.fcitx-vmk-off.svg @@ -0,0 +1 @@ +fcitx-vmk-off.svg \ No newline at end of file diff --git a/src/vmk.cpp b/src/vmk.cpp index 9c9dd76..6d95604 100644 --- a/src/vmk.cpp +++ b/src/vmk.cpp @@ -1327,13 +1327,10 @@ namespace fcitx { config_.mode.setValue(modeEnumToString(mode)); saveConfig(); - realMode = mode; + setMode(mode, ic); globalMode_ = mode; reloadConfig(); updateModeAction(ic); - if (ic) { - ic->updateUserInterface(UserInterfaceComponent::StatusArea); - } })); modeMenu_->addAction(action.get()); modeSubAction_.push_back(std::move(action)); @@ -1579,7 +1576,7 @@ namespace fcitx { updateInputMethodAction(event.inputContext()); updateCharsetAction(event.inputContext()); - realMode = targetMode; + setMode(targetMode, event.inputContext()); auto state = ic->propertyFor(&factory_); @@ -1746,7 +1743,7 @@ namespace fcitx { saveAppRules(); } - realMode = selectedMode; + setMode(selectedMode, ic); selectionMade = true; } @@ -1835,8 +1832,9 @@ namespace fcitx { void vmkEngine::updateModeAction(InputContext* ic) { std::string currentModeStr = config_.mode.value(); - realMode = modeStringToEnum(currentModeStr); - globalMode_ = realMode; + VMKMode newMode = modeStringToEnum(currentModeStr); + globalMode_ = newMode; + setMode(newMode, ic); for (const auto& action : modeSubAction_) { action->setChecked(action->name() == "vmk-mode-" + currentModeStr); @@ -2012,7 +2010,7 @@ namespace fcitx { saveAppRules(); } - realMode = mode; + setMode(mode, ic); cleanup(ic); }; }; @@ -2060,6 +2058,20 @@ namespace fcitx { ic->inputPanel().setCandidateList(std::move(candidateList)); ic->updateUserInterface(UserInterfaceComponent::InputPanel); } + + void vmkEngine::setMode(VMKMode mode, InputContext* ic) { + realMode = mode; + if (ic) { + ic->updateUserInterface(UserInterfaceComponent::StatusArea); + } + } + + std::string vmkEngine::overrideIcon(const fcitx::InputMethodEntry& /*entry*/) { + if (realMode == VMKMode::Off) { + return "fcitx-vmk-off"; + } + return {}; + } } // namespace fcitx FCITX_ADDON_FACTORY(fcitx::vmkFactory) diff --git a/src/vmk.h b/src/vmk.h index ddad168..c667c4f 100644 --- a/src/vmk.h +++ b/src/vmk.h @@ -111,6 +111,8 @@ namespace fcitx { std::string subMode(const fcitx::InputMethodEntry& entry, fcitx::InputContext& inputContext) override; + std::string overrideIcon(const fcitx::InputMethodEntry& entry) override; + const auto& config() const { return config_; } @@ -157,6 +159,7 @@ namespace fcitx { EmojiLoader& emojiLoader() { return emojiLoader_; } + void setMode(VMKMode mode, InputContext* ic); private: Instance* instance_; From 2af4f6acc7f380852b4037a10ac58627bfe82848 Mon Sep 17 00:00:00 2001 From: Nguyen Hoang Ky Date: Mon, 16 Feb 2026 20:55:28 +0700 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20Change=20icon=20to=20=F0=9F=98=84?= =?UTF-8?q?=20when=20switch=20to=20"Emoji"=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/icons/scalable/apps/fcitx-vmk-emoji.svg | 10 ++++++++++ data/icons/scalable/apps/fcitx-vmk-off.svg | 6 ++---- data/icons/scalable/apps/fcitx-vmk.svg | 6 ++---- .../scalable/apps/org.fcitx.Fcitx5.fcitx-vmk-emoji.svg | 1 + src/vmk.cpp | 8 +++++--- 5 files changed, 20 insertions(+), 11 deletions(-) create mode 100644 data/icons/scalable/apps/fcitx-vmk-emoji.svg create mode 120000 data/icons/scalable/apps/org.fcitx.Fcitx5.fcitx-vmk-emoji.svg diff --git a/data/icons/scalable/apps/fcitx-vmk-emoji.svg b/data/icons/scalable/apps/fcitx-vmk-emoji.svg new file mode 100644 index 0000000..4848ff3 --- /dev/null +++ b/data/icons/scalable/apps/fcitx-vmk-emoji.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/data/icons/scalable/apps/fcitx-vmk-off.svg b/data/icons/scalable/apps/fcitx-vmk-off.svg index c091ce6..ca3397c 100644 --- a/data/icons/scalable/apps/fcitx-vmk-off.svg +++ b/data/icons/scalable/apps/fcitx-vmk-off.svg @@ -1,8 +1,6 @@ - - E + \ No newline at end of file diff --git a/data/icons/scalable/apps/fcitx-vmk.svg b/data/icons/scalable/apps/fcitx-vmk.svg index 8a3f41b..93f7e02 100644 --- a/data/icons/scalable/apps/fcitx-vmk.svg +++ b/data/icons/scalable/apps/fcitx-vmk.svg @@ -1,8 +1,6 @@ - - V + \ No newline at end of file diff --git a/data/icons/scalable/apps/org.fcitx.Fcitx5.fcitx-vmk-emoji.svg b/data/icons/scalable/apps/org.fcitx.Fcitx5.fcitx-vmk-emoji.svg new file mode 120000 index 0000000..fdc96e5 --- /dev/null +++ b/data/icons/scalable/apps/org.fcitx.Fcitx5.fcitx-vmk-emoji.svg @@ -0,0 +1 @@ +fcitx-vmk-emoji.svg \ No newline at end of file diff --git a/src/vmk.cpp b/src/vmk.cpp index 6d95604..e5fc45a 100644 --- a/src/vmk.cpp +++ b/src/vmk.cpp @@ -7,6 +7,7 @@ * */ #include "vmk.h" +#include "vmk-config.h" #include #include @@ -2067,10 +2068,11 @@ namespace fcitx { } std::string vmkEngine::overrideIcon(const fcitx::InputMethodEntry& /*entry*/) { - if (realMode == VMKMode::Off) { - return "fcitx-vmk-off"; + switch (realMode) { + case VMKMode::Off: return "fcitx-vmk-off"; + case VMKMode::Emoji: return "fcitx-vmk-emoji"; + default: return {}; } - return {}; } } // namespace fcitx From 1475125f82268884f400e40ad7405e677f571008 Mon Sep 17 00:00:00 2001 From: Nguyen Hoang Ky Date: Mon, 16 Feb 2026 22:20:01 +0700 Subject: [PATCH 05/10] fix: add ack for some applications --- misc/fcitx5-vmk-server@.service | 2 +- server/vmk-server.cpp | 15 ++++++++--- src/ack-apps.h | 4 +++ src/vmk-config.h | 1 + src/vmk.cpp | 45 ++++++++++++++++++++++++++++++--- src/vmk.h | 2 ++ 6 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 src/ack-apps.h diff --git a/misc/fcitx5-vmk-server@.service b/misc/fcitx5-vmk-server@.service index 39c338e..89835d7 100644 --- a/misc/fcitx5-vmk-server@.service +++ b/misc/fcitx5-vmk-server@.service @@ -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 \ No newline at end of file diff --git a/server/vmk-server.cpp b/server/vmk-server.cpp index b5dceee..fba91d3 100644 --- a/server/vmk-server.cpp +++ b/server/vmk-server.cpp @@ -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 @@ -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); + } } } diff --git a/src/ack-apps.h b/src/ack-apps.h new file mode 100644 index 0000000..ad1d602 --- /dev/null +++ b/src/ack-apps.h @@ -0,0 +1,4 @@ +#include +#include + +static std::vector ack_apps = {"chrome", "chromium", "brave", "edge", "vivaldi", "opera", "coccoc", "cromite", "helium", "thorium", "slimjet", "yandex"}; \ No newline at end of file diff --git a/src/vmk-config.h b/src/vmk-config.h index fc3b5bf..6cd0ae4 100644 --- a/src/vmk-config.h +++ b/src/vmk-config.h @@ -135,6 +135,7 @@ namespace fcitx { Option autoNonVnRestore{this, "AutoNonVnRestore", _("Auto restore keys with invalid words"), true}; Option modernStyle{this, "ModernStyle", _("Use oà, _uý (instead of òa, úy)"), true}; Option freeMarking{this, "FreeMarking", _("Allow type with more freedom"), true}; + Option fixVmk1WithAck{this, "FixVmk1WithAck", _("Fix uinput mode with ack"), false}; SubConfigOption customKeymap{this, "CustomKeymap", _("Custom Keymap"), "fcitx://config/addon/vmk/custom_keymap"};); } // namespace fcitx diff --git a/src/vmk.cpp b/src/vmk.cpp index e5fc45a..e0a8030 100644 --- a/src/vmk.cpp +++ b/src/vmk.cpp @@ -7,7 +7,7 @@ * */ #include "vmk.h" -#include "vmk-config.h" +#include "ack-apps.h" #include #include @@ -68,9 +68,8 @@ std::atomic needEngineReset{false}; std::string BASE_SOCKET_PATH; // Global flag to signal mouse click for closing app mode menu static std::atomic g_mouse_clicked{false}; - std::atomic 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; @@ -78,6 +77,7 @@ std::atomic stop_flag_monitor{false}; std::atomic monitor_running{false}; int uinput_client_fd_ = -1; int realtextLen = 0; +bool waitAck = false; std::atomic mouse_socket_fd{-1}; std::atomic replacement_start_ms_{0}; @@ -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) { @@ -1463,6 +1468,18 @@ namespace fcitx { })); uiManager.registerAction("vmk-freemarking", freeMarkingAction_.get()); + fixVmk1WithAckAction_ = std::make_unique(); + fixVmk1WithAckAction_->setLongText(_("Fix uinput mode with ack")); + fixVmk1WithAckAction_->setIcon("network-transmit-receive"); + fixVmk1WithAckAction_->setCheckable(true); + connections_.emplace_back(fixVmk1WithAckAction_->connect([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); @@ -1530,6 +1547,7 @@ namespace fcitx { updateAutoNonVnRestoreAction(nullptr); updateModernStyleAction(nullptr); updateFreeMarkingAction(nullptr); + updateFixVmk1WithAckAction(nullptr); } void vmkEngine::setSubConfig(const std::string& path, const RawConfig& config) { @@ -1577,6 +1595,17 @@ namespace fcitx { updateInputMethodAction(event.inputContext()); updateCharsetAction(event.inputContext()); + if (*config_.fixVmk1WithAck) { + if (targetMode == VMKMode::VMK1 || targetMode == VMKMode::VMK1HC || targetMode == VMKMode::VMKSmooth) { + for (const auto& ackApp : ack_apps) { + if (appName.find(ackApp) != std::string::npos) { + waitAck = true; + break; + } + } + } + } + setMode(targetMode, event.inputContext()); auto state = ic->propertyFor(&factory_); @@ -1597,6 +1626,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) { @@ -1918,6 +1948,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_); diff --git a/src/vmk.h b/src/vmk.h index c667c4f..0fb4290 100644 --- a/src/vmk.h +++ b/src/vmk.h @@ -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(); @@ -188,6 +189,7 @@ namespace fcitx { std::unique_ptr autoNonVnRestoreAction_; std::unique_ptr modernStyleAction_; std::unique_ptr freeMarkingAction_; + std::unique_ptr fixVmk1WithAckAction_; std::vector connections_; CGoObject dictionary_; // ibus-bamboo mode save/load From a7635b3c90cc3e7203637e6438a4b0dcb7cd29bb Mon Sep 17 00:00:00 2001 From: Nguyen Hoang Ky Date: Mon, 16 Feb 2026 23:07:41 +0700 Subject: [PATCH 06/10] fix reset chromium --- src/vmk.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vmk.cpp b/src/vmk.cpp index e0a8030..1b90d70 100644 --- a/src/vmk.cpp +++ b/src/vmk.cpp @@ -1175,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)) { @@ -1816,7 +1819,9 @@ namespace fcitx { return; } - state->reset(); + if (event.type() == EventType::InputContextFocusOut) { + state->reset(); + } } void vmkEngine::deactivate(const InputMethodEntry& entry, InputContextEvent& event) { From d0a9f63101350467572f78a8ca9be96f68f8b5ae Mon Sep 17 00:00:00 2001 From: Nguyen Hoang Ky Date: Mon, 16 Feb 2026 23:23:09 +0700 Subject: [PATCH 07/10] fix: duplicate word when use grave key to exit app mode menu --- src/vmk.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vmk.cpp b/src/vmk.cpp index e5fc45a..8d21d96 100644 --- a/src/vmk.cpp +++ b/src/vmk.cpp @@ -1090,16 +1090,12 @@ namespace fcitx { // This ensures text is saved regardless of which mode we're switching from. if (vmkEngine_) { // Finalize preedit properly (required for Preedit mode) - EngineCommitPreedit(vmkEngine_.handle()); - UniqueCPtr commit(EnginePullCommit(vmkEngine_.handle())); - if (commit && commit.get()[0]) { - ic_->commitString(commit.get()); - } - - // Pull any remaining preedit text (for VMK1/VMKSmooth modes) - UniqueCPtr preedit(EnginePullPreedit(vmkEngine_.handle())); - if (preedit && preedit.get()[0]) { - ic_->commitString(preedit.get()); + if (realMode == VMKMode::Preedit) { + EngineCommitPreedit(vmkEngine_.handle()); + UniqueCPtr commit(EnginePullCommit(vmkEngine_.handle())); + if (commit && commit.get()[0]) { + ic_->commitString(commit.get()); + } } } From 69e5cec5a5b3ece8e40727ef92e136912a093e5d Mon Sep 17 00:00:00 2001 From: Nguyen Hoang Ky Date: Tue, 17 Feb 2026 07:03:43 +0700 Subject: [PATCH 08/10] fix: waitAck don't change when focus to other app --- src/vmk.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vmk.cpp b/src/vmk.cpp index 1b90d70..43818cf 100644 --- a/src/vmk.cpp +++ b/src/vmk.cpp @@ -1600,12 +1600,14 @@ namespace fcitx { 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) { - waitAck = true; + needWaitAck = true; break; } } + waitAck = needWaitAck; } } From eea8c53e78ed4696da00237be61745e7527c75ec Mon Sep 17 00:00:00 2001 From: Loc Huynh <148019203+hthienloc@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:07:40 +0700 Subject: [PATCH 09/10] fix: update translation, menu label, logo, README (#59) * change translation string * fix: update app mode menu labels for clarity * feat: add project logo and update README * fix: update TOC --- README.md | 135 ++++++++------------ data/icons/scalable/apps/fcitx-vmk-logo.svg | 41 ++++++ po/vi.po | 6 +- src/vmk.cpp | 14 +- 4 files changed, 102 insertions(+), 94 deletions(-) create mode 100644 data/icons/scalable/apps/fcitx-vmk-logo.svg diff --git a/README.md b/README.md index 0d1c039..58d8971 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@
- Logo + Logo

Fcitx5 VMK

@@ -51,7 +51,6 @@ Dự án này là một bản fork được tối ưu hóa từ [bộ gõ VMK g
  • Bật bộ gõ
  • Hướng dẫn sử dụng
  • Gỡ cài đặt
  • -
  • Cải tiến nổi bật
  • Đóng góp
  • Giấy phép
  • @@ -75,16 +74,17 @@ Hiện tại AUR có 3 gói cài đặt để bạn lựa chọn: | `fcitx5-vmk-bin` | Dùng binary đã build sẵn (không cần biên dịch) | | `fcitx5-vmk-git` | Build từ danh sách commit mới nhất | -Cài đặt bằng `yay` hoặc `paru`: +Cài đặt bằng `yay`: ```bash # Cú pháp: yay -S yay -S fcitx5-vmk - -# Hoặc nếu dùng paru +``` +Hoặc `paru`: +```bash +# Cú pháp: paru -S paru -S fcitx5-vmk ``` -
    @@ -108,7 +108,7 @@ Hoặc có thể xem cách cài của từng distro [tại đây](INSTALL.md). Thêm input của fcitx5-vmk vào `flake.nix`: -``` +```nix { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; @@ -127,7 +127,7 @@ Thêm input của fcitx5-vmk vào `flake.nix`: Bật fcitx5-vmk service trong `configuration.nix`: -``` +```nix { inputs, ... @@ -158,18 +158,18 @@ Rebuild lại system để cài đặt. > Việc biên dịch thủ công đòi hỏi bạn phải hiểu rõ về cấu trúc thư mục của hệ thống. Nếu bạn gặp lỗi "Not Available" hoặc thiếu thư viện khi cài theo cách này trên các distro phổ biến (Ubuntu/Fedora...), hãy quay lại dùng OBS để đảm bảo tính ổn định và tự động cập nhật. ##### Yêu cầu hệ thống - +- Ubuntu/Debian ```bash -# Ubuntu/Debian sudo apt-get install cmake extra-cmake-modules libfcitx5core-dev libfcitx5config-dev libfcitx5utils-dev libinput-dev libudev-dev g++ golang hicolor-icon-theme pkg-config libx11-dev - -# Fedora/RHEL +``` +- Fedora/RHEL +```bash sudo dnf install cmake extra-cmake-modules fcitx5-devel libinput-devel libudev-devel gcc-c++ golang hicolor-icon-theme systemd-devel libX11-devel - -# openSUSE +``` +- openSUSE +```bash sudo zypper install cmake extra-cmake-modules fcitx5-devel libinput-devel systemd-devel gcc-c++ go hicolor-icon-theme systemd-devel libX11-devel udev ``` - ##### Biên dịch và cài đặt ```bash @@ -262,12 +262,11 @@ killall ibus-daemon || ibus exit Thêm `fcitx5` vào danh sách ứng dụng khởi động cùng hệ thống (Autostart).
    -Hướng dẫn Autostart cho từng Desktop Environment (GNOME, KDE, ...) -
    +Hướng dẫn thêm Autostart cho từng DE / WM (GNOME, Hyprland ...) | DE / WM | Hướng dẫn chi tiết | | :------------- | :----------------------------------------------------------------------------------------------------------------------------- | -| **GNOME** | Mở **GNOME Tweaks** → _Startup Applications_ → Add → `Fcitx 5` | +| **GNOME** | **GNOME Tweaks** → _Startup Applications_ → Add → `Fcitx 5` | | **KDE Plasma** | **System Settings** → _Autostart_ → Add... → Add Application... → `Fcitx 5` | | **Xfce** | **Settings** → _Session and Startup_ → _Application Autostart_ → Add → `Fcitx 5` | | **Cinnamon** | **System Settings** → _Startup Applications_ → `+` → Choose application → `Fcitx 5` | @@ -291,16 +290,18 @@ Sau khi đã log out và log in lại: 2. Tìm **VMK** ở cột bên phải. 3. Nhấn mũi tên **<** để thêm nó sang cột bên trái. 4. Apply. +
    +Cấu hình thêm cho Wayland (KDE, Hyprland) -### 5. Cấu hình cho Wayland (KDE và Hyprland) - -Nếu bạn sử dụng **Wayland**, Fcitx5 cần được cấp quyền để hoạt động như bàn phím ảo: +Nếu bạn sử dụng Wayland, Fcitx5 cần được cấu hình thêm để hoạt động như bàn phím ảo: - **KDE Plasma:** Vào _System Settings_ → _Keyboard_ → _Virtual Keyboard_ → Chọn **Fcitx 5**. - **Hyprland:** Thêm dòng sau vào `~/.config/hypr/hyprland.conf`: ```ini permission = fcitx5-vmk-server, keyboard, allow ``` +
    + --- @@ -310,23 +311,25 @@ Nếu bạn sử dụng **Wayland**, Fcitx5 cần được cấp quyền để h ### 1. Menu chuyển mode nhanh -Khi đang ở trong bất kỳ ứng dụng nào, nhấn phím **`** (dấu huyền) để mở menu chọn nhanh: +Khi đang ở trong bất kỳ ứng dụng nào, nhấn phím **`** (dấu huyền) để mở menu chọn chế độ gõ: + +| Chế độ | Mô tả | +|---|---| +| 🚀 **Mode 1 — Uinput (Smooth)** | Chế độ mặc định, phản hồi nhanh. Sử dụng server để gửi phím xoá.
    _Hạn chế:_ Có thể không tương thích với ứng dụng xử lý chậm (ví dụ: LibreOffice). | +| 🐢 **Mode 2 — Uinput (Slow)** | Tương tự Mode 1 nhưng tốc độ gửi phím chậm hơn.
    _Khuyên dùng:_ Cho ứng dụng có tốc độ xử lý input thấp. | +| 🍷 **Mode 3 — Uinput (Hardcore)** | Biến thể của Mode 1.
    _Khuyên dùng:_ Khi chạy ứng dụng Windows qua Wine. | +| ✨ **Mode 4 — Surrounding Text** | Dùng cơ chế Surrounding Text của ứng dụng (tối ưu cho Qt/GTK). Cho phép sửa dấu trên văn bản đã gõ, hoạt động mượt.
    _Lưu ý:_ Phụ thuộc mức hỗ trợ của ứng dụng (có thể không ổn định trên Firefox). | +| 📝 **Mode 5 — Preedit** | Hiển thị gạch chân khi gõ. Độ tương thích cao nhất nhưng trải nghiệm kém tự nhiên hơn các mode trên. | +| 😃 **Emoji Picker** | Tìm kiếm và nhập Emoji (nguồn EmojiOne, hỗ trợ fuzzy search). Xem danh sách [tại đây](data/emoji/EMOJI_GUIDE.md). | +| 📴 **OFF** | Tắt bộ gõ. | +| 🔄 **Remove App Settings** | Khôi phục cấu hình mặc định cho ứng dụng hiện tại. | +| 🚪 **Type `** | Nhập ký tự dấu huyền. | -| Chế độ | Mô tả | -| :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 🚀 **Mode 1 (Uinput smooth)** | Chế độ mặc định, tốc độ phản hồi cao. Sử dụng server để gửi phím xóa.
    _Hạn chế:_ Không tương thích với ứng dụng xử lý chậm (ví dụ: LibreOffice). | -| 🐢 **Mode 2 (Uinput)** | Tương tự Mode 1 nhưng tốc độ gửi phím chậm hơn.
    _Khuyên dùng:_ Cho các ứng dụng có tốc độ xử lý input thấp. | -| 🍷 **Mode 3 (Uinput hardcore)** | Biến thể của Mode 1.
    _Khuyên dùng:_ Chạy ứng dụng Windows qua Wine. | -| ✨ **Mode 4 (Surrounding Text)** | Sử dụng cơ chế Surrounding Text của ứng dụng (tối ưu cho Qt/GTK). Cho phép sửa dấu từ đã gõ và hoạt động mượt mà.
    _Lưu ý:_ Phụ thuộc vào sự hỗ trợ của ứng dụng (có thể không ổn định trên Firefox). | -| 📝 **Mode 5 (Preedit)** | Hiển thị gạch chân khi gõ. Độ tương thích cao nhất nhưng trải nghiệm không tự nhiên bằng các mode trên. | -| 😃 **Emoji mode** | Chế độ tìm kiếm và nhập Emoji (nguồn EmojiOne, hỗ trợ Fuzzy Search). Xem danh sách [tại đây](data/emoji/EMOJI_GUIDE.md). | -| 📴 **OFF** | Tắt bộ gõ cho ứng dụng hiện tại. | -| 🔄 **Remove app settings** | Khôi phục cấu hình mặc định cho ứng dụng. | -| 🚪 **Close menu and type `** | Đóng menu và nhập ký tự dấu huyền. | +Bộ gõ sẽ lưu chế độ đã dùng gần nhất cho từng ứng dụng và tự động khôi phục cấu hình đó khi bạn mở lại cùng ứng dụng. -### 2. Cơ chế đặt lại thông minh +### 2. Cơ chế Smart Reset -Khi bạn click chuột hoặc chạm vào touchpad để đổi vị trí nhập liệu, bộ gõ sẽ tự động đặt lại trạng thái ngay lập tức. Điều này giúp tránh lỗi dính chữ cũ vào từ mới (một lỗi rất phổ biến trên các bộ gõ Linux khác). +Tự động reset trạng thái bộ gõ khi người dùng click chuột hoặc chạm touchpad để di chuyển con trỏ. Điều này ngăn chặn hiện tượng dính ký tự giữa các từ. --- @@ -338,19 +341,16 @@ Khi bạn click chuột hoặc chạm vào touchpad để đổi vị trí nhậ Arch / Arch-based - AUR
    -Bạn có thể dùng `pacman`, `yay` hoặc `paru` để gỡ cài đặt: - +Bạn có thể dùng `pacman` (khuyên dùng), `yay` hoặc `paru` để gỡ cài đặt: ```bash -# Sử dụng pacman (Khuyên dùng) sudo pacman -Rns fcitx5-vmk - -# Nếu dùng yay +``` +```bash yay -Rns fcitx5-vmk - -# Nếu dùng paru +``` +```bash paru -Rns fcitx5-vmk ``` - > **Lưu ý:** Các file config ở `$HOME` sẽ được giữ lại.
    @@ -360,18 +360,18 @@ paru -Rns fcitx5-vmk
    Gỡ package thông thường qua trình quản lý gói: - +- Debian/Ubuntu ```bash -# Debian/Ubuntu sudo apt remove fcitx5-vmk - -# Fedora +``` +- Fedora +```bash sudo dnf remove fcitx5-vmk - -# openSUSE +``` +- openSUSE +```bash sudo zypper remove fcitx5-vmk ``` -
    @@ -396,46 +396,13 @@ sudo make uninstall --- - - -## 🚀 Cải tiến nổi bật - -
    -Click để xem chi tiết kỹ thuật -
    - -Bản fork này thay đổi hoàn toàn kiến trúc của Server và Addon để đạt hiệu năng tối ưu và bảo mật tốt hơn. - -### 1. VMK Server (Backend) - -Server được viết lại theo phong cách **System Programming** hiện đại: - -- **Kiến trúc Event-Driven (Sử dụng `poll`):** Thay thế cơ chế polling liên tục (gây tốn CPU) bằng `poll()` với timeout hợp lý. Server sẽ "ngủ đông" khi không có sự kiện, giúp mức tiêu thụ CPU khi nhàn rỗi gần như 0%. -- **Single-Threaded:** Loại bỏ đa luồng phức tạp, gộp chung việc lắng nghe Socket và sự kiện đầu vào vào một vòng lặp sự kiện duy nhất, giảm overhead và dung lượng binary. -- **Real-time I/O:** Sử dụng socket để giao tiếp trực tiếp giữa server và addon thay vì ghi file log, giúp phản hồi tức thì và bảo vệ ổ cứng. -- **Bảo mật Socket:** - - Sử dụng **Abstract Socket** (không tạo file trên đĩa) kết hợp với xác thực `getsockopt` để đảm bảo chỉ tiến trình hợp lệ mới có thể gửi tín hiệu. - - Khắc phục các rủi ro bảo mật liên quan đến quyền truy cập file socket công khai ở phiên bản cũ. - -### 2. VMK Addon (Frontend) - -Cải thiện trải nghiệm người dùng với các tính năng tiện ích: - -- **Per-App Configuration:** Tự động ghi nhớ chế độ gõ (Mode) riêng biệt cho từng ứng dụng (Ví dụ: Tắt bộ gõ ở Terminal, bật ở Trình duyệt). -- **Menu Phím Tắt Thông Minh (`):** Menu ngữ cảnh hiển thị ngay tại con trỏ văn bản, cho phép chuyển đổi chế độ nhanh chóng. -- **Tính năng mở rộng:** Hỗ trợ sửa dấu từ cũ (Surrounding Text), chế độ nhập Emoji và nhiều cải tiến khác. - -
    - ---- - ## 🤝 Đóng góp Đóng góp là điều làm cho cộng đồng mã nguồn mở trở thành một nơi tuyệt vời để học hỏi, truyền cảm hứng và sáng tạo. Mọi đóng góp của bạn đều được **đánh giá cao**. -Vui lòng xem hướng dẫn chi tiết tại [đây](CONTRIBUTING.md) để biết cách tham gia phát triển dự án, quy trình Pull Request và quy tắc code style. +Vui lòng xem hướng dẫn chi tiết [tại đây](CONTRIBUTING.md) để biết cách tham gia phát triển dự án, quy trình Pull Request và quy tắc code style. Đừng quên tặng dự án một ⭐! Cảm ơn bạn rất nhiều! diff --git a/data/icons/scalable/apps/fcitx-vmk-logo.svg b/data/icons/scalable/apps/fcitx-vmk-logo.svg new file mode 100644 index 0000000..9a907bc --- /dev/null +++ b/data/icons/scalable/apps/fcitx-vmk-logo.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/po/vi.po b/po/vi.po index fa6938e..98235da 100644 --- a/po/vi.po +++ b/po/vi.po @@ -135,8 +135,8 @@ msgid " (Default)" msgstr " (Mặc định)" #: src/vmk.cpp:1871 -msgid "App name detected by fcitx5: " -msgstr "Tên ứng dụng nhận dạng bởi fcitx5: " +msgid "App: " +msgstr "Ứng dụng: " #: src/vmk.cpp:1872 msgid "1. Fake backspace by Uinput (smooth)" @@ -148,7 +148,7 @@ msgstr "2. Phím xóa giả bởi uinput" #: src/vmk.cpp:1874 msgid "3. Fake backspace by Uinput for wine apps" -msgstr "3. Phím xóa giả bởi uinput cho các ứng dụng wine" +msgstr "3. Phím xóa giả bởi uinput cho ứng dụng wine" #: src/vmk.cpp:1875 msgid "4. Surrounding Text" diff --git a/src/vmk.cpp b/src/vmk.cpp index 5d2b8ff..69701b5 100644 --- a/src/vmk.cpp +++ b/src/vmk.cpp @@ -2061,15 +2061,15 @@ namespace fcitx { // Build candidate list for app mode menu // Structure: Header + 8 selectable items (4 VMK modes + 4 special options) candidateList->append(std::make_unique(Text(_("App: ") + currentConfigureApp_))); - candidateList->append(std::make_unique(getLabel(VMKMode::VMKSmooth, _("[1] Fake backspace by Uinput (smooth)")), applyMode(VMKMode::VMKSmooth))); - candidateList->append(std::make_unique(getLabel(VMKMode::VMK1, _("[2] Fake backspace by Uinput")), applyMode(VMKMode::VMK1))); - candidateList->append(std::make_unique(getLabel(VMKMode::VMK1HC, _("[3] Fake backspace by Uinput for wine apps")), applyMode(VMKMode::VMK1HC))); + candidateList->append(std::make_unique(getLabel(VMKMode::VMKSmooth, _("[1] Uinput (smooth)")), applyMode(VMKMode::VMKSmooth))); + candidateList->append(std::make_unique(getLabel(VMKMode::VMK1, _("[2] Uinput (Slow)")), applyMode(VMKMode::VMK1))); + candidateList->append(std::make_unique(getLabel(VMKMode::VMK1HC, _("[3] Uinput (Hardcore)")), applyMode(VMKMode::VMK1HC))); candidateList->append(std::make_unique(getLabel(VMKMode::VMK2, _("[4] Surrounding Text")), applyMode(VMKMode::VMK2))); candidateList->append(std::make_unique(getLabel(VMKMode::Preedit, _("[q] Preedit")), applyMode(VMKMode::Preedit))); - candidateList->append(std::make_unique(getLabel(VMKMode::Emoji, _("[w] Emoji mode")), applyMode(VMKMode::Emoji))); - candidateList->append(std::make_unique(getLabel(VMKMode::Off, _("[e] OFF - Disable Input Method")), applyMode(VMKMode::Off))); + candidateList->append(std::make_unique(getLabel(VMKMode::Emoji, _("[w] Emoji Picker")), applyMode(VMKMode::Emoji))); + candidateList->append(std::make_unique(getLabel(VMKMode::Off, _("[e] OFF")), applyMode(VMKMode::Off))); - candidateList->append(std::make_unique(Text(_("[r] Remove app settings")), [this, cleanup](InputContext* ic) { + candidateList->append(std::make_unique(Text(_("[r] Remove App Settings")), [this, cleanup](InputContext* ic) { if (appRules_.count(currentConfigureApp_)) { appRules_.erase(currentConfigureApp_); saveAppRules(); @@ -2077,7 +2077,7 @@ namespace fcitx { cleanup(ic); })); - candidateList->append(std::make_unique(Text(_("[`] Close menu and type `")), [cleanup](InputContext* ic) { + candidateList->append(std::make_unique(Text(_("[`] Type `")), [cleanup](InputContext* ic) { cleanup(ic); ic->commitString("`"); })); From 61e17044fc635b0813ac3e12caf1dab5fb12c4db Mon Sep 17 00:00:00 2001 From: Nguyen Hoang Ky Date: Tue, 17 Feb 2026 20:47:01 +0700 Subject: [PATCH 10/10] Update translation --- README.md | 48 +++++++++++++------ po/fcitx5-vmk.pot | 102 ++++++++++++++++++++++----------------- po/vi.po | 120 ++++++++++++++++++++++++++-------------------- 3 files changed, 161 insertions(+), 109 deletions(-) diff --git a/README.md b/README.md index 58d8971..09ec6a5 100644 --- a/README.md +++ b/README.md @@ -80,11 +80,14 @@ Cài đặt bằng `yay`: # Cú pháp: yay -S yay -S fcitx5-vmk ``` + Hoặc `paru`: + ```bash # Cú pháp: paru -S paru -S fcitx5-vmk ``` +
    @@ -158,18 +161,25 @@ Rebuild lại system để cài đặt. > Việc biên dịch thủ công đòi hỏi bạn phải hiểu rõ về cấu trúc thư mục của hệ thống. Nếu bạn gặp lỗi "Not Available" hoặc thiếu thư viện khi cài theo cách này trên các distro phổ biến (Ubuntu/Fedora...), hãy quay lại dùng OBS để đảm bảo tính ổn định và tự động cập nhật. ##### Yêu cầu hệ thống + - Ubuntu/Debian + ```bash sudo apt-get install cmake extra-cmake-modules libfcitx5core-dev libfcitx5config-dev libfcitx5utils-dev libinput-dev libudev-dev g++ golang hicolor-icon-theme pkg-config libx11-dev ``` + - Fedora/RHEL + ```bash sudo dnf install cmake extra-cmake-modules fcitx5-devel libinput-devel libudev-devel gcc-c++ golang hicolor-icon-theme systemd-devel libX11-devel ``` + - openSUSE + ```bash sudo zypper install cmake extra-cmake-modules fcitx5-devel libinput-devel systemd-devel gcc-c++ go hicolor-icon-theme systemd-devel libX11-devel udev ``` + ##### Biên dịch và cài đặt ```bash @@ -266,7 +276,7 @@ Thêm `fcitx5` vào danh sách ứng dụng khởi động cùng hệ thống (A | DE / WM | Hướng dẫn chi tiết | | :------------- | :----------------------------------------------------------------------------------------------------------------------------- | -| **GNOME** | **GNOME Tweaks** → _Startup Applications_ → Add → `Fcitx 5` | +| **GNOME** | **GNOME Tweaks** → _Startup Applications_ → Add → `Fcitx 5` | | **KDE Plasma** | **System Settings** → _Autostart_ → Add... → Add Application... → `Fcitx 5` | | **Xfce** | **Settings** → _Session and Startup_ → _Application Autostart_ → Add → `Fcitx 5` | | **Cinnamon** | **System Settings** → _Startup Applications_ → `+` → Choose application → `Fcitx 5` | @@ -300,8 +310,7 @@ Nếu bạn sử dụng Wayland, Fcitx5 cần được cấu hình thêm để h ```ini permission = fcitx5-vmk-server, keyboard, allow ``` -
    - + --- @@ -313,17 +322,17 @@ Nếu bạn sử dụng Wayland, Fcitx5 cần được cấu hình thêm để h Khi đang ở trong bất kỳ ứng dụng nào, nhấn phím **`** (dấu huyền) để mở menu chọn chế độ gõ: -| Chế độ | Mô tả | -|---|---| -| 🚀 **Mode 1 — Uinput (Smooth)** | Chế độ mặc định, phản hồi nhanh. Sử dụng server để gửi phím xoá.
    _Hạn chế:_ Có thể không tương thích với ứng dụng xử lý chậm (ví dụ: LibreOffice). | -| 🐢 **Mode 2 — Uinput (Slow)** | Tương tự Mode 1 nhưng tốc độ gửi phím chậm hơn.
    _Khuyên dùng:_ Cho ứng dụng có tốc độ xử lý input thấp. | -| 🍷 **Mode 3 — Uinput (Hardcore)** | Biến thể của Mode 1.
    _Khuyên dùng:_ Khi chạy ứng dụng Windows qua Wine. | -| ✨ **Mode 4 — Surrounding Text** | Dùng cơ chế Surrounding Text của ứng dụng (tối ưu cho Qt/GTK). Cho phép sửa dấu trên văn bản đã gõ, hoạt động mượt.
    _Lưu ý:_ Phụ thuộc mức hỗ trợ của ứng dụng (có thể không ổn định trên Firefox). | -| 📝 **Mode 5 — Preedit** | Hiển thị gạch chân khi gõ. Độ tương thích cao nhất nhưng trải nghiệm kém tự nhiên hơn các mode trên. | -| 😃 **Emoji Picker** | Tìm kiếm và nhập Emoji (nguồn EmojiOne, hỗ trợ fuzzy search). Xem danh sách [tại đây](data/emoji/EMOJI_GUIDE.md). | -| 📴 **OFF** | Tắt bộ gõ. | -| 🔄 **Remove App Settings** | Khôi phục cấu hình mặc định cho ứng dụng hiện tại. | -| 🚪 **Type `** | Nhập ký tự dấu huyền. | +| Chế độ | Mô tả | +| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 🚀 **Mode 1 — Uinput (Smooth)** | Chế độ mặc định, phản hồi nhanh. Sử dụng server để gửi phím xoá.
    _Hạn chế:_ Có thể không tương thích với ứng dụng xử lý chậm (ví dụ: LibreOffice). | +| 🐢 **Mode 2 — Uinput (Slow)** | Tương tự Mode 1 nhưng tốc độ gửi phím chậm hơn.
    _Khuyên dùng:_ Cho ứng dụng có tốc độ xử lý input thấp. | +| 🍷 **Mode 3 — Uinput (Hardcore)** | Biến thể của Mode 1.
    _Khuyên dùng:_ Khi chạy ứng dụng Windows qua Wine. | +| ✨ **Mode 4 — Surrounding Text** | Dùng cơ chế Surrounding Text của ứng dụng (tối ưu cho Qt/GTK). Cho phép sửa dấu trên văn bản đã gõ, hoạt động mượt.
    _Lưu ý:_ Phụ thuộc mức hỗ trợ của ứng dụng (có thể không ổn định trên Firefox). | +| 📝 **Mode 5 — Preedit** | Hiển thị gạch chân khi gõ. Độ tương thích cao nhất nhưng trải nghiệm kém tự nhiên hơn các mode trên. | +| 😃 **Emoji Picker** | Tìm kiếm và nhập Emoji (nguồn EmojiOne, hỗ trợ fuzzy search). Xem danh sách [tại đây](data/emoji/EMOJI_GUIDE.md). | +| 📴 **OFF** | Tắt bộ gõ. | +| 🔄 **Remove App Settings** | Khôi phục cấu hình mặc định cho ứng dụng hiện tại. | +| 🚪 **Type `** | Nhập ký tự dấu huyền. | Bộ gõ sẽ lưu chế độ đã dùng gần nhất cho từng ứng dụng và tự động khôi phục cấu hình đó khi bạn mở lại cùng ứng dụng. @@ -342,15 +351,19 @@ Tự động reset trạng thái bộ gõ khi người dùng click chuột hoặ
    Bạn có thể dùng `pacman` (khuyên dùng), `yay` hoặc `paru` để gỡ cài đặt: + ```bash sudo pacman -Rns fcitx5-vmk ``` + ```bash yay -Rns fcitx5-vmk ``` + ```bash paru -Rns fcitx5-vmk ``` + > **Lưu ý:** Các file config ở `$HOME` sẽ được giữ lại. @@ -360,18 +373,25 @@ paru -Rns fcitx5-vmk
    Gỡ package thông thường qua trình quản lý gói: + - Debian/Ubuntu + ```bash sudo apt remove fcitx5-vmk ``` + - Fedora + ```bash sudo dnf remove fcitx5-vmk ``` + - openSUSE + ```bash sudo zypper remove fcitx5-vmk ``` +
    diff --git a/po/fcitx5-vmk.pot b/po/fcitx5-vmk.pot index 33469bc..44a9f4b 100644 --- a/po/fcitx5-vmk.pot +++ b/po/fcitx5-vmk.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: fcitx5-vmk 0.12.1\n" "Report-Msgid-Bugs-To: nhktmdzhg@gmail.com\n" -"POT-Creation-Date: 2026-02-15 12:12+0700\n" +"POT-Creation-Date: 2026-02-17 20:07+0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -29,7 +29,7 @@ msgstr "" msgid "Macro" msgstr "" -#: src/vmk-config.h:124 src/vmk-config.h:138 +#: src/vmk-config.h:124 src/vmk-config.h:139 msgid "Custom Keymap" msgstr "" @@ -45,126 +45,142 @@ msgstr "" msgid "Output Charset" msgstr "" -#: src/vmk-config.h:133 src/vmk.cpp:1345 +#: src/vmk-config.h:133 src/vmk.cpp:1399 msgid "Enable spell check" msgstr "" -#: src/vmk-config.h:133 src/vmk.cpp:1357 +#: src/vmk-config.h:133 src/vmk.cpp:1411 msgid "Enable Macro" msgstr "" -#: src/vmk-config.h:134 src/vmk.cpp:1369 +#: src/vmk-config.h:134 src/vmk.cpp:1423 msgid "Capitalize Macro" msgstr "" -#: src/vmk-config.h:135 src/vmk.cpp:1381 +#: src/vmk-config.h:135 src/vmk.cpp:1435 msgid "Auto restore keys with invalid words" msgstr "" -#: src/vmk-config.h:136 src/vmk.cpp:1393 +#: src/vmk-config.h:136 src/vmk.cpp:1447 msgid "Use oà, _uý (instead of òa, úy)" msgstr "" -#: src/vmk-config.h:137 src/vmk.cpp:1405 +#: src/vmk-config.h:137 src/vmk.cpp:1459 msgid "Allow type with more freedom" msgstr "" -#: src/vmk.cpp:1260 -msgid "Typing Mode" +#: src/vmk-config.h:138 src/vmk.cpp:1471 +msgid "Fix uinput mode with ack" +msgstr "" + +#: src/vmk.cpp:420 +msgid "Page " msgstr "" #: src/vmk.cpp:1317 +msgid "Typing Mode" +msgstr "" + +#: src/vmk.cpp:1371 msgid "Charset" msgstr "" -#: src/vmk.cpp:1734 +#: src/vmk.cpp:1878 msgid "Typing Mode: " msgstr "" -#: src/vmk.cpp:1765 +#: src/vmk.cpp:1909 msgid "Spell Check: On" msgstr "" -#: src/vmk.cpp:1765 +#: src/vmk.cpp:1909 msgid "Spell Check: Off" msgstr "" -#: src/vmk.cpp:1773 +#: src/vmk.cpp:1917 msgid "Macro: On" msgstr "" -#: src/vmk.cpp:1773 +#: src/vmk.cpp:1917 msgid "Macro: Off" msgstr "" -#: src/vmk.cpp:1781 +#: src/vmk.cpp:1925 msgid "Capitalize Macro: On" msgstr "" -#: src/vmk.cpp:1781 +#: src/vmk.cpp:1925 msgid "Capitalize Macro: Off" msgstr "" -#: src/vmk.cpp:1789 +#: src/vmk.cpp:1933 msgid "Auto Non-VN Restore: On" msgstr "" -#: src/vmk.cpp:1789 +#: src/vmk.cpp:1933 msgid "Auto Non-VN Restore: Off" msgstr "" -#: src/vmk.cpp:1797 +#: src/vmk.cpp:1941 msgid "Modern Style: On" msgstr "" -#: src/vmk.cpp:1797 +#: src/vmk.cpp:1941 msgid "Modern Style: Off" msgstr "" -#: src/vmk.cpp:1805 +#: src/vmk.cpp:1949 msgid "Free Marking: On" msgstr "" -#: src/vmk.cpp:1805 +#: src/vmk.cpp:1949 msgid "Free Marking: Off" msgstr "" -#: src/vmk.cpp:1865 -msgid " (Default)" +#: src/vmk.cpp:1957 +msgid "Fix Vmk1 With Ack: On" +msgstr "" + +#: src/vmk.cpp:1957 +msgid "Fix Vmk1 With Ack: Off" +msgstr "" + +#: src/vmk.cpp:2063 +msgid "App: " msgstr "" -#: src/vmk.cpp:1871 -msgid "App name detected by fcitx5: " +#: src/vmk.cpp:2064 +msgid "[1] Uinput (smooth)" msgstr "" -#: src/vmk.cpp:1872 -msgid "1. Fake backspace by Uinput (smooth)" +#: src/vmk.cpp:2065 +msgid "[2] Uinput (Slow)" msgstr "" -#: src/vmk.cpp:1873 -msgid "2. Fake backspace by Uinput" +#: src/vmk.cpp:2066 +msgid "[3] Uinput (Hardcore)" msgstr "" -#: src/vmk.cpp:1874 -msgid "3. Fake backspace by Uinput for wine apps" +#: src/vmk.cpp:2067 +msgid "[4] Surrounding Text" msgstr "" -#: src/vmk.cpp:1875 -msgid "4. Surrounding Text" +#: src/vmk.cpp:2068 +msgid "[q] Preedit" msgstr "" -#: src/vmk.cpp:1876 -msgid "5. Preedit" +#: src/vmk.cpp:2069 +msgid "[w] Emoji Picker" msgstr "" -#: src/vmk.cpp:1877 -msgid "6. Emoji mode" +#: src/vmk.cpp:2070 +msgid "[e] OFF" msgstr "" -#: src/vmk.cpp:1879 -msgid "8. Remove app settings" +#: src/vmk.cpp:2072 +msgid "[r] Remove App Settings" msgstr "" -#: src/vmk.cpp:1880 -msgid "`. Close menu and type `" +#: src/vmk.cpp:2080 +msgid "[`] Type `" msgstr "" diff --git a/po/vi.po b/po/vi.po index 98235da..f9a6cbf 100644 --- a/po/vi.po +++ b/po/vi.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: nhktmdzhg@gmail.com\n" -"POT-Creation-Date: 2026-02-15 12:12+0700\n" -"PO-Revision-Date: 2026-02-13 21:59+0700\n" +"POT-Creation-Date: 2026-02-17 20:07+0700\n" +"PO-Revision-Date: 2026-02-17 20:44+0700\n" "Last-Translator: Loc Huynh \n" "Language-Team: none\n" "Language: vi\n" @@ -30,7 +30,7 @@ msgstr "Giá trị" msgid "Macro" msgstr "Gõ tắt" -#: src/vmk-config.h:124 src/vmk-config.h:138 +#: src/vmk-config.h:124 src/vmk-config.h:139 msgid "Custom Keymap" msgstr "Keymap tùy chỉnh" @@ -46,126 +46,142 @@ msgstr "Kiểu gõ" msgid "Output Charset" msgstr "Bảng mã" -#: src/vmk-config.h:133 src/vmk.cpp:1345 +#: src/vmk-config.h:133 src/vmk.cpp:1399 msgid "Enable spell check" msgstr "Bật kiểm tra chính tả" -#: src/vmk-config.h:133 src/vmk.cpp:1357 +#: src/vmk-config.h:133 src/vmk.cpp:1411 msgid "Enable Macro" msgstr "Bật gõ tắt" -#: src/vmk-config.h:134 src/vmk.cpp:1369 +#: src/vmk-config.h:134 src/vmk.cpp:1423 msgid "Capitalize Macro" msgstr "Gõ tắt chữ hoa" -#: src/vmk-config.h:135 src/vmk.cpp:1381 +#: src/vmk-config.h:135 src/vmk.cpp:1435 msgid "Auto restore keys with invalid words" msgstr "Tự động khôi phục phím với từ sai" -#: src/vmk-config.h:136 src/vmk.cpp:1393 +#: src/vmk-config.h:136 src/vmk.cpp:1447 msgid "Use oà, _uý (instead of òa, úy)" msgstr "Dùng oà, _uý (thay vì òa, úy)" -#: src/vmk-config.h:137 src/vmk.cpp:1405 +#: src/vmk-config.h:137 src/vmk.cpp:1459 msgid "Allow type with more freedom" msgstr "Cho phép gõ dấu tự do" -#: src/vmk.cpp:1260 +#: src/vmk-config.h:138 src/vmk.cpp:1471 +msgid "Fix uinput mode with ack" +msgstr "Sửa lỗi chế độ uinput với ack" + +#: src/vmk.cpp:420 +msgid "Page " +msgstr "Trang " + +#: src/vmk.cpp:1317 msgid "Typing Mode" msgstr "Chế độ gõ" -#: src/vmk.cpp:1317 +#: src/vmk.cpp:1371 msgid "Charset" msgstr "Bảng mã" -#: src/vmk.cpp:1734 +#: src/vmk.cpp:1878 msgid "Typing Mode: " msgstr "Chế độ gõ: " -#: src/vmk.cpp:1765 +#: src/vmk.cpp:1909 msgid "Spell Check: On" msgstr "Kiểm tra chính tả: Bật" -#: src/vmk.cpp:1765 +#: src/vmk.cpp:1909 msgid "Spell Check: Off" msgstr "Kiểm tra chính tả: Tắt" -#: src/vmk.cpp:1773 +#: src/vmk.cpp:1917 msgid "Macro: On" msgstr "Gõ tắt: Bật" -#: src/vmk.cpp:1773 +#: src/vmk.cpp:1917 msgid "Macro: Off" msgstr "Gõ tắt: Tắt" -#: src/vmk.cpp:1781 +#: src/vmk.cpp:1925 msgid "Capitalize Macro: On" msgstr "Gõ tắt chữ hoa: Bật" -#: src/vmk.cpp:1781 +#: src/vmk.cpp:1925 msgid "Capitalize Macro: Off" msgstr "Gõ tắt chữ hoa: Tắt" -#: src/vmk.cpp:1789 +#: src/vmk.cpp:1933 msgid "Auto Non-VN Restore: On" msgstr "Tự động khôi phục từ không phải Tiếng Việt: Bật" -#: src/vmk.cpp:1789 +#: src/vmk.cpp:1933 msgid "Auto Non-VN Restore: Off" msgstr "Tự động khôi phục từ không phải Tiếng Việt: Tắt" -#: src/vmk.cpp:1797 +#: src/vmk.cpp:1941 msgid "Modern Style: On" msgstr "Dấu chuẩn: Bật" -#: src/vmk.cpp:1797 +#: src/vmk.cpp:1941 msgid "Modern Style: Off" msgstr "Dấu chuẩn: Tắt" -#: src/vmk.cpp:1805 +#: src/vmk.cpp:1949 msgid "Free Marking: On" msgstr "Dấu tự do: Bật" -#: src/vmk.cpp:1805 +#: src/vmk.cpp:1949 msgid "Free Marking: Off" msgstr "Dấu tự do: Tắt" -#: src/vmk.cpp:1865 -msgid " (Default)" -msgstr " (Mặc định)" +#: src/vmk.cpp:1957 +msgid "Fix Vmk1 With Ack: On" +msgstr "Sửa lỗi Vmk1 với Ack: Bật" + +#: src/vmk.cpp:1957 +msgid "Fix Vmk1 With Ack: Off" +msgstr "Sửa lỗi Vmk1 với Ack: Tắt" -#: src/vmk.cpp:1871 +#: src/vmk.cpp:2063 msgid "App: " msgstr "Ứng dụng: " -#: src/vmk.cpp:1872 -msgid "1. Fake backspace by Uinput (smooth)" -msgstr "1. Phím xóa giả bởi uinput (mượt)" +#: src/vmk.cpp:2064 +msgid "[1] Uinput (smooth)" +msgstr "[1] Uinput (mượt)" + +#: src/vmk.cpp:2065 +msgid "[2] Uinput (Slow)" +msgstr "[2] Uinput (chậm)" -#: src/vmk.cpp:1873 -msgid "2. Fake backspace by Uinput" -msgstr "2. Phím xóa giả bởi uinput" +#: src/vmk.cpp:2066 +msgid "[3] Uinput (Hardcore)" +msgstr "[3] Uinput (Hardcore)" -#: src/vmk.cpp:1874 -msgid "3. Fake backspace by Uinput for wine apps" -msgstr "3. Phím xóa giả bởi uinput cho ứng dụng wine" +#: src/vmk.cpp:2067 +msgid "[4] Surrounding Text" +msgstr "[4] Văn bản xung quanh" -#: src/vmk.cpp:1875 -msgid "4. Surrounding Text" -msgstr "4. Văn bản xung quanh" +#: src/vmk.cpp:2068 +msgid "[q] Preedit" +msgstr "[q] Gạch chân" -#: src/vmk.cpp:1876 -msgid "5. Preedit" -msgstr "5. Gạch chân" +#: src/vmk.cpp:2069 +msgid "[w] Emoji Picker" +msgstr "[w] Chọn Emoji" -#: src/vmk.cpp:1877 -msgid "6. Emoji mode" -msgstr "6. Chế độ emoji" +#: src/vmk.cpp:2070 +msgid "[e] OFF" +msgstr "[e] Tắt" -#: src/vmk.cpp:1879 -msgid "8. Remove app settings" -msgstr "8. Xóa cài đặt ứng dụng" +#: src/vmk.cpp:2072 +msgid "[r] Remove App Settings" +msgstr "[r] Xóa cài đặt ứng dụng" -#: src/vmk.cpp:1880 -msgid "`. Close menu and type `" -msgstr "`. Đóng menu và gõ `" +#: src/vmk.cpp:2080 +msgid "[`] Type `" +msgstr "[`] Nhập `" \ No newline at end of file